Compare commits
9 Commits
91e1f18575
...
0f8b6c6b29
Author | SHA1 | Date | |
---|---|---|---|
0f8b6c6b29 | |||
94af8462d3 | |||
d020552af5 | |||
f53a797169 | |||
8dec198afe | |||
3bed3e0578 | |||
fee0b4e681 | |||
61914d2392 | |||
3de838dded |
@ -1 +1,8 @@
|
|||||||
WEBSITE_PORT=5000
|
WEBSITE_PORT=5000
|
||||||
|
API_PORT=5500
|
||||||
|
|
||||||
|
DATABASE_USER=wikipedia_user
|
||||||
|
DATABASE_PASSWORD=password
|
||||||
|
DATABASE_NAME=wikipedia
|
||||||
|
DATABASE_HOST=127.0.0.1
|
||||||
|
DATABASE_PORT=3306
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -20,8 +20,12 @@ build/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
.turbo
|
.turbo
|
||||||
bin/
|
tmp/
|
||||||
cache.json
|
cache.json
|
||||||
|
data/dump
|
||||||
|
data/sql/*
|
||||||
|
!data/sql/0000-tables-create.sql
|
||||||
|
!data/sql/0999-constraints.sql
|
||||||
|
|
||||||
# debug
|
# debug
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
14
README.md
14
README.md
@ -22,6 +22,7 @@ Available online: <https://wikipedia-game-solver.theoludwig.fr>
|
|||||||
|
|
||||||
- [Node.js](https://nodejs.org/) >= 22.0.0
|
- [Node.js](https://nodejs.org/) >= 22.0.0
|
||||||
- [pnpm](https://pnpm.io/) >= 9.5.0
|
- [pnpm](https://pnpm.io/) >= 9.5.0
|
||||||
|
- [Docker](https://www.docker.com/)
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ cd wikipedia-game-solver
|
|||||||
# Configure environment variables
|
# Configure environment variables
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
cp apps/website/.env.example apps/website/.env
|
cp apps/website/.env.example apps/website/.env
|
||||||
|
cp apps/api/.env.example apps/api/.env
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
pnpm install --frozen-lockfile
|
pnpm install --frozen-lockfile
|
||||||
@ -43,9 +45,12 @@ pnpm exec playwright install --with-deps
|
|||||||
### Development
|
### Development
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# Start the development server
|
# Start the development servers
|
||||||
node --run dev
|
node --run dev
|
||||||
|
|
||||||
|
# Start the development Docker services (e.g: Database)
|
||||||
|
docker compose up --file compose.dev.yaml
|
||||||
|
|
||||||
# Lint
|
# Lint
|
||||||
node --run lint:editorconfig
|
node --run lint:editorconfig
|
||||||
node --run lint:prettier
|
node --run lint:prettier
|
||||||
@ -57,6 +62,10 @@ node --run test
|
|||||||
|
|
||||||
# Build
|
# Build
|
||||||
node --run build
|
node --run build
|
||||||
|
|
||||||
|
# To execute a command in a specific package (e.g: apps/api)
|
||||||
|
cd apps/api
|
||||||
|
node --run ace -- list
|
||||||
```
|
```
|
||||||
|
|
||||||
### Production environment with [Docker](https://www.docker.com/)
|
### Production environment with [Docker](https://www.docker.com/)
|
||||||
@ -68,7 +77,8 @@ docker compose up --build
|
|||||||
|
|
||||||
#### Services started
|
#### Services started
|
||||||
|
|
||||||
`wikipedia-game-solver`: <http://127.0.0.1:5000>
|
- `apps/website`: <http://127.0.0.1:5000>
|
||||||
|
- `apps/api`: <http://127.0.0.1:5500>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
33
TODO.md
33
TODO.md
@ -1,13 +1,40 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
- [x] chore: initial commit (+ mirror on GitHub)
|
- [x] chore: initial commit
|
||||||
- [x] Deploy first staging version (v1.0.0-staging.1)
|
- [x] Deploy first staging version (v1.0.0-staging.1)
|
||||||
|
- [x] Wikipedia Database Dump
|
||||||
|
- [x] Download SQL files
|
||||||
|
- [x] Extract SQL files
|
||||||
|
- [x] Tables structure `CREATE TABLE`
|
||||||
|
- [x] `page.sql` (`pages` table)
|
||||||
|
- [x] `pagelinks.sql` (`internal_links` table)
|
||||||
|
- [x] Adapt downloaded SQL files
|
||||||
|
- [x] `page.sql` (`pages` table)
|
||||||
|
- [x] `pagelinks.sql` (`internal_links` table)
|
||||||
|
- [x] Import SQL files
|
||||||
|
- [x] Try `SELECT count(*) FROM internal_links il WHERE il.from_page_id = (SELECT p.id FROM pages p WHERE p.title = 'Linux'); -- Count of internal links for 'Linux' page`
|
||||||
|
- [x] Try:
|
||||||
|
```sql
|
||||||
|
SELECT il.to_page_id, pl.title
|
||||||
|
FROM internal_links il
|
||||||
|
JOIN pages pl ON pl.id = il.to_page_id
|
||||||
|
WHERE il.from_page_id = (
|
||||||
|
SELECT p.id FROM pages p WHERE p.title = 'Node.js'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
- [ ] Move from POC (Proof of concept) in `data` folder to `apps/cli` folder
|
||||||
|
- [ ] Documentation how to use + Last execution date
|
||||||
|
- [ ] Rewrite bash script to download and extract SQL files from Wikipedia Database Dump to Node.js for better cross-platform support and easier maintenance + automation, preferably one Node.js script to generate everything to create the database
|
||||||
|
- [ ] Verify file content up to before inserts, to check if it matches last version, and diff with last version
|
||||||
|
- [ ] Update logic to create custom `internal_links` table to make it work with latest wikipedia dumps (notably concerning the change in `pagelinks.sql` where the title is not included anymore, but instead it uses `pl_target_id`, foreign key to `linktarget`), last tested dumb working `20240420`
|
||||||
|
- [ ] Handle redirects
|
||||||
|
- [ ] Implement REST API (`api`) with JSON responses ([AdonisJS](https://adonisjs.com/)) to get shortest paths between 2 pages
|
||||||
- [ ] Implement Wikipedia Game Solver (`website`) with inputs, button to submit, and list all pages to go from one to another, or none if it is not possible
|
- [ ] Implement Wikipedia Game Solver (`website`) with inputs, button to submit, and list all pages to go from one to another, or none if it is not possible
|
||||||
- [ ] Check, cache and store (in `.json` file) all Wikipedia Pages and its internal links, maybe use Wikipedia Dump (<https://en.wikipedia.org/wiki/Wikipedia:Database_download>)?
|
|
||||||
- [ ] Implement toast notifications for errors, warnings, and success messages
|
- [ ] Implement toast notifications for errors, warnings, and success messages
|
||||||
- [ ] Implement CLI (`cli`)
|
- [ ] Implement CLI (`cli`)
|
||||||
- [ ] Implement REST API (`api`) with JSON responses ([AdonisJS](https://adonisjs.com/))
|
|
||||||
- [ ] Add docs to add locale/edit translations, create component, install a dependency in a package, create a new package, technology used, architecture, links where it's deployed, how to use/install for end users, how to update dependencies with `npx taze -l` etc.
|
- [ ] Add docs to add locale/edit translations, create component, install a dependency in a package, create a new package, technology used, architecture, links where it's deployed, how to use/install for end users, how to update dependencies with `npx taze -l` etc.
|
||||||
|
- [ ] GitHub Mirror
|
||||||
|
- [ ] Delete `TODO.md` file and instead use issues for the remaining tasks
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
12
apps/api/.env.example
Normal file
12
apps/api/.env.example
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
TZ=UTC
|
||||||
|
PORT=5500
|
||||||
|
HOST=0.0.0.0
|
||||||
|
LOG_LEVEL=info
|
||||||
|
APP_KEY=LFGmw8iGkYF7vfS18ZB9-1Gn-6LfmoAk
|
||||||
|
NODE_ENV=development
|
||||||
|
|
||||||
|
DATABASE_USER=wikipedia_user
|
||||||
|
DATABASE_PASSWORD=password
|
||||||
|
DATABASE_NAME=wikipedia
|
||||||
|
DATABASE_HOST=127.0.0.1
|
||||||
|
DATABASE_PORT=3306
|
14
apps/api/.eslintrc.json
Normal file
14
apps/api/.eslintrc.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"extends": ["@repo/eslint-config"],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"files": ["*.ts", "*.tsx"],
|
||||||
|
"plugins": ["@typescript-eslint"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"project": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
29
apps/api/Dockerfile
Normal file
29
apps/api/Dockerfile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
FROM node:22.4.1-slim AS node-pnpm
|
||||||
|
ENV PNPM_HOME="/pnpm"
|
||||||
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
|
RUN corepack enable
|
||||||
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
FROM node-pnpm AS builder
|
||||||
|
RUN pnpm install --global turbo@2.0.12
|
||||||
|
COPY ./ ./
|
||||||
|
RUN turbo prune @repo/api --docker
|
||||||
|
|
||||||
|
FROM node-pnpm AS installer
|
||||||
|
COPY .gitignore .gitignore
|
||||||
|
COPY --from=builder /usr/src/app/out/json/ ./
|
||||||
|
COPY --from=builder /usr/src/app/out/pnpm-lock.yaml ./pnpm-lock.yaml
|
||||||
|
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile --prod
|
||||||
|
COPY --from=builder /usr/src/app/out/full/ ./
|
||||||
|
COPY turbo.json turbo.json
|
||||||
|
# RUN pnpm --filter=@repo/api... exec turbo run build
|
||||||
|
|
||||||
|
FROM node-pnpm AS runner
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV HOSTNAME=0.0.0.0
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 applicationrunner
|
||||||
|
USER applicationrunner
|
||||||
|
COPY --from=installer --chown=applicationrunner:nodejs /usr/src/app ./
|
||||||
|
WORKDIR /usr/src/app/apps/api
|
||||||
|
CMD ["node", "--import=tsx", "./src/bin/server.ts"]
|
46
apps/api/package.json
Normal file
46
apps/api/package.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "@repo/api",
|
||||||
|
"version": "1.0.0-staging.3",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"imports": {
|
||||||
|
"#*": "./src/*"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "node --import=tsx ./src/bin/server.ts",
|
||||||
|
"dev": "node --import=tsx --watch --watch-preserve-output ./src/bin/server.ts",
|
||||||
|
"ace": "node --import=tsx ./src/bin/console.ts",
|
||||||
|
"test": "node --import=tsx ./src/bin/test.ts",
|
||||||
|
"lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives",
|
||||||
|
"lint:typescript": "tsc --noEmit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@adonisjs/auth": "catalog:",
|
||||||
|
"@adonisjs/core": "catalog:",
|
||||||
|
"@adonisjs/cors": "catalog:",
|
||||||
|
"@adonisjs/lucid": "catalog:",
|
||||||
|
"@repo/utils": "workspace:*",
|
||||||
|
"@repo/wikipedia-game-solver": "workspace:*",
|
||||||
|
"@vinejs/vine": "catalog:",
|
||||||
|
"luxon": "catalog:",
|
||||||
|
"mysql2": "catalog:",
|
||||||
|
"reflect-metadata": "catalog:",
|
||||||
|
"tsx": "catalog:",
|
||||||
|
"pino-pretty": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@adonisjs/assembler": "catalog:",
|
||||||
|
"@japa/api-client": "catalog:",
|
||||||
|
"@japa/assert": "catalog:",
|
||||||
|
"@japa/plugin-adonisjs": "catalog:",
|
||||||
|
"@japa/runner": "catalog:",
|
||||||
|
"@repo/config-typescript": "workspace:*",
|
||||||
|
"@repo/eslint-config": "workspace:*",
|
||||||
|
"@total-typescript/ts-reset": "catalog:",
|
||||||
|
"@types/luxon": "catalog:",
|
||||||
|
"@types/node": "catalog:",
|
||||||
|
"eslint": "catalog:",
|
||||||
|
"openapi-types": "catalog:",
|
||||||
|
"typescript": "catalog:"
|
||||||
|
}
|
||||||
|
}
|
56
apps/api/src/adonisrc.ts
Normal file
56
apps/api/src/adonisrc.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { defineConfig } from "@adonisjs/core/app"
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
commands: [
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/core/commands")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/lucid/commands")
|
||||||
|
},
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/core/providers/app_provider")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/core/providers/hash_provider")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: async () => {
|
||||||
|
return await import("@adonisjs/core/providers/repl_provider")
|
||||||
|
},
|
||||||
|
environment: ["repl", "test"],
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/core/providers/vinejs_provider")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/cors/cors_provider")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/lucid/database_provider")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/auth/auth_provider")
|
||||||
|
},
|
||||||
|
],
|
||||||
|
preloads: [
|
||||||
|
async () => {
|
||||||
|
return await import("#start/routes.js")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("#start/kernel.js")
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tests: {
|
||||||
|
suites: [
|
||||||
|
{
|
||||||
|
files: ["**/*.test.ts"],
|
||||||
|
name: "functional",
|
||||||
|
timeout: 30_000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
forceExit: false,
|
||||||
|
},
|
||||||
|
})
|
32
apps/api/src/app/exceptions/handler.ts
Normal file
32
apps/api/src/app/exceptions/handler.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import type { HttpContext } from "@adonisjs/core/http"
|
||||||
|
import { ExceptionHandler } from "@adonisjs/core/http"
|
||||||
|
import app from "@adonisjs/core/services/app"
|
||||||
|
|
||||||
|
export default class HttpExceptionHandler extends ExceptionHandler {
|
||||||
|
/**
|
||||||
|
* In debug mode, the exception handler will display verbose errors with pretty printed stack traces.
|
||||||
|
*/
|
||||||
|
protected override debug = !app.inProduction
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method is used for handling errors and returning response to the client.
|
||||||
|
*/
|
||||||
|
public override async handle(
|
||||||
|
error: unknown,
|
||||||
|
ctx: HttpContext,
|
||||||
|
): Promise<unknown> {
|
||||||
|
return await super.handle(error, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method is used to report error to the logging service or the third party error monitoring service.
|
||||||
|
*
|
||||||
|
* @note You should not attempt to send a response from this method.
|
||||||
|
*/
|
||||||
|
public override async report(
|
||||||
|
error: unknown,
|
||||||
|
ctx: HttpContext,
|
||||||
|
): Promise<void> {
|
||||||
|
return await super.report(error, ctx)
|
||||||
|
}
|
||||||
|
}
|
27
apps/api/src/app/middleware/auth_middleware.ts
Normal file
27
apps/api/src/app/middleware/auth_middleware.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { Authenticators } from "@adonisjs/auth/types"
|
||||||
|
import type { HttpContext } from "@adonisjs/core/http"
|
||||||
|
import type { NextFn } from "@adonisjs/core/types/http"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auth middleware is used authenticate HTTP requests and deny
|
||||||
|
* access to unauthenticated users.
|
||||||
|
*/
|
||||||
|
export default class AuthMiddleware {
|
||||||
|
/**
|
||||||
|
* The URL to redirect to, when authentication fails
|
||||||
|
*/
|
||||||
|
redirectTo = "/login"
|
||||||
|
|
||||||
|
public async handle(
|
||||||
|
ctx: HttpContext,
|
||||||
|
next: NextFn,
|
||||||
|
options: {
|
||||||
|
guards?: Array<keyof Authenticators>
|
||||||
|
} = {},
|
||||||
|
): Promise<void> {
|
||||||
|
await ctx.auth.authenticateUsing(options.guards, {
|
||||||
|
loginRoute: this.redirectTo,
|
||||||
|
})
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
18
apps/api/src/app/middleware/container_bindings_middleware.ts
Normal file
18
apps/api/src/app/middleware/container_bindings_middleware.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { HttpContext } from "@adonisjs/core/http"
|
||||||
|
import { Logger } from "@adonisjs/core/logger"
|
||||||
|
import type { NextFn } from "@adonisjs/core/types/http"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container bindings middleware binds classes to their request specific value using the container resolver.
|
||||||
|
*
|
||||||
|
* - We bind "HttpContext" class to the "ctx" object.
|
||||||
|
* - And bind "Logger" class to the "ctx.logger" object.
|
||||||
|
*/
|
||||||
|
export default class ContainerBindingsMiddleware {
|
||||||
|
public async handle(ctx: HttpContext, next: NextFn): Promise<void> {
|
||||||
|
ctx.containerResolver.bindValue(HttpContext, ctx)
|
||||||
|
ctx.containerResolver.bindValue(Logger, ctx.logger)
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import type { HttpContext } from "@adonisjs/core/http"
|
||||||
|
import type { NextFn } from "@adonisjs/core/types/http"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updating the "Accept" header to always accept "application/json" response from the server. This will force the internals of the framework like validator errors or auth errors to return a JSON response.
|
||||||
|
*/
|
||||||
|
export default class ForceJsonResponseMiddleware {
|
||||||
|
public async handle({ request }: HttpContext, next: NextFn): Promise<void> {
|
||||||
|
const headers = request.headers()
|
||||||
|
headers.accept = "application/json"
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
}
|
51
apps/api/src/app/models/user.ts
Normal file
51
apps/api/src/app/models/user.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { DbAccessTokensProvider } from "@adonisjs/auth/access_tokens"
|
||||||
|
import { withAuthFinder } from "@adonisjs/auth/mixins/lucid"
|
||||||
|
import { compose } from "@adonisjs/core/helpers"
|
||||||
|
import hash from "@adonisjs/core/services/hash"
|
||||||
|
import { BaseModel, column } from "@adonisjs/lucid/orm"
|
||||||
|
import { DateTime } from "luxon"
|
||||||
|
|
||||||
|
const AuthFinder = withAuthFinder(
|
||||||
|
() => {
|
||||||
|
return hash.use("scrypt")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
uids: ["email"],
|
||||||
|
passwordColumnName: "password",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
export default class User extends compose(BaseModel, AuthFinder) {
|
||||||
|
protected tableName = "users"
|
||||||
|
|
||||||
|
@column({ columnName: "id", isPrimary: true })
|
||||||
|
declare id: number
|
||||||
|
|
||||||
|
@column({
|
||||||
|
columnName: "full_name",
|
||||||
|
})
|
||||||
|
declare fullName: string | null
|
||||||
|
|
||||||
|
@column({
|
||||||
|
columnName: "email",
|
||||||
|
})
|
||||||
|
declare email: string
|
||||||
|
|
||||||
|
@column({ columnName: "password", serializeAs: null })
|
||||||
|
declare password: string
|
||||||
|
|
||||||
|
@column.dateTime({
|
||||||
|
columnName: "created_at",
|
||||||
|
autoCreate: true,
|
||||||
|
})
|
||||||
|
declare createdAt: DateTime
|
||||||
|
|
||||||
|
@column.dateTime({
|
||||||
|
columnName: "updated_at",
|
||||||
|
autoCreate: true,
|
||||||
|
autoUpdate: true,
|
||||||
|
})
|
||||||
|
declare updatedAt: DateTime | null
|
||||||
|
|
||||||
|
static accessTokens = DbAccessTokensProvider.forModel(User)
|
||||||
|
}
|
7
apps/api/src/app/routes/get.ts
Normal file
7
apps/api/src/app/routes/get.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import router from "@adonisjs/core/services/router"
|
||||||
|
|
||||||
|
router.get("/", async () => {
|
||||||
|
return {
|
||||||
|
hello: "world",
|
||||||
|
}
|
||||||
|
})
|
1
apps/api/src/app/routes/index.ts
Normal file
1
apps/api/src/app/routes/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import "./get.js"
|
16
apps/api/src/app/routes/tests/get.test.ts
Normal file
16
apps/api/src/app/routes/tests/get.test.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { test } from "@japa/runner"
|
||||||
|
|
||||||
|
test.group("GET /", () => {
|
||||||
|
test("should get hello world", async ({ client }) => {
|
||||||
|
// Arrange - Given
|
||||||
|
|
||||||
|
// Act - When
|
||||||
|
const response = await client.get("/")
|
||||||
|
|
||||||
|
// Assert - Then
|
||||||
|
response.assertStatus(200)
|
||||||
|
response.assertBody({
|
||||||
|
hello: "world",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
48
apps/api/src/bin/console.ts
Executable file
48
apps/api/src/bin/console.ts
Executable file
@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env -S node --import=tsx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ace entry point
|
||||||
|
*
|
||||||
|
* Entrypoint for booting the AdonisJS command-line framework and executing commands.
|
||||||
|
* Commands do not boot the application, unless the currently running command has `options.startApp` flag set to true.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Ignitor, prettyPrintError } from "@adonisjs/core"
|
||||||
|
import "reflect-metadata"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the application root. AdonisJS need it to resolve paths to file and directories for scaffolding commands
|
||||||
|
*/
|
||||||
|
const APP_ROOT = new URL("../", import.meta.url)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importer is used to import files in context of the application.
|
||||||
|
*/
|
||||||
|
const IMPORTER = async (filePath: string): Promise<unknown> => {
|
||||||
|
if (filePath.startsWith("./") || filePath.startsWith("../")) {
|
||||||
|
return await import(new URL(filePath, APP_ROOT).href)
|
||||||
|
}
|
||||||
|
return await import(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignitor = new Ignitor(APP_ROOT, { importer: IMPORTER })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ignitor
|
||||||
|
.tap((app) => {
|
||||||
|
app.booting(async () => {
|
||||||
|
await import("#start/env.js")
|
||||||
|
})
|
||||||
|
app.listen("SIGTERM", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
app.listenIf(app.managedByPm2, "SIGINT", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.ace()
|
||||||
|
.handle(process.argv.splice(2))
|
||||||
|
} catch (error) {
|
||||||
|
process.exitCode = 1
|
||||||
|
await prettyPrintError(error)
|
||||||
|
}
|
47
apps/api/src/bin/server.ts
Executable file
47
apps/api/src/bin/server.ts
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env -S node --import=tsx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP server entrypoint
|
||||||
|
*
|
||||||
|
* Entrypoint for starting the AdonisJS HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Ignitor, prettyPrintError } from "@adonisjs/core"
|
||||||
|
import "reflect-metadata"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the application root. AdonisJS need it to resolve paths to file and directories for scaffolding commands.
|
||||||
|
*/
|
||||||
|
const APP_ROOT = new URL("../", import.meta.url)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importer is used to import files in context of the application.
|
||||||
|
*/
|
||||||
|
const IMPORTER = async (filePath: string): Promise<unknown> => {
|
||||||
|
if (filePath.startsWith("./") || filePath.startsWith("../")) {
|
||||||
|
return await import(new URL(filePath, APP_ROOT).href)
|
||||||
|
}
|
||||||
|
return await import(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignitor = new Ignitor(APP_ROOT, { importer: IMPORTER })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ignitor
|
||||||
|
.tap((app) => {
|
||||||
|
app.booting(async () => {
|
||||||
|
await import("#start/env.js")
|
||||||
|
})
|
||||||
|
app.listen("SIGTERM", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
app.listenIf(app.managedByPm2, "SIGINT", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.httpServer()
|
||||||
|
.start()
|
||||||
|
} catch (error) {
|
||||||
|
process.exitCode = 1
|
||||||
|
await prettyPrintError(error)
|
||||||
|
}
|
69
apps/api/src/bin/test.ts
Executable file
69
apps/api/src/bin/test.ts
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env -S node --import=tsx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test runner entrypoint
|
||||||
|
*
|
||||||
|
* Entrypoint for running tests using Japa.
|
||||||
|
*/
|
||||||
|
|
||||||
|
process.env["NODE_ENV"] = "test"
|
||||||
|
|
||||||
|
import { Ignitor, prettyPrintError } from "@adonisjs/core"
|
||||||
|
import { configure, processCLIArgs, run } from "@japa/runner"
|
||||||
|
import "reflect-metadata"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* URL to the application root. AdonisJS need it to resolve paths to file and directories for scaffolding commands
|
||||||
|
*/
|
||||||
|
const APP_ROOT = new URL("../", import.meta.url)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The importer is used to import files in context of the application.
|
||||||
|
*/
|
||||||
|
const IMPORTER = async (filePath: string): Promise<unknown> => {
|
||||||
|
if (filePath.startsWith("./") || filePath.startsWith("../")) {
|
||||||
|
return await import(new URL(filePath, APP_ROOT).href)
|
||||||
|
}
|
||||||
|
return await import(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ignitor = new Ignitor(APP_ROOT, { importer: IMPORTER })
|
||||||
|
|
||||||
|
try {
|
||||||
|
await ignitor
|
||||||
|
.tap((app) => {
|
||||||
|
app.booting(async () => {
|
||||||
|
await import("#start/env.js")
|
||||||
|
})
|
||||||
|
app.listen("SIGTERM", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
app.listenIf(app.managedByPm2, "SIGINT", async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.testRunner()
|
||||||
|
.configure(async (app) => {
|
||||||
|
const { runnerHooks, ...config } = await import("#tests/bootstrap.js")
|
||||||
|
|
||||||
|
processCLIArgs(process.argv.splice(2))
|
||||||
|
configure({
|
||||||
|
...app.rcFile.tests,
|
||||||
|
...config,
|
||||||
|
...{
|
||||||
|
setup: runnerHooks.setup,
|
||||||
|
teardown: runnerHooks.teardown.concat([
|
||||||
|
async () => {
|
||||||
|
return await app.terminate()
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.run(async () => {
|
||||||
|
return await run()
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
process.exitCode = 1
|
||||||
|
await prettyPrintError(error)
|
||||||
|
}
|
37
apps/api/src/config/app.ts
Normal file
37
apps/api/src/config/app.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import env from "#start/env.js"
|
||||||
|
import { Secret } from "@adonisjs/core/helpers"
|
||||||
|
import { defineConfig } from "@adonisjs/core/http"
|
||||||
|
import app from "@adonisjs/core/services/app"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The app key is used for encrypting cookies, generating signed URLs, and by the "encryption" module.
|
||||||
|
*
|
||||||
|
* The encryption module will fail to decrypt data if the key is lost or changed.
|
||||||
|
* Therefore it is recommended to keep the app key secure.
|
||||||
|
*/
|
||||||
|
export const appKey = new Secret(env.get("APP_KEY"))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration settings used by the HTTP server
|
||||||
|
*/
|
||||||
|
export const http = defineConfig({
|
||||||
|
generateRequestId: true,
|
||||||
|
allowMethodSpoofing: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enabling async local storage will let you access HTTP context from anywhere inside your application.
|
||||||
|
*/
|
||||||
|
useAsyncLocalStorage: false,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manage cookies configuration. The settings for the session id cookie are defined inside the "config/session.ts" file.
|
||||||
|
*/
|
||||||
|
cookie: {
|
||||||
|
domain: "",
|
||||||
|
path: "/",
|
||||||
|
maxAge: "2h",
|
||||||
|
httpOnly: true,
|
||||||
|
secure: app.inProduction,
|
||||||
|
sameSite: "lax",
|
||||||
|
},
|
||||||
|
})
|
29
apps/api/src/config/auth.ts
Normal file
29
apps/api/src/config/auth.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { defineConfig } from "@adonisjs/auth"
|
||||||
|
import { tokensGuard, tokensUserProvider } from "@adonisjs/auth/access_tokens"
|
||||||
|
import type { Authenticators, InferAuthEvents } from "@adonisjs/auth/types"
|
||||||
|
|
||||||
|
const authConfig = defineConfig({
|
||||||
|
default: "api",
|
||||||
|
guards: {
|
||||||
|
api: tokensGuard({
|
||||||
|
provider: tokensUserProvider({
|
||||||
|
tokens: "accessTokens",
|
||||||
|
model: async () => {
|
||||||
|
return await import("#app/models/user.js")
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default authConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inferring types from the configured auth guards.
|
||||||
|
*/
|
||||||
|
declare module "@adonisjs/auth/types" {
|
||||||
|
interface Authenticators extends InferAuthenticators<typeof authConfig> {}
|
||||||
|
}
|
||||||
|
declare module "@adonisjs/core/types" {
|
||||||
|
interface EventsList extends InferAuthEvents<Authenticators> {}
|
||||||
|
}
|
50
apps/api/src/config/bodyparser.ts
Normal file
50
apps/api/src/config/bodyparser.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { defineConfig } from "@adonisjs/core/bodyparser"
|
||||||
|
|
||||||
|
const bodyParserConfig = defineConfig({
|
||||||
|
/**
|
||||||
|
* The bodyparser middleware will parse the request body for the following HTTP methods.
|
||||||
|
*/
|
||||||
|
allowedMethods: ["POST", "PUT", "PATCH", "DELETE"],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config for the "application/x-www-form-urlencoded" content-type parser.
|
||||||
|
*/
|
||||||
|
form: {
|
||||||
|
convertEmptyStringsToNull: true,
|
||||||
|
types: ["application/x-www-form-urlencoded"],
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config for the JSON parser
|
||||||
|
*/
|
||||||
|
json: {
|
||||||
|
convertEmptyStringsToNull: true,
|
||||||
|
types: [
|
||||||
|
"application/json",
|
||||||
|
"application/json-patch+json",
|
||||||
|
"application/vnd.api+json",
|
||||||
|
"application/csp-report",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Config for the "multipart/form-data" content-type parser.
|
||||||
|
* File uploads are handled by the multipart parser.
|
||||||
|
*/
|
||||||
|
multipart: {
|
||||||
|
/**
|
||||||
|
* Enabling auto process allows bodyparser middleware to move all uploaded files inside the tmp folder of your operating system.
|
||||||
|
*/
|
||||||
|
autoProcess: true,
|
||||||
|
convertEmptyStringsToNull: true,
|
||||||
|
processManually: [],
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum limit of data to parse including all files and fields.
|
||||||
|
*/
|
||||||
|
limit: "20mb",
|
||||||
|
types: ["multipart/form-data"],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default bodyParserConfig
|
18
apps/api/src/config/cors.ts
Normal file
18
apps/api/src/config/cors.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { defineConfig } from "@adonisjs/cors"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options to tweak the CORS policy. The following options are documented on the official documentation website.
|
||||||
|
*
|
||||||
|
* https://docs.adonisjs.com/guides/security/cors
|
||||||
|
*/
|
||||||
|
const corsConfig = defineConfig({
|
||||||
|
enabled: true,
|
||||||
|
origin: true,
|
||||||
|
methods: ["GET", "HEAD", "POST", "PUT", "DELETE"],
|
||||||
|
headers: true,
|
||||||
|
exposeHeaders: [],
|
||||||
|
credentials: true,
|
||||||
|
maxAge: 90,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default corsConfig
|
24
apps/api/src/config/database.ts
Normal file
24
apps/api/src/config/database.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import env from "#start/env.js"
|
||||||
|
import { defineConfig } from "@adonisjs/lucid"
|
||||||
|
|
||||||
|
const databaseConfig = defineConfig({
|
||||||
|
connection: "mysql",
|
||||||
|
connections: {
|
||||||
|
mysql: {
|
||||||
|
client: "mysql2",
|
||||||
|
connection: {
|
||||||
|
host: env.get("DATABASE_HOST"),
|
||||||
|
port: env.get("DATABASE_PORT"),
|
||||||
|
user: env.get("DATABASE_USER"),
|
||||||
|
password: env.get("DATABASE_PASSWORD"),
|
||||||
|
database: env.get("DATABASE_NAME"),
|
||||||
|
},
|
||||||
|
migrations: {
|
||||||
|
naturalSort: true,
|
||||||
|
paths: ["database/migrations"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default databaseConfig
|
24
apps/api/src/config/hash.ts
Normal file
24
apps/api/src/config/hash.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { defineConfig, drivers } from '@adonisjs/core/hash'
|
||||||
|
|
||||||
|
const hashConfig = defineConfig({
|
||||||
|
default: 'scrypt',
|
||||||
|
|
||||||
|
list: {
|
||||||
|
scrypt: drivers.scrypt({
|
||||||
|
cost: 16384,
|
||||||
|
blockSize: 8,
|
||||||
|
parallelization: 1,
|
||||||
|
maxMemory: 33554432,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default hashConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inferring types for the list of hashers you have configured
|
||||||
|
* in your application.
|
||||||
|
*/
|
||||||
|
declare module '@adonisjs/core/types' {
|
||||||
|
export interface HashersList extends InferHashers<typeof hashConfig> {}
|
||||||
|
}
|
34
apps/api/src/config/logger.ts
Normal file
34
apps/api/src/config/logger.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import env from "#start/env.js"
|
||||||
|
import { defineConfig, targets } from "@adonisjs/core/logger"
|
||||||
|
import app from "@adonisjs/core/services/app"
|
||||||
|
|
||||||
|
const loggerConfig = defineConfig({
|
||||||
|
default: "app",
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The loggers object can be used to define multiple loggers.
|
||||||
|
* By default, we configure only one logger (named "app").
|
||||||
|
*/
|
||||||
|
loggers: {
|
||||||
|
app: {
|
||||||
|
enabled: true,
|
||||||
|
name: env.get("APP_NAME"),
|
||||||
|
level: env.get("LOG_LEVEL"),
|
||||||
|
transport: {
|
||||||
|
targets: targets()
|
||||||
|
.pushIf(!app.inProduction, targets.pretty())
|
||||||
|
.pushIf(app.inProduction, targets.file({ destination: 1 }))
|
||||||
|
.toArray(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default loggerConfig
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inferring types for the list of loggers you have configured in your application.
|
||||||
|
*/
|
||||||
|
declare module "@adonisjs/core/types" {
|
||||||
|
export interface LoggersList extends InferLoggers<typeof loggerConfig> {}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { BaseSchema } from "@adonisjs/lucid/schema"
|
||||||
|
|
||||||
|
export default class CreateUsersTable extends BaseSchema {
|
||||||
|
protected tableName = "users"
|
||||||
|
|
||||||
|
public override async up(): Promise<void> {
|
||||||
|
await this.schema.createTable(this.tableName, (table) => {
|
||||||
|
table.increments("id").notNullable()
|
||||||
|
table.string("full_name").nullable()
|
||||||
|
table.string("email", 254).notNullable().unique()
|
||||||
|
table.string("password").notNullable()
|
||||||
|
|
||||||
|
table.timestamp("created_at").notNullable()
|
||||||
|
table.timestamp("updated_at").nullable()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async down(): Promise<void> {
|
||||||
|
await this.schema.dropTable(this.tableName)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
import { BaseSchema } from "@adonisjs/lucid/schema"
|
||||||
|
|
||||||
|
export default class CreateAccessTokensTable extends BaseSchema {
|
||||||
|
protected tableName = "auth_access_tokens"
|
||||||
|
|
||||||
|
public override async up(): Promise<void> {
|
||||||
|
await this.schema.createTable(this.tableName, (table) => {
|
||||||
|
table.increments("id")
|
||||||
|
table
|
||||||
|
.integer("tokenable_id")
|
||||||
|
.notNullable()
|
||||||
|
.unsigned()
|
||||||
|
.references("id")
|
||||||
|
.inTable("users")
|
||||||
|
.onDelete("CASCADE")
|
||||||
|
|
||||||
|
table.string("type").notNullable()
|
||||||
|
table.string("name").nullable()
|
||||||
|
table.string("hash").notNullable()
|
||||||
|
table.text("abilities").notNullable()
|
||||||
|
table.timestamp("created_at")
|
||||||
|
table.timestamp("updated_at")
|
||||||
|
table.timestamp("last_used_at").nullable()
|
||||||
|
table.timestamp("expires_at").nullable()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async down(): Promise<void> {
|
||||||
|
await this.schema.dropTable(this.tableName)
|
||||||
|
}
|
||||||
|
}
|
29
apps/api/src/start/env.ts
Normal file
29
apps/api/src/start/env.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* Environment variables service
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Env } from "@adonisjs/core/env"
|
||||||
|
|
||||||
|
export default await Env.create(new URL("../..", import.meta.url), {
|
||||||
|
NODE_ENV: Env.schema.enum(["development", "production", "test"] as const),
|
||||||
|
PORT: Env.schema.number(),
|
||||||
|
APP_KEY: Env.schema.string(),
|
||||||
|
HOST: Env.schema.string({ format: "host" }),
|
||||||
|
LOG_LEVEL: Env.schema.enum([
|
||||||
|
"fatal",
|
||||||
|
"error",
|
||||||
|
"warn",
|
||||||
|
"info",
|
||||||
|
"debug",
|
||||||
|
"trace",
|
||||||
|
] as const),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variables for configuring database connection
|
||||||
|
*/
|
||||||
|
DATABASE_HOST: Env.schema.string({ format: "host" }),
|
||||||
|
DATABASE_PORT: Env.schema.number(),
|
||||||
|
DATABASE_USER: Env.schema.string(),
|
||||||
|
DATABASE_PASSWORD: Env.schema.string(),
|
||||||
|
DATABASE_NAME: Env.schema.string(),
|
||||||
|
})
|
54
apps/api/src/start/kernel.ts
Normal file
54
apps/api/src/start/kernel.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* HTTP kernel file
|
||||||
|
*
|
||||||
|
* The HTTP kernel file is used to register the middleware with the server or the router.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import router from "@adonisjs/core/services/router"
|
||||||
|
import server from "@adonisjs/core/services/server"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error handler is used to convert an exception
|
||||||
|
* to a HTTP response.
|
||||||
|
*/
|
||||||
|
server.errorHandler(async () => {
|
||||||
|
return await import("#app/exceptions/handler.js")
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server middleware stack runs middleware on all the HTTP
|
||||||
|
* requests, even if there is no route registered for
|
||||||
|
* the request URL.
|
||||||
|
*/
|
||||||
|
server.use([
|
||||||
|
async () => {
|
||||||
|
return await import("#app/middleware/container_bindings_middleware.js")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("#app/middleware/force_json_response_middleware.js")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/cors/cors_middleware")
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The router middleware stack runs middleware on all the HTTP requests with a registered route.
|
||||||
|
*/
|
||||||
|
router.use([
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/core/bodyparser_middleware")
|
||||||
|
},
|
||||||
|
async () => {
|
||||||
|
return await import("@adonisjs/auth/initialize_auth_middleware")
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Named middleware collection must be explicitly assigned to the routes or the routes group.
|
||||||
|
*/
|
||||||
|
export const middleware = router.named({
|
||||||
|
auth: async () => {
|
||||||
|
return await import("#app/middleware/auth_middleware.js")
|
||||||
|
},
|
||||||
|
})
|
7
apps/api/src/start/routes.ts
Normal file
7
apps/api/src/start/routes.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Routes file
|
||||||
|
*
|
||||||
|
* The routes file is used for defining the HTTP routes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import "#app/routes/index.js"
|
41
apps/api/src/tests/bootstrap.ts
Normal file
41
apps/api/src/tests/bootstrap.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import app from "@adonisjs/core/services/app"
|
||||||
|
import testUtils from "@adonisjs/core/services/test_utils"
|
||||||
|
import { apiClient } from "@japa/api-client"
|
||||||
|
import { assert } from "@japa/assert"
|
||||||
|
import { pluginAdonisJS } from "@japa/plugin-adonisjs"
|
||||||
|
import type { Config } from "@japa/runner/types"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is imported by the "bin/test.ts" entrypoint file
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Japa plugins in the plugins array.
|
||||||
|
* Learn more - https://japa.dev/docs/runner-config#plugins-optional
|
||||||
|
*/
|
||||||
|
export const plugins: Config["plugins"] = [
|
||||||
|
assert(),
|
||||||
|
apiClient(),
|
||||||
|
pluginAdonisJS(app),
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure lifecycle function to run before and after all the tests.
|
||||||
|
*
|
||||||
|
* The setup functions are executed before all the tests.
|
||||||
|
* The teardown functions are executer after all the tests.
|
||||||
|
*/
|
||||||
|
export const runnerHooks: Required<Pick<Config, "setup" | "teardown">> = {
|
||||||
|
setup: [],
|
||||||
|
teardown: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure suites by tapping into the test suite instance.
|
||||||
|
* Learn more - https://japa.dev/docs/test-suites#lifecycle-hooks
|
||||||
|
*/
|
||||||
|
export const configureSuite: Config["configureSuite"] = (suite) => {
|
||||||
|
return suite.setup(async () => {
|
||||||
|
return await testUtils.httpServer().start()
|
||||||
|
})
|
||||||
|
}
|
15
apps/api/tsconfig.json
Normal file
15
apps/api/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"extends": "@repo/config-typescript/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"types": ["@total-typescript/ts-reset", "@types/node"],
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
|
||||||
|
"noEmit": true
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,6 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --import=tsx ./src/index.ts",
|
"start": "node --import=tsx ./src/index.ts",
|
||||||
"dev-test": "node --import=tsx --watch --watch-preserve-output ./src/index.ts",
|
|
||||||
"lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives",
|
"lint:eslint": "eslint src --max-warnings 0 --report-unused-disable-directives",
|
||||||
"lint:typescript": "tsc --noEmit"
|
"lint:typescript": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
|
@ -2,7 +2,7 @@ import sharedConfig from "@repo/config-tailwind"
|
|||||||
|
|
||||||
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
|
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
|
||||||
const config = {
|
const config = {
|
||||||
content: [".storybook/preview.tsx", "../../packages/**/*.tsx"],
|
content: [".storybook/preview.tsx", "../../packages/*/src/**/*.tsx"],
|
||||||
presets: [sharedConfig],
|
presets: [sharedConfig],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ RUN corepack enable
|
|||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
FROM node-pnpm AS builder
|
FROM node-pnpm AS builder
|
||||||
RUN pnpm install --global turbo@2.0.11
|
RUN pnpm install --global turbo@2.0.12
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
RUN turbo prune @repo/website --docker
|
RUN turbo prune @repo/website --docker
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import sharedConfig from "@repo/config-tailwind"
|
|||||||
|
|
||||||
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
|
/** @type {Pick<import('tailwindcss').Config, "presets" | "content">} */
|
||||||
const config = {
|
const config = {
|
||||||
content: ["./**/*.tsx", "../../packages/**/*.tsx"],
|
content: ["./app/**/*.tsx", "../../packages/*/src/**/*.tsx"],
|
||||||
presets: [sharedConfig],
|
presets: [sharedConfig],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
41
compose.dev.yaml
Normal file
41
compose.dev.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
services:
|
||||||
|
wikipedia-solver-dev-database:
|
||||||
|
container_name: "wikipedia-solver-dev-database"
|
||||||
|
image: "mariadb:10.6.17"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
env_file: ".env"
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: ${DATABASE_USER}
|
||||||
|
MARIADB_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_DATABASE: ${DATABASE_NAME}
|
||||||
|
command:
|
||||||
|
--innodb_buffer_pool_size=4G
|
||||||
|
--key-buffer-size=4G
|
||||||
|
--innodb_log_buffer_size=256M
|
||||||
|
--innodb_log_file_size=1G
|
||||||
|
--innodb_write_io_threads=16
|
||||||
|
--innodb_flush_log_at_trx_commit=0
|
||||||
|
--max_allowed_packet=1G
|
||||||
|
ports:
|
||||||
|
- "${DATABASE_PORT-3306}:${DATABASE_PORT-3306}"
|
||||||
|
volumes:
|
||||||
|
- "wikipedia-solver-dev-mariadb-data:/var/lib/mysql"
|
||||||
|
# - "./sql:/docker-entrypoint-initdb.d/"
|
||||||
|
|
||||||
|
wikipedia-solver-dev-adminer:
|
||||||
|
container_name: "wikipedia-solver-dev-adminer"
|
||||||
|
image: "adminer:4.8.1"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
env_file: ".env"
|
||||||
|
environment:
|
||||||
|
ADMINER_DEFAULT_SERVER: "wikipedia-solver-dev-database"
|
||||||
|
volumes:
|
||||||
|
- "./adminer/default-orange.css:/var/www/html/adminer.css"
|
||||||
|
- "./adminer/logo.png:/var/www/html/logo.png"
|
||||||
|
- "./adminer/fonts/:/var/www/html/fonts"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
wikipedia-solver-dev-mariadb-data:
|
46
compose.yaml
46
compose.yaml
@ -1,7 +1,7 @@
|
|||||||
services:
|
services:
|
||||||
wikipedia-game-solver:
|
wikipedia-game-solver-website:
|
||||||
container_name: "wikipedia-game-solver"
|
container_name: "wikipedia-game-solver-website"
|
||||||
image: "wikipedia-game-solver"
|
image: "wikipedia-game-solver-website"
|
||||||
restart: "unless-stopped"
|
restart: "unless-stopped"
|
||||||
build:
|
build:
|
||||||
context: "./"
|
context: "./"
|
||||||
@ -11,3 +11,43 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
PORT: ${WEBSITE_PORT-5000}
|
PORT: ${WEBSITE_PORT-5000}
|
||||||
env_file: "./apps/website/.env"
|
env_file: "./apps/website/.env"
|
||||||
|
|
||||||
|
wikipedia-game-solver-api:
|
||||||
|
container_name: "wikipedia-game-solver-api"
|
||||||
|
image: "wikipedia-game-solver-api"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
build:
|
||||||
|
context: "./"
|
||||||
|
dockerfile: "./apps/api/Dockerfile"
|
||||||
|
ports:
|
||||||
|
- "${API_PORT-5000}:${API_PORT-5000}"
|
||||||
|
environment:
|
||||||
|
PORT: ${API_PORT-5000}
|
||||||
|
env_file: "./apps/api/.env"
|
||||||
|
|
||||||
|
wikipedia-solver-database:
|
||||||
|
container_name: "wikipedia-solver-database"
|
||||||
|
image: "mariadb:10.6.17"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
env_file: ".env"
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: ${DATABASE_USER}
|
||||||
|
MARIADB_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_DATABASE: ${DATABASE_NAME}
|
||||||
|
command:
|
||||||
|
--innodb_buffer_pool_size=4G
|
||||||
|
--key-buffer-size=4G
|
||||||
|
--innodb_log_buffer_size=256M
|
||||||
|
--innodb_log_file_size=1G
|
||||||
|
--innodb_write_io_threads=16
|
||||||
|
--innodb_flush_log_at_trx_commit=0
|
||||||
|
--max_allowed_packet=1G
|
||||||
|
ports:
|
||||||
|
- "${DATABASE_PORT-3306}:${DATABASE_PORT-3306}"
|
||||||
|
volumes:
|
||||||
|
- "wikipedia-solver-mariadb-data:/var/lib/mysql"
|
||||||
|
# - "./sql:/docker-entrypoint-initdb.d/"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
wikipedia-solver-mariadb-data:
|
||||||
|
3
data/.env.example
Normal file
3
data/.env.example
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
DATABASE_USER=wikipedia_user
|
||||||
|
DATABASE_PASSWORD=password
|
||||||
|
DATABASE_NAME=wikipedia
|
4
data/.eslintrc.json
Normal file
4
data/.eslintrc.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"root": true,
|
||||||
|
"extends": ["@repo/eslint-config"]
|
||||||
|
}
|
155
data/README.md
155
data/README.md
@ -1,17 +1,158 @@
|
|||||||
# Wikipedia data
|
# Wikipedia data
|
||||||
|
|
||||||
Database layout: <https://www.mediawiki.org/wiki/Manual:Database_layout>
|
```sh
|
||||||
|
./download-wikipedia-dump.sh
|
||||||
|
node --max-old-space-size=8096 database-wikipedia.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Utils
|
||||||
|
|
||||||
|
Show the first 10 line of sql file: `head -n 10 ./dump/page.sql`
|
||||||
|
|
||||||
|
Show the first 10 characters of sql file: `head -c 10 ./dump/page.sql`
|
||||||
|
|
||||||
|
To inspect volume size used by database: `docker system df -v | grep 'wikipedia-solver-mariadb-data'`
|
||||||
|
|
||||||
|
To enter in the database container: `docker exec -it wikipedia-solver-database sh`
|
||||||
|
|
||||||
|
Then: `mariadb --password="${DATABASE_PASSWORD}" --user="${DATABASE_USER}"`
|
||||||
|
|
||||||
|
And `use wikipedia;`, for example: `SELECT * FROM pages LIMIT 10;` or to execute a SQL script: `source /docker-entrypoint-initdb.d/3-internal-links-inserts.sql;`.
|
||||||
|
|
||||||
|
## Remove a volume
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# List all volumes
|
||||||
|
docker volume ls
|
||||||
|
|
||||||
|
# Remove a volume
|
||||||
|
docker volume rm data_wikipedia-solver-mariadb-data
|
||||||
|
|
||||||
|
# Or by using docker compose down
|
||||||
|
docker-compose down --volumes
|
||||||
|
```
|
||||||
|
|
||||||
|
## MySQL Related
|
||||||
|
|
||||||
<https://stackoverflow.com/questions/43954631/issues-with-wikipedia-dump-table-pagelinks>
|
<https://stackoverflow.com/questions/43954631/issues-with-wikipedia-dump-table-pagelinks>
|
||||||
|
|
||||||
<https://stackoverflow.com/questions/40384864/importing-wikipedia-dump-to-mysql>
|
MySQL any way to import a huge (32 GB) sql dump faster?: <https://stackoverflow.com/questions/40384864/importing-wikipedia-dump-to-mysql>
|
||||||
|
|
||||||
|
Import data.sql MySQL Docker Container: <https://stackoverflow.com/questions/43880026/import-data-sql-mysql-docker-container>
|
||||||
|
|
||||||
|
<https://dba.stackexchange.com/questions/83125/mysql-any-way-to-import-a-huge-32-gb-sql-dump-faster>
|
||||||
|
|
||||||
## Dumps Links
|
## Dumps Links
|
||||||
|
|
||||||
|
- Database layout: <https://www.mediawiki.org/wiki/Manual:Database_layout>
|
||||||
|
- <https://en.wikipedia.org/wiki/Wikipedia:Database_download>
|
||||||
- <https://dumps.wikimedia.org/enwiki/>
|
- <https://dumps.wikimedia.org/enwiki/>
|
||||||
|
- Run SQL queries against Wikipedia: <https://quarry.wmcloud.org/>
|
||||||
|
|
||||||
- <https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-pagelinks.sql.gz>
|
```sql
|
||||||
- <https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-page.sql.gz>
|
-- Get the sanitized title of a page linked in the page with title 'Node.js'
|
||||||
- <https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-all-titles-in-ns0.gz>
|
SELECT lt.lt_title FROM linktarget lt WHERE lt.lt_id = (
|
||||||
- <https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-iwlinks.sql.gz>
|
SELECT pl.pl_target_id FROM pagelinks pl WHERE pl.pl_from = (
|
||||||
- <https://dumps.wikimedia.org/enwiki/latest/enwiki-latest-all-titles.gz>
|
SELECT p.page_id FROM page p WHERE p.page_title = 'Node.js' AND p.page_namespace = 0
|
||||||
|
) LIMIT 1
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## `page.sql.gz` - MySQL full version up until inserts
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- MySQL dump 10.19 Distrib 10.3.38-MariaDB, for debian-linux-gnu (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: db1206 Database: enwiki
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 10.4.26-MariaDB-log
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `page`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `page`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `page` (
|
||||||
|
`page_id` int(8) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`page_namespace` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`page_title` varbinary(255) NOT NULL DEFAULT '',
|
||||||
|
`page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`page_is_new` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`page_random` double unsigned NOT NULL DEFAULT 0,
|
||||||
|
`page_touched` binary(14) NOT NULL,
|
||||||
|
`page_links_updated` varbinary(14) DEFAULT NULL,
|
||||||
|
`page_latest` int(8) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`page_len` int(8) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`page_content_model` varbinary(32) DEFAULT NULL,
|
||||||
|
`page_lang` varbinary(35) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`page_id`),
|
||||||
|
UNIQUE KEY `page_name_title` (`page_namespace`,`page_title`),
|
||||||
|
KEY `page_random` (`page_random`),
|
||||||
|
KEY `page_len` (`page_len`),
|
||||||
|
KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=76684425 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `page`
|
||||||
|
--
|
||||||
|
```
|
||||||
|
|
||||||
|
## `pagelinks.sql.gz` - MySQL full version up until inserts
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- MySQL dump 10.19 Distrib 10.3.38-MariaDB, for debian-linux-gnu (x86_64)
|
||||||
|
--
|
||||||
|
-- Host: db1206 Database: enwiki
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 10.4.26-MariaDB-log
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!40101 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `pagelinks`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `pagelinks`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `pagelinks` (
|
||||||
|
`pl_from` int(8) unsigned NOT NULL DEFAULT 0,
|
||||||
|
`pl_namespace` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`pl_title` varbinary(255) NOT NULL DEFAULT '',
|
||||||
|
`pl_from_namespace` int(11) NOT NULL DEFAULT 0,
|
||||||
|
`pl_target_id` bigint(20) unsigned DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`pl_from`,`pl_namespace`,`pl_title`),
|
||||||
|
KEY `pl_namespace` (`pl_namespace`,`pl_title`,`pl_from`),
|
||||||
|
KEY `pl_backlinks_namespace` (`pl_from_namespace`,`pl_namespace`,`pl_title`,`pl_from`),
|
||||||
|
KEY `pl_target_id` (`pl_target_id`,`pl_from`),
|
||||||
|
KEY `pl_backlinks_namespace_target_id` (`pl_from_namespace`,`pl_target_id`,`pl_from`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `pagelinks`
|
||||||
|
--
|
||||||
|
```
|
||||||
|
1376
data/adminer/default-orange.css
Normal file
1376
data/adminer/default-orange.css
Normal file
File diff suppressed because it is too large
Load Diff
BIN
data/adminer/fonts/entypo.eot
Normal file
BIN
data/adminer/fonts/entypo.eot
Normal file
Binary file not shown.
264
data/adminer/fonts/entypo.svg
Normal file
264
data/adminer/fonts/entypo.svg
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata></metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="entyporegular" horiz-adv-x="1228" >
|
||||||
|
<font-face units-per-em="2048" ascent="1536" descent="-512" />
|
||||||
|
<missing-glyph horiz-adv-x="500" />
|
||||||
|
<glyph unicode="
" horiz-adv-x="681" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="513" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1026" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="513" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="1026" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="342" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="256" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="171" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="171" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="128" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="205" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="57" />
|
||||||
|
<glyph unicode="‖" horiz-adv-x="747" d="M553 870q92 0 92 -65v-584q0 -68 -92 -67.5t-92 67.5v584q0 65 92 65zM194.5 870q92.5 0 92.5 -65v-584q0 -68 -92.5 -67.5t-92.5 67.5v584q0 65 92.5 65z" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="205" />
|
||||||
|
<glyph unicode=" " horiz-adv-x="256" />
|
||||||
|
<glyph unicode="ℹ" horiz-adv-x="675" d="M463 1024q49 0 75.5 -27.5t26.5 -70.5q0 -51 -40 -90t-97 -39q-49 0 -75.5 26.5t-24.5 73.5q0 47 36 87t99 40zM252 0q-102 0 -55 182l61 260q14 57 0 58q-12 0 -55 -18.5t-74 -39.5l-27 45q92 80 193.5 129.5t155.5 49.5q80 0 36 -166l-71 -273q-16 -66 6 -65 q45 0 121 61l30 -41q-86 -88 -179 -135t-142 -47z" />
|
||||||
|
<glyph unicode="←" horiz-adv-x="1208" d="M348 256l-246 256l246 256v-164h758v-182h-758v-166z" />
|
||||||
|
<glyph unicode="↑" horiz-adv-x="716" d="M614 770h-165v-760h-181v760h-166l256 244z" />
|
||||||
|
<glyph unicode="→" horiz-adv-x="1208" d="M862 256v166h-760v182h760v164l244 -256z" />
|
||||||
|
<glyph unicode="↓" horiz-adv-x="716" d="M614 256l-256 -246l-256 246h166v758h181v-758h165z" />
|
||||||
|
<glyph unicode="↰" horiz-adv-x="1075" d="M307 512v-92l-205 164l205 174v-103h563q41 0 72 -29.5t31 -72.5v-287h-144v246h-522z" />
|
||||||
|
<glyph unicode="↳" horiz-adv-x="966" d="M205 358q-43 0 -73 31t-30 72v358h144v-317h372v153l246 -225l-246 -225v153h-413z" />
|
||||||
|
<glyph unicode="⇆" d="M819 760v-144h-512v-92l-205 164l205 174v-102h512zM1126 330l-204 -164v92h-512v143h512v103z" />
|
||||||
|
<glyph unicode="∞" d="M918 737q86 0 147 -54t61 -171q0 -115 -61 -170t-147 -55q-80 0 -161 41t-143 108q-59 -68 -140 -108.5t-161 -40.5q-88 0 -149.5 55t-61.5 170q0 117 61.5 171t149.5 54q80 0 161 -40t140 -107q59 68 141.5 107.5t162.5 39.5zM313 377q61 0 130 38t116 97 q-47 59 -114.5 97t-131.5 38q-117 0 -117 -135t117 -135zM918 377q117 0 116.5 135t-116.5 135q-63 0 -132 -38t-114 -97q45 -59 114 -97t132 -38z" />
|
||||||
|
<glyph unicode="⊕" horiz-adv-x="1064" d="M532.5 942q178.5 0 304.5 -126t126 -304t-126 -304t-304.5 -126t-304.5 126t-126 304t126 304t304.5 126zM586 461h205v104h-205v207h-105v-207h-207v-104h207v-207h105v207z" />
|
||||||
|
<glyph unicode="⊖" horiz-adv-x="1064" d="M532.5 942q178.5 0 304.5 -126t126 -304t-126 -304t-304.5 -126t-304.5 126t-126 304t126 304t304.5 126zM791 565h-517v-104h517v104z" />
|
||||||
|
<glyph unicode="⊞" horiz-adv-x="1024" d="M819 922q43 0 73 -30t30 -73v-614q0 -41 -30 -72t-73 -31h-614q-41 0 -72 31t-31 72v614q0 43 31 73t72 30h614zM768 461v102h-205v205h-102v-205h-205v-102h205v-205h102v205h205z" />
|
||||||
|
<glyph unicode="⊟" horiz-adv-x="1024" d="M819 922q43 0 73 -30t30 -73v-614q0 -41 -30 -72t-73 -31h-614q-41 0 -72 31t-31 72v614q0 43 31 73t72 30h614zM768 461v102h-512v-102h512z" />
|
||||||
|
<glyph unicode="⌂" horiz-adv-x="1128" d="M1012 498q16 -16 11 -27.5t-28 -11.5h-86v-318q0 -14 -1 -21t-8 -13.5t-23 -6.5h-209v318h-209v-318h-199q-29 0 -36 10.5t-7 30.5v318h-86q-23 0 -28 11t12 28l409 411q16 16 39 16.5t39 -16.5z" />
|
||||||
|
<glyph unicode="⌨" d="M1055 819q29 0 50 -21.5t21 -49.5v-472q0 -31 -21.5 -51t-49.5 -20h-881q-29 0 -50.5 20.5t-21.5 50.5v472q0 29 21.5 50t50.5 21h881zM666 717v-103h102v103h-102zM819 563h-102v-102h102v102zM512 717v-103h102v103h-102zM666 563h-103v-102h103v102zM358 717v-103h103 v103h-103zM512 563h-102v-102h102v102zM205 717v-103h102v103h-102zM358 563h-102v-102h102v102zM307 307v103h-102v-103h102zM870 307v103h-512v-103h512zM1024 307v103h-102v-103h102zM870 461h103v102h-103v-102zM1024 614v103h-205v-103h205z" />
|
||||||
|
<glyph unicode="⌫" d="M1024 870q43 0 72.5 -29.5t29.5 -72.5v-512q0 -41 -29.5 -71.5t-72.5 -30.5h-489q-39 0 -72 28l-348 303q-29 27 0 56l348 303q31 27 72 26h489zM881 307l73 76l-131 129l131 131l-73 74l-131 -129l-132 129l-73 -74l131 -131l-131 -129l73 -76l132 131z" />
|
||||||
|
<glyph unicode="⏩" horiz-adv-x="1105" d="M989 537q14 -10 15 -25q0 -14 -15 -23l-381 -253q-23 -14 -38 -6t-15 36v494q0 29 15.5 37t37.5 -6zM524 537q14 -10 15 -25q0 -14 -15 -23l-368 -253q-20 -14 -37 -6t-17 36v494q0 29 16.5 37t37.5 -6z" />
|
||||||
|
<glyph unicode="⏪" horiz-adv-x="1105" d="M102 512q0 14 15 25l383 254q20 14 36.5 5.5t16.5 -36.5v-494q0 -29 -16.5 -37t-36.5 7l-383 253q-15 9 -15 23zM567 512q0 14 15 25l368 254q20 14 37 5.5t17 -36.5v-494q0 -29 -16.5 -37t-37.5 7l-368 253q-15 9 -15 23z" />
|
||||||
|
<glyph unicode="⏭" horiz-adv-x="819" d="M524 537q14 -10 15 -25q0 -12 -15 -23l-370 -233q-23 -14 -37.5 -5t-14.5 36v452q0 27 14.5 36t37.5 -5zM641 811q76 0 76 -59v-478q0 -59 -76 -59q-78 0 -78 59v478q0 59 78 59z" />
|
||||||
|
<glyph unicode="⏮" horiz-adv-x="819" d="M281 512q0 14 14 25l373 233q20 14 34.5 5t14.5 -36v-452q0 -27 -14.5 -36t-34.5 5l-373 233q-14 11 -14 23zM102 752q0 59 78 59q76 0 76 -59v-478q0 -59 -76 -59q-78 0 -78 59v478z" />
|
||||||
|
<glyph unicode="⏳" horiz-adv-x="778" d="M676 791q0 -45 -49 -98.5t-99.5 -101.5t-50.5 -79t50.5 -78t99.5 -99t49 -99v-121q0 -35 -88 -75t-198.5 -40t-199 40t-88.5 75v121q0 47 49.5 99t99.5 99t50 78t-50 79t-99.5 101t-49.5 99v120q0 33 89.5 73t198 40t197.5 -40t89 -73v-120zM182 905l-18 -14q-4 -8 4 -14 q94 -53 221 -54q135 0 225 51q14 10 -16 31q-98 55 -207 56q-123 -1 -209 -56zM416 512q0 18 4 33.5t18.5 34t20.5 25.5t31.5 32t29.5 29q94 94 94 125l2 51q-102 -55 -227 -55.5t-227 55.5l4 -51q0 -33 92 -125q6 -6 22.5 -21.5t23.5 -23t19.5 -19.5t17.5 -21.5t11 -20.5 t9.5 -23.5t3.5 -24.5q0 -10 -1.5 -19.5t-6.5 -18.5t-8 -16t-11 -17.5t-12 -15.5t-15.5 -16.5t-16.5 -15.5t-18.5 -16t-17.5 -17q-92 -92 -92 -124v-68q8 4 67.5 23.5t94 44t34.5 59.5q0 31 27 31t27 -31q0 -35 33.5 -59.5t96 -44t68.5 -23.5v68q0 31 -94 124 q-4 4 -21.5 20.5t-22.5 22t-18.5 19.5t-18.5 22.5t-12 20.5t-9 23.5t-2 23.5z" />
|
||||||
|
<glyph unicode="⏴" horiz-adv-x="430" d="M215 625q47 0 80 -33t33 -80q0 -45 -33 -79t-80 -34t-80 34t-33 79q0 47 33 80t80 33z" />
|
||||||
|
<glyph unicode="⏵" horiz-adv-x="788" d="M215 625q47 0 80 -33t33 -80q0 -45 -33 -79t-80 -34t-80 33t-33 80t33 80t80 33zM573.5 625q47.5 0 80 -33t32.5 -80q0 -45 -34 -79t-79 -34q-47 0 -79.5 33t-32.5 80t32.5 80t80 33z" />
|
||||||
|
<glyph unicode="⏶" horiz-adv-x="1146" d="M215 625q47 0 80 -33t33 -80q0 -45 -33 -79t-80 -34t-80 34t-33 79q0 47 33 80t80 33zM573.5 625q47.5 0 80 -33t32.5 -80q0 -45 -34 -79t-79 -34t-78.5 34t-33.5 79q0 47 32.5 80t80 33zM932 625q47 0 79.5 -33t32.5 -80q0 -45 -32.5 -79t-79.5 -34t-80 34t-33 79 q0 47 33 80t80 33z" />
|
||||||
|
<glyph unicode="⏷" horiz-adv-x="1122" d="M1020 338q0 -35 -24.5 -58.5t-57.5 -23.5h-799q-23 0 -32 5t-2.5 15.5t24.5 20.5l821 463q29 18 49.5 7t20.5 -46v-383z" />
|
||||||
|
<glyph unicode="■" horiz-adv-x="819" d="M641 819q76 0 76 -65v-482q0 -68 -76 -67h-461q-78 0 -78 67v482q0 37 18.5 51t59.5 14h461z" />
|
||||||
|
<glyph unicode="▴" horiz-adv-x="675" d="M102 307l236 410l235 -410h-471z" />
|
||||||
|
<glyph unicode="▶" horiz-adv-x="716" d="M600 539q14 -10 14 -27q0 -14 -14 -25l-438 -272q-25 -16 -42.5 -6t-17.5 41v526q0 31 17.5 41t42.5 -6z" />
|
||||||
|
<glyph unicode="▸" horiz-adv-x="614" d="M102 748l410 -236l-410 -236v472z" />
|
||||||
|
<glyph unicode="▾" horiz-adv-x="675" d="M573 717l-235 -410l-236 410h471z" />
|
||||||
|
<glyph unicode="◂" horiz-adv-x="614" d="M512 748v-472l-410 236z" />
|
||||||
|
<glyph unicode="●" horiz-adv-x="921" d="M460.5 870q149.5 0 254 -104t104.5 -254q0 -147 -104.5 -252.5t-254 -105.5t-254 105.5t-104.5 252.5q0 150 104.5 254t254 104z" />
|
||||||
|
<glyph unicode="◑" d="M1075 553q20 0 35.5 -12.5t15.5 -28.5q0 -41 -51 -41h-49q-51 0 -51 41q0 16 15.5 28.5t35.5 12.5h49zM614.5 793q116.5 0 199.5 -82t83 -199q0 -119 -83 -201t-199.5 -82t-198.5 82t-82 201q0 117 82 199t198.5 82zM621 307v410q-88 0 -149.5 -60.5t-61.5 -144.5 q0 -86 61.5 -145.5t149.5 -59.5zM256 512q0 -41 -51 -41h-51q-51 0 -52 41q0 16 15.5 28.5t36.5 12.5h51q20 0 35.5 -12.5t15.5 -28.5zM614.5 870q-16.5 0 -29 15.5t-12.5 36.5v51q0 20 12.5 35.5t29 15.5t28.5 -15.5t12 -35.5v-51q0 -20 -12 -36t-28.5 -16zM614.5 154 q16.5 0 28.5 -15.5t12 -36.5v-51q0 -20 -12 -35.5t-28.5 -15.5t-29 15.5t-12.5 35.5v51q0 20 12.5 36t29 16zM991 829l-35 -34q-35 -35 -65 -9q-29 29 8 66q4 6 35 37q37 35 65.5 6t-8.5 -66zM274 227q14 16 34 18.5t30 -9.5q12 -12 10 -32t-16 -34l-37 -37 q-14 -14 -33.5 -16t-30.5 10q-31 29 7 66q5 3 36 34zM295 889l37 -37q37 -37 6 -66q-10 -10 -29.5 -8t-34.5 17q-31 31 -36 34q-14 14 -16.5 34t9.5 32q10 12 30 10t34 -16zM899 170q-37 37 -8 65.5t65 -8.5l35 -34q37 -37 8.5 -66t-65.5 6q-31 31 -35 37z" />
|
||||||
|
<glyph unicode="◴" horiz-adv-x="1064" d="M479 942v-377h-377q18 150 124 255.5t253 121.5zM588 942q160 -20 267.5 -142t107.5 -286q0 -178 -126 -305t-307 -127q-164 0 -284.5 107.5t-143.5 269.5h435q20 0 35.5 14t15.5 37v432z" />
|
||||||
|
<glyph unicode="◼" horiz-adv-x="1024" d="M0 0v0v0v0v0z" />
|
||||||
|
<glyph unicode="☁" d="M881 659q102 0 173.5 -69.5t71.5 -170t-71.5 -170t-173.5 -69.5h-592q-76 0 -131.5 53.5t-55.5 126.5q0 76 54.5 129.5t132.5 53.5q2 0 10 -1t10 -1q-2 12 -2 39q0 111 80 188.5t193 77.5q92 0 163.5 -53.5t96.5 -137.5q29 4 41 4z" />
|
||||||
|
<glyph unicode="★" horiz-adv-x="1105" d="M553 963l123 -345h328l-269 -200l96 -357l-278 213l-279 -213l97 357l-269 200h328z" />
|
||||||
|
<glyph unicode="☆" horiz-adv-x="1105" d="M1004 618l-269 -200l96 -357l-278 213l-279 -213l97 357l-269 200h328l123 345l123 -345h328zM553 375l154 -127l-64 182l148 117l-181 -4l-57 207l-55 -207l-181 4l146 -117l-64 -182z" />
|
||||||
|
<glyph unicode="☕" horiz-adv-x="905" d="M453 932q156 0 255 -42t93 -89l-74 -608q-2 -14 -35 -37t-99.5 -43.5t-140 -20.5t-139 20.5t-99.5 43t-36 37.5l-74 608q-4 29 37 58.5t124.5 51t187.5 21.5zM452.5 711q73.5 0 140 15t100.5 33.5t34 31t-34 30t-100.5 32.5t-140 15t-140 -15t-100.5 -32.5t-34 -30 t34 -31t100.5 -33.5t140 -15z" />
|
||||||
|
<glyph unicode="☰" horiz-adv-x="921" d="M768 563q23 0 37 -15.5t14 -35.5t-15.5 -35.5t-35.5 -15.5h-614q-20 0 -36 15.5t-16 35.5t14.5 35.5t37.5 15.5h614zM154 666q-20 0 -36 15t-16 35.5t14.5 36t37.5 15.5h614q23 0 37 -15.5t14 -36t-15.5 -35.5t-35.5 -15h-614zM768 358q23 0 37 -15t14 -35.5t-15.5 -36 t-35.5 -15.5h-614q-20 0 -36 15.5t-16 36t14.5 35.5t37.5 15h614z" />
|
||||||
|
<glyph unicode="☽" horiz-adv-x="1046" d="M639 397.5q109 108.5 128 258t-54 276.5q53 -27 98 -74q131 -131 131 -316.5t-131 -316.5t-317.5 -131t-317.5 131q-41 41 -74 97q127 -72 277.5 -52.5t259.5 128z" />
|
||||||
|
<glyph unicode="♡" horiz-adv-x="1089" d="M913 811q72 -66 72 -160t-72 -159l-368 -338l-369 338q-72 66 -72 159.5t72 159.5q66 59 156 59t153 -59l60 -53l57 53q66 59 155.5 59t155.5 -59zM858 545q43 41 43 106q0 68 -39 103q-39 39 -104 39q-53 0 -107 -50l-106 -94l-109 94q-49 49 -104 50q-66 0 -107 -39 q-39 -37 -39 -103q0 -68 45 -106l314 -293z" />
|
||||||
|
<glyph unicode="♥" horiz-adv-x="1089" d="M913 813q72 -66 72 -160t-72 -161l-368 -338l-369 338q-72 68 -72 162t72 159q63 59 154.5 59t156.5 -59l58 -53l59 53q63 59 153 59t156 -59z" />
|
||||||
|
<glyph unicode="♪" horiz-adv-x="800" d="M494 993q0 -43 47 -99t92 -102.5t62.5 -122t-40.5 -157.5q-20 -35 -26 -16q-2 6 0 16q10 18 6 57t-13.5 80t-44 75t-83.5 42v-549q2 -49 -38 -97t-108 -73q-76 -29 -146.5 -8.5t-91 78t20.5 118t119 87.5q88 31 162 4v667h82z" />
|
||||||
|
<glyph unicode="♫" horiz-adv-x="964" d="M313 885l547 119v-721q2 -43 -30.5 -83t-88.5 -61q-66 -25 -112.5 -8.5t-63.5 66.5q-18 49 7.5 99t86.5 75q53 20 109 10v385l-362 -84v-502q0 -43 -33 -83t-88 -60q-66 -23 -112 -7.5t-62 64.5q-18 49 6 99.5t86 74.5q55 20 110 11v606z" />
|
||||||
|
<glyph unicode="⚏" horiz-adv-x="819" d="M276 819q82 0 82 -82v-92q0 -82 -82 -82h-92q-82 0 -82 82v92q0 82 82 82h92zM635 819q82 0 82 -82v-92q0 -82 -82 -82h-92q-82 0 -82 82v92q0 82 82 82h92zM276 461q82 0 82 -82v-92q0 -82 -82 -82h-92q-82 0 -82 82v92q0 82 82 82h92zM635 461q82 0 82 -82v-92 q0 -82 -82 -82h-92q-82 0 -82 82v92q0 82 82 82h92z" />
|
||||||
|
<glyph unicode="⚑" horiz-adv-x="1128" d="M997 784q14 6 22.5 -1t0.5 -19q-98 -141 -168 -218t-113 -92.5t-74.5 -2t-61.5 38t-64.5 41t-95 -4t-142.5 -88.5l92 -360h-102l-189 737l95 35q92 68 155.5 88t100 3t65.5 -52t63.5 -73t81 -63.5t132 -20.5t202.5 52z" />
|
||||||
|
<glyph unicode="⚒" horiz-adv-x="1226" d="M260 672q-8 -8 -11 -22.5t-3 -26t-2 -11.5q-2 -2 -17.5 -15t-19.5 -17q-16 -14 -29 4l-72 78q-10 12 3 24q2 2 18 14.5t20 16.5q6 6 28 6t38 14q14 14 18.5 39t10.5 31q2 0 9 7t26.5 22.5t41.5 31.5q137 92 191 99q125 0 152 -2q12 0 -9 -9q-123 -53 -155 -77 q-82 -57 -37 -117q35 -47 39 -49q8 -8 -2 -15q-2 -2 -39 -35.5t-39 -35.5q-14 -8 -19 -4q-43 49 -72.5 61.5t-68.5 -12.5zM553 645l420 -487q18 -23 -2 -39l-49 -43q-23 -14 -39 4l-424 483q-8 8 0 21l73 63q13 8 21 -2zM1120 852q16 -106 -16 -170q-51 -90 -158 -64 q-57 12 -102 -32l-84 -80l-70 80l70 71q25 25 32 54.5t6 66.5t5 60q12 57 143 114q12 6 18.5 -3t2.5 -15q-12 -12 -47 -82q-14 -10 -12.5 -35.5t40.5 -54.5q59 -41 99 22q6 12 26.5 42t22.5 34q4 10 13 9t11 -17zM242 152l260 254l78 -89l-252 -247q-20 -20 -39 -4l-47 47 q-23 19 0 39z" />
|
||||||
|
<glyph unicode="⚙" horiz-adv-x="1064" d="M881 512q0 -74 82 -125q-12 -41 -35 -84q-72 18 -140 -45q-55 -59 -34 -139q-41 -20 -86 -37q-47 84 -135.5 84t-135.5 -84q-45 16 -86 37q20 82 -35 139q-55 55 -139 35q-14 27 -35 84q84 53 84 135q0 74 -84 127q20 57 35 84q76 -18 139 45q55 57 35 139q43 23 86 35 q47 -82 135.5 -82t135.5 82q43 -12 86 -35q-20 -80 34 -139q68 -63 140 -45q23 -43 35 -84q-82 -51 -82 -127zM532.5 326q77.5 0 132 54t54.5 132t-54.5 133t-132 55t-132 -55t-54.5 -133t54.5 -132t132 -54z" />
|
||||||
|
<glyph unicode="⚠" horiz-adv-x="1191" d="M1083 129q10 -16 0 -35q-10 -16 -30 -16h-914q-18 0 -28 16q-12 18 -2 35l456 801q8 18 30.5 18t31.5 -18zM653 180v103h-112v-103h112zM653 358v308h-112v-308h112z" />
|
||||||
|
<glyph unicode="⚡" horiz-adv-x="618" d="M145 51q-4 4 36 96.5t81 186.5t39 100t-96 46t-101 57q-4 12 86.5 122.5t184.5 214t98 99.5q6 -4 -76 -190.5t-80 -190.5t97.5 -44t99.5 -59q4 -20 -178.5 -232t-190.5 -206z" />
|
||||||
|
<glyph unicode="⛈" d="M881 659q102 0 173.5 -69.5t71.5 -170t-71.5 -170t-173.5 -69.5h-592q-76 0 -131.5 53.5t-55.5 126.5q0 76 54.5 129.5t132.5 53.5q2 0 10 -1t10 -1q-2 12 -2 39q0 111 80 188.5t193 77.5q92 0 163.5 -53.5t96.5 -137.5q29 4 41 4zM684 438q14 16 14 31q0 20 -30 33h-4 q-27 14 -39 16l51 119q6 0 6 20q0 14 -8 19q-16 10 -35 -8q-2 -2 -30.5 -33t-62.5 -68t-46 -53q-12 -18 -13 -31q0 -23 31 -30l4 -2q8 -4 39 -17l-53 -117l-2 -8q-2 -8 -2 -14q0 -10 8 -19q18 -10 35 11q102 102 137 151z" />
|
||||||
|
<glyph unicode="✇" d="M891 748q98 0 166.5 -69t68.5 -167q0 -96 -68.5 -166t-166.5 -70h-553q-96 0 -166 70t-70 166q0 98 70 167t166 69q98 0 166.5 -69t68.5 -167q0 -74 -41 -133h164q-41 66 -41 133q0 98 70 167t166 69zM205 512q0 -53 39 -93t94 -40t94 40t39 93q0 55 -39 94t-94 39 t-94 -39t-39 -94zM891 379q55 0 94 40t39 93q0 55 -39 94t-94 39t-94 -39t-39 -94q0 -53 39 -93t94 -40z" />
|
||||||
|
<glyph unicode="✈" d="M377 31l127 409h-185l-114 -102h-103l82 174l-82 174h103l114 -102h185l-127 409h102l230 -409h264h16t37 -4.5t47.5 -11.5t36.5 -21.5t16 -34.5q0 -33 -38.5 -50.5t-75.5 -19.5l-39 -2h-264l-230 -409h-102z" />
|
||||||
|
<glyph unicode="✉" horiz-adv-x="1126" d="M133 754q-33 18 -29 41q2 14 27 14h866q39 0 21 -33q-8 -14 -25 -22q-14 -6 -196.5 -104.5t-186.5 -100.5q-16 -10 -47 -10q-29 0 -47 10q-4 2 -186.5 100.5t-196.5 104.5zM1004 651q20 10 20 -10v-377q0 -16 -17.5 -32.5t-33.5 -16.5h-819q-16 0 -34 16.5t-18 32.5v377 q0 20 21 10l393 -205q18 -10 47 -10t47 10z" />
|
||||||
|
<glyph unicode="✎" horiz-adv-x="1005" d="M838 850q33 -33 48 -65.5t15 -49.5v-16l-258 -258l-297 -295l-244 -53l52 245l297 295l258 258q55 13 129 -61zM332 195l24 24q-2 45 -53 96q-23 23 -46.5 36.5t-35.5 13.5l-14 2l-23 -25l-18 -82q29 -16 47 -35q25 -25 37 -49z" />
|
||||||
|
<glyph unicode="✒" horiz-adv-x="921" d="M166 12q-6 -20 -27 -8q-18 8 -16 35q4 102 51 231q-102 158 -53 324q10 -33 32.5 -80t45 -82t32.5 -31q8 4 0 85t-11 170t26 161q23 45 82 96.5t106 71.5q-25 -47 -34 -96t-4 -80t22 -33q12 0 86 123t108 125q47 4 117 -29.5t84 -66.5q12 -25 0 -81t-41 -85 q-45 -45 -149.5 -63.5t-116.5 -24.5q-16 -10 12 -35q55 -49 180 -21q-57 -82 -139 -116.5t-135 -38.5t-55 -10q-4 -25 50 -55.5t103 -14.5q-31 -57 -64.5 -86t-55 -36t-78 -11t-86.5 -8q-21 -66 -72 -230z" />
|
||||||
|
<glyph unicode="✓" horiz-adv-x="890" d="M358 154q-35 0 -57 28l-184 242q-16 25 -12 53.5t26.5 47t52 14.5t47.5 -29l121 -158l303 486q16 25 44 30.5t55 -8.5q25 -16 31 -44t-9 -54l-358 -574q-20 -33 -58 -32z" />
|
||||||
|
<glyph unicode="✖" horiz-adv-x="1064" d="M532.5 942q178.5 0 304.5 -126t126 -304t-126 -304t-304.5 -126t-304.5 126t-126 304t126 304t304.5 126zM621 512l157 158l-88 88l-158 -156l-155 156l-90 -88l157 -158l-157 -156l90 -88l155 156l158 -156l88 88z" />
|
||||||
|
<glyph unicode="❌" horiz-adv-x="688" d="M567 352q18 -18 18.5 -43.5t-18.5 -44.5q-18 -16 -43.5 -16t-44.5 16l-135 156l-135 -156q-18 -16 -44 -16t-44 16q-16 18 -16.5 44t16.5 44l141 160l-141 162q-16 18 -16.5 43.5t16.5 44.5q18 16 43.5 16t44.5 -16l135 -156l135 156q18 16 44 16t44 -16q18 -18 18.5 -44 t-18.5 -44l-141 -162z" />
|
||||||
|
<glyph unicode="❎" horiz-adv-x="1024" d="M819 922q43 0 73 -30t30 -73v-614q0 -41 -30 -72t-73 -31h-614q-41 0 -72 31t-31 72v614q0 43 31 73t72 30h614zM670 268l88 88l-158 156l158 158l-88 88l-158 -156l-156 156l-90 -88l158 -158l-158 -156l90 -88l156 156z" />
|
||||||
|
<glyph unicode="❓" horiz-adv-x="798" d="M608 911q88 -63 88 -188q0 -66 -43 -127q-12 -20 -90 -82l-47 -31q-41 -35 -49 -61q-6 -16 -8 -45q0 -14 -17 -15h-131q-16 0 -16 13q4 100 29 127q16 23 49 49.5t57 42.5l25 14q23 16 34 35q29 45 29 72q0 41 -26 80q-29 37 -95 36q-70 0 -96 -45q-29 -43 -29 -94h-170 q6 166 117 238q72 43 170 43q133 -1 219 -62zM387 260q45 0 75 -30.5t27 -75.5q-2 -47 -32.5 -75t-75.5 -26q-45 0 -75 30t-27.5 77t33 74.5t75.5 25.5z" />
|
||||||
|
<glyph unicode="❞" horiz-adv-x="985" d="M252 850q150 0 188 -150q39 -143 -41 -309q-82 -172 -229 -209q-33 -8 -68 -8v72q115 0 187 110q55 88 26 150q-16 37 -63 37q-61 0 -105.5 45t-44.5 108.5t44.5 108.5t105.5 45zM682 850q150 0 188 -150q39 -143 -41 -309q-82 -172 -229 -209q-33 -8 -68 -8v72 q115 0 187 110q55 88 26 150q-16 37 -63 37q-61 0 -105.5 45t-44.5 108.5t44.5 108.5t105.5 45z" />
|
||||||
|
<glyph unicode="➕" horiz-adv-x="798" d="M666 563q31 0 30.5 -51t-30.5 -51h-215v-215q0 -31 -51.5 -31t-51.5 31v215h-215q-31 0 -31 51t31 51h215v215q0 31 51.5 31t51.5 -31v-215h215z" />
|
||||||
|
<glyph unicode="➖" horiz-adv-x="798" d="M666 563q31 0 30.5 -51t-30.5 -51h-533q-31 0 -31 51t31 51h533z" />
|
||||||
|
<glyph unicode="➡" horiz-adv-x="952" d="M461 850l389 -338l-389 -338v197h-359v284h359v195z" />
|
||||||
|
<glyph unicode="➢" horiz-adv-x="1085" d="M971 940q8 -8 11 -16.5t-2 -22.5t-10 -26.5t-19.5 -40t-24.5 -50.5q-55 -115 -150.5 -293t-161.5 -298l-67 -121l-55 389l-390 57q453 252 713 377q20 10 49 25.5t40 20.5t25.5 9t24 1t17.5 -11zM877 842l-312 -287l29 -240z" />
|
||||||
|
<glyph unicode="➦" horiz-adv-x="1126" d="M655 412q-215 0 -340 -46.5t-213 -201.5q4 20 13.5 54t51.5 120t98.5 151.5t159.5 120t230 54.5v196l369 -330l-369 -342v224z" />
|
||||||
|
<glyph unicode="⟲" horiz-adv-x="1167" d="M647 907q174 0 296 -122.5t122 -297t-122 -297t-296 -122.5q-145 0 -258 90l72 75q86 -61 186 -61q129 0 221.5 92t92.5 223t-92.5 223.5t-221.5 92.5q-127 0 -219 -89t-94 -216h145l-188 -209l-189 209h127q2 170 125 289.5t293 119.5z" />
|
||||||
|
<glyph unicode="⟳" horiz-adv-x="1167" d="M520 932q172 0 294 -119t126 -289h125l-188 -211l-189 211h148q-4 127 -96.5 215t-219.5 88q-129 0 -221 -92t-92 -223q0 -129 92 -221t221 -92q106 0 187 61l71 -78q-113 -90 -258 -90q-172 0 -295 123t-123 297t123 297t295 123z" />
|
||||||
|
<glyph unicode="⬅" horiz-adv-x="952" d="M489 174l-387 338l387 338v-195h361v-284h-361v-197z" />
|
||||||
|
<glyph unicode="⬆" horiz-adv-x="880" d="M778 498h-196v-359h-283v359h-197l338 389z" />
|
||||||
|
<glyph unicode="⬇" horiz-adv-x="880" d="M778 528l-338 -389l-338 389h197v359h283v-359h196z" />
|
||||||
|
<glyph unicode="" d="M461 563q23 0 37 -15.5t14 -35.5t-15.5 -35.5t-35.5 -15.5h-307q-20 0 -36 15.5t-16 35.5t14.5 35.5t37.5 15.5h307zM461 358q23 0 37 -15t14 -35.5t-15.5 -36t-35.5 -15.5h-307q-20 0 -36 15.5t-16 36t14.5 35.5t37.5 15h307zM1096 563q31 0 30.5 -51t-30.5 -51h-174 v-174q0 -31 -51.5 -31t-51.5 31v174h-168q-31 0 -30.5 51t30.5 51h168v174q0 31 51.5 31t51.5 -31v-174h174zM461 768q23 0 37 -15.5t14 -36t-15.5 -35.5t-35.5 -15h-307q-20 0 -36 15t-16 35.5t14.5 36t37.5 15.5h307z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="921" d="M205 358q20 0 35.5 -15t15.5 -35.5t-15.5 -36t-35.5 -15.5h-51q-20 0 -36 15.5t-16 36t14.5 35.5t37.5 15h51zM205 563q20 0 35.5 -15.5t15.5 -35.5t-15.5 -35.5t-35.5 -15.5h-51q-20 0 -36 15.5t-16 35.5t14.5 35.5t37.5 15.5h51zM205 768q20 0 35.5 -15.5t15.5 -36 t-15.5 -35.5t-35.5 -15h-51q-20 0 -36 15t-16 35.5t14.5 36t37.5 15.5h51zM410 666q-20 0 -36 15t-16 35.5t15.5 36t36.5 15.5h358q23 0 37 -15.5t14 -36t-15.5 -35.5t-35.5 -15h-358zM768 563q23 0 37 -15.5t14 -35.5t-15.5 -35.5t-35.5 -15.5h-358q-20 0 -36 15.5 t-16 35.5t15.5 35.5t36.5 15.5h358zM768 358q23 0 37 -15t14 -35.5t-15.5 -36t-35.5 -15.5h-358q-20 0 -36 15.5t-16 36t15.5 35.5t36.5 15h358z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="952" d="M489 901v-194h361v-388h-361v-196l-387 389z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="952" d="M461 901l389 -389l-389 -389v196h-359v388h359v194z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="983" d="M881 498h-197v-359h-385v359h-197l390 389z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="983" d="M881 528l-389 -389l-390 389h197v359h385v-359h197z" />
|
||||||
|
<glyph unicode="" d="M737 285q184 -66 185 -125v-109h-820v207q37 14 84 27q96 35 132 70.5t36 97.5q0 23 -23.5 49t-31.5 76q-2 12 -23.5 25t-25.5 63q0 16 5 26t9 13l4 4q-8 51 -12 90q-6 55 41 114.5t164 59.5t164 -59.5t43 -114.5l-15 -90q18 -8 19 -43q-2 -29 -9.5 -44.5t-14.5 -17.5 t-14 -8t-9 -18q-10 -47 -34 -75t-24 -50q0 -61 37 -97t133 -71zM973 563h153v-102h-153v-154h-103v154h-153v102h153v154h103v-154z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1150" d="M569.5 983q194.5 2 333.5 -133t144 -330q2 -195 -134.5 -335t-330.5 -144q-195 -2 -335.5 134t-142.5 331q-4 195 133.5 335t332 142zM567 225q31 0 50.5 19.5t19.5 48.5q2 31 -17.5 50t-50.5 19h-2q-29 0 -48 -18t-21 -47q0 -31 19.5 -50.5t47.5 -21.5h2zM737 561 q27 35 27 80q0 80 -55 119q-53 39 -138 39q-66 0 -106 -27q-70 -43 -74 -149v-5h113v5q0 27 16 55q16 25 55 24q41 0 54 -20q16 -20 16 -45q0 -18 -16 -41q-8 -12 -21 -21l-6 -4l-16.5 -11t-20.5 -15t-21.5 -17.5t-17.5 -17.5q-14 -20 -18 -80v-8h110v4q0 12 5 29 q6 20 28 37l29 18q47 35 57 51z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1150" d="M569.5 983q194.5 2 333.5 -133t144 -330q2 -195 -134.5 -335t-330.5 -144q-195 -2 -335.5 134t-142.5 331q-4 195 133.5 335t332 142zM623 827q-43 0 -67 -24.5t-24 -50.5q-2 -29 15.5 -45.5t50.5 -16.5q39 0 62.5 22.5t23.5 55.5q0 59 -61 59zM500 219q31 0 86 26.5 t108 80.5l-18 24q-49 -37 -74 -37q-14 0 -4 39l43 164q27 98 -23 98q-31 0 -91 -29.5t-117 -76.5l16 -27q53 35 76 35q12 0 0 -35l-37 -155q-26 -107 35 -107z" />
|
||||||
|
<glyph unicode="" d="M614.5 799q94.5 0 181.5 -25.5t144 -63.5t101 -79t64.5 -73t20.5 -46t-20.5 -45t-64.5 -73t-101 -80t-144 -63.5t-181.5 -25.5t-181.5 25.5t-144.5 63.5t-101.5 80t-64.5 73t-20.5 45t20.5 46t64.5 73t101.5 79t144.5 63.5t181.5 25.5zM614.5 293q94.5 0 161 64.5 t66.5 154.5q0 92 -66.5 156.5t-161 64.5t-161 -64.5t-66.5 -156.5q0 -90 66.5 -154.5t161 -64.5zM614 512q8 -8 38 -2t51.5 11t25.5 -9q0 -45 -33.5 -77t-81 -32t-80 32t-32.5 77q0 47 32.5 79t79.5 32q14 0 10.5 -24t-12 -48.5t1.5 -38.5z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1187" d="M1069 1004q37 -109 -8 -204t-131 -161l18 -25q16 -29 6 -55l-49 -162q-12 -31 -37 -47l-475 -336q-43 -31 -65 4l-215 312q-12 18 -9.5 39.5t21.5 33.5l475 336q27 18 55 19h162q31 0 49 -27l29 -41q172 133 117 293q-10 29 18 41q33 9 39 -20zM848 524q41 33 35 82 l-33 -16q-8 -4 -12 -4q-18 0 -29 18q-12 31 16 41l25 14q-49 35 -94 0q-29 -18 -35 -52t14 -62q18 -27 52 -33t61 12z" />
|
||||||
|
<glyph unicode="" d="M881 659q102 0 173.5 -69.5t71.5 -170t-71.5 -170t-173.5 -69.5h-195v195h109l-181 235l-178 -235h107v-195h-254q-76 0 -131.5 53.5t-55.5 126.5q0 76 54.5 129.5t132.5 53.5q14 0 20 -2q-2 12 -2 39q0 111 80 188.5t193 77.5q92 0 163.5 -53.5t96.5 -137.5q29 4 41 4z " />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M1024 164q-88 156 -213 202t-338 46v-224l-371 342l371 330v-196q92 0 172 -28t134.5 -72t98.5 -97t70.5 -106.5t45 -97.5t24.5 -70z" />
|
||||||
|
<glyph unicode="" d="M473 723l-217 -193l217 -200v-142l-371 342l371 330v-137zM729 664q106 0 186 -51.5t118 -125t61.5 -147.5t27.5 -125l4 -51q-88 158 -172 203t-225 45v-224l-371 342l371 330v-196z" />
|
||||||
|
<glyph unicode="" d="M492 805q16 -14 16 -32.5t-16 -31.5l-252 -229l252 -231q16 -12 16 -31t-16 -33q-31 -31 -62 0l-328 295l328 293q31 31 62 0zM801 805l325 -293l-325 -295q-33 -31 -64 0q-33 33 0 64l254 231l-254 229q-33 31 0 64q31 31 64 0z" />
|
||||||
|
<glyph unicode="" d="M870 215v57l103 84v-192q0 -20 -15.5 -35.5t-35.5 -15.5h-768q-20 0 -36 15t-16 36v563q0 23 14.5 37t37.5 14h295q-33 -25 -61 -50.5t-40 -39.5l-10 -12h-133v-461h665zM786 571q-170 0 -247.5 -42t-163.5 -185q0 8 1 22.5t9 57.5t22.5 81t45 85t71.5 81t109.5 57.5 t152.5 23.5v159l340 -256l-340 -266v182z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1208" d="M170 692q-27 0 -23 23q4 10 13 14q2 0 50 17.5t95 33t60 15.5h45v153h389v-153h47q12 0 58 -15.5t94.5 -33t50.5 -17.5q18 -8 12 -27q-4 -10 -21 -10h-870zM1051 635q20 0 37.5 -19.5t17.5 -42.5v-178q0 -23 -17.5 -42t-37.5 -19h-103l45 -256h-778l45 256h-100 q-20 0 -39 19.5t-19 41.5v178q0 23 18.5 42.5t39.5 19.5h891zM317 180h574l-72 332h-430z" />
|
||||||
|
<glyph unicode="" d="M358 348h279l131 -143h-459q-43 0 -72.5 30.5t-29.5 71.5v309h-105l181 203l178 -203h-103v-268zM1024 410h102l-178 -205l-180 205h104v266h-280l-131 143h461q41 0 71.5 -29.5t30.5 -72.5v-307z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1024" d="M819 870q43 0 73 -29.5t30 -72.5v-358q0 -41 -30 -72t-73 -31h-205v-153l-204 153h-205q-41 0 -72 31t-31 72v358q0 43 31 72.5t72 29.5h614z" />
|
||||||
|
<glyph unicode="" d="M399 399h359q2 0 6 2h4v-94q0 -41 -29.5 -71.5t-72.5 -30.5h-256l-154 -154v154h-51q-41 0 -72 30.5t-31 71.5v307q0 43 31 73t72 30h194v-318zM1024 973q43 0 72.5 -30t29.5 -73v-307q0 -41 -29.5 -71.5t-72.5 -30.5h-51v-154l-154 154h-358v409q0 43 30.5 73t71.5 30 h461z" />
|
||||||
|
<glyph unicode="" d="M1024 922q43 0 72.5 -30t29.5 -73v-614q0 -41 -29.5 -72t-72.5 -31h-819q-41 0 -72 31t-31 72v614q0 43 31 73t72 30h819zM1024 205v614h-819v-614h819zM563 406v-93h-256v93h256zM563 559v-92h-256v92h256zM563 713v-92h-256v92h256zM918 385l4 -72h-256q0 72 6 72 q86 23 86 68q0 16 -28 57t-28 90q0 113 92.5 113t92.5 -113q0 -49 -29 -90t-29 -57q0 -20 21.5 -37t44.5 -23z" />
|
||||||
|
<glyph unicode="" d="M539 973q20 0 20 -21v-880q0 -20 -20 -21h-47q-20 0 -21 21v450h-180q-16 0 -29 6q-12 2 -26 13l-123 84q-10 6 -10.5 16t10.5 16l123 84q14 10 26 13q8 4 29 4h180v194q0 20 21 21h47zM1116 760q10 -6 10 -16.5t-10 -16.5l-121 -84q-23 -12 -26 -12q-14 -6 -29 -6h-309 l-41 235h350q18 0 28.5 -4t26.5 -12z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="716" d="M358.5 922q106.5 0 181 -75t74.5 -181q0 -109 -63.5 -249.5t-128.5 -228.5l-64 -86q-10 12 -27.5 36t-61.5 91.5t-77.5 133t-61.5 150.5t-28 153q0 106 75 181t181.5 75zM358.5 524q57.5 0 98.5 41t41 98.5t-41 97.5t-98.5 40t-97.5 -40t-40 -97.5t40 -98.5t97.5 -41z " />
|
||||||
|
<glyph unicode="" d="M1110 768q16 -10 16 -31v-598q0 -20 -16 -30q-8 -6 -16 -7q-8 0 -19 7l-221 139l-221 -139q-18 -10 -35 0l-223 139l-221 -139q-16 -10 -35 0q-16 10 -17 30v598q0 20 17 31l239 150q18 10 35 0l221 -140l224 140q16 10 32 0zM342 307v518l-172 -106v-518zM582 201v518 l-172 106v-518zM821 307v518l-174 -106v-518zM1059 201v518l-172 106v-518z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1191" d="M590 1004q203 2 348 -139.5t149 -344.5q2 -205 -139 -350t-346 -150q-203 -2 -349.5 140.5t-148.5 345.5q-4 205 138.5 350.5t347.5 147.5zM602 125q160 2 272.5 116.5t110.5 276.5t-117.5 273.5t-275.5 109.5q-162 -2 -273.5 -116.5t-109.5 -276.5t116.5 -273.5 t276.5 -109.5zM362 283q4 27 12.5 67.5t42.5 130.5t79 135.5t128 78t144 42.5l61 11q-4 -27 -12 -68t-42 -131t-79 -135q-43 -43 -127 -76t-145 -43zM547 565q-23 -20 -23 -49t23 -51q20 -23 50 -23t50 23q53 53 90 190q-139 -37 -190 -90z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="983" d="M154 623q125 -72 338 -72t337 72l-55 -498q-2 -14 -35.5 -36.5t-102.5 -44t-144.5 -21.5t-143 21.5t-102.5 44t-37 36.5zM653 930q96 -18 162 -56t66 -73v-10q0 -59 -115 -101.5t-274.5 -42.5t-274.5 42t-115 102v10q0 35 65.5 72.5t162.5 56.5l43 49q23 27 71 27h95 q53 0 71 -27zM598 815h86q-94 113 -106 129q-14 16 -33 17h-105q-23 0 -32 -17l-109 -129h86l66 68h84z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="921" d="M717 973q43 0 72.5 -30t29.5 -73v-716q0 -41 -29.5 -72t-72.5 -31h-512q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h512zM717 154v716h-512v-716h512z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="921" d="M717 973q43 0 72.5 -30t29.5 -73v-716q0 -41 -29.5 -72t-72.5 -31h-512q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h512zM246 760v-90h430v90h-430zM676 268v90h-430v-90h430zM676 469v92h-428v-92h428z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1230" d="M1096 645q39 -10 30 -47l-153 -569q-4 -16 -18.5 -23.5t-30.5 -3.5l-416 113q-16 4 -24.5 18t-4.5 29l25 94l-185 -49q-41 -10 -51 26l-164 617q-10 37 29 49l465 125q16 4 30.5 -3t18.5 -24l68 -249zM186 840l148 -555l401 108l-147 553zM905 80l135 504l-305 84 l78 -289q10 -35 -29 -47l-200 -53l-27 -105z" />
|
||||||
|
<glyph unicode="" d="M102 768q0 43 31 72.5t72 29.5h819q43 0 72.5 -29.5t29.5 -72.5v-512q0 -41 -29.5 -71.5t-72.5 -30.5h-819q-41 0 -72 30.5t-31 71.5v512zM1024 768h-819v-512h819v512z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="983" d="M102 901q322 0 550.5 -228.5t228.5 -549.5h-121q0 272 -192.5 463.5t-465.5 191.5v123zM102 657q223 0 380 -156.5t157 -377.5h-121q0 170 -122 292t-294 122v120zM219 354q47 0 82 -33.5t35 -82.5q0 -47 -35 -81t-82 -34t-82 33.5t-35 81.5q0 49 35 82.5t82 33.5z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1024" d="M768 358q63 0 108.5 -44t45.5 -109q0 -63 -45.5 -108.5t-108.5 -45.5t-108.5 45t-45.5 109q0 6 1 14t1 12l-266 160q-43 -33 -94 -33q-63 0 -108.5 45.5t-45.5 108.5t45.5 108.5t108.5 45.5q55 0 94 -31l266 160q0 4 -1 12t-1 12q0 63 45.5 108.5t108.5 45.5t108.5 -44 t45.5 -110q0 -63 -45.5 -108t-108.5 -45q-53 0 -92 32l-268 -159q2 -8 2 -27q0 -16 -2 -25l268 -159q37 30 92 30z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M256 154q0 41 30.5 71.5t71.5 30.5q43 0 73 -30.5t30 -71.5q0 -43 -30 -73t-73 -30q-41 0 -71.5 30t-30.5 73zM768 154q0 41 30.5 71.5t71.5 30.5q43 0 73 -30.5t30 -71.5q0 -43 -30 -73t-73 -30q-41 0 -71.5 30t-30.5 73zM438 395q-37 -10 -34.5 -23.5t45.5 -13.5h575 v-77q0 -20 -20 -21h-134h-512h-24q-20 0 -21 21v77l-10 48l-100 464h-101v82q0 20 21 21h160q20 0 20 -21v-88h721v-280q0 -23 -18 -27z" />
|
||||||
|
<glyph unicode="" d="M451 512q0 70 48 117t115.5 47t115.5 -47t48 -117q0 -68 -48 -116t-115.5 -48t-115.5 48t-48 116zM334 573q-14 -61 -68 -61h-164v123h121q41 127 148.5 207t242.5 80q168 0 291 -119q16 -18 16.5 -44t-16.5 -44q-18 -16 -43.5 -16.5t-44.5 16.5q-80 84 -203 84 q-102 0 -179.5 -64t-100.5 -162zM963 512h163v-123h-120q-41 -127 -147.5 -207t-244.5 -80q-168 0 -288 121q-18 18 -18.5 44t18.5 42q16 18 41.5 18.5t44.5 -18.5q84 -84 202 -84q102 0 180 64.5t101 161.5q13 61 68 61z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M922 973q43 0 72.5 -30t29.5 -73v-716q0 -41 -29.5 -72t-72.5 -31h-461q-41 0 -71 31t-30 72v102h101v-102h461v716h-461v-153h-101v153q0 43 30 73t71 30h461zM563 287v123h-461v153h461v123l205 -199z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M616 154v102h101v-102q0 -41 -30 -72t-73 -31h-409q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h409q43 0 73 -30t30 -73v-153h-101v153h-411v-716h411zM1024 487l-203 -200v123h-461v153h461v123z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1015" d="M590 918h323v-324l-102 127l-149 -156l-103 103l156 149zM354 463l103 -103l-156 -149l125 -102h-324v323l103 -125z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M262 303l-108 103h303v-304l-103 109l-149 -160l-103 103zM1024 870l-158 -147l107 -102h-301v301l102 -107l148 158z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1024" d="M819 922q43 0 73 -30t30 -73v-409q0 -41 -30 -72t-73 -31h-409q-41 0 -72 31t-31 72v411q0 41 30 71t73 30h409zM819 410v409h-409v-409h409zM205 512v-307h307v-103h-307q-41 0 -72 31t-31 72v307h103z" />
|
||||||
|
<glyph unicode="" d="M1024 973q43 0 72.5 -31t29.5 -72v-614q0 -43 -29.5 -72.5t-72.5 -29.5h-203v100h205v473h-821v-473h205v-100h-205q-41 0 -72 29.5t-31 72.5v614q0 41 31 72t72 31h819zM236 801q39 0 38 39q0 16 -11 26.5t-27.5 10.5t-27.5 -11.5t-11 -25.5q0 -16 11.5 -27.5 t27.5 -11.5zM338 801q39 0 39 39q0 16 -11.5 26.5t-27.5 10.5t-27.5 -11.5t-11.5 -25.5q0 -16 11.5 -27.5t27.5 -11.5zM1026 807v63h-616v-63h616zM612 604l248 -246h-153v-307h-189v307h-153z" />
|
||||||
|
<glyph unicode="" d="M1024 922q43 0 72.5 -31t29.5 -72v-614q0 -43 -29.5 -73t-72.5 -30h-819q-41 0 -72 30t-31 73v614q0 41 31 72t72 31h819zM338 825q-16 0 -27.5 -11t-11.5 -26q0 -16 11.5 -27t27.5 -11q39 0 39 38q0 16 -11.5 26.5t-27.5 10.5zM197 788q0 -16 11 -27t28 -11q39 0 38 38 q0 16 -11 26.5t-27.5 10.5t-27.5 -11.5t-11 -25.5zM1026 205v471h-821v-471h821zM1026 758v61h-616v-61h616z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="675" d="M338 1024l235 -373h-471zM338 0l-236 375h471z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1146" d="M573.5 983q194.5 0 332.5 -138t138 -333t-138 -333t-332.5 -138t-333 138t-138.5 333t138.5 333t333 138zM573.5 143q151.5 0 260 108.5t108.5 260.5q0 154 -108.5 261.5t-260 107.5t-260 -107.5t-108.5 -261.5q0 -152 108.5 -260.5t260 -108.5zM666 711v-211h114 l-207 -195l-206 195h114v211h185z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1146" d="M1044 512q0 -195 -138 -333t-332.5 -138t-333 138t-138.5 333q0 197 138.5 334t333 137t332.5 -137t138 -334zM205 512q0 -152 108.5 -260.5t260 -108.5t260 108.5t108.5 260.5q0 154 -108.5 261.5t-260 107.5t-260 -107.5t-108.5 -261.5zM770 420h-209v-115l-194 207 l194 209v-117h209v-184z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1146" d="M102 512q0 195 138.5 333t333 138t332.5 -138t138 -333t-138 -333t-332.5 -138t-333 138t-138.5 333zM942 512q0 154 -107.5 261.5t-261.5 107.5q-152 0 -260 -107.5t-108 -261.5q0 -152 108.5 -260.5t259.5 -108.5q154 0 261.5 108.5t107.5 260.5zM377 604h209v117 l194 -209l-194 -207v115h-209v184z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1146" d="M573.5 41q-194.5 0 -333 138t-138.5 333q0 197 138.5 334t333 137t332.5 -137t138 -334q0 -195 -138 -333t-332.5 -138zM573 881q-152 0 -260 -107.5t-108 -261.5q0 -152 108.5 -260.5t259.5 -108.5q154 0 261.5 108.5t107.5 260.5q0 154 -107.5 261.5t-261.5 107.5z M481 315v209h-114l206 197l207 -197h-114v-209h-185z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="798" d="M680 586l-240 -230q-18 -18 -40.5 -18t-41.5 18l-239 230q-16 16 -16.5 41.5t16.5 42.5q39 39 80 0l200 -193l201 193q41 39 80 0q16 -16 16 -42t-16 -42z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="552" d="M350 795q14 16 40 16t42 -16q39 -37 0 -82l-190 -201l190 -199q39 -45 0 -82q-16 -16 -40.5 -16t-41.5 16l-231 242q-16 16 -17 39q0 25 17 41q211 219 231 242z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="552" d="M203 795l231 -242q16 -16 17 -41q0 -23 -17 -39l-231 -242q-16 -16 -41 -16t-41 16q-37 37 0 82l190 199l-190 201q-37 45 0 82q16 16 42 16t40 -16z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="798" d="M680 440q16 -16 16 -41.5t-16 -42.5q-39 -39 -80 0l-201 193l-200 -193q-41 -39 -80 0q-16 16 -16.5 42t16.5 42l239 230q16 16 41 16t41 -16z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="679" d="M516 635q23 27 49 0q27 -23 0 -49l-200 -197q-23 -23 -50 0l-200 197q-27 27 0 49q25 25 51 0l174 -160z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="475" d="M360 338q27 -27 0 -49q-27 -27 -49 0l-196 198q-25 25 0 52l196 198q23 27 49 0q27 -23 0 -49l-159 -176z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="475" d="M115 338l161 174l-161 176q-27 27 0 49q27 27 49 0l196 -198q25 -27 0 -52l-196 -198q-23 -27 -49 0q-27 22 0 49z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="679" d="M166 389q-27 -23 -51 0q-25 25 0 51l200 195q27 27 50 0l200 -195q25 -27 0 -51q-25 -23 -51 0l-174 162z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1089" d="M166 737l379 -364l381 364q23 27 49 0q27 -23 0 -49l-406 -401q-23 -23 -49 0l-405 401q-27 27 0 49q24 25 51 0z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="679" d="M565 133q27 -27 0 -49q-27 -27 -49 0l-401 403q-25 25 0 52l401 403q23 27 49 0q27 -23 0 -49l-366 -381z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="679" d="M115 133l366 379l-366 381q-27 27 0 49q27 27 49 0l401 -403q25 -27 0 -52l-401 -403q-23 -27 -49 0q-27 22 0 49z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1089" d="M926 287l-381 366l-379 -366q-27 -23 -51 0q-25 25 0 51l405 399q27 27 49 0l406 -399q25 -27 0 -51q-26 -23 -49 0z" />
|
||||||
|
<glyph unicode="" d="M1126 614v-256q0 -43 -29.5 -72.5t-72.5 -29.5h-819q-41 0 -72 29.5t-31 72.5v308q0 41 31 71.5t72 30.5h819q43 0 72.5 -30.5t29.5 -71.5v-52zM1024 358v308h-819v-308h819z" />
|
||||||
|
<glyph unicode="" d="M1126 614v-256q0 -43 -29.5 -72.5t-72.5 -29.5h-819q-41 0 -72 29.5t-31 72.5v308q0 41 31 71.5t72 30.5h819q43 0 72.5 -30.5t29.5 -71.5v-52zM1024 358v308h-819v-308h819zM256 410v202h205v-202h-205z" />
|
||||||
|
<glyph unicode="" d="M1126 614v-256q0 -43 -29.5 -72.5t-72.5 -29.5h-819q-41 0 -72 29.5t-31 72.5v308q0 41 31 71.5t72 30.5h819q43 0 72.5 -30.5t29.5 -71.5v-52zM1024 358v308h-819v-308h819zM256 410v202h205v-202h-205zM512 410v202h205v-202h-205z" />
|
||||||
|
<glyph unicode="" d="M1126 614v-256q0 -43 -29.5 -72.5t-72.5 -29.5h-819q-41 0 -72 29.5t-31 72.5v308q0 41 31 71.5t72 30.5h819q43 0 72.5 -30.5t29.5 -71.5v-52zM1024 358v308h-819v-308h819zM256 410v202h205v-202h-205zM512 410v202h205v-202h-205zM768 612h205v-202h-205v202z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1167" d="M647 932q174 0 296 -123t122 -297t-122 -297t-296 -123q-141 0 -258 90l72 78q84 -61 186 -61q129 0 221.5 92t92.5 221q0 131 -92.5 223t-221.5 92q-127 0 -218 -88t-95 -215h145l-188 -211l-189 211h127q4 170 126 289t292 119zM610 737h72v-209l133 -133l-51 -51 l-154 154v239z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1146" d="M332 670q-23 0 -43 -11q-49 53 -94 132q55 76 131 120q94 -39 155 -84q-6 -16 -6 -32q0 -6 4 -23q-63 -49 -119 -106q-16 4 -28 4zM231 569q0 -35 21 -61q-61 -117 -82 -238q-68 109 -68 242q0 113 52 211q39 -63 86 -115q-9 -25 -9 -39zM575 895q-29 0 -51 -14 q-59 43 -116 71q86 31 165 31q123 0 236 -63q-78 -14 -166 -52q-27 27 -68 27zM725 424q-164 25 -297 115q4 20 4 30q0 25 -14 54q39 45 100 92q27 -20 57 -21q14 0 39 8q96 -111 138 -245q-17 -15 -27 -33zM856 289q39 14 55 61q59 4 111 19q-47 -150 -170 -238q6 49 6 100 q0 10 -1 29t-1 29zM702 356q-195 -98 -311 -278q-92 37 -162 112q12 147 82 281q6 -2 21 -2q31 0 53 14q145 -100 317 -127zM891 860q154 -139 153 -348q0 -23 -4 -68q-66 -18 -133 -24q-25 57 -90 61q-49 150 -151 271q10 20 10 43v10q102 43 215 55zM752 303 q14 -10 32 -18q2 -18 2 -54q0 -82 -14 -147q-88 -43 -199 -43q-59 0 -114 12q113 164 293 250z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1230" d="M1094 561q41 -43 30 -74l-28 -157q-4 -20 -22.5 -33.5t-41.5 -13.5h-835q-23 0 -41.5 13t-22.5 34l-29 157q-8 33 33 74q8 10 37 39t70 69t53 52q23 23 53 22h264h265q31 0 53 -22q16 -16 54 -53t69 -67t39 -40zM821 528h183l-105 117h-569l-105 -117h183q8 0 12 -8 l41 -102h307l41 102q4 8 12 8z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1128" d="M1008 467q25 -53 14 -98l-35 -189q-2 -20 -19.5 -35.5t-39.5 -15.5h-729q-23 0 -40.5 15.5t-19.5 35.5l-35 189q-8 51 15 98l162 383q23 47 73 47h107l-21 -209h-137l260 -215l262 215h-139l-18 209h104q51 0 76 -47zM938 332q2 23 -10.5 39t-34.5 16h-660 q-23 0 -35 -16.5t-10 -38.5l15 -76q2 -23 19.5 -38t37.5 -15h606q23 0 40.5 15t19.5 38z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1150" d="M569.5 983q194.5 2 333.5 -133t144 -330q2 -195 -134.5 -335t-330.5 -144q-195 -2 -335.5 134t-142.5 331q-4 195 133.5 335t332 142zM569 922q-96 0 -182 -45l64 -107q57 29 124.5 29t124.5 -29l64 107q-91 47 -195 45zM317 387q-29 61 -28 125q0 66 28 127l-104 63 q-47 -90 -47 -194q2 -98 47 -184zM582 102q100 4 182 48l-64 106q-61 -31 -124.5 -31t-124.5 31l-64 -106q89 -48 195 -48zM575.5 287q94.5 0 160 66.5t65.5 158.5q0 94 -65.5 159.5t-160 65.5t-160 -65.5t-65.5 -159.5q0 -92 65.5 -158.5t160 -66.5zM834 387l106 -63 q47 98 45 194q0 98 -45 184l-106 -63q29 -61 28 -127q1 -64 -28 -125z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="780" d="M666 287q29 -82 -17.5 -161t-142.5 -114q-96 -29 -179 9t-106 120l-108 394q-20 70 6 137t86 108l-99 191q-14 35 15 49q31 18 49 -14l100 -197q82 23 158 -16t104 -119zM334 567q29 10 41 37t4 55q-10 29 -36 42.5t-54 5.5q-29 -10 -41 -37t-4 -56q10 -29 35.5 -42 t54.5 -5z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1189" d="M223 328q39 35 87 29.5t89 -46.5q43 -41 49.5 -89t-30.5 -85q-88 -86 -234 -104q-86 -12 -82 14q0 4 7 10q53 61 65 148.5t49 122.5zM1083 989q27 -27 -151 -254t-299 -346q-39 -39 -127 -106q-8 -6 -17 8q-18 35 -49 65q-33 33 -67 50q-16 6 -8 16q66 86 106 125 q121 119 352 294t260 148z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1044" d="M633 109l16 167l266 -20l-16 -170q-4 -29 -33 -25l-204 17q-29 0 -29 31zM131 256l264 20l17 -167q2 -12 -6.5 -21.5t-22.5 -9.5l-203 -17q-12 -2 -22.5 6.5t-10.5 18.5zM104 532q-2 12 -2 35q0 164 123 280t297 116t297 -116t123 -280q0 -23 -2 -35l-16 -174l-265 23 l17 174v12q0 59 -45 101.5t-108.5 42.5t-108.5 -42t-45 -102v-12l16 -174l-264 -23z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1208" d="M961 768v-51h-713v51q0 23 13 36t26 15h12h610q6 0 14.5 -1t23 -14t14.5 -36zM809 922q6 0 14 -1t22.5 -14.5t14.5 -36.5h-510q0 23 13.5 36.5t25.5 15.5h12h408zM1063 717q35 -33 39 -47q6 -18 0 -56l-78 -460q-4 -23 -20.5 -36.5t-28.5 -15.5h-14h-713q-53 0 -62 52 q-6 27 -39.5 228.5t-40.5 231.5q-10 23 -2.5 45.5t10.5 26.5t21 21l10 10l31 31v-82h856v82zM809 440v103h-72v-82h-266v82h-70v-103q0 -51 50 -51h307q23 0 36 12.5t13 24.5z" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1024" />
|
||||||
|
<glyph unicode="" horiz-adv-x="1126" d="M973 819q20 0 35.5 -15.5t15.5 -35.5v-205h-512v256h461zM102 256v205h308v-256h-267q-41 0 -41 51zM512 205v256h512v-205q0 -23 -14.5 -37t-36.5 -14h-461zM102 768q0 51 41 51h267v-256h-308v205z" />
|
||||||
|
<glyph unicode="🌄" d="M979 684h-102l-127 154l-220 -154h-184q-53 0 -92 -40t-39 -93v-164l-111 303q-10 39 23 53l696 254q37 10 51 -24zM1087 592q16 0 27.5 -12.5t11.5 -28.5v-483q0 -16 -11 -28.5t-28 -12.5h-741q-16 0 -27.5 12t-11.5 29v483q0 16 11.5 28.5t27.5 12.5h741zM1030 129v166 l-74 164l-170 -62l-133 -135l-141 174l-94 -219v-88h612z" />
|
||||||
|
<glyph unicode="🌎" horiz-adv-x="1187" d="M594 1004q205 0 348 -144.5t143 -347.5q0 -205 -143 -348.5t-348 -143.5q-203 0 -347.5 143.5t-144.5 348.5q0 203 144.5 347.5t347.5 144.5zM1014 512q0 135 -80 244.5t-207 152.5q-18 -25 -16 -32q4 -39 18 -52.5t30.5 -7.5t33 12.5t20.5 1.5q23 -25 0.5 -48t-46.5 -57 t-1 -79q35 -66 98 -65q29 -2 44.5 -37t17.5 -68q10 -82 -15 -143q-23 -45 15 -78q88 115 88 256zM537 926q-115 -14 -204 -86t-130 -178q6 0 22.5 -2.5t28.5 -3.5t26.5 -4t24.5 -8t12 -13q4 -12 -14 -46t-18 -63q0 -31 38.5 -57.5t38.5 -46.5q0 -29 8.5 -70t8.5 -45 q0 -12 37 -55t53 -43q10 0 11 22.5t-2 55t-3 41.5q0 33 14 75q12 43 60.5 72t56.5 47q16 35 9 62.5t-17 44t-34.5 29t-42 17.5t-38 9t-22.5 4q-16 6 -43 7t-37 -3t-27.5 11.5t-17.5 29.5q0 10 15.5 27.5t36 38t28.5 30.5q8 14 17 21.5t22.5 16.5t27.5 22q4 4 26 17.5 t28 23.5zM463 113q68 -20 131 -21q131 0 231 70q-27 45 -120 35q-25 -2 -67 -17.5t-48 -17.5q-76 -16 -78 -17q-12 -2 -26.5 -14t-22.5 -18z" />
|
||||||
|
<glyph unicode="🍂" horiz-adv-x="1167" d="M344 815q186 109 518 68q172 -23 201 -52q4 -6 -2 -10q-78 -41 -133.5 -111.5t-80 -135t-66.5 -135t-95 -107.5q-141 -98 -391 -4q-68 -78 -117 -181q-12 -25 -48 -7t-26 40q45 102 132.5 197.5t180.5 157t180 108.5t144 70l56 20q-14 0 -42 -1t-107 -14t-151.5 -39 t-165.5 -86.5t-165 -143.5q-23 247 178 366z" />
|
||||||
|
<glyph unicode="🎓" d="M272 397l342 -172l283 140q-4 -23 -8 -48.5t-6 -36t-11.5 -23.5t-25 -23.5t-45.5 -22.5q-41 -18 -82 -42t-64.5 -35t-40 -11t-41 13.5t-65.5 38t-82 40.5q-74 33 -106 70.5t-48 111.5zM1102 649q25 -14 24.5 -33.5t-24.5 -33.5l-80 -45l-315 104q-23 37 -93 37 q-41 0 -68.5 -16.5t-27.5 -41t27.5 -41t68.5 -16.5q27 0 37 4l299 -69l-274 -156q-61 -33 -123 0l-426 240q-25 14 -25 33.5t25 33.5l426 240q61 33 123 0zM971 197q18 119 13 186.5t-19 91.5l-15 23l72 39q6 -8 12 -29t17.5 -103.5t-7.5 -201.5q-4 -27 -22 -31t-35.5 5.5 t-15.5 19.5z" />
|
||||||
|
<glyph unicode="🎔" horiz-adv-x="1230" d="M1114 467q31 -84 -10 -180.5t-137 -163.5q-10 0 -12.5 2t-16.5 19.5t-16 19.5q-2 6 2 10q88 61 119.5 155.5t-11.5 151.5q-16 -39 -39.5 -77.5t-60.5 -81.5t-88 -67t-109 -15q-53 6 -86 41.5t-33 95.5q0 86 62 151q51 51 117 68l-2 102q-143 -25 -150 -24q-6 -2 -10 4 q0 2 -5 29.5t-5 31.5q-2 2 1 4t7 2l160 29q0 113 -3 117q0 8 9 8q47 0 53 2q10 0 10 -8v-107q162 23 168 23q8 4 10 -6q0 -2 4.5 -23.5t4.5 -25.5q4 -10 -5 -13l-180 -30v-105h12q88 0 151.5 -36.5t88.5 -102.5zM735 303q29 -6 64 6l-4 219q-35 -12 -62 -41 q-45 -45 -45 -110q0 -68 47 -74zM860 332q29 25 59.5 69.5t46 80.5t7.5 42q-37 18 -99 19q-2 0 -6 -1t-6 -1zM401 723q10 -29 54.5 -169t85.5 -267t41 -129q0 -4 -4 -4h-89q-6 0 -6 4l-51 170h-180q-49 -168 -51 -170q0 -4 -6 -4h-89q-4 0 -4 4q10 18 181 565q2 8 10 8h98 q10 0 10 -8zM268 399h148l-74 271z" />
|
||||||
|
<glyph unicode="🎤" horiz-adv-x="860" d="M737 653q20 0 21 -20v-141q0 -94 -71 -168t-206 -86v-136h133q20 0 21 -20v-62q0 -20 -21 -20h-368q-20 0 -21 20v62q0 20 21 20h133v136q-135 12 -206 85.5t-71 168.5v141q0 20 21 20h31q20 0 20 -20v-141q0 -68 60.5 -126.5t195.5 -58.5t195.5 58.5t60.5 126.5v141 q0 20 21 20h30zM430 410q-82 0 -118 25.5t-36 56.5v161h308v-161q0 -31 -36 -56.5t-118 -25.5zM584 942v-217h-308v217q0 31 36 56.5t118 25.5t118 -25.5t36 -56.5z" />
|
||||||
|
<glyph unicode="🎨" horiz-adv-x="1210" d="M981 791q74 -49 103.5 -113t20.5 -107t-36 -49q-16 -4 -55 10.5t-82 10.5t-82 -47q-31 -47 -21.5 -77t35 -67t23.5 -51q-2 -27 -37 -64.5t-129 -75.5t-221 -38q-190 0 -298 103.5t-98 250.5q8 121 106.5 241t221.5 154q297 87 549 -81zM655 313q31 0 53.5 22.5t22.5 55.5 t-22.5 54.5t-53.5 21.5q-33 0 -55 -21.5t-22 -54.5t22 -55.5t55 -22.5z" />
|
||||||
|
<glyph unicode="🎫" horiz-adv-x="1169" d="M324 432l333 334l183 -182l-334 -334zM1051 682q14 -14 14 -36.5t-14 -37.5l-564 -563q-16 -16 -36.5 -16t-36.5 16l-78 78q12 20 12 49q0 43 -29.5 73.5t-72.5 30.5q-23 0 -51 -14l-76 78q-16 16 -16.5 36.5t16.5 37.5l563 563q14 14 36.5 14t37.5 -14l75 -78 q-12 -23 -12 -49q0 -43 31 -72.5t74 -29.5q27 0 49 12zM506 168l416 416l-265 264l-417 -416z" />
|
||||||
|
<glyph unicode="🎬" horiz-adv-x="1208" d="M1106 768h-102v-102h102v-103h-102v-102h102v-103h-102v-102h102v-61q0 -16 -12.5 -28.5t-28.5 -12.5h-922q-16 0 -28.5 12t-12.5 29v61h103v102h-103v103h103v102h-103v103h103v102h-103v61q0 18 12.5 29.5t28.5 11.5h922q16 0 28.5 -11t12.5 -30v-61zM492 358l256 154 l-256 154v-308z" />
|
||||||
|
<glyph unicode="🎯" horiz-adv-x="1085" d="M542.5 952q182.5 0 311.5 -129t129 -311t-129 -311t-311.5 -129t-311.5 129t-129 311t129 311t311.5 129zM580 156q127 14 217 104t102 217h-197v72h197q-12 127 -102 217t-217 104v-198h-72v198q-127 -14 -218 -104t-104 -217h199v-72h-199q12 -127 103.5 -217 t218.5 -104v198h72v-198z" />
|
||||||
|
<glyph unicode="🎵" horiz-adv-x="921" d="M717 973q43 0 72.5 -30t29.5 -73v-716q0 -41 -29.5 -72t-72.5 -31h-512q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h512zM604 485q33 45 23.5 94.5t-34 82t-50 67.5t-25.5 55h-61v-376q-43 16 -92 -2q-43 -14 -66 -49t-12 -66q12 -33 54 -46.5t87 1.5q90 31 90 100v268 q37 -6 56.5 -32.5t18.5 -53t-3 -43.5q-4 -10 2 -10q4 0 12 10z" />
|
||||||
|
<glyph unicode="🏆" horiz-adv-x="1126" d="M625 291v-68q72 -8 119 -32.5t47 -57.5q0 -37 -67 -64.5t-161 -27.5q-92 0 -159.5 27.5t-67.5 64.5q0 33 47 57.5t121 32.5v68q0 51 -34 86t-116 90q-57 37 -89 62.5t-77 73.5t-65.5 110.5t-20.5 140.5q0 14 11.5 24.5t25.5 10.5h176q49 94 248 94q201 0 250 -94h174 q14 0 25.5 -10.5t11.5 -24.5q0 -78 -20.5 -140.5t-65.5 -110.5t-77 -73.5t-89 -62.5q-80 -53 -113.5 -89t-33.5 -87zM766 549q82 57 129 116.5t55 151.5h-129q-6 -162 -55 -268zM563.5 922q-63.5 0 -110.5 -15.5t-65.5 -33t-18.5 -29.5q0 -14 18.5 -31.5t65.5 -33 t110.5 -15.5t110.5 15.5t65.5 32.5t18.5 32q0 12 -18.5 29.5t-65.5 33t-110.5 15.5zM176 817q8 -92 55.5 -151.5t128.5 -116.5q-49 106 -55 268h-129z" />
|
||||||
|
<glyph unicode="👍" horiz-adv-x="1024" d="M698 645q2 -6 59.5 -13t111 -24.5t53.5 -48.5q0 -74 -62.5 -291t-109.5 -217q-147 0 -295 43t-148 90v351q0 14 15.5 34.5t47 46t54 42t63.5 44t48 31.5q51 35 106 102.5t87 106.5t42 27q49 -78 29.5 -140.5t-60.5 -122t-41 -61.5zM256 641q14 0 0 -14q-51 -51 -51 -107 v-325q0 -51 53 -107q10 -10 -2 -10q-27 0 -56.5 8t-63.5 46t-34 101v248q0 63 34 102.5t64.5 48.5t55.5 9z" />
|
||||||
|
<glyph unicode="👎" horiz-adv-x="1024" d="M326 377q-2 6 -58.5 13t-111 24.5t-54.5 48.5q0 74 63.5 292t108.5 218q147 0 295 -44t148 -91v-351q0 -10 -8.5 -24t-25.5 -30.5t-32.5 -30t-43 -33t-42 -29.5t-42 -28.5t-34.5 -22.5q-51 -35 -106 -102.5t-87 -106.5t-42 -27q-49 78 -29.5 140.5t60.5 122t41 61.5z M768 381q-12 0 2 14q49 51 49 107v325q0 51 -53 107q-10 10 2 10q27 0 56.5 -8t63.5 -46t34 -102v-247q0 -49 -18.5 -83t-46.5 -49.5t-49.5 -21.5t-39.5 -6z" />
|
||||||
|
<glyph unicode="👜" horiz-adv-x="1087" d="M958 838q29 -27 25 -62l-100 -663q-8 -31 -39 -31h-600q-29 0 -41 31q-96 635 -99 663q-4 35 23 62q6 6 55 44t58 44q18 16 57 16h491q39 0 58 -16q79 -59 112 -88zM543 391q57 0 100 35t64.5 91t31 91t13.5 68h-95q-39 -193 -114.5 -193t-114.5 193h-94 q47 -285 209 -285zM182 768h721l-112 119h-496z" />
|
||||||
|
<glyph unicode="👤" horiz-adv-x="1167" d="M856 285q209 -74 209 -125v-109h-481h-482v109q0 51 209 125q96 35 131 70.5t35 97.5q0 23 -22.5 50t-32.5 75q-2 12 -9 18t-14.5 8t-14.5 17.5t-9 44.5q0 16 5 26t9 13l4 4q-8 51 -12 90q-4 55 42 114.5t160.5 59.5t162 -59.5t40.5 -114.5l-12 -90q18 -8 19 -43 q-2 -29 -9.5 -44.5t-14.5 -17.5t-14 -8t-10 -18q-8 -49 -31.5 -76t-23.5 -49q0 -61 36 -97t130 -71z" />
|
||||||
|
<glyph unicode="👥" d="M1126 61h-229v154q0 55 -30.5 83t-157.5 91q41 31 41 86q0 16 -13.5 33.5t-19.5 52.5q-2 8 -14.5 16.5t-14.5 43.5q0 25 12 30q-6 35 -8 62q-4 39 23.5 80t97.5 41t98.5 -41t24.5 -80l-8 -62q12 -6 12 -30q-2 -35 -14.5 -43.5t-14.5 -16.5q-6 -35 -19 -52t-13 -34 q0 -43 21.5 -67.5t78.5 -49.5q115 -47 133 -82q6 -8 9 -62t5 -103v-50zM627 330q186 -80 186 -127v-142h-711v189q0 45 86 80q78 33 107 65.5t29 89.5q0 20 -19.5 45t-25.5 70q-2 10 -18.5 22.5t-20.5 57.5q0 14 3 23.5t7 13.5l4 2q-6 47 -10 84q-4 51 33.5 105.5t130 54.5 t130 -54.5t33.5 -105.5l-10 -84q14 -8 14 -39q-4 -45 -20 -57.5t-18 -22.5q-6 -45 -25.5 -69.5t-19.5 -45.5q0 -57 28.5 -89.5t106.5 -65.5z" />
|
||||||
|
<glyph unicode="💡" horiz-adv-x="923" d="M317 41v106h289v-106q-72 -43 -145 -41q-72 -2 -144 41zM600 209h-276q0 74 -37 143.5t-80 115.5t-76 114.5t-27 142.5q8 123 96.5 211t260.5 88q174 0 261 -88t97 -211q4 -61 -16.5 -115.5t-53 -98.5t-66.5 -87t-58.5 -98.5t-24.5 -116.5zM213 717q-4 -4 0 -20.5 t2 -20.5t5 -19.5t6 -18.5t8.5 -18.5t11.5 -19.5t13 -19.5t14 -19.5t15.5 -21.5t16.5 -23.5q90 -125 115 -217h84q25 96 114 217q4 6 26 36t26 37t17 29.5t16.5 34t6.5 28.5t1 36q-16 201 -250 201q-232 0 -248 -201z" />
|
||||||
|
<glyph unicode="💥" horiz-adv-x="1130" d="M1010 393q20 -16 16 -33.5t-29 -23.5l-79 -23q-25 -6 -41.5 -28.5t-14.5 -48.5l4 -84q2 -25 -14 -35.5t-39 0.5l-88 45q-23 12 -48.5 4t-35.5 -31l-47 -90q-12 -23 -29.5 -24t-34.5 20l-51 80q-35 49 -90 20l-125 -71q-23 -14 -33 -6t-2 32l56 168q8 25 -4.5 45.5 t-36.5 22.5l-109 12q-25 4 -30 18.5t16 30.5l88 78q20 16 20 42t-20 42l-88 78q-20 16 -16 33.5t28 23.5l80 23q25 6 42 28.5t15 49.5l-6 83q0 27 15.5 37.5t37.5 -0.5l82 -39q25 -10 50.5 -1.5t37.5 30.5l48 82q12 23 30.5 22t30.5 -24l51 -88q12 -23 36 -30t46 7l139 86 q23 14 31 6t0 -32l-61 -174q-10 -23 2 -42.5t39 -21.5l116 -12q27 -2 31 -16.5t-16 -30.5l-88 -78q-18 -16 -18.5 -42t18.5 -42zM616 299v107h-102v-107h102zM616 463v266h-102v-266h102z" />
|
||||||
|
<glyph unicode="💦" horiz-adv-x="1167" d="M274 1018q10 -88 51.5 -159t75 -126t33.5 -115q0 -68 -49 -115.5t-116.5 -47.5t-117 48t-49.5 115q0 59 34 114.5t75 126.5t51 159q2 4 7 4t5 -4zM905 1018q10 -88 51 -159t75 -126t34 -115q0 -68 -49 -115.5t-117 -47.5t-117 48t-49 115q0 49 21.5 95.5t49 80t54.5 94 t35 130.5q2 4 7 4t5 -4zM578 563q2 4 7 4t5 -4q10 -88 51 -158.5t75 -126t34 -114.5q0 -68 -49.5 -116t-117 -48t-116.5 48t-49 116q0 59 33.5 114.5t75 126t51.5 158.5z" />
|
||||||
|
<glyph unicode="💧" horiz-adv-x="778" d="M399 995q14 -121 61.5 -224t94.5 -162.5t84 -139.5t37 -164q0 -117 -85 -201t-202 -84t-202 84t-85 201q0 84 37 164t84 139.5t94.5 162.5t61.5 224q2 8 11 8t9 -8zM356 594q2 4 -2 14q-6 6 -14 6t-12 -6l-41 -59q-33 -47 -49.5 -71.5t-35 -77t-18.5 -103.5 q0 -25 17.5 -42t42.5 -17q59 0 59 69q0 96 43 252q2 6 5 17.5t5 17.5z" />
|
||||||
|
<glyph unicode="💨" horiz-adv-x="1130" d="M188 700q-16 -14 -36.5 -12t-34.5 19q-14 14 -12.5 36.5t18.5 36.5q49 41 81 61.5t91 41t132 4t163 -67.5t158.5 -54t102.5 16.5t91 68.5q39 31 72 -6q33 -41 -6 -74q-125 -113 -240 -113q-102 0 -227 72q-70 39 -122 53.5t-95 0t-67 -30t-69 -52.5zM942 588q39 33 72 -6 q33 -41 -6 -74q-41 -35 -67 -54.5t-74 -39t-99 -19.5q-98 0 -227 72q-70 39 -122 53.5t-95 0t-67 -30t-69 -52.5q-14 -14 -35.5 -12t-35.5 18q-33 41 6 74q39 35 61.5 51.5t70.5 39t90 23.5t107.5 -15.5t137.5 -57.5q70 -39 122 -53t95 0t66.5 29.5t68.5 52.5zM942 326 q39 33 72 -7q14 -14 12 -36.5t-18 -36.5q-41 -35 -67 -54.5t-74 -39t-99 -19.5q-98 0 -227 72q-70 39 -122 53t-95 1t-68 -29.5t-68 -53.5q-14 -14 -35.5 -12t-35.5 18q-33 41 6 74q39 35 61.5 51.5t70.5 39t90 23.5t107.5 -15.5t137.5 -57.5q70 -39 122 -53.5t95 0t66.5 30 t68.5 52.5z" />
|
||||||
|
<glyph unicode="💳" d="M1024 870q43 0 72.5 -30.5t29.5 -71.5v-512q0 -43 -29.5 -72.5t-72.5 -29.5h-819q-41 0 -72 29.5t-31 72.5v512q0 41 31 71.5t72 30.5h819zM1024 256v307h-819v-307h819zM1024 717v51h-819v-51h819zM307 455h31v-31h-31v31zM492 393h30v31h31v31h61v-31h-30v-31h-31v-31 h-61v31zM614 362h-30v31h30v-31zM461 362h-62v31h62v-31zM492 424v-31h-31v62h61v-31h-30zM369 393v-31h-62v31h31v31h31v31h61v-31h-31v-31h-30z" />
|
||||||
|
<glyph unicode="💻" d="M1024 963q43 0 72.5 -31t29.5 -72v-563q0 -43 -29.5 -79t-70.5 -44l-223 -45l88 -39q51 -29 -21 -29h-512q-100 0 33 54l37 14l-225 45q-41 8 -71 44t-30 79v563q0 41 31 72t72 31h819zM1024 301v569h-819v-569h819z" />
|
||||||
|
<glyph unicode="💼" d="M569 487v-102h-467q8 231 11 299q4 111 102 111h164q16 27 37.5 68.5t23.5 45.5q14 27 23.5 33t38.5 6h227q27 0 37 -7t22 -32q18 -33 62 -114h164q98 0 102 -111l10 -299h-464v102h-93zM494 850l-29 -55h299l-29 55q-14 27 -43 27h-155q-29 0 -43 -27zM662 231v103h440 q-6 -90 -10 -170q-6 -86 -93 -86h-768q-92 0 -92 86l-10 170h440v-103h93z" />
|
||||||
|
<glyph unicode="💾" horiz-adv-x="1024" d="M776 922l146 -160v-557q0 -41 -30 -72t-73 -31h-614q-41 0 -72 31t-31 72v614q0 43 31 73t72 30h571zM717 614v256h-410v-256q0 -20 15.5 -35.5t35.5 -15.5h308q20 0 35.5 15.5t15.5 35.5zM666 819v-205h-103v205h103z" />
|
||||||
|
<glyph unicode="💿" horiz-adv-x="1146" d="M573.5 983q194.5 0 332.5 -138t138 -333t-138 -333t-332.5 -138t-333 138t-138.5 333t138.5 333t333 138zM573.5 358q63.5 0 108.5 45.5t45 108.5q0 66 -44 110t-110 44q-63 0 -108 -45.5t-45 -108.5t45 -108.5t108.5 -45.5z" />
|
||||||
|
<glyph unicode="📁" horiz-adv-x="1232" d="M1081 666q33 0 41 -12.5t6 -37.5l-43 -462q-2 -25 -12 -38.5t-43 -13.5h-825q-53 0 -58 52l-43 462q-2 25 6.5 37.5t41.5 12.5h929zM1047 778l10 -41h-867l15 135q4 20 20.5 35t36.5 15h168q53 0 88 -35l31 -31q33 -37 88 -37h348q20 0 39 -12.5t23 -28.5z" />
|
||||||
|
<glyph unicode="📄" horiz-adv-x="921" d="M319 469v92h287v-92h-287zM717 973q43 0 72.5 -30t29.5 -73v-716q0 -41 -29.5 -72t-72.5 -31h-512q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h512zM717 154v716h-512v-716h512zM604 760v-90h-287v90h287zM604 358v-90h-287v90h287z" />
|
||||||
|
<glyph unicode="📅" horiz-adv-x="1126" d="M922 870q43 0 72.5 -29.5t29.5 -72.5v-614q0 -41 -29.5 -72t-72.5 -31h-717q-41 0 -72 31t-31 72v614q0 43 31 72.5t72 29.5h47v-102h164v102h297v-102h164v102h45zM922 154v409h-717v-409h717zM369 973v-174h-72v174h72zM829 973v-174h-71v174h71z" />
|
||||||
|
<glyph unicode="📈" horiz-adv-x="1230" d="M137 444q-43 10 -33 58q10 43 56 33l100 -25l-53 -82zM1049 432q14 12 33.5 11t31.5 -15q33 -33 -2 -66l-258 -231q-12 -12 -31 -12q-14 0 -28 10l-293 225l-56 15l52 82l37 -9q12 -4 16 -8l270 -209zM547 657l-359 -563q-12 -23 -38 -22q-12 0 -25 8q-16 10 -20.5 29.5 t6.5 33.5l383 602q8 16 28 21q18 6 37 -6l252 -160l231 334q10 16 29 19t35 -9q39 -25 12 -63l-258 -371q-25 -37 -63 -12z" />
|
||||||
|
<glyph unicode="📊" horiz-adv-x="1024" d="M870 973q23 0 37.5 -15.5t14.5 -35.5v-871h-205v871q0 51 41 51h112zM563 666q23 0 37 -15.5t14 -36.5v-563h-204v563q0 51 41 52h112zM256 358q23 0 37 -15t14 -36v-256h-205v256q0 51 41 51h113z" />
|
||||||
|
<glyph unicode="📋" horiz-adv-x="921" d="M748 922q29 0 50 -21.5t21 -50.5v-778q0 -31 -21.5 -51.5t-49.5 -20.5h-574q-29 0 -50.5 20.5t-21.5 51.5v778q0 29 21.5 50.5t50.5 21.5l62 -154h450zM645 819h-369l-45 103h111l37 102h164l37 -102h112z" />
|
||||||
|
<glyph unicode="📎" horiz-adv-x="1165" d="M352 10q-104 0 -174 74q-74 72 -76 170t86 195l508 507q82 82 178 56q45 -12 81 -48t49 -81q27 -98 -56 -181l-485 -485q-41 -41 -90 -47q-49 -4 -82 29q-31 25 -28 76t48 94l340 342q25 27 51.5 0t-0.5 -52l-340 -340q-45 -45 -20 -71q12 -8 25 -6q25 4 47 26l485 486 q51 51 35 110q-16 61 -78 78q-55 14 -111 -37l-505 -506q-68 -78 -66 -146.5t53 -119.5q51 -49 120 -51t145 63l507 506q25 25 52 0q27 -23 0 -49l-508 -508q-85 -84 -191 -84z" />
|
||||||
|
<glyph unicode="📑" horiz-adv-x="768" d="M614 1024q20 0 36 -15.5t16 -35.5v-871l-154 185v635q0 20 -15.5 35.5t-35.5 15.5h-103q0 51 41 51h215zM358 870q20 0 36 -15t16 -36v-819l-154 184l-154 -184v819q0 51 41 51h215z" />
|
||||||
|
<glyph unicode="📕" horiz-adv-x="921" d="M801 762q18 -8 18 -29v-575q0 -14 -12 -25.5t-29 -11.5q-47 0 -47 37v534q0 12 -12 19l-414 221q-33 10 -69 -10q-45 -20 -58 -45l418 -234q18 -8 18 -29v-563q0 -23 -18 -28q-6 -4 -16 -5q-14 0 -21 5q-8 6 -206.5 129.5t-217.5 134.5q-27 18 -26 35l-7 536q0 29 15 53 q29 47 104.5 79t118.5 9z" />
|
||||||
|
<glyph unicode="📖" horiz-adv-x="1126" d="M451 397v-69l-205 82v69zM451 610v-69l-205 82v69zM1001 965q23 -12 23 -43v-656q0 -35 -33 -47l-407 -164q-8 -2 -10.5 -2t-5.5 -1t-5 -1t-5 1t-5 1l-10 2l-408 164q-33 12 -33 47v656q0 31 23 43q23 16 47 6l391 -158l391 158q24 10 47 -6zM512 158v573l-328 131v-573z M942 289v573l-328 -131v-573zM881 479v-69l-205 -82v69zM881 692v-69l-205 -82v69z" />
|
||||||
|
<glyph unicode="📞" horiz-adv-x="1024" d="M575 451q166 166 121 211l-8 8q-31 31 -42 49t-4 55t50 90q20 25 38 40.5t36 16.5t30.5 1t30 -13.5t24.5 -18.5t26.5 -25.5t21.5 -22.5q49 -49 -6 -198.5t-209 -301.5q-154 -154 -302.5 -210t-197.5 -7q-2 2 -23.5 22.5t-25.5 25.5t-18.5 24.5t-13.5 32t2 31t15.5 35.5 t39.5 38q43 35 71.5 48t55 2t36 -18.5t39.5 -37.5q45 -45 213 123z" />
|
||||||
|
<glyph unicode="📣" horiz-adv-x="1087" d="M913 666q59 -141 68.5 -264t-39.5 -144q-29 -12 -62.5 3t-66.5 41t-101.5 42t-152.5 8q-29 -4 -43 -19.5t-6 -37.5q23 -57 47 -111q4 -10 24.5 -22t24.5 -21q14 -35 -22 -47q-51 -23 -105 -41q-31 -10 -55 43q-33 78 -59 135q-6 12 -35 17.5t-47 32.5q-31 -10 -39 -15 q-35 -12 -76 12.5t-55 61.5q-16 33 -5 81t44 62q129 53 218 110.5t127 106t60.5 94.5t25.5 79.5t15 60.5t37 37q49 20 133 -72t145 -233zM885 358q8 4 10 39t-11.5 100.5t-41.5 131.5q-29 68 -69 126t-68.5 85.5t-37 23.5t-10.5 -43t10.5 -107.5t41 -136t69.5 -122t70 -78 t37 -19.5z" />
|
||||||
|
<glyph unicode="📤" horiz-adv-x="1230" d="M614 948l267 -250h-168v-262h-195v262h-170zM1094 356q18 -10 27 -32.5t3 -40.5l-28 -158q-4 -20 -22.5 -33.5t-41.5 -13.5h-835q-23 0 -41.5 13t-22.5 34l-29 158q-10 49 33 73l162 111h100l-174 -133h183q8 0 12 -8l41 -113h307l41 113q8 8 12 8h183l-175 133h101z" />
|
||||||
|
<glyph unicode="📥" horiz-adv-x="1230" d="M1094 356q18 -10 27 -32.5t3 -40.5l-28 -158q-4 -20 -22.5 -33.5t-41.5 -13.5h-835q-23 0 -41.5 13t-22.5 34l-29 158q-10 49 33 73l162 111h100l-174 -133h183q8 0 12 -8l41 -113h307l41 113q8 8 12 8h183l-175 133h101zM881 686l-267 -250l-266 250h170v262h195v-262 h168z" />
|
||||||
|
<glyph unicode="📦" horiz-adv-x="1126" d="M993 922q12 0 21.5 -9.5t9.5 -21.5v-123h-922v123q0 12 9.5 21.5t21.5 9.5h860zM154 174v543h819v-543q0 -31 -21.5 -51.5t-50.5 -20.5h-676q-29 0 -50 20.5t-21 51.5zM410 614v-102h307v102h-307z" />
|
||||||
|
<glyph unicode="📰" horiz-adv-x="1024" d="M819 973q43 0 73 -30t30 -73v-716q0 -41 -30 -72t-73 -31h-614q-41 0 -72 31t-31 72v716q0 43 31 73t72 30h614zM819 154v716h-614v-716h614zM563 410v-52h-256v52h256zM717 614v-51h-205v51h205zM512 666v102h205v-102h-205zM461 768v-205h-154v205h154zM410 512v-51 h-103v51h103zM461 461v51h256v-51h-256zM717 307v-51h-410v51h410zM614 358v52h103v-52h-103z" />
|
||||||
|
<glyph unicode="📱" horiz-adv-x="798" d="M594 1014q43 0 72.5 -30t29.5 -73v-798q0 -41 -29.5 -72t-72.5 -31h-389q-41 0 -72 31t-31 72v798q0 43 31 73t72 30h389zM399 51q31 0 51.5 15.5t20.5 35.5q0 23 -20.5 37.5t-51.5 14.5q-29 0 -50 -15.5t-21 -36t21 -36t50 -15.5zM614 205v676h-430v-676h430z" />
|
||||||
|
<glyph unicode="📶" horiz-adv-x="1208" d="M604 307q43 0 73 -30.5t30 -71.5q0 -43 -30 -73t-73 -30q-41 0 -70.5 30t-29.5 73q0 41 29.5 71.5t70.5 30.5zM389 422q90 90 215 90t215 -90l-71 -74q-59 59 -143.5 59.5t-143.5 -59.5zM246 565q150 152 359.5 152t357.5 -152l-72 -71q-119 121 -286 120.5t-288 -120.5z M102 711q209 211 503 211t501 -211l-72 -72q-178 180 -430 180t-430 -180z" />
|
||||||
|
<glyph unicode="📷" d="M614 614q66 0 110 -45t44 -108.5t-45 -108.5t-108.5 -45t-108.5 45t-45 108.5t45 108.5t108 45zM1024 768q43 0 72.5 -29.5t29.5 -72.5v-461q0 -41 -29.5 -72t-72.5 -31h-819q-41 0 -72 31t-31 72v461q0 43 31 72.5t72 29.5h123q29 0 41 31l30 94q10 29 41 29h348 q31 0 41 -29l31 -94q12 -31 41 -31h123zM614.5 205q106.5 0 181 74.5t74.5 181t-74.5 181.5t-181 75t-181.5 -75t-75 -181.5t75 -181t181.5 -74.5zM989.5 594q14.5 0 24.5 11t10 25.5t-10 25t-25 10.5q-37 0 -37 -35q0 -16 11.5 -26.5t26 -10.5z" />
|
||||||
|
<glyph unicode="📸" horiz-adv-x="921" d="M805 367q14 20 14 -2v-103q0 -76 -106.5 -138.5t-251.5 -62.5q-143 0 -251 62.5t-108 138.5v103q0 8 4.5 10t10.5 -8q33 -53 128 -88t216 -35t216 35t128 88zM807 627q8 16 12 0v-119q0 -70 -104.5 -117t-253.5 -47q-147 0 -253 47t-106 117v119q0 20 15 0 q31 -47 127 -77t217 -30t217 30t129 77zM460.5 963q147.5 0 253 -40t105.5 -96v-65q0 -59 -105.5 -101.5t-253 -42.5t-253 42t-105.5 102v65q0 55 105.5 95.5t253 40.5z" />
|
||||||
|
<glyph unicode="📽" horiz-adv-x="1128" d="M1008 465q27 -45 14 -98l-35 -189q-2 -20 -19.5 -35.5t-39.5 -15.5h-729q-20 0 -39 15.5t-21 35.5l-35 189q-8 53 15 98l162 383q23 47 73 47h418q51 0 76 -47zM938 330q2 23 -10.5 39t-34.5 16h-660q-23 0 -35 -16.5t-10 -38.5l15 -76q2 -23 19.5 -38t39.5 -15h604 q23 0 40.5 15t19.5 38z" />
|
||||||
|
<glyph unicode="📾" horiz-adv-x="1142" d="M639 952q178 0 293 -50t106 -107q-6 -39 -49 -314.5t-45 -287.5q-2 -18 -38 -45t-109.5 -51.5t-157.5 -24.5t-156.5 24.5t-108.5 51t-38 45.5q0 2 -4 30q84 -6 167 36t142 120q29 0 49.5 20.5t20.5 51.5q0 29 -20.5 50t-51.5 21q-29 0 -50.5 -21.5t-21.5 -49.5 q0 -20 11 -37q-49 -59 -118 -91t-134 -28q-104 10 -160.5 58t-61.5 112q-8 125 160 188q-18 96 -22 142q-8 57 106.5 107t290.5 50zM176 471q4 -33 38 -60.5t93 -39.5l-33 209q-102 -46 -98 -109zM639 688q84 0 161 18.5t115.5 40t38.5 36t-38.5 36t-114.5 40t-162 18.5 q-84 0 -160 -18.5t-114.5 -40t-38.5 -36t38.5 -36t114.5 -40t160 -18.5z" />
|
||||||
|
<glyph unicode="📿" horiz-adv-x="757" d="M512 518q66 -37 104.5 -100.5t38.5 -141.5q0 -115 -80.5 -195.5t-195.5 -80.5t-196 81t-81 195q0 78 39 141.5t105 100.5v455q0 51 41 51h174q20 0 35.5 -15.5t15.5 -35.5v-455zM379 102q72 0 123 51.5t51 122.5q0 57 -33 102.5t-86 61.5v379h-102v-377q-55 -16 -91 -62 t-36 -104q0 -72 51 -123t123 -51z" />
|
||||||
|
<glyph unicode="🔀" d="M874 682q-55 0 -107 -33t-82 -67.5t-85 -106.5q-49 -63 -76.5 -96t-80 -79t-110 -67.5t-124.5 -21.5h-107v143h107q55 0 108.5 33t83 67.5t84.5 106.5q63 84 103.5 129t119 90t166.5 45h37v123l215 -184l-215 -184v102h-37zM379 592q-76 80 -170 80h-107v143h107 q143 0 260 -110q-14 -16 -37.5 -46t-28.5 -34q-8 -13 -24 -33zM911 344v102l215 -184l-215 -184v123h-37q-143 0 -266 118q47 59 74 95q0 2 6 9t8 11q86 -90 178 -90h37z" />
|
||||||
|
<glyph unicode="🔁" horiz-adv-x="1126" d="M922 707q43 0 72.5 -30t29.5 -73v-297q0 -41 -29.5 -71.5t-72.5 -30.5h-717q-41 0 -72 30.5t-31 71.5v297q0 43 31 73t72 30h256v112l205 -184l-205 -184v112h-215v-215h635v215h-154v144h195z" />
|
||||||
|
<glyph unicode="🔄" horiz-adv-x="1048" d="M295 297l119 119v-299l-283 16l90 88q-119 125 -117 297t123 295q102 102 246 119l4 -105q-102 -16 -176 -90q-90 -90 -92 -218t86 -222zM635 909l283 -16l-91 -88q119 -125 117 -297t-123 -295q-98 -100 -246 -121l-2 107q100 16 175 90q90 90 92 218t-86 222l-117 -119 z" />
|
||||||
|
<glyph unicode="🔅" horiz-adv-x="921" d="M461 676q70 0 117 -48t47 -116q0 -70 -47.5 -117t-116.5 -47q-68 0 -116 47t-48 117q0 68 48 116t116 48zM461 406q45 0 74.5 30.5t29.5 75.5q0 43 -29.5 73.5t-74.5 30.5q-43 0 -74 -30.5t-31 -73.5q0 -45 31 -75.5t74 -30.5zM153.5 553q20.5 0 36 -12.5t15.5 -28.5 q0 -41 -51.5 -41t-51.5 41q0 16 15.5 28.5t36 12.5zM712.5 762q28.5 -29 -7.5 -66q-14 -14 -34 -16t-30 10q-12 12 -10 31.5t16 34.5q37 35 65.5 6zM768 553q20 0 35.5 -12.5t15.5 -28.5q0 -41 -51 -41q-49 0 -49 41q0 16 14.5 28.5t34.5 12.5zM460.5 256q16.5 0 29 -15.5 t12.5 -36t-12.5 -35.5t-29 -15t-28.5 15t-12 35.5t12 36t28.5 15.5zM217 319.5q37 36.5 65.5 8t-8.5 -65.5q-14 -14 -33.5 -16t-29.5 8q-31 29 6 65.5zM207 759.5q29 28.5 65 -7.5q14 -14 16.5 -34t-7.5 -30q-31 -29 -66 6q-37 37 -8 65.5zM649 264q-35 37 -6 65.5t66 -7.5 q14 -14 16 -34t-10 -30q-31 -29 -66 6zM460.5 768q-16.5 0 -28.5 15.5t-12 36t12 35.5t28.5 15t29 -15t12.5 -35.5t-12.5 -36t-29 -15.5z" />
|
||||||
|
<glyph unicode="🔆" d="M1075 553q20 0 35.5 -12.5t15.5 -28.5q0 -41 -51 -41h-49q-51 0 -51 41q0 16 15.5 28.5t35.5 12.5h49zM614.5 793q116.5 0 199.5 -82t83 -199q0 -119 -83 -201t-199.5 -82t-198.5 82t-82 201q0 117 82 199t198.5 82zM614 307q84 0 144.5 59.5t60.5 145.5 q0 84 -60.5 144.5t-144.5 60.5t-144 -60.5t-60 -144.5q0 -86 60 -145.5t144 -59.5zM256 512q0 -41 -51 -41h-51q-51 0 -52 41q0 16 15.5 28.5t36.5 12.5h51q20 0 35.5 -12.5t15.5 -28.5zM614.5 870q-16.5 0 -29 15.5t-12.5 36.5v51q0 20 12.5 35.5t29 15.5t28.5 -15.5 t12 -35.5v-51q0 -20 -12 -36t-28.5 -16zM614.5 154q16.5 0 28.5 -15.5t12 -36.5v-51q0 -20 -12 -35.5t-28.5 -15.5t-29 15.5t-12.5 35.5v51q0 20 12.5 36t29 16zM991 829l-35 -34q-35 -35 -65 -9q-29 29 8 66q4 6 35 37q37 35 65.5 6t-8.5 -66zM274 227q14 16 34 18.5 t30 -9.5q12 -12 10 -32t-16 -34l-37 -37q-14 -14 -33.5 -16t-30.5 10q-31 29 7 66q5 3 36 34zM295 889l37 -37q37 -37 6 -66q-10 -10 -29.5 -8t-34.5 17q-31 31 -36 34q-14 14 -16.5 34t9.5 32q10 12 30 10t34 -16zM899 170q-37 37 -8 65.5t65 -8.5l35 -34q37 -37 8.5 -66 t-65.5 6q-31 31 -35 37z" />
|
||||||
|
<glyph unicode="🔇" horiz-adv-x="1110" d="M991 950q16 -16 16.5 -36.5t-16.5 -36.5l-801 -801q-18 -14 -34 -15q-18 0 -37 15q-16 14 -16.5 36.5t16.5 37.5l801 800q34 33 71 0zM770 555l51 51q76 -94 103.5 -176t-6.5 -119q-25 -25 -77.5 -58.5t-134 -72.5t-164.5 -46t-169 24l284 282q45 -33 90.5 -55.5 t69 -25.5t33.5 1q6 10 2 35t-26.5 70t-55.5 90zM487 618l-276 -276q-41 135 28.5 289.5t135.5 220.5q35 33 107.5 11.5t162.5 -87.5l-53 -51q-59 39 -107.5 54.5t-58.5 4.5q-4 -8 -2 -28.5t19 -59.5t44 -78z" />
|
||||||
|
<glyph unicode="🔊" horiz-adv-x="1138" d="M283 756q43 43 152.5 -5.5t222 -161t160.5 -222t5 -152.5q-29 -29 -94 -69t-159.5 -79.5t-199 -29.5t-180 86t-86 180.5t29.5 198.5t80 159.5t69 94.5zM758 264q8 10 -3.5 50t-50 103.5t-98.5 121.5q-57 59 -120.5 98t-103.5 50t-50 3q-8 -10 3 -50t50 -103.5t96 -122.5 q59 -57 123 -96t104 -50.5t50 -3.5zM764 668q-18 0 -35 16q-16 14 -16 35.5t16 36.5l96 98q37 33 74 0q33 -37 0 -74l-98 -96q-17 -16 -37 -16zM580 795q-18 10 -23.5 30.5t4.5 38.5l55 99q27 45 70 20q18 -10 23.5 -30.5t-4.5 -39.5l-56 -98q-14 -27 -43 -27q-14 1 -26 7z M1028 641q10 -18 4 -38.5t-24 -31.5l-99 -55q-16 -8 -24 -8q-29 0 -45 27q-10 18 -4 38.5t24 30.5l98 55q18 10 39 5t31 -23z" />
|
||||||
|
<glyph unicode="🔋" d="M891 512q0 -100 37 -160.5t80 -60.5h67q-31 -47 -65.5 -66.5t-120.5 -19.5h-512q-133 0 -204 96t-71 211q0 113 71 210t204 97h512q86 0 120.5 -19.5t65.5 -66.5h-67q-43 0 -80 -61.5t-37 -159.5zM752 420q10 12 -9 26q-139 137 -182 168q-16 10 -26.5 13.5t-18.5 -5 t-10 -12.5t-8 -18l-23 -57l-151 67q-27 12 -35 0q-8 -14 8 -29q139 -135 184 -165q35 -16 43 -11.5t19 31.5l24 59l150 -69q27 -12 35 2zM1069 616q23 0 40 -27.5t17 -72.5t-17 -73.5t-40 -28.5h-39q-23 0 -39 28.5t-16 73.5t16.5 72.5t38.5 27.5h39z" />
|
||||||
|
<glyph unicode="🔍" horiz-adv-x="1013" d="M893 233q31 -35 6 -63l-47 -47q-37 -33 -70 0l-194 194q-76 -43 -160 -43q-131 0 -228.5 97.5t-97.5 228.5t92.5 224.5t223.5 93.5t229 -97.5t98 -228.5q0 -90 -47 -166zM199 600q0 -90 69.5 -159.5t159.5 -69.5t154.5 64.5t64.5 156.5q0 90 -69.5 158.5t-159.5 68.5 t-154.5 -64.5t-64.5 -154.5z" />
|
||||||
|
<glyph unicode="🔑" horiz-adv-x="1003" d="M895 780q20 -119 -29 -220t-153 -120q-68 -12 -133 -2l-121 -198l-72 -13l-106 -170q-14 -29 -48 -32l-77 -15q-12 -4 -22.5 4.5t-12.5 22.5l-17 100q-8 31 13 58l264 395q-25 51 -39 123q-18 109 54.5 191.5t189.5 103.5q109 20 200 -46.5t109 -181.5zM766 702 q31 45 21.5 99.5t-52.5 85.5q-43 33 -94 22.5t-82 -55.5q-8 -12 -12 -23.5t-1 -20.5t5 -16.5t13 -17.5t18.5 -15t23 -16.5t23.5 -17.5q6 -4 22.5 -16.5t23.5 -16.5t19.5 -12t19.5 -8t17 1t18.5 8t16.5 19z" />
|
||||||
|
<glyph unicode="🔒" horiz-adv-x="921" d="M758 641q20 0 40.5 -19.5t20.5 -41.5v-400q0 -49 -49 -67l-61 -19q-43 -16 -99 -16h-297q-57 0 -100 16l-61 19q-49 18 -50 67v400q0 23 15.5 42t36.5 19h102v72q0 113 52 174t152.5 61t153 -61.5t52.5 -173.5v-72h92zM358 733v-92h205v92q0 53 -27.5 83t-74.5 30 t-75 -30t-28 -83z" />
|
||||||
|
<glyph unicode="🔓" horiz-adv-x="921" d="M758 614q20 0 40.5 -20.5t20.5 -40.5v-399q0 -20 -14 -40t-35 -26l-61 -20q-53 -16 -99 -17h-297q-47 0 -100 17l-61 20q-20 6 -35 25.5t-15 40.5v399q0 23 15.5 42t36.5 19h409v144q0 113 -102.5 112.5t-102.5 -112.5v-41h-102v20q0 113 52 174.5t153 61.5 q205 0 205 -236v-123h92z" />
|
||||||
|
<glyph unicode="🔔" horiz-adv-x="1024" d="M750 590q16 -35 40.5 -53.5t46 -22.5t45 -23.5t36.5 -56.5q23 -63 -76 -164.5t-258 -160.5q-168 -59 -304.5 -46t-158.5 76q-20 55 12.5 113.5t18.5 114.5q-57 197 -48 307t115 196q27 23 30 52.5t30 40.5q25 8 47 -12.5t57 -18.5q135 2 203 -67.5t164 -274.5zM559 176 q90 33 163 87t102.5 93t25.5 52q-8 23 -50 34t-127 0.5t-192 -49.5q-104 -39 -177 -89t-96.5 -86t-17.5 -54q4 -12 51.5 -22.5t137.5 -4.5t180 39zM496 354q8 2 21 7.5t18 7.5l2 -2q14 -41 -18 -85t-91 -65q-98 -37 -156 14q81 70 224 123z" />
|
||||||
|
<glyph unicode="🔖" horiz-adv-x="573" d="M420 973q23 0 37 -15.5t14 -35.5v-871l-184 185l-185 -185v871q0 51 41 51h277z" />
|
||||||
|
<glyph unicode="🔗" horiz-adv-x="1024" d="M403 272q14 14 35 14.5t37 -14.5q33 -35 0 -71l-43 -41q-57 -57 -135 -58q-80 0 -137.5 57.5t-57.5 135.5q0 80 58 137l151 152q72 70 148 79t131 -45q16 -16 16 -36.5t-16 -36.5q-37 -33 -72 0q-51 49 -135 -35l-152 -150q-27 -27 -26.5 -65.5t26.5 -63.5 q27 -27 65 -26.5t64 26.5zM864 860q57 -57 58 -135q0 -80 -58 -137l-162 -162q-76 -74 -153 -74q-63 0 -115 51q-14 14 -14 35t14 37q14 14 36 14t36 -14q51 -49 125 25l162 159q29 29 28 66q0 39 -28 63q-25 27 -58 32t-61 -21l-51 -51q-16 -14 -37 -14.5t-35 14.5 q-35 35 0 71l51 51q55 55 130 52t132 -62z" />
|
||||||
|
<glyph unicode="🔙" horiz-adv-x="1075" d="M870 707q41 0 72 -30t31 -73v-297q0 -41 -31 -71.5t-72 -30.5h-706v143h665v215h-512v-112l-215 184l215 184v-112h553z" />
|
||||||
|
<glyph unicode="🔦" horiz-adv-x="1128" d="M929.5 876.5q63.5 -63.5 87 -133t-4.5 -94.5l-138 -137q-16 -16 -63 -26.5t-98 -4.5l-418 -418q-18 -18 -58 -5.5t-77 51.5q-37 37 -50.5 75.5t5.5 57.5l417 417q-6 51 4.5 98.5t26.5 63.5l139 140q25 29 94.5 4t133 -88.5zM471 459q33 -33 82 14q47 47 14 84 q-14 14 -38.5 10t-45 -24.5t-23.5 -44t11 -39.5zM815 764q31 -31 70 -51.5t63.5 -25.5t28.5 -1q2 4 -4 27.5t-26.5 61.5t-51.5 68.5t-67.5 51t-60.5 27t-28 2.5t1 -29t25.5 -63.5t49.5 -67.5z" />
|
||||||
|
<glyph unicode="🔾" horiz-adv-x="1208" d="M1090 903q16 23 16 -4v-786h-987q-12 0 -15.5 7t5.5 17l235 295q20 23 41 2l76 -67q10 -8 21.5 -7.5t17.5 11.5l162 243q16 27 38 4l115 -106q20 -20 39 4z" />
|
||||||
|
<glyph unicode="🔿" horiz-adv-x="1150" d="M1020 891q14 4 22.5 -3t4.5 -20q-2 -6 -74 -317t-76 -324q-2 -14 -14.5 -19t-24.5 1l-254 137l-31 16l23 27q397 430 403 436q4 4 -1 9.5t-9 1.5l-563 -412l-115 45l-194 78q-12 4 -12.5 12t12.5 12q8 4 451.5 161t451.5 159zM424 145v209l164 -84q-133 -119 -146 -131 q-18 -14 -18 6z" />
|
||||||
|
<glyph unicode="🕅" horiz-adv-x="1191" d="M590 1004q203 2 348 -139.5t149 -344.5q2 -205 -139 -350t-346 -150q-203 -2 -349.5 140.5t-148.5 345.5q-4 205 138.5 350.5t347.5 147.5zM602 125q160 2 272.5 116.5t110.5 276.5t-117.5 273.5t-275.5 109.5q-162 -2 -273.5 -116.5t-109.5 -276.5t116.5 -273.5 t276.5 -109.5zM475 430q41 0 60 41l57 -31q-20 -37 -51 -53q-33 -20 -72 -20q-63 0 -102 39q-39 37 -39 106.5t39 108t98 38.5q88 0 127 -67l-64 -33q-10 20 -24.5 28.5t-28.5 8.5q-61 0 -61 -84q0 -39 14 -59q18 -23 47 -23zM748 430q43 0 57 41l59 -31q-18 -33 -51 -53 t-72 -20q-66 0 -102 39q-39 37 -39 106q0 66 39 109q39 39 100 38q86 0 123 -67l-61 -33q-10 20 -24.5 28.5t-28.5 8.5q-63 0 -64 -84q0 -37 16.5 -59.5t47.5 -22.5z" />
|
||||||
|
<glyph unicode="🕆" horiz-adv-x="1187" d="M594 692q-68 0 -68 69.5t68 69.5q70 0 70 -69.5t-70 -69.5zM694 666q14 0 23 -9q10 -10 10 -22v-201h-57v-239h-152v239h-57v201q0 12 10 22q8 8 23 9h200zM594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5 q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5t-283.5 116.5t-283.5 -116.5t-117.5 -284.5q0 -166 117.5 -283.5t283.5 -117.5z" />
|
||||||
|
<glyph unicode="🕇" horiz-adv-x="1187" d="M594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM215 645q-23 -63 -22 -133q0 -166 117.5 -283.5t283.5 -117.5q113 0 207 57t145 151l-182 82q-8 -47 -47 -75q-39 -31 -88 -35 v-76h-58v76q-80 0 -149 59l67 68q51 -45 111 -45q25 0 43.5 12t18.5 37q0 18 -15 31l-47 20l-57 27l-78 32zM733 520l248 -110q14 45 14 102q0 168 -117.5 284.5t-283.5 116.5q-104 0 -192.5 -49t-143.5 -133l186 -84q12 37 48 64q33 23 79 24v76h58v-76q70 -4 123 -45 l-64 -65q-45 29 -86 28q-25 0 -39 -8q-18 -10 -18 -31q0 -8 4 -12l61 -29l43 -18z" />
|
||||||
|
<glyph unicode="🕈" horiz-adv-x="1187" d="M594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM215 643q-23 -63 -22 -131q0 -166 117.5 -283.5t283.5 -117.5q113 0 206 56t146 152l-252 111h-178q10 -37 27 -57 q39 -41 106 -41q47 0 94 20l19 -92q-57 -31 -127 -31q-131 0 -201 95q-35 45 -47 106h-53v59h45v15q0 4 1 12t1 12h-47v58h10zM715 528l268 -118q12 49 12 102q0 168 -117.5 284.5t-283.5 116.5q-104 0 -193.5 -49t-144.5 -133l162 -72q8 14 28 39q74 84 189 84 q72 0 125 -24l-25 -94q-41 20 -90 20q-66 0 -102 -45q-10 -10 -17 -29l58 -24h139v-58h-8z" />
|
||||||
|
<glyph unicode="🕉" horiz-adv-x="1187" d="M594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM211 631q-18 -55 -18 -119q0 -166 117.5 -283.5t283.5 -117.5q109 0 200 53t144 143l-156 70v-70h-129v-110h-121v110h-126v76 h126v37l-12 24h-114v76h55zM653 383h115l-109 49l-6 -12v-37zM782 485l197 -88q16 59 16 115q0 168 -117.5 284.5t-283.5 116.5q-109 0 -199 -52t-143 -140l162 -72l-56 101h131l78 -170l47 -21l84 191h131l-124 -230h77v-35z" />
|
||||||
|
<glyph unicode="🕊" horiz-adv-x="1187" d="M592 772q117 0 184 -76q68 -74 68 -190q0 -113 -70 -189q-76 -76 -184 -75q-82 0 -146 51q-59 49 -71 141h123q6 -88 108 -88q51 0 84 43q31 45 31 121q0 78 -29 119q-31 41 -84 41q-98 0 -110 -88h36l-98 -97l-96 97h37q14 92 74 141q59 49 143 49zM594 1004 q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5t-283.5 116.5t-283.5 -116.5t-117.5 -284.5q0 -166 117.5 -283.5t283.5 -117.5 z" />
|
||||||
|
<glyph unicode="🕋" horiz-adv-x="1187" d="M416 545v84h356v-84h-356zM416 387v84h356v-84h-356zM594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5 t-283.5 116.5t-283.5 -116.5t-117.5 -284.5q0 -166 117.5 -283.5t283.5 -117.5z" />
|
||||||
|
<glyph unicode="🕌" horiz-adv-x="1187" d="M594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM215 643q-23 -63 -22 -131q0 -166 117.5 -283.5t283.5 -117.5q113 0 206 56t146 152l-434 193q2 -55 28.5 -98t77.5 -43 q37 0 66 26l6 6l72 -86q-4 -2 -10.5 -7t-8.5 -9q-63 -43 -139 -43q-88 0 -162.5 59.5t-74.5 192.5q0 33 6 63zM532 608l451 -198q12 49 12 102q0 168 -117.5 284.5t-283.5 116.5q-104 0 -193.5 -49t-144.5 -133l152 -67q66 104 200 104q90 0 154 -55l-80 -82q-8 8 -14 12 q-23 16 -54 16q-53 0 -82 -51z" />
|
||||||
|
<glyph unicode="🕍" horiz-adv-x="1187" d="M594 797q111 0 157 -83t46 -202q0 -117 -46 -200t-157 -83t-157 83t-46 200q0 119 46 202t157 83zM506 512q0 -18 4 -68l108 199q14 25 -6 43q-12 4 -18 4q-88 0 -88 -178zM594 336q88 0 88 176q0 41 -6 86l-121 -209q-23 -31 12 -47q2 -2 6 -2q2 0 2 -2q2 0 8.5 -1 t10.5 -1zM594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5t-283.5 116.5t-283.5 -116.5t-117.5 -284.5 q0 -166 117.5 -283.5t283.5 -117.5z" />
|
||||||
|
<glyph unicode="🕎" horiz-adv-x="1187" d="M795 653q12 0 20 -8t8 -18v-363q0 -10 -8 -18t-20 -8h-267q-12 0 -20 8t-8 18v107h-107q-10 0 -18 8t-8 20v361q0 12 6 18q4 6 18 10h271q10 0 18 -8t8 -20v-107h107zM524 653h111v80h-215v-307h80v201q0 10 8 18q4 4 16 8zM768 291v309h-215v-309h215zM594 1004 q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5t-283.5 116.5t-283.5 -116.5t-117.5 -284.5q0 -166 117.5 -283.5t283.5 -117.5 z" />
|
||||||
|
<glyph unicode="🕏" horiz-adv-x="1187" d="M915 504l11 -4v-140l-11 -4l-118 -51l-4 -2l-7 2l-258 107l-8 4l-127 -54l-127 56v125l119 49l-2 2v139l133 58l301 -125v-121zM776 342v88h-2v2l-225 92v-88l225 -94v2zM791 457l79 32l-73 31l-78 -33zM895 381v86l-88 -37v-86zM594 1004q205 0 348 -143.5t143 -348.5 q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM594 111q166 0 283.5 117.5t117.5 283.5q0 168 -117.5 284.5t-283.5 116.5t-283.5 -116.5t-117.5 -284.5q0 -166 117.5 -283.5t283.5 -117.5z" />
|
||||||
|
<glyph unicode="🕔" horiz-adv-x="1146" d="M573.5 983q194.5 0 332.5 -138t138 -333t-138 -333t-332.5 -138t-333 138t-138.5 333t138.5 333t333 138zM573 143q154 0 261.5 108.5t107.5 260.5q0 154 -107.5 261.5t-261.5 107.5q-152 0 -260 -107.5t-108 -261.5q0 -152 108 -260.5t260 -108.5zM610 778v-250 l154 -153l-51 -51l-174 174v280h71z" />
|
||||||
|
<glyph unicode="🕨" horiz-adv-x="860" d="M635 276q51 0 87 -35.5t36 -87t-36 -87t-87 -35.5q-82 0 -113 75h-168q-90 0 -134 55.5t-44 121.5v475q-74 35 -74 112q0 51 36 87t87 36t87 -35.5t36 -87.5q0 -78 -74 -112v-117q0 -80 80 -80h168q31 74 113 74q51 0 87 -36t36 -87t-36 -87t-87 -36q-82 0 -113 76h-168 q-43 0 -80 16v-198q0 -80 80 -80h168q31 73 113 73zM635 584q-29 0 -50.5 -20.5t-21.5 -51.5q0 -29 21.5 -49.5t50.5 -20.5t50.5 20.5t21.5 49.5q0 31 -21.5 51.5t-50.5 20.5zM154 870q0 -29 21.5 -49t50 -20t50 20.5t21.5 48.5q0 31 -21.5 51.5t-50 20.5t-50 -20.5 t-21.5 -51.5zM635 84q29 0 50.5 20.5t21.5 49.5q0 31 -21.5 51t-50.5 20t-50.5 -20.5t-21.5 -50.5q0 -29 21.5 -49.5t50.5 -20.5z" />
|
||||||
|
<glyph unicode="🕩" horiz-adv-x="860" d="M758 819q0 -82 -76 -112q-6 -59 -28.5 -103.5t-62.5 -71t-69.5 -39t-77.5 -26.5q-43 -14 -64.5 -22.5t-48 -25t-38.5 -41t-17 -61.5q72 -31 72 -112q0 -51 -36 -87t-87 -36t-87 36t-36 87q0 80 74 114v388q-74 35 -74 112q0 51 36 87t87 36t87 -36t36 -87q0 -78 -74 -112 v-209q41 31 142 61q59 18 85.5 29.5t52.5 42.5t30 78q-72 33 -72 110q0 51 36 87t87 36t87 -36t36 -87zM156 819q0 -29 20.5 -49t49 -20t50 20.5t21.5 48.5q0 31 -21.5 51.5t-50 20.5t-49 -20.5t-20.5 -51.5zM225.5 135q28.5 0 50 20.5t21.5 49.5q0 31 -21.5 51t-50 20 t-49 -20t-20.5 -51q0 -29 20.5 -49.5t49 -20.5zM635 750q29 0 50.5 20.5t21.5 48.5q0 31 -21.5 51.5t-50.5 20.5t-49.5 -20.5t-20.5 -51.5q0 -29 20.5 -49t49.5 -20z" />
|
||||||
|
<glyph unicode="🕪" horiz-adv-x="1167" d="M991 268q74 -35 74 -114q0 -51 -36 -87t-87 -36t-87 35.5t-36 87.5q0 80 74 114v117q0 80 -78 80h-102q-45 0 -80 12v-209q74 -35 74 -114q0 -51 -36 -87t-87.5 -36t-87 35.5t-35.5 87.5q0 80 74 114v209q-31 -12 -78 -12h-103q-35 0 -54 -19.5t-22.5 -34t-3.5 -26.5 v-117q74 -35 74 -114q0 -51 -36 -87t-87 -36t-87 35.5t-36 87.5q0 80 74 114v117q0 66 44 121t134 55h103q78 0 78 53v144q-74 35 -74 112q0 51 35.5 87t87 36t87.5 -35.5t36 -87.5q0 -78 -74 -112v-144q0 -53 80 -53h102q88 0 132 -55t44 -121v-117zM297 154q0 31 -21.5 51 t-50 20t-49 -20.5t-20.5 -50.5q0 -29 20.5 -49.5t49 -20.5t50 20.5t21.5 49.5zM514 870q0 -29 20.5 -49t49 -20t50 20.5t21.5 48.5q0 31 -21.5 51.5t-50 20.5t-49 -20.5t-20.5 -51.5zM655 154q0 31 -21.5 51t-50 20t-49 -20.5t-20.5 -50.5q0 -29 20.5 -49.5t49 -20.5 t50 20.5t21.5 49.5zM942 84q29 0 50.5 20.5t21.5 49.5q0 31 -21.5 51t-50.5 20t-49.5 -20.5t-20.5 -50.5q0 -29 20.5 -49.5t49.5 -20.5z" />
|
||||||
|
<glyph unicode="🕫" horiz-adv-x="450" d="M274 319q74 -35 74 -114q0 -51 -36 -87t-87 -36t-87 36t-36 87q0 80 74 114v388q-74 35 -74 112q0 51 36 87t87 36t87 -36t36 -87q0 -78 -74 -112v-388zM156 819q0 -29 20.5 -49t49 -20t50 20.5t21.5 48.5q0 31 -21.5 51.5t-50 20.5t-49 -20.5t-20.5 -51.5zM225.5 135 q28.5 0 50 20.5t21.5 49.5q0 31 -21.5 51t-50 20t-49 -20t-20.5 -51q0 -29 20.5 -49.5t49 -20.5z" />
|
||||||
|
<glyph unicode="🕬" horiz-adv-x="860" d="M348 819q0 -78 -74 -112v-388q74 -35 74 -114q0 -51 -36 -87t-87 -36t-87 36t-36 87q0 80 74 114v388q-74 35 -74 112q0 51 36 87t87 36t87 -36t36 -87zM297 205q0 31 -21.5 51t-50 20t-49 -20t-20.5 -51q0 -29 20.5 -49.5t49 -20.5t50 20.5t21.5 49.5zM225.5 750 q28.5 0 50 20.5t21.5 48.5q0 31 -21.5 51.5t-50 20.5t-49 -20.5t-20.5 -51.5q0 -29 20.5 -49t49 -20zM684 319q74 -35 74 -114q0 -51 -36 -87t-87 -36t-87 36t-36 87q0 80 74 114v388q-74 35 -74 112q0 51 36 87t87 36t87 -36t36 -87q0 -78 -74 -112v-388zM565 819 q0 -29 20.5 -49t49.5 -20t50.5 20.5t21.5 48.5q0 31 -21.5 51.5t-50.5 20.5t-49.5 -20.5t-20.5 -51.5zM635 135q29 0 50.5 20.5t21.5 49.5q0 31 -21.5 51t-50.5 20t-49.5 -20t-20.5 -51q0 -29 20.5 -49.5t49.5 -20.5z" />
|
||||||
|
<glyph unicode="🗹" horiz-adv-x="948" d="M662 399q49 -92 -5 -184q-43 -72 -117.5 -90.5t-154.5 27.5l-66 36l-110 -188l-57 33l210 366q-70 -10 -137 29l-123 72l191 331l125 -71q68 -41 94 -103l211 367l59 -35l-110 -190l65 -37q78 -45 100.5 -122t-20.5 -146q-49 -91 -155 -95zM707 709l-66 39l-127 -222 q0 -2 -2 -6l63 -37q53 -31 102.5 -17.5t78 63t15.5 99.5t-64 81zM256 481q53 -31 102.5 -17.5t78 62.5t15 100.5t-64.5 80.5l-65 38l-132 -227zM416 205q53 -31 102 -17.5t78 62.5t14.5 99t-65.5 81l-64 37l-4 -4l-127 -221z" />
|
||||||
|
<glyph unicode="🗺" horiz-adv-x="1126" d="M563 973q283 0 372 -90t89 -371q0 -283 -89 -372t-372 -89q-281 0 -371 89t-90 372q0 281 90 371t371 90z" />
|
||||||
|
<glyph unicode="🚀" horiz-adv-x="1087" d="M659 395q6 -51 8.5 -82.5t-8 -60.5t-13.5 -41t-35.5 -32.5t-46 -27t-72 -32t-86.5 -37.5q-33 -12 -46.5 4t-3.5 45l41 113l-133 135l-108 -41q-29 -12 -44.5 2t-2.5 47q12 31 31.5 81t27.5 66.5t22.5 46t25.5 37t29.5 20.5t42 13h53.5t73 -6q10 14 29.5 40t79 87.5 t121 106.5t148.5 76.5t169 19.5q8 0 14 -6q4 -4 6 -15q10 -84 -18.5 -172t-77.5 -154.5t-100.5 -120.5t-88.5 -83zM711 698q23 -23 55.5 -22.5t54.5 22.5q23 25 23 57.5t-23 57.5q-23 23 -55.5 23t-54.5 -23q-23 -25 -23 -57.5t23 -57.5z" />
|
||||||
|
<glyph unicode="🚫" horiz-adv-x="1187" d="M594 1004q205 0 348 -143.5t143 -348.5q0 -203 -143 -347.5t-348 -144.5q-203 0 -347.5 144.5t-144.5 347.5q0 205 144.5 348.5t347.5 143.5zM858 778zM221 512q0 -135 84 -236l526 527q-102 84 -237 84q-156 0 -264.5 -109.5t-108.5 -265.5zM330 248zM594 139 q156 0 265.5 109.5t109.5 263.5q0 133 -84 238l-527 -527q101 -84 236 -84z" />
|
||||||
|
<glyph unicode="🛆" d="M1024 819q43 0 72.5 -30.5t29.5 -71.5v-563q0 -43 -29.5 -73t-72.5 -30h-51v768h51zM102 717q0 41 31 71.5t72 30.5h51v-768h-51q-41 0 -72 30t-31 73v563zM788 926v-107h113v-768h-573v768h112v107q100 47 174 47t174 -47zM727 819v68q-53 25 -113 24q-55 0 -112 -24 v-68h225z" />
|
||||||
|
<glyph unicode="🛇" d="M518 336q35 57 219.5 290.5t198.5 225.5q12 -6 -98.5 -284.5t-141.5 -334.5q-51 -88 -139 -36.5t-39 139.5zM614 725q-172 0 -290.5 -130t-118.5 -319q0 -31 2 -47q2 -23 -12.5 -38t-35 -17t-37 12.5t-18.5 34.5q0 8 -1 26.5t-1 28.5q0 231 148.5 391t363.5 160 q74 0 138 -18l-72 -88q-41 4 -66 4zM985 662q141 -158 141 -386q0 -39 -2 -57q-2 -20 -16 -33.5t-35 -13.5h-4q-23 4 -36 20.5t-11 36.5q2 14 2 47q0 154 -82 275q6 14 20.5 52.5t22.5 58.5z" />
|
||||||
|
<glyph unicode="🛈" horiz-adv-x="1191" d="M596 406q-92 0 -159.5 27.5t-71.5 66.5q45 127 57 162q10 -29 60 -47.5t114 -18.5q66 0 115 18.5t59 47.5q12 -35 57 -162q-4 -39 -71.5 -66.5t-159.5 -27.5zM596 748q-98 0 -127 45l53 145q10 35 73.5 35t76.5 -35q12 -39 51 -145q-29 -45 -127 -45zM1047 354 q41 -16 42 -41.5t-38 -48.5l-361 -192q-39 -23 -93 -23t-93 23l-363 192q-39 20 -37 47t43 43l193 78l-23 -61q0 -49 82 -84t197 -35t196.5 35t84.5 84l-23 61z" />
|
||||||
|
</font>
|
||||||
|
</defs></svg>
|
After Width: | Height: | Size: 81 KiB |
BIN
data/adminer/fonts/entypo.ttf
Normal file
BIN
data/adminer/fonts/entypo.ttf
Normal file
Binary file not shown.
BIN
data/adminer/fonts/entypo.woff
Normal file
BIN
data/adminer/fonts/entypo.woff
Normal file
Binary file not shown.
BIN
data/adminer/logo.png
Normal file
BIN
data/adminer/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
39
data/compose.yaml
Normal file
39
data/compose.yaml
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
services:
|
||||||
|
wikipedia-solver-database:
|
||||||
|
container_name: "wikipedia-solver-database"
|
||||||
|
image: "mariadb:10.6.17"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
env_file: ".env"
|
||||||
|
environment:
|
||||||
|
MARIADB_USER: ${DATABASE_USER}
|
||||||
|
MARIADB_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_ROOT_PASSWORD: ${DATABASE_PASSWORD}
|
||||||
|
MARIADB_DATABASE: ${DATABASE_NAME}
|
||||||
|
command:
|
||||||
|
--innodb_buffer_pool_size=4G
|
||||||
|
--key-buffer-size=4G
|
||||||
|
--innodb_log_buffer_size=256M
|
||||||
|
--innodb_log_file_size=1G
|
||||||
|
--innodb_write_io_threads=16
|
||||||
|
--innodb_flush_log_at_trx_commit=0
|
||||||
|
--max_allowed_packet=1G
|
||||||
|
volumes:
|
||||||
|
- "wikipedia-solver-mariadb-data:/var/lib/mysql"
|
||||||
|
- "./sql:/docker-entrypoint-initdb.d/"
|
||||||
|
|
||||||
|
adminer:
|
||||||
|
container_name: "adminer"
|
||||||
|
image: "adminer:4.8.1"
|
||||||
|
restart: "unless-stopped"
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
env_file: ".env"
|
||||||
|
environment:
|
||||||
|
ADMINER_DEFAULT_SERVER: "wikipedia-solver-database"
|
||||||
|
volumes:
|
||||||
|
- "./adminer/default-orange.css:/var/www/html/adminer.css"
|
||||||
|
- "./adminer/logo.png:/var/www/html/logo.png"
|
||||||
|
- "./adminer/fonts/:/var/www/html/fonts"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
wikipedia-solver-mariadb-data:
|
269
data/database-wikipedia.js
Normal file
269
data/database-wikipedia.js
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
import fs from "node:fs"
|
||||||
|
import path from "node:path"
|
||||||
|
import {
|
||||||
|
extractRowsFromSQLValues,
|
||||||
|
swapKeysAndValues,
|
||||||
|
zeroPad,
|
||||||
|
} from "./utils.js"
|
||||||
|
|
||||||
|
const SQL_DUMP_PATH = path.join(process.cwd(), "dump")
|
||||||
|
const SQL_OUTPUT_PATH = path.join(process.cwd(), "sql")
|
||||||
|
const SQL_FILENAME_NUMBER_PAD = 4
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, number>} WikipediaPagesKeyTitle
|
||||||
|
*
|
||||||
|
* Object to store pages from Wikipedia:
|
||||||
|
* - Key: page title sanitized - The real title shown is this title with underscores (_) converted to spaces ( ).
|
||||||
|
* - Value: page id.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Record<string, number>} WikipediaPagesKeyId
|
||||||
|
*
|
||||||
|
* Object to store pages from Wikipedia:
|
||||||
|
* - Key: page id.
|
||||||
|
* - Value: page title sanitized - The real title shown is this title with underscores (_) converted to spaces ( ).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef WikipediaInternalLink
|
||||||
|
* @property {number} fromPageId
|
||||||
|
* @property {number} toPageId
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to clean the `page.sql` file by:
|
||||||
|
* - Removing all lines that don't start with `INSERT INTO...`.
|
||||||
|
* - Filter by keeping rows where `page_namespace` (2nd column) is equal to 0, and where `page_is_redirect` (4th column) is equal to false (0).
|
||||||
|
* - Only keep columns `page_id` (1st column) and `page_title` (3rd column).
|
||||||
|
* @returns {Promise<WikipediaPagesKeyId>}
|
||||||
|
*/
|
||||||
|
const cleanPagesSQL = async () => {
|
||||||
|
/** @type {WikipediaPagesKeyId} */
|
||||||
|
const wikipediaPagesKeyId = {}
|
||||||
|
|
||||||
|
const INSERT_INTO_START_INPUT = "INSERT INTO `page` VALUES "
|
||||||
|
const sqlInputPath = path.join(SQL_DUMP_PATH, "page.sql")
|
||||||
|
const sqlInputStat = await fs.promises.stat(sqlInputPath)
|
||||||
|
const sqlInputFileStream = fs.createReadStream(sqlInputPath, "utf-8")
|
||||||
|
|
||||||
|
let isInsideInsert = false
|
||||||
|
let current = ""
|
||||||
|
let lastPercent = 0
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
sqlInputFileStream
|
||||||
|
.on("data", (dataInput) => {
|
||||||
|
const bytesReadRatio = sqlInputFileStream.bytesRead / sqlInputStat.size
|
||||||
|
const bytesReadPercent = bytesReadRatio * 100
|
||||||
|
|
||||||
|
if (bytesReadPercent - lastPercent >= 1) {
|
||||||
|
console.log(
|
||||||
|
`cleanPagesSQL - Bytes read (${bytesReadPercent.toFixed(2)}%): ${sqlInputFileStream.bytesRead} / ${sqlInputStat.size}`,
|
||||||
|
)
|
||||||
|
lastPercent = bytesReadPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = current + dataInput
|
||||||
|
|
||||||
|
if (!isInsideInsert) {
|
||||||
|
const lines = data.split("\n").filter((line) => {
|
||||||
|
return line.startsWith(INSERT_INTO_START_INPUT)
|
||||||
|
})
|
||||||
|
const [line] = lines
|
||||||
|
if (line == null) {
|
||||||
|
sqlInputFileStream.close()
|
||||||
|
return reject(new Error(`No "${INSERT_INTO_START_INPUT}" found.`))
|
||||||
|
}
|
||||||
|
isInsideInsert = true
|
||||||
|
const lineStripped = line.slice(INSERT_INTO_START_INPUT.length)
|
||||||
|
data = lineStripped
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, unCompleted } = extractRowsFromSQLValues(data)
|
||||||
|
current = unCompleted
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.length !== 12) {
|
||||||
|
sqlInputFileStream.close()
|
||||||
|
console.error([row])
|
||||||
|
return reject(new Error(`Invalid Row values.`))
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = Number.parseInt(row[0] ?? "0", 10)
|
||||||
|
const namespace = row[1] ?? ""
|
||||||
|
const title = row[2] ?? ""
|
||||||
|
const isRedirect = row[3] === "1"
|
||||||
|
|
||||||
|
if (namespace === "0" && !isRedirect) {
|
||||||
|
wikipediaPagesKeyId[id] = title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("error", (error) => {
|
||||||
|
return reject(error)
|
||||||
|
})
|
||||||
|
.on("close", () => {
|
||||||
|
console.log("cleanPagesSQL - Bytes read (100%).")
|
||||||
|
return resolve(wikipediaPagesKeyId)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const wikipediaPagesKeyId = await cleanPagesSQL()
|
||||||
|
|
||||||
|
const cleanPagesSQLWriteToFile = async () => {
|
||||||
|
console.log("cleanPagesSQLWriteToFile - Writing to file...")
|
||||||
|
const sqlOutputPath = path.join(
|
||||||
|
SQL_OUTPUT_PATH,
|
||||||
|
`${zeroPad(1, SQL_FILENAME_NUMBER_PAD)}-pages-inserts.sql`,
|
||||||
|
)
|
||||||
|
const INSERT_INTO_START_OUTPUT = "INSERT INTO pages (id, title) VALUES "
|
||||||
|
|
||||||
|
const wikipediaPagesString = Object.entries(wikipediaPagesKeyId)
|
||||||
|
.map(([id, title]) => {
|
||||||
|
return `(${id},${title})`
|
||||||
|
})
|
||||||
|
.join(",")
|
||||||
|
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
sqlOutputPath,
|
||||||
|
`${INSERT_INTO_START_OUTPUT}${wikipediaPagesString};`,
|
||||||
|
{ encoding: "utf-8" },
|
||||||
|
)
|
||||||
|
console.log("cleanPagesSQLWriteToFile - Done.")
|
||||||
|
}
|
||||||
|
|
||||||
|
await cleanPagesSQLWriteToFile()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to clean the `pagelinks.sql` file by:
|
||||||
|
* - Removing all lines that don't start with `INSERT INTO...`.
|
||||||
|
* - Filter by keeping rows where `pl_from_namespace` (2nd column) is equal to 0.
|
||||||
|
* - Transform the rows to internal links with fromPageId and toPageId.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const cleanInternalLinksSQL = async () => {
|
||||||
|
let internalLinksFileCount = 2
|
||||||
|
const INSERT_INTO_START_OUTPUT =
|
||||||
|
"INSERT INTO internal_links (from_page_id, to_page_id) VALUES "
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {WikipediaPagesKeyTitle}
|
||||||
|
*/
|
||||||
|
const wikipediaPagesKeyTitle = swapKeysAndValues(wikipediaPagesKeyId)
|
||||||
|
|
||||||
|
const INSERT_INTO_START_INPUT = "INSERT INTO `pagelinks` VALUES "
|
||||||
|
const sqlInputPath = path.join(SQL_DUMP_PATH, "pagelinks.sql")
|
||||||
|
const sqlInputStat = await fs.promises.stat(sqlInputPath)
|
||||||
|
const sqlInputFileStream = fs.createReadStream(sqlInputPath, "utf-8")
|
||||||
|
|
||||||
|
let isInsideInsert = false
|
||||||
|
let current = ""
|
||||||
|
let lastPercent = 0
|
||||||
|
|
||||||
|
const BATCH_SIZE = 4_000_000
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {string[]}
|
||||||
|
*/
|
||||||
|
let batch = []
|
||||||
|
|
||||||
|
const flushBatch = async () => {
|
||||||
|
if (batch.length > 0) {
|
||||||
|
const batchString = batch.join(",")
|
||||||
|
const fileName = `${zeroPad(internalLinksFileCount, SQL_FILENAME_NUMBER_PAD)}-internal-links-inserts.sql`
|
||||||
|
const sqlOutputPath = path.join(SQL_OUTPUT_PATH, fileName)
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
sqlOutputPath,
|
||||||
|
`${INSERT_INTO_START_OUTPUT}${batchString};`,
|
||||||
|
{
|
||||||
|
encoding: "utf-8",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
console.log(`flushBatch - ${fileName}, batch.length: ${batch.length}`)
|
||||||
|
internalLinksFileCount += 1
|
||||||
|
batch = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
sqlInputFileStream
|
||||||
|
.on("data", async (dataInput) => {
|
||||||
|
const bytesReadRatio = sqlInputFileStream.bytesRead / sqlInputStat.size
|
||||||
|
const bytesReadPercent = bytesReadRatio * 100
|
||||||
|
|
||||||
|
if (bytesReadPercent - lastPercent >= 0.5) {
|
||||||
|
console.log(
|
||||||
|
`cleanInternalLinksSQL - Bytes read (${bytesReadPercent.toFixed(2)}%): ${sqlInputFileStream.bytesRead} / ${sqlInputStat.size}`,
|
||||||
|
)
|
||||||
|
lastPercent = bytesReadPercent
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = current + dataInput
|
||||||
|
|
||||||
|
if (!isInsideInsert) {
|
||||||
|
const lines = data.split("\n").filter((line) => {
|
||||||
|
return line.startsWith(INSERT_INTO_START_INPUT)
|
||||||
|
})
|
||||||
|
const [line] = lines
|
||||||
|
if (line == null) {
|
||||||
|
sqlInputFileStream.close()
|
||||||
|
return reject(new Error(`No "${INSERT_INTO_START_INPUT}" found.`))
|
||||||
|
}
|
||||||
|
isInsideInsert = true
|
||||||
|
const lineStripped = line.slice(INSERT_INTO_START_INPUT.length)
|
||||||
|
data = lineStripped
|
||||||
|
}
|
||||||
|
|
||||||
|
const { rows, unCompleted } = extractRowsFromSQLValues(data)
|
||||||
|
current = unCompleted
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
if (row.length !== 5) {
|
||||||
|
sqlInputFileStream.close()
|
||||||
|
console.error([row])
|
||||||
|
return reject(new Error(`Invalid Row values.`))
|
||||||
|
}
|
||||||
|
|
||||||
|
const plFromPageId = Number.parseInt(row[0] ?? "0", 10)
|
||||||
|
const plTargetNamespace = row[1] ?? ""
|
||||||
|
const plTargetTitle = row[2] ?? ""
|
||||||
|
const plFromNamespace = row[3] ?? ""
|
||||||
|
|
||||||
|
if (plFromNamespace === "0" && plTargetNamespace === "0") {
|
||||||
|
const toPageId = wikipediaPagesKeyTitle[plTargetTitle]
|
||||||
|
if (toPageId != null && wikipediaPagesKeyId[plFromPageId] != null) {
|
||||||
|
/**
|
||||||
|
* @type {WikipediaInternalLink}
|
||||||
|
*/
|
||||||
|
const wikipediaInternalLink = {
|
||||||
|
fromPageId: plFromPageId,
|
||||||
|
toPageId,
|
||||||
|
}
|
||||||
|
batch.push(
|
||||||
|
`(${wikipediaInternalLink.fromPageId},${wikipediaInternalLink.toPageId})`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (batch.length >= BATCH_SIZE) {
|
||||||
|
sqlInputFileStream.pause()
|
||||||
|
await flushBatch()
|
||||||
|
sqlInputFileStream.resume()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("error", (error) => {
|
||||||
|
return reject(error)
|
||||||
|
})
|
||||||
|
.on("close", async () => {
|
||||||
|
await flushBatch()
|
||||||
|
console.log("cleanInternalLinksSQL - Bytes read (100%).")
|
||||||
|
return resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await cleanInternalLinksSQL()
|
50
data/download-wikipedia-dump.sh
Executable file
50
data/download-wikipedia-dump.sh
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Usage: ./download-wikipedia-dump.sh
|
||||||
|
# Description: Download and extract Wikipedia database dumps.
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o nounset
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
DUMP_DIRECTORY="dump"
|
||||||
|
DOWNLOAD_DATE="20240420"
|
||||||
|
# DOWNLOAD_DATE="latest"
|
||||||
|
WIKIPEDIA_DUMP_URL="https://dumps.wikimedia.org/enwiki/${DOWNLOAD_DATE}/enwiki-${DOWNLOAD_DATE}-"
|
||||||
|
|
||||||
|
mkdir --parents "${DUMP_DIRECTORY}"
|
||||||
|
|
||||||
|
download_file() {
|
||||||
|
local filename="${1}"
|
||||||
|
local file_path_output="${DUMP_DIRECTORY}/${filename}"
|
||||||
|
local file_url="${WIKIPEDIA_DUMP_URL}${filename}"
|
||||||
|
|
||||||
|
if [[ ! -f "${file_path_output%.gz}" ]]; then
|
||||||
|
echo "Downloading \"${filename}\" from \"${file_url}\"..."
|
||||||
|
wget --output-document="${file_path_output}" "${file_url}"
|
||||||
|
else
|
||||||
|
echo "File \"${filename%.gz}\" from \"${file_url}\" already exists."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# download_file "page.sql.gz"
|
||||||
|
# download_file "pagelinks.sql.gz"
|
||||||
|
|
||||||
|
extract_file() {
|
||||||
|
local filename="${1}"
|
||||||
|
local file_path_input="${DUMP_DIRECTORY}/${filename}"
|
||||||
|
local file_path_output="${DUMP_DIRECTORY}/${filename%.gz}"
|
||||||
|
|
||||||
|
if [[ ! -f "${file_path_output}" ]]; then
|
||||||
|
echo "Extracting \"${filename}\" to \"${file_path_output}\"..."
|
||||||
|
gzip --decompress "${file_path_input}"
|
||||||
|
|
||||||
|
# `--keep` flag to keep the original file, not needed here.
|
||||||
|
# gzip --decompress --keep "${file_path_input}"
|
||||||
|
else
|
||||||
|
echo "File \"${filename}\" already extracted."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_file "page.sql.gz"
|
||||||
|
extract_file "pagelinks.sql.gz"
|
28
data/sql/0000-tables-create.sql
Normal file
28
data/sql/0000-tables-create.sql
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
CREATE TABLE `pages` (
|
||||||
|
`id` INT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`title` VARBINARY(255) NOT NULL DEFAULT '',
|
||||||
|
-- `is_redirect` tinyint(1) unsigned NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY (`title`)
|
||||||
|
) ENGINE=MyISAM AUTO_INCREMENT=76684425 DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
|
||||||
|
|
||||||
|
-- VARBINARY usage instead of VARCHAR explanation: <https://stackoverflow.com/a/13397437>
|
||||||
|
-- > War on varchar. Changed all occurrences of varchar(N) and varchar(N) binary to varbinary(N). varchars cause problems ("Invalid mix of collations" errors) on MySQL databases with certain configs, most notably the default MySQL config.
|
||||||
|
|
||||||
|
CREATE TABLE `internal_links` (
|
||||||
|
-- `id` INT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`from_page_id` INT(8) UNSIGNED NOT NULL,
|
||||||
|
`to_page_id` INT(8) UNSIGNED NOT NULL,
|
||||||
|
|
||||||
|
-- PRIMARY KEY (`id`)
|
||||||
|
PRIMARY KEY (`from_page_id`, `to_page_id`),
|
||||||
|
FOREIGN KEY (`from_page_id`) REFERENCES `pages` (`id`) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (`to_page_id`) REFERENCES `pages` (`id`) ON DELETE CASCADE
|
||||||
|
) ENGINE=MyISAM DEFAULT CHARSET=binary ROW_FORMAT=COMPRESSED;
|
||||||
|
|
||||||
|
SET @@session.unique_checks = 0;
|
||||||
|
SET @@session.foreign_key_checks = 0;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
SET UNIQUE_CHECKS = 0;
|
11
data/sql/0999-constraints.sql
Normal file
11
data/sql/0999-constraints.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
-- SET @@session.foreign_key_checks = 0;
|
||||||
|
-- SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ALTER TABLE `internal_links` ADD CONSTRAINT fk_from_page_id FOREIGN KEY (`from_page_id`) REFERENCES `pages` (`id`);
|
||||||
|
-- ALTER TABLE `internal_links` ADD CONSTRAINT fk_to_page_id FOREIGN KEY (`to_page_id`) REFERENCES `pages` (`id`);
|
||||||
|
|
||||||
|
SET @@session.unique_checks = 1;
|
||||||
|
SET @@session.foreign_key_checks = 1;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
SET UNIQUE_CHECKS = 1;
|
88
data/utils.js
Normal file
88
data/utils.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/**
|
||||||
|
* Extracts rows from a string of values in a SQL INSERT INTO statement, where each row is a comma-separated list of values enclosed in parentheses, possibly with the last row incomplete.
|
||||||
|
* @param {string} input
|
||||||
|
* @returns {{rows: string[][], unCompleted:string}}
|
||||||
|
* @example extractRowsFromSQLValues("(1,'-)',0),(2,'Demographics_of_American_Samoa',0)") // { rows: [["1","'-)'","0"],["2","'Demographics_of_American_Samoa'","0"]], unCompleted: "" }
|
||||||
|
*/
|
||||||
|
export const extractRowsFromSQLValues = (input) => {
|
||||||
|
const rows = []
|
||||||
|
let index = 0
|
||||||
|
let unCompleted = ""
|
||||||
|
|
||||||
|
while (index < input.length) {
|
||||||
|
if (input[index] === "(") {
|
||||||
|
const row = []
|
||||||
|
index++ // Skip the opening '('
|
||||||
|
let value = ""
|
||||||
|
let insideQuotes = false
|
||||||
|
let rowComplete = false
|
||||||
|
|
||||||
|
while (index < input.length && !rowComplete) {
|
||||||
|
if (input[index] === "'") {
|
||||||
|
// An escaped quote is preceded by an odd number of backslashes.
|
||||||
|
let backslashCount = 0
|
||||||
|
let backIndex = index - 1
|
||||||
|
while (backIndex >= 0 && input[backIndex] === "\\") {
|
||||||
|
backslashCount++
|
||||||
|
backIndex--
|
||||||
|
}
|
||||||
|
if (backslashCount % 2 === 0) {
|
||||||
|
insideQuotes = !insideQuotes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input[index] === "," && !insideQuotes) {
|
||||||
|
row.push(value)
|
||||||
|
value = ""
|
||||||
|
} else if (input[index] === ")" && !insideQuotes) {
|
||||||
|
row.push(value)
|
||||||
|
rows.push(row)
|
||||||
|
rowComplete = true
|
||||||
|
} else {
|
||||||
|
value += input[index]
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rowComplete) {
|
||||||
|
// If row is not completed, save it to unCompleted
|
||||||
|
unCompleted = "("
|
||||||
|
if (row.length > 0) {
|
||||||
|
unCompleted += row.join(",") + "," + value
|
||||||
|
} else if (value.length > 0) {
|
||||||
|
unCompleted += value
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { rows, unCompleted }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps the keys and values of an object.
|
||||||
|
* @param {*} object
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const swapKeysAndValues = (object) => {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(object).map(([key, value]) => {
|
||||||
|
return [value, key]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {number} number
|
||||||
|
* @param {number} places
|
||||||
|
* @returns {string}
|
||||||
|
* @example zeroPad(1, 2) // '01'
|
||||||
|
* @example zeroPad(10, 2) // '10'
|
||||||
|
*/
|
||||||
|
export const zeroPad = (number, places = 2) => {
|
||||||
|
return number.toString().padStart(places, "0")
|
||||||
|
}
|
@ -29,7 +29,7 @@
|
|||||||
"prettier-plugin-tailwindcss": "0.6.5",
|
"prettier-plugin-tailwindcss": "0.6.5",
|
||||||
"replace-in-files-cli": "3.0.0",
|
"replace-in-files-cli": "3.0.0",
|
||||||
"semantic-release": "23.1.1",
|
"semantic-release": "23.1.1",
|
||||||
"turbo": "2.0.11",
|
"turbo": "2.0.12",
|
||||||
"typescript": "catalog:"
|
"typescript": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"strictBindCallApply": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
"allowUnusedLabels": false,
|
"allowUnusedLabels": false,
|
||||||
"allowUnreachableCode": false,
|
"allowUnreachableCode": false,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitOverride": true,
|
"noImplicitOverride": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
"noPropertyAccessFromIndexSignature": true,
|
"noPropertyAccessFromIndexSignature": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
|
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
3938
pnpm-lock.yaml
generated
3938
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -21,15 +21,35 @@ catalog:
|
|||||||
# TypeScript
|
# TypeScript
|
||||||
"typescript": "5.5.4"
|
"typescript": "5.5.4"
|
||||||
"@total-typescript/ts-reset": "0.5.1"
|
"@total-typescript/ts-reset": "0.5.1"
|
||||||
"@types/node": "22.0.2"
|
"@types/node": "22.1.0"
|
||||||
"tsx": "4.16.5"
|
"tsx": "4.17.0"
|
||||||
|
|
||||||
|
# AdonisJS
|
||||||
|
"@adonisjs/auth": "9.2.3"
|
||||||
|
"@adonisjs/core": "6.12.1"
|
||||||
|
"@adonisjs/cors": "2.2.1"
|
||||||
|
"@adonisjs/lucid": "21.2.0"
|
||||||
|
"mysql2": "3.11.0"
|
||||||
|
"@adonisjs/assembler": "7.7.0"
|
||||||
|
"@vinejs/vine": "2.1.0"
|
||||||
|
"luxon": "3.5.0"
|
||||||
|
"@types/luxon": "3.4.2"
|
||||||
|
"reflect-metadata": "0.2.2"
|
||||||
|
"openapi-types": "12.1.3"
|
||||||
|
"pino-pretty": "11.2.2"
|
||||||
|
|
||||||
|
# Japa (AdonisJS Testing)
|
||||||
|
"@japa/api-client": "2.0.3"
|
||||||
|
"@japa/assert": "3.0.0"
|
||||||
|
"@japa/plugin-adonisjs": "3.0.1"
|
||||||
|
"@japa/runner": "3.1.4"
|
||||||
|
|
||||||
# ESLint
|
# ESLint
|
||||||
"@typescript-eslint/eslint-plugin": "7.18.0"
|
"@typescript-eslint/eslint-plugin": "7.18.0"
|
||||||
"@typescript-eslint/parser": "7.18.0"
|
"@typescript-eslint/parser": "7.18.0"
|
||||||
"eslint": "8.57.0"
|
"eslint": "8.57.0"
|
||||||
"eslint-config-conventions": "14.4.0"
|
"eslint-config-conventions": "14.4.0"
|
||||||
"eslint-plugin-promise": "7.0.0"
|
"eslint-plugin-promise": "7.1.0"
|
||||||
"eslint-plugin-unicorn": "55.0.0"
|
"eslint-plugin-unicorn": "55.0.0"
|
||||||
"eslint-config-next": "14.2.5"
|
"eslint-config-next": "14.2.5"
|
||||||
"eslint-plugin-storybook": "0.8.0"
|
"eslint-plugin-storybook": "0.8.0"
|
||||||
@ -37,25 +57,25 @@ catalog:
|
|||||||
|
|
||||||
# Storybook
|
# Storybook
|
||||||
"@chromatic-com/storybook": "1.6.1"
|
"@chromatic-com/storybook": "1.6.1"
|
||||||
"@storybook/addon-a11y": "8.2.7"
|
"@storybook/addon-a11y": "8.2.8"
|
||||||
"@storybook/addon-essentials": "8.2.7"
|
"@storybook/addon-essentials": "8.2.8"
|
||||||
"@storybook/addon-interactions": "8.2.7"
|
"@storybook/addon-interactions": "8.2.8"
|
||||||
"@storybook/addon-links": "8.2.7"
|
"@storybook/addon-links": "8.2.8"
|
||||||
"@storybook/addon-storysource": "8.2.7"
|
"@storybook/addon-storysource": "8.2.8"
|
||||||
"@storybook/addon-themes": "8.2.7"
|
"@storybook/addon-themes": "8.2.8"
|
||||||
"@storybook/blocks": "8.2.7"
|
"@storybook/blocks": "8.2.8"
|
||||||
"@storybook/nextjs": "8.2.7"
|
"@storybook/nextjs": "8.2.8"
|
||||||
"@storybook/react": "8.2.7"
|
"@storybook/react": "8.2.8"
|
||||||
"@storybook/test": "8.2.7"
|
"@storybook/test": "8.2.8"
|
||||||
"@storybook/test-runner": "0.19.1"
|
"@storybook/test-runner": "0.19.1"
|
||||||
"chromatic": "11.7.0"
|
"chromatic": "11.7.0"
|
||||||
"http-server": "14.1.1"
|
"http-server": "14.1.1"
|
||||||
"storybook": "8.2.7"
|
"storybook": "8.2.8"
|
||||||
"storybook-dark-mode": "4.0.2"
|
"storybook-dark-mode": "4.0.2"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
"playwright": "1.45.3"
|
"playwright": "1.46.0"
|
||||||
"@playwright/test": "1.45.3"
|
"@playwright/test": "1.46.0"
|
||||||
"axe-playwright": "2.0.1"
|
"axe-playwright": "2.0.1"
|
||||||
"start-server-and-test": "2.0.5"
|
"start-server-and-test": "2.0.5"
|
||||||
"@vitest/browser": "2.0.5"
|
"@vitest/browser": "2.0.5"
|
||||||
@ -65,8 +85,8 @@ catalog:
|
|||||||
"@testing-library/react": "16.0.0"
|
"@testing-library/react": "16.0.0"
|
||||||
|
|
||||||
# CSS
|
# CSS
|
||||||
"postcss": "8.4.40"
|
"postcss": "8.4.41"
|
||||||
"tailwindcss": "3.4.7"
|
"tailwindcss": "3.4.9"
|
||||||
"@fontsource/montserrat": "5.0.18"
|
"@fontsource/montserrat": "5.0.18"
|
||||||
"clsx": "2.1.0"
|
"clsx": "2.1.0"
|
||||||
"cva": "1.0.0-beta.1"
|
"cva": "1.0.0-beta.1"
|
||||||
|
Reference in New Issue
Block a user