diff --git a/.changeset/stale-waves-protect.md b/.changeset/stale-waves-protect.md deleted file mode 100644 index f118b29413a..00000000000 --- a/.changeset/stale-waves-protect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@atproto/api": patch ---- - -new com.atproto.identity endpoints: resolveDid, resolveIdentity, refreshIdentity diff --git a/.eslintrc b/.eslintrc index 8afe9bb56dc..ef8c3a4a6c5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -44,7 +44,7 @@ } ], "@typescript-eslint/no-unused-vars": [ - "warn", + "error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" } ], "@typescript-eslint/ban-ts-comment": "off", diff --git a/.github/workflows/repo.yaml b/.github/workflows/repo.yaml index e8db4b20b8d..de2e2d01dda 100644 --- a/.github/workflows/repo.yaml +++ b/.github/workflows/repo.yaml @@ -35,6 +35,7 @@ jobs: path: | packages/*/dist packages/*/*/dist + packages/oauth/oauth-provider/src/assets/app/locales/*/messages.ts retention-days: 1 test: name: Test diff --git a/.npmrc b/.npmrc index 8e012302ad8..287c2380418 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ enable-pre-post-scripts = true +include-workspace-root = true diff --git a/.prettierignore b/.prettierignore index e6453c5dfbb..570ee8a72ba 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,3 +12,6 @@ packages/api/src/client packages/bsky/src/lexicon packages/pds/src/lexicon packages/ozone/src/lexicon + +# Automatically generated by lingui +packages/oauth/oauth-provider/src/assets/app/locales/*/messages.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 4f8e93dede2..2d95c43f7f0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,6 +12,7 @@ "consolas", "dpop", "googleusercontent", + "hcaptcha", "hexeditor", "ingester", "insertable", diff --git a/lexicons/app/bsky/embed/video.json b/lexicons/app/bsky/embed/video.json index da58ded8bb4..92511af1636 100644 --- a/lexicons/app/bsky/embed/video.json +++ b/lexicons/app/bsky/embed/video.json @@ -9,8 +9,9 @@ "properties": { "video": { "type": "blob", + "description": "The mp4 video file. May be up to 100mb, formerly limited to 50mb.", "accept": ["video/mp4"], - "maxSize": 50000000 + "maxSize": 100000000 }, "captions": { "type": "array", diff --git a/lexicons/com/atproto/sync/subscribeRepos.json b/lexicons/com/atproto/sync/subscribeRepos.json index b52a70d31f8..373b050f2b8 100644 --- a/lexicons/com/atproto/sync/subscribeRepos.json +++ b/lexicons/com/atproto/sync/subscribeRepos.json @@ -17,16 +17,7 @@ "message": { "schema": { "type": "union", - "refs": [ - "#commit", - "#sync", - "#identity", - "#account", - "#handle", - "#migrate", - "#tombstone", - "#info" - ] + "refs": ["#commit", "#sync", "#identity", "#account", "#info"] } }, "errors": [ @@ -186,39 +177,6 @@ } } }, - "handle": { - "type": "object", - "description": "DEPRECATED -- Use #identity event instead", - "required": ["seq", "did", "handle", "time"], - "properties": { - "seq": { "type": "integer" }, - "did": { "type": "string", "format": "did" }, - "handle": { "type": "string", "format": "handle" }, - "time": { "type": "string", "format": "datetime" } - } - }, - "migrate": { - "type": "object", - "description": "DEPRECATED -- Use #account event instead", - "required": ["seq", "did", "migrateTo", "time"], - "nullable": ["migrateTo"], - "properties": { - "seq": { "type": "integer" }, - "did": { "type": "string", "format": "did" }, - "migrateTo": { "type": "string" }, - "time": { "type": "string", "format": "datetime" } - } - }, - "tombstone": { - "type": "object", - "description": "DEPRECATED -- Use #account event instead", - "required": ["seq", "did", "time"], - "properties": { - "seq": { "type": "integer" }, - "did": { "type": "string", "format": "did" }, - "time": { "type": "string", "format": "datetime" } - } - }, "info": { "type": "object", "required": ["name"], diff --git a/package.json b/package.json index 2601cf8493d..a97bf952d60 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "packageManager": "pnpm@8.15.9", "scripts": { "lint:fix": "pnpm lint --fix", - "lint": "eslint . --ext .ts,.js", + "lint": "eslint . --ext .ts,.js,.tsx,.jsx", "style:fix": "prettier --write .", "style": "prettier --check .", "verify": "pnpm --stream '/^verify:.+$/'", @@ -19,13 +19,13 @@ "verify:lint": "pnpm lint", "verify:types": "tsc --build tsconfig.json", "format": "pnpm lint:fix && pnpm style:fix", - "codegen": "pnpm run --recursive --stream --filter '@atproto/lex-cli...' build --force && pnpm run --recursive --stream --parallel codegen", - "build": "pnpm --recursive --stream build", - "dev": "NODE_ENV=development pnpm --stream '/^dev:.+$/'", - "dev:tsc": "tsc --build tsconfig.json --watch", - "dev:pkg": "pnpm --recursive --parallel --stream dev", - "test": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm --stream -r test", - "test:withFlags": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm --stream -r test --", + "precodegen": "pnpm run --recursive --stream --filter '@atproto/lex-cli...' build --force", + "codegen": "pnpm run --recursive --stream --parallel codegen", + "build": "pnpm run --recursive --stream '/^(build|build:.+)$/'", + "dev": "NODE_ENV=development pnpm run --recursive --parallel --stream '/^(dev|dev:.+)$/'", + "dev:tsc": "tsc --build tsconfig.json --preserveWatchOutput --watch", + "test": "LOG_ENABLED=false ./packages/dev-infra/with-test-redis-and-db.sh pnpm test --stream --recursive", + "test:withFlags": "pnpm run test --", "changeset": "changeset", "release": "pnpm build && changeset publish", "version-packages": "changeset version && git add ." diff --git a/packages/api/CHANGELOG.md b/packages/api/CHANGELOG.md index 6e77407b00d..3bdc03d66e9 100644 --- a/packages/api/CHANGELOG.md +++ b/packages/api/CHANGELOG.md @@ -1,5 +1,20 @@ # @atproto/api +## 0.14.8 + +### Patch Changes + +- [#3585](https://github.com/bluesky-social/atproto/pull/3585) [`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1) Thanks [@dholms](https://github.com/dholms)! - Wrap sync v1.1 semantics. Add #sync event to subscribeRepos and deprecate #handle and #tombstone events + +- [#3602](https://github.com/bluesky-social/atproto/pull/3602) [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49) Thanks [@devinivy](https://github.com/devinivy)! - Permit 100mb video embeds. + +- [#2264](https://github.com/bluesky-social/atproto/pull/2264) [`dc6e4ecb0`](https://github.com/bluesky-social/atproto/commit/dc6e4ecb0e09bbf4bc7a79c6ac43fb6da4166200) Thanks [@bnewbold](https://github.com/bnewbold)! - new com.atproto.identity endpoints: resolveDid, resolveIdentity, refreshIdentity + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + - @atproto/lexicon@0.4.8 + - @atproto/xrpc@0.6.10 + ## 0.14.7 ### Patch Changes diff --git a/packages/api/package.json b/packages/api/package.json index 8b97fc50645..476c8c7a246 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/api", - "version": "0.14.7", + "version": "0.14.8", "license": "MIT", "description": "Client library for atproto and Bluesky", "keywords": [ diff --git a/packages/api/src/client/lexicons.ts b/packages/api/src/client/lexicons.ts index 938c406bb35..65d1a545dc2 100644 --- a/packages/api/src/client/lexicons.ts +++ b/packages/api/src/client/lexicons.ts @@ -4020,9 +4020,6 @@ export const schemaDict = { 'lex:com.atproto.sync.subscribeRepos#sync', 'lex:com.atproto.sync.subscribeRepos#identity', 'lex:com.atproto.sync.subscribeRepos#account', - 'lex:com.atproto.sync.subscribeRepos#handle', - 'lex:com.atproto.sync.subscribeRepos#migrate', - 'lex:com.atproto.sync.subscribeRepos#tombstone', 'lex:com.atproto.sync.subscribeRepos#info', ], }, @@ -4226,68 +4223,6 @@ export const schemaDict = { }, }, }, - handle: { - type: 'object', - description: 'DEPRECATED -- Use #identity event instead', - required: ['seq', 'did', 'handle', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - handle: { - type: 'string', - format: 'handle', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - migrate: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'migrateTo', 'time'], - nullable: ['migrateTo'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - migrateTo: { - type: 'string', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - tombstone: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, info: { type: 'object', required: ['name'], @@ -5787,8 +5722,10 @@ export const schemaDict = { properties: { video: { type: 'blob', + description: + 'The mp4 video file. May be up to 100mb, formerly limited to 50mb.', accept: ['video/mp4'], - maxSize: 50000000, + maxSize: 100000000, }, captions: { type: 'array', diff --git a/packages/api/src/client/types/app/bsky/embed/video.ts b/packages/api/src/client/types/app/bsky/embed/video.ts index 10e59e37539..822c2cb2a22 100644 --- a/packages/api/src/client/types/app/bsky/embed/video.ts +++ b/packages/api/src/client/types/app/bsky/embed/video.ts @@ -13,6 +13,7 @@ const id = 'app.bsky.embed.video' export interface Main { $type?: 'app.bsky.embed.video' + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ video: BlobRef captions?: Caption[] /** Alt text description of the video, for accessibility. */ diff --git a/packages/api/src/client/types/com/atproto/sync/subscribeRepos.ts b/packages/api/src/client/types/com/atproto/sync/subscribeRepos.ts index c46173b458d..1d3b628179a 100644 --- a/packages/api/src/client/types/com/atproto/sync/subscribeRepos.ts +++ b/packages/api/src/client/types/com/atproto/sync/subscribeRepos.ts @@ -122,62 +122,6 @@ export function validateAccount(v: V) { return validate(v, id, hashAccount) } -/** DEPRECATED -- Use #identity event instead */ -export interface Handle { - $type?: 'com.atproto.sync.subscribeRepos#handle' - seq: number - did: string - handle: string - time: string -} - -const hashHandle = 'handle' - -export function isHandle(v: V) { - return is$typed(v, id, hashHandle) -} - -export function validateHandle(v: V) { - return validate(v, id, hashHandle) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Migrate { - $type?: 'com.atproto.sync.subscribeRepos#migrate' - seq: number - did: string - migrateTo: string | null - time: string -} - -const hashMigrate = 'migrate' - -export function isMigrate(v: V) { - return is$typed(v, id, hashMigrate) -} - -export function validateMigrate(v: V) { - return validate(v, id, hashMigrate) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Tombstone { - $type?: 'com.atproto.sync.subscribeRepos#tombstone' - seq: number - did: string - time: string -} - -const hashTombstone = 'tombstone' - -export function isTombstone(v: V) { - return is$typed(v, id, hashTombstone) -} - -export function validateTombstone(v: V) { - return validate(v, id, hashTombstone) -} - export interface Info { $type?: 'com.atproto.sync.subscribeRepos#info' name: 'OutdatedCursor' | (string & {}) diff --git a/packages/aws/CHANGELOG.md b/packages/aws/CHANGELOG.md index 1897bed12c1..121f02b7a9d 100644 --- a/packages/aws/CHANGELOG.md +++ b/packages/aws/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/aws +## 0.2.17 + +### Patch Changes + +- Updated dependencies []: + - @atproto/repo@0.7.1 + ## 0.2.16 ### Patch Changes diff --git a/packages/aws/package.json b/packages/aws/package.json index fd865d154c0..21d1dc47ab2 100644 --- a/packages/aws/package.json +++ b/packages/aws/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/aws", - "version": "0.2.16", + "version": "0.2.17", "license": "MIT", "description": "Shared AWS cloud API helpers for atproto services", "keywords": [ diff --git a/packages/bsky/CHANGELOG.md b/packages/bsky/CHANGELOG.md index af43944c091..b15c7aa69be 100644 --- a/packages/bsky/CHANGELOG.md +++ b/packages/bsky/CHANGELOG.md @@ -1,5 +1,19 @@ # @atproto/bsky +## 0.0.125 + +### Patch Changes + +- Updated dependencies [[`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1), [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`dc6e4ecb0`](https://github.com/bluesky-social/atproto/commit/dc6e4ecb0e09bbf4bc7a79c6ac43fb6da4166200)]: + - @atproto/api@0.14.8 + - @atproto/syntax@0.3.4 + - @atproto-labs/fetch-node@0.1.8 + - @atproto/lexicon@0.4.8 + - @atproto/sync@0.1.16 + - @atproto/repo@0.7.1 + - @atproto/xrpc-server@0.7.12 + - @atproto-labs/xrpc-utils@0.0.8 + ## 0.0.124 ### Patch Changes diff --git a/packages/bsky/package.json b/packages/bsky/package.json index 3d2ed1269c2..ce8f0a0ae9d 100644 --- a/packages/bsky/package.json +++ b/packages/bsky/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/bsky", - "version": "0.0.124", + "version": "0.0.125", "license": "MIT", "description": "Reference implementation of app.bsky App View (Bluesky API)", "keywords": [ diff --git a/packages/bsky/src/api/app/bsky/notification/listNotifications.ts b/packages/bsky/src/api/app/bsky/notification/listNotifications.ts index 106cdafd490..31dd5d87d48 100644 --- a/packages/bsky/src/api/app/bsky/notification/listNotifications.ts +++ b/packages/bsky/src/api/app/bsky/notification/listNotifications.ts @@ -1,5 +1,6 @@ import { mapDefined } from '@atproto/common' import { InvalidRequestError } from '@atproto/xrpc-server' +import { ServerConfig } from '../../../../config' import { AppContext } from '../../../../context' import { HydrateCtx, Hydrator } from '../../../../hydration/hydrator' import { Server } from '../../../../lexicon' @@ -15,7 +16,7 @@ import { import { Notification } from '../../../../proto/bsky_pb' import { uriToDid as didFromUri } from '../../../../util/uris' import { Views } from '../../../../views' -import { clearlyBadCursor, resHeaders } from '../../../util' +import { resHeaders } from '../../../util' export default function (server: Server, ctx: AppContext) { const listNotifications = createPipeline( @@ -93,6 +94,23 @@ const paginateNotifications = async (opts: { } } +/** + * Applies a configurable delay to the datetime string of a cursor, + * effectively allowing for a delay on listing the notifications. + * This is useful to allow time for services to process notifications + * before they are listed to the user. + */ +export const delayCursor = ( + cursorStr: string | undefined, + delayMs: number, +): string => { + const nowMinusDelay = Date.now() - delayMs + if (cursorStr === undefined) return new Date(nowMinusDelay).toISOString() + const cursor = new Date(cursorStr).getTime() + if (isNaN(cursor)) return cursorStr + return new Date(Math.min(cursor, nowMinusDelay)).toISOString() +} + const skeleton = async ( input: SkeletonFnInput, ): Promise => { @@ -100,17 +118,20 @@ const skeleton = async ( if (params.seenAt) { throw new InvalidRequestError('The seenAt parameter is unsupported') } + + const originalCursor = params.cursor + const delayedCursor = delayCursor( + originalCursor, + ctx.cfg.notificationsDelayMs, + ) const viewer = params.hydrateCtx.viewer const priority = params.priority ?? (await getPriority(ctx, viewer)) - if (clearlyBadCursor(params.cursor)) { - return { notifs: [], priority } - } const [res, lastSeenRes] = await Promise.all([ paginateNotifications({ ctx, priority, reasons: params.reasons, - cursor: params.cursor, + cursor: delayedCursor, limit: params.limit, viewer, }), @@ -122,7 +143,7 @@ const skeleton = async ( // @NOTE for the first page of results if there's no last-seen time, consider top notification unread // rather than all notifications. bit of a hack to be more graceful when seen times are out of sync. let lastSeenDate = lastSeenRes.timestamp?.toDate() - if (!lastSeenDate && !params.cursor) { + if (!lastSeenDate && !originalCursor) { lastSeenDate = res.notifications.at(0)?.timestamp?.toDate() } return { @@ -210,6 +231,7 @@ const presentation = ( type Context = { hydrator: Hydrator views: Views + cfg: ServerConfig } type Params = QueryParams & { diff --git a/packages/bsky/src/config.ts b/packages/bsky/src/config.ts index a94dde58bc3..e273fdd840b 100644 --- a/packages/bsky/src/config.ts +++ b/packages/bsky/src/config.ts @@ -49,6 +49,8 @@ export interface ServerConfigValues { bigThreadUris: Set bigThreadDepth?: number maxThreadDepth?: number + // notifications + notificationsDelayMs?: number // client config clientCheckEmailConfirmed?: boolean topicsEnabled?: boolean @@ -170,6 +172,10 @@ export class ServerConfig { ? parseInt(process.env.BSKY_MAX_THREAD_DEPTH || '', 10) : undefined + const notificationsDelayMs = process.env.BSKY_NOTIFICATIONS_DELAY_MS + ? parseInt(process.env.BSKY_NOTIFICATIONS_DELAY_MS || '', 10) + : 0 + const disableSsrfProtection = process.env.BSKY_DISABLE_SSRF_PROTECTION ? process.env.BSKY_DISABLE_SSRF_PROTECTION === 'true' : debugMode @@ -231,6 +237,7 @@ export class ServerConfig { bigThreadUris, bigThreadDepth, maxThreadDepth, + notificationsDelayMs, disableSsrfProtection, proxyAllowHTTP2, proxyHeadersTimeout, @@ -426,6 +433,10 @@ export class ServerConfig { return this.cfg.maxThreadDepth } + get notificationsDelayMs() { + return this.cfg.notificationsDelayMs ?? 0 + } + get disableSsrfProtection(): boolean { return this.cfg.disableSsrfProtection ?? false } diff --git a/packages/bsky/src/data-plane/server/db/pagination.ts b/packages/bsky/src/data-plane/server/db/pagination.ts index 90d7be0b84d..acbc47163fb 100644 --- a/packages/bsky/src/data-plane/server/db/pagination.ts +++ b/packages/bsky/src/data-plane/server/db/pagination.ts @@ -2,8 +2,8 @@ import { sql } from 'kysely' import { InvalidRequestError } from '@atproto/xrpc-server' import { AnyQb, DbRef } from './util' -export type Cursor = { primary: string; secondary: string } -export type LabeledResult = { +type KeysetCursor = { primary: string; secondary: string } +type KeysetLabeledResult = { primary: string | number secondary: string | number } @@ -22,14 +22,14 @@ export type LabeledResult = { * Result -*-> LabeledResult <-*-> Cursor <--> packed/string cursor * ↳ SQL Condition */ -export abstract class GenericKeyset { +export abstract class GenericKeyset { constructor( public primary: DbRef, public secondary: DbRef, ) {} abstract labelResult(result: R): LR - abstract labeledResultToCursor(labeled: LR): Cursor - abstract cursorToLabeledResult(cursor: Cursor): LR + abstract labeledResultToCursor(labeled: LR): KeysetCursor + abstract cursorToLabeledResult(cursor: KeysetCursor): LR packFromResult(results: R | R[]): string | undefined { const result = Array.isArray(results) ? results.at(-1) : results if (!result) return @@ -45,11 +45,11 @@ export abstract class GenericKeyset { if (!cursor) return return this.cursorToLabeledResult(cursor) } - packCursor(cursor?: Cursor): string | undefined { + packCursor(cursor?: KeysetCursor): string | undefined { if (!cursor) return return `${cursor.primary}__${cursor.secondary}` } - unpackCursor(cursorStr?: string): Cursor | undefined { + unpackCursor(cursorStr?: string): KeysetCursor | undefined { if (!cursorStr) return const result = cursorStr.split('__') const [primary, secondary, ...others] = result @@ -79,10 +79,43 @@ export abstract class GenericKeyset { } } } + paginate( + qb: QB, + opts: { + limit?: number + cursor?: string + direction?: 'asc' | 'desc' + tryIndex?: boolean + // By default, pg does nullsFirst + nullsLast?: boolean + }, + ): QB { + const { limit, cursor, direction = 'desc', tryIndex, nullsLast } = opts + const keysetSql = this.getSql(this.unpack(cursor), direction, tryIndex) + return qb + .if(!!limit, (q) => q.limit(limit as number)) + .if(!nullsLast, (q) => + q.orderBy(this.primary, direction).orderBy(this.secondary, direction), + ) + .if(!!nullsLast, (q) => + q + .orderBy( + direction === 'asc' + ? sql`${this.primary} asc nulls last` + : sql`${this.primary} desc nulls last`, + ) + .orderBy( + direction === 'asc' + ? sql`${this.secondary} asc nulls last` + : sql`${this.secondary} desc nulls last`, + ), + ) + .if(!!keysetSql, (qb) => (keysetSql ? qb.where(keysetSql) : qb)) as QB + } } type SortAtCidResult = { sortAt: string; cid: string } -type TimeCidLabeledResult = Cursor +type TimeCidLabeledResult = KeysetCursor export class TimeCidKeyset< TimeCidResult = SortAtCidResult, @@ -97,7 +130,7 @@ export class TimeCidKeyset< secondary: labeled.secondary, } } - cursorToLabeledResult(cursor: Cursor) { + cursorToLabeledResult(cursor: KeysetCursor) { const primaryDate = new Date(parseInt(cursor.primary, 10)) if (isNaN(primaryDate.getTime())) { throw new InvalidRequestError('Malformed cursor') @@ -127,6 +160,9 @@ export class IndexedAtDidKeyset extends TimeCidKeyset<{ } } +/** + * This is being deprecated. Use {@link GenericKeyset#paginate} instead. + */ export const paginate = < QB extends AnyQb, K extends GenericKeyset, @@ -142,32 +178,119 @@ export const paginate = < nullsLast?: boolean }, ): QB => { - const { - limit, - cursor, - keyset, - direction = 'desc', - tryIndex, - nullsLast, - } = opts - const keysetSql = keyset.getSql(keyset.unpack(cursor), direction, tryIndex) - return qb - .if(!!limit, (q) => q.limit(limit as number)) - .if(!nullsLast, (q) => - q.orderBy(keyset.primary, direction).orderBy(keyset.secondary, direction), - ) - .if(!!nullsLast, (q) => - q - .orderBy( - direction === 'asc' - ? sql`${keyset.primary} asc nulls last` - : sql`${keyset.primary} desc nulls last`, - ) - .orderBy( + return opts.keyset.paginate(qb, opts) +} + +type SingleKeyCursor = { + primary: string +} + +type SingleKeyLabeledResult = { + primary: string | number +} + +/** + * GenericSingleKey is similar to {@link GenericKeyset} but for a single key cursor. + */ +export abstract class GenericSingleKey { + constructor(public primary: DbRef) {} + abstract labelResult(result: R): LR + abstract labeledResultToCursor(labeled: LR): SingleKeyCursor + abstract cursorToLabeledResult(cursor: SingleKeyCursor): LR + packFromResult(results: R | R[]): string | undefined { + const result = Array.isArray(results) ? results.at(-1) : results + if (!result) return + return this.pack(this.labelResult(result)) + } + pack(labeled?: LR): string | undefined { + if (!labeled) return + const cursor = this.labeledResultToCursor(labeled) + return this.packCursor(cursor) + } + unpack(cursorStr?: string): LR | undefined { + const cursor = this.unpackCursor(cursorStr) + if (!cursor) return + return this.cursorToLabeledResult(cursor) + } + packCursor(cursor?: SingleKeyCursor): string | undefined { + if (!cursor) return + return cursor.primary + } + unpackCursor(cursorStr?: string): SingleKeyCursor | undefined { + if (!cursorStr) return + const result = cursorStr.split('__') + const [primary, ...others] = result + if (!primary || others.length > 0) { + throw new InvalidRequestError('Malformed cursor') + } + return { + primary, + } + } + getSql(labeled?: LR, direction?: 'asc' | 'desc') { + if (labeled === undefined) return + if (direction === 'asc') { + return sql`${this.primary} > ${labeled.primary}` + } + return sql`${this.primary} < ${labeled.primary}` + } + paginate( + qb: QB, + opts: { + limit?: number + cursor?: string + direction?: 'asc' | 'desc' + // By default, pg does nullsFirst + nullsLast?: boolean + }, + ): QB { + const { limit, cursor, direction = 'desc', nullsLast } = opts + const keySql = this.getSql(this.unpack(cursor), direction) + return qb + .if(!!limit, (q) => q.limit(limit as number)) + .if(!nullsLast, (q) => q.orderBy(this.primary, direction)) + .if(!!nullsLast, (q) => + q.orderBy( direction === 'asc' - ? sql`${keyset.secondary} asc nulls last` - : sql`${keyset.secondary} desc nulls last`, + ? sql`${this.primary} asc nulls last` + : sql`${this.primary} desc nulls last`, ), - ) - .if(!!keysetSql, (qb) => (keysetSql ? qb.where(keysetSql) : qb)) as QB + ) + .if(!!keySql, (qb) => (keySql ? qb.where(keySql) : qb)) as QB + } +} + +type SortAtResult = { sortAt: string } +type TimeLabeledResult = SingleKeyCursor + +export class IsoTimeKey extends GenericSingleKey< + TimeResult, + TimeLabeledResult +> { + labelResult(result: TimeResult): TimeLabeledResult + labelResult(result: TimeResult) { + return { primary: result.sortAt } + } + labeledResultToCursor(labeled: TimeLabeledResult) { + return { + primary: new Date(labeled.primary).toISOString(), + } + } + cursorToLabeledResult(cursor: SingleKeyCursor) { + const primaryDate = new Date(cursor.primary) + if (isNaN(primaryDate.getTime())) { + throw new InvalidRequestError('Malformed cursor') + } + return { + primary: primaryDate.toISOString(), + } + } +} + +export class IsoSortAtKey extends IsoTimeKey<{ + sortAt: string +}> { + labelResult(result: { sortAt: string }) { + return { primary: result.sortAt } + } } diff --git a/packages/bsky/src/data-plane/server/routes/notifs.ts b/packages/bsky/src/data-plane/server/routes/notifs.ts index f8a53efca03..6af5ba004e3 100644 --- a/packages/bsky/src/data-plane/server/routes/notifs.ts +++ b/packages/bsky/src/data-plane/server/routes/notifs.ts @@ -3,7 +3,7 @@ import { ServiceImpl } from '@connectrpc/connect' import { sql } from 'kysely' import { Service } from '../../../proto/bsky_connect' import { Database } from '../db' -import { TimeCidKeyset, paginate } from '../db/pagination' +import { IsoSortAtKey } from '../db/pagination' import { countAll, notSoftDeletedClause } from '../db/util' export default (db: Database): Partial> => ({ @@ -41,15 +41,10 @@ export default (db: Database): Partial> => ({ ]) .select(priorityFollowQb.as('priority')) - const keyset = new TimeCidKeyset( - ref('notif.sortAt'), - ref('notif.recordCid'), - ) - builder = paginate(builder, { + const key = new IsoSortAtKey(ref('notif.sortAt')) + builder = key.paginate(builder, { cursor, limit, - keyset, - tryIndex: true, }) const notifsRes = await builder.execute() @@ -63,7 +58,7 @@ export default (db: Database): Partial> => ({ })) return { notifications, - cursor: keyset.packFromResult(notifsRes), + cursor: key.packFromResult(notifsRes), } }, diff --git a/packages/bsky/src/lexicon/lexicons.ts b/packages/bsky/src/lexicon/lexicons.ts index 285183c9028..3297c4731c3 100644 --- a/packages/bsky/src/lexicon/lexicons.ts +++ b/packages/bsky/src/lexicon/lexicons.ts @@ -4020,9 +4020,6 @@ export const schemaDict = { 'lex:com.atproto.sync.subscribeRepos#sync', 'lex:com.atproto.sync.subscribeRepos#identity', 'lex:com.atproto.sync.subscribeRepos#account', - 'lex:com.atproto.sync.subscribeRepos#handle', - 'lex:com.atproto.sync.subscribeRepos#migrate', - 'lex:com.atproto.sync.subscribeRepos#tombstone', 'lex:com.atproto.sync.subscribeRepos#info', ], }, @@ -4226,68 +4223,6 @@ export const schemaDict = { }, }, }, - handle: { - type: 'object', - description: 'DEPRECATED -- Use #identity event instead', - required: ['seq', 'did', 'handle', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - handle: { - type: 'string', - format: 'handle', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - migrate: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'migrateTo', 'time'], - nullable: ['migrateTo'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - migrateTo: { - type: 'string', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - tombstone: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, info: { type: 'object', required: ['name'], @@ -5787,8 +5722,10 @@ export const schemaDict = { properties: { video: { type: 'blob', + description: + 'The mp4 video file. May be up to 100mb, formerly limited to 50mb.', accept: ['video/mp4'], - maxSize: 50000000, + maxSize: 100000000, }, captions: { type: 'array', diff --git a/packages/bsky/src/lexicon/types/app/bsky/embed/video.ts b/packages/bsky/src/lexicon/types/app/bsky/embed/video.ts index 10e59e37539..822c2cb2a22 100644 --- a/packages/bsky/src/lexicon/types/app/bsky/embed/video.ts +++ b/packages/bsky/src/lexicon/types/app/bsky/embed/video.ts @@ -13,6 +13,7 @@ const id = 'app.bsky.embed.video' export interface Main { $type?: 'app.bsky.embed.video' + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ video: BlobRef captions?: Caption[] /** Alt text description of the video, for accessibility. */ diff --git a/packages/bsky/src/lexicon/types/com/atproto/sync/subscribeRepos.ts b/packages/bsky/src/lexicon/types/com/atproto/sync/subscribeRepos.ts index decfb210b25..318d096aa83 100644 --- a/packages/bsky/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +++ b/packages/bsky/src/lexicon/types/com/atproto/sync/subscribeRepos.ts @@ -22,9 +22,6 @@ export type OutputSchema = | $Typed | $Typed | $Typed - | $Typed - | $Typed - | $Typed | $Typed | { $type: string } export type HandlerError = ErrorFrame<'FutureCursor' | 'ConsumerTooSlow'> @@ -150,62 +147,6 @@ export function validateAccount(v: V) { return validate(v, id, hashAccount) } -/** DEPRECATED -- Use #identity event instead */ -export interface Handle { - $type?: 'com.atproto.sync.subscribeRepos#handle' - seq: number - did: string - handle: string - time: string -} - -const hashHandle = 'handle' - -export function isHandle(v: V) { - return is$typed(v, id, hashHandle) -} - -export function validateHandle(v: V) { - return validate(v, id, hashHandle) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Migrate { - $type?: 'com.atproto.sync.subscribeRepos#migrate' - seq: number - did: string - migrateTo: string | null - time: string -} - -const hashMigrate = 'migrate' - -export function isMigrate(v: V) { - return is$typed(v, id, hashMigrate) -} - -export function validateMigrate(v: V) { - return validate(v, id, hashMigrate) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Tombstone { - $type?: 'com.atproto.sync.subscribeRepos#tombstone' - seq: number - did: string - time: string -} - -const hashTombstone = 'tombstone' - -export function isTombstone(v: V) { - return is$typed(v, id, hashTombstone) -} - -export function validateTombstone(v: V) { - return validate(v, id, hashTombstone) -} - export interface Info { $type?: 'com.atproto.sync.subscribeRepos#info' name: 'OutdatedCursor' | (string & {}) diff --git a/packages/bsky/tests/views/__snapshots__/notifications.test.ts.snap b/packages/bsky/tests/views/__snapshots__/notifications.test.ts.snap index 112b8c7ad8e..68753f6f26b 100644 --- a/packages/bsky/tests/views/__snapshots__/notifications.test.ts.snap +++ b/packages/bsky/tests/views/__snapshots__/notifications.test.ts.snap @@ -384,7 +384,7 @@ Array [ exports[`notification views fetches notifications with default priority 1`] = ` Object { - "cursor": "0000000000000__bafycid", + "cursor": "1970-01-01T00:00:00.000Z", "notifications": Array [ Object { "author": Object { @@ -486,7 +486,7 @@ Object { exports[`notification views fetches notifications with explicit priority 1`] = ` Object { - "cursor": "0000000000000__bafycid", + "cursor": "1970-01-01T00:00:00.000Z", "notifications": Array [ Object { "author": Object { @@ -588,7 +588,7 @@ Object { exports[`notification views fetches notifications with explicit priority 2`] = ` Object { - "cursor": "0000000000000__bafycid", + "cursor": "1970-01-01T00:00:00.000Z", "notifications": Array [ Object { "author": Object { diff --git a/packages/bsky/tests/views/notifications.test.ts b/packages/bsky/tests/views/notifications.test.ts index 94250ae1bb3..31f462eed16 100644 --- a/packages/bsky/tests/views/notifications.test.ts +++ b/packages/bsky/tests/views/notifications.test.ts @@ -1,5 +1,6 @@ import { AtpAgent } from '@atproto/api' import { SeedClient, TestNetwork, basicSeed } from '@atproto/dev-env' +import { delayCursor } from '../../src/api/app/bsky/notification/listNotifications' import { ids } from '../../src/lexicon/lexicons' import { Notification } from '../../src/lexicon/types/app/bsky/notification/listNotifications' import { forSnapshot, paginateAll } from '../_util' @@ -497,17 +498,196 @@ describe('notification views', () => { expect(results(paginatedAll)).toEqual(results([full.data])) }) - it('fails open on clearly bad cursor.', async () => { - const { data: notifs } = - await agent.api.app.bsky.notification.listNotifications( - { cursor: '90210::bafycid' }, - { - headers: await network.serviceHeaders( - alice, - ids.AppBskyNotificationListNotifications, - ), + describe('notifications delay', () => { + const notificationsDelayMs = 5_000 + + let delayNetwork: TestNetwork + let delayAgent: AtpAgent + let delaySc: SeedClient + let delayAlice: string + + beforeAll(async () => { + delayNetwork = await TestNetwork.create({ + bsky: { + notificationsDelayMs, }, + dbPostgresSchema: 'bsky_views_notifications_delay', + }) + delayAgent = delayNetwork.bsky.getClient() + delaySc = delayNetwork.getSeedClient() + await basicSeed(delaySc) + await delayNetwork.processAll() + delayAlice = delaySc.dids.alice + + // Add to reply chain, post ancestors: alice -> bob -> alice -> carol. + // Should have added one notification for each of alice and bob. + await delaySc.reply( + delaySc.dids.carol, + delaySc.posts[delayAlice][1].ref, + delaySc.replies[delayAlice][0].ref, + 'indeed', + ) + await delayNetwork.processAll() + + // @NOTE: Use fake timers after inserting seed data, + // to avoid inserting all notifications with the same timestamp. + jest.useFakeTimers({ + doNotFake: [ + 'nextTick', + 'performance', + 'setImmediate', + 'setInterval', + 'setTimeout', + ], + }) + }) + + afterAll(async () => { + jest.useRealTimers() + await delayNetwork.close() + }) + + it('paginates', async () => { + const firstNotification = await delayNetwork.bsky.db.db + .selectFrom('notification') + .selectAll() + .limit(1) + .orderBy('sortAt', 'asc') + .executeTakeFirstOrThrow() + // Sets the system time to when the first notification happened. + // At this point we won't have any notifications that already crossed the delay threshold. + jest.setSystemTime(new Date(firstNotification.sortAt)) + + const results = (results) => + sort(results.flatMap((res) => res.notifications)) + const paginator = async (cursor?: string) => { + const res = + await delayAgent.api.app.bsky.notification.listNotifications( + { cursor, limit: 6 }, + { + headers: await delayNetwork.serviceHeaders( + delayAlice, + ids.AppBskyNotificationListNotifications, + ), + }, + ) + return res.data + } + + const paginatedAllBeforeDelay = await paginateAll(paginator) + paginatedAllBeforeDelay.forEach((res) => + expect(res.notifications.length).toBe(0), + ) + const fullBeforeDelay = + await delayAgent.api.app.bsky.notification.listNotifications( + {}, + { + headers: await delayNetwork.serviceHeaders( + delayAlice, + ids.AppBskyNotificationListNotifications, + ), + }, + ) + + expect(fullBeforeDelay.data.notifications.length).toEqual(0) + expect(results(paginatedAllBeforeDelay)).toEqual( + results([fullBeforeDelay.data]), + ) + + const lastNotification = await delayNetwork.bsky.db.db + .selectFrom('notification') + .selectAll() + .limit(1) + .orderBy('sortAt', 'desc') + .executeTakeFirstOrThrow() + // Sets the system time to when the last notification happened and the delay has elapsed. + // At this point we all notifications already crossed the delay threshold. + jest.setSystemTime( + new Date( + new Date(lastNotification.sortAt).getTime() + + notificationsDelayMs + + 1, + ), ) - expect(notifs).toMatchObject({ notifications: [] }) + + const paginatedAllAfterDelay = await paginateAll(paginator) + paginatedAllAfterDelay.forEach((res) => + expect(res.notifications.length).toBeLessThanOrEqual(6), + ) + const fullAfterDelay = + await delayAgent.api.app.bsky.notification.listNotifications( + {}, + { + headers: await delayNetwork.serviceHeaders( + delayAlice, + ids.AppBskyNotificationListNotifications, + ), + }, + ) + + expect(fullAfterDelay.data.notifications.length).toEqual(13) + expect(results(paginatedAllAfterDelay)).toEqual( + results([fullAfterDelay.data]), + ) + }) + + describe('cursor delay', () => { + const delay0s = 0 + const delay5s = 5_000 + + const now = '2021-01-01T01:00:00.000Z' + const nowMinus2s = '2021-01-01T00:59:58.000Z' + const nowMinus5s = '2021-01-01T00:59:55.000Z' + const nowMinus8s = '2021-01-01T00:59:52.000Z' + + beforeAll(async () => { + jest.useFakeTimers({ doNotFake: ['performance'] }) + jest.setSystemTime(new Date(now)) + }) + + afterAll(async () => { + jest.useRealTimers() + }) + + describe('for undefined cursor', () => { + it('returns now minus delay', async () => { + const delayedCursor = delayCursor(undefined, delay5s) + expect(delayedCursor).toBe(nowMinus5s) + }) + + it('returns now if delay is 0', async () => { + const delayedCursor = delayCursor(undefined, delay0s) + expect(delayedCursor).toBe(now) + }) + }) + + describe('for defined cursor', () => { + it('returns original cursor if delay is 0', async () => { + const originalCursor = nowMinus2s + const delayedCursor = delayCursor(originalCursor, delay0s) + expect(delayedCursor).toBe(originalCursor) + }) + + it('returns "now minus delay" for cursor that is after that', async () => { + // Cursor is "now - 2s", should become "now - 5s" + const originalCursor = nowMinus2s + const cursor = delayCursor(originalCursor, delay5s) + expect(cursor).toBe(nowMinus5s) + }) + + it('returns original cursor for cursor that is before "now minus delay"', async () => { + // Cursor is "now - 8s", should stay like that. + const originalCursor = nowMinus8s + const cursor = delayCursor(originalCursor, delay5s) + expect(cursor).toBe(originalCursor) + }) + + it('passes through a non-date cursor', async () => { + const originalCursor = '123_abc' + const cursor = delayCursor(originalCursor, delay5s) + expect(cursor).toBe(originalCursor) + }) + }) + }) }) }) diff --git a/packages/bsync/CHANGELOG.md b/packages/bsync/CHANGELOG.md index f413bfd4ae2..bf35fe02c6b 100644 --- a/packages/bsync/CHANGELOG.md +++ b/packages/bsync/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/bsync +## 0.0.15 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + ## 0.0.14 ### Patch Changes diff --git a/packages/bsync/package.json b/packages/bsync/package.json index 3b6707eb312..beaf5d1ca6b 100644 --- a/packages/bsync/package.json +++ b/packages/bsync/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/bsync", - "version": "0.0.14", + "version": "0.0.15", "license": "MIT", "description": "Sychronizing service for app.bsky App View (Bluesky API)", "keywords": [ diff --git a/packages/crypto/tests/random.test.ts b/packages/crypto/tests/random.test.ts index 349bf0c2f27..1922f32529f 100644 --- a/packages/crypto/tests/random.test.ts +++ b/packages/crypto/tests/random.test.ts @@ -10,6 +10,6 @@ describe('randomIntFromSeed()', () => { } const [zero, one] = counts expect(zero + one).toEqual(10000) - expect(Math.max(zero, one) / Math.min(zero, one)).toBeLessThan(1.05) + expect(Math.max(zero, one) / Math.min(zero, one)).toBeLessThan(1.1) }) }) diff --git a/packages/dev-env/CHANGELOG.md b/packages/dev-env/CHANGELOG.md index f50ca104365..d5fec022055 100644 --- a/packages/dev-env/CHANGELOG.md +++ b/packages/dev-env/CHANGELOG.md @@ -1,5 +1,20 @@ # @atproto/dev-env +## 0.3.98 + +### Patch Changes + +- Updated dependencies [[`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1), [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`dc6e4ecb0`](https://github.com/bluesky-social/atproto/commit/dc6e4ecb0e09bbf4bc7a79c6ac43fb6da4166200), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/api@0.14.8 + - @atproto/pds@0.4.105 + - @atproto/syntax@0.3.4 + - @atproto/bsky@0.0.125 + - @atproto/ozone@0.1.87 + - @atproto/bsync@0.0.15 + - @atproto/lexicon@0.4.8 + - @atproto/sync@0.1.16 + - @atproto/xrpc-server@0.7.12 + ## 0.3.97 ### Patch Changes diff --git a/packages/dev-env/package.json b/packages/dev-env/package.json index 435a3fc8408..def7529a921 100644 --- a/packages/dev-env/package.json +++ b/packages/dev-env/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/dev-env", - "version": "0.3.97", + "version": "0.3.98", "license": "MIT", "description": "Local development environment helper for atproto development", "keywords": [ diff --git a/packages/dev-env/src/pds.ts b/packages/dev-env/src/pds.ts index d721a8797cd..5c2f7bd7e02 100644 --- a/packages/dev-env/src/pds.ts +++ b/packages/dev-env/src/pds.ts @@ -37,7 +37,9 @@ export class TestPds { recoveryDidKey: recoveryKey, adminPassword: ADMIN_PASSWORD, jwtSecret: JWT_SECRET, - serviceHandleDomains: ['.test'], + // @NOTE ".example" will not actually work and is only used to display + // multiple domains in the sing-up UI + serviceHandleDomains: ['.test', '.example'], bskyAppViewUrl: 'https://appview.invalid', bskyAppViewDid: 'did:example:invalid', bskyAppViewCdnUrlPattern: 'http://cdn.appview.com/%s/%s/%s', @@ -47,10 +49,15 @@ export class TestPds { inviteRequired: false, disableSsrfProtection: true, serviceName: 'Development PDS', - brandColor: '#ffcb1e', - errorColor: undefined, + brandColor: '#8338ec', + errorColor: '#ff006e', + warningColor: '#fb5607', + successColor: '#02c39a', logoUrl: - 'https://uxwing.com/wp-content/themes/uxwing/download/animals-and-birds/bee-icon.png', + // Using a "data:" instead of a real URL to avoid making CORS requests in dev. + // License: https://uxwing.com/license/ + // Source: https://uxwing.com/bee-icon/ + `data:image/svg+xml;base64,${Buffer.from('bee', 'utf8').toString('base64')}`, homeUrl: 'https://bsky.social/', termsOfServiceUrl: 'https://bsky.social/about/support/tos', privacyPolicyUrl: 'https://bsky.social/about/support/privacy-policy', diff --git a/packages/internal/did-resolver/CHANGELOG.md b/packages/internal/did-resolver/CHANGELOG.md index 4d76e1da92f..6bed1f2dd4b 100644 --- a/packages/internal/did-resolver/CHANGELOG.md +++ b/packages/internal/did-resolver/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto-labs/did-resolver +## 0.1.11 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto-labs/fetch@0.2.2 + ## 0.1.10 ### Patch Changes diff --git a/packages/internal/did-resolver/package.json b/packages/internal/did-resolver/package.json index 4418651516b..b8514221b18 100644 --- a/packages/internal/did-resolver/package.json +++ b/packages/internal/did-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/did-resolver", - "version": "0.1.10", + "version": "0.1.11", "license": "MIT", "description": "DID resolution and verification library", "keywords": [ diff --git a/packages/internal/fetch-node/CHANGELOG.md b/packages/internal/fetch-node/CHANGELOG.md index 8c08c5fb633..877c4f08b47 100644 --- a/packages/internal/fetch-node/CHANGELOG.md +++ b/packages/internal/fetch-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto-labs/fetch-node +## 0.1.8 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto-labs/fetch@0.2.2 + ## 0.1.7 ### Patch Changes diff --git a/packages/internal/fetch-node/package.json b/packages/internal/fetch-node/package.json index d9dfebfa57d..5e66ddda1d4 100644 --- a/packages/internal/fetch-node/package.json +++ b/packages/internal/fetch-node/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/fetch-node", - "version": "0.1.7", + "version": "0.1.8", "license": "MIT", "description": "SSRF protection for fetch() in Node.js", "keywords": [ diff --git a/packages/internal/fetch/CHANGELOG.md b/packages/internal/fetch/CHANGELOG.md index 5471107d4ba..3ab0d02dbcd 100644 --- a/packages/internal/fetch/CHANGELOG.md +++ b/packages/internal/fetch/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto-labs/fetch +## 0.2.2 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improved error response parsing + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove explicit dependency on "zod". Improved typing of `fetchJsonZodProcessor` function. + ## 0.2.1 ### Patch Changes diff --git a/packages/internal/fetch/package.json b/packages/internal/fetch/package.json index 4c5b913a303..5c490d9a083 100644 --- a/packages/internal/fetch/package.json +++ b/packages/internal/fetch/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/fetch", - "version": "0.2.1", + "version": "0.2.2", "license": "MIT", "description": "Isomorphic wrapper utilities for fetch API", "keywords": [ @@ -28,9 +28,6 @@ "devDependencies": { "typescript": "^5.6.3" }, - "optionalDependencies": { - "zod": "^3.23.8" - }, "scripts": { "build": "tsc --build tsconfig.json" } diff --git a/packages/internal/fetch/src/fetch-response.ts b/packages/internal/fetch/src/fetch-response.ts index 09e903bfefd..d483fc81951 100644 --- a/packages/internal/fetch/src/fetch-response.ts +++ b/packages/internal/fetch/src/fetch-response.ts @@ -1,4 +1,3 @@ -import type { ParseParams, TypeOf, ZodTypeAny } from 'zod' import { Transformer, pipe } from '@atproto-labs/pipe' import { FetchError } from './fetch-error.js' import { TransformedResponse } from './transformed-response.js' @@ -6,7 +5,6 @@ import { Json, MaxBytesTransformStream, cancelBody, - ifObject, ifString, logCancellationError, } from './util.js' @@ -69,15 +67,16 @@ const extractResponseMessage: ResponseMessageGetter = async (response) => { const json: unknown = await response.json() if (typeof json === 'string') return json + if (typeof json === 'object' && json != null) { + const errorDescription = ifString(json['error_description']) + if (errorDescription) return errorDescription - const errorDescription = ifString(ifObject(json)?.['error_description']) - if (errorDescription) return errorDescription + const error = ifString(json['error']) + if (error) return error - const error = ifString(ifObject(json)?.['error']) - if (error) return error - - const message = ifString(ifObject(json)?.['message']) - if (message) return message + const message = ifString(json['message']) + if (message) return message + } } } catch { // noop @@ -283,10 +282,31 @@ export function fetchJsonProcessor( ) } -export function fetchJsonZodProcessor( - schema: S, - params?: Partial, -): Transformer> { - return async (jsonResponse: ParsedJsonResponse): Promise> => - schema.parseAsync(jsonResponse.json, params) +export type SyncValidationSchema = { + parse(value: unknown, params?: P): S +} + +export type AsyncValidationSchema = { + parseAsync(value: unknown, params?: P): Promise +} + +export function fetchJsonValidatorProcessor( + schema: SyncValidationSchema | AsyncValidationSchema, + params?: P, +): Transformer { + if ('parseAsync' in schema && typeof schema.parseAsync === 'function') { + return async (jsonResponse: ParsedJsonResponse): Promise => + schema.parseAsync(jsonResponse.json, params) + } + + if ('parse' in schema && typeof schema.parse === 'function') { + return async (jsonResponse: ParsedJsonResponse): Promise => + schema.parse(jsonResponse.json, params) + } + + // Needed for type safety (and allows fool proofing the usage of this function) + throw new TypeError('Invalid schema') } + +/** @note Use {@link fetchJsonValidatorProcessor} instead */ +export const fetchJsonZodProcessor = fetchJsonValidatorProcessor diff --git a/packages/internal/fetch/src/util.ts b/packages/internal/fetch/src/util.ts index 67d5a81e28f..00b64d0e328 100644 --- a/packages/internal/fetch/src/util.ts +++ b/packages/internal/fetch/src/util.ts @@ -24,24 +24,6 @@ export function isIp(hostname: string) { return false } -const plainObjectProto = Object.prototype -export const ifObject = (v: V) => { - if (typeof v === 'object' && v != null && !Array.isArray(v)) { - const proto = Object.getPrototypeOf(v) - if (proto === null || proto === plainObjectProto) { - // eslint-disable-next-line @typescript-eslint/ban-types - return v as V extends JsonScalar | JsonArray | Function | symbol - ? never - : V extends Json - ? V - : // Plain object are (mostly) safe to access using a string index - Record - } - } - - return undefined -} - export const ifString = (v: V) => (typeof v === 'string' ? v : undefined) export class MaxBytesTransformStream extends TransformStream< diff --git a/packages/internal/handle-resolver-node/CHANGELOG.md b/packages/internal/handle-resolver-node/CHANGELOG.md index c651786ad0d..c6a5925c97e 100644 --- a/packages/internal/handle-resolver-node/CHANGELOG.md +++ b/packages/internal/handle-resolver-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto-labs/handle-resolver-node +## 0.1.14 + +### Patch Changes + +- Updated dependencies []: + - @atproto-labs/fetch-node@0.1.8 + ## 0.1.13 ### Patch Changes diff --git a/packages/internal/handle-resolver-node/package.json b/packages/internal/handle-resolver-node/package.json index 6afb68f21d8..be2e5f60fc1 100644 --- a/packages/internal/handle-resolver-node/package.json +++ b/packages/internal/handle-resolver-node/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/handle-resolver-node", - "version": "0.1.13", + "version": "0.1.14", "license": "MIT", "description": "Node specific ATProto handle to DID resolver", "keywords": [ diff --git a/packages/internal/identity-resolver/CHANGELOG.md b/packages/internal/identity-resolver/CHANGELOG.md index 4c8c5068b5d..e33760b24c8 100644 --- a/packages/internal/identity-resolver/CHANGELOG.md +++ b/packages/internal/identity-resolver/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto-labs/identity-resolver +## 0.1.14 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + - @atproto-labs/did-resolver@0.1.11 + ## 0.1.13 ### Patch Changes diff --git a/packages/internal/identity-resolver/package.json b/packages/internal/identity-resolver/package.json index 00ab6469b0b..97e62b2eede 100644 --- a/packages/internal/identity-resolver/package.json +++ b/packages/internal/identity-resolver/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/identity-resolver", - "version": "0.1.13", + "version": "0.1.14", "license": "MIT", "description": "A library resolving ATPROTO identities", "keywords": [ diff --git a/packages/internal/rollup-plugin-bundle-manifest/CHANGELOG.md b/packages/internal/rollup-plugin-bundle-manifest/CHANGELOG.md index 616507a3cd6..4b836bbc890 100644 --- a/packages/internal/rollup-plugin-bundle-manifest/CHANGELOG.md +++ b/packages/internal/rollup-plugin-bundle-manifest/CHANGELOG.md @@ -1,5 +1,11 @@ # @atproto-labs/rollup-plugin-bundle-manifest +## 0.1.2 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Improve typing of plugin + ## 0.1.1 ### Patch Changes diff --git a/packages/internal/rollup-plugin-bundle-manifest/package.json b/packages/internal/rollup-plugin-bundle-manifest/package.json index 0c99c0b0395..a68ec4408d2 100644 --- a/packages/internal/rollup-plugin-bundle-manifest/package.json +++ b/packages/internal/rollup-plugin-bundle-manifest/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/rollup-plugin-bundle-manifest", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "description": "Library for generating a manifest of bundled files from a Rollup build", "keywords": [ diff --git a/packages/internal/rollup-plugin-bundle-manifest/src/index.ts b/packages/internal/rollup-plugin-bundle-manifest/src/index.ts index dae083970d2..8c5623e0d64 100644 --- a/packages/internal/rollup-plugin-bundle-manifest/src/index.ts +++ b/packages/internal/rollup-plugin-bundle-manifest/src/index.ts @@ -32,7 +32,7 @@ export default function bundleManifest({ }: { name?: string data?: boolean -} = {}): Plugin { +} = {}): Plugin { return { name: 'bundle-manifest', generateBundle(outputOptions, bundle) { diff --git a/packages/internal/xrpc-utils/CHANGELOG.md b/packages/internal/xrpc-utils/CHANGELOG.md index f955202ed08..606a6374906 100644 --- a/packages/internal/xrpc-utils/CHANGELOG.md +++ b/packages/internal/xrpc-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto-labs/xrpc-utils +## 0.0.8 + +### Patch Changes + +- Updated dependencies []: + - @atproto/xrpc@0.6.10 + - @atproto/xrpc-server@0.7.12 + ## 0.0.7 ### Patch Changes diff --git a/packages/internal/xrpc-utils/package.json b/packages/internal/xrpc-utils/package.json index 2ec69b13659..90fa7b50894 100644 --- a/packages/internal/xrpc-utils/package.json +++ b/packages/internal/xrpc-utils/package.json @@ -1,6 +1,6 @@ { "name": "@atproto-labs/xrpc-utils", - "version": "0.0.7", + "version": "0.0.8", "license": "MIT", "description": "XRPC server utilities for Node.JS", "keywords": [ diff --git a/packages/lex-cli/CHANGELOG.md b/packages/lex-cli/CHANGELOG.md index 6ed1b9e2adf..ec7cd11f16e 100644 --- a/packages/lex-cli/CHANGELOG.md +++ b/packages/lex-cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto/lex-cli +## 0.6.2 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + - @atproto/lexicon@0.4.8 + ## 0.6.1 ### Patch Changes diff --git a/packages/lex-cli/package.json b/packages/lex-cli/package.json index 1ac629dba4f..e2e13b7e999 100644 --- a/packages/lex-cli/package.json +++ b/packages/lex-cli/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/lex-cli", - "version": "0.6.1", + "version": "0.6.2", "license": "MIT", "description": "TypeScript codegen tool for atproto Lexicon schemas", "keywords": [ diff --git a/packages/lexicon/CHANGELOG.md b/packages/lexicon/CHANGELOG.md index d90eece13cc..0070b229290 100644 --- a/packages/lexicon/CHANGELOG.md +++ b/packages/lexicon/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/lexicon +## 0.4.8 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + ## 0.4.7 ### Patch Changes diff --git a/packages/lexicon/package.json b/packages/lexicon/package.json index 70299e3760c..cd14cc4e5f9 100644 --- a/packages/lexicon/package.json +++ b/packages/lexicon/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/lexicon", - "version": "0.4.7", + "version": "0.4.8", "license": "MIT", "description": "atproto Lexicon schema language library", "keywords": [ diff --git a/packages/oauth/jwk-jose/CHANGELOG.md b/packages/oauth/jwk-jose/CHANGELOG.md index 22e3f9649ff..35ce9a31356 100644 --- a/packages/oauth/jwk-jose/CHANGELOG.md +++ b/packages/oauth/jwk-jose/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/jwk-jose +## 0.1.5 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/jwk@0.1.4 + ## 0.1.4 ### Patch Changes diff --git a/packages/oauth/jwk-jose/package.json b/packages/oauth/jwk-jose/package.json index ff016b94877..fe9d05f78e8 100644 --- a/packages/oauth/jwk-jose/package.json +++ b/packages/oauth/jwk-jose/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/jwk-jose", - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "description": "`jose` based implementation of @atproto/jwk Key's", "keywords": [ diff --git a/packages/oauth/jwk-webcrypto/CHANGELOG.md b/packages/oauth/jwk-webcrypto/CHANGELOG.md index 689b135f8bb..7f18fb62c7a 100644 --- a/packages/oauth/jwk-webcrypto/CHANGELOG.md +++ b/packages/oauth/jwk-webcrypto/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto/jwk-webcrypto +## 0.1.5 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/jwk@0.1.4 + - @atproto/jwk-jose@0.1.5 + ## 0.1.4 ### Patch Changes diff --git a/packages/oauth/jwk-webcrypto/package.json b/packages/oauth/jwk-webcrypto/package.json index 625314c14cf..55045934254 100644 --- a/packages/oauth/jwk-webcrypto/package.json +++ b/packages/oauth/jwk-webcrypto/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/jwk-webcrypto", - "version": "0.1.4", + "version": "0.1.5", "license": "MIT", "description": "Webcrypto based implementation of @atproto/jwk Key's", "keywords": [ diff --git a/packages/oauth/jwk/CHANGELOG.md b/packages/oauth/jwk/CHANGELOG.md index e63d1aa2d30..4a92d95014f 100644 --- a/packages/oauth/jwk/CHANGELOG.md +++ b/packages/oauth/jwk/CHANGELOG.md @@ -1,5 +1,11 @@ # @atproto/jwk +## 0.1.4 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Properly support locales with 3 chars (Asturian) + ## 0.1.3 ### Patch Changes diff --git a/packages/oauth/jwk/package.json b/packages/oauth/jwk/package.json index 6679f0fc6ad..2fa94d071eb 100644 --- a/packages/oauth/jwk/package.json +++ b/packages/oauth/jwk/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/jwk", - "version": "0.1.3", + "version": "0.1.4", "license": "MIT", "description": "A library for working with JSON Web Keys (JWKs) in TypeScript. This is meant to be extended by environment-specific libraries like @atproto/jwk-jose.", "keywords": [ diff --git a/packages/oauth/jwk/src/jwt.ts b/packages/oauth/jwk/src/jwt.ts index 75a84557fc4..264857fd3fc 100644 --- a/packages/oauth/jwk/src/jwt.ts +++ b/packages/oauth/jwk/src/jwt.ts @@ -129,7 +129,7 @@ export const jwtPayloadSchema = z .optional(), locale: z .string() - .regex(/^[a-z]{2}(-[A-Z]{2})?$/) + .regex(/^[a-z]{2,3}(-[A-Z]{2})?$/) .optional(), updated_at: z.number().int().optional(), diff --git a/packages/oauth/oauth-client-browser-example/CHANGELOG.md b/packages/oauth/oauth-client-browser-example/CHANGELOG.md index 2446a72cb0b..7bf3e5666c5 100644 --- a/packages/oauth/oauth-client-browser-example/CHANGELOG.md +++ b/packages/oauth/oauth-client-browser-example/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto/oauth-client-browser-example +## 0.0.2 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Update react to version 19 + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Build using SWC + ## 0.0.1 ### Patch Changes diff --git a/packages/oauth/oauth-client-browser-example/package.json b/packages/oauth/oauth-client-browser-example/package.json index ba383a705fa..b931cda7254 100644 --- a/packages/oauth/oauth-client-browser-example/package.json +++ b/packages/oauth/oauth-client-browser-example/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-client-browser-example", - "version": "0.0.1", + "version": "0.0.2", "license": "MIT", "description": "Example single page application app using ATPROTO OAuth", "keywords": [ @@ -38,15 +38,14 @@ "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-html": "^1.0.4", "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.5", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", - "@types/react": "^18.2.50", - "@types/react-dom": "^18.2.18", + "@rollup/plugin-swc": "^0.4.0", + "@swc/helpers": "^0.5.15", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "autoprefixer": "^10.4.17", "postcss": "^8.4.33", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rollup": "^4.13.0", "rollup-plugin-postcss": "^4.0.2", "rollup-plugin-serve": "^1.1.1", diff --git a/packages/oauth/oauth-client-browser-example/rollup.config.js b/packages/oauth/oauth-client-browser-example/rollup.config.js index 73f561c3d22..b5b79e5899b 100644 --- a/packages/oauth/oauth-client-browser-example/rollup.config.js +++ b/packages/oauth/oauth-client-browser-example/rollup.config.js @@ -4,9 +4,7 @@ const { default: commonjs } = require('@rollup/plugin-commonjs') const { default: html, makeHtmlAttributes } = require('@rollup/plugin-html') const { default: json } = require('@rollup/plugin-json') const { default: nodeResolve } = require('@rollup/plugin-node-resolve') -const { default: replace } = require('@rollup/plugin-replace') -const { default: terser } = require('@rollup/plugin-terser') -const { default: typescript } = require('@rollup/plugin-typescript') +const { default: swc } = require('@rollup/plugin-swc') const { defineConfig } = require('rollup') const { default: manifest, @@ -19,7 +17,7 @@ module.exports = defineConfig((commandLineArguments) => { process.env['NODE_ENV'] ?? (commandLineArguments.watch ? 'development' : 'production') - const minify = NODE_ENV !== 'development' + const devMode = NODE_ENV === 'development' return { input: 'src/main.tsx', @@ -30,17 +28,46 @@ module.exports = defineConfig((commandLineArguments) => { format: 'iife', }, plugins: [ + { + name: 'resolve-swc-helpers', + resolveId(src) { + // For some reason, "nodeResolve" doesn't resolve these: + if (src.startsWith('@swc/helpers/')) return require.resolve(src) + }, + }, nodeResolve({ preferBuiltins: false, browser: true }), commonjs(), json(), postcss({ config: true, extract: true, minimize: false }), - typescript({ - tsconfig: './tsconfig.build.json', - outputToFilesystem: true, - }), - replace({ - preventAssignment: true, - values: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + swc({ + swc: { + swcrc: false, + configFile: false, + sourceMaps: true, + minify: !devMode, + jsc: { + minify: { + compress: { + module: true, + unused: true, + }, + mangle: true, + }, + externalHelpers: true, + target: 'es2020', + parser: { syntax: 'typescript', tsx: true }, + transform: { + useDefineForClassFields: true, + react: { runtime: 'automatic' }, + optimizer: { + simplify: true, + globals: { + vars: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + }, + }, + }, + }, + }, }), html({ title: 'OAuth Client Example', @@ -79,7 +106,6 @@ module.exports = defineConfig((commandLineArguments) => { `, }), - minify && terser({}), manifest({ name: 'files.json', data: true }), commandLineArguments.watch && diff --git a/packages/oauth/oauth-client-browser-example/src/app.tsx b/packages/oauth/oauth-client-browser-example/src/app.tsx index 82e01236170..77958788e55 100644 --- a/packages/oauth/oauth-client-browser-example/src/app.tsx +++ b/packages/oauth/oauth-client-browser-example/src/app.tsx @@ -1,6 +1,6 @@ import { useCallback, useState } from 'react' -import { useAuthContext } from './auth/auth-provider' import { OAuthSession } from '@atproto/oauth-client' +import { useAuthContext } from './auth/auth-provider.tsx' function App() { const { pdsAgent, signOut, refresh } = useAuthContext() @@ -18,7 +18,7 @@ function App() { const [serviceAuth, setServiceAuth] = useState(undefined) const loadServiceAuth = useCallback(async () => { const serviceAuth = await pdsAgent.com.atproto.server.getServiceAuth({ - aud: pdsAgent.accountDid, + aud: pdsAgent.assertDid, }) console.log('serviceAuth', serviceAuth) setServiceAuth(serviceAuth.data) @@ -28,7 +28,7 @@ function App() { const [profile, setProfile] = useState(undefined) const loadProfile = useCallback(async () => { const profile = await pdsAgent.com.atproto.repo.getRecord({ - repo: pdsAgent.accountDid, + repo: pdsAgent.assertDid, collection: 'app.bsky.actor.profile', rkey: 'self', }) diff --git a/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx b/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx index acec28717c5..0933d6ca4e2 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/auth-form.tsx @@ -1,18 +1,17 @@ import { useEffect, useState } from 'react' - import { AtpSignIn, CredentialSignInForm, -} from './credential/credential-sign-in-form' -import { OAuthSignIn, OAuthSignInForm } from './oauth/oauth-sign-in-form' +} from './credential/credential-sign-in-form.tsx' +import { OAuthSignIn, OAuthSignInForm } from './oauth/oauth-sign-in-form.tsx' -export function AuthForm({ - atpSignIn, - oauthSignIn, -}: { +export type AuthFormProps = { atpSignIn?: AtpSignIn oauthSignIn?: OAuthSignIn -}) { + signUpUrl?: string +} + +export function AuthForm({ atpSignIn, oauthSignIn, signUpUrl }: AuthFormProps) { const defaultMethod = oauthSignIn ? 'oauth' : atpSignIn @@ -58,7 +57,9 @@ export function AuthForm({ - {method === 'oauth' && } + {method === 'oauth' && ( + + )} {method === 'credential' && } {method == null &&
No auth method available
} diff --git a/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx b/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx index 4ddc18ececd..a97f6d6957b 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/auth-provider.tsx @@ -1,26 +1,31 @@ 'use client' +import { ReactNode, createContext, useContext, useMemo } from 'react' import { Agent } from '@atproto/api' -import { createContext, ReactNode, useContext, useMemo } from 'react' +import { AuthForm } from './auth-form.tsx' +import { useCredentialAuth } from './credential/use-credential-auth.ts' +import { UseOAuthOptions, useOAuth } from './oauth/use-oauth.ts' -import { useCredentialAuth } from './credential/use-credential-auth' -import { AuthForm } from './auth-form' -import { useOAuth, UseOAuthOptions } from './oauth/use-oauth' - -export type AuthContext = { +export type AuthContextValue = { pdsAgent: Agent signOut: () => void refresh: () => void } -const AuthContext = createContext(null) +const AuthContext = createContext(null) + +export type AuthProviderProps = UseOAuthOptions & { + children: ReactNode + signUpUrl?: string +} export const AuthProvider = ({ children, + signUpUrl, + + // UseOAuthOptions ...options -}: { - children: ReactNode -} & UseOAuthOptions) => { +}: AuthProviderProps) => { const { isLoginPopup, isInitializing, @@ -38,7 +43,7 @@ export const AuthProvider = ({ refresh: credentialRefresh, } = useCredentialAuth() - const value = useMemo(() => { + const value = useMemo(() => { if (oauthAgent) { return { pdsAgent: oauthAgent, @@ -77,6 +82,7 @@ export const AuthProvider = ({ return ( ) @@ -85,7 +91,7 @@ export const AuthProvider = ({ return {children} } -export function useAuthContext(): AuthContext { +export function useAuthContext(): AuthContextValue { const context = useContext(AuthContext) if (context) return context diff --git a/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx b/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx index c965209aeb3..1f5c8cae1ac 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx +++ b/packages/oauth/oauth-client-browser-example/src/auth/oauth/oauth-sign-in-form.tsx @@ -1,59 +1,69 @@ +import { FormEvent, JSX, useState } from 'react' import { AuthorizeOptions } from '@atproto/oauth-client-browser' -import { FormEvent, useCallback, useState } from 'react' export type OAuthSignIn = (input: string, options?: AuthorizeOptions) => unknown +export type OAuthSignInFormProps = Omit< + JSX.IntrinsicElements['form'], + 'onSubmit' +> & { + signIn: OAuthSignIn + signUpUrl?: string +} + /** * @returns Nice tailwind css form asking to enter either a handle or the host * to use to login. */ export function OAuthSignInForm({ signIn, + signUpUrl, + + // form + className, ...props -}: { - signIn: OAuthSignIn -} & Omit, 'onSubmit'>) { +}: OAuthSignInFormProps) { const [value, setValue] = useState('') const [error, setError] = useState(null) const [loading, setLoading] = useState(false) - const onSubmit = useCallback( - async (e: FormEvent) => { - e.preventDefault() - if (loading) return + const onSubmit = async (event: FormEvent) => { + event.preventDefault() - setError(null) - setLoading(true) + if (loading) return + if (!event.currentTarget.reportValidity()) return - try { - if (value.startsWith('did:')) { - if (value.length > 5) await signIn(value) - else setError('DID must be at least 6 characters') - } else if ( - value.startsWith('https://') || - value.startsWith('http://') - ) { - const url = new URL(value) - if (value !== url.origin) throw new Error('PDS URL must be a origin') - await signIn(value) - } else if (value.includes('.') && value.length > 3) { - const handle = value.startsWith('@') ? value.slice(1) : value - if (handle.length > 3) await signIn(handle) - else setError('Handle must be at least 4 characters') - } + setError(null) + setLoading(true) - throw new Error('Please provide a valid handle, DID or PDS URL') - } catch (err) { - setError((err as any)?.message || String(err)) - } finally { - setLoading(false) + try { + if (value.startsWith('did:')) { + if (value.length > 5) await signIn(value) + else setError('DID must be at least 6 characters') + } else if (value.startsWith('https://') || value.startsWith('http://')) { + const url = new URL(value) + if (value !== url.origin) throw new Error('PDS URL must be a origin') + await signIn(value) + } else if (value.includes('.') && value.length > 3) { + const handle = value.startsWith('@') ? value.slice(1) : value + if (handle.length > 3) await signIn(handle) + else setError('Handle must be at least 4 characters') } - }, - [loading, value, signIn], - ) + + throw new Error('Please provide a valid handle, DID or PDS URL') + } catch (err) { + setError((err as any)?.message || String(err)) + } finally { + setLoading(false) + } + } return ( -
+
+ {signUpUrl && ( + + )} + {error ?
{error}
: null}
) diff --git a/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts b/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts index 9bd414f969a..3d77cb8d058 100644 --- a/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts +++ b/packages/oauth/oauth-client-browser-example/src/auth/oauth/use-oauth.ts @@ -151,7 +151,7 @@ export function useOAuth(options: UseOAuthOptions) { const [isInitializing, setIsInitializing] = useState(true) const [isLoginPopup, setIsLoginPopup] = useState(false) - const clientForInitRef = useRef() + const clientForInitRef = useRef(null) useEffect(() => { // In strict mode, we don't want to re-init() the client if it's the same if (clientForInitRef.current === clientForInit) return diff --git a/packages/oauth/oauth-client-browser-example/src/constants.ts b/packages/oauth/oauth-client-browser-example/src/constants.ts index f437722747d..48962659c89 100644 --- a/packages/oauth/oauth-client-browser-example/src/constants.ts +++ b/packages/oauth/oauth-client-browser-example/src/constants.ts @@ -12,3 +12,7 @@ export const PLC_DIRECTORY_URL: string | undefined = export const HANDLE_RESOLVER_URL: string = searchParams.get('handle_resolver') ?? (ENV === 'development' ? 'http://localhost:2584' : 'https://bsky.social') + +export const SIGN_UP_URL: string = + searchParams.get('sign_up_url') ?? + (ENV === 'development' ? 'http://localhost:2583' : 'https://bsky.social') diff --git a/packages/oauth/oauth-client-browser-example/src/main.tsx b/packages/oauth/oauth-client-browser-example/src/main.tsx index fdf51f138f7..fb837dfec0b 100644 --- a/packages/oauth/oauth-client-browser-example/src/main.tsx +++ b/packages/oauth/oauth-client-browser-example/src/main.tsx @@ -1,11 +1,15 @@ import './index.css' -import React from 'react' -import ReactDOM from 'react-dom/client' - -import App from './app' -import { AuthProvider } from './auth/auth-provider' -import { ENV, HANDLE_RESOLVER_URL, PLC_DIRECTORY_URL } from './constants' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './app.tsx' +import { AuthProvider } from './auth/auth-provider.tsx' +import { + ENV, + HANDLE_RESOLVER_URL, + PLC_DIRECTORY_URL, + SIGN_UP_URL, +} from './constants.ts' const clientId = `http://localhost?${new URLSearchParams({ scope: 'atproto transition:generic', @@ -14,20 +18,22 @@ const clientId = `http://localhost?${new URLSearchParams({ search: new URLSearchParams({ env: ENV, handle_resolver: HANDLE_RESOLVER_URL, + sign_up_url: SIGN_UP_URL, ...(PLC_DIRECTORY_URL && { plc_directory_url: PLC_DIRECTORY_URL }), }).toString(), }).href, })}` -ReactDOM.createRoot(document.getElementById('root')!).render( - +createRoot(document.getElementById('root')!).render( + - , + , ) diff --git a/packages/oauth/oauth-client-browser-example/tailwind.config.js b/packages/oauth/oauth-client-browser-example/tailwind.config.js index aea8d354b11..50ce094366b 100644 --- a/packages/oauth/oauth-client-browser-example/tailwind.config.js +++ b/packages/oauth/oauth-client-browser-example/tailwind.config.js @@ -5,4 +5,6 @@ export default { extend: {}, }, plugins: [], + // See rollup.config.js for classes used in the HTML template + safelist: ['bg-white', 'dark:bg-slate-800'], } diff --git a/packages/oauth/oauth-client-browser/CHANGELOG.md b/packages/oauth/oauth-client-browser/CHANGELOG.md index 3d3ef4dcaa6..3fa8dc84175 100644 --- a/packages/oauth/oauth-client-browser/CHANGELOG.md +++ b/packages/oauth/oauth-client-browser/CHANGELOG.md @@ -1,5 +1,16 @@ # @atproto/oauth-client-browser +## 0.3.11 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/oauth-types@0.2.4 + - @atproto/jwk@0.1.4 + - @atproto/oauth-client@0.3.11 + - @atproto-labs/did-resolver@0.1.11 + - @atproto/jwk-webcrypto@0.1.5 + ## 0.3.10 ### Patch Changes diff --git a/packages/oauth/oauth-client-browser/package.json b/packages/oauth/oauth-client-browser/package.json index 0b5123ec68e..91435e022ad 100644 --- a/packages/oauth/oauth-client-browser/package.json +++ b/packages/oauth/oauth-client-browser/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-client-browser", - "version": "0.3.10", + "version": "0.3.11", "license": "MIT", "description": "ATPROTO OAuth client for the browser (relies on WebCrypto & Indexed DB)", "keywords": [ diff --git a/packages/oauth/oauth-client-node/CHANGELOG.md b/packages/oauth/oauth-client-node/CHANGELOG.md index 8aa7c4805a5..46c7f08de66 100644 --- a/packages/oauth/oauth-client-node/CHANGELOG.md +++ b/packages/oauth/oauth-client-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @atproto/oauth-client-node +## 0.2.12 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/oauth-types@0.2.4 + - @atproto/jwk@0.1.4 + - @atproto/oauth-client@0.3.11 + - @atproto-labs/did-resolver@0.1.11 + - @atproto/jwk-jose@0.1.5 + - @atproto/jwk-webcrypto@0.1.5 + - @atproto-labs/handle-resolver-node@0.1.14 + ## 0.2.11 ### Patch Changes diff --git a/packages/oauth/oauth-client-node/package.json b/packages/oauth/oauth-client-node/package.json index 0bef52e5706..3ab2a587ef0 100644 --- a/packages/oauth/oauth-client-node/package.json +++ b/packages/oauth/oauth-client-node/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-client-node", - "version": "0.2.11", + "version": "0.2.12", "license": "MIT", "description": "ATPROTO OAuth client for the NodeJS", "keywords": [ diff --git a/packages/oauth/oauth-client/CHANGELOG.md b/packages/oauth/oauth-client/CHANGELOG.md index 62c9b240e48..b064ec8d461 100644 --- a/packages/oauth/oauth-client/CHANGELOG.md +++ b/packages/oauth/oauth-client/CHANGELOG.md @@ -1,5 +1,19 @@ # @atproto/oauth-client +## 0.3.11 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Minor code optimizations + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto-labs/fetch@0.2.2 + - @atproto/oauth-types@0.2.4 + - @atproto/jwk@0.1.4 + - @atproto-labs/did-resolver@0.1.11 + - @atproto-labs/identity-resolver@0.1.14 + - @atproto/xrpc@0.6.10 + ## 0.3.10 ### Patch Changes diff --git a/packages/oauth/oauth-client/package.json b/packages/oauth/oauth-client/package.json index 0f941078db6..e6bdc121f22 100644 --- a/packages/oauth/oauth-client/package.json +++ b/packages/oauth/oauth-client/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-client", - "version": "0.3.10", + "version": "0.3.11", "license": "MIT", "description": "OAuth client for ATPROTO PDS. This package serves as common base for environment-specific implementations (NodeJS, Browser, React-Native).", "keywords": [ diff --git a/packages/oauth/oauth-client/src/oauth-response-error.ts b/packages/oauth/oauth-client/src/oauth-response-error.ts index 5c08a0f4d19..de6d17b194a 100644 --- a/packages/oauth/oauth-client/src/oauth-response-error.ts +++ b/packages/oauth/oauth-client/src/oauth-response-error.ts @@ -1,4 +1,5 @@ -import { Json, ifObject, ifString } from '@atproto-labs/fetch' +import { Json } from '@atproto-labs/fetch' +import { ifString } from './util.js' export class OAuthResponseError extends Error { readonly error?: string @@ -8,8 +9,9 @@ export class OAuthResponseError extends Error { public readonly response: Response, public readonly payload: Json, ) { - const error = ifString(ifObject(payload)?.['error']) - const errorDescription = ifString(ifObject(payload)?.['error_description']) + const objPayload = typeof payload === 'object' ? payload : undefined + const error = ifString(objPayload?.['error']) + const errorDescription = ifString(objPayload?.['error_description']) const messageError = error ? `"${error}"` : 'unknown' const messageDesc = errorDescription ? `: ${errorDescription}` : '' diff --git a/packages/oauth/oauth-client/src/util.ts b/packages/oauth/oauth-client/src/util.ts index eef60c18654..051c19a6ec8 100644 --- a/packages/oauth/oauth-client/src/util.ts +++ b/packages/oauth/oauth-client/src/util.ts @@ -4,6 +4,8 @@ export type Simplify = { [K in keyof T]: T[K] } & NonNullable // @ts-expect-error Symbol.dispose ??= Symbol('@@dispose') +export const ifString = (v: V) => (typeof v === 'string' ? v : undefined) + /** * @todo (?) move to common package */ diff --git a/packages/oauth/oauth-provider/.gitignore b/packages/oauth/oauth-provider/.gitignore new file mode 100644 index 00000000000..200f0ce2af6 --- /dev/null +++ b/packages/oauth/oauth-provider/.gitignore @@ -0,0 +1,2 @@ +src/assets/app/locales/*/*.ts +.swc diff --git a/packages/oauth/oauth-provider/.linguirc b/packages/oauth/oauth-provider/.linguirc new file mode 100644 index 00000000000..800b873e848 --- /dev/null +++ b/packages/oauth/oauth-provider/.linguirc @@ -0,0 +1,57 @@ +{ + "format": "po", + "sourceLocale": "en", + "locales": [ + "en", + "an", + "ast", + "ca", + "da", + "de", + "el", + "en-GB", + "es", + "eu", + "fi", + "fr", + "ga", + "gl", + "hi", + "hu", + "ia", + "id", + "it", + "ja", + "km", + "ko", + "ne", + "nl", + "pl", + "pt-BR", + "ro", + "ru", + "sv", + "th", + "tr", + "uk", + "vi", + "zh-CN", + "zh-HK", + "zh-TW" + ], + "fallbackLocales": { + "default": "en" + }, + "catalogs": [ + { + "path": "/src/assets/app/locales/{locale}/messages", + "include": [ + "/src/assets/app" + ], + "exclude": [ + "**/dist/**", + "**/node_modules/**" + ] + } + ] +} diff --git a/packages/oauth/oauth-provider/.postcssrc.yml b/packages/oauth/oauth-provider/.postcssrc.yml deleted file mode 100644 index 0114fbc9e78..00000000000 --- a/packages/oauth/oauth-provider/.postcssrc.yml +++ /dev/null @@ -1,3 +0,0 @@ -plugins: - tailwindcss: {} - autoprefixer: {} diff --git a/packages/oauth/oauth-provider/CHANGELOG.md b/packages/oauth/oauth-provider/CHANGELOG.md index f96f6851ff2..f50e7df7bed 100644 --- a/packages/oauth/oauth-provider/CHANGELOG.md +++ b/packages/oauth/oauth-provider/CHANGELOG.md @@ -1,5 +1,26 @@ # @atproto/oauth-provider +## 0.5.0 + +### Minor Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add support for account sign-up + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add support for password reset + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Properly support locales with 3 chars (Asturian) + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add support for multiple locales + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto-labs/fetch@0.2.2 + - @atproto/oauth-types@0.2.4 + - @atproto/jwk@0.1.4 + - @atproto-labs/fetch-node@0.1.8 + - @atproto/jwk-jose@0.1.5 + ## 0.4.0 ### Minor Changes diff --git a/packages/oauth/oauth-provider/package.json b/packages/oauth/oauth-provider/package.json index 9f43d55d2a3..9e05d897372 100644 --- a/packages/oauth/oauth-provider/package.json +++ b/packages/oauth/oauth-provider/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-provider", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "description": "Generic OAuth2 and OpenID Connect provider for Node.js. Currently only supports features needed for Atproto.", "keywords": [ @@ -41,9 +41,11 @@ "@atproto/jwk-jose": "workspace:*", "@atproto/oauth-types": "workspace:*", "@hapi/accept": "^6.0.3", + "@hapi/address": "^5.1.1", "@hapi/bourne": "^3.0.0", "@hapi/content": "^6.0.0", "cookie": "^0.6.0", + "disposable-email-domains-js": "^1.5.0", "forwarded": "^0.2.0", "http-errors": "^2.0.0", "ioredis": "^5.3.2", @@ -53,31 +55,52 @@ }, "devDependencies": { "@atproto-labs/rollup-plugin-bundle-manifest": "workspace:*", - "@rollup/plugin-commonjs": "^25.0.7", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-replace": "^5.0.5", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", + "@hcaptcha/react-hcaptcha": "^1.11.2", + "@lingui/cli": "^5.2.0", + "@lingui/core": "^5.2.0", + "@lingui/react": "^5.2.0", + "@lingui/swc-plugin": "^5.4.0", + "@lingui/vite-plugin": "^5.2.0", + "@rollup/plugin-commonjs": "^28.0.2", + "@rollup/plugin-dynamic-import-vars": "^2.1.5", + "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-swc": "^0.4.0", + "@swc/core": "^1.10.18", + "@swc/helpers": "^0.5.15", "@types/cookie": "^0.6.0", "@types/forwarded": "0.1.3", "@types/psl": "1.1.3", - "@types/react": "^18.2.50", - "@types/react-dom": "^18.2.18", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", "@types/send": "^0.17.4", + "@vitejs/plugin-react-swc": "^3.8.0", "@web/rollup-plugin-import-meta-assets": "^2.2.1", "autoprefixer": "^10.4.17", - "postcss": "^8.4.33", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "postcss": "^8.4.38", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-error-boundary": "^5.0.0", "rollup": "^4.13.0", "rollup-plugin-postcss": "^4.0.2", - "tailwindcss": "^3.4.1", - "typescript": "^5.6.3" + "tailwindcss": "^3.4.3", + "typescript": "^5.6.3", + "vite": "^6.2.0" }, "scripts": { + "po:extract": "lingui extract --clean", + "po:compile": "lingui compile --typescript", + "prebuild:frontend": "pnpm po:compile", "build:frontend": "rollup --config rollup.config.js", "build:backend": "tsc --build --force tsconfig.backend.json", - "build": "pnpm --parallel --stream '/^build:.+$/'", - "dev": "rollup --config rollup.config.js --watch" + "start:ui": "vite", + "dev:frontend": "pnpm run build:frontend --watch", + "dev:catalogs": "pnpm run po:extract --debounce 250 --watch > /dev/null", + "dev:messages": "pnpm run po:compile --debounce 500 --watch" + }, + "postcss": { + "plugins": { + "tailwindcss": {}, + "autoprefixer": {} + } } } diff --git a/packages/oauth/oauth-provider/rollup.config.js b/packages/oauth/oauth-provider/rollup.config.js index 7ff599c3a65..c1b25d213fd 100644 --- a/packages/oauth/oauth-provider/rollup.config.js +++ b/packages/oauth/oauth-provider/rollup.config.js @@ -1,10 +1,11 @@ /* eslint-env node */ const { default: commonjs } = require('@rollup/plugin-commonjs') +const { + default: dynamicImportVars, +} = require('@rollup/plugin-dynamic-import-vars') const { default: nodeResolve } = require('@rollup/plugin-node-resolve') -const { default: replace } = require('@rollup/plugin-replace') -const { default: terser } = require('@rollup/plugin-terser') -const { default: typescript } = require('@rollup/plugin-typescript') +const { default: swc } = require('@rollup/plugin-swc') const { defineConfig } = require('rollup') const { default: manifest, @@ -16,34 +17,77 @@ module.exports = defineConfig((commandLineArguments) => { process.env['NODE_ENV'] ?? (commandLineArguments.watch ? 'development' : 'production') - const minify = NODE_ENV !== 'development' + const devMode = NODE_ENV === 'development' return { - input: 'src/assets/app/main.tsx', + input: ['src/assets/app/main.tsx', 'src/assets/app/main.css'], output: { manualChunks: undefined, sourcemap: true, - file: 'dist/assets/app/main.js', - format: 'iife', + dir: 'dist/assets/app', + format: 'module', + entryFileNames: 'main-[hash].js', }, plugins: [ - nodeResolve({ preferBuiltins: false, browser: true }), - commonjs(), - postcss({ config: true, extract: true, minimize: minify }), - typescript({ - tsconfig: './tsconfig.frontend.json', - outputToFilesystem: true, + { + name: 'resolve-swc-helpers', + resolveId(src) { + // For some reason, "nodeResolve" doesn't resolve these: + if (src.startsWith('@swc/helpers/')) return require.resolve(src) + }, + }, + nodeResolve({ + preferBuiltins: false, + browser: true, + exportConditions: ['browser', 'module', 'import', 'default'], }), - replace({ - preventAssignment: true, - values: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + commonjs(), + postcss({ config: true, extract: true, minimize: !devMode }), + swc({ + swc: { + swcrc: false, + configFile: false, + sourceMaps: true, + minify: !devMode, + jsc: { + experimental: { + // @NOTE Because of the experimental nature of SWC plugins, A + // very particular version of @swc/core needs to be used. The + // link below allows to determine with version of @swc/core is + // compatible based on the version of @lingui/swc-plugin used + // (click on the swc_core version in the right column to see + // which version of the @swc/core is compatible) + // + // https://github.com/lingui/swc-plugin?tab=readme-ov-file#compatibility + plugins: [['@lingui/swc-plugin', {}]], + }, + minify: { + compress: true, + mangle: true, + }, + externalHelpers: true, + target: 'es2020', + parser: { syntax: 'typescript', tsx: true }, + transform: { + useDefineForClassFields: true, + react: { runtime: 'automatic' }, + optimizer: { + simplify: true, + globals: { + vars: { 'process.env.NODE_ENV': JSON.stringify(NODE_ENV) }, + }, + }, + }, + }, + }, }), + dynamicImportVars({ errorWhenNoFilesFound: true }), + // Change `data` to `true` to include assets data in the manifest, // allowing for easier bundling of the backend code (eg. using esbuild) as // bundlers know how to bundle JSON files but not how to bundle assets // referenced at runtime. manifest({ data: false }), - minify && terser({}), ], onwarn(warning, warn) { // 'use client' directives are fine diff --git a/packages/oauth/oauth-provider/src/account/account-manager.ts b/packages/oauth/oauth-provider/src/account/account-manager.ts index 64a8b8c1b0e..1abc3d05e7b 100644 --- a/packages/oauth/oauth-provider/src/account/account-manager.ts +++ b/packages/oauth/oauth-provider/src/account/account-manager.ts @@ -1,31 +1,164 @@ -import { isOAuthClientIdLoopback } from '@atproto/oauth-types' +import { + OAuthIssuerIdentifier, + isOAuthClientIdLoopback, +} from '@atproto/oauth-types' import { Client } from '../client/client.js' import { DeviceId } from '../device/device-id.js' +import { InvalidRequestError } from '../errors/invalid-request-error.js' +import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js' +import { callAsync } from '../lib/util/function.js' import { constantTime } from '../lib/util/time.js' -import { InvalidRequestError } from '../oauth-errors.js' +import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js' +import { Customization } from '../oauth-provider.js' import { Sub } from '../oidc/sub.js' import { ClientAuth } from '../token/token-store.js' import { Account, AccountInfo, AccountStore, - SignInCredentials, + ResetPasswordConfirmData, + ResetPasswordRequestData, } from './account-store.js' +import { SignInData } from './sign-in-data.js' +import { SignUpData } from './sign-up-data.js' const TIMING_ATTACK_MITIGATION_DELAY = 400 +const BRUTE_FORCE_MITIGATION_DELAY = 300 export class AccountManager { - constructor(protected readonly store: AccountStore) {} + protected readonly inviteCodeRequired: boolean + protected readonly hcaptchaClient?: HCaptchaClient + + constructor( + issuer: OAuthIssuerIdentifier, + protected readonly store: AccountStore, + protected readonly hooks: OAuthHooks, + customization: Customization, + ) { + this.inviteCodeRequired = customization.inviteCodeRequired !== false + this.hcaptchaClient = customization.hcaptcha + ? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha) + : undefined + } + + protected async verifySignupData( + data: SignUpData, + deviceId: DeviceId, + deviceMetadata: RequestMetadata, + ): Promise { + let hcaptchaResult: undefined | HcaptchaVerifyResult + + if (this.inviteCodeRequired && !data.inviteCode) { + throw new InvalidRequestError('Invite code is required') + } + + if (this.hcaptchaClient) { + if (!data.hcaptchaToken) { + throw new InvalidRequestError('hCaptcha token is required') + } + + const { allowed, result } = await this.hcaptchaClient.verify( + 'signup', + data.hcaptchaToken, + deviceMetadata.ipAddress, + data.handle, + deviceMetadata.userAgent, + ) + + await callAsync(this.hooks.onSignupHcaptchaResult, { + data, + allowed, + result, + deviceId, + deviceMetadata, + }) + + if (!allowed) { + throw new InvalidRequestError('hCaptcha verification failed') + } + + hcaptchaResult = result + } + + await callAsync(this.hooks.onSignupAttempt, { + data, + deviceId, + deviceMetadata, + hcaptchaResult, + }) + } + + public async signUp( + data: SignUpData, + deviceId: DeviceId, + deviceMetadata: RequestMetadata, + ): Promise { + await this.verifySignupData(data, deviceId, deviceMetadata) + + // Mitigation against brute forcing email of users. + // @TODO Add rate limit to all the OAuth routes. + return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => { + let account: Account + try { + account = await this.store.createAccount(data) + } catch (err) { + throw InvalidRequestError.from(err, 'Account creation failed') + } + + try { + const info = await this.store.addDeviceAccount( + deviceId, + account.sub, + false, + ) + + await callAsync(this.hooks.onSignedUp, { + data, + info, + account, + deviceId, + deviceMetadata, + }) + + return { account, info } + } catch (err) { + throw InvalidRequestError.from( + err, + 'Something went wrong, try singing-in', + ) + } + }) + } public async signIn( - credentials: SignInCredentials, + data: SignInData, deviceId: DeviceId, + deviceMetadata: RequestMetadata, ): Promise { return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { - const result = await this.store.authenticateAccount(credentials, deviceId) - if (result) return result + try { + const account = await this.store.authenticateAccount(data) + const info = await this.store.addDeviceAccount( + deviceId, + account.sub, + data.remember, + ) - throw new InvalidRequestError('Invalid credentials') + await callAsync(this.hooks.onSignedIn, { + data, + info, + account, + deviceId, + deviceMetadata, + }) + + return { account, info } + } catch (err) { + throw InvalidRequestError.from( + err, + 'Unable to sign-in due to an unexpected server error', + ) + } }) } @@ -52,4 +185,22 @@ export class AccountManager { const results = await this.store.listDeviceAccounts(deviceId) return results.filter((result) => result.info.remembered) } + + public async resetPasswordRequest(data: ResetPasswordRequestData) { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + await this.store.resetPasswordRequest(data) + }) + } + + public async resetPasswordConfirm(data: ResetPasswordConfirmData) { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + await this.store.resetPasswordConfirm(data) + }) + } + + public async verifyHandleAvailability(handle: string): Promise { + return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => { + return this.store.verifyHandleAvailability(handle) + }) + } } diff --git a/packages/oauth/oauth-provider/src/account/account-store.ts b/packages/oauth/oauth-provider/src/account/account-store.ts index ad49f8d98eb..9e7037f2c20 100644 --- a/packages/oauth/oauth-provider/src/account/account-store.ts +++ b/packages/oauth/oauth-provider/src/account/account-store.ts @@ -1,25 +1,86 @@ +import { isEmailValid } from '@hapi/address' +import { isDisposableEmail } from 'disposable-email-domains-js' import { z } from 'zod' import { ClientId } from '../client/client-id.js' import { DeviceId } from '../device/device-id.js' -import { Awaitable } from '../lib/util/type.js' +import { localeSchema } from '../lib/locale.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' +import { + HandleUnavailableError, + InvalidRequestError, + SecondAuthenticationFactorRequiredError, +} from '../oauth-errors.js' import { Sub } from '../oidc/sub.js' import { Account } from './account.js' -export const signInCredentialsSchema = z.object({ - username: z.string(), - password: z.string(), +// @NOTE Change the length here to force stronger passwords (through a reset) +export const oldPasswordSchema = z.string().min(1) +export const newPasswordSchema = z.string().min(8) +export const tokenSchema = z.string().regex(/^[A-Z2-7]{5}-[A-Z2-7]{5}$/) +export const handleSchema = z + .string() + .min(3) + .max(30) + .regex(/^[a-z0-9][a-z0-9-]+[a-z0-9](?:\.[a-z0-9][a-z0-9-]+[a-z0-9])+$/) +export const emailSchema = z + .string() + .email() + // @NOTE using @hapi/address here, in addition to the email() check to ensure + // compatibility with the current email validation in the PDS's account + // manager + .refine(isEmailValid, { + message: 'Invalid email address', + }) + .refine((email) => !isDisposableEmail(email), { + message: 'Disposable email addresses are not allowed', + }) - /** - * If false, the account must not be returned from - * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when - * used with a device ID. - */ - remember: z.boolean().optional().default(false), +export const authenticateAccountDataSchema = z + .object({ + locale: localeSchema, + username: z.string(), + password: oldPasswordSchema, + emailOtp: z.string().optional(), + }) + .strict() + +export type AuthenticateAccountData = z.TypeOf< + typeof authenticateAccountDataSchema +> + +export const createAccountDataSchema = z + .object({ + locale: localeSchema, + handle: handleSchema, + email: emailSchema, + password: z.intersection(oldPasswordSchema, newPasswordSchema), + inviteCode: tokenSchema.optional(), + }) + .strict() + +export type CreateAccountData = z.TypeOf + +export const resetPasswordRequestDataSchema = z + .object({ + locale: localeSchema, + email: emailSchema, + }) + .strict() + +export type ResetPasswordRequestData = z.TypeOf< + typeof resetPasswordRequestDataSchema +> - emailOtp: z.string().optional(), -}) +export const resetPasswordConfirmDataSchema = z + .object({ + token: tokenSchema, + password: z.intersection(oldPasswordSchema, newPasswordSchema), + }) + .strict() -export type SignInCredentials = z.TypeOf +export type ResetPasswordConfirmData = z.TypeOf< + typeof resetPasswordConfirmDataSchema +> export type DeviceAccountInfo = { remembered: boolean @@ -28,7 +89,14 @@ export type DeviceAccountInfo = { } // Export all types needed to implement the AccountStore interface -export type { Account, DeviceId, Sub } +export { + type Account, + type DeviceId, + HandleUnavailableError, + InvalidRequestError, + SecondAuthenticationFactorRequiredError, + type Sub, +} export type AccountInfo = { account: Account @@ -36,10 +104,17 @@ export type AccountInfo = { } export interface AccountStore { - authenticateAccount( - credentials: SignInCredentials, - deviceId: DeviceId, - ): Awaitable + /** + * @throws {HandleUnavailableError} - To indicate that the handle is already taken + * @throws {InvalidRequestError} - To indicate that some data is invalid + */ + createAccount(data: CreateAccountData): Awaitable + + /** + * @throws {InvalidRequestError} - When the credentials are not valid + * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials + */ + authenticateAccount(data: AuthenticateAccountData): Awaitable addAuthorizedClient( deviceId: DeviceId, @@ -47,6 +122,19 @@ export interface AccountStore { clientId: ClientId, ): Awaitable + /** + * @param remember If false, the account must not be returned from + * {@link AccountStore.listDeviceAccounts}. + */ + addDeviceAccount( + deviceId: DeviceId, + sub: Sub, + remember: boolean, + ): Awaitable + + /** + * @returns The account info, whether the account, even if remember was false. + */ getDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable @@ -55,23 +143,30 @@ export interface AccountStore { * be returned. The others will be ignored. */ listDeviceAccounts(deviceId: DeviceId): Awaitable -} -export function isAccountStore( - implementation: Record & Partial, -): implementation is Record & AccountStore { - return ( - typeof implementation.authenticateAccount === 'function' && - typeof implementation.getDeviceAccount === 'function' && - typeof implementation.addAuthorizedClient === 'function' && - typeof implementation.listDeviceAccounts === 'function' && - typeof implementation.removeDeviceAccount === 'function' - ) + resetPasswordRequest(data: ResetPasswordRequestData): Awaitable + resetPasswordConfirm(data: ResetPasswordConfirmData): Awaitable + + /** + * @throws {HandleUnavailableError} - To indicate that the handle is already taken + */ + verifyHandleAvailability(handle: string): Awaitable } -export function asAccountStore( - implementation?: Record & Partial, -): AccountStore { +export const isAccountStore = buildInterfaceChecker([ + 'createAccount', + 'authenticateAccount', + 'addAuthorizedClient', + 'addDeviceAccount', + 'getDeviceAccount', + 'removeDeviceAccount', + 'listDeviceAccounts', + 'resetPasswordRequest', + 'resetPasswordConfirm', + 'verifyHandleAvailability', +]) + +export function asAccountStore(implementation: V): V & AccountStore { if (!implementation || !isAccountStore(implementation)) { throw new Error('Invalid AccountStore implementation') } diff --git a/packages/oauth/oauth-provider/src/account/sign-in-data.ts b/packages/oauth/oauth-provider/src/account/sign-in-data.ts new file mode 100644 index 00000000000..47a742a3a7e --- /dev/null +++ b/packages/oauth/oauth-provider/src/account/sign-in-data.ts @@ -0,0 +1,15 @@ +import { z } from 'zod' +import { authenticateAccountDataSchema } from './account-store.js' + +export const signInDataSchema = authenticateAccountDataSchema + .extend({ + /** + * If false, the account must not be returned from + * {@link AccountStore.listDeviceAccounts}. Note that this only makes sense when + * used with a device ID. + */ + remember: z.boolean().optional().default(false), + }) + .strict() + +export type SignInData = z.TypeOf diff --git a/packages/oauth/oauth-provider/src/account/sign-up-data.ts b/packages/oauth/oauth-provider/src/account/sign-up-data.ts new file mode 100644 index 00000000000..cf9aac8abd1 --- /dev/null +++ b/packages/oauth/oauth-provider/src/account/sign-up-data.ts @@ -0,0 +1,11 @@ +import { z } from 'zod' +import { hcaptchaTokenSchema } from '../lib/hcaptcha.js' +import { createAccountDataSchema } from './account-store.js' + +export const signUpDataSchema = createAccountDataSchema + .extend({ + hcaptchaToken: hcaptchaTokenSchema.optional(), + }) + .strict() + +export type SignUpData = z.TypeOf diff --git a/packages/oauth/oauth-provider/src/assets/app/app.tsx b/packages/oauth/oauth-provider/src/assets/app/app.tsx index e925ffae5dd..000ab3300ef 100644 --- a/packages/oauth/oauth-provider/src/assets/app/app.tsx +++ b/packages/oauth/oauth-provider/src/assets/app/app.tsx @@ -1,28 +1,43 @@ +import { ErrorBoundary } from 'react-error-boundary' import type { AuthorizeData, + AvailableLocales, CustomizationData, ErrorData, -} from './backend-data' -import { AuthorizeView } from './views/authorize-view' -import { ErrorView } from './views/error-view' +} from './backend-types.ts' +import { LocaleProvider } from './locales/locale-provider.tsx' +import { AuthorizeView } from './views/authorize/authorize-view.tsx' +import { ErrorView } from './views/error/error-view.tsx' export type AppProps = { + availableLocales?: AvailableLocales authorizeData?: AuthorizeData customizationData?: CustomizationData errorData?: ErrorData } -export function App({ authorizeData, customizationData, errorData }: AppProps) { - if (authorizeData && !errorData) { - return ( - - ) - } else { - return ( - - ) - } +export function App({ + availableLocales, + authorizeData, + customizationData, + errorData, +}: AppProps) { + return ( + + ( + + )} + > + {errorData || !authorizeData ? ( + + ) : ( + + )} + + + ) } diff --git a/packages/oauth/oauth-provider/src/assets/app/backend-data.ts b/packages/oauth/oauth-provider/src/assets/app/backend-data.ts index ae9f4874812..0884246d4e4 100644 --- a/packages/oauth/oauth-provider/src/assets/app/backend-data.ts +++ b/packages/oauth/oauth-provider/src/assets/app/backend-data.ts @@ -1,72 +1,27 @@ -import { OAuthClientMetadata } from '@atproto/oauth-types' - -// TODO: Find a way to share these types with the backend code - -export type Account = { - sub: string - aud: string - - email?: string - name?: string - preferred_username?: string - picture?: string -} - -export type Session = { - account: Account - info?: never // Prevent relying on this in the frontend - - selected: boolean - loginRequired: boolean - consentRequired: boolean -} - -export type LinkDefinition = { - title: string - href: string - rel?: string -} - -export type CustomizationData = { - name?: string - logo?: string - links?: LinkDefinition[] -} - -export type ErrorData = { - error: string - error_description: string -} - -export type ScopeDetail = { - scope: string - description?: string -} - -export type AuthorizeData = { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - requestUri: string - csrfCookie: string - loginHint?: string - scopeDetails?: ScopeDetail[] - newSessionsRequireConsent: boolean - sessions: Session[] -} - -// see "declareBackendData()" in the backend -const readBackendData = (key: string): T | undefined => { +import { + AuthorizeData, + AvailableLocales, + CustomizationData, + ErrorData, +} from './backend-types.ts' + +function readBackendData(key: string): T | undefined { const value = window[key] as T | undefined delete window[key] // Prevent accidental usage / potential leaks to dependencies return value } // These values are injected by the backend when it builds the -// page HTML. +// page HTML. See "declareBackendData()" in the backend. +/** @deprecated Do not import directly. Only import this from main.tsx */ +export const availableLocales = + readBackendData('__availableLocales') +/** @deprecated Do not import directly. Only import this from main.tsx */ export const customizationData = readBackendData( '__customizationData', ) +/** @deprecated Do not import directly. Only import this from main.tsx */ export const errorData = readBackendData('__errorData') +/** @deprecated Do not import directly. Only import this from main.tsx */ export const authorizeData = readBackendData('__authorizeData') diff --git a/packages/oauth/oauth-provider/src/assets/app/backend-types.ts b/packages/oauth/oauth-provider/src/assets/app/backend-types.ts new file mode 100644 index 00000000000..212bc234971 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/backend-types.ts @@ -0,0 +1,66 @@ +import type { OAuthClientMetadata } from '@atproto/oauth-types' + +// @TODO: Find a way to share these types with the backend code + +export type Account = { + sub: string + aud: string | [string, ...string[]] + + email?: string + email_verified?: boolean + name?: string + preferred_username?: string + picture?: string +} + +export type Session = { + account: Account + info?: never // Prevent relying on this in the frontend + + selected: boolean + loginRequired: boolean + consentRequired: boolean +} + +export type LocalizedString = string | ({ en: string } & Record) + +export type AvailableLocales = readonly string[] + +export type LinkDefinition = { + title: LocalizedString + href: string + rel?: string +} + +export type CustomizationData = { + // Functional customization + hcaptchaSiteKey?: string + inviteCodeRequired?: boolean + availableUserDomains?: string[] + + // Aesthetic customization + name?: string + logo?: string + links?: LinkDefinition[] +} + +export type ErrorData = { + error: string + error_description: string +} + +export type ScopeDetail = { + scope: string + description?: string +} + +export type AuthorizeData = { + clientId: string + clientMetadata: OAuthClientMetadata + clientTrusted: boolean + requestUri: string + loginHint?: string + scopeDetails?: ScopeDetail[] + newSessionsRequireConsent: boolean + sessions: Session[] +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx deleted file mode 100644 index 27c2a1aecbf..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/accept-form.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { OAuthClientMetadata } from '@atproto/oauth-types' -import { FormEvent } from 'react' - -import { Account, ScopeDetail } from '../backend-data' -import { Override } from '../lib/util' -import { AccountIdentifier } from './account-identifier' -import { Button } from './button' -import { ClientName } from './client-name' -import { FormCard, FormCardProps } from './form-card' - -export type AcceptFormProps = Override< - FormCardProps, - { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - - account: Account - scopeDetails?: ScopeDetail[] - - onAccept: () => void - acceptLabel?: string - - onReject: () => void - rejectLabel?: string - - onBack?: () => void - backLabel?: string - } -> - -export function AcceptForm({ - clientId, - clientMetadata, - clientTrusted, - - account, - scopeDetails, - - onAccept, - acceptLabel = 'Accept', - onReject, - rejectLabel = 'Deny access', - onBack, - backLabel = 'Back', - - ...props -}: AcceptFormProps) { - const doSubmit = (e: FormEvent) => { - e.preventDefault() - onAccept() - } - - return ( - {backLabel}} - actions={ - <> - - - - - } - {...props} - > - {clientTrusted && clientMetadata.logo_uri && ( -
- {clientMetadata.client_name} -
- )} -

- {' '} - is asking for permission to access your account ( - - ). -

- -

- By clicking {acceptLabel}, you allow this application to perform - the following actions in accordance to their{' '} - - terms of service - - {' and '} - - privacy policy - - : -

- - {scopeDetails?.length ? ( -
    - {scopeDetails.map( - ({ scope, description = getScopeDescription(scope) }) => ( -
  • {description}
  • - ), - )} -
- ) : null} -
- ) -} - -function getScopeDescription(scope: string): string { - switch (scope) { - case 'atproto': - return 'Uniquely identify you' - case 'transition:generic': - return 'Access your account data (except chat messages)' - case 'transition:chat.bsky': - return 'Access your chat messages' - default: - return scope - } -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx b/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx deleted file mode 100644 index 96b860e3261..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/account-identifier.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { HTMLAttributes } from 'react' - -import { Account } from '../backend-data' - -export type AccountIdentifierProps = { - account: Account -} - -export function AccountIdentifier({ - account, - ...attrs -}: AccountIdentifierProps & HTMLAttributes) { - return ( - - {account.preferred_username || account.email || account.sub} - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx b/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx deleted file mode 100644 index 93b195941ad..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/account-picker.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import type { ReactNode } from 'react' -import { Account } from '../backend-data' -import { Override } from '../lib/util' -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { AtSymbolIcon } from './icons/at-symbol-icon' -import { CaretRightIcon } from './icons/caret-right-icon' -import { InputContainer } from './input-container' -import { Fieldset } from './fieldset' - -export type AccountPickerProps = Override< - FormCardProps, - { - accounts: readonly Account[] - - onAccount: (account: Account) => void - accountAria?: (account: Account) => string - - onOther?: () => void - otherLabel?: ReactNode - otherAria?: string - - onBack?: () => void - backLabel?: ReactNode - backAria?: string - } -> - -export function AccountPicker({ - accounts, - - onAccount, - accountAria = (a) => `Sign in as ${a.name}`, - - onOther = undefined, - otherLabel = 'Another account', - otherAria = 'Login to account that is not listed', - - onBack, - backAria, - backLabel = backAria, - - ...props -}: AccountPickerProps) { - return ( - - {backLabel} - - ) - } - > -
- {accounts.map((account) => { - const [name, identifier] = [ - account.name, - account.preferred_username, - account.email, - account.sub, - ].filter(Boolean) as [string, string?] - - return ( - onAccount(account)} - role="button" - aria-label={accountAria(account)} - icon={ - account.picture ? ( - {name} - ) : ( - - - - - - ) - } - append={} - > - - {name} - {identifier && ( - - {identifier} - - )} - - - ) - })} - - {onOther && ( - } - icon={} - > - - {otherLabel} - - - )} -
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/button.tsx b/packages/oauth/oauth-provider/src/assets/app/components/button.tsx deleted file mode 100644 index 5900c7241c0..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/button.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { ButtonHTMLAttributes } from 'react' -import { clsx } from '../lib/clsx' - -export function Button({ - children, - className, - type = 'button', - role = 'Button', - color = 'grey', - disabled = false, - loading = undefined, - ...props -}: { - color?: 'brand' | 'grey' - loading?: boolean -} & ButtonHTMLAttributes) { - return ( - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx b/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx deleted file mode 100644 index 98244d4d289..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/client-name.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { - isOAuthClientIdDiscoverable, - isOAuthClientIdLoopback, - OAuthClientMetadata, -} from '@atproto/oauth-types' -import { HTMLAttributes } from 'react' - -import { UrlViewer } from './url-viewer' - -export type ClientNameProps = { - clientId: string - clientMetadata: OAuthClientMetadata - clientTrusted: boolean - loopbackClientName?: string -} & HTMLAttributes - -export function ClientName({ - clientId, - clientMetadata, - clientTrusted, - loopbackClientName = 'An application on your device', - ...attrs -}: ClientNameProps) { - if (clientTrusted && clientMetadata.client_name) { - return {clientMetadata.client_name} - } - - if (isOAuthClientIdLoopback(clientId)) { - return {loopbackClientName} - } - - if (isOAuthClientIdDiscoverable(clientId)) { - return - } - - return {clientId} -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx b/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx deleted file mode 100644 index 4185246c977..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/fieldset.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { FieldsetHTMLAttributes, forwardRef, ReactNode } from 'react' -import { Override } from '../lib/util' - -export type FieldsetCardProps = Override< - FieldsetHTMLAttributes, - { - title?: ReactNode - } -> - -export const Fieldset = forwardRef( - ({ title, children, ...props }, ref) => ( -
- {title && ( -

- {title} -

- )} - -
{children}
-
- ), -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx deleted file mode 100644 index 029e9867802..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/form-card.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { FormHTMLAttributes, forwardRef, ReactNode } from 'react' -import { InfoCard } from './info-card' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type FormCardProps = Override< - FormHTMLAttributes, - { - append?: ReactNode - error?: ReactNode - cancel?: ReactNode - actions?: ReactNode - } -> - -export const FormCard = forwardRef( - ({ actions, cancel, append, className, children, error, ...props }, ref) => { - return ( -
-
{children}
- - {append &&
{append}
} - - {error && ( - - {error} - - )} - - {(actions || cancel) && ( -
- {actions} -
- {cancel} -
- )} - - ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx new file mode 100644 index 00000000000..7b35a2216e3 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/button-toggle-visibility.tsx @@ -0,0 +1,43 @@ +import { useLingui } from '@lingui/react/macro' +import { Override } from '../../lib/util.ts' +import { EyeIcon, EyeSlashIcon } from '../utils/icons.tsx' +import { Button, ButtonProps } from './button.tsx' + +export type ButtonToggleVisibilityProps = Override< + Omit, + { + visible: boolean + toggleVisible: () => void + } +> + +/** + * Generic button to toggle visibility of an item (e.g. password). + */ +export function ButtonToggleVisibility({ + visible, + toggleVisible, + + // button + onClick, + ...props +}: ButtonToggleVisibilityProps) { + const { t } = useLingui() + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx new file mode 100644 index 00000000000..5e5f5626db5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/button.tsx @@ -0,0 +1,60 @@ +import { JSX } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type ButtonProps = Override< + JSX.IntrinsicElements['button'], + { + color?: 'brand' | 'grey' + loading?: boolean + transparent?: boolean + square?: boolean + } +> + +export function Button({ + color = 'grey', + transparent = false, + loading = undefined, + square = false, + + // button + children, + className, + type = 'button', + role = 'Button', + disabled = false, + ...props +}: ButtonProps) { + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx new file mode 100644 index 00000000000..d8b83fea8a2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/fieldset.tsx @@ -0,0 +1,55 @@ +import { JSX, ReactNode, createContext, useMemo } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { Override } from '../../lib/util.ts' + +export type FieldsetContextValue = { + disabled: boolean + labelId?: string +} + +export const FieldsetContext = createContext({ + disabled: false, +}) +FieldsetContext.displayName = 'FieldsetContext' + +export type FieldsetCardProps = Override< + Omit, + { + label?: ReactNode + } +> + +export function Fieldset({ + label, + children, + disabled, + ...props +}: FieldsetCardProps) { + const labelId = useRandomString({ prefix: 'fieldset-' }) + + const contextValue = useMemo( + () => ({ + disabled: disabled ?? false, + labelId: label ? labelId : undefined, + }), + [disabled, label, labelId], + ) + + return ( +
+ {label && ( + + {label} + + )} + +
+ {children} +
+
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx new file mode 100644 index 00000000000..387054d68fc --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card-async.tsx @@ -0,0 +1,103 @@ +import { Trans } from '@lingui/react/macro' +import { FormEvent, ReactNode, useCallback } from 'react' +import { + UseAsyncActionOptions, + useAsyncAction, +} from '../../hooks/use-async-action.ts' +import { Override } from '../../lib/util.ts' +import { ErrorCard } from '../utils/error-card.tsx' +import { Button } from './button.tsx' +import { FormCard, FormCardProps } from './form-card.tsx' + +export type { AsyncActionController } from '../../hooks/use-async-action.ts' + +export type ErrorRender = (data: { error: Error }) => ReactNode +export const errorRenderDefault: ErrorRender = ({ error }) => ( + +) + +export type FormCardAsyncProps = Override< + Override< + Omit, + Pick + >, + { + invalid?: boolean + disabled?: boolean + + onSubmit: (signal: AbortSignal) => void | PromiseLike + submitLabel?: ReactNode + + onCancel?: () => void + cancelLabel?: ReactNode + + errorRender?: ErrorRender + } +> + +export function FormCardAsync({ + invalid, + disabled, + + onSubmit, + submitLabel, + + onCancel = undefined, + cancelLabel, + + errorRender = errorRenderDefault, + + // UseAsyncActionOptions + ref, + onLoading, + onError, + + // FormCardProps + children, + ...props +}: FormCardAsyncProps) { + const { run, loading, error } = useAsyncAction(onSubmit, { + ref, + onError, + onLoading, + }) + + const doSubmit = useCallback( + (event: FormEvent) => { + event.preventDefault() + + if (!event.currentTarget.reportValidity()) return + + if (!disabled && !invalid) void run() + }, + [disabled, invalid, run], + ) + + return ( + + {cancelLabel || Cancel} + + ) + } + actions={ + + } + > + {children} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx new file mode 100644 index 00000000000..6ef610507dd --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/form-card.tsx @@ -0,0 +1,49 @@ +import { JSX, ReactNode } from 'react' +import { Override } from '../../lib/util.ts' + +export type FormCardProps = Override< + JSX.IntrinsicElements['form'], + { + disabled?: boolean + append?: ReactNode + prepend?: ReactNode + cancel?: ReactNode + actions?: ReactNode + } +> + +export function FormCard({ + actions, + cancel, + append, + children, + prepend, + disabled, + + // form + inert = disabled, + ...props +}: FormCardProps) { + return ( +
+ {prepend &&
{prepend}
} + +
+ {children} +
+ + {append &&
{append}
} + + {(actions || cancel) && ( +
+ {actions} +
+ {cancel} +
+ )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx new file mode 100644 index 00000000000..006d05fa49a --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-checkbox.tsx @@ -0,0 +1,73 @@ +import { JSX, ReactNode, useContext, useRef } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { clsx } from '../../lib/clsx.ts' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { FieldsetContext } from './fieldset.tsx' +import { InputContainer } from './input-container.tsx' + +export type InputCheckboxProps = Override< + Omit, + { + className?: string + children?: ReactNode + } +> + +export function InputCheckbox({ + className, + children, + + // input + id, + ref, + disabled, + 'aria-labelledby': ariaLabelledBy, + ...props +}: InputCheckboxProps) { + const htmlFor = useRandomString('input-checkbox-') + const containerRef = useRef(null) + const inputRef = useRef(null) + const ctx = useContext(FieldsetContext) + + const inputId = id ?? htmlFor + + return ( + " element (through "htmlFor") over the wrapping "
" to describe the checkbox. + undefined + : ariaLabelledBy ?? ctx.labelId + } + ref={mergeRefs([ref, inputRef])} + id={inputId} + className="accent-brand outline-none" + type="checkbox" + /> + } + tabIndex={-1} + onClick={(event) => { + if (event.target === containerRef.current && !event.defaultPrevented) { + inputRef.current?.click() + inputRef.current?.focus() + } + }} + > + {children && ( + + )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx new file mode 100644 index 00000000000..b1e92abd358 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-container.tsx @@ -0,0 +1,107 @@ +import { JSX, ReactNode, useState } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type InputContainerProps = Override< + JSX.IntrinsicElements['div'], + { + icon: ReactNode + append?: ReactNode + bellow?: ReactNode + } +> + +export function InputContainer({ + icon, + append, + bellow, + + // div + className, + children, + onFocus, + onBlur, + ...props +}: InputContainerProps) { + const [hasFocus, setHasFocus] = useState(false) + + return ( +
{ + onFocus?.(event) + if (!event.defaultPrevented) setHasFocus(true) + }} + onBlur={(event) => { + onBlur?.(event) + if (!event.defaultPrevented) setHasFocus(false) + }} + className={clsx( + // Layout + 'min-h-12', + 'max-w-full', + 'overflow-hidden', + // Border + 'rounded-lg', + className, + )} + > +
+ {icon && ( +
+ {icon} +
+ )} + + {children} + +
{append}
+
+ {bellow && ( +
+ {bellow} +
+ )} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx new file mode 100644 index 00000000000..ced2b5002a6 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-email-address.tsx @@ -0,0 +1,66 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useState } from 'react' +import { Override } from '../../lib/util.ts' +import { AtSymbolIcon } from '../utils/icons.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputEmailAddressProps = Override< + Omit, + { + onEmail?: (email: string | undefined) => void + } +> + +export function InputEmailAddress({ + onEmail, + + // InputTextProps + autoCapitalize = 'none', + autoComplete = 'email', + autoCorrect = 'off', + dir = 'auto', + icon = , + onBlur, + onChange, + pattern = '^[^@]+@[^@]+\\.[^@]+$', + spellCheck = 'false', + value, + defaultValue = value, + ...props +}: InputEmailAddressProps) { + const { t } = useLingui() + const [email, setEmail] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + const doChange = useCallback( + (event: ChangeEvent) => { + const email = event.target.value.toLowerCase() + + setEmail(email) + onChange?.(event) + onEmail?.(event.target.validity.valid ? email : undefined) + }, + [onChange, onEmail], + ) + + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx new file mode 100644 index 00000000000..ec84e36d5e1 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-new-password.tsx @@ -0,0 +1,62 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useState } from 'react' +import { MIN_PASSWORD_LENGTH } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' +import { PasswordStrengthLabel } from '../utils/password-strength-label.tsx' +import { PasswordStrengthMeter } from '../utils/password-strength-meter.tsx' +import { InputPassword, InputPasswordProps } from './input-password.tsx' + +export type InputNewPasswordProps = Override< + Omit, + { + password?: string + onPassword?: (password: undefined | string) => void + } +> + +export function InputNewPassword({ + password: passwordInit = '', + onPassword, + + // InputPasswordProps + onChange, + autoComplete = 'new-password', + minLength = MIN_PASSWORD_LENGTH, + ...props +}: InputNewPasswordProps) { + const { t } = useLingui() + const [password, setPassword] = useState(passwordInit) + + const doChange = useCallback( + (event: ChangeEvent) => { + const { value } = event.target + onChange?.(event) + if (event.defaultPrevented) return + setPassword(value) + onPassword?.(event.target.validity.valid ? value : undefined) + }, + [onChange, onPassword], + ) + + return ( + + + + + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx new file mode 100644 index 00000000000..d98cd06e5f9 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-password.tsx @@ -0,0 +1,88 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useCallback, useRef, useState } from 'react' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { LockIcon } from '../utils/icons.tsx' +import { ButtonToggleVisibility } from './button-toggle-visibility.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputPasswordProps = Override< + Omit, + { + autoHide?: boolean + } +> + +export function InputPassword({ + autoHide = true, + + // InputTextProps + onBlur, + onChange, + append, + autoComplete = 'current-password', + icon = , + value, + defaultValue = value, + ref, + dir = 'auto', + autoCapitalize = 'none', + autoCorrect = 'off', + spellCheck = 'false', + ...props +}: InputPasswordProps) { + const { t } = useLingui() + const inputRef = useRef(null) + const [visible, setVisible] = useState(false) + const [password, setPassword] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + const doChange = useCallback( + (event: ChangeEvent) => { + onChange?.(event) + setPassword(event.target.value) + }, + [onChange], + ) + + return ( + { + onBlur?.(event) + if (!event.defaultPrevented) setVisible(false) + } + : onBlur + } + value={password} + onChange={doChange} + type={visible ? 'text' : 'password'} + autoComplete={autoComplete} + append={ + <> + { + setVisible((prev) => !prev) + inputRef.current?.focus() + }} + /> + {append} + + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx new file mode 100644 index 00000000000..f093dc0ae11 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-text.tsx @@ -0,0 +1,76 @@ +import { JSX, ReactNode, useContext, useRef } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { mergeRefs } from '../../lib/ref.ts' +import { Override } from '../../lib/util.ts' +import { FieldsetContext } from './fieldset.tsx' +import { InputContainer } from './input-container.tsx' + +export type InputTextProps = Override< + Omit, + { + icon?: ReactNode + append?: ReactNode + bellow?: ReactNode + className?: string + } +> + +export function InputText({ + icon, + append, + bellow, + className, + + // input + onFocus, + onBlur, + ref, + disabled, + 'aria-labelledby': ariaLabelledBy, + ...props +}: InputTextProps) { + const ctx = useContext(FieldsetContext) + + const inputRef = useRef(null) + const focusedRef = useRef(false) // ref instead of state to avoid re-renders + + return ( + { + if (inputRef.current !== event.target) { + event.preventDefault() + event.stopPropagation() + inputRef.current?.focus() + } + }} + onMouseDown={(event) => { + if (focusedRef.current && event.target !== inputRef.current) { + // Prevent "blur" event from firing when clicking outside the input + event.preventDefault() + event.stopPropagation() + } + }} + > + { + onFocus?.(event) + if (!event.defaultPrevented) focusedRef.current = true + }} + onBlur={(event) => { + onBlur?.(event) + if (!event.defaultPrevented) focusedRef.current = false + }} + /> + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx new file mode 100644 index 00000000000..78f6702c7ae --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/input-token.tsx @@ -0,0 +1,94 @@ +import { useLingui } from '@lingui/react/macro' +import { ChangeEvent, useState } from 'react' +import { Override } from '../../lib/util.ts' +import { TokenIcon } from '../utils/icons.tsx' +import { InputText, InputTextProps } from './input-text.tsx' + +export type InputTokenProps = Override< + Omit< + InputTextProps, + | 'type' + | 'pattern' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoComplete' + | 'spellCheck' + | 'minLength' + | 'maxLength' + | 'placeholder' + | 'dir' + >, + { + example?: string + onToken?: (code: string | null) => void + } +> + +export const OTP_CODE_EXAMPLE = 'XXXXX-XXXXX' + +export function InputToken({ + example = OTP_CODE_EXAMPLE, + onToken, + + // InputTextProps + icon = , + title = example, + onChange, + value, + defaultValue = value, + ...props +}: InputTokenProps) { + const { t } = useLingui() + const [token, setToken] = useState( + typeof defaultValue === 'string' ? defaultValue : '', + ) + + return ( + ) => { + const { value, selectionEnd, selectionStart } = event.currentTarget + + const fixedValue = fix(value) + + event.currentTarget.value = fixedValue + + // Move the cursor back where it was relative to the original value + const pos = selectionEnd ?? selectionStart + if (pos != null) { + const fixedSlicedValue = fix(value.slice(0, pos)) + event.currentTarget.selectionStart = + event.currentTarget.selectionEnd = fixedSlicedValue.length + } + + setToken(fixedValue) + onChange?.(event) + + if (!event.isDefaultPrevented()) { + onToken?.(fixedValue.length === 11 ? fixedValue : null) + } + }} + /> + ) +} + +function fix(value: string) { + const normalized = value.toUpperCase().replaceAll(/[^A-Z2-7]/g, '') + + if (normalized.length <= 5) return normalized + + return `${normalized.slice(0, 5)}-${normalized.slice(5, 10)}` +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx new file mode 100644 index 00000000000..3ed6727ca72 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/forms/wizard-card.tsx @@ -0,0 +1,116 @@ +import { Trans } from '@lingui/react/macro' +import { JSX, ReactNode, useCallback } from 'react' +import { DisabledStep, Step, useStepper } from '../../hooks/use-stepper.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type DoneFn = (...a: any) => unknown + +export type WizardRenderProps = { + /** + * Indicates wether the render function being invoked corresponds to the step + * currently active. The steps titles could, for example, be rendered in a + * list of links, where the current step is highlighted (based on `current`). + * + * Another use for this is to render the next/previous steps in order to + * provide animated transitions between steps. In this case, `current` would + * be used to disable any form interaction with the form transitioning in/out. + */ + current: boolean + invalid: boolean + + prev?: () => void + prevLabel: ReactNode + + // On the last step, the "next()" function will actually be the done function + next: (() => void) | TDone + nextLabel: ReactNode +} + +export type WizardRenderFn = ( + data: WizardRenderProps, +) => ReactNode + +export type WizardStep = Step & { + titleRender?: WizardRenderFn + contentRender: WizardRenderFn +} + +export type WizardCardProps = Override< + Omit, + { + prevLabel?: ReactNode + nextLabel?: ReactNode + + onBack?: () => void + backLabel?: ReactNode + + onDone: TDone + doneLabel?: ReactNode + + steps: readonly (WizardStep | DisabledStep)[] + } +> + +export function WizardCard({ + prevLabel, + nextLabel, + + onBack, + backLabel, + + onDone, + doneLabel, + + steps, + className, + + ...props +}: WizardCardProps) { + const { + atFirst, + atLast, + count, + current, + currentPosition, + completed, + toNext, + toPrev, + toRequired, + } = useStepper(steps) + + // Memoized to avoid re-renders in child (rendered) components + const onNext = useCallback(() => { + // If already at last step, go to the first incomplete (required) step + if (!toNext()) toRequired() + }, [toNext, toRequired]) + + const data: WizardRenderProps = { + // The current UI only displays the current title & content. + current: true, + invalid: current ? current.invalid : false, + + prevLabel: (atFirst && backLabel) || prevLabel || Back, + prev: atFirst ? onBack : toPrev, + + nextLabel: (atLast && doneLabel) || nextLabel || Next, + next: atLast && completed ? onDone : onNext, + } + + const stepTitle = current?.titleRender?.(data) + const stepContent = current?.contentRender?.(data) + + return ( +
+

+ + Step {currentPosition} of {count} + +

+ + {stepTitle &&

{stepTitle}

} + + {stepContent} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx deleted file mode 100644 index f71b6f47e97..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/help-card.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { HTMLAttributes } from 'react' -import { LinkDefinition } from '../backend-data' -import { clsx } from '../lib/clsx' - -export type HelpCardProps = { - links?: readonly LinkDefinition[] -} - -export function HelpCard({ - links, - - className, - ...attrs -}: HelpCardProps & - Omit< - HTMLAttributes, - keyof HelpCardProps | 'children' - >) { - const helpLink = links?.find((l) => l.rel === 'help') - - if (!helpLink) return null - - return ( -

- Having trouble?{' '} - - Contact {helpLink.title} - -

- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx deleted file mode 100644 index ee3c840b5db..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/alert-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const AlertIcon = makeSvgComponent( - 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx deleted file mode 100644 index 5410dbe7a65..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/at-symbol-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const AtSymbolIcon = makeSvgComponent( - 'M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx deleted file mode 100644 index fe8b62e807d..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/caret-right-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const CaretRightIcon = makeSvgComponent( - 'M8.293 3.293a1 1 0 0 1 1.414 0l8 8a1 1 0 0 1 0 1.414l-8 8a1 1 0 0 1-1.414-1.414L15.586 12 8.293 4.707a1 1 0 0 1 0-1.414Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx deleted file mode 100644 index 135ecc9d910..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/lock-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const LockIcon = makeSvgComponent( - 'M7 7a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h1V7Zm-1 4v9h12v-9H6Zm9-2H9V7a3 3 0 1 1 6 0v2Zm-3 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx deleted file mode 100644 index b7f57c0e303..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/token-icon.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { makeSvgComponent } from './util' - -export const TokenIcon = makeSvgComponent( - 'M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.498 3.498 0 0 1 5.5 12a3.498 3.498 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.498 3.498 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z', -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx b/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx deleted file mode 100644 index 9ef11cde1f1..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/icons/util.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { SVGProps } from 'react' - -export const makeSvgComponent = (path: string) => - function ( - props: Omit, 'viewBox' | 'children' | 'xmlns'>, - ) { - return ( - - - - ) - } diff --git a/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx deleted file mode 100644 index 69c2fa6af12..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/info-card.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' -import { AlertIcon } from './icons/alert-icon' -import { InputLayout, InputLayoutProps } from './input-layout' - -export type InfoCardProps = Override< - InputLayoutProps, - { - role: 'alert' | 'status' - } -> - -export function InfoCard({ - children, - className, - role = 'alert', - ...props -}: InfoCardProps) { - return ( - - } - {...props} - > -
- {children} -
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx deleted file mode 100644 index 3bda701ff43..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-checkbox.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { InputHTMLAttributes, useRef, useState } from 'react' -import { InputContainer } from './input-container' - -const generateUniqueId = () => Math.random().toString(36).slice(2) - -export type InputCheckboxProps = Omit< - InputHTMLAttributes, - 'type' -> - -export function InputCheckbox({ - id, - children, - className, - ...props -}: InputCheckboxProps) { - const [htmlFor] = useState(generateUniqueId) - const ref = useRef(null) - const inputRef = useRef(null) - - return ( - - } - className={className} - onClick={(event) => { - if (event.target === ref.current && !event.defaultPrevented) { - inputRef.current?.click() - inputRef.current?.focus() - } - }} - > - - - ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx deleted file mode 100644 index dc4165260c6..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-container.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { forwardRef, useState } from 'react' -import { clsx } from '../lib/clsx' -import { InputLayout, InputLayoutProps } from './input-layout' - -export type InputContainerProps = InputLayoutProps - -export const InputContainer = forwardRef( - ({ className, onFocus, icon, onBlur, ...props }, ref) => { - const [focused, setFocused] = useState(false) - - return ( - { - onFocus?.(event) - if (!event.defaultPrevented) setFocused(true) - }} - onBlur={(event) => { - onBlur?.(event) - if (!event.defaultPrevented) setFocused(false) - }} - icon={
{icon}
} - {...props} - /> - ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx deleted file mode 100644 index 686f0ab7962..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-layout.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { forwardRef, HTMLAttributes, ReactNode } from 'react' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type InputLayoutProps = Override< - HTMLAttributes, - { - icon?: ReactNode - append?: ReactNode - } -> - -export const InputLayout = forwardRef( - ({ className, icon, append, children, ...props }, ref) => { - return ( -
-
- {icon} -
-
{children}
- {append &&
{append}
} -
- ) - }, -) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx b/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx deleted file mode 100644 index fea570f9e03..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/input-text.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { - forwardRef, - InputHTMLAttributes, - MouseEventHandler, - ReactNode, - useCallback, - useImperativeHandle, - useRef, - useState, -} from 'react' -import { InputContainer } from './input-container' - -export const InputText = forwardRef< - HTMLInputElement, - { - icon?: ReactNode - } & InputHTMLAttributes ->(({ className, icon, children, onFocus, onBlur, ...props }, ref) => { - const [focused, setFocused] = useState(false) - const inputRef = useRef(null) - - useImperativeHandle(ref, () => inputRef.current!, []) - - const handleClick = useCallback>( - (event) => { - if (inputRef.current !== event.target) { - event.preventDefault() - event.stopPropagation() - inputRef.current?.focus() - } - }, - [], - ) - - const handleMouseDown = useCallback>( - (event) => { - if (focused && event.target !== inputRef.current) { - // Prevent "blur" event from firing when clicking outside the input - event.preventDefault() - event.stopPropagation() - } - }, - [focused], - ) - - return ( - - { - setFocused(true) - onFocus?.(event) - }} - onBlur={(event) => { - setFocused(false) - onBlur?.(event) - }} - {...props} - /> - {children} - - ) -}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx deleted file mode 100644 index a68b597d499..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/layout-title-page.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { HTMLAttributes, ReactNode } from 'react' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' - -export type LayoutTitlePageProps = Override< - HTMLAttributes, - { - title?: ReactNode - subtitle?: ReactNode - } -> - -export function LayoutTitlePage({ - children, - title, - subtitle, - className, - ...props -}: LayoutTitlePageProps) { - return ( -
-
- {title && ( -

- {title} -

- )} - - {subtitle && ( -

- {subtitle} -

- )} -
- -
{children}
-
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx deleted file mode 100644 index 0e1f8189ebf..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/layout-welcome.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { HTMLAttributes } from 'react' -import { Override } from '../lib/util' -import { clsx } from '../lib/clsx' - -export type LayoutWelcomeProps = Override< - HTMLAttributes, - { - name?: string - logo?: string - links?: Array<{ - title: string - href: string - rel?: string - }> - logoAlt?: string - } -> - -export function LayoutWelcome({ - name, - logo, - logoAlt = name || 'Logo', - links, - children, - className, - ...props -}: LayoutWelcomeProps) { - return ( -
-
- {logo && ( - {logoAlt} - )} - - {name && ( -

- {name} -

- )} - - {children} -
- - {links != null && links.length > 0 && ( - - )} -
- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx new file mode 100644 index 00000000000..f612cc26523 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-title-page.tsx @@ -0,0 +1,77 @@ +import { JSX, ReactNode } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { LocaleSelector } from '../../locales/locale-selector.tsx' + +export type LayoutTitlePageProps = Override< + JSX.IntrinsicElements['div'], + { + title?: string + subtitle?: ReactNode + children?: ReactNode + } +> + +export function LayoutTitlePage({ + children, + title, + subtitle, + + // HTMLDivElement + className, + ...props +}: LayoutTitlePageProps) { + return ( +
+ {title && {title}} + +
+
+ {title && ( +

+ {title} +

+ )} + + {subtitle && ( +

+ {subtitle} +

+ )} +
+ + +
+ +
{children}
+
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx new file mode 100644 index 00000000000..a4e8175aeab --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/layouts/layout-welcome.tsx @@ -0,0 +1,73 @@ +import { JSX } from 'react' +import { CustomizationData } from '../../backend-types.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { LocaleSelector } from '../../locales/locale-selector.tsx' +import { LinkAnchor } from '../utils/link-anchor.tsx' + +export type LayoutWelcomeProps = Override< + JSX.IntrinsicElements['div'], + { + customizationData: CustomizationData | undefined + title?: string + } +> + +export function LayoutWelcome({ + customizationData: { logo, name, links } = {}, + title = name, + + // div + className, + children, + ...props +}: LayoutWelcomeProps) { + return ( +
+ {title && {title}} + +
+ {logo && ( + {name + )} + + {name && ( +

+ {name} +

+ )} + + {children} +
+ + +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx deleted file mode 100644 index ff30bb1ac1e..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-in-form.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import { ReactNode, SyntheticEvent, useCallback, useState } from 'react' - -import { - InvalidCredentialsError, - SecondAuthenticationFactorRequiredError, -} from '../lib/api' -import { clsx } from '../lib/clsx' -import { Override } from '../lib/util' -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { AtSymbolIcon } from './icons/at-symbol-icon' -import { LockIcon } from './icons/lock-icon' -import { InfoCard } from './info-card' -import { InputCheckbox } from './input-checkbox' -import { InputText } from './input-text' -import { TokenIcon } from './icons/token-icon' -import { Fieldset } from './fieldset' - -export type SignInFormOutput = { - username: string - password: string - remember?: boolean -} - -export type SignInFormProps = Override< - FormCardProps, - { - onSubmit: (credentials: SignInFormOutput) => void | PromiseLike - submitLabel?: ReactNode - submitAria?: string - - onCancel?: () => void - cancelLabel?: ReactNode - cancelAria?: string - - accountSection?: ReactNode - sessionSection?: ReactNode - secondFactorSection?: ReactNode - - usernameDefault?: string - usernameReadonly?: boolean - usernameLabel?: string - usernamePlaceholder?: string - usernameAria?: string - usernamePattern?: string - usernameFormat?: string - - passwordLabel?: string - passwordPlaceholder?: string - passwordWarning?: ReactNode - passwordAria?: string - passwordPattern?: string - passwordFormat?: string - - secondFactorLabel?: string - secondFactorPlaceholder?: string - secondFactorAria?: string - secondFactorPattern?: string - secondFactorFormat?: string - secondFactorHint?: string - secondFactorParseValue?: (value: string) => string | false - - rememberVisible?: boolean - rememberDefault?: boolean - rememberLabel?: string - rememberAria?: string - } -> - -export function SignInForm({ - onSubmit, - submitAria = 'Next', - submitLabel = submitAria, - - onCancel = undefined, - cancelAria = 'Cancel', - cancelLabel = cancelAria, - - accountSection = 'Account', - sessionSection = 'Session', - secondFactorSection = '2FA Confirmation', - - usernameDefault = '', - usernameReadonly = false, - usernameLabel = 'Username or email address', - usernameAria = usernameLabel, - usernamePlaceholder = usernameLabel, - usernamePattern = undefined, - usernameFormat = 'valid email address or username', - - passwordLabel = 'Password', - passwordAria = passwordLabel, - passwordPlaceholder = passwordLabel, - passwordPattern = undefined, - passwordFormat = 'non empty string', - passwordWarning = ( - <> -

Warning

-

- Please verify the domain name of the website before entering your - password. Never enter your password on a domain you do not trust. -

- - ), - - secondFactorLabel = 'Confirmation code', - secondFactorAria = secondFactorLabel, - secondFactorPlaceholder = secondFactorLabel, - secondFactorPattern = '^[A-Z2-7]{5}-[A-Z2-7]{5}$', - secondFactorFormat = 'XXXXX-XXXXX', - secondFactorHint = 'Check your $1 email for a login code and enter it here.', - secondFactorParseValue = checkAndFormatEmailOtpCode, - - rememberVisible = true, - rememberDefault = false, - rememberLabel = 'Remember this account on this device', - rememberAria = rememberLabel, - - ...props -}: SignInFormProps) { - const [focused, setFocused] = useState(false) - const [loading, setLoading] = useState(false) - const [secondFactor, setSecondFactor] = useState(null) - - const [errorMessage, setErrorMessage] = useState(null) - - const resetState = useCallback(() => { - setSecondFactor(null) - setErrorMessage(null) - }, []) - - const passwordReadonly = secondFactor != null - - const doSubmit = useCallback( - async ( - event: SyntheticEvent< - HTMLFormElement & { - username: HTMLInputElement - password: HTMLInputElement - remember?: HTMLInputElement - secondFactor?: HTMLInputElement - }, - SubmitEvent - >, - ) => { - event.preventDefault() - - const credentials: SignInFormOutput = { - username: event.currentTarget.username.value, - password: event.currentTarget.password.value, - remember: event.currentTarget.remember?.checked, - } - - if (secondFactor) { - const element = event.currentTarget.secondFactor - if (!element) throw new Error('Second factor input not found') - const value = secondFactorParseValue(element.value) - if (!value) { - setSecondFactor({ - type: secondFactor.type, - hint: `Make sure to match the format: ${secondFactorFormat}`, - }) - return - } - credentials[secondFactor.type] = value - } - - setLoading(true) - setErrorMessage(null) - try { - await onSubmit(credentials) - } catch (err) { - if (err instanceof SecondAuthenticationFactorRequiredError) { - setSecondFactor({ - type: err.type, - hint: err.hint, - }) - } else { - setErrorMessage(parseErrorMessage(err)) - } - } finally { - setLoading(false) - } - }, - [secondFactor, onSubmit], - ) - - return ( - - {cancelLabel} - - ) - } - actions={ - - } - {...props} - > -
- } - name="username" - type="text" - onChange={resetState} - placeholder={usernamePlaceholder} - aria-label={usernameAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="username" - spellCheck="false" - dir="auto" - enterKeyHint="next" - required - defaultValue={usernameDefault} - readOnly={usernameReadonly} - disabled={usernameReadonly} - pattern={usernamePattern} - title={usernameFormat} - /> - - } - name="password" - type="password" - onChange={resetState} - onFocus={() => setFocused(true)} - onBlur={() => setTimeout(setFocused, 100, false)} - placeholder={passwordPlaceholder} - aria-label={passwordAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="current-password" - dir="auto" - enterKeyHint="done" - spellCheck="false" - required - readOnly={passwordReadonly} - disabled={passwordReadonly} - pattern={passwordPattern} - title={passwordFormat} - /> - - {passwordWarning && ( -
- {passwordWarning} -
- )} -
- - {rememberVisible && ( -
- - {rememberLabel} - -
- )} - - {secondFactor && ( -
-
- } - name="secondFactor" - type="text" - placeholder={secondFactorPlaceholder} - aria-label={secondFactorAria} - autoCapitalize="none" - autoCorrect="off" - autoComplete="off" - spellCheck="false" - dir="auto" - enterKeyHint="done" - required - pattern={secondFactorPattern} - title={secondFactorFormat} - autoFocus={true} - /> -

- {secondFactorHint.replaceAll('$1', secondFactor.hint)} -

-
-
- )} -
- ) -} - -function parseErrorMessage(err: unknown): string { - if (err instanceof InvalidCredentialsError) { - return 'Invalid username or password' - } - - return 'An unknown error occurred' -} - -export function checkAndFormatEmailOtpCode(code: string): string | false { - const EMAIL_CODE_REGEX = /^[A-Z2-7]{5}-[A-Z2-7]{5}$/ - - // Trim the reset code - let fixed = code.trim().toUpperCase() - - // Add a dash if needed - if (fixed.length === 10) { - fixed = `${fixed.slice(0, 5)}-${fixed.slice(5, 10)}` - } - - // Check that it is a valid format - if (!EMAIL_CODE_REGEX.test(fixed)) { - return false - } - - return fixed -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx deleted file mode 100644 index ef97af9fb86..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-account-form.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { - FormHTMLAttributes, - ReactNode, - SyntheticEvent, - useCallback, - useState, -} from 'react' - -import { Button } from './button' -import { FormCard, FormCardProps } from './form-card' -import { Override } from '../lib/util' -import { Fieldset } from './fieldset' - -export type SignUpAccountFormOutput = { - username: string - password: string -} - -export type SignUpAccountFormProps = Override< - FormCardProps, - { - onSubmit: (credentials: SignUpAccountFormOutput) => void | PromiseLike - submitLabel?: ReactNode - submitAria?: string - - onCancel?: () => void - cancelLabel?: ReactNode - cancelAria?: string - - username?: string - usernamePlaceholder?: string - usernameLabel?: string - usernameAria?: string - usernamePattern?: string - usernameTitle?: string - - passwordPlaceholder?: string - passwordLabel?: string - passwordAria?: string - passwordPattern?: string - passwordTitle?: string - } -> - -export function SignUpAccountForm({ - onSubmit, - submitAria = 'Next', - submitLabel = submitAria, - - onCancel = undefined, - cancelAria = 'Cancel', - cancelLabel = cancelAria, - - username: defaultUsername = '', - usernameLabel = 'Username', - usernameAria = usernameLabel, - usernamePlaceholder = usernameLabel, - usernamePattern, - usernameTitle, - - passwordLabel = 'Password', - passwordAria = passwordLabel, - passwordPlaceholder = passwordLabel, - passwordPattern, - passwordTitle, - - children, - ...props -}: SignUpAccountFormProps & - Omit, keyof SignUpAccountFormProps>) { - const [loading, setLoading] = useState(false) - const [errorMessage, setErrorMessage] = useState(null) - - const doSubmit = useCallback( - async ( - event: SyntheticEvent< - HTMLFormElement & { - username: HTMLInputElement - password: HTMLInputElement - }, - SubmitEvent - >, - ) => { - event.preventDefault() - - const credentials = { - username: event.currentTarget.username.value, - password: event.currentTarget.password.value, - } - - setLoading(true) - setErrorMessage(null) - try { - await onSubmit(credentials) - } catch (err) { - setErrorMessage(parseErrorMessage(err)) - } finally { - setLoading(false) - } - }, - [onSubmit, setErrorMessage, setLoading], - ) - - return ( - - {cancelLabel} - - ) - } - actions={ - - } - {...props} - > -
- - - -
-
- ) -} - -function parseErrorMessage(err: unknown): string { - switch ((err as any)?.message) { - case 'Invalid credentials': - return 'Invalid username or password' - default: - console.error(err) - return 'An unknown error occurred' - } -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx b/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx deleted file mode 100644 index 053511f7e80..00000000000 --- a/packages/oauth/oauth-provider/src/assets/app/components/sign-up-disclaimer.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { HTMLAttributes } from 'react' -import { LinkDefinition } from '../backend-data' -import { clsx } from '../lib/clsx' - -export type SignUpDisclaimerProps = { - links?: readonly LinkDefinition[] -} - -export function SignUpDisclaimer({ - links, - - className, - ...attrs -}: SignUpDisclaimerProps & - Omit< - HTMLAttributes, - keyof SignUpDisclaimerProps | 'children' - >) { - const relevantLinks = links?.filter( - (l) => l.rel === 'privacy-policy' || l.rel === 'terms-of-service', - ) - - return ( -

- By creating an account you agree to the{' '} - {relevantLinks && relevantLinks.length - ? relevantLinks.map((l, i, a) => ( - - {i > 0 && (i < a.length - 1 ? ', ' : ' and ')} - - {l.title} - - - )) - : 'Terms of Service and Privacy Policy'} - . -

- ) -} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx new file mode 100644 index 00000000000..150a6bb42f1 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-identifier.tsx @@ -0,0 +1,23 @@ +import { JSX } from 'react' +import { Account } from '../../backend-types.ts' +import { Override } from '../../lib/util.ts' + +export type AccountIdentifierProps = Override< + Omit, + { + account: Account + } +> + +export function AccountIdentifier({ + account, + + // b + ...props +}: AccountIdentifierProps) { + return ( + + {account.preferred_username || account.email || account.sub} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx new file mode 100644 index 00000000000..a1c67c62ac5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/account-image.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react' +import { AccountIcon } from './icons.tsx' + +export type AccountIconProps = { + src?: string + alt: string +} + +export function AccountImage({ src, alt }: AccountIconProps) { + const [errored, setErrored] = useState(false) + + useEffect(() => { + setErrored(false) + }, [src]) + + return src && !errored ? ( + {alt} setErrored(true)} + /> + ) : ( +
+ +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx new file mode 100644 index 00000000000..c9e263b9301 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/admonition.tsx @@ -0,0 +1,52 @@ +import { JSX, memo } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' +import { AlertIcon, EyeIcon } from './icons.tsx' + +export type AdmonitionProps = Override< + JSX.IntrinsicElements['div'], + { + role: 'alert' | 'status' | 'info' + } +> + +export const Admonition = memo(function Admonition({ + role = 'alert', + children, + className, + ...props +}: AdmonitionProps) { + return ( +
+ {role === 'info' ? ( + + ) : ( + + )} + +
{children}
+
+ ) +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx new file mode 100644 index 00000000000..1307425207f --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/client-name.tsx @@ -0,0 +1,45 @@ +import { Trans } from '@lingui/react/macro' +import { JSX } from 'react' +import type { OAuthClientMetadata } from '@atproto/oauth-types' +import { Override } from '../../lib/util.ts' +import { UrlViewer } from './url-viewer.tsx' + +export type ClientNameProps = Override< + Omit, + { + clientId: string + clientMetadata: OAuthClientMetadata + clientTrusted: boolean + } +> + +export function ClientName({ + clientId, + clientMetadata, + clientTrusted, + + // span + ...attrs +}: ClientNameProps) { + if (clientTrusted && clientMetadata.client_name) { + return {clientMetadata.client_name} + } + + // @NOTE: not using isOAuthClientIdLoopback & isOAuthClientIdDiscoverable from + // @atproto/oauth-types here because 1) we don't need to validate here and 2) + // we prefer not to import un-necessary code to improve bundle size. + + if (clientId.startsWith('http://')) { + return ( + + An application on your device + + ) + } + + if (clientId.startsWith('https://')) { + return + } + + return {clientId} +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx new file mode 100644 index 00000000000..d80f64eba35 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-card.tsx @@ -0,0 +1,93 @@ +import { Trans } from '@lingui/react/macro' +import { memo, useEffect, useMemo, useState } from 'react' +import { useRandomString } from '../../hooks/use-random-string.ts' +import { Api } from '../../lib/api.ts' +import { JsonErrorResponse } from '../../lib/json-client.ts' +import { Override } from '../../lib/util.ts' +import { Admonition, AdmonitionProps } from './admonition.tsx' +import { ErrorMessage } from './error-message.tsx' + +export type ErrorCardProps = Override< + Omit, + { + error: unknown + } +> +export const ErrorCard = memo(function ErrorCard({ + error, + + // Admonition + children, + onClick, + onKeyDown, + ...props +}: ErrorCardProps) { + const [inputCount, setInputCount] = useState(0) + // Every 5th input will toggle showing the details + const showDetails = ((inputCount / 5) | 0) % 2 === 1 + + const detailsDivId = useRandomString('error-card-') + + const parsedError = useMemo( + () => + error instanceof JsonErrorResponse + ? // Already parsed: + error + : // If "error" is a json object, try parsing it as a JsonErrorResponse: + Api.parseError(error) ?? error, + [error], + ) + + useEffect(() => { + // For debugging purposes + console.warn('Displayed error details:', parsedError) + + // Reset the input count when the error changes + setInputCount(0) + }, [parsedError]) + + return ( + { + onKeyDown?.(event) + if (!event.defaultPrevented) { + setInputCount((c) => c + 1) + } + }} + onClick={(event) => { + onClick?.(event) + if (!event.defaultPrevented) { + setInputCount((c) => c + 1) + } + }} + {...props} + > + + + {children &&
{children}
} + + +
+ ) +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx new file mode 100644 index 00000000000..06b070f2349 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/error-message.tsx @@ -0,0 +1,62 @@ +import { Trans } from '@lingui/react/macro' +import { ReactNode, memo } from 'react' +import { + EmailTakenError, + HandleUnavailableError, + InvalidCredentialsError, + RequestExpiredError, + SecondAuthenticationFactorRequiredError, + UnknownRequestUriError, +} from '../../lib/api.ts' +import { JsonErrorResponse } from '../../lib/json-client.ts' + +export type ApiErrorMessageProps = { + error: unknown +} + +export const ErrorMessage = memo(function ErrorMessage({ + error, +}: ApiErrorMessageProps): ReactNode { + if (error instanceof InvalidCredentialsError) { + return Wrong identifier or password + } + + if (error instanceof EmailTakenError) { + return This email is already used + } + + if (error instanceof HandleUnavailableError) { + switch (error.reason) { + case 'syntax': + return The handle is invalid + case 'domain': + return The domain name is not allowed + case 'slur': + return The handle contains inappropriate language + case 'taken': + if (error.description === 'Reserved handle') { + return This handle is reserved + } + return The handle is already in use + default: + return That handle cannot be used + } + } + + if (error instanceof SecondAuthenticationFactorRequiredError) { + return A second authentication factor is required + } + + if ( + error instanceof UnknownRequestUriError || + error instanceof RequestExpiredError + ) { + return This sign-in session has expired + } + + if (error instanceof JsonErrorResponse) { + return Unexpected server response + } + + return An unknown error occurred +}) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx new file mode 100644 index 00000000000..3b457090329 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/help-card.tsx @@ -0,0 +1,46 @@ +import { Trans } from '@lingui/react/macro' +import { JSX } from 'react' +import { LinkDefinition } from '../../backend-types.ts' +import { clsx } from '../../lib/clsx.ts' +import { Override } from '../../lib/util.ts' + +export type HelpCardProps = Override< + Omit, + { + links?: readonly LinkDefinition[] + } +> + +export function HelpCard({ + links, + + className, + ...props +}: HelpCardProps) { + const helpLink = links?.find((l) => l.rel === 'help') + + if (!helpLink) return null + + return ( +

+ + Having trouble?{' '} + + Contact support + + +

+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx new file mode 100644 index 00000000000..a6b63758996 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/icons.tsx @@ -0,0 +1,88 @@ +import type { FunctionComponent, JSX } from 'react' +import { Override } from '../../lib/util.ts' + +export type IconProps = Override< + Omit, + { + /** + * The title of the icon, used for accessibility. + */ + title?: string + } +> + +const makeSvgComponent = (path: string, displayName: string) => { + const SvgComponent: FunctionComponent = ({ title, ...props }) => ( + + {title && {title}} + + + ) + SvgComponent.displayName = displayName + return SvgComponent +} + +export const AccountIcon = makeSvgComponent( + 'M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z', + 'AccountIcon', +) + +export const AlertIcon = makeSvgComponent( + 'M11.14 4.494a.995.995 0 0 1 1.72 0l7.001 12.008a.996.996 0 0 1-.86 1.498H4.999a.996.996 0 0 1-.86-1.498L11.14 4.494Zm3.447-1.007c-1.155-1.983-4.019-1.983-5.174 0L2.41 15.494C1.247 17.491 2.686 20 4.998 20h14.004c2.312 0 3.751-2.509 2.587-4.506L14.587 3.487ZM13 9.019a1 1 0 1 0-2 0v2.994a1 1 0 1 0 2 0V9.02Zm-1 4.731a1.25 1.25 0 1 0 0 2.5 1.25 1.25 0 0 0 0-2.5Z', + 'AlertIcon', +) + +export const AtSymbolIcon = makeSvgComponent( + 'M12 4a8 8 0 1 0 4.21 14.804 1 1 0 0 1 1.054 1.7A9.958 9.958 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 1.104-.27 2.31-.949 3.243-.716.984-1.849 1.6-3.331 1.465a4.207 4.207 0 0 1-2.93-1.585c-.94 1.21-2.388 1.94-3.985 1.715-2.53-.356-4.04-2.91-3.682-5.458.358-2.547 2.514-4.586 5.044-4.23.905.127 1.68.536 2.286 1.126a1 1 0 0 1 1.964.368l-.515 3.545v.002a2.222 2.222 0 0 0 1.999 2.526c.75.068 1.212-.21 1.533-.65.358-.493.566-1.245.566-2.067a8 8 0 0 0-8-8Zm-.112 5.13c-1.195-.168-2.544.819-2.784 2.529-.24 1.71.784 3.03 1.98 3.198 1.195.168 2.543-.819 2.784-2.529.24-1.71-.784-3.03-1.98-3.198Z', + 'AtSymbolIcon', +) + +export const CaretRightIcon = makeSvgComponent( + 'M8.293 3.293a1 1 0 0 1 1.414 0l8 8a1 1 0 0 1 0 1.414l-8 8a1 1 0 0 1-1.414-1.414L15.586 12 8.293 4.707a1 1 0 0 1 0-1.414Z', + 'CaretRightIcon', +) + +export const CheckMarkIcon = makeSvgComponent( + 'M21.59 3.193a1 1 0 0 1 .217 1.397l-11.706 16a1 1 0 0 1-1.429.193l-6.294-5a1 1 0 1 1 1.244-1.566l5.48 4.353 11.09-15.16a1 1 0 0 1 1.398-.217Z', + 'CheckMarkIcon', +) + +export const EmailIcon = makeSvgComponent( + 'M4.568 4h14.864c.252 0 .498 0 .706.017.229.019.499.063.77.201a2 2 0 0 1 .874.874c.138.271.182.541.201.77.017.208.017.454.017.706v10.864c0 .252 0 .498-.017.706a2.022 2.022 0 0 1-.201.77 2 2 0 0 1-.874.874 2.022 2.022 0 0 1-.77.201c-.208.017-.454.017-.706.017H4.568c-.252 0-.498 0-.706-.017a2.022 2.022 0 0 1-.77-.201 2 2 0 0 1-.874-.874 2.022 2.022 0 0 1-.201-.77C2 17.93 2 17.684 2 17.432V6.568c0-.252 0-.498.017-.706.019-.229.063-.499.201-.77a2 2 0 0 1 .874-.874c.271-.138.541-.182.77-.201C4.07 4 4.316 4 4.568 4Zm.456 2L12 11.708 18.976 6H5.024ZM20 7.747l-6.733 5.509a2 2 0 0 1-2.534 0L4 7.746V17.4a8.187 8.187 0 0 0 .011.589h.014c.116.01.278.011.575.011h14.8a8.207 8.207 0 0 0 .589-.012v-.013c.01-.116.011-.279.011-.575V7.747Z', + 'EmailIcon', +) + +export const EyeIcon = makeSvgComponent( + 'M12 6.5c3.79 0 7.17 2.13 8.82 5.5-1.65 3.37-5.02 5.5-8.82 5.5S4.83 15.37 3.18 12C4.83 8.63 8.21 6.5 12 6.5m0-2C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5m0 5c1.38 0 2.5 1.12 2.5 2.5s-1.12 2.5-2.5 2.5-2.5-1.12-2.5-2.5 1.12-2.5 2.5-2.5m0-2c-2.48 0-4.5 2.02-4.5 4.5s2.02 4.5 4.5 4.5 4.5-2.02 4.5-4.5-2.02-4.5-4.5-4.5', + 'EyeIcon', +) + +export const EyeSlashIcon = makeSvgComponent( + 'M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2.41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6m-1.07 1.14L13 9.21c.57.25 1.03.71 1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07.14M2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45zm7.5 7.5 2.61 2.61c-.04.01-.08.02-.12.02-1.38 0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13m-3.4-3.4 1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53', + 'EyeSlashIcon', +) + +export const LockIcon = makeSvgComponent( + 'M7 7a5 5 0 0 1 10 0v2h1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h1V7Zm-1 4v9h12v-9H6Zm9-2H9V7a3 3 0 1 1 6 0v2Zm-3 4a1 1 0 0 1 1 1v3a1 1 0 1 1-2 0v-3a1 1 0 0 1 1-1Z', + 'LockIcon', +) + +export const TokenIcon = makeSvgComponent( + 'M4 5.5a.5.5 0 0 0-.5.5v2.535a.5.5 0 0 0 .25.433A3.498 3.498 0 0 1 5.5 12a3.498 3.498 0 0 1-1.75 3.032.5.5 0 0 0-.25.433V18a.5.5 0 0 0 .5.5h16a.5.5 0 0 0 .5-.5v-2.535a.5.5 0 0 0-.25-.433A3.498 3.498 0 0 1 18.5 12a3.5 3.5 0 0 1 1.75-3.032.5.5 0 0 0 .25-.433V6a.5.5 0 0 0-.5-.5H4ZM2.5 6A1.5 1.5 0 0 1 4 4.5h16A1.5 1.5 0 0 1 21.5 6v3.17a.5.5 0 0 1-.333.472 2.501 2.501 0 0 0 0 4.716.5.5 0 0 1 .333.471V18a1.5 1.5 0 0 1-1.5 1.5H4A1.5 1.5 0 0 1 2.5 18v-3.17a.5.5 0 0 1 .333-.472 2.501 2.501 0 0 0 0-4.716.5.5 0 0 1-.333-.471V6Zm12 2a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Zm0 4a.5.5 0 1 1 1 0 .5.5 0 0 1-1 0Z', + 'TokenIcon', +) + +export const XMarkIcon = makeSvgComponent( + 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z', + 'XMarkIcon', +) diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx new file mode 100644 index 00000000000..cc24ead7b83 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-anchor.tsx @@ -0,0 +1,28 @@ +import { JSX } from 'react' +import { LinkDefinition } from '../../backend-types.ts' +import { Override } from '../../lib/util.ts' +import { LinkTitle } from './link-title.tsx' + +export type LinkAnchorProps = Override< + JSX.IntrinsicElements['a'], + { + link: LinkDefinition + } +> +export function LinkAnchor({ + link, + + // a + children = , + role = 'link', + target = '_blank', + href = link.href, + rel = link.rel, + ...props +}: LinkAnchorProps) { + return ( + + {children} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx new file mode 100644 index 00000000000..048788f43e9 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/link-title.tsx @@ -0,0 +1,26 @@ +import { Trans } from '@lingui/react/macro' +import { LinkDefinition } from '../../backend-types.ts' +import { MultiLangString } from './multi-lang-string.tsx' + +export type LinkNameProps = { + link: LinkDefinition +} + +export function LinkTitle({ link }: LinkNameProps) { + return ( + Home + ) : link.rel === 'privacy-policy' ? ( + Privacy Policy + ) : link.rel === 'terms-of-service' ? ( + Terms of Service + ) : link.rel === 'help' ? ( + Support + ) : undefined + } + /> + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx new file mode 100644 index 00000000000..d971c236c99 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/multi-lang-string.tsx @@ -0,0 +1,56 @@ +import { useLingui } from '@lingui/react/macro' +import { ReactNode } from 'react' +import type { LocalizedString } from '../../backend-types.ts' + +export type MultiLangStringProps = { + value: LocalizedString + fallback?: ReactNode +} + +export function MultiLangString({ + value, + fallback, +}: MultiLangStringProps): ReactNode { + const { i18n } = useLingui() + return ( + findMatchingString(value, i18n.locale) ?? + fallback ?? + (typeof value === 'string' ? value : value.en) + ) +} + +/** + * Only returns a string if it matches the desired locale. + */ +function findMatchingString( + value: LocalizedString, + locale: string, +): string | undefined { + switch (typeof value) { + case 'string': + // By convention, string values are in english ("en") + if (locale.startsWith('en')) return value + break + + case 'object': { + // Exact match + const localeMatch = value[locale] + if (typeof localeMatch === 'string') return localeMatch + + // Fallback to language match + const lang = locale.split('-')[0] + const langMatch = value[lang] + if (typeof langMatch === 'string') return langMatch + + // Fallback to any locale from same language (e.g. "pt-PT" -> "pt-BR") + for (const k in value) { + if (k.startsWith(`${lang}-`)) { + const fallbackMatch = value[k] + if (typeof fallbackMatch === 'string') return fallbackMatch + } + } + } + } + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx new file mode 100644 index 00000000000..7c1bfbd5331 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-label.tsx @@ -0,0 +1,37 @@ +import { Trans, useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' + +export type PasswordStrengthLabelProps = Override< + Omit, + { + password: string + } +> + +export function PasswordStrengthLabel({ + password, + + // span + ...props +}: PasswordStrengthLabelProps) { + const { t } = useLingui() + const strength = getPasswordStrength(password) + + return ( + + {strength === PasswordStrength.extra ? ( + Extra + ) : strength === PasswordStrength.strong ? ( + Strong + ) : strength === PasswordStrength.moderate ? ( + Moderate + ) : password ? ( + Weak + ) : ( + Missing + )} + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx new file mode 100644 index 00000000000..233c81104f8 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/password-strength-meter.tsx @@ -0,0 +1,58 @@ +import { useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { clsx } from '../../lib/clsx.ts' +import { PasswordStrength, getPasswordStrength } from '../../lib/password.ts' +import { Override } from '../../lib/util.ts' + +export type PasswordStrengthMeterProps = Override< + Omit< + JSX.IntrinsicElements['div'], + | 'children' + | 'role' + | 'aria-label' + | 'aria-valuemin' + | 'aria-valuemax' + | 'aria-valuenow' + >, + { + password: string + } +> + +export function PasswordStrengthMeter({ + password, + + // div + className, + ...props +}: PasswordStrengthMeterProps) { + const { t } = useLingui() + const strength = password ? getPasswordStrength(password) : 0 + + const colorBg = 'bg-gray-300 dark:bg-slate-500' + const color = + strength === PasswordStrength.extra || strength === PasswordStrength.strong + ? 'bg-success' + : strength === PasswordStrength.moderate + ? 'bg-warning' + : 'bg-error' + + return ( +
+ {Array.from({ length: 4 }, (_, i) => ( +
i ? color : colorBg}`} + /> + ))} +
+ ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx b/packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx similarity index 83% rename from packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx rename to packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx index 4879a5d8e23..bdbd4cb19a2 100644 --- a/packages/oauth/oauth-provider/src/assets/app/components/url-viewer.tsx +++ b/packages/oauth/oauth-provider/src/assets/app/components/utils/url-viewer.tsx @@ -1,4 +1,5 @@ -import { HTMLAttributes, useMemo } from 'react' +import { JSX, useMemo } from 'react' +import { Override } from '../../lib/util.ts' export type UrlPartRenderingOptions = { faded?: boolean @@ -12,10 +13,10 @@ export type UrlRendererProps = { path?: boolean | UrlPartRenderingOptions query?: boolean | UrlPartRenderingOptions hash?: boolean | UrlPartRenderingOptions - as?: keyof JSX.IntrinsicElements + as?: string } -export function UrlViewer({ +export function UrlViewer({ url, proto = false, host = true, @@ -23,12 +24,14 @@ export function UrlViewer({ query = false, hash = false, as: As = 'span', - ...attrs -}: UrlRendererProps & HTMLAttributes) { + + // Element + ...props +}: Override) { const urlObj = useMemo(() => new URL(url), [url]) return ( - + {proto && ( any>(fn: F, deps: unknown[]) { + const { showBoundary } = useErrorBoundary() - remember?: boolean + return useCallback( + async (...args: Parameters): Promise>> => { + try { + return await fn(...args) + } catch (error) { + if (error instanceof UnknownRequestUriError) showBoundary(error) + throw error + } + }, + deps.concat(showBoundary), + ) } -export type SignUpData = { - username: string - password: string - extra?: Record +export type UseApiOptions = { + requestUri: string + sessions?: readonly Session[] + newSessionsRequireConsent?: boolean + onRedirected?: () => void } -export function useApi( - { - clientId, - requestUri, - csrfCookie, - sessions: initialSessions, - newSessionsRequireConsent, - }: AuthorizeData, - { - onRedirected, - }: { - onRedirected?: () => void - } = {}, -) { - const csrfToken = useCsrfToken(csrfCookie) ?? '' // Invalid value - const [sessions, setSessions] = useState(initialSessions) - - const setSession = useCallback( +export function useApi({ + requestUri, + sessions: sessionsInit = [], + newSessionsRequireConsent = true, + onRedirected, +}: UseApiOptions) { + const csrfToken = useCsrfToken(`csrf-${requestUri}`) + if (!csrfToken) throw new Error('CSRF token is missing') + + const api = useMemo(() => new Api(csrfToken), [csrfToken]) + const [sessions, setSessions] = useState(sessionsInit) + + const { i18n } = useLingui() + const { locale } = i18n + + const selectSub = useCallback( (sub: string | null) => { setSessions((sessions) => sub === (sessions.find((s) => s.selected)?.account.sub || null) @@ -46,9 +73,23 @@ export function useApi( [setSessions], ) - const api = useMemo( - () => new Api(requestUri, clientId, csrfToken, newSessionsRequireConsent), - [requestUri, clientId, csrfToken, newSessionsRequireConsent], + const upsertSession = useCallback( + ({ account, consentRequired }: SessionResponse) => { + const session: Session = { + account, + selected: true, + loginRequired: false, + consentRequired: newSessionsRequireConsent || consentRequired, + } + + setSessions((sessions) => + upsert(sessions, session, (s) => s.account.sub === account.sub).map( + // Make sure to de-select any other selected session + (s) => (s === session || !s.selected ? s : { ...s, selected: false }), + ), + ) + }, + [setSessions, newSessionsRequireConsent], ) const performRedirect = useCallback( @@ -59,45 +100,77 @@ export function useApi( [onRedirected], ) - const doSignIn = useCallback( - async (credentials: SignInCredentials): Promise => { - const session = await api.signIn(credentials) - const { sub } = session.account + const doSignIn = useSafeCallback( + async (data: Omit, signal?: AbortSignal) => { + const response = await api.fetch( + '/sign-in', + { ...data, locale }, + { signal }, + ) + upsertSession(response) + }, + [api, locale, upsertSession], + ) - setSessions((sessions) => { - return upsert(sessions, session, (s) => s.account.sub === sub).map( - // Make sure to de-select any other selected session - (s) => (s === session || !s.selected ? s : { ...s, selected: false }), - ) - }) + const doInitiatePasswordReset = useSafeCallback( + async ( + data: Omit, + signal?: AbortSignal, + ) => { + await api.fetch( + '/reset-password-request', + { ...data, locale }, + { signal }, + ) + }, + [api, locale], + ) + + const doConfirmResetPassword = useSafeCallback( + async (data: ConfirmResetPasswordData, signal?: AbortSignal) => { + await api.fetch('/reset-password-confirm', data, { signal }) }, - [api, performRedirect, clientId, setSessions], + [api], ) - const doSignUp = useCallback( - (_data: SignUpData) => { - // - throw new Error('Not implemented') + const doValidateNewHandle = useSafeCallback( + async (data: VerifyHandleAvailabilityData, signal?: AbortSignal) => { + await api.fetch('/verify-handle-availability', data, { signal }) }, [api], ) - const doAccept = useCallback( - async (account: Account) => { - performRedirect(await api.accept(account)) + const doSignUp = useSafeCallback( + async (data: Omit, signal?: AbortSignal) => { + const response = await api.fetch( + '/sign-up', + { ...data, locale }, + { signal }, + ) + upsertSession(response) + }, + [api, locale, upsertSession], + ) + + const doAccept = useSafeCallback( + async (data: AcceptData) => { + performRedirect(api.buildAcceptUrl(data)) }, [api, performRedirect], ) - const doReject = useCallback(async () => { - performRedirect(await api.reject()) + const doReject = useSafeCallback(async () => { + performRedirect(api.buildRejectUrl()) }, [api, performRedirect]) return { sessions, - setSession, + selectSub, doSignIn, + doInitiatePasswordReset, + doConfirmResetPassword, + doValidateNewHandle, doSignUp, doAccept, doReject, diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts new file mode 100644 index 00000000000..d04fec81df6 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-async-action.ts @@ -0,0 +1,120 @@ +import { + ForwardedRef, + useCallback, + useEffect, + useImperativeHandle, + useRef, + useState, +} from 'react' + +export type AsyncActionController = { + reset: () => void +} + +export type UseAsyncActionOptions = { + ref?: ForwardedRef + onLoading?: (loading: boolean) => void + onError?: (error: Error | undefined) => void +} + +export function useAsyncAction( + fn: (signal: AbortSignal) => void | PromiseLike, + { ref, onLoading, onError }: UseAsyncActionOptions = {}, +) { + const [loading, setLoading] = useState(false) + const [error, setError] = useState() + + const doSetError = useCallback( + (error: Error | undefined) => { + setError(error) + onError?.(error) + }, + [onError], + ) + + const doSetLoading = useCallback( + (loading: boolean) => { + setLoading(loading) + onLoading?.(loading) + }, + [onLoading], + ) + + const controllerRef = useRef(null) + + const resetRef = useRef<() => void>(null) + useEffect(() => { + resetRef.current = () => { + controllerRef.current?.abort() + controllerRef.current = null + doSetError(undefined) + doSetLoading(false) + } + return () => { + resetRef.current = null + } + }, [doSetError, doSetLoading]) + + useImperativeHandle( + ref, + (): AsyncActionController => ({ + reset: () => resetRef.current?.(), + }), + [], + ) + + // Cancel pending action when unmounted + useEffect(() => { + return () => { + controllerRef.current?.abort() + controllerRef.current = null + } + }, []) + + const run = useCallback(async (): Promise => { + // Cancel previous run + controllerRef.current?.abort() + + doSetLoading(true) + doSetError(undefined) + + const controller = new AbortController() + const { signal } = controller + + controllerRef.current = controller + + try { + await fn(signal) + } catch (err) { + if (controller === controllerRef.current) { + doSetError(err instanceof Error ? err : new Error(String(err))) + } else { + if (!isAbortReason(signal, err)) { + console.warn('Async action error after abort', err) + } + } + } finally { + if (controller === controllerRef.current) { + controllerRef.current = null + doSetLoading(false) + } + + controller.abort() + } + }, [fn, doSetLoading, doSetError]) + + return { + loading, + error, + run, + } +} + +function isAbortReason(signal: AbortSignal, err: unknown): boolean { + return ( + signal.aborted && + (signal.reason === err || + signal.reason === err?.['cause'] || + (err instanceof DOMException && err.name === 'AbortError')) + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts new file mode 100644 index 00000000000..4784636145e --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-browser-color-scheme.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' + +const query = + typeof window === 'undefined' + ? null + : window.matchMedia('(prefers-color-scheme: dark)') + +export function useBrowserColorScheme() { + const [theme, setTheme] = useState<'light' | 'dark'>( + query?.matches ? 'dark' : 'light', + ) + + useEffect(() => { + if (!query) return + + const listener = () => { + setTheme(query.matches ? 'dark' : 'light') + } + + query.addEventListener('change', listener) + + return () => { + query.removeEventListener('change', listener) + } + + // @NOTE "query" is a global constant and does not need to be part of the + // array bellow: + }, []) + + return theme +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts index ee272abcb0c..36f5cd78556 100644 --- a/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-csrf-token.ts @@ -1,4 +1,4 @@ -import { cookies } from '../cookies' +import { cookies } from '../cookies.ts' export function useCsrfToken(cookieName: string) { return cookies[cookieName] diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts new file mode 100644 index 00000000000..6a69bdce774 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-random-string.ts @@ -0,0 +1,37 @@ +import { useEffect, useState } from 'react' + +export const UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +export const LOWER = UPPER.toLowerCase() as Lowercase +export const DIGITS = '0123456789' + +export const ALPHANUMERIC = `${UPPER}${LOWER}${DIGITS}` as const + +export type UseRandomStringOptions = BuildRandomStringOptions & { + prefix?: string + suffix?: string +} + +export function useRandomString(options?: UseRandomStringOptions) { + const [state, setState] = useState(() => buildRandomString(options)) + useEffect(() => { + setState(buildRandomString(options)) + }, [options?.length, options?.alphabet]) + + return `${options?.prefix ?? ''}${state}${options?.suffix ?? ''}` +} + +type BuildRandomStringOptions = { + length?: number + alphabet?: string +} + +function buildRandomString({ + length = 16, + alphabet = ALPHANUMERIC, +}: BuildRandomStringOptions = {}) { + return Array.from({ length }, () => getRandomCharFrom(alphabet)).join('') +} + +function getRandomCharFrom(alphabet: string) { + return alphabet.charAt((Math.random() * alphabet.length) | 0) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts b/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts new file mode 100644 index 00000000000..a4d6167a206 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/hooks/use-stepper.ts @@ -0,0 +1,87 @@ +import { useCallback, useEffect, useState } from 'react' + +export type DisabledStep = false | null | undefined +export type Step = { + invalid: boolean +} + +const isEnabled = ( + s: S, +): s is S extends DisabledStep ? never : S => s != null && s !== false +const isRequired = ( + s: S, +): s is S extends DisabledStep ? never : S & { invalid: true } => + isEnabled(s) && s.invalid === true +const isCompleted = ( + s: S, +): s is S extends DisabledStep ? S : S & { invalid: false } => + !isEnabled(s) || s.invalid === false + +export function useStepper( + steps: readonly (S | DisabledStep)[], +) { + const firstIdx = steps.findIndex(isEnabled) + const lastIdx = steps.findLastIndex(isEnabled) + const requiredIdx = steps.findIndex(isRequired) + + const [currentIdx, setCurrentIdx] = useState(firstIdx) + + const to = useCallback( + (idx: number) => { + if (idx !== -1 && steps[idx]) { + setCurrentIdx(idx) + return true + } else { + return false + } + }, + [steps.map(isEnabled).join()], + ) + + const prevIdx = steps.findLastIndex((s, i) => isEnabled(s) && i < currentIdx) + const nextIdx = steps.findIndex((s, i) => isEnabled(s) && i > currentIdx) + + const toFirst = useCallback(() => to(firstIdx), [to, firstIdx]) + const toLast = useCallback(() => to(lastIdx), [to, lastIdx]) + const toPrev = useCallback(() => to(prevIdx), [to, prevIdx]) + const toNext = useCallback(() => to(nextIdx), [to, nextIdx]) + const toRequired = useCallback(() => to(requiredIdx), [to, requiredIdx]) + + // Step number in user friendly terms (accounting for disabled steps) + const currentPosition = + currentIdx + + // use "1 indexed position" (for user friendliness): + 1 + + // Adjust the position by counting the number of disabled steps before the + // current step (if any): + steps.reduce( + (acc, s, i) => (i >= currentIdx || isEnabled(s) ? acc : acc - 1), + 0, + ) + + const count = steps.filter(isEnabled).length + const completed = steps.every(isCompleted) + + const current = + currentIdx === -1 || !steps[currentIdx] ? undefined : steps[currentIdx] + + // Fool-proof (reset current step in case the current step becomes disabled) + const broken = currentIdx === -1 + useEffect(() => { + if (broken) toFirst() + }, [broken]) + + return { + current, + currentPosition, + count, + completed, + atFirst: currentPosition === 1, + atLast: currentPosition === count, + toFirst, + toLast, + toPrev, + toNext, + toRequired, + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/index.html b/packages/oauth/oauth-provider/src/assets/app/index.html new file mode 100644 index 00000000000..815d51605d2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/index.html @@ -0,0 +1,182 @@ + + + + + + + OAuth Provider + + +
+ + + + + + diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/api.ts b/packages/oauth/oauth-provider/src/assets/app/lib/api.ts index 1237e9a22aa..34d0496e8dd 100644 --- a/packages/oauth/oauth-provider/src/assets/app/lib/api.ts +++ b/packages/oauth/oauth-provider/src/assets/app/lib/api.ts @@ -1,98 +1,267 @@ -import { FetchResponseError, Json } from '@atproto-labs/fetch' -import { Account, Session } from '../backend-data' +import { Account } from '../backend-types.ts' +import { + JsonClient, + JsonErrorPayload, + JsonErrorResponse, +} from './json-client.ts' -export class Api { - constructor( - private requestUri: string, - private clientId: string, - private csrfToken: string, - private newSessionsRequireConsent: boolean, - ) {} - - async signIn(credentials: { - username: string - password: string - remember?: boolean - }): Promise { - const response = await fetch('/oauth/authorize/sign-in', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - mode: 'same-origin', - body: JSON.stringify({ - csrf_token: this.csrfToken, - request_uri: this.requestUri, - client_id: this.clientId, - credentials, - }), - }) - - const json: Json = await response.json() - - if (response.ok) { - const data = json as { - account: Account - consentRequired: boolean - } - - return { - account: data.account, - - selected: true, - loginRequired: false, - consentRequired: this.newSessionsRequireConsent || data.consentRequired, - } - } else if ( - response.status === 400 && - json?.['error'] === 'invalid_request' && - json?.['error_description'] === 'Invalid credentials' - ) { - throw new InvalidCredentialsError() - } else if ( - response.status === 401 && - json?.['error'] === 'second_authentication_factor_required' - ) { - const data = json as { - type: 'emailOtp' - hint: string - } - - throw new SecondAuthenticationFactorRequiredError(data.type, data.hint) - } else { - throw new FetchResponseError(response) - } +export type { Options } from './json-client.ts' + +export type AcceptData = { + sub: string +} + +export type SignInData = { + locale: string + username: string + password: string + emailOtp?: string + remember?: boolean +} + +export type SignUpData = { + locale: string + handle: string + email: string + password: string + inviteCode?: string + hcaptchaToken?: string +} + +export type InitiatePasswordResetData = { + locale: string + email: string +} + +export type ConfirmResetPasswordData = { + token: string + password: string +} + +export type VerifyHandleAvailabilityData = { + handle: string +} + +export type SessionResponse = { + account: Account + consentRequired: boolean +} + +export class Api extends JsonClient<{ + '/verify-handle-availability': { + input: VerifyHandleAvailabilityData + output: void + } + '/sign-up': { + input: SignUpData + output: SessionResponse + } + '/sign-in': { + input: SignInData + output: SessionResponse + } + '/reset-password-request': { + input: InitiatePasswordResetData + output: void + } + '/reset-password-confirm': { + input: ConfirmResetPasswordData + output: void + } +}> { + constructor(csrfToken: string) { + const baseUrl = new URL('/oauth/authorize', window.origin).toString() + super(baseUrl, csrfToken) } - async accept(account: Account): Promise { - const url = new URL('/oauth/authorize/accept', window.origin) - url.searchParams.set('request_uri', this.requestUri) - url.searchParams.set('account_sub', account.sub) - url.searchParams.set('client_id', this.clientId) + public buildAcceptUrl(data: AcceptData): URL { + const url = new URL(`${this.baseUrl}/accept`) + url.searchParams.set('account_sub', data.sub) url.searchParams.set('csrf_token', this.csrfToken) - return url } - async reject(): Promise { - const url = new URL('/oauth/authorize/reject', window.origin) - url.searchParams.set('request_uri', this.requestUri) - url.searchParams.set('client_id', this.clientId) + public buildRejectUrl(): URL { + const url = new URL(`${this.baseUrl}/reject`) url.searchParams.set('csrf_token', this.csrfToken) - return url } + + public static override parseError( + json: unknown, + ): undefined | JsonErrorResponse { + // @NOTE Most specific errors first ! + if (SecondAuthenticationFactorRequiredError.is(json)) { + return new SecondAuthenticationFactorRequiredError(json) + } + if (InvalidCredentialsError.is(json)) { + return new InvalidCredentialsError(json) + } + if (HandleUnavailableError.is(json)) { + return new HandleUnavailableError(json) + } + if (EmailTakenError.is(json)) { + return new EmailTakenError(json) + } + if (RequestExpiredError.is(json)) { + return new RequestExpiredError(json) + } + if (UnknownRequestUriError.is(json)) { + return new UnknownRequestUriError(json) + } + if (InvalidRequestError.is(json)) { + return new InvalidRequestError(json) + } + if (AccessDeniedError.is(json)) { + return new AccessDeniedError(json) + } + return super.parseError(json) + } +} + +export type AccessDeniedPayload = JsonErrorPayload<'access_denied'> +export class AccessDeniedError< + P extends AccessDeniedPayload = AccessDeniedPayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || 'Access denied', + ) { + super(payload, message) + } + + static is(json: unknown): json is AccessDeniedPayload { + return super.is(json) && json.error === 'access_denied' + } +} + +export type InvalidRequestPayload = JsonErrorPayload<'invalid_request'> +export class InvalidRequestError< + P extends InvalidRequestPayload = InvalidRequestPayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || 'Invalid request', + ) { + super(payload, message) + } + + static is(json: unknown): json is InvalidRequestPayload { + return super.is(json) && json.error === 'invalid_request' + } +} + +export type RequestExpiredPayload = AccessDeniedPayload & { + error_description: 'This request has expired' +} +export class RequestExpiredError< + P extends RequestExpiredPayload = RequestExpiredPayload, +> extends AccessDeniedError

{ + static is(json: unknown): json is RequestExpiredPayload { + return ( + super.is(json) && json.error_description === 'This request has expired' + ) + } +} + +export type InvalidCredentialsPayload = InvalidRequestPayload & { + error_description: 'Invalid identifier or password' +} +export class InvalidCredentialsError< + P extends InvalidCredentialsPayload = InvalidCredentialsPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is InvalidCredentialsPayload { + return ( + super.is(json) && + json.error_description === 'Invalid identifier or password' + ) + } +} + +export type UnknownRequestPayload = InvalidRequestPayload & { + error_description: 'Unknown request_uri' +} +export class UnknownRequestUriError< + P extends UnknownRequestPayload = UnknownRequestPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is UnknownRequestPayload { + return super.is(json) && json.error_description === 'Unknown request_uri' + } +} +export type EmailTakenPayload = InvalidRequestPayload & { + error_description: 'Email already taken' +} +export class EmailTakenError< + P extends EmailTakenPayload = EmailTakenPayload, +> extends InvalidRequestError

{ + static is(json: unknown): json is EmailTakenPayload { + return super.is(json) && json.error_description === 'Email already taken' + } } -export class InvalidCredentialsError extends Error { - constructor() { - super('Invalid credentials') +export type HandleUnavailablePayload = + JsonErrorPayload<'handle_unavailable'> & { + reason: 'syntax' | 'domain' | 'slur' | 'taken' + } +export class HandleUnavailableError< + P extends HandleUnavailablePayload = HandleUnavailablePayload, +> extends JsonErrorResponse

{ + constructor( + payload: P, + message = payload.error_description || 'That handle cannot be used', + ) { + super(payload, message) + } + + get reason() { + return this.payload.reason + } + + static is(json: unknown): json is HandleUnavailablePayload { + return ( + super.is(json) && + json.error === 'handle_unavailable' && + 'reason' in json && + (json.reason === 'syntax' || + json.reason === 'domain' || + json.reason === 'slur' || + json.reason === 'taken') + ) } } -export class SecondAuthenticationFactorRequiredError extends Error { +export type SecondAuthenticationFactorRequiredPayload = + JsonErrorPayload<'second_authentication_factor_required'> & { + type: 'emailOtp' + hint: string + } +export class SecondAuthenticationFactorRequiredError< + P extends + SecondAuthenticationFactorRequiredPayload = SecondAuthenticationFactorRequiredPayload, +> extends JsonErrorResponse

{ constructor( - public type: 'emailOtp', - public hint: string, + payload: P, + message = payload.error_description || + `${payload.type} authentication factor required (hint: ${payload.hint})`, ) { - super(`${type} authentication factor required (hint: ${hint})`) + super(payload, message) + } + + get type() { + return this.payload.type + } + get hint() { + return this.payload.hint + } + + static is(json: unknown): json is SecondAuthenticationFactorRequiredPayload { + return ( + super.is(json) && + json.error === 'second_authentication_factor_required' && + 'type' in json && + json.type === 'emailOtp' && + 'hint' in json && + typeof json.hint === 'string' + ) } } diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts b/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts index 08fbfbfb2fc..91d453ca281 100644 --- a/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts +++ b/packages/oauth/oauth-provider/src/assets/app/lib/clsx.ts @@ -1,9 +1,6 @@ -export function clsx( - a?: string, - ...args: readonly (string | undefined)[] -): string | undefined { - if (args.length === 0) return a - const b = clsx(...args) - if (a && b) return `${a} ${b}` - return a || b +type ClsxArg = string | false | undefined + +export function clsx(...args: [ClsxArg, ...ClsxArg[]]): string | undefined { + const filtered = args.filter(Boolean) as string[] + return filtered.length > 0 ? filtered.join(' ') : undefined } diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts b/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts new file mode 100644 index 00000000000..554949b446d --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/json-client.ts @@ -0,0 +1,94 @@ +// Using a type import to avoid bundling this lib +import type { Json } from '@atproto-labs/fetch' + +export { type Json } + +export type Options = { + signal?: AbortSignal +} + +export type EndpointDefinition = { + input: Json + output: Json | void +} + +export class JsonClient { + constructor( + protected readonly baseUrl: string, + protected readonly csrfToken: string, + ) {} + + public async fetch

( + path: P, + payload: E[P]['input'], + options?: Options, + ): Promise { + const response = await fetch(`${this.baseUrl}${path}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': this.csrfToken, + }, + mode: 'same-origin', + body: JSON.stringify(payload), + signal: options?.signal, + }) + + if (response.status === 204) { + return undefined + } + + return response.json().then((json: Json) => { + if (response.ok) return json as E[P]['output'] + else throw this.parseError(response, json) + }) + } + + protected parseError(response: Response, json: Json): Error { + const Class = this.constructor as typeof JsonClient + const error = Class.parseError(json) + if (error) return error + + return new Error('Invalid JSON response', { cause: response }) + } + + public static parseError(json: unknown): undefined | JsonErrorResponse { + if (JsonErrorResponse.is(json)) { + return new JsonErrorResponse(json) + } + } +} + +export type JsonErrorPayload = { + error: E + error_description?: string +} + +export class JsonErrorResponse< + P extends JsonErrorPayload = JsonErrorPayload, +> extends Error { + constructor( + public readonly payload: P, + message = payload.error_description, + ) { + super(message || `Error "${payload.error}"`) + } + + get error(): string { + return this.payload.error + } + + get description(): string | undefined { + return this.payload.error_description + } + + static is(json: unknown): json is JsonErrorPayload { + return ( + json != null && + typeof json === 'object' && + typeof json['error'] === 'string' && + (json['error_description'] === undefined || + typeof json['error_description'] === 'string') + ) + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/password.ts b/packages/oauth/oauth-provider/src/assets/app/lib/password.ts new file mode 100644 index 00000000000..29c30e0e330 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/password.ts @@ -0,0 +1,98 @@ +export const MIN_PASSWORD_LENGTH = 8 + +const EMOJI = + /(\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/ +const UPPER = /[A-Z]/ +const LOWER = /[a-z]/ +const DEC = /[0-9]/ +const SPECIAL = /[^a-zA-Z0-9]/ + +export enum PasswordStrength { + weak = 1, + moderate = 2, + strong = 3, + extra = 4, +} + +export function getPasswordStrength(pwd: string): PasswordStrength { + if (pwd.length < MIN_PASSWORD_LENGTH) { + return PasswordStrength.weak + } + + // Very long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 12) { + return PasswordStrength.extra + } + + // Long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 8) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.extra + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.extra + } + return PasswordStrength.strong + } + + // Emojis make passwords strong + if (pwd.length >= MIN_PASSWORD_LENGTH) { + if (matches(pwd, [EMOJI])) { + return PasswordStrength.strong + } + } + + // Pretty long passwords + if (pwd.length >= MIN_PASSWORD_LENGTH + 6) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.strong + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.strong + } + // Only 1 type of alpha-num characters + return PasswordStrength.moderate + } + + // Longish password + if (pwd.length >= MIN_PASSWORD_LENGTH + 4) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.moderate + } + if (matches(pwd, [UPPER, LOWER, DEC], 2)) { + return PasswordStrength.moderate + } + + // Only 1 type of alpha-num characters + return PasswordStrength.weak + } + + // Short password (8-11 characters) + if (pwd.length >= MIN_PASSWORD_LENGTH) { + if (matches(pwd, [SPECIAL])) { + return PasswordStrength.moderate + } + if (matches(pwd, [UPPER, LOWER, DEC])) { + return PasswordStrength.moderate + } + } + + return PasswordStrength.weak +} + +function matches( + pwd: string, + regexps: RegExp[], + regexpsCountToMatch: number = regexps.length, +): boolean { + if (regexpsCountToMatch < 1 || regexpsCountToMatch > regexps.length) { + throw new TypeError('Invalid regexpsCountToMatch') + } + for (const regexp of regexps) { + if (regexp.test(pwd)) { + regexpsCountToMatch-- + if (regexpsCountToMatch === 0) return true + } + } + return false +} diff --git a/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts b/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts new file mode 100644 index 00000000000..f9c966855fa --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/lib/ref.ts @@ -0,0 +1,17 @@ +import { ForwardedRef } from 'react' + +export function updateRef(ref: ForwardedRef, value: T | null) { + if (typeof ref === 'function') { + ref(value) + } else if (ref) { + ref.current = value + } +} + +export function mergeRefs(refs: readonly (ForwardedRef | undefined)[]) { + return (value: T | null) => { + for (const ref of refs) { + if (ref) updateRef(ref, value) + } + } +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po new file mode 100644 index 00000000000..12d1c9bb2c5 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/an/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: an\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po new file mode 100644 index 00000000000..050b5bc94b2 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ast/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ast\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po new file mode 100644 index 00000000000..243c79422ca --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ca/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ca\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po new file mode 100644 index 00000000000..bf02908f2d0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/da/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: da\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po new file mode 100644 index 00000000000..f8644abc641 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/de/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: de\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po new file mode 100644 index 00000000000..6d7e44425b4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/el/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: el\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po new file mode 100644 index 00000000000..08c02c01a1d --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/en-GB/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en-GB\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po new file mode 100644 index 00000000000..79c9a01ef66 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/en/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:03+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "<0/> is asking for permission to access your account (<1/>)." + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "2FA Confirmation" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "A second authentication factor is required" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "Access your account data (except chat messages)" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "Access your chat messages" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "Account" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "Already have a code?" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "An application on your device" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "An unknown error occurred" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "Another account" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "Authenticate" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "Authorize" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "Back" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "Between {minLength} and {maxLength} characters" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "By creating an account you agree to the {0} and the {1} of this service." + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "Cancel" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "Check your {0} email for a login code and enter it here." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "Choose a username" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "Code" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "Confirm" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "Confirm your password to continue" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "Confirmation code" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "Create a new account" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "Create Account" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "Deny access" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "Description" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "Email" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "Email address" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "Enter a password" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "Enter the code you received to reset your password." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "Enter your email address" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "Enter your new password" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "Enter your password" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "Enter your username and password" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "Error" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "Extra" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "Forgot Password" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "Forgot?" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "Grant access to your <0>{0} account" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "Having trouble? <0>Contact support" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "Hide" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "Home" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "Identifier" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "Interface language selector" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "Invalid" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "Invite code" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "Let's get your password reset!" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "Login complete" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "Login to account that is not listed" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "Looks like {example}" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "Make visible" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "Missing" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "Moderate" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "Name" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "New password" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "Next" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "Okay" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "Only letters, numbers, and hyphens" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "Password" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "Password strength" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "Password strength indicator" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "Password Updated" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "Password updated!" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "Password with at least {MIN_PASSWORD_LENGTH} characters" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "Privacy Policy" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "Remember this account on this device" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "Requested permissions" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "Reset code" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "Reset Password" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "Reset your password" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "Select domain" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "Select from an existing account" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "Session" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "Sign in" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "Sign in as {0}" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "Sign in as..." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "Sign up" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "Step {currentPosition} of {count}" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "Strong" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "Submit" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "Support" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "Terms of Service" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "That handle cannot be used" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "The domain name is not allowed" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "The handle contains inappropriate language" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "The handle is already in use" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "The handle is invalid" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "This email is already used" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "This handle is reserved" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "This sign-in session has expired" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "Type your desired username" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "Unexpected server response" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "Uniquely identify you" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "Username or email address" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "Valid" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "Valid email address or username" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "Verify you are human" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "Warning" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "We're so excited to have you join us!" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "Weak" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "Wrong identifier or password" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "You are being redirected..." + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "You can change this username to any domain name you control after your account is set up." + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "You can now sign in with your new password." + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "You will receive an email with a \"reset code\". Enter that code here then enter your new password." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "Your account" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "Your full username will be: {0}" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "Your password has been updated!" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po new file mode 100644 index 00000000000..5540cecb032 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/es/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: es\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po new file mode 100644 index 00000000000..d92d1771745 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/eu/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: eu\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po new file mode 100644 index 00000000000..fac181cba29 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/fi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: fi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po new file mode 100644 index 00000000000..8a8434475a4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/fr/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:03+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: fr\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "<0/> demande la permission d'accéder à votre compte (<1/>)." + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "Confirmation 2FA" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "Un second facteur d'authentification est requis" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "Accéder aux données de votre compte (sauf les messages de chat)" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "Accéder à vos messages de chat" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "Compte d'utilisateur" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "Vous avez déjà un code ?" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "Une application sur votre appareil" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "Une erreur inconnue s'est produite" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "Un autre compte" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "Authentification" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "Authoriser l'accès" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "Retour" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "Entre {minLength} et {maxLength} caractères" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "En cliquant sur <0>Authoriser l'accès, vous autorisez cette application à effectuer les actions suivantes conformément à leurs <1>conditions d'utilisation et leur <2>politique de confidentialité :" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "En créant un compte, vous acceptez les {0} et la {1} de ce service." + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "Annuler" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "Vérifiez vos emails {0} pour un code de connexion et saisissez-le ici." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "Choisissez un nom d'utilisateur" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "Code" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "Confirmer" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "Confirmez votre mot de passe pour continuer" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "Code de confirmation" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "Créer un nouveau compte" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "Créer un compte" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "Refuser l'accès" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "Description" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "Email" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "Adresse email" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "Saisissez un mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "Saisissez le code que vous avez reçu pour réinitialiser votre mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "Saisissez l'email que vous avez utilisé pour créer votre compte. Nous vous enverrons un \"code de réinitialisation\" pour que vous puissiez définir un nouveau mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "Saisissez votre adresse email" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "Saisissez votre nouveau mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "Saisissez votre mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "Saisissez votre nom d'utilisateur et votre mot de passe" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "Erreur" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "Mot de passe oublié" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "Oublié ?" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "Accorder l'accès à votre compte <0>{0}" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "Vous rencontrez des difficultés ? <0>Contactez le support" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "Cacher" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "Accueil" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "Identifiant" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "Sélecteur de langue" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "Invalide" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "Code d'invitation" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "Réinitialisons votre mot de passe !" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "Connexion terminée" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "Se connecter à un compte qui n'est pas listé" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "Ressemble à {example}" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "Rendre visible" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "Manquant" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "Modéré" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "Nom" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "Nouveau mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "Suivant" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "OK" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "Uniquement des lettres, des chiffres et des traits d'union" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "Mot de passe" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "Complexité du mot de passe" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "Indicateur de complexité du mot de passe" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "Mot de passe mis à jour" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "Mot de passe mis à jour !" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "Mot de passe d'au moins {MIN_PASSWORD_LENGTH} caractères" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "Veuillez vérifier le nom de domaine du site web avant de saisir votre mot de passe. N'entrez jamais votre mot de passe sur un domaine auquel vous ne faites pas confiance." + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "Politique de Confidentialité" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "Se souvenir de ce compte sur cet appareil" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "Permissions demandées" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "Code de réinitialisation" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "Réinitialiser le mot de passe" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "Réinitialisez votre mot de passe" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "Sélectionner le domaine" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "Sélectionner un compte" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "Session" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "Se connecter" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "Se connecter en tant que {0}" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "Se connecter en tant que..." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "S'inscrire" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "Étape {currentPosition} sur {count}" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "Fort" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "Soumettre" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "Support" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "Conditions d'Utilisation" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "Ce pseudonyme ne peut pas être utilisé" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "Le nom de domaine n'est pas autorisé" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "Le pseudonyme contient un langage inapproprié" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "Ce pseudonyme est déjà utilisé" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "Le pseudonyme est invalide" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "Cet email est déjà utilisé" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "Ce pseudonyme est réservé" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "Cette session de connexion a expiré" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "Saisissez le nom d'utilisateur souhaité" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "Réponse inattendue du serveur" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "Vous identifier de façon unique" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "Nom d'utilisateur ou adresse email" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "Valide" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "Adresse email ou nom d'utilisateur valide" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "Vérifiez que vous êtes humain" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "Avertissement" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "Nous sommes ravis de vous accueillir parmi nous !" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "Faible" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "Identifiant ou mot de passe incorrect" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "Vous êtes redirigé..." + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "Vous pourrez changer ce nom d'utilisateur pour n'importe quel nom de domaine sous votre contrôle une fois votre compte créé." + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "Vous pouvez maintenant vous connecter avec votre nouveau mot de passe." + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "Vous aver reçu un email avec un \"code de réinitialisation\". Saisissez ce code ici puis entrez votre nouveau mot de passe." + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "Votre compte" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "Votre nom d'utilisateur complet sera : {0}" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "Votre mot de passe a été mis à jour !" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po new file mode 100644 index 00000000000..83e5f16ea7a --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ga/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ga\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po new file mode 100644 index 00000000000..08c019f0104 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/gl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: gl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po new file mode 100644 index 00000000000..ca5bdcfa283 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/hi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: hi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po new file mode 100644 index 00000000000..58d146435f4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/hu/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: hu\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po new file mode 100644 index 00000000000..e459921b012 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ia/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ia\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po new file mode 100644 index 00000000000..f2e96668f6b --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/id/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: id\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po new file mode 100644 index 00000000000..77db5cc0e64 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/it/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: it\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po new file mode 100644 index 00000000000..b5fcde7e3cd --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ja/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ja\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po new file mode 100644 index 00000000000..c82a1a6e967 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/km/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: km\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po new file mode 100644 index 00000000000..8c466da562c --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ko/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ko\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/load.ts b/packages/oauth/oauth-provider/src/assets/app/locales/load.ts new file mode 100644 index 00000000000..4026701d431 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/load.ts @@ -0,0 +1,8 @@ +import { Messages } from '@lingui/core' + +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files + +export async function loadMessages(locale: string): Promise { + const { messages } = await import(`./${locale}/messages.ts`) + return messages +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts b/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts new file mode 100644 index 00000000000..4b793a4d048 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-context.ts @@ -0,0 +1,19 @@ +import { createContext, useContext } from 'react' +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files +import { KnownLocale } from './locales.ts' + +export type LocaleContextValue = { + locale: string + locales: Partial> + setLocale: (locale: KnownLocale) => void +} + +export const LocaleContext = createContext(null) + +export function useLocaleContext(): LocaleContextValue { + const context = useContext(LocaleContext) + if (!context) { + throw new Error('useLocaleContext must be used within a LocaleProvider') + } + return context +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx b/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx new file mode 100644 index 00000000000..cd6a177ab59 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-provider.tsx @@ -0,0 +1,112 @@ +import { I18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import { ReactNode, useEffect, useMemo, useState } from 'react' +// @NOTE run "pnpm run po:compile" to compile the messages from the PO files +import { messages as en } from './en/messages.ts' +import { loadMessages } from './load.ts' +import { LocaleContext, LocaleContextValue } from './locale-context.ts' +import { KnownLocale, knownLocales, locales, resolveLocale } from './locales.ts' + +export type LocaleProviderProps = { + availableLocales?: readonly string[] + children?: ReactNode +} + +export function LocaleProvider({ + availableLocales, + children, +}: LocaleProviderProps) { + // Bundle "en" messages with the app + const [i18n] = useState(() => new I18n({ locale: 'en', messages: { en } })) + + const [desiredLocale, setDesiredLocale] = useState(() => + detectLocale( + knownLocales.filter( + (l) => !availableLocales || availableLocales.includes(l), + ), + ), + ) + const [currentLocale, setCurrentLocale] = useState(() => i18n.locale) + + const [loaded, setLoaded] = useState(desiredLocale === currentLocale) + + // Keep currentLocale in sync with i18n + useEffect(() => { + const onChange = () => { + setCurrentLocale(i18n.locale) + document.documentElement.setAttribute('lang', i18n.locale) + } + i18n.on('change', onChange) + return () => i18n.removeListener('change', onChange) + }, [i18n]) + + useEffect(() => { + if (currentLocale === desiredLocale) { + setLoaded(true) + return + } + + let canceled = false + loadMessages(desiredLocale) + .then((messages) => { + i18n.load(desiredLocale, messages) + if (!canceled) i18n.activate(desiredLocale) + }) + .catch((err) => { + console.error(`Failed to load locale "${desiredLocale}":`, err) + }) + .finally(() => { + if (!canceled) setLoaded(true) + }) + return () => { + canceled = true + } + }, [currentLocale, desiredLocale]) + + const value = useMemo( + () => ({ + locale: currentLocale, + locales: Object.fromEntries( + knownLocales + .filter((l) => !availableLocales || availableLocales.includes(l)) + .map((l) => [l, locales[l]]), + ), + setLocale: setDesiredLocale, + }), + [currentLocale, availableLocales, locales, setDesiredLocale], + ) + + return ( + + {loaded && children} + + ) +} + +function detectLocale( + availableLocales: readonly L[], + fallbackLocale: L | 'en' = 'en', +): L { + // Use, in priority, the locale that was set by the backend + if (typeof document === 'object') { + const htmlLang = document.documentElement.getAttribute('lang') + const resolved = htmlLang && resolveLocale(htmlLang, availableLocales) + if (resolved) return resolved + } + + // Should that fail (though it should probably never), negotiate with the browser + if (typeof navigator === 'object' && navigator.languages) { + for (const locale of navigator.languages) { + const resolved = resolveLocale(locale, availableLocales) + if (resolved) return resolved + } + } + + const fallback = resolveLocale(fallbackLocale, availableLocales) + if (fallback) return fallback + + // Type-safety + throw new TypeError( + `Available locales should always contain "${fallbackLocale}"`, + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx b/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx new file mode 100644 index 00000000000..9de7d18f073 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locale-selector.tsx @@ -0,0 +1,58 @@ +import { useLingui } from '@lingui/react/macro' +import { JSX } from 'react' +import { clsx } from '../lib/clsx.ts' +import { useLocaleContext } from './locale-context.ts' + +export type LocaleSelectorProps = Omit< + JSX.IntrinsicElements['select'], + 'value' | 'defaultValue' +> + +export function LocaleSelector({ + className, + onChange, + ...props +}: LocaleSelectorProps) { + const { locale, locales, setLocale } = useLocaleContext() + const { t } = useLingui() + + return ( + + ) +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts b/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts new file mode 100644 index 00000000000..9846ef474ac --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/locales.ts @@ -0,0 +1,168 @@ +// @TODO Enable locales once they get translated +export const locales = { + an: { + name: 'Aragonés', + }, + ast: { + name: 'Asturianu', + }, + ca: { + name: 'Català', + flag: '🇦🇩', // Andorra's flag (though Andorra does not cover the entire zone speaking Català) + }, + da: { + name: 'Dansk', + flag: '🇩🇰', + }, + de: { + name: 'Deutsch', + flag: '🇩🇪', + }, + el: { + name: 'Ελληνικά', + flag: '🇬🇷', + }, + en: { + name: 'English', + flag: '🇺🇸', + }, + 'en-GB': { + name: 'English (UK)', + flag: '🇬🇧', + }, + es: { + name: 'Español', + flag: '🇪🇸', + }, + eu: { + name: 'Euskara', + }, + fi: { + name: 'Suomi', + flag: '🇫🇮', + }, + fr: { + name: 'Français', + flag: '🇫🇷', + }, + ga: { + name: 'Gaeilge', + flag: '🇮🇪', + }, + gl: { + name: 'Galego', + }, + hi: { + name: 'हिन्दी', + flag: '🇮🇳', + }, + hu: { + name: 'Magyar', + flag: '🇭🇺', + }, + ia: { + name: 'Interlingua', + }, + id: { + name: 'Bahasa Indonesia', + flag: '🇮🇩', + }, + it: { + name: 'Italiano', + flag: '🇮🇹', + }, + ja: { + name: '日本語', + flag: '🇯🇵', + }, + km: { + name: 'ភាសាខ្មែរ', + flag: '🇰🇭', + }, + ko: { + name: '한국어', + flag: '🇰🇷', + }, + ne: { + name: 'नेपाली', + flag: '🇳🇵', + }, + nl: { + name: 'Nederlands', + flag: '🇳🇱', + }, + pl: { + name: 'Polski', + flag: '🇵🇱', + }, + 'pt-BR': { + name: 'Português (Brasil)', + flag: '🇧🇷', + }, + ro: { + name: 'Română', + flag: '🇷🇴', + }, + ru: { + name: 'Русский', + flag: '🇷🇺', + }, + sv: { + name: 'Svenska', + flag: '🇸🇪', + }, + th: { + name: 'ไทย', + flag: '🇹🇭', + }, + tr: { + name: 'Türkçe', + flag: '🇹🇷', + }, + uk: { + name: 'Українська', + flag: '🇺🇦', + }, + vi: { + name: 'Tiếng Việt', + flag: '🇻🇳', + }, + 'zh-CN': { + name: '中文(简体)', + flag: '🇨🇳', + }, + 'zh-HK': { + name: '中文(香港)', + flag: '🇭🇰', + }, + 'zh-TW': { + name: '中文(繁體)', + flag: '🇹🇼', + }, +} as const satisfies Record + +export const knownLocales = Object.keys(locales) as readonly KnownLocale[] +export type KnownLocale = keyof typeof locales +export const isKnownLocale = (v: unknown): v is KnownLocale => + (knownLocales as readonly unknown[]).includes(v) + +export function resolveLocale( + locale: string, + availableLocales: readonly L[], +): L | undefined { + if ((availableLocales as readonly string[]).includes(locale)) { + return locale as L + } + + const lang = locale.split('-')[0] + if ((availableLocales as readonly string[]).includes(lang)) { + return lang as L + } + + const similar = availableLocales.find((l) => l.startsWith(`${lang}-`)) + if (similar) { + return similar as L + } + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po new file mode 100644 index 00000000000..af34514ba4b --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ne/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ne\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po new file mode 100644 index 00000000000..b7c6f1bf952 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/nl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: nl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po new file mode 100644 index 00000000000..0869730af68 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/pl/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: pl\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po new file mode 100644 index 00000000000..935b74aa515 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/pt-BR/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: pt-BR\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po new file mode 100644 index 00000000000..eb50db8fcb0 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ro/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ro\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po new file mode 100644 index 00000000000..9969d085768 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/ru/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: ru\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po new file mode 100644 index 00000000000..8001da8b3ce --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/sv/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: sv\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po new file mode 100644 index 00000000000..7f8cb174b1f --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/th/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: th\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po new file mode 100644 index 00000000000..aa026bf29d9 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/tr/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: tr\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po new file mode 100644 index 00000000000..79d8544f49f --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/uk/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: uk\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po new file mode 100644 index 00000000000..6d8a76c7e2b --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/vi/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: vi\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po new file mode 100644 index 00000000000..094a9c64368 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-CN/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: zh-CN\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po new file mode 100644 index 00000000000..53c9c52a7b6 --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-HK/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: zh-HK\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po b/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po new file mode 100644 index 00000000000..aec7ce5d48f --- /dev/null +++ b/packages/oauth/oauth-provider/src/assets/app/locales/zh-TW/messages.po @@ -0,0 +1,492 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2025-02-27 14:42+0100\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: zh-TW\n" +"Project-Id-Version: \n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Plural-Forms: \n" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:76 +msgid "<0/> is asking for permission to access your account (<1/>)." +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:221 +msgid "2FA Confirmation" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:47 +msgid "A second authentication factor is required" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:144 +msgid "Access your account data (except chat messages)" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:146 +msgid "Access your chat messages" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:138 +msgid "Account" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:71 +msgid "Already have a code?" +msgstr "" + +#: src/assets/app/components/utils/client-name.tsx:35 +msgid "An application on your device" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:61 +msgid "An unknown error occurred" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:110 +msgid "Another account" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:28 +msgid "Authenticate" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-view.tsx:55 +#: src/assets/app/views/authorize/accept/accept-form.tsx:56 +msgid "Authorize" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:47 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:130 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:65 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:95 +#: src/assets/app/components/forms/wizard-card.tsx:93 +msgid "Back" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:152 +msgid "Between {minLength} and {maxLength} characters" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:89 +msgid "By clicking <0>Authorize, you allow this application to perform the following actions in accordance with their <1>terms of service and <2>privacy policy:" +msgstr "" + +#. placeholder {0}: tosLink ? ( Terms of Service ) : ( Terms of Service ) +#. placeholder {1}: ppLink ? ( Privacy Policy ) : ( Privacy Policy ) +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:30 +msgid "By creating an account you agree to the {0} and the {1} of this service." +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:51 +#: src/assets/app/components/forms/form-card-async.tsx:85 +msgid "Cancel" +msgstr "" + +#. placeholder {0}: secondFactor.hint +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:234 +msgid "Check your {0} email for a login code and enter it here." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:94 +msgid "Choose a username" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:76 +msgid "Code" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Confirm" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:65 +msgid "Confirm your password to continue" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:225 +msgid "Confirmation code" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:35 +msgid "Create a new account" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:78 +msgid "Create Account" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:60 +msgid "Deny access" +msgstr "" + +#: src/assets/app/components/utils/error-card.tsx:83 +msgid "Description" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:112 +#: src/assets/app/components/forms/input-email-address.tsx:49 +#: src/assets/app/components/forms/input-email-address.tsx:50 +#: src/assets/app/components/forms/input-email-address.tsx:51 +msgid "Email" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:55 +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:60 +msgid "Email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:48 +msgid "Enter a password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:85 +msgid "Enter the code you received to reset your password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:71 +msgid "Enter the email you used to create your account. We'll send you a \"reset code\" so you can set a new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-request-form.tsx:58 +msgid "Enter your email address" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:49 +msgid "Enter your new password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:86 +msgid "Enter your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:104 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:120 +msgid "Enter your username and password" +msgstr "" + +#: src/assets/app/views/error/error-view.tsx:27 +msgid "Error" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:25 +msgid "Extra" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:53 +msgid "Forgot Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:180 +msgid "Forgot?" +msgstr "" + +#. placeholder {0}: account.preferred_username || account.email || account.sub +#: src/assets/app/views/authorize/accept/accept-view.tsx:40 +msgid "Grant access to your <0>{0} account" +msgstr "" + +#: src/assets/app/components/utils/help-card.tsx:32 +msgid "Having trouble? <0>Contact support" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Hide" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:15 +msgid "Home" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:86 +msgid "Identifier" +msgstr "" + +#: src/assets/app/locales/locale-selector.tsx:48 +msgid "Interface language selector" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:277 +msgid "Invalid" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:95 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:99 +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:100 +msgid "Invite code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:55 +msgid "Let's get your password reset!" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:172 +msgid "Login complete" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:104 +msgid "Login to account that is not listed" +msgstr "" + +#: src/assets/app/components/forms/input-token.tsx:59 +msgid "Looks like {example}" +msgstr "" + +#: src/assets/app/components/forms/button-toggle-visibility.tsx:34 +msgid "Make visible" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:33 +msgid "Missing" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:29 +msgid "Moderate" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:80 +msgid "Name" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:76 +msgid "New password" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:60 +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:90 +#: src/assets/app/components/forms/wizard-card.tsx:96 +msgid "Next" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:119 +msgid "Okay" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:157 +msgid "Only letters, numbers, and hyphens" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-account-form.tsx:126 +#: src/assets/app/components/forms/input-password.tsx:51 +#: src/assets/app/components/forms/input-password.tsx:52 +#: src/assets/app/components/forms/input-password.tsx:53 +msgid "Password" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:23 +msgid "Password strength" +msgstr "" + +#: src/assets/app/components/utils/password-strength-meter.tsx:45 +msgid "Password strength indicator" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:106 +msgid "Password Updated" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:113 +msgid "Password updated!" +msgstr "" + +#: src/assets/app/components/forms/input-new-password.tsx:50 +msgid "Password with at least {MIN_PASSWORD_LENGTH} characters" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:194 +msgid "Please verify the domain name of the website before entering your password. Never enter your password on a domain you do not trust." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:42 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:45 +#: src/assets/app/components/utils/link-title.tsx:17 +msgid "Privacy Policy" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:208 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:213 +msgid "Remember this account on this device" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:123 +msgid "Requested permissions" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:61 +msgid "Reset code" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:82 +msgid "Reset Password" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:178 +msgid "Reset your password" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:198 +msgid "Select domain" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:135 +msgid "Select from an existing account" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:204 +msgid "Session" +msgstr "" + +#: src/assets/app/views/authorize/welcome/welcome-view.tsx:45 +#: src/assets/app/views/authorize/sign-in/sign-in-view.tsx:48 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:135 +msgid "Sign in" +msgstr "" + +#. placeholder {0}: account.name +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:75 +msgid "Sign in as {0}" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-picker.tsx:53 +msgid "Sign in as..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:84 +msgid "Sign up" +msgstr "" + +#: src/assets/app/components/forms/wizard-card.tsx:106 +msgid "Step {currentPosition} of {count}" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:27 +msgid "Strong" +msgstr "" + +#: src/assets/app/components/forms/form-card-async.tsx:96 +msgid "Submit" +msgstr "" + +#: src/assets/app/components/utils/link-title.tsx:21 +msgid "Support" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:34 +#: src/assets/app/views/authorize/sign-up/sign-up-disclaimer.tsx:37 +#: src/assets/app/components/utils/link-title.tsx:19 +msgid "Terms of Service" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:42 +msgid "That handle cannot be used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:33 +msgid "The domain name is not allowed" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:35 +msgid "The handle contains inappropriate language" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:40 +msgid "The handle is already in use" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:31 +msgid "The handle is invalid" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:25 +msgid "This email is already used" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:38 +msgid "This handle is reserved" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:54 +msgid "This sign-in session has expired" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:166 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:167 +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:168 +msgid "Type your desired username" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:58 +msgid "Unexpected server response" +msgstr "" + +#: src/assets/app/views/authorize/accept/accept-form.tsx:142 +msgid "Uniquely identify you" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:143 +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:144 +msgid "Username or email address" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:272 +msgid "Valid" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:154 +msgid "Valid email address or username" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:135 +msgid "Verify you are human" +msgstr "" + +#: src/assets/app/views/authorize/sign-in/sign-in-form.tsx:191 +msgid "Warning" +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:80 +msgid "We're so excited to have you join us!" +msgstr "" + +#: src/assets/app/components/utils/password-strength-label.tsx:31 +msgid "Weak" +msgstr "" + +#: src/assets/app/components/utils/error-message.tsx:21 +msgid "Wrong identifier or password" +msgstr "" + +#: src/assets/app/views/authorize/authorize-view.tsx:173 +msgid "You are being redirected..." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:237 +msgid "You can change this username to any domain name you control after your account is set up." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:116 +msgid "You can now sign in with your new password." +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-confirm-form.tsx:54 +msgid "You will receive an email with a \"reset code\". Enter that code here then enter your new password." +msgstr "" + +#: src/assets/app/views/authorize/sign-up/sign-up-view.tsx:116 +msgid "Your account" +msgstr "" + +#. placeholder {0}: segment.length ? ( {preview} ) : ( ) +#: src/assets/app/views/authorize/sign-up/sign-up-handle-form.tsx:219 +msgid "Your full username will be: {0}" +msgstr "" + +#: src/assets/app/views/authorize/reset-password/reset-password-view.tsx:108 +msgid "Your password has been updated!" +msgstr "" diff --git a/packages/oauth/oauth-provider/src/assets/app/main.css b/packages/oauth/oauth-provider/src/assets/app/main.css index c6fb6868cef..b84e6b2190e 100644 --- a/packages/oauth/oauth-provider/src/assets/app/main.css +++ b/packages/oauth/oauth-provider/src/assets/app/main.css @@ -2,11 +2,32 @@ @tailwind components; @tailwind utilities; -/* Matches colors defined in tailwind.config.js */ +/* + * These are the default branding variables for the oauth authorization page. + * The colors below are in the R G B format, with values ranging from 0 to 255. + * + * The variables defined here are typically overridden by the backend, based on + * the provided branding colors configuration, if any, by injecting a ` // hash validity requires no space around the content - : html`` + : html`` } diff --git a/packages/oauth/oauth-provider/src/lib/http/middleware.ts b/packages/oauth/oauth-provider/src/lib/http/middleware.ts index b052f0ef4eb..5152663632b 100644 --- a/packages/oauth/oauth-provider/src/lib/http/middleware.ts +++ b/packages/oauth/oauth-provider/src/lib/http/middleware.ts @@ -2,6 +2,8 @@ import type { IncomingMessage, ServerResponse } from 'node:http' import { writeJson } from './response.js' import { Handler, Middleware, NextFunction } from './types.js' +const isNonNullable = (x: X): x is NonNullable => x != null + export function combineMiddlewares>( middlewares: Iterable, options?: { skipKeyword?: string }, @@ -15,12 +17,11 @@ export function combineMiddlewares( middlewares: Iterable>, { skipKeyword }: { skipKeyword?: string } = {}, ): Middleware { - const middlewaresArray = Array.from(middlewares).filter( - (x): x is NonNullable => x != null, - ) + const middlewaresArray = Array.from(middlewares).filter(isNonNullable) // Optimization: if there are no middlewares, return a noop middleware. if (middlewaresArray.length === 0) return (req, res, next) => void next() + if (middlewaresArray.length === 1) return middlewaresArray[0] return function (req, res, next) { let i = 0 diff --git a/packages/oauth/oauth-provider/src/lib/http/request.ts b/packages/oauth/oauth-provider/src/lib/http/request.ts index 4479c2ef208..2f4b9f63029 100644 --- a/packages/oauth/oauth-provider/src/lib/http/request.ts +++ b/packages/oauth/oauth-provider/src/lib/http/request.ts @@ -1,5 +1,6 @@ import { randomBytes } from 'node:crypto' import type { IncomingMessage, ServerResponse } from 'node:http' +import { languages, mediaType } from '@hapi/accept' import { parse as parseCookie, serialize as serializeCookie } from 'cookie' import forwarded from 'forwarded' import createHttpError from 'http-errors' @@ -79,6 +80,18 @@ export function validateFetchSite( validateHeaderValue(req, 'sec-fetch-site', expectedSite) } +export function validateReferer( + req: IncomingMessage, + res: ServerResponse, + reference: UrlReference, + allowNull: true, +): URL | null +export function validateReferer( + req: IncomingMessage, + res: ServerResponse, + reference: UrlReference, + allowNull?: false, +): URL export function validateReferer( req: IncomingMessage, res: ServerResponse, @@ -90,6 +103,7 @@ export function validateReferer( if (refererUrl ? !urlMatch(refererUrl, reference) : !allowNull) { throw createHttpError(400, `Invalid referer ${referer}`) } + return refererUrl } export async function setupCsrfToken( @@ -126,12 +140,13 @@ export function validateSameOrigin( export function validateCsrfToken( req: IncomingMessage, res: ServerResponse, - csrfToken: string, + csrfToken: unknown, cookieName = 'csrf_token', clearCookie = false, ) { const cookies = parseHttpCookies(req) if ( + typeof csrfToken !== 'string' || !csrfToken || !cookies || !cookieName || @@ -248,3 +263,18 @@ function extractPort(req: IncomingMessage, ip: string): number { throw new Error('Could not determine port') } + +export function extractLocales(req: IncomingMessage) { + const acceptLanguage = req.headers['accept-language'] + return acceptLanguage ? languages(acceptLanguage) : [] +} + +export function negotiateResponseContent( + req: IncomingMessage, + types: readonly T[], +): T | undefined { + const type = mediaType(req.headers['accept'], types) + if (type) return type as T + + return undefined +} diff --git a/packages/oauth/oauth-provider/src/lib/http/response.ts b/packages/oauth/oauth-provider/src/lib/http/response.ts index 12ba2627a27..32ab8b7e462 100644 --- a/packages/oauth/oauth-provider/src/lib/http/response.ts +++ b/packages/oauth/oauth-provider/src/lib/http/response.ts @@ -1,6 +1,6 @@ import type { ServerResponse } from 'node:http' import { type Readable, pipeline } from 'node:stream' -import { Handler } from './types.js' +import type { Handler, Middleware } from './types.js' export function appendHeader( res: ServerResponse, @@ -53,22 +53,27 @@ export function writeStream( export function writeBuffer( res: ServerResponse, chunk: string | Buffer, - { - status = 200, - contentType = 'application/octet-stream', - }: WriteResponseOptions = {}, + opts: WriteResponseOptions, ): void { - res.statusCode = status - res.setHeader('content-type', contentType) + if (opts?.status != null) res.statusCode = opts.status + res.setHeader('content-type', opts?.contentType || 'application/octet-stream') res.end(chunk) } +export function toJsonBuffer(value: unknown): Buffer { + try { + return Buffer.from(JSON.stringify(value)) + } catch (cause) { + throw new Error(`Failed to serialize as JSON`, { cause }) + } +} + export function writeJson( res: ServerResponse, payload: unknown, { contentType = 'application/json', ...options }: WriteResponseOptions = {}, ): void { - const buffer = Buffer.from(JSON.stringify(payload)) + const buffer = toJsonBuffer(payload) writeBuffer(res, buffer, { ...options, contentType }) } @@ -76,7 +81,7 @@ export function staticJsonMiddleware( value: unknown, { contentType = 'application/json', ...options }: WriteResponseOptions = {}, ): Handler { - const buffer = Buffer.from(JSON.stringify(value)) + const buffer = toJsonBuffer(value) const staticOptions: WriteResponseOptions = { ...options, contentType } return function (req, res) { writeBuffer(res, buffer, staticOptions) @@ -90,3 +95,11 @@ export function writeHtml( ): void { writeBuffer(res, html, { ...options, contentType }) } + +export function cacheControlMiddleware(maxAge: number): Middleware { + const header = `max-age=${maxAge}` + return function (req, res, next) { + res.setHeader('Cache-Control', header) + next() + } +} diff --git a/packages/oauth/oauth-provider/src/lib/locale.ts b/packages/oauth/oauth-provider/src/lib/locale.ts new file mode 100644 index 00000000000..877a40542e4 --- /dev/null +++ b/packages/oauth/oauth-provider/src/lib/locale.ts @@ -0,0 +1,21 @@ +import { z } from 'zod' + +export const localeSchema = z + .string() + .regex(/^[a-z]{2,3}(-[A-Z]{2})?$/, 'Invalid locale') +export type Locale = z.infer + +export const multiLangStringSchema = z.intersection( + z.object({ en: z.string() }), // en is required + z.record(localeSchema, z.union([z.string(), z.undefined()])), +) +export type MultiLangString = z.infer + +export const AVAILABLE_LOCALES = [ + // TODO: Add more in this list as translations are added in the PO files + 'en', + 'fr', +] as const satisfies readonly Locale[] +export type AvailableLocale = (typeof AVAILABLE_LOCALES)[number] +export const isAvailableLocale = (v: unknown): v is AvailableLocale => + (AVAILABLE_LOCALES as readonly unknown[]).includes(v) diff --git a/packages/oauth/oauth-provider/src/lib/util/function.ts b/packages/oauth/oauth-provider/src/lib/util/function.ts index e755ebc4bf0..f74f9081924 100644 --- a/packages/oauth/oauth-provider/src/lib/util/function.ts +++ b/packages/oauth/oauth-provider/src/lib/util/function.ts @@ -6,17 +6,14 @@ * particularly useful when the function is a member of a "private" object. */ export async function callAsync unknown>( - this: ThisParameterType, fn: F, ...args: Parameters ): Promise>> export async function callAsync unknown>( - this: ThisParameterType, fn?: F, ...args: Parameters ): Promise> | undefined> export async function callAsync unknown>( - this: ThisParameterType, fn?: F, ...args: Parameters ): Promise> | undefined> { diff --git a/packages/oauth/oauth-provider/src/lib/util/type.ts b/packages/oauth/oauth-provider/src/lib/util/type.ts index 735e0b585ff..6a345159570 100644 --- a/packages/oauth/oauth-provider/src/lib/util/type.ts +++ b/packages/oauth/oauth-provider/src/lib/util/type.ts @@ -1,4 +1,133 @@ // eslint-disable-next-line @typescript-eslint/ban-types export type Simplify = { [K in keyof T]: T[K] } & {} -export type Override = Simplify> +export type Override = Simplify<{ + [K in keyof (V & T)]: K extends keyof V + ? V[K] + : K extends keyof T + ? T[K] + : never +}> export type Awaitable = T | Promise + +/** + * Similar to {@link Required} but also ensures that all values are defined. + */ +export type RequiredDefined = { [K in keyof T]-?: Exclude } + +// (don't touch this) + +/** + * @example + * ```ts + * type F = UnionToFnUnion<'a' | 'b'> // (() => 'a') | (() => 'b') + * ``` + */ +type UnionToFnUnion = T extends any ? () => T : never + +/** + * @example + * ```ts + * type A = UnionToIntersection<(() => 'a') | (() => 'b')> // (() => 'a') & (() => 'b') + * + * UnionToIntersection<{ foo: string | number } | { foo: number; bar: 4 }> // { foo: number; bar: 4 } + * ``` + */ +type UnionToIntersection = (T extends any ? (x: T) => void : never) extends ( + x: infer U, +) => void + ? U + : never + +/** + * @example + * ```ts + * type B = ExtractUnionItem<'a' | 'b'> // 'b' + * ``` + */ +type ExtractUnionItem = + // There exists a quirk in the way TypeScript works when inferring return + // types of an (disjoined) intersection of functions: + // + // type AnB = (() => 'a') & (() => 'b') + // type B = AnB extends () => infer R ? R : never // 'b' + // + // By turning the input union T (e.g. 'a' | 'b') into a union of function + // (() => 'a') | (() => 'b') and then into an intersection of those functions + // (() => 'a') & (() => 'b'), we can exploit the special TypeScript behavior + // to infer only the last return type from the functions, which is effectively + // equal to the last item of the input union T. + UnionToIntersection> extends () => infer R ? R : never + +/** + * Utility that turn a union of types (`'a' | 'b'`) into a tuple with matching + * types (`['a', 'b']`). + * + * @note this only work with unions of "const" types. Using this with globals + * types (`string`, etc.) will yield unexpected results. + * + * @example + * ```ts + * type T = UnionToTuple<'a' | 'b'> // ['a', 'b'] + * type T = UnionToTuple<'a' | 'b' | 'c'> // ['a', 'b', 'c'] + * ``` + */ +type UnionToTuple = UnionToTupleInternal + +type UnionToTupleInternal< + T, + // Accumulator for terminal recursivity (initialized to empty tuple) + Acc extends readonly any[] = [], + // Get the next item from the union (if any) + Next = ExtractUnionItem, +> = + // If there were no more items to extract from the union T, then we are done + [Next] extends [never] + ? // Return result of previous recursive calls + Acc + : // Recursively call UnionToTupleInternal by Exclude'ing the Next item from + // the union (T) and adding it to the accumulator. + UnionToTupleInternal, readonly [Next, ...Acc]> + +/** + * This utility allows to create an assertion function that checks if a + * particular interface is fully implemented by some value. + * + * The use of the (rather complex) {@link UnionToTuple} allows to ensure that, + * at runtime, all the required interface keys are indeed checked, and that + * future additions to the interface do not result in a false sense of type + * safety. + * + * @note This function should not be made public, as it relies on a quirk of + * TypeScript to work properly. + * + * @example Valid use + * + * ```ts + * const isFoo = buildInterfaceChecker<{ foo: string }>(['foo']) + * const isFooBar = buildInterfaceChecker<{ foo: string; bar: boolean }>([ + * 'foo', + * 'bar', + * ]) + * + * declare const val: { foo?: string } + * + * if (isFoo(val)) { + * val // { foo: string } + * } + * ``` + * + * @example Use cases where the runtime keys do not match the interface keys + * + * ```ts + * buildInterfaceChecker<{ foo: string }>([]) + * buildInterfaceChecker<{ foo: string }>(['fee']) + * buildInterfaceChecker<{ foo: string; bar: string }>(['foo']) + * buildInterfaceChecker<{ foo: string; bar: string }>(['foo', 'baz']) + * ``` + */ +export const buildInterfaceChecker = + (keys: readonly string[] & UnionToTuple) => + >(value: V): value is V & RequiredDefined => + keys.every((name) => value[name] !== undefined) + +// diff --git a/packages/oauth/oauth-provider/src/metadata/build-metadata.ts b/packages/oauth/oauth-provider/src/metadata/build-metadata.ts index 9a9172fd114..e67ea3fe8f7 100644 --- a/packages/oauth/oauth-provider/src/metadata/build-metadata.ts +++ b/packages/oauth/oauth-provider/src/metadata/build-metadata.ts @@ -1,6 +1,7 @@ import { Keyset } from '@atproto/jwk' import { OAuthAuthorizationServerMetadata, + OAuthIssuerIdentifier, oauthAuthorizationServerMetadataSchema, } from '@atproto/oauth-types' import { Client } from '../client/client.js' @@ -17,7 +18,7 @@ export type CustomMetadata = { * @see {@link https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata} */ export function buildMetadata( - issuer: string, + issuer: OAuthIssuerIdentifier, keyset: Keyset, customMetadata?: CustomMetadata, ): OAuthAuthorizationServerMetadata { diff --git a/packages/oauth/oauth-provider/src/oauth-errors.ts b/packages/oauth/oauth-provider/src/oauth-errors.ts index f53eabac076..97a303421a4 100644 --- a/packages/oauth/oauth-provider/src/oauth-errors.ts +++ b/packages/oauth/oauth-provider/src/oauth-errors.ts @@ -4,6 +4,7 @@ export { OAuthError } from './errors/oauth-error.js' export { AccessDeniedError } from './errors/access-denied-error.js' export { AccountSelectionRequiredError } from './errors/account-selection-required-error.js' export { ConsentRequiredError } from './errors/consent-required-error.js' +export { HandleUnavailableError } from './errors/handle-unavailable-error.js' export { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js' export { InvalidClientError } from './errors/invalid-client-error.js' export { InvalidClientIdError } from './errors/invalid-client-id-error.js' diff --git a/packages/oauth/oauth-provider/src/oauth-hooks.ts b/packages/oauth/oauth-provider/src/oauth-hooks.ts index b5cf3784fb0..56af40b75d0 100644 --- a/packages/oauth/oauth-provider/src/oauth-hooks.ts +++ b/packages/oauth/oauth-provider/src/oauth-hooks.ts @@ -6,15 +6,18 @@ import { OAuthTokenResponse, } from '@atproto/oauth-types' import { Account } from './account/account.js' +import { SignInData } from './account/sign-in-data.js' +import { SignUpData } from './account/sign-up-data.js' import { ClientAuth } from './client/client-auth.js' import { ClientId } from './client/client-id.js' import { ClientInfo } from './client/client-info.js' import { Client } from './client/client.js' -import { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js' +import { InvalidRequestError } from './errors/invalid-request-error.js' +import { HcaptchaConfig, HcaptchaVerifyResult } from './lib/hcaptcha.js' import { RequestMetadata } from './lib/http/request.js' import { Awaitable } from './lib/util/type.js' import { AccessDeniedError, OAuthError } from './oauth-errors.js' -import { DeviceId } from './oauth-store.js' +import { DeviceAccountInfo, DeviceId } from './oauth-store.js' // Make sure all types needed to implement the OAuthHooks are exported export { @@ -25,8 +28,11 @@ export { type ClientAuth, type ClientId, type ClientInfo, + type DeviceAccountInfo, type DeviceId, - InvalidAuthorizationDetailsError, + type HcaptchaConfig, + type HcaptchaVerifyResult, + InvalidRequestError, type Jwks, type OAuthAuthorizationDetails, type OAuthAuthorizationRequestParameters, @@ -34,6 +40,8 @@ export { OAuthError, type OAuthTokenResponse, type RequestMetadata, + type SignInData, + type SignUpData, } export type OAuthHooks = { @@ -63,6 +71,64 @@ export type OAuthHooks = { account: Account }) => Awaitable + /** + * This hook is called whenever an hcaptcha challenge is verified + * during sign-up (if hcaptcha is enabled). + * + * @throws {InvalidRequestError} to deny the sign-up + */ + onSignupHcaptchaResult?: (data: { + data: SignUpData + /** + * This indicates not only wether the hCaptcha challenge succeeded, but also + * if the score was low enough according to the + * {@link HcaptchaConfig.scoreThreshold}. + * + * @see {@link HCaptchaClient.isAllowed} + */ + allowed: boolean + result: HcaptchaVerifyResult + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + + /** + * This hook is called when a user attempts to sign up, after every validation + * has passed (including hcaptcha). + */ + onSignupAttempt?: (data: { + data: SignUpData + deviceId: DeviceId + deviceMetadata: RequestMetadata + hcaptchaResult?: HcaptchaVerifyResult + }) => Awaitable + + /** + * This hook is called when a user successfully signs up. + * + * @throws {AccessDeniedError} to deny the sign-up + */ + onSignedUp?: (data: { + data: SignUpData + info: DeviceAccountInfo + account: Account + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + + /** + * This hook is called when a user successfully signs in. + * + * @throws {InvalidRequestError} when the sing-in should be denied + */ + onSignedIn?: (data: { + data: SignInData + info: DeviceAccountInfo + account: Account + deviceId: DeviceId + deviceMetadata: RequestMetadata + }) => Awaitable + /** * This hook is called when a client is authorized. * diff --git a/packages/oauth/oauth-provider/src/oauth-provider.ts b/packages/oauth/oauth-provider/src/oauth-provider.ts index 2147b39983e..387b22b755f 100644 --- a/packages/oauth/oauth-provider/src/oauth-provider.ts +++ b/packages/oauth/oauth-provider/src/oauth-provider.ts @@ -1,5 +1,4 @@ import type { IncomingMessage, ServerResponse } from 'node:http' -import { mediaType } from '@hapi/accept' import createHttpError from 'http-errors' import type { Redis, RedisOptions } from 'ioredis' import { ZodError, z } from 'zod' @@ -39,14 +38,16 @@ import { AccountManager } from './account/account-manager.js' import { AccountStore, DeviceAccountInfo, - SignInCredentials, asAccountStore, - signInCredentialsSchema, + handleSchema, + resetPasswordConfirmDataSchema, + resetPasswordRequestDataSchema, } from './account/account-store.js' import { Account } from './account/account.js' +import { signInDataSchema } from './account/sign-in-data.js' +import { signUpDataSchema } from './account/sign-up-data.js' import { authorizeAssetsMiddleware } from './assets/assets-middleware.js' import { ClientAuth, authJwkThumbprint } from './client/client-auth.js' -import { ClientId, clientIdSchema } from './client/client-id.js' import { ClientManager, LoopbackMetadataGetter, @@ -55,7 +56,12 @@ import { ClientStore, ifClientStore } from './client/client-store.js' import { Client } from './client/client.js' import { AUTHENTICATION_MAX_AGE, TOKEN_MAX_AGE } from './constants.js' import { DeviceId } from './device/device-id.js' -import { DeviceManager, DeviceManagerOptions } from './device/device-manager.js' +import { + DeviceInfo, + DeviceManager, + DeviceManagerOptions, + deviceManagerOptionsSchema, +} from './device/device-manager.js' import { DeviceStore, asDeviceStore } from './device/device-store.js' import { AccessDeniedError } from './errors/access-denied-error.js' import { AccountSelectionRequiredError } from './errors/account-selection-required-error.js' @@ -65,13 +71,14 @@ import { InvalidGrantError } from './errors/invalid-grant-error.js' import { InvalidParametersError } from './errors/invalid-parameters-error.js' import { InvalidRequestError } from './errors/invalid-request-error.js' import { LoginRequiredError } from './errors/login-required-error.js' -import { OAuthError } from './errors/oauth-error.js' import { UnauthorizedClientError } from './errors/unauthorized-client-error.js' import { WWWAuthenticateError } from './errors/www-authenticate-error.js' +import { HcaptchaConfig } from './lib/hcaptcha.js' import { Handler, Middleware, Router, + cacheControlMiddleware, combineMiddlewares, parseHttpRequest, setupCsrfToken, @@ -84,18 +91,26 @@ import { validateSameOrigin, writeJson, } from './lib/http/index.js' -import { RequestMetadata } from './lib/http/request.js' +import { + RequestMetadata, + extractLocales, + negotiateResponseContent as negotiateContent, +} from './lib/http/request.js' import { dateToEpoch, dateToRelativeSeconds } from './lib/util/date.js' -import { Override } from './lib/util/type.js' +import { Awaitable, Override } from './lib/util/type.js' import { CustomMetadata, buildMetadata } from './metadata/build-metadata.js' -import { OAuthHooks } from './oauth-hooks.js' +import { OAuthHooks, SignInData, SignUpData } from './oauth-hooks.js' import { OAuthVerifier, OAuthVerifierOptions } from './oauth-verifier.js' import { AuthorizationResultAuthorize } from './output/build-authorize-data.js' +import { + BrandingConfig, + Customization, + customizationSchema, +} from './output/build-customization-data.js' import { buildErrorPayload, buildErrorStatus, } from './output/build-error-payload.js' -import { Customization } from './output/customization.js' import { OutputManager } from './output/output-manager.js' import { AuthorizationResultRedirect, @@ -114,32 +129,36 @@ import { TokenManager } from './token/token-manager.js' import { TokenStore, asTokenStore } from './token/token-store.js' import { VerifyTokenClaimsOptions } from './token/verify-token-claims.js' -export type OAuthProviderStore = Partial< - ClientStore & - AccountStore & - DeviceStore & - TokenStore & - RequestStore & - ReplayStore -> - export { + type BrandingConfig, type CustomMetadata, type Customization, type Handler, + type HcaptchaConfig, Keyset, type OAuthAuthorizationServerMetadata, } +type ApiContext = { + requestUri: RequestUri + deviceId: DeviceId + deviceMetadata: RequestMetadata +} + +export type ErrorHandler< + Req extends IncomingMessage = IncomingMessage, + Res extends ServerResponse = ServerResponse, +> = (req: Req, res: Res, err: unknown, message: string) => void + export type RouterOptions< Req extends IncomingMessage = IncomingMessage, Res extends ServerResponse = ServerResponse, > = { - onError?: (req: Req, res: Res, err: unknown, message: string) => void + onError?: ErrorHandler } export type OAuthProviderOptions = Override< - OAuthVerifierOptions & OAuthHooks & DeviceManagerOptions, + OAuthVerifierOptions & OAuthHooks & DeviceManagerOptions & Customization, { /** * Maximum age a device/account session can be before requiring @@ -157,11 +176,6 @@ export type OAuthProviderOptions = Override< */ metadata?: CustomMetadata - /** - * UI customizations - */ - customization?: Customization - /** * A custom fetch function that can be used to fetch the client metadata from * the internet. By default, the fetch function is a safeFetchWrap() function @@ -184,11 +198,18 @@ export type OAuthProviderOptions = Override< * this store implements all the interfaces not provided in the other * `Store` options. */ - store?: OAuthProviderStore + store?: Partial< + AccountStore & + ClientStore & + DeviceStore & + ReplayStore & + RequestStore & + TokenStore + > accountStore?: AccountStore - deviceStore?: DeviceStore clientStore?: ClientStore + deviceStore?: DeviceStore replayStore?: ReplayStore requestStore?: RequestStore tokenStore?: TokenStore @@ -235,7 +256,6 @@ export class OAuthProvider extends OAuthVerifier { public constructor({ metadata, - customization = undefined, authenticationMaxAge = AUTHENTICATION_MAX_AGE, tokenMaxAge = TOKEN_MAX_AGE, @@ -264,10 +284,30 @@ export class OAuthProvider extends OAuthVerifier { loopbackMetadata = atprotoLoopbackClientMetadata, - // OAuthHooks & OAuthVerifierOptions & DeviceManagerOptions + // OAuthHooks & + // OAuthVerifierOptions & + // DeviceManagerOptions & + // Customization ...rest }: OAuthProviderOptions) { - super({ replayStore, redis, ...rest }) + const customization: Customization = customizationSchema.parse(rest) + const deviceManagerOptions: DeviceManagerOptions = + deviceManagerOptionsSchema.parse(rest) + + // @NOTE: hooks don't really need a type parser, as all zod can actually + // check at runtime is the fact that the values are functions. The only way + // we would benefit from zod here would be to wrap the functions with a + // validator for the provided function's return types, which we do not add + // because it would impact runtime performance and we trust the users of + // this lib (basically ourselves) to rely on the typing system to ensure the + // correct types are returned. + const hooks: OAuthHooks = rest + + // @NOTE: validation of super params (if we wanted to implement it) should + // be the responsibility of the super class. + const superOptions: OAuthVerifierOptions = rest + + super({ replayStore, redis, ...superOptions }) requestStore ??= redis ? new RequestStoreRedis({ redis }) @@ -276,13 +316,18 @@ export class OAuthProvider extends OAuthVerifier { this.authenticationMaxAge = authenticationMaxAge this.metadata = buildMetadata(this.issuer, this.keyset, metadata) - this.deviceManager = new DeviceManager(deviceStore, rest) + this.deviceManager = new DeviceManager(deviceStore, deviceManagerOptions) this.outputManager = new OutputManager(customization) - this.accountManager = new AccountManager(accountStore) + this.accountManager = new AccountManager( + this.issuer, + accountStore, + hooks, + customization, + ) this.clientManager = new ClientManager( this.metadata, this.keyset, - rest, + hooks, clientStore || null, loopbackMetadata || null, safeFetch, @@ -293,12 +338,12 @@ export class OAuthProvider extends OAuthVerifier { requestStore, this.signer, this.metadata, - rest, + hooks, ) this.tokenManager = new TokenManager( tokenStore, this.signer, - rest, + hooks, this.accessTokenType, tokenMaxAge, ) @@ -451,8 +496,7 @@ export class OAuthProvider extends OAuthVerifier { // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3-1 // > Since initial processing of the pushed authorization request does not // > involve resource owner interaction, error codes related to user - // > interaction, such as consent_required defined by [OIDC], are never - // > returned. + // > interaction, such as "access_denied", are never returned. if (err instanceof AccessDeniedError) { throw new InvalidRequestError(err.error_description, err) } @@ -470,7 +514,7 @@ export class OAuthProvider extends OAuthVerifier { .parseAsync(query.request_uri, { path: ['query', 'request_uri'] }) .catch(throwInvalidRequest) - return this.requestManager.get(requestUri, client.id, deviceId) + return this.requestManager.get(requestUri, deviceId, client.id) } if ('request' in query) { @@ -514,11 +558,11 @@ export class OAuthProvider extends OAuthVerifier { } private async deleteRequest( - uri: RequestUri, + requestUri: RequestUri, parameters: OAuthAuthorizationRequestParameters, ) { try { - await this.requestManager.delete(uri) + await this.requestManager.delete(requestUri) } catch (err) { throw AccessDeniedError.from(parameters, err) } @@ -690,24 +734,46 @@ export class OAuthProvider extends OAuthVerifier { })) } - protected async signIn( - deviceId: DeviceId, - uri: RequestUri, - clientId: ClientId, - credentials: SignInCredentials, + protected async signUp( + { requestUri, deviceId, deviceMetadata }: ApiContext, + data: SignUpData, ): Promise<{ account: Account consentRequired: boolean }> { + const { clientId } = await this.requestManager.get(requestUri, deviceId) + const client = await this.clientManager.getClient(clientId) + const { account } = await this.accountManager.signUp( + data, + deviceId, + deviceMetadata, + ) + + return { + account, + consentRequired: !client.info.isFirstParty, + } + } + + protected async signIn( + { requestUri, deviceId, deviceMetadata }: ApiContext, + data: SignInData, + ): Promise<{ + account: Account + consentRequired: boolean + }> { // Ensure the request is still valid (and update the request expiration) // @TODO use the returned scopes to determine if consent is required - await this.requestManager.get(uri, clientId, deviceId) + const { clientId } = await this.requestManager.get(requestUri, deviceId) + + const client = await this.clientManager.getClient(clientId) const { account, info } = await this.accountManager.signIn( - credentials, + data, deviceId, + deviceMetadata, ) return { @@ -722,22 +788,21 @@ export class OAuthProvider extends OAuthVerifier { } protected async acceptRequest( - uri: RequestUri, - clientId: ClientId, + { requestUri, deviceId, deviceMetadata }: ApiContext, sub: string, - deviceId: DeviceId, - deviceMetadata: RequestMetadata, ): Promise { const { issuer } = this - const client = await this.clientManager.getClient(clientId) - const { parameters, clientAuth } = await this.requestManager.get( - uri, - clientId, + const { parameters, clientId, clientAuth } = await this.requestManager.get( + requestUri, deviceId, ) + const client = await this.clientManager.getClient(clientId) + try { + // @TODO Currently, a user can "accept" a request for any did that sing-in + // on the device, even if "remember" was set to false. const { account, info } = await this.accountManager.get(deviceId, sub) // The user is trying to authorize without a fresh login @@ -749,7 +814,7 @@ export class OAuthProvider extends OAuthVerifier { } const code = await this.requestManager.setAuthorized( - uri, + requestUri, client, account, deviceId, @@ -765,24 +830,19 @@ export class OAuthProvider extends OAuthVerifier { return { issuer, parameters, redirect: { code } } } catch (err) { - await this.deleteRequest(uri, parameters) + await this.deleteRequest(requestUri, parameters) throw AccessDeniedError.from(parameters, err) } } - protected async rejectRequest( - deviceId: DeviceId, - uri: RequestUri, - clientId: ClientId, - ): Promise { - const { parameters } = await this.requestManager.get( - uri, - clientId, - deviceId, - ) + protected async rejectRequest({ + requestUri, + deviceId, + }: ApiContext): Promise { + const { parameters } = await this.requestManager.get(requestUri, deviceId) - await this.deleteRequest(uri, parameters) + await this.deleteRequest(requestUri, parameters) return { issuer: this.issuer, @@ -1033,57 +1093,67 @@ export class OAuthProvider extends OAuthVerifier { // Utils - const csrfCookie = (uri: RequestUri) => `csrf-${uri}` - const onError = + const csrfCookie = (requestUri: RequestUri) => `csrf-${requestUri}` + const onError: null | ErrorHandler = options?.onError ?? (process.env['NODE_ENV'] === 'development' - ? (req, res, err, msg): void => + ? (req, res, err, msg) => { console.error(`OAuthProvider error (${msg}):`, err) - : undefined) + } + : null) - /** - * Creates a middleware that will serve static JSON content. - */ - const staticJson = (json: unknown): Middleware => - combineMiddlewares([ - function (req, res, next) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') + // CORS preflight + const corsHeaders: Middleware = function (req, res, next) { + res.setHeader('Access-Control-Max-Age', '86400') // 1 day - res.setHeader('Cache-Control', 'max-age=300') - next() - }, - staticJsonMiddleware(json), - ]) + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin + // + // > For requests without credentials, the literal value "*" can be + // > specified as a wildcard; the value tells browsers to allow + // > requesting code from any origin to access the resource. + // > Attempting to use the wildcard with credentials results in an + // > error. + // + // A "*" is safer to use than reflecting the request origin. + res.setHeader('Access-Control-Allow-Origin', '*') + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods + // > The value "*" only counts as a special wildcard value for + // > requests without credentials (requests without HTTP cookies or + // > HTTP authentication information). In requests with credentials, + // > it is treated as the literal method name "*" without special + // > semantics. + res.setHeader('Access-Control-Allow-Methods', '*') + + res.setHeader('Access-Control-Allow-Headers', 'Content-Type,DPoP') + + next() + } + + const corsPreflight: Middleware = combineMiddlewares([ + corsHeaders, + (req, res) => { + res.writeHead(200).end() + }, + ]) /** * Wrap an OAuth endpoint in a middleware that will set the appropriate * response headers and format the response as JSON. */ const jsonHandler = ( - buildJson: (this: T, req: TReq, res: TRes) => Json | Promise, + buildJson: (this: T, req: TReq, res: TRes) => Awaitable, status?: number, ): Handler => async function (req, res) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') - - // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1 - res.setHeader('Cache-Control', 'no-store') - res.setHeader('Pragma', 'no-cache') - - // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2 - const dpopNonce = server.nextDpopNonce() - if (dpopNonce) { - const name = 'DPoP-Nonce' - res.setHeader(name, dpopNonce) - res.appendHeader('Access-Control-Expose-Headers', name) - } - try { + // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1 + res.setHeader('Cache-Control', 'no-store') + res.setHeader('Pragma', 'no-cache') + // Ensure we can agree on a content encoding & type before starting to // build the JSON response. - if (!mediaType(req.headers['accept'], ['application/json'])) { + if (!negotiateContent(req, ['application/json'])) { throw createHttpError(406, 'Unsupported media type') } @@ -1094,39 +1164,107 @@ export class OAuthProvider extends OAuthVerifier { res.writeHead(status ?? 204).end() } } catch (err) { - if (!res.headersSent) { - if (err instanceof WWWAuthenticateError) { - const name = 'WWW-Authenticate' - res.setHeader(name, err.wwwAuthenticateHeader) - res.appendHeader('Access-Control-Expose-Headers', name) - } + onError?.(req, res, err, 'OAuth request error') + if (!res.headersSent) { const payload = buildErrorPayload(err) const status = buildErrorStatus(err) writeJson(res, payload, { status }) } else { res.destroy() } - - // OAuthError are used to build expected responses, so we don't log - // them as errors. - if (!(err instanceof OAuthError) || err.statusCode >= 500) { - onError?.(req, res, err, 'Unexpected error') - } } } + const oauthHandler = ( + buildOAuthResponse: (this: T, req: TReq, res: TRes) => Awaitable, + status?: number, + ) => + combineMiddlewares([ + corsHeaders, + jsonHandler(async function (req, res) { + try { + // https://datatracker.ietf.org/doc/html/rfc9449#section-8.2 + const dpopNonce = server.nextDpopNonce() + if (dpopNonce) { + const name = 'DPoP-Nonce' + res.setHeader(name, dpopNonce) + res.appendHeader('Access-Control-Expose-Headers', name) + } + + return await buildOAuthResponse.call(this, req, res) + } catch (err) { + if (!res.headersSent && err instanceof WWWAuthenticateError) { + const name = 'WWW-Authenticate' + res.setHeader(name, err.wwwAuthenticateHeader) + res.appendHeader('Access-Control-Expose-Headers', name) + } + + throw err + } + }, status), + ]) + + const apiHandler = < + T, + TReq extends Req, + TRes extends Res, + S extends z.ZodTypeAny, + Json, + >( + inputSchema: S, + buildJson: ( + this: T, + req: TReq, + res: TRes, + input: z.infer, + context: ApiContext, + ) => Json | Promise, + status?: number, + ) => + jsonHandler(async function (req, res) { + validateFetchMode(req, res, ['same-origin']) + validateFetchSite(req, res, ['same-origin']) + validateSameOrigin(req, res, issuerOrigin) + const referer = validateReferer(req, res, { + origin: issuerOrigin, + pathname: '/oauth/authorize', + }) + + const requestUri = await requestUriSchema.parseAsync( + referer.searchParams.get('request_uri'), + { path: ['query', 'request_uri'] }, + ) + + validateCsrfToken( + req, + res, + req.headers['x-csrf-token'], + csrfCookie(requestUri), + ) + + const { deviceId, deviceMetadata } = await server.deviceManager.load( + req, + res, + ) + + const payload = await parseHttpRequest(req, ['json']) + const input = await inputSchema.parseAsync(payload, { path: ['body'] }) + + const context: ApiContext = { requestUri, deviceId, deviceMetadata } + return buildJson.call(this, req, res, input, context) + }, status) + const navigationHandler = ( - handler: (this: T, req: TReq, res: TRes) => void | Promise, + handler: (this: T, req: TReq, res: TRes) => Awaitable, ): Handler => async function (req, res) { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') + try { + res.setHeader('Cache-Control', 'no-store') + res.setHeader('Pragma', 'no-cache') - res.setHeader('Cache-Control', 'no-store') - res.setHeader('Pragma', 'no-cache') + res.setHeader('Referrer-Policy', 'same-origin') - try { validateFetchMode(req, res, ['navigate']) validateFetchDest(req, res, ['document']) validateSameOrigin(req, res, issuerOrigin) @@ -1141,11 +1279,78 @@ export class OAuthProvider extends OAuthVerifier { ) if (!res.headersSent) { - await server.outputManager.sendErrorPage(res, err) + await server.outputManager.sendErrorPage(res, err, { + preferredLocales: extractLocales(req), + }) } } } + // Simple GET requests fall under the category of "no-cors" request, meaning + // that the browser will allow any cross-origin request, with credentials, + // to be sent to the oauth server. The OAuth Server will, however: + // 1) validate the request origin (see navigationHandler), + // 2) validate the CSRF token, + // 3) validate the referer, + // 4) validate the sec-fetch-site header, + // 4) validate the sec-fetch-mode header (see navigationHandler), + // 5) validate the sec-fetch-dest header (see navigationHandler). + // And will error (refuse to serve the request) if any of these checks fail. + const sameOriginNavigationHandler = < + T extends { url: URL }, + TReq extends Req, + TRes extends Res, + >( + handler: ( + this: T, + req: TReq, + res: TRes, + deviceInfo: DeviceInfo, + ) => Awaitable, + ): Handler => + navigationHandler(async function (req, res) { + validateFetchSite(req, res, ['same-origin']) + + const deviceInfo = await server.deviceManager.load(req, res) + + return handler.call(this, req, res, deviceInfo) + }) + + const authorizeRedirectNavigationHandler = < + T extends { url: URL }, + TReq extends Req, + TRes extends Res, + >( + handler: ( + this: T, + req: TReq, + res: TRes, + context: ApiContext, + ) => Awaitable, + ): Handler => + sameOriginNavigationHandler(async function (req, res, deviceInfo) { + const referer = validateReferer(req, res, { + origin: issuerOrigin, + pathname: '/oauth/authorize', + }) + + const requestUri = await requestUriSchema.parseAsync( + referer.searchParams.get('request_uri'), + ) + + const csrfToken = this.url.searchParams.get('csrf_token') + const csrfCookieName = csrfCookie(requestUri) + + // Next line will "clear" the CSRF token cookie, preventing replay of + // this request (navigating "back" will result in an error). + validateCsrfToken(req, res, csrfToken, csrfCookieName, true) + + const context: ApiContext = { ...deviceInfo, requestUri } + + const redirect = await handler.call(this, req, res, context) + return sendAuthorizeRedirect(res, redirect) + }) + /** * Provides a better UX when a request is denied by redirecting to the * client with the error details. This will also log any error that caused @@ -1172,44 +1377,26 @@ export class OAuthProvider extends OAuthVerifier { //- Public OAuth endpoints + router.options('/.well-known/oauth-authorization-server', corsPreflight) router.get( '/.well-known/oauth-authorization-server', - staticJson(server.metadata), + corsHeaders, + cacheControlMiddleware(300), + staticJsonMiddleware(server.metadata), ) - // CORS preflight - const corsPreflight: Middleware = function (req, res, _next) { - res - .writeHead(204, { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin - // - // > For requests without credentials, the literal value "*" can be - // > specified as a wildcard; the value tells browsers to allow - // > requesting code from any origin to access the resource. - // > Attempting to use the wildcard with credentials results in an - // > error. - // - // A "*" is safer to use than reflecting the request origin. - 'Access-Control-Allow-Origin': '*', - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods - // > The value "*" only counts as a special wildcard value for - // > requests without credentials (requests without HTTP cookies or - // > HTTP authentication information). In requests with credentials, - // > it is treated as the literal method name "*" without special - // > semantics. - 'Access-Control-Allow-Methods': '*', - 'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP', - 'Access-Control-Max-Age': '86400', // 1 day - }) - .end() - } - - router.get('/oauth/jwks', staticJson(server.jwks)) + router.options('/oauth/jwks', corsPreflight) + router.get( + '/oauth/jwks', + corsHeaders, + cacheControlMiddleware(300), + staticJsonMiddleware(server.jwks), + ) router.options('/oauth/par', corsPreflight) router.post( '/oauth/par', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const credentials = await oauthClientCredentialsSchema @@ -1233,11 +1420,9 @@ export class OAuthProvider extends OAuthVerifier { ) }, 201), ) - // https://datatracker.ietf.org/doc/html/rfc9126#section-2.3 // > If the request did not use the POST method, the authorization server // > responds with an HTTP 405 (Method Not Allowed) status code. - router.options('/oauth/par', corsPreflight) router.all('/oauth/par', (req, res) => { res.writeHead(405).end() }) @@ -1245,7 +1430,7 @@ export class OAuthProvider extends OAuthVerifier { router.options('/oauth/token', corsPreflight) router.post( '/oauth/token', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const clientMetadata = @@ -1277,7 +1462,7 @@ export class OAuthProvider extends OAuthVerifier { router.options('/oauth/revoke', corsPreflight) router.post( '/oauth/revoke', - jsonHandler(async function (req, res) { + oauthHandler(async function (req, res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const tokenIdentification = await oauthTokenIdentificationSchema @@ -1291,8 +1476,6 @@ export class OAuthProvider extends OAuthVerifier { } }), ) - - router.options('/oauth/revoke', corsPreflight) router.get( '/oauth/revoke', navigationHandler(async function (req, res) { @@ -1317,9 +1500,10 @@ export class OAuthProvider extends OAuthVerifier { }), ) + router.options('/oauth/introspect', corsPreflight) router.post( '/oauth/introspect', - jsonHandler(async function (req, _res) { + oauthHandler(async function (req, _res) { const payload = await parseHttpRequest(req, ['json', 'urlencoded']) const credentials = await oauthClientCredentialsSchema @@ -1346,7 +1530,7 @@ export class OAuthProvider extends OAuthVerifier { const query = Object.fromEntries(this.url.searchParams) const clientCredentials = await oauthClientCredentialsSchema - .parseAsync(query, { path: ['body'] }) + .parseAsync(query, { path: ['query'] }) .catch(throwInvalidRequest) if ('client_secret' in clientCredentials) { @@ -1362,7 +1546,9 @@ export class OAuthProvider extends OAuthVerifier { res, ) - const data = await server + const result: + | AuthorizationResultRedirect + | AuthorizationResultAuthorize = await server .authorize( clientCredentials, authorizationRequest, @@ -1371,173 +1557,79 @@ export class OAuthProvider extends OAuthVerifier { ) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - switch (true) { - case 'redirect' in data: { - return sendAuthorizeRedirect(res, data) - } - case 'authorize' in data: { - await setupCsrfToken(req, res, csrfCookie(data.authorize.uri)) - return server.outputManager.sendAuthorizePage(res, data) - } - default: { - // Should never happen - throw new Error('Unexpected authorization result') - } + if ('redirect' in result) { + return sendAuthorizeRedirect(res, result) + } else { + await setupCsrfToken(req, res, csrfCookie(result.authorize.uri)) + return server.outputManager.sendAuthorizePage(res, result, { + preferredLocales: extractLocales(req), + }) } }), ) - const signInPayloadSchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - credentials: signInCredentialsSchema, - }) - - router.options('/oauth/authorize/sign-in', corsPreflight) router.post( - '/oauth/authorize/sign-in', - jsonHandler(async function (req, res) { - validateFetchMode(req, res, ['same-origin']) - validateFetchSite(req, res, ['same-origin']) - validateSameOrigin(req, res, issuerOrigin) - - const payload = await parseHttpRequest(req, ['json']) - const input = await signInPayloadSchema.parseAsync(payload, { - path: ['body'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - ) + '/oauth/authorize/verify-handle-availability', + apiHandler( + z.object({ handle: handleSchema }).strict(), + async function (req, res, data) { + return server.accountManager.verifyHandleAvailability(data.handle) + }, + ), + ) - const { deviceId } = await server.deviceManager.load(req, res, true) + router.post( + '/oauth/authorize/sign-up', + apiHandler(signUpDataSchema, async function (req, res, data, ctx) { + return server.signUp(ctx, data) + }), + ) - return server.signIn( - deviceId, - input.request_uri, - input.client_id, - input.credentials, - ) + router.post( + '/oauth/authorize/sign-in', + apiHandler(signInDataSchema, async function (req, res, data, ctx) { + return server.signIn(ctx, data) }), ) - const acceptQuerySchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - account_sub: z.string(), - }) + router.post( + '/oauth/authorize/reset-password-request', + apiHandler( + resetPasswordRequestDataSchema, + async function (req, res, data) { + await server.accountManager.resetPasswordRequest(data) + }, + ), + ) + + router.post( + '/oauth/authorize/reset-password-confirm', + apiHandler( + resetPasswordConfirmDataSchema, + async function (req, res, data) { + await server.accountManager.resetPasswordConfirm(data) + }, + ), + ) - // Though this is a "no-cors" request, meaning that the browser will allow - // any cross-origin request, with credentials, to be sent, the handler will - // 1) validate the request origin, - // 2) validate the CSRF token, - // 3) validate the referer, - // 4) validate the sec-fetch-site header, - // 4) validate the sec-fetch-mode header, - // 5) validate the sec-fetch-dest header (see navigationHandler). - // And will error if any of these checks fail. router.get( '/oauth/authorize/accept', - navigationHandler(async function (req, res) { - validateFetchSite(req, res, ['same-origin']) - - const query = Object.fromEntries(this.url.searchParams) - const input = await acceptQuerySchema.parseAsync(query, { - path: ['query'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - searchParams: [ - ['request_uri', input.request_uri], - ['client_id', input.client_id], - ], - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - true, - ) - - const { deviceId, deviceMetadata } = await server.deviceManager.load( - req, - res, - ) + authorizeRedirectNavigationHandler(async function (req, res, ctx) { + const sub = this.url.searchParams.get('account_sub') + if (!sub) throw new InvalidRequestError('Account sub not provided') - const data = await server - .acceptRequest( - input.request_uri, - input.client_id, - input.account_sub, - deviceId, - deviceMetadata, - ) + return server + .acceptRequest(ctx, sub) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - - return await sendAuthorizeRedirect(res, data) }), ) - const rejectQuerySchema = z.object({ - csrf_token: z.string(), - request_uri: requestUriSchema, - client_id: clientIdSchema, - }) - - // Though this is a "no-cors" request, meaning that the browser will allow - // any cross-origin request, with credentials, to be sent, the handler will - // 1) validate the request origin, - // 2) validate the CSRF token, - // 3) validate the referer, - // 4) validate the sec-fetch-site header, - // 4) validate the sec-fetch-mode header, - // 5) validate the sec-fetch-dest header (see navigationHandler). - // And will error if any of these checks fail. router.get( '/oauth/authorize/reject', - navigationHandler(async function (req, res) { - validateFetchSite(req, res, ['same-origin']) - - const query = Object.fromEntries(this.url.searchParams) - const input = await rejectQuerySchema.parseAsync(query, { - path: ['query'], - }) - - validateReferer(req, res, { - origin: issuerOrigin, - pathname: '/oauth/authorize', - searchParams: [ - ['request_uri', input.request_uri], - ['client_id', input.client_id], - ], - }) - validateCsrfToken( - req, - res, - input.csrf_token, - csrfCookie(input.request_uri), - true, - ) - - const { deviceId } = await server.deviceManager.load(req, res) - - const data = await server - .rejectRequest(deviceId, input.request_uri, input.client_id) + authorizeRedirectNavigationHandler(async function (req, res, ctx) { + return server + .rejectRequest(ctx) .catch((err) => accessDeniedToRedirectCatcher(req, res, err)) - - return await sendAuthorizeRedirect(res, data) }), ) diff --git a/packages/oauth/oauth-provider/src/oauth-verifier.ts b/packages/oauth/oauth-provider/src/oauth-verifier.ts index 4c1b60b27dd..66598f25b4f 100644 --- a/packages/oauth/oauth-provider/src/oauth-verifier.ts +++ b/packages/oauth/oauth-provider/src/oauth-verifier.ts @@ -94,8 +94,10 @@ export class OAuthVerifier { : new ReplayStoreMemory(), accessTokenType = AccessTokenType.jwt, - ...dpopMgrOptions + ...rest }: OAuthVerifierOptions) { + const dpopMgrOptions: DpopManagerOptions = rest + const issuerParsed = oauthIssuerIdentifierSchema.parse(issuer) const issuerUrl = new URL(issuerParsed) diff --git a/packages/oauth/oauth-provider/src/output/build-authorize-data.ts b/packages/oauth/oauth-provider/src/output/build-authorize-data.ts index d86bf80b5c2..3ccf0cb09ba 100644 --- a/packages/oauth/oauth-provider/src/output/build-authorize-data.ts +++ b/packages/oauth/oauth-provider/src/output/build-authorize-data.ts @@ -31,7 +31,7 @@ export type AuthorizationResultAuthorize = { } // TODO: find a way to share this type with the frontend code -// (app/backend-data.ts) +// (app/backend-types.ts) type Session = { account: Account @@ -47,7 +47,6 @@ export type AuthorizeData = { clientMetadata: OAuthClientMetadata clientTrusted: boolean requestUri: string - csrfCookie: string loginHint?: string scopeDetails?: ScopeDetail[] newSessionsRequireConsent: boolean @@ -62,7 +61,6 @@ export function buildAuthorizeData( clientMetadata: data.client.metadata, clientTrusted: data.client.info.isTrusted, requestUri: data.authorize.uri, - csrfCookie: `csrf-${data.authorize.uri}`, loginHint: data.parameters.login_hint, newSessionsRequireConsent: data.parameters.prompt === 'consent', scopeDetails: data.authorize.scopeDetails, diff --git a/packages/oauth/oauth-provider/src/output/build-customization-data.ts b/packages/oauth/oauth-provider/src/output/build-customization-data.ts new file mode 100644 index 00000000000..7eaf57f3a4c --- /dev/null +++ b/packages/oauth/oauth-provider/src/output/build-customization-data.ts @@ -0,0 +1,189 @@ +import { z } from 'zod' +import { hcaptchaConfigSchema } from '../lib/hcaptcha.js' +import { isLinkRel } from '../lib/html/build-document.js' +import { multiLangStringSchema } from '../lib/locale.js' +export { type HcaptchaConfig, hcaptchaConfigSchema } from '../lib/hcaptcha.js' + +// Matches colors defined in tailwind.config.js +export const colorNames = ['brand', 'error', 'warning', 'success'] as const +export const colorNameSchema = z.enum(colorNames) +export type ColorName = z.infer + +export const ColorsDefinitionSchema = z.record(colorNameSchema, z.string()) +export type ColorsDefinition = z.infer + +export const localizedStringSchema = z.union([ + z.string(), + multiLangStringSchema, +]) +export type LocalizedString = z.infer + +export const linkRelSchema = z.string().refine(isLinkRel, 'Invalid link rel') +export type LinkRel = z.infer + +export const linkDefinitionSchema = z.object({ + title: localizedStringSchema, + href: z.string().url(), + rel: linkRelSchema.optional(), +}) +export type LinkDefinition = z.infer + +/** + * Aesthetic customization + */ +export const brandingConfigSchema = z.object({ + name: z.string().optional(), + logo: z.string().optional(), + colors: ColorsDefinitionSchema.optional(), + links: z.array(linkDefinitionSchema).readonly().optional(), +}) +export type BrandingConfig = z.infer + +export const customizationSchema = z.object({ + /** + * Available user domains that can be used to sign up. A non-empty array + * is required to enable the sign-up feature. + */ + availableUserDomains: z.array(z.string()).optional(), + /** + * UI customizations + */ + branding: brandingConfigSchema.optional(), + /** + * Is an invite code required to sign up? + */ + inviteCodeRequired: z.boolean().optional(), + /** + * Enables hCaptcha during sign-up. + */ + hcaptcha: hcaptchaConfigSchema.optional(), +}) +export type Customization = z.infer + +export type CustomizationData = { + // Functional customization + hcaptchaSiteKey?: string + inviteCodeRequired?: boolean + availableUserDomains?: string[] + + // Aesthetic customization + name?: string + logo?: string + links?: readonly LinkDefinition[] +} + +export function buildCustomizationData({ + branding, + availableUserDomains, + inviteCodeRequired, + hcaptcha, +}: Customization): CustomizationData { + // @NOTE the front end does not need colors here as they will be injected as + // CSS variables. + // @NOTE We only copy the values explicitly needed to avoid leaking sensitive + // data (in case the caller passed more than what we expect). + return { + availableUserDomains, + inviteCodeRequired, + hcaptchaSiteKey: hcaptcha?.siteKey, + name: branding?.name, + logo: branding?.logo, + links: branding?.links, + } +} + +export function buildCustomizationCss({ branding }: Customization) { + const vars = Array.from(buildCustomizationVars(branding)) + if (vars.length) return `:root { ${vars.join(' ')} }` + + return '' +} + +function* buildCustomizationVars(branding?: BrandingConfig) { + if (branding?.colors) { + for (const name of colorNames) { + const value = branding.colors[name] + if (!value) continue + + // Skip undefined values + if (value === undefined) continue + + const { r, g, b, a } = parseColor(value) + + // Tailwind does not apply alpha values to base colors + if (a !== undefined) throw new TypeError('Alpha not supported') + + const contrast = computeLuma({ r, g, b }) > 128 ? '0 0 0' : '255 255 255' + + yield `--color-${name}: ${r} ${g} ${b};` + yield `--color-${name}-c: ${contrast};` + } + } +} + +type RgbaColor = { r: number; g: number; b: number; a?: number } +function parseColor(color: unknown): RgbaColor { + if (typeof color !== 'string') { + throw new TypeError(`Invalid color value: ${typeof color}`) + } + + if (color.startsWith('#')) { + if (color.length === 4 || color.length === 5) { + const r = parseUi8Hex(color.slice(1, 2)) + const g = parseUi8Hex(color.slice(2, 3)) + const b = parseUi8Hex(color.slice(3, 4)) + const a = color.length > 4 ? parseUi8Hex(color.slice(4, 5)) : undefined + return { r, g, b, a } + } + + if (color.length === 7 || color.length === 9) { + const r = parseUi8Hex(color.slice(1, 3)) + const g = parseUi8Hex(color.slice(3, 5)) + const b = parseUi8Hex(color.slice(5, 7)) + const a = color.length > 8 ? parseUi8Hex(color.slice(7, 9)) : undefined + return { r, g, b, a } + } + + throw new TypeError(`Invalid hex color: ${color}`) + } + + const rgbMatch = color.match( + /^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, + ) + if (rgbMatch) { + const r = parseUi8Dec(rgbMatch[1]) + const g = parseUi8Dec(rgbMatch[2]) + const b = parseUi8Dec(rgbMatch[3]) + return { r, g, b } + } + + const rgbaMatch = color.match( + /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, + ) + if (rgbaMatch) { + const r = parseUi8Dec(rgbaMatch[1]) + const g = parseUi8Dec(rgbaMatch[2]) + const b = parseUi8Dec(rgbaMatch[3]) + const a = parseUi8Dec(rgbaMatch[4]) + return { r, g, b, a } + } + + throw new TypeError(`Unsupported color format: ${color}`) +} + +function computeLuma({ r, g, b }: RgbaColor) { + return 0.299 * r + 0.587 * g + 0.114 * b +} + +function parseUi8Hex(v: string) { + return asUi8(parseInt(v, 16)) +} + +function parseUi8Dec(v: string) { + return asUi8(parseInt(v, 10)) +} + +function asUi8(v: number) { + if (v >= 0 && v <= 255 && v === (v | 0)) return v + throw new TypeError(`Invalid color component: ${v}`) +} diff --git a/packages/oauth/oauth-provider/src/output/customization.ts b/packages/oauth/oauth-provider/src/output/customization.ts deleted file mode 100644 index 855e01992f4..00000000000 --- a/packages/oauth/oauth-provider/src/output/customization.ts +++ /dev/null @@ -1,118 +0,0 @@ -// Matches colors defined in tailwind.config.js -const colorNames = ['brand', 'error', 'warning'] as const -type ColorName = (typeof colorNames)[number] -const isColorName = (name: string): name is ColorName => - (colorNames as readonly string[]).includes(name) - -export type Customization = { - name?: string - logo?: string - colors?: { [_ in ColorName]?: string } - links?: Array<{ - href: string - title: string - rel?: string - }> -} - -export function buildCustomizationData({ - name, - logo, - links, -}: Customization = {}) { - return { - name, - logo, - links, - } -} - -export function buildCustomizationCss(customization?: Customization) { - const vars = Array.from(buildCustomizationVars(customization)) - if (vars.length) return `:root { ${vars.join(' ')} }` - - return '' -} - -export function* buildCustomizationVars(customization?: Customization) { - if (customization?.colors) { - for (const [name, value] of Object.entries(customization.colors)) { - if (!isColorName(name)) { - throw new TypeError(`Invalid color name: ${name}`) - } - - // Skip undefined values - if (value === undefined) continue - - const { r, g, b, a } = parseColor(value) - - // Tailwind does not apply alpha values to base colors - if (a !== undefined) throw new TypeError('Alpha not supported') - - yield `--color-${name}: ${r} ${g} ${b};` - } - } -} - -type RgbaColor = { r: number; g: number; b: number; a?: number } -function parseColor(color: unknown): RgbaColor { - if (typeof color !== 'string') { - throw new TypeError(`Invalid color value: ${typeof color}`) - } - - if (color.startsWith('#')) { - if (color.length === 4 || color.length === 5) { - const r = parseUi8Hex(color.slice(1, 2)) - const g = parseUi8Hex(color.slice(2, 3)) - const b = parseUi8Hex(color.slice(3, 4)) - const a = color.length > 4 ? parseUi8Hex(color.slice(4, 5)) : undefined - return { r, g, b, a } - } - - if (color.length === 7 || color.length === 9) { - const r = parseUi8Hex(color.slice(1, 3)) - const g = parseUi8Hex(color.slice(3, 5)) - const b = parseUi8Hex(color.slice(5, 7)) - const a = color.length > 8 ? parseUi8Hex(color.slice(7, 9)) : undefined - return { r, g, b, a } - } - - throw new TypeError(`Invalid hex color: ${color}`) - } - - const rgbMatch = color.match( - /^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, - ) - if (rgbMatch) { - const r = parseUi8Dec(rgbMatch[1]) - const g = parseUi8Dec(rgbMatch[2]) - const b = parseUi8Dec(rgbMatch[3]) - return { r, g, b } - } - - const rgbaMatch = color.match( - /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/, - ) - if (rgbaMatch) { - const r = parseUi8Dec(rgbaMatch[1]) - const g = parseUi8Dec(rgbaMatch[2]) - const b = parseUi8Dec(rgbaMatch[3]) - const a = parseUi8Dec(rgbaMatch[4]) - return { r, g, b, a } - } - - throw new TypeError(`Unsupported color format: ${color}`) -} - -function parseUi8Hex(v: string) { - return asUi8(parseInt(v, 16)) -} - -function parseUi8Dec(v: string) { - return asUi8(parseInt(v, 10)) -} - -function asUi8(v: number) { - if (v >= 0 && v <= 255 && v === (v | 0)) return v - throw new TypeError(`Invalid color component: ${v}`) -} diff --git a/packages/oauth/oauth-provider/src/output/output-manager.ts b/packages/oauth/oauth-provider/src/output/output-manager.ts index c8a35d4f73b..645b459b5cd 100644 --- a/packages/oauth/oauth-provider/src/output/output-manager.ts +++ b/packages/oauth/oauth-provider/src/output/output-manager.ts @@ -1,86 +1,149 @@ import type { ServerResponse } from 'node:http' import { Asset } from '../assets/asset.js' -import { getAsset } from '../assets/index.js' -import { Html, cssCode, html } from '../lib/html/index.js' +import { enumerateAssets } from '../assets/index.js' +import { CspConfig, mergeCsp } from '../lib/csp/index.js' +import { + Html, + LinkAttrs, + MetaAttrs, + cssCode, + html, + isLinkRel, +} from '../lib/html/index.js' +import { AVAILABLE_LOCALES, Locale, isAvailableLocale } from '../lib/locale.js' import { AuthorizationResultAuthorize, buildAuthorizeData, } from './build-authorize-data.js' -import { buildErrorPayload, buildErrorStatus } from './build-error-payload.js' import { Customization, + LinkDefinition, buildCustomizationCss, buildCustomizationData, -} from './customization.js' -import { declareBackendData, sendWebPage } from './send-web-page.js' +} from './build-customization-data.js' +import { buildErrorPayload, buildErrorStatus } from './build-error-payload.js' +import { assetToCsp, declareBackendData, sendWebPage } from './send-web-page.js' + +const HCAPTCHA_CSP = { + 'script-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'frame-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'style-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], + 'connect-src': ['https://hcaptcha.com', 'https://*.hcaptcha.com'], +} as const satisfies CspConfig + +export type SendPageOptions = { + preferredLocales?: readonly string[] +} export class OutputManager { - readonly customizationScript: Html - readonly customizationStyle: Html - readonly customizationLinks?: Customization['links'] - - // Could technically cause an "UnhandledPromiseRejection", which might cause - // the process to exit. This is intentional, as it's a critical error. It - // should never happen in practice, as the built assets are bundled with the - // package. - readonly assetsPromise: Promise<[js: Asset, css: Asset]> = Promise.all([ - getAsset('main.js'), - getAsset('main.css'), - ] as const) - - constructor(customization?: Customization) { - // Note: building this here for two reasons: + readonly links?: readonly LinkDefinition[] + readonly meta: readonly MetaAttrs[] = [ + { name: 'robots', content: 'noindex' }, + { name: 'description', content: 'ATProto OAuth authorization page' }, + ] + readonly scripts: readonly (Asset | Html)[] + readonly styles: readonly (Asset | Html)[] + readonly csp: CspConfig + + constructor(customization: Customization) { + this.links = customization.branding?.links + + const scripts = Array.from(enumerateAssets('application/javascript')) + const styles = Array.from(enumerateAssets('text/css')) + + // Note: building scripts/styles/csp here for two reasons: // 1. To avoid re-building it on every request - // 2. To throw during init if the customization is invalid - this.customizationScript = declareBackendData( - '__customizationData', - buildCustomizationData(customization), - ) - this.customizationStyle = cssCode(buildCustomizationCss(customization)) - this.customizationLinks = customization?.links + // 2. To throw during init if the customization/config is invalid + + this.scripts = [ + declareBackendData('__availableLocales', AVAILABLE_LOCALES), + declareBackendData( + '__customizationData', + buildCustomizationData(customization), + ), + // Last (to be able to read the "backend data" variables) + ...scripts.filter((asset) => asset.isEntry), + ] + + this.styles = [ + // First (to be overridden by customization) + ...styles, + cssCode(buildCustomizationCss(customization)), + ] + + const customizationCsp = customization?.hcaptcha ? HCAPTCHA_CSP : undefined + const assetsCsp: CspConfig = { + 'script-src': scripts.map(assetToCsp), + 'style-src': styles.map(assetToCsp), + } + + this.csp = mergeCsp(customizationCsp, assetsCsp) } async sendAuthorizePage( res: ServerResponse, data: AuthorizationResultAuthorize, + options?: SendPageOptions, ): Promise { - const [jsAsset, cssAsset] = await this.assetsPromise + const locale = negotiateLocale( + data.parameters.ui_locales?.split(' ') ?? options?.preferredLocales, + ) return sendWebPage(res, { scripts: [ declareBackendData('__authorizeData', buildAuthorizeData(data)), - this.customizationScript, - jsAsset, // Last (to be able to read the "backend data" variables) + ...this.scripts, ], - styles: [ - cssAsset, // First (to be overridden by customization) - this.customizationStyle, - ], - links: this.customizationLinks, - htmlAttrs: { lang: 'en' }, - title: 'Authorize', + styles: this.styles, + meta: this.meta, + links: this.buildLinks(locale), + htmlAttrs: { lang: locale }, body: html`

`, + csp: this.csp, }) } - async sendErrorPage(res: ServerResponse, err: unknown): Promise { - const [jsAsset, cssAsset] = await this.assetsPromise + async sendErrorPage( + res: ServerResponse, + err: unknown, + options?: SendPageOptions, + ): Promise { + const locale = negotiateLocale(options?.preferredLocales) return sendWebPage(res, { status: buildErrorStatus(err), scripts: [ declareBackendData('__errorData', buildErrorPayload(err)), - this.customizationScript, - jsAsset, // Last (to be able to read the "backend data" variables) + ...this.scripts, ], - styles: [ - cssAsset, // First (to be overridden by customization) - this.customizationStyle, - ], - links: this.customizationLinks, - htmlAttrs: { lang: 'en' }, - title: 'Error', + styles: this.styles, + meta: this.meta, + links: this.buildLinks(locale), + htmlAttrs: { lang: locale }, body: html`
`, + csp: this.csp, }) } + + buildLinks(locale: Locale) { + return this.links + ?.map(({ rel, href, title }: LinkDefinition): LinkAttrs | undefined => + isLinkRel(rel) + ? typeof title === 'string' + ? { href, rel, title } + : { href, rel, title: title[locale] || title.en } + : undefined, + ) + .filter((v) => v != null) + } +} + +function negotiateLocale(desiredLocales?: readonly string[]): Locale { + if (desiredLocales) { + for (const locale of desiredLocales) { + if (locale === '*') break // use default + if (isAvailableLocale(locale)) return locale + } + } + return 'en' } diff --git a/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts b/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts index 7d5aa46a7c6..c15a935f9d0 100644 --- a/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts +++ b/packages/oauth/oauth-provider/src/output/send-authorize-redirect.ts @@ -11,31 +11,42 @@ import { sendWebPage } from './send-web-page.js' // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-7.5.4 const REDIRECT_STATUS_CODE = 303 -export type AuthorizationResponseParameters = { - // Will be added from AuthorizationResultRedirect['issuer'] - // iss: string // rfc9207 - - // Will be added from AuthorizationResultRedirect['parameters'] - // state?: string - - code?: Code - id_token?: string - access_token?: string - token_type?: OAuthTokenType - expires_in?: string - - response?: string // FAPI JARM - session_state?: string // OIDC Session Management - - error?: string - error_description?: string - error_uri?: string -} +/** + * @note `iss` and `state` will be added from the + * {@link AuthorizationResultRedirect} object. + */ +export type AuthorizationRedirectParameters = + | { + // iss: string + // state?: string + code: Code + id_token?: string + access_token?: string + token_type?: OAuthTokenType + expires_in?: string + } + | { + // iss: string + // state?: string + error: string + error_description?: string + error_uri?: string + } + +const SUCCESS_REDIRECT_KEYS = [ + 'code', + 'id_token', + 'access_token', + 'expires_in', + 'token_type', +] as const + +const ERROR_REDIRECT_KEYS = ['error', 'error_description', 'error_uri'] as const export type AuthorizationResultRedirect = { issuer: string parameters: OAuthAuthorizationRequestParameters - redirect: AuthorizationResponseParameters + redirect: AuthorizationRedirectParameters } export async function sendAuthorizeRedirect( @@ -49,23 +60,19 @@ export async function sendAuthorizeRedirect( const mode = parameters.response_mode || 'query' // @TODO: default should depend on response_type - const entries: [string, string][] = Object.entries({ - iss: issuer, // rfc9207 - state: parameters.state, - - response: redirect.response, // FAPI JARM - session_state: redirect.session_state, // OIDC Session Management + const entries: [string, string][] = [ + ['iss', issuer], // rfc9207 + ] - code: redirect.code, - id_token: redirect.id_token, - access_token: redirect.access_token, - expires_in: redirect.expires_in, - token_type: redirect.token_type, + if (parameters.state != null) { + entries.push(['state', parameters.state]) + } - error: redirect.error, - error_description: redirect.error_description, - error_uri: redirect.error_uri, - }).filter((entry): entry is [string, string] => entry[1] != null) + const keys = 'code' in redirect ? SUCCESS_REDIRECT_KEYS : ERROR_REDIRECT_KEYS + for (const key of keys) { + const value = redirect[key] + if (value != null) entries.push([key, value]) + } res.setHeader('Cache-Control', 'no-store') diff --git a/packages/oauth/oauth-provider/src/output/send-web-page.ts b/packages/oauth/oauth-provider/src/output/send-web-page.ts index 32e6ba77ca6..5953e2c244f 100644 --- a/packages/oauth/oauth-provider/src/output/send-web-page.ts +++ b/packages/oauth/oauth-provider/src/output/send-web-page.ts @@ -1,5 +1,6 @@ import { createHash } from 'node:crypto' import type { ServerResponse } from 'node:http' +import { CspConfig, CspValue, buildCsp, mergeCsp } from '../lib/csp/index.js' import { AssetRef, BuildDocumentOptions, @@ -13,16 +14,38 @@ export function declareBackendData(name: string, data: unknown) { // The script tag is removed after the data is assigned to the global variable // to prevent other scripts from deducing the value of the variable. The "app" // script will read the global variable and then unset it. See - // "readBackendData" in "src/assets/app/backend-data.ts". + // "readBackendData" in "src/assets/app/backend-types.ts". return js`window[${name}]=${data};document.currentScript.remove();` } -export type SendWebPageOptions = BuildDocumentOptions & WriteResponseOptions +export type SendWebPageOptions = BuildDocumentOptions & + WriteResponseOptions & { + csp?: CspConfig + } export async function sendWebPage( res: ServerResponse, options: SendWebPageOptions, ): Promise { + const csp = mergeCsp(options.csp, { + 'default-src': ["'none'"], + 'base-uri': options.base?.origin as undefined | `https://${string}`, + 'script-src': ["'self'", ...assetsToCsp(options.scripts)], + 'style-src': ["'self'", ...assetsToCsp(options.styles)], + 'img-src': ["'self'", 'data:', 'https:'], + 'connect-src': ["'self'"], + 'upgrade-insecure-requests': true, + + // Prevents the CSP to be embedded in a page : + 'frame-ancestors': ["'none'"], + }) + + // @NOTE the csp string might become too long. However, since we need to + // specify the "frame-ancestors" directive, we can't use a meta tag. For that + // reason, we won't try to avoid too long headers and let the proxy throw + // in case of a too long header. + res.setHeader('Content-Security-Policy', buildCsp(csp)) + // @TODO: make these headers configurable (?) res.setHeader('Permissions-Policy', 'otp-credentials=*, document-domain=()') res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless') @@ -33,36 +56,27 @@ export async function sendWebPage( res.setHeader('X-Content-Type-Options', 'nosniff') res.setHeader('X-XSS-Protection', '0') res.setHeader('Strict-Transport-Security', 'max-age=63072000') - res.setHeader( - 'Content-Security-Policy', - [ - `default-src 'none'`, - `frame-ancestors 'none'`, - `form-action 'none'`, - `base-uri ${options.base?.origin || `'none'`}`, - `script-src 'self' ${ - options.scripts?.map(assetToHash).map(hashToCspRule).join(' ') ?? '' - }`, - `style-src 'self' ${ - options.styles?.map(assetToHash).map(hashToCspRule).join(' ') ?? '' - }`, - `img-src 'self' data: https:`, - `connect-src 'self'`, - `upgrade-insecure-requests`, - ].join('; '), - ) const html = buildDocument(options) return writeHtml(res, html.toString(), options) } -function assetToHash(asset: Html | AssetRef): string { - return asset instanceof Html - ? createHash('sha256').update(asset.toString()).digest('base64') - : asset.sha256 +export function* assetsToCsp( + assets?: Iterable, +): Generator { + if (assets) { + for (const asset of assets) { + yield assetToCsp(asset) + } + } } -function hashToCspRule(hash: string): string { - return `'sha256-${hash}'` +export function assetToCsp(asset: Html | AssetRef): CspValue { + if (asset instanceof Html) { + const hash = createHash('sha256').update(asset.toString()).digest('base64') + return `'sha256-${hash}'` + } else { + return `'sha256-${asset.sha256}'` + } } diff --git a/packages/oauth/oauth-provider/src/request/request-manager.ts b/packages/oauth/oauth-provider/src/request/request-manager.ts index 9fa872a814a..d551e9c7cb9 100644 --- a/packages/oauth/oauth-provider/src/request/request-manager.ts +++ b/packages/oauth/oauth-provider/src/request/request-manager.ts @@ -308,13 +308,13 @@ export class RequestManager { async get( uri: RequestUri, - clientId: ClientId, deviceId: DeviceId, + clientId?: ClientId, ): Promise { const id = decodeRequestUri(uri) const data = await this.store.readRequest(id) - if (!data) throw new InvalidRequestError(`Unknown request_uri "${uri}"`) + if (!data) throw new InvalidRequestError('Unknown request_uri') const updates: UpdateRequestData = {} @@ -336,7 +336,7 @@ export class RequestManager { ) } - if (data.clientId !== clientId) { + if (clientId != null && data.clientId !== clientId) { throw new AccessDeniedError( data.parameters, 'This request was initiated for another client', @@ -380,7 +380,7 @@ export class RequestManager { const id = decodeRequestUri(uri) const data = await this.store.readRequest(id) - if (!data) throw new InvalidRequestError(`Unknown request_uri "${uri}"`) + if (!data) throw new InvalidRequestError('Unknown request_uri') try { if (data.expiresAt < new Date()) { diff --git a/packages/oauth/oauth-provider/src/request/request-store.ts b/packages/oauth/oauth-provider/src/request/request-store.ts index 0763e1e81e3..02faca873f5 100644 --- a/packages/oauth/oauth-provider/src/request/request-store.ts +++ b/packages/oauth/oauth-provider/src/request/request-store.ts @@ -1,12 +1,12 @@ -import { Awaitable } from '../lib/util/type.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' import { Code } from './code.js' import { RequestData } from './request-data.js' import { RequestId } from './request-id.js' // Export all types needed to implement the RequestStore interface export * from './code.js' -export * from './request-id.js' export * from './request-data.js' +export * from './request-id.js' export type { Awaitable } export type UpdateRequestData = Pick< @@ -31,21 +31,17 @@ export interface RequestStore { findRequestByCode(code: Code): Awaitable } -export function isRequestStore( - implementation: Record & Partial, -): implementation is Record & RequestStore { - return ( - typeof implementation.createRequest === 'function' && - typeof implementation.readRequest === 'function' && - typeof implementation.updateRequest === 'function' && - typeof implementation.deleteRequest === 'function' && - typeof implementation.findRequestByCode === 'function' - ) -} +export const isRequestStore = buildInterfaceChecker([ + 'createRequest', + 'readRequest', + 'updateRequest', + 'deleteRequest', + 'findRequestByCode', +]) -export function ifRequestStore( - implementation?: Record & Partial, -): RequestStore | undefined { +export function ifRequestStore>( + implementation?: V, +): (V & RequestStore) | undefined { if (implementation && isRequestStore(implementation)) { return implementation } diff --git a/packages/oauth/oauth-provider/src/token/token-store.ts b/packages/oauth/oauth-provider/src/token/token-store.ts index 53d8e9a9b90..4b985f820b2 100644 --- a/packages/oauth/oauth-provider/src/token/token-store.ts +++ b/packages/oauth/oauth-provider/src/token/token-store.ts @@ -1,15 +1,15 @@ import { DeviceAccountInfo } from '../account/account-store.js' import { Account } from '../account/account.js' -import { Awaitable } from '../lib/util/type.js' +import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js' import { Code } from '../request/code.js' import { RefreshToken } from './refresh-token.js' import { TokenData } from './token-data.js' import { TokenId } from './token-id.js' // Export all types needed to implement the TokenStore interface -export * from './token-id.js' -export * from './token-data.js' export * from './refresh-token.js' +export * from './token-data.js' +export * from './token-id.js' export type { Awaitable } export type TokenInfo = { @@ -55,22 +55,18 @@ export interface TokenStore { findTokenByCode(code: Code): Awaitable } -export function isTokenStore( - implementation: Record & Partial, -): implementation is Record & TokenStore { - return ( - typeof implementation.createToken === 'function' && - typeof implementation.readToken === 'function' && - typeof implementation.rotateToken === 'function' && - typeof implementation.deleteToken === 'function' && - typeof implementation.findTokenByCode === 'function' && - typeof implementation.findTokenByRefreshToken === 'function' - ) -} +export const isTokenStore = buildInterfaceChecker([ + 'createToken', + 'readToken', + 'deleteToken', + 'rotateToken', + 'findTokenByRefreshToken', + 'findTokenByCode', +]) -export function asTokenStore( - implementation?: Record & Partial, -): TokenStore { +export function asTokenStore>( + implementation?: V, +): V & TokenStore { if (!implementation || !isTokenStore(implementation)) { throw new Error('Invalid TokenStore implementation') } diff --git a/packages/oauth/oauth-provider/tailwind.config.js b/packages/oauth/oauth-provider/tailwind.config.js index 57dc9f6e10b..55745b36fec 100644 --- a/packages/oauth/oauth-provider/tailwind.config.js +++ b/packages/oauth/oauth-provider/tailwind.config.js @@ -17,8 +17,13 @@ export default { extend: { colors: { brand: 'rgb(var(--color-brand) / )', + 'brand-c': 'rgb(var(--color-brand-c) / )', error: 'rgb(var(--color-error) / )', + 'error-c': 'rgb(var(--color-error-c) / )', warning: 'rgb(var(--color-warning) / )', + 'warning-c': 'rgb(var(--color-warning-c) / )', + success: 'rgb(var(--color-success) / )', + 'success-c': 'rgb(var(--color-success-c) / )', }, }, }, diff --git a/packages/oauth/oauth-provider/vite.config.mjs b/packages/oauth/oauth-provider/vite.config.mjs new file mode 100644 index 00000000000..844b02e3075 --- /dev/null +++ b/packages/oauth/oauth-provider/vite.config.mjs @@ -0,0 +1,16 @@ +import { lingui } from '@lingui/vite-plugin' +import react from '@vitejs/plugin-react-swc' + +/** + * @see {@link https://vitejs.dev/config/} + * @type {import('vite').UserConfig} + */ +export default { + root: './src/assets/app', + plugins: [ + react({ + plugins: [['@lingui/swc-plugin', {}]], + }), + lingui(), + ], +} diff --git a/packages/oauth/oauth-types/CHANGELOG.md b/packages/oauth/oauth-types/CHANGELOG.md index 3c60233afed..6cc0e7c385c 100644 --- a/packages/oauth/oauth-types/CHANGELOG.md +++ b/packages/oauth/oauth-types/CHANGELOG.md @@ -1,5 +1,14 @@ # @atproto/oauth-types +## 0.2.4 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Properly support locales with 3 chars (Asturian) + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/jwk@0.1.4 + ## 0.2.3 ### Patch Changes diff --git a/packages/oauth/oauth-types/package.json b/packages/oauth/oauth-types/package.json index e03df40a967..17969bb047b 100644 --- a/packages/oauth/oauth-types/package.json +++ b/packages/oauth/oauth-types/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/oauth-types", - "version": "0.2.3", + "version": "0.2.4", "license": "MIT", "description": "OAuth typing & validation library", "keywords": [ diff --git a/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts b/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts index 13e4bf71f46..4cee85c4357 100644 --- a/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts +++ b/packages/oauth/oauth-types/src/oauth-authorization-request-parameters.ts @@ -66,7 +66,7 @@ export const oauthAuthorizationRequestParametersSchema = z.object({ ui_locales: z .string() - .regex(/^[a-z]{2}(-[A-Z]{2})?( [a-z]{2}(-[A-Z]{2})?)*$/) // fr-CA fr en + .regex(/^[a-z]{2,3}(-[A-Z]{2})?( [a-z]{2,3}(-[A-Z]{2})?)*$/) // fr-CA fr en .optional(), // Previous ID Token, should be provided when prompt=none is used diff --git a/packages/ozone/CHANGELOG.md b/packages/ozone/CHANGELOG.md index 87376c7b447..759d5004bda 100644 --- a/packages/ozone/CHANGELOG.md +++ b/packages/ozone/CHANGELOG.md @@ -1,5 +1,16 @@ # @atproto/ozone +## 0.1.87 + +### Patch Changes + +- Updated dependencies [[`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1), [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`dc6e4ecb0`](https://github.com/bluesky-social/atproto/commit/dc6e4ecb0e09bbf4bc7a79c6ac43fb6da4166200)]: + - @atproto/api@0.14.8 + - @atproto/syntax@0.3.4 + - @atproto/lexicon@0.4.8 + - @atproto/xrpc@0.6.10 + - @atproto/xrpc-server@0.7.12 + ## 0.1.86 ### Patch Changes diff --git a/packages/ozone/package.json b/packages/ozone/package.json index ec72162106a..eac4618a65f 100644 --- a/packages/ozone/package.json +++ b/packages/ozone/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/ozone", - "version": "0.1.86", + "version": "0.1.87", "license": "MIT", "description": "Backend service for moderating the Bluesky network.", "keywords": [ diff --git a/packages/ozone/src/lexicon/lexicons.ts b/packages/ozone/src/lexicon/lexicons.ts index 938c406bb35..65d1a545dc2 100644 --- a/packages/ozone/src/lexicon/lexicons.ts +++ b/packages/ozone/src/lexicon/lexicons.ts @@ -4020,9 +4020,6 @@ export const schemaDict = { 'lex:com.atproto.sync.subscribeRepos#sync', 'lex:com.atproto.sync.subscribeRepos#identity', 'lex:com.atproto.sync.subscribeRepos#account', - 'lex:com.atproto.sync.subscribeRepos#handle', - 'lex:com.atproto.sync.subscribeRepos#migrate', - 'lex:com.atproto.sync.subscribeRepos#tombstone', 'lex:com.atproto.sync.subscribeRepos#info', ], }, @@ -4226,68 +4223,6 @@ export const schemaDict = { }, }, }, - handle: { - type: 'object', - description: 'DEPRECATED -- Use #identity event instead', - required: ['seq', 'did', 'handle', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - handle: { - type: 'string', - format: 'handle', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - migrate: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'migrateTo', 'time'], - nullable: ['migrateTo'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - migrateTo: { - type: 'string', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - tombstone: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, info: { type: 'object', required: ['name'], @@ -5787,8 +5722,10 @@ export const schemaDict = { properties: { video: { type: 'blob', + description: + 'The mp4 video file. May be up to 100mb, formerly limited to 50mb.', accept: ['video/mp4'], - maxSize: 50000000, + maxSize: 100000000, }, captions: { type: 'array', diff --git a/packages/ozone/src/lexicon/types/app/bsky/embed/video.ts b/packages/ozone/src/lexicon/types/app/bsky/embed/video.ts index 10e59e37539..822c2cb2a22 100644 --- a/packages/ozone/src/lexicon/types/app/bsky/embed/video.ts +++ b/packages/ozone/src/lexicon/types/app/bsky/embed/video.ts @@ -13,6 +13,7 @@ const id = 'app.bsky.embed.video' export interface Main { $type?: 'app.bsky.embed.video' + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ video: BlobRef captions?: Caption[] /** Alt text description of the video, for accessibility. */ diff --git a/packages/ozone/src/lexicon/types/com/atproto/sync/subscribeRepos.ts b/packages/ozone/src/lexicon/types/com/atproto/sync/subscribeRepos.ts index decfb210b25..318d096aa83 100644 --- a/packages/ozone/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +++ b/packages/ozone/src/lexicon/types/com/atproto/sync/subscribeRepos.ts @@ -22,9 +22,6 @@ export type OutputSchema = | $Typed | $Typed | $Typed - | $Typed - | $Typed - | $Typed | $Typed | { $type: string } export type HandlerError = ErrorFrame<'FutureCursor' | 'ConsumerTooSlow'> @@ -150,62 +147,6 @@ export function validateAccount(v: V) { return validate(v, id, hashAccount) } -/** DEPRECATED -- Use #identity event instead */ -export interface Handle { - $type?: 'com.atproto.sync.subscribeRepos#handle' - seq: number - did: string - handle: string - time: string -} - -const hashHandle = 'handle' - -export function isHandle(v: V) { - return is$typed(v, id, hashHandle) -} - -export function validateHandle(v: V) { - return validate(v, id, hashHandle) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Migrate { - $type?: 'com.atproto.sync.subscribeRepos#migrate' - seq: number - did: string - migrateTo: string | null - time: string -} - -const hashMigrate = 'migrate' - -export function isMigrate(v: V) { - return is$typed(v, id, hashMigrate) -} - -export function validateMigrate(v: V) { - return validate(v, id, hashMigrate) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Tombstone { - $type?: 'com.atproto.sync.subscribeRepos#tombstone' - seq: number - did: string - time: string -} - -const hashTombstone = 'tombstone' - -export function isTombstone(v: V) { - return is$typed(v, id, hashTombstone) -} - -export function validateTombstone(v: V) { - return validate(v, id, hashTombstone) -} - export interface Info { $type?: 'com.atproto.sync.subscribeRepos#info' name: 'OutdatedCursor' | (string & {}) diff --git a/packages/pds/CHANGELOG.md b/packages/pds/CHANGELOG.md index c88ebe78c6c..15c66278d04 100644 --- a/packages/pds/CHANGELOG.md +++ b/packages/pds/CHANGELOG.md @@ -1,5 +1,27 @@ # @atproto/pds +## 0.4.105 + +### Patch Changes + +- [#3585](https://github.com/bluesky-social/atproto/pull/3585) [`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1) Thanks [@dholms](https://github.com/dholms)! - Wrap sync v1.1 semantics. Add #sync event to subscribeRepos and deprecate #handle and #tombstone events + +- [#3602](https://github.com/bluesky-social/atproto/pull/3602) [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49) Thanks [@devinivy](https://github.com/devinivy)! - Permit 100mb video embeds. + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add support for account sign-ups during OAuth flows + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`38320191e`](https://github.com/bluesky-social/atproto/commit/38320191e559f8b928c6e951a9b4a6207240bfc1), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`6bcbb6d8c`](https://github.com/bluesky-social/atproto/commit/6bcbb6d8cd3696280935ff7892d8e191fd21fa49), [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29), [`dc6e4ecb0`](https://github.com/bluesky-social/atproto/commit/dc6e4ecb0e09bbf4bc7a79c6ac43fb6da4166200)]: + - @atproto/oauth-provider@0.5.0 + - @atproto/api@0.14.8 + - @atproto/syntax@0.3.4 + - @atproto-labs/fetch-node@0.1.8 + - @atproto/lexicon@0.4.8 + - @atproto/repo@0.7.1 + - @atproto/xrpc@0.6.10 + - @atproto/xrpc-server@0.7.12 + - @atproto/aws@0.2.17 + - @atproto-labs/xrpc-utils@0.0.8 + ## 0.4.104 ### Patch Changes diff --git a/packages/pds/package.json b/packages/pds/package.json index f601c292dc1..e1f98c423ef 100644 --- a/packages/pds/package.json +++ b/packages/pds/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/pds", - "version": "0.4.104", + "version": "0.4.105", "license": "MIT", "description": "Reference implementation of atproto Personal Data Server (PDS)", "keywords": [ diff --git a/packages/pds/src/account-manager/index.ts b/packages/pds/src/account-manager/account-manager.ts similarity index 59% rename from packages/pds/src/account-manager/index.ts rename to packages/pds/src/account-manager/account-manager.ts index 12d744c40d6..8d8aad6bd82 100644 --- a/packages/pds/src/account-manager/index.ts +++ b/packages/pds/src/account-manager/account-manager.ts @@ -1,67 +1,47 @@ import { KeyObject } from 'node:crypto' -import { Selectable } from 'kysely' import { CID } from 'multiformats/cid' import { HOUR, wait } from '@atproto/common' -import { - Account, - AccountInfo, - AccountStore, - Code, - DeviceData, - DeviceId, - DeviceStore, - FoundRequestResult, - NewTokenData, - RefreshToken, - RequestData, - RequestId, - RequestStore, - SignInCredentials, - TokenData, - TokenId, - TokenInfo, - TokenStore, - UpdateRequestData, -} from '@atproto/oauth-provider' -import { AuthRequiredError } from '@atproto/xrpc-server' -import { ActorStore } from '../actor-store/actor-store' +import { IdResolver } from '@atproto/identity' +import { isValidTld } from '@atproto/syntax' +import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' import { AuthScope } from '../auth-verifier' -import { BackgroundQueue } from '../background' import { softDeleted } from '../db' -import { ImageUrlBuilder } from '../image/image-url-builder' +import { hasExplicitSlur } from '../handle/explicit-slurs' +import { + baseNormalizeAndValidate, + ensureHandleServiceConstraints, + isServiceDomain, +} from '../handle/index' import { StatusAttr } from '../lexicon/types/com/atproto/admin/defs' import { AccountDb, EmailTokenPurpose, getDb, getMigrator } from './db' import * as account from './helpers/account' import { AccountStatus, ActorAccount } from './helpers/account' import * as auth from './helpers/auth' -import * as authRequest from './helpers/authorization-request' -import * as device from './helpers/device' -import * as deviceAccount from './helpers/device-account' import * as emailToken from './helpers/email-token' import * as invite from './helpers/invite' import * as password from './helpers/password' import * as repo from './helpers/repo' import * as scrypt from './helpers/scrypt' import * as token from './helpers/token' -import * as usedRefreshToken from './helpers/used-refresh-token' export { AccountStatus, formatAccountStatus } from './helpers/account' -export class AccountManager - implements AccountStore, RequestStore, DeviceStore, TokenStore -{ - db: AccountDb +export type AccountManagerDbConfig = { + accountDbLoc: string + disableWalAutoCheckpoint: boolean +} + +export class AccountManager { + readonly db: AccountDb constructor( - private actorStore: ActorStore, - private imageUrlBuilder: ImageUrlBuilder, - private backgroundQueue: BackgroundQueue, - dbLocation: string, - private jwtKey: KeyObject, - private serviceDid: string, - disableWalAutoCheckpoint = false, + readonly idResolver: IdResolver, + readonly jwtKey: KeyObject, + readonly serviceDid: string, + readonly serviceHandleDomains: string[], + db: AccountManagerDbConfig, ) { - this.db = getDb(dbLocation, disableWalAutoCheckpoint) + this.db = getDb(db.accountDbLoc, db.disableWalAutoCheckpoint) } async migrateOrThrow() { @@ -121,7 +101,67 @@ export class AccountManager return res.active ? AccountStatus.Active : res.status } - async createAccount(opts: { + async normalizeAndValidateHandle( + handle: string, + { + did, + allowAnyValid, + }: { + did?: string + allowAnyValid?: boolean + } = {}, + ): Promise { + const normalized = baseNormalizeAndValidate(handle) + + // tld validation + if (!isValidTld(normalized)) { + throw new InvalidRequestError( + 'Handle TLD is invalid or disallowed', + 'InvalidHandle', + ) + } + // slur check + if (!allowAnyValid && hasExplicitSlur(normalized)) { + throw new InvalidRequestError( + 'Inappropriate language in handle', + 'InvalidHandle', + ) + } + if (isServiceDomain(normalized, this.serviceHandleDomains)) { + // verify constraints on a service domain + ensureHandleServiceConstraints( + normalized, + this.serviceHandleDomains, + allowAnyValid, + ) + } else { + if (did == null) { + throw new InvalidRequestError( + 'Not a supported handle domain', + 'UnsupportedDomain', + ) + } + // verify resolution of a non-service domain + const resolvedDid = await this.idResolver.handle.resolve(normalized) + if (resolvedDid !== did) { + throw new InvalidRequestError('External handle did not resolve to DID') + } + } + + return normalized + } + + async createAccount({ + did, + handle, + email, + password, + repoCid, + repoRev, + inviteCode, + deactivated, + refreshJwt, + }: { did: string handle: string email?: string @@ -130,28 +170,12 @@ export class AccountManager repoRev: string inviteCode?: string deactivated?: boolean + refreshJwt?: string }) { - const { - did, - handle, - email, - password, - repoCid, - repoRev, - inviteCode, - deactivated, - } = opts const passwordScrypt = password ? await scrypt.genSaltAndHash(password) : undefined - const { accessJwt, refreshJwt } = await auth.createTokens({ - did, - jwtKey: this.jwtKey, - serviceDid: this.serviceDid, - scope: AuthScope.Access, - }) - const refreshPayload = auth.decodeRefreshToken(refreshJwt) const now = new Date().toISOString() await this.db.transaction(async (dbTxn) => { if (inviteCode) { @@ -167,10 +191,36 @@ export class AccountManager inviteCode, now, }), - auth.storeRefreshToken(dbTxn, refreshPayload, null), + refreshJwt && + auth.storeRefreshToken( + dbTxn, + auth.decodeRefreshToken(refreshJwt), + null, + ), repo.updateRoot(dbTxn, did, repoCid, repoRev), ]) }) + } + + async createAccountAndSession(opts: { + did: string + handle: string + email?: string + password?: string + repoCid: CID + repoRev: string + inviteCode?: string + deactivated?: boolean + }) { + const { accessJwt, refreshJwt } = await auth.createTokens({ + did: opts.did, + jwtKey: this.jwtKey, + serviceDid: this.serviceDid, + scope: AuthScope.Access, + }) + + await this.createAccount({ ...opts, refreshJwt }) + return { accessJwt, refreshJwt } } @@ -502,250 +552,4 @@ export class AccountManager ]), ) } - - // AccountStore - - private async buildAccount(row: Selectable): Promise { - const account = deviceAccount.toAccount(row, this.serviceDid) - - if (!account.name || !account.picture) { - const did = account.sub - - const profile = await this.actorStore.read(did, async (store) => { - return store.record.getProfileRecord() - }) - - if (profile) { - const { avatar, displayName } = profile - - account.name ||= displayName - account.picture ||= avatar - ? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString()) - : undefined - } - } - - return account - } - - async authenticateAccount( - { username: identifier, password, remember = false }: SignInCredentials, - deviceId: DeviceId, - ): Promise { - try { - const { user, appPassword } = await this.login({ identifier, password }) - - if (appPassword) { - throw new AuthRequiredError('App passwords are not allowed') - } - - await this.db.executeWithRetry( - deviceAccount.createOrUpdateQB(this.db, deviceId, user.did, remember), - ) - - return await this.getDeviceAccount(deviceId, user.did) - } catch (err) { - if (err instanceof AuthRequiredError) return null - throw err - } - } - - async addAuthorizedClient( - deviceId: DeviceId, - sub: string, - clientId: string, - ): Promise { - await this.db.transaction(async (dbTxn) => { - const row = await deviceAccount - .readQB(dbTxn, deviceId, sub) - .executeTakeFirstOrThrow() - - const { authorizedClients } = deviceAccount.toDeviceAccountInfo(row) - if (!authorizedClients.includes(clientId)) { - await deviceAccount - .updateQB(dbTxn, deviceId, sub, { - authorizedClients: [...authorizedClients, clientId], - }) - .execute() - } - }) - } - - async getDeviceAccount( - deviceId: DeviceId, - sub: string, - ): Promise { - const row = await deviceAccount - .getAccountInfoQB(this.db, deviceId, sub) - .executeTakeFirst() - - if (!row) return null - - return { - account: await this.buildAccount(row), - info: deviceAccount.toDeviceAccountInfo(row), - } - } - - async listDeviceAccounts(deviceId: DeviceId): Promise { - const rows = await deviceAccount - .listRememberedQB(this.db, deviceId) - .execute() - - return Promise.all( - rows.map(async (row) => ({ - account: await this.buildAccount(row), - info: deviceAccount.toDeviceAccountInfo(row), - })), - ) - } - - async removeDeviceAccount(deviceId: DeviceId, sub: string): Promise { - await this.db.executeWithRetry( - deviceAccount.removeQB(this.db, deviceId, sub), - ) - } - - // RequestStore - - async createRequest(id: RequestId, data: RequestData): Promise { - await this.db.executeWithRetry(authRequest.createQB(this.db, id, data)) - } - - async readRequest(id: RequestId): Promise { - try { - const row = await authRequest.readQB(this.db, id).executeTakeFirst() - if (!row) return null - return authRequest.rowToRequestData(row) - } finally { - // Take the opportunity to clean up expired requests. Do this after we got - // the current (potentially expired) request data to allow the provider to - // handle expired requests. - this.backgroundQueue.add(async () => { - await this.db.executeWithRetry(authRequest.removeOldExpiredQB(this.db)) - }) - } - } - - async updateRequest(id: RequestId, data: UpdateRequestData): Promise { - await this.db.executeWithRetry(authRequest.updateQB(this.db, id, data)) - } - - async deleteRequest(id: RequestId): Promise { - await this.db.executeWithRetry(authRequest.removeByIdQB(this.db, id)) - } - - async findRequestByCode(code: Code): Promise { - const row = await authRequest.findByCodeQB(this.db, code).executeTakeFirst() - return row ? authRequest.rowToFoundRequestResult(row) : null - } - - // DeviceStore - - async createDevice(deviceId: DeviceId, data: DeviceData): Promise { - await this.db.executeWithRetry(device.createQB(this.db, deviceId, data)) - } - - async readDevice(deviceId: DeviceId): Promise { - const row = await device.readQB(this.db, deviceId).executeTakeFirst() - return row ? device.rowToDeviceData(row) : null - } - - async updateDevice( - deviceId: DeviceId, - data: Partial, - ): Promise { - await this.db.executeWithRetry(device.updateQB(this.db, deviceId, data)) - } - - async deleteDevice(deviceId: DeviceId): Promise { - // Will cascade to device_account (device_account_device_id_fk) - await this.db.executeWithRetry(device.removeQB(this.db, deviceId)) - } - - // TokenStore - - async createToken( - id: TokenId, - data: TokenData, - refreshToken?: RefreshToken, - ): Promise { - await this.db.transaction(async (dbTxn) => { - if (refreshToken) { - const { count } = await usedRefreshToken - .countQB(dbTxn, refreshToken) - .executeTakeFirstOrThrow() - - if (count > 0) { - throw new Error('Refresh token already in use') - } - } - - return token.createQB(dbTxn, id, data, refreshToken).execute() - }) - } - - async readToken(tokenId: TokenId): Promise { - const row = await token.findByQB(this.db, { tokenId }).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } - - async deleteToken(tokenId: TokenId): Promise { - // Will cascade to used_refresh_token (used_refresh_token_fk) - await this.db.executeWithRetry(token.removeQB(this.db, tokenId)) - } - - async rotateToken( - tokenId: TokenId, - newTokenId: TokenId, - newRefreshToken: RefreshToken, - newData: NewTokenData, - ): Promise { - const err = await this.db.transaction(async (dbTxn) => { - const { id, currentRefreshToken } = await token - .forRotateQB(dbTxn, tokenId) - .executeTakeFirstOrThrow() - - if (currentRefreshToken) { - await usedRefreshToken - .insertQB(dbTxn, id, currentRefreshToken) - .execute() - } - - const { count } = await usedRefreshToken - .countQB(dbTxn, newRefreshToken) - .executeTakeFirstOrThrow() - - if (count > 0) { - // Do NOT throw (we don't want the transaction to be rolled back) - return new Error('New refresh token already in use') - } - - await token - .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData) - .execute() - }) - - if (err) throw err - } - - async findTokenByRefreshToken( - refreshToken: RefreshToken, - ): Promise { - const used = await usedRefreshToken - .findByTokenQB(this.db, refreshToken) - .executeTakeFirst() - - const search = used - ? { id: used.tokenId } - : { currentRefreshToken: refreshToken } - - const row = await token.findByQB(this.db, search).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } - - async findTokenByCode(code: Code): Promise { - const row = await token.findByQB(this.db, { code }).executeTakeFirst() - return row ? token.toTokenInfo(row, this.serviceDid) : null - } } diff --git a/packages/pds/src/account-manager/helpers/device-account.ts b/packages/pds/src/account-manager/helpers/device-account.ts index 2c8a5a21875..abb703b75bf 100644 --- a/packages/pds/src/account-manager/helpers/device-account.ts +++ b/packages/pds/src/account-manager/helpers/device-account.ts @@ -114,6 +114,7 @@ export const createOrUpdateQB = ( .insertInto('device_account') .values({ did, deviceId, authorizedClients, ...values }) .onConflict((oc) => oc.columns(['deviceId', 'did']).doUpdateSet(values)) + .returning(['remember', 'authorizedClients', 'authenticatedAt']) } export const getAccountInfoQB = ( diff --git a/packages/pds/src/account-manager/oauth-store.ts b/packages/pds/src/account-manager/oauth-store.ts new file mode 100644 index 00000000000..c72d1253450 --- /dev/null +++ b/packages/pds/src/account-manager/oauth-store.ts @@ -0,0 +1,494 @@ +import { Client, createOp as createPlcOp } from '@did-plc/lib' +import { Selectable } from 'kysely' +import { Keypair, Secp256k1Keypair } from '@atproto/crypto' +import { + Account, + AccountInfo, + AccountStore, + AuthenticateAccountData, + Code, + DeviceAccountInfo, + DeviceData, + DeviceId, + DeviceStore, + FoundRequestResult, + HandleUnavailableError, + InvalidRequestError, + NewTokenData, + RefreshToken, + RequestData, + RequestId, + RequestStore, + ResetPasswordConfirmData, + ResetPasswordRequestData, + SignUpData, + TokenData, + TokenId, + TokenInfo, + TokenStore, + UpdateRequestData, +} from '@atproto/oauth-provider' +import { + AuthRequiredError as XrpcAuthRequiredError, + InvalidRequestError as XrpcInvalidRequestError, +} from '@atproto/xrpc-server' +import { ActorStore } from '../actor-store/actor-store' +import { BackgroundQueue } from '../background' +import { ImageUrlBuilder } from '../image/image-url-builder' +import { ServerMailer } from '../mailer' +import { Sequencer } from '../sequencer' +import { AccountManager } from './account-manager' +import { AccountStatus, ActorAccount } from './helpers/account' +import * as authRequest from './helpers/authorization-request' +import * as device from './helpers/device' +import * as deviceAccount from './helpers/device-account' +import * as token from './helpers/token' +import * as usedRefreshToken from './helpers/used-refresh-token' + +/** + * This class' purpose is to implement the interface needed by the OAuthProvider + * to interact with the account database (through the {@link AccountManager}). + * + * @note The use of this class assumes that there is no entryway. + */ +export class OAuthStore + implements AccountStore, RequestStore, DeviceStore, TokenStore +{ + constructor( + private readonly accountManager: AccountManager, + private readonly actorStore: ActorStore, + private readonly imageUrlBuilder: ImageUrlBuilder, + private readonly backgroundQueue: BackgroundQueue, + private readonly mailer: ServerMailer, + private readonly sequencer: Sequencer, + private readonly plcClient: Client, + private readonly plcRotationKey: Keypair, + private readonly publicUrl: string, + private readonly recoveryDidKey: string | null, + ) {} + + private get db() { + const { db } = this.accountManager + if (db.destroyed) throw new Error('Database connection is closed') + return db + } + + private get serviceDid() { + return this.accountManager.serviceDid + } + + private async buildAccount(row: Selectable): Promise { + const account = deviceAccount.toAccount(row, this.serviceDid) + + if (!account.name || !account.picture) { + const did = account.sub + + const profile = await this.actorStore.read(did, async (store) => { + return store.record.getProfileRecord() + }) + + if (profile) { + const { avatar, displayName } = profile + + account.name ||= displayName + account.picture ||= avatar + ? this.imageUrlBuilder.build('avatar', did, avatar.ref.toString()) + : undefined + } + } + + return account + } + + private async verifyEmailAvailability(email: string): Promise { + // @NOTE Email validity & disposability check performed by the OAuthProvider + + const account = await this.accountManager.getAccountByEmail(email, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (account) { + throw new InvalidRequestError(`Email already taken`) + } + } + + // AccountStore + + async createAccount({ + locale: _locale, + inviteCode, + handle, + email, + password, + }: SignUpData): Promise { + // @TODO Send an account creation confirmation email (+verification link) to the user (in their locale) + // @NOTE Password strength already enforced by the OAuthProvider + + await Promise.all([ + this.verifyEmailAvailability(email), + this.verifyHandleAvailability(handle), + !inviteCode || this.accountManager.ensureInviteIsAvailable(inviteCode), + ]) + + // @TODO The code bellow should probably be refactored to be common with the + // code of the `com.atproto.server.createAccount` XRPC endpoint. + + const signingKey = await Secp256k1Keypair.create({ exportable: true }) + const signingKeyDid = signingKey.did() + + const plcCreate = await createPlcOp({ + signingKey: signingKeyDid, + rotationKeys: this.recoveryDidKey + ? [this.recoveryDidKey, this.plcRotationKey.did()] + : [this.plcRotationKey.did()], + handle, + pds: this.publicUrl, + signer: this.plcRotationKey, + }) + + const { did, op } = plcCreate + + await this.actorStore.create(did, signingKey) + try { + const commit = await this.actorStore.transact(did, (actorTxn) => + actorTxn.repo.createRepo([]), + ) + + await this.plcClient.sendOperation(did, op) + + await this.accountManager.createAccount({ + did, + handle, + email, + password, + inviteCode, + repoCid: commit.cid, + repoRev: commit.rev, + }) + try { + await this.sequencer.sequenceIdentityEvt(did, handle) + await this.sequencer.sequenceAccountEvt(did, AccountStatus.Active) + await this.sequencer.sequenceCommit(did, commit) + await this.accountManager.updateRepoRoot(did, commit.cid, commit.rev) + await this.actorStore.clearReservedKeypair(signingKeyDid, did) + + const account = await this.accountManager.getAccount(did) + if (!account) throw new Error('Account not found') + + return await this.buildAccount(account) + } catch (err) { + this.accountManager.deleteAccount(did) + throw err + } + } catch (err) { + await this.actorStore.destroy(did) + throw err + } + } + + async authenticateAccount({ + locale: _locale, + username: identifier, + password, + // Not supported by the PDS (yet?) + emailOtp = undefined, + }: AuthenticateAccountData): Promise { + // @TODO (?) Send an email to the user to notify them of the login attempt + try { + // Should never happen + if (emailOtp != null) { + throw new Error('Email OTP is not supported') + } + + const { user, appPassword, isSoftDeleted } = + await this.accountManager.login({ identifier, password }) + + if (isSoftDeleted) { + throw new InvalidRequestError('Account was taken down') + } + + if (appPassword) { + throw new InvalidRequestError('App passwords are not allowed') + } + + return this.buildAccount(user) + } catch (err) { + if (err instanceof XrpcAuthRequiredError) { + throw new InvalidRequestError(err.message, err) + } + throw err + } + } + + async addDeviceAccount( + deviceId: DeviceId, + sub: string, + remember: boolean, + ): Promise { + const [row] = await this.db.executeWithRetry( + deviceAccount.createOrUpdateQB(this.db, deviceId, sub, remember), + ) + if (!row) throw new Error('Failed to create device account') + return deviceAccount.toDeviceAccountInfo(row) + } + + async addAuthorizedClient( + deviceId: DeviceId, + sub: string, + clientId: string, + ): Promise { + await this.db.transaction(async (dbTxn) => { + const row = await deviceAccount + .readQB(dbTxn, deviceId, sub) + .executeTakeFirstOrThrow() + + const { authorizedClients } = deviceAccount.toDeviceAccountInfo(row) + if (!authorizedClients.includes(clientId)) { + await deviceAccount + .updateQB(dbTxn, deviceId, sub, { + authorizedClients: [...authorizedClients, clientId], + }) + .execute() + } + }) + } + + async getDeviceAccount( + deviceId: DeviceId, + sub: string, + ): Promise { + const row = await deviceAccount + .getAccountInfoQB(this.db, deviceId, sub) + .executeTakeFirst() + + if (!row) return null + + return { + account: await this.buildAccount(row), + info: deviceAccount.toDeviceAccountInfo(row), + } + } + + async listDeviceAccounts(deviceId: DeviceId): Promise { + const rows = await deviceAccount + .listRememberedQB(this.db, deviceId) + .execute() + + return Promise.all( + rows.map(async (row) => ({ + account: await this.buildAccount(row), + info: deviceAccount.toDeviceAccountInfo(row), + })), + ) + } + + async removeDeviceAccount(deviceId: DeviceId, sub: string): Promise { + await this.db.executeWithRetry( + deviceAccount.removeQB(this.db, deviceId, sub), + ) + } + + async resetPasswordRequest({ + locale: _locale, + email, + }: ResetPasswordRequestData): Promise { + const account = await this.accountManager.getAccountByEmail(email, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (!account?.email || !account?.handle) return + + const { handle } = account + const token = await this.accountManager.createEmailToken( + account.did, + 'reset_password', + ) + + // @TODO Use the locale to send the email in the right language + await this.mailer.sendResetPassword( + { handle, token }, + { to: account.email }, + ) + } + + async resetPasswordConfirm(data: ResetPasswordConfirmData): Promise { + await this.accountManager.resetPassword(data) + } + + async verifyHandleAvailability(handle: string): Promise { + // @NOTE Handle validity & normalization already enforced by the OAuthProvider + try { + const normalized = + await this.accountManager.normalizeAndValidateHandle(handle) + + // Should never happen (OAuthProvider should have already validated the + // handle) This check is just a safeguard against future normalization + // changes. + if (normalized !== handle) { + throw new HandleUnavailableError('syntax', 'Invalid handle') + } + + const account = await this.accountManager.getAccount(normalized, { + includeDeactivated: true, + includeTakenDown: true, + }) + + if (account) { + throw new HandleUnavailableError('taken') + } + } catch (err) { + if (err instanceof XrpcInvalidRequestError) { + throw err.customErrorName === 'HandleNotAvailable' + ? new HandleUnavailableError('taken', err.message) + : new HandleUnavailableError('syntax', err.message) + } + + throw err + } + } + + // RequestStore + + async createRequest(id: RequestId, data: RequestData): Promise { + await this.db.executeWithRetry(authRequest.createQB(this.db, id, data)) + } + + async readRequest(id: RequestId): Promise { + try { + const row = await authRequest.readQB(this.db, id).executeTakeFirst() + if (!row) return null + return authRequest.rowToRequestData(row) + } finally { + // Take the opportunity to clean up expired requests. Do this after we got + // the current (potentially expired) request data to allow the provider to + // handle expired requests. + this.backgroundQueue.add(async () => { + await this.db.executeWithRetry(authRequest.removeOldExpiredQB(this.db)) + }) + } + } + + async updateRequest(id: RequestId, data: UpdateRequestData): Promise { + await this.db.executeWithRetry(authRequest.updateQB(this.db, id, data)) + } + + async deleteRequest(id: RequestId): Promise { + await this.db.executeWithRetry(authRequest.removeByIdQB(this.db, id)) + } + + async findRequestByCode(code: Code): Promise { + const row = await authRequest.findByCodeQB(this.db, code).executeTakeFirst() + return row ? authRequest.rowToFoundRequestResult(row) : null + } + + // DeviceStore + + async createDevice(deviceId: DeviceId, data: DeviceData): Promise { + await this.db.executeWithRetry(device.createQB(this.db, deviceId, data)) + } + + async readDevice(deviceId: DeviceId): Promise { + const row = await device.readQB(this.db, deviceId).executeTakeFirst() + return row ? device.rowToDeviceData(row) : null + } + + async updateDevice( + deviceId: DeviceId, + data: Partial, + ): Promise { + await this.db.executeWithRetry(device.updateQB(this.db, deviceId, data)) + } + + async deleteDevice(deviceId: DeviceId): Promise { + // Will cascade to device_account (device_account_device_id_fk) + await this.db.executeWithRetry(device.removeQB(this.db, deviceId)) + } + + // TokenStore + + async createToken( + id: TokenId, + data: TokenData, + refreshToken?: RefreshToken, + ): Promise { + await this.db.transaction(async (dbTxn) => { + if (refreshToken) { + const { count } = await usedRefreshToken + .countQB(dbTxn, refreshToken) + .executeTakeFirstOrThrow() + + if (count > 0) { + throw new Error('Refresh token already in use') + } + } + + return token.createQB(dbTxn, id, data, refreshToken).execute() + }) + } + + async readToken(tokenId: TokenId): Promise { + const row = await token.findByQB(this.db, { tokenId }).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } + + async deleteToken(tokenId: TokenId): Promise { + // Will cascade to used_refresh_token (used_refresh_token_fk) + await this.db.executeWithRetry(token.removeQB(this.db, tokenId)) + } + + async rotateToken( + tokenId: TokenId, + newTokenId: TokenId, + newRefreshToken: RefreshToken, + newData: NewTokenData, + ): Promise { + const err = await this.db.transaction(async (dbTxn) => { + const { id, currentRefreshToken } = await token + .forRotateQB(dbTxn, tokenId) + .executeTakeFirstOrThrow() + + if (currentRefreshToken) { + await usedRefreshToken + .insertQB(dbTxn, id, currentRefreshToken) + .execute() + } + + const { count } = await usedRefreshToken + .countQB(dbTxn, newRefreshToken) + .executeTakeFirstOrThrow() + + if (count > 0) { + // Do NOT throw (we don't want the transaction to be rolled back) + return new Error('New refresh token already in use') + } + + await token + .rotateQB(dbTxn, id, newTokenId, newRefreshToken, newData) + .execute() + }) + + if (err) throw err + } + + async findTokenByRefreshToken( + refreshToken: RefreshToken, + ): Promise { + const used = await usedRefreshToken + .findByTokenQB(this.db, refreshToken) + .executeTakeFirst() + + const search = used + ? { id: used.tokenId } + : { currentRefreshToken: refreshToken } + + const row = await token.findByQB(this.db, search).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } + + async findTokenByCode(code: Code): Promise { + const row = await token.findByQB(this.db, { code }).executeTakeFirst() + return row ? token.toTokenInfo(row, this.serviceDid) : null + } +} diff --git a/packages/pds/src/actor-store/repo/reader.ts b/packages/pds/src/actor-store/repo/reader.ts index 6a327a62629..ebfccbb814d 100644 --- a/packages/pds/src/actor-store/repo/reader.ts +++ b/packages/pds/src/actor-store/repo/reader.ts @@ -1,4 +1,5 @@ import { BlobStore } from '@atproto/repo' +import { SyncEvtData } from '../../repo' import { BlobReader } from '../blob/reader' import { ActorDb } from '../db' import { RecordReader } from '../record/reader' @@ -17,4 +18,14 @@ export class RepoReader { this.record = new RecordReader(db) this.storage = new SqlRepoReader(db) } + + async getSyncEventData(): Promise { + const root = await this.storage.getRootDetailed() + const { blocks } = await this.storage.getBlocks([root.cid]) + return { + cid: root.cid, + rev: root.rev, + blocks, + } + } } diff --git a/packages/pds/src/actor-store/repo/transactor.ts b/packages/pds/src/actor-store/repo/transactor.ts index 06357ccd96d..f227f040078 100644 --- a/packages/pds/src/actor-store/repo/transactor.ts +++ b/packages/pds/src/actor-store/repo/transactor.ts @@ -18,7 +18,6 @@ import { ActorDb } from '../db' import { RecordTransactor } from '../record/transactor' import { RepoReader } from './reader' import { SqlRepoTransactor } from './sql-repo-transactor' -import { blobCidsFromWrites, commitOpsFromCreates } from './util' export class RepoTransactor extends RepoReader { blob: BlobTransactor @@ -61,10 +60,14 @@ export class RepoTransactor extends RepoReader { this.indexWrites(writes, commit.rev), this.blob.processWriteBlobs(commit.rev, writes), ]) + const ops = writes.map((w) => ({ + action: 'create' as const, + path: formatDataKey(w.uri.collection, w.uri.rkey), + cid: w.cid, + })) return { ...commit, - ops: commitOpsFromCreates(writes), - blobs: blobCidsFromWrites(writes), + ops, prevData: null, } } @@ -74,7 +77,16 @@ export class RepoTransactor extends RepoReader { swapCommitCid?: CID, ): Promise { this.db.assertTransaction() + if (writes.length > 200) { + throw new InvalidRequestError('Too many writes. Max: 200') + } + const commit = await this.formatCommit(writes, swapCommitCid) + // Do not allow commits > 2MB + if (commit.relevantBlocks.byteSize > 2000000) { + throw new InvalidRequestError('Too many writes. Max event size: 2MB') + } + await Promise.all([ // persist the commit to repo storage this.storage.applyCommit(commit), @@ -166,7 +178,6 @@ export class RepoTransactor extends RepoReader { return { ...commit, ops: commitOps, - blobs: blobCidsFromWrites(writes), prevData, } } diff --git a/packages/pds/src/actor-store/repo/util.ts b/packages/pds/src/actor-store/repo/util.ts deleted file mode 100644 index 6d2c7799a47..00000000000 --- a/packages/pds/src/actor-store/repo/util.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CidSet, formatDataKey } from '@atproto/repo' -import { CommitOp, PreparedCreate, PreparedWrite } from '../../repo' - -export const blobCidsFromWrites = (writes: PreparedWrite[]): CidSet => { - const blobCids = new CidSet() - for (const w of writes) { - if (w.action === 'create' || w.action === 'update') { - for (const blob of w.blobs) { - blobCids.add(blob.cid) - } - } - } - return blobCids -} - -export const commitOpsFromCreates = (writes: PreparedCreate[]): CommitOp[] => { - return writes.map((w) => ({ - action: 'create' as const, - path: formatDataKey(w.uri.collection, w.uri.rkey), - cid: w.cid, - })) -} diff --git a/packages/pds/src/api/com/atproto/admin/deleteAccount.ts b/packages/pds/src/api/com/atproto/admin/deleteAccount.ts index 33251eabb97..9d688551470 100644 --- a/packages/pds/src/api/com/atproto/admin/deleteAccount.ts +++ b/packages/pds/src/api/com/atproto/admin/deleteAccount.ts @@ -1,4 +1,4 @@ -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' @@ -9,12 +9,11 @@ export default function (server: Server, ctx: AppContext) { const { did } = input.body await ctx.actorStore.destroy(did) await ctx.accountManager.deleteAccount(did) - const tombstoneSeq = await ctx.sequencer.sequenceTombstone(did) const accountSeq = await ctx.sequencer.sequenceAccountEvt( did, AccountStatus.Deleted, ) - await ctx.sequencer.deleteAllForUser(did, [accountSeq, tombstoneSeq]) + await ctx.sequencer.deleteAllForUser(did, [accountSeq]) }, }) } diff --git a/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts b/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts index 4b8beea2aa5..304df361a48 100644 --- a/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts +++ b/packages/pds/src/api/com/atproto/admin/updateAccountHandle.ts @@ -1,6 +1,5 @@ import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' -import { normalizeAndValidateHandle } from '../../../../handle' import { Server } from '../../../../lexicon' import { httpLogger } from '../../../../logger' @@ -9,12 +8,13 @@ export default function (server: Server, ctx: AppContext) { auth: ctx.authVerifier.adminToken, handler: async ({ input }) => { const { did } = input.body - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.body.handle, - did, - allowReserved: true, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.body.handle, + { + did, + allowAnyValid: true, + }, + ) // Pessimistic check to handle spam: also enforced by updateHandle() and the db. const account = await ctx.accountManager.getAccount(handle, { @@ -44,7 +44,6 @@ export default function (server: Server, ctx: AppContext) { } try { - await ctx.sequencer.sequenceHandleUpdate(did, handle) await ctx.sequencer.sequenceIdentityEvt(did, handle) } catch (err) { httpLogger.error( diff --git a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts index d0639ef43a5..85539ca197a 100644 --- a/packages/pds/src/api/com/atproto/identity/resolveHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/resolveHandle.ts @@ -1,20 +1,11 @@ -import * as ident from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' +import { baseNormalizeAndValidate } from '../../../../handle' import { Server } from '../../../../lexicon' export default function (server: Server, ctx: AppContext) { server.com.atproto.identity.resolveHandle(async ({ params }) => { - let handle: string - try { - handle = ident.normalizeAndEnsureValidHandle(params.handle) - } catch (err) { - if (err instanceof ident.InvalidHandleError) { - throw new InvalidRequestError(err.message, 'InvalidHandle') - } else { - throw err - } - } + const handle = baseNormalizeAndValidate(params.handle) let did: string | undefined const user = await ctx.accountManager.getAccount(handle) diff --git a/packages/pds/src/api/com/atproto/identity/updateHandle.ts b/packages/pds/src/api/com/atproto/identity/updateHandle.ts index 61c42bae53e..d1025cd0925 100644 --- a/packages/pds/src/api/com/atproto/identity/updateHandle.ts +++ b/packages/pds/src/api/com/atproto/identity/updateHandle.ts @@ -1,7 +1,6 @@ import { DAY, MINUTE } from '@atproto/common' import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' -import { normalizeAndValidateHandle } from '../../../../handle' import { Server } from '../../../../lexicon' import { ids } from '../../../../lexicon/lexicons' import { httpLogger } from '../../../../logger' @@ -40,11 +39,10 @@ export default function (server: Server, ctx: AppContext) { return } - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.body.handle, - did: requester, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.body.handle, + { did: requester }, + ) // Pessimistic check to handle spam: also enforced by updateHandle() and the db. const account = await ctx.accountManager.getAccount(handle, { @@ -79,7 +77,6 @@ export default function (server: Server, ctx: AppContext) { } try { - await ctx.sequencer.sequenceHandleUpdate(requester, handle) await ctx.sequencer.sequenceIdentityEvt(requester, handle) } catch (err) { httpLogger.error( diff --git a/packages/pds/src/api/com/atproto/server/activateAccount.ts b/packages/pds/src/api/com/atproto/server/activateAccount.ts index d3c73d077d7..63342b264a6 100644 --- a/packages/pds/src/api/com/atproto/server/activateAccount.ts +++ b/packages/pds/src/api/com/atproto/server/activateAccount.ts @@ -1,4 +1,3 @@ -import { CidSet } from '@atproto/repo' import { INVALID_HANDLE } from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' import { AppContext } from '../../../../context' @@ -31,22 +30,9 @@ export default function (server: Server, ctx: AppContext) { await ctx.accountManager.activateAccount(requester) - const commitData = await ctx.actorStore.read(requester, async (store) => { - const root = await store.repo.storage.getRootDetailed() - const blocks = await store.repo.storage.getBlocks([root.cid]) - return { - cid: root.cid, - rev: root.rev, - since: null, - prev: null, - newBlocks: blocks.blocks, - relevantBlocks: blocks.blocks, - removedCids: new CidSet(), - ops: [], - blobs: new CidSet(), - prevData: null, - } - }) + const syncData = await ctx.actorStore.read(requester, (store) => + store.repo.getSyncEventData(), + ) // @NOTE: we're over-emitting for now for backwards compatibility, can reduce this in the future const status = await ctx.accountManager.getAccountStatus(requester) @@ -55,7 +41,7 @@ export default function (server: Server, ctx: AppContext) { requester, account.handle ?? INVALID_HANDLE, ) - await ctx.sequencer.sequenceCommit(requester, commitData) + await ctx.sequencer.sequenceSyncEvt(requester, syncData) }, }) } diff --git a/packages/pds/src/api/com/atproto/server/createAccount.ts b/packages/pds/src/api/com/atproto/server/createAccount.ts index 58c0dbd84c0..3a74b09c4b2 100644 --- a/packages/pds/src/api/com/atproto/server/createAccount.ts +++ b/packages/pds/src/api/com/atproto/server/createAccount.ts @@ -5,12 +5,9 @@ import { DidDocument, MINUTE, check } from '@atproto/common' import { ExportableKeypair, Keypair, Secp256k1Keypair } from '@atproto/crypto' import { AtprotoData, ensureAtpDocument } from '@atproto/identity' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' -import { - baseNormalizeAndValidate, - normalizeAndValidateHandle, -} from '../../../../handle' +import { baseNormalizeAndValidate } from '../../../../handle' import { Server } from '../../../../lexicon' import { InputSchema as CreateAccountInput } from '../../../../lexicon/types/com/atproto/server/createAccount' import { safeResolveDidDoc } from './util' @@ -23,6 +20,9 @@ export default function (server: Server, ctx: AppContext) { }, auth: ctx.authVerifier.userServiceAuthOptional, handler: async ({ input, auth, req }) => { + // @NOTE Until this code and the OAuthStore's `createAccount` are + // refactored together, any change made here must be reflected over there. + const requester = auth.credentials?.did ?? null const { did, @@ -60,7 +60,7 @@ export default function (server: Server, ctx: AppContext) { didDoc = await safeResolveDidDoc(ctx, did, true) - creds = await ctx.accountManager.createAccount({ + creds = await ctx.accountManager.createAccountAndSession({ did, handle, email, @@ -183,11 +183,10 @@ const validateInputsForLocalPds = async ( } // normalize & ensure valid handle - const handle = await normalizeAndValidateHandle({ - ctx, - handle: input.handle, - did: input.did, - }) + const handle = await ctx.accountManager.normalizeAndValidateHandle( + input.handle, + { did: input.did }, + ) // check that the invite code still has uses if (ctx.cfg.invites.required && inviteCode) { diff --git a/packages/pds/src/api/com/atproto/server/createSession.ts b/packages/pds/src/api/com/atproto/server/createSession.ts index 8d8cc3a1e1b..1228aa5609c 100644 --- a/packages/pds/src/api/com/atproto/server/createSession.ts +++ b/packages/pds/src/api/com/atproto/server/createSession.ts @@ -1,7 +1,7 @@ import { DAY, MINUTE } from '@atproto/common' import { INVALID_HANDLE } from '@atproto/syntax' import { AuthRequiredError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' import { resultPassthru } from '../../../proxy' diff --git a/packages/pds/src/api/com/atproto/server/deleteAccount.ts b/packages/pds/src/api/com/atproto/server/deleteAccount.ts index c7aa20da9f4..afc23d77b4f 100644 --- a/packages/pds/src/api/com/atproto/server/deleteAccount.ts +++ b/packages/pds/src/api/com/atproto/server/deleteAccount.ts @@ -1,6 +1,6 @@ import { MINUTE } from '@atproto/common' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { AccountStatus } from '../../../../account-manager' +import { AccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' @@ -48,8 +48,7 @@ export default function (server: Server, ctx: AppContext) { did, AccountStatus.Deleted, ) - const tombstoneSeq = await ctx.sequencer.sequenceTombstone(did) - await ctx.sequencer.deleteAllForUser(did, [accountSeq, tombstoneSeq]) + await ctx.sequencer.deleteAllForUser(did, [accountSeq]) }, }) } diff --git a/packages/pds/src/api/com/atproto/server/getSession.ts b/packages/pds/src/api/com/atproto/server/getSession.ts index b1fde56962a..9660bbb32ec 100644 --- a/packages/pds/src/api/com/atproto/server/getSession.ts +++ b/packages/pds/src/api/com/atproto/server/getSession.ts @@ -1,6 +1,6 @@ import { INVALID_HANDLE } from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AuthScope } from '../../../../auth-verifier' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/server/refreshSession.ts b/packages/pds/src/api/com/atproto/server/refreshSession.ts index e81ae05dc16..683202e42ad 100644 --- a/packages/pds/src/api/com/atproto/server/refreshSession.ts +++ b/packages/pds/src/api/com/atproto/server/refreshSession.ts @@ -1,6 +1,6 @@ import { INVALID_HANDLE } from '@atproto/syntax' import { AuthRequiredError, InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { softDeleted } from '../../../../db/util' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/sync/getRecord.ts b/packages/pds/src/api/com/atproto/sync/getRecord.ts index 23c9a1ac453..74feeb03d1d 100644 --- a/packages/pds/src/api/com/atproto/sync/getRecord.ts +++ b/packages/pds/src/api/com/atproto/sync/getRecord.ts @@ -1,5 +1,4 @@ import stream from 'node:stream' -import { CID } from 'multiformats/cid' import { byteIterableToStream } from '@atproto/common' import * as repo from '@atproto/repo' import { InvalidRequestError } from '@atproto/xrpc-server' diff --git a/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts b/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts index 725e2895e29..6eb63dd188b 100644 --- a/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts +++ b/packages/pds/src/api/com/atproto/sync/getRepoStatus.ts @@ -1,4 +1,4 @@ -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Server } from '../../../../lexicon' import { assertRepoAvailability } from './util' diff --git a/packages/pds/src/api/com/atproto/sync/listRepos.ts b/packages/pds/src/api/com/atproto/sync/listRepos.ts index a3a3a7657eb..6e998c90314 100644 --- a/packages/pds/src/api/com/atproto/sync/listRepos.ts +++ b/packages/pds/src/api/com/atproto/sync/listRepos.ts @@ -1,5 +1,5 @@ import { InvalidRequestError } from '@atproto/xrpc-server' -import { formatAccountStatus } from '../../../../account-manager' +import { formatAccountStatus } from '../../../../account-manager/account-manager' import { AppContext } from '../../../../context' import { Cursor, GenericKeyset, paginate } from '../../../../db/pagination' import { Server } from '../../../../lexicon' diff --git a/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts b/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts index cfa5e75ecc4..c2418d343aa 100644 --- a/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts +++ b/packages/pds/src/api/com/atproto/sync/subscribeRepos.ts @@ -45,9 +45,9 @@ export default function (server: Server, ctx: AppContext) { time: evt.time, ...evt.evt, } - } else if (evt.type === 'handle') { + } else if (evt.type === 'sync') { yield { - $type: '#handle', + $type: '#sync', seq: evt.seq, time: evt.time, ...evt.evt, @@ -66,13 +66,6 @@ export default function (server: Server, ctx: AppContext) { time: evt.time, ...evt.evt, } - } else if (evt.type === 'tombstone') { - yield { - $type: '#tombstone', - seq: evt.seq, - time: evt.time, - ...evt.evt, - } } } }) diff --git a/packages/pds/src/app-view.ts b/packages/pds/src/app-view.ts new file mode 100644 index 00000000000..6bcb53f9c44 --- /dev/null +++ b/packages/pds/src/app-view.ts @@ -0,0 +1,24 @@ +import { format } from 'node:util' +import { AtpAgent } from '@atproto/api' + +export type AppViewOptions = { + url: string + did: string + cdnUrlPattern?: string +} + +export class AppView { + public did: string + public agent: AtpAgent + private cdnUrlPattern?: string + + constructor(options: AppViewOptions) { + this.did = options.did + this.agent = new AtpAgent({ service: options.url }) + this.cdnUrlPattern = options.cdnUrlPattern + } + + getImageUrl(pattern: string, did: string, cid: string): string | undefined { + if (this.cdnUrlPattern) return format(this.cdnUrlPattern, pattern, did, cid) + } +} diff --git a/packages/pds/src/auth-routes.ts b/packages/pds/src/auth-routes.ts index d952f336d36..7af2dd64fa8 100644 --- a/packages/pds/src/auth-routes.ts +++ b/packages/pds/src/auth-routes.ts @@ -1,8 +1,9 @@ import { Router } from 'express' import { oauthProtectedResourceMetadataSchema } from '@atproto/oauth-provider' import { AppContext } from './context' +import { oauthLogger } from './logger' -export const createRouter = ({ authProvider, cfg }: AppContext): Router => { +export const createRouter = ({ oauthProvider, cfg }: AppContext): Router => { const router = Router() const oauthProtectedResourceMetadata = @@ -28,8 +29,13 @@ export const createRouter = ({ authProvider, cfg }: AppContext): Router => { res.status(200).json(oauthProtectedResourceMetadata) }) - if (authProvider) { - router.use(authProvider.createRouter()) + if (oauthProvider) { + const oauthMiddleware = oauthProvider.httpHandler({ + onError: (req, res, err, message) => { + oauthLogger.error({ err, req }, message) + }, + }) + router.use(oauthMiddleware) } return router diff --git a/packages/pds/src/auth-verifier.ts b/packages/pds/src/auth-verifier.ts index 7db671421ba..16c1520501c 100644 --- a/packages/pds/src/auth-verifier.ts +++ b/packages/pds/src/auth-verifier.ts @@ -19,7 +19,7 @@ import { parseReqNsid, verifyJwt as verifyServiceJwt, } from '@atproto/xrpc-server' -import { AccountManager } from './account-manager' +import { AccountManager } from './account-manager/account-manager' import { softDeleted } from './db' type ReqCtx = AuthVerifierContext | StreamAuthVerifierContext diff --git a/packages/pds/src/config/config.ts b/packages/pds/src/config/config.ts index 0c5f5d9e998..8658fac6272 100644 --- a/packages/pds/src/config/config.ts +++ b/packages/pds/src/config/config.ts @@ -1,7 +1,7 @@ import assert from 'node:assert' import path from 'node:path' import { DAY, HOUR, SECOND } from '@atproto/common' -import { Customization } from '@atproto/oauth-provider' +import { BrandingConfig, HcaptchaConfig } from '@atproto/oauth-provider' import { ServerEnvironment } from './env' // off-config but still from env: @@ -261,38 +261,49 @@ export const envToCfg = (env: ServerEnvironment): ServerConfig => { : { issuer: serviceCfg.publicUrl, provider: { - customization: { + hcaptcha: + env.hcaptchaSiteKey && + env.hcaptchaSecretKey && + env.hcaptchaTokenSalt + ? { + siteKey: env.hcaptchaSiteKey, + secretKey: env.hcaptchaSecretKey, + tokenSalt: env.hcaptchaTokenSalt, + } + : undefined, + branding: { name: env.serviceName ?? 'Personal PDS', logo: env.logoUrl, colors: { brand: env.brandColor, error: env.errorColor, warning: env.warningColor, + success: env.successColor, }, links: [ { - title: 'Home', + title: { en: 'Home', fr: 'Accueil' }, href: env.homeUrl, - rel: 'bookmark', + rel: 'canonical' as const, // Prevents login page from being indexed }, { - title: 'Terms of Service', + title: { en: 'Terms of Service' }, href: env.termsOfServiceUrl, - rel: 'terms-of-service', + rel: 'terms-of-service' as const, }, { - title: 'Privacy Policy', + title: { en: 'Privacy Policy' }, href: env.privacyPolicyUrl, - rel: 'privacy-policy', + rel: 'privacy-policy' as const, }, { - title: 'Support', + title: { en: 'Support' }, href: env.supportUrl, - rel: 'help', + rel: 'help' as const, }, ].filter( - (f): f is typeof f & { href: NonNullable<(typeof f)['href']> } => - f.href != null, + (f: T): f is T & { href: string } => + f.href != null && f.href !== '', ), }, }, @@ -435,7 +446,8 @@ export type OAuthConfig = { provider: | false | { - customization: Customization + hcaptcha?: HcaptchaConfig + branding: BrandingConfig } } diff --git a/packages/pds/src/config/env.ts b/packages/pds/src/config/env.ts index d7964ed3798..b545bc3243e 100644 --- a/packages/pds/src/config/env.ts +++ b/packages/pds/src/config/env.ts @@ -18,10 +18,16 @@ export const readEnv = (): ServerEnvironment => { blobUploadLimit: envInt('PDS_BLOB_UPLOAD_LIMIT'), devMode: envBool('PDS_DEV_MODE'), + // OAuth + hcaptchaSiteKey: envStr('PDS_HCAPTCHA_SITE_KEY'), + hcaptchaSecretKey: envStr('PDS_HCAPTCHA_SECRET_KEY'), + hcaptchaTokenSalt: envStr('PDS_HCAPTCHA_TOKEN_SALT'), + // branding brandColor: envStr('PDS_PRIMARY_COLOR'), errorColor: envStr('PDS_ERROR_COLOR'), warningColor: envStr('PDS_WARNING_COLOR'), + successColor: envStr('PDS_SUCCESS_COLOR'), // database dataDirectory: envStr('PDS_DATA_DIRECTORY'), @@ -151,10 +157,16 @@ export type ServerEnvironment = { blobUploadLimit?: number devMode?: boolean + // OAuth + hcaptchaSiteKey?: string + hcaptchaSecretKey?: string + hcaptchaTokenSalt?: string + // branding brandColor?: string errorColor?: string warningColor?: string + successColor?: string // database dataDirectory?: string diff --git a/packages/pds/src/context.ts b/packages/pds/src/context.ts index 4d8122ffb5d..33e51c71b47 100644 --- a/packages/pds/src/context.ts +++ b/packages/pds/src/context.ts @@ -9,7 +9,12 @@ import { AtpAgent } from '@atproto/api' import { KmsKeypair, S3BlobStore } from '@atproto/aws' import * as crypto from '@atproto/crypto' import { IdResolver } from '@atproto/identity' -import { JoseKey, OAuthVerifier } from '@atproto/oauth-provider' +import { + AccessTokenType, + JoseKey, + OAuthProvider, + OAuthVerifier, +} from '@atproto/oauth-provider' import { BlobStore } from '@atproto/repo' import { RateLimiter, @@ -25,7 +30,8 @@ import { safeFetchWrap, unicastLookup, } from '@atproto-labs/fetch-node' -import { AccountManager } from './account-manager' +import { AccountManager } from './account-manager/account-manager' +import { OAuthStore } from './account-manager/oauth-store' import { ActorStore } from './actor-store/actor-store' import { authPassthru, forwardedFor } from './api/proxy' import { @@ -43,7 +49,6 @@ import { ImageUrlBuilder } from './image/image-url-builder' import { fetchLogger } from './logger' import { ServerMailer } from './mailer' import { ModerationMailer } from './mailer/moderation' -import { PdsOAuthProvider } from './oauth/provider' import { LocalViewer, LocalViewerCreator } from './read-after-write/viewer' import { getRedisClient } from './redis' import { Sequencer } from './sequencer' @@ -70,7 +75,7 @@ export type AppContextOptions = { entrywayAdminAgent?: AtpAgent proxyAgent: undici.Dispatcher safeFetch: Fetch - authProvider?: PdsOAuthProvider + oauthProvider?: OAuthProvider authVerifier: AuthVerifier plcRotationKey: crypto.Keypair cfg: ServerConfig @@ -99,7 +104,7 @@ export class AppContext { public proxyAgent: undici.Dispatcher public safeFetch: Fetch public authVerifier: AuthVerifier - public authProvider?: PdsOAuthProvider + public oauthProvider?: OAuthProvider public plcRotationKey: crypto.Keypair public cfg: ServerConfig @@ -126,7 +131,7 @@ export class AppContext { this.proxyAgent = opts.proxyAgent this.safeFetch = opts.safeFetch this.authVerifier = opts.authVerifier - this.authProvider = opts.authProvider + this.oauthProvider = opts.oauthProvider this.plcRotationKey = opts.plcRotationKey this.cfg = opts.cfg } @@ -259,13 +264,11 @@ export class AppContext { }) const accountManager = new AccountManager( - actorStore, - imageUrlBuilder, - backgroundQueue, - cfg.db.accountDbLoc, + idResolver, jwtSecretKey, cfg.service.did, - cfg.db.disableWalAutoCheckpoint, + cfg.identity.serviceHandleDomains, + cfg.db, ) await accountManager.migrateOrThrow() @@ -335,26 +338,43 @@ export class AppContext { logError: false, }) - const authProvider = cfg.oauth.provider - ? new PdsOAuthProvider({ + const oauthProvider = cfg.oauth.provider + ? new OAuthProvider({ issuer: cfg.oauth.issuer, - keyset: [ - // Note: OpenID compatibility would require an RS256 private key in this list - await JoseKey.fromKeyLike(jwtSecretKey, undefined, 'HS256'), - ], - accountManager, + keyset: [await JoseKey.fromKeyLike(jwtSecretKey, undefined, 'HS256')], + store: new OAuthStore( + accountManager, + actorStore, + imageUrlBuilder, + backgroundQueue, + mailer, + sequencer, + plcClient, + plcRotationKey, + cfg.service.publicUrl, + cfg.identity.recoveryDidKey, + ), redis: redisScratch, dpopSecret: secrets.dpopSecret, - customization: cfg.oauth.provider.customization, + inviteCodeRequired: cfg.invites.required, + availableUserDomains: cfg.identity.serviceHandleDomains, + hcaptcha: cfg.oauth.provider.hcaptcha, + branding: cfg.oauth.provider.branding, safeFetch, - // @TODO: Make this configurable. The legacy implementation used to - // blindly trust the X-Forwarded-For header. - trustProxy: (_addr: string, _i: number) => true, + metadata: { + protected_resources: [new URL(cfg.oauth.issuer).origin], + scopes_supported: ['transition:generic', 'transition:chat.bsky'], + }, + // If the PDS is both an authorization server & resource server (no + // entryway), there is no need to use JWTs as access tokens. Instead, + // the PDS can use tokenId as access tokens. This allows the PDS to + // always use up-to-date token data from the token store. + accessTokenType: AccessTokenType.id, }) : undefined const oauthVerifier: OAuthVerifier = - authProvider ?? // OAuthProvider extends OAuthVerifier + oauthProvider ?? // OAuthProvider extends OAuthVerifier new OAuthVerifier({ issuer: cfg.oauth.issuer, keyset: [await JoseKey.fromKeyLike(jwtPublicKey!, undefined, 'ES256K')], @@ -401,7 +421,7 @@ export class AppContext { proxyAgent, safeFetch, authVerifier, - authProvider, + oauthProvider, plcRotationKey, cfg, ...(overrides ?? {}), diff --git a/packages/pds/src/handle/index.ts b/packages/pds/src/handle/index.ts index 0be31a5f6a3..5bf7275c923 100644 --- a/packages/pds/src/handle/index.ts +++ b/packages/pds/src/handle/index.ts @@ -1,61 +1,15 @@ -import * as ident from '@atproto/syntax' +import { + InvalidHandleError, + normalizeAndEnsureValidHandle, +} from '@atproto/syntax' import { InvalidRequestError } from '@atproto/xrpc-server' -import { AppContext } from '../context' -import { hasExplicitSlur } from './explicit-slurs' import { reservedSubdomains } from './reserved' -export const normalizeAndValidateHandle = async (opts: { - ctx: AppContext - handle: string - did?: string - allowReserved?: boolean -}): Promise => { - const { ctx, did, allowReserved } = opts - // base formatting validation - const handle = baseNormalizeAndValidate(opts.handle) - // tld validation - if (!ident.isValidTld(handle)) { - throw new InvalidRequestError( - 'Handle TLD is invalid or disallowed', - 'InvalidHandle', - ) - } - // slur check - if (hasExplicitSlur(handle)) { - throw new InvalidRequestError( - 'Inappropriate language in handle', - 'InvalidHandle', - ) - } - if (isServiceDomain(handle, ctx.cfg.identity.serviceHandleDomains)) { - // verify constraints on a service domain - ensureHandleServiceConstraints( - handle, - ctx.cfg.identity.serviceHandleDomains, - allowReserved, - ) - } else { - if (opts.did === undefined) { - throw new InvalidRequestError( - 'Not a supported handle domain', - 'UnsupportedDomain', - ) - } - // verify resolution of a non-service domain - const resolvedDid = await ctx.idResolver.handle.resolve(handle) - if (resolvedDid !== did) { - throw new InvalidRequestError('External handle did not resolve to DID') - } - } - return handle -} - export const baseNormalizeAndValidate = (handle: string) => { try { - const normalized = ident.normalizeAndEnsureValidHandle(handle) - return normalized + return normalizeAndEnsureValidHandle(handle) } catch (err) { - if (err instanceof ident.InvalidHandleError) { + if (err instanceof InvalidHandleError) { throw new InvalidRequestError(err.message, 'InvalidHandle') } throw err diff --git a/packages/pds/src/image/image-url.ts b/packages/pds/src/image/image-url.ts new file mode 100644 index 00000000000..05020f561ec --- /dev/null +++ b/packages/pds/src/image/image-url.ts @@ -0,0 +1,16 @@ +import { AppView } from '../app-view' +import { ids } from '../lexicon/lexicons' + +export class ImageUrlBuilder { + constructor( + readonly pdsHostname: string, + readonly appview?: AppView, + ) {} + + build(pattern: string, did: string, cid: string): string { + return ( + this.appview?.getImageUrl(pattern, did, cid) ?? + `https://${this.pdsHostname}/xrpc/${ids.ComAtprotoSyncGetBlob}?did=${did}&cid=${cid}` + ) + } +} diff --git a/packages/pds/src/lexicon/lexicons.ts b/packages/pds/src/lexicon/lexicons.ts index 938c406bb35..65d1a545dc2 100644 --- a/packages/pds/src/lexicon/lexicons.ts +++ b/packages/pds/src/lexicon/lexicons.ts @@ -4020,9 +4020,6 @@ export const schemaDict = { 'lex:com.atproto.sync.subscribeRepos#sync', 'lex:com.atproto.sync.subscribeRepos#identity', 'lex:com.atproto.sync.subscribeRepos#account', - 'lex:com.atproto.sync.subscribeRepos#handle', - 'lex:com.atproto.sync.subscribeRepos#migrate', - 'lex:com.atproto.sync.subscribeRepos#tombstone', 'lex:com.atproto.sync.subscribeRepos#info', ], }, @@ -4226,68 +4223,6 @@ export const schemaDict = { }, }, }, - handle: { - type: 'object', - description: 'DEPRECATED -- Use #identity event instead', - required: ['seq', 'did', 'handle', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - handle: { - type: 'string', - format: 'handle', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - migrate: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'migrateTo', 'time'], - nullable: ['migrateTo'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - migrateTo: { - type: 'string', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, - tombstone: { - type: 'object', - description: 'DEPRECATED -- Use #account event instead', - required: ['seq', 'did', 'time'], - properties: { - seq: { - type: 'integer', - }, - did: { - type: 'string', - format: 'did', - }, - time: { - type: 'string', - format: 'datetime', - }, - }, - }, info: { type: 'object', required: ['name'], @@ -5787,8 +5722,10 @@ export const schemaDict = { properties: { video: { type: 'blob', + description: + 'The mp4 video file. May be up to 100mb, formerly limited to 50mb.', accept: ['video/mp4'], - maxSize: 50000000, + maxSize: 100000000, }, captions: { type: 'array', diff --git a/packages/pds/src/lexicon/types/app/bsky/embed/video.ts b/packages/pds/src/lexicon/types/app/bsky/embed/video.ts index 10e59e37539..822c2cb2a22 100644 --- a/packages/pds/src/lexicon/types/app/bsky/embed/video.ts +++ b/packages/pds/src/lexicon/types/app/bsky/embed/video.ts @@ -13,6 +13,7 @@ const id = 'app.bsky.embed.video' export interface Main { $type?: 'app.bsky.embed.video' + /** The mp4 video file. May be up to 100mb, formerly limited to 50mb. */ video: BlobRef captions?: Caption[] /** Alt text description of the video, for accessibility. */ diff --git a/packages/pds/src/lexicon/types/com/atproto/sync/subscribeRepos.ts b/packages/pds/src/lexicon/types/com/atproto/sync/subscribeRepos.ts index decfb210b25..318d096aa83 100644 --- a/packages/pds/src/lexicon/types/com/atproto/sync/subscribeRepos.ts +++ b/packages/pds/src/lexicon/types/com/atproto/sync/subscribeRepos.ts @@ -22,9 +22,6 @@ export type OutputSchema = | $Typed | $Typed | $Typed - | $Typed - | $Typed - | $Typed | $Typed | { $type: string } export type HandlerError = ErrorFrame<'FutureCursor' | 'ConsumerTooSlow'> @@ -150,62 +147,6 @@ export function validateAccount(v: V) { return validate(v, id, hashAccount) } -/** DEPRECATED -- Use #identity event instead */ -export interface Handle { - $type?: 'com.atproto.sync.subscribeRepos#handle' - seq: number - did: string - handle: string - time: string -} - -const hashHandle = 'handle' - -export function isHandle(v: V) { - return is$typed(v, id, hashHandle) -} - -export function validateHandle(v: V) { - return validate(v, id, hashHandle) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Migrate { - $type?: 'com.atproto.sync.subscribeRepos#migrate' - seq: number - did: string - migrateTo: string | null - time: string -} - -const hashMigrate = 'migrate' - -export function isMigrate(v: V) { - return is$typed(v, id, hashMigrate) -} - -export function validateMigrate(v: V) { - return validate(v, id, hashMigrate) -} - -/** DEPRECATED -- Use #account event instead */ -export interface Tombstone { - $type?: 'com.atproto.sync.subscribeRepos#tombstone' - seq: number - did: string - time: string -} - -const hashTombstone = 'tombstone' - -export function isTombstone(v: V) { - return is$typed(v, id, hashTombstone) -} - -export function validateTombstone(v: V) { - return validate(v, id, hashTombstone) -} - export interface Info { $type?: 'com.atproto.sync.subscribeRepos#info' name: 'OutdatedCursor' | (string & {}) diff --git a/packages/pds/src/mailer/index.ts b/packages/pds/src/mailer/index.ts index 8ae5780eb1d..ceed8184efc 100644 --- a/packages/pds/src/mailer/index.ts +++ b/packages/pds/src/mailer/index.ts @@ -6,6 +6,8 @@ import { ServerConfig } from '../config' import { mailerLogger } from '../logger' import * as templates from './templates' +// @TODO Add support for i18n + export class ServerMailer { constructor( public readonly transporter: Transporter, @@ -23,35 +25,35 @@ export class ServerMailer { params: { handle: string; token: string }, mailOpts: Mail.Options, ) { - return this.sendTemplate('resetPassword', params, { + await this.sendTemplate('resetPassword', params, { subject: 'Password Reset Requested', ...mailOpts, }) } async sendAccountDelete(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('deleteAccount', params, { + await this.sendTemplate('deleteAccount', params, { subject: 'Account Deletion Requested', ...mailOpts, }) } async sendConfirmEmail(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('confirmEmail', params, { + await this.sendTemplate('confirmEmail', params, { subject: 'Email Confirmation', ...mailOpts, }) } async sendUpdateEmail(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('updateEmail', params, { + await this.sendTemplate('updateEmail', params, { subject: 'Email Update Requested', ...mailOpts, }) } async sendPlcOperation(params: { token: string }, mailOpts: Mail.Options) { - return this.sendTemplate('plcOperation', params, { + await this.sendTemplate('plcOperation', params, { subject: 'PLC Update Operation Requested', ...mailOpts, }) diff --git a/packages/pds/src/oauth/provider.ts b/packages/pds/src/oauth/provider.ts deleted file mode 100644 index 9f1bf937f59..00000000000 --- a/packages/pds/src/oauth/provider.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - AccessTokenType, - OAuthProvider, - OAuthProviderOptions, -} from '@atproto/oauth-provider' -import { AccountManager } from '../account-manager/index' -import { oauthLogger } from '../logger' - -export type AuthProviderOptions = { - accountManager: AccountManager -} & Pick< - OAuthProviderOptions, - 'issuer' | 'redis' | 'keyset' | 'dpopSecret' | 'customization' | 'trustProxy' -> & - Required> - -export class PdsOAuthProvider extends OAuthProvider { - constructor({ - accountManager, - keyset, - redis, - dpopSecret, - issuer, - customization, - safeFetch, - trustProxy, - }: AuthProviderOptions) { - super({ - issuer, - keyset, - dpopSecret, - redis, - safeFetch, - customization, - store: accountManager, - trustProxy, - metadata: { - // PdsOAuthProvider is used when the PDS is both an authorization server - // & resource server, in which case the issuer origin is also the - // resource server uri. - protected_resources: [new URL(issuer).origin], - - scopes_supported: ['transition:generic', 'transition:chat.bsky'], - }, - - // If the PDS is both an authorization server & resource server (no - // entryway), there is no need to use JWTs as access tokens. Instead, - // the PDS can use tokenId as access tokens. This allows the PDS to - // always use up-to-date token data from the token store. - accessTokenType: AccessTokenType.id, - }) - } - - createRouter() { - return this.httpHandler({ - onError: (req, res, err, message) => oauthLogger.error({ err }, message), - }) - } -} diff --git a/packages/pds/src/read-after-write/viewer.ts b/packages/pds/src/read-after-write/viewer.ts index c71e20e0911..5512ae615c4 100644 --- a/packages/pds/src/read-after-write/viewer.ts +++ b/packages/pds/src/read-after-write/viewer.ts @@ -1,6 +1,6 @@ import { AtUri, INVALID_HANDLE } from '@atproto/syntax' import { createServiceAuthHeaders } from '@atproto/xrpc-server' -import { AccountManager } from '../account-manager' +import { AccountManager } from '../account-manager/account-manager' import { ActorStoreReader } from '../actor-store/actor-store-reader' import { BskyAppView } from '../bsky-app-view' import { ImageUrlBuilder } from '../image/image-url-builder' diff --git a/packages/pds/src/repo/types.ts b/packages/pds/src/repo/types.ts index 79deac58c3d..67816902c44 100644 --- a/packages/pds/src/repo/types.ts +++ b/packages/pds/src/repo/types.ts @@ -1,6 +1,6 @@ import { CID } from 'multiformats/cid' import { RepoRecord } from '@atproto/lexicon' -import { CidSet, CommitData, WriteOpAction } from '@atproto/repo' +import { BlockMap, CommitData, WriteOpAction } from '@atproto/repo' import { AtUri } from '@atproto/syntax' export type ValidationStatus = 'valid' | 'unknown' | undefined @@ -52,12 +52,17 @@ export type CommitOp = { export type CommitDataWithOps = CommitData & { ops: CommitOp[] - blobs: CidSet prevData: CID | null } export type PreparedWrite = PreparedCreate | PreparedUpdate | PreparedDelete +export type SyncEvtData = { + cid: CID + rev: string + blocks: BlockMap +} + export class InvalidRecordError extends Error {} export class BadCommitSwapError extends Error { diff --git a/packages/pds/src/scripts/rebuild-repo.ts b/packages/pds/src/scripts/rebuild-repo.ts index 4497551d03d..7dc31439c7e 100644 --- a/packages/pds/src/scripts/rebuild-repo.ts +++ b/packages/pds/src/scripts/rebuild-repo.ts @@ -7,7 +7,7 @@ import { MemoryBlockstore, signCommit, } from '@atproto/repo' -import { AccountManager } from '../account-manager' +import { AccountManager } from '../account-manager/account-manager' import { ActorStore } from '../actor-store/actor-store' import { Sequencer } from '../sequencer' @@ -104,7 +104,10 @@ export const rebuildRepo = async ( } }) await ctx.accountManager.updateRepoRoot(did, commit.cid, commit.rev) - await ctx.sequencer.sequenceCommit(did, commit) + const syncData = await ctx.actorStore.read(did, (store) => + store.repo.getSyncEventData(), + ) + await ctx.sequencer.sequenceSyncEvt(did, syncData) } const promptContinue = async (): Promise => { diff --git a/packages/pds/src/scripts/sequencer-recovery/index.ts b/packages/pds/src/scripts/sequencer-recovery/index.ts index d7b4cf69cac..f417580db6c 100644 --- a/packages/pds/src/scripts/sequencer-recovery/index.ts +++ b/packages/pds/src/scripts/sequencer-recovery/index.ts @@ -1,8 +1,8 @@ -import { AccountManager } from '../../account-manager' +import { IdResolver } from '@atproto/identity' +import { AccountManager } from '../../account-manager/account-manager' import { ActorStore } from '../../actor-store/actor-store' import { BackgroundQueue } from '../../background' import { Crawlers } from '../../crawlers' -import { ImageUrlBuilder } from '../../image/image-url-builder' import { Sequencer } from '../../sequencer' import { parseIntArg } from '../util' import { Recoverer, RecovererContext } from './recoverer' @@ -35,15 +35,16 @@ const run = async () => { }, { blobstore: () => ({}) as any, backgroundQueue }, ) - const imageUrlBuilder = new ImageUrlBuilder('') const accountManager = new AccountManager( - actorStore, - imageUrlBuilder, - backgroundQueue, - './backup/account.sqlite', - {} as any, - '', + new IdResolver(), + {} as any, // jwtKey + 'did:example:serviceDid', + [], // service handle domains + { + accountDbLoc: './backup/account.sqlite', + disableWalAutoCheckpoint: false, + }, ) const recoveryDb = await getAndMigrateRecoveryDb('./backup/recovery.sqlite') const ctx = { sequencer, accountManager, actorStore, recoveryDb } diff --git a/packages/pds/src/scripts/sequencer-recovery/recoverer.ts b/packages/pds/src/scripts/sequencer-recovery/recoverer.ts index e1be796f48f..ca49f145d77 100644 --- a/packages/pds/src/scripts/sequencer-recovery/recoverer.ts +++ b/packages/pds/src/scripts/sequencer-recovery/recoverer.ts @@ -9,7 +9,10 @@ import { parseDataKey, readCar, } from '@atproto/repo' -import { AccountManager, AccountStatus } from '../../account-manager' +import { + AccountManager, + AccountStatus, +} from '../../account-manager/account-manager' import { ActorStore } from '../../actor-store/actor-store' import { ActorStoreTransactor } from '../../actor-store/actor-store-transactor' import { countAll } from '../../db' diff --git a/packages/pds/src/sequencer/db/schema.ts b/packages/pds/src/sequencer/db/schema.ts index 815125c39ca..cc9294a878b 100644 --- a/packages/pds/src/sequencer/db/schema.ts +++ b/packages/pds/src/sequencer/db/schema.ts @@ -1,13 +1,6 @@ import { Generated, GeneratedAlways, Insertable, Selectable } from 'kysely' -export type RepoSeqEventType = - | 'append' - | 'rebase' - | 'handle' - | 'migrate' - | 'identity' - | 'account' - | 'tombstone' +export type RepoSeqEventType = 'append' | 'sync' | 'identity' | 'account' export interface RepoSeq { seq: GeneratedAlways diff --git a/packages/pds/src/sequencer/events.ts b/packages/pds/src/sequencer/events.ts index 536d874c196..a0720b3951a 100644 --- a/packages/pds/src/sequencer/events.ts +++ b/packages/pds/src/sequencer/events.ts @@ -1,8 +1,8 @@ import { z } from 'zod' import { cborEncode, noUndefinedVals, schema } from '@atproto/common' import { BlockMap, blocksToCarFile } from '@atproto/repo' -import { AccountStatus } from '../account-manager' -import { CommitDataWithOps } from '../repo' +import { AccountStatus } from '../account-manager/account-manager' +import { CommitDataWithOps, SyncEvtData } from '../repo' import { RepoSeqInsert } from './db' export const formatSeqCommit = async ( @@ -13,41 +13,18 @@ export const formatSeqCommit = async ( blocksToSend.addMap(commitData.newBlocks) blocksToSend.addMap(commitData.relevantBlocks) - let evt: CommitEvt - - // If event is too big (max 200 ops or 1MB of data) - if (commitData.ops.length > 200 || blocksToSend.byteSize > 1000000) { - const justRoot = new BlockMap() - const rootBlock = blocksToSend.get(commitData.cid) - if (rootBlock) { - justRoot.set(commitData.cid, rootBlock) - } - - evt = { - rebase: false, - tooBig: true, - repo: did, - commit: commitData.cid, - rev: commitData.rev, - since: commitData.since, - blocks: await blocksToCarFile(commitData.cid, justRoot), - ops: [], - blobs: [], - prevData: commitData.prevData ?? undefined, - } - } else { - evt = { - rebase: false, - tooBig: false, - repo: did, - commit: commitData.cid, - rev: commitData.rev, - since: commitData.since, - blocks: await blocksToCarFile(commitData.cid, blocksToSend), - ops: commitData.ops, - blobs: commitData.blobs.toList(), - prevData: commitData.prevData ?? undefined, - } + const evt = { + repo: did, + commit: commitData.cid, + rev: commitData.rev, + since: commitData.since, + blocks: await blocksToCarFile(commitData.cid, blocksToSend), + ops: commitData.ops, + prevData: commitData.prevData ?? undefined, + // deprecated (but still required) fields + rebase: false, + tooBig: false, + blobs: [], } return { @@ -58,17 +35,19 @@ export const formatSeqCommit = async ( } } -export const formatSeqHandleUpdate = async ( +export const formatSeqSyncEvt = async ( did: string, - handle: string, + data: SyncEvtData, ): Promise => { - const evt: HandleEvt = { + const blocks = await blocksToCarFile(data.cid, data.blocks) + const evt: SyncEvt = { did, - handle, + rev: data.rev, + blocks, } return { did, - eventType: 'handle', + eventType: 'sync', event: cborEncode(evt), sequencedAt: new Date().toISOString(), } @@ -112,20 +91,6 @@ export const formatSeqAccountEvt = async ( } } -export const formatSeqTombstone = async ( - did: string, -): Promise => { - const evt: TombstoneEvt = { - did, - } - return { - did, - eventType: 'tombstone', - event: cborEncode(evt), - sequencedAt: new Date().toISOString(), - } -} - export const commitEvtOp = z.object({ action: z.union([ z.literal('create'), @@ -152,11 +117,12 @@ export const commitEvt = z.object({ }) export type CommitEvt = z.infer -export const handleEvt = z.object({ +export const syncEvt = z.object({ did: z.string(), - handle: z.string(), + blocks: schema.bytes, + rev: z.string(), }) -export type HandleEvt = z.infer +export type SyncEvt = z.infer export const identityEvt = z.object({ did: z.string(), @@ -178,22 +144,17 @@ export const accountEvt = z.object({ }) export type AccountEvt = z.infer -export const tombstoneEvt = z.object({ - did: z.string(), -}) -export type TombstoneEvt = z.infer - type TypedCommitEvt = { type: 'commit' seq: number time: string evt: CommitEvt } -type TypedHandleEvt = { - type: 'handle' +type TypedSyncEvt = { + type: 'sync' seq: number time: string - evt: HandleEvt + evt: SyncEvt } type TypedIdentityEvt = { type: 'identity' @@ -207,15 +168,8 @@ type TypedAccountEvt = { time: string evt: AccountEvt } -type TypedTombstoneEvt = { - type: 'tombstone' - seq: number - time: string - evt: TombstoneEvt -} export type SeqEvt = | TypedCommitEvt - | TypedHandleEvt + | TypedSyncEvt | TypedIdentityEvt | TypedAccountEvt - | TypedTombstoneEvt diff --git a/packages/pds/src/sequencer/sequencer.ts b/packages/pds/src/sequencer/sequencer.ts index bab95e6bfda..707f22e26da 100644 --- a/packages/pds/src/sequencer/sequencer.ts +++ b/packages/pds/src/sequencer/sequencer.ts @@ -4,7 +4,7 @@ import { SECOND, cborDecode, wait } from '@atproto/common' import { AccountStatus } from '../account-manager/helpers/account' import { Crawlers } from '../crawlers' import { seqLogger as log } from '../logger' -import { CommitDataWithOps } from '../repo' +import { CommitDataWithOps, SyncEvtData } from '../repo' import { RepoSeqEntry, RepoSeqInsert, @@ -15,15 +15,13 @@ import { import { AccountEvt, CommitEvt, - HandleEvt, IdentityEvt, SeqEvt, - TombstoneEvt, + SyncEvt, formatSeqAccountEvt, formatSeqCommit, - formatSeqHandleUpdate, formatSeqIdentityEvt, - formatSeqTombstone, + formatSeqSyncEvt, } from './events' export * from './events' @@ -177,8 +175,8 @@ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) { return await this.sequenceEvt(evt) } - async sequenceHandleUpdate(did: string, handle: string): Promise { - const evt = await formatSeqHandleUpdate(did, handle) + async sequenceSyncEvt(did: string, data: SyncEvtData) { + const evt = await formatSeqSyncEvt(did, data) return await this.sequenceEvt(evt) } @@ -195,11 +193,6 @@ export class Sequencer extends (EventEmitter as new () => SequencerEmitter) { return await this.sequenceEvt(evt) } - async sequenceTombstone(did: string): Promise { - const evt = await formatSeqTombstone(did) - return await this.sequenceEvt(evt) - } - async deleteAllForUser(did: string, excludingSeqs: number[] = []) { await this.db.executeWithRetry( this.db.db @@ -220,19 +213,19 @@ export const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => { continue } const evt = cborDecode(row.event) - if (row.eventType === 'append' || row.eventType === 'rebase') { + if (row.eventType === 'append') { seqEvts.push({ type: 'commit', seq: row.seq, time: row.sequencedAt, evt: evt as CommitEvt, }) - } else if (row.eventType === 'handle') { + } else if (row.eventType === 'sync') { seqEvts.push({ - type: 'handle', + type: 'sync', seq: row.seq, time: row.sequencedAt, - evt: evt as HandleEvt, + evt: evt as SyncEvt, }) } else if (row.eventType === 'identity') { seqEvts.push({ @@ -248,13 +241,6 @@ export const parseRepoSeqRows = (rows: RepoSeqEntry[]): SeqEvt[] => { time: row.sequencedAt, evt: evt as AccountEvt, }) - } else if (row.eventType === 'tombstone') { - seqEvts.push({ - type: 'tombstone', - seq: row.seq, - time: row.sequencedAt, - evt: evt as TombstoneEvt, - }) } } return seqEvts diff --git a/packages/pds/tests/account-deletion.test.ts b/packages/pds/tests/account-deletion.test.ts index b2fe0a41143..be0d048ac70 100644 --- a/packages/pds/tests/account-deletion.test.ts +++ b/packages/pds/tests/account-deletion.test.ts @@ -151,14 +151,12 @@ describe('account deletion', () => { expect( updatedDbContents.repoSeqs .filter((row) => row.did === carol.did) - .every( - (row) => row.eventType === 'tombstone' || row.eventType === 'account', - ), + .every((row) => row.eventType === 'account'), ).toBe(true) - // check we do have a tombstone for this did + // check we do have a account (deletion) event for this did expect( updatedDbContents.repoSeqs.filter( - (row) => row.did === carol.did && row.eventType === 'tombstone', + (row) => row.did === carol.did && row.eventType === 'account', ).length, ).toEqual(1) expect(updatedDbContents.appPasswords).toEqual( diff --git a/packages/pds/tests/oauth.test.ts b/packages/pds/tests/oauth.test.ts index 088773d75aa..37f20a4a1d8 100644 --- a/packages/pds/tests/oauth.test.ts +++ b/packages/pds/tests/oauth.test.ts @@ -1,33 +1,99 @@ import assert from 'node:assert' import { once } from 'node:events' -import { Server, createServer } from 'node:http' +import { + IncomingMessage, + Server, + ServerResponse, + createServer, +} from 'node:http' import { AddressInfo } from 'node:net' -import { Browser, Page, launch } from 'puppeteer' +import { type Browser, type Page, launch } from 'puppeteer' import { TestNetworkNoAppView } from '@atproto/dev-env' // @ts-expect-error (json file) import files from '@atproto/oauth-client-browser-example' -const getVisibleElement = async (page: Page, selector: string) => { - const elementHandle = await page.waitForSelector(selector) +class PageHelper implements AsyncDisposable { + constructor(protected readonly page: Page) {} - expect(elementHandle).not.toBeNull() - assert(elementHandle) + async goto(url: string) { + await this.page.goto(url) + } - await expect(elementHandle.isVisible()).resolves.toBe(true) + async waitForNetworkIdle() { + await this.page.waitForNetworkIdle() + } - return elementHandle + async navigationAction(run: () => Promise): Promise { + const promise = this.page.waitForNavigation() + await run() + await promise + await this.waitForNetworkIdle() + } + + async checkTitle(expected: string) { + await this.waitForNetworkIdle() + await expect(this.page.title()).resolves.toBe(expected) + } + + async clickOn(selector: string) { + const elementHandle = await this.getVisibleElement(selector) + await elementHandle.click() + return elementHandle + } + + async clickOnButton(text: string) { + return this.clickOn(`button::-p-text(${text})`) + } + + async typeIn(selector: string, text: string) { + const elementHandle = await this.getVisibleElement(selector) + elementHandle.focus() + await elementHandle.type(text) + return elementHandle + } + + async typeInInput(name: string, text: string) { + return this.typeIn(`input[name="${name}"]`, text) + } + + async ensureTextVisibility(text: string, tag = 'p') { + await this.page.waitForSelector(`${tag}::-p-text(${text})`) + } + + protected async getVisibleElement(selector: string) { + const elementHandle = await this.page.waitForSelector(selector) + + expect(elementHandle).not.toBeNull() + assert(elementHandle) + + await expect(elementHandle.isVisible()).resolves.toBe(true) + + return elementHandle + } + + async [Symbol.asyncDispose]() { + return this.page.close() + } + + static async from(browser: Browser) { + const page = await browser.newPage() + return new PageHelper(page) + } } describe('oauth', () => { let browser: Browser let network: TestNetworkNoAppView - let server: Server + let client: Server let appUrl: string beforeAll(async () => { browser = await launch({ browser: 'chrome', + // @NOTE We are using another language than "en" as default language to + // test the language negotiation. + args: ['--accept-lang=fr-BE,en-GB,en'], // For debugging: // headless: false, @@ -47,106 +113,255 @@ describe('oauth', () => { password: 'alice-pass', }) - server = await createClientServer() + client = createServer(clientHandler) + client.listen(0) + await once(client, 'listening') - const { port } = server.address() as AddressInfo + const { port } = client.address() as AddressInfo appUrl = `http://127.0.0.1:${port}?${new URLSearchParams({ plc_directory_url: network.plc.url, handle_resolver: network.pds.url, + sign_up_url: network.pds.url, env: 'test', })}` }) afterAll(async () => { - await server?.close() + await client?.close() await network?.close() await browser?.close() }) - it('starts', async () => { - const page = await browser.newPage() + it('Allows to sign-up trough OAuth', async () => { + const page = await PageHelper.from(browser) await page.goto(appUrl) - await expect(page.title()).resolves.toBe('OAuth Client Example') + await page.checkTitle('OAuth Client Example') - const handleInput = await getVisibleElement( - page, - 'input[placeholder="@handle, DID or PDS url"]', - ) + await page.navigationAction(async () => { + await page.clickOnButton('Sign up') + }) - await handleInput.focus() + await page.checkTitle('Authentification') - await handleInput.type('alice.test') + await page.clickOnButton('Créer un nouveau compte') - await Promise.all([ - // - handleInput.press('Enter'), - page.waitForNavigation(), - ]) + await page.typeInInput('handle', 'bob') - await expect(page.title()).resolves.toBe('Authorize') + await page.clickOnButton('Suivant') - const passwordInput = await getVisibleElement( - page, - 'input[type="password"]', - ) + await page.typeInInput('email', 'bob@test.com') + await page.typeInInput('password', 'bob-pass') - await passwordInput.focus() + await page.clickOnButton("S'inscrire") - // Make sure the warning is visible - await getVisibleElement(page, 'p::-p-text(Warning)') + // Make sure the new account is propagated to the PLC directory, allowing + // the client to resolve the account's did + await network.processAll() - await passwordInput.type('alice-pass') + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) - const rememberCheckbox = await getVisibleElement( - page, - 'label::-p-text(Remember this account on this device)', - ) + await page.checkTitle('OAuth Client Example') + + await page.ensureTextVisibility('Logged in!') + + await page.clickOnButton('Sign-out') + + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() + }) + + it('allows resetting the password', async () => { + const sendTemplateMock = await withMokedMailer(network) + + const page = await PageHelper.from(browser) + + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle('Se connecter') + + await page.clickOnButton('Oublié ?') + + await page.checkTitle('Mot de passe oublié') + + await page.typeInInput('email', 'alice@test.com') + + expect(sendTemplateMock).toHaveBeenCalledTimes(0) + + await page.clickOnButton('Suivant') + + await page.checkTitle('Réinitialiser le mot de passe') + + expect(sendTemplateMock).toHaveBeenCalledTimes(1) + + const [templateName, params] = sendTemplateMock.mock.calls[0] + + expect(templateName).toBe('resetPassword') + expect(params).toEqual({ + handle: 'alice.test', + token: expect.any(String), + }) + + const { token } = params as { token: string } + + await page.typeInInput('code', token) + + await page.typeInInput('password', 'alice-new-pass') + + await page.clickOnButton('Suivant') + + await page.checkTitle('Mot de passe mis à jour') + + await page.ensureTextVisibility('Mot de passe mis à jour !', 'h2') + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() + + // TODO: Find out why we can't use "using" here + sendTemplateMock[Symbol.dispose]() + }) - await rememberCheckbox.click() + it('Allows to sign-in trough OAuth', async () => { + const page = await PageHelper.from(browser) - const nextButton = await getVisibleElement(page, 'button::-p-text(Next)') + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle('Se connecter') - await nextButton.click() + await page.typeIn('input[type="password"]', 'alice-new-pass') - const acceptButton = await getVisibleElement( - page, - 'button::-p-text(Accept)', + // Make sure the warning is visible + await page.ensureTextVisibility('Avertissement') + + await page.clickOn( + 'label::-p-text(Se souvenir de ce compte sur cet appareil)', ) - await Promise.all([ - // - acceptButton.click(), - page.waitForNavigation(), - ]) + await page.clickOnButton('Se connecter') + + await page.checkTitle("Authoriser l'accès") + + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) + + await page.checkTitle('OAuth Client Example') + + await page.ensureTextVisibility('Logged in!') - await expect(page.title()).resolves.toBe('OAuth Client Example') + await page.clickOnButton('Sign-out') - // Check that the "Logged in!" message is visible - await getVisibleElement(page, 'p::-p-text(Logged in!)') + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() }) -}) -async function createClientServer() { - const server = createServer((req, res) => { - const path = req.url?.split('?')[0].slice(1) || 'index.html' - const file = Object.hasOwn(files, path) ? files[path] : null - - if (file) { - res - .writeHead(200, 'OK', { 'content-type': file.type }) - .end(Buffer.from(file.data, 'base64')) - } else { - res - .writeHead(404, 'Not Found', { 'content-type': 'text/plain' }) - .end('Page not found') - } + it('remembers the session', async () => { + const page = await PageHelper.from(browser) + + await page.goto(appUrl) + + await page.checkTitle('OAuth Client Example') + + await page.navigationAction(async () => { + const input = await page.typeIn( + 'input[placeholder="@handle, DID or PDS url"]', + 'alice.test', + ) + + await input.press('Enter') + }) + + await page.checkTitle("Authoriser l'accès") + + await page.navigationAction(async () => { + await page.clickOnButton("Authoriser l'accès") + }) + + await page.checkTitle('OAuth Client Example') + + await page.ensureTextVisibility('Logged in!') + + await page.clickOnButton('Sign-out') + + await page.waitForNetworkIdle() + + // TODO: Find out why we can't use "using" here + await page[Symbol.asyncDispose]() }) +}) - server.listen(0) - await once(server, 'listening') +async function withMokedMailer(network: TestNetworkNoAppView) { + // @ts-expect-error + const sendTemplateOrig = network.pds.ctx.mailer.sendTemplate + const sendTemplateMock = jest.fn( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async (templateName: unknown, params: unknown, mailOpts: unknown) => { + // + }, + ) as jest.Mock< + Promise, + [templateName: unknown, params: unknown, mailOpts: unknown] + > & + Disposable + + sendTemplateMock[Symbol.dispose] = () => { + // @ts-expect-error + network.pds.ctx.mailer.sendTemplate = sendTemplateOrig + } + + // @ts-expect-error + network.pds.ctx.mailer.sendTemplate = sendTemplateMock + + return sendTemplateMock +} - return server +function clientHandler( + req: IncomingMessage, + res: ServerResponse, + next?: (err?: unknown) => void, +): void { + const path = req.url?.split('?')[0].slice(1) || 'index.html' + const file = Object.hasOwn(files, path) ? files[path] : null + + if (file) { + res + .writeHead(200, 'OK', { 'content-type': file.type }) + .end(Buffer.from(file.data, 'base64')) + } else if (next) { + next() + } else { + res + .writeHead(404, 'Not Found', { 'content-type': 'text/plain' }) + .end('Page not found') + } } diff --git a/packages/pds/tests/sequencer.test.ts b/packages/pds/tests/sequencer.test.ts index b8e64fcd08c..4be1e57593b 100644 --- a/packages/pds/tests/sequencer.test.ts +++ b/packages/pds/tests/sequencer.test.ts @@ -7,9 +7,8 @@ import { import { randomStr } from '@atproto/crypto' import { SeedClient, TestNetworkNoAppView } from '@atproto/dev-env' import { readCarWithRoot } from '@atproto/repo' -import { repoPrepare, sequencer } from '../../pds' -import { ids } from '../src/lexicon/lexicons' -import { SeqEvt, Sequencer, formatSeqCommit } from '../src/sequencer' +import { sequencer } from '../../pds' +import { SeqEvt, Sequencer, formatSeqSyncEvt } from '../src/sequencer' import { Outbox } from '../src/sequencer/outbox' import userSeed from './seeds/users' @@ -220,35 +219,27 @@ describe('sequencer', () => { lastSeen = results[0].at(-1)?.seq ?? lastSeen }) - it('root block must be returned in tooBig seq commit', async () => { - // Create good records to exceed the event limit (the current limit is 200 events) - // it creates events completely locally, so it doesn't need to be in the network - const eventsToCreate = 250 - const createPostRecord = () => - repoPrepare.prepareCreate({ - did: sc.dids.alice, - collection: ids.AppBskyFeedPost, - record: { text: 'valid', createdAt: new Date().toISOString() }, - }) - const writesPromises = Array.from( - { length: eventsToCreate }, - createPostRecord, - ) - const writes = await Promise.all(writesPromises) - // just format commit without processing writes - const writeCommit = await network.pds.ctx.actorStore.transact( + it('root block must be returned in sync event', async () => { + const syncData = await network.pds.ctx.actorStore.read( sc.dids.alice, - (store) => store.repo.formatCommit(writes), + async (store) => { + const root = await store.repo.storage.getRootDetailed() + const { blocks } = await store.repo.storage.getBlocks([root.cid]) + return { + cid: root.cid, + rev: root.rev, + blocks, + } + }, ) - const repoSeqInsert = await formatSeqCommit(sc.dids.alice, writeCommit) - - const evt = cborDecode(repoSeqInsert.event) - expect(evt.tooBig).toBe(true) - + const dbEvt = await formatSeqSyncEvt(sc.dids.alice, syncData) + const evt = cborDecode(dbEvt.event) + expect(evt.did).toBe(sc.dids.alice) const car = await readCarWithRoot(evt.blocks) - expect(car.root.toString()).toBe(writeCommit.cid.toString()) + expect(car.root.toString()).toBe(syncData.cid.toString()) // in the case of tooBig, the blocks must contain the root block only expect(car.blocks.size).toBe(1) + expect(car.blocks.has(syncData.cid)).toBeTruthy() }) }) diff --git a/packages/pds/tests/sync/subscribe-repos.test.ts b/packages/pds/tests/sync/subscribe-repos.test.ts index 1fe81c7fa03..b1aad0a1be7 100644 --- a/packages/pds/tests/sync/subscribe-repos.test.ts +++ b/packages/pds/tests/sync/subscribe-repos.test.ts @@ -14,13 +14,12 @@ import * as repo from '@atproto/repo' import { readCar } from '@atproto/repo' import { ErrorFrame, Frame, MessageFrame, byFrame } from '@atproto/xrpc-server' import { AppContext } from '../../src' -import { AccountStatus } from '../../src/account-manager' +import { AccountStatus } from '../../src/account-manager/account-manager' import { Account as AccountEvt, Commit as CommitEvt, - Handle as HandleEvt, Identity as IdentityEvt, - Tombstone as TombstoneEvt, + Sync as SyncEvt, } from '../../src/lexicon/types/com/atproto/sync/subscribeRepos' import basicSeed from '../seeds/basic' @@ -74,10 +73,12 @@ describe('repo subscribe repos', () => { if ( (frame.header.t === '#commit' && (frame.body as CommitEvt).repo === userDid) || - (frame.header.t === '#handle' && - (frame.body as HandleEvt).did === userDid) || - (frame.header.t === '#tombstone' && - (frame.body as TombstoneEvt).did === userDid) + (frame.header.t === '#sync' && + (frame.body as SyncEvt).did === userDid) || + (frame.header.t === '#identity' && + (frame.body as IdentityEvt).did === userDid) || + (frame.header.t === '#account' && + (frame.body as AccountEvt).did === userDid) ) { types.push(frame.body) } @@ -96,6 +97,10 @@ describe('repo subscribe repos', () => { return evts } + const getSyncEvts = (frames: Frame[]): SyncEvt[] => { + return getEventType(frames, '#sync') + } + const getAccountEvts = (frames: Frame[]): AccountEvt[] => { return getEventType(frames, '#account') } @@ -104,14 +109,6 @@ describe('repo subscribe repos', () => { return getEventType(frames, '#identity') } - const getHandleEvts = (frames: Frame[]): HandleEvt[] => { - return getEventType(frames, '#handle') - } - - const getTombstoneEvts = (frames: Frame[]): TombstoneEvt[] => { - return getEventType(frames, '#tombstone') - } - const getCommitEvents = (frames: Frame[]): CommitEvt[] => { return getEventType(frames, '#commit') } @@ -127,13 +124,6 @@ describe('repo subscribe repos', () => { expect(evt.handle).toEqual(handle) } - const verifyHandleEvent = (evt: HandleEvt, did: string, handle: string) => { - expect(typeof evt.seq).toBe('number') - expect(evt.did).toBe(did) - expect(evt.handle).toBe(handle) - expect(typeof evt.time).toBe('string') - } - const verifyAccountEvent = ( evt: AccountEvt, did: string, @@ -147,10 +137,20 @@ describe('repo subscribe repos', () => { expect(evt.status).toBe(status) } - const verifyTombstoneEvent = (evt: unknown, did: string) => { - expect(evt?.['did']).toBe(did) - expect(typeof evt?.['time']).toBe('string') - expect(typeof evt?.['seq']).toBe('number') + const verifySyncEvent = async ( + evt: SyncEvt, + did: string, + commit: CID, + rev: string, + ) => { + expect(typeof evt.seq).toBe('number') + expect(evt.did).toBe(did) + expect(typeof evt.time).toBe('string') + expect(evt.rev).toBe(rev) + const car = await repo.readCarWithRoot(evt.blocks) + expect(car.root.equals(commit)).toBe(true) + expect(car.blocks.size).toBe(1) + expect(car.blocks.has(car.root)).toBe(true) } const verifyCommitEvents = async (frames: Frame[]) => { @@ -329,7 +329,7 @@ describe('repo subscribe repos', () => { } }) - it('syncs handle changes', async () => { + it('syncs handle changes (identity evts)', async () => { await sc.updateHandle(alice, 'alice2.test') await sc.updateHandle(bob, 'bob2.test') await sc.updateHandle(bob, 'bob2.test') // idempotent update re-sends @@ -344,20 +344,14 @@ describe('repo subscribe repos', () => { await verifyCommitEvents(evts) - const handleEvts = getHandleEvts(evts.slice(-6)) - expect(handleEvts.length).toBe(3) - verifyHandleEvent(handleEvts[0], alice, 'alice2.test') - verifyHandleEvent(handleEvts[1], bob, 'bob2.test') - verifyHandleEvent(handleEvts[2], bob, 'bob2.test') - - const identityEvts = getIdentityEvts(evts.slice(-6)) + const identityEvts = getIdentityEvts(evts.slice(-3)) expect(identityEvts.length).toBe(3) verifyIdentityEvent(identityEvts[0], alice, 'alice2.test') verifyIdentityEvent(identityEvts[1], bob, 'bob2.test') verifyIdentityEvent(identityEvts[2], bob, 'bob2.test') }) - it('resends handle events on idempotent updates', async () => { + it('resends identity events on idempotent updates', async () => { const update = sc.updateHandle(bob, 'bob2.test') const ws = new WebSocket( @@ -368,8 +362,8 @@ describe('repo subscribe repos', () => { const evts = await readTillCaughtUp(gen, update) ws.terminate() - const handleEvts = getHandleEvts(evts.slice(-2)) - verifyHandleEvent(handleEvts[0], bob, 'bob2.test') + const identityEvts = getIdentityEvts(evts.slice(-1)) + verifyIdentityEvent(identityEvts[0], bob, 'bob2.test') }) it('syncs account events', async () => { @@ -487,7 +481,35 @@ describe('repo subscribe repos', () => { verifyAccountEvent(accountEvts[3], alice, true) }) - it('syncs tombstones', async () => { + it('emits sync event on account activation', async () => { + await agent.api.com.atproto.server.deactivateAccount( + {}, + { + encoding: 'application/json', + headers: sc.getHeaders(alice), + }, + ) + await agent.api.com.atproto.server.activateAccount(undefined, { + headers: sc.getHeaders(alice), + }) + + const ws = new WebSocket( + `ws://${serverHost}/xrpc/com.atproto.sync.subscribeRepos?cursor=${-1}`, + ) + + const gen = byFrame(ws) + const evts = await readTillCaughtUp(gen) + ws.terminate() + + const syncEvts = getSyncEvts(evts.slice(-1)) + expect(syncEvts.length).toBe(1) + const root = await ctx.actorStore.read(alice, (store) => + store.repo.storage.getRootDetailed(), + ) + await verifySyncEvent(syncEvts[0], alice, root.cid, root.rev) + }) + + it('syncs account deletions (account evt)', async () => { const baddie1 = ( await sc.createAccount('baddie1.test', { email: 'baddie1@test.com', @@ -529,12 +551,7 @@ describe('repo subscribe repos', () => { const evts = await readTillCaughtUp(gen) ws.terminate() - const tombstoneEvts = getTombstoneEvts(evts.slice(-4)) - expect(tombstoneEvts.length).toBe(2) - verifyTombstoneEvent(tombstoneEvts[0], baddie1) - verifyTombstoneEvent(tombstoneEvts[1], baddie2) - - const accountEvts = getAccountEvts(evts.slice(-4)) + const accountEvts = getAccountEvts(evts.slice(-2)) expect(accountEvts.length).toBe(2) verifyAccountEvent(accountEvts[0], baddie1, false, AccountStatus.Deleted) verifyAccountEvent(accountEvts[1], baddie2, false, AccountStatus.Deleted) @@ -571,7 +588,12 @@ describe('repo subscribe repos', () => { const didEvts = getAllEvents(baddie3, evts) expect(didEvts.length).toBe(1) - verifyTombstoneEvent(didEvts[0], baddie3) + verifyAccountEvent( + didEvts[0] as AccountEvt, + baddie3, + false, + AccountStatus.Deleted, + ) }) it('sends info frame on out of date cursor', async () => { diff --git a/packages/repo/CHANGELOG.md b/packages/repo/CHANGELOG.md index fed4b2049df..73e2fc23e6b 100644 --- a/packages/repo/CHANGELOG.md +++ b/packages/repo/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/repo +## 0.7.1 + +### Patch Changes + +- Updated dependencies []: + - @atproto/lexicon@0.4.8 + ## 0.7.0 ### Minor Changes diff --git a/packages/repo/package.json b/packages/repo/package.json index 3664f326391..dcf8d927e1b 100644 --- a/packages/repo/package.json +++ b/packages/repo/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/repo", - "version": "0.7.0", + "version": "0.7.1", "license": "MIT", "description": "atproto repo and MST implementation", "keywords": [ diff --git a/packages/sync/CHANGELOG.md b/packages/sync/CHANGELOG.md index c9c80ff4615..2bfd20eeda8 100644 --- a/packages/sync/CHANGELOG.md +++ b/packages/sync/CHANGELOG.md @@ -1,5 +1,15 @@ # @atproto/sync +## 0.1.16 + +### Patch Changes + +- Updated dependencies [[`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29)]: + - @atproto/syntax@0.3.4 + - @atproto/lexicon@0.4.8 + - @atproto/repo@0.7.1 + - @atproto/xrpc-server@0.7.12 + ## 0.1.15 ### Patch Changes diff --git a/packages/sync/package.json b/packages/sync/package.json index f54fe6285f7..5b349ace53b 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/sync", - "version": "0.1.15", + "version": "0.1.16", "license": "MIT", "description": "atproto sync library", "keywords": [ diff --git a/packages/syntax/CHANGELOG.md b/packages/syntax/CHANGELOG.md index f42bb1a51a3..9094c234e08 100644 --- a/packages/syntax/CHANGELOG.md +++ b/packages/syntax/CHANGELOG.md @@ -1,5 +1,11 @@ # @atproto/syntax +## 0.3.4 + +### Patch Changes + +- [#2945](https://github.com/bluesky-social/atproto/pull/2945) [`850e39843`](https://github.com/bluesky-social/atproto/commit/850e39843cb0ec9ea716675f7568c0c601f45e29) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Deprecate unused classes + ## 0.3.3 ### Patch Changes diff --git a/packages/syntax/package.json b/packages/syntax/package.json index 8333ba6718d..49be4d57252 100644 --- a/packages/syntax/package.json +++ b/packages/syntax/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/syntax", - "version": "0.3.3", + "version": "0.3.4", "license": "MIT", "description": "Validation for atproto identifiers and formats: DID, handle, NSID, AT URI, etc", "keywords": [ diff --git a/packages/syntax/src/handle.ts b/packages/syntax/src/handle.ts index 8c3cacbc88b..35dfb3b84bb 100644 --- a/packages/syntax/src/handle.ts +++ b/packages/syntax/src/handle.ts @@ -115,6 +115,9 @@ export const isValidTld = (handle: string): boolean => { } export class InvalidHandleError extends Error {} +/** @deprecated Never used */ export class ReservedHandleError extends Error {} +/** @deprecated Never used */ export class UnsupportedDomainError extends Error {} +/** @deprecated Never used */ export class DisallowedDomainError extends Error {} diff --git a/packages/xrpc-server/CHANGELOG.md b/packages/xrpc-server/CHANGELOG.md index 0ad68ce2d22..0a936091ad1 100644 --- a/packages/xrpc-server/CHANGELOG.md +++ b/packages/xrpc-server/CHANGELOG.md @@ -1,5 +1,13 @@ # @atproto/xrpc-server +## 0.7.12 + +### Patch Changes + +- Updated dependencies []: + - @atproto/lexicon@0.4.8 + - @atproto/xrpc@0.6.10 + ## 0.7.11 ### Patch Changes diff --git a/packages/xrpc-server/package.json b/packages/xrpc-server/package.json index 83268ef9c23..7d4856fee16 100644 --- a/packages/xrpc-server/package.json +++ b/packages/xrpc-server/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/xrpc-server", - "version": "0.7.11", + "version": "0.7.12", "license": "MIT", "description": "atproto HTTP API (XRPC) server library", "keywords": [ diff --git a/packages/xrpc/CHANGELOG.md b/packages/xrpc/CHANGELOG.md index 91afd349655..ad872adea73 100644 --- a/packages/xrpc/CHANGELOG.md +++ b/packages/xrpc/CHANGELOG.md @@ -1,5 +1,12 @@ # @atproto/xrpc +## 0.6.10 + +### Patch Changes + +- Updated dependencies []: + - @atproto/lexicon@0.4.8 + ## 0.6.9 ### Patch Changes diff --git a/packages/xrpc/package.json b/packages/xrpc/package.json index 1ed4ccf8e5f..88223f737f3 100644 --- a/packages/xrpc/package.json +++ b/packages/xrpc/package.json @@ -1,6 +1,6 @@ { "name": "@atproto/xrpc", - "version": "0.6.9", + "version": "0.6.10", "license": "MIT", "description": "atproto HTTP API (XRPC) client library", "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 287e527df21..eb58950f239 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -535,7 +535,7 @@ importers: devDependencies: '@swc/jest': specifier: ^0.2.24 - version: 0.2.24(@swc/core@1.3.42) + version: 0.2.24(@swc/core@1.11.4) jest: specifier: ^28.1.2 version: 28.1.2(@types/node@18.19.67)(ts-node@10.8.2) @@ -604,10 +604,6 @@ importers: '@atproto-labs/pipe': specifier: workspace:* version: link:../pipe - optionalDependencies: - zod: - specifier: ^3.23.8 - version: 3.23.8 devDependencies: typescript: specifier: ^5.6.3 @@ -944,21 +940,18 @@ importers: '@rollup/plugin-node-resolve': specifier: ^15.2.3 version: 15.2.3(rollup@4.18.0) - '@rollup/plugin-replace': - specifier: ^5.0.5 - version: 5.0.5(rollup@4.18.0) - '@rollup/plugin-terser': - specifier: ^0.4.4 - version: 0.4.4(rollup@4.18.0) - '@rollup/plugin-typescript': - specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(typescript@5.6.3) + '@rollup/plugin-swc': + specifier: ^0.4.0 + version: 0.4.0(@swc/core@1.11.4)(rollup@4.18.0) + '@swc/helpers': + specifier: ^0.5.15 + version: 0.5.15 '@types/react': - specifier: ^18.2.50 - version: 18.3.2 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.2.18 - version: 18.3.0 + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) autoprefixer: specifier: ^10.4.17 version: 10.4.19(postcss@8.4.38) @@ -966,11 +959,11 @@ importers: specifier: ^8.4.33 version: 8.4.38 react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) rollup: specifier: ^4.13.0 version: 4.18.0 @@ -1053,6 +1046,9 @@ importers: '@hapi/accept': specifier: ^6.0.3 version: 6.0.3 + '@hapi/address': + specifier: ^5.1.1 + version: 5.1.1 '@hapi/bourne': specifier: ^3.0.0 version: 3.0.0 @@ -1062,6 +1058,9 @@ importers: cookie: specifier: ^0.6.0 version: 0.6.0 + disposable-email-domains-js: + specifier: ^1.5.0 + version: 1.5.0 forwarded: specifier: ^0.2.0 version: 0.2.0 @@ -1084,21 +1083,42 @@ importers: '@atproto-labs/rollup-plugin-bundle-manifest': specifier: workspace:* version: link:../../internal/rollup-plugin-bundle-manifest + '@hcaptcha/react-hcaptcha': + specifier: ^1.11.2 + version: 1.11.2(react-dom@19.0.0)(react@19.0.0) + '@lingui/cli': + specifier: ^5.2.0 + version: 5.2.0(typescript@5.6.3) + '@lingui/core': + specifier: ^5.2.0 + version: 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/react': + specifier: ^5.2.0 + version: 5.2.0(react@19.0.0) + '@lingui/swc-plugin': + specifier: ^5.4.0 + version: 5.4.0(@lingui/core@5.2.0)(@swc/core@1.10.18) + '@lingui/vite-plugin': + specifier: ^5.2.0 + version: 5.2.0(typescript@5.6.3)(vite@6.2.0) '@rollup/plugin-commonjs': - specifier: ^25.0.7 - version: 25.0.8(rollup@4.18.0) + specifier: ^28.0.2 + version: 28.0.2(rollup@4.18.0) + '@rollup/plugin-dynamic-import-vars': + specifier: ^2.1.5 + version: 2.1.5(rollup@4.18.0) '@rollup/plugin-node-resolve': - specifier: ^15.2.3 - version: 15.2.3(rollup@4.18.0) - '@rollup/plugin-replace': - specifier: ^5.0.5 - version: 5.0.5(rollup@4.18.0) - '@rollup/plugin-terser': - specifier: ^0.4.4 - version: 0.4.4(rollup@4.18.0) - '@rollup/plugin-typescript': - specifier: ^11.1.6 - version: 11.1.6(rollup@4.18.0)(typescript@5.6.3) + specifier: ^16.0.0 + version: 16.0.0(rollup@4.18.0) + '@rollup/plugin-swc': + specifier: ^0.4.0 + version: 0.4.0(@swc/core@1.10.18)(rollup@4.18.0) + '@swc/core': + specifier: ^1.10.18 + version: 1.10.18(@swc/helpers@0.5.15) + '@swc/helpers': + specifier: ^0.5.15 + version: 0.5.15 '@types/cookie': specifier: ^0.6.0 version: 0.6.0 @@ -1109,14 +1129,17 @@ importers: specifier: 1.1.3 version: 1.1.3 '@types/react': - specifier: ^18.2.50 - version: 18.3.2 + specifier: ^19.0.10 + version: 19.0.10 '@types/react-dom': - specifier: ^18.2.18 - version: 18.3.0 + specifier: ^19.0.4 + version: 19.0.4(@types/react@19.0.10) '@types/send': specifier: ^0.17.4 version: 0.17.4 + '@vitejs/plugin-react-swc': + specifier: ^3.8.0 + version: 3.8.0(@swc/helpers@0.5.15)(vite@6.2.0) '@web/rollup-plugin-import-meta-assets': specifier: ^2.2.1 version: 2.2.1(rollup@4.18.0) @@ -1124,14 +1147,17 @@ importers: specifier: ^10.4.17 version: 10.4.19(postcss@8.4.38) postcss: - specifier: ^8.4.33 + specifier: ^8.4.38 version: 8.4.38 react: - specifier: ^18.2.0 - version: 18.3.1 + specifier: ^19.0.0 + version: 19.0.0 react-dom: - specifier: ^18.2.0 - version: 18.3.1(react@18.3.1) + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + react-error-boundary: + specifier: ^5.0.0 + version: 5.0.0(react@19.0.0) rollup: specifier: ^4.13.0 version: 4.18.0 @@ -1139,11 +1165,14 @@ importers: specifier: ^4.0.2 version: 4.0.2(postcss@8.4.38) tailwindcss: - specifier: ^3.4.1 + specifier: ^3.4.3 version: 3.4.3 typescript: specifier: ^5.6.3 version: 5.6.3 + vite: + specifier: ^6.2.0 + version: 6.2.0(@types/node@18.19.67) packages/oauth/oauth-types: dependencies: @@ -1870,7 +1899,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.224.0: @@ -1878,7 +1907,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.257.0: @@ -1886,7 +1915,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/abort-controller@3.374.0: @@ -1902,13 +1931,13 @@ packages: resolution: {integrity: sha512-JeOZ95PW+fJ6bbuqPySYqLqHk1n4+4ueEEraJsiUrPBV0S1ZtyvOGHcnGztKUjr2PYNaiexmpWuvUve9K12HRA==} dependencies: '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/chunked-blob-reader@3.188.0: resolution: {integrity: sha512-zkPRFZZPL3eH+kH86LDYYXImiClA1/sW60zYOjse9Pgka+eDJlvBN6hcYxwDEKjcwATYiSRR1aVQHcfCinlGXg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/client-cloudfront@3.261.0: @@ -2099,7 +2128,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2139,7 +2168,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.257.0 '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2180,7 +2209,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.193.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2220,7 +2249,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2260,7 +2289,7 @@ packages: '@aws-sdk/util-user-agent-browser': 3.257.0 '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2305,7 +2334,7 @@ packages: '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.188.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2349,7 +2378,7 @@ packages: '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2393,7 +2422,7 @@ packages: '@aws-sdk/util-user-agent-node': 3.259.0 '@aws-sdk/util-utf8': 3.254.0 fast-xml-parser: 4.0.11 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2406,7 +2435,7 @@ packages: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-config-provider': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/config-resolver@3.224.0: @@ -2417,7 +2446,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/config-resolver@3.259.0: @@ -2428,7 +2457,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.193.0: @@ -2437,7 +2466,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.224.0: @@ -2446,7 +2475,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-env@3.257.0: @@ -2455,7 +2484,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.193.0: @@ -2466,7 +2495,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/url-parser': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.224.0: @@ -2477,7 +2506,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/url-parser': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-imds@3.259.0: @@ -2488,7 +2517,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/url-parser': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-ini@3.196.0: @@ -2502,7 +2531,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2518,7 +2547,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2535,7 +2564,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2553,7 +2582,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2571,7 +2600,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2589,7 +2618,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2601,7 +2630,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-process@3.224.0: @@ -2611,7 +2640,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-process@3.257.0: @@ -2621,7 +2650,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-sso@3.196.0: @@ -2632,7 +2661,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2646,7 +2675,7 @@ packages: '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/token-providers': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2660,7 +2689,7 @@ packages: '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/token-providers': 3.261.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -2671,7 +2700,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-web-identity@3.224.0: @@ -2680,7 +2709,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/credential-provider-web-identity@3.257.0: @@ -2689,7 +2718,7 @@ packages: dependencies: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-codec@3.224.0: @@ -2698,7 +2727,7 @@ packages: '@aws-crypto/crc32': 2.0.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-hex-encoding': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-browser@3.224.0: @@ -2707,7 +2736,7 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-config-resolver@3.224.0: @@ -2715,7 +2744,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-node@3.224.0: @@ -2724,7 +2753,7 @@ packages: dependencies: '@aws-sdk/eventstream-serde-universal': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/eventstream-serde-universal@3.224.0: @@ -2733,7 +2762,7 @@ packages: dependencies: '@aws-sdk/eventstream-codec': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.193.0: @@ -2743,7 +2772,7 @@ packages: '@aws-sdk/querystring-builder': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-base64-browser': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.224.0: @@ -2753,7 +2782,7 @@ packages: '@aws-sdk/querystring-builder': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/fetch-http-handler@3.257.0: @@ -2763,7 +2792,7 @@ packages: '@aws-sdk/querystring-builder': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/util-base64': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-blob-browser@3.224.0: @@ -2772,7 +2801,7 @@ packages: '@aws-sdk/chunked-blob-reader': 3.188.0 '@aws-sdk/chunked-blob-reader-native': 3.208.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.193.0: @@ -2781,7 +2810,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.224.0: @@ -2790,7 +2819,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-node@3.257.0: @@ -2800,7 +2829,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-buffer-from': 3.208.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/hash-stream-node@3.224.0: @@ -2808,42 +2837,42 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.193.0: resolution: {integrity: sha512-54DCknekLwJAI1os76XJ8XCzfAH7BGkBGtlWk5WCNkZTfj3rf5RUiXz4uoKUMWE1rZmyMDoDDS1PBo+yTVKW5w==} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.224.0: resolution: {integrity: sha512-6huV8LBYQYx84uMhQ2SS7nqEkhTkAufwhKceXnysrcrLDuUmyth09Y7fcFblFIDTr4wTgSI0mf6DKVF4nqYCwQ==} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/invalid-dependency@3.257.0: resolution: {integrity: sha512-T68SAPRNMEhpke0wlxURgogL7q0B8dfqZsSeS20BVR/lksJxLse9+pbmCDxiu1RrXoEIsEwl5rbLN+Hw8BFFYw==} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/is-array-buffer@3.188.0: resolution: {integrity: sha512-n69N4zJZCNd87Rf4NzufPzhactUeM877Y0Tp/F3KiHqGeTnVjYUa4Lv1vLBjqtfjYb2HWT3NKlYn5yzrhaEwiQ==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/is-array-buffer@3.201.0: resolution: {integrity: sha512-UPez5qLh3dNgt0DYnPD/q0mVJY84rA17QE26hVNOW3fAji8W2wrwrxdacWOxyXvlxWsVRcKmr+lay1MDqpAMfg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/lib-storage@3.226.0(@aws-sdk/abort-controller@3.374.0)(@aws-sdk/client-s3@3.224.0): @@ -2869,7 +2898,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-utf8-browser': 3.188.0 '@aws-sdk/util-utf8-node': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-bucket-endpoint@3.224.0: @@ -2880,7 +2909,7 @@ packages: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 '@aws-sdk/util-config-provider': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.193.0: @@ -2889,7 +2918,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.224.0: @@ -2898,7 +2927,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-content-length@3.257.0: @@ -2907,7 +2936,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.193.0: @@ -2921,7 +2950,7 @@ packages: '@aws-sdk/url-parser': 3.193.0 '@aws-sdk/util-config-provider': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.224.0: @@ -2935,7 +2964,7 @@ packages: '@aws-sdk/url-parser': 3.224.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.226.0: @@ -2949,7 +2978,7 @@ packages: '@aws-sdk/url-parser': 3.226.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-endpoint@3.257.0: @@ -2963,7 +2992,7 @@ packages: '@aws-sdk/url-parser': 3.257.0 '@aws-sdk/util-config-provider': 3.208.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-expect-continue@3.224.0: @@ -2972,7 +3001,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-flexible-checksums@3.224.0: @@ -2984,7 +3013,7 @@ packages: '@aws-sdk/is-array-buffer': 3.201.0 '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.193.0: @@ -2993,7 +3022,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.224.0: @@ -3002,7 +3031,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-host-header@3.257.0: @@ -3011,7 +3040,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-location-constraint@3.224.0: @@ -3019,7 +3048,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.193.0: @@ -3027,7 +3056,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.224.0: @@ -3035,7 +3064,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-logger@3.257.0: @@ -3043,7 +3072,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.193.0: @@ -3052,7 +3081,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.224.0: @@ -3061,7 +3090,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-recursion-detection@3.257.0: @@ -3070,7 +3099,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-retry@3.193.0: @@ -3081,7 +3110,7 @@ packages: '@aws-sdk/service-error-classification': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3093,7 +3122,7 @@ packages: '@aws-sdk/service-error-classification': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3106,7 +3135,7 @@ packages: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-middleware': 3.257.0 '@aws-sdk/util-retry': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 uuid: 8.3.2 dev: false @@ -3118,7 +3147,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.193.0: @@ -3130,7 +3159,7 @@ packages: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/signature-v4': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.224.0: @@ -3142,7 +3171,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-sdk-sts@3.257.0: @@ -3154,7 +3183,7 @@ packages: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/signature-v4': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.193.0: @@ -3162,7 +3191,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.224.0: @@ -3170,7 +3199,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.226.0: @@ -3178,7 +3207,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-serde@3.257.0: @@ -3186,7 +3215,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.193.0: @@ -3198,7 +3227,7 @@ packages: '@aws-sdk/signature-v4': 3.193.0 '@aws-sdk/types': 3.193.0 '@aws-sdk/util-middleware': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.224.0: @@ -3210,7 +3239,7 @@ packages: '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-middleware': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-signing@3.257.0: @@ -3222,7 +3251,7 @@ packages: '@aws-sdk/signature-v4': 3.257.0 '@aws-sdk/types': 3.257.0 '@aws-sdk/util-middleware': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-ssec@3.224.0: @@ -3230,35 +3259,35 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.193.0: resolution: {integrity: sha512-Ix5d7gE6bZwFNIVf0dGnjYuymz1gjitNoAZDPpv1nEZlUMek/jcno5lmzWFzUZXY/azpbIyaPwq/wm/c69au5A==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.224.0: resolution: {integrity: sha512-8mBrc3nj4h6FnDWnxbjfFXUPr/7UIAaGAG15D27Z/KNFnMjOqNTtpkbcoh3QQHRLX3PjTuvzT5WCqXmgD2/oiw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.226.0: resolution: {integrity: sha512-85wF29LvPvpoed60fZGDYLwv1Zpd/cM0C22WSSFPw1SSJeqO4gtFYyCg2squfT3KI6kF43IIkOCJ+L7GtryPug==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-stack@3.257.0: resolution: {integrity: sha512-awg2F0SvwACBaw4HIObK8pQGfSqAc4Vy+YFzWSfZNVC35oRO6RsRdKHVU99lRC0LrT2Ptmfghl2DMPSrRDbvlQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.193.0: @@ -3267,7 +3296,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.224.0: @@ -3276,7 +3305,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/middleware-user-agent@3.257.0: @@ -3285,7 +3314,7 @@ packages: dependencies: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.193.0: @@ -3295,7 +3324,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/shared-ini-file-loader': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.224.0: @@ -3305,7 +3334,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-config-provider@3.259.0: @@ -3315,7 +3344,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.193.0: @@ -3326,7 +3355,7 @@ packages: '@aws-sdk/protocol-http': 3.193.0 '@aws-sdk/querystring-builder': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.224.0: @@ -3337,7 +3366,7 @@ packages: '@aws-sdk/protocol-http': 3.224.0 '@aws-sdk/querystring-builder': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/node-http-handler@3.257.0: @@ -3348,7 +3377,7 @@ packages: '@aws-sdk/protocol-http': 3.257.0 '@aws-sdk/querystring-builder': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.193.0: @@ -3356,7 +3385,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.224.0: @@ -3364,7 +3393,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/property-provider@3.257.0: @@ -3372,7 +3401,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.193.0: @@ -3380,7 +3409,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.224.0: @@ -3388,7 +3417,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.226.0: @@ -3396,7 +3425,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/protocol-http@3.257.0: @@ -3404,7 +3433,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.193.0: @@ -3413,7 +3442,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 '@aws-sdk/util-uri-escape': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.224.0: @@ -3422,7 +3451,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-builder@3.257.0: @@ -3431,7 +3460,7 @@ packages: dependencies: '@aws-sdk/types': 3.257.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.193.0: @@ -3439,7 +3468,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.224.0: @@ -3447,7 +3476,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.226.0: @@ -3455,7 +3484,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/querystring-parser@3.257.0: @@ -3463,7 +3492,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/service-error-classification@3.193.0: @@ -3486,7 +3515,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/shared-ini-file-loader@3.224.0: @@ -3494,7 +3523,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/shared-ini-file-loader@3.257.0: @@ -3502,7 +3531,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4-multi-region@3.224.0: @@ -3518,7 +3547,7 @@ packages: '@aws-sdk/signature-v4': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-arn-parser': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.193.0: @@ -3530,7 +3559,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.188.0 '@aws-sdk/util-middleware': 3.193.0 '@aws-sdk/util-uri-escape': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.224.0: @@ -3542,7 +3571,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-middleware': 3.224.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.226.0: @@ -3554,7 +3583,7 @@ packages: '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-middleware': 3.226.0 '@aws-sdk/util-uri-escape': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/signature-v4@3.257.0: @@ -3567,7 +3596,7 @@ packages: '@aws-sdk/util-middleware': 3.257.0 '@aws-sdk/util-uri-escape': 3.201.0 '@aws-sdk/util-utf8': 3.254.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.193.0: @@ -3576,7 +3605,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.224.0: @@ -3585,7 +3614,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.226.0: @@ -3594,7 +3623,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.226.0 '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/smithy-client@3.261.0: @@ -3603,7 +3632,7 @@ packages: dependencies: '@aws-sdk/middleware-stack': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/token-providers@3.224.0: @@ -3614,7 +3643,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/shared-ini-file-loader': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -3627,7 +3656,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/shared-ini-file-loader': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 transitivePeerDependencies: - aws-crt dev: false @@ -3646,14 +3675,14 @@ packages: resolution: {integrity: sha512-MmmNHrWeO4man7wpOwrAhXlevqtOV9ZLcH4RhnG5LmRce0RFOApx24HoKENfFCcOyCm5LQBlsXCqi0dZWDWU0A==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/types@3.257.0: resolution: {integrity: sha512-LmqXuBQBGeaGi/3Rp7XiEX1B5IPO2UUfBVvu0wwGqVsmstT0SbOVDZGPmxygACbm64n+PRx3uTSDefRfoiWYZg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.193.0: @@ -3661,7 +3690,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.224.0: @@ -3669,7 +3698,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.226.0: @@ -3677,7 +3706,7 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.226.0 '@aws-sdk/types': 3.226.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/url-parser@3.257.0: @@ -3685,20 +3714,20 @@ packages: dependencies: '@aws-sdk/querystring-parser': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-arn-parser@3.208.0: resolution: {integrity: sha512-QV4af+kscova9dv4VuHOgH8wEr/IIYHDGcnyVtkUEqahCejWr1Kuk+SBK0xMwnZY5LSycOtQ8aeqHOn9qOjZtA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64-browser@3.188.0: resolution: {integrity: sha512-qlH+5NZBLiyKziL335BEPedYxX6j+p7KFRWXvDQox9S+s+gLCayednpK+fteOhBenCcR9fUZOVuAPScy1I8qCg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64-node@3.188.0: @@ -3706,7 +3735,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-base64@3.208.0: @@ -3714,27 +3743,27 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-browser@3.188.0: resolution: {integrity: sha512-8VpnwFWXhnZ/iRSl9mTf+VKOX9wDE8QtN4bj9pBfxwf90H1X7E8T6NkiZD3k+HubYf2J94e7DbeHs7fuCPW5Qg==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-node@3.188.0: resolution: {integrity: sha512-XwqP3vxk60MKp4YDdvDeCD6BPOiG2e+/Ou4AofZOy5/toB6NKz2pFNibQIUg2+jc7mPMnGnvOW3MQEgSJ+gu/Q==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-body-length-node@3.208.0: resolution: {integrity: sha512-3zj50e5g7t/MQf53SsuuSf0hEELzMtD8RX8C76f12OSRo2Bca4FLLYHe0TZbxcfQHom8/hOaeZEyTyMogMglqg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-buffer-from@3.188.0: @@ -3742,7 +3771,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/is-array-buffer': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-buffer-from@3.208.0: @@ -3750,21 +3779,21 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/is-array-buffer': 3.201.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-config-provider@3.188.0: resolution: {integrity: sha512-LBA7tLbi7v4uvbOJhSnjJrxbcRifKK/1ZVK94JTV2MNSCCyNkFotyEI5UWDl10YKriTIUyf7o5cakpiDZ3O4xg==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-config-provider@3.208.0: resolution: {integrity: sha512-DSRqwrERUsT34ug+anlMBIFooBEGwM8GejC7q00Y/9IPrQy50KnG5PW2NiTjuLKNi7pdEOlwTSEocJE15eDZIg==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.193.0: @@ -3774,7 +3803,7 @@ packages: '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.224.0: @@ -3784,7 +3813,7 @@ packages: '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-browser@3.261.0: @@ -3794,7 +3823,7 @@ packages: '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.193.0: @@ -3806,7 +3835,7 @@ packages: '@aws-sdk/node-config-provider': 3.193.0 '@aws-sdk/property-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.224.0: @@ -3818,7 +3847,7 @@ packages: '@aws-sdk/node-config-provider': 3.224.0 '@aws-sdk/property-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-defaults-mode-node@3.261.0: @@ -3830,7 +3859,7 @@ packages: '@aws-sdk/node-config-provider': 3.259.0 '@aws-sdk/property-provider': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.196.0: @@ -3838,7 +3867,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.224.0: @@ -3846,7 +3875,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-endpoints@3.257.0: @@ -3854,56 +3883,56 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-hex-encoding@3.188.0: resolution: {integrity: sha512-QyWovTtjQ2RYxqVM+STPh65owSqzuXURnfoof778spyX4iQ4z46wOge1YV2ZtwS8w5LWd9eeVvDrLu5POPYOnA==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-hex-encoding@3.201.0: resolution: {integrity: sha512-7t1vR1pVxKx0motd3X9rI3m/xNp78p3sHtP5yo4NP4ARpxyJ0fokBomY8ScaH2D/B+U5o9ARxldJUdMqyBlJcA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-locate-window@3.310.0: resolution: {integrity: sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.193.0: resolution: {integrity: sha512-+aC6pmkcGgpxaMWCH/FXTsGWl2W342oQGs1OYKGi+W8z9UguXrqamWjdkdMqgunvj9qOEG2KBMKz1FWFFZlUyA==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.224.0: resolution: {integrity: sha512-yA20k9sJdFgs7buVilWExUSJ/Ecr5UJRNQlmgzIpBo9kh5x/N8WyB4kN5MQw5UAA1UZ+j3jmA9+YLFT/mbX3IQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.226.0: resolution: {integrity: sha512-B96CQnwX4gRvQdaQkdUtqvDPkrptV5+va6FVeJOocU/DbSYMAScLxtR3peMS8cnlOT6nL1Eoa42OI9AfZz1VwQ==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-middleware@3.257.0: resolution: {integrity: sha512-F9ieon8B8eGVs5tyZtAIG3DZEObDvujkspho0qRbUTHUosM0ylJLsMU800fmC/uRHLRrZvb/RSp59+kNDwSAMw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-retry@3.257.0: @@ -3911,7 +3940,7 @@ packages: engines: {node: '>= 14.0.0'} dependencies: '@aws-sdk/service-error-classification': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-stream-browser@3.224.0: @@ -3922,7 +3951,7 @@ packages: '@aws-sdk/util-base64': 3.208.0 '@aws-sdk/util-hex-encoding': 3.201.0 '@aws-sdk/util-utf8-browser': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-stream-node@3.224.0: @@ -3932,21 +3961,21 @@ packages: '@aws-sdk/node-http-handler': 3.224.0 '@aws-sdk/types': 3.224.0 '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-uri-escape@3.188.0: resolution: {integrity: sha512-4Y6AYZMT483Tiuq8dxz5WHIiPNdSFPGrl6tRTo2Oi2FcwypwmFhqgEGcqxeXDUJktvaCBxeA08DLr/AemVhPCg==} engines: {node: '>= 12.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-uri-escape@3.201.0: resolution: {integrity: sha512-TeTWbGx4LU2c5rx0obHeDFeO9HvwYwQtMh1yniBz00pQb6Qt6YVOETVQikRZ+XRQwEyCg/dA375UplIpiy54mA==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.193.0: @@ -3954,7 +3983,7 @@ packages: dependencies: '@aws-sdk/types': 3.193.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.224.0: @@ -3962,7 +3991,7 @@ packages: dependencies: '@aws-sdk/types': 3.224.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-browser@3.257.0: @@ -3970,7 +3999,7 @@ packages: dependencies: '@aws-sdk/types': 3.257.0 bowser: 2.11.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.193.0: @@ -3984,7 +4013,7 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.193.0 '@aws-sdk/types': 3.193.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.224.0: @@ -3998,7 +4027,7 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-user-agent-node@3.259.0: @@ -4012,19 +4041,19 @@ packages: dependencies: '@aws-sdk/node-config-provider': 3.259.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-browser@3.188.0: resolution: {integrity: sha512-jt627x0+jE+Ydr9NwkFstg3cUvgWh56qdaqAMDsqgRlKD21md/6G226z/Qxl7lb1VEW2LlmCx43ai/37Qwcj2Q==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-browser@3.259.0: resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-node@3.188.0: @@ -4032,7 +4061,7 @@ packages: engines: {node: '>= 12.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.188.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8-node@3.208.0: @@ -4040,7 +4069,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-utf8@3.254.0: @@ -4048,7 +4077,7 @@ packages: engines: {node: '>=14.0.0'} dependencies: '@aws-sdk/util-buffer-from': 3.208.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-waiter@3.224.0: @@ -4057,7 +4086,7 @@ packages: dependencies: '@aws-sdk/abort-controller': 3.224.0 '@aws-sdk/types': 3.224.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/util-waiter@3.257.0: @@ -4066,14 +4095,14 @@ packages: dependencies: '@aws-sdk/abort-controller': 3.257.0 '@aws-sdk/types': 3.257.0 - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@aws-sdk/xml-builder@3.201.0: resolution: {integrity: sha512-brRdB1wwMgjWEnOQsv7zSUhIQuh7DEicrfslAqHop4S4FtSI3GQAShpQqgOpMTNFYcpaWKmE/Y1MJmNY7xLCnw==} engines: {node: '>=14.0.0'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: false /@babel/code-frame@7.22.10: @@ -4084,11 +4113,25 @@ packages: chalk: 2.4.2 dev: true + /@babel/code-frame@7.26.2: + resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.25.9 + js-tokens: 4.0.0 + picocolors: 1.1.1 + dev: true + /@babel/compat-data@7.22.9: resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} engines: {node: '>=6.9.0'} dev: true + /@babel/compat-data@7.26.8: + resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/core@7.18.6: resolution: {integrity: sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ==} engines: {node: '>=6.9.0'} @@ -4112,6 +4155,29 @@ packages: - supports-color dev: true + /@babel/core@7.26.9: + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + engines: {node: '>=6.9.0'} + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.9 + '@babel/helper-compilation-targets': 7.26.5 + '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) + '@babel/helpers': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/generator@7.22.10: resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} engines: {node: '>=6.9.0'} @@ -4122,6 +4188,17 @@ packages: jsesc: 2.5.2 dev: true + /@babel/generator@7.26.9: + resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 3.1.0 + dev: true + /@babel/helper-compilation-targets@7.22.10: resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} engines: {node: '>=6.9.0'} @@ -4133,6 +4210,17 @@ packages: semver: 6.3.1 dev: true + /@babel/helper-compilation-targets@7.26.5: + resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/compat-data': 7.26.8 + '@babel/helper-validator-option': 7.25.9 + browserslist: 4.24.4 + lru-cache: 5.1.1 + semver: 6.3.1 + dev: true + /@babel/helper-environment-visitor@7.22.5: resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} engines: {node: '>=6.9.0'} @@ -4160,6 +4248,16 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/helper-module-imports@7.25.9: + resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/traverse': 7.26.9 + '@babel/types': 7.26.9 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-module-transforms@7.22.9(@babel/core@7.18.6): resolution: {integrity: sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==} engines: {node: '>=6.9.0'} @@ -4174,6 +4272,20 @@ packages: '@babel/helper-validator-identifier': 7.22.5 dev: true + /@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9): + resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + dependencies: + '@babel/core': 7.26.9 + '@babel/helper-module-imports': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + '@babel/traverse': 7.26.9 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} engines: {node: '>=6.9.0'} @@ -4198,16 +4310,31 @@ packages: engines: {node: '>=6.9.0'} dev: true + /@babel/helper-string-parser@7.25.9: + resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-identifier@7.25.9: + resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helper-validator-option@7.22.5: resolution: {integrity: sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==} engines: {node: '>=6.9.0'} dev: true + /@babel/helper-validator-option@7.25.9: + resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + engines: {node: '>=6.9.0'} + dev: true + /@babel/helpers@7.22.10: resolution: {integrity: sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==} engines: {node: '>=6.9.0'} @@ -4219,6 +4346,14 @@ packages: - supports-color dev: true + /@babel/helpers@7.26.9: + resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 + dev: true + /@babel/highlight@7.22.10: resolution: {integrity: sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==} engines: {node: '>=6.9.0'} @@ -4236,6 +4371,14 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/parser@7.26.9: + resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.26.9 + dev: true + /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.18.6): resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: @@ -4371,6 +4514,15 @@ packages: '@babel/types': 7.22.10 dev: true + /@babel/template@7.26.9: + resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + dev: true + /@babel/traverse@7.22.10: resolution: {integrity: sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==} engines: {node: '>=6.9.0'} @@ -4389,6 +4541,21 @@ packages: - supports-color dev: true + /@babel/traverse@7.26.9: + resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/generator': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/template': 7.26.9 + '@babel/types': 7.26.9 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + dev: true + /@babel/types@7.22.10: resolution: {integrity: sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==} engines: {node: '>=6.9.0'} @@ -4398,6 +4565,14 @@ packages: to-fast-properties: 2.0.0 dev: true + /@babel/types@7.26.9: + resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-string-parser': 7.25.9 + '@babel/helper-validator-identifier': 7.25.9 + dev: true + /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true @@ -4957,34 +5132,466 @@ packages: dev: false optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 + /@esbuild/aix-ppc64@0.21.5: + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true dev: true + optional: true - /@eslint-community/eslint-utils@4.4.1(eslint@8.57.0): - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 + /@esbuild/aix-ppc64@0.25.0: + resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + requiresBuild: true dev: true + optional: true - /@eslint-community/regexpp@4.10.0: - resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@esbuild/android-arm64@0.21.5: + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true dev: true + optional: true - /@eslint-community/regexpp@4.12.1: - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + /@esbuild/android-arm64@0.25.0: + resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.21.5: + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.25.0: + resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.21.5: + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.25.0: + resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.21.5: + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.25.0: + resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.21.5: + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.25.0: + resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.21.5: + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.25.0: + resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.21.5: + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.25.0: + resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.21.5: + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.25.0: + resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.21.5: + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.25.0: + resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.21.5: + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.25.0: + resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.21.5: + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.25.0: + resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.21.5: + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.25.0: + resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.21.5: + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.25.0: + resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.21.5: + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.25.0: + resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.21.5: + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.25.0: + resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.21.5: + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.25.0: + resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-arm64@0.25.0: + resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.21.5: + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.25.0: + resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-arm64@0.25.0: + resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.21.5: + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.25.0: + resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.21.5: + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.25.0: + resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.21.5: + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.25.0: + resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.21.5: + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.25.0: + resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.21.5: + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.25.0: + resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/eslint-utils@4.4.1(eslint@8.57.0): + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.10.0: + resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint-community/regexpp@4.12.1: + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} dev: true /@eslint/eslintrc@2.1.4: @@ -5073,6 +5680,22 @@ packages: resolution: {integrity: sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==} dev: false + /@hcaptcha/loader@1.2.4: + resolution: {integrity: sha512-3MNrIy/nWBfyVVvMPBKdKrX7BeadgiimW0AL/a/8TohNtJqxoySKgTJEXOQvYwlHemQpUzFrIsK74ody7JiMYw==} + dev: true + + /@hcaptcha/react-hcaptcha@1.11.2(react-dom@19.0.0)(react@19.0.0): + resolution: {integrity: sha512-F6+aZknrpTHoAhKIP80wWNJI5nhOJg0qyazM3pAw0bCyvVLSsveWeZyTK7W8EhO6nvovTE061gehlC+bmS/vMw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + dependencies: + '@babel/runtime': 7.22.10 + '@hcaptcha/loader': 1.2.4 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + dev: true + /@humanwhocodes/config-array@0.11.14: resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -5474,6 +6097,13 @@ packages: '@sinclair/typebox': 0.24.51 dev: true + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + /@jest/source-map@28.1.2: resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -5533,15 +6163,27 @@ packages: '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 '@types/node': 18.19.67 - '@types/yargs': 16.0.5 + '@types/yargs': 16.0.5 + chalk: 4.1.2 + dev: true + + /@jest/types@28.1.3: + resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} + engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + dependencies: + '@jest/schemas': 28.1.3 + '@types/istanbul-lib-coverage': 2.0.4 + '@types/istanbul-reports': 3.0.1 + '@types/node': 18.19.67 + '@types/yargs': 17.0.24 chalk: 4.1.2 dev: true - /@jest/types@28.1.3: - resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 28.1.3 + '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 '@types/node': 18.19.67 @@ -5582,13 +6224,6 @@ packages: engines: {node: '>=6.0.0'} dev: true - /@jridgewell/source-map@0.3.6: - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} dev: true @@ -5618,6 +6253,174 @@ packages: resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==} dev: false + /@lingui/babel-plugin-extract-messages@5.2.0: + resolution: {integrity: sha512-hQ6tFK72ZXX2813PU9thJbnwJ+SjSrfR3/tt4aqHJcOUdrb67wMVY/0xiUe+vb5y6kVZjZ4oPqdgCfGZ2jWBEw==} + engines: {node: '>=20.0.0'} + dev: true + + /@lingui/babel-plugin-lingui-macro@5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3): + resolution: {integrity: sha512-IEpEfKW2WoGiK30dbovwXaPj69dKUP+GEAk00/6KUMB0sonaBWO4NO3Bj9G6NSdA6fB1lm9BtvuPqJQ2DvjF5g==} + engines: {node: '>=20.0.0'} + peerDependencies: + babel-plugin-macros: 2 || 3 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dependencies: + '@babel/core': 7.26.9 + '@babel/runtime': 7.22.10 + '@babel/types': 7.26.9 + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@lingui/cli@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-SLMPi9VMNAmhKRGt3HCGIZVHHmxfAcb7zNK9qwrEhlvcwxNmtsPtLb4iJvKy/VpdCQYm7C6D34tFjyVjUZ4ROg==} + engines: {node: '>=20.0.0'} + hasBin: true + dependencies: + '@babel/core': 7.26.9 + '@babel/generator': 7.26.9 + '@babel/parser': 7.26.9 + '@babel/runtime': 7.22.10 + '@babel/types': 7.26.9 + '@lingui/babel-plugin-extract-messages': 5.2.0 + '@lingui/babel-plugin-lingui-macro': 5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3) + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@lingui/format-po': 5.2.0(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + chalk: 4.1.2 + chokidar: 3.5.1 + cli-table: 0.3.11 + commander: 10.0.1 + convert-source-map: 2.0.0 + date-fns: 3.6.0 + esbuild: 0.21.5 + glob: 11.0.1 + inquirer: 7.3.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + ora: 5.4.1 + pathe: 1.1.2 + pkg-up: 3.1.0 + pofile: 1.1.4 + pseudolocale: 2.1.0 + source-map: 0.8.0-beta.0 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@lingui/conf@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-3biQJxGntCP+EnOe9jjlquGCBfk6ogq+I8ZduHwmBceY5aQ0OR7V23ItDrMz0NBy8dFNk5YoeHun3CYKYOS/Jg==} + engines: {node: '>=20.0.0'} + dependencies: + '@babel/runtime': 7.22.10 + chalk: 4.1.2 + cosmiconfig: 8.3.6(typescript@5.6.3) + jest-validate: 29.7.0 + jiti: 1.21.0 + lodash.get: 4.4.2 + transitivePeerDependencies: + - typescript + dev: true + + /@lingui/core@5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0): + resolution: {integrity: sha512-cz35uKDxIGb/CPvgwn7BM/QYpxtARmQm7n+mHUoNJdNKSrg9R7vKkLRG7k9dukZwix2Mdjh+2dPIJnAkor2CiA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@lingui/babel-plugin-lingui-macro': 5.2.0 + babel-plugin-macros: 2 || 3 + peerDependenciesMeta: + '@lingui/babel-plugin-lingui-macro': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/runtime': 7.22.10 + '@lingui/babel-plugin-lingui-macro': 5.2.0(babel-plugin-macros@3.1.0)(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + babel-plugin-macros: 3.1.0 + unraw: 3.0.0 + dev: true + + /@lingui/format-po@5.2.0(typescript@5.6.3): + resolution: {integrity: sha512-viUQaoRa8UxSghayTY7xjtwXbfXIVdlM8C4HsxmozQnl5TXnPVEwlaPYds3sdJ8PmQGcYCm35r8EsmuKBoWYDQ==} + engines: {node: '>=20.0.0'} + dependencies: + '@lingui/conf': 5.2.0(typescript@5.6.3) + '@lingui/message-utils': 5.2.0 + date-fns: 3.6.0 + pofile: 1.1.4 + transitivePeerDependencies: + - typescript + dev: true + + /@lingui/message-utils@5.2.0: + resolution: {integrity: sha512-qJFKNc1b7SRX6y5ywtA1x+2/gaY22e09hjC6fiDvDpAFdEguI4qAJGmBmqlAZG/kcokR0tmMpo9zYUF8jjcHEA==} + engines: {node: '>=20.0.0'} + dependencies: + '@messageformat/parser': 5.1.1 + js-sha256: 0.10.1 + dev: true + bundledDependencies: + - '@messageformat/date-skeleton' + + /@lingui/react@5.2.0(react@19.0.0): + resolution: {integrity: sha512-Ok9ZsA3hPPzeTXpp1woFk0Bgqv6shMB8AzbSQCixudLdbuAPkBu/EKlwTGBs0yYlq5cUXeDYrKKHF1R5BcX5HA==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@lingui/babel-plugin-lingui-macro': 5.2.0 + babel-plugin-macros: 2 || 3 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@lingui/babel-plugin-lingui-macro': + optional: true + babel-plugin-macros: + optional: true + dependencies: + '@babel/runtime': 7.22.10 + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + react: 19.0.0 + dev: true + + /@lingui/swc-plugin@5.4.0(@lingui/core@5.2.0)(@swc/core@1.10.18): + resolution: {integrity: sha512-xn3na6sA+wQPhw6oxIZvDvL51XkLZtyCMSdrUZDMf5/j18JJI8QRewlfn13Ai+Ze8qw8tCbCKnry7a6Dlq38yw==} + peerDependencies: + '@lingui/core': '5' + '@swc/core': '*' + next: '*' + peerDependenciesMeta: + '@swc/core': + optional: true + next: + optional: true + dependencies: + '@lingui/core': 5.2.0(@lingui/babel-plugin-lingui-macro@5.2.0)(babel-plugin-macros@3.1.0) + '@swc/core': 1.10.18(@swc/helpers@0.5.15) + dev: true + + /@lingui/vite-plugin@5.2.0(typescript@5.6.3)(vite@6.2.0): + resolution: {integrity: sha512-jMpf6JJY1s3t4eFRBseTyuQNxy6ERRwg+uLi8EZ/qcaQgQW+GK6qWX/Qg5xQ8k1mJpaP6ihanMQMrkS6d5oR/A==} + engines: {node: '>=20.0.0'} + peerDependencies: + vite: ^3 || ^4 || ^5.0.9 || ^6 + dependencies: + '@lingui/cli': 5.2.0(typescript@5.6.3) + '@lingui/conf': 5.2.0(typescript@5.6.3) + vite: 6.2.0(@types/node@18.19.67) + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} dependencies: @@ -5638,6 +6441,12 @@ packages: read-yaml-file: 1.1.0 dev: true + /@messageformat/parser@5.1.1: + resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} + dependencies: + moo: 0.5.2 + dev: true + /@noble/curves@1.8.0: resolution: {integrity: sha512-j84kjAbzEnQHaSIhRPUmB3/eVXu2k3dKPl2LOrR8fSOIL+89U+7lV117EWHtq/GHM3ReGHM46iRBdZfpc4HRUQ==} engines: {node: ^14.21.3 || >=16} @@ -5839,8 +6648,27 @@ packages: rollup: 4.18.0 dev: true - /@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.18.0): - resolution: {integrity: sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==} + /@rollup/plugin-commonjs@28.0.2(rollup@4.18.0): + resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.3(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.10 + picomatch: 4.0.2 + rollup: 4.18.0 + dev: true + + /@rollup/plugin-dynamic-import-vars@2.1.5(rollup@4.18.0): + resolution: {integrity: sha512-Mymi24fd9hlRifdZV/jYIFj1dn99F34imiYu3KzlAcgBcRi3i9SucgW/VRo5SQ9K4NuQ7dCep6pFWgNyhRdFHQ==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -5851,7 +6679,7 @@ packages: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) astring: 1.8.6 estree-walker: 2.0.2 - fast-glob: 3.3.1 + fast-glob: 3.3.2 magic-string: 0.30.10 rollup: 4.18.0 dev: true @@ -5899,52 +6727,53 @@ packages: rollup: 4.18.0 dev: true - /@rollup/plugin-replace@5.0.5(rollup@4.18.0): - resolution: {integrity: sha512-rYO4fOi8lMaTg/z5Jb+hKnrHHVn8j2lwkqwyS4kTRhKyWOLf2wST2sWXr4WzWiTcoHTp2sTjqUbqIj2E39slKQ==} + /@rollup/plugin-node-resolve@16.0.0(rollup@4.18.0): + resolution: {integrity: sha512-0FPvAeVUT/zdWoO0jnb/V5BlBsUSNfkIOtFHzMO4H9MOklrmQFY6FduVHKucNb/aTFxvnGhj4MNj/T1oNdDfNg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + rollup: ^2.78.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) - magic-string: 0.30.10 + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.4 rollup: 4.18.0 dev: true - /@rollup/plugin-terser@0.4.4(rollup@4.18.0): - resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + /@rollup/plugin-swc@0.4.0(@swc/core@1.10.18)(rollup@4.18.0): + resolution: {integrity: sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^2.0.0||^3.0.0||^4.0.0 + '@swc/core': ^1.3.0 + rollup: ^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true dependencies: + '@rollup/pluginutils': 5.1.0(rollup@4.18.0) + '@swc/core': 1.10.18(@swc/helpers@0.5.15) rollup: 4.18.0 - serialize-javascript: 6.0.2 smob: 1.5.0 - terser: 5.31.0 dev: true - /@rollup/plugin-typescript@11.1.6(rollup@4.18.0)(typescript@5.6.3): - resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} + /@rollup/plugin-swc@0.4.0(@swc/core@1.11.4)(rollup@4.18.0): + resolution: {integrity: sha512-oAtqXa8rOl7BOK1Rz3rRxI+LIL53S9SqO2KSq2UUUzWgOgXg6492Jh5mL2mv/f9cpit8zFWdwILuVeozZ0C8mg==} engines: {node: '>=14.0.0'} peerDependencies: - rollup: ^2.14.0||^3.0.0||^4.0.0 - tslib: '*' - typescript: '>=3.7.0' + '@swc/core': ^1.3.0 + rollup: ^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - tslib: - optional: true dependencies: '@rollup/pluginutils': 5.1.0(rollup@4.18.0) - resolve: 1.22.4 + '@swc/core': 1.11.4(@swc/helpers@0.5.15) rollup: 4.18.0 - typescript: 5.6.3 + smob: 1.5.0 dev: true /@rollup/pluginutils@5.1.0(rollup@4.18.0): @@ -5970,6 +6799,14 @@ packages: dev: true optional: true + /@rollup/rollup-android-arm-eabi@4.34.9: + resolution: {integrity: sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-android-arm64@4.18.0: resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} cpu: [arm64] @@ -5978,6 +6815,14 @@ packages: dev: true optional: true + /@rollup/rollup-android-arm64@4.34.9: + resolution: {integrity: sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-arm64@4.18.0: resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} cpu: [arm64] @@ -5986,6 +6831,14 @@ packages: dev: true optional: true + /@rollup/rollup-darwin-arm64@4.34.9: + resolution: {integrity: sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-darwin-x64@4.18.0: resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} cpu: [x64] @@ -5994,6 +6847,30 @@ packages: dev: true optional: true + /@rollup/rollup-darwin-x64@4.34.9: + resolution: {integrity: sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-arm64@4.34.9: + resolution: {integrity: sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-freebsd-x64@4.34.9: + resolution: {integrity: sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.18.0: resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} cpu: [arm] @@ -6002,6 +6879,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm-gnueabihf@4.34.9: + resolution: {integrity: sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm-musleabihf@4.18.0: resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} cpu: [arm] @@ -6010,6 +6895,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm-musleabihf@4.34.9: + resolution: {integrity: sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-gnu@4.18.0: resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} cpu: [arm64] @@ -6018,6 +6911,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm64-gnu@4.34.9: + resolution: {integrity: sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-arm64-musl@4.18.0: resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} cpu: [arm64] @@ -6026,6 +6927,22 @@ packages: dev: true optional: true + /@rollup/rollup-linux-arm64-musl@4.34.9: + resolution: {integrity: sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-loongarch64-gnu@4.34.9: + resolution: {integrity: sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-powerpc64le-gnu@4.18.0: resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} cpu: [ppc64] @@ -6034,6 +6951,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-powerpc64le-gnu@4.34.9: + resolution: {integrity: sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-riscv64-gnu@4.18.0: resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} cpu: [riscv64] @@ -6042,6 +6967,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-riscv64-gnu@4.34.9: + resolution: {integrity: sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-s390x-gnu@4.18.0: resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} cpu: [s390x] @@ -6050,6 +6983,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-s390x-gnu@4.34.9: + resolution: {integrity: sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-gnu@4.18.0: resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} cpu: [x64] @@ -6058,6 +6999,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-x64-gnu@4.34.9: + resolution: {integrity: sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-linux-x64-musl@4.18.0: resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} cpu: [x64] @@ -6066,6 +7015,14 @@ packages: dev: true optional: true + /@rollup/rollup-linux-x64-musl@4.34.9: + resolution: {integrity: sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-arm64-msvc@4.18.0: resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} cpu: [arm64] @@ -6074,6 +7031,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-arm64-msvc@4.34.9: + resolution: {integrity: sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-ia32-msvc@4.18.0: resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} cpu: [ia32] @@ -6082,6 +7047,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-ia32-msvc@4.34.9: + resolution: {integrity: sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rollup/rollup-win32-x64-msvc@4.18.0: resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} cpu: [x64] @@ -6090,6 +7063,14 @@ packages: dev: true optional: true + /@rollup/rollup-win32-x64-msvc@4.34.9: + resolution: {integrity: sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@rtsao/scc@1.1.0: resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} dev: true @@ -6098,6 +7079,10 @@ packages: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@sinonjs/commons@1.8.6: resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} dependencies: @@ -6125,6 +7110,24 @@ packages: tslib: 2.8.0 dev: false + /@swc/core-darwin-arm64@1.10.18: + resolution: {integrity: sha512-FdGqzAIKVQJu8ROlnHElP59XAUsUzCFSNsou+tY/9ba+lhu8R9v0OI5wXiPErrKGZpQFMmx/BPqqhx3X4SuGNg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-arm64@1.11.4: + resolution: {integrity: sha512-Oi4lt4wqjpp80pcCh+vzvpsESJ8XXozYCE5EM/dDpr+9m2oRpkseds7Gq4ulzgdbUDPo1jJ1PonjjrKpfKY+sQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@swc/core-darwin-arm64@1.3.42: resolution: {integrity: sha512-hM6RrZFyoCM9mX3cj/zM5oXwhAqjUdOCLXJx7KTQps7NIkv/Qjvobgvyf2gAb89j3ARNo9NdIoLjTjJ6oALtiA==} engines: {node: '>=10'} @@ -6134,6 +7137,24 @@ packages: dev: true optional: true + /@swc/core-darwin-x64@1.10.18: + resolution: {integrity: sha512-RZ73gZRituL/ZVLgrW6BYnQ5g8tuStG4cLUiPGJsUZpUm0ullSH6lHFvZTCBNFTfpQChG6eEhi2IdG6DwFp1lw==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.11.4: + resolution: {integrity: sha512-Tb7ez94DXxhX5iJ5slnAlT2gwJinQk3pMnQ46Npi6adKr3ZXM5Bdk0jpRUp8XjEcgNXkQRV1DtrySgCz6YlEnQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@swc/core-darwin-x64@1.3.42: resolution: {integrity: sha512-bjsWtHMb6wJK1+RGlBs2USvgZ0txlMk11y0qBLKo32gLKTqzUwRw0Fmfzuf6Ue2a/w//7eqMlPFEre4LvJajGw==} engines: {node: '>=10'} @@ -6143,6 +7164,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm-gnueabihf@1.10.18: + resolution: {integrity: sha512-8iJqI3EkxJuuq21UHoen1VS+QlS23RvynRuk95K+Q2HBjygetztCGGEc+Xelx9a0uPkDaaAtFvds4JMDqb9SAA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.11.4: + resolution: {integrity: sha512-p1uV+6Mi+0M+1kL7qL206ZaohomYMW7yroXSLDTJXbIylx7wG2xrUQL6AFtz2DwqDoX/E8jMNBjp+GcEy8r8Ig==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm-gnueabihf@1.3.42: resolution: {integrity: sha512-Oe0ggMz3MyqXNfeVmY+bBTL0hFSNY3bx8dhcqsh4vXk/ZVGse94QoC4dd92LuPHmKT0x6nsUzB86x2jU9QHW5g==} engines: {node: '>=10'} @@ -6152,6 +7191,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm64-gnu@1.10.18: + resolution: {integrity: sha512-8f1kSktWzMB6PG+r8lOlCfXz5E8Qhsmfwonn77T/OfjvGwQaWrcoASh2cdjpk3dydbf8jsKGPQE1lSc7GyjXRQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.11.4: + resolution: {integrity: sha512-4ijX4bWf9oc7kWkT6xUhugVGzEJ7U9c7CHNmt/xhI/yWsQdfM11+HECqWh7ay3m+aaEoVdvTeU5gykeF5jSxDA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm64-gnu@1.3.42: resolution: {integrity: sha512-ZJsa8NIW1RLmmHGTJCbM7OPSbBZ9rOMrLqDtUOGrT0uoJXZnnQqolflamB5wviW0X6h3Z3/PSTNGNDCJ3u3Lqg==} engines: {node: '>=10'} @@ -6161,6 +7218,24 @@ packages: dev: true optional: true + /@swc/core-linux-arm64-musl@1.10.18: + resolution: {integrity: sha512-4rv+E4VLdgQw6zjbTAauCAEExxChvxMpBUMCiZweTNPKbJJ2dY6BX2WGJ1ea8+RcgqR/Xysj3AFbOz1LBz6dGA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.11.4: + resolution: {integrity: sha512-XI+gOgcuSanejbAC5QXKTjNA3GUJi7bzHmeJbNhKpX9d349RdVwan0k9okHmhMBY7BywAg3LK0ovF9PmOLgMHg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-arm64-musl@1.3.42: resolution: {integrity: sha512-YpZwlFAfOp5vkm/uVUJX1O7N3yJDO1fDQRWqsOPPNyIJkI2ydlRQtgN6ZylC159Qv+TimfXnGTlNr7o3iBAqjg==} engines: {node: '>=10'} @@ -6170,6 +7245,24 @@ packages: dev: true optional: true + /@swc/core-linux-x64-gnu@1.10.18: + resolution: {integrity: sha512-vTNmyRBVP+sZca+vtwygYPGTNudTU6Gl6XhaZZ7cEUTBr8xvSTgEmYXoK/2uzyXpaTUI4Bmtp1x81cGN0mMoLQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.11.4: + resolution: {integrity: sha512-wyD6noaCPFayKOvl9mTxuiQoEULAagGuO0od2VkW7h4HvlgpOAZNekZYX73WEP/b+WuePNHurZ9KGpom43IzmA==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-x64-gnu@1.3.42: resolution: {integrity: sha512-0ccpKnsZbyHBzaQFdP8U9i29nvOfKitm6oJfdJzlqsY/jCqwvD8kv2CAKSK8WhJz//ExI2LqNrDI0yazx5j7+A==} engines: {node: '>=10'} @@ -6179,6 +7272,24 @@ packages: dev: true optional: true + /@swc/core-linux-x64-musl@1.10.18: + resolution: {integrity: sha512-1TZPReKhFCeX776XaT6wegknfg+g3zODve+r4oslFHI+g7cInfWlxoGNDS3niPKyuafgCdOjme2g3OF+zzxfsQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.11.4: + resolution: {integrity: sha512-e2vG9gUF1BRX0BWqSEHop6u14l5BtV3VS2Pmr+oquc0Ycs/zj81xhYc3ML4ByK5OxDkAaKBWryAOKTLaJA/DVg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@swc/core-linux-x64-musl@1.3.42: resolution: {integrity: sha512-7eckRRuTZ6+3K21uyfXXgc2ZCg0mSWRRNwNT3wap2bYkKPeqTgb8pm8xYSZNEiMuDonHEat6XCCV36lFY6kOdQ==} engines: {node: '>=10'} @@ -6188,6 +7299,24 @@ packages: dev: true optional: true + /@swc/core-win32-arm64-msvc@1.10.18: + resolution: {integrity: sha512-o/2CsaWSN3bkzVQ6DA+BiFKSVEYvhWGA1h+wnL2zWmIDs2Knag54sOEXZkCaf8YQyZesGeXJtPEy9hh/vjJgkA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.11.4: + resolution: {integrity: sha512-rm51iljNqjCA/41gxYameuyjX1ENaTlvdxmaoPPYeUDt6hfypG93IxMJJCewaeHN9XfNxqZU7d4cupNqk+8nng==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-arm64-msvc@1.3.42: resolution: {integrity: sha512-t27dJkdw0GWANdN4TV0lY/V5vTYSx5SRjyzzZolep358ueCGuN1XFf1R0JcCbd1ojosnkQg2L7A7991UjXingg==} engines: {node: '>=10'} @@ -6197,6 +7326,24 @@ packages: dev: true optional: true + /@swc/core-win32-ia32-msvc@1.10.18: + resolution: {integrity: sha512-eTPASeJtk4mJDfWiYEiOC6OYUi/N7meHbNHcU8e+aKABonhXrIo/FmnTE8vsUtC6+jakT1TQBdiQ8fzJ1kJVwA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.11.4: + resolution: {integrity: sha512-PHy3N6zlyU8te7Umi0ggXNbcx2VUkwpE59PW9FQQy9MBZM1Qn+OEGnO/4KLWjGFABw+9CwIeaRYgq6uCi1ry6A==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-ia32-msvc@1.3.42: resolution: {integrity: sha512-xfpc/Zt/aMILX4IX0e3loZaFyrae37u3MJCv1gJxgqrpeLi7efIQr3AmERkTK3mxTO6R5urSliWw2W3FyZ7D3Q==} engines: {node: '>=10'} @@ -6206,6 +7353,24 @@ packages: dev: true optional: true + /@swc/core-win32-x64-msvc@1.10.18: + resolution: {integrity: sha512-1Dud8CDBnc34wkBOboFBQud9YlV1bcIQtKSg7zC8LtwR3h+XAaCayZPkpGmmAlCv1DLQPvkF+s0JcaVC9mfffQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.11.4: + resolution: {integrity: sha512-0TiriDGl7Dr4ObfMBk07PS4Ql5hgQH0QnU3E8I+fbs45hqfwC5OrN47HOsXx4ZbEw8XYxp2NM8SGnVoTIm4J8w==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@swc/core-win32-x64-msvc@1.3.42: resolution: {integrity: sha512-ra2K4Tu++EJLPhzZ6L8hWUsk94TdK/2UKhL9dzCBhtzKUixsGCEqhtqH1zISXNvW8qaVLFIMUP37ULe80/IJaA==} engines: {node: '>=10'} @@ -6215,6 +7380,58 @@ packages: dev: true optional: true + /@swc/core@1.10.18(@swc/helpers@0.5.15): + resolution: {integrity: sha512-IUWKD6uQYGRy8w2X9EZrtYg1O3SCijlHbCXzMaHQYc1X7yjijQh4H3IVL9ssZZyVp2ZDfQZu4bD5DWxxvpyjvg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + '@swc/types': 0.1.19 + optionalDependencies: + '@swc/core-darwin-arm64': 1.10.18 + '@swc/core-darwin-x64': 1.10.18 + '@swc/core-linux-arm-gnueabihf': 1.10.18 + '@swc/core-linux-arm64-gnu': 1.10.18 + '@swc/core-linux-arm64-musl': 1.10.18 + '@swc/core-linux-x64-gnu': 1.10.18 + '@swc/core-linux-x64-musl': 1.10.18 + '@swc/core-win32-arm64-msvc': 1.10.18 + '@swc/core-win32-ia32-msvc': 1.10.18 + '@swc/core-win32-x64-msvc': 1.10.18 + dev: true + + /@swc/core@1.11.4(@swc/helpers@0.5.15): + resolution: {integrity: sha512-EHl6eNod/914xDRK4nu7gr78riK2cfi4DkAMvJt6COdaNGOnbR5eKrLe3SnRizyzzrPcxUMhflDL5hrcXS8rAQ==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + '@swc/types': 0.1.19 + optionalDependencies: + '@swc/core-darwin-arm64': 1.11.4 + '@swc/core-darwin-x64': 1.11.4 + '@swc/core-linux-arm-gnueabihf': 1.11.4 + '@swc/core-linux-arm64-gnu': 1.11.4 + '@swc/core-linux-arm64-musl': 1.11.4 + '@swc/core-linux-x64-gnu': 1.11.4 + '@swc/core-linux-x64-musl': 1.11.4 + '@swc/core-win32-arm64-msvc': 1.11.4 + '@swc/core-win32-ia32-msvc': 1.11.4 + '@swc/core-win32-x64-msvc': 1.11.4 + dev: true + /@swc/core@1.3.42: resolution: {integrity: sha512-nVFUd5+7tGniM2cT3LXaqnu3735Cu4az8A9gAKK+8sdpASI52SWuqfDBmjFCK9xG90MiVDVp2PTZr0BWqCIzpw==} engines: {node: '>=10'} @@ -6232,6 +7449,27 @@ packages: '@swc/core-win32-x64-msvc': 1.3.42 dev: true + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/helpers@0.5.15: + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + dependencies: + tslib: 2.8.0 + dev: true + + /@swc/jest@0.2.24(@swc/core@1.11.4): + resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} + engines: {npm: '>= 7.0.0'} + peerDependencies: + '@swc/core': '*' + dependencies: + '@jest/create-cache-key-function': 27.5.1 + '@swc/core': 1.11.4(@swc/helpers@0.5.15) + jsonc-parser: 3.2.0 + dev: true + /@swc/jest@0.2.24(@swc/core@1.3.42): resolution: {integrity: sha512-fwgxQbM1wXzyKzl1+IW0aGrRvAA8k0Y3NxFhKigbPjOJ4mCKnWEcNX9HQS3gshflcxq8YKhadabGUVfdwjCr6Q==} engines: {npm: '>= 7.0.0'} @@ -6243,6 +7481,12 @@ packages: jsonc-parser: 3.2.0 dev: true + /@swc/types@0.1.19: + resolution: {integrity: sha512-WkAZaAfj44kh/UFdAQcrMP1I0nwRqpt27u+08LMBYMqmQfwwMofYoMh/48NGkMMRfC4ynpfwRbJuu8ErfNloeA==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + /@tokenizer/token@0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} @@ -6347,6 +7591,10 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true + /@types/estree@1.0.6: + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + dev: true + /@types/express-serve-static-core@4.17.36: resolution: {integrity: sha512-zbivROJ0ZqLAtMzgzIUC4oNqDG9iF0lSsAqpOD9kbs5xcIM3dTiyuHvBc7R8MtWBp3AAWGaovJa+wzWPjLYW7Q==} dependencies: @@ -6458,6 +7706,10 @@ packages: resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} dev: true + /@types/parse-json@4.0.2: + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + dev: true + /@types/pg@8.6.6: resolution: {integrity: sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==} dependencies: @@ -6470,10 +7722,6 @@ packages: resolution: {integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==} dev: true - /@types/prop-types@15.7.12: - resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - dev: true - /@types/psl@1.1.3: resolution: {integrity: sha512-Iu174JHfLd7i/XkXY6VDrqSlPvTDQOtQI7wNAXKKOAADJ9TduRLkNdMgjGiMxSttUIZnomv81JAbAbC0DhggxA==} dev: true @@ -6484,16 +7732,17 @@ packages: /@types/range-parser@1.2.4: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} - /@types/react-dom@18.3.0: - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + /@types/react-dom@19.0.4(@types/react@19.0.10): + resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} + peerDependencies: + '@types/react': ^19.0.0 dependencies: - '@types/react': 18.3.2 + '@types/react': 19.0.10 dev: true - /@types/react@18.3.2: - resolution: {integrity: sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==} + /@types/react@19.0.10: + resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} dependencies: - '@types/prop-types': 15.7.12 csstype: 3.1.3 dev: true @@ -6707,11 +7956,22 @@ packages: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true + /@vitejs/plugin-react-swc@3.8.0(@swc/helpers@0.5.15)(vite@6.2.0): + resolution: {integrity: sha512-T4sHPvS+DIqDP51ifPqa9XIRAz/kIvIi8oXcnOZZgHmMotgmmdxe/DD5tMFlt5nuIRzT0/QuiwmKlH0503Aapw==} + peerDependencies: + vite: ^4 || ^5 || ^6 + dependencies: + '@swc/core': 1.11.4(@swc/helpers@0.5.15) + vite: 6.2.0(@types/node@18.19.67) + transitivePeerDependencies: + - '@swc/helpers' + dev: true + /@web/rollup-plugin-import-meta-assets@2.2.1(rollup@4.18.0): resolution: {integrity: sha512-nG7nUQqSJWdl63pBTmnIElJuFi2V1x9eVje19BJuFvfz266jSmZtX3m30ncb7fOJxQt3/ge+FVL8tuNI9+63dQ==} engines: {node: '>=18.0.0'} dependencies: - '@rollup/plugin-dynamic-import-vars': 2.1.2(rollup@4.18.0) + '@rollup/plugin-dynamic-import-vars': 2.1.5(rollup@4.18.0) '@rollup/pluginutils': 5.1.0(rollup@4.18.0) estree-walker: 2.0.2 globby: 13.2.2 @@ -7027,7 +8287,7 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} dependencies: - tslib: 2.6.2 + tslib: 2.8.0 dev: true /astring@1.8.6: @@ -7050,7 +8310,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001621 + caniuse-lite: 1.0.30001700 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.1 @@ -7129,6 +8389,15 @@ packages: '@types/babel__traverse': 7.20.1 dev: true + /babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + dependencies: + '@babel/runtime': 7.22.10 + cosmiconfig: 7.1.0 + resolve: 1.22.4 + dev: true + /babel-preset-current-node-syntax@1.0.1(@babel/core@7.18.6): resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==} peerDependencies: @@ -7308,6 +8577,13 @@ packages: dependencies: fill-range: 7.0.1 + /braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.1.1 + dev: true + /breakword@1.0.6: resolution: {integrity: sha512-yjxDAYyK/pBvws9H4xKYpLDpYKEH6CzrBPAuXq3x18I+c/2MkVtT3qAr7Oloi6Dss9qNhPVueAAVU1CSeNDIXw==} dependencies: @@ -7322,7 +8598,7 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001522 + caniuse-lite: 1.0.30001700 electron-to-chromium: 1.4.499 node-releases: 2.0.13 update-browserslist-db: 1.0.11(browserslist@4.21.10) @@ -7333,12 +8609,23 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001621 + caniuse-lite: 1.0.30001700 electron-to-chromium: 1.4.779 node-releases: 2.0.14 update-browserslist-db: 1.0.16(browserslist@4.23.0) dev: true + /browserslist@4.24.4: + resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + dependencies: + caniuse-lite: 1.0.30001700 + electron-to-chromium: 1.5.107 + node-releases: 2.0.19 + update-browserslist-db: 1.1.3(browserslist@4.24.4) + dev: true + /bser@2.1.1: resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} dependencies: @@ -7476,17 +8763,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.21.10 - caniuse-lite: 1.0.30001522 + caniuse-lite: 1.0.30001700 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: true - /caniuse-lite@1.0.30001522: - resolution: {integrity: sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==} - dev: true - - /caniuse-lite@1.0.30001621: - resolution: {integrity: sha512-+NLXZiviFFKX0fk8Piwv3PfLPGtRqJeq2TiNoUff/qB5KJgwecJTvCXDpmlyP/eCI/GUEmp/h/y5j0yckiiZrA==} + /caniuse-lite@1.0.30001700: + resolution: {integrity: sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==} dev: true /cbor-extract@2.2.0: @@ -7540,6 +8823,21 @@ packages: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} dev: true + /chokidar@3.5.1: + resolution: {integrity: sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.5.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -7587,6 +8885,30 @@ packages: engines: {node: '>=6'} dev: true + /cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + dependencies: + restore-cursor: 3.1.0 + dev: true + + /cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + dev: true + + /cli-table@0.3.11: + resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} + engines: {node: '>= 0.2.0'} + dependencies: + colors: 1.0.3 + dev: true + + /cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + dev: true + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -7675,14 +8997,20 @@ packages: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} dev: true + /colors@1.0.3: + resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} + engines: {node: '>=0.1.90'} + dev: true + /combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} dev: true /commander@4.1.1: @@ -7752,6 +9080,10 @@ packages: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} dev: true + /convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + dev: true + /cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} @@ -7771,6 +9103,33 @@ packages: object-assign: 4.1.1 vary: 1.1.2 + /cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + dev: true + + /cosmiconfig@8.3.6(typescript@5.6.3): + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + typescript: 5.6.3 + dev: true + /cosmiconfig@9.0.0(typescript@5.6.3): resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -7977,6 +9336,10 @@ packages: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} dev: true + /date-fns@3.6.0: + resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} + dev: true + /dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} dev: true @@ -8337,6 +9700,10 @@ packages: resolution: {integrity: sha512-oaTiIcszNfySXVJzKcjxd2YjPxziAd+GmXyb2HbidCeFo6Z88ygOT7EimlrEQhM2U08VhSrbKhLOXP0kKUCZ6g==} dev: true + /electron-to-chromium@1.5.107: + resolution: {integrity: sha512-dJr1o6yCntRkXElnhsHh1bAV19bo/hKyFf7tCcWgpXbuFIF0Lakjgqv5LRfSDaNzAII8Fnxg2tqgHkgCvxdbxw==} + dev: true + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -8777,10 +10144,79 @@ packages: esbuild-windows-arm64: 0.14.48 dev: true + /esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 + dev: true + + /esbuild@0.25.0: + resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + engines: {node: '>=18'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.0 + '@esbuild/android-arm': 0.25.0 + '@esbuild/android-arm64': 0.25.0 + '@esbuild/android-x64': 0.25.0 + '@esbuild/darwin-arm64': 0.25.0 + '@esbuild/darwin-x64': 0.25.0 + '@esbuild/freebsd-arm64': 0.25.0 + '@esbuild/freebsd-x64': 0.25.0 + '@esbuild/linux-arm': 0.25.0 + '@esbuild/linux-arm64': 0.25.0 + '@esbuild/linux-ia32': 0.25.0 + '@esbuild/linux-loong64': 0.25.0 + '@esbuild/linux-mips64el': 0.25.0 + '@esbuild/linux-ppc64': 0.25.0 + '@esbuild/linux-riscv64': 0.25.0 + '@esbuild/linux-s390x': 0.25.0 + '@esbuild/linux-x64': 0.25.0 + '@esbuild/netbsd-arm64': 0.25.0 + '@esbuild/netbsd-x64': 0.25.0 + '@esbuild/openbsd-arm64': 0.25.0 + '@esbuild/openbsd-x64': 0.25.0 + '@esbuild/sunos-x64': 0.25.0 + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 + dev: true + /escalade@3.1.2: resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} engines: {node: '>=6'} + /escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + dev: true + /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} @@ -9260,7 +10696,7 @@ packages: '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.8 dev: true /fast-json-stable-stringify@2.1.0: @@ -9313,13 +10749,31 @@ packages: /fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} dependencies: - bser: 2.1.1 + bser: 2.1.1 + dev: true + + /fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + dependencies: + pend: 1.2.0 + dev: true + + /fdir@6.4.3(picomatch@4.0.2): + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + dependencies: + picomatch: 4.0.2 dev: true - /fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + /figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} dependencies: - pend: 1.2.0 + escape-string-regexp: 1.0.5 dev: true /file-entry-cache@6.0.1: @@ -9346,6 +10800,13 @@ packages: dependencies: to-regex-range: 5.0.1 + /fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -9360,6 +10821,13 @@ packages: transitivePeerDependencies: - supports-color + /find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + /find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -9651,6 +11119,19 @@ packages: minipass: 5.0.0 path-scurry: 1.10.1 + /glob@11.0.1: + resolution: {integrity: sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==} + engines: {node: 20 || >=22} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 4.1.0 + minimatch: 10.0.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.0 + dev: true + /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -9711,7 +11192,7 @@ packages: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.1 + fast-glob: 3.3.2 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 @@ -10092,6 +11573,25 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + /inquirer@7.3.3: + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + run-async: 2.4.1 + rxjs: 6.6.7 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + dev: true + /int64-buffer@0.1.10: resolution: {integrity: sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==} dev: false @@ -10290,6 +11790,11 @@ packages: dependencies: is-extglob: 2.1.1 + /is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + dev: true + /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} dev: true @@ -10406,6 +11911,11 @@ packages: which-typed-array: 1.1.16 dev: true + /is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + dev: true + /is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} @@ -10498,6 +12008,13 @@ packages: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + /jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} + engines: {node: 20 || >=22} + dependencies: + '@isaacs/cliui': 8.0.2 + dev: true + /jest-changed-files@28.1.3: resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10653,6 +12170,11 @@ packages: engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} dev: true + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /jest-haste-map@28.1.3: resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10869,6 +12391,18 @@ packages: pretty-format: 28.1.3 dev: true + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + dev: true + /jest-watcher@28.1.3: resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} @@ -10934,6 +12468,10 @@ packages: engines: {node: '>=10'} dev: true + /js-sha256@0.10.1: + resolution: {integrity: sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -10963,6 +12501,12 @@ packages: hasBin: true dev: true + /jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -11128,6 +12672,14 @@ packages: engines: {node: '>= 12.13.0'} dev: true + /locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + /locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -11148,6 +12700,11 @@ packages: /lodash.defaults@4.2.0: resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. + dev: true + /lodash.includes@4.3.0: resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} dev: true @@ -11197,7 +12754,6 @@ packages: /lodash.sortby@4.7.0: resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - dev: false /lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} @@ -11206,21 +12762,31 @@ packages: /lodash.uniq@4.5.0: resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - /long@5.2.3: - resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} - dev: false + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + /log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} dependencies: - js-tokens: 4.0.0 + chalk: 4.1.2 + is-unicode-supported: 0.1.0 dev: true + /long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + dev: false + /lru-cache@10.2.0: resolution: {integrity: sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==} engines: {node: 14 || >=16.14} + /lru-cache@11.0.2: + resolution: {integrity: sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==} + engines: {node: 20 || >=22} + dev: true + /lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} dependencies: @@ -11349,6 +12915,14 @@ packages: braces: 3.0.2 picomatch: 2.3.1 + /micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + dev: true + /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -11396,6 +12970,13 @@ packages: /minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + /minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} dependencies: @@ -11483,6 +13064,11 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} + /minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + dev: true + /minizlib@2.1.2: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} @@ -11512,6 +13098,10 @@ packages: resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==} dev: false + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: true + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -11538,6 +13128,10 @@ packages: resolution: {integrity: sha512-5vQEh3y+DG/lMPM0mCGPDnyV8chYg/g7rl6v3Gd8WMF9S429ox3Xk8qrk174kWhG767KQMqqxLD1WnGd77hiew==} dev: false + /mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + dev: true + /mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} dependencies: @@ -11552,6 +13146,12 @@ packages: hasBin: true dev: true + /nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + /napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -11646,6 +13246,10 @@ packages: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} dev: true + /node-releases@2.0.19: + resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + dev: true + /nodemailer-html-to-text@3.2.0: resolution: {integrity: sha512-RJUC6640QV1PzTHHapOrc6IzrAJUZtk2BdVdINZ9VTLm+mcQNyBO9LYyhrnufkzqiD9l8hPLJ97rSyK4WanPNg==} engines: {node: '>= 10.23.0'} @@ -11844,6 +13448,21 @@ packages: type-check: 0.4.0 dev: true + /ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + dev: true + /os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} @@ -11877,6 +13496,13 @@ packages: dependencies: yocto-queue: 0.1.0 + /p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + /p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -11951,6 +13577,10 @@ packages: netmask: 2.0.2 dev: true + /package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + dev: true + /packet-reader@1.0.0: resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} @@ -11979,6 +13609,11 @@ packages: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} dev: false + /path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + dev: true + /path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -12003,6 +13638,14 @@ packages: lru-cache: 10.2.0 minipass: 5.0.0 + /path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + dependencies: + lru-cache: 11.0.2 + minipass: 7.1.2 + dev: true + /path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} @@ -12011,6 +13654,10 @@ packages: engines: {node: '>=8'} dev: true + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: true + /peek-readable@4.1.0: resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} engines: {node: '>=8'} @@ -12076,10 +13723,19 @@ packages: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} dev: true + /picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + /picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + dev: true + /pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -12166,6 +13822,17 @@ packages: find-up: 4.1.0 dev: true + /pkg-up@3.1.0: + resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} + engines: {node: '>=8'} + dependencies: + find-up: 3.0.0 + dev: true + + /pofile@1.1.4: + resolution: {integrity: sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==} + dev: true + /possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} engines: {node: '>= 0.4'} @@ -12597,6 +14264,15 @@ packages: source-map-js: 1.2.0 dev: true + /postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + dev: true + /postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} @@ -12688,6 +14364,15 @@ packages: react-is: 18.2.0 dev: true + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + /process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} @@ -12775,6 +14460,14 @@ packages: /proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + /pseudolocale@2.1.0: + resolution: {integrity: sha512-af5fsrRvVwD+MBasBJvuDChT0KDqT0nEwD9NTgbtHJ16FKomWac9ua0z6YVNB4G9x9IOaiGWym62aby6n4tFMA==} + engines: {node: '>=16.0.0'} + hasBin: true + dependencies: + commander: 10.0.1 + dev: true + /pseudomap@1.0.2: resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} dev: true @@ -12853,12 +14546,6 @@ packages: engines: {node: '>=8'} dev: true - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: true - /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -12884,25 +14571,31 @@ packages: minimist: 1.2.8 strip-json-comments: 2.0.1 - /react-dom@18.3.1(react@18.3.1): - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + /react-dom@19.0.0(react@19.0.0): + resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + peerDependencies: + react: ^19.0.0 + dependencies: + react: 19.0.0 + scheduler: 0.25.0 + dev: true + + /react-error-boundary@5.0.0(react@19.0.0): + resolution: {integrity: sha512-tnjAxG+IkpLephNcePNA7v6F/QpWLH8He65+DmedchDwg162JZqx4NmbXj0mlAYVVEd81OW7aFhmbsScYfiAFQ==} peerDependencies: - react: ^18.3.1 + react: '>=16.13.1' dependencies: - loose-envify: 1.4.0 - react: 18.3.1 - scheduler: 0.23.2 + '@babel/runtime': 7.22.10 + react: 19.0.0 dev: true /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true - /react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + /react@19.0.0: + resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} - dependencies: - loose-envify: 1.4.0 dev: true /read-cache@1.0.0: @@ -12964,6 +14657,13 @@ packages: dependencies: readable-stream: 3.6.2 + /readdirp@3.5.0: + resolution: {integrity: sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + /readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -13091,6 +14791,14 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + dev: true + /retry@0.10.1: resolution: {integrity: sha512-ZXUSQYTHdl3uS7IuCehYfMzKyIDBNoAuUblvy5oGO5UJSUTmStUUVPXbA9Qxd173Bgre53yCQczQuHgRWAdvJQ==} dev: false @@ -13194,16 +14902,57 @@ packages: fsevents: 2.3.3 dev: true + /rollup@4.34.9: + resolution: {integrity: sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.9 + '@rollup/rollup-android-arm64': 4.34.9 + '@rollup/rollup-darwin-arm64': 4.34.9 + '@rollup/rollup-darwin-x64': 4.34.9 + '@rollup/rollup-freebsd-arm64': 4.34.9 + '@rollup/rollup-freebsd-x64': 4.34.9 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.9 + '@rollup/rollup-linux-arm-musleabihf': 4.34.9 + '@rollup/rollup-linux-arm64-gnu': 4.34.9 + '@rollup/rollup-linux-arm64-musl': 4.34.9 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.9 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.9 + '@rollup/rollup-linux-riscv64-gnu': 4.34.9 + '@rollup/rollup-linux-s390x-gnu': 4.34.9 + '@rollup/rollup-linux-x64-gnu': 4.34.9 + '@rollup/rollup-linux-x64-musl': 4.34.9 + '@rollup/rollup-win32-arm64-msvc': 4.34.9 + '@rollup/rollup-win32-ia32-msvc': 4.34.9 + '@rollup/rollup-win32-x64-msvc': 4.34.9 + fsevents: 2.3.3 + dev: true + + /run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + dev: true + /run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 + /rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + dependencies: + tslib: 1.14.1 + dev: true + /rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} requiresBuild: true dependencies: - tslib: 2.6.2 + tslib: 2.8.0 optional: true /safe-array-concat@1.0.1: @@ -13261,10 +15010,8 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} requiresBuild: true - /scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - dependencies: - loose-envify: 1.4.0 + /scheduler@0.25.0: + resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} dev: true /scmp@2.1.0: @@ -13327,12 +15074,6 @@ packages: transitivePeerDependencies: - supports-color - /serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - dependencies: - randombytes: 2.1.0 - dev: true - /serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -13564,15 +15305,13 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 + /source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} dev: true - /source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + /source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} dependencies: buffer-from: 1.1.2 source-map: 0.6.1 @@ -13587,6 +15326,13 @@ packages: engines: {node: '>= 8'} dev: false + /source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + dependencies: + whatwg-url: 7.1.0 + dev: true + /spawndamnit@2.0.0: resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} dependencies: @@ -13923,7 +15669,7 @@ packages: engines: {node: ^14.18.0 || >=16.0.0} dependencies: '@pkgr/core': 0.1.1 - tslib: 2.6.2 + tslib: 2.8.0 dev: true /tailwindcss@3.4.3: @@ -14023,17 +15769,6 @@ packages: supports-hyperlinks: 2.3.0 dev: true - /terser@5.31.0: - resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==} - engines: {node: '>=10'} - hasBin: true - dependencies: - '@jridgewell/source-map': 0.3.6 - acorn: 8.10.0 - commander: 2.20.3 - source-map-support: 0.5.21 - dev: true - /test-exclude@6.0.0: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} @@ -14120,6 +15855,12 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + /tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + dependencies: + punycode: 2.3.0 + dev: true + /trim-newlines@3.0.1: resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} engines: {node: '>=8'} @@ -14188,15 +15929,14 @@ packages: /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - dev: false /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} requiresBuild: true + dev: false /tslib@2.8.0: resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} - dev: false /tty-table@4.2.1: resolution: {integrity: sha512-xz0uKo+KakCQ+Dxj1D/tKn2FSyreSYWzdkL/BYhgN6oMW808g8QRMuh1atAV9fjTPbWBjfbkKQpI/5rEcnAc7g==} @@ -14460,6 +16200,10 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} + /unraw@3.0.0: + resolution: {integrity: sha512-08/DA66UF65OlpUDIQtbJyrqTR0jTAlJ+jsnkQ4jxR7+K5g5YG1APZKQSMCE1vqqmD+2pv6+IdEjmopFatacvg==} + dev: true + /update-browserslist-db@1.0.11(browserslist@4.21.10): resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} hasBin: true @@ -14482,6 +16226,17 @@ packages: picocolors: 1.0.1 dev: true + /update-browserslist-db@1.1.3(browserslist@4.24.4): + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + dependencies: + browserslist: 4.24.4 + escalade: 3.2.0 + picocolors: 1.1.1 + dev: true + /uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -14538,6 +16293,54 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + /vite@6.2.0(@types/node@18.19.67): + resolution: {integrity: sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + dependencies: + '@types/node': 18.19.67 + esbuild: 0.25.0 + postcss: 8.5.3 + rollup: 4.34.9 + optionalDependencies: + fsevents: 2.3.3 + dev: true + /walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} dependencies: @@ -14553,12 +16356,24 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + /webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + dev: true + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 + /whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + dev: true + /which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} dependencies: diff --git a/tsconfig/bundler.json b/tsconfig/bundler.json index 6aea841facb..d0480c35caf 100644 --- a/tsconfig/bundler.json +++ b/tsconfig/bundler.json @@ -3,6 +3,7 @@ "compilerOptions": { "module": "ESNext", "moduleResolution": "Bundler", + "allowImportingTsExtensions": true, "declaration": false, "declarationMap": false, "noEmit": true