@drakkar.software/octospaces-sdk 0.1.0 → 0.4.3
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 +200 -0
- package/dist/index.d.ts +481 -274
- package/dist/index.js +1000 -493
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/types.ts +50 -83
- package/src/index.ts +62 -28
- package/src/objects/objects.test.ts +55 -95
- package/src/objects/objects.ts +23 -136
- package/src/spaces/members.test.ts +10 -3
- package/src/spaces/members.ts +86 -49
- package/src/spaces/nodes.test.ts +225 -0
- package/src/spaces/nodes.ts +427 -0
- package/src/spaces/object-index.test.ts +127 -71
- package/src/spaces/object-index.ts +61 -107
- package/src/spaces/registry.test.ts +59 -46
- package/src/spaces/registry.ts +28 -47
- package/src/sync/client.ts +20 -15
- package/src/sync/pairing.ts +10 -12
- package/src/sync/paths.test.ts +124 -16
- package/src/sync/paths.ts +73 -32
- package/src/sync/space-access-store.ts +17 -0
- package/src/sync/space-access.ts +112 -67
- package/src/utils/invite-preview.test.ts +169 -0
- package/src/utils/invite-preview.ts +101 -0
- package/src/utils/live-sync-bus.test.ts +116 -0
- package/src/utils/live-sync-bus.ts +71 -0
- package/src/utils/search-match.test.ts +149 -0
- package/src/utils/search-match.ts +145 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/core/adapters.ts","../src/core/ids.ts","../src/sync/paths.ts","../src/sync/client.ts","../src/sync/fetch-timeout.ts","../src/sync/pull-cache.ts","../src/sync/profile-cache.ts","../src/core/space-access-error.ts","../src/sync/identity.ts","../src/sync/account-seal.ts","../src/sync/space-access-store.ts","../src/sync/space-access.ts","../src/spaces/registry.ts","../src/objects/objects.ts","../src/spaces/object-index.ts","../src/spaces/members.ts","../src/sync/base64url.ts","../src/sync/pairing.ts","../src/sync/base64.ts"],"sourcesContent":["/**\n * Runtime configuration for the OctoSpaces SDK — the Starfish sync server URL,\n * optional namespace, events-stream URL, and public web origin.\n *\n * The SDK is headless and platform-agnostic, so it does NOT read environment\n * variables itself. The host app reads its own env (e.g. Expo `EXPO_PUBLIC_*`) and\n * calls {@link configureOctoSpaces} once at boot, before any sync/identity API runs.\n * Getters throw a clear error if called before configuration so a misconfigured\n * host fails fast rather than silently signing the wrong path.\n */\nexport interface OctoSpacesConfig {\n /** Starfish sync server base URL (e.g. `http://localhost:8787`). */\n syncBase: string;\n /** Bare namespace name; the SDK prepends `/v1/<namespace>` to signed paths.\n * Unset for a root-mounted (local dev) server. */\n syncNamespace?: string;\n /**\n * Optional SEPARATE namespace for cross-app shared-spaces storage. When set,\n * space registry ops use this namespace instead of `syncNamespace`, enabling a\n * single shared space list across multiple app namespaces (e.g. OctoChat and\n * OctoVault sharing spaces at `/v1/shared`). If unset, falls back to the default\n * namespace for all operations (single-app behavior).\n */\n sharedSpacesNamespace?: string;\n /** Override the live change-event SSE endpoint. Defaults to\n * `${syncBase}${syncPrefix}/events`. */\n eventsUrl?: string;\n /** Public origin of the web app, used to build shareable invite links on\n * platforms without `window.location` (native). Empty by default. */\n webBase?: string;\n /**\n * Called when a background Starfish revalidation succeeds after a 429/5xx\n * cache-fallback (stale-while-revalidate). Use it to signal that the server\n * is reachable again so any stale views re-pull and recover.\n */\n onServerReachable?: () => void;\n}\n\nlet cfg: OctoSpacesConfig | null = null;\n\n/** Configure the SDK. Call once at app boot before any sync/identity API. */\nexport function configureOctoSpaces(config: OctoSpacesConfig): void {\n // Guard against the common mistake of passing `namespace` (wrong key) instead of\n // `syncNamespace`. TypeScript's excess-property check is bypassed when the config\n // is assembled via a conditional spread, so the wrong key would be silently ignored.\n if ('namespace' in config && !config.syncNamespace) {\n throw new Error(\n `octospaces-sdk: configureOctoSpaces received \"namespace\" — did you mean \"syncNamespace\"?`,\n );\n }\n const ns = (config.syncNamespace ?? '').trim();\n if (ns !== '' && !/^[A-Za-z0-9_-]+$/.test(ns)) {\n throw new Error(`octospaces-sdk: syncNamespace must be a bare name ([A-Za-z0-9_-]+), got \"${ns}\"`);\n }\n const sharedNs = (config.sharedSpacesNamespace ?? '').trim();\n if (sharedNs !== '' && !/^[A-Za-z0-9_-]+$/.test(sharedNs)) {\n throw new Error(`octospaces-sdk: sharedSpacesNamespace must be a bare name ([A-Za-z0-9_-]+), got \"${sharedNs}\"`);\n }\n cfg = {\n ...config,\n syncNamespace: ns || undefined,\n sharedSpacesNamespace: sharedNs || undefined,\n };\n}\n\nfunction req(): OctoSpacesConfig {\n if (!cfg) throw new Error('octospaces-sdk: configureOctoSpaces() not called — wire it at app boot.');\n return cfg;\n}\n\n/** Starfish sync server base URL. */\nexport const getSyncBase = (): string => req().syncBase;\n/** Bare namespace name (or `undefined` for a root-mounted server). */\nexport const getSyncNamespace = (): string | undefined => req().syncNamespace;\n/** Namespaced path prefix (`/v1/<namespace>`, or `''` locally). */\nexport const getSyncPrefix = (): string => {\n const ns = req().syncNamespace;\n return ns ? `/v1/${ns}` : '';\n};\n/** Optional separate namespace for shared-spaces storage. `undefined` means use the default namespace. */\nexport const getSharedSpacesNamespace = (): string | undefined => cfg?.sharedSpacesNamespace;\n/** Live change-event SSE endpoint. */\nexport const getEventsUrl = (): string => req().eventsUrl ?? `${getSyncBase()}${getSyncPrefix()}/events`;\n/** Public web origin (right-trimmed of trailing slashes; `''` by default). */\nexport const getWebBase = (): string => (req().webBase ?? '').replace(/\\/+$/, '');\n/** Callback to invoke when a background Starfish revalidation succeeds. */\nexport const getOnServerReachable = (): (() => void) | undefined => cfg?.onServerReachable;\n","/**\n * Platform adapters the headless SDK needs the host app to provide.\n *\n * The SDK can't do Metro `.native.ts` file-extension resolution and must not bind\n * to localStorage / AsyncStorage / SecureStore directly, so the host injects a\n * key/value store at boot via {@link configureKv}. This holds account-scoped state\n * the SDK persists offline (joined-space member caps, the public-space access map,\n * read marks, mutes, profile/pull caches).\n */\n\n/** Async key/value store — web `localStorage`, native `AsyncStorage`, etc. */\nexport interface KvAdapter {\n get(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n remove(key: string): Promise<void>;\n}\n\nlet kv: KvAdapter | null = null;\n\n/** Install the host's key/value store. Call once at app boot. */\nexport function configureKv(adapter: KvAdapter): void {\n kv = adapter;\n}\n\n/** The configured KV store, or throw if the host never called {@link configureKv}. */\nexport function getKv(): KvAdapter {\n if (!kv) throw new Error('octospaces-sdk: configureKv() not called — wire it at app boot.');\n return kv;\n}\n\n// Free-function shims matching the historical `kv` module surface.\nexport const kvGet = (key: string): Promise<string | null> => getKv().get(key);\nexport const kvSet = (key: string, value: string): Promise<void> => getKv().set(key, value);\nexport const kvRemove = (key: string): Promise<void> => getKv().remove(key);\n","/**\n * Identifier helpers — one source for unguessable ids.\n *\n * `randomId()` is a CSPRNG-backed 128-bit id (16 random bytes, hex). Use it for\n * EVERY storage/space/room/object/blob id. Hex output is path-safe and server-safe.\n */\nexport function randomId(): string {\n const bytes = new Uint8Array(16);\n globalThis.crypto.getRandomValues(bytes);\n let s = '';\n for (const b of bytes) s += b.toString(16).padStart(2, '0');\n return s;\n}\n\n/**\n * Slug for the human part of an id (e.g. `<spaceId>-<slug>-<ts>`). Restricted to\n * URL-clean `[a-z0-9-]` so the id is safe as both a URL path segment and a\n * server storage-path leaf (the server's FilesystemObjectStore rejects any key\n * outside `[a-zA-Z0-9._:@/-]`). Falls back to `'room'` when a name strips to nothing.\n */\nexport function roomSlug(name: string): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'room'\n );\n}\n","/**\n * Collection path + cap-scope helpers (merged from OctoChat + OctoVault).\n *\n * Paths are signed relative to SYNC_BASE; the server mounts the sync router at\n * root, so they start with /pull or /push. Everything for a space is nested under\n * `spaces/{spaceId}/…` so the `{spaceId}` segment gates it all uniformly through the\n * space:owner/space:member enricher, and a single `spaces/{spaceId}/**` member cap\n * covers a whole space.\n *\n * **Generic object collections** — scopes use the `obj*` collection names (the\n * domain-neutral storage layer both apps migrate onto). App-specific collection\n * names like `'chat'` are left for the consumer's own `paths.ts` extension until\n * that app finishes migrating.\n */\nimport type { ScopePreset } from '@drakkar.software/starfish-identities';\n\nconst pull = (rest: string) => `/pull/${rest}`;\nconst push = (rest: string) => `/push/${rest}`;\n\n/** A room id is `sp-<rand>-<name>`; the space is its first two `-` segments. */\nexport const spaceIdFromRoomId = (roomId: string) => roomId.split('-').slice(0, 2).join('-');\n\n// ── Space-wide keyring (one per space, shared by all its channels) ────────────\nexport const keyringName = (spaceId: string) => `spaces/${spaceId}`;\nexport const keyringPull = (spaceId: string) => pull(`${keyringName(spaceId)}/_keyring`);\nexport const keyringPush = (spaceId: string) => push(`${keyringName(spaceId)}/_keyring`);\n\n// ── Attachments (sealed blobs, in a per-space subtree keyed by room) ──────────\n/** Storage path of one attachment blob — also the AAD bound into its seal. */\nexport const attachmentName = (roomId: string, blobId: string) =>\n `spaces/${spaceIdFromRoomId(roomId)}/attachments/${roomId}/${blobId}`;\nexport const attachmentPull = (roomId: string, blobId: string) => pull(attachmentName(roomId, blobId));\nexport const attachmentPush = (roomId: string, blobId: string) => push(attachmentName(roomId, blobId));\n\n// ── Profile + registries ──────────────────────────────────────────────────────\nexport const profilePull = (userId: string) => pull(`user/${userId}/profile`);\nexport const profilePush = (userId: string) => push(`user/${userId}/profile`);\n\nexport const spacesPull = (userId: string) => pull(`user/${userId}/_spaces`);\nexport const spacesPush = (userId: string) => push(`user/${userId}/_spaces`);\n\nexport const roomsRegistryPull = (spaceId: string) => pull(`spaces/${spaceId}/_rooms`);\nexport const roomsRegistryPush = (spaceId: string) => push(`spaces/${spaceId}/_rooms`);\n\n// ── Unified Object index + content (private/E2EE) ─────────────────────────────\n// ALL Object content lives in one generic path family — no type-specific prefixes:\n//\n// objects/_index — union-merged ObjectNode tree (every Object in the space)\n// objects/logs/{id} — WAL/CRDT append-only op-log (contentKind \"append\")\n// objects/logs/{id}__snapshot — sibling LWW snapshot for fast cold-start\n// objects/docs/{id} — LWW merge-doc (contentKind \"merge\": records, captions)\n// objects/blobs/{id} — sealed raw binary blob (file/image objects)\n//\n// Keep in sync with the objindex/objlog/objsnap/objdoc/objblob collections in\n// apps/server AND Infra collections.py.\nexport const objIndexName = (spaceId: string) => `spaces/${spaceId}/objects/_index`;\nexport const objIndexPull = (spaceId: string) => pull(objIndexName(spaceId));\nexport const objIndexPush = (spaceId: string) => push(objIndexName(spaceId));\n\nexport const objLogName = (spaceId: string, objectId: string) => `spaces/${spaceId}/objects/logs/${objectId}`;\nexport const objLogPull = (spaceId: string, objectId: string) => pull(objLogName(spaceId, objectId));\nexport const objLogPush = (spaceId: string, objectId: string) => push(objLogName(spaceId, objectId));\n\nexport const objDocName = (spaceId: string, objectId: string) => `spaces/${spaceId}/objects/docs/${objectId}`;\nexport const objDocPull = (spaceId: string, objectId: string) => pull(objDocName(spaceId, objectId));\nexport const objDocPush = (spaceId: string, objectId: string) => push(objDocName(spaceId, objectId));\n\n/** Storage path of one sealed object blob — also the AAD bound into its seal. */\nexport const objectBlobName = (spaceId: string, blobId: string) => `spaces/${spaceId}/objects/blobs/${blobId}`;\nexport const objectBlobPull = (spaceId: string, blobId: string) => pull(objectBlobName(spaceId, blobId));\nexport const objectBlobPush = (spaceId: string, blobId: string) => push(objectBlobName(spaceId, blobId));\n\n// ── Per-space custom type registry (private/E2EE) ─────────────────────────────\nexport const typesIndexName = (spaceId: string) => `spaces/${spaceId}/types/_index`;\nexport const typesIndexPull = (spaceId: string) => pull(typesIndexName(spaceId));\nexport const typesIndexPush = (spaceId: string) => push(typesIndexName(spaceId));\n\n// ── Public-space directory index (server-maintained projection) ───────────────\nexport const spaceIndexName = (shard: 'public') => `_index/spaces/${shard}`;\nexport const spaceIndexPull = (shard: 'public') => pull(spaceIndexName(shard));\n\n// ── Generic object collections — used in cap scopes ──────────────────────────\n// These are the domain-neutral storage collections both apps migrate onto. The\n// server ignores unrecognized collection names, so a cap minted with this set still\n// authorizes an app whose data currently lives under a legacy collection name during\n// the migration transition.\nexport const OBJECT_COLLECTIONS: string[] = [\n 'keyring', 'objindex', 'objlog', 'objsnap', 'objdoc', 'objblob', 'typeindex',\n];\n\n// ── Cap scopes ────────────────────────────────────────────────────────────────\n\n/** Full owner/device access to every space the identity owns. */\nexport function ownerScope(): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: OBJECT_COLLECTIONS,\n paths: ['spaces/**'],\n };\n}\n\n/**\n * Member access to one SPACE — its keyring + every channel's messages and\n * attachments + the room registry, all under `spaces/{spaceId}/**`. One cap\n * covers current AND future channels.\n */\nexport function spaceMemberScope(spaceId: string, canWrite: boolean): ScopePreset {\n const ops: ('read' | 'write' | 'list')[] = canWrite ? ['read', 'list', 'write'] : ['read', 'list'];\n return {\n ops,\n collections: OBJECT_COLLECTIONS,\n paths: [`spaces/${spaceId}/**`],\n };\n}\n\n/**\n * Personal cap: profile + space registry + device directory + all spaces.\n * Note: app-specific collections like `'dminbox'` (chat) are NOT included here —\n * add them in the consumer's own `paths.ts` extension.\n */\nexport function accountScope(userId: string): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: ['profile', 'devices', 'spaces', 'rooms'],\n paths: [\n `user/${userId}/profile`,\n `users/${userId}/_devices`,\n `user/${userId}/_spaces`,\n 'spaces/**',\n ],\n };\n}\n\n/**\n * The single cap-cert scope granted to a PAIRED (linked) device. Covers both the\n * object-store client (ownerScope) and the account client (accountScope), deduped,\n * because a paired device cannot self-mint — it presents one root-signed cap-cert.\n */\nexport function linkedDeviceScope(userId: string): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: [...OBJECT_COLLECTIONS, 'profile', 'devices', 'spaces', 'rooms'],\n paths: [\n 'spaces/**',\n `user/${userId}/profile`,\n `users/${userId}/_devices`,\n `user/${userId}/_spaces`,\n ],\n };\n}\n\n/** Extract the single space id a member cap is scoped to (from its `spaces/<id>/**`).\n * Returns null if the cap names no space path OR more than one distinct space. */\nexport function spaceIdFromCap(cap: { scope?: { paths?: string[] } }): string | null {\n let found: string | null = null;\n for (const p of cap.scope?.paths ?? []) {\n const m = /^spaces\\/([^/]+)\\//.exec(p);\n if (!m) continue;\n if (found !== null && found !== m[1]) return null;\n found = m[1]!;\n }\n return found;\n}\n\nexport function bytesToHex(b: Uint8Array): string {\n let s = '';\n for (const x of b) s += x.toString(16).padStart(2, '0');\n return s;\n}\n\n/** The canonical identity derivation: `userId = sha256(edPub)[0:32]` (hex). */\nexport async function userIdFromEdPub(edPubHex: string): Promise<string> {\n const bytes = new Uint8Array(edPubHex.length / 2);\n for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(edPubHex.slice(i * 2, i * 2 + 2), 16);\n const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return bytesToHex(new Uint8Array(digest)).slice(0, 32);\n}\n","/**\n * Starfish client construction + space keyring/encryptor helpers.\n */\nimport { StarfishClient } from '@drakkar.software/starfish-client';\nimport type { BatchPullEntry, Encryptor, StarfishCapProvider } from '@drakkar.software/starfish-client';\nimport { createKeyring, createKeyringEncryptor } from '@drakkar.software/starfish-keyring';\nimport type { Keyring } from '@drakkar.software/starfish-keyring';\nimport { signRequest, stableStringify } from '@drakkar.software/starfish-protocol';\nimport type { SignableMethod } from '@drakkar.software/starfish-protocol';\n\nimport { getSyncBase, getSyncNamespace, getSyncPrefix, getOnServerReachable } from '../core/config.js';\nimport { fetchWithTimeout } from './fetch-timeout.js';\nimport { pullCache, PULL_CACHE_MAX_AGE_MS } from './pull-cache.js';\nimport { cacheProfile, loadCachedProfile } from './profile-cache.js';\nimport { keyringPull, keyringPush, profilePull, profilePush } from './paths.js';\nimport { SpaceAccessError } from '../core/space-access-error.js';\n\nexport interface DeviceKeys {\n edPriv: string;\n edPub: string;\n kemPriv: string;\n kemPub: string;\n}\n\nexport function capProviderFor(cap: unknown, devEdPrivHex: string): StarfishCapProvider {\n return {\n async getCap() {\n return { cap: cap as never, devEdPrivHex };\n },\n };\n}\n\n/**\n * Build a Starfish client. `namespaceOverride` overrides the configured namespace,\n * enabling the shared-spaces feature (a separate namespace for cross-app registry ops).\n */\nexport function makeClient(cap: unknown, devEdPrivHex: string, namespaceOverride?: string): StarfishClient {\n return new StarfishClient({\n baseUrl: getSyncBase(),\n namespace: namespaceOverride ?? getSyncNamespace(),\n capProvider: capProviderFor(cap, devEdPrivHex),\n fetch: fetchWithTimeout(),\n cache: pullCache(),\n cacheMaxAgeMs: PULL_CACHE_MAX_AGE_MS,\n cacheFallbackStatuses: [429, 500, 502, 503, 504],\n onRevalidated: () => getOnServerReachable()?.(),\n });\n}\n\n/**\n * Open a SPACE's decryptor, throwing a descriptive error per failure mode\n * (unreachable server / no keyring yet / not a recipient).\n *\n * A `SpaceAccessError` is a hard access denial; any other thrown error is a\n * transient offline state.\n */\nexport async function openEncryptor(\n client: StarfishClient,\n keys: DeviceKeys,\n spaceId: string,\n trustedAdders: string[],\n): Promise<Encryptor> {\n const res = await client.pull(keyringPull(spaceId)).catch(() => {\n throw new Error('Could not reach the server to fetch space keys.');\n });\n const keyring = res?.data as unknown as Keyring | undefined;\n if (!keyring || !keyring.epochs) {\n throw new SpaceAccessError('This space has no keyring yet — ask the owner to open it first.');\n }\n try {\n const enc = await createKeyringEncryptor(\n keyring,\n { kemPubHex: keys.kemPub, kemPrivHex: keys.kemPriv },\n { trustedAdders },\n );\n return enc as unknown as Encryptor;\n } catch {\n throw new SpaceAccessError(\"You're not a recipient of this space's keyring yet — ask the owner to re-invite.\");\n }\n}\n\n/** Soft variant of {@link openEncryptor}: returns null instead of throwing. */\nexport async function buildEncryptor(\n client: StarfishClient,\n keys: DeviceKeys,\n spaceId: string,\n trustedAdders: string[],\n): Promise<Encryptor | null> {\n try {\n return await openEncryptor(client, keys, spaceId, trustedAdders);\n } catch {\n return null;\n }\n}\n\n/**\n * Owner-side: create the SPACE keyring if missing, return an encryptor.\n */\nexport async function ownerEnsureKeyring(\n client: StarfishClient,\n keys: DeviceKeys,\n spaceId: string,\n trustedAdders: string[] = [keys.edPub],\n): Promise<Encryptor> {\n const krRes = await client.pull(keyringPull(spaceId)).catch(() => null);\n let keyring = krRes?.data as unknown as Keyring | undefined;\n if (!keyring || !keyring.epochs) {\n const created = await createKeyring({ edPrivHex: keys.edPriv, edPubHex: keys.edPub }, [\n { subKemHex: keys.kemPub },\n ]);\n keyring = created.keyring;\n await client.push(keyringPush(spaceId), keyring as unknown as Record<string, unknown>, krRes?.hash ?? null);\n }\n const enc = await createKeyringEncryptor(\n keyring,\n { kemPubHex: keys.kemPub, kemPrivHex: keys.kemPriv },\n { trustedAdders },\n );\n return enc as unknown as Encryptor;\n}\n\n/** A user's public profile: display pseudo + optional inline avatar + public identity keys. */\nexport interface PublicProfile {\n pseudo: string | null;\n avatar: string | null;\n edPub: string | null;\n kemPub: string | null;\n}\n\n/** Read any user's public profile. */\nexport async function readProfile(userId: string): Promise<PublicProfile> {\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (!r.ok) return { pseudo: null, avatar: null, edPub: null, kemPub: null };\n const body = await r.json();\n const data = body?.data as { pseudo?: unknown; avatar?: unknown; edPub?: unknown; kemPub?: unknown } | undefined;\n const profile: PublicProfile = {\n pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null,\n avatar: typeof data?.avatar === 'string' ? data.avatar : null,\n edPub: typeof data?.edPub === 'string' ? data.edPub : null,\n kemPub: typeof data?.kemPub === 'string' ? data.kemPub : null,\n };\n cacheProfile(userId, profile);\n return profile;\n } catch {\n return (await loadCachedProfile(userId)) ?? { pseudo: null, avatar: null, edPub: null, kemPub: null };\n }\n}\n\n/** Read any user's public profile pseudo. */\nexport async function readPseudo(userId: string): Promise<string | null> {\n return (await readProfile(userId)).pseudo;\n}\n\nlet profileBatchClient: StarfishClient | undefined;\nfunction getProfileBatchClient(): StarfishClient {\n if (!profileBatchClient) {\n profileBatchClient = new StarfishClient({ baseUrl: getSyncBase(), namespace: getSyncNamespace(), fetch: fetchWithTimeout() });\n }\n return profileBatchClient;\n}\n\nconst PROFILE_BATCH_CHUNK = 24;\n\n/**\n * Read MANY users' public profiles in one /batch/pull round-trip per chunk.\n */\nexport async function readProfiles(ids: string[]): Promise<Map<string, PublicProfile>> {\n const out = new Map<string, PublicProfile>();\n const client = getProfileBatchClient();\n for (let i = 0; i < ids.length; i += PROFILE_BATCH_CHUNK) {\n const chunk = ids.slice(i, i + PROFILE_BATCH_CHUNK);\n let entries: BatchPullEntry[];\n try {\n entries = await client.batchPullMany('profile', chunk.map((id) => ({ identity: id })));\n } catch {\n for (const id of chunk) {\n const cached = await loadCachedProfile(id);\n if (cached) out.set(id, cached);\n }\n continue;\n }\n chunk.forEach((id, j) => {\n const entry = entries[j];\n if (!entry || entry.error) return;\n const data = (entry.data ?? null) as { pseudo?: unknown; avatar?: unknown; edPub?: unknown; kemPub?: unknown } | null;\n const profile: PublicProfile = {\n pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null,\n avatar: typeof data?.avatar === 'string' ? data.avatar : null,\n edPub: typeof data?.edPub === 'string' ? data.edPub : null,\n kemPub: typeof data?.kemPub === 'string' ? data.kemPub : null,\n };\n cacheProfile(id, profile);\n out.set(id, profile);\n });\n }\n return out;\n}\n\n/**\n * Merge a patch into the caller's own profile doc.\n */\nexport async function writeProfile(\n client: StarfishClient,\n userId: string,\n patch: { pseudo?: string; avatar?: string | null; edPub?: string; kemPub?: string },\n): Promise<void> {\n const current = await client.pull(profilePull(userId)).catch(() => null);\n const base = (current?.data as Record<string, unknown> | undefined) ?? {};\n const next: Record<string, unknown> = { ...base, ...patch, v: 1 };\n if (next.avatar == null) delete next.avatar;\n await client.push(profilePush(userId), next, current?.hash ?? null);\n}\n\n/** Write the caller's own profile pseudo. */\nexport async function writePseudo(client: StarfishClient, userId: string, pseudo: string): Promise<void> {\n await writeProfile(client, userId, { pseudo });\n}\n\n/**\n * Publish this identity's PUBLIC keys in its profile so a peer can start an E2EE DM.\n * One-time + idempotent. ROOT-DEVICE ONLY — `profile` is `device:root`-write.\n */\nexport async function ensureProfileKeys(\n client: StarfishClient,\n userId: string,\n keys: { edPub: string; kemPub: string },\n): Promise<void> {\n let confirmedAbsent = false;\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (r.status === 404) confirmedAbsent = true;\n else if (r.ok) {\n const body = await r.json();\n const data = body?.data as { edPub?: unknown; kemPub?: unknown } | undefined;\n confirmedAbsent = !(typeof data?.edPub === 'string' && typeof data?.kemPub === 'string');\n } else return;\n } catch {\n return;\n }\n if (!confirmedAbsent) return;\n await writeProfile(client, userId, { edPub: keys.edPub, kemPub: keys.kemPub });\n}\n\n/**\n * Build cap-cert auth headers for a raw `fetch` outside the StarfishClient.\n */\nexport async function buildAuthHeaders(\n cap: unknown,\n devEdPrivHex: string,\n method: string,\n pathAndQuery: string,\n): Promise<Record<string, string>> {\n let host = '';\n try {\n host = new URL(getSyncBase()).host;\n } catch { /* relative base */ }\n\n const { sig, ts, nonce } = await signRequest(\n { method: method as SignableMethod, pathAndQuery, host },\n devEdPrivHex,\n );\n\n const capJson = stableStringify(cap as Record<string, unknown>);\n const capB64 =\n typeof btoa === 'function'\n ? btoa(capJson)\n : Buffer.from(capJson, 'utf-8').toString('base64');\n\n return {\n Authorization: `Cap ${capB64}`,\n 'X-Starfish-Sig': sig,\n 'X-Starfish-Ts': String(ts),\n 'X-Starfish-Nonce': nonce,\n };\n}\n\nasync function readOwnPseudo(userId: string): Promise<{ read: boolean; pseudo: string | null }> {\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (r.status === 404) return { read: true, pseudo: null };\n if (!r.ok) return { read: false, pseudo: null };\n const body = await r.json();\n const data = body?.data as { pseudo?: unknown } | undefined;\n return { read: true, pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null };\n } catch {\n return { read: false, pseudo: null };\n }\n}\n\n/**\n * Seed the caller's profile pseudo only if none exists yet, returning the\n * authoritative server value.\n */\nexport async function ensurePseudo(client: StarfishClient, userId: string, fallback: string): Promise<string> {\n const { read, pseudo } = await readOwnPseudo(userId);\n if (pseudo && pseudo.trim()) return pseudo;\n if (!read) return fallback;\n await writeProfile(client, userId, { pseudo: fallback });\n return fallback;\n}\n","/**\n * A `fetch` wrapper that bounds the CONNECT/TTFB phase only.\n *\n * Aborts a request that hasn't RESPONDED within {@link CONNECT_TIMEOUT_MS}, turning\n * an opaque infinite spinner into a normal rejection the open path can surface as a\n * retriable error. Clears the timer once response headers arrive, so it bounds ONLY\n * the connect phase — body downloads and long-lived streams stay unbounded.\n */\n\nexport const CONNECT_TIMEOUT_MS = 12_000; // generous: trips only on a truly stalled socket\n\nexport function fetchWithTimeout(timeoutMs = CONNECT_TIMEOUT_MS): typeof fetch {\n return (input, init) => {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), timeoutMs);\n const caller = init?.signal;\n if (caller) {\n if (caller.aborted) ctrl.abort();\n else caller.addEventListener('abort', () => ctrl.abort(), { once: true });\n }\n return fetch(input as RequestInfo, { ...init, signal: ctrl.signal }).finally(() => clearTimeout(timer));\n };\n}\n","/**\n * The app's offline-first read cache for every {@link StarfishClient}.\n *\n * Backs the SDK's {@link PullCache} (read-through pull cache) with the kv layer\n * (localStorage on web, AsyncStorage on native). When a client is built with this\n * cache, every successful structured `pull()` is written through, and a pull that\n * fails because the transport is unreachable falls back to the last-synced snapshot.\n *\n * SECURITY: the SDK caches the RAW server response only. For E2E collections that\n * payload is the SEALED ciphertext the server holds — never the decrypted form —\n * so this cache is ciphertext-at-rest by construction.\n */\nimport type { PullCache } from '@drakkar.software/starfish-client';\n\nimport { kvGet, kvSet } from '../core/adapters.js';\n\nconst PREFIX = 'octospaces.pullcache.';\n\n/**\n * Max age for a cached snapshot before it's treated as a miss. Generous (30 days)\n * because for an offline-first app any last-synced data beats none.\n */\nexport const PULL_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n\nlet shared: PullCache | undefined;\n\n/** The shared app-wide pull cache (one instance, reused across every client). */\nexport function pullCache(): PullCache {\n return (shared ??= {\n get: (key) => kvGet(PREFIX + key),\n set: (key, value) => kvSet(PREFIX + key, value),\n });\n}\n","/**\n * Offline-first cache for public profiles (pseudo + inline avatar).\n *\n * Profiles are PUBLIC plaintext so they don't flow through the SDK's read-through\n * pull cache. This kv cache gives them the same offline-first behavior: a successful\n * read is persisted per user, and a read that fails because the device is offline\n * falls back to the last-known pseudo/avatar.\n */\nimport { kvGet, kvSet } from '../core/adapters.js';\nimport type { PublicProfile } from './client.js';\n\nconst key = (userId: string) => `octospaces.profile.v1.${userId}`;\n\n/** Persist a freshly-read profile (fire-and-forget). */\nexport function cacheProfile(userId: string, profile: PublicProfile): void {\n void kvSet(key(userId), JSON.stringify(profile)).catch(() => {});\n}\n\n/** Last-known profile for a user, or null if never cached / unparseable. */\nexport async function loadCachedProfile(userId: string): Promise<PublicProfile | null> {\n try {\n const raw = await kvGet(key(userId));\n if (!raw) return null;\n const d = JSON.parse(raw) as Partial<PublicProfile>;\n return {\n pseudo: typeof d.pseudo === 'string' ? d.pseudo : null,\n avatar: typeof d.avatar === 'string' ? d.avatar : null,\n edPub: typeof d.edPub === 'string' ? d.edPub : null,\n kemPub: typeof d.kemPub === 'string' ? d.kemPub : null,\n };\n } catch {\n return null;\n }\n}\n","/**\n * {@link SpaceAccessError} — a GENUINE access denial (not a transient connectivity failure).\n *\n * Lives in its own dependency-free module so both the low-level keyring opener\n * (`client.ts`) and the higher-level space-encryptor cache (`space-encryptor.ts`) can\n * throw it without an import cycle.\n */\nexport class SpaceAccessError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SpaceAccessError';\n }\n}\n","/**\n * Identity & 12-word recovery seed. The seed is a BIP-39 mnemonic used as the\n * passphrase for Starfish's `bootstrapRootIdentity`; the same words deterministically\n * recover the identity.\n */\nimport { generateMnemonic, validateMnemonic } from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport { bootstrapRootIdentity, mintDeviceCap } from '@drakkar.software/starfish-identities';\nimport type { StarfishClient } from '@drakkar.software/starfish-client';\nimport type { CapCert } from '@drakkar.software/starfish-protocol';\n\nimport { makeClient, ensureProfileKeys, ensurePseudo, type DeviceKeys } from './client.js';\nimport { accountScope, ownerScope } from './paths.js';\nimport { getSharedSpacesNamespace } from '../core/config.js';\nimport type { DerivedIdentity } from '../core/storage-types.js';\n\nexport interface Session {\n userId: string;\n name: string;\n keys: DeviceKeys;\n chatCap: unknown;\n accountCap: unknown;\n /**\n * The primary Starfish client for space content (keyring, channels, objects).\n * Uses the app's default namespace.\n */\n chatClient: StarfishClient;\n /**\n * The Starfish client for account-scoped content (profile, _spaces registry).\n * Uses the app's default namespace.\n */\n accountClient: StarfishClient;\n /**\n * Starfish client for cross-app shared-spaces registry operations.\n * When `sharedSpacesNamespace` is configured, uses that namespace override so\n * the spaces list lives in a separate namespace shared across multiple apps.\n * Falls back to `accountClient` when no shared namespace is configured.\n */\n spacesRegistryClient: StarfishClient;\n /**\n * Starfish client for cross-app shared-spaces keyring operations.\n * Same namespace logic as `spacesRegistryClient`, scoped to space content.\n * Falls back to `chatClient` when no shared namespace is configured.\n */\n spacesKeyringClient: StarfishClient;\n fingerprint: string;\n /**\n * The Ed25519 pubkey that signs this identity's OWNED-space keyring entries —\n * the trusted-adder provenance anchor for opening them.\n */\n ownerEdPub: string;\n}\n\n/**\n * Trusted-adder allow-list for opening an OWNED space's keyring.\n */\nexport function ownerTrustedAdders(session: Session): string[] {\n return session.ownerEdPub === session.keys.edPub\n ? [session.keys.edPub]\n : [session.ownerEdPub, session.keys.edPub];\n}\n\n/** Fresh 12-word recovery seed. */\nexport function generateSeedWords(): string[] {\n return generateMnemonic(wordlist, 128).split(' ');\n}\n\nexport function isValidSeed(words: string[]): boolean {\n return validateMnemonic(words.join(' ').trim(), wordlist);\n}\n\n/** Human-readable fingerprint derived from the identity's user id. */\nexport function fingerprintFromUserId(userId: string): string {\n const h = userId.replace(/[^0-9a-f]/gi, '').toUpperCase();\n return [h.slice(0, 4), h.slice(4, 8), h.slice(8, 12)].filter(Boolean).join(' · ');\n}\n\n/**\n * Build a full owner session (caps + clients + pseudo) from an already-derived\n * root identity. No Argon2id — only fast Ed25519 cap-minting plus a profile fetch.\n */\nexport async function buildSession({ userId, keys }: DerivedIdentity, name?: string): Promise<Session> {\n const fallback = name && name.trim() ? name.trim() : `user-${userId.slice(0, 6)}`;\n const sub = { edPubHex: keys.edPub, kemPubHex: keys.kemPub };\n const chatCap = await mintDeviceCap(keys.edPriv, keys.edPub, sub, ownerScope());\n const accountCap = await mintDeviceCap(keys.edPriv, keys.edPub, sub, accountScope(userId));\n const chatClient = makeClient(chatCap, keys.edPriv);\n const accountClient = makeClient(accountCap, keys.edPriv);\n\n const sharedNs = getSharedSpacesNamespace();\n const spacesRegistryClient = sharedNs ? makeClient(accountCap, keys.edPriv, sharedNs) : accountClient;\n const spacesKeyringClient = sharedNs ? makeClient(chatCap, keys.edPriv, sharedNs) : chatClient;\n\n const displayName = await ensurePseudo(accountClient, userId, fallback).catch(() => fallback);\n void ensureProfileKeys(accountClient, userId, keys).catch(() => {});\n return {\n userId,\n name: displayName,\n keys,\n chatCap,\n accountCap,\n chatClient,\n accountClient,\n spacesRegistryClient,\n spacesKeyringClient,\n fingerprint: fingerprintFromUserId(userId),\n ownerEdPub: keys.edPub,\n };\n}\n\n/** A paired device's credentials: its own keypair + the root-signed cap-cert. */\nexport interface LinkedIdentity {\n userId: string;\n keys: DeviceKeys;\n capCert: CapCert;\n}\n\n/**\n * Build a session for a PAIRED (linked) device. Unlike {@link buildSession}, the\n * device keypair is NOT the root, so it cannot self-mint caps — both clients are\n * driven by the single root-signed `capCert` from the pairing bundle.\n */\nexport async function buildLinkedSession({ userId, keys, capCert }: LinkedIdentity, name?: string): Promise<Session> {\n const fallback = name && name.trim() ? name.trim() : `user-${userId.slice(0, 6)}`;\n const chatClient = makeClient(capCert, keys.edPriv);\n const accountClient = makeClient(capCert, keys.edPriv);\n\n const sharedNs = getSharedSpacesNamespace();\n const spacesRegistryClient = sharedNs ? makeClient(capCert, keys.edPriv, sharedNs) : accountClient;\n const spacesKeyringClient = sharedNs ? makeClient(capCert, keys.edPriv, sharedNs) : chatClient;\n\n const displayName = await ensurePseudo(accountClient, userId, fallback).catch(() => fallback);\n return {\n userId,\n name: displayName,\n keys,\n chatCap: capCert,\n accountCap: capCert,\n chatClient,\n accountClient,\n spacesRegistryClient,\n spacesKeyringClient,\n fingerprint: fingerprintFromUserId(userId),\n ownerEdPub: capCert.iss,\n };\n}\n\n/** Derive a full owner session (identity + caps + clients) from a seed. */\nexport async function deriveSession(seedWords: string[], name?: string): Promise<Session> {\n const passphrase = seedWords.join(' ').trim();\n const creds = await bootstrapRootIdentity(passphrase);\n return buildSession({ userId: creds.userId, keys: creds.device as DeviceKeys }, name);\n}\n\n/** The cached root identity (userId + keys) carried by a built session. */\nexport function rootIdentityOf(s: Session): DerivedIdentity {\n return { userId: s.userId, keys: s.keys };\n}\n","/**\n * Seal a small secret to an X25519 KEM key so it can ride in a plaintext synced\n * doc without exposing it to the server.\n *\n * - {@link sealToSelf}/{@link unsealFromSelf} — sealed to THIS account's own key\n * (public-space join credentials, which embed a bearer secret). Recovered on\n * any device with the same seed.\n * - {@link sealToRecipient}/{@link unsealFromRecipient} — sealed to ANOTHER user's\n * published KEM key (DM-invite delivery).\n */\nimport {\n bytesToHex,\n hexToBytes,\n unwrapFromEntry,\n verifyEntrySignature,\n wrapForRecipient,\n} from '@drakkar.software/starfish-keyring';\nimport type { WrappedKeyEntry } from '@drakkar.software/starfish-keyring';\n\nimport type { Session } from './identity.js';\n\n/** A payload sealed to a KEM key: the wrapped CEK + hex(iv ‖ AES-GCM ct). */\nexport interface SealedBlob {\n entry: WrappedKeyEntry;\n ct: string;\n}\n\nconst SELF_EPOCH = 0;\n\nconst subtle = () => globalThis.crypto.subtle;\n\nasync function seal(session: Session, recipientKemPub: string, plaintext: string): Promise<SealedBlob> {\n const cek = globalThis.crypto.getRandomValues(new Uint8Array(32));\n const entry = await wrapForRecipient(cek, recipientKemPub, {\n adderEdPrivHex: session.keys.edPriv,\n adderEdPubHex: session.keys.edPub,\n addedAt: Math.floor(Date.now() / 1000),\n epoch: SELF_EPOCH,\n });\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const key = await subtle().importKey('raw', cek, { name: 'AES-GCM' }, false, ['encrypt']);\n const ctBuf = await subtle().encrypt({ name: 'AES-GCM', iv }, key, new TextEncoder().encode(plaintext));\n const packed = new Uint8Array(iv.length + ctBuf.byteLength);\n packed.set(iv, 0);\n packed.set(new Uint8Array(ctBuf), iv.length);\n return { entry, ct: bytesToHex(packed) };\n}\n\nasync function open(session: Session, blob: SealedBlob): Promise<string> {\n const cek = await unwrapFromEntry(blob.entry, session.keys.kemPriv);\n const packed = hexToBytes(blob.ct);\n const iv = new Uint8Array(packed.subarray(0, 12));\n const ctBytes = new Uint8Array(packed.subarray(12));\n const key = await subtle().importKey('raw', new Uint8Array(cek), { name: 'AES-GCM' }, false, ['decrypt']);\n const out = await subtle().decrypt({ name: 'AES-GCM', iv }, key, ctBytes);\n return new TextDecoder().decode(out);\n}\n\n/** Seal `plaintext` so only this account (its seed) can open it. */\nexport function sealToSelf(session: Session, plaintext: string): Promise<SealedBlob> {\n return seal(session, session.keys.kemPub, plaintext);\n}\n\n/** Open a {@link SealedBlob} sealed by {@link sealToSelf} for this account. */\nexport async function unsealFromSelf(session: Session, blob: SealedBlob): Promise<string> {\n if (blob.entry.addedBy !== session.keys.edPub) throw new Error('sealed blob not self-signed');\n if (!(await verifyEntrySignature(blob.entry, SELF_EPOCH))) throw new Error('sealed blob signature invalid');\n return open(session, blob);\n}\n\n/** Seal `plaintext` to ANOTHER user's published KEM key, signed by this session. */\nexport function sealToRecipient(session: Session, recipientKemPub: string, plaintext: string): Promise<SealedBlob> {\n return seal(session, recipientKemPub, plaintext);\n}\n\n/** Open a {@link SealedBlob} sealed to THIS account by some (arbitrary) sender. */\nexport async function unsealFromRecipient(session: Session, blob: SealedBlob): Promise<string> {\n if (!(await verifyEntrySignature(blob.entry, SELF_EPOCH))) throw new Error('sealed blob signature invalid');\n return open(session, blob);\n}\n","/**\n * Unified local access store for spaces this identity has joined.\n *\n * Replaces the separate `member-caps.ts` (private spaces) and `pubspace-caps.ts`\n * (public/link spaces). Two entry kinds:\n * - `member`: a member cap-cert (plain JSON, no bearer secret — safe to store\n * in the clear). Used for PRIVATE space keyring opens.\n * - `link`: an ephemeral-subject cap + the link's Ed25519 private key. Embeds a\n * bearer secret so it is SEALED in the synced `_spaces.pubAccess` field before\n * leaving this device; the local kv stores it plaintext only on the owning device.\n *\n * Two tiers (same as old member-caps): device-local kv (fast, offline) and the\n * user's synced `_spaces` doc (durable source of truth; merged over local on hydrate).\n * Keyed PER-USER so multiple accounts on one device never see each other's entries.\n */\nimport type { CapMap, PubAccessMap } from '../core/types.js';\nimport type { SealedBlob } from './account-seal.js';\nimport { kvGet, kvSet } from '../core/adapters.js';\n\nexport type SpaceAccessEntry =\n | { kind: 'member'; cap: string }\n | { kind: 'link'; cap: unknown; key: string; write: boolean };\n\nexport type SpaceAccessMap = Record<string, SpaceAccessEntry>;\n\nconst keyFor = (userId: string) => `octospaces.spaceaccess.${userId}`;\n\nlet cache: SpaceAccessMap = {};\nlet activeKey: string | null = null;\n\n/**\n * Load the active account's space-access entries into memory. Call (and await) on\n * sign-in and on every account switch, before opening rooms.\n *\n * `serverCaps` (private member caps from `_spaces.caps`) and `serverPubAccess`\n * (sealed link credentials from `_spaces.pubAccess`, already unsealed by the caller)\n * are merged OVER the local kv cache (server wins).\n */\nexport async function hydrateSpaceAccessStore(\n userId: string,\n serverCaps: CapMap,\n serverLinkAccess: Record<string, { cap: unknown; key: string; write: boolean }>,\n): Promise<void> {\n const key = keyFor(userId);\n if (activeKey === key) return;\n activeKey = key;\n cache = {};\n const raw = await kvGet(key);\n if (raw) {\n try {\n cache = JSON.parse(raw) as SpaceAccessMap;\n } catch (e) {\n console.error('[octospaces] space-access-store: corrupt cache, resetting:', e);\n cache = {};\n }\n }\n let changed = false;\n for (const [spaceId, capJson] of Object.entries(serverCaps)) {\n cache[spaceId] = { kind: 'member', cap: capJson };\n changed = true;\n }\n for (const [spaceId, access] of Object.entries(serverLinkAccess)) {\n cache[spaceId] = { kind: 'link', cap: access.cap, key: access.key, write: access.write };\n changed = true;\n }\n if (changed) await kvSet(key, JSON.stringify(cache));\n}\n\nfunction persist(): void {\n if (activeKey) void kvSet(activeKey, JSON.stringify(cache));\n}\n\nexport function getSpaceAccessEntry(spaceId: string): SpaceAccessEntry | null {\n return cache[spaceId] ?? null;\n}\n\nexport function saveSpaceAccessEntry(spaceId: string, entry: SpaceAccessEntry): void {\n cache = { ...cache, [spaceId]: entry };\n persist();\n}\n\n/** Forget one space's access (on leaving that space). */\nexport function removeSpaceAccessEntry(spaceId: string): void {\n if (!(spaceId in cache)) return;\n const next = { ...cache };\n delete next[spaceId];\n cache = next;\n persist();\n}\n\n/** A snapshot of the in-memory cache — used by `recoverSpaceAccess` to find entries\n * not yet on the server. */\nexport function localSpaceAccessEntries(): SpaceAccessMap {\n return cache;\n}\n\n/** Build the `CapMap` slice (member entries only) for persisting into `_spaces.caps`. */\nexport function memberCapsFromStore(): CapMap {\n const out: CapMap = {};\n for (const [id, e] of Object.entries(cache)) if (e.kind === 'member') out[id] = e.cap;\n return out;\n}\n\n/** Build the `PubAccessMap` slice (link entries already sealed by the caller). */\nexport function linkAccessFromStore(): Record<string, { cap: unknown; key: string; write: boolean }> {\n const out: Record<string, { cap: unknown; key: string; write: boolean }> = {};\n for (const [id, e] of Object.entries(cache)) {\n if (e.kind === 'link') out[id] = { cap: e.cap, key: e.key, write: e.write };\n }\n return out;\n}\n\n/** Drop the in-memory cache (on account switch / sign-out). */\nexport function clearSpaceAccessStore(): void {\n cache = {};\n activeKey = null;\n}\n","/**\n * Space access resolver — returns the right (client, encryptor) pair for any\n * space regardless of whether it is private (E2EE) or public (plaintext).\n *\n * Replaces `space-encryptor.ts`. The key invariant: public spaces have\n * `encryptor: null`; private spaces always have a live `Encryptor`.\n *\n * Resolution order (same semantics as the old `getSpaceEncryptor`):\n * 1. Link entry in the access store → sign requests as the ephemeral identity;\n * no keyring, encryptor null.\n * 2. Member entry → open the keyring as a recipient with the stored cap.\n * 3. No entry + visibility === 'public' (from `reg`) → owner mode, no keyring.\n * 4. No entry, private → either owner (open/mint keyring) or SpaceAccessError\n * if the space's roster shows we're a member but we're not holding a cap yet.\n */\nimport type { Encryptor, StarfishClient } from '@drakkar.software/starfish-client';\n\nimport { buildEncryptor, makeClient, openEncryptor, ownerEnsureKeyring } from './client.js';\nimport type { Session } from './identity.js';\nimport { ownerTrustedAdders } from './identity.js';\nimport { getSpaceAccessEntry } from './space-access-store.js';\nimport { SpaceAccessError } from '../core/space-access-error.js';\nimport type { SpaceVisibility } from '../core/types.js';\n\n// Re-export so existing importers keep reaching SpaceAccessError through this module.\nexport { SpaceAccessError };\n\nexport interface SpaceAccessHandle {\n encryptor: Encryptor | null;\n client: StarfishClient;\n /** True when opened as the space OWNER (so the caller must seed the room doc). */\n isOwnerOpen: boolean;\n}\n\nconst cache = new Map<string, Promise<SpaceAccessHandle>>();\n\n/** Drop every cached handle (on account switch — keys are per-identity). */\nexport function clearSpaceAccessCache(): void {\n cache.clear();\n}\n\n/**\n * Resolve the right (client, encryptor) for a space, opening and caching on first use.\n *\n * `reg` is the space's `_rooms` access record if already known. Pass null when the\n * caller has not yet read the registry; the resolver will probe if needed.\n */\nexport function getSpaceAccess(\n spaceId: string,\n session: Session,\n reg: { owner: string | null; members: string[]; visibility?: SpaceVisibility } | null,\n): Promise<SpaceAccessHandle> {\n const hit = cache.get(spaceId);\n if (hit) return hit;\n const p = (async (): Promise<SpaceAccessHandle> => {\n const entry = getSpaceAccessEntry(spaceId);\n\n // 1. Link entry — ephemeral identity; no keyring\n if (entry?.kind === 'link') {\n const cap = entry.cap;\n const client = makeClient(cap, entry.key);\n return { encryptor: null, client, isOwnerOpen: false };\n }\n\n // 2. Member entry — open as a keyring recipient\n if (entry?.kind === 'member') {\n const cap = JSON.parse(entry.cap) as { iss?: string };\n const client = makeClient(cap, session.keys.edPriv);\n const encryptor = await openEncryptor(client, session.keys, spaceId, cap.iss ? [cap.iss] : []);\n return { encryptor, client, isOwnerOpen: false };\n }\n\n // 3. No entry — branch on visibility\n const visibility = reg?.visibility;\n if (visibility === 'public') {\n return { encryptor: null, client: session.chatClient, isOwnerOpen: reg!.owner === session.userId };\n }\n\n // 4. No entry, private — owner or error\n const owner = reg?.owner ?? null;\n const members = reg?.members ?? [];\n if (owner !== null && owner !== session.userId) {\n throw new SpaceAccessError(\n members.includes(session.userId)\n ? \"You're a member of this space, but its key isn't on this device yet — reconnect, or ask the owner to re-invite.\"\n : \"You don't have access to this space.\",\n );\n }\n const encryptor = await ownerEnsureKeyring(\n session.chatClient,\n session.keys,\n spaceId,\n ownerTrustedAdders(session),\n );\n return { encryptor, client: session.chatClient, isOwnerOpen: true };\n })();\n cache.set(spaceId, p);\n p.catch(() => cache.delete(spaceId));\n return p;\n}\n\n/**\n * SOFT resolve — never mints a keyring, never throws on missing access.\n * Returns null when the identity has no usable access for the space yet.\n */\nexport async function buildSpaceAccess(\n session: Session,\n spaceId: string,\n hint?: { visibility?: SpaceVisibility },\n): Promise<{ client: StarfishClient; encryptor: Encryptor | null } | null> {\n const entry = getSpaceAccessEntry(spaceId);\n\n if (entry?.kind === 'link') {\n const client = makeClient(entry.cap, entry.key);\n return { client, encryptor: null };\n }\n\n let client = session.chatClient;\n let trustedAdders = ownerTrustedAdders(session);\n\n if (entry?.kind === 'member') {\n const cap = JSON.parse(entry.cap) as { iss?: string };\n client = makeClient(cap, session.keys.edPriv);\n if (cap.iss) trustedAdders = [cap.iss];\n const encryptor = await buildEncryptor(client, session.keys, spaceId, trustedAdders);\n return encryptor ? { client, encryptor } : null;\n }\n\n if (hint?.visibility === 'public') {\n return { client, encryptor: null };\n }\n\n // No entry, no hint — try the keyring probe (owner path)\n const encryptor = await buildEncryptor(client, session.keys, spaceId, trustedAdders);\n return encryptor ? { client, encryptor } : null;\n}\n","/**\n * Space + room registries (plaintext metadata docs). A user's spaces live at\n * `user/<userId>/_spaces`; each space's ACCESS RECORD (owner/members + shared\n * name/image) at `spaces/<spaceId>/_rooms`. The room/category LIST no longer lives\n * here — it moved to the encrypted unified object index (`objects/_index`, see\n * `object-index.ts`); `_rooms` is now just the owner-only access record.\n */\nimport { ConflictError, StarfishHttpError } from '@drakkar.software/starfish-client';\nimport type { StarfishClient } from '@drakkar.software/starfish-client';\n\nimport type { ArchivedDms, CapMap, DmMap, MutePrefs, PubAccessMap, ReadPrefs, Room, Space, SpaceVisibility } from '../core/types.js';\nimport type { SealedBlob } from '../sync/account-seal.js';\nimport { randomId } from '../core/ids.js';\nimport type { Session } from '../sync/identity.js';\nimport { DEFAULT_CATEGORY } from '../objects/objects.js';\nimport { seedSpaceObjectIndex } from './object-index.js';\nimport { roomsRegistryPull, roomsRegistryPush, spacesPull, spacesPush } from '../sync/paths.js';\n\n// Re-export so existing `import { DEFAULT_CATEGORY } from './registry'` consumers keep working.\nexport { DEFAULT_CATEGORY };\n\n/** Owner-set, SHARED space identity, persisted in the `_rooms` registry doc\n * (plaintext — NOT E2EE). `image` is a data URI. Both optional for back-compat. */\nexport interface SpaceMeta {\n name?: string | null;\n image?: string | null;\n visibility?: SpaceVisibility;\n}\n\n/** A resolved name/image update fanned out so the SpacesProvider adopts a\n * freshly-reconciled value without waiting for its next navigation refresh. */\nexport interface SpaceMetaUpdate {\n name: string;\n short: string;\n image?: string;\n}\n\nconst spaceMetaListeners = new Set<(spaceId: string, meta: SpaceMetaUpdate) => void>();\n\nexport function onSpaceMeta(fn: (spaceId: string, meta: SpaceMetaUpdate) => void): () => void {\n spaceMetaListeners.add(fn);\n return () => { spaceMetaListeners.delete(fn); };\n}\n\nexport function broadcastSpaceMeta(spaceId: string, meta: SpaceMetaUpdate): void {\n for (const fn of spaceMetaListeners) fn(spaceId, meta);\n}\n\ninterface SpacesDoc {\n spaces: Space[];\n caps: CapMap;\n mutes: MutePrefs;\n reads: ReadPrefs;\n pubAccess: PubAccessMap;\n dms: DmMap;\n quickReactions: string[];\n archivedDms: ArchivedDms;\n hash: string | null;\n}\n\nfunction coerceDms(raw: unknown): DmMap {\n const src = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const out: DmMap = {};\n for (const [k, v] of Object.entries(src)) if (typeof v === 'string') out[k] = v;\n return out;\n}\n\nfunction coerceMutes(raw: unknown): MutePrefs {\n const r = (raw && typeof raw === 'object' ? raw : {}) as { rooms?: unknown; spaces?: unknown };\n const pick = (v: unknown): Record<string, true | number> =>\n v && typeof v === 'object' ? (v as Record<string, true | number>) : {};\n return { rooms: pick(r.rooms), spaces: pick(r.spaces) };\n}\n\nfunction coerceReads(raw: unknown): ReadPrefs {\n const r = (raw && typeof raw === 'object' ? raw : {}) as { rooms?: unknown };\n const src = r.rooms && typeof r.rooms === 'object' ? (r.rooms as Record<string, unknown>) : {};\n const rooms: Record<string, number> = {};\n for (const [id, v] of Object.entries(src)) if (typeof v === 'number' && Number.isFinite(v)) rooms[id] = v;\n return { rooms };\n}\n\nfunction coerceQuickReactions(raw: unknown): string[] {\n return Array.isArray(raw) ? raw.filter((v): v is string => typeof v === 'string') : [];\n}\n\nfunction coerceArchivedDms(raw: unknown): ArchivedDms {\n const src = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const out: ArchivedDms = {};\n for (const [k, v] of Object.entries(src)) if (v === true) out[k] = true;\n return out;\n}\n\nasync function pullSpacesDoc(client: StarfishClient, userId: string): Promise<SpacesDoc> {\n const res = await client.pull(spacesPull(userId)).catch((err: unknown) => {\n if (err instanceof StarfishHttpError && err.status === 404) return null;\n throw err;\n });\n const data = res?.data as\n | {\n spaces?: Space[];\n caps?: CapMap;\n mutes?: unknown;\n reads?: unknown;\n pubAccess?: PubAccessMap;\n dms?: unknown;\n quickReactions?: unknown;\n archivedDms?: unknown;\n }\n | undefined;\n return {\n spaces: Array.isArray(data?.spaces) ? data!.spaces! : [],\n caps: data?.caps && typeof data.caps === 'object' ? data.caps : {},\n mutes: coerceMutes(data?.mutes),\n reads: coerceReads(data?.reads),\n pubAccess: data?.pubAccess && typeof data.pubAccess === 'object' ? data.pubAccess : {},\n dms: coerceDms(data?.dms),\n quickReactions: coerceQuickReactions(data?.quickReactions),\n archivedDms: coerceArchivedDms(data?.archivedDms),\n hash: res?.hash ?? null,\n };\n}\n\nexport async function readSpaces(client: StarfishClient, userId: string): Promise<SpacesDoc> {\n try {\n return await pullSpacesDoc(client, userId);\n } catch (err) {\n console.error('[readSpaces] failed to pull spaces registry', err);\n return {\n spaces: [],\n caps: {},\n mutes: coerceMutes(undefined),\n reads: coerceReads(undefined),\n pubAccess: {},\n dms: {},\n quickReactions: [],\n archivedDms: {},\n hash: null,\n };\n }\n}\n\nexport async function updateSpacesDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: { spaces: Space[]; caps: CapMap; pubAccess: PubAccessMap }) => { spaces: Space[]; caps: CapMap; pubAccess: PubAccessMap },\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const cur = { spaces, caps, pubAccess };\n const next = mutator(cur);\n if (next === cur) return;\n try {\n await client.push(\n spacesPush(userId),\n { v: 1, spaces: next.spaces, caps: next.caps, mutes, reads, pubAccess: next.pubAccess, dms, quickReactions, archivedDms },\n hash,\n );\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateMutesDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: MutePrefs) => MutePrefs | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(mutes);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes: next, reads, pubAccess, dms, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateReadsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: ReadPrefs) => ReadPrefs | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(reads);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads: next, pubAccess, dms, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateDmsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: DmMap) => DmMap | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(dms);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms: next, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateQuickReactionsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: string[]) => string[] | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(quickReactions);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms, quickReactions: next, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateArchivedDmsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: ArchivedDms) => ArchivedDms | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(archivedDms);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms: next }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function setDmMapping(\n client: StarfishClient,\n userId: string,\n peerUserId: string,\n spaceId: string,\n): Promise<void> {\n await updateDmsDoc(client, userId, (cur) => (cur[peerUserId] === spaceId ? null : { ...cur, [peerUserId]: spaceId }));\n}\n\nexport async function writeSpaces(\n client: StarfishClient,\n userId: string,\n spaces: Space[],\n _hash: string | null,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({ spaces, caps: cur.caps, pubAccess: cur.pubAccess }));\n}\n\nexport async function reorderSpaces(client: StarfishClient, userId: string, order: string[]): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => {\n const byId = new Map(cur.spaces.map((s) => [s.id, s]));\n const next: Space[] = [];\n for (const id of order) {\n const s = byId.get(id);\n if (s) { next.push(s); byId.delete(id); }\n }\n for (const s of cur.spaces) if (byId.has(s.id)) next.push(s);\n const unchanged = next.length === cur.spaces.length && next.every((s, i) => s === cur.spaces[i]);\n if (unchanged) return cur;\n return { spaces: next, caps: cur.caps, pubAccess: cur.pubAccess };\n });\n}\n\nfunction newSpaceId(): string {\n return `sp-${randomId()}`;\n}\n\nexport function normalizeCategories(rooms: Room[], stored: unknown): string[] {\n const distinct: string[] = [];\n for (const r of rooms) if (r.category && !distinct.includes(r.category)) distinct.push(r.category);\n const list = Array.isArray(stored) ? stored.filter((c): c is string => typeof c === 'string') : [];\n if (!list.length) return distinct;\n const result = [...list];\n for (const c of distinct) if (!result.includes(c)) result.push(c);\n return result;\n}\n\nexport async function readRooms(\n client: StarfishClient,\n spaceId: string,\n): Promise<{ owner: string | null; members: string[]; visibility: SpaceVisibility | null; name: string | null; image: string | null; hash: string | null }> {\n const res = await client.pull(roomsRegistryPull(spaceId)).catch((err: unknown) => {\n if (err instanceof StarfishHttpError && err.status === 404) return null;\n throw err;\n });\n const data = res?.data as { owner?: string; members?: unknown[]; visibility?: string; name?: string; image?: string } | undefined;\n return {\n owner: typeof data?.owner === 'string' ? data.owner : null,\n members: Array.isArray(data?.members) ? data!.members!.filter((m): m is string => typeof m === 'string') : [],\n visibility: data?.visibility === 'public' ? 'public' : null,\n name: typeof data?.name === 'string' ? data.name : null,\n image: typeof data?.image === 'string' ? data.image : null,\n hash: res?.hash ?? null,\n };\n}\n\nexport async function writeRooms(\n client: StarfishClient,\n spaceId: string,\n owner: string,\n members: string[],\n hash: string | null,\n meta?: SpaceMeta,\n): Promise<void> {\n const name = meta?.name?.trim() || undefined;\n const image = meta?.image || undefined;\n const visibility = meta?.visibility === 'public' ? 'public' : undefined;\n await client.push(\n roomsRegistryPush(spaceId),\n { v: 1, owner, members, ...(visibility ? { visibility } : {}), ...(name ? { name } : {}), ...(image ? { image } : {}) },\n hash,\n );\n}\n\nexport async function addSpaceMember(\n client: StarfishClient,\n spaceId: string,\n ownerUserId: string,\n memberUserId: string,\n): Promise<void> {\n const { owner, members, visibility, name, image, hash } = await readRooms(client, spaceId);\n if (memberUserId === (owner ?? ownerUserId) || members.includes(memberUserId)) return;\n await writeRooms(client, spaceId, owner ?? ownerUserId, [...members, memberUserId], hash, { name, image, visibility: visibility ?? undefined });\n}\n\n/** Remove a member from the space roster (used for link revocation). */\nexport async function removeSpaceMember(\n client: StarfishClient,\n spaceId: string,\n memberUserId: string,\n): Promise<void> {\n const { owner, members, visibility, name, image, hash } = await readRooms(client, spaceId);\n if (!members.includes(memberUserId)) return;\n await writeRooms(client, spaceId, owner ?? memberUserId, members.filter((m) => m !== memberUserId), hash, { name, image, visibility: visibility ?? undefined });\n}\n\nexport async function addJoinedSpace(client: StarfishClient, userId: string, space: Space): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) =>\n cur.spaces.some((s) => s.id === space.id)\n ? cur\n : { spaces: [...cur.spaces, space], caps: cur.caps, pubAccess: cur.pubAccess },\n );\n}\n\nexport async function addJoinedSpaceWithCap(\n client: StarfishClient,\n userId: string,\n space: Space,\n capJson: string,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({\n spaces: cur.spaces.some((s) => s.id === space.id) ? cur.spaces : [...cur.spaces, space],\n caps: { ...cur.caps, [space.id]: capJson },\n pubAccess: cur.pubAccess,\n }));\n}\n\nexport async function addJoinedSpaceWithLinkAccess(\n client: StarfishClient,\n userId: string,\n space: Space,\n sealed: SealedBlob,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({\n spaces: cur.spaces.some((s) => s.id === space.id) ? cur.spaces : [...cur.spaces, space],\n caps: cur.caps,\n pubAccess: { ...cur.pubAccess, [space.id]: sealed },\n }));\n}\n\n/**\n * Create a new space owned by the identity. Seeds ONE generic `general` object node\n * into the object index (encrypted for private, plaintext for public).\n *\n * `opts.visibility` defaults to `'private'`.\n */\nexport async function createSpace(\n session: Session,\n name: string,\n opts?: { visibility?: SpaceVisibility },\n): Promise<Space> {\n const { accountClient, userId } = session;\n const { spaces, hash } = await readSpaces(accountClient, userId);\n const trimmed = name.trim() || 'New Space';\n const visibility = opts?.visibility ?? 'private';\n const id = newSpaceId();\n const space: Space = {\n id,\n name: trimmed,\n short: trimmed.slice(0, 2).toUpperCase(),\n members: 1,\n ...(visibility === 'public' ? { visibility: 'public', ownerId: userId, write: true } : {}),\n };\n await writeRooms(accountClient, id, userId, [], null, { name: trimmed, visibility: visibility === 'public' ? 'public' : undefined });\n await seedSpaceObjectIndex(session, id, [{ id: `${id}-general`, name: 'general', kind: 'channel', category: DEFAULT_CATEGORY }], { visibility });\n await writeSpaces(accountClient, userId, [...spaces, space], hash);\n return space;\n}\n\nexport class CategoryError extends Error {}\n\nexport async function reconcileSpaceMeta(\n client: StarfishClient,\n userId: string,\n spaceId: string,\n shared: SpaceMeta,\n knownSpaces?: Space[],\n): Promise<void> {\n const sharedName = typeof shared.name === 'string' && shared.name.trim() ? shared.name : null;\n const sharedImage = typeof shared.image === 'string' && shared.image ? shared.image : null;\n if (sharedName === null && sharedImage === null) return;\n const known = knownSpaces?.find((s) => s.id === spaceId);\n if (known) {\n const name = sharedName ?? known.name;\n const short = name.slice(0, 2).toUpperCase();\n const image = sharedImage ?? known.image;\n if (name === known.name && short === known.short && (image ?? null) === (known.image ?? null)) return;\n }\n const { spaces, hash } = await readSpaces(client, userId);\n const cur = spaces.find((s) => s.id === spaceId);\n if (!cur) return;\n const name = sharedName ?? cur.name;\n const image = sharedImage ?? cur.image;\n const short = name.slice(0, 2).toUpperCase();\n if (name === cur.name && short === cur.short && (image ?? null) === (cur.image ?? null)) return;\n const next = spaces.map((s) => (s.id === spaceId ? { ...s, name, short, image } : s));\n await writeSpaces(client, userId, next, hash);\n broadcastSpaceMeta(spaceId, { name, short, image });\n}\n","/**\n * Unified Object model — pure logic over the space object index.\n *\n * A space's contents (rooms, categories, docs, projects, tasks, …) are\n * {@link ObjectNode}s in one union-merged index doc at\n * `spaces/{spaceId}/objects/_index`. THIS module is the pure, testable core:\n * the tree builder + merge-artifact guards, breadcrumbs, ordering, and the node\n * reducers a `store.set` applies.\n *\n * Because the index is union-merged (per-node last-write-wins keyed on `updatedAt`),\n * the tree is eventually consistent — two devices can concurrently produce a cycle\n * or an orphan. The builder below is the single place those are repaired so every\n * consumer renders a well-formed tree.\n *\n * **Transitional bridges** (`objectsToRoomCategories`, `roomKindToSubtype`, …) project\n * the object tree into the legacy `Room`-based shape that apps still speak during their\n * migration onto the object model. They are purely mechanical projections over\n * `ObjectNode` and carry no domain-specific names.\n */\nimport type { ID, ObjectNode, ObjectType, Room, RoomSubtype } from '../core/types.js';\nimport { randomId, roomSlug } from '../core/ids.js';\n\n/** The bucket new/unfiled objects land in, and the fallback a deleted category's\n * objects are reassigned to. */\nexport const DEFAULT_CATEGORY = 'CHANNELS';\n\n/** Deterministic category-node id from its name, so two devices that concurrently\n * create the SAME category mint the SAME id → the union-merge dedupes them. */\nexport const categoryId = (name: string): ID => `cat-${roomSlug(name) || randomId()}`;\n\n/** A node plus its resolved children — the shape a tree view renders. */\nexport interface ObjectTreeNode extends ObjectNode {\n depth: number;\n children: ObjectTreeNode[];\n}\n\n/** Map a legacy {@link Room} `kind` to the unified room {@link RoomSubtype}. */\nexport function roomKindToSubtype(kind: Room['kind']): RoomSubtype {\n switch (kind) {\n case 'dm': return 'dm';\n case 'automated': return 'automation';\n default: return 'channel';\n }\n}\n\n/** Inverse of {@link roomKindToSubtype}. A legacy persisted `'stream'` subtype hits\n * the `default` and reads back as a plain `'channel'` (normalization). */\nexport function subtypeToRoomKind(subtype: RoomSubtype | undefined): Room['kind'] {\n switch (subtype) {\n case 'dm': return 'dm';\n case 'automation': return 'automated';\n default: return 'channel';\n }\n}\n\nfunction compareSiblings(a: ObjectNode, b: ObjectNode): number {\n if (a.order !== b.order) return a.order - b.order;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\n/** The order value for a new node appended after `siblings`. */\nexport function nextOrder(siblings: ObjectNode[]): number {\n let max = 0;\n for (const s of siblings) if (s.order > max) max = s.order;\n return max + 1;\n}\n\n/**\n * Build the render tree from a flat node list, repairing merge artifacts:\n * - **archived** nodes (and their subtrees) are dropped.\n * - **orphans** — a `parentId` that is missing or archived — reparent to root.\n * - **cycles** — a node reachable from itself via `parentId` — reparent to root.\n * - **siblings** sort by {@link compareSiblings} for cross-device determinism.\n */\nexport function buildTree(nodes: ObjectNode[], includeArchived = false): ObjectTreeNode[] {\n const live = includeArchived ? nodes : nodes.filter((n) => !n.archived);\n const byId = new Map<ID, ObjectNode>(live.map((n) => [n.id, n]));\n\n const effectiveParent = (n: ObjectNode): ID | null => {\n if (n.parentId == null) return null;\n if (!byId.has(n.parentId)) return null;\n const seen = new Set<ID>([n.id]);\n let cur: ID | null = n.parentId;\n while (cur != null) {\n if (seen.has(cur)) return null;\n seen.add(cur);\n const parent = byId.get(cur);\n if (!parent) return null;\n cur = parent.parentId;\n }\n return n.parentId;\n };\n\n const childrenOf = new Map<ID | null, ObjectNode[]>();\n for (const n of live) {\n const p = effectiveParent(n);\n const bucket = childrenOf.get(p) ?? [];\n bucket.push(n);\n childrenOf.set(p, bucket);\n }\n\n function attach(parent: ID | null, depth: number): ObjectTreeNode[] {\n return (childrenOf.get(parent) ?? [])\n .slice()\n .sort(compareSiblings)\n .map((n): ObjectTreeNode => ({ ...n, depth, children: attach(n.id, depth + 1) }));\n }\n\n return attach(null, 0);\n}\n\n/** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */\nexport function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[] {\n const byId = new Map<ID, ObjectNode>(nodes.map((n) => [n.id, n]));\n const trail: ObjectNode[] = [];\n const seen = new Set<ID>();\n let cur: ID | null = id;\n while (cur != null && byId.has(cur) && !seen.has(cur)) {\n seen.add(cur);\n const node: ObjectNode = byId.get(cur)!;\n trail.unshift(node);\n cur = node.parentId;\n }\n return trail;\n}\n\n/** The root→parent trail (EXCLUSIVE of the node itself). */\nexport function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[] {\n return breadcrumbs(nodes, id).slice(0, -1);\n}\n\n/** The ids of a node and its whole subtree (for cascade-archive). */\nexport function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID> {\n const childrenOf = new Map<ID | null, ID[]>();\n for (const n of nodes) {\n const bucket = childrenOf.get(n.parentId) ?? [];\n bucket.push(n.id);\n childrenOf.set(n.parentId, bucket);\n }\n const out = new Set<ID>();\n const walk = (id: ID) => {\n if (out.has(id)) return;\n out.add(id);\n for (const child of childrenOf.get(id) ?? []) walk(child);\n };\n walk(rootId);\n return out;\n}\n\n// ── Node reducers (pure: ObjectNode[] → ObjectNode[]) ─────────────────────────\n\nexport interface NewObjectInput {\n type: ObjectType;\n subtype?: RoomSubtype;\n parentId?: ID | null;\n title: string;\n emoji?: string;\n automation?: import('../core/types.js').AutomationMeta;\n /** Provide to reuse an id (e.g. a room id derived elsewhere); else minted. */\n id?: ID;\n}\n\n/** Append a new node under `parentId` at the end of its sibling order. */\nexport function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): { nodes: ObjectNode[]; node: ObjectNode } {\n const parentId = input.parentId ?? null;\n const siblings = nodes.filter((n) => n.parentId === parentId);\n const node: ObjectNode = {\n id: input.id ?? `obj-${randomId()}`,\n type: input.type,\n ...(input.subtype ? { subtype: input.subtype } : {}),\n parentId,\n order: nextOrder(siblings),\n title: input.title,\n ...(input.emoji ? { emoji: input.emoji } : {}),\n updatedAt: now,\n ...(input.automation ? { automation: input.automation } : {}),\n };\n return { nodes: [...nodes, node], node };\n}\n\n/** Patch a node's mutable metadata (title/emoji/automation), bumping `updatedAt`. */\nexport function patchObject(nodes: ObjectNode[], id: ID, patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'automation'>>, now: number): ObjectNode[] {\n return nodes.map((n) => (n.id === id ? { ...n, ...patch, updatedAt: now } : n));\n}\n\n/** Reparent a node (move in the tree). Rejects making a node its own descendant. */\nexport function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[] {\n if (id === parentId) return nodes;\n if (parentId != null && subtreeIds(nodes, id).has(parentId)) return nodes;\n const siblings = nodes.filter((n) => n.parentId === parentId && n.id !== id);\n return nodes.map((n) => (n.id === id ? { ...n, parentId, order: nextOrder(siblings), updatedAt: now } : n));\n}\n\n/** Set explicit sibling order (drag-reorder). */\nexport function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[] {\n return nodes.map((n) => (n.id in orderById ? { ...n, order: orderById[n.id]!, updatedAt: now } : n));\n}\n\n/** Cascade-archive a node and its whole subtree (soft delete). */\nexport function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[] {\n const ids = subtreeIds(nodes, id);\n return nodes.map((n) => (ids.has(n.id) ? { ...n, archived: true, updatedAt: now } : n));\n}\n\n// ── Transitional bridges (Room-based projections) ─────────────────────────────\n// Used while apps migrate their content onto the generic object model. Both apps\n// still speak the legacy `Room` type; these projections stay until that migration\n// is complete.\n\n/** The category→rooms grouping the legacy UI consumes. */\nexport interface AdaptedCategory {\n name: string;\n rooms: Room[];\n}\n\n/**\n * Project the room/category nodes of an index into the legacy `{ name, rooms }[]`\n * shape that app UIs still consume. Category nodes become buckets; room nodes become\n * {@link Room}s grouped under their parent category (or `fallbackCategory` at root).\n * Returns null when the index holds no room/category nodes yet.\n *\n * @deprecated Use the object tree directly once apps complete their migration.\n */\nexport function objectsToRoomCategories(nodes: ObjectNode[], spaceId: string, fallbackCategory: string): AdaptedCategory[] | null {\n const live = nodes.filter((n) => !n.archived);\n const cats = live.filter((n) => n.type === 'category').slice().sort(compareSiblings);\n const rooms = live.filter((n) => n.type === 'room');\n if (cats.length === 0 && rooms.length === 0) return null;\n\n const titleById = new Map<ID, string>(cats.map((c) => [c.id, c.title]));\n const buckets = new Map<string, Room[]>();\n for (const c of cats) buckets.set(c.title, []);\n\n const toRoom = (n: ObjectNode, category: string): Room => ({\n id: n.id,\n spaceId,\n category,\n name: n.title,\n kind: subtypeToRoomKind(n.subtype),\n ...(n.automation ? { automation: n.automation } : {}),\n });\n\n for (const n of rooms.slice().sort(compareSiblings)) {\n const category = (n.parentId != null && titleById.get(n.parentId)) || fallbackCategory;\n if (!buckets.has(category)) buckets.set(category, []);\n buckets.get(category)!.push(toRoom(n, category));\n }\n return [...buckets.entries()].map(([name, rs]) => ({ name, rooms: rs }));\n}\n\n/**\n * Drop `kind: 'automated'` rooms from a category list (they belong to an Agents\n * view, not the main room list). A category that held only agents is removed too.\n *\n * @deprecated Use the object tree directly once apps complete their migration.\n */\nexport function excludeAutomatedRooms(categories: AdaptedCategory[]): AdaptedCategory[] {\n return categories\n .map((c) => ({ ...c, rooms: c.rooms.filter((r) => r.kind !== 'automated') }))\n .filter((c, i) => c.rooms.length > 0 || categories[i].rooms.length === 0);\n}\n\n// ── Seed: build the initial index nodes for a freshly-created space ────────────\n\n/** A minimal object descriptor the {@link seedIndexNodes} builder turns into nodes. */\nexport interface SeedRoom {\n id: ID;\n name: string;\n kind: Room['kind'];\n category: string;\n}\n\n/**\n * Build the initial `ObjectNode[]` for a brand-new space's index: a `category` node\n * per distinct category and a `room` node per seed object parented under it. Pure +\n * deterministic (category ids via {@link categoryId}).\n */\nexport function seedIndexNodes(rooms: SeedRoom[], now: number): ObjectNode[] {\n const out: ObjectNode[] = [];\n const catId = new Map<string, ID>();\n let catOrder = 0;\n for (const r of rooms) {\n if (catId.has(r.category)) continue;\n const id = categoryId(r.category);\n catId.set(r.category, id);\n out.push({ id, type: 'category', parentId: null, order: catOrder++, title: r.category, updatedAt: now });\n }\n const orderInCat = new Map<ID, number>();\n for (const r of rooms) {\n const parentId = catId.get(r.category)!;\n const order = (orderInCat.get(parentId) ?? 0) + 1;\n orderInCat.set(parentId, order);\n out.push({ id: r.id, type: 'room', subtype: roomKindToSubtype(r.kind), parentId, order, title: r.name, updatedAt: now });\n }\n return out;\n}\n","/**\n * Headless reads + create-time seeding of a space's unified OBJECT INDEX.\n *\n * Both PRIVATE (encrypted) and PUBLIC (plaintext) spaces store their room/category\n * tree in `spaces/{spaceId}/objects/_index`. The `encryptor` parameter is null for\n * public spaces — the helpers pass data through unchanged.\n */\nimport { ConflictError } from '@drakkar.software/starfish-client';\nimport type { Encryptor, StarfishClient } from '@drakkar.software/starfish-client';\n\nimport type { ObjectNode, Room, SpaceVisibility } from '../core/types.js';\nimport type { Session } from '../sync/identity.js';\nimport { DEFAULT_CATEGORY, objectsToRoomCategories, seedIndexNodes } from '../objects/objects.js';\nimport type { SeedRoom } from '../objects/objects.js';\nimport { objIndexPull, objIndexPush } from '../sync/paths.js';\nimport { buildSpaceAccess, getSpaceAccess } from '../sync/space-access.js';\n\nexport type { SeedRoom };\n\nfunction indexNodes(plain: Record<string, unknown>): ObjectNode[] {\n return Array.isArray((plain as { objects?: unknown }).objects)\n ? (plain as { objects: ObjectNode[] }).objects\n : [];\n}\n\n/**\n * Pull + (private: decrypt) + project a space's object index into the legacy\n * `{ rooms, categories }` shape. `encryptor` is null for a PUBLIC space.\n * Returns null on failure or an empty index.\n */\nexport async function readIndexRooms(\n client: StarfishClient,\n encryptor: Encryptor | null,\n indexPath: string,\n spaceId: string,\n): Promise<{ rooms: Room[]; categories: string[] } | null> {\n try {\n const res = await client.pull(indexPath).catch(() => null);\n if (!res?.data) return null;\n const plain = encryptor\n ? await encryptor.decrypt(res.data as Record<string, unknown>)\n : (res.data as Record<string, unknown>);\n const cats = objectsToRoomCategories(indexNodes(plain), spaceId, DEFAULT_CATEGORY);\n if (!cats) return null;\n return { rooms: cats.flatMap((c) => c.rooms), categories: cats.map((c) => c.name) };\n } catch {\n return null;\n }\n}\n\n/**\n * Read a space's index rooms + categories, resolving access automatically.\n * Pass `reg` (from `readRooms`) so the accessor picks the right auth mode.\n */\nexport async function readSpaceIndexRooms(\n session: Session,\n spaceId: string,\n reg: { owner: string | null; members: string[]; visibility?: SpaceVisibility },\n): Promise<{ rooms: Room[]; categories: string[] } | null> {\n if (reg.owner === null && reg.visibility !== 'public') return null;\n try {\n const { encryptor, client } = await getSpaceAccess(spaceId, session, reg);\n return await readIndexRooms(client, encryptor, objIndexPull(spaceId), spaceId);\n } catch {\n return null;\n }\n}\n\n/** SOFT read — never throws, never mints. Returns an empty array on any failure. */\nexport async function readSpaceRooms(\n session: Session,\n spaceId: string,\n hint?: { visibility?: SpaceVisibility },\n): Promise<Room[]> {\n const access = await buildSpaceAccess(session, spaceId, hint).catch(() => null);\n if (!access) return [];\n const idx = await readIndexRooms(access.client, access.encryptor, objIndexPull(spaceId), spaceId);\n return idx?.rooms ?? [];\n}\n\n/**\n * Write the create-time seed into a space's index doc with an already-open access handle.\n * Accepts a nullable encryptor — plaintext push when null (public spaces).\n * Idempotent: a no-op if the index doc already exists (either encrypted or plaintext).\n */\nexport async function pushIndexSeed(\n client: StarfishClient,\n encryptor: Encryptor | null,\n spaceId: string,\n rooms: SeedRoom[],\n): Promise<void> {\n const res = await client.pull(objIndexPull(spaceId)).catch(() => null);\n const existing = res?.data as Record<string, unknown> | undefined;\n if (existing?._encrypted || Array.isArray(existing?.objects)) return;\n const nodes = seedIndexNodes(rooms, Date.now());\n const payload = encryptor\n ? await encryptor.encrypt({ objects: nodes }) as Record<string, unknown>\n : { objects: nodes };\n await client.push(objIndexPush(spaceId), payload, res?.hash ?? null);\n}\n\n/**\n * Seed a brand-new space's index as the OWNER.\n * For private spaces: opens (minting if needed) the space keyring.\n * For public spaces: pushes plaintext nodes.\n */\nexport async function seedSpaceObjectIndex(\n session: Session,\n spaceId: string,\n rooms: SeedRoom[],\n opts?: { visibility?: SpaceVisibility },\n): Promise<void> {\n const { encryptor, client } = await getSpaceAccess(spaceId, session, {\n owner: session.userId,\n members: [],\n visibility: opts?.visibility,\n });\n await pushIndexSeed(client, encryptor, spaceId, rooms);\n}\n\n/**\n * Headless read-modify-write of a space's unified OBJECT INDEX. Works for both\n * private (encrypt round-trip) and public (plaintext) spaces. Retries up to 3 times\n * on ConflictError.\n */\nexport async function updateObjectIndex(\n session: Session,\n spaceId: string,\n mutator: (nodes: ObjectNode[], now: number) => ObjectNode[] | null,\n reg?: { owner: string | null; members: string[]; visibility?: SpaceVisibility } | null,\n): Promise<void> {\n const { client, encryptor } = await getSpaceAccess(spaceId, session, reg ?? null);\n const pullPath = objIndexPull(spaceId);\n const pushPath = objIndexPush(spaceId);\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const res = await client.pull(pullPath).catch(() => null);\n const raw = res?.data as Record<string, unknown> | undefined;\n const plain = raw\n ? encryptor\n ? await encryptor.decrypt(raw)\n : raw\n : {};\n const cur = Array.isArray((plain as { objects?: unknown }).objects)\n ? (plain as { objects: ObjectNode[] }).objects\n : [];\n const next = mutator(cur, Date.now());\n if (!next) return;\n const payload = encryptor\n ? await encryptor.encrypt({ objects: next }) as Record<string, unknown>\n : { objects: next };\n try {\n await client.push(pushPath, payload, res?.hash ?? null);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n","/**\n * Space membership — both keyring-based (private spaces) and link-based (public spaces).\n *\n * PRIVATE join: the owner adds the invitee to the space keyring, records them in the\n * roster, and mints a space-scoped member cap. The invitee verifies keyring access on\n * accept and stores a `{kind:'member'}` entry.\n *\n * PUBLIC (link) join: the owner mints an ephemeral Ed/KEM keypair whose *private* key\n * ships inside a URL-fragment token, adds the ephemeral userId to the roster so the\n * server grants `space:member`, and mints a member cap scoped to that ephemeral subject.\n * Any bearer of the link stores a `{kind:'link'}` entry. Revocation = `removeSpaceMember`.\n */\nimport { generateDeviceKeys } from '@drakkar.software/starfish-identities';\nimport { addCollectionRecipient } from '@drakkar.software/starfish-keyring';\nimport { mintMemberCap } from '@drakkar.software/starfish-sharing';\n\nimport type { Space } from '../core/types.js';\nimport { buildEncryptor, makeClient } from '../sync/client.js';\nimport type { Session } from '../sync/identity.js';\nimport {\n getSpaceAccessEntry,\n hydrateSpaceAccessStore,\n localSpaceAccessEntries,\n saveSpaceAccessEntry,\n} from '../sync/space-access-store.js';\nimport { keyringName, spaceMemberScope, userIdFromEdPub } from '../sync/paths.js';\nimport { addJoinedSpaceWithCap, addJoinedSpaceWithLinkAccess, addSpaceMember, readSpaces, updateSpacesDoc } from './registry.js';\nimport { sealToSelf, unsealFromSelf } from '../sync/account-seal.js';\nimport { toBase64Url, fromBase64Url } from '../sync/base64url.js';\n\nexport interface JoinRequest {\n edPub: string;\n kemPub: string;\n userId: string;\n}\n\nexport function makeJoinRequest(session: Session): string {\n const req: JoinRequest = { edPub: session.keys.edPub, kemPub: session.keys.kemPub, userId: session.userId };\n return JSON.stringify(req);\n}\n\ninterface SpaceInvite {\n spaceId: string;\n spaceName: string;\n cap: unknown;\n}\n\nfunction isAlreadyPresentRecipient(err: unknown): boolean {\n return err instanceof Error && /already present in epoch/.test(err.message);\n}\n\n/**\n * Owner-side: add a recipient's KEM key to a SPACE keyring (one keyring → every\n * object in the space). Reused by {@link inviteToSpace} and by device pairing.\n */\nexport async function addDeviceToSpaceKeyring(\n session: Session,\n spaceId: string,\n recipient: { kemPub: string; userId: string },\n): Promise<void> {\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: recipient.kemPub, userId: recipient.userId, label: recipient.userId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err)) throw err;\n }\n}\n\n/**\n * Owner: invite an identity into a PRIVATE space. Adds them to the keyring, records\n * them in the roster, and mints a single space-scoped member cap.\n * Returns the invite bundle JSON.\n */\nexport async function inviteToSpace(\n session: Session,\n spaceId: string,\n requestJson: string,\n canWrite = true,\n spaceName?: string,\n): Promise<string> {\n const req = JSON.parse(requestJson) as JoinRequest;\n if (!req.edPub || !req.kemPub || !req.userId) throw new Error('That is not a valid join request.');\n await addDeviceToSpaceKeyring(session, spaceId, { kemPub: req.kemPub, userId: req.userId });\n await addSpaceMember(session.accountClient, spaceId, session.userId, req.userId);\n // NOTE: 'chat' is the cap collection the deployed server's space-member enricher recognises.\n const cap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: req.edPub, kemPubHex: req.kemPub, userIdHex: req.userId },\n 'chat',\n spaceMemberScope(spaceId, canWrite),\n );\n let name = spaceName?.trim();\n if (!name) {\n const { spaces } = await readSpaces(session.accountClient, session.userId);\n name = spaces.find((s) => s.id === spaceId)?.name ?? 'Space';\n }\n const invite: SpaceInvite = { spaceId, spaceName: name, cap };\n return JSON.stringify(invite);\n}\n\n/**\n * Invitee: accept a PRIVATE space invite — verify keyring access, store the cap,\n * and register the space. Returns the joined space.\n */\nexport async function acceptSpaceInvite(session: Session, inviteJson: string): Promise<Space> {\n const inv = JSON.parse(inviteJson) as Partial<SpaceInvite>;\n const cap = inv.cap as { kind?: string; sub?: string; iss?: string } | undefined;\n if (!cap || !inv.spaceId) throw new Error('That is not a valid space invite.');\n if (cap.kind !== 'member') throw new Error('That is not a valid space invite.');\n if (!cap.sub || cap.sub !== session.keys.edPub) {\n throw new Error('This invite was issued for a different identity.');\n }\n if (!cap.iss) throw new Error('This invite is missing its issuer.');\n const spaceId = inv.spaceId;\n const client = makeClient(cap, session.keys.edPriv);\n const enc = await buildEncryptor(client, session.keys, spaceId, [cap.iss]);\n if (!enc) throw new Error(\"Accepted, but you're not in the space keyring yet — ask the owner to re-invite.\");\n const capJson = JSON.stringify(cap);\n const name = inv.spaceName?.trim() || `space-${spaceId.slice(-6)}`;\n const space: Space = { id: spaceId, name, short: name.slice(0, 2).toUpperCase(), members: 1 };\n await addJoinedSpaceWithCap(session.accountClient, session.userId, space, capJson);\n saveSpaceAccessEntry(spaceId, { kind: 'member', cap: capJson });\n return space;\n}\n\n// ── Link-based joins (public spaces) ─────────────────────────────────────────\n\n/** A space invite link token (v:1, no ownerId — derive from cap.iss instead). */\nexport interface SpaceInviteLinkToken {\n v: 1;\n spaceId: string;\n spaceName: string;\n cap: unknown;\n /** The throwaway ephemeral subject's Ed25519 private key (hex). */\n key: string;\n write: boolean;\n}\n\nexport function encodeSpaceInviteLink(origin: string, token: SpaceInviteLinkToken): string {\n const base = origin.replace(/\\/+$/, '');\n return `${base}/join#${toBase64Url(JSON.stringify(token))}`;\n}\n\nexport function decodeSpaceInviteLink(fragment: string): SpaceInviteLinkToken {\n const frag = fragment.startsWith('#') ? fragment.slice(1) : fragment;\n const tok = JSON.parse(fromBase64Url(frag)) as Partial<SpaceInviteLinkToken>;\n if (!tok || !tok.spaceId || !tok.cap || !tok.key) {\n throw new Error('That space invite link is malformed or incomplete.');\n }\n return {\n v: 1,\n spaceId: tok.spaceId,\n spaceName: tok.spaceName ?? 'Space',\n cap: tok.cap,\n key: tok.key,\n write: !!tok.write,\n };\n}\n\n/**\n * Owner: create a shareable invite link for a PUBLIC space.\n *\n * Mints an ephemeral Ed/KEM keypair, adds its userId to the roster (so the server\n * grants `space:member` to any bearer), and encodes the private key + cap in the URL.\n * Anyone with the link can join; revoke by calling `removeSpaceMember(ephemeralUserId)`.\n */\nexport async function createSpaceInviteLink(\n session: Session,\n spaceId: string,\n spaceName: string,\n write: boolean,\n origin: string,\n): Promise<{ token: SpaceInviteLinkToken; link: string }> {\n const ek = generateDeviceKeys();\n const ephemeralUserId = await userIdFromEdPub(ek.edPub);\n const cap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: ek.edPub, kemPubHex: ek.kemPub, userIdHex: ephemeralUserId },\n 'chat',\n spaceMemberScope(spaceId, write),\n );\n // Add the ephemeral userId to the roster so the server grants `space:member`\n await addSpaceMember(session.accountClient, spaceId, session.userId, ephemeralUserId);\n const token: SpaceInviteLinkToken = { v: 1, spaceId, spaceName, cap, key: ek.edPriv, write };\n return { token, link: encodeSpaceInviteLink(origin, token) };\n}\n\n/**\n * Any user: join a PUBLIC space by redeeming an invite link token.\n * Stores the link credential locally and seals it into the synced `_spaces` doc.\n */\nexport async function joinSpaceByLink(session: Session, token: SpaceInviteLinkToken): Promise<Space> {\n const cap = token.cap as { iss?: string };\n const ownerId = cap.iss ? await userIdFromEdPub(cap.iss) : undefined;\n const name = token.spaceName.trim() || `space-${token.spaceId.slice(-6)}`;\n const space: Space = {\n id: token.spaceId,\n name,\n short: name.slice(0, 2).toUpperCase(),\n members: 1,\n visibility: 'public',\n ...(ownerId ? { ownerId } : {}),\n write: token.write,\n };\n const accessPayload = { cap: token.cap, key: token.key, write: token.write };\n const sealed = await sealToSelf(session, JSON.stringify(accessPayload));\n await addJoinedSpaceWithLinkAccess(session.accountClient, session.userId, space, sealed);\n saveSpaceAccessEntry(token.spaceId, { kind: 'link', cap: token.cap, key: token.key, write: token.write });\n return space;\n}\n\n/**\n * Single sign-in hydration: merges server-side caps (plaintext member caps from\n * `_spaces.caps`) and sealed link access (from `_spaces.pubAccess`) into the\n * unified space-access store. Call once on sign-in / account switch.\n * Backfills any local-only entries to the server.\n */\nexport async function recoverSpaceAccess(\n session: Session,\n server: { caps: Record<string, string>; pubAccess: Record<string, import('../sync/account-seal.js').SealedBlob> },\n): Promise<void> {\n // Unseal link access blobs\n const linkAccess: Record<string, { cap: unknown; key: string; write: boolean }> = {};\n for (const [spaceId, sealed] of Object.entries(server.pubAccess)) {\n try {\n const raw = await unsealFromSelf(session, sealed);\n const parsed = JSON.parse(raw) as { cap: unknown; key: string; write: boolean };\n if (parsed.cap && parsed.key) linkAccess[spaceId] = parsed;\n } catch (e) {\n console.error('[octospaces] recoverSpaceAccess: failed to unseal', spaceId, e);\n }\n }\n\n await hydrateSpaceAccessStore(session.userId, server.caps, linkAccess);\n\n // Backfill local-only entries to the server\n const local = localSpaceAccessEntries();\n const missingMemberCaps = Object.entries(local)\n .filter(([id, e]) => e.kind === 'member' && !(id in server.caps));\n const missingLinks = Object.entries(local)\n .filter(([id, e]) => e.kind === 'link' && !(id in server.pubAccess));\n\n if (missingMemberCaps.length === 0 && missingLinks.length === 0) return;\n\n try {\n const newCaps: Record<string, string> = {};\n for (const [id, e] of missingMemberCaps) if (e.kind === 'member') newCaps[id] = e.cap;\n\n const newPubAccess: Record<string, import('../sync/account-seal.js').SealedBlob> = {};\n for (const [id, e] of missingLinks) {\n if (e.kind === 'link') {\n newPubAccess[id] = await sealToSelf(session, JSON.stringify({ cap: e.cap, key: e.key, write: e.write }));\n }\n }\n\n await updateSpacesDoc(session.accountClient, session.userId, (cur) => ({\n spaces: cur.spaces,\n caps: { ...cur.caps, ...newCaps },\n pubAccess: { ...cur.pubAccess, ...newPubAccess },\n }));\n } catch (e) {\n console.error('[octospaces] recoverSpaceAccess: backfill failed', e);\n }\n}\n","/**\n * base64url for link fragments (UTF-8 safe, web + native) — the encoding both\n * invitation-link kinds ride in a URL `#fragment`. No padding, `+/` → `-_`.\n */\nexport function toBase64Url(json: string): string {\n const bytes = new TextEncoder().encode(json);\n let bin = '';\n for (const b of bytes) bin += String.fromCharCode(b);\n const b64 = typeof btoa === 'function' ? btoa(bin) : Buffer.from(json, 'utf-8').toString('base64');\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function fromBase64Url(b64url: string): string {\n const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');\n if (typeof atob === 'function') {\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n return Buffer.from(b64, 'base64').toString('utf-8');\n}\n","/**\n * Device pairing (one-way, PIN-sealed). The existing device provisions a new\n * device's keypair + cap bundle, seals it with the PIN (Argon2id → AES-GCM), and\n * drops it on the public `_pairing/<nonce>` rendezvous. The QR carries only the\n * nonce; the new device fetches the sealed blob, opens it with the PIN, and\n * validates the cap bundle.\n */\nimport { StarfishClient } from '@drakkar.software/starfish-client';\nimport {\n installPairingBundle,\n openWithPassphrase,\n provisionDevice,\n sealWithPassphrase,\n} from '@drakkar.software/starfish-identities';\nimport type { CapCert } from '@drakkar.software/starfish-protocol';\n\nimport type { DeviceKeys } from './client.js';\nimport { getSyncBase, getSyncNamespace } from '../core/config.js';\nimport { fetchWithTimeout } from './fetch-timeout.js';\nimport type { Session } from './identity.js';\nimport { fingerprintFromUserId } from './identity.js';\nimport { addDeviceToSpaceKeyring } from '../spaces/members.js';\nimport { bytesToHex, linkedDeviceScope } from './paths.js';\nimport { readSpaces } from '../spaces/registry.js';\n\n/** The QR-payload prefix this SDK uses. Kept distinct from `octochat-pair:` so apps\n * can route QR payloads to the correct handler during their migration window. */\nexport const PAIR_PREFIX = 'octospaces-pair:';\n\n// Linked-device cap-cert lifetime — one year keeps a linked device usable long-term.\nconst LINKED_DEVICE_TTL_SEC = 365 * 24 * 60 * 60;\n\nfunction anonClient(): StarfishClient {\n return new StarfishClient({ baseUrl: getSyncBase(), namespace: getSyncNamespace(), fetch: fetchWithTimeout() });\n}\n\nfunction randomNonce(): string {\n const b = new Uint8Array(16);\n globalThis.crypto.getRandomValues(b);\n return bytesToHex(b);\n}\n\n/** Existing device: provision + PIN-seal a new device, publish to rendezvous, return the QR payload. */\nexport async function startDevicePairing(session: Session, pin: string): Promise<string> {\n const { deviceKeys, bundle } = await provisionDevice(\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub },\n { scope: linkedDeviceScope(session.userId), ttlSec: LINKED_DEVICE_TTL_SEC },\n );\n const { spaces, caps } = await readSpaces(session.accountClient, session.userId);\n for (const space of spaces) {\n if (caps[space.id]) continue;\n try {\n await addDeviceToSpaceKeyring(session, space.id, { kemPub: deviceKeys.kemPub, userId: session.userId });\n } catch (err) {\n console.log('[pairing] keyring grant failed', { spaceId: space.id, error: String((err as Error)?.message ?? err) });\n }\n }\n const blob = JSON.stringify({ v: 1, keys: deviceKeys, bundle });\n const sealed = await sealWithPassphrase(pin, new TextEncoder().encode(blob));\n const nonce = randomNonce();\n await anonClient().push(`/push/_pairing/${nonce}`, sealed as unknown as Record<string, unknown>, null);\n return `${PAIR_PREFIX}${nonce}.${session.keys.edPub}`;\n}\n\nexport interface PairResult {\n userId: string;\n fingerprint: string;\n deviceKeys: DeviceKeys;\n capCert: CapCert;\n}\n\n/** New device: fetch the sealed blob by nonce, open with PIN, validate the bundle. */\nexport async function completeDevicePairing(payload: string, pin: string): Promise<PairResult> {\n // Accept both `octospaces-pair:` and legacy `octochat-pair:` so apps still using the\n // old QR format can complete a pairing against this SDK during the migration window.\n const body = (payload.startsWith(PAIR_PREFIX) || payload.includes('-pair:')\n ? payload.slice(payload.indexOf(':') + 1)\n : payload).trim();\n const [nonce, expectedRootEdPub] = body.split('.');\n const res = await anonClient().pull(`/pull/_pairing/${nonce}`).catch(() => null);\n const sealed = res?.data as Record<string, unknown> | undefined;\n if (!sealed || !sealed.v) throw new Error('Pairing code not found or expired.');\n let inner: Uint8Array;\n try {\n inner = await openWithPassphrase(pin, sealed as never);\n } catch {\n throw new Error('Wrong PIN or corrupted pairing code.');\n }\n const blob = JSON.parse(new TextDecoder().decode(inner)) as { keys: unknown; bundle: unknown };\n const opts = (expectedRootEdPub ? { expectedRootEdPub } : {}) as Parameters<typeof installPairingBundle>[2];\n const installed = await installPairingBundle(\n blob.bundle as Parameters<typeof installPairingBundle>[0],\n blob.keys as Parameters<typeof installPairingBundle>[1],\n opts,\n );\n const userId = installed.credentials.userId;\n return {\n userId,\n fingerprint: fingerprintFromUserId(userId),\n deviceKeys: installed.credentials.device,\n capCert: installed.credentials.capCert,\n };\n}\n","/**\n * A chunked base64 provider for the Starfish platform.\n *\n * The SDK's default web encoder is `btoa(String.fromCharCode(...data))`, which\n * spreads the entire byte array into one call — a multi-megabyte attachment\n * overflows the argument/stack limit and throws \"Maximum call stack size exceeded\".\n * This provider walks the bytes in fixed windows instead, so it scales to large blobs.\n *\n * Prefers the platform's own `btoa`/`atob` (web) and falls back to a pure\n * implementation where they're absent (Hermes/native).\n */\nimport type { Base64Provider } from '@drakkar.software/starfish-protocol';\n\nconst CHUNK = 0x6000; // 24 576 bytes — multiple of 3, well under V8's apply limit\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nconst REVERSE = (() => {\n const table = new Int16Array(128).fill(-1);\n for (let i = 0; i < ALPHABET.length; i++) table[ALPHABET.charCodeAt(i)] = i;\n return table;\n})();\n\nconst nativeCodec =\n typeof globalThis !== 'undefined' &&\n typeof globalThis.btoa === 'function' &&\n typeof globalThis.atob === 'function';\n\nfunction encodeViaBtoa(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i += CHUNK) {\n binary += String.fromCharCode.apply(null, data.subarray(i, i + CHUNK) as unknown as number[]);\n }\n return globalThis.btoa(binary);\n}\n\nfunction decodeViaAtob(encoded: string): Uint8Array {\n const binary = globalThis.atob(encoded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nfunction encodePure(data: Uint8Array): string {\n const len = data.length;\n const full = len - (len % 3);\n const parts: string[] = [];\n for (let start = 0; start < full; start += CHUNK) {\n const stop = Math.min(start + CHUNK, full);\n let s = '';\n for (let i = start; i < stop; i += 3) {\n const n = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];\n s += ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + ALPHABET[(n >> 6) & 63] + ALPHABET[n & 63];\n }\n parts.push(s);\n }\n if (len - full === 1) {\n const n = data[full] << 16;\n parts.push(ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + '==');\n } else if (len - full === 2) {\n const n = (data[full] << 16) | (data[full + 1] << 8);\n parts.push(ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + ALPHABET[(n >> 6) & 63] + '=');\n }\n return parts.join('');\n}\n\nfunction decodePure(encoded: string): Uint8Array {\n let validLen = encoded.length;\n while (validLen > 0 && encoded.charCodeAt(validLen - 1) === 61) validLen--;\n const out = new Uint8Array((validLen * 3) >> 2);\n let o = 0, buf = 0, bits = 0;\n for (let i = 0; i < validLen; i++) {\n const code = encoded.charCodeAt(i);\n const v = code < 128 ? REVERSE[code] : -1;\n if (v < 0) continue;\n buf = (buf << 6) | v;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[o++] = (buf >> bits) & 0xff;\n }\n }\n return o === out.length ? out : out.subarray(0, o);\n}\n\n/** Spread-free, chunked base64 — a drop-in for the SDK's default provider. */\nexport const starfishBase64: Base64Provider = nativeCodec\n ? { encode: encodeViaBtoa, decode: decodeViaAtob }\n : { encode: encodePure, decode: decodePure };\n"],"mappings":"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","names":["key","key","bytesToHex","key","key","cache","encryptor","client","ConflictError","ConflictError","shared","name","short","image","req","StarfishClient","StarfishClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/core/adapters.ts","../src/core/ids.ts","../src/sync/paths.ts","../src/sync/fetch-timeout.ts","../src/sync/pull-cache.ts","../src/sync/profile-cache.ts","../src/core/space-access-error.ts","../src/sync/client.ts","../src/sync/identity.ts","../src/sync/space-access-store.ts","../src/sync/space-access.ts","../src/spaces/object-index.ts","../src/spaces/registry.ts","../src/index.ts","../src/sync/account-seal.ts","../src/spaces/members.ts","../src/sync/base64url.ts","../src/spaces/nodes.ts","../src/objects/objects.ts","../src/sync/pairing.ts","../src/sync/base64.ts","../src/utils/search-match.ts","../src/utils/live-sync-bus.ts","../src/utils/invite-preview.ts"],"sourcesContent":["/**\n * Runtime configuration for the OctoSpaces SDK — the Starfish sync server URL,\n * optional namespace, events-stream URL, and public web origin.\n *\n * The SDK is headless and platform-agnostic, so it does NOT read environment\n * variables itself. The host app reads its own env (e.g. Expo `EXPO_PUBLIC_*`) and\n * calls {@link configureOctoSpaces} once at boot, before any sync/identity API runs.\n * Getters throw a clear error if called before configuration so a misconfigured\n * host fails fast rather than silently signing the wrong path.\n */\nexport interface OctoSpacesConfig {\n /** Starfish sync server base URL (e.g. `http://localhost:8787`). */\n syncBase: string;\n /** Bare namespace name; the SDK prepends `/v1/<namespace>` to signed paths.\n * Unset for a root-mounted (local dev) server. */\n syncNamespace?: string;\n /**\n * Optional SEPARATE namespace for cross-app shared-spaces storage. When set,\n * space registry ops use this namespace instead of `syncNamespace`, enabling a\n * single shared space list across multiple app namespaces (e.g. OctoChat and\n * OctoVault sharing spaces at `/v1/shared`). If unset, falls back to the default\n * namespace for all operations (single-app behavior).\n */\n sharedSpacesNamespace?: string;\n /** Override the live change-event SSE endpoint. Defaults to\n * `${syncBase}${syncPrefix}/events`. */\n eventsUrl?: string;\n /** Public origin of the web app, used to build shareable invite links on\n * platforms without `window.location` (native). Empty by default. */\n webBase?: string;\n /**\n * Called when a background Starfish revalidation succeeds after a 429/5xx\n * cache-fallback (stale-while-revalidate). Use it to signal that the server\n * is reachable again so any stale views re-pull and recover.\n */\n onServerReachable?: () => void;\n}\n\nlet cfg: OctoSpacesConfig | null = null;\n\n/** Configure the SDK. Call once at app boot before any sync/identity API. */\nexport function configureOctoSpaces(config: OctoSpacesConfig): void {\n // Guard against the common mistake of passing `namespace` (wrong key) instead of\n // `syncNamespace`. TypeScript's excess-property check is bypassed when the config\n // is assembled via a conditional spread, so the wrong key would be silently ignored.\n if ('namespace' in config && !config.syncNamespace) {\n throw new Error(\n `octospaces-sdk: configureOctoSpaces received \"namespace\" — did you mean \"syncNamespace\"?`,\n );\n }\n const ns = (config.syncNamespace ?? '').trim();\n if (ns !== '' && !/^[A-Za-z0-9_-]+$/.test(ns)) {\n throw new Error(`octospaces-sdk: syncNamespace must be a bare name ([A-Za-z0-9_-]+), got \"${ns}\"`);\n }\n const sharedNs = (config.sharedSpacesNamespace ?? '').trim();\n if (sharedNs !== '' && !/^[A-Za-z0-9_-]+$/.test(sharedNs)) {\n throw new Error(`octospaces-sdk: sharedSpacesNamespace must be a bare name ([A-Za-z0-9_-]+), got \"${sharedNs}\"`);\n }\n cfg = {\n ...config,\n syncNamespace: ns || undefined,\n sharedSpacesNamespace: sharedNs || undefined,\n };\n}\n\nfunction req(): OctoSpacesConfig {\n if (!cfg) throw new Error('octospaces-sdk: configureOctoSpaces() not called — wire it at app boot.');\n return cfg;\n}\n\n/** Starfish sync server base URL. */\nexport const getSyncBase = (): string => req().syncBase;\n/** Bare namespace name (or `undefined` for a root-mounted server). */\nexport const getSyncNamespace = (): string | undefined => req().syncNamespace;\n/** Namespaced path prefix (`/v1/<namespace>`, or `''` locally). */\nexport const getSyncPrefix = (): string => {\n const ns = req().syncNamespace;\n return ns ? `/v1/${ns}` : '';\n};\n/** Optional separate namespace for shared-spaces storage. `undefined` means use the default namespace. */\nexport const getSharedSpacesNamespace = (): string | undefined => cfg?.sharedSpacesNamespace;\n/** Live change-event SSE endpoint. */\nexport const getEventsUrl = (): string => req().eventsUrl ?? `${getSyncBase()}${getSyncPrefix()}/events`;\n/** Public web origin (right-trimmed of trailing slashes; `''` by default). */\nexport const getWebBase = (): string => (req().webBase ?? '').replace(/\\/+$/, '');\n/** Callback to invoke when a background Starfish revalidation succeeds. */\nexport const getOnServerReachable = (): (() => void) | undefined => cfg?.onServerReachable;\n","/**\n * Platform adapters the headless SDK needs the host app to provide.\n *\n * The SDK can't do Metro `.native.ts` file-extension resolution and must not bind\n * to localStorage / AsyncStorage / SecureStore directly, so the host injects a\n * key/value store at boot via {@link configureKv}. This holds account-scoped state\n * the SDK persists offline (joined-space member caps, the public-space access map,\n * read marks, mutes, profile/pull caches).\n */\n\n/** Async key/value store — web `localStorage`, native `AsyncStorage`, etc. */\nexport interface KvAdapter {\n get(key: string): Promise<string | null>;\n set(key: string, value: string): Promise<void>;\n remove(key: string): Promise<void>;\n}\n\nlet kv: KvAdapter | null = null;\n\n/** Install the host's key/value store. Call once at app boot. */\nexport function configureKv(adapter: KvAdapter): void {\n kv = adapter;\n}\n\n/** The configured KV store, or throw if the host never called {@link configureKv}. */\nexport function getKv(): KvAdapter {\n if (!kv) throw new Error('octospaces-sdk: configureKv() not called — wire it at app boot.');\n return kv;\n}\n\n// Free-function shims matching the historical `kv` module surface.\nexport const kvGet = (key: string): Promise<string | null> => getKv().get(key);\nexport const kvSet = (key: string, value: string): Promise<void> => getKv().set(key, value);\nexport const kvRemove = (key: string): Promise<void> => getKv().remove(key);\n","/**\n * Identifier helpers — one source for unguessable ids.\n *\n * `randomId()` is a CSPRNG-backed 128-bit id (16 random bytes, hex). Use it for\n * EVERY storage/space/room/object/blob id. Hex output is path-safe and server-safe.\n */\nexport function randomId(): string {\n const bytes = new Uint8Array(16);\n globalThis.crypto.getRandomValues(bytes);\n let s = '';\n for (const b of bytes) s += b.toString(16).padStart(2, '0');\n return s;\n}\n\n/**\n * Slug for the human part of an id (e.g. `<spaceId>-<slug>-<ts>`). Restricted to\n * URL-clean `[a-z0-9-]` so the id is safe as both a URL path segment and a\n * server storage-path leaf (the server's FilesystemObjectStore rejects any key\n * outside `[a-zA-Z0-9._:@/-]`). Falls back to `'room'` when a name strips to nothing.\n */\nexport function roomSlug(name: string): string {\n return (\n name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 40) || 'room'\n );\n}\n","/**\n * Collection path + cap-scope helpers for OctoSpaces.\n *\n * Paths are signed relative to SYNC_BASE; the server mounts the sync router at\n * root, so they start with /pull or /push. Everything for a space is nested under\n * `spaces/{spaceId}/…` so the `{spaceId}` segment gates it all uniformly through the\n * space:owner/space:member enricher, and a single `spaces/{spaceId}/**` member cap\n * covers a whole space.\n *\n * **Generic object collections** — scopes use the `obj*` collection names (the\n * domain-neutral storage layer). The access record lives at\n * `spaces/{spaceId}/_access` (collection `spaceregistry`); the space-wide keyring at\n * `spaces/{spaceId}/_keyring` (collection `spacekeyring`). ONE keyring per space\n * encrypts ALL the space's `enc` nodes.\n *\n * Note: `objinv` (invite-plaintext content) is intentionally EXCLUDED from\n * OBJECT_COLLECTIONS / spaceMemberScope — only a per-node cap can reach it.\n */\nimport type { ScopePreset } from '@drakkar.software/starfish-identities';\n\nconst pull = (rest: string) => `/pull/${rest}`;\nconst push = (rest: string) => `/push/${rest}`;\n\n/** A room id is `sp-<rand>-<name>`; the space is its first two `-` segments. */\nexport const spaceIdFromRoomId = (roomId: string) => roomId.split('-').slice(0, 2).join('-');\n\n// ── Space-wide keyring (one keyring per space, encrypts all enc nodes) ────────\n/** Base name used as the `collectionName` arg to `addCollectionRecipient`.\n * Appending `/_keyring` gives the full storage path. */\nexport const keyringName = (spaceId: string) => `spaces/${spaceId}`;\nexport const keyringPull = (spaceId: string) => pull(`${keyringName(spaceId)}/_keyring`);\nexport const keyringPush = (spaceId: string) => push(`${keyringName(spaceId)}/_keyring`);\n\n// ── Attachments (sealed blobs, in a per-space subtree keyed by room) ──────────\n/** Storage path of one attachment blob — also the AAD bound into its seal. */\nexport const attachmentName = (roomId: string, blobId: string) =>\n `spaces/${spaceIdFromRoomId(roomId)}/attachments/${roomId}/${blobId}`;\nexport const attachmentPull = (roomId: string, blobId: string) => pull(attachmentName(roomId, blobId));\nexport const attachmentPush = (roomId: string, blobId: string) => push(attachmentName(roomId, blobId));\n\n// ── Profile + registries ──────────────────────────────────────────────────────\nexport const profilePull = (userId: string) => pull(`user/${userId}/profile`);\nexport const profilePush = (userId: string) => push(`user/${userId}/profile`);\n\nexport const spacesPull = (userId: string) => pull(`user/${userId}/_spaces`);\nexport const spacesPush = (userId: string) => push(`user/${userId}/_spaces`);\n\nexport const spaceAccessPull = (spaceId: string) => pull(`spaces/${spaceId}/_access`);\nexport const spaceAccessPush = (spaceId: string) => push(`spaces/${spaceId}/_access`);\n\n// ── Object index (member-gated, always plaintext) ────────────────────────────\n// The index lists every node with structural fields plaintext. For `invite`\n// nodes the title/emoji are stripped before storage; only invited members read\n// the real title from the node's content doc. Keep in sync with the objindex\n// collection in apps/server AND Infra collections.py.\nexport const objIndexName = (spaceId: string) => `spaces/${spaceId}/objects/_index`;\nexport const objIndexPull = (spaceId: string) => pull(objIndexName(spaceId));\nexport const objIndexPush = (spaceId: string) => push(objIndexName(spaceId));\n\n// ── Space-tier & general object content (space:member gated) ─────────────────\n//\n// objects/logs/{id} — WAL/CRDT append-only op-log (contentKind \"append\")\n// objects/logs/{id}__snapshot — sibling LWW snapshot for fast cold-start\n// objects/docs/{id} — LWW merge-doc (contentKind \"merge\")\n// objects/blobs/{id} — sealed raw binary blob\n//\n// Keep in sync with the objlog/objsnap/objdoc/objblob collections in\n// apps/server AND Infra collections.py.\nexport const objLogName = (spaceId: string, objectId: string) => `spaces/${spaceId}/objects/logs/${objectId}`;\nexport const objLogPull = (spaceId: string, objectId: string) => pull(objLogName(spaceId, objectId));\nexport const objLogPush = (spaceId: string, objectId: string) => push(objLogName(spaceId, objectId));\n\nexport const objDocName = (spaceId: string, objectId: string) => `spaces/${spaceId}/objects/docs/${objectId}`;\nexport const objDocPull = (spaceId: string, objectId: string) => pull(objDocName(spaceId, objectId));\nexport const objDocPush = (spaceId: string, objectId: string) => push(objDocName(spaceId, objectId));\n\n/** Storage path of one sealed object blob — also the AAD bound into its seal. */\nexport const objectBlobName = (spaceId: string, blobId: string) => `spaces/${spaceId}/objects/blobs/${blobId}`;\nexport const objectBlobPull = (spaceId: string, blobId: string) => pull(objectBlobName(spaceId, blobId));\nexport const objectBlobPush = (spaceId: string, blobId: string) => push(objectBlobName(spaceId, blobId));\n\n// ── Public node content (world-readable) ─────────────────────────────────────\n// For `access:'public'` nodes, content is stored here so anonymous readers can\n// fetch it without being a space member. Keep in sync with objpub in server config.\nexport const objPubName = (spaceId: string, nodeId: string) => `spaces/${spaceId}/objects/pub/${nodeId}`;\nexport const objPubPull = (spaceId: string, nodeId: string) => pull(objPubName(spaceId, nodeId));\nexport const objPubPush = (spaceId: string, nodeId: string) => push(objPubName(spaceId, nodeId));\n\n// ── Invite-only plaintext content (cap-gated) ────────────────────────────────\n// For `access:'invite' + enc:false` nodes, content is stored here. The `objinv`\n// collection is intentionally excluded from spaceMemberScope — only a per-node cap\n// (nodeMemberScope) can reach it. Keep in sync with objinv in server config.\nexport const objInvName = (spaceId: string, nodeId: string) => `spaces/${spaceId}/objects/n/${nodeId}/content`;\nexport const objInvPull = (spaceId: string, nodeId: string) => pull(objInvName(spaceId, nodeId));\nexport const objInvPush = (spaceId: string, nodeId: string) => push(objInvName(spaceId, nodeId));\n\n// ── Per-space custom type registry ────────────────────────────────────────────\nexport const typesIndexName = (spaceId: string) => `spaces/${spaceId}/types/_index`;\nexport const typesIndexPull = (spaceId: string) => pull(typesIndexName(spaceId));\nexport const typesIndexPush = (spaceId: string) => push(typesIndexName(spaceId));\n\n// ── Global object directory (server-maintained projection) ───────────────────\n// Pull `_index/objects/{shard}` to discover world-readable public nodes.\n// Default shard 'public'; future: sharded by app-supplied type string.\nexport const objectDirName = (shard: string = 'public') => `_index/objects/${shard}`;\nexport const objectDirPull = (shard: string = 'public') => pull(objectDirName(shard));\n\n// ── Generic object collections — used in cap scopes ──────────────────────────\n// These are the domain-neutral storage collections both apps migrate onto.\n// IMPORTANT: `objinv` is NOT included here — it is excluded from the broad space\n// member scope so that only per-node caps (nodeMemberScope) can reach it.\n// `spacekeyring` IS included — space members need to reach the keyring to decrypt enc content.\nexport const OBJECT_COLLECTIONS: string[] = [\n 'spacekeyring', 'objindex', 'objlog', 'objsnap', 'objdoc', 'objblob', 'typeindex', 'objpub',\n];\n\n// ── Cap scopes ────────────────────────────────────────────────────────────────\n\n/** Full owner/device access to every space the identity owns. */\nexport function ownerScope(): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: OBJECT_COLLECTIONS,\n paths: ['spaces/**'],\n };\n}\n\n/**\n * Member access to one SPACE — the space keyring, every node's content docs and\n * attachments, all under `spaces/{spaceId}/**`. Does NOT cover `objinv` (invite-\n * plaintext content) — use `nodeMemberScope` for that. One cap covers current AND\n * future nodes.\n */\nexport function spaceMemberScope(spaceId: string, canWrite: boolean): ScopePreset {\n const ops: ('read' | 'write' | 'list')[] = canWrite ? ['read', 'list', 'write'] : ['read', 'list'];\n return {\n ops,\n collections: OBJECT_COLLECTIONS,\n paths: [`spaces/${spaceId}/**`],\n };\n}\n\n/**\n * Narrow per-node cap for `invite+plaintext` nodes. Covers ONLY the node's `objinv`\n * content path — the space keyring is space-wide and is covered by the broader space\n * member scope. Use `spaceMemberScope` when the bearer also needs to decrypt enc content.\n */\nexport function nodeMemberScope(spaceId: string, nodeId: string, canWrite: boolean): ScopePreset {\n const ops: ('read' | 'write' | 'list')[] = canWrite ? ['read', 'list', 'write'] : ['read', 'list'];\n return {\n ops,\n collections: ['objinv'],\n paths: [`spaces/${spaceId}/objects/n/${nodeId}/**`],\n };\n}\n\n/**\n * Personal cap: profile + space registry + device directory + all spaces.\n * Note: app-specific collections like `'dminbox'` (chat) are NOT included here —\n * add them in the consumer's own `paths.ts` extension.\n */\nexport function accountScope(userId: string): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: ['profile', 'devices', 'spaces', 'spaceregistry'],\n paths: [\n `user/${userId}/profile`,\n `users/${userId}/_devices`,\n `user/${userId}/_spaces`,\n 'spaces/**',\n ],\n };\n}\n\n/**\n * The single cap-cert scope granted to a PAIRED (linked) device. Covers both the\n * object-store client (ownerScope) and the account client (accountScope), deduped,\n * because a paired device cannot self-mint — it presents one root-signed cap-cert.\n */\nexport function linkedDeviceScope(userId: string): ScopePreset {\n return {\n ops: ['read', 'list', 'write'],\n collections: [...OBJECT_COLLECTIONS, 'profile', 'devices', 'spaces', 'spaceregistry'],\n paths: [\n 'spaces/**',\n `user/${userId}/profile`,\n `users/${userId}/_devices`,\n `user/${userId}/_spaces`,\n ],\n };\n}\n\n/** Extract the single space id a member cap is scoped to (from its `spaces/<id>/**`).\n * Returns null if the cap names no space path OR more than one distinct space. */\nexport function spaceIdFromCap(cap: { scope?: { paths?: string[] } }): string | null {\n let found: string | null = null;\n for (const p of cap.scope?.paths ?? []) {\n const m = /^spaces\\/([^/]+)\\//.exec(p);\n if (!m) continue;\n if (found !== null && found !== m[1]) return null;\n found = m[1]!;\n }\n return found;\n}\n\nexport function bytesToHex(b: Uint8Array): string {\n let s = '';\n for (const x of b) s += x.toString(16).padStart(2, '0');\n return s;\n}\n\n/** The canonical identity derivation: `userId = sha256(edPub)[0:32]` (hex). */\nexport async function userIdFromEdPub(edPubHex: string): Promise<string> {\n const bytes = new Uint8Array(edPubHex.length / 2);\n for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(edPubHex.slice(i * 2, i * 2 + 2), 16);\n const digest = await globalThis.crypto.subtle.digest('SHA-256', bytes);\n return bytesToHex(new Uint8Array(digest)).slice(0, 32);\n}\n","/**\n * A `fetch` wrapper that bounds the CONNECT/TTFB phase only.\n *\n * Aborts a request that hasn't RESPONDED within {@link CONNECT_TIMEOUT_MS}, turning\n * an opaque infinite spinner into a normal rejection the open path can surface as a\n * retriable error. Clears the timer once response headers arrive, so it bounds ONLY\n * the connect phase — body downloads and long-lived streams stay unbounded.\n */\n\nexport const CONNECT_TIMEOUT_MS = 12_000; // generous: trips only on a truly stalled socket\n\nexport function fetchWithTimeout(timeoutMs = CONNECT_TIMEOUT_MS): typeof fetch {\n return (input, init) => {\n const ctrl = new AbortController();\n const timer = setTimeout(() => ctrl.abort(), timeoutMs);\n const caller = init?.signal;\n if (caller) {\n if (caller.aborted) ctrl.abort();\n else caller.addEventListener('abort', () => ctrl.abort(), { once: true });\n }\n return fetch(input as RequestInfo, { ...init, signal: ctrl.signal }).finally(() => clearTimeout(timer));\n };\n}\n","/**\n * The app's offline-first read cache for every {@link StarfishClient}.\n *\n * Backs the SDK's {@link PullCache} (read-through pull cache) with the kv layer\n * (localStorage on web, AsyncStorage on native). When a client is built with this\n * cache, every successful structured `pull()` is written through, and a pull that\n * fails because the transport is unreachable falls back to the last-synced snapshot.\n *\n * SECURITY: the SDK caches the RAW server response only. For E2E collections that\n * payload is the SEALED ciphertext the server holds — never the decrypted form —\n * so this cache is ciphertext-at-rest by construction.\n */\nimport type { PullCache } from '@drakkar.software/starfish-client';\n\nimport { kvGet, kvSet } from '../core/adapters.js';\n\nconst PREFIX = 'octospaces.pullcache.';\n\n/**\n * Max age for a cached snapshot before it's treated as a miss. Generous (30 days)\n * because for an offline-first app any last-synced data beats none.\n */\nexport const PULL_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;\n\nlet shared: PullCache | undefined;\n\n/** The shared app-wide pull cache (one instance, reused across every client). */\nexport function pullCache(): PullCache {\n return (shared ??= {\n get: (key) => kvGet(PREFIX + key),\n set: (key, value) => kvSet(PREFIX + key, value),\n });\n}\n","/**\n * Offline-first cache for public profiles (pseudo + inline avatar).\n *\n * Profiles are PUBLIC plaintext so they don't flow through the SDK's read-through\n * pull cache. This kv cache gives them the same offline-first behavior: a successful\n * read is persisted per user, and a read that fails because the device is offline\n * falls back to the last-known pseudo/avatar.\n */\nimport { kvGet, kvSet } from '../core/adapters.js';\nimport type { PublicProfile } from './client.js';\n\nconst key = (userId: string) => `octospaces.profile.v1.${userId}`;\n\n/** Persist a freshly-read profile (fire-and-forget). */\nexport function cacheProfile(userId: string, profile: PublicProfile): void {\n void kvSet(key(userId), JSON.stringify(profile)).catch(() => {});\n}\n\n/** Last-known profile for a user, or null if never cached / unparseable. */\nexport async function loadCachedProfile(userId: string): Promise<PublicProfile | null> {\n try {\n const raw = await kvGet(key(userId));\n if (!raw) return null;\n const d = JSON.parse(raw) as Partial<PublicProfile>;\n return {\n pseudo: typeof d.pseudo === 'string' ? d.pseudo : null,\n avatar: typeof d.avatar === 'string' ? d.avatar : null,\n edPub: typeof d.edPub === 'string' ? d.edPub : null,\n kemPub: typeof d.kemPub === 'string' ? d.kemPub : null,\n };\n } catch {\n return null;\n }\n}\n","/**\n * {@link SpaceAccessError} — a GENUINE access denial (not a transient connectivity failure).\n *\n * Lives in its own dependency-free module so both the low-level keyring opener\n * (`client.ts`) and the higher-level space-encryptor cache (`space-encryptor.ts`) can\n * throw it without an import cycle.\n */\nexport class SpaceAccessError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SpaceAccessError';\n }\n}\n","/**\n * Starfish client construction + space keyring/encryptor helpers.\n */\nimport { StarfishClient } from '@drakkar.software/starfish-client';\nimport type { BatchPullEntry, Encryptor, StarfishCapProvider } from '@drakkar.software/starfish-client';\nimport { createKeyring, createKeyringEncryptor } from '@drakkar.software/starfish-keyring';\nimport type { Keyring } from '@drakkar.software/starfish-keyring';\nimport { signRequest, stableStringify } from '@drakkar.software/starfish-protocol';\nimport type { SignableMethod } from '@drakkar.software/starfish-protocol';\n\nimport { getSyncBase, getSyncNamespace, getSyncPrefix, getOnServerReachable } from '../core/config.js';\nimport { fetchWithTimeout } from './fetch-timeout.js';\nimport { pullCache, PULL_CACHE_MAX_AGE_MS } from './pull-cache.js';\nimport { cacheProfile, loadCachedProfile } from './profile-cache.js';\nimport { profilePull, profilePush } from './paths.js';\nimport { SpaceAccessError } from '../core/space-access-error.js';\n\nexport interface DeviceKeys {\n edPriv: string;\n edPub: string;\n kemPriv: string;\n kemPub: string;\n}\n\nexport function capProviderFor(cap: unknown, devEdPrivHex: string): StarfishCapProvider {\n return {\n async getCap() {\n return { cap: cap as never, devEdPrivHex };\n },\n };\n}\n\n/**\n * Build a Starfish client. `namespaceOverride` overrides the configured namespace,\n * enabling the shared-spaces feature (a separate namespace for cross-app registry ops).\n */\nexport function makeClient(cap: unknown, devEdPrivHex: string, namespaceOverride?: string): StarfishClient {\n return new StarfishClient({\n baseUrl: getSyncBase(),\n namespace: namespaceOverride ?? getSyncNamespace(),\n capProvider: capProviderFor(cap, devEdPrivHex),\n fetch: fetchWithTimeout(),\n cache: pullCache(),\n cacheMaxAgeMs: PULL_CACHE_MAX_AGE_MS,\n cacheFallbackStatuses: [429, 500, 502, 503, 504],\n onRevalidated: () => getOnServerReachable()?.(),\n });\n}\n\n/**\n * Open a node's decryptor, throwing a descriptive error per failure mode\n * (unreachable server / no keyring yet / not a recipient).\n *\n * `keyringPullPath` is the full `/pull/.../_keyring` path (e.g. from\n * `keyringPull(spaceId)`). A `SpaceAccessError` is a hard access\n * denial; any other thrown error is a transient offline state.\n */\nexport async function openEncryptor(\n client: StarfishClient,\n keys: DeviceKeys,\n keyringPullPath: string,\n trustedAdders: string[],\n): Promise<Encryptor> {\n const res = await client.pull(keyringPullPath).catch(() => {\n throw new Error('Could not reach the server to fetch node keys.');\n });\n const keyring = res?.data as unknown as Keyring | undefined;\n if (!keyring || !keyring.epochs) {\n throw new SpaceAccessError('This node has no keyring yet — ask the owner to create it first.');\n }\n try {\n const enc = await createKeyringEncryptor(\n keyring,\n { kemPubHex: keys.kemPub, kemPrivHex: keys.kemPriv },\n { trustedAdders },\n );\n return enc as unknown as Encryptor;\n } catch {\n throw new SpaceAccessError(\"You're not a recipient of this node's keyring yet — ask the owner to invite you.\");\n }\n}\n\n/** Soft variant of {@link openEncryptor}: returns null instead of throwing. */\nexport async function buildEncryptor(\n client: StarfishClient,\n keys: DeviceKeys,\n keyringPullPath: string,\n trustedAdders: string[],\n): Promise<Encryptor | null> {\n try {\n return await openEncryptor(client, keys, keyringPullPath, trustedAdders);\n } catch {\n return null;\n }\n}\n\n/**\n * Owner-side: create a per-node keyring if missing, return an encryptor.\n *\n * `keyringPullPath` / `keyringPushPath` are the full `/pull|push/.../_keyring`\n * paths (e.g. from `keyringPull`/`keyringPush`).\n */\nexport async function ownerEnsureKeyring(\n client: StarfishClient,\n keys: DeviceKeys,\n keyringPullPath: string,\n keyringPushPath: string,\n trustedAdders: string[] = [keys.edPub],\n): Promise<Encryptor> {\n const krRes = await client.pull(keyringPullPath).catch(() => null);\n let keyring = krRes?.data as unknown as Keyring | undefined;\n if (!keyring || !keyring.epochs) {\n const created = await createKeyring({ edPrivHex: keys.edPriv, edPubHex: keys.edPub }, [\n { subKemHex: keys.kemPub },\n ]);\n keyring = created.keyring;\n await client.push(keyringPushPath, keyring as unknown as Record<string, unknown>, krRes?.hash ?? null);\n }\n const enc = await createKeyringEncryptor(\n keyring,\n { kemPubHex: keys.kemPub, kemPrivHex: keys.kemPriv },\n { trustedAdders },\n );\n return enc as unknown as Encryptor;\n}\n\n/** A user's public profile: display pseudo + optional inline avatar + public identity keys. */\nexport interface PublicProfile {\n pseudo: string | null;\n avatar: string | null;\n edPub: string | null;\n kemPub: string | null;\n}\n\n/** Read any user's public profile. */\nexport async function readProfile(userId: string): Promise<PublicProfile> {\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (!r.ok) return { pseudo: null, avatar: null, edPub: null, kemPub: null };\n const body = await r.json();\n const data = body?.data as { pseudo?: unknown; avatar?: unknown; edPub?: unknown; kemPub?: unknown } | undefined;\n const profile: PublicProfile = {\n pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null,\n avatar: typeof data?.avatar === 'string' ? data.avatar : null,\n edPub: typeof data?.edPub === 'string' ? data.edPub : null,\n kemPub: typeof data?.kemPub === 'string' ? data.kemPub : null,\n };\n cacheProfile(userId, profile);\n return profile;\n } catch {\n return (await loadCachedProfile(userId)) ?? { pseudo: null, avatar: null, edPub: null, kemPub: null };\n }\n}\n\n/** Read any user's public profile pseudo. */\nexport async function readPseudo(userId: string): Promise<string | null> {\n return (await readProfile(userId)).pseudo;\n}\n\nlet profileBatchClient: StarfishClient | undefined;\nfunction getProfileBatchClient(): StarfishClient {\n if (!profileBatchClient) {\n profileBatchClient = new StarfishClient({ baseUrl: getSyncBase(), namespace: getSyncNamespace(), fetch: fetchWithTimeout() });\n }\n return profileBatchClient;\n}\n\nconst PROFILE_BATCH_CHUNK = 24;\n\n/**\n * Read MANY users' public profiles in one /batch/pull round-trip per chunk.\n */\nexport async function readProfiles(ids: string[]): Promise<Map<string, PublicProfile>> {\n const out = new Map<string, PublicProfile>();\n const client = getProfileBatchClient();\n for (let i = 0; i < ids.length; i += PROFILE_BATCH_CHUNK) {\n const chunk = ids.slice(i, i + PROFILE_BATCH_CHUNK);\n let entries: BatchPullEntry[];\n try {\n entries = await client.batchPullMany('profile', chunk.map((id) => ({ identity: id })));\n } catch {\n for (const id of chunk) {\n const cached = await loadCachedProfile(id);\n if (cached) out.set(id, cached);\n }\n continue;\n }\n chunk.forEach((id, j) => {\n const entry = entries[j];\n if (!entry || entry.error) return;\n const data = (entry.data ?? null) as { pseudo?: unknown; avatar?: unknown; edPub?: unknown; kemPub?: unknown } | null;\n const profile: PublicProfile = {\n pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null,\n avatar: typeof data?.avatar === 'string' ? data.avatar : null,\n edPub: typeof data?.edPub === 'string' ? data.edPub : null,\n kemPub: typeof data?.kemPub === 'string' ? data.kemPub : null,\n };\n cacheProfile(id, profile);\n out.set(id, profile);\n });\n }\n return out;\n}\n\n/**\n * Merge a patch into the caller's own profile doc.\n */\nexport async function writeProfile(\n client: StarfishClient,\n userId: string,\n patch: { pseudo?: string; avatar?: string | null; edPub?: string; kemPub?: string },\n): Promise<void> {\n const current = await client.pull(profilePull(userId)).catch(() => null);\n const base = (current?.data as Record<string, unknown> | undefined) ?? {};\n const next: Record<string, unknown> = { ...base, ...patch, v: 1 };\n if (next.avatar == null) delete next.avatar;\n await client.push(profilePush(userId), next, current?.hash ?? null);\n}\n\n/** Write the caller's own profile pseudo. */\nexport async function writePseudo(client: StarfishClient, userId: string, pseudo: string): Promise<void> {\n await writeProfile(client, userId, { pseudo });\n}\n\n/**\n * Publish this identity's PUBLIC keys in its profile so a peer can start an E2EE DM.\n * One-time + idempotent. ROOT-DEVICE ONLY — `profile` is `device:root`-write.\n */\nexport async function ensureProfileKeys(\n client: StarfishClient,\n userId: string,\n keys: { edPub: string; kemPub: string },\n): Promise<void> {\n let confirmedAbsent = false;\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (r.status === 404) confirmedAbsent = true;\n else if (r.ok) {\n const body = await r.json();\n const data = body?.data as { edPub?: unknown; kemPub?: unknown } | undefined;\n confirmedAbsent = !(typeof data?.edPub === 'string' && typeof data?.kemPub === 'string');\n } else return;\n } catch {\n return;\n }\n if (!confirmedAbsent) return;\n await writeProfile(client, userId, { edPub: keys.edPub, kemPub: keys.kemPub });\n}\n\n/**\n * Build cap-cert auth headers for a raw `fetch` outside the StarfishClient.\n */\nexport async function buildAuthHeaders(\n cap: unknown,\n devEdPrivHex: string,\n method: string,\n pathAndQuery: string,\n): Promise<Record<string, string>> {\n let host = '';\n try {\n host = new URL(getSyncBase()).host;\n } catch { /* relative base */ }\n\n const { sig, ts, nonce } = await signRequest(\n { method: method as SignableMethod, pathAndQuery, host },\n devEdPrivHex,\n );\n\n const capJson = stableStringify(cap as Record<string, unknown>);\n const capB64 =\n typeof btoa === 'function'\n ? btoa(capJson)\n : Buffer.from(capJson, 'utf-8').toString('base64');\n\n return {\n Authorization: `Cap ${capB64}`,\n 'X-Starfish-Sig': sig,\n 'X-Starfish-Ts': String(ts),\n 'X-Starfish-Nonce': nonce,\n };\n}\n\nasync function readOwnPseudo(userId: string): Promise<{ read: boolean; pseudo: string | null }> {\n try {\n const r = await fetchWithTimeout()(`${getSyncBase()}${getSyncPrefix()}${profilePull(userId)}`);\n if (r.status === 404) return { read: true, pseudo: null };\n if (!r.ok) return { read: false, pseudo: null };\n const body = await r.json();\n const data = body?.data as { pseudo?: unknown } | undefined;\n return { read: true, pseudo: typeof data?.pseudo === 'string' ? data.pseudo : null };\n } catch {\n return { read: false, pseudo: null };\n }\n}\n\n/**\n * Seed the caller's profile pseudo only if none exists yet, returning the\n * authoritative server value.\n */\nexport async function ensurePseudo(client: StarfishClient, userId: string, fallback: string): Promise<string> {\n const { read, pseudo } = await readOwnPseudo(userId);\n if (pseudo && pseudo.trim()) return pseudo;\n if (!read) return fallback;\n await writeProfile(client, userId, { pseudo: fallback });\n return fallback;\n}\n","/**\n * Identity & 12-word recovery seed. The seed is a BIP-39 mnemonic used as the\n * passphrase for Starfish's `bootstrapRootIdentity`; the same words deterministically\n * recover the identity.\n */\nimport { generateMnemonic, validateMnemonic } from '@scure/bip39';\nimport { wordlist } from '@scure/bip39/wordlists/english.js';\nimport { bootstrapRootIdentity, mintDeviceCap } from '@drakkar.software/starfish-identities';\nimport type { StarfishClient } from '@drakkar.software/starfish-client';\nimport type { CapCert } from '@drakkar.software/starfish-protocol';\n\nimport { makeClient, ensureProfileKeys, ensurePseudo, type DeviceKeys } from './client.js';\nimport { accountScope, ownerScope } from './paths.js';\nimport { getSharedSpacesNamespace } from '../core/config.js';\nimport type { DerivedIdentity } from '../core/storage-types.js';\n\nexport interface Session {\n userId: string;\n name: string;\n keys: DeviceKeys;\n chatCap: unknown;\n accountCap: unknown;\n /**\n * The primary Starfish client for space content (keyring, channels, objects).\n * Uses the app's default namespace.\n */\n chatClient: StarfishClient;\n /**\n * The Starfish client for account-scoped content (profile, _spaces registry).\n * Uses the app's default namespace.\n */\n accountClient: StarfishClient;\n /**\n * Starfish client for cross-app shared-spaces registry operations.\n * When `sharedSpacesNamespace` is configured, uses that namespace override so\n * the spaces list lives in a separate namespace shared across multiple apps.\n * Falls back to `accountClient` when no shared namespace is configured.\n */\n spacesRegistryClient: StarfishClient;\n /**\n * Starfish client for cross-app shared-spaces keyring operations.\n * Same namespace logic as `spacesRegistryClient`, scoped to space content.\n * Falls back to `chatClient` when no shared namespace is configured.\n */\n spacesKeyringClient: StarfishClient;\n fingerprint: string;\n /**\n * The Ed25519 pubkey that signs this identity's OWNED-space keyring entries —\n * the trusted-adder provenance anchor for opening them.\n */\n ownerEdPub: string;\n}\n\n/**\n * Trusted-adder allow-list for opening an OWNED space's keyring.\n */\nexport function ownerTrustedAdders(session: Session): string[] {\n return session.ownerEdPub === session.keys.edPub\n ? [session.keys.edPub]\n : [session.ownerEdPub, session.keys.edPub];\n}\n\n/** Fresh 12-word recovery seed. */\nexport function generateSeedWords(): string[] {\n return generateMnemonic(wordlist, 128).split(' ');\n}\n\nexport function isValidSeed(words: string[]): boolean {\n return validateMnemonic(words.join(' ').trim(), wordlist);\n}\n\n/** Human-readable fingerprint derived from the identity's user id. */\nexport function fingerprintFromUserId(userId: string): string {\n const h = userId.replace(/[^0-9a-f]/gi, '').toUpperCase();\n return [h.slice(0, 4), h.slice(4, 8), h.slice(8, 12)].filter(Boolean).join(' · ');\n}\n\n/**\n * Build a full owner session (caps + clients + pseudo) from an already-derived\n * root identity. No Argon2id — only fast Ed25519 cap-minting plus a profile fetch.\n */\nexport async function buildSession({ userId, keys }: DerivedIdentity, name?: string): Promise<Session> {\n const fallback = name && name.trim() ? name.trim() : `user-${userId.slice(0, 6)}`;\n const sub = { edPubHex: keys.edPub, kemPubHex: keys.kemPub };\n const chatCap = await mintDeviceCap(keys.edPriv, keys.edPub, sub, ownerScope());\n const accountCap = await mintDeviceCap(keys.edPriv, keys.edPub, sub, accountScope(userId));\n const chatClient = makeClient(chatCap, keys.edPriv);\n const accountClient = makeClient(accountCap, keys.edPriv);\n\n const sharedNs = getSharedSpacesNamespace();\n const spacesRegistryClient = sharedNs ? makeClient(accountCap, keys.edPriv, sharedNs) : accountClient;\n const spacesKeyringClient = sharedNs ? makeClient(chatCap, keys.edPriv, sharedNs) : chatClient;\n\n const displayName = await ensurePseudo(accountClient, userId, fallback).catch(() => fallback);\n void ensureProfileKeys(accountClient, userId, keys).catch(() => {});\n return {\n userId,\n name: displayName,\n keys,\n chatCap,\n accountCap,\n chatClient,\n accountClient,\n spacesRegistryClient,\n spacesKeyringClient,\n fingerprint: fingerprintFromUserId(userId),\n ownerEdPub: keys.edPub,\n };\n}\n\n/** A paired device's credentials: its own keypair + the root-signed cap-cert. */\nexport interface LinkedIdentity {\n userId: string;\n keys: DeviceKeys;\n capCert: CapCert;\n}\n\n/**\n * Build a session for a PAIRED (linked) device. Unlike {@link buildSession}, the\n * device keypair is NOT the root, so it cannot self-mint caps — both clients are\n * driven by the single root-signed `capCert` from the pairing bundle.\n */\nexport async function buildLinkedSession({ userId, keys, capCert }: LinkedIdentity, name?: string): Promise<Session> {\n const fallback = name && name.trim() ? name.trim() : `user-${userId.slice(0, 6)}`;\n const chatClient = makeClient(capCert, keys.edPriv);\n const accountClient = makeClient(capCert, keys.edPriv);\n\n const sharedNs = getSharedSpacesNamespace();\n const spacesRegistryClient = sharedNs ? makeClient(capCert, keys.edPriv, sharedNs) : accountClient;\n const spacesKeyringClient = sharedNs ? makeClient(capCert, keys.edPriv, sharedNs) : chatClient;\n\n const displayName = await ensurePseudo(accountClient, userId, fallback).catch(() => fallback);\n return {\n userId,\n name: displayName,\n keys,\n chatCap: capCert,\n accountCap: capCert,\n chatClient,\n accountClient,\n spacesRegistryClient,\n spacesKeyringClient,\n fingerprint: fingerprintFromUserId(userId),\n ownerEdPub: capCert.iss,\n };\n}\n\n/** Derive a full owner session (identity + caps + clients) from a seed. */\nexport async function deriveSession(seedWords: string[], name?: string): Promise<Session> {\n const passphrase = seedWords.join(' ').trim();\n const creds = await bootstrapRootIdentity(passphrase);\n return buildSession({ userId: creds.userId, keys: creds.device as DeviceKeys }, name);\n}\n\n/** The cached root identity (userId + keys) carried by a built session. */\nexport function rootIdentityOf(s: Session): DerivedIdentity {\n return { userId: s.userId, keys: s.keys };\n}\n","/**\n * Unified local access store for spaces this identity has joined.\n *\n * Replaces the separate `member-caps.ts` (private spaces) and `pubspace-caps.ts`\n * (public/link spaces). Two entry kinds:\n * - `member`: a member cap-cert (plain JSON, no bearer secret — safe to store\n * in the clear). Used for PRIVATE space keyring opens.\n * - `link`: an ephemeral-subject cap + the link's Ed25519 private key. Embeds a\n * bearer secret so it is SEALED in the synced `_spaces.pubAccess` field before\n * leaving this device; the local kv stores it plaintext only on the owning device.\n *\n * Two tiers (same as old member-caps): device-local kv (fast, offline) and the\n * user's synced `_spaces` doc (durable source of truth; merged over local on hydrate).\n * Keyed PER-USER so multiple accounts on one device never see each other's entries.\n */\nimport type { CapMap, PubAccessMap } from '../core/types.js';\nimport type { SealedBlob } from './account-seal.js';\nimport { kvGet, kvSet } from '../core/adapters.js';\n\nexport type SpaceAccessEntry =\n | { kind: 'member'; cap: string }\n | { kind: 'link'; cap: unknown; key: string; write: boolean };\n\nexport type SpaceAccessMap = Record<string, SpaceAccessEntry>;\n\nconst keyFor = (userId: string) => `octospaces.spaceaccess.${userId}`;\n\nlet cache: SpaceAccessMap = {};\nlet activeKey: string | null = null;\n\n/**\n * Load the active account's space-access entries into memory. Call (and await) on\n * sign-in and on every account switch, before opening rooms.\n *\n * `serverCaps` (private member caps from `_spaces.caps`) and `serverPubAccess`\n * (sealed link credentials from `_spaces.pubAccess`, already unsealed by the caller)\n * are merged OVER the local kv cache (server wins).\n */\nexport async function hydrateSpaceAccessStore(\n userId: string,\n serverCaps: CapMap,\n serverLinkAccess: Record<string, { cap: unknown; key: string; write: boolean }>,\n): Promise<void> {\n const key = keyFor(userId);\n if (activeKey === key) return;\n activeKey = key;\n cache = {};\n const raw = await kvGet(key);\n if (raw) {\n try {\n cache = JSON.parse(raw) as SpaceAccessMap;\n } catch (e) {\n console.error('[octospaces] space-access-store: corrupt cache, resetting:', e);\n cache = {};\n }\n }\n let changed = false;\n for (const [spaceId, capJson] of Object.entries(serverCaps)) {\n cache[spaceId] = { kind: 'member', cap: capJson };\n changed = true;\n }\n for (const [spaceId, access] of Object.entries(serverLinkAccess)) {\n cache[spaceId] = { kind: 'link', cap: access.cap, key: access.key, write: access.write };\n changed = true;\n }\n if (changed) await kvSet(key, JSON.stringify(cache));\n}\n\nfunction persist(): void {\n if (activeKey) void kvSet(activeKey, JSON.stringify(cache));\n}\n\nexport function getSpaceAccessEntry(spaceId: string): SpaceAccessEntry | null {\n return cache[spaceId] ?? null;\n}\n\nexport function saveSpaceAccessEntry(spaceId: string, entry: SpaceAccessEntry): void {\n cache = { ...cache, [spaceId]: entry };\n persist();\n}\n\n/** Forget one space's access (on leaving that space). */\nexport function removeSpaceAccessEntry(spaceId: string): void {\n if (!(spaceId in cache)) return;\n const next = { ...cache };\n delete next[spaceId];\n cache = next;\n persist();\n}\n\n// ── Per-node access entries (keyed by `${spaceId}:${nodeId}`) ────────────────\n\n/** Look up a per-node invite access entry. Returns null if not invited or unknown. */\nexport function getNodeAccessEntry(spaceId: string, nodeId: string): SpaceAccessEntry | null {\n return cache[`${spaceId}:${nodeId}`] ?? null;\n}\n\n/** Persist an invite access entry for one node. */\nexport function saveNodeAccessEntry(spaceId: string, nodeId: string, entry: SpaceAccessEntry): void {\n saveSpaceAccessEntry(`${spaceId}:${nodeId}`, entry);\n}\n\n/** Forget a node's invite access entry (e.g. on leaving the node). */\nexport function removeNodeAccessEntry(spaceId: string, nodeId: string): void {\n removeSpaceAccessEntry(`${spaceId}:${nodeId}`);\n}\n\n/** A snapshot of the in-memory cache — used by `recoverSpaceAccess` to find entries\n * not yet on the server. */\nexport function localSpaceAccessEntries(): SpaceAccessMap {\n return cache;\n}\n\n/** Build the `CapMap` slice (member entries only) for persisting into `_spaces.caps`. */\nexport function memberCapsFromStore(): CapMap {\n const out: CapMap = {};\n for (const [id, e] of Object.entries(cache)) if (e.kind === 'member') out[id] = e.cap;\n return out;\n}\n\n/** Build the `PubAccessMap` slice (link entries already sealed by the caller). */\nexport function linkAccessFromStore(): Record<string, { cap: unknown; key: string; write: boolean }> {\n const out: Record<string, { cap: unknown; key: string; write: boolean }> = {};\n for (const [id, e] of Object.entries(cache)) {\n if (e.kind === 'link') out[id] = { cap: e.cap, key: e.key, write: e.write };\n }\n return out;\n}\n\n/** Drop the in-memory cache (on account switch / sign-out). */\nexport function clearSpaceAccessStore(): void {\n cache = {};\n activeKey = null;\n}\n","/**\n * Space and node access resolver.\n *\n * Encryption is per-node (each node has an `enc` flag) but keyed under ONE\n * space-wide keyring at `spaces/{spaceId}/_keyring`. All `enc` nodes in a space\n * share the same CEK; `access` gates *fetching*, the keyring gates *decryption*.\n *\n * Two entry points:\n * - `getSpaceClient` — returns the right StarfishClient for member-gated\n * space docs (index, _access). No encryptor.\n * - `getNodeAccess` — resolves the (client, encryptor) for a specific node's\n * CONTENT. Encryptor is null for plaintext nodes; for enc nodes the encryptor\n * opens the SPACE keyring (not a per-node keyring).\n *\n * Resolution order for `getNodeAccess`:\n * 1. Per-node link entry → sign as ephemeral identity; encryptor from space keyring.\n * 2. Per-node member entry → open space keyring as recipient.\n * 3. Space-level link entry → same client; open space keyring if enc.\n * 4. Space-level member entry → open space keyring if enc.\n * 5. No entry, owner → mint space keyring if enc; plain client otherwise.\n * 6. No entry, non-owner → SpaceAccessError if enc; plain client otherwise.\n */\nimport type { Encryptor, StarfishClient } from '@drakkar.software/starfish-client';\n\nimport { buildEncryptor, makeClient, openEncryptor, ownerEnsureKeyring } from './client.js';\nimport type { Session } from './identity.js';\nimport { ownerTrustedAdders } from './identity.js';\nimport { getNodeAccessEntry, getSpaceAccessEntry } from './space-access-store.js';\nimport { SpaceAccessError } from '../core/space-access-error.js';\nimport { keyringPull, keyringPush } from './paths.js';\nimport type { NodeAccess } from '../core/types.js';\n\n// Re-export so existing importers keep reaching SpaceAccessError through this module.\nexport { SpaceAccessError };\n\nexport interface NodeAccessHandle {\n encryptor: Encryptor | null;\n client: StarfishClient;\n /** True when opened as the space OWNER (may seed / mint the space keyring). */\n isOwnerOpen: boolean;\n}\n\nconst cache = new Map<string, Promise<NodeAccessHandle>>();\n\n/** Drop every cached handle (on account switch — keys are per-identity). */\nexport function clearNodeAccessCache(): void {\n cache.clear();\n}\n\n/**\n * Return the right StarfishClient for reading/writing member-gated space docs\n * (e.g. the `_index`, `_access`). Spaces are always plaintext — no encryptor.\n */\nexport function getSpaceClient(spaceId: string, session: Session): StarfishClient {\n const entry = getSpaceAccessEntry(spaceId);\n if (entry?.kind === 'link') return makeClient(entry.cap, entry.key);\n if (entry?.kind === 'member') {\n const cap = JSON.parse(entry.cap) as { iss?: string };\n return makeClient(cap, session.keys.edPriv);\n }\n return session.chatClient;\n}\n\n/**\n * Resolve the right (client, encryptor) for a node's CONTENT, opening and\n * caching on first use.\n *\n * `node` carries `{ access?, enc? }` — the plaintext flags from the index.\n * `reg` is the space's access record if already known; used to determine\n * ownership. Pass null if unknown.\n */\nexport function getNodeAccess(\n spaceId: string,\n nodeId: string,\n node: { access?: NodeAccess; enc?: boolean },\n session: Session,\n reg?: { owner: string | null; members: string[] } | null,\n): Promise<NodeAccessHandle> {\n const cacheKey = `${spaceId}:${nodeId}`;\n const hit = cache.get(cacheKey);\n if (hit) return hit;\n\n const p = (async (): Promise<NodeAccessHandle> => {\n // Prefer a per-node entry, fall back to the space-level entry for the client.\n const nodeEntry = getNodeAccessEntry(spaceId, nodeId);\n const spaceEntry = getSpaceAccessEntry(spaceId);\n const activeEntry = nodeEntry ?? spaceEntry;\n\n // Build the client.\n let client: StarfishClient;\n let capIss: string | undefined;\n if (activeEntry?.kind === 'link') {\n client = makeClient(activeEntry.cap, activeEntry.key);\n } else if (activeEntry?.kind === 'member') {\n const cap = JSON.parse(activeEntry.cap) as { iss?: string };\n capIss = cap.iss;\n client = makeClient(cap, session.keys.edPriv);\n } else {\n client = session.chatClient;\n }\n\n const isOwnerOpen =\n reg != null ? reg.owner === session.userId : activeEntry == null;\n\n // Plaintext node — no keyring needed.\n if (!node.enc) {\n return { encryptor: null, client, isOwnerOpen };\n }\n\n // E2EE node — resolve the SPACE-WIDE keyring.\n const spacePullPath = keyringPull(spaceId);\n const trustedAdders = capIss\n ? [capIss]\n : reg?.owner\n ? [reg.owner]\n : ownerTrustedAdders(session);\n\n if (activeEntry?.kind === 'member' || activeEntry?.kind === 'link') {\n const encryptor = await openEncryptor(client, session.keys, spacePullPath, trustedAdders);\n return { encryptor, client, isOwnerOpen: false };\n }\n\n // No access entry — owner mints/opens the keyring; non-owner errors.\n const owner = reg?.owner ?? null;\n const members = reg?.members ?? [];\n if (owner !== null && owner !== session.userId) {\n throw new SpaceAccessError(\n members.includes(session.userId)\n ? \"You're a member of this space, but the space key isn't on this device yet — ask the owner to invite you.\"\n : \"You don't have access to this node.\",\n );\n }\n const encryptor = await ownerEnsureKeyring(\n session.chatClient,\n session.keys,\n spacePullPath,\n keyringPush(spaceId),\n ownerTrustedAdders(session),\n );\n return { encryptor, client: session.chatClient, isOwnerOpen: true };\n })();\n\n cache.set(cacheKey, p);\n p.catch(() => cache.delete(cacheKey));\n return p;\n}\n\n/**\n * SOFT resolve — never mints a keyring, never throws on missing access.\n * Returns null when the identity has no usable access for the node yet.\n */\nexport async function buildNodeAccess(\n session: Session,\n spaceId: string,\n nodeId: string,\n node: { enc?: boolean },\n): Promise<{ client: StarfishClient; encryptor: Encryptor | null } | null> {\n const nodeEntry = getNodeAccessEntry(spaceId, nodeId);\n const spaceEntry = getSpaceAccessEntry(spaceId);\n const activeEntry = nodeEntry ?? spaceEntry;\n\n let client: StarfishClient;\n let trustedAdders = ownerTrustedAdders(session);\n\n if (activeEntry?.kind === 'link') {\n client = makeClient(activeEntry.cap, activeEntry.key);\n } else if (activeEntry?.kind === 'member') {\n const cap = JSON.parse(activeEntry.cap) as { iss?: string };\n client = makeClient(cap, session.keys.edPriv);\n if (cap.iss) trustedAdders = [cap.iss];\n } else {\n client = session.chatClient;\n }\n\n if (!node.enc) return { client, encryptor: null };\n\n // Soft-open the SPACE-WIDE keyring.\n const spacePullPath = keyringPull(spaceId);\n const encryptor = await buildEncryptor(client, session.keys, spacePullPath, trustedAdders);\n return encryptor ? { client, encryptor } : null;\n}\n","/**\n * Headless reads + create-time seeding of a space's unified OBJECT INDEX.\n *\n * The index at `spaces/{spaceId}/objects/_index` is always PLAINTEXT (member-gated).\n * For `invite` nodes the title/emoji are stripped before storage so non-invited\n * members see only the structural fields (id, type, parentId, order, access, enc).\n * Invited members read the real title from the node's content doc.\n *\n * Encryption lives at the node content level, not here.\n */\nimport { ConflictError } from '@drakkar.software/starfish-client';\n\nimport type { ObjectNode } from '../core/types.js';\nimport type { Session } from '../sync/identity.js';\nimport { objIndexPull, objIndexPush } from '../sync/paths.js';\nimport { getSpaceClient } from '../sync/space-access.js';\n\n/** Strip title/emoji from invite nodes before writing to the index. */\nfunction serializeForIndex(node: ObjectNode): ObjectNode {\n if (node.access === 'invite') {\n const { emoji: _e, ...rest } = node;\n return { ...rest, title: '' };\n }\n return node;\n}\n\n/**\n * Write the create-time seed into a space's index doc.\n * Idempotent: a no-op if the index doc already exists.\n * Pass `nodes` to seed with initial objects; defaults to an empty index.\n */\nexport async function pushIndexSeed(\n client: import('@drakkar.software/starfish-client').StarfishClient,\n spaceId: string,\n nodes: ObjectNode[] = [],\n): Promise<void> {\n const res = await client.pull(objIndexPull(spaceId)).catch(() => null);\n const existing = res?.data as Record<string, unknown> | undefined;\n if (Array.isArray(existing?.objects)) return;\n await client.push(\n objIndexPush(spaceId),\n { v: 2, objects: nodes.map(serializeForIndex), updatedAt: Date.now() },\n res?.hash ?? null,\n );\n}\n\n/**\n * Seed a brand-new space's index as the OWNER. Always plaintext.\n * Pass `nodes` to seed with initial objects; defaults to an empty index.\n */\nexport async function seedSpaceObjectIndex(\n session: Session,\n spaceId: string,\n nodes: ObjectNode[] = [],\n): Promise<void> {\n const client = getSpaceClient(spaceId, session);\n await pushIndexSeed(client, spaceId, nodes);\n}\n\n/**\n * Headless read-modify-write of a space's unified OBJECT INDEX.\n * Always plaintext. Retries up to 3 times on ConflictError.\n *\n * The mutator receives the current nodes with real (or empty, for invite) titles.\n * Before writing back, invite nodes have their title/emoji stripped again.\n */\nexport async function updateObjectIndex(\n session: Session,\n spaceId: string,\n mutator: (nodes: ObjectNode[], now: number) => ObjectNode[] | null,\n reg?: { owner: string | null; members: string[] } | null,\n): Promise<void> {\n void reg; // no longer needed for encryption; kept for API compat during migration\n const client = getSpaceClient(spaceId, session);\n const pullPath = objIndexPull(spaceId);\n const pushPath = objIndexPush(spaceId);\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const res = await client.pull(pullPath).catch(() => null);\n const raw = res?.data as Record<string, unknown> | undefined;\n const cur: ObjectNode[] = Array.isArray((raw as { objects?: unknown })?.objects)\n ? (raw as { objects: ObjectNode[] }).objects\n : [];\n const next = mutator(cur, Date.now());\n if (!next) return;\n try {\n await client.push(\n pushPath,\n { v: 2, objects: next.map(serializeForIndex), updatedAt: Date.now() },\n res?.hash ?? null,\n );\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\n/**\n * Read the current object tree (read-only, no mutation). Returns the stored\n * nodes (titles are empty for invite nodes the caller is not invited to).\n */\nexport async function readObjectTree(\n session: Session,\n spaceId: string,\n): Promise<ObjectNode[]> {\n const client = getSpaceClient(spaceId, session);\n const res = await client.pull(objIndexPull(spaceId)).catch(() => null);\n const raw = res?.data as Record<string, unknown> | undefined;\n return Array.isArray((raw as { objects?: unknown })?.objects)\n ? (raw as { objects: ObjectNode[] }).objects\n : [];\n}\n","/**\n * Space registries (plaintext metadata docs). A user's spaces live at\n * `user/<userId>/_spaces`; each space's ACCESS RECORD (owner/members + shared\n * name/image) at `spaces/<spaceId>/_access`. The object tree lives in the plaintext\n * unified object index (`objects/_index`, see `object-index.ts`); `_access` is the\n * owner-only access record. Spaces are neutral containers — visibility and encryption\n * are per-node properties (see `ObjectNode.access` / `ObjectNode.enc`).\n */\nimport { ConflictError, StarfishHttpError } from '@drakkar.software/starfish-client';\nimport type { StarfishClient } from '@drakkar.software/starfish-client';\n\nimport type { ArchivedDms, CapMap, DmMap, MutePrefs, PubAccessMap, ReadPrefs, Space } from '../core/types.js';\nimport type { SealedBlob } from '../sync/account-seal.js';\nimport { randomId } from '../core/ids.js';\nimport type { Session } from '../sync/identity.js';\nimport { seedSpaceObjectIndex } from './object-index.js';\nimport { spaceAccessPull, spaceAccessPush, spacesPull, spacesPush } from '../sync/paths.js';\n\n/** Owner-set, SHARED space identity, persisted in the `_access` registry doc\n * (plaintext — NOT E2EE). `image` is a data URI. All fields optional for back-compat. */\nexport interface SpaceMeta {\n name?: string | null;\n image?: string | null;\n}\n\n/** A resolved name/image update fanned out so the SpacesProvider adopts a\n * freshly-reconciled value without waiting for its next navigation refresh. */\nexport interface SpaceMetaUpdate {\n name: string;\n short: string;\n image?: string;\n}\n\nconst spaceMetaListeners = new Set<(spaceId: string, meta: SpaceMetaUpdate) => void>();\n\nexport function onSpaceMeta(fn: (spaceId: string, meta: SpaceMetaUpdate) => void): () => void {\n spaceMetaListeners.add(fn);\n return () => { spaceMetaListeners.delete(fn); };\n}\n\nexport function broadcastSpaceMeta(spaceId: string, meta: SpaceMetaUpdate): void {\n for (const fn of spaceMetaListeners) fn(spaceId, meta);\n}\n\ninterface SpacesDoc {\n spaces: Space[];\n caps: CapMap;\n mutes: MutePrefs;\n reads: ReadPrefs;\n pubAccess: PubAccessMap;\n dms: DmMap;\n quickReactions: string[];\n archivedDms: ArchivedDms;\n hash: string | null;\n}\n\nfunction coerceDms(raw: unknown): DmMap {\n const src = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const out: DmMap = {};\n for (const [k, v] of Object.entries(src)) if (typeof v === 'string') out[k] = v;\n return out;\n}\n\nfunction coerceMutes(raw: unknown): MutePrefs {\n const r = (raw && typeof raw === 'object' ? raw : {}) as { rooms?: unknown; spaces?: unknown };\n const pick = (v: unknown): Record<string, true | number> =>\n v && typeof v === 'object' ? (v as Record<string, true | number>) : {};\n return { rooms: pick(r.rooms), spaces: pick(r.spaces) };\n}\n\nfunction coerceReads(raw: unknown): ReadPrefs {\n const r = (raw && typeof raw === 'object' ? raw : {}) as { rooms?: unknown };\n const src = r.rooms && typeof r.rooms === 'object' ? (r.rooms as Record<string, unknown>) : {};\n const rooms: Record<string, number> = {};\n for (const [id, v] of Object.entries(src)) if (typeof v === 'number' && Number.isFinite(v)) rooms[id] = v;\n return { rooms };\n}\n\nfunction coerceQuickReactions(raw: unknown): string[] {\n return Array.isArray(raw) ? raw.filter((v): v is string => typeof v === 'string') : [];\n}\n\nfunction coerceArchivedDms(raw: unknown): ArchivedDms {\n const src = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const out: ArchivedDms = {};\n for (const [k, v] of Object.entries(src)) if (v === true) out[k] = true;\n return out;\n}\n\nasync function pullSpacesDoc(client: StarfishClient, userId: string): Promise<SpacesDoc> {\n const res = await client.pull(spacesPull(userId)).catch((err: unknown) => {\n if (err instanceof StarfishHttpError && err.status === 404) return null;\n throw err;\n });\n const data = res?.data as\n | {\n spaces?: Space[];\n caps?: CapMap;\n mutes?: unknown;\n reads?: unknown;\n pubAccess?: PubAccessMap;\n dms?: unknown;\n quickReactions?: unknown;\n archivedDms?: unknown;\n }\n | undefined;\n return {\n spaces: Array.isArray(data?.spaces) ? data!.spaces! : [],\n caps: data?.caps && typeof data.caps === 'object' ? data.caps : {},\n mutes: coerceMutes(data?.mutes),\n reads: coerceReads(data?.reads),\n pubAccess: data?.pubAccess && typeof data.pubAccess === 'object' ? data.pubAccess : {},\n dms: coerceDms(data?.dms),\n quickReactions: coerceQuickReactions(data?.quickReactions),\n archivedDms: coerceArchivedDms(data?.archivedDms),\n hash: res?.hash ?? null,\n };\n}\n\nexport async function readSpaces(client: StarfishClient, userId: string): Promise<SpacesDoc> {\n try {\n return await pullSpacesDoc(client, userId);\n } catch (err) {\n console.error('[readSpaces] failed to pull spaces registry', err);\n return {\n spaces: [],\n caps: {},\n mutes: coerceMutes(undefined),\n reads: coerceReads(undefined),\n pubAccess: {},\n dms: {},\n quickReactions: [],\n archivedDms: {},\n hash: null,\n };\n }\n}\n\nexport async function updateSpacesDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: { spaces: Space[]; caps: CapMap; pubAccess: PubAccessMap }) => { spaces: Space[]; caps: CapMap; pubAccess: PubAccessMap },\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const cur = { spaces, caps, pubAccess };\n const next = mutator(cur);\n if (next === cur) return;\n try {\n await client.push(\n spacesPush(userId),\n { v: 1, spaces: next.spaces, caps: next.caps, mutes, reads, pubAccess: next.pubAccess, dms, quickReactions, archivedDms },\n hash,\n );\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateMutesDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: MutePrefs) => MutePrefs | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(mutes);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes: next, reads, pubAccess, dms, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateReadsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: ReadPrefs) => ReadPrefs | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(reads);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads: next, pubAccess, dms, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateDmsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: DmMap) => DmMap | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(dms);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms: next, quickReactions, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateQuickReactionsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: string[]) => string[] | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(quickReactions);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms, quickReactions: next, archivedDms }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function updateArchivedDmsDoc(\n client: StarfishClient,\n userId: string,\n mutator: (cur: ArchivedDms) => ArchivedDms | null,\n): Promise<void> {\n const MAX_ATTEMPTS = 3;\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n const { spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms, hash } = await pullSpacesDoc(client, userId);\n const next = mutator(archivedDms);\n if (!next) return;\n try {\n await client.push(spacesPush(userId), { v: 1, spaces, caps, mutes, reads, pubAccess, dms, quickReactions, archivedDms: next }, hash);\n return;\n } catch (err) {\n if (err instanceof ConflictError && attempt < MAX_ATTEMPTS - 1) continue;\n throw err;\n }\n }\n}\n\nexport async function setDmMapping(\n client: StarfishClient,\n userId: string,\n peerUserId: string,\n spaceId: string,\n): Promise<void> {\n await updateDmsDoc(client, userId, (cur) => (cur[peerUserId] === spaceId ? null : { ...cur, [peerUserId]: spaceId }));\n}\n\nexport async function writeSpaces(\n client: StarfishClient,\n userId: string,\n spaces: Space[],\n _hash: string | null,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({ spaces, caps: cur.caps, pubAccess: cur.pubAccess }));\n}\n\nexport async function reorderSpaces(client: StarfishClient, userId: string, order: string[]): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => {\n const byId = new Map(cur.spaces.map((s) => [s.id, s]));\n const next: Space[] = [];\n for (const id of order) {\n const s = byId.get(id);\n if (s) { next.push(s); byId.delete(id); }\n }\n for (const s of cur.spaces) if (byId.has(s.id)) next.push(s);\n const unchanged = next.length === cur.spaces.length && next.every((s, i) => s === cur.spaces[i]);\n if (unchanged) return cur;\n return { spaces: next, caps: cur.caps, pubAccess: cur.pubAccess };\n });\n}\n\nfunction newSpaceId(): string {\n return `sp-${randomId()}`;\n}\n\nexport async function readSpaceAccess(\n client: StarfishClient,\n spaceId: string,\n): Promise<{ owner: string | null; members: string[]; name: string | null; image: string | null; hash: string | null }> {\n const res = await client.pull(spaceAccessPull(spaceId)).catch((err: unknown) => {\n if (err instanceof StarfishHttpError && err.status === 404) return null;\n throw err;\n });\n const data = res?.data as { owner?: string; members?: unknown[]; name?: string; image?: string } | undefined;\n return {\n owner: typeof data?.owner === 'string' ? data.owner : null,\n members: Array.isArray(data?.members) ? data!.members!.filter((m): m is string => typeof m === 'string') : [],\n name: typeof data?.name === 'string' ? data.name : null,\n image: typeof data?.image === 'string' ? data.image : null,\n hash: res?.hash ?? null,\n };\n}\n\nexport async function writeSpaceAccess(\n client: StarfishClient,\n spaceId: string,\n owner: string,\n members: string[],\n hash: string | null,\n meta?: SpaceMeta,\n): Promise<void> {\n const name = meta?.name?.trim() || undefined;\n const image = meta?.image || undefined;\n await client.push(\n spaceAccessPush(spaceId),\n {\n v: 1, owner, members,\n ...(name ? { name } : {}),\n ...(image ? { image } : {}),\n },\n hash,\n );\n}\n\nexport async function addSpaceMember(\n client: StarfishClient,\n spaceId: string,\n ownerUserId: string,\n memberUserId: string,\n): Promise<void> {\n const { owner, members, name, image, hash } = await readSpaceAccess(client, spaceId);\n if (memberUserId === (owner ?? ownerUserId) || members.includes(memberUserId)) return;\n await writeSpaceAccess(client, spaceId, owner ?? ownerUserId, [...members, memberUserId], hash, { name, image });\n}\n\n/** Remove a member from the space roster (used for link revocation). */\nexport async function removeSpaceMember(\n client: StarfishClient,\n spaceId: string,\n memberUserId: string,\n): Promise<void> {\n const { owner, members, name, image, hash } = await readSpaceAccess(client, spaceId);\n if (!members.includes(memberUserId)) return;\n await writeSpaceAccess(client, spaceId, owner ?? memberUserId, members.filter((m) => m !== memberUserId), hash, { name, image });\n}\n\nexport async function addJoinedSpace(client: StarfishClient, userId: string, space: Space): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) =>\n cur.spaces.some((s) => s.id === space.id)\n ? cur\n : { spaces: [...cur.spaces, space], caps: cur.caps, pubAccess: cur.pubAccess },\n );\n}\n\nexport async function addJoinedSpaceWithCap(\n client: StarfishClient,\n userId: string,\n space: Space,\n capJson: string,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({\n spaces: cur.spaces.some((s) => s.id === space.id) ? cur.spaces : [...cur.spaces, space],\n caps: { ...cur.caps, [space.id]: capJson },\n pubAccess: cur.pubAccess,\n }));\n}\n\nexport async function addJoinedSpaceWithLinkAccess(\n client: StarfishClient,\n userId: string,\n space: Space,\n sealed: SealedBlob,\n): Promise<void> {\n await updateSpacesDoc(client, userId, (cur) => ({\n spaces: cur.spaces.some((s) => s.id === space.id) ? cur.spaces : [...cur.spaces, space],\n caps: cur.caps,\n pubAccess: { ...cur.pubAccess, [space.id]: sealed },\n }));\n}\n\n/**\n * Create a new space owned by the identity. Seeds an empty plaintext object index.\n * Apps populate the index with their own object types after creation using `createNode`.\n */\nexport async function createSpace(\n session: Session,\n name: string,\n): Promise<Space> {\n const { accountClient, userId } = session;\n const { spaces, hash } = await readSpaces(accountClient, userId);\n const trimmed = name.trim() || 'New Space';\n const id = newSpaceId();\n const space: Space = {\n id,\n name: trimmed,\n short: trimmed.slice(0, 2).toUpperCase(),\n members: 1,\n };\n await writeSpaceAccess(accountClient, id, userId, [], null, { name: trimmed });\n await seedSpaceObjectIndex(session, id);\n await writeSpaces(accountClient, userId, [...spaces, space], hash);\n return space;\n}\n\nexport async function reconcileSpaceMeta(\n client: StarfishClient,\n userId: string,\n spaceId: string,\n shared: SpaceMeta,\n knownSpaces?: Space[],\n): Promise<void> {\n const sharedName = typeof shared.name === 'string' && shared.name.trim() ? shared.name : null;\n const sharedImage = typeof shared.image === 'string' && shared.image ? shared.image : null;\n if (sharedName === null && sharedImage === null) return;\n const known = knownSpaces?.find((s) => s.id === spaceId);\n if (known) {\n const name = sharedName ?? known.name;\n const short = name.slice(0, 2).toUpperCase();\n const image = sharedImage ?? known.image;\n if (name === known.name && short === known.short && (image ?? null) === (known.image ?? null)) return;\n }\n const { spaces, hash } = await readSpaces(client, userId);\n const cur = spaces.find((s) => s.id === spaceId);\n if (!cur) return;\n const name = sharedName ?? cur.name;\n const image = sharedImage ?? cur.image;\n const short = name.slice(0, 2).toUpperCase();\n if (name === cur.name && short === cur.short && (image ?? null) === (cur.image ?? null)) return;\n const next = spaces.map((s) => (s.id === spaceId ? { ...s, name, short, image } : s));\n await writeSpaces(client, userId, next, hash);\n broadcastSpaceMeta(spaceId, { name, short, image });\n}\n","/** @drakkar.software/octospaces-sdk — public surface */\n\n// Configuration\nexport { configureOctoSpaces, getSyncBase, getSyncNamespace, getSyncPrefix, getSharedSpacesNamespace } from './core/config.js';\nexport type { OctoSpacesConfig } from './core/config.js';\n\n// KV adapter\nexport { configureKv, kvGet, kvSet, kvRemove } from './core/adapters.js';\nexport type { KvAdapter } from './core/adapters.js';\n\n// Domain types\nexport type {\n ID,\n NodeAccess,\n ObjectNode,\n ObjectType,\n ObjectsIndex,\n ObjectContentKind,\n Space,\n CapMap,\n PubAccessMap,\n DmMap,\n MuteValue,\n MutePrefs,\n ReadValue,\n ReadPrefs,\n ArchivedDms,\n PresenceStatus,\n VerificationLevel,\n} from './core/types.js';\n\n// Ids\nexport { randomId, roomSlug } from './core/ids.js';\n\n// Paths / cap scopes\nexport {\n OBJECT_COLLECTIONS,\n ownerScope,\n spaceMemberScope,\n nodeMemberScope,\n accountScope,\n linkedDeviceScope,\n keyringName,\n keyringPull,\n keyringPush,\n objIndexName,\n objIndexPull,\n objIndexPush,\n objPubName,\n objPubPull,\n objPubPush,\n objInvName,\n objInvPull,\n objInvPush,\n objectDirName,\n objectDirPull,\n spacesPull,\n spacesPush,\n spaceAccessPull,\n spaceAccessPush,\n profilePull,\n profilePush,\n objLogName,\n objLogPull,\n objLogPush,\n objDocName,\n objDocPull,\n objDocPush,\n objectBlobName,\n objectBlobPull,\n objectBlobPush,\n typesIndexName,\n typesIndexPull,\n typesIndexPush,\n attachmentName,\n attachmentPull,\n attachmentPush,\n spaceIdFromRoomId,\n userIdFromEdPub,\n bytesToHex,\n} from './sync/paths.js';\n\n// Client\nexport {\n makeClient,\n capProviderFor,\n openEncryptor,\n buildEncryptor,\n ownerEnsureKeyring,\n readProfile,\n readPseudo,\n readProfiles,\n writeProfile,\n writePseudo,\n ensureProfileKeys,\n buildAuthHeaders,\n ensurePseudo,\n} from './sync/client.js';\nexport type { DeviceKeys, PublicProfile } from './sync/client.js';\n\n// Identity / session\nexport {\n buildSession,\n buildLinkedSession,\n deriveSession,\n rootIdentityOf,\n ownerTrustedAdders,\n generateSeedWords,\n isValidSeed,\n fingerprintFromUserId,\n} from './sync/identity.js';\nexport type { Session, LinkedIdentity } from './sync/identity.js';\n\n// Storage types\nexport type {\n DerivedIdentity,\n PersistedSession,\n Vault,\n VaultLoad,\n UnlockMethod,\n PasskeyEnrollment,\n SeedLock,\n} from './core/storage-types.js';\n\n// Sealed blobs\nexport { sealToSelf, unsealFromSelf, sealToRecipient, unsealFromRecipient } from './sync/account-seal.js';\nexport type { SealedBlob } from './sync/account-seal.js';\n\n// Node access (per-node encryptor + client resolver, replaces per-space access)\nexport {\n SpaceAccessError,\n getSpaceClient,\n getNodeAccess,\n buildNodeAccess,\n clearNodeAccessCache,\n} from './sync/space-access.js';\nexport type { NodeAccessHandle } from './sync/space-access.js';\n\n// Space access store (replaces member-caps + pubspace-caps)\nexport {\n hydrateSpaceAccessStore,\n getSpaceAccessEntry,\n saveSpaceAccessEntry,\n removeSpaceAccessEntry,\n getNodeAccessEntry,\n saveNodeAccessEntry,\n removeNodeAccessEntry,\n localSpaceAccessEntries,\n memberCapsFromStore,\n linkAccessFromStore,\n clearSpaceAccessStore,\n} from './sync/space-access-store.js';\nexport type { SpaceAccessEntry, SpaceAccessMap } from './sync/space-access-store.js';\n\n// Registry\nexport {\n readSpaces,\n updateSpacesDoc,\n updateMutesDoc,\n updateReadsDoc,\n updateDmsDoc,\n updateQuickReactionsDoc,\n updateArchivedDmsDoc,\n setDmMapping,\n writeSpaces,\n reorderSpaces,\n readSpaceAccess,\n writeSpaceAccess,\n addSpaceMember,\n removeSpaceMember,\n addJoinedSpace,\n addJoinedSpaceWithCap,\n addJoinedSpaceWithLinkAccess,\n createSpace,\n reconcileSpaceMeta,\n onSpaceMeta,\n broadcastSpaceMeta,\n} from './spaces/registry.js';\nexport type { SpaceMeta, SpaceMetaUpdate } from './spaces/registry.js';\n\n// Members\nexport {\n makeJoinRequest,\n inviteToSpace,\n acceptSpaceInvite,\n encodeSpaceInviteLink,\n decodeSpaceInviteLink,\n createSpaceInviteLink,\n joinSpaceByLink,\n recoverSpaceAccess,\n addDeviceToSpaceKeyring,\n} from './spaces/members.js';\nexport type { JoinRequest, SpaceInviteLinkToken } from './spaces/members.js';\n\n// Nodes (per-node creation + access management + invite flows)\nexport {\n createNode,\n setNodeAccess,\n inviteToNode,\n acceptNodeInvite,\n createNodeInviteLink,\n decodeNodeInviteLink,\n encodeNodeInviteLink,\n joinNodeByLink,\n} from './spaces/nodes.js';\nexport type { CreateNodeInput, NodeInviteBundle, NodeInviteLinkToken } from './spaces/nodes.js';\n\n// Object core\nexport {\n buildTree,\n breadcrumbs,\n ancestors,\n subtreeIds,\n nextOrder,\n addObject,\n patchObject,\n reparentObject,\n reorderObjects,\n archiveObject,\n} from './objects/objects.js';\nexport type { ObjectTreeNode, NewObjectInput } from './objects/objects.js';\n\n// Object index\nexport {\n pushIndexSeed,\n seedSpaceObjectIndex,\n updateObjectIndex,\n readObjectTree,\n} from './spaces/object-index.js';\n\n// Pairing\nexport { startDevicePairing, completeDevicePairing, PAIR_PREFIX } from './sync/pairing.js';\nexport type { PairResult } from './sync/pairing.js';\n\n// Pull cache\nexport { pullCache, PULL_CACHE_MAX_AGE_MS } from './sync/pull-cache.js';\n\n// Profile cache\nexport { cacheProfile, loadCachedProfile } from './sync/profile-cache.js';\n\n// Fetch\nexport { fetchWithTimeout, CONNECT_TIMEOUT_MS } from './sync/fetch-timeout.js';\n\n// Base64\nexport { starfishBase64 } from './sync/base64.js';\nexport { toBase64Url, fromBase64Url } from './sync/base64url.js';\n\n// Utilities\nexport { matchTitle, rankResults, fold, isWordStart } from './utils/search-match.js';\nexport type { MatchRange, TitleMatch, RankedResult } from './utils/search-match.js';\n\nexport { registerPull, dispatchDocChange, emitSseStatus, onSseStatus, clearLiveSyncBus } from './utils/live-sync-bus.js';\n\nexport { previewInvite } from './utils/invite-preview.js';\nexport type { InvitePreview } from './utils/invite-preview.js';\n","/**\n * Seal a small secret to an X25519 KEM key so it can ride in a plaintext synced\n * doc without exposing it to the server.\n *\n * - {@link sealToSelf}/{@link unsealFromSelf} — sealed to THIS account's own key\n * (public-space join credentials, which embed a bearer secret). Recovered on\n * any device with the same seed.\n * - {@link sealToRecipient}/{@link unsealFromRecipient} — sealed to ANOTHER user's\n * published KEM key (DM-invite delivery).\n */\nimport {\n bytesToHex,\n hexToBytes,\n unwrapFromEntry,\n verifyEntrySignature,\n wrapForRecipient,\n} from '@drakkar.software/starfish-keyring';\nimport type { WrappedKeyEntry } from '@drakkar.software/starfish-keyring';\n\nimport type { Session } from './identity.js';\n\n/** A payload sealed to a KEM key: the wrapped CEK + hex(iv ‖ AES-GCM ct). */\nexport interface SealedBlob {\n entry: WrappedKeyEntry;\n ct: string;\n}\n\nconst SELF_EPOCH = 0;\n\nconst subtle = () => globalThis.crypto.subtle;\n\nasync function seal(session: Session, recipientKemPub: string, plaintext: string): Promise<SealedBlob> {\n const cek = globalThis.crypto.getRandomValues(new Uint8Array(32));\n const entry = await wrapForRecipient(cek, recipientKemPub, {\n adderEdPrivHex: session.keys.edPriv,\n adderEdPubHex: session.keys.edPub,\n addedAt: Math.floor(Date.now() / 1000),\n epoch: SELF_EPOCH,\n });\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const key = await subtle().importKey('raw', cek, { name: 'AES-GCM' }, false, ['encrypt']);\n const ctBuf = await subtle().encrypt({ name: 'AES-GCM', iv }, key, new TextEncoder().encode(plaintext));\n const packed = new Uint8Array(iv.length + ctBuf.byteLength);\n packed.set(iv, 0);\n packed.set(new Uint8Array(ctBuf), iv.length);\n return { entry, ct: bytesToHex(packed) };\n}\n\nasync function open(session: Session, blob: SealedBlob): Promise<string> {\n const cek = await unwrapFromEntry(blob.entry, session.keys.kemPriv);\n const packed = hexToBytes(blob.ct);\n const iv = new Uint8Array(packed.subarray(0, 12));\n const ctBytes = new Uint8Array(packed.subarray(12));\n const key = await subtle().importKey('raw', new Uint8Array(cek), { name: 'AES-GCM' }, false, ['decrypt']);\n const out = await subtle().decrypt({ name: 'AES-GCM', iv }, key, ctBytes);\n return new TextDecoder().decode(out);\n}\n\n/** Seal `plaintext` so only this account (its seed) can open it. */\nexport function sealToSelf(session: Session, plaintext: string): Promise<SealedBlob> {\n return seal(session, session.keys.kemPub, plaintext);\n}\n\n/** Open a {@link SealedBlob} sealed by {@link sealToSelf} for this account. */\nexport async function unsealFromSelf(session: Session, blob: SealedBlob): Promise<string> {\n if (blob.entry.addedBy !== session.keys.edPub) throw new Error('sealed blob not self-signed');\n if (!(await verifyEntrySignature(blob.entry, SELF_EPOCH))) throw new Error('sealed blob signature invalid');\n return open(session, blob);\n}\n\n/** Seal `plaintext` to ANOTHER user's published KEM key, signed by this session. */\nexport function sealToRecipient(session: Session, recipientKemPub: string, plaintext: string): Promise<SealedBlob> {\n return seal(session, recipientKemPub, plaintext);\n}\n\n/** Open a {@link SealedBlob} sealed to THIS account by some (arbitrary) sender. */\nexport async function unsealFromRecipient(session: Session, blob: SealedBlob): Promise<string> {\n if (!(await verifyEntrySignature(blob.entry, SELF_EPOCH))) throw new Error('sealed blob signature invalid');\n return open(session, blob);\n}\n","/**\n * Space membership — invite-based (member cap) and link-based (open access).\n *\n * MEMBER join: the owner records the invitee in the roster, mints a space-scoped\n * member cap, and adds the invitee to the space-wide keyring (if it exists) so they\n * can decrypt `enc` content. The invitee stores a `{kind:'member'}` entry.\n *\n * LINK join: the owner mints an ephemeral Ed/KEM keypair whose *private* key ships\n * inside a URL-fragment token, adds the ephemeral userId to the roster so the server\n * grants `space:member`, and mints a member cap scoped to that ephemeral subject.\n * Any bearer of the link stores a `{kind:'link'}` entry. Revocation = `removeSpaceMember`.\n *\n * DEVICE PAIRING: after pairing, call `addDeviceToSpaceKeyring(session, spaceId, device)`\n * for each space the paired device should decrypt. ONE keyring per space encrypts all\n * `enc` nodes; adding the device once unlocks the whole space's E2EE content.\n */\nimport { generateDeviceKeys } from '@drakkar.software/starfish-identities';\nimport { addCollectionRecipient } from '@drakkar.software/starfish-keyring';\nimport { mintMemberCap } from '@drakkar.software/starfish-sharing';\n\nimport type { Space } from '../core/types.js';\nimport type { Session } from '../sync/identity.js';\nimport {\n getSpaceAccessEntry,\n hydrateSpaceAccessStore,\n localSpaceAccessEntries,\n saveSpaceAccessEntry,\n} from '../sync/space-access-store.js';\nimport { keyringName, spaceMemberScope, userIdFromEdPub } from '../sync/paths.js';\nimport { addJoinedSpaceWithCap, addJoinedSpaceWithLinkAccess, addSpaceMember, readSpaces, updateSpacesDoc } from './registry.js';\nimport { sealToSelf, unsealFromSelf } from '../sync/account-seal.js';\nimport { toBase64Url, fromBase64Url } from '../sync/base64url.js';\n\nexport interface JoinRequest {\n edPub: string;\n kemPub: string;\n userId: string;\n}\n\nexport function makeJoinRequest(session: Session): string {\n const req: JoinRequest = { edPub: session.keys.edPub, kemPub: session.keys.kemPub, userId: session.userId };\n return JSON.stringify(req);\n}\n\nfunction isAlreadyPresentRecipient(err: unknown): boolean {\n return err instanceof Error && /already present in epoch/.test(err.message);\n}\n\nfunction isKeyringMissing(err: unknown): boolean {\n return err instanceof Error && /not found|404|does not exist/i.test(err.message);\n}\n\ninterface SpaceInvite {\n spaceId: string;\n spaceName: string;\n cap: unknown;\n}\n\n/**\n * Owner: invite an identity into a space. Records them in the roster, mints a\n * space-scoped member cap, and adds them to the space-wide keyring if it exists\n * (so they can decrypt `enc` nodes from the start).\n * Returns the invite bundle JSON.\n */\nexport async function inviteToSpace(\n session: Session,\n spaceId: string,\n requestJson: string,\n canWrite = true,\n spaceName?: string,\n): Promise<string> {\n const req = JSON.parse(requestJson) as JoinRequest;\n if (!req.edPub || !req.kemPub || !req.userId) throw new Error('That is not a valid join request.');\n await addSpaceMember(session.accountClient, spaceId, session.userId, req.userId);\n\n // Add invitee to the space-wide keyring so they can decrypt enc nodes.\n // Silently skip if the keyring doesn't exist yet (no enc nodes in the space).\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: req.kemPub, userId: req.userId, label: req.userId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err) && !isKeyringMissing(err)) {\n // Only rethrow unexpected errors — missing keyring (no enc nodes yet) is normal.\n console.warn('[octospaces] inviteToSpace: keyring add skipped', err);\n }\n }\n\n // NOTE: 'chat' is the cap collection the deployed server's space-member enricher recognises.\n const cap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: req.edPub, kemPubHex: req.kemPub, userIdHex: req.userId },\n 'chat',\n spaceMemberScope(spaceId, canWrite),\n );\n let name = spaceName?.trim();\n if (!name) {\n const { spaces } = await readSpaces(session.accountClient, session.userId);\n name = spaces.find((s) => s.id === spaceId)?.name ?? 'Space';\n }\n const invite: SpaceInvite = { spaceId, spaceName: name, cap };\n return JSON.stringify(invite);\n}\n\n/**\n * Invitee: accept a space invite — store the cap and register the space.\n * Returns the joined space.\n */\nexport async function acceptSpaceInvite(session: Session, inviteJson: string): Promise<Space> {\n const inv = JSON.parse(inviteJson) as Partial<SpaceInvite>;\n const cap = inv.cap as { kind?: string; sub?: string } | undefined;\n if (!cap || !inv.spaceId) throw new Error('That is not a valid space invite.');\n if (cap.kind !== 'member') throw new Error('That is not a valid space invite.');\n if (!cap.sub || cap.sub !== session.keys.edPub) {\n throw new Error('This invite was issued for a different identity.');\n }\n const spaceId = inv.spaceId;\n const capJson = JSON.stringify(cap);\n const name = inv.spaceName?.trim() || `space-${spaceId.slice(-6)}`;\n const space: Space = { id: spaceId, name, short: name.slice(0, 2).toUpperCase(), members: 1 };\n await addJoinedSpaceWithCap(session.accountClient, session.userId, space, capJson);\n saveSpaceAccessEntry(spaceId, { kind: 'member', cap: capJson });\n return space;\n}\n\n// ── Link-based joins (public spaces) ─────────────────────────────────────────\n\n/** A space invite link token (v:1, no ownerId — derive from cap.iss instead). */\nexport interface SpaceInviteLinkToken {\n v: 1;\n spaceId: string;\n spaceName: string;\n cap: unknown;\n /** The throwaway ephemeral subject's Ed25519 private key (hex). */\n key: string;\n write: boolean;\n}\n\nexport function encodeSpaceInviteLink(origin: string, token: SpaceInviteLinkToken): string {\n const base = origin.replace(/\\/+$/, '');\n return `${base}/join#${toBase64Url(JSON.stringify(token))}`;\n}\n\nexport function decodeSpaceInviteLink(fragment: string): SpaceInviteLinkToken {\n const frag = fragment.startsWith('#') ? fragment.slice(1) : fragment;\n const tok = JSON.parse(fromBase64Url(frag)) as Partial<SpaceInviteLinkToken>;\n if (!tok || !tok.spaceId || !tok.cap || !tok.key) {\n throw new Error('That space invite link is malformed or incomplete.');\n }\n return {\n v: 1,\n spaceId: tok.spaceId,\n spaceName: tok.spaceName ?? 'Space',\n cap: tok.cap,\n key: tok.key,\n write: !!tok.write,\n };\n}\n\n/**\n * Owner: create a shareable invite link for a PUBLIC space.\n *\n * Mints an ephemeral Ed/KEM keypair, adds its userId to the roster (so the server\n * grants `space:member` to any bearer), and encodes the private key + cap in the URL.\n * Anyone with the link can join; revoke by calling `removeSpaceMember(ephemeralUserId)`.\n */\nexport async function createSpaceInviteLink(\n session: Session,\n spaceId: string,\n spaceName: string,\n write: boolean,\n origin: string,\n): Promise<{ token: SpaceInviteLinkToken; link: string }> {\n const ek = generateDeviceKeys();\n const ephemeralUserId = await userIdFromEdPub(ek.edPub);\n const cap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: ek.edPub, kemPubHex: ek.kemPub, userIdHex: ephemeralUserId },\n 'chat',\n spaceMemberScope(spaceId, write),\n );\n // Add the ephemeral userId to the roster so the server grants `space:member`\n await addSpaceMember(session.accountClient, spaceId, session.userId, ephemeralUserId);\n\n // Add ephemeral KEM to the space keyring so link-bearers can decrypt enc content.\n // Silently skip if the keyring doesn't exist yet (no enc nodes).\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: ek.kemPub, userId: ephemeralUserId, label: ephemeralUserId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err) && !isKeyringMissing(err)) {\n console.warn('[octospaces] createSpaceInviteLink: keyring add skipped', err);\n }\n }\n\n const token: SpaceInviteLinkToken = { v: 1, spaceId, spaceName, cap, key: ek.edPriv, write };\n return { token, link: encodeSpaceInviteLink(origin, token) };\n}\n\n/**\n * Any user: join a space by redeeming an invite link token.\n * Stores the link credential locally and seals it into the synced `_spaces` doc.\n */\nexport async function joinSpaceByLink(session: Session, token: SpaceInviteLinkToken): Promise<Space> {\n const name = token.spaceName.trim() || `space-${token.spaceId.slice(-6)}`;\n const space: Space = {\n id: token.spaceId,\n name,\n short: name.slice(0, 2).toUpperCase(),\n members: 1,\n };\n const accessPayload = { cap: token.cap, key: token.key, write: token.write };\n const sealed = await sealToSelf(session, JSON.stringify(accessPayload));\n await addJoinedSpaceWithLinkAccess(session.accountClient, session.userId, space, sealed);\n saveSpaceAccessEntry(token.spaceId, { kind: 'link', cap: token.cap, key: token.key, write: token.write });\n return space;\n}\n\n/**\n * Add a device's KEM key as a recipient of a space's keyring.\n *\n * Call this after device pairing (for each space the new device should be able to\n * decrypt). ONE space keyring encrypts ALL the space's `enc` nodes — adding the device\n * once unlocks the whole space's E2EE content. Silently a no-op if the keyring doesn't\n * exist yet.\n */\nexport async function addDeviceToSpaceKeyring(\n session: Session,\n spaceId: string,\n device: { kemPub: string; edPub: string; userId: string },\n): Promise<void> {\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: device.kemPub, userId: device.userId, label: device.userId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err) && !isKeyringMissing(err)) throw err;\n }\n}\n\n/**\n * Single sign-in hydration: merges server-side caps (plaintext member caps from\n * `_spaces.caps`) and sealed link access (from `_spaces.pubAccess`) into the\n * unified space-access store. Call once on sign-in / account switch.\n * Backfills any local-only entries to the server.\n */\nexport async function recoverSpaceAccess(\n session: Session,\n server: { caps: Record<string, string>; pubAccess: Record<string, import('../sync/account-seal.js').SealedBlob> },\n): Promise<void> {\n // Unseal link access blobs\n const linkAccess: Record<string, { cap: unknown; key: string; write: boolean }> = {};\n for (const [spaceId, sealed] of Object.entries(server.pubAccess)) {\n try {\n const raw = await unsealFromSelf(session, sealed);\n const parsed = JSON.parse(raw) as { cap: unknown; key: string; write: boolean };\n if (parsed.cap && parsed.key) linkAccess[spaceId] = parsed;\n } catch (e) {\n console.error('[octospaces] recoverSpaceAccess: failed to unseal', spaceId, e);\n }\n }\n\n await hydrateSpaceAccessStore(session.userId, server.caps, linkAccess);\n\n // Backfill local-only entries to the server\n const local = localSpaceAccessEntries();\n const missingMemberCaps = Object.entries(local)\n .filter(([id, e]) => e.kind === 'member' && !(id in server.caps));\n const missingLinks = Object.entries(local)\n .filter(([id, e]) => e.kind === 'link' && !(id in server.pubAccess));\n\n if (missingMemberCaps.length === 0 && missingLinks.length === 0) return;\n\n try {\n const newCaps: Record<string, string> = {};\n for (const [id, e] of missingMemberCaps) if (e.kind === 'member') newCaps[id] = e.cap;\n\n const newPubAccess: Record<string, import('../sync/account-seal.js').SealedBlob> = {};\n for (const [id, e] of missingLinks) {\n if (e.kind === 'link') {\n newPubAccess[id] = await sealToSelf(session, JSON.stringify({ cap: e.cap, key: e.key, write: e.write }));\n }\n }\n\n await updateSpacesDoc(session.accountClient, session.userId, (cur) => ({\n spaces: cur.spaces,\n caps: { ...cur.caps, ...newCaps },\n pubAccess: { ...cur.pubAccess, ...newPubAccess },\n }));\n } catch (e) {\n console.error('[octospaces] recoverSpaceAccess: backfill failed', e);\n }\n}\n","/**\n * base64url for link fragments (UTF-8 safe, web + native) — the encoding both\n * invitation-link kinds ride in a URL `#fragment`. No padding, `+/` → `-_`.\n */\nexport function toBase64Url(json: string): string {\n const bytes = new TextEncoder().encode(json);\n let bin = '';\n for (const b of bytes) bin += String.fromCharCode(b);\n const b64 = typeof btoa === 'function' ? btoa(bin) : Buffer.from(json, 'utf-8').toString('base64');\n return b64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nexport function fromBase64Url(b64url: string): string {\n const b64 = b64url.replace(/-/g, '+').replace(/_/g, '/');\n if (typeof atob === 'function') {\n const bin = atob(b64);\n const bytes = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);\n return new TextDecoder().decode(bytes);\n }\n return Buffer.from(b64, 'base64').toString('utf-8');\n}\n","/**\n * Per-node creation, access management, and invite flows.\n *\n * Nodes are the atomic content units of a space (rooms in OctoChat, pages/projects in\n * OctoVault). Each node carries two independent axes:\n * - `access`: `'public' | 'space' | 'invite'` — who may reach the node.\n * - `enc`: `boolean` — whether content is E2EE under the SPACE-WIDE keyring.\n *\n * Invalid combo: `access:'public'` + `enc:true` is rejected outright.\n *\n * Encryption uses ONE space keyring (at `spaces/{spaceId}/_keyring`). Any space member\n * holding the keyring can decrypt ALL `enc` nodes in the space — the keyring is coarse-\n * grained by design. For `access:'invite'` + `enc:true` nodes, inviting someone to the\n * node also grants them the space key (and thus access to all enc content in the space).\n *\n * Invite flows mirror the space membership flows in `members.ts` but scoped per node.\n *\n * DIRECT INVITE:\n * - `enc` node: owner adds invitee to space keyring + mints space cap → invitee calls\n * `acceptNodeInvite`, storing the space cap.\n * - `invite+plaintext` node: owner mints per-node narrow cap (nodeMemberScope) →\n * invitee calls `acceptNodeInvite`, storing the per-node cap.\n *\n * LINK INVITE:\n * - `enc` node: owner adds ephemeral KEM to space keyring; link cap uses spaceMemberScope.\n * - `invite+plaintext` node: ephemeral keypair, narrow per-node cap (nodeMemberScope).\n * - Bearer: `joinNodeByLink` — stores per-node `{kind:'link'}` entry.\n */\nimport { generateDeviceKeys } from '@drakkar.software/starfish-identities';\nimport { addCollectionRecipient } from '@drakkar.software/starfish-keyring';\nimport { mintMemberCap } from '@drakkar.software/starfish-sharing';\n\nimport type { NodeAccess, ObjectNode, ObjectType } from '../core/types.js';\nimport { ownerEnsureKeyring } from '../sync/client.js';\nimport type { Session } from '../sync/identity.js';\nimport { ownerTrustedAdders } from '../sync/identity.js';\nimport {\n keyringName,\n keyringPull,\n keyringPush,\n nodeMemberScope,\n spaceMemberScope,\n userIdFromEdPub,\n} from '../sync/paths.js';\nimport {\n getSpaceClient,\n} from '../sync/space-access.js';\nimport {\n getNodeAccessEntry,\n saveNodeAccessEntry,\n saveSpaceAccessEntry,\n} from '../sync/space-access-store.js';\nimport { sealToSelf } from '../sync/account-seal.js';\nimport { toBase64Url, fromBase64Url } from '../sync/base64url.js';\nimport { addObject } from '../objects/objects.js';\nimport { updateObjectIndex } from './object-index.js';\nimport { addSpaceMember, readSpaces } from './registry.js';\nimport { randomId } from '../core/ids.js';\nimport type { JoinRequest } from './members.js';\n\n// ── helpers ──────────────────────────────────────────────────────────────────\n\nfunction isAlreadyPresentRecipient(err: unknown): boolean {\n return err instanceof Error && /already present in epoch/.test(err.message);\n}\n\n// ── createNode ────────────────────────────────────────────────────────────────\n\nexport interface CreateNodeInput {\n type: ObjectType;\n title: string;\n emoji?: string;\n parentId?: string | null;\n /** Who may reach this node. Default: `'space'`. */\n access?: NodeAccess;\n /** Whether node content is E2EE under the space-wide keyring. Default: `false`. */\n enc?: boolean;\n /** App-specific metadata. */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Create a new node in a space's object index.\n *\n * - Rejects the invalid combo `public+enc`.\n * - For `enc` nodes, ensures the space-wide keyring exists (minted once per space,\n * idempotent on subsequent creates).\n * - Returns the created node as it was inserted into the index.\n */\nexport async function createNode(\n session: Session,\n spaceId: string,\n input: CreateNodeInput,\n reg?: { owner: string | null; members: string[] } | null,\n): Promise<ObjectNode> {\n const access = input.access ?? 'space';\n const enc = input.enc ?? false;\n if (access === 'public' && enc) throw new Error('public+enc is not a valid combination.');\n\n const nodeId = `obj-${randomId()}`;\n\n if (enc) {\n // Ensure the space-wide keyring exists (idempotent — minted once per space).\n const client = getSpaceClient(spaceId, session);\n await ownerEnsureKeyring(\n client,\n session.keys,\n keyringPull(spaceId),\n keyringPush(spaceId),\n ownerTrustedAdders(session),\n );\n }\n\n let createdNode: ObjectNode | null = null;\n\n await updateObjectIndex(session, spaceId, (nodes, now) => {\n const { nodes: next, node } = addObject(nodes, {\n id: nodeId,\n type: input.type,\n title: input.title,\n ...(input.emoji ? { emoji: input.emoji } : {}),\n parentId: input.parentId ?? null,\n ...(input.meta ? { meta: input.meta } : {}),\n access,\n enc: enc || undefined,\n }, now);\n createdNode = next.find((n) => n.id === nodeId) ?? node;\n return next;\n }, reg);\n\n if (!createdNode) throw new Error('createNode: index update did not produce a node');\n return createdNode;\n}\n\n// ── setNodeAccess ─────────────────────────────────────────────────────────────\n\n/**\n * Patch the `access`/`enc` axes of a node in the index.\n *\n * - Rejects `public+enc`.\n * - For enabling `enc`, ensures the space keyring exists (idempotent).\n * - Content migration (moving between `objpub`/`objdoc`/`objinv`) is the caller's\n * responsibility — this only flips the metadata flags.\n */\nexport async function setNodeAccess(\n session: Session,\n spaceId: string,\n nodeId: string,\n patch: { access?: NodeAccess; enc?: boolean },\n reg?: { owner: string | null; members: string[] } | null,\n): Promise<void> {\n if (patch.access === 'public' && patch.enc) throw new Error('public+enc is not valid.');\n\n if (patch.enc) {\n // Ensure the space-wide keyring exists (idempotent).\n const client = getSpaceClient(spaceId, session);\n await ownerEnsureKeyring(\n client,\n session.keys,\n keyringPull(spaceId),\n keyringPush(spaceId),\n ownerTrustedAdders(session),\n );\n }\n\n await updateObjectIndex(session, spaceId, (nodes, now) => {\n const idx = nodes.findIndex((n) => n.id === nodeId);\n if (idx < 0) return null;\n const cur = nodes[idx]!;\n\n const next: ObjectNode = { ...cur, updatedAt: now };\n\n if (patch.access !== undefined) {\n if (patch.access === 'space') {\n delete (next as unknown as Record<string, unknown>).access;\n } else {\n next.access = patch.access;\n }\n }\n\n if (patch.enc !== undefined) {\n if (!patch.enc) {\n delete (next as unknown as Record<string, unknown>).enc;\n } else {\n next.enc = true;\n }\n }\n\n // Re-validate after applying both patches\n if (next.access === 'public' && next.enc) throw new Error('public+enc is not valid.');\n\n const unchanged =\n next.access === cur.access &&\n (next.enc ?? false) === (cur.enc ?? false);\n if (unchanged) return null;\n\n return nodes.map((n, i) => (i === idx ? next : n));\n }, reg);\n}\n\n// ── Direct invite ─────────────────────────────────────────────────────────────\n\nexport interface NodeInviteBundle {\n spaceId: string;\n nodeId: string;\n nodeName: string;\n /** Space-level member cap (always present — grants index read access). */\n cap: unknown;\n /** Per-node narrow cap (only for `invite+plaintext` nodes). */\n nodeCap?: unknown;\n}\n\n/**\n * Owner: invite an identity to a specific node.\n *\n * - For `enc` nodes: adds the invitee to the space-wide keyring (granting decryption\n * access to ALL enc nodes in the space) and mints a space-level member cap.\n * - For `invite+plaintext` nodes: mints both a space-level cap (index) and a\n * narrow per-node cap (`nodeMemberScope`, covers `objinv` content).\n *\n * Returns the invite bundle JSON; pass to the invitee who calls `acceptNodeInvite`.\n */\nexport async function inviteToNode(\n session: Session,\n spaceId: string,\n nodeId: string,\n requestJson: string,\n node: { enc?: boolean },\n nodeName?: string,\n): Promise<string> {\n const req = JSON.parse(requestJson) as JoinRequest;\n if (!req.edPub || !req.kemPub || !req.userId) throw new Error('Invalid join request.');\n\n if (node.enc) {\n // Add invitee's KEM key to the SPACE-WIDE keyring (grants access to all enc nodes).\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: req.kemPub, userId: req.userId, label: req.userId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err)) throw err;\n }\n }\n\n // Always ensure space membership (for index access)\n await addSpaceMember(session.accountClient, spaceId, session.userId, req.userId);\n\n const spaceCap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: req.edPub, kemPubHex: req.kemPub, userIdHex: req.userId },\n 'chat',\n spaceMemberScope(spaceId, true),\n );\n\n const bundle: NodeInviteBundle = {\n spaceId,\n nodeId,\n nodeName: nodeName ?? nodeId,\n cap: spaceCap,\n };\n\n if (!node.enc) {\n // invite+plaintext: also mint narrow per-node cap for objinv content\n const perNodeCap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: req.edPub, kemPubHex: req.kemPub, userIdHex: req.userId },\n 'chat',\n nodeMemberScope(spaceId, nodeId, true),\n );\n bundle.nodeCap = perNodeCap;\n }\n\n return JSON.stringify(bundle);\n}\n\n/**\n * Invitee: accept a direct node invite — store the cap(s) and register access.\n * Returns the nodeId.\n */\nexport async function acceptNodeInvite(session: Session, bundleJson: string): Promise<string> {\n const bundle = JSON.parse(bundleJson) as Partial<NodeInviteBundle>;\n const cap = bundle.cap as { kind?: string; sub?: string } | undefined;\n if (!cap || !bundle.spaceId || !bundle.nodeId) throw new Error('Invalid node invite.');\n if (cap.kind !== 'member') throw new Error('Invalid node invite.');\n if (!cap.sub || cap.sub !== session.keys.edPub) {\n throw new Error('This invite was issued for a different identity.');\n }\n\n const capJson = JSON.stringify(cap);\n // Store space-level cap so the invitee can read the index\n saveSpaceAccessEntry(bundle.spaceId, { kind: 'member', cap: capJson });\n\n if (bundle.nodeCap) {\n // invite+plaintext: also store narrow per-node cap\n const nodeCapJson = JSON.stringify(bundle.nodeCap);\n saveNodeAccessEntry(bundle.spaceId, bundle.nodeId, { kind: 'member', cap: nodeCapJson });\n }\n\n return bundle.nodeId;\n}\n\n// ── Link-based node invite ────────────────────────────────────────────────────\n\n/** A node invite link token (v:1). */\nexport interface NodeInviteLinkToken {\n v: 1;\n spaceId: string;\n nodeId: string;\n nodeName: string;\n /** Cap scope depends on `enc`: spaceMemberScope for enc nodes, nodeMemberScope for plaintext. */\n cap: unknown;\n /** The ephemeral subject's Ed25519 private key (hex). */\n key: string;\n write: boolean;\n}\n\nexport function encodeNodeInviteLink(origin: string, token: NodeInviteLinkToken): string {\n const base = origin.replace(/\\/+$/, '');\n return `${base}/join/node#${toBase64Url(JSON.stringify(token))}`;\n}\n\nexport function decodeNodeInviteLink(fragment: string): NodeInviteLinkToken {\n const frag = fragment.startsWith('#') ? fragment.slice(1) : fragment;\n const tok = JSON.parse(fromBase64Url(frag)) as Partial<NodeInviteLinkToken>;\n if (!tok || !tok.spaceId || !tok.nodeId || !tok.cap || !tok.key) {\n throw new Error('That node invite link is malformed or incomplete.');\n }\n return {\n v: 1,\n spaceId: tok.spaceId,\n nodeId: tok.nodeId,\n nodeName: tok.nodeName ?? tok.nodeId,\n cap: tok.cap,\n key: tok.key,\n write: !!tok.write,\n };\n}\n\n/**\n * Owner: create a shareable invite link for a specific node.\n *\n * - For `enc` nodes: adds ephemeral KEM to the space-wide keyring; the link cap uses\n * `spaceMemberScope` so the bearer can read the keyring and decrypt enc content.\n * - For `invite+plaintext` nodes: narrow per-node cap (`nodeMemberScope`), no keyring.\n *\n * Anyone with the link can access the node; revoke by calling\n * `removeSpaceMember(ephemeralUserId)` (and rotating the space keyring for enc nodes).\n */\nexport async function createNodeInviteLink(\n session: Session,\n spaceId: string,\n nodeId: string,\n nodeName: string,\n node: { enc?: boolean },\n write: boolean,\n origin: string,\n): Promise<{ token: NodeInviteLinkToken; link: string }> {\n const ek = generateDeviceKeys();\n const ephemeralUserId = await userIdFromEdPub(ek.edPub);\n\n await addSpaceMember(session.accountClient, spaceId, session.userId, ephemeralUserId);\n\n if (node.enc) {\n // Add ephemeral KEM to the SPACE-WIDE keyring\n try {\n await addCollectionRecipient(\n session.chatClient,\n keyringName(spaceId),\n { subKem: ek.kemPub, userId: ephemeralUserId, label: ephemeralUserId.slice(0, 8) },\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub, kemPriv: session.keys.kemPriv },\n { trustedAdders: [session.keys.edPub] },\n );\n } catch (err) {\n if (!isAlreadyPresentRecipient(err)) throw err;\n }\n }\n\n // enc nodes need space-scoped cap (must reach the space keyring);\n // plaintext invite nodes use the narrow per-node cap.\n const cap = await mintMemberCap(\n session.keys.edPriv,\n session.keys.edPub,\n { edPubHex: ek.edPub, kemPubHex: ek.kemPub, userIdHex: ephemeralUserId },\n 'chat',\n node.enc\n ? spaceMemberScope(spaceId, write)\n : nodeMemberScope(spaceId, nodeId, write),\n );\n\n const token: NodeInviteLinkToken = { v: 1, spaceId, nodeId, nodeName, cap, key: ek.edPriv, write };\n return { token, link: encodeNodeInviteLink(origin, token) };\n}\n\n/**\n * Any user: access a node by redeeming an invite link token.\n * Stores the per-node link entry locally and seals it into the synced `_spaces` doc.\n */\nexport async function joinNodeByLink(session: Session, token: NodeInviteLinkToken): Promise<string> {\n const accessPayload = { cap: token.cap, key: token.key, write: token.write };\n const sealed = await sealToSelf(session, JSON.stringify(accessPayload));\n\n // Persist sealed entry into _spaces.pubAccess keyed by spaceId:nodeId\n const { updateSpacesDoc } = await import('./registry.js');\n await updateSpacesDoc(session.accountClient, session.userId, (cur) => ({\n spaces: cur.spaces,\n caps: cur.caps,\n pubAccess: {\n ...cur.pubAccess,\n [`${token.spaceId}:${token.nodeId}`]: sealed,\n },\n }));\n\n saveNodeAccessEntry(token.spaceId, token.nodeId, {\n kind: 'link',\n cap: token.cap,\n key: token.key,\n write: token.write,\n });\n\n return token.nodeId;\n}\n","/**\n * Generic object-tree model — pure logic over a space's object index.\n *\n * A space's contents are {@link ObjectNode}s in one union-merged index doc at\n * `spaces/{spaceId}/objects/_index`. This module is the pure, testable core:\n * the tree builder + merge-artifact guards, breadcrumbs, ordering, and the node\n * reducers a `store.set` applies.\n *\n * Because the index is union-merged (per-node last-write-wins keyed on `updatedAt`),\n * the tree is eventually consistent — two devices can concurrently produce a cycle\n * or an orphan. The builder below is the single place those are repaired so every\n * consumer renders a well-formed tree.\n *\n * No domain types (room, category, task, …) are defined here. Apps define their own.\n */\nimport type { ID, NodeAccess, ObjectNode, ObjectType } from '../core/types.js';\nimport { randomId } from '../core/ids.js';\n\n/** A node plus its resolved children — the shape a tree view renders. */\nexport interface ObjectTreeNode extends ObjectNode {\n depth: number;\n children: ObjectTreeNode[];\n}\n\nfunction compareSiblings(a: ObjectNode, b: ObjectNode): number {\n if (a.order !== b.order) return a.order - b.order;\n return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;\n}\n\n/** The order value for a new node appended after `siblings`. */\nexport function nextOrder(siblings: ObjectNode[]): number {\n let max = 0;\n for (const s of siblings) if (s.order > max) max = s.order;\n return max + 1;\n}\n\n/**\n * Build the render tree from a flat node list, repairing merge artifacts:\n * - **archived** nodes (and their subtrees) are dropped.\n * - **orphans** — a `parentId` that is missing or archived — reparent to root.\n * - **cycles** — a node reachable from itself via `parentId` — reparent to root.\n * - **siblings** sort by {@link compareSiblings} for cross-device determinism.\n */\nexport function buildTree(nodes: ObjectNode[], includeArchived = false): ObjectTreeNode[] {\n const live = includeArchived ? nodes : nodes.filter((n) => !n.archived);\n const byId = new Map<ID, ObjectNode>(live.map((n) => [n.id, n]));\n\n const effectiveParent = (n: ObjectNode): ID | null => {\n if (n.parentId == null) return null;\n if (!byId.has(n.parentId)) return null;\n const seen = new Set<ID>([n.id]);\n let cur: ID | null = n.parentId;\n while (cur != null) {\n if (seen.has(cur)) return null;\n seen.add(cur);\n const parent = byId.get(cur);\n if (!parent) return null;\n cur = parent.parentId;\n }\n return n.parentId;\n };\n\n const childrenOf = new Map<ID | null, ObjectNode[]>();\n for (const n of live) {\n const p = effectiveParent(n);\n const bucket = childrenOf.get(p) ?? [];\n bucket.push(n);\n childrenOf.set(p, bucket);\n }\n\n function attach(parent: ID | null, depth: number): ObjectTreeNode[] {\n return (childrenOf.get(parent) ?? [])\n .slice()\n .sort(compareSiblings)\n .map((n): ObjectTreeNode => ({ ...n, depth, children: attach(n.id, depth + 1) }));\n }\n\n return attach(null, 0);\n}\n\n/** The root→node trail (inclusive) for breadcrumbs. Returns `[]` if unknown. */\nexport function breadcrumbs(nodes: ObjectNode[], id: ID): ObjectNode[] {\n const byId = new Map<ID, ObjectNode>(nodes.map((n) => [n.id, n]));\n const trail: ObjectNode[] = [];\n const seen = new Set<ID>();\n let cur: ID | null = id;\n while (cur != null && byId.has(cur) && !seen.has(cur)) {\n seen.add(cur);\n const node: ObjectNode = byId.get(cur)!;\n trail.unshift(node);\n cur = node.parentId;\n }\n return trail;\n}\n\n/** The root→parent trail (EXCLUSIVE of the node itself). */\nexport function ancestors(nodes: ObjectNode[], id: ID): ObjectNode[] {\n return breadcrumbs(nodes, id).slice(0, -1);\n}\n\n/** The ids of a node and its whole subtree (for cascade-archive). */\nexport function subtreeIds(nodes: ObjectNode[], rootId: ID): Set<ID> {\n const childrenOf = new Map<ID | null, ID[]>();\n for (const n of nodes) {\n const bucket = childrenOf.get(n.parentId) ?? [];\n bucket.push(n.id);\n childrenOf.set(n.parentId, bucket);\n }\n const out = new Set<ID>();\n const walk = (id: ID) => {\n if (out.has(id)) return;\n out.add(id);\n for (const child of childrenOf.get(id) ?? []) walk(child);\n };\n walk(rootId);\n return out;\n}\n\n// ── Node reducers (pure: ObjectNode[] → ObjectNode[]) ─────────────────────────\n\nexport interface NewObjectInput {\n type: ObjectType;\n parentId?: ID | null;\n title: string;\n emoji?: string;\n /** App-specific metadata passed through to node.meta. */\n meta?: Record<string, unknown>;\n /** Provide to reuse an id (e.g. a node id derived elsewhere); else minted. */\n id?: ID;\n /** Who may reach this node. Absent ⇒ `'space'` (all space members). */\n access?: NodeAccess;\n /** Whether the node's content is E2EE under its own per-node keyring. Absent ⇒ false. */\n enc?: boolean;\n}\n\n/** Append a new node under `parentId` at the end of its sibling order. */\nexport function addObject(nodes: ObjectNode[], input: NewObjectInput, now: number): { nodes: ObjectNode[]; node: ObjectNode } {\n const parentId = input.parentId ?? null;\n const siblings = nodes.filter((n) => n.parentId === parentId);\n const node: ObjectNode = {\n id: input.id ?? `obj-${randomId()}`,\n type: input.type,\n parentId,\n order: nextOrder(siblings),\n title: input.title,\n ...(input.emoji ? { emoji: input.emoji } : {}),\n updatedAt: now,\n ...(input.meta ? { meta: input.meta } : {}),\n ...(input.access && input.access !== 'space' ? { access: input.access } : {}),\n ...(input.enc ? { enc: true as const } : {}),\n };\n return { nodes: [...nodes, node], node };\n}\n\n/** Patch a node's mutable metadata (title/emoji/meta/access/enc), bumping `updatedAt`. */\nexport function patchObject(\n nodes: ObjectNode[],\n id: ID,\n patch: Partial<Pick<ObjectNode, 'title' | 'emoji' | 'meta' | 'access' | 'enc'>>,\n now: number,\n): ObjectNode[] {\n return nodes.map((n) => (n.id === id ? { ...n, ...patch, updatedAt: now } : n));\n}\n\n/** Reparent a node (move in the tree). Rejects making a node its own descendant. */\nexport function reparentObject(nodes: ObjectNode[], id: ID, parentId: ID | null, now: number): ObjectNode[] {\n if (id === parentId) return nodes;\n if (parentId != null && subtreeIds(nodes, id).has(parentId)) return nodes;\n const siblings = nodes.filter((n) => n.parentId === parentId && n.id !== id);\n return nodes.map((n) => (n.id === id ? { ...n, parentId, order: nextOrder(siblings), updatedAt: now } : n));\n}\n\n/** Set explicit sibling order (drag-reorder). */\nexport function reorderObjects(nodes: ObjectNode[], orderById: Record<ID, number>, now: number): ObjectNode[] {\n return nodes.map((n) => (n.id in orderById ? { ...n, order: orderById[n.id]!, updatedAt: now } : n));\n}\n\n/** Cascade-archive a node and its whole subtree (soft delete). */\nexport function archiveObject(nodes: ObjectNode[], id: ID, now: number): ObjectNode[] {\n const ids = subtreeIds(nodes, id);\n return nodes.map((n) => (ids.has(n.id) ? { ...n, archived: true, updatedAt: now } : n));\n}\n\n","/**\n * Device pairing (one-way, PIN-sealed). The existing device provisions a new\n * device's keypair + cap bundle, seals it with the PIN (Argon2id → AES-GCM), and\n * drops it on the public `_pairing/<nonce>` rendezvous. The QR carries only the\n * nonce; the new device fetches the sealed blob, opens it with the PIN, and\n * validates the cap bundle.\n */\nimport { StarfishClient } from '@drakkar.software/starfish-client';\nimport {\n installPairingBundle,\n openWithPassphrase,\n provisionDevice,\n sealWithPassphrase,\n} from '@drakkar.software/starfish-identities';\nimport type { CapCert } from '@drakkar.software/starfish-protocol';\n\nimport type { DeviceKeys } from './client.js';\nimport { getSyncBase, getSyncNamespace } from '../core/config.js';\nimport { fetchWithTimeout } from './fetch-timeout.js';\nimport type { Session } from './identity.js';\nimport { fingerprintFromUserId } from './identity.js';\nimport { bytesToHex, linkedDeviceScope } from './paths.js';\n\n/** The QR-payload prefix this SDK uses. Kept distinct from `octochat-pair:` so apps\n * can route QR payloads to the correct handler during their migration window. */\nexport const PAIR_PREFIX = 'octospaces-pair:';\n\n// Linked-device cap-cert lifetime — one year keeps a linked device usable long-term.\nconst LINKED_DEVICE_TTL_SEC = 365 * 24 * 60 * 60;\n\nfunction anonClient(): StarfishClient {\n return new StarfishClient({ baseUrl: getSyncBase(), namespace: getSyncNamespace(), fetch: fetchWithTimeout() });\n}\n\nfunction randomNonce(): string {\n const b = new Uint8Array(16);\n globalThis.crypto.getRandomValues(b);\n return bytesToHex(b);\n}\n\n/**\n * Existing device: provision + PIN-seal a new device, publish to rendezvous, return\n * the QR payload.\n *\n * After pairing, call `addDeviceToSpaceKeyring(session, spaceId, newDeviceKeys)` for\n * each space whose E2EE content the new device should decrypt. ONE space keyring\n * encrypts ALL `enc` nodes in a space — one call per space unlocks everything.\n * Plaintext (`space` / `public`) nodes are immediately accessible via the linked-device\n * cap-cert (no extra keyring step).\n */\nexport async function startDevicePairing(session: Session, pin: string): Promise<string> {\n const { deviceKeys, bundle } = await provisionDevice(\n { edPriv: session.keys.edPriv, edPub: session.keys.edPub },\n { scope: linkedDeviceScope(session.userId), ttlSec: LINKED_DEVICE_TTL_SEC },\n );\n const blob = JSON.stringify({ v: 1, keys: deviceKeys, bundle });\n const sealed = await sealWithPassphrase(pin, new TextEncoder().encode(blob));\n const nonce = randomNonce();\n await anonClient().push(`/push/_pairing/${nonce}`, sealed as unknown as Record<string, unknown>, null);\n return `${PAIR_PREFIX}${nonce}.${session.keys.edPub}`;\n}\n\nexport interface PairResult {\n userId: string;\n fingerprint: string;\n deviceKeys: DeviceKeys;\n capCert: CapCert;\n}\n\n/** New device: fetch the sealed blob by nonce, open with PIN, validate the bundle. */\nexport async function completeDevicePairing(payload: string, pin: string): Promise<PairResult> {\n // Accept both `octospaces-pair:` and legacy `octochat-pair:` so apps still using the\n // old QR format can complete a pairing against this SDK during the migration window.\n const body = (payload.startsWith(PAIR_PREFIX) || payload.includes('-pair:')\n ? payload.slice(payload.indexOf(':') + 1)\n : payload).trim();\n const [nonce, expectedRootEdPub] = body.split('.');\n const res = await anonClient().pull(`/pull/_pairing/${nonce}`).catch(() => null);\n const sealed = res?.data as Record<string, unknown> | undefined;\n if (!sealed || !sealed.v) throw new Error('Pairing code not found or expired.');\n let inner: Uint8Array;\n try {\n inner = await openWithPassphrase(pin, sealed as never);\n } catch {\n throw new Error('Wrong PIN or corrupted pairing code.');\n }\n const blob = JSON.parse(new TextDecoder().decode(inner)) as { keys: unknown; bundle: unknown };\n const opts = (expectedRootEdPub ? { expectedRootEdPub } : {}) as Parameters<typeof installPairingBundle>[2];\n const installed = await installPairingBundle(\n blob.bundle as Parameters<typeof installPairingBundle>[0],\n blob.keys as Parameters<typeof installPairingBundle>[1],\n opts,\n );\n const userId = installed.credentials.userId;\n return {\n userId,\n fingerprint: fingerprintFromUserId(userId),\n deviceKeys: installed.credentials.device,\n capCert: installed.credentials.capCert,\n };\n}\n","/**\n * A chunked base64 provider for the Starfish platform.\n *\n * The SDK's default web encoder is `btoa(String.fromCharCode(...data))`, which\n * spreads the entire byte array into one call — a multi-megabyte attachment\n * overflows the argument/stack limit and throws \"Maximum call stack size exceeded\".\n * This provider walks the bytes in fixed windows instead, so it scales to large blobs.\n *\n * Prefers the platform's own `btoa`/`atob` (web) and falls back to a pure\n * implementation where they're absent (Hermes/native).\n */\nimport type { Base64Provider } from '@drakkar.software/starfish-protocol';\n\nconst CHUNK = 0x6000; // 24 576 bytes — multiple of 3, well under V8's apply limit\n\nconst ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\nconst REVERSE = (() => {\n const table = new Int16Array(128).fill(-1);\n for (let i = 0; i < ALPHABET.length; i++) table[ALPHABET.charCodeAt(i)] = i;\n return table;\n})();\n\nconst nativeCodec =\n typeof globalThis !== 'undefined' &&\n typeof globalThis.btoa === 'function' &&\n typeof globalThis.atob === 'function';\n\nfunction encodeViaBtoa(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i += CHUNK) {\n binary += String.fromCharCode.apply(null, data.subarray(i, i + CHUNK) as unknown as number[]);\n }\n return globalThis.btoa(binary);\n}\n\nfunction decodeViaAtob(encoded: string): Uint8Array {\n const binary = globalThis.atob(encoded);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) out[i] = binary.charCodeAt(i);\n return out;\n}\n\nfunction encodePure(data: Uint8Array): string {\n const len = data.length;\n const full = len - (len % 3);\n const parts: string[] = [];\n for (let start = 0; start < full; start += CHUNK) {\n const stop = Math.min(start + CHUNK, full);\n let s = '';\n for (let i = start; i < stop; i += 3) {\n const n = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];\n s += ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + ALPHABET[(n >> 6) & 63] + ALPHABET[n & 63];\n }\n parts.push(s);\n }\n if (len - full === 1) {\n const n = data[full] << 16;\n parts.push(ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + '==');\n } else if (len - full === 2) {\n const n = (data[full] << 16) | (data[full + 1] << 8);\n parts.push(ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + ALPHABET[(n >> 6) & 63] + '=');\n }\n return parts.join('');\n}\n\nfunction decodePure(encoded: string): Uint8Array {\n let validLen = encoded.length;\n while (validLen > 0 && encoded.charCodeAt(validLen - 1) === 61) validLen--;\n const out = new Uint8Array((validLen * 3) >> 2);\n let o = 0, buf = 0, bits = 0;\n for (let i = 0; i < validLen; i++) {\n const code = encoded.charCodeAt(i);\n const v = code < 128 ? REVERSE[code] : -1;\n if (v < 0) continue;\n buf = (buf << 6) | v;\n bits += 6;\n if (bits >= 8) {\n bits -= 8;\n out[o++] = (buf >> bits) & 0xff;\n }\n }\n return o === out.length ? out : out.subarray(0, o);\n}\n\n/** Spread-free, chunked base64 — a drop-in for the SDK's default provider. */\nexport const starfishBase64: Base64Provider = nativeCodec\n ? { encode: encodeViaBtoa, decode: decodeViaAtob }\n : { encode: encodePure, decode: decodePure };\n","/**\n * Pure title matcher + ranker for Quick Find / Search. No React, no I/O.\n *\n * Relevance is tiered the way a human reads a match, strongest first:\n *\n * 1. PREFIX — the title starts with the query (\"not\" → \"Notes\").\n * 2. WORD boundary — some word starts with the query (\"pa\" → \"New page\").\n * 3. SUBSTRING — the query appears mid-word (\"page\" → \"Homepage\").\n * 4. FUZZY — the query is a subsequence (\"rdm\" → \"Roadmap\").\n *\n * Within a tier, earlier and tighter matches in shorter titles score higher;\n * tier gaps are wider than any intra-tier penalty, so a fuzzy hit can never\n * outrank a real substring. Ties (same score) fall back to `updatedAt` DESC in\n * {@link rankResults} — between two objects named \"Notes\", the one touched last\n * is almost always the one wanted.\n *\n * Matching is case- and diacritic-insensitive via a per-UTF-16-unit fold that\n * PRESERVES STRING LENGTH, so the returned ranges index straight into the\n * ORIGINAL title for highlight rendering.\n */\n\n/** Half-open [start, end) span into the original title. */\nexport interface MatchRange {\n start: number;\n end: number;\n}\n\nexport interface TitleMatch {\n score: number;\n ranges: MatchRange[];\n}\n\n// Tier bases. Gaps (1000) exceed the max intra-tier penalty (≤900), keeping\n// tiers strictly ordered no matter the inputs.\nconst TIER_PREFIX = 4000;\nconst TIER_WORD = 3000;\nconst TIER_SUBSTRING = 2000;\nconst TIER_FUZZY = 1000;\n\n/**\n * Lowercase + strip diacritics WITHOUT changing length: each UTF-16 unit maps\n * to exactly one folded unit (NFD base char, first lowercase unit). Surrogate\n * halves pass through unchanged — they can't match an ASCII query, which is\n * exactly right for emoji-bearing titles.\n */\nexport function fold(s: string): string {\n let out = '';\n for (let i = 0; i < s.length; i++) {\n const base = s[i]!.normalize('NFD')[0]!;\n const lower = base.toLowerCase();\n // Some locale-specific lowercasings expand (e.g. 'İ' → 'i̇'); keep unit 0.\n out += lower.length === 1 ? lower : lower[0]!;\n }\n return out;\n}\n\n/** A word starts where the previous folded char is not alphanumeric. */\nexport function isWordStart(folded: string, i: number): boolean {\n if (i === 0) return true;\n return !/[a-z0-9]/.test(folded[i - 1]!);\n}\n\nconst startPenalty = (i: number) => Math.min(i * 8, 600);\nconst lengthPenalty = (titleLen: number, queryLen: number) => Math.min(Math.max(titleLen - queryLen, 0), 100);\n\n/**\n * Match one title against a query. Returns `null` for an empty query or a miss.\n * Ranges cover every highlighted span (one for contiguous tiers, several merged\n * runs for fuzzy).\n */\nexport function matchTitle(query: string, title: string): TitleMatch | null {\n const q = fold(query.trim());\n if (!q) return null;\n const t = fold(title);\n\n let first = -1;\n let wordAt = -1;\n for (let i = t.indexOf(q); i !== -1; i = t.indexOf(q, i + 1)) {\n if (first === -1) first = i;\n if (isWordStart(t, i)) {\n wordAt = i;\n break;\n }\n }\n\n if (first === 0) {\n return { score: TIER_PREFIX - lengthPenalty(t.length, q.length), ranges: [{ start: 0, end: q.length }] };\n }\n if (wordAt !== -1) {\n return {\n score: TIER_WORD - startPenalty(wordAt) - lengthPenalty(t.length, q.length),\n ranges: [{ start: wordAt, end: wordAt + q.length }],\n };\n }\n if (first !== -1) {\n return {\n score: TIER_SUBSTRING - startPenalty(first) - lengthPenalty(t.length, q.length),\n ranges: [{ start: first, end: first + q.length }],\n };\n }\n\n // Fuzzy subsequence (greedy left-to-right). Whitespace in the query is\n // skipped so \"new pg\" can still reach \"New page\". Adjacent hits merge into\n // one range so the highlight reads as runs, not confetti.\n const chars = q.replace(/\\s+/g, '');\n if (!chars) return null;\n const ranges: MatchRange[] = [];\n let from = 0;\n for (let ci = 0; ci < chars.length; ci++) {\n const at = t.indexOf(chars[ci]!, from);\n if (at === -1) return null;\n const last = ranges[ranges.length - 1];\n if (last && last.end === at) last.end = at + 1;\n else ranges.push({ start: at, end: at + 1 });\n from = at + 1;\n }\n const firstHit = ranges[0]!.start;\n const spread = ranges[ranges.length - 1]!.end - firstHit - chars.length;\n const score = TIER_FUZZY - Math.min(spread * 8, 600) - Math.min(firstHit * 2, 200) - lengthPenalty(t.length, chars.length);\n return { score, ranges };\n}\n\nexport interface RankedResult<T> {\n item: T;\n score: number;\n ranges: MatchRange[];\n}\n\n/**\n * Rank a candidate list against a query: score every title, drop misses, sort\n * by score DESC then `updatedAt` DESC (recency breaks ties), cap at `limit`.\n */\nexport function rankResults<T extends { title: string; updatedAt: number }>(\n query: string,\n items: readonly T[],\n limit = 50,\n): RankedResult<T>[] {\n const out: RankedResult<T>[] = [];\n for (const item of items) {\n const m = matchTitle(query, item.title);\n if (m) out.push({ item, score: m.score, ranges: m.ranges });\n }\n out.sort((a, b) => b.score - a.score || b.item.updatedAt - a.item.updatedAt);\n return out.slice(0, limit);\n}\n","/**\n * Single dispatch point for live-sync events from a global SSE connection.\n *\n * When a server-sent event arrives, the unread/notification layer calls\n * `dispatchDocChange(docPath)`:\n * - if a hook has registered a pull for that path → call it (the user is\n * actively viewing that doc) and return `true` — the caller skips the\n * unread bump.\n * - otherwise return `false` → the caller bumps unread.\n *\n * Hooks register/unregister via `registerPull`. SSE connection health is\n * broadcast via `emitSseStatus` so hooks can gate their fallback polling.\n *\n * Call `clearLiveSyncBus()` on account switch to flush all registrations.\n */\n\ntype PullFn = () => void;\ntype StatusListener = (up: boolean) => void;\n\nconst pullRegistry = new Map<string, PullFn>();\nconst statusListeners = new Set<StatusListener>();\nlet sseUp = false;\n\n/**\n * Register a pull function keyed by `docPath`. Returns an unsubscribe\n * function — call it when the hook unmounts.\n */\nexport function registerPull(docPath: string, fn: PullFn): () => void {\n pullRegistry.set(docPath, fn);\n return () => {\n if (pullRegistry.get(docPath) === fn) pullRegistry.delete(docPath);\n };\n}\n\n/**\n * Dispatch a doc-change event. If a pull is registered for `docPath`, calls\n * it and returns `true`. Returns `false` if no listener is registered\n * (the caller should bump unread).\n */\nexport function dispatchDocChange(docPath: string): boolean {\n const pull = pullRegistry.get(docPath);\n if (!pull) return false;\n pull();\n return true;\n}\n\n/** Broadcast the current SSE health to all subscribers. */\nexport function emitSseStatus(up: boolean): void {\n sseUp = up;\n for (const l of statusListeners) l(up);\n}\n\n/**\n * Subscribe to SSE health changes. Fires immediately with the current state.\n * Returns an unsubscribe function.\n */\nexport function onSseStatus(cb: StatusListener): () => void {\n statusListeners.add(cb);\n cb(sseUp);\n return () => statusListeners.delete(cb);\n}\n\n/**\n * Flush all registered doc pulls and reset SSE health. Call on account\n * switch. `statusListeners` are React subscriptions that self-unsubscribe on\n * unmount and are intentionally left intact.\n */\nexport function clearLiveSyncBus(): void {\n pullRegistry.clear();\n sseUp = false;\n}\n","/**\n * Parse an invite (pasted text or a `#fragment` deep link) into a preview the\n * join screen can show on a consent card — name, type, identifying fingerprint\n * — WITHOUT joining. Actual join calls (`joinSpaceByLink`, `joinNodeByLink`,\n * `acceptSpaceInvite`, `acceptNodeInvite`) only run after the user confirms.\n *\n * Accepts three input forms:\n * - A space invite link (URL fragment encoded by `createSpaceInviteLink`)\n * - A node invite link (URL fragment encoded by `createNodeInviteLink`)\n * - A private member-bundle JSON minted by `inviteToSpace`\n */\nimport { decodeSpaceInviteLink } from '../spaces/members.js';\nimport { decodeNodeInviteLink } from '../spaces/nodes.js';\nimport type { SpaceInviteLinkToken } from '../spaces/members.js';\nimport type { NodeInviteLinkToken } from '../spaces/nodes.js';\n\nexport type { SpaceInviteLinkToken, NodeInviteLinkToken };\n\nexport type InvitePreview =\n | {\n kind: 'space-link';\n spaceName: string;\n /** True if the link grants write access, false for read-only. */\n write: boolean;\n token: SpaceInviteLinkToken;\n }\n | {\n kind: 'node-link';\n spaceName: string;\n /** The node's display name, absent for legacy tokens that omit it. */\n nodeTitle?: string;\n token: NodeInviteLinkToken;\n }\n | {\n kind: 'member-bundle';\n spaceName: string;\n spaceId: string;\n /** Short hex fingerprint of the issuing owner's signing key, or null if absent. */\n issuerKey: string | null;\n /** The raw cap-bundle JSON — pass verbatim to `acceptSpaceInvite` on consent. */\n inviteJson: string;\n };\n\n/** Shape of the private invite bundle minted by `inviteToSpace`. */\ninterface PrivateInviteShape {\n spaceId?: string;\n spaceName?: string;\n cap?: { kind?: string; iss?: string };\n}\n\n/**\n * Classify and decode an invite string into a typed {@link InvitePreview}.\n * Throws a human-readable `Error` on invalid input (safe to surface verbatim\n * in a toast or inline error message).\n */\nexport function previewInvite(raw: string): InvitePreview {\n const text = raw.trim();\n if (!text) throw new Error('Paste an invite link or code first.');\n\n // Invite links carry their credential in a URL fragment.\n if (text.includes('#')) {\n const fragment = text.slice(text.indexOf('#'));\n // Try node-invite link first — it has a `nodeId` field the space link lacks.\n try {\n const token = decodeNodeInviteLink(fragment);\n return {\n kind: 'node-link',\n spaceName: `space-${token.spaceId.slice(-6)}`,\n nodeTitle: token.nodeName,\n token,\n };\n } catch {\n // fall through to space link\n }\n try {\n const token = decodeSpaceInviteLink(fragment);\n return { kind: 'space-link', spaceName: token.spaceName, write: token.write, token };\n } catch {\n throw new Error('That invite link appears to be invalid or expired.');\n }\n }\n\n // Private member-bundle JSON minted by `inviteToSpace`.\n let parsed: PrivateInviteShape;\n try {\n parsed = JSON.parse(text) as PrivateInviteShape;\n } catch {\n throw new Error(\"That doesn't look like an invite. Paste the full invite code or link.\");\n }\n if (!parsed?.spaceId || parsed.cap?.kind !== 'member') {\n throw new Error('That is not a valid space invite.');\n }\n const iss = parsed.cap?.iss;\n return {\n kind: 'member-bundle',\n spaceName: parsed.spaceName?.trim() || `space-${parsed.spaceId.slice(-6)}`,\n spaceId: parsed.spaceId,\n issuerKey: typeof iss === 'string' && iss.length >= 8 ? `${iss.slice(0, 8)}…${iss.slice(-8)}` : null,\n inviteJson: text,\n };\n}\n"],"mappings":";;;;;;;;;;;AAyCO,SAAS,oBAAoB,QAAgC;AAIlE,MAAI,eAAe,UAAU,CAAC,OAAO,eAAe;AAClD,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,MAAM,OAAO,iBAAiB,IAAI,KAAK;AAC7C,MAAI,OAAO,MAAM,CAAC,mBAAmB,KAAK,EAAE,GAAG;AAC7C,UAAM,IAAI,MAAM,4EAA4E,EAAE,GAAG;AAAA,EACnG;AACA,QAAM,YAAY,OAAO,yBAAyB,IAAI,KAAK;AAC3D,MAAI,aAAa,MAAM,CAAC,mBAAmB,KAAK,QAAQ,GAAG;AACzD,UAAM,IAAI,MAAM,oFAAoF,QAAQ,GAAG;AAAA,EACjH;AACA,QAAM;AAAA,IACJ,GAAG;AAAA,IACH,eAAe,MAAM;AAAA,IACrB,uBAAuB,YAAY;AAAA,EACrC;AACF;AAEA,SAAS,MAAwB;AAC/B,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,8EAAyE;AACnG,SAAO;AACT;AApEA,IAsCI,KAiCS,aAEA,kBAEA,eAKA,0BAMA;AAtFb;AAAA;AAAA;AAsCA,IAAI,MAA+B;AAiC5B,IAAM,cAAc,MAAc,IAAI,EAAE;AAExC,IAAM,mBAAmB,MAA0B,IAAI,EAAE;AAEzD,IAAM,gBAAgB,MAAc;AACzC,YAAM,KAAK,IAAI,EAAE;AACjB,aAAO,KAAK,OAAO,EAAE,KAAK;AAAA,IAC5B;AAEO,IAAM,2BAA2B,MAA0B,KAAK;AAMhE,IAAM,uBAAuB,MAAgC,KAAK;AAAA;AAAA;;;AClElE,SAAS,YAAY,SAA0B;AACpD,OAAK;AACP;AAGO,SAAS,QAAmB;AACjC,MAAI,CAAC,GAAI,OAAM,IAAI,MAAM,sEAAiE;AAC1F,SAAO;AACT;AA5BA,IAiBI,IAcS,OACA,OACA;AAjCb;AAAA;AAAA;AAiBA,IAAI,KAAuB;AAcpB,IAAM,QAAQ,CAACA,SAAwC,MAAM,EAAE,IAAIA,IAAG;AACtE,IAAM,QAAQ,CAACA,MAAa,UAAiC,MAAM,EAAE,IAAIA,MAAK,KAAK;AACnF,IAAM,WAAW,CAACA,SAA+B,MAAM,EAAE,OAAOA,IAAG;AAAA;AAAA;;;AC3BnE,SAAS,WAAmB;AACjC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,aAAW,OAAO,gBAAgB,KAAK;AACvC,MAAI,IAAI;AACR,aAAW,KAAK,MAAO,MAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1D,SAAO;AACT;AAQO,SAAS,SAAS,MAAsB;AAC7C,SACE,KACG,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE,EACtB,MAAM,GAAG,EAAE,KAAK;AAEvB;AA5BA;AAAA;AAAA;AAAA;AAAA;;;ACuHO,SAAS,aAA0B;AACxC,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ,QAAQ,OAAO;AAAA,IAC7B,aAAa;AAAA,IACb,OAAO,CAAC,WAAW;AAAA,EACrB;AACF;AAQO,SAAS,iBAAiB,SAAiB,UAAgC;AAChF,QAAM,MAAqC,WAAW,CAAC,QAAQ,QAAQ,OAAO,IAAI,CAAC,QAAQ,MAAM;AACjG,SAAO;AAAA,IACL;AAAA,IACA,aAAa;AAAA,IACb,OAAO,CAAC,UAAU,OAAO,KAAK;AAAA,EAChC;AACF;AAOO,SAAS,gBAAgB,SAAiB,QAAgB,UAAgC;AAC/F,QAAM,MAAqC,WAAW,CAAC,QAAQ,QAAQ,OAAO,IAAI,CAAC,QAAQ,MAAM;AACjG,SAAO;AAAA,IACL;AAAA,IACA,aAAa,CAAC,QAAQ;AAAA,IACtB,OAAO,CAAC,UAAU,OAAO,cAAc,MAAM,KAAK;AAAA,EACpD;AACF;AAOO,SAAS,aAAa,QAA6B;AACxD,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ,QAAQ,OAAO;AAAA,IAC7B,aAAa,CAAC,WAAW,WAAW,UAAU,eAAe;AAAA,IAC7D,OAAO;AAAA,MACL,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,kBAAkB,QAA6B;AAC7D,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ,QAAQ,OAAO;AAAA,IAC7B,aAAa,CAAC,GAAG,oBAAoB,WAAW,WAAW,UAAU,eAAe;AAAA,IACpF,OAAO;AAAA,MACL;AAAA,MACA,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AACF;AAeO,SAAS,WAAW,GAAuB;AAChD,MAAI,IAAI;AACR,aAAW,KAAK,EAAG,MAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AACtD,SAAO;AACT;AAGA,eAAsB,gBAAgB,UAAmC;AACvE,QAAM,QAAQ,IAAI,WAAW,SAAS,SAAS,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,IAAK,OAAM,CAAC,IAAI,SAAS,SAAS,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAC/F,QAAM,SAAS,MAAM,WAAW,OAAO,OAAO,OAAO,WAAW,KAAK;AACrE,SAAO,WAAW,IAAI,WAAW,MAAM,CAAC,EAAE,MAAM,GAAG,EAAE;AACvD;AAzNA,IAoBM,MACA,MAGO,mBAKA,aACA,aACA,aAIA,gBAEA,gBACA,gBAGA,aACA,aAEA,YACA,YAEA,iBACA,iBAOA,cACA,cACA,cAWA,YACA,YACA,YAEA,YACA,YACA,YAGA,gBACA,gBACA,gBAKA,YACA,YACA,YAMA,YACA,YACA,YAGA,gBACA,gBACA,gBAKA,eACA,eAOA;AAhHb;AAAA;AAAA;AAoBA,IAAM,OAAO,CAAC,SAAiB,SAAS,IAAI;AAC5C,IAAM,OAAO,CAAC,SAAiB,SAAS,IAAI;AAGrC,IAAM,oBAAoB,CAAC,WAAmB,OAAO,MAAM,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAKpF,IAAM,cAAc,CAAC,YAAoB,UAAU,OAAO;AAC1D,IAAM,cAAc,CAAC,YAAoB,KAAK,GAAG,YAAY,OAAO,CAAC,WAAW;AAChF,IAAM,cAAc,CAAC,YAAoB,KAAK,GAAG,YAAY,OAAO,CAAC,WAAW;AAIhF,IAAM,iBAAiB,CAAC,QAAgB,WAC7C,UAAU,kBAAkB,MAAM,CAAC,gBAAgB,MAAM,IAAI,MAAM;AAC9D,IAAM,iBAAiB,CAAC,QAAgB,WAAmB,KAAK,eAAe,QAAQ,MAAM,CAAC;AAC9F,IAAM,iBAAiB,CAAC,QAAgB,WAAmB,KAAK,eAAe,QAAQ,MAAM,CAAC;AAG9F,IAAM,cAAc,CAAC,WAAmB,KAAK,QAAQ,MAAM,UAAU;AACrE,IAAM,cAAc,CAAC,WAAmB,KAAK,QAAQ,MAAM,UAAU;AAErE,IAAM,aAAa,CAAC,WAAmB,KAAK,QAAQ,MAAM,UAAU;AACpE,IAAM,aAAa,CAAC,WAAmB,KAAK,QAAQ,MAAM,UAAU;AAEpE,IAAM,kBAAkB,CAAC,YAAoB,KAAK,UAAU,OAAO,UAAU;AAC7E,IAAM,kBAAkB,CAAC,YAAoB,KAAK,UAAU,OAAO,UAAU;AAO7E,IAAM,eAAe,CAAC,YAAoB,UAAU,OAAO;AAC3D,IAAM,eAAe,CAAC,YAAoB,KAAK,aAAa,OAAO,CAAC;AACpE,IAAM,eAAe,CAAC,YAAoB,KAAK,aAAa,OAAO,CAAC;AAWpE,IAAM,aAAa,CAAC,SAAiB,aAAqB,UAAU,OAAO,iBAAiB,QAAQ;AACpG,IAAM,aAAa,CAAC,SAAiB,aAAqB,KAAK,WAAW,SAAS,QAAQ,CAAC;AAC5F,IAAM,aAAa,CAAC,SAAiB,aAAqB,KAAK,WAAW,SAAS,QAAQ,CAAC;AAE5F,IAAM,aAAa,CAAC,SAAiB,aAAqB,UAAU,OAAO,iBAAiB,QAAQ;AACpG,IAAM,aAAa,CAAC,SAAiB,aAAqB,KAAK,WAAW,SAAS,QAAQ,CAAC;AAC5F,IAAM,aAAa,CAAC,SAAiB,aAAqB,KAAK,WAAW,SAAS,QAAQ,CAAC;AAG5F,IAAM,iBAAiB,CAAC,SAAiB,WAAmB,UAAU,OAAO,kBAAkB,MAAM;AACrG,IAAM,iBAAiB,CAAC,SAAiB,WAAmB,KAAK,eAAe,SAAS,MAAM,CAAC;AAChG,IAAM,iBAAiB,CAAC,SAAiB,WAAmB,KAAK,eAAe,SAAS,MAAM,CAAC;AAKhG,IAAM,aAAa,CAAC,SAAiB,WAAmB,UAAU,OAAO,gBAAgB,MAAM;AAC/F,IAAM,aAAa,CAAC,SAAiB,WAAmB,KAAK,WAAW,SAAS,MAAM,CAAC;AACxF,IAAM,aAAa,CAAC,SAAiB,WAAmB,KAAK,WAAW,SAAS,MAAM,CAAC;AAMxF,IAAM,aAAa,CAAC,SAAiB,WAAmB,UAAU,OAAO,cAAc,MAAM;AAC7F,IAAM,aAAa,CAAC,SAAiB,WAAmB,KAAK,WAAW,SAAS,MAAM,CAAC;AACxF,IAAM,aAAa,CAAC,SAAiB,WAAmB,KAAK,WAAW,SAAS,MAAM,CAAC;AAGxF,IAAM,iBAAiB,CAAC,YAAoB,UAAU,OAAO;AAC7D,IAAM,iBAAiB,CAAC,YAAoB,KAAK,eAAe,OAAO,CAAC;AACxE,IAAM,iBAAiB,CAAC,YAAoB,KAAK,eAAe,OAAO,CAAC;AAKxE,IAAM,gBAAgB,CAAC,QAAgB,aAAa,kBAAkB,KAAK;AAC3E,IAAM,gBAAgB,CAAC,QAAgB,aAAa,KAAK,cAAc,KAAK,CAAC;AAO7E,IAAM,qBAA+B;AAAA,MAC1C;AAAA,MAAgB;AAAA,MAAY;AAAA,MAAU;AAAA,MAAW;AAAA,MAAU;AAAA,MAAW;AAAA,MAAa;AAAA,IACrF;AAAA;AAAA;;;ACvGO,SAAS,iBAAiB,YAAY,oBAAkC;AAC7E,SAAO,CAAC,OAAO,SAAS;AACtB,UAAM,OAAO,IAAI,gBAAgB;AACjC,UAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,SAAS;AACtD,UAAM,SAAS,MAAM;AACrB,QAAI,QAAQ;AACV,UAAI,OAAO,QAAS,MAAK,MAAM;AAAA,UAC1B,QAAO,iBAAiB,SAAS,MAAM,KAAK,MAAM,GAAG,EAAE,MAAM,KAAK,CAAC;AAAA,IAC1E;AACA,WAAO,MAAM,OAAsB,EAAE,GAAG,MAAM,QAAQ,KAAK,OAAO,CAAC,EAAE,QAAQ,MAAM,aAAa,KAAK,CAAC;AAAA,EACxG;AACF;AAtBA,IASa;AATb;AAAA;AAAA;AASO,IAAM,qBAAqB;AAAA;AAAA;;;ACkB3B,SAAS,YAAuB;AACrC,SAAQ,oBAAW;AAAA,IACjB,KAAK,CAACC,SAAQ,MAAM,SAASA,IAAG;AAAA,IAChC,KAAK,CAACA,MAAK,UAAU,MAAM,SAASA,MAAK,KAAK;AAAA,EAChD;AACF;AAhCA,IAgBM,QAMO,uBAET;AAxBJ;AAAA;AAAA;AAcA;AAEA,IAAM,SAAS;AAMR,IAAM,wBAAwB,KAAK,KAAK,KAAK,KAAK;AAAA;AAAA;;;ACRlD,SAAS,aAAa,QAAgB,SAA8B;AACzE,OAAK,MAAM,IAAI,MAAM,GAAG,KAAK,UAAU,OAAO,CAAC,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AACjE;AAGA,eAAsB,kBAAkB,QAA+C;AACrF,MAAI;AACF,UAAM,MAAM,MAAM,MAAM,IAAI,MAAM,CAAC;AACnC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,IAAI,KAAK,MAAM,GAAG;AACxB,WAAO;AAAA,MACL,QAAQ,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AAAA,MAClD,QAAQ,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AAAA,MAClD,OAAO,OAAO,EAAE,UAAU,WAAW,EAAE,QAAQ;AAAA,MAC/C,QAAQ,OAAO,EAAE,WAAW,WAAW,EAAE,SAAS;AAAA,IACpD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAjCA,IAWM;AAXN;AAAA;AAAA;AAQA;AAGA,IAAM,MAAM,CAAC,WAAmB,yBAAyB,MAAM;AAAA;AAAA;;;ACX/D,IAOa;AAPb;AAAA;AAAA;AAOO,IAAM,mBAAN,cAA+B,MAAM;AAAA,MAC1C,YAAY,SAAiB;AAC3B,cAAM,OAAO;AACb,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA;AAAA;;;ACTA,SAAS,sBAAsB;AAE/B,SAAS,eAAe,8BAA8B;AAEtD,SAAS,aAAa,uBAAuB;AAiBtC,SAAS,eAAe,KAAc,cAA2C;AACtF,SAAO;AAAA,IACL,MAAM,SAAS;AACb,aAAO,EAAE,KAAmB,aAAa;AAAA,IAC3C;AAAA,EACF;AACF;AAMO,SAAS,WAAW,KAAc,cAAsB,mBAA4C;AACzG,SAAO,IAAI,eAAe;AAAA,IACxB,SAAS,YAAY;AAAA,IACrB,WAAW,qBAAqB,iBAAiB;AAAA,IACjD,aAAa,eAAe,KAAK,YAAY;AAAA,IAC7C,OAAO,iBAAiB;AAAA,IACxB,OAAO,UAAU;AAAA,IACjB,eAAe;AAAA,IACf,uBAAuB,CAAC,KAAK,KAAK,KAAK,KAAK,GAAG;AAAA,IAC/C,eAAe,MAAM,qBAAqB,IAAI;AAAA,EAChD,CAAC;AACH;AAUA,eAAsB,cACpB,QACA,MACA,iBACA,eACoB;AACpB,QAAM,MAAM,MAAM,OAAO,KAAK,eAAe,EAAE,MAAM,MAAM;AACzD,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE,CAAC;AACD,QAAM,UAAU,KAAK;AACrB,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAQ;AAC/B,UAAM,IAAI,iBAAiB,uEAAkE;AAAA,EAC/F;AACA,MAAI;AACF,UAAM,MAAM,MAAM;AAAA,MAChB;AAAA,MACA,EAAE,WAAW,KAAK,QAAQ,YAAY,KAAK,QAAQ;AAAA,MACnD,EAAE,cAAc;AAAA,IAClB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,UAAM,IAAI,iBAAiB,uFAAkF;AAAA,EAC/G;AACF;AAGA,eAAsB,eACpB,QACA,MACA,iBACA,eAC2B;AAC3B,MAAI;AACF,WAAO,MAAM,cAAc,QAAQ,MAAM,iBAAiB,aAAa;AAAA,EACzE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQA,eAAsB,mBACpB,QACA,MACA,iBACA,iBACA,gBAA0B,CAAC,KAAK,KAAK,GACjB;AACpB,QAAM,QAAQ,MAAM,OAAO,KAAK,eAAe,EAAE,MAAM,MAAM,IAAI;AACjE,MAAI,UAAU,OAAO;AACrB,MAAI,CAAC,WAAW,CAAC,QAAQ,QAAQ;AAC/B,UAAM,UAAU,MAAM,cAAc,EAAE,WAAW,KAAK,QAAQ,UAAU,KAAK,MAAM,GAAG;AAAA,MACpF,EAAE,WAAW,KAAK,OAAO;AAAA,IAC3B,CAAC;AACD,cAAU,QAAQ;AAClB,UAAM,OAAO,KAAK,iBAAiB,SAA+C,OAAO,QAAQ,IAAI;AAAA,EACvG;AACA,QAAM,MAAM,MAAM;AAAA,IAChB;AAAA,IACA,EAAE,WAAW,KAAK,QAAQ,YAAY,KAAK,QAAQ;AAAA,IACnD,EAAE,cAAc;AAAA,EAClB;AACA,SAAO;AACT;AAWA,eAAsB,YAAY,QAAwC;AACxE,MAAI;AACF,UAAM,IAAI,MAAM,iBAAiB,EAAE,GAAG,YAAY,CAAC,GAAG,cAAc,CAAC,GAAG,YAAY,MAAM,CAAC,EAAE;AAC7F,QAAI,CAAC,EAAE,GAAI,QAAO,EAAE,QAAQ,MAAM,QAAQ,MAAM,OAAO,MAAM,QAAQ,KAAK;AAC1E,UAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,UAAM,OAAO,MAAM;AACnB,UAAM,UAAyB;AAAA,MAC7B,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,MACzD,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,MACzD,OAAO,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAAA,MACtD,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,IAC3D;AACA,iBAAa,QAAQ,OAAO;AAC5B,WAAO;AAAA,EACT,QAAQ;AACN,WAAQ,MAAM,kBAAkB,MAAM,KAAM,EAAE,QAAQ,MAAM,QAAQ,MAAM,OAAO,MAAM,QAAQ,KAAK;AAAA,EACtG;AACF;AAGA,eAAsB,WAAW,QAAwC;AACvE,UAAQ,MAAM,YAAY,MAAM,GAAG;AACrC;AAGA,SAAS,wBAAwC;AAC/C,MAAI,CAAC,oBAAoB;AACvB,yBAAqB,IAAI,eAAe,EAAE,SAAS,YAAY,GAAG,WAAW,iBAAiB,GAAG,OAAO,iBAAiB,EAAE,CAAC;AAAA,EAC9H;AACA,SAAO;AACT;AAOA,eAAsB,aAAa,KAAoD;AACrF,QAAM,MAAM,oBAAI,IAA2B;AAC3C,QAAM,SAAS,sBAAsB;AACrC,WAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK,qBAAqB;AACxD,UAAM,QAAQ,IAAI,MAAM,GAAG,IAAI,mBAAmB;AAClD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,OAAO,cAAc,WAAW,MAAM,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,EAAE,CAAC;AAAA,IACvF,QAAQ;AACN,iBAAW,MAAM,OAAO;AACtB,cAAM,SAAS,MAAM,kBAAkB,EAAE;AACzC,YAAI,OAAQ,KAAI,IAAI,IAAI,MAAM;AAAA,MAChC;AACA;AAAA,IACF;AACA,UAAM,QAAQ,CAAC,IAAI,MAAM;AACvB,YAAM,QAAQ,QAAQ,CAAC;AACvB,UAAI,CAAC,SAAS,MAAM,MAAO;AAC3B,YAAM,OAAQ,MAAM,QAAQ;AAC5B,YAAM,UAAyB;AAAA,QAC7B,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,QACzD,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,QACzD,OAAO,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAAA,QACtD,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS;AAAA,MAC3D;AACA,mBAAa,IAAI,OAAO;AACxB,UAAI,IAAI,IAAI,OAAO;AAAA,IACrB,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAKA,eAAsB,aACpB,QACA,QACA,OACe;AACf,QAAM,UAAU,MAAM,OAAO,KAAK,YAAY,MAAM,CAAC,EAAE,MAAM,MAAM,IAAI;AACvE,QAAM,OAAQ,SAAS,QAAgD,CAAC;AACxE,QAAM,OAAgC,EAAE,GAAG,MAAM,GAAG,OAAO,GAAG,EAAE;AAChE,MAAI,KAAK,UAAU,KAAM,QAAO,KAAK;AACrC,QAAM,OAAO,KAAK,YAAY,MAAM,GAAG,MAAM,SAAS,QAAQ,IAAI;AACpE;AAGA,eAAsB,YAAY,QAAwB,QAAgB,QAA+B;AACvG,QAAM,aAAa,QAAQ,QAAQ,EAAE,OAAO,CAAC;AAC/C;AAMA,eAAsB,kBACpB,QACA,QACA,MACe;AACf,MAAI,kBAAkB;AACtB,MAAI;AACF,UAAM,IAAI,MAAM,iBAAiB,EAAE,GAAG,YAAY,CAAC,GAAG,cAAc,CAAC,GAAG,YAAY,MAAM,CAAC,EAAE;AAC7F,QAAI,EAAE,WAAW,IAAK,mBAAkB;AAAA,aAC/B,EAAE,IAAI;AACb,YAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,YAAM,OAAO,MAAM;AACnB,wBAAkB,EAAE,OAAO,MAAM,UAAU,YAAY,OAAO,MAAM,WAAW;AAAA,IACjF,MAAO;AAAA,EACT,QAAQ;AACN;AAAA,EACF;AACA,MAAI,CAAC,gBAAiB;AACtB,QAAM,aAAa,QAAQ,QAAQ,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO,CAAC;AAC/E;AAKA,eAAsB,iBACpB,KACA,cACA,QACA,cACiC;AACjC,MAAI,OAAO;AACX,MAAI;AACF,WAAO,IAAI,IAAI,YAAY,CAAC,EAAE;AAAA,EAChC,QAAQ;AAAA,EAAsB;AAE9B,QAAM,EAAE,KAAK,IAAI,MAAM,IAAI,MAAM;AAAA,IAC/B,EAAE,QAAkC,cAAc,KAAK;AAAA,IACvD;AAAA,EACF;AAEA,QAAM,UAAU,gBAAgB,GAA8B;AAC9D,QAAM,SACJ,OAAO,SAAS,aACZ,KAAK,OAAO,IACZ,OAAO,KAAK,SAAS,OAAO,EAAE,SAAS,QAAQ;AAErD,SAAO;AAAA,IACL,eAAe,OAAO,MAAM;AAAA,IAC5B,kBAAkB;AAAA,IAClB,iBAAiB,OAAO,EAAE;AAAA,IAC1B,oBAAoB;AAAA,EACtB;AACF;AAEA,eAAe,cAAc,QAAmE;AAC9F,MAAI;AACF,UAAM,IAAI,MAAM,iBAAiB,EAAE,GAAG,YAAY,CAAC,GAAG,cAAc,CAAC,GAAG,YAAY,MAAM,CAAC,EAAE;AAC7F,QAAI,EAAE,WAAW,IAAK,QAAO,EAAE,MAAM,MAAM,QAAQ,KAAK;AACxD,QAAI,CAAC,EAAE,GAAI,QAAO,EAAE,MAAM,OAAO,QAAQ,KAAK;AAC9C,UAAM,OAAO,MAAM,EAAE,KAAK;AAC1B,UAAM,OAAO,MAAM;AACnB,WAAO,EAAE,MAAM,MAAM,QAAQ,OAAO,MAAM,WAAW,WAAW,KAAK,SAAS,KAAK;AAAA,EACrF,QAAQ;AACN,WAAO,EAAE,MAAM,OAAO,QAAQ,KAAK;AAAA,EACrC;AACF;AAMA,eAAsB,aAAa,QAAwB,QAAgB,UAAmC;AAC5G,QAAM,EAAE,MAAM,OAAO,IAAI,MAAM,cAAc,MAAM;AACnD,MAAI,UAAU,OAAO,KAAK,EAAG,QAAO;AACpC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,aAAa,QAAQ,QAAQ,EAAE,QAAQ,SAAS,CAAC;AACvD,SAAO;AACT;AAjTA,IA+JI,oBAQE;AAvKN;AAAA;AAAA;AAUA;AACA;AACA;AACA;AACA;AACA;AAwJA,IAAM,sBAAsB;AAAA;AAAA;;;AClK5B,SAAS,kBAAkB,wBAAwB;AACnD,SAAS,gBAAgB;AACzB,SAAS,uBAAuB,qBAAqB;AAiD9C,SAAS,mBAAmB,SAA4B;AAC7D,SAAO,QAAQ,eAAe,QAAQ,KAAK,QACvC,CAAC,QAAQ,KAAK,KAAK,IACnB,CAAC,QAAQ,YAAY,QAAQ,KAAK,KAAK;AAC7C;AAGO,SAAS,oBAA8B;AAC5C,SAAO,iBAAiB,UAAU,GAAG,EAAE,MAAM,GAAG;AAClD;AAEO,SAAS,YAAY,OAA0B;AACpD,SAAO,iBAAiB,MAAM,KAAK,GAAG,EAAE,KAAK,GAAG,QAAQ;AAC1D;AAGO,SAAS,sBAAsB,QAAwB;AAC5D,QAAM,IAAI,OAAO,QAAQ,eAAe,EAAE,EAAE,YAAY;AACxD,SAAO,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK;AAClF;AAMA,eAAsB,aAAa,EAAE,QAAQ,KAAK,GAAoB,MAAiC;AACrG,QAAM,WAAW,QAAQ,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,QAAQ,OAAO,MAAM,GAAG,CAAC,CAAC;AAC/E,QAAM,MAAM,EAAE,UAAU,KAAK,OAAO,WAAW,KAAK,OAAO;AAC3D,QAAM,UAAU,MAAM,cAAc,KAAK,QAAQ,KAAK,OAAO,KAAK,WAAW,CAAC;AAC9E,QAAM,aAAa,MAAM,cAAc,KAAK,QAAQ,KAAK,OAAO,KAAK,aAAa,MAAM,CAAC;AACzF,QAAM,aAAa,WAAW,SAAS,KAAK,MAAM;AAClD,QAAM,gBAAgB,WAAW,YAAY,KAAK,MAAM;AAExD,QAAM,WAAW,yBAAyB;AAC1C,QAAM,uBAAuB,WAAW,WAAW,YAAY,KAAK,QAAQ,QAAQ,IAAI;AACxF,QAAM,sBAAsB,WAAW,WAAW,SAAS,KAAK,QAAQ,QAAQ,IAAI;AAEpF,QAAM,cAAc,MAAM,aAAa,eAAe,QAAQ,QAAQ,EAAE,MAAM,MAAM,QAAQ;AAC5F,OAAK,kBAAkB,eAAe,QAAQ,IAAI,EAAE,MAAM,MAAM;AAAA,EAAC,CAAC;AAClE,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,sBAAsB,MAAM;AAAA,IACzC,YAAY,KAAK;AAAA,EACnB;AACF;AAcA,eAAsB,mBAAmB,EAAE,QAAQ,MAAM,QAAQ,GAAmB,MAAiC;AACnH,QAAM,WAAW,QAAQ,KAAK,KAAK,IAAI,KAAK,KAAK,IAAI,QAAQ,OAAO,MAAM,GAAG,CAAC,CAAC;AAC/E,QAAM,aAAa,WAAW,SAAS,KAAK,MAAM;AAClD,QAAM,gBAAgB,WAAW,SAAS,KAAK,MAAM;AAErD,QAAM,WAAW,yBAAyB;AAC1C,QAAM,uBAAuB,WAAW,WAAW,SAAS,KAAK,QAAQ,QAAQ,IAAI;AACrF,QAAM,sBAAsB,WAAW,WAAW,SAAS,KAAK,QAAQ,QAAQ,IAAI;AAEpF,QAAM,cAAc,MAAM,aAAa,eAAe,QAAQ,QAAQ,EAAE,MAAM,MAAM,QAAQ;AAC5F,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN;AAAA,IACA,SAAS;AAAA,IACT,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAa,sBAAsB,MAAM;AAAA,IACzC,YAAY,QAAQ;AAAA,EACtB;AACF;AAGA,eAAsB,cAAc,WAAqB,MAAiC;AACxF,QAAM,aAAa,UAAU,KAAK,GAAG,EAAE,KAAK;AAC5C,QAAM,QAAQ,MAAM,sBAAsB,UAAU;AACpD,SAAO,aAAa,EAAE,QAAQ,MAAM,QAAQ,MAAM,MAAM,OAAqB,GAAG,IAAI;AACtF;AAGO,SAAS,eAAe,GAA6B;AAC1D,SAAO,EAAE,QAAQ,EAAE,QAAQ,MAAM,EAAE,KAAK;AAC1C;AA7JA;AAAA;AAAA;AAWA;AACA;AACA;AAAA;AAAA;;;ACyBA,eAAsB,wBACpB,QACA,YACA,kBACe;AACf,QAAMC,OAAM,OAAO,MAAM;AACzB,MAAI,cAAcA,KAAK;AACvB,cAAYA;AACZ,UAAQ,CAAC;AACT,QAAM,MAAM,MAAM,MAAMA,IAAG;AAC3B,MAAI,KAAK;AACP,QAAI;AACF,cAAQ,KAAK,MAAM,GAAG;AAAA,IACxB,SAAS,GAAG;AACV,cAAQ,MAAM,8DAA8D,CAAC;AAC7E,cAAQ,CAAC;AAAA,IACX;AAAA,EACF;AACA,MAAI,UAAU;AACd,aAAW,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,UAAU,GAAG;AAC3D,UAAM,OAAO,IAAI,EAAE,MAAM,UAAU,KAAK,QAAQ;AAChD,cAAU;AAAA,EACZ;AACA,aAAW,CAAC,SAAS,MAAM,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AAChE,UAAM,OAAO,IAAI,EAAE,MAAM,QAAQ,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,OAAO,OAAO,MAAM;AACvF,cAAU;AAAA,EACZ;AACA,MAAI,QAAS,OAAM,MAAMA,MAAK,KAAK,UAAU,KAAK,CAAC;AACrD;AAEA,SAAS,UAAgB;AACvB,MAAI,UAAW,MAAK,MAAM,WAAW,KAAK,UAAU,KAAK,CAAC;AAC5D;AAEO,SAAS,oBAAoB,SAA0C;AAC5E,SAAO,MAAM,OAAO,KAAK;AAC3B;AAEO,SAAS,qBAAqB,SAAiB,OAA+B;AACnF,UAAQ,EAAE,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM;AACrC,UAAQ;AACV;AAGO,SAAS,uBAAuB,SAAuB;AAC5D,MAAI,EAAE,WAAW,OAAQ;AACzB,QAAM,OAAO,EAAE,GAAG,MAAM;AACxB,SAAO,KAAK,OAAO;AACnB,UAAQ;AACR,UAAQ;AACV;AAKO,SAAS,mBAAmB,SAAiB,QAAyC;AAC3F,SAAO,MAAM,GAAG,OAAO,IAAI,MAAM,EAAE,KAAK;AAC1C;AAGO,SAAS,oBAAoB,SAAiB,QAAgB,OAA+B;AAClG,uBAAqB,GAAG,OAAO,IAAI,MAAM,IAAI,KAAK;AACpD;AAGO,SAAS,sBAAsB,SAAiB,QAAsB;AAC3E,yBAAuB,GAAG,OAAO,IAAI,MAAM,EAAE;AAC/C;AAIO,SAAS,0BAA0C;AACxD,SAAO;AACT;AAGO,SAAS,sBAA8B;AAC5C,QAAM,MAAc,CAAC;AACrB,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAG,KAAI,EAAE,SAAS,SAAU,KAAI,EAAE,IAAI,EAAE;AAClF,SAAO;AACT;AAGO,SAAS,sBAAqF;AACnG,QAAM,MAAqE,CAAC;AAC5E,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC3C,QAAI,EAAE,SAAS,OAAQ,KAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM;AAAA,EAC5E;AACA,SAAO;AACT;AAGO,SAAS,wBAA8B;AAC5C,UAAQ,CAAC;AACT,cAAY;AACd;AArIA,IAyBM,QAEF,OACA;AA5BJ;AAAA;AAAA;AAiBA;AAQA,IAAM,SAAS,CAAC,WAAmB,0BAA0B,MAAM;AAEnE,IAAI,QAAwB,CAAC;AAC7B,IAAI,YAA2B;AAAA;AAAA;;;ACiBxB,SAAS,uBAA6B;AAC3C,EAAAC,OAAM,MAAM;AACd;AAMO,SAAS,eAAe,SAAiB,SAAkC;AAChF,QAAM,QAAQ,oBAAoB,OAAO;AACzC,MAAI,OAAO,SAAS,OAAQ,QAAO,WAAW,MAAM,KAAK,MAAM,GAAG;AAClE,MAAI,OAAO,SAAS,UAAU;AAC5B,UAAM,MAAM,KAAK,MAAM,MAAM,GAAG;AAChC,WAAO,WAAW,KAAK,QAAQ,KAAK,MAAM;AAAA,EAC5C;AACA,SAAO,QAAQ;AACjB;AAUO,SAAS,cACd,SACA,QACA,MACA,SACA,KAC2B;AAC3B,QAAM,WAAW,GAAG,OAAO,IAAI,MAAM;AACrC,QAAM,MAAMA,OAAM,IAAI,QAAQ;AAC9B,MAAI,IAAK,QAAO;AAEhB,QAAM,KAAK,YAAuC;AAEhD,UAAM,YAAY,mBAAmB,SAAS,MAAM;AACpD,UAAM,aAAa,oBAAoB,OAAO;AAC9C,UAAM,cAAc,aAAa;AAGjC,QAAI;AACJ,QAAI;AACJ,QAAI,aAAa,SAAS,QAAQ;AAChC,eAAS,WAAW,YAAY,KAAK,YAAY,GAAG;AAAA,IACtD,WAAW,aAAa,SAAS,UAAU;AACzC,YAAM,MAAM,KAAK,MAAM,YAAY,GAAG;AACtC,eAAS,IAAI;AACb,eAAS,WAAW,KAAK,QAAQ,KAAK,MAAM;AAAA,IAC9C,OAAO;AACL,eAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,cACJ,OAAO,OAAO,IAAI,UAAU,QAAQ,SAAS,eAAe;AAG9D,QAAI,CAAC,KAAK,KAAK;AACb,aAAO,EAAE,WAAW,MAAM,QAAQ,YAAY;AAAA,IAChD;AAGA,UAAM,gBAAgB,YAAY,OAAO;AACzC,UAAM,gBAAgB,SAClB,CAAC,MAAM,IACP,KAAK,QACH,CAAC,IAAI,KAAK,IACV,mBAAmB,OAAO;AAEhC,QAAI,aAAa,SAAS,YAAY,aAAa,SAAS,QAAQ;AAClE,YAAMC,aAAY,MAAM,cAAc,QAAQ,QAAQ,MAAM,eAAe,aAAa;AACxF,aAAO,EAAE,WAAAA,YAAW,QAAQ,aAAa,MAAM;AAAA,IACjD;AAGA,UAAM,QAAQ,KAAK,SAAS;AAC5B,UAAM,UAAU,KAAK,WAAW,CAAC;AACjC,QAAI,UAAU,QAAQ,UAAU,QAAQ,QAAQ;AAC9C,YAAM,IAAI;AAAA,QACR,QAAQ,SAAS,QAAQ,MAAM,IAC3B,kHACA;AAAA,MACN;AAAA,IACF;AACA,UAAM,YAAY,MAAM;AAAA,MACtB,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,mBAAmB,OAAO;AAAA,IAC5B;AACA,WAAO,EAAE,WAAW,QAAQ,QAAQ,YAAY,aAAa,KAAK;AAAA,EACpE,GAAG;AAEH,EAAAD,OAAM,IAAI,UAAU,CAAC;AACrB,IAAE,MAAM,MAAMA,OAAM,OAAO,QAAQ,CAAC;AACpC,SAAO;AACT;AAMA,eAAsB,gBACpB,SACA,SACA,QACA,MACyE;AACzE,QAAM,YAAY,mBAAmB,SAAS,MAAM;AACpD,QAAM,aAAa,oBAAoB,OAAO;AAC9C,QAAM,cAAc,aAAa;AAEjC,MAAI;AACJ,MAAI,gBAAgB,mBAAmB,OAAO;AAE9C,MAAI,aAAa,SAAS,QAAQ;AAChC,aAAS,WAAW,YAAY,KAAK,YAAY,GAAG;AAAA,EACtD,WAAW,aAAa,SAAS,UAAU;AACzC,UAAM,MAAM,KAAK,MAAM,YAAY,GAAG;AACtC,aAAS,WAAW,KAAK,QAAQ,KAAK,MAAM;AAC5C,QAAI,IAAI,IAAK,iBAAgB,CAAC,IAAI,GAAG;AAAA,EACvC,OAAO;AACL,aAAS,QAAQ;AAAA,EACnB;AAEA,MAAI,CAAC,KAAK,IAAK,QAAO,EAAE,QAAQ,WAAW,KAAK;AAGhD,QAAM,gBAAgB,YAAY,OAAO;AACzC,QAAM,YAAY,MAAM,eAAe,QAAQ,QAAQ,MAAM,eAAe,aAAa;AACzF,SAAO,YAAY,EAAE,QAAQ,UAAU,IAAI;AAC7C;AApLA,IA0CMA;AA1CN;AAAA;AAAA;AAwBA;AAEA;AACA;AACA;AACA;AAaA,IAAMA,SAAQ,oBAAI,IAAuC;AAAA;AAAA;;;AChCzD,SAAS,qBAAqB;AAQ9B,SAAS,kBAAkB,MAA8B;AACvD,MAAI,KAAK,WAAW,UAAU;AAC5B,UAAM,EAAE,OAAO,IAAI,GAAG,KAAK,IAAI;AAC/B,WAAO,EAAE,GAAG,MAAM,OAAO,GAAG;AAAA,EAC9B;AACA,SAAO;AACT;AAOA,eAAsB,cACpB,QACA,SACA,QAAsB,CAAC,GACR;AACf,QAAM,MAAM,MAAM,OAAO,KAAK,aAAa,OAAO,CAAC,EAAE,MAAM,MAAM,IAAI;AACrE,QAAM,WAAW,KAAK;AACtB,MAAI,MAAM,QAAQ,UAAU,OAAO,EAAG;AACtC,QAAM,OAAO;AAAA,IACX,aAAa,OAAO;AAAA,IACpB,EAAE,GAAG,GAAG,SAAS,MAAM,IAAI,iBAAiB,GAAG,WAAW,KAAK,IAAI,EAAE;AAAA,IACrE,KAAK,QAAQ;AAAA,EACf;AACF;AAMA,eAAsB,qBACpB,SACA,SACA,QAAsB,CAAC,GACR;AACf,QAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,QAAM,cAAc,QAAQ,SAAS,KAAK;AAC5C;AASA,eAAsB,kBACpB,SACA,SACA,SACA,KACe;AACf,OAAK;AACL,QAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,QAAM,WAAW,aAAa,OAAO;AACrC,QAAM,WAAW,aAAa,OAAO;AACrC,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,MAAM,MAAM,OAAO,KAAK,QAAQ,EAAE,MAAM,MAAM,IAAI;AACxD,UAAM,MAAM,KAAK;AACjB,UAAM,MAAoB,MAAM,QAAS,KAA+B,OAAO,IAC1E,IAAkC,UACnC,CAAC;AACL,UAAM,OAAO,QAAQ,KAAK,KAAK,IAAI,CAAC;AACpC,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO;AAAA,QACX;AAAA,QACA,EAAE,GAAG,GAAG,SAAS,KAAK,IAAI,iBAAiB,GAAG,WAAW,KAAK,IAAI,EAAE;AAAA,QACpE,KAAK,QAAQ;AAAA,MACf;AACA;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAe,iBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAMA,eAAsB,eACpB,SACA,SACuB;AACvB,QAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,QAAM,MAAM,MAAM,OAAO,KAAK,aAAa,OAAO,CAAC,EAAE,MAAM,MAAM,IAAI;AACrE,QAAM,MAAM,KAAK;AACjB,SAAO,MAAM,QAAS,KAA+B,OAAO,IACvD,IAAkC,UACnC,CAAC;AACP;AAjHA;AAAA;AAAA;AAcA;AACA;AAAA;AAAA;;;ACfA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA,SAAS,iBAAAE,gBAAe,yBAAyB;AA2B1C,SAAS,YAAY,IAAkE;AAC5F,qBAAmB,IAAI,EAAE;AACzB,SAAO,MAAM;AAAE,uBAAmB,OAAO,EAAE;AAAA,EAAG;AAChD;AAEO,SAAS,mBAAmB,SAAiB,MAA6B;AAC/E,aAAW,MAAM,mBAAoB,IAAG,SAAS,IAAI;AACvD;AAcA,SAAS,UAAU,KAAqB;AACtC,QAAM,MAAM,OAAO,OAAO,QAAQ,WAAY,MAAkC,CAAC;AACjF,QAAM,MAAa,CAAC;AACpB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,OAAO,MAAM,SAAU,KAAI,CAAC,IAAI;AAC9E,SAAO;AACT;AAEA,SAAS,YAAY,KAAyB;AAC5C,QAAM,IAAK,OAAO,OAAO,QAAQ,WAAW,MAAM,CAAC;AACnD,QAAM,OAAO,CAAC,MACZ,KAAK,OAAO,MAAM,WAAY,IAAsC,CAAC;AACvE,SAAO,EAAE,OAAO,KAAK,EAAE,KAAK,GAAG,QAAQ,KAAK,EAAE,MAAM,EAAE;AACxD;AAEA,SAAS,YAAY,KAAyB;AAC5C,QAAM,IAAK,OAAO,OAAO,QAAQ,WAAW,MAAM,CAAC;AACnD,QAAM,MAAM,EAAE,SAAS,OAAO,EAAE,UAAU,WAAY,EAAE,QAAoC,CAAC;AAC7F,QAAM,QAAgC,CAAC;AACvC,aAAW,CAAC,IAAI,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,OAAO,MAAM,YAAY,OAAO,SAAS,CAAC,EAAG,OAAM,EAAE,IAAI;AACxG,SAAO,EAAE,MAAM;AACjB;AAEA,SAAS,qBAAqB,KAAwB;AACpD,SAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAAI,CAAC;AACvF;AAEA,SAAS,kBAAkB,KAA2B;AACpD,QAAM,MAAM,OAAO,OAAO,QAAQ,WAAY,MAAkC,CAAC;AACjF,QAAM,MAAmB,CAAC;AAC1B,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,KAAI,MAAM,KAAM,KAAI,CAAC,IAAI;AACnE,SAAO;AACT;AAEA,eAAe,cAAc,QAAwB,QAAoC;AACvF,QAAM,MAAM,MAAM,OAAO,KAAK,WAAW,MAAM,CAAC,EAAE,MAAM,CAAC,QAAiB;AACxE,QAAI,eAAe,qBAAqB,IAAI,WAAW,IAAK,QAAO;AACnE,UAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,KAAK;AAYlB,SAAO;AAAA,IACL,QAAQ,MAAM,QAAQ,MAAM,MAAM,IAAI,KAAM,SAAU,CAAC;AAAA,IACvD,MAAM,MAAM,QAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,CAAC;AAAA,IACjE,OAAO,YAAY,MAAM,KAAK;AAAA,IAC9B,OAAO,YAAY,MAAM,KAAK;AAAA,IAC9B,WAAW,MAAM,aAAa,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY,CAAC;AAAA,IACrF,KAAK,UAAU,MAAM,GAAG;AAAA,IACxB,gBAAgB,qBAAqB,MAAM,cAAc;AAAA,IACzD,aAAa,kBAAkB,MAAM,WAAW;AAAA,IAChD,MAAM,KAAK,QAAQ;AAAA,EACrB;AACF;AAEA,eAAsB,WAAW,QAAwB,QAAoC;AAC3F,MAAI;AACF,WAAO,MAAM,cAAc,QAAQ,MAAM;AAAA,EAC3C,SAAS,KAAK;AACZ,YAAQ,MAAM,+CAA+C,GAAG;AAChE,WAAO;AAAA,MACL,QAAQ,CAAC;AAAA,MACT,MAAM,CAAC;AAAA,MACP,OAAO,YAAY,MAAS;AAAA,MAC5B,OAAO,YAAY,MAAS;AAAA,MAC5B,WAAW,CAAC;AAAA,MACZ,KAAK,CAAC;AAAA,MACN,gBAAgB,CAAC;AAAA,MACjB,aAAa,CAAC;AAAA,MACd,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,gBACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,MAAM,EAAE,QAAQ,MAAM,UAAU;AACtC,UAAM,OAAO,QAAQ,GAAG;AACxB,QAAI,SAAS,IAAK;AAClB,QAAI;AACF,YAAM,OAAO;AAAA,QACX,WAAW,MAAM;AAAA,QACjB,EAAE,GAAG,GAAG,QAAQ,KAAK,QAAQ,MAAM,KAAK,MAAM,OAAO,OAAO,WAAW,KAAK,WAAW,KAAK,gBAAgB,YAAY;AAAA,QACxH;AAAA,MACF;AACA;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,eACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,OAAO,MAAM,OAAO,WAAW,KAAK,gBAAgB,YAAY,GAAG,IAAI;AACnI;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,eACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,OAAO,QAAQ,KAAK;AAC1B,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,OAAO,OAAO,MAAM,WAAW,KAAK,gBAAgB,YAAY,GAAG,IAAI;AACnI;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,aACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,OAAO,QAAQ,GAAG;AACxB,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,MAAM,gBAAgB,YAAY,GAAG,IAAI;AACnI;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,wBACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,OAAO,QAAQ,cAAc;AACnC,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,MAAM,YAAY,GAAG,IAAI;AACnI;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,qBACpB,QACA,QACA,SACe;AACf,QAAM,eAAe;AACrB,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACvD,UAAM,EAAE,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,IAAI,MAAM,cAAc,QAAQ,MAAM;AAC5H,UAAM,OAAO,QAAQ,WAAW;AAChC,QAAI,CAAC,KAAM;AACX,QAAI;AACF,YAAM,OAAO,KAAK,WAAW,MAAM,GAAG,EAAE,GAAG,GAAG,QAAQ,MAAM,OAAO,OAAO,WAAW,KAAK,gBAAgB,aAAa,KAAK,GAAG,IAAI;AACnI;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,eAAeA,kBAAiB,UAAU,eAAe,EAAG;AAChE,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,eAAsB,aACpB,QACA,QACA,YACA,SACe;AACf,QAAM,aAAa,QAAQ,QAAQ,CAAC,QAAS,IAAI,UAAU,MAAM,UAAU,OAAO,EAAE,GAAG,KAAK,CAAC,UAAU,GAAG,QAAQ,CAAE;AACtH;AAEA,eAAsB,YACpB,QACA,QACA,QACA,OACe;AACf,QAAM,gBAAgB,QAAQ,QAAQ,CAAC,SAAS,EAAE,QAAQ,MAAM,IAAI,MAAM,WAAW,IAAI,UAAU,EAAE;AACvG;AAEA,eAAsB,cAAc,QAAwB,QAAgB,OAAgC;AAC1G,QAAM,gBAAgB,QAAQ,QAAQ,CAAC,QAAQ;AAC7C,UAAM,OAAO,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACrD,UAAM,OAAgB,CAAC;AACvB,eAAW,MAAM,OAAO;AACtB,YAAM,IAAI,KAAK,IAAI,EAAE;AACrB,UAAI,GAAG;AAAE,aAAK,KAAK,CAAC;AAAG,aAAK,OAAO,EAAE;AAAA,MAAG;AAAA,IAC1C;AACA,eAAW,KAAK,IAAI,OAAQ,KAAI,KAAK,IAAI,EAAE,EAAE,EAAG,MAAK,KAAK,CAAC;AAC3D,UAAM,YAAY,KAAK,WAAW,IAAI,OAAO,UAAU,KAAK,MAAM,CAAC,GAAG,MAAM,MAAM,IAAI,OAAO,CAAC,CAAC;AAC/F,QAAI,UAAW,QAAO;AACtB,WAAO,EAAE,QAAQ,MAAM,MAAM,IAAI,MAAM,WAAW,IAAI,UAAU;AAAA,EAClE,CAAC;AACH;AAEA,SAAS,aAAqB;AAC5B,SAAO,MAAM,SAAS,CAAC;AACzB;AAEA,eAAsB,gBACpB,QACA,SACsH;AACtH,QAAM,MAAM,MAAM,OAAO,KAAK,gBAAgB,OAAO,CAAC,EAAE,MAAM,CAAC,QAAiB;AAC9E,QAAI,eAAe,qBAAqB,IAAI,WAAW,IAAK,QAAO;AACnE,UAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,KAAK;AAClB,SAAO;AAAA,IACL,OAAO,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAAA,IACtD,SAAS,MAAM,QAAQ,MAAM,OAAO,IAAI,KAAM,QAAS,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,IAC5G,MAAM,OAAO,MAAM,SAAS,WAAW,KAAK,OAAO;AAAA,IACnD,OAAO,OAAO,MAAM,UAAU,WAAW,KAAK,QAAQ;AAAA,IACtD,MAAM,KAAK,QAAQ;AAAA,EACrB;AACF;AAEA,eAAsB,iBACpB,QACA,SACA,OACA,SACA,MACA,MACe;AACf,QAAM,OAAO,MAAM,MAAM,KAAK,KAAK;AACnC,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,OAAO;AAAA,IACX,gBAAgB,OAAO;AAAA,IACvB;AAAA,MACE,GAAG;AAAA,MAAG;AAAA,MAAO;AAAA,MACb,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;AAAA,MACvB,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,IAC3B;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAsB,eACpB,QACA,SACA,aACA,cACe;AACf,QAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK,IAAI,MAAM,gBAAgB,QAAQ,OAAO;AACnF,MAAI,kBAAkB,SAAS,gBAAgB,QAAQ,SAAS,YAAY,EAAG;AAC/E,QAAM,iBAAiB,QAAQ,SAAS,SAAS,aAAa,CAAC,GAAG,SAAS,YAAY,GAAG,MAAM,EAAE,MAAM,MAAM,CAAC;AACjH;AAGA,eAAsB,kBACpB,QACA,SACA,cACe;AACf,QAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK,IAAI,MAAM,gBAAgB,QAAQ,OAAO;AACnF,MAAI,CAAC,QAAQ,SAAS,YAAY,EAAG;AACrC,QAAM,iBAAiB,QAAQ,SAAS,SAAS,cAAc,QAAQ,OAAO,CAAC,MAAM,MAAM,YAAY,GAAG,MAAM,EAAE,MAAM,MAAM,CAAC;AACjI;AAEA,eAAsB,eAAe,QAAwB,QAAgB,OAA6B;AACxG,QAAM;AAAA,IAAgB;AAAA,IAAQ;AAAA,IAAQ,CAAC,QACrC,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,IACpC,MACA,EAAE,QAAQ,CAAC,GAAG,IAAI,QAAQ,KAAK,GAAG,MAAM,IAAI,MAAM,WAAW,IAAI,UAAU;AAAA,EACjF;AACF;AAEA,eAAsB,sBACpB,QACA,QACA,OACA,SACe;AACf,QAAM,gBAAgB,QAAQ,QAAQ,CAAC,SAAS;AAAA,IAC9C,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,IAAI,IAAI,SAAS,CAAC,GAAG,IAAI,QAAQ,KAAK;AAAA,IACtF,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,QAAQ;AAAA,IACzC,WAAW,IAAI;AAAA,EACjB,EAAE;AACJ;AAEA,eAAsB,6BACpB,QACA,QACA,OACA,QACe;AACf,QAAM,gBAAgB,QAAQ,QAAQ,CAAC,SAAS;AAAA,IAC9C,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,IAAI,IAAI,SAAS,CAAC,GAAG,IAAI,QAAQ,KAAK;AAAA,IACtF,MAAM,IAAI;AAAA,IACV,WAAW,EAAE,GAAG,IAAI,WAAW,CAAC,MAAM,EAAE,GAAG,OAAO;AAAA,EACpD,EAAE;AACJ;AAMA,eAAsB,YACpB,SACA,MACgB;AAChB,QAAM,EAAE,eAAe,OAAO,IAAI;AAClC,QAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,WAAW,eAAe,MAAM;AAC/D,QAAM,UAAU,KAAK,KAAK,KAAK;AAC/B,QAAM,KAAK,WAAW;AACtB,QAAM,QAAe;AAAA,IACnB;AAAA,IACA,MAAM;AAAA,IACN,OAAO,QAAQ,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA,IACvC,SAAS;AAAA,EACX;AACA,QAAM,iBAAiB,eAAe,IAAI,QAAQ,CAAC,GAAG,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC7E,QAAM,qBAAqB,SAAS,EAAE;AACtC,QAAM,YAAY,eAAe,QAAQ,CAAC,GAAG,QAAQ,KAAK,GAAG,IAAI;AACjE,SAAO;AACT;AAEA,eAAsB,mBACpB,QACA,QACA,SACAC,SACA,aACe;AACf,QAAM,aAAa,OAAOA,QAAO,SAAS,YAAYA,QAAO,KAAK,KAAK,IAAIA,QAAO,OAAO;AACzF,QAAM,cAAc,OAAOA,QAAO,UAAU,YAAYA,QAAO,QAAQA,QAAO,QAAQ;AACtF,MAAI,eAAe,QAAQ,gBAAgB,KAAM;AACjD,QAAM,QAAQ,aAAa,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AACvD,MAAI,OAAO;AACT,UAAMC,QAAO,cAAc,MAAM;AACjC,UAAMC,SAAQD,MAAK,MAAM,GAAG,CAAC,EAAE,YAAY;AAC3C,UAAME,SAAQ,eAAe,MAAM;AACnC,QAAIF,UAAS,MAAM,QAAQC,WAAU,MAAM,UAAUC,UAAS,WAAW,MAAM,SAAS,MAAO;AAAA,EACjG;AACA,QAAM,EAAE,QAAQ,KAAK,IAAI,MAAM,WAAW,QAAQ,MAAM;AACxD,QAAM,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO;AAC/C,MAAI,CAAC,IAAK;AACV,QAAM,OAAO,cAAc,IAAI;AAC/B,QAAM,QAAQ,eAAe,IAAI;AACjC,QAAM,QAAQ,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY;AAC3C,MAAI,SAAS,IAAI,QAAQ,UAAU,IAAI,UAAU,SAAS,WAAW,IAAI,SAAS,MAAO;AACzF,QAAM,OAAO,OAAO,IAAI,CAAC,MAAO,EAAE,OAAO,UAAU,EAAE,GAAG,GAAG,MAAM,OAAO,MAAM,IAAI,CAAE;AACpF,QAAM,YAAY,QAAQ,QAAQ,MAAM,IAAI;AAC5C,qBAAmB,SAAS,EAAE,MAAM,OAAO,MAAM,CAAC;AACpD;AA9bA,IAiCM;AAjCN;AAAA;AAAA;AAaA;AAEA;AACA;AAiBA,IAAM,qBAAqB,oBAAI,IAAsD;AAAA;AAAA;;;AC9BrF;AAIA;AAyBA;AAGA;AAgDA;AAkBA;;;AC3FA;AAAA,EACE,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAWP,IAAM,aAAa;AAEnB,IAAM,SAAS,MAAM,WAAW,OAAO;AAEvC,eAAe,KAAK,SAAkB,iBAAyB,WAAwC;AACrG,QAAM,MAAM,WAAW,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAChE,QAAM,QAAQ,MAAM,iBAAiB,KAAK,iBAAiB;AAAA,IACzD,gBAAgB,QAAQ,KAAK;AAAA,IAC7B,eAAe,QAAQ,KAAK;AAAA,IAC5B,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;AAAA,IACrC,OAAO;AAAA,EACT,CAAC;AACD,QAAM,KAAK,WAAW,OAAO,gBAAgB,IAAI,WAAW,EAAE,CAAC;AAC/D,QAAMC,OAAM,MAAM,OAAO,EAAE,UAAU,OAAO,KAAK,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;AACxF,QAAM,QAAQ,MAAM,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,GAAG,GAAGA,MAAK,IAAI,YAAY,EAAE,OAAO,SAAS,CAAC;AACtG,QAAM,SAAS,IAAI,WAAW,GAAG,SAAS,MAAM,UAAU;AAC1D,SAAO,IAAI,IAAI,CAAC;AAChB,SAAO,IAAI,IAAI,WAAW,KAAK,GAAG,GAAG,MAAM;AAC3C,SAAO,EAAE,OAAO,IAAID,YAAW,MAAM,EAAE;AACzC;AAEA,eAAe,KAAK,SAAkB,MAAmC;AACvE,QAAM,MAAM,MAAM,gBAAgB,KAAK,OAAO,QAAQ,KAAK,OAAO;AAClE,QAAM,SAAS,WAAW,KAAK,EAAE;AACjC,QAAM,KAAK,IAAI,WAAW,OAAO,SAAS,GAAG,EAAE,CAAC;AAChD,QAAM,UAAU,IAAI,WAAW,OAAO,SAAS,EAAE,CAAC;AAClD,QAAMC,OAAM,MAAM,OAAO,EAAE,UAAU,OAAO,IAAI,WAAW,GAAG,GAAG,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;AACxG,QAAM,MAAM,MAAM,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,GAAG,GAAGA,MAAK,OAAO;AACxE,SAAO,IAAI,YAAY,EAAE,OAAO,GAAG;AACrC;AAGO,SAAS,WAAW,SAAkB,WAAwC;AACnF,SAAO,KAAK,SAAS,QAAQ,KAAK,QAAQ,SAAS;AACrD;AAGA,eAAsB,eAAe,SAAkB,MAAmC;AACxF,MAAI,KAAK,MAAM,YAAY,QAAQ,KAAK,MAAO,OAAM,IAAI,MAAM,6BAA6B;AAC5F,MAAI,CAAE,MAAM,qBAAqB,KAAK,OAAO,UAAU,EAAI,OAAM,IAAI,MAAM,+BAA+B;AAC1G,SAAO,KAAK,SAAS,IAAI;AAC3B;AAGO,SAAS,gBAAgB,SAAkB,iBAAyB,WAAwC;AACjH,SAAO,KAAK,SAAS,iBAAiB,SAAS;AACjD;AAGA,eAAsB,oBAAoB,SAAkB,MAAmC;AAC7F,MAAI,CAAE,MAAM,qBAAqB,KAAK,OAAO,UAAU,EAAI,OAAM,IAAI,MAAM,+BAA+B;AAC1G,SAAO,KAAK,SAAS,IAAI;AAC3B;;;ADkDA;AAUA;AAgBA;;;AErIA;AAMA;AACA;AAbA,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,qBAAqB;;;ACdvB,SAAS,YAAY,MAAsB;AAChD,QAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,IAAI;AAC3C,MAAI,MAAM;AACV,aAAW,KAAK,MAAO,QAAO,OAAO,aAAa,CAAC;AACnD,QAAM,MAAM,OAAO,SAAS,aAAa,KAAK,GAAG,IAAI,OAAO,KAAK,MAAM,OAAO,EAAE,SAAS,QAAQ;AACjG,SAAO,IAAI,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,EAAE;AACtE;AAEO,SAAS,cAAc,QAAwB;AACpD,QAAM,MAAM,OAAO,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AACvD,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,MAAM,KAAK,GAAG;AACpB,UAAM,QAAQ,IAAI,WAAW,IAAI,MAAM;AACvC,aAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,IAAK,OAAM,CAAC,IAAI,IAAI,WAAW,CAAC;AAChE,WAAO,IAAI,YAAY,EAAE,OAAO,KAAK;AAAA,EACvC;AACA,SAAO,OAAO,KAAK,KAAK,QAAQ,EAAE,SAAS,OAAO;AACpD;;;ADkBO,SAAS,gBAAgB,SAA0B;AACxD,QAAMC,OAAmB,EAAE,OAAO,QAAQ,KAAK,OAAO,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,QAAQ,OAAO;AAC1G,SAAO,KAAK,UAAUA,IAAG;AAC3B;AAEA,SAAS,0BAA0B,KAAuB;AACxD,SAAO,eAAe,SAAS,2BAA2B,KAAK,IAAI,OAAO;AAC5E;AAEA,SAAS,iBAAiB,KAAuB;AAC/C,SAAO,eAAe,SAAS,gCAAgC,KAAK,IAAI,OAAO;AACjF;AAcA,eAAsB,cACpB,SACA,SACA,aACA,WAAW,MACX,WACiB;AACjB,QAAMA,OAAM,KAAK,MAAM,WAAW;AAClC,MAAI,CAACA,KAAI,SAAS,CAACA,KAAI,UAAU,CAACA,KAAI,OAAQ,OAAM,IAAI,MAAM,mCAAmC;AACjG,QAAM,eAAe,QAAQ,eAAe,SAAS,QAAQ,QAAQA,KAAI,MAAM;AAI/E,MAAI;AACF,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,YAAY,OAAO;AAAA,MACnB,EAAE,QAAQA,KAAI,QAAQ,QAAQA,KAAI,QAAQ,OAAOA,KAAI,OAAO,MAAM,GAAG,CAAC,EAAE;AAAA,MACxE,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,QAAQ;AAAA,MACxF,EAAE,eAAe,CAAC,QAAQ,KAAK,KAAK,EAAE;AAAA,IACxC;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,CAAC,0BAA0B,GAAG,KAAK,CAAC,iBAAiB,GAAG,GAAG;AAE7D,cAAQ,KAAK,mDAAmD,GAAG;AAAA,IACrE;AAAA,EACF;AAGA,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,EAAE,UAAUA,KAAI,OAAO,WAAWA,KAAI,QAAQ,WAAWA,KAAI,OAAO;AAAA,IACpE;AAAA,IACA,iBAAiB,SAAS,QAAQ;AAAA,EACpC;AACA,MAAI,OAAO,WAAW,KAAK;AAC3B,MAAI,CAAC,MAAM;AACT,UAAM,EAAE,OAAO,IAAI,MAAM,WAAW,QAAQ,eAAe,QAAQ,MAAM;AACzE,WAAO,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,GAAG,QAAQ;AAAA,EACvD;AACA,QAAM,SAAsB,EAAE,SAAS,WAAW,MAAM,IAAI;AAC5D,SAAO,KAAK,UAAU,MAAM;AAC9B;AAMA,eAAsB,kBAAkB,SAAkB,YAAoC;AAC5F,QAAM,MAAM,KAAK,MAAM,UAAU;AACjC,QAAM,MAAM,IAAI;AAChB,MAAI,CAAC,OAAO,CAAC,IAAI,QAAS,OAAM,IAAI,MAAM,mCAAmC;AAC7E,MAAI,IAAI,SAAS,SAAU,OAAM,IAAI,MAAM,mCAAmC;AAC9E,MAAI,CAAC,IAAI,OAAO,IAAI,QAAQ,QAAQ,KAAK,OAAO;AAC9C,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AACA,QAAM,UAAU,IAAI;AACpB,QAAM,UAAU,KAAK,UAAU,GAAG;AAClC,QAAM,OAAO,IAAI,WAAW,KAAK,KAAK,SAAS,QAAQ,MAAM,EAAE,CAAC;AAChE,QAAM,QAAe,EAAE,IAAI,SAAS,MAAM,OAAO,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY,GAAG,SAAS,EAAE;AAC5F,QAAM,sBAAsB,QAAQ,eAAe,QAAQ,QAAQ,OAAO,OAAO;AACjF,uBAAqB,SAAS,EAAE,MAAM,UAAU,KAAK,QAAQ,CAAC;AAC9D,SAAO;AACT;AAeO,SAAS,sBAAsB,QAAgB,OAAqC;AACzF,QAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE;AACtC,SAAO,GAAG,IAAI,SAAS,YAAY,KAAK,UAAU,KAAK,CAAC,CAAC;AAC3D;AAEO,SAAS,sBAAsB,UAAwC;AAC5E,QAAM,OAAO,SAAS,WAAW,GAAG,IAAI,SAAS,MAAM,CAAC,IAAI;AAC5D,QAAM,MAAM,KAAK,MAAM,cAAc,IAAI,CAAC;AAC1C,MAAI,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK;AAChD,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,IAAI;AAAA,IACb,WAAW,IAAI,aAAa;AAAA,IAC5B,KAAK,IAAI;AAAA,IACT,KAAK,IAAI;AAAA,IACT,OAAO,CAAC,CAAC,IAAI;AAAA,EACf;AACF;AASA,eAAsB,sBACpB,SACA,SACA,WACA,OACA,QACwD;AACxD,QAAM,KAAK,mBAAmB;AAC9B,QAAM,kBAAkB,MAAM,gBAAgB,GAAG,KAAK;AACtD,QAAM,MAAM,MAAM;AAAA,IAChB,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,EAAE,UAAU,GAAG,OAAO,WAAW,GAAG,QAAQ,WAAW,gBAAgB;AAAA,IACvE;AAAA,IACA,iBAAiB,SAAS,KAAK;AAAA,EACjC;AAEA,QAAM,eAAe,QAAQ,eAAe,SAAS,QAAQ,QAAQ,eAAe;AAIpF,MAAI;AACF,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,YAAY,OAAO;AAAA,MACnB,EAAE,QAAQ,GAAG,QAAQ,QAAQ,iBAAiB,OAAO,gBAAgB,MAAM,GAAG,CAAC,EAAE;AAAA,MACjF,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,QAAQ;AAAA,MACxF,EAAE,eAAe,CAAC,QAAQ,KAAK,KAAK,EAAE;AAAA,IACxC;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,CAAC,0BAA0B,GAAG,KAAK,CAAC,iBAAiB,GAAG,GAAG;AAC7D,cAAQ,KAAK,2DAA2D,GAAG;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,QAA8B,EAAE,GAAG,GAAG,SAAS,WAAW,KAAK,KAAK,GAAG,QAAQ,MAAM;AAC3F,SAAO,EAAE,OAAO,MAAM,sBAAsB,QAAQ,KAAK,EAAE;AAC7D;AAMA,eAAsB,gBAAgB,SAAkB,OAA6C;AACnG,QAAM,OAAO,MAAM,UAAU,KAAK,KAAK,SAAS,MAAM,QAAQ,MAAM,EAAE,CAAC;AACvE,QAAM,QAAe;AAAA,IACnB,IAAI,MAAM;AAAA,IACV;AAAA,IACA,OAAO,KAAK,MAAM,GAAG,CAAC,EAAE,YAAY;AAAA,IACpC,SAAS;AAAA,EACX;AACA,QAAM,gBAAgB,EAAE,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM;AAC3E,QAAM,SAAS,MAAM,WAAW,SAAS,KAAK,UAAU,aAAa,CAAC;AACtE,QAAM,6BAA6B,QAAQ,eAAe,QAAQ,QAAQ,OAAO,MAAM;AACvF,uBAAqB,MAAM,SAAS,EAAE,MAAM,QAAQ,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM,CAAC;AACxG,SAAO;AACT;AAUA,eAAsB,wBACpB,SACA,SACA,QACe;AACf,MAAI;AACF,UAAM;AAAA,MACJ,QAAQ;AAAA,MACR,YAAY,OAAO;AAAA,MACnB,EAAE,QAAQ,OAAO,QAAQ,QAAQ,OAAO,QAAQ,OAAO,OAAO,OAAO,MAAM,GAAG,CAAC,EAAE;AAAA,MACjF,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,QAAQ;AAAA,MACxF,EAAE,eAAe,CAAC,QAAQ,KAAK,KAAK,EAAE;AAAA,IACxC;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,CAAC,0BAA0B,GAAG,KAAK,CAAC,iBAAiB,GAAG,EAAG,OAAM;AAAA,EACvE;AACF;AAQA,eAAsB,mBACpB,SACA,QACe;AAEf,QAAM,aAA4E,CAAC;AACnF,aAAW,CAAC,SAAS,MAAM,KAAK,OAAO,QAAQ,OAAO,SAAS,GAAG;AAChE,QAAI;AACF,YAAM,MAAM,MAAM,eAAe,SAAS,MAAM;AAChD,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,OAAO,OAAO,OAAO,IAAK,YAAW,OAAO,IAAI;AAAA,IACtD,SAAS,GAAG;AACV,cAAQ,MAAM,qDAAqD,SAAS,CAAC;AAAA,IAC/E;AAAA,EACF;AAEA,QAAM,wBAAwB,QAAQ,QAAQ,OAAO,MAAM,UAAU;AAGrE,QAAM,QAAQ,wBAAwB;AACtC,QAAM,oBAAoB,OAAO,QAAQ,KAAK,EAC3C,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,MAAM,OAAO,KAAK;AAClE,QAAM,eAAe,OAAO,QAAQ,KAAK,EACtC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,MAAM,OAAO,UAAU;AAErE,MAAI,kBAAkB,WAAW,KAAK,aAAa,WAAW,EAAG;AAEjE,MAAI;AACF,UAAM,UAAkC,CAAC;AACzC,eAAW,CAAC,IAAI,CAAC,KAAK,kBAAmB,KAAI,EAAE,SAAS,SAAU,SAAQ,EAAE,IAAI,EAAE;AAElF,UAAM,eAA6E,CAAC;AACpF,eAAW,CAAC,IAAI,CAAC,KAAK,cAAc;AAClC,UAAI,EAAE,SAAS,QAAQ;AACrB,qBAAa,EAAE,IAAI,MAAM,WAAW,SAAS,KAAK,UAAU,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,MAAM,CAAC,CAAC;AAAA,MACzG;AAAA,IACF;AAEA,UAAM,gBAAgB,QAAQ,eAAe,QAAQ,QAAQ,CAAC,SAAS;AAAA,MACrE,QAAQ,IAAI;AAAA,MACZ,MAAM,EAAE,GAAG,IAAI,MAAM,GAAG,QAAQ;AAAA,MAChC,WAAW,EAAE,GAAG,IAAI,WAAW,GAAG,aAAa;AAAA,IACjD,EAAE;AAAA,EACJ,SAAS,GAAG;AACV,YAAQ,MAAM,oDAAoD,CAAC;AAAA,EACrE;AACF;;;AElRA;AAEA;AACA;AAQA;AAGA;AAnBA,SAAS,sBAAAC,2BAA0B;AACnC,SAAS,0BAAAC,+BAA8B;AACvC,SAAS,iBAAAC,sBAAqB;;;ACd9B;AAQA,SAAS,gBAAgB,GAAe,GAAuB;AAC7D,MAAI,EAAE,UAAU,EAAE,MAAO,QAAO,EAAE,QAAQ,EAAE;AAC5C,SAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI;AAC9C;AAGO,SAAS,UAAU,UAAgC;AACxD,MAAI,MAAM;AACV,aAAW,KAAK,SAAU,KAAI,EAAE,QAAQ,IAAK,OAAM,EAAE;AACrD,SAAO,MAAM;AACf;AASO,SAAS,UAAU,OAAqB,kBAAkB,OAAyB;AACxF,QAAM,OAAO,kBAAkB,QAAQ,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AACtE,QAAM,OAAO,IAAI,IAAoB,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAE/D,QAAM,kBAAkB,CAAC,MAA6B;AACpD,QAAI,EAAE,YAAY,KAAM,QAAO;AAC/B,QAAI,CAAC,KAAK,IAAI,EAAE,QAAQ,EAAG,QAAO;AAClC,UAAM,OAAO,oBAAI,IAAQ,CAAC,EAAE,EAAE,CAAC;AAC/B,QAAI,MAAiB,EAAE;AACvB,WAAO,OAAO,MAAM;AAClB,UAAI,KAAK,IAAI,GAAG,EAAG,QAAO;AAC1B,WAAK,IAAI,GAAG;AACZ,YAAM,SAAS,KAAK,IAAI,GAAG;AAC3B,UAAI,CAAC,OAAQ,QAAO;AACpB,YAAM,OAAO;AAAA,IACf;AACA,WAAO,EAAE;AAAA,EACX;AAEA,QAAM,aAAa,oBAAI,IAA6B;AACpD,aAAW,KAAK,MAAM;AACpB,UAAM,IAAI,gBAAgB,CAAC;AAC3B,UAAM,SAAS,WAAW,IAAI,CAAC,KAAK,CAAC;AACrC,WAAO,KAAK,CAAC;AACb,eAAW,IAAI,GAAG,MAAM;AAAA,EAC1B;AAEA,WAAS,OAAO,QAAmB,OAAiC;AAClE,YAAQ,WAAW,IAAI,MAAM,KAAK,CAAC,GAChC,MAAM,EACN,KAAK,eAAe,EACpB,IAAI,CAAC,OAAuB,EAAE,GAAG,GAAG,OAAO,UAAU,OAAO,EAAE,IAAI,QAAQ,CAAC,EAAE,EAAE;AAAA,EACpF;AAEA,SAAO,OAAO,MAAM,CAAC;AACvB;AAGO,SAAS,YAAY,OAAqB,IAAsB;AACrE,QAAM,OAAO,IAAI,IAAoB,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAChE,QAAM,QAAsB,CAAC;AAC7B,QAAM,OAAO,oBAAI,IAAQ;AACzB,MAAI,MAAiB;AACrB,SAAO,OAAO,QAAQ,KAAK,IAAI,GAAG,KAAK,CAAC,KAAK,IAAI,GAAG,GAAG;AACrD,SAAK,IAAI,GAAG;AACZ,UAAM,OAAmB,KAAK,IAAI,GAAG;AACrC,UAAM,QAAQ,IAAI;AAClB,UAAM,KAAK;AAAA,EACb;AACA,SAAO;AACT;AAGO,SAAS,UAAU,OAAqB,IAAsB;AACnE,SAAO,YAAY,OAAO,EAAE,EAAE,MAAM,GAAG,EAAE;AAC3C;AAGO,SAAS,WAAW,OAAqB,QAAqB;AACnE,QAAM,aAAa,oBAAI,IAAqB;AAC5C,aAAW,KAAK,OAAO;AACrB,UAAM,SAAS,WAAW,IAAI,EAAE,QAAQ,KAAK,CAAC;AAC9C,WAAO,KAAK,EAAE,EAAE;AAChB,eAAW,IAAI,EAAE,UAAU,MAAM;AAAA,EACnC;AACA,QAAM,MAAM,oBAAI,IAAQ;AACxB,QAAM,OAAO,CAAC,OAAW;AACvB,QAAI,IAAI,IAAI,EAAE,EAAG;AACjB,QAAI,IAAI,EAAE;AACV,eAAW,SAAS,WAAW,IAAI,EAAE,KAAK,CAAC,EAAG,MAAK,KAAK;AAAA,EAC1D;AACA,OAAK,MAAM;AACX,SAAO;AACT;AAoBO,SAAS,UAAU,OAAqB,OAAuB,KAAwD;AAC5H,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AAC5D,QAAM,OAAmB;AAAA,IACvB,IAAI,MAAM,MAAM,OAAO,SAAS,CAAC;AAAA,IACjC,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,OAAO,UAAU,QAAQ;AAAA,IACzB,OAAO,MAAM;AAAA,IACb,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,IAC5C,WAAW;AAAA,IACX,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,IACzC,GAAI,MAAM,UAAU,MAAM,WAAW,UAAU,EAAE,QAAQ,MAAM,OAAO,IAAI,CAAC;AAAA,IAC3E,GAAI,MAAM,MAAM,EAAE,KAAK,KAAc,IAAI,CAAC;AAAA,EAC5C;AACA,SAAO,EAAE,OAAO,CAAC,GAAG,OAAO,IAAI,GAAG,KAAK;AACzC;AAGO,SAAS,YACd,OACA,IACA,OACA,KACc;AACd,SAAO,MAAM,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,GAAG,OAAO,WAAW,IAAI,IAAI,CAAE;AAChF;AAGO,SAAS,eAAe,OAAqB,IAAQ,UAAqB,KAA2B;AAC1G,MAAI,OAAO,SAAU,QAAO;AAC5B,MAAI,YAAY,QAAQ,WAAW,OAAO,EAAE,EAAE,IAAI,QAAQ,EAAG,QAAO;AACpE,QAAM,WAAW,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,OAAO,EAAE;AAC3E,SAAO,MAAM,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,UAAU,OAAO,UAAU,QAAQ,GAAG,WAAW,IAAI,IAAI,CAAE;AAC5G;AAGO,SAAS,eAAe,OAAqB,WAA+B,KAA2B;AAC5G,SAAO,MAAM,IAAI,CAAC,MAAO,EAAE,MAAM,YAAY,EAAE,GAAG,GAAG,OAAO,UAAU,EAAE,EAAE,GAAI,WAAW,IAAI,IAAI,CAAE;AACrG;AAGO,SAAS,cAAc,OAAqB,IAAQ,KAA2B;AACpF,QAAM,MAAM,WAAW,OAAO,EAAE;AAChC,SAAO,MAAM,IAAI,CAAC,MAAO,IAAI,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,GAAG,UAAU,MAAM,WAAW,IAAI,IAAI,CAAE;AACxF;;;AD9HA;AACA;AACA;AAKA,SAASC,2BAA0B,KAAuB;AACxD,SAAO,eAAe,SAAS,2BAA2B,KAAK,IAAI,OAAO;AAC5E;AAyBA,eAAsB,WACpB,SACA,SACA,OACA,KACqB;AACrB,QAAM,SAAS,MAAM,UAAU;AAC/B,QAAM,MAAM,MAAM,OAAO;AACzB,MAAI,WAAW,YAAY,IAAK,OAAM,IAAI,MAAM,wCAAwC;AAExF,QAAM,SAAS,OAAO,SAAS,CAAC;AAEhC,MAAI,KAAK;AAEP,UAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,UAAM;AAAA,MACJ;AAAA,MACA,QAAQ;AAAA,MACR,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO;AAAA,MACnB,mBAAmB,OAAO;AAAA,IAC5B;AAAA,EACF;AAEA,MAAI,cAAiC;AAErC,QAAM,kBAAkB,SAAS,SAAS,CAAC,OAAO,QAAQ;AACxD,UAAM,EAAE,OAAO,MAAM,KAAK,IAAI,UAAU,OAAO;AAAA,MAC7C,IAAI;AAAA,MACJ,MAAM,MAAM;AAAA,MACZ,OAAO,MAAM;AAAA,MACb,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;AAAA,MAC5C,UAAU,MAAM,YAAY;AAAA,MAC5B,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,MACzC;AAAA,MACA,KAAK,OAAO;AAAA,IACd,GAAG,GAAG;AACN,kBAAc,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,KAAK;AACnD,WAAO;AAAA,EACT,GAAG,GAAG;AAEN,MAAI,CAAC,YAAa,OAAM,IAAI,MAAM,iDAAiD;AACnF,SAAO;AACT;AAYA,eAAsB,cACpB,SACA,SACA,QACA,OACA,KACe;AACf,MAAI,MAAM,WAAW,YAAY,MAAM,IAAK,OAAM,IAAI,MAAM,0BAA0B;AAEtF,MAAI,MAAM,KAAK;AAEb,UAAM,SAAS,eAAe,SAAS,OAAO;AAC9C,UAAM;AAAA,MACJ;AAAA,MACA,QAAQ;AAAA,MACR,YAAY,OAAO;AAAA,MACnB,YAAY,OAAO;AAAA,MACnB,mBAAmB,OAAO;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,kBAAkB,SAAS,SAAS,CAAC,OAAO,QAAQ;AACxD,UAAM,MAAM,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM;AAClD,QAAI,MAAM,EAAG,QAAO;AACpB,UAAM,MAAM,MAAM,GAAG;AAErB,UAAM,OAAmB,EAAE,GAAG,KAAK,WAAW,IAAI;AAElD,QAAI,MAAM,WAAW,QAAW;AAC9B,UAAI,MAAM,WAAW,SAAS;AAC5B,eAAQ,KAA4C;AAAA,MACtD,OAAO;AACL,aAAK,SAAS,MAAM;AAAA,MACtB;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ,QAAW;AAC3B,UAAI,CAAC,MAAM,KAAK;AACd,eAAQ,KAA4C;AAAA,MACtD,OAAO;AACL,aAAK,MAAM;AAAA,MACb;AAAA,IACF;AAGA,QAAI,KAAK,WAAW,YAAY,KAAK,IAAK,OAAM,IAAI,MAAM,0BAA0B;AAEpF,UAAM,YACJ,KAAK,WAAW,IAAI,WACnB,KAAK,OAAO,YAAY,IAAI,OAAO;AACtC,QAAI,UAAW,QAAO;AAEtB,WAAO,MAAM,IAAI,CAAC,GAAG,MAAO,MAAM,MAAM,OAAO,CAAE;AAAA,EACnD,GAAG,GAAG;AACR;AAwBA,eAAsB,aACpB,SACA,SACA,QACA,aACA,MACA,UACiB;AACjB,QAAMC,OAAM,KAAK,MAAM,WAAW;AAClC,MAAI,CAACA,KAAI,SAAS,CAACA,KAAI,UAAU,CAACA,KAAI,OAAQ,OAAM,IAAI,MAAM,uBAAuB;AAErF,MAAI,KAAK,KAAK;AAEZ,QAAI;AACF,YAAMC;AAAA,QACJ,QAAQ;AAAA,QACR,YAAY,OAAO;AAAA,QACnB,EAAE,QAAQD,KAAI,QAAQ,QAAQA,KAAI,QAAQ,OAAOA,KAAI,OAAO,MAAM,GAAG,CAAC,EAAE;AAAA,QACxE,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,QAAQ;AAAA,QACxF,EAAE,eAAe,CAAC,QAAQ,KAAK,KAAK,EAAE;AAAA,MACxC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAACD,2BAA0B,GAAG,EAAG,OAAM;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,eAAe,QAAQ,eAAe,SAAS,QAAQ,QAAQC,KAAI,MAAM;AAE/E,QAAM,WAAW,MAAME;AAAA,IACrB,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,EAAE,UAAUF,KAAI,OAAO,WAAWA,KAAI,QAAQ,WAAWA,KAAI,OAAO;AAAA,IACpE;AAAA,IACA,iBAAiB,SAAS,IAAI;AAAA,EAChC;AAEA,QAAM,SAA2B;AAAA,IAC/B;AAAA,IACA;AAAA,IACA,UAAU,YAAY;AAAA,IACtB,KAAK;AAAA,EACP;AAEA,MAAI,CAAC,KAAK,KAAK;AAEb,UAAM,aAAa,MAAME;AAAA,MACvB,QAAQ,KAAK;AAAA,MACb,QAAQ,KAAK;AAAA,MACb,EAAE,UAAUF,KAAI,OAAO,WAAWA,KAAI,QAAQ,WAAWA,KAAI,OAAO;AAAA,MACpE;AAAA,MACA,gBAAgB,SAAS,QAAQ,IAAI;AAAA,IACvC;AACA,WAAO,UAAU;AAAA,EACnB;AAEA,SAAO,KAAK,UAAU,MAAM;AAC9B;AAMA,eAAsB,iBAAiB,SAAkB,YAAqC;AAC5F,QAAM,SAAS,KAAK,MAAM,UAAU;AACpC,QAAM,MAAM,OAAO;AACnB,MAAI,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,OAAO,OAAQ,OAAM,IAAI,MAAM,sBAAsB;AACrF,MAAI,IAAI,SAAS,SAAU,OAAM,IAAI,MAAM,sBAAsB;AACjE,MAAI,CAAC,IAAI,OAAO,IAAI,QAAQ,QAAQ,KAAK,OAAO;AAC9C,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,QAAM,UAAU,KAAK,UAAU,GAAG;AAElC,uBAAqB,OAAO,SAAS,EAAE,MAAM,UAAU,KAAK,QAAQ,CAAC;AAErE,MAAI,OAAO,SAAS;AAElB,UAAM,cAAc,KAAK,UAAU,OAAO,OAAO;AACjD,wBAAoB,OAAO,SAAS,OAAO,QAAQ,EAAE,MAAM,UAAU,KAAK,YAAY,CAAC;AAAA,EACzF;AAEA,SAAO,OAAO;AAChB;AAiBO,SAAS,qBAAqB,QAAgB,OAAoC;AACvF,QAAM,OAAO,OAAO,QAAQ,QAAQ,EAAE;AACtC,SAAO,GAAG,IAAI,cAAc,YAAY,KAAK,UAAU,KAAK,CAAC,CAAC;AAChE;AAEO,SAAS,qBAAqB,UAAuC;AAC1E,QAAM,OAAO,SAAS,WAAW,GAAG,IAAI,SAAS,MAAM,CAAC,IAAI;AAC5D,QAAM,MAAM,KAAK,MAAM,cAAc,IAAI,CAAC;AAC1C,MAAI,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,IAAI,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK;AAC/D,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,IAAI;AAAA,IACb,QAAQ,IAAI;AAAA,IACZ,UAAU,IAAI,YAAY,IAAI;AAAA,IAC9B,KAAK,IAAI;AAAA,IACT,KAAK,IAAI;AAAA,IACT,OAAO,CAAC,CAAC,IAAI;AAAA,EACf;AACF;AAYA,eAAsB,qBACpB,SACA,SACA,QACA,UACA,MACA,OACA,QACuD;AACvD,QAAM,KAAKG,oBAAmB;AAC9B,QAAM,kBAAkB,MAAM,gBAAgB,GAAG,KAAK;AAEtD,QAAM,eAAe,QAAQ,eAAe,SAAS,QAAQ,QAAQ,eAAe;AAEpF,MAAI,KAAK,KAAK;AAEZ,QAAI;AACF,YAAMF;AAAA,QACJ,QAAQ;AAAA,QACR,YAAY,OAAO;AAAA,QACnB,EAAE,QAAQ,GAAG,QAAQ,QAAQ,iBAAiB,OAAO,gBAAgB,MAAM,GAAG,CAAC,EAAE;AAAA,QACjF,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,OAAO,SAAS,QAAQ,KAAK,QAAQ;AAAA,QACxF,EAAE,eAAe,CAAC,QAAQ,KAAK,KAAK,EAAE;AAAA,MACxC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,CAACF,2BAA0B,GAAG,EAAG,OAAM;AAAA,IAC7C;AAAA,EACF;AAIA,QAAM,MAAM,MAAMG;AAAA,IAChB,QAAQ,KAAK;AAAA,IACb,QAAQ,KAAK;AAAA,IACb,EAAE,UAAU,GAAG,OAAO,WAAW,GAAG,QAAQ,WAAW,gBAAgB;AAAA,IACvE;AAAA,IACA,KAAK,MACD,iBAAiB,SAAS,KAAK,IAC/B,gBAAgB,SAAS,QAAQ,KAAK;AAAA,EAC5C;AAEA,QAAM,QAA6B,EAAE,GAAG,GAAG,SAAS,QAAQ,UAAU,KAAK,KAAK,GAAG,QAAQ,MAAM;AACjG,SAAO,EAAE,OAAO,MAAM,qBAAqB,QAAQ,KAAK,EAAE;AAC5D;AAMA,eAAsB,eAAe,SAAkB,OAA6C;AAClG,QAAM,gBAAgB,EAAE,KAAK,MAAM,KAAK,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM;AAC3E,QAAM,SAAS,MAAM,WAAW,SAAS,KAAK,UAAU,aAAa,CAAC;AAGtE,QAAM,EAAE,iBAAAE,iBAAgB,IAAI,MAAM;AAClC,QAAMA,iBAAgB,QAAQ,eAAe,QAAQ,QAAQ,CAAC,SAAS;AAAA,IACrE,QAAQ,IAAI;AAAA,IACZ,MAAM,IAAI;AAAA,IACV,WAAW;AAAA,MACT,GAAG,IAAI;AAAA,MACP,CAAC,GAAG,MAAM,OAAO,IAAI,MAAM,MAAM,EAAE,GAAG;AAAA,IACxC;AAAA,EACF,EAAE;AAEF,sBAAoB,MAAM,SAAS,MAAM,QAAQ;AAAA,IAC/C,MAAM;AAAA,IACN,KAAK,MAAM;AAAA,IACX,KAAK,MAAM;AAAA,IACX,OAAO,MAAM;AAAA,EACf,CAAC;AAED,SAAO,MAAM;AACf;;;AJ3MA;;;AM9MA;AACA;AAEA;AACA;AAdA,SAAS,kBAAAC,uBAAsB;AAC/B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAYA,IAAM,cAAc;AAG3B,IAAM,wBAAwB,MAAM,KAAK,KAAK;AAE9C,SAAS,aAA6B;AACpC,SAAO,IAAIA,gBAAe,EAAE,SAAS,YAAY,GAAG,WAAW,iBAAiB,GAAG,OAAO,iBAAiB,EAAE,CAAC;AAChH;AAEA,SAAS,cAAsB;AAC7B,QAAM,IAAI,IAAI,WAAW,EAAE;AAC3B,aAAW,OAAO,gBAAgB,CAAC;AACnC,SAAO,WAAW,CAAC;AACrB;AAYA,eAAsB,mBAAmB,SAAkB,KAA8B;AACvF,QAAM,EAAE,YAAY,OAAO,IAAI,MAAM;AAAA,IACnC,EAAE,QAAQ,QAAQ,KAAK,QAAQ,OAAO,QAAQ,KAAK,MAAM;AAAA,IACzD,EAAE,OAAO,kBAAkB,QAAQ,MAAM,GAAG,QAAQ,sBAAsB;AAAA,EAC5E;AACA,QAAM,OAAO,KAAK,UAAU,EAAE,GAAG,GAAG,MAAM,YAAY,OAAO,CAAC;AAC9D,QAAM,SAAS,MAAM,mBAAmB,KAAK,IAAI,YAAY,EAAE,OAAO,IAAI,CAAC;AAC3E,QAAM,QAAQ,YAAY;AAC1B,QAAM,WAAW,EAAE,KAAK,kBAAkB,KAAK,IAAI,QAA8C,IAAI;AACrG,SAAO,GAAG,WAAW,GAAG,KAAK,IAAI,QAAQ,KAAK,KAAK;AACrD;AAUA,eAAsB,sBAAsB,SAAiB,KAAkC;AAG7F,QAAM,QAAQ,QAAQ,WAAW,WAAW,KAAK,QAAQ,SAAS,QAAQ,IACtE,QAAQ,MAAM,QAAQ,QAAQ,GAAG,IAAI,CAAC,IACtC,SAAS,KAAK;AAClB,QAAM,CAAC,OAAO,iBAAiB,IAAI,KAAK,MAAM,GAAG;AACjD,QAAM,MAAM,MAAM,WAAW,EAAE,KAAK,kBAAkB,KAAK,EAAE,EAAE,MAAM,MAAM,IAAI;AAC/E,QAAM,SAAS,KAAK;AACpB,MAAI,CAAC,UAAU,CAAC,OAAO,EAAG,OAAM,IAAI,MAAM,oCAAoC;AAC9E,MAAI;AACJ,MAAI;AACF,YAAQ,MAAM,mBAAmB,KAAK,MAAe;AAAA,EACvD,QAAQ;AACN,UAAM,IAAI,MAAM,sCAAsC;AAAA,EACxD;AACA,QAAM,OAAO,KAAK,MAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AACvD,QAAM,OAAQ,oBAAoB,EAAE,kBAAkB,IAAI,CAAC;AAC3D,QAAM,YAAY,MAAM;AAAA,IACtB,KAAK;AAAA,IACL,KAAK;AAAA,IACL;AAAA,EACF;AACA,QAAM,SAAS,UAAU,YAAY;AACrC,SAAO;AAAA,IACL;AAAA,IACA,aAAa,sBAAsB,MAAM;AAAA,IACzC,YAAY,UAAU,YAAY;AAAA,IAClC,SAAS,UAAU,YAAY;AAAA,EACjC;AACF;;;ANuIA;AAGA;AAGA;;;AOpOA,IAAM,QAAQ;AAEd,IAAM,WAAW;AAEjB,IAAM,WAAW,MAAM;AACrB,QAAM,QAAQ,IAAI,WAAW,GAAG,EAAE,KAAK,EAAE;AACzC,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,IAAK,OAAM,SAAS,WAAW,CAAC,CAAC,IAAI;AAC1E,SAAO;AACT,GAAG;AAEH,IAAM,cACJ,OAAO,eAAe,eACtB,OAAO,WAAW,SAAS,cAC3B,OAAO,WAAW,SAAS;AAE7B,SAAS,cAAc,MAA0B;AAC/C,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,OAAO;AAC3C,cAAU,OAAO,aAAa,MAAM,MAAM,KAAK,SAAS,GAAG,IAAI,KAAK,CAAwB;AAAA,EAC9F;AACA,SAAO,WAAW,KAAK,MAAM;AAC/B;AAEA,SAAS,cAAc,SAA6B;AAClD,QAAM,SAAS,WAAW,KAAK,OAAO;AACtC,QAAM,MAAM,IAAI,WAAW,OAAO,MAAM;AACxC,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,IAAK,KAAI,CAAC,IAAI,OAAO,WAAW,CAAC;AACpE,SAAO;AACT;AAEA,SAAS,WAAW,MAA0B;AAC5C,QAAM,MAAM,KAAK;AACjB,QAAM,OAAO,MAAO,MAAM;AAC1B,QAAM,QAAkB,CAAC;AACzB,WAAS,QAAQ,GAAG,QAAQ,MAAM,SAAS,OAAO;AAChD,UAAM,OAAO,KAAK,IAAI,QAAQ,OAAO,IAAI;AACzC,QAAI,IAAI;AACR,aAAS,IAAI,OAAO,IAAI,MAAM,KAAK,GAAG;AACpC,YAAM,IAAK,KAAK,CAAC,KAAK,KAAO,KAAK,IAAI,CAAC,KAAK,IAAK,KAAK,IAAI,CAAC;AAC3D,WAAK,SAAU,KAAK,KAAM,EAAE,IAAI,SAAU,KAAK,KAAM,EAAE,IAAI,SAAU,KAAK,IAAK,EAAE,IAAI,SAAS,IAAI,EAAE;AAAA,IACtG;AACA,UAAM,KAAK,CAAC;AAAA,EACd;AACA,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,IAAI,KAAK,IAAI,KAAK;AACxB,UAAM,KAAK,SAAU,KAAK,KAAM,EAAE,IAAI,SAAU,KAAK,KAAM,EAAE,IAAI,IAAI;AAAA,EACvE,WAAW,MAAM,SAAS,GAAG;AAC3B,UAAM,IAAK,KAAK,IAAI,KAAK,KAAO,KAAK,OAAO,CAAC,KAAK;AAClD,UAAM,KAAK,SAAU,KAAK,KAAM,EAAE,IAAI,SAAU,KAAK,KAAM,EAAE,IAAI,SAAU,KAAK,IAAK,EAAE,IAAI,GAAG;AAAA,EAChG;AACA,SAAO,MAAM,KAAK,EAAE;AACtB;AAEA,SAAS,WAAW,SAA6B;AAC/C,MAAI,WAAW,QAAQ;AACvB,SAAO,WAAW,KAAK,QAAQ,WAAW,WAAW,CAAC,MAAM,GAAI;AAChE,QAAM,MAAM,IAAI,WAAY,WAAW,KAAM,CAAC;AAC9C,MAAI,IAAI,GAAG,MAAM,GAAG,OAAO;AAC3B,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,UAAM,OAAO,QAAQ,WAAW,CAAC;AACjC,UAAM,IAAI,OAAO,MAAM,QAAQ,IAAI,IAAI;AACvC,QAAI,IAAI,EAAG;AACX,UAAO,OAAO,IAAK;AACnB,YAAQ;AACR,QAAI,QAAQ,GAAG;AACb,cAAQ;AACR,UAAI,GAAG,IAAK,OAAO,OAAQ;AAAA,IAC7B;AAAA,EACF;AACA,SAAO,MAAM,IAAI,SAAS,MAAM,IAAI,SAAS,GAAG,CAAC;AACnD;AAGO,IAAM,iBAAiC,cAC1C,EAAE,QAAQ,eAAe,QAAQ,cAAc,IAC/C,EAAE,QAAQ,YAAY,QAAQ,WAAW;;;ACtD7C,IAAM,cAAc;AACpB,IAAM,YAAY;AAClB,IAAM,iBAAiB;AACvB,IAAM,aAAa;AAQZ,SAAS,KAAK,GAAmB;AACtC,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,UAAM,OAAO,EAAE,CAAC,EAAG,UAAU,KAAK,EAAE,CAAC;AACrC,UAAM,QAAQ,KAAK,YAAY;AAE/B,WAAO,MAAM,WAAW,IAAI,QAAQ,MAAM,CAAC;AAAA,EAC7C;AACA,SAAO;AACT;AAGO,SAAS,YAAY,QAAgB,GAAoB;AAC9D,MAAI,MAAM,EAAG,QAAO;AACpB,SAAO,CAAC,WAAW,KAAK,OAAO,IAAI,CAAC,CAAE;AACxC;AAEA,IAAM,eAAe,CAAC,MAAc,KAAK,IAAI,IAAI,GAAG,GAAG;AACvD,IAAM,gBAAgB,CAAC,UAAkB,aAAqB,KAAK,IAAI,KAAK,IAAI,WAAW,UAAU,CAAC,GAAG,GAAG;AAOrG,SAAS,WAAW,OAAe,OAAkC;AAC1E,QAAM,IAAI,KAAK,MAAM,KAAK,CAAC;AAC3B,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,KAAK,KAAK;AAEpB,MAAI,QAAQ;AACZ,MAAI,SAAS;AACb,WAAS,IAAI,EAAE,QAAQ,CAAC,GAAG,MAAM,IAAI,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC,GAAG;AAC5D,QAAI,UAAU,GAAI,SAAQ;AAC1B,QAAI,YAAY,GAAG,CAAC,GAAG;AACrB,eAAS;AACT;AAAA,IACF;AAAA,EACF;AAEA,MAAI,UAAU,GAAG;AACf,WAAO,EAAE,OAAO,cAAc,cAAc,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,EAAE,OAAO,CAAC,EAAE;AAAA,EACzG;AACA,MAAI,WAAW,IAAI;AACjB,WAAO;AAAA,MACL,OAAO,YAAY,aAAa,MAAM,IAAI,cAAc,EAAE,QAAQ,EAAE,MAAM;AAAA,MAC1E,QAAQ,CAAC,EAAE,OAAO,QAAQ,KAAK,SAAS,EAAE,OAAO,CAAC;AAAA,IACpD;AAAA,EACF;AACA,MAAI,UAAU,IAAI;AAChB,WAAO;AAAA,MACL,OAAO,iBAAiB,aAAa,KAAK,IAAI,cAAc,EAAE,QAAQ,EAAE,MAAM;AAAA,MAC9E,QAAQ,CAAC,EAAE,OAAO,OAAO,KAAK,QAAQ,EAAE,OAAO,CAAC;AAAA,IAClD;AAAA,EACF;AAKA,QAAM,QAAQ,EAAE,QAAQ,QAAQ,EAAE;AAClC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAuB,CAAC;AAC9B,MAAI,OAAO;AACX,WAAS,KAAK,GAAG,KAAK,MAAM,QAAQ,MAAM;AACxC,UAAM,KAAK,EAAE,QAAQ,MAAM,EAAE,GAAI,IAAI;AACrC,QAAI,OAAO,GAAI,QAAO;AACtB,UAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,QAAI,QAAQ,KAAK,QAAQ,GAAI,MAAK,MAAM,KAAK;AAAA,QACxC,QAAO,KAAK,EAAE,OAAO,IAAI,KAAK,KAAK,EAAE,CAAC;AAC3C,WAAO,KAAK;AAAA,EACd;AACA,QAAM,WAAW,OAAO,CAAC,EAAG;AAC5B,QAAM,SAAS,OAAO,OAAO,SAAS,CAAC,EAAG,MAAM,WAAW,MAAM;AACjE,QAAM,QAAQ,aAAa,KAAK,IAAI,SAAS,GAAG,GAAG,IAAI,KAAK,IAAI,WAAW,GAAG,GAAG,IAAI,cAAc,EAAE,QAAQ,MAAM,MAAM;AACzH,SAAO,EAAE,OAAO,OAAO;AACzB;AAYO,SAAS,YACd,OACA,OACA,QAAQ,IACW;AACnB,QAAM,MAAyB,CAAC;AAChC,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,WAAW,OAAO,KAAK,KAAK;AACtC,QAAI,EAAG,KAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,OAAO,CAAC;AAAA,EAC5D;AACA,MAAI,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS;AAC3E,SAAO,IAAI,MAAM,GAAG,KAAK;AAC3B;;;AC7HA,IAAM,eAAe,oBAAI,IAAoB;AAC7C,IAAM,kBAAkB,oBAAI,IAAoB;AAChD,IAAI,QAAQ;AAML,SAAS,aAAa,SAAiB,IAAwB;AACpE,eAAa,IAAI,SAAS,EAAE;AAC5B,SAAO,MAAM;AACX,QAAI,aAAa,IAAI,OAAO,MAAM,GAAI,cAAa,OAAO,OAAO;AAAA,EACnE;AACF;AAOO,SAAS,kBAAkB,SAA0B;AAC1D,QAAMC,QAAO,aAAa,IAAI,OAAO;AACrC,MAAI,CAACA,MAAM,QAAO;AAClB,EAAAA,MAAK;AACL,SAAO;AACT;AAGO,SAAS,cAAc,IAAmB;AAC/C,UAAQ;AACR,aAAW,KAAK,gBAAiB,GAAE,EAAE;AACvC;AAMO,SAAS,YAAY,IAAgC;AAC1D,kBAAgB,IAAI,EAAE;AACtB,KAAG,KAAK;AACR,SAAO,MAAM,gBAAgB,OAAO,EAAE;AACxC;AAOO,SAAS,mBAAyB;AACvC,eAAa,MAAM;AACnB,UAAQ;AACV;;;ACfO,SAAS,cAAc,KAA4B;AACxD,QAAM,OAAO,IAAI,KAAK;AACtB,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,qCAAqC;AAGhE,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,UAAM,WAAW,KAAK,MAAM,KAAK,QAAQ,GAAG,CAAC;AAE7C,QAAI;AACF,YAAM,QAAQ,qBAAqB,QAAQ;AAC3C,aAAO;AAAA,QACL,MAAM;AAAA,QACN,WAAW,SAAS,MAAM,QAAQ,MAAM,EAAE,CAAC;AAAA,QAC3C,WAAW,MAAM;AAAA,QACjB;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AACA,QAAI;AACF,YAAM,QAAQ,sBAAsB,QAAQ;AAC5C,aAAO,EAAE,MAAM,cAAc,WAAW,MAAM,WAAW,OAAO,MAAM,OAAO,MAAM;AAAA,IACrF,QAAQ;AACN,YAAM,IAAI,MAAM,oDAAoD;AAAA,IACtE;AAAA,EACF;AAGA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,UAAM,IAAI,MAAM,uEAAuE;AAAA,EACzF;AACA,MAAI,CAAC,QAAQ,WAAW,OAAO,KAAK,SAAS,UAAU;AACrD,UAAM,IAAI,MAAM,mCAAmC;AAAA,EACrD;AACA,QAAM,MAAM,OAAO,KAAK;AACxB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW,OAAO,WAAW,KAAK,KAAK,SAAS,OAAO,QAAQ,MAAM,EAAE,CAAC;AAAA,IACxE,SAAS,OAAO;AAAA,IAChB,WAAW,OAAO,QAAQ,YAAY,IAAI,UAAU,IAAI,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,SAAI,IAAI,MAAM,EAAE,CAAC,KAAK;AAAA,IAChG,YAAY;AAAA,EACd;AACF;","names":["key","key","key","cache","encryptor","ConflictError","shared","name","short","image","bytesToHex","key","req","generateDeviceKeys","addCollectionRecipient","mintMemberCap","isAlreadyPresentRecipient","req","addCollectionRecipient","mintMemberCap","generateDeviceKeys","updateSpacesDoc","StarfishClient","pull"]}
|