@gamerdexapp/gamerdex-sync 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/LICENSE +21 -0
- package/README.md +81 -2
- package/dist/index.d.mts +392 -0
- package/dist/index.d.ts +392 -0
- package/dist/index.js +829 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +767 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +62 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/layout.ts","../src/meta.ts","../src/merge/equal.ts","../src/merge/cloud.ts","../src/merge/merge.ts","../src/merge/resolve.ts","../src/provider/base64.ts","../src/provider/types.ts","../src/provider/github.ts","../src/provider/memory.ts","../src/sync/sync.ts","../src/auth/app.ts","../src/auth/tokens.ts","../src/auth/source.ts","../src/auth/urls.ts","../src/provider/repositories.ts"],"sourcesContent":["export * from './layout';\nexport * from './meta';\nexport * from './merge/equal';\nexport * from './merge/merge';\nexport * from './merge/resolve';\nexport * from './merge/types';\nexport * from './provider/base64';\nexport * from './provider/github';\nexport * from './provider/memory';\nexport * from './provider/types';\nexport * from './sync/sync';\nexport * from './merge/cloud';\nexport * from './auth/app';\nexport * from './auth/source';\nexport * from './auth/tokens';\nexport * from './auth/urls';\nexport * from './provider/repositories';\n","export const ROOT_FOLDER = 'Gamerdex';\nexport const DATA_FILE = 'data.json';\nexport const META_FILE = 'meta.json';\n\nconst APP_ID = /^[a-z0-9]+(-[a-z0-9]+)*$/;\n\n/** An app id names a folder, so it is lowercase words joined by single dashes. */\nexport const isAppId = (value: string): boolean => APP_ID.test(value);\n\nconst folderOf = (appId: string): string => {\n if (!isAppId(appId)) throw new RangeError(`Invalid app id: ${appId}`);\n return `${ROOT_FOLDER}/${appId}`;\n};\n\n/** The path of the saved data of an app, such as `Gamerdex/whiteout-survival/data.json`. */\nexport const dataPath = (appId: string): string => `${folderOf(appId)}/${DATA_FILE}`;\n\n/** The path of the bookkeeping file of an app, next to its saved data. */\nexport const metaPath = (appId: string): string => `${folderOf(appId)}/${META_FILE}`;\n","export const META_FORMAT = 1;\n\nexport interface DeviceRecord {\n name: string;\n /** ISO time of the last sync of the device. */\n lastSeenAt: string;\n}\n\n/**\n * The bookkeeping file beside the saved data. It holds no game data and no token. A revision only\n * counts up, so a device with a wrong clock cannot make old data look new.\n */\nexport interface SyncMeta {\n format: typeof META_FORMAT;\n appId: string;\n /** One counter for each chief id. A chief that was removed keeps its counter. */\n revisions: Record<string, number>;\n settingsRevision: number;\n /** Chief ids removed on purpose, with the revision of the removal. */\n removed: Record<string, number>;\n devices: Record<string, DeviceRecord>;\n lastSyncAt: string | null;\n}\n\nexport type ParsedMeta = { ok: true; meta: SyncMeta } | { ok: false; error: string };\n\nexport const emptyMeta = (appId: string): SyncMeta => ({\n format: META_FORMAT,\n appId,\n revisions: {},\n settingsRevision: 0,\n removed: {},\n devices: {},\n lastSyncAt: null,\n});\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst isCounter = (value: unknown): value is number =>\n typeof value === 'number' && Number.isInteger(value) && value >= 0;\n\nconst isCounterMap = (value: unknown): value is Record<string, number> =>\n isRecord(value) && Object.values(value).every(isCounter);\n\nconst isDevice = (value: unknown): value is DeviceRecord =>\n isRecord(value) && typeof value.name === 'string' && typeof value.lastSeenAt === 'string';\n\n/** Reads the meta file and checks the whole shape. Nothing is returned for a file that fails. */\nexport const parseMeta = (text: string, appId: string): ParsedMeta => {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, error: 'The meta file is not valid JSON.' };\n }\n if (!isRecord(value)) return { ok: false, error: 'The meta file is not an object.' };\n if (value.format !== META_FORMAT) return { ok: false, error: 'The meta format is not known.' };\n if (value.appId !== appId) return { ok: false, error: 'The meta file belongs to another app.' };\n if (!isCounterMap(value.revisions)) return { ok: false, error: 'The revisions are not valid.' };\n if (!isCounter(value.settingsRevision)) {\n return { ok: false, error: 'The settings revision is not valid.' };\n }\n if (!isCounterMap(value.removed)) return { ok: false, error: 'The removed list is not valid.' };\n if (!isRecord(value.devices) || !Object.values(value.devices).every(isDevice)) {\n return { ok: false, error: 'The devices are not valid.' };\n }\n if (value.lastSyncAt !== null && typeof value.lastSyncAt !== 'string') {\n return { ok: false, error: 'The last sync time is not valid.' };\n }\n return { ok: true, meta: value as unknown as SyncMeta };\n};\n\n/** Counts one change of a chief. A chief that returns after a removal leaves the removed list. */\nexport const withChiefRevision = (meta: SyncMeta, chiefId: string): SyncMeta => {\n return {\n ...meta,\n revisions: { ...meta.revisions, [chiefId]: (meta.revisions[chiefId] ?? 0) + 1 },\n removed: Object.fromEntries(Object.entries(meta.removed).filter(([id]) => id !== chiefId)),\n };\n};\n\n/** Counts the removal of a chief. The counter keeps rising so an old copy cannot return it. */\nexport const withChiefRemoved = (meta: SyncMeta, chiefId: string): SyncMeta => {\n const revision = (meta.revisions[chiefId] ?? 0) + 1;\n return {\n ...meta,\n revisions: { ...meta.revisions, [chiefId]: revision },\n removed: { ...meta.removed, [chiefId]: revision },\n };\n};\n\nexport const withSettingsRevision = (meta: SyncMeta): SyncMeta => ({\n ...meta,\n settingsRevision: meta.settingsRevision + 1,\n});\n\nexport const withDevice = (meta: SyncMeta, deviceId: string, device: DeviceRecord): SyncMeta => ({\n ...meta,\n devices: { ...meta.devices, [deviceId]: device },\n lastSyncAt: device.lastSeenAt,\n});\n","const isObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\n/** Deep equality of two JSON values. The order of the keys of an object does not matter. */\nexport const equal = (a: unknown, b: unknown): boolean => {\n if (a === b) return true;\n if (Array.isArray(a)) {\n return Array.isArray(b) && a.length === b.length && a.every((item, i) => equal(item, b[i]));\n }\n if (isObject(a) && isObject(b)) {\n const keys = Object.keys(a);\n return (\n keys.length === Object.keys(b).length &&\n keys.every((key) => Object.hasOwn(b, key) && equal(a[key], b[key]))\n );\n }\n return false;\n};\n\nexport { isObject };\n","import { isObject } from './equal';\nimport { Schema } from './types';\n\nconst own = (value: Record<string, unknown>, key: string): unknown =>\n Object.hasOwn(value, key) ? value[key] : undefined;\n\nconst childSchema = (schema: Extract<Schema, { type: 'object' }>, key: string): Schema =>\n (schema.fields && Object.hasOwn(schema.fields, key) ? schema.fields[key] : undefined) ??\n schema.each ?? { type: 'record' };\n\nconst holdsDeviceValue = (schema: Schema): boolean => {\n if (schema.type === 'local') return true;\n if (schema.type === 'keyed') return holdsDeviceValue(schema.item);\n if (schema.type !== 'object') return false;\n return (\n (schema.each !== undefined && holdsDeviceValue(schema.each)) ||\n Object.values(schema.fields ?? {}).some(holdsDeviceValue)\n );\n};\n\n/**\n * Returns the merged data as it must be stored in the cloud: every part with the `local` strategy\n * keeps the value that the cloud already holds, so a value of one device never changes the cloud.\n */\nexport const restoreCloudValues = (merged: unknown, remote: unknown, schema: Schema): unknown => {\n if (schema.type === 'local') return remote;\n if (!holdsDeviceValue(schema)) return merged;\n\n if (schema.type === 'object' && isObject(merged) && (isObject(remote) || remote === undefined)) {\n const cloud = remote ?? {};\n const result: Record<string, unknown> = {};\n for (const key of new Set([...Object.keys(merged), ...Object.keys(cloud)])) {\n const child = childSchema(schema, key);\n const value = restoreCloudValues(own(merged, key), own(cloud, key), child);\n if (value !== undefined) result[key] = value;\n }\n return result;\n }\n\n if (schema.type === 'keyed' && Array.isArray(merged) && Array.isArray(remote)) {\n const remoteById = new Map(\n remote.filter(isObject).map((item) => [item[schema.key], item] as const),\n );\n return merged.map((item) =>\n isObject(item)\n ? restoreCloudValues(item, remoteById.get(item[schema.key]), schema.item)\n : item,\n );\n }\n return merged;\n};\n","import { restoreCloudValues } from './cloud';\nimport { equal, isObject } from './equal';\nimport { Conflict, MergeInput, MergeResult, Path, Schema, Side } from './types';\n\ninterface Context {\n prefer: Side;\n conflicts: Conflict[];\n}\n\n/** Reads an own key only, so an id such as `constructor` never reads from the prototype. */\nconst own = <T>(map: Record<string, T> | undefined, key: string): T | undefined =>\n map !== undefined && Object.hasOwn(map, key) ? map[key] : undefined;\n\nconst record = (\n base: unknown,\n local: unknown,\n remote: unknown,\n path: Path,\n context: Context,\n): unknown => {\n context.conflicts.push({\n path,\n kind: 'both-changed',\n base,\n local,\n remote,\n taken: context.prefer,\n });\n return context.prefer === 'local' ? local : remote;\n};\n\nconst joinLists = (base: unknown[], local: unknown[], remote: unknown[]): unknown[] => {\n const has = (list: unknown[], value: unknown) => list.some((item) => equal(item, value));\n const kept = (value: unknown) => {\n const inBase = has(base, value);\n const inLocal = has(local, value);\n const inRemote = has(remote, value);\n return inBase ? inLocal && inRemote : inLocal || inRemote;\n };\n const result: unknown[] = [];\n for (const value of [...local, ...remote]) {\n if (kept(value) && !has(result, value)) result.push(value);\n }\n return result;\n};\n\nconst entriesOf = (list: unknown[], key: string): Map<string, unknown> | null => {\n const entries = new Map<string, unknown>();\n for (const item of list) {\n if (!isObject(item) || typeof item[key] !== 'string') return null;\n entries.set(item[key], item);\n }\n return entries;\n};\n\nconst mergeEntries = (\n ids: string[],\n pick: (id: string) => [unknown, unknown, unknown],\n schemaOf: (id: string) => Schema,\n path: Path,\n context: Context,\n deleteWins: boolean,\n): Map<string, unknown> => {\n const merged = new Map<string, unknown>();\n for (const id of ids) {\n const [base, local, remote] = pick(id);\n const value = mergeNode(base, local, remote, schemaOf(id), [...path, id], context, deleteWins);\n if (value !== undefined) merged.set(id, value);\n }\n return merged;\n};\n\nconst orderedIds = (local: Iterable<string>, remote: Iterable<string>, base: Iterable<string>) => [\n ...new Set([...local, ...remote, ...base]),\n];\n\nconst mergeNode = (\n base: unknown,\n local: unknown,\n remote: unknown,\n schema: Schema,\n path: Path,\n context: Context,\n deleteWins: boolean,\n): unknown => {\n if (schema.type === 'local') return local;\n if (equal(local, remote)) return local;\n if (equal(local, base)) return remote;\n if (equal(remote, base)) return local;\n\n if (local === undefined || remote === undefined) {\n if (deleteWins) return undefined;\n const taken: Side = local === undefined ? 'remote' : 'local';\n context.conflicts.push({ path, kind: 'delete-edit', base, local, remote, taken });\n return taken === 'local' ? local : remote;\n }\n\n switch (schema.type) {\n case 'max': {\n const l = schema.order(local);\n const r = schema.order(remote);\n if (l === r) return record(base, local, remote, path, context);\n return l > r ? local : remote;\n }\n case 'join':\n if (Array.isArray(local) && Array.isArray(remote)) {\n return joinLists(Array.isArray(base) ? base : [], local, remote);\n }\n return record(base, local, remote, path, context);\n case 'object': {\n if (!isObject(local) || !isObject(remote)) return record(base, local, remote, path, context);\n const baseObject = isObject(base) ? base : {};\n const ids = orderedIds(Object.keys(local), Object.keys(remote), Object.keys(baseObject));\n const merged = mergeEntries(\n ids,\n (id) => [own(baseObject, id), own(local, id), own(remote, id)],\n (id) => own(schema.fields, id) ?? schema.each ?? { type: 'record' },\n path,\n context,\n deleteWins || schema.deleteWins === true,\n );\n return Object.fromEntries(merged);\n }\n case 'keyed': {\n const localEntries = Array.isArray(local) ? entriesOf(local, schema.key) : null;\n const remoteEntries = Array.isArray(remote) ? entriesOf(remote, schema.key) : null;\n const baseEntries = (Array.isArray(base) ? entriesOf(base, schema.key) : null) ?? new Map();\n if (!localEntries || !remoteEntries) return record(base, local, remote, path, context);\n const ids = orderedIds(localEntries.keys(), remoteEntries.keys(), baseEntries.keys());\n const merged = mergeEntries(\n ids,\n (id) => [baseEntries.get(id), localEntries.get(id), remoteEntries.get(id)],\n () => schema.item,\n path,\n context,\n deleteWins || schema.deleteWins === true,\n );\n return [...merged.values()];\n }\n case 'record':\n return record(base, local, remote, path, context);\n }\n};\n\n/**\n * Merges the data of this device and the data of the cloud against the data of the last sync.\n * A part that only one side changed takes that change. A part that both sides changed merges by its\n * schema, and a part that cannot merge is reported in `conflicts` with the preferred side already\n * in `merged`.\n */\nexport const merge = ({\n base,\n local,\n remote,\n schema,\n prefer = 'remote',\n}: MergeInput): MergeResult => {\n const context: Context = { prefer, conflicts: [] };\n const merged = mergeNode(base, local, remote, schema, [], context, false);\n return {\n merged,\n cloud: restoreCloudValues(merged, remote, schema),\n conflicts: context.conflicts,\n };\n};\n","import { isObject } from './equal';\nimport { Conflict, Schema, Side } from './types';\n\nconst own = (value: Record<string, unknown>, key: string): unknown =>\n Object.hasOwn(value, key) ? value[key] : undefined;\n\nconst childSchema = (schema: Schema, step: string): Schema => {\n if (schema.type === 'object') {\n const named =\n schema.fields && Object.hasOwn(schema.fields, step) ? schema.fields[step] : undefined;\n return named ?? schema.each ?? { type: 'record' };\n }\n return { type: 'record' };\n};\n\nconst setAt = (data: unknown, schema: Schema, path: string[], value: unknown): unknown => {\n if (path.length === 0) return value;\n const [step, ...rest] = path;\n if (schema.type === 'keyed') {\n const list = Array.isArray(data) ? data : [];\n const index = list.findIndex((item) => isObject(item) && item[schema.key] === step);\n const next = setAt(index >= 0 ? list[index] : undefined, schema.item, rest, value);\n if (index < 0) return next === undefined ? list : [...list, next];\n if (next === undefined) return list.filter((_, i) => i !== index);\n return list.map((item, i) => (i === index ? next : item));\n }\n const object = isObject(data) ? data : {};\n const next = setAt(own(object, step), childSchema(schema, step), rest, value);\n if (next === undefined) {\n return Object.fromEntries(Object.entries(object).filter(([key]) => key !== step));\n }\n return { ...object, [step]: next };\n};\n\n/**\n * Puts the value of one side of a conflict into the merged data. The player uses this to pick the\n * side that the merge did not take. A side that holds no value removes the entry.\n */\nexport const resolveConflict = (\n merged: unknown,\n schema: Schema,\n conflict: Conflict,\n side: Side,\n): unknown => setAt(merged, schema, conflict.path, conflict[side]);\n","const CHUNK = 0x8000;\n\n/** Encodes text as base64 from its UTF-8 bytes. It works in the browser and in Node. */\nexport const encodeBase64 = (text: string): string => {\n const bytes = new TextEncoder().encode(text);\n let binary = '';\n for (let i = 0; i < bytes.length; i += CHUNK) {\n binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));\n }\n return btoa(binary);\n};\n\n/** Decodes base64 to text. Line breaks in the base64 text are ignored. */\nexport const decodeBase64 = (base64: string): string => {\n const binary = atob(base64.replace(/\\s/g, ''));\n return new TextDecoder().decode(Uint8Array.from(binary, (char) => char.charCodeAt(0)));\n};\n","/** A file in the cloud. The version changes with every write and settles which write is newer. */\nexport interface RemoteFile {\n content: string;\n version: string;\n}\n\n/**\n * Where the files of an app are kept. A provider reads, writes and removes whole files by path, and\n * refuses a write that was made from an old version of the file. That refusal is what keeps two\n * devices from overwriting each other.\n */\nexport interface SyncProvider {\n /** Returns null when the file does not exist. */\n read(path: string): Promise<RemoteFile | null>;\n /**\n * Writes the file. The expected version is the version that was read, or null to create a file\n * that must not exist yet. Throws `VersionConflictError` when the file changed since.\n */\n write(\n path: string,\n content: string,\n expectedVersion: string | null,\n ): Promise<{ version: string }>;\n /** Removes the file. Throws `VersionConflictError` when the file changed since. */\n remove(path: string, expectedVersion: string): Promise<void>;\n}\n\n/** The file changed in the cloud after it was read. Read it again and merge. */\nexport class VersionConflictError extends Error {\n constructor(public readonly path: string) {\n super(`The file ${path} changed in the cloud.`);\n this.name = 'VersionConflictError';\n }\n}\n\nexport type ProviderErrorKind =\n /** The token is missing, expired, revoked or has no permission. The player signs in again. */\n | 'auth'\n /** The provider limits the calls for now. Try again after `retryAfterSeconds`. */\n | 'rate-limit'\n /** The repository or folder does not exist, or the token cannot see it. */\n | 'not-found'\n /** The device is offline or the provider cannot be reached. */\n | 'network'\n /** The provider failed. A later try may work. */\n | 'server'\n /** The provider answered with something this package does not understand. */\n | 'invalid';\n\nexport class ProviderError extends Error {\n constructor(\n public readonly kind: ProviderErrorKind,\n message: string,\n public readonly status?: number,\n public readonly retryAfterSeconds?: number,\n ) {\n super(message);\n this.name = 'ProviderError';\n }\n}\n","import { decodeBase64, encodeBase64 } from './base64';\nimport { ProviderError, RemoteFile, SyncProvider, VersionConflictError } from './types';\n\nconst API = 'https://api.github.com';\nconst API_VERSION = '2022-11-28';\n\nexport interface GitHubOptions {\n /** The owner and the name of the repository, such as `chiefpansancolt/gamerdex-data`. */\n repository: string;\n /** Returns the token. It may refresh the token first, so it is called for every request. */\n getToken: () => Promise<string>;\n /** The branch to use. Without it GitHub uses the default branch of the repository. */\n branch?: string;\n /** The message of the commit that a write or a removal makes. */\n commitMessage?: (path: string) => string;\n fetch?: typeof fetch;\n}\n\nexport interface RepositoryCheck {\n private: boolean;\n canWrite: boolean;\n defaultBranch: string;\n}\n\ninterface ContentsResponse {\n sha?: unknown;\n content?: unknown;\n encoding?: unknown;\n}\n\nconst encodePath = (path: string): string => path.split('/').map(encodeURIComponent).join('/');\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst retryAfterOf = (response: Response): number | undefined => {\n const header = response.headers.get('retry-after');\n if (header !== null && Number.isFinite(Number(header))) return Number(header);\n const reset = response.headers.get('x-ratelimit-reset');\n if (reset !== null && Number.isFinite(Number(reset))) {\n return Math.max(0, Number(reset) - Math.floor(Date.now() / 1000));\n }\n return undefined;\n};\n\n/** Keeps the files of an app in a private GitHub repository, through the contents API. */\nexport class GitHubProvider implements SyncProvider {\n private readonly send: typeof fetch;\n\n constructor(private readonly options: GitHubOptions) {\n this.send = options.fetch ?? ((...args) => globalThis.fetch(...args));\n }\n\n async read(path: string): Promise<RemoteFile | null> {\n const response = await this.request('GET', this.contentsUrl(path));\n if (response.status === 404) return null;\n await this.assertOk(response);\n const body = (await response.json()) as ContentsResponse;\n if (typeof body.sha !== 'string') throw this.invalid('The file has no version.');\n if (body.encoding === 'base64' && typeof body.content === 'string') {\n return { content: decodeBase64(body.content), version: body.sha };\n }\n if (body.encoding === 'none') {\n const raw = await this.request('GET', this.contentsUrl(path), undefined, {\n Accept: 'application/vnd.github.raw+json',\n });\n await this.assertOk(raw);\n return { content: await raw.text(), version: body.sha };\n }\n throw this.invalid('The file content is not in a known encoding.');\n }\n\n async write(\n path: string,\n content: string,\n expectedVersion: string | null,\n ): Promise<{ version: string }> {\n const body: Record<string, unknown> = {\n message: this.message(path),\n content: encodeBase64(content),\n };\n if (expectedVersion !== null) body.sha = expectedVersion;\n if (this.options.branch) body.branch = this.options.branch;\n const response = await this.request('PUT', this.contentsUrl(path), body);\n if (response.status === 409 || response.status === 422) throw new VersionConflictError(path);\n await this.assertOk(response);\n const result = (await response.json()) as { content?: { sha?: unknown } };\n const version = result.content?.sha;\n if (typeof version !== 'string') throw this.invalid('The write returned no version.');\n return { version };\n }\n\n async remove(path: string, expectedVersion: string): Promise<void> {\n const body: Record<string, unknown> = { message: this.message(path), sha: expectedVersion };\n if (this.options.branch) body.branch = this.options.branch;\n const response = await this.request('DELETE', this.contentsUrl(path), body);\n if (response.status === 404) return;\n if (response.status === 409 || response.status === 422) throw new VersionConflictError(path);\n await this.assertOk(response);\n }\n\n /**\n * Checks that the repository can be used: it exists, the token can write to it, and it is\n * private. The saved data of a player must never sit in a public repository.\n */\n async checkRepository(): Promise<RepositoryCheck> {\n const response = await this.request('GET', `${API}/repos/${this.options.repository}`);\n await this.assertOk(response);\n const body: unknown = await response.json();\n if (!isRecord(body) || typeof body.private !== 'boolean') {\n throw this.invalid('The repository answer is not understood.');\n }\n const permissions = isRecord(body.permissions) ? body.permissions : {};\n return {\n private: body.private,\n canWrite: permissions.push === true,\n defaultBranch: typeof body.default_branch === 'string' ? body.default_branch : 'main',\n };\n }\n\n private contentsUrl(path: string): string {\n const base = `${API}/repos/${this.options.repository}/contents/${encodePath(path)}`;\n return this.options.branch ? `${base}?ref=${encodeURIComponent(this.options.branch)}` : base;\n }\n\n private message(path: string): string {\n return this.options.commitMessage?.(path) ?? `Gamerdex sync: ${path}`;\n }\n\n private async request(\n method: string,\n url: string,\n body?: Record<string, unknown>,\n headers: Record<string, string> = {},\n ): Promise<Response> {\n const token = await this.options.getToken();\n try {\n return await this.send(url, {\n method,\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${token}`,\n 'X-GitHub-Api-Version': API_VERSION,\n ...(body ? { 'Content-Type': 'application/json' } : {}),\n ...headers,\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch {\n throw new ProviderError('network', 'GitHub could not be reached.');\n }\n }\n\n private async assertOk(response: Response): Promise<void> {\n if (response.ok) return;\n const { status } = response;\n if (status === 401) throw new ProviderError('auth', 'The token is not valid.', status);\n if (status === 403 || status === 429) {\n const limited = status === 429 || response.headers.get('x-ratelimit-remaining') === '0';\n if (limited) {\n throw new ProviderError(\n 'rate-limit',\n 'GitHub limits the calls.',\n status,\n retryAfterOf(response),\n );\n }\n throw new ProviderError('auth', 'The token has no permission for the repository.', status);\n }\n if (status === 404)\n throw new ProviderError('not-found', 'The repository was not found.', status);\n if (status >= 500) throw new ProviderError('server', 'GitHub failed.', status);\n throw this.invalid(`GitHub answered ${status}.`, status);\n }\n\n private invalid(message: string, status?: number): ProviderError {\n return new ProviderError('invalid', message, status);\n }\n}\n","import { RemoteFile, SyncProvider, VersionConflictError } from './types';\n\n/**\n * A provider that keeps the files in memory. It follows the same version rules as a real provider,\n * so the sync flow of an app can be tested without a network.\n */\nexport class MemoryProvider implements SyncProvider {\n private files = new Map<string, { content: string; counter: number }>();\n private counter = 0;\n\n async read(path: string): Promise<RemoteFile | null> {\n const file = this.files.get(path);\n return file ? { content: file.content, version: String(file.counter) } : null;\n }\n\n async write(\n path: string,\n content: string,\n expectedVersion: string | null,\n ): Promise<{ version: string }> {\n const current = this.files.get(path);\n const currentVersion = current ? String(current.counter) : null;\n if (currentVersion !== expectedVersion) throw new VersionConflictError(path);\n this.counter += 1;\n this.files.set(path, { content, counter: this.counter });\n return { version: String(this.counter) };\n }\n\n async remove(path: string, expectedVersion: string): Promise<void> {\n const current = this.files.get(path);\n if (current && String(current.counter) !== expectedVersion) {\n throw new VersionConflictError(path);\n }\n this.files.delete(path);\n }\n\n /** The paths of the files that exist, in the order they were first written. */\n paths(): string[] {\n return [...this.files.keys()];\n }\n}\n","import { dataPath, metaPath } from '../layout';\nimport { restoreCloudValues } from '../merge/cloud';\nimport { equal } from '../merge/equal';\nimport { merge } from '../merge/merge';\nimport { Conflict, Schema } from '../merge/types';\nimport { emptyMeta, parseMeta, SyncMeta, withDevice } from '../meta';\nimport { SyncProvider, VersionConflictError } from '../provider/types';\n\nconst MAX_ATTEMPTS = 4;\n\n/**\n * What this device remembers about the last sync. The app keeps it in the browser, next to the\n * saved data but outside it, and passes it back on the next sync.\n */\nexport interface SyncState {\n /** The cloud data as this device last saw it. */\n base: unknown;\n dataVersion: string | null;\n metaVersion: string | null;\n}\n\n/** What to do when this device never synced and both the device and the cloud hold data. */\nexport type FirstSyncChoice = 'merge' | 'keep-local' | 'keep-cloud';\n\nexport interface SyncOptions {\n appId: string;\n deviceId: string;\n deviceName: string;\n provider: SyncProvider;\n schema: Schema;\n /** The data of this device. */\n local: unknown;\n /** The state kept after the last sync, or null when this device never synced. */\n state: SyncState | null;\n firstSync?: FirstSyncChoice;\n /** Returns true when the data is whole and safe to store. Called before any write. */\n validate?: (data: unknown) => boolean;\n /** Lets the app change the revisions of the meta file for this sync. */\n updateMeta?: (meta: SyncMeta) => SyncMeta;\n /** The current time as an ISO text. */\n now: () => string;\n}\n\nexport type SyncResult =\n | {\n status: 'synced';\n /** The data to store on this device. */\n data: unknown;\n /** The state to keep for the next sync. */\n state: SyncState;\n conflicts: Conflict[];\n /** True when the cloud changed during this sync. */\n uploaded: boolean;\n }\n | {\n /** The device and the cloud both hold data and the app has not said what to do. */\n status: 'needs-choice';\n local: unknown;\n remote: unknown;\n };\n\n/** The data in the cloud is not whole, so nothing was written. */\nexport class RemoteDataError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RemoteDataError';\n }\n}\n\n/** Other devices kept writing during every try, so the sync stopped. Try again later. */\nexport class SyncBusyError extends Error {\n constructor() {\n super('The cloud kept changing during the sync.');\n this.name = 'SyncBusyError';\n }\n}\n\nconst parseData = (text: string, validate: SyncOptions['validate']): unknown => {\n let data: unknown;\n try {\n data = JSON.parse(text);\n } catch {\n throw new RemoteDataError('The synced data is not valid JSON.');\n }\n if (validate && !validate(data)) throw new RemoteDataError('The synced data is not valid.');\n return data;\n};\n\nconst readMeta = (text: string | undefined, appId: string): SyncMeta => {\n if (text === undefined) return emptyMeta(appId);\n const parsed = parseMeta(text, appId);\n if (!parsed.ok) throw new RemoteDataError(parsed.error);\n return parsed.meta;\n};\n\nconst writeMeta = async (options: SyncOptions): Promise<string> => {\n const { provider, appId, deviceId, deviceName, updateMeta, now } = options;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {\n const current = await provider.read(metaPath(appId));\n const meta = readMeta(current?.content, appId);\n const time = now();\n const updated = withDevice(updateMeta ? updateMeta(meta) : meta, deviceId, {\n name: deviceName,\n lastSeenAt: time,\n });\n try {\n const written = await provider.write(\n metaPath(appId),\n JSON.stringify({ ...updated, lastSyncAt: time }, null, 2),\n current?.version ?? null,\n );\n return written.version;\n } catch (error) {\n if (!(error instanceof VersionConflictError)) throw error;\n }\n }\n throw new SyncBusyError();\n};\n\n/**\n * Brings this device and the cloud to the same data. It reads the cloud, merges the three copies,\n * writes the cloud copy when it changed, and returns the data to store on this device with the\n * state to keep. It never changes the device by itself, and it does not write when the cloud data\n * or the merged data fail `validate`.\n *\n * Throws `ProviderError` when the provider fails, `RemoteDataError` when the cloud data is not\n * whole, and `SyncBusyError` when other devices keep writing.\n */\nexport const sync = async (options: SyncOptions): Promise<SyncResult> => {\n const { appId, provider, schema, local, state, firstSync, validate } = options;\n const path = dataPath(appId);\n\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {\n const file = await provider.read(path);\n\n if (file === null) {\n if (validate && !validate(local))\n throw new RemoteDataError('The data of this device is not valid.');\n const cloud = restoreCloudValues(local, undefined, schema);\n const created = await tryWrite(provider, path, cloud, null);\n if (created === null) continue;\n const metaVersion = await writeMeta(options);\n return done(local, cloud, created, metaVersion, [], true);\n }\n\n const remote = parseData(file.content, validate);\n const differs = state === null && !equal(restoreCloudValues(local, remote, schema), remote);\n const choice = differs ? firstSync : undefined;\n if (differs && choice === undefined) return { status: 'needs-choice', local, remote };\n\n const base = state === null ? undefined : state.base;\n const merged = mergeFor(choice, { base, local, remote, schema });\n if (validate && !validate(merged.merged)) {\n throw new RemoteDataError('The merged data is not valid.');\n }\n\n if (equal(merged.cloud, remote)) {\n const metaVersion = await writeMeta(options);\n return done(merged.merged, merged.cloud, file.version, metaVersion, merged.conflicts, false);\n }\n\n const version = await tryWrite(provider, path, merged.cloud, file.version);\n if (version === null) continue;\n const metaVersion = await writeMeta(options);\n return done(merged.merged, merged.cloud, version, metaVersion, merged.conflicts, true);\n }\n throw new SyncBusyError();\n};\n\nconst mergeFor = (\n choice: FirstSyncChoice | undefined,\n input: { base: unknown; local: unknown; remote: unknown; schema: Schema },\n) => {\n if (choice === 'keep-local') {\n const cloud = restoreCloudValues(input.local, input.remote, input.schema);\n return { merged: input.local, cloud, conflicts: [] };\n }\n if (choice === 'keep-cloud') {\n const merged = restoreCloudValues(input.remote, input.local, input.schema);\n return { merged, cloud: input.remote, conflicts: [] };\n }\n return merge(input);\n};\n\nconst tryWrite = async (\n provider: SyncProvider,\n path: string,\n data: unknown,\n version: string | null,\n): Promise<string | null> => {\n try {\n return (await provider.write(path, JSON.stringify(data, null, 2), version)).version;\n } catch (error) {\n if (error instanceof VersionConflictError) return null;\n throw error;\n }\n};\n\nconst done = (\n data: unknown,\n cloud: unknown,\n dataVersion: string,\n metaVersion: string,\n conflicts: Conflict[],\n uploaded: boolean,\n): SyncResult => ({\n status: 'synced',\n data,\n state: { base: cloud, dataVersion, metaVersion },\n conflicts,\n uploaded,\n});\n","/** The public values of the Gamerdex Sync GitHub App. The client secret is never kept here. */\nexport const GITHUB_APP = {\n slug: 'gamerdex-sync',\n clientId: 'Iv23liwzjj3x8O2K5ALA',\n} as const;\n","/** The tokens of a signed in player. Times are milliseconds since 1970. */\nexport interface StoredTokens {\n accessToken: string;\n accessExpiresAt: number;\n refreshToken: string;\n refreshExpiresAt: number;\n}\n\n/** The answer of the token function, with lifetimes in seconds. */\nexport interface TokenResponse {\n accessToken: string;\n expiresIn: number;\n refreshToken: string;\n refreshExpiresIn: number;\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst isSeconds = (value: unknown): value is number =>\n typeof value === 'number' && Number.isFinite(value) && value > 0;\n\nexport const parseTokenResponse = (body: unknown): TokenResponse | null => {\n if (!isRecord(body)) return null;\n const { accessToken, expiresIn, refreshToken, refreshExpiresIn } = body;\n if (typeof accessToken !== 'string' || accessToken === '') return null;\n if (typeof refreshToken !== 'string' || refreshToken === '') return null;\n if (!isSeconds(expiresIn) || !isSeconds(refreshExpiresIn)) return null;\n return { accessToken, expiresIn, refreshToken, refreshExpiresIn };\n};\n\nexport const toStoredTokens = (response: TokenResponse, nowMs: number): StoredTokens => ({\n accessToken: response.accessToken,\n accessExpiresAt: nowMs + response.expiresIn * 1000,\n refreshToken: response.refreshToken,\n refreshExpiresAt: nowMs + response.refreshExpiresIn * 1000,\n});\n","import { ProviderError } from '../provider/types';\nimport { parseTokenResponse, StoredTokens, toStoredTokens } from './tokens';\n\nconst RENEW_BEFORE_MS = 60_000;\n\nexport interface TokenSourceOptions {\n /** The address of the token function, without a trailing slash. */\n endpoint: string;\n /** Reads the tokens that this device saved, or null when the player is signed out. */\n load: () => StoredTokens | null;\n /** Saves the tokens, or removes them when null. */\n save: (tokens: StoredTokens | null) => void;\n now: () => number;\n fetch?: typeof fetch;\n}\n\n/**\n * Keeps a player signed in. `getToken` returns a token that is valid for at least a minute and\n * renews it first when needed. It is meant to be the `getToken` of `GitHubProvider`.\n */\nexport const createTokenSource = (options: TokenSourceOptions) => {\n const send = options.fetch ?? ((...args) => globalThis.fetch(...args));\n let renewing: Promise<StoredTokens> | null = null;\n\n const post = async (path: string, body: Record<string, string>): Promise<StoredTokens> => {\n let response: Response;\n try {\n response = await send(`${options.endpoint}/${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n } catch {\n throw new ProviderError('network', 'The sign in service could not be reached.');\n }\n if (response.status >= 500) throw new ProviderError('server', 'The sign in service failed.');\n const parsed = response.ok ? parseTokenResponse(await response.json().catch(() => null)) : null;\n if (parsed === null)\n throw new ProviderError('auth', 'The sign in was refused.', response.status);\n const tokens = toStoredTokens(parsed, options.now());\n options.save(tokens);\n return tokens;\n };\n\n const renew = (tokens: StoredTokens): Promise<StoredTokens> => {\n if (tokens.refreshExpiresAt <= options.now()) {\n options.save(null);\n throw new ProviderError('auth', 'The sign in expired.');\n }\n renewing ??= post('refresh', { refreshToken: tokens.refreshToken })\n .catch((error: unknown) => {\n if (error instanceof ProviderError && error.kind === 'auth') options.save(null);\n throw error;\n })\n .finally(() => {\n renewing = null;\n });\n return renewing;\n };\n\n return {\n /** Finishes a sign in with the code from the callback page. */\n signIn: (code: string, redirectUri: string): Promise<StoredTokens> =>\n post('token', { code, redirectUri }),\n signOut: (): void => options.save(null),\n isSignedIn: (): boolean => options.load() !== null,\n getToken: async (): Promise<string> => {\n const tokens = options.load();\n if (tokens === null) throw new ProviderError('auth', 'The player is not signed in.');\n if (tokens.accessExpiresAt - RENEW_BEFORE_MS > options.now()) return tokens.accessToken;\n return (await renew(tokens)).accessToken;\n },\n };\n};\n","import { GITHUB_APP } from './app';\n\n/** A random value that ties a sign-in answer to the sign-in that this device started. */\nexport const newState = (): string => crypto.randomUUID();\n\nexport const authorizeUrl = (redirectUri: string, state: string): string => {\n const query = new URLSearchParams({\n client_id: GITHUB_APP.clientId,\n redirect_uri: redirectUri,\n state,\n });\n return `https://github.com/login/oauth/authorize?${query}`;\n};\n\n/** The page where the player picks the repository that the app may use. */\nexport const installUrl = (state: string): string =>\n `https://github.com/apps/${GITHUB_APP.slug}/installations/new?${new URLSearchParams({ state })}`;\n\nexport type CallbackResult =\n { ok: true; code: string } | { ok: false; reason: 'denied' | 'state' | 'missing' };\n\n/**\n * Reads the query of the sign-in callback page. The state must equal the state that was saved when\n * the sign-in started, or the answer is refused.\n */\nexport const readCallback = (search: string, expectedState: string | null): CallbackResult => {\n const query = new URLSearchParams(search);\n if (query.get('error') !== null) return { ok: false, reason: 'denied' };\n const code = query.get('code');\n if (code === null || code === '') return { ok: false, reason: 'missing' };\n if (expectedState === null || query.get('state') !== expectedState) {\n return { ok: false, reason: 'state' };\n }\n return { ok: true, code };\n};\n","import { ProviderError } from './types';\n\nexport interface InstalledRepository {\n /** The owner and the name, such as `chiefpansancolt/gamerdex-data`. */\n fullName: string;\n private: boolean;\n}\n\nconst API = 'https://api.github.com';\nconst PAGE_SIZE = 100;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nconst getJson = async (\n send: typeof fetch,\n token: string,\n url: string,\n): Promise<Record<string, unknown>> => {\n let response: Response;\n try {\n response = await send(url, {\n headers: {\n Accept: 'application/vnd.github+json',\n Authorization: `Bearer ${token}`,\n 'X-GitHub-Api-Version': '2022-11-28',\n },\n });\n } catch {\n throw new ProviderError('network', 'GitHub could not be reached.');\n }\n if (response.status === 401 || response.status === 403) {\n throw new ProviderError('auth', 'The token is not valid.', response.status);\n }\n if (response.status >= 500) throw new ProviderError('server', 'GitHub failed.', response.status);\n const body: unknown = response.ok ? await response.json().catch(() => null) : null;\n if (!isRecord(body))\n throw new ProviderError('invalid', 'GitHub answer is not understood.', response.status);\n return body;\n};\n\n/**\n * Lists the repositories that the player gave to the GitHub App, for the first 100 installations\n * and the first 100 repositories of each. The player picks the repository for the synced data.\n */\nexport const listInstalledRepositories = async (\n token: string,\n send: typeof fetch = (...args) => globalThis.fetch(...args),\n): Promise<InstalledRepository[]> => {\n const installations = await getJson(\n send,\n token,\n `${API}/user/installations?per_page=${PAGE_SIZE}`,\n );\n const ids = (Array.isArray(installations.installations) ? installations.installations : [])\n .filter(isRecord)\n .map((installation) => installation.id)\n .filter((id): id is number => typeof id === 'number');\n\n const repositories: InstalledRepository[] = [];\n for (const id of ids) {\n const page = await getJson(\n send,\n token,\n `${API}/user/installations/${id}/repositories?per_page=${PAGE_SIZE}`,\n );\n for (const item of Array.isArray(page.repositories) ? page.repositories : []) {\n if (\n isRecord(item) &&\n typeof item.full_name === 'string' &&\n typeof item.private === 'boolean'\n ) {\n repositories.push({ fullName: item.full_name, private: item.private });\n }\n }\n }\n return repositories;\n};\n"],"mappings":"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","names":["own","own","childSchema","next","isRecord","metaVersion","isRecord","API","isRecord"]}
|