snapback2 0.0.1

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.
Files changed (132) hide show
  1. package/README.md +127 -0
  2. package/bin/snapback2.mjs +36 -0
  3. package/dist/api.d.ts +20 -0
  4. package/dist/assets.d.ts +34 -0
  5. package/dist/client-assets.d.ts +26 -0
  6. package/dist/client-offline.d.ts +94 -0
  7. package/dist/client-outbox.d.ts +58 -0
  8. package/dist/client-upload.d.ts +41 -0
  9. package/dist/client-wire.d.ts +41 -0
  10. package/dist/client.d.ts +12 -0
  11. package/dist/client.mjs +5 -0
  12. package/dist/client.mjs.map +7 -0
  13. package/dist/compiler-core.mjs +60 -0
  14. package/dist/compiler-core.mjs.map +7 -0
  15. package/dist/compiler-lib.d.ts +71 -0
  16. package/dist/compiler.mjs +5 -0
  17. package/dist/compiler.mjs.map +7 -0
  18. package/dist/drain.d.ts +33 -0
  19. package/dist/expo/files.d.ts +3 -0
  20. package/dist/expo/index.d.ts +16 -0
  21. package/dist/expo/token-store.d.ts +7 -0
  22. package/dist/expo/witness.d.ts +28 -0
  23. package/dist/expo/witness.mjs +2 -0
  24. package/dist/expo/witness.mjs.map +7 -0
  25. package/dist/expo.d.ts +4 -0
  26. package/dist/expo.mjs +9 -0
  27. package/dist/expo.mjs.map +7 -0
  28. package/dist/guide/auth.md +89 -0
  29. package/dist/guide/effects.md +52 -0
  30. package/dist/guide/families.json +446 -0
  31. package/dist/guide/grammar.md +52 -0
  32. package/dist/guide/live-query.md +27 -0
  33. package/dist/guide/offline.md +75 -0
  34. package/dist/guide/personas.md +15 -0
  35. package/dist/guide/quarry.md +297 -0
  36. package/dist/guide/testing.md +57 -0
  37. package/dist/index.d.ts +362 -0
  38. package/dist/index.mjs +2 -0
  39. package/dist/index.mjs.map +7 -0
  40. package/dist/offline-protocol.d.ts +180 -0
  41. package/dist/offline-schema.d.ts +1 -0
  42. package/dist/offline.d.ts +177 -0
  43. package/dist/react-core.d.ts +91 -0
  44. package/dist/react-core.mjs +2 -0
  45. package/dist/react-core.mjs.map +7 -0
  46. package/dist/react-native/components.d.ts +23 -0
  47. package/dist/react-native/files.d.ts +43 -0
  48. package/dist/react-native/source.d.ts +16 -0
  49. package/dist/react-native.d.ts +4 -0
  50. package/dist/react-native.mjs +2 -0
  51. package/dist/react-native.mjs.map +7 -0
  52. package/dist/react.d.ts +19 -0
  53. package/dist/react.mjs +2 -0
  54. package/dist/react.mjs.map +7 -0
  55. package/dist/sqlite-test.mjs +1720 -0
  56. package/dist/sqlite-test.mjs.map +7 -0
  57. package/dist/sse.d.ts +44 -0
  58. package/dist/store/byte-cache.d.ts +162 -0
  59. package/dist/store/canonical.d.ts +2 -0
  60. package/dist/store/indexeddb.d.ts +23 -0
  61. package/dist/store/locks.d.ts +11 -0
  62. package/dist/store/outbox.d.ts +113 -0
  63. package/dist/store/overlay-retirement.d.ts +17 -0
  64. package/dist/store/projection.d.ts +11 -0
  65. package/dist/store/range-store.d.ts +388 -0
  66. package/dist/store/sqlite-driver.conformance.d.ts +7 -0
  67. package/dist/store/sqlite-driver.d.ts +16 -0
  68. package/dist/store/sqlite-expo.d.ts +3 -0
  69. package/dist/store/sqlite.d.ts +31 -0
  70. package/dist/templates/chat/expo/App.tsx +29 -0
  71. package/dist/templates/chat/expo/app.json +11 -0
  72. package/dist/templates/chat/expo/index.js +3 -0
  73. package/dist/templates/chat/expo/shared/log.js +92 -0
  74. package/dist/templates/chat/expo/shared/log.ts +130 -0
  75. package/dist/templates/chat/expo/shared/offline.js +8 -0
  76. package/dist/templates/chat/expo/shared/offline.ts +9 -0
  77. package/dist/templates/chat/expo/src/Chat.tsx +23 -0
  78. package/dist/templates/chat/expo/src/Composer.tsx +23 -0
  79. package/dist/templates/chat/expo/src/MediaView.tsx +29 -0
  80. package/dist/templates/chat/expo/src/Witness.tsx +131 -0
  81. package/dist/templates/chat/expo/src/screens/Inbox.tsx +9 -0
  82. package/dist/templates/chat/expo/src/screens/Search.tsx +10 -0
  83. package/dist/templates/chat/expo/src/screens/Thread.tsx +22 -0
  84. package/dist/templates/chat/expo/src/witness-state.ts +68 -0
  85. package/dist/templates/chat/expo/tsconfig.json +5 -0
  86. package/dist/templates/chat/react/index.html +5 -0
  87. package/dist/templates/chat/react/shared/log.js +92 -0
  88. package/dist/templates/chat/react/shared/log.ts +130 -0
  89. package/dist/templates/chat/react/shared/offline.js +8 -0
  90. package/dist/templates/chat/react/shared/offline.ts +9 -0
  91. package/dist/templates/chat/react/src/App.tsx +36 -0
  92. package/dist/templates/chat/react/src/Composer.tsx +45 -0
  93. package/dist/templates/chat/react/src/Inbox.tsx +24 -0
  94. package/dist/templates/chat/react/src/Search.tsx +26 -0
  95. package/dist/templates/chat/react/src/Thread.tsx +126 -0
  96. package/dist/templates/chat/react/src/env.d.ts +1 -0
  97. package/dist/templates/chat/react/src/main.tsx +20 -0
  98. package/dist/templates/chat/shared/log.js +92 -0
  99. package/dist/templates/chat/shared/log.ts +130 -0
  100. package/dist/templates/chat/shared/offline.js +8 -0
  101. package/dist/templates/chat/shared/offline.ts +9 -0
  102. package/dist/templates/chat/snapback/deliveries.q +33 -0
  103. package/dist/templates/chat/snapback/delivery.ts +30 -0
  104. package/dist/templates/chat/snapback/feeds.q +14 -0
  105. package/dist/templates/chat/snapback/follows.q +11 -0
  106. package/dist/templates/chat/snapback/groups.q +40 -0
  107. package/dist/templates/chat/snapback/messages.q +32 -0
  108. package/dist/templates/chat/snapback/notifications.q +7 -0
  109. package/dist/templates/chat/snapback/posts.q +4 -0
  110. package/dist/templates/chat/snapback/profiles.q +17 -0
  111. package/dist/templates/chat/snapback/schema.q +136 -0
  112. package/dist/templates/chat/snapback/seed.ts +45 -0
  113. package/dist/templates/chat/snapback/tests/chat.test.ts +759 -0
  114. package/dist/templates/react/index.html +5 -0
  115. package/dist/templates/react/src/App.tsx +5 -0
  116. package/dist/templates/react/src/main.tsx +18 -0
  117. package/dist/templates/todos/snapback/schema.q +11 -0
  118. package/dist/templates/todos/snapback/seed.ts +10 -0
  119. package/dist/templates/todos/snapback/tests/todos.test.ts +13 -0
  120. package/dist/templates/todos/snapback/todos.q +6 -0
  121. package/dist/test-runner.mjs +8541 -0
  122. package/dist/test-runner.mjs.map +7 -0
  123. package/dist/test.d.ts +28 -0
  124. package/dist/test.mjs +8541 -0
  125. package/dist/test.mjs.map +7 -0
  126. package/dist/token-store.d.ts +3 -0
  127. package/dist/twin-hydrate.d.ts +43 -0
  128. package/dist/twin.d.ts +70 -0
  129. package/dist/types.d.ts +410 -0
  130. package/dist/witness-test.mjs +2 -0
  131. package/dist/witness-test.mjs.map +7 -0
  132. package/package.json +117 -0
@@ -0,0 +1,71 @@
1
+ interface SymbolConstructor { readonly iterator: unique symbol; }
2
+ declare const Symbol: SymbolConstructor;
3
+ interface IteratorResult<T> { done?: boolean; value: T; }
4
+ interface Iterator<T> { next(): IteratorResult<T>; }
5
+ interface Iterable<T> { [Symbol.iterator](): Iterator<T>; }
6
+ interface Array<T> extends Iterable<T> {
7
+ readonly length: number;
8
+ [index: number]: T;
9
+ map<U>(callback: (value: T) => U): U[];
10
+ flatMap<U>(callback: (value: T) => U[]): U[];
11
+ filter(callback: (value: T) => boolean): T[];
12
+ reduce<U>(callback: (value: U, item: T) => U, initial: U): U;
13
+ some(callback: (value: T) => boolean): boolean;
14
+ slice(start: number, end: number): T[];
15
+ sort(comparator: (a: T, b: T) => number): T[];
16
+ push(value: T): number;
17
+ count(): number;
18
+ countCapped(): { count: number; atCap: boolean };
19
+ }
20
+ interface ReadonlyArray<T> extends Array<T> {}
21
+ interface String { readonly length: number; split(separator: string): string[]; }
22
+ interface Number {}
23
+ interface Boolean {}
24
+ interface Function {}
25
+ interface Object {}
26
+ interface RegExp {}
27
+ interface IArguments {}
28
+ interface CallableFunction extends Function {}
29
+ interface NewableFunction extends Function {}
30
+ interface PromiseLike<T> { then<TResult>(onfulfilled: (value: T) => TResult | PromiseLike<TResult>): PromiseLike<TResult>; }
31
+ interface Promise<T> extends PromiseLike<T> {}
32
+ declare const Promise: { resolve<T>(value: T): Promise<T> };
33
+ declare class Map<K, V> {
34
+ constructor(values?: Array<[K, V]>);
35
+ readonly size: number;
36
+ get(key: K): V | undefined;
37
+ set(key: K, value: V): this;
38
+ has(key: K): boolean;
39
+ }
40
+ declare class Set<T> {
41
+ constructor(values?: Array<T>);
42
+ readonly size: number;
43
+ add(value: T): this;
44
+ has(value: T): boolean;
45
+ }
46
+ declare const Math: {
47
+ min(first: number, second: number, ...values: number[]): number;
48
+ max(first: number, second: number, ...values: number[]): number;
49
+ abs(value: number): number;
50
+ random(): number;
51
+ };
52
+ declare const JSON: { stringify(value: unknown): string; parse(value: string): unknown };
53
+ declare const console: { log(...values: unknown[]): void; error(...values: unknown[]): void };
54
+ declare const Date: { now(): number; new(): unknown };
55
+ declare class URL { constructor(input: string, base?: string); readonly origin: string; }
56
+ declare class TextEncoder { encode(input?: string): unknown; }
57
+ declare class TextDecoder { decode(input?: unknown): string; }
58
+ declare function btoa(value: string): string;
59
+ declare function atob(value: string): string;
60
+ declare function setTimeout(callback: () => void, ms: number): number;
61
+ declare function fetch(input: string, init?: unknown): Promise<{ ok: boolean; status: number; json(): Promise<any>; text(): Promise<string> }>;
62
+ declare const meta: { readonly id: string; readonly effect?: string; readonly attempt?: number };
63
+ declare function eval(source: string): unknown;
64
+ declare const crypto: { randomUUID(): string };
65
+ declare class Error { constructor(message?: string); readonly message: string; }
66
+ type Record<K extends string | number | symbol, T> = { [P in K]: T };
67
+ type Exclude<T, U> = T extends U ? never : T;
68
+ type Pick<T, K extends keyof T> = { [P in K]: T[P] };
69
+ type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
70
+ type Partial<T> = { [P in keyof T]?: T[P] };
71
+ type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import{createInterface as m}from"node:readline";import{access as u}from"node:fs/promises";import{createRequire as f}from"node:module";import{isAbsolute as w,resolve as g}from"node:path";var p="snapback2/compiler-worker/v1",d=["compile","seed","journeys"],y=new URL("./compiler-core.mjs",import.meta.url),k,j=()=>k??=import("./compiler-core.mjs"),c=new Map;async function v(o){if(!o||typeof o.root!="string"||!w(o.root))return e(o?.id,"E_OP_INVALID","compiler request requires an absolute project root");if(o.op==="probe"){try{await u(y)}catch{return a(o.id,"compiler-core.mjs")}try{f(import.meta.url).resolve("typescript")}catch{return a(o.id,"typescript")}return{id:o.id,ok:!0,protocol:p,operations:d}}let{compileJourneys:r,lowerProject:i,lowerSeed:t}=await j();if(o.op==="compile"){let n=await i(o.root);return n.ok&&c.set(o.root,n.apiDts),{id:o.id,...n}}if(o.op==="seed")return{id:o.id,...await t(o.root)};if(o.op==="journeys"){let n=c.get(o.root);if(n===void 0){let s=await i(o.root);if(!s.ok)return{id:o.id,...s};n=s.apiDts,c.set(o.root,n)}return{id:o.id,...await r(o.root,n)}}return e(o.id,"E_OP_INVALID",`unknown compiler operation ${o.op??"<missing>"}`)}function a(o,r){return{id:o,ok:!1,protocol:p,operations:d,dependency:r}}function e(o,r,i){return{id:o,ok:!1,diagnostics:[{code:r,family:"op",message:i,site:"<worker>:1:1",rewrite:"send { id, op: 'probe' | 'compile' | 'seed' | 'journeys', root: '/absolute/project' }",guide:"programs/operations"}]}}async function l(o){try{return await v(o)}catch(r){return e(o?.id,"E_OP_INVALID",r instanceof Error?r.message:String(r))}}async function P(){let o=m({input:process.stdin,crlfDelay:1/0});for await(let r of o){if(!r.trim())continue;let i;try{i=JSON.parse(r)}catch(t){process.stdout.write(`${JSON.stringify(e(void 0,"E_OP_INVALID",`invalid JSON: ${String(t)}`))}
3
+ `);continue}process.stdout.write(`${JSON.stringify(await l(i))}
4
+ `)}}async function R(){if(process.argv.includes("--worker")){await P();return}let o=process.argv.indexOf("--project");if(o<0||!process.argv[o+1]){console.error("usage: node compiler.mjs --worker | --project <root>"),process.exitCode=2;return}let r=await l({op:"compile",root:g(process.argv[o+1])});delete r.id,console.log(JSON.stringify(r,null,2)),r.ok||(process.exitCode=1)}await R();
5
+ //# sourceMappingURL=compiler.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../compiler/src/worker.ts"],
4
+ "sourcesContent": ["#!/usr/bin/env node\nimport { createInterface } from \"node:readline\";\nimport { access } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { isAbsolute, resolve } from \"node:path\";\n\ntype Request = { id?: string | number; op: \"probe\" | \"compile\" | \"seed\" | \"journeys\"; root: string };\n\nconst protocol = \"snapback2/compiler-worker/v1\";\nconst operations = [\"compile\", \"seed\", \"journeys\"] as const;\n// @ref LLP 1002#3-the-compiler-worker-and-the-project-json \u2014 doctor proves the fixed worker closure without eagerly parsing the heavy lowering graph.\nconst compilerCore = new URL(\"./compiler-core.mjs\", import.meta.url);\nlet compilerPromise: Promise<typeof import(\"./project\")> | undefined;\nconst loadCompiler = () => compilerPromise ??= import(\"./project\");\n\nconst apiDeclarations = new Map<string, string>();\n\nasync function respond(request: Request): Promise<Record<string, unknown>> {\n if (!request || typeof request.root !== \"string\" || !isAbsolute(request.root)) {\n return refusal(request?.id, \"E_OP_INVALID\", \"compiler request requires an absolute project root\");\n }\n if (request.op === \"probe\") {\n try {\n await access(compilerCore);\n } catch {\n return dependencyRefusal(request.id, \"compiler-core.mjs\");\n }\n try {\n createRequire(import.meta.url).resolve(\"typescript\");\n } catch {\n return dependencyRefusal(request.id, \"typescript\");\n }\n return { id: request.id, ok: true, protocol, operations };\n }\n const { compileJourneys, lowerProject, lowerSeed } = await loadCompiler();\n if (request.op === \"compile\") {\n const result = await lowerProject(request.root);\n if (result.ok) apiDeclarations.set(request.root, result.apiDts);\n return { id: request.id, ...result };\n }\n if (request.op === \"seed\") return { id: request.id, ...await lowerSeed(request.root) };\n if (request.op === \"journeys\") {\n let apiDts = apiDeclarations.get(request.root);\n if (apiDts === undefined) {\n const compiled = await lowerProject(request.root);\n if (!compiled.ok) return { id: request.id, ...compiled };\n apiDts = compiled.apiDts;\n apiDeclarations.set(request.root, apiDts);\n }\n return { id: request.id, ...await compileJourneys(request.root, apiDts) };\n }\n return refusal(request.id, \"E_OP_INVALID\", `unknown compiler operation ${(request as { op?: unknown }).op ?? \"<missing>\"}`);\n}\n\nfunction dependencyRefusal(id: unknown, dependency: string): Record<string, unknown> {\n return { id, ok: false, protocol, operations, dependency };\n}\n\nfunction refusal(id: unknown, code: string, message: string): Record<string, unknown> {\n return {\n id,\n ok: false,\n diagnostics: [{\n code,\n family: \"op\",\n message,\n site: \"<worker>:1:1\",\n rewrite: \"send { id, op: 'probe' | 'compile' | 'seed' | 'journeys', root: '/absolute/project' }\",\n guide: \"programs/operations\",\n }],\n };\n}\n\nasync function safeRespond(value: unknown): Promise<Record<string, unknown>> {\n try {\n return await respond(value as Request);\n } catch (error) {\n return refusal((value as { id?: unknown } | null)?.id, \"E_OP_INVALID\", error instanceof Error ? error.message : String(error));\n }\n}\n\nasync function runWorker(): Promise<void> {\n const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });\n for await (const line of lines) {\n if (!line.trim()) continue;\n let value: unknown;\n try {\n value = JSON.parse(line);\n } catch (error) {\n process.stdout.write(`${JSON.stringify(refusal(undefined, \"E_OP_INVALID\", `invalid JSON: ${String(error)}`))}\\n`);\n continue;\n }\n process.stdout.write(`${JSON.stringify(await safeRespond(value))}\\n`);\n }\n}\n\nasync function main(): Promise<void> {\n if (process.argv.includes(\"--worker\")) {\n await runWorker();\n return;\n }\n const projectAt = process.argv.indexOf(\"--project\");\n if (projectAt < 0 || !process.argv[projectAt + 1]) {\n console.error(\"usage: node compiler.mjs --worker | --project <root>\");\n process.exitCode = 2;\n return;\n }\n const response = await safeRespond({ op: \"compile\", root: resolve(process.argv[projectAt + 1]) });\n delete response.id;\n console.log(JSON.stringify(response, null, 2));\n if (!response.ok) process.exitCode = 1;\n}\n\nawait main();\n"],
5
+ "mappings": "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",
6
+ "names": ["createInterface", "access", "createRequire", "isAbsolute", "resolve", "protocol", "operations", "compilerCore", "compilerPromise", "loadCompiler", "apiDeclarations", "respond", "request", "refusal", "dependencyRefusal", "compileJourneys", "lowerProject", "lowerSeed", "result", "apiDts", "compiled", "id", "dependency", "code", "message", "safeRespond", "value", "error", "runWorker", "lines", "line", "main", "projectAt", "response"]
7
+ }
@@ -0,0 +1,33 @@
1
+ import type { Cursor, QueryResult, Subscription, SubscriptionDelivery } from "./types.js";
2
+ export interface DrainOptions<Args, Result> {
3
+ args: Args;
4
+ query(args: Args): Promise<QueryResult<Result>>;
5
+ subscribe(args: Args, callback: (event: SubscriptionDelivery<Result>) => void): Subscription<SubscriptionDelivery<Result>>;
6
+ /** `offline`: every page came from the store; the watermark is the persisted one and nothing is recorded. */
7
+ mark(watermark: Cursor | null, current: () => boolean, offline: boolean): Promise<void>;
8
+ onEvent(event: SubscriptionDelivery<Result>): void;
9
+ /** The cursor argument's name, resolved when a page is built (the manifest may still be loading); `c` by default. */
10
+ cursor?(): Promise<string> | string;
11
+ }
12
+ /** Follows a cursor to exhaustion, records its watermark, then watches the tail. */
13
+ export declare class DrainSubscription<Args, Result> implements Subscription<SubscriptionDelivery<Result>> {
14
+ private readonly options;
15
+ private readonly waiters;
16
+ private tail?;
17
+ private last?;
18
+ private watermark;
19
+ private cursorName;
20
+ private closed;
21
+ private generation;
22
+ constructor(options: DrainOptions<Args, Result>);
23
+ close(): void;
24
+ /** Discard a stale cursor after E_OFFLINE_RESET and cold-drain from the prefix root. */
25
+ reset(): void;
26
+ until(predicate: (event: SubscriptionDelivery<Result>) => boolean, timeoutMs?: number): Promise<SubscriptionDelivery<Result>>;
27
+ private start;
28
+ private current;
29
+ private drain;
30
+ private watchTail;
31
+ private deliver;
32
+ private fail;
33
+ }
@@ -0,0 +1,3 @@
1
+ import type { NativeFileSystem } from "../react-native/files.js";
2
+ /** SDK 54 File.write accepts Uint8Array directly; no Blob or base64 file ingress. */
3
+ export declare function expoAssetFileSystem(): NativeFileSystem;
@@ -0,0 +1,16 @@
1
+ import type { CreateClientOptions, OfflineClientOptions, SnapbackClient } from "../types.js";
2
+ import { type SqliteRangeStoreOptions } from "../store/sqlite.js";
3
+ import { type RangeStoreFactory } from "../store/range-store.js";
4
+ export { expoSecureTokenStore } from "./token-store.js";
5
+ export { expoSqliteDriver } from "../store/sqlite-expo.js";
6
+ export { sqliteRangeStore, inProcessLocks } from "../store/sqlite.js";
7
+ export type { SqliteDriver, SqliteExecutor, SqliteParams, SqliteRangeStoreOptions } from "../store/sqlite.js";
8
+ export declare function expoSqliteStore(name?: string, options?: SqliteRangeStoreOptions): RangeStoreFactory;
9
+ export type CreateExpoClientOptions = CreateClientOptions extends infer Options ? Options extends CreateClientOptions ? Omit<Options, "offline"> & {
10
+ /** Restore a persisted transport cut before the first request or carrier resume. */
11
+ startOffline?: boolean;
12
+ offline?: Omit<OfflineClientOptions, "store"> & {
13
+ store?: OfflineClientOptions["store"];
14
+ };
15
+ } : never : never;
16
+ export declare function createExpoClient(options: CreateExpoClientOptions): SnapbackClient;
@@ -0,0 +1,7 @@
1
+ import type { AuthTokenStore } from "../types.js";
2
+ export interface SecureStoreAdapter {
3
+ getItemAsync(key: string): Promise<string | null>;
4
+ setItemAsync(key: string, value: string): Promise<void>;
5
+ deleteItemAsync(key: string): Promise<void>;
6
+ }
7
+ export declare function expoSecureTokenStore(name?: string, adapter?: SecureStoreAdapter): AuthTokenStore;
@@ -0,0 +1,28 @@
1
+ import { twinLastDecline } from "../twin.js";
2
+ import type { SnapbackClient } from "../types.js";
3
+ import type { PersistedRangeState } from "../store/range-store.js";
4
+ import type { ByteDelta } from "../store/byte-cache.js";
5
+ import type { SqliteDriver } from "../store/sqlite-driver.js";
6
+ export interface WitnessHooks {
7
+ beforeCommit(state: PersistedRangeState, bytes: ByteDelta): Promise<void>;
8
+ emit(event: string, value?: unknown): void;
9
+ }
10
+ export interface WitnessApplication {
11
+ construct(hooks: WitnessHooks, args: Record<string, unknown>): Promise<SnapbackClient>;
12
+ /** Persist transport/fault configuration outside the range store, before acknowledging. */
13
+ persist?(change: {
14
+ offline?: boolean;
15
+ fault?: "carrier" | "none";
16
+ }): Promise<void>;
17
+ command?(client: SnapbackClient | undefined, verb: string, args: Record<string, unknown>, hooks: WitnessHooks): Promise<unknown>;
18
+ }
19
+ export interface WitnessControl {
20
+ close(): void;
21
+ }
22
+ /** The factory overload connects before any carrier opens; the client overload attaches to an existing client. */
23
+ export { twinLastDecline };
24
+ export declare function witnessControl(source: SnapbackClient | WitnessApplication, url: string): WitnessControl;
25
+ export declare function runSqliteDriverConformance(open: () => SqliteDriver | Promise<SqliteDriver>): Promise<{
26
+ journalMode: string;
27
+ checks: string[];
28
+ }>;
@@ -0,0 +1,2 @@
1
+ var I={systemPrincipalId:"system",retentionPolicies:["until-revoked","delivered-history"],valueModelVersion:"i53-codepoint-bytes-v2"};var N=9007199254740991,fe=-N;var ze=I.valueModelVersion;function Y(){return globalThis.__snapbackTwinLast}import{isSnapbackRefusal as Q}from"snapback2/client";async function T(c){let r=await c(),u=await c(),n=[],s=(a,l)=>{if(!a)throw new Error(`sqlite conformance: ${l}`)};try{let a=await r.get("PRAGMA journal_mode=WAL"),l=await r.get("PRAGMA journal_mode");s(a?.journal_mode===l?.journal_mode,"journal_mode readback"),n.push("journal_mode readback"),await r.exec("CREATE TABLE IF NOT EXISTS conformance (key TEXT PRIMARY KEY, value BLOB NOT NULL); DELETE FROM conformance;");for(let e of[1,1024*1024,0]){let y=new Uint8Array(e);for(let p=0;p<e;p++)y[p]=p%251;await r.run("INSERT INTO conformance VALUES (?, ?)",[String(e),y]);let E=await r.get("SELECT value FROM conformance WHERE key = ?",[String(e)]);s(E?.value instanceof Uint8Array&&E.value.length===e&&E.value.every((p,V)=>p===y[V]),`BLOB ${e}`),n.push(`BLOB ${e}`)}let f=[["messages","m\0x"],["messages","m\0y"],["messages",""],["messages",'quote",\u96EA'],["thread",JSON.stringify({nested:{words:["a","b"],empty:null}})]];for(let e of f)await r.run("INSERT INTO conformance VALUES (?, ?)",[JSON.stringify(e),new Uint8Array([9])]);for(let e of f){let y=await r.get("SELECT key FROM conformance WHERE key = ?",[JSON.stringify(e)]);s(y?.key===JSON.stringify(e),"JSON tuple key round trip")}n.push("JSON tuple key round trip");let g=await r.get("SELECT key FROM conformance WHERE key = ?",["missing"]);s(g===void 0,"missing row is undefined"),n.push("missing row is undefined");let h=new Error("rollback marker");try{throw await r.transaction(async e=>{throw await e.run("INSERT INTO conformance VALUES (?, ?)",["rollback",new Uint8Array([1])]),await Promise.resolve(),h}),new Error("transaction swallowed throw")}catch(e){s(e===h,"rollback propagates the body error")}s(await r.get("SELECT key FROM conformance WHERE key = ?",["rollback"])===void 0,"rollback on throw"),n.push("rollback on throw");let d=!1;await r.transaction(async e=>{await e.run("INSERT INTO conformance VALUES (?, ?)",["failed-commit",new Uint8Array([7])]),await e.run("ROLLBACK")}).catch(()=>{d=!0}),s(d,"COMMIT after rollback must fail"),s(await r.get("SELECT key FROM conformance WHERE key = ?",["failed-commit"])===void 0,"failed COMMIT leaves no row"),await r.transaction(e=>e.run("INSERT INTO conformance VALUES (?, ?)",["after-failed-commit",new Uint8Array([8])])),s(await r.get("SELECT key FROM conformance WHERE key = ?",["after-failed-commit"])!==void 0,"next transaction after failed COMMIT succeeds"),n.push("body-ended transaction rejects and next transaction succeeds");let b,x,m=!1,t=!1,i=new Promise(e=>{b=e}),o=new Promise(e=>{x=e}),k=r.transaction(async e=>(await e.run("INSERT INTO conformance VALUES (?, ?)",["barrier",new Uint8Array([2])]),x(),await i,42)).then(e=>(m=!0,e));await o;let A=u.run("INSERT INTO conformance VALUES (?, ?)",["competing",new Uint8Array([3])]).then(()=>{m||(t=!0)},e=>{s(/busy|locked/i.test(String(e)),`competing writer must busy or wait: ${e}`)}),v=!1,R=r.run("INSERT INTO conformance VALUES (?, ?)",["outer",new Uint8Array([4])]).then(()=>{v=!0});await new Promise(e=>setTimeout(e,25));let O=v;return b(),s(await k===42,"transaction result"),await Promise.all([A,R]),s(!t&&!O,"late beforeCommit holds transaction"),n.push("late beforeCommit holds transaction","transaction result"),{journalMode:l.journal_mode,checks:n}}finally{await r.close(),await u.close()}}function jt(c,r){if(!/^wss?:\/\//.test(r))throw new TypeError("witness URL must be ws:// or wss://");let u="construct"in c?c:void 0,n=u?void 0:c,s=!1,a,l,f=!1,g=new Set,h=t=>{a?.readyState===1&&a.send(JSON.stringify(t))},d={emit:(t,i)=>h({event:t,value:i}),async beforeCommit(){if(!f)return;let t=new Promise(i=>{g.add(i)});d.emit("barrier reached"),await t}},b=async t=>{if(u&&(n=await u.construct(d,t)),!n)throw new Error("construct did not return a client");return await n.ready,{constructed:!0,snapshot:n.snapshot}},x=async(t,i)=>{if(t==="barrier"){if(i.mode!=="on"&&i.mode!=="off")throw new TypeError("barrier mode must be on or off");if(f=i.mode==="on",!f){for(let o of g)o();g.clear()}return{barrier:i.mode}}if(t==="fault"){if(i.mode!=="carrier"&&i.mode!=="none")throw new TypeError("fault mode must be carrier or none");if(!u?.persist)throw new Error("witness application must persist carrier faults");return await u.persist({fault:i.mode}),{fault:i.mode}}if(t==="construct")return b(i);if(t==="offline"||t==="online"){if(!n)throw new Error("construct first");return await u?.persist?.({offline:t==="offline"}),await n[t](),{connection:n.snapshot.connection}}if(u?.command)return u.command(n,t,i,d);if(t==="state"&&n)return{snapshot:n.snapshot,outbox:await n.outbox.entries(),coverage:await n.assetCache?.store.coverage()};throw new Error(`unsupported witness verb ${t}`)},m=()=>{s||(a=new WebSocket(r),a.onopen=()=>d.emit("connected",{constructed:n!==void 0}),a.onmessage=t=>{(async()=>{let i;try{let o=JSON.parse(String(t.data));if(i=o.id,typeof o.verb!="string")throw new TypeError("witness command needs a verb");let k=await x(o.verb,o.args??{});h({id:i,ok:!0,result:k})}catch(o){h({id:i,ok:!1,error:Q(o)?o.envelope:{message:String(o),stack:o instanceof Error?o.stack:void 0}})}})()},a.onerror=()=>{},a.onclose=()=>{s||(l=setTimeout(m,250))})};return m(),{close(){s=!0,clearTimeout(l),a.close()}}}function Dt(c){return T(c)}export{Dt as runSqliteDriverConformance,Y as twinLastDecline,jt as witnessControl};
2
+ //# sourceMappingURL=witness.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../crates/snapback2-core/ir-contract.json", "../../../evaluator/src/value-runtime.ts", "../../../evaluator/src/runner.ts", "../../src/twin.ts", "../../src/expo/witness.ts", "../../src/store/sqlite-driver.conformance.ts"],
4
+ "sourcesContent": ["{\n \"systemPrincipalId\": \"system\",\n \"retentionPolicies\": [\"until-revoked\", \"delivered-history\"],\n \"valueModelVersion\": \"i53-codepoint-bytes-v2\"\n}\n", "import coreIrContract from \"../../../crates/snapback2-core/ir-contract.json\" with { type: \"json\" };\nimport { canonicalRuleJson, canonicalSchemaJson } from \"./certificate-identity\";\nimport { EvalError } from \"./errors\";\nimport { sha256, utf8Length } from \"./hash\";\nimport { cursorSuffix, decodeCursor, decodeCursorFor } from \"./cursor\";\nimport type { TraceRange, Value } from \"./types\";\n\nconst I53_MAX = 9_007_199_254_740_991;\nconst I53_MIN = -I53_MAX;\n\nexport function canonicalValue(value: Value): string {\n if (value === \"Null\") return '\"Null\"';\n const kind = kindOf(value);\n const data = (value as any)[kind];\n if (kind === \"Bool\" || kind === \"Int\") return `{\"${kind}\":${JSON.stringify(data)}}`;\n if (kind === \"String\" || kind === \"Id\") return `{\"${kind}\":${JSON.stringify(data)}}`;\n if (kind === \"Bytes\") {\n if (!canonicalBytes(data)) throw new EvalError(\"E_INPUT\", \"Bytes is not canonical lowercase base16\");\n return `{\"Bytes\":${JSON.stringify(data)}}`;\n }\n if (kind === \"Array\") return `{\"Array\":${canonicalValues(data)}}`;\n const entries = Object.keys(data)\n .sort(compareCodepoints)\n .map((key) => `${JSON.stringify(key)}:${canonicalValue(data[key])}`)\n .join(\",\");\n return `{\"${kind}\":{${entries}}}`;\n}\n\nexport function canonicalValues(values: Value[]): string {\n return `[${values.map(canonicalValue).join(\",\")}]`;\n}\n\nexport function rangeStops(ranges: TraceRange[]): Record<string, string[]> {\n const result: Record<string, string[]> = Object.create(null);\n for (const range of ranges) (result[range.site] ??= []).push(range.stop);\n return sortObject(result);\n}\n\n// @ref LLP 1001#2-what-a-commit-records \u2014 omitted Optional fields are Null before rules and maintains.\nexport function materializeOptionalFields(schema: any, tableName: string, value: Value): Value {\n if (!isVariant(value, \"Row\")) return clone(value);\n const entries: Record<string, Value> = Object.assign(Object.create(null), value.Row);\n for (const [name, descriptor] of Object.entries<any>(\n schema.tables?.[tableName]?.columns ?? {},\n )) {\n if (descriptor?.Optional !== undefined && !hasOwn(entries, name)) {\n entries[name] = \"Null\";\n }\n }\n return rowValue(entries);\n}\n\nexport function rowValue(entries: Record<string, Value>): Value {\n const sorted: Record<string, Value> = Object.create(null);\n for (const key of Object.keys(entries).sort(compareCodepoints)) sorted[key] = clone(entries[key]);\n return { Row: sorted };\n}\n\nexport function field(value: Value, name: string): Value {\n const object = asObject(value);\n if (!hasOwn(object, name)) throw new EvalError(\"E_FIELD\", `missing field ${name}`);\n return object[name];\n}\n\nexport function fieldOrNull(value: Value, name: string): Value {\n const object = asObject(value);\n return hasOwn(object, name) ? object[name] : \"Null\";\n}\n\nexport function asObject(value: Value): Record<string, Value> {\n if (isVariant(value, \"Row\")) return value.Row;\n if (isVariant(value, \"Map\")) return value.Map;\n throw new EvalError(\"E_TYPE\", \"expected row or map\");\n}\n\nexport function asArray(value: Value): Value[] {\n if (!isVariant(value, \"Array\")) throw new EvalError(\"E_TYPE\", \"expected array\");\n return value.Array;\n}\n\nexport function asString(value: Value): string {\n if (isVariant(value, \"String\")) return value.String;\n if (isVariant(value, \"Id\")) return value.Id;\n throw new EvalError(\"E_TYPE\", \"expected string or id\");\n}\n\nexport function asInt(value: Value): number {\n if (!isVariant(value, \"Int\")) throw new EvalError(\"E_TYPE\", \"expected int\");\n return value.Int;\n}\n\nexport function asBool(value: Value): boolean {\n if (!isVariant(value, \"Bool\")) throw new EvalError(\"E_TYPE\", \"expected bool\");\n return value.Bool;\n}\n\nexport function int(value: number): Value {\n return { Int: safeNumber(value) };\n}\n\nexport function safeNumber(value: number): number {\n if (!Number.isSafeInteger(value) || value < I53_MIN || value > I53_MAX)\n throw new EvalError(\"E_OVERFLOW\", `${value} is outside the i53 value model`);\n return value;\n}\n\nexport function totalCompare(left: Value, right: Value): number {\n const order = [\"Null\", \"Bool\", \"Int\", \"String\", \"Id\", \"Bytes\", \"Row\", \"Array\", \"Map\"];\n const leftKind = kindOf(left);\n const rightKind = kindOf(right);\n const tag = order.indexOf(leftKind) - order.indexOf(rightKind);\n if (tag !== 0) return Math.sign(tag);\n if (leftKind === \"Null\") return 0;\n const leftData = (left as any)[leftKind];\n const rightData = (right as any)[rightKind];\n if (leftKind === \"Bool\" || leftKind === \"Int\") return comparePrimitive(leftData, rightData);\n if (leftKind === \"String\" || leftKind === \"Id\") return compareCodepoints(leftData, rightData);\n if (leftKind === \"Bytes\") {\n if (!canonicalBytes(leftData) || !canonicalBytes(rightData))\n throw new EvalError(\"E_INPUT\", \"Bytes is not canonical lowercase base16\");\n return compareCodepoints(leftData, rightData);\n }\n if (leftKind === \"Array\") return compareArrays(leftData, rightData);\n const leftEntries = Object.keys(leftData).sort(compareCodepoints);\n const rightEntries = Object.keys(rightData).sort(compareCodepoints);\n const length = comparePrimitive(leftEntries.length, rightEntries.length);\n if (length !== 0) return length;\n for (let index = 0; index < leftEntries.length; index += 1) {\n const key = compareCodepoints(leftEntries[index], rightEntries[index]);\n if (key !== 0) return key;\n const value = totalCompare(leftData[leftEntries[index]], rightData[rightEntries[index]]);\n if (value !== 0) return value;\n }\n return 0;\n}\n\nexport function compareArrays(left: Value[], right: Value[]): number {\n const length = Math.min(left.length, right.length);\n for (let index = 0; index < length; index += 1) {\n const compared = totalCompare(left[index], right[index]);\n if (compared !== 0) return compared;\n }\n return comparePrimitive(left.length, right.length);\n}\n\nexport function compareCodepoints(left: string, right: string): number {\n const leftPoints = Array.from(left, (character) => character.codePointAt(0)!);\n const rightPoints = Array.from(right, (character) => character.codePointAt(0)!);\n const length = Math.min(leftPoints.length, rightPoints.length);\n for (let index = 0; index < length; index += 1) {\n const compared = comparePrimitive(leftPoints[index], rightPoints[index]);\n if (compared !== 0) return compared;\n }\n return comparePrimitive(leftPoints.length, rightPoints.length);\n}\n\nexport function comparePrimitive(left: number | boolean, right: number | boolean): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nexport function stableSort<T>(values: T[], compare: (left: T, right: T) => number): T[] {\n if (values.length <= 1) return values;\n const middle = Math.floor(values.length / 2);\n const left = stableSort(values.slice(0, middle), compare);\n const right = stableSort(values.slice(middle), compare);\n const result: T[] = [];\n while (left.length > 0 && right.length > 0) {\n result.push(compare(left[0], right[0]) <= 0 ? left.shift()! : right.shift()!);\n }\n return result.concat(left, right);\n}\n\nexport function variant(value: any): [string, any] {\n if (typeof value === \"string\") return [value, null];\n const keys = Object.keys(value);\n if (keys.length !== 1) throw new EvalError(\"E_CORPUS\", \"invalid externally tagged variant\");\n return [keys[0], value[keys[0]]];\n}\n\n/** The wire encoding of an IR value \u2014 ids and text as strings, integers as\n * numbers, null as null, bytes as `{ Bytes: hex }`, rows and maps as objects,\n * arrays as arrays \u2014 the form the delivery envelope carries\n * (`offline.rs` `value_to_wire_json`). Verdict inputs are digested in it. */\nexport function wireJson(value: Value): unknown {\n const [kind, data] = variant(value);\n switch (kind) {\n case \"Null\": return null;\n case \"Bool\": case \"Int\": case \"String\": case \"Id\": return data;\n case \"Bytes\": return { Bytes: data };\n case \"Row\": case \"Map\":\n return Object.fromEntries(Object.keys(data).sort(compareCodepoints).map((key) => [key, wireJson(data[key])]));\n case \"Array\": return data.map(wireJson);\n default: throw new EvalError(\"E_INPUT\", `unsupported value kind ${kind}`);\n }\n}\n\n/** The digest a replay trace carries for a point read: SHA-256 over the canonical\n * wire row, exactly `snapback2-core`'s `row_digest` (`offline.rs`). */\nexport function rowDigest(row: Value): string {\n return sha256(JSON.stringify(wireJson(row)));\n}\n\n/** `Schema::rule_hash`: the schema hash beside the rule in serde's field order. */\nexport function scopedRuleHash(schema: any, rule: any): string {\n return sha256(`[${JSON.stringify(sha256(canonicalSchemaJson(schema)))},${canonicalRuleJson(rule)}]`);\n}\n\nexport function kindOf(value: Value): string {\n if (value === \"Null\") return \"Null\";\n const keys = Object.keys(value);\n if (keys.length !== 1) throw new EvalError(\"E_INPUT\", \"invalid externally tagged value\");\n return keys[0];\n}\n\nexport function isVariant<K extends Exclude<ReturnType<typeof kindOf>, \"Null\">>(\n value: Value,\n kind: K,\n): value is Extract<Value, Record<K, unknown>> {\n return value !== \"Null\" && kindOf(value) === kind;\n}\n\nexport function valuesEqual(left: Value[], right: Value[]): boolean {\n return canonicalValues(left) === canonicalValues(right);\n}\n\nexport function sameValueZero(left: Value, right: Value): boolean {\n // @ref LLP 1002#2-the-authored-surface-the-snapback2-module \u2014 Keep the client twin on the compiler's primitive collection identity contract.\n const leftKind = kindOf(left);\n const rightKind = kindOf(right);\n if ([\"Bytes\", \"Row\", \"Array\", \"Map\"].includes(leftKind)) {\n throw new EvalError(\"E_TYPE\", `SameValueZero expects primitive operands, got ${leftKind}`);\n }\n if ([\"Bytes\", \"Row\", \"Array\", \"Map\"].includes(rightKind)) {\n throw new EvalError(\"E_TYPE\", `SameValueZero expects primitive operands, got ${rightKind}`);\n }\n if ((leftKind === \"String\" || leftKind === \"Id\") && (rightKind === \"String\" || rightKind === \"Id\")) {\n const leftText = isVariant(left, \"String\") ? left.String : (left as { Id: string }).Id;\n const rightText = isVariant(right, \"String\") ? right.String : (right as { Id: string }).Id;\n return leftText === rightText;\n }\n return canonicalValue(left) === canonicalValue(right);\n}\n\nexport function startsWith<T>(values: T[], prefix: T[]): boolean {\n return prefix.every((value, index) => values[index] === value);\n}\n\nexport function validHttpUrl(value: string): boolean {\n const rest = value.startsWith(\"https://\")\n ? value.slice(\"https://\".length)\n : value.startsWith(\"http://\")\n ? value.slice(\"http://\".length)\n : \"\";\n return rest.length > 0 && !/[\\p{White_Space}\\p{Cc}]/u.test(rest);\n}\n\nexport function canonicalJson(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(\",\")}]`;\n if (value !== null && typeof value === \"object\") {\n return `{${Object.keys(value)\n .sort(compareCodepoints)\n .map((key) => `${JSON.stringify(key)}:${canonicalJson((value as any)[key])}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nexport function validateWriteRow(schema: any, tableName: string, row: Value): void {\n const columns = schema.tables?.[tableName]?.columns;\n if (columns === undefined) throw new EvalError(\"E_SCHEMA\", `missing table ${tableName}`);\n const object = asObject(row);\n if (!hasOwn(object, \"id\")) throw new EvalError(\"E_SCHEMA\", `${tableName} row lacks id`);\n const unknown = Object.keys(object).find((name) => !hasOwn(columns, name));\n if (unknown !== undefined)\n throw new EvalError(\"E_INPUT\", `${tableName} row contains undeclared field ${unknown}`);\n for (const [name, descriptor] of Object.entries<any>(columns)) {\n const value = hasOwn(object, name) ? object[name] : undefined;\n if (value === undefined && descriptor?.Optional === undefined)\n throw new EvalError(\"E_INPUT\", `${tableName}.${name} is required`);\n if (value !== undefined) validateTypedValue(descriptor, value, `${tableName}.${name}`, false);\n }\n}\n\nexport function validateInputs(schema: any, program: any, inputs: Record<string, Value>): void {\n for (const [name, descriptor] of Object.entries<any>(program.args ?? {})) {\n const value = inputs[name];\n if (value === undefined) throw new EvalError(\"E_INPUT\", `missing arg ${name}`);\n validateTypedValue(descriptor, value, `arg ${name}`, true);\n }\n validateOpaqueCursors(schema, program.body, inputs);\n}\n\nfunction validateOpaqueCursors(schema: any, value: any, inputs: Record<string, Value>): void {\n const visitValue = (candidate: any, values: Record<string, Value>): void => {\n if (Array.isArray(candidate)) {\n for (const item of candidate) visitValue(item, values);\n return;\n }\n if (candidate === null || typeof candidate !== \"object\") return;\n const scan = candidate.Scan;\n if (scan?.opaque_cursor) {\n if (scan.before !== null) throw new EvalError(\"E_INPUT\", \"opaque cursors only support after\");\n const cursor = scan.after === null ? \"Null\" : inputExpression(scan.after, values);\n if (cursor !== \"Null\") {\n if (!isVariant(cursor, \"String\")) throw new EvalError(\"E_INPUT\", \"opaque cursor must be a string\");\n decodeCursorFor(cursor.String, {\n table: scan.table,\n index: scan.index,\n order: scan.order,\n suffix: cursorSuffix(schema, scan),\n });\n }\n }\n for (const child of Object.values(candidate)) visitValue(child, values);\n };\n const visitBody = (statements: any[], values: Record<string, Value>): void => {\n for (const statement of statements) {\n if (statement === null || typeof statement !== \"object\") continue;\n const binding = statement.Const ?? statement.Let ?? statement.Assign;\n if (binding) {\n visitValue(binding.value, values);\n const resolved = staticInputExpression(binding.value, values);\n if (resolved === undefined) delete values[binding.name];\n else values[binding.name] = resolved;\n continue;\n }\n if (statement.If) {\n visitValue(statement.If.condition, values);\n const thenValues = { ...values };\n const elseValues = { ...values };\n visitBody(statement.If.then_body, thenValues);\n visitBody(statement.If.else_body, elseValues);\n for (const name of Object.keys(values)) {\n if (!(name in thenValues) || !(name in elseValues)\n || canonicalValue(thenValues[name]) !== canonicalValue(elseValues[name])) delete values[name];\n }\n continue;\n }\n if (statement.For) {\n visitValue(statement.For.domain, values);\n visitBody(statement.For.body, { ...values });\n continue;\n }\n visitValue(statement, values);\n }\n };\n visitBody(value, { ...inputs });\n}\n\nfunction inputExpression(expression: any, inputs: Record<string, Value>): Value {\n const value = staticInputExpression(expression, inputs);\n if (value !== undefined) return value;\n throw new EvalError(\"E_INPUT\", \"opaque cursor expression must derive from an operation argument\");\n}\n\nfunction staticInputExpression(expression: any, inputs: Record<string, Value>): Value | undefined {\n if (expression?.Variable !== undefined) return inputs[expression.Variable];\n if (expression?.Literal !== undefined) return expression.Literal;\n return undefined;\n}\n\nexport function validateTypedValue(\n descriptor: any,\n value: Value,\n label: string,\n fanoutForLongList: boolean,\n): void {\n assertValue(value, label);\n if (valueContainsReservedPrincipal(value))\n throw new EvalError(\"E_INPUT\", `${label} cannot carry the reserved system principal`);\n if (descriptorContainsAsset(descriptor)) {\n validateAssetTypedValue(descriptor, value, label);\n return;\n }\n if (descriptor?.Optional !== undefined) {\n if (value !== \"Null\") validateTypedValue(descriptor.Optional, value, label, fanoutForLongList);\n return;\n }\n if (descriptor?.List !== undefined) {\n if (!isVariant(value, \"Array\")) throw new EvalError(\"E_INPUT\", `${label} must be a list`);\n if (value.Array.length > descriptor.List.max)\n throw new EvalError(fanoutForLongList ? \"E_FANOUT\" : \"E_INPUT\", `${label} exceeds list max ${descriptor.List.max}`);\n if (descriptor.List.of !== null && descriptor.List.of !== undefined) {\n for (const item of value.Array)\n validateTypedValue(descriptor.List.of, item, label, fanoutForLongList);\n }\n return;\n }\n if (descriptor === \"Cursor\") {\n if (!isVariant(value, \"String\")) throw new EvalError(\"E_INPUT\", `${label} must be a cursor string`);\n decodeCursor(value.String);\n return;\n }\n if (descriptor === \"Scalar\") return;\n if (descriptor === \"Id\" || descriptor === \"Principal\" || descriptor?.Ref !== undefined) {\n if (!isVariant(value, \"Id\")) throw new EvalError(\"E_INPUT\", `${label} must be an id`);\n return;\n }\n if (descriptor?.Text !== undefined) {\n if (!isVariant(value, \"String\")) throw new EvalError(\"E_INPUT\", `${label} must be text`);\n const codePoint = disallowedTextControlCodePoint(value.String);\n if (codePoint !== undefined)\n throw new EvalError(\"E_INPUT\", `${label} contains disallowed control character U+${codePoint.toString(16).toUpperCase().padStart(4, \"0\")}`);\n if (Array.from(value.String).length > descriptor.Text.max)\n throw new EvalError(\"E_INPUT\", `${label} exceeds text max ${descriptor.Text.max}`);\n return;\n }\n if (descriptor?.Bytes !== undefined) {\n if (!isVariant(value, \"Bytes\") || !canonicalBytes(value.Bytes))\n throw new EvalError(\"E_INPUT\", `${label} must be bytes`);\n if (value.Bytes.length / 2 > descriptor.Bytes.max)\n throw new EvalError(\"E_INPUT\", `${label} exceeds bytes max ${descriptor.Bytes.max}`);\n return;\n }\n if ([\"Int\", \"Time\"].includes(descriptor) || descriptor?.Decimal !== undefined || descriptor?.Money !== undefined) {\n if (!isVariant(value, \"Int\") || !Number.isSafeInteger(value.Int))\n throw new EvalError(\"E_INPUT\", `${label} must be an i53 integer`);\n return;\n }\n if (descriptor === \"Bool\") {\n if (!isVariant(value, \"Bool\")) throw new EvalError(\"E_INPUT\", `${label} must be a boolean`);\n return;\n }\n if (descriptor?.Json !== undefined) {\n if (utf8Length(canonicalValue(value)) > descriptor.Json.max_bytes)\n throw new EvalError(\"E_INPUT\", `${label} exceeds JSON max ${descriptor.Json.max_bytes} bytes`);\n return;\n }\n if (descriptor?.Enum !== undefined) {\n if (!isVariant(value, \"String\") || !descriptor.Enum.variants.includes(value.String))\n throw new EvalError(\"E_INPUT\", `${label} is not a declared enum value`);\n return;\n }\n throw new EvalError(\"E_SCHEMA\", `unsupported type for ${label}`);\n}\n\nfunction disallowedTextControlCodePoint(text: string): number | undefined {\n for (const character of text) {\n const codePoint = character.codePointAt(0)!;\n if (codePoint < 0x20 && ![0x09, 0x0a, 0x0d].includes(codePoint)) return codePoint;\n }\n return undefined;\n}\n\nfunction descriptorContainsAsset(descriptor: any): boolean {\n if (descriptor?.Image !== undefined || descriptor?.Video !== undefined) return true;\n if (descriptor?.Optional !== undefined) return descriptorContainsAsset(descriptor.Optional);\n if (descriptor?.List?.of !== null && descriptor?.List?.of !== undefined)\n return descriptorContainsAsset(descriptor.List.of);\n return false;\n}\n\n// @ref LLP 1006#6-the-write-validated-not-stamped \u2014 the twin enforces the closed asset record and list uniqueness but owns no ledger authority.\nfunction validateAssetTypedValue(descriptor: any, value: Value, label: string): string[] {\n if (descriptor?.Optional !== undefined) {\n return value === \"Null\" ? [] : validateAssetTypedValue(descriptor.Optional, value, label);\n }\n if (descriptor?.List !== undefined) {\n if (!isVariant(value, \"Array\")) throw assetType(`${label} must be a list`);\n if (value.Array.length > descriptor.List.max)\n throw assetType(`${label} exceeds list max ${descriptor.List.max}`);\n const ids = value.Array.flatMap((item, index) =>\n validateAssetTypedValue(descriptor.List.of, item, `${label}[${index}]`),\n );\n const seen = new Set<string>();\n const duplicate = ids.find((id) => seen.has(id) || !seen.add(id));\n if (duplicate !== undefined)\n throw assetType(`${label} repeats asset id ${duplicate}; asset ids in a list must be unique`);\n return ids;\n }\n const kind = descriptor?.Image !== undefined ? \"image\" : descriptor?.Video !== undefined ? \"video\" : null;\n if (kind === null) throw new EvalError(\"E_SCHEMA\", `unsupported asset type for ${label}`);\n if (!isVariant(value, \"Map\")) throw assetType(`${label} must be a closed ${kind} asset map`);\n const expected = kind === \"image\" ? [\"height\", \"id\", \"width\"] : [\"duration\", \"height\", \"id\", \"width\"];\n const actual = Object.keys(value.Map).sort(compareCodepoints);\n if (actual.length !== expected.length || actual.some((name, index) => name !== expected[index]))\n throw assetType(`${label} must contain exactly ${expected.join(\", \")}`);\n const idValue = value.Map.id;\n const id = isVariant(idValue, \"String\") ? idValue.String : \"\";\n if (!validEngineId(id)) throw assetType(`${label}.id must be a minted s2id`);\n for (const field of kind === \"image\" ? [\"width\", \"height\"] : [\"width\", \"height\", \"duration\"]) {\n const member = value.Map[field];\n if (!isVariant(member, \"Int\") || !Number.isSafeInteger(member.Int) || member.Int <= 0)\n throw assetType(`${label}.${field} must be a positive i53 integer`);\n }\n return [id];\n}\n\nfunction validEngineId(id: string): boolean {\n return /^s2id-[0-9a-f]{32}-[0-9a-f]{16}$/.test(id) && !id.endsWith(\"-0000000000000000\");\n}\n\nfunction assetType(message: string): EvalError {\n return new EvalError(\"E_ASSET_TYPE\", message);\n}\n\nexport function assertValue(value: Value, label: string): void {\n if (value === \"Null\") return;\n const kind = kindOf(value);\n const data = (value as any)[kind];\n if (kind === \"Bool\" && typeof data === \"boolean\") return;\n if (kind === \"Int\" && Number.isSafeInteger(data)) return;\n if ((kind === \"String\" || kind === \"Id\") && typeof data === \"string\") return;\n if (kind === \"Bytes\" && canonicalBytes(data)) return;\n if (kind === \"Array\" && Array.isArray(data)) {\n for (const item of data) assertValue(item, label);\n return;\n }\n if ((kind === \"Row\" || kind === \"Map\") && data !== null && typeof data === \"object\" && !Array.isArray(data)) {\n for (const item of Object.values(data)) assertValue(item as Value, label);\n return;\n }\n throw new EvalError(\"E_INPUT\", `${label} is not a valid value`);\n}\n\nexport function valueContainsReservedPrincipal(value: Value): boolean {\n if (isVariant(value, \"Id\")) return value.Id === coreIrContract.systemPrincipalId;\n if (isVariant(value, \"Array\")) return value.Array.some(valueContainsReservedPrincipal);\n if (isVariant(value, \"Row\")) return Object.values(value.Row).some(valueContainsReservedPrincipal);\n if (isVariant(value, \"Map\")) return Object.values(value.Map).some(valueContainsReservedPrincipal);\n return false;\n}\n\nexport function canonicalBytes(value: unknown): value is string {\n return typeof value === \"string\" && /^(?:[0-9a-f]{2})*$/.test(value);\n}\n\nexport function hasOwn(value: object, key: PropertyKey): boolean {\n return Object.prototype.hasOwnProperty.call(value, key);\n}\n\nexport function clone<T>(value: T): T {\n if (value === null || value === undefined) return value;\n return JSON.parse(JSON.stringify(value));\n}\n\nexport function sortObject<T>(value: Record<string, T>): Record<string, T> {\n const sorted: Record<string, T> = Object.create(null);\n for (const key of Object.keys(value).sort(compareCodepoints)) sorted[key] = value[key];\n return sorted;\n}\n\nexport function builtinArity(kind: string): number {\n switch (kind) {\n case \"Len\": case \"Sort\": case \"DedupeById\": case \"MentionTokens\": case \"Count\": case \"CountCapped\": case \"Sum\": return 1;\n case \"Concat\": case \"Min\": case \"Max\": case \"Divmod\": return 2;\n case \"Slice\": case \"DeterministicShare\": return 3;\n default: throw new EvalError(\"E_OPAQUE\", `unsupported builtin ${kind}`);\n }\n}\n\nexport function valueText(value: Value): string {\n if (value === \"Null\") return \"null\";\n if (isVariant(value, \"Bool\")) return String(value.Bool);\n if (isVariant(value, \"Int\")) return String(value.Int);\n if (isVariant(value, \"String\")) return value.String;\n if (isVariant(value, \"Id\")) return value.Id;\n if (isVariant(value, \"Bytes\")) return value.Bytes;\n if (isVariant(value, \"Array\")) return value.Array.map(valueText).join(\",\");\n return \"[object Object]\";\n}\n\nexport function isWordCharacter(character: string): boolean {\n return character === \"_\" || /[\\p{L}\\p{N}]/u.test(character);\n}\n", "import coreIrContract from \"../../../crates/snapback2-core/ir-contract.json\" with { type: \"json\" };\nimport { EvalError, requireRowIdentity } from \"./errors\";\nimport { cursorSuffix, decodeCursorFor, encodeCursor } from \"./cursor\";\nimport { sha256 } from \"./hash\";\nimport { splitMentionTokens } from \"./text\";\nimport type { ClientStore } from \"./store\";\nimport {\n asArray,\n asBool,\n asInt,\n asObject,\n asString,\n builtinArity,\n canonicalJson,\n canonicalValue,\n canonicalValues,\n clone,\n compareCodepoints,\n field,\n fieldOrNull,\n int,\n isVariant,\n isWordCharacter,\n materializeOptionalFields,\n rowDigest,\n rowValue,\n safeNumber,\n sameValueZero,\n scopedRuleHash,\n stableSort,\n startsWith,\n totalCompare,\n validHttpUrl,\n validateWriteRow,\n valueText,\n variant, wireJson,\n} from \"./value-runtime\";\nimport type { Context, Execution, Trace, TraceVerdict, TraceWrite, Value } from \"./types\";\n\nexport const CURRENT_VALUE_MODEL_VERSION = coreIrContract.valueModelVersion;\n\ntype Flow =\n | { kind: \"next\" }\n | { kind: \"break\" }\n | { kind: \"continue\" }\n | { kind: \"return\"; value: Value };\n\nconst NEXT: Flow = { kind: \"next\" };\n\nexport class Runner {\n private readonly env = new Map<string, Value>();\n private readonly consts = new Set<string>();\n private readonly context: Context;\n readonly trace: Trace = { ranges: [], absences: [], points: [], merges: [], verdicts: [] };\n private readonly ruleMemo = new Map<string, boolean>();\n /** Verdicts the rule check in progress derived; committed to the trace only when the check discloses the row. */\n private pendingVerdicts: TraceVerdict[] = [];\n private readonly tracedVerdicts = new Set<string>();\n private readonly writes = new Map<string, TraceWrite>();\n private readonly partial = new Set<string>();\n private authorityMissSerial = 0;\n /** Engine-owned aggregate targets this run could not update because they are not held. */\n private readonly unheldMaintainTargets: string[] = [];\n private readonly createdRows = new Set<string>();\n private readonly insertedRows: Array<{ table: string; row: Value }> = [];\n private createdPoints: Array<{\n table: string;\n index: string;\n key: string;\n rowId: string;\n }> = [];\n\n constructor(\n private readonly store: ClientStore,\n private readonly schema: any,\n context: Context,\n private readonly certificate: any,\n ) {\n this.context = clone(context);\n for (const [name, value] of Object.entries(context.args)) {\n this.env.set(name, clone(value));\n }\n }\n\n execute(program: any): Execution {\n const flow = this.statements(program.body);\n this.checkFinalConstraints();\n return {\n result: this.partial.size === 0 && flow.kind === \"return\" ? flow.value : \"Null\",\n trace: this.trace,\n next: mintNext(this.schema, this.certificate, this.trace),\n writes: [...this.writes.values()],\n };\n }\n\n coverageMisses(): string[] {\n return Array.from(this.partial).sort(compareCodepoints);\n }\n\n /** Where `missing-authority` came from: unheld maintained targets versus rule inputs and write rows. */\n authorityMisses(): { maintainTargets: string[]; other: number } {\n return { maintainTargets: [...this.unheldMaintainTargets], other: this.authorityMissSerial - this.unheldMaintainTargets.length };\n }\n\n private statements(statements: any[]): Flow {\n for (const statement of statements) {\n const [kind, data] = variant(statement);\n let flow: Flow = NEXT;\n switch (kind) {\n case \"Const\":\n this.env.set(data.name, this.expr(data.value));\n this.consts.add(data.name);\n break;\n case \"Let\":\n this.env.set(data.name, this.expr(data.value));\n break;\n case \"Assign\":\n if (this.consts.has(data.name))\n throw new EvalError(\"E_INPUT\", `cannot assign to const binding ${data.name}`);\n this.env.set(data.name, this.expr(data.value));\n break;\n case \"Push\": {\n const target = this.env.get(data.target);\n if (target === undefined || !isVariant(target, \"Array\"))\n throw new EvalError(\"E_TYPE\", \"push target is not an array\");\n target.Array.push(this.expr(data.value));\n break;\n }\n case \"If\":\n flow = this.statements(\n asBool(this.expr(data.condition)) ? data.then_body : data.else_body,\n );\n break;\n case \"For\": {\n for (const value of this.domain(data.domain)) {\n this.env.set(data.binding, value);\n const bodyFlow = this.statements(data.body);\n if (bodyFlow.kind === \"continue\" || bodyFlow.kind === \"next\") continue;\n if (bodyFlow.kind === \"break\") break;\n flow = bodyFlow;\n break;\n }\n break;\n }\n case \"Require\":\n if (!asBool(this.expr(data.condition)))\n throw new EvalError(\"E_REQUIRE\", \"program requirement failed\", data.code);\n break;\n case \"Insert\":\n this.insert(data.table, this.expr(data.row));\n break;\n case \"Update\":\n this.update(data.table, this.expr(data.key), this.expr(data.patch));\n break;\n case \"Upsert\": {\n const key = this.expr(data.key);\n const row = this.expr(data.row);\n this.validateAddressedRowId(data.table, key, row);\n if (this.store.byId(data.table, asString(key)) === null) this.insert(data.table, row);\n else this.replace(data.table, key, row);\n break;\n }\n case \"Delete\":\n this.delete(data.table, asString(this.expr(data.key)));\n break;\n // @ref LLP 1001#8-emit-and-the-outbox \u2014 the twin does not run effects, but it must consume the authoritative outbox id.\n case \"Emit\":\n this.expr(data.payload);\n if (this.context.new_ids.shift() === undefined)\n throw new EvalError(\"E_INPUT\", \"newId envelope exhausted\");\n break;\n case \"Return\":\n flow = { kind: \"return\", value: this.expr(data) };\n break;\n case \"Break\":\n flow = { kind: \"break\" };\n break;\n case \"Continue\":\n flow = { kind: \"continue\" };\n break;\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported statement ${kind}`);\n }\n if (flow.kind !== \"next\") return flow;\n }\n return NEXT;\n }\n\n private domain(domain: any): Value[] {\n const [kind, data] = variant(domain);\n switch (kind) {\n case \"Page\":\n return asArray(this.scan(data));\n case \"LiteralRange\": {\n const values: Value[] = [];\n for (let value = data.start; value < data.end_exclusive; value += 1) {\n values.push(int(value));\n }\n return values;\n }\n case \"ArgList\": {\n const values = asArray(this.requiredEnv(data.name));\n if (values.length > data.max) throw new EvalError(\"E_FANOUT\", \"argument list bound\");\n return values;\n }\n case \"DerivedArray\": {\n const values = asArray(this.expr(data.value));\n if (values.length > data.max) throw new EvalError(\"E_FANOUT\", \"derived array bound\");\n return values;\n }\n case \"Recurse\":\n return this.recurse(data);\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported loop domain ${kind}`);\n }\n }\n\n private recurse(recurse: any): Value[] {\n const queue: [Value, number][] = [[this.expr(recurse.seed), 0]];\n const visited = new Set<string>();\n const rows: Value[] = [];\n while (queue.length > 0) {\n const [key, depth] = queue.shift()!;\n const canonical = canonicalValue(key);\n if (depth > recurse.depth || visited.has(canonical)) continue;\n visited.add(canonical);\n const row = this.get({\n site: recurse.site,\n table: recurse.table,\n index: recurse.index,\n key: [{ Literal: key }],\n provenance: recurse.provenance,\n });\n if (row === \"Null\") continue;\n const next = field(row, recurse.next_field);\n rows.push(row);\n if (next === \"Null\") continue;\n const successors = isVariant(next, \"Array\") ? next.Array : [next];\n if (successors.length > recurse.fanout)\n throw new EvalError(\"E_FANOUT\", \"recursive fanout exceeded\");\n for (const successor of successors) queue.push([successor, depth + 1]);\n }\n return rows;\n }\n\n private expr(expression: any): Value {\n const [kind, data] = variant(expression);\n switch (kind) {\n case \"Literal\":\n return clone(data);\n case \"Variable\":\n return clone(this.requiredEnv(data));\n case \"Input\":\n return this.input(data);\n case \"Field\":\n return clone(field(this.expr(data.base), data.name));\n case \"Array\":\n return { Array: data.map((value: any) => this.expr(value)) };\n case \"Object\": {\n const row: Record<string, Value> = Object.create(null);\n for (const key of Object.keys(data).sort(compareCodepoints)) row[key] = this.expr(data[key]);\n return { Row: row };\n }\n case \"Binary\":\n return this.binary(data);\n case \"Unary\": {\n const value = this.expr(data.value);\n if (data.op === \"Not\") return { Bool: !asBool(value) };\n if (data.op === \"Neg\") return int(-asInt(value));\n throw new EvalError(\"E_OPAQUE\", `unsupported unary ${data.op}`);\n }\n case \"Builtin\": {\n const builtinKind = variant(data.builtin)[0];\n const expected = builtinArity(builtinKind);\n if (data.args.length !== expected) {\n throw new EvalError(\"E_INPUT\", `${builtinKind} requires ${expected} arguments, received ${data.args.length}`);\n }\n return this.builtin(data.builtin, data.args.map((value: any) => this.expr(value)));\n }\n case \"Get\":\n return this.get(data);\n case \"Exists\":\n return { Bool: this.get(data) !== \"Null\" };\n case \"Scan\":\n return this.scan(data);\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported expression ${kind}`);\n }\n }\n\n private input(input: string): Value {\n switch (input) {\n case \"Viewer\":\n return { Id: this.context.viewer };\n case \"Now\":\n if (this.context.now === null) throw new EvalError(\"E_INPUT\", \"now was not pinned\");\n return int(this.context.now);\n case \"NewId\": {\n const next = this.context.new_ids.shift();\n if (next === undefined) throw new EvalError(\"E_INPUT\", \"newId envelope exhausted\");\n return { Id: next };\n }\n case \"ReadTime\":\n if (this.context.read_time === null)\n throw new EvalError(\"E_INPUT\", \"readTime was not pinned\");\n return int(this.context.read_time);\n default:\n throw new EvalError(\"E_INPUT\", `unknown modeled input ${input}`);\n }\n }\n\n private binary(data: any): Value {\n const left = this.expr(data.left);\n if (data.op === \"And\" && !asBool(left)) return { Bool: false };\n if (data.op === \"Or\" && asBool(left)) return { Bool: true };\n const right = this.expr(data.right);\n switch (data.op) {\n case \"Eq\":\n return { Bool: canonicalValue(left) === canonicalValue(right) };\n case \"Ne\":\n return { Bool: canonicalValue(left) !== canonicalValue(right) };\n case \"SameValueZero\":\n return { Bool: sameValueZero(left, right) };\n case \"Lt\":\n return { Bool: totalCompare(left, right) < 0 };\n case \"Lte\":\n return { Bool: totalCompare(left, right) <= 0 };\n case \"Gt\":\n return { Bool: totalCompare(left, right) > 0 };\n case \"Gte\":\n return { Bool: totalCompare(left, right) >= 0 };\n case \"Add\":\n return int(asInt(left) + asInt(right));\n case \"Sub\":\n return int(asInt(left) - asInt(right));\n case \"Mul\":\n return int(asInt(left) * asInt(right));\n case \"And\":\n return { Bool: asBool(left) && asBool(right) };\n case \"Or\":\n return { Bool: asBool(left) || asBool(right) };\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported binary ${data.op}`);\n }\n }\n\n private builtin(builtin: any, args: Value[]): Value {\n const [kind, data] = variant(builtin);\n switch (kind) {\n case \"Len\": {\n const value = args[0];\n if (isVariant(value, \"String\")) return int(Array.from(value.String).length);\n if (isVariant(value, \"Id\")) return int(Array.from(value.Id).length);\n if (isVariant(value, \"Array\")) return int(value.Array.length);\n if (isVariant(value, \"Row\")) return int(Object.keys(value.Row).length);\n if (isVariant(value, \"Map\")) return int(Object.keys(value.Map).length);\n throw new EvalError(\"E_TYPE\", \"len expects string, array, or map\");\n }\n case \"Slice\": {\n const start = Math.max(0, asInt(args[1]));\n const end = Math.max(0, asInt(args[2]));\n if (isVariant(args[0], \"Array\")) return { Array: args[0].Array.slice(start, end) };\n if (isVariant(args[0], \"String\"))\n return { String: Array.from(args[0].String).slice(start, end).join(\"\") };\n throw new EvalError(\"E_TYPE\", \"slice expects array or string\");\n }\n case \"Concat\": {\n const [left, right] = args;\n if (isVariant(left, \"String\") || isVariant(right, \"String\"))\n return { String: valueText(left) + valueText(right) };\n if (isVariant(left, \"Id\") && isVariant(right, \"Id\"))\n return { Id: canonicalValues([left, right]) };\n if (isVariant(left, \"Array\") && isVariant(right, \"Array\"))\n return { Array: left.Array.concat(right.Array) };\n throw new EvalError(\"E_TYPE\", \"concat operands differ\");\n }\n case \"Min\":\n return totalCompare(args[0], args[1]) <= 0 ? args[0] : args[1];\n case \"Max\":\n return totalCompare(args[0], args[1]) >= 0 ? args[0] : args[1];\n case \"Divmod\": {\n const numerator = asInt(args[0]);\n const denominator = asInt(args[1]);\n if (denominator === 0) throw new EvalError(\"E_DIV_ZERO\", \"division by zero\");\n let quotient = Math.trunc(numerator / denominator);\n let remainder = numerator - quotient * denominator;\n if (remainder < 0) {\n if (denominator > 0) {\n quotient -= 1;\n remainder += denominator;\n } else {\n quotient += 1;\n remainder -= denominator;\n }\n }\n return rowValue({\n quotient: int(quotient),\n remainder: int(remainder),\n sink: { String: data.remainder_sink },\n });\n }\n case \"Sort\": {\n const values = asArray(args[0]).slice();\n for (const value of values) {\n for (const key of data.keys) field(value, key.field);\n }\n return {\n Array: stableSort(values, (left, right) => {\n for (const key of data.keys) {\n let compared = totalCompare(field(left, key.field), field(right, key.field));\n if (key.order === \"Desc\") compared = -compared;\n if (compared !== 0) return compared;\n }\n return 0;\n }),\n };\n }\n case \"DedupeById\": {\n const seen = new Set<string>();\n const values: Value[] = [];\n for (const value of asArray(args[0])) {\n const id = asString(field(value, \"id\"));\n if (!seen.has(id)) {\n seen.add(id);\n values.push(value);\n }\n }\n const page = this.certificate?.result_page_site;\n const take = this.certificate?.merge_sites?.find((entry: any) => entry.site === data.site)?.take;\n const row = page?.merge && page.site === data.site && Number.isInteger(take)\n ? values.slice(0, take).at(-1) : undefined;\n this.trace.merges.push({\n site: data.site,\n deduplicated_union_len: values.length,\n last_returned_suffix: row === undefined ? null : page.suffix.map((column: any) => clone(field(row, column.name))),\n });\n return { Array: values };\n }\n case \"MentionTokens\": {\n const seen = new Set<string>();\n const mentions: Value[] = [];\n for (const token of splitMentionTokens(asString(args[0]))) {\n if (!token.startsWith(\"@\")) continue;\n const characters = Array.from(token.slice(1));\n while (characters.length > 0 && !isWordCharacter(characters[0])) characters.shift();\n while (\n characters.length > 0 &&\n !isWordCharacter(characters[characters.length - 1])\n )\n characters.pop();\n const mention = characters.join(\"\");\n if (mention.length > 0 && !seen.has(mention)) {\n seen.add(mention);\n mentions.push({ String: mention });\n }\n }\n return { Array: mentions };\n }\n case \"DeterministicShare\": {\n const amount = asInt(args[0]);\n const recipients = stableSort(asArray(args[1]).slice(), totalCompare);\n if (recipients.length === 0) throw new EvalError(\"E_DIV_ZERO\", \"no recipients\");\n if (new Set(recipients.map(canonicalValue)).size !== recipients.length)\n throw new EvalError(\"E_INPUT\", \"share recipients must be unique\");\n const recipient = args[2];\n const position = recipients.findIndex(\n (candidate) => canonicalValue(candidate) === canonicalValue(recipient),\n );\n if (position < 0) throw new EvalError(\"E_INPUT\", \"recipient missing from shares\");\n const base = Math.floor(amount / recipients.length);\n const remainder = amount - base * recipients.length;\n return int(base + (position < remainder ? 1 : 0));\n }\n case \"Count\":\n case \"CountCapped\":\n return int(asArray(args[0]).length);\n case \"Sum\": {\n let sum = 0;\n for (const value of asArray(args[0])) sum = safeNumber(sum + asInt(value));\n return int(sum);\n }\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported builtin ${kind}`);\n }\n }\n\n private get(read: any): Value {\n const key = read.key.map((value: any) => this.expr(value));\n const found = this.store.lookup(read.table, read.index, key);\n const disclosed = found !== null && this.checkRule(read.table, \"read\", null, found);\n // @ref LLP 1012#7-writes-offline \u2014 the point digest is what the program\n // observed: the wire row when disclosed, an absence when missing or hidden.\n this.trace.points.push({\n site: read.site,\n table: read.table,\n index: read.index,\n key: key.map(clone),\n digest: disclosed ? rowDigest(found) : null,\n ...(typeof read.provenance === \"string\" ? { provenance: read.provenance } : {}),\n });\n if (found === null) {\n this.trace.absences.push({\n site: read.site,\n table: read.table,\n index: read.index,\n key,\n ...(typeof read.provenance === \"string\" ? { provenance: read.provenance } : {}),\n });\n return \"Null\";\n }\n return disclosed ? clone(found) : \"Null\";\n }\n\n private scan(scan: any): Value {\n const prefix = scan.prefix.map((value: any) => this.expr(value));\n let after = scan.after === null ? null : this.expr(scan.after);\n const before = scan.before === null ? null : this.expr(scan.before);\n if (scan.opaque_cursor && after === \"Null\") after = null;\n else if (scan.opaque_cursor && after !== null) {\n if (!isVariant(after, \"String\")) throw new EvalError(\"E_INPUT\", \"opaque cursor must be a string\");\n after = decodeCursorFor(after.String, {\n table: scan.table, index: scan.index, order: scan.order, suffix: cursorSuffix(this.schema, scan),\n });\n }\n const evaluatedRange = scan.range === null ? null : this.evaluateRange(scan.range);\n const [boundKind, boundData] = variant(scan.take);\n const take =\n boundKind === \"Literal\"\n ? boundData\n : asInt(this.context.args[boundData.name] ?? this.requiredEnv(boundData.name));\n if (!Number.isInteger(take) || take < 0 || (boundKind === \"Arg\" && take > boundData.max))\n throw new EvalError(\"E_FANOUT\", \"scan take exceeded\");\n const examined = this.store.scan(\n scan.table,\n scan.index,\n prefix,\n after,\n before,\n evaluatedRange,\n scan.order,\n take + 2,\n );\n const stop = examined.length > take ? \"limit\" : \"exhausted\";\n const visible = examined.filter((row) =>\n this.checkRule(scan.table, \"read\", null, row)\n );\n const returned = visible.slice(0, take);\n const suffix = cursorSuffix(this.schema, scan);\n this.trace.ranges.push({\n site: scan.site,\n table: scan.table,\n index: scan.index,\n ...(typeof scan.provenance === \"string\" ? { provenance: scan.provenance } : {}),\n prefix,\n order: scan.order,\n take,\n after,\n before,\n gt: evaluatedRange?.gt ?? null,\n gte: evaluatedRange?.gte ?? null,\n lt: evaluatedRange?.lt ?? null,\n lte: evaluatedRange?.lte ?? null,\n rule_hash: scopedRuleHash(this.schema, this.schema.tables[scan.table].rules.read),\n stop,\n lastReturnedSuffix: returned.length === 0 ? null\n : suffix.map((column) => clone(field(returned[returned.length - 1]!, column.name))),\n });\n return { Array: returned.map(clone) };\n }\n\n private evaluateRange(range: any): any {\n return {\n gt: range.gt === null ? null : this.expr(range.gt),\n gte: range.gte === null ? null : this.expr(range.gte),\n lt: range.lt === null ? null : this.expr(range.lt),\n lte: range.lte === null ? null : this.expr(range.lte),\n };\n }\n\n private insert(table: string, row: Value): void {\n row = materializeOptionalFields(this.schema, table, row);\n validateWriteRow(this.schema, table, row);\n if (!this.checkRule(table, \"insert\", null, row))\n throw new EvalError(\"E_RULE\", `${table} insert`);\n this.checkConstraints(table, null, row, \"insert\");\n if (this.store.byId(table, asString(field(row, \"id\"))) !== null)\n throw new EvalError(\"E_CONSTRAINT\", `duplicate id on ${table}`);\n this.store.put(table, row);\n this.recordWrite(table, row);\n this.rememberCreatedRow(table, row);\n this.insertedRows.push({ table, row: clone(row) });\n this.ruleMemo.clear();\n this.maintain(table, null, row);\n }\n\n private update(table: string, key: Value, patch: Value): void {\n const old = this.store.byId(table, asString(key));\n if (old === null) {\n this.missingWriteRow(table, asString(key));\n return;\n }\n const object = { ...asObject(old), ...asObject(patch) };\n this.replace(table, key, rowValue(object));\n }\n\n private replace(table: string, key: Value, row: Value): void {\n const old = this.store.byId(table, asString(key));\n if (old === null) throw new EvalError(\"E_INPUT\", `missing ${table} row`);\n row = materializeOptionalFields(this.schema, table, row);\n validateWriteRow(this.schema, table, row);\n this.validateAddressedRowId(table, key, row);\n // @ref LLP 1001#9-schema-additions \u2014 update authority is one relation\n // evaluated once over the old and next row images.\n if (!this.checkRule(table, \"update\", old, row))\n throw new EvalError(\"E_RULE\", `${table} update`);\n this.checkConstraints(table, old, row, \"update\");\n this.store.put(table, row);\n this.recordWrite(table, row);\n this.refreshCreatedPoints(table, row);\n this.ruleMemo.clear();\n this.maintain(table, old, row);\n }\n\n private delete(table: string, id: string): void {\n const old = this.store.byId(table, id);\n if (old === null) {\n this.missingWriteRow(table, id);\n return;\n }\n if (!this.checkRule(table, \"delete\", null, old))\n throw new EvalError(\"E_RULE\", `${table} delete`);\n this.checkConstraints(table, old, old, \"delete\");\n this.store.remove(table, id);\n this.writes.set(`${table}\\0${id}`, { table, id, row: null });\n this.forgetCreatedPoints(table, id);\n this.ruleMemo.clear();\n this.maintain(table, old, null);\n }\n\n // @ref LLP 1011#b3-one-missing-row-rule-no-id-in-an-upsert-row \u2014 preserve\n // the engine's held/absent/unheld distinction in the receipt-only twin.\n private missingWriteRow(table: string, id: string): void {\n if (!this.store.authorityAbsence(table, id)) {\n this.partial.add(\"missing-authority\");\n this.authorityMissSerial += 1;\n return;\n }\n throw new EvalError(\"E_REQUIRE\", \"program requirement failed\", \"NOT_FOUND\");\n }\n\n private maintain(source: string, old: Value | null, next: Value | null): void {\n for (const maintain of this.schema.maintains ?? []) {\n if (maintain.source !== source) continue;\n if (old !== null) this.applyAggregate(maintain, old, -1);\n if (next !== null) this.applyAggregate(maintain, next, 1);\n }\n }\n\n private applyAggregate(maintain: any, source: Value, direction: number): void {\n // @ref LLP 1001#4-generation-and-runtime-registryrs-successorrs \u2014 maintained targets are engine-owned but still validated.\n source = materializeOptionalFields(this.schema, maintain.source, source);\n const group = maintain.by.map((name: string) => clone(field(source, name)));\n const idValue = canonicalValues(group);\n const [kind, data] = variant(maintain.kind);\n const delta =\n kind === \"Count\" ? direction : safeNumber(direction * asInt(field(source, data.of)));\n const existing = this.store.byId(maintain.target, idValue);\n if (existing === null && !this.store.authorityAbsence(maintain.target, idValue)) {\n this.partial.add(\"missing-authority\");\n this.authorityMissSerial += 1;\n this.unheldMaintainTargets.push(maintain.target);\n return;\n }\n const oldValue = existing === null ? 0 : asInt(field(existing, \"value\"));\n const object: Record<string, Value> = Object.assign(Object.create(null), {\n id: { Id: idValue },\n value: int(oldValue + delta),\n });\n maintain.by.forEach((name: string, index: number) => {\n object[name] = group[index];\n });\n const row = materializeOptionalFields(this.schema, maintain.target, rowValue(object));\n const effect = existing === null ? \"insert\" : \"update\";\n // Maintained targets are engine-owned. They bypass authored write rules\n // while retaining the same constraints and created-row provenance as Rust.\n validateWriteRow(this.schema, maintain.target, row);\n this.checkConstraints(maintain.target, existing, row, effect);\n this.store.put(maintain.target, row);\n this.recordWrite(maintain.target, row);\n if (existing === null) {\n this.rememberCreatedRow(maintain.target, row);\n this.insertedRows.push({ table: maintain.target, row: clone(row) });\n } else this.refreshCreatedPoints(maintain.target, row);\n this.ruleMemo.clear();\n }\n\n private recordWrite(table: string, row: Value): void {\n const id = asString(field(row, \"id\"));\n this.writes.set(`${table}\\0${id}`, { table, id, row: clone(row) });\n }\n\n private validateAddressedRowId(table: string, key: Value, row: Value): void {\n requireRowIdentity(table, \"addressed write\", asString(key), asString(field(row, \"id\")));\n }\n\n private rememberCreatedRow(table: string, row: Value): void {\n const id = asString(field(row, \"id\"));\n this.createdRows.add(`${table}\\0${id}`);\n this.refreshCreatedPoints(table, row);\n }\n\n private refreshCreatedPoints(table: string, row: Value): void {\n const rowId = asString(field(row, \"id\"));\n if (!this.createdRows.has(`${table}\\0${rowId}`)) return;\n this.forgetCreatedPoints(table, rowId);\n for (const [index, definition] of Object.entries<any>(\n this.schema.tables[table]?.indexes ?? {},\n )) {\n this.createdPoints.push({\n table,\n index,\n key: canonicalValues(\n definition.columns.map((column: string) => fieldOrNull(row, column)),\n ),\n rowId,\n });\n }\n }\n\n private forgetCreatedPoints(table: string, rowId: string): void {\n this.createdPoints = this.createdPoints.filter(\n (point) => point.table !== table || point.rowId !== rowId,\n );\n }\n\n private checkConstraints(\n tableName: string,\n old: Value | null,\n row: Value,\n effect: \"insert\" | \"update\" | \"delete\",\n ): void {\n const table = this.schema.tables[tableName];\n for (const constraint of table.constraints ?? []) {\n const [kind, data] = variant(constraint);\n if (kind === \"AppendOnly\" && effect !== \"insert\")\n throw new EvalError(\"E_CONSTRAINT\", `${tableName} is appendOnly`);\n if (\n kind === \"Immutable\" &&\n effect === \"update\" &&\n data.fields.some(\n (name: string) => canonicalValue(field(old!, name)) !== canonicalValue(field(row, name)),\n )\n )\n throw new EvalError(\"E_CONSTRAINT\", `${tableName} immutable fields changed`);\n if (kind === \"Unique\") this.checkUnique(tableName, data.index, old, row);\n if (kind === \"Cap\") {\n const entersGroup =\n effect === \"insert\" ||\n (effect === \"update\" && data.by.some((name: string) =>\n canonicalValue(field(old!, name)) !== canonicalValue(field(row, name))));\n if (!entersGroup) continue;\n const entry = Object.entries<any>(table.indexes).find(([, index]) =>\n startsWith(index.columns, data.by),\n );\n if (entry === undefined) throw new EvalError(\"E_SCHEMA\", \"cap index missing\");\n const prefix = data.by.map((name: string) => field(row, name));\n const existing = this.store.scan(\n tableName,\n entry[0],\n prefix,\n null,\n null,\n null,\n \"Asc\",\n data.max,\n );\n if (existing.length >= data.max)\n throw new EvalError(\"E_CONSTRAINT\", `${tableName} cap reached`);\n if (!this.store.hasCompleteRange(tableName, entry[0], prefix)) {\n this.partial.add(`${tableName}.${entry[0]}`);\n }\n }\n if (kind === \"Text\" && (effect === \"insert\" || effect === \"update\")) {\n const value = asObject(row)[data.field];\n if (value !== undefined && value !== \"Null\") {\n const text = asString(value);\n if (Array.from(text).length < (data.min ?? 0))\n throw new EvalError(\n \"E_CONSTRAINT\",\n `${tableName}.${data.field} must contain at least ${data.min ?? 0} characters`,\n );\n const valid =\n data.format === undefined ||\n data.format === null ||\n (data.format === \"handle\" && /^[a-z0-9_]*$/.test(text)) ||\n (data.format === \"url\" && validHttpUrl(text));\n if (!valid)\n throw new EvalError(\n \"E_CONSTRAINT\",\n `${tableName}.${data.field} has invalid ${String(data.format)} format`,\n );\n }\n }\n }\n for (const [indexName, index] of Object.entries<any>(table.indexes)) {\n if (index.unique) this.checkUnique(tableName, indexName, old, row);\n }\n }\n\n private checkFinalConstraints(): void {\n for (const { table: sourceTable, row } of this.insertedRows) {\n for (const constraint of this.schema.tables[sourceTable]?.constraints ?? []) {\n const [kind, data] = variant(constraint);\n if (kind !== \"FinalExistsOnInsert\") continue;\n const key = data.key.map((term: any) => this.ruleTerm(term, null, row));\n if (this.store.lookup(data.table, data.index, key) === null)\n throw new EvalError(\n \"E_CONSTRAINT\",\n `${sourceTable} insert requires final row at ${data.table}.${data.index}`,\n );\n }\n }\n }\n\n private checkUnique(table: string, indexName: string, old: Value | null, row: Value): void {\n const index = this.schema.tables[table].indexes[indexName];\n const key = index.columns.map((name: string) => fieldOrNull(row, name));\n const found = this.store.lookup(table, indexName, key);\n if (\n found !== null &&\n (old === null || asString(field(found, \"id\")) !== asString(field(old, \"id\")))\n )\n throw new EvalError(\"E_CONSTRAINT\", `sealed unique conflict on ${table}.${indexName}`);\n if (\n indexName !== \"byId\" &&\n found === null &&\n !this.store.hasAbsence(table, indexName, key) &&\n !this.store.hasCompleteRange(table, indexName, key)\n ) {\n this.partial.add(`${table}.${indexName}`);\n }\n }\n\n private checkRule(\n table: string,\n effect: \"read\" | \"insert\" | \"update\" | \"delete\",\n old: Value | null,\n next: Value,\n ): boolean {\n // @ref LLP 1012#5-retention-revocation-and-the-fence \u2014 under `retain\n // delivered-history` a held row is readable to the twin: delivery was the\n // authorization, so the kept row renders under local evaluation too.\n if (effect === \"read\" && this.store.readable(table)) return true;\n const rule = this.schema.tables[table].rules[effect];\n const authorityMissSerial = this.authorityMissSerial;\n const outer = this.pendingVerdicts;\n this.pendingVerdicts = [];\n const allowed = this.evaluateRule(\n rule,\n scopedRuleHash(this.schema, rule),\n old,\n next,\n effect === \"insert\",\n ) && this.authorityMissSerial === authorityMissSerial;\n // Only a check that disclosed its row leaves verdicts on the trace: the\n // server discards the rule inputs a hidden candidate minted, so a traced\n // verdict from a failed check would be one nothing at commit derived.\n if (allowed) {\n for (const verdict of this.pendingVerdicts) {\n const key = `${verdict.rule_hash}\\0${verdict.inputs_digest}\\0${verdict.result}`;\n if (this.tracedVerdicts.has(key)) continue;\n this.tracedVerdicts.add(key);\n this.trace.verdicts.push(verdict);\n }\n }\n this.pendingVerdicts = outer;\n return allowed;\n }\n\n private evaluateRule(\n rule: any,\n ruleHash: string,\n old: Value | null,\n next: Value,\n createdAllowed: boolean,\n ): boolean {\n const [kind, data] = variant(rule);\n switch (kind) {\n case \"Public\":\n case \"Allow\":\n return true;\n case \"Deny\":\n return false;\n case \"Eq\":\n return canonicalValue(this.ruleTerm(data[0], old, next)) === canonicalValue(this.ruleTerm(data[1], old, next));\n case \"Ne\":\n return canonicalValue(this.ruleTerm(data[0], old, next)) !== canonicalValue(this.ruleTerm(data[1], old, next));\n case \"And\":\n return data.every((child: any) =>\n this.evaluateRule(child, ruleHash, old, next, createdAllowed)\n );\n case \"Or\":\n return data.some((child: any) =>\n this.evaluateRule(child, ruleHash, old, next, createdAllowed)\n );\n case \"Not\":\n return !this.evaluateRule(data, ruleHash, old, next, createdAllowed);\n case \"Exists\": {\n // @ref LLP 1002#9-the-client-snapback2client-snapback2react-snapback2test \u2014 absence and authorization are authority-only.\n const key = data.key.map((term: any) => this.ruleTerm(term, old, next));\n // The verdict's inputs digest as the wire defines it: `sha256(canonical([table, index, key]))` (LLP 1012 \u00A77).\n const pinnedDigest = sha256(canonicalJson([data.table, data.index, key.map(wireJson)]));\n const memoKey = `${ruleHash}:${pinnedDigest}`;\n const memoized = this.ruleMemo.get(memoKey);\n if (memoized !== undefined) {\n this.pendingVerdicts.push({ rule_hash: ruleHash, inputs_digest: pinnedDigest, result: memoized });\n return memoized;\n }\n if (!this.ruleInputIsReadable(data)) {\n // @ref LLP 1001#2-what-a-commit-records \u2014 held rows cannot replace authority-only verdicts.\n const verdict = this.store.verdict(ruleHash, pinnedDigest, this.context);\n if (verdict !== null) {\n this.ruleMemo.set(memoKey, verdict);\n this.pendingVerdicts.push({ rule_hash: ruleHash, inputs_digest: pinnedDigest, result: verdict });\n return verdict;\n }\n this.partial.add(\"missing-authority\");\n this.authorityMissSerial += 1;\n return false;\n }\n const found = this.store.lookup(data.table, data.index, key);\n if (found === null && !this.store.hasAbsence(data.table, data.index, key)) {\n this.partial.add(`${data.table}.${data.index}`);\n this.ruleMemo.set(memoKey, false);\n return false;\n }\n const result = found !== null;\n this.ruleMemo.set(memoKey, result);\n this.pendingVerdicts.push({ rule_hash: ruleHash, inputs_digest: pinnedDigest, result });\n return result;\n }\n case \"Created\": {\n if (!createdAllowed)\n throw new EvalError(\n \"E_SCHEMA\",\n \"created-row probe evaluated outside a table insert rule\",\n );\n const key = data.key.map((term: any) => this.ruleTerm(term, old, next));\n const encoded = canonicalValues(key);\n return this.createdPoints.some(\n (point) =>\n point.table === data.table &&\n point.index === data.index &&\n point.key === encoded,\n );\n }\n default:\n throw new EvalError(\"E_OPAQUE\", `unsupported rule ${kind}`);\n }\n }\n\n private ruleInputIsReadable(probe: any): boolean {\n const table = this.schema.tables[probe.table];\n const index = table?.indexes?.[probe.index];\n if (!table || !index) return false;\n const prove = (rule: any): boolean => {\n const [kind, data] = variant(rule);\n if (kind === \"Public\" || kind === \"Allow\") return true;\n if (kind === \"Eq\") {\n const left = substituteRuleTerm(data[0], index.columns, probe.key);\n const right = substituteRuleTerm(data[1], index.columns, probe.key);\n return left !== null && right !== null && canonicalJson(left) === canonicalJson(right);\n }\n if (kind === \"And\") return data.length > 0 && data.every(prove);\n if (kind === \"Or\") return data.some(prove);\n if (kind === \"Exists\" && data.table === probe.table && data.index === probe.index) {\n const key = data.key.map((term: any) => substituteRuleTerm(term, index.columns, probe.key));\n return key.every((term: any) => term !== null) && canonicalJson(key) === canonicalJson(probe.key);\n }\n return false;\n };\n return prove(table.rules.read);\n }\n\n private ruleTerm(term: any, old: Value | null, next: Value): Value {\n const [kind, data] = variant(term);\n if (kind === \"Field\") return clone(field(next, data));\n if (kind === \"NextField\") {\n if (old === null)\n throw new EvalError(\"E_SCHEMA\", \"next-row rule term evaluated outside an update rule\");\n return clone(field(next, data));\n }\n if (kind === \"OldField\") {\n if (old === null)\n throw new EvalError(\"E_SCHEMA\", \"old-row rule term evaluated outside an update rule\");\n return clone(field(old, data));\n }\n if (kind === \"Viewer\") return { Id: this.context.viewer };\n if (kind === \"Literal\") return clone(data);\n throw new EvalError(\"E_OPAQUE\", `unsupported rule term ${kind}`);\n }\n\n private requiredEnv(name: string): Value {\n const value = this.env.get(name);\n if (value === undefined) throw new EvalError(\"E_INPUT\", `unknown variable ${name}`);\n return value;\n }\n}\n\n\nfunction substituteRuleTerm(term: any, columns: string[], probeKey: any[]): any | null {\n const [kind, data] = variant(term);\n if (kind === \"OldField\" || kind === \"NextField\") return null;\n if (kind !== \"Field\") return clone(term);\n const position = columns.indexOf(data);\n return position < 0 || position >= probeKey.length ? null : clone(probeKey[position]);\n}\n\nfunction mintNext(schema: any, certificate: any, trace: Trace): string | null {\n const page = certificate?.result_page_site;\n if (!page || !queryCapped(certificate, trace)) return null;\n const values = page.merge\n ? trace.merges.slice().reverse().find((entry) => entry.site === page.site)?.last_returned_suffix\n : trace.ranges.slice().reverse().find((entry) => entry.site === page.site)?.lastReturnedSuffix;\n if (values === null || values === undefined) return null;\n return encodeCursor({\n table: page.table,\n index: page.index,\n order: page.order,\n suffix: page.suffix,\n values,\n });\n}\n\n/** The wire state a query run reports: `capped` exactly when the engine's `query_state` says so. */\nexport function queryState(certificate: any, trace: Trace): \"complete\" | \"capped\" {\n return queryCapped(certificate, trace) ? \"capped\" : \"complete\";\n}\n\nfunction queryCapped(certificate: any, trace: Trace): boolean {\n if (certificate.completeness === \"Capped\") return true;\n if (trace.ranges.some((range) => range.stop === \"limit\"\n || !certificate.read_sites?.find((site: any) => site.site === range.site)?.hoist)) return true;\n return (certificate.merge_sites ?? []).some((site: any) => {\n const merge = trace.merges.find((entry) => entry.site === site.site);\n const unionTooLarge = merge === undefined || merge.deduplicated_union_len > site.take;\n const laneLimited = trace.ranges.some((range) => site.lane_sites.includes(range.site) && range.stop !== \"exhausted\");\n return unionTooLarge || laneLimited;\n });\n}\n", "// @ref LLP 1012#6-reads-offline \u2014 the twin is the client: with the backend\n// unreachable the certified program runs here over `coverage()`; a read inside\n// coverage is exact, a read outside is `partial` at that site with its reason.\n// @ref LLP 1012#7-writes-offline \u2014 on enqueue the twin runs the mutation over\n// held rows, writes the prediction overlay, and records the trace the server\n// validates at commit: every point read with its digest, every rule verdict.\nimport { wireJson } from \"../../evaluator/src/value-runtime.js\";\nimport { tagWireRow, type WireValue } from \"./offline-protocol.js\";\nimport { EvalError } from \"../../evaluator/src/errors.js\";\nimport { queryState } from \"../../evaluator/src/runner.js\";\nimport { ClientStore } from \"../../evaluator/src/store.js\";\nimport { emptyTrace } from \"./store/outbox.js\";\nimport { closureFromCoverage, partialReasons, tagArguments, type PartialSite } from \"./twin-hydrate.js\";\nimport type { Context, Trace } from \"../../evaluator/src/types.js\";\nimport type { Coverage, HeldProgram, OutboxEntry, OutboxTrace, OverlayRow } from \"./store/range-store.js\";\n\nexport interface TwinSources {\n /** The certified program held for this partition, if any. */\n program(): HeldProgram | undefined;\n coverage(): Promise<Coverage | undefined>;\n overlay(): Promise<Record<string, OverlayRow[]>>;\n /** Every durable intent, FIFO; overlays are layered in this order. */\n entries(): Promise<OutboxEntry[]>;\n principal(): string | undefined;\n now(): number;\n seq(): number;\n}\n\n/** A query the twin answered locally, in the wire's shape plus its offline facts. */\nexport interface LocalAnswer {\n state: \"complete\" | \"capped\" | \"partial\" | \"denied\";\n data?: unknown;\n next?: string | null;\n seq: number;\n reason?: string;\n rule?: string;\n /** The first uncovered site (`partial`). */\n site?: string;\n /** Every uncovered site with its reason (`partial`). */\n sites?: PartialSite[];\n /** Queued intents whose predicted rows are visible in `data`, in order. */\n predicted?: string[];\n /** The newest delivery the answer was computed from. */\n since?: number;\n}\n\nexport interface Prediction {\n predicted: boolean;\n overlay: OverlayRow[];\n trace: OutboxTrace;\n /** Range reads evaluated over held rows only; ranges re-execute fresh at commit and are never validated. */\n partialRanges: string[];\n /** The point reads or rule inputs outside coverage that stopped the prediction. */\n uncovered: PartialSite[];\n /**\n * Engine-owned aggregate targets (`maintain \u2026 count`) this write updates on the\n * server but the overlay omits, because the store does not hold them: named so\n * a reader can tell an intentionally omitted aggregate from a lost write.\n */\n omitted: string[];\n}\n\ninterface Hydrated {\n program: HeldProgram;\n coverage: Coverage;\n store: ClientStore;\n predicted: string[];\n}\n\nfunction tagged<T>(call: (...args: any[]) => T, ...args: any[]): T | undefined {\n try { return call(...args); }\n catch (error) {\n // DeliveryEnvelopeDecodeError is a TypeError; these are exactly the three\n // closed tagging/hashing failures the offline contract admits.\n if (!(error instanceof TypeError || error instanceof EvalError)) throw error;\n (globalThis as { __snapbackTwinLast?: string }).__snapbackTwinLast = `tagged:${String(error)}`;\n }\n}\n\n/** Debug seam for the witness: why the twin last declined to evaluate (never part of a result). */\nexport function twinLastDecline(): string | undefined { return (globalThis as { __snapbackTwinLast?: string }).__snapbackTwinLast; }\nfunction decline(reason: string): undefined { (globalThis as { __snapbackTwinLast?: string }).__snapbackTwinLast = reason; return undefined; }\n\nexport class Twin {\n constructor(private readonly sources: TwinSources) {}\n\n /**\n * The certified program run over coverage. `undefined` when no program is\n * held, when the coverage was delivered under another generation or schema\n * (the successor fence: a predecessor's program never evaluates into current\n * state), or when the operation is not a query the program knows.\n */\n async evaluate(op: string, args: unknown): Promise<LocalAnswer | undefined> {\n const hydrated = await this.hydrate();\n if (!hydrated) return decline(\"hydrate\");\n const operation = hydrated.program.programs[op];\n if (!operation || operation.kind !== \"query\") return decline(\"unknown-op\");\n const ir = operation.ir as { args?: unknown };\n const context = this.context(hydrated, ir, args, []);\n if (!context) return decline(\"context\");\n const since = hydrated.coverage.deliveredAt;\n const base = {\n seq: this.sources.seq(),\n ...(since === undefined ? {} : { since }),\n ...(hydrated.predicted.length ? { predicted: hydrated.predicted } : {}),\n };\n const partial = (sites: string[], trace: Trace, data?: unknown): LocalAnswer => {\n const reasons = partialReasons(sites, trace, hydrated.coverage);\n return {\n state: \"partial\",\n ...(data === undefined ? {} : { data }),\n reason: reasons[0]?.reason ?? \"never-held\",\n ...(reasons[0] === undefined ? {} : { site: reasons[0].site }),\n sites: reasons,\n ...base,\n };\n };\n try {\n const local = hydrated.store.reexecute(ir, context, operation.certificate);\n const result = local.execution.result;\n const data = result === \"Null\" && local.state !== \"fresh\" ? undefined : wireJson(result);\n if (local.state === \"fresh\") {\n return { state: queryState(operation.certificate, local.execution.trace), data, next: local.execution.next, ...base };\n }\n return partial(local.partialSites, local.execution.trace, data);\n } catch (error) {\n if (!(error instanceof EvalError)) throw error;\n if (error.trace && error.partialSites?.length) return partial(error.partialSites, error.trace);\n if (error.code === \"E_REQUIRE\") return { state: \"denied\", rule: error.authoredCode ?? error.code, ...base };\n // A covered execution failure is not missing coverage.\n throw error;\n }\n }\n\n /**\n * The mutation run over held rows with the intent's pinned ids as its `new`\n * envelope, so the prediction and the commit mint the same ids. Predicted\n * when every point read and rule input was covered; a range the store does\n * not hold whole is evaluated over the rows it has and named in\n * `partialRanges` \u2014 ranges re-execute at commit and are never validated. An\n * uncovered point or rule input, or a local refusal, leaves the intent\n * queued without a prediction and with an empty trace: the server judges.\n */\n async predict(op: string, args: unknown, ids: string[]): Promise<Prediction> {\n const none: Prediction = { predicted: false, overlay: [], trace: emptyTrace(), partialRanges: [], uncovered: [], omitted: [] };\n const hydrated = await this.hydrate();\n if (!hydrated) return none;\n const operation = hydrated.program.programs[op];\n if (!operation || operation.kind !== \"mutation\") return none;\n const ir = operation.ir as { args?: unknown };\n const context = this.context(hydrated, ir, args, ids);\n if (!context) return none;\n let local: ReturnType<ClientStore[\"applyMutation\"]>;\n try {\n local = hydrated.store.applyMutation(ir, context);\n } catch (error) {\n // A typed local mutation refusal (including an unsupported local IR or\n // schema) does not reject the intent: the server still judges it (\u00A77).\n // Unexpected JS errors propagate; they cannot enqueue an empty trace.\n if (!(error instanceof EvalError)) throw error;\n return {\n ...none,\n uncovered: error.trace && error.partialSites ? partialReasons(error.partialSites, error.trace, hydrated.coverage) : [],\n };\n }\n const trace = local.execution.trace;\n // An engine-owned aggregate target the store does not hold (`maintain \u2026\n // count`) is the server's to update: it is no read of the program's and\n // nothing the viewer renders from this write, so it does not block the\n // prediction; the overlay simply carries no count row, and names the\n // target under `omitted`. Every other `missing-authority` (a rule input,\n // a write row) does block.\n const misses = local.authorityMisses;\n const onlyMaintainTargets = local.state === \"partial\"\n && local.partialSites.every((site) => site === \"missing-authority\")\n && misses?.other === 0 && misses.maintainTargets.length;\n if (local.state !== \"predicted\" && !onlyMaintainTargets) return { ...none, uncovered: partialReasons(local.partialSites, trace, hydrated.coverage) };\n const rangeSites = new Set(trace.ranges.map((range) => range.site));\n const uncovered = local.uncoveredSites ?? [];\n const blocking = uncovered.filter((site) => !rangeSites.has(site) || trace.absences.some((absence) => absence.site === site));\n if (blocking.length) return { ...none, uncovered: partialReasons(blocking, trace, hydrated.coverage) };\n return {\n predicted: true,\n overlay: local.execution.writes.map((write) => write.row === null\n ? { table: write.table, id: write.id, row: {}, removed: true as const }\n : { table: write.table, id: write.id, row: wireJson(write.row) as Record<string, unknown> }),\n trace: {\n points: trace.points.map((point) => ({\n table: point.table,\n index: point.index,\n key: point.key.map(wireJson),\n digest: point.digest,\n })),\n verdicts: trace.verdicts.map((verdict) => ({\n ruleHash: verdict.rule_hash,\n inputsDigest: verdict.inputs_digest,\n result: verdict.result,\n })),\n },\n partialRanges: uncovered,\n uncovered: [],\n omitted: [...new Set(misses?.maintainTargets ?? [])],\n };\n }\n\n private context(hydrated: Hydrated, ir: { args?: unknown }, args: unknown, ids: string[]): Context | undefined {\n const principal = this.sources.principal();\n if (principal === undefined) throw new TypeError(\"no partition is open\");\n const localArgs = tagged(tagArguments, ir.args, args);\n return localArgs && {\n viewer: principal,\n principal_kind: \"user\",\n // @ref LLP 1002#9-the-client-snapback2client-snapback2react-snapback2test \u2014 the auth-epoch fence:\n // a verdict minted under another epoch never matches, so it cannot vouch here.\n auth_epoch: hydrated.coverage.facts?.authEpoch ?? 0,\n args: localArgs,\n now: this.sources.now(),\n new_ids: [...ids],\n read_time: null,\n };\n }\n\n private async hydrate(): Promise<Hydrated | undefined> {\n const program = this.sources.program();\n if (!program) return decline(\"no-program\");\n const coverage = await this.sources.coverage();\n if (!coverage) return decline(\"no-coverage\");\n // Coverage a rotation superseded is never evaluated, exact or partial: the\n // reset path rebuilds it under the live generation (LLP 1012 \u00A78).\n if (coverage.superseded) return decline(\"superseded\");\n if (coverage.facts\n && (coverage.facts.generation !== program.generation || coverage.facts.schemaHash !== program.schemaHash)) {\n return decline(`facts:${coverage.facts.generation}/${program.generation}`);\n }\n // @ref LLP 1012#5-retention-revocation-and-the-fence \u2014 rule 4's twin-side\n // meaning: a held row of a `retain delivered-history` table is readable.\n const readable = Object.keys(program.schema.tables ?? {})\n .filter((name) => program.schema.tables![name]?.retain === \"delivered-history\");\n const overlay = await this.sources.overlay();\n const entries = await this.sources.entries();\n const predicted: string[] = [];\n // Only the tagger/hasher's closed failure set means this coverage was not\n // delivered under the held program. Store construction and overlay\n // mutation sit outside this catch: their bugs must never look hydrating.\n const closure = tagged(closureFromCoverage, coverage, program.schema, program.schemaHash);\n if (!closure) return decline(\"closure\");\n const store = new ClientStore(program.schema, closure, { readable });\n for (const entry of entries) {\n const rows = overlay[entry.intent];\n if (!rows) continue;\n for (const row of rows) {\n if (row.removed) store.remove(row.table, row.id);\n else {\n const value = tagged(tagWireRow, program.schema, row.table, row.row as Record<string, WireValue>);\n if (!value) return decline(`overlay-row:${row.table}`);\n store.put(row.table, value);\n }\n }\n predicted.push(entry.intent);\n }\n return { program, coverage, store, predicted };\n }\n}\n", "import { twinLastDecline } from \"../twin.js\";\n// @ref LLP 1013#7-the-simulator-witness \u2014 the app connects outward before construction.\nimport type { SnapbackClient } from \"../types.js\";\nimport type { PersistedRangeState } from \"../store/range-store.js\";\nimport type { ByteCache, ByteDelta } from \"../store/byte-cache.js\";\nimport { isSnapbackRefusal } from \"../client.js\";\nimport { runSqliteDriverConformance as conformance } from \"../store/sqlite-driver.conformance.js\";\nimport type { SqliteDriver } from \"../store/sqlite-driver.js\";\n\nexport interface WitnessHooks {\n beforeCommit(state: PersistedRangeState, bytes: ByteDelta): Promise<void>;\n emit(event: string, value?: unknown): void;\n}\nexport interface WitnessApplication {\n construct(hooks: WitnessHooks, args: Record<string, unknown>): Promise<SnapbackClient>;\n /** Persist transport/fault configuration outside the range store, before acknowledging. */\n persist?(change: { offline?: boolean; fault?: \"carrier\" | \"none\" }): Promise<void>;\n command?(client: SnapbackClient | undefined, verb: string, args: Record<string, unknown>, hooks: WitnessHooks): Promise<unknown>;\n}\nexport interface WitnessControl { close(): void }\n\n/** The factory overload connects before any carrier opens; the client overload attaches to an existing client. */\nexport { twinLastDecline };\nexport function witnessControl(source: SnapbackClient | WitnessApplication, url: string): WitnessControl {\n if (!/^wss?:\\/\\//.test(url)) throw new TypeError(\"witness URL must be ws:// or wss://\");\n const app = \"construct\" in source ? source : undefined;\n let client = app ? undefined : source as SnapbackClient;\n let closed = false;\n let socket: WebSocket;\n let retry: ReturnType<typeof setTimeout> | undefined;\n let barrier = false;\n const releases = new Set<() => void>();\n const send = (value: unknown) => { if (socket?.readyState === 1) socket.send(JSON.stringify(value)); };\n const hooks: WitnessHooks = {\n emit: (event, value) => send({ event, value }),\n async beforeCommit() {\n if (!barrier) return;\n // Install the release before the acknowledgement. Neither socket.close nor\n // control.close resolves or rejects it: only barrier(off) can allow COMMIT.\n const held = new Promise<void>((resolve) => { releases.add(resolve); });\n hooks.emit(\"barrier reached\");\n await held;\n },\n };\n const construct = async (args: Record<string, unknown>) => {\n if (app) client = await app.construct(hooks, args);\n if (!client) throw new Error(\"construct did not return a client\");\n await client.ready;\n return { constructed: true, snapshot: client.snapshot };\n };\n const run = async (verb: string, args: Record<string, unknown>) => {\n if (verb === \"barrier\") {\n if (args.mode !== \"on\" && args.mode !== \"off\") throw new TypeError(\"barrier mode must be on or off\");\n barrier = args.mode === \"on\";\n if (!barrier) { for (const release of releases) release(); releases.clear(); }\n return { barrier: args.mode };\n }\n if (verb === \"fault\") {\n if (args.mode !== \"carrier\" && args.mode !== \"none\") throw new TypeError(\"fault mode must be carrier or none\");\n if (!app?.persist) throw new Error(\"witness application must persist carrier faults\");\n await app.persist({ fault: args.mode });\n return { fault: args.mode };\n }\n if (verb === \"construct\") return construct(args);\n if (verb === \"offline\" || verb === \"online\") {\n if (!client) throw new Error(\"construct first\");\n await app?.persist?.({ offline: verb === \"offline\" });\n await client[verb]();\n return { connection: client.snapshot.connection };\n }\n if (app?.command) return app.command(client, verb, args, hooks);\n if (verb === \"state\" && client) return { snapshot: client.snapshot, outbox: await client.outbox.entries(),\n coverage: await (client.assetCache as ByteCache | undefined)?.store.coverage() };\n throw new Error(`unsupported witness verb ${verb}`);\n };\n const connect = () => {\n if (closed) return;\n socket = new WebSocket(url);\n socket.onopen = () => hooks.emit(\"connected\", { constructed: client !== undefined });\n socket.onmessage = (message) => {\n // Commands are independent so barrier(off) can release a suspended swap.\n void (async () => {\n let id: unknown;\n try {\n const request = JSON.parse(String(message.data));\n id = request.id;\n if (typeof request.verb !== \"string\") throw new TypeError(\"witness command needs a verb\");\n const result = await run(request.verb, request.args ?? {});\n send({ id, ok: true, result });\n } catch (error) {\n send({ id, ok: false, error: isSnapbackRefusal(error) ? error.envelope : { message: String(error), stack: error instanceof Error ? error.stack : undefined } });\n }\n })();\n };\n socket.onerror = () => {}; // close schedules reconnect; it never releases the barrier.\n socket.onclose = () => { if (!closed) retry = setTimeout(connect, 250); };\n };\n connect();\n return { close() { closed = true; clearTimeout(retry); socket.close(); } };\n}\nexport function runSqliteDriverConformance(open: () => SqliteDriver | Promise<SqliteDriver>): Promise<{ journalMode: string; checks: string[] }> {\n return conformance(open);\n}\n", "import type { SqliteDriver } from \"./sqlite-driver.js\";\n\nexport interface SqliteConformanceResult { journalMode: string; checks: string[] }\n\n/** Portable: the witness passes two independently opened connections to the same scratch database. */\nexport async function runSqliteDriverConformance(open: () => SqliteDriver | Promise<SqliteDriver>): Promise<SqliteConformanceResult> {\n const first = await open();\n const second = await open();\n const checks: string[] = [];\n const check = (condition: unknown, message: string) => { if (!condition) throw new Error(`sqlite conformance: ${message}`); };\n try {\n const mode = await first.get<{ journal_mode: string }>(\"PRAGMA journal_mode=WAL\");\n const readback = await first.get<{ journal_mode: string }>(\"PRAGMA journal_mode\");\n check(mode?.journal_mode === readback?.journal_mode, \"journal_mode readback\");\n checks.push(\"journal_mode readback\");\n await first.exec(\"CREATE TABLE IF NOT EXISTS conformance (key TEXT PRIMARY KEY, value BLOB NOT NULL); DELETE FROM conformance;\");\n for (const length of [1, 1024 * 1024, 0]) {\n const bytes = new Uint8Array(length);\n for (let index = 0; index < length; index++) bytes[index] = index % 251;\n await first.run(\"INSERT INTO conformance VALUES (?, ?)\", [String(length), bytes]);\n const held = await first.get<{ value: Uint8Array }>(\"SELECT value FROM conformance WHERE key = ?\", [String(length)]);\n check(held?.value instanceof Uint8Array && held.value.length === length && held.value.every((value, index) => value === bytes[index]), `BLOB ${length}`);\n checks.push(`BLOB ${length}`);\n }\n const keys = [[\"messages\", \"m\\0x\"], [\"messages\", \"m\\0y\"], [\"messages\", \"\"], [\"messages\", \"quote\\\",\u96EA\"],\n [\"thread\", JSON.stringify({ nested: { words: [\"a\", \"b\"], empty: null } })]];\n for (const key of keys) await first.run(\"INSERT INTO conformance VALUES (?, ?)\", [JSON.stringify(key), new Uint8Array([9])]);\n for (const key of keys) {\n const held = await first.get<{ key: string }>(\"SELECT key FROM conformance WHERE key = ?\", [JSON.stringify(key)]);\n check(held?.key === JSON.stringify(key), \"JSON tuple key round trip\");\n }\n checks.push(\"JSON tuple key round trip\");\n const absent = await first.get(\"SELECT key FROM conformance WHERE key = ?\", [\"missing\"]);\n check(absent === undefined, \"missing row is undefined\");\n checks.push(\"missing row is undefined\");\n const marker = new Error(\"rollback marker\");\n try {\n await first.transaction(async (tx) => {\n await tx.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"rollback\", new Uint8Array([1])]);\n await Promise.resolve();\n throw marker;\n });\n throw new Error(\"transaction swallowed throw\");\n } catch (error) { check(error === marker, \"rollback propagates the body error\"); }\n check(await first.get(\"SELECT key FROM conformance WHERE key = ?\", [\"rollback\"]) === undefined, \"rollback on throw\");\n checks.push(\"rollback on throw\");\n // The body ends its transaction. This proves error propagation and reuse,\n // not recovery from a COMMIT failure that leaves a live transaction.\n let commitFailed = false;\n await first.transaction(async (tx) => {\n await tx.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"failed-commit\", new Uint8Array([7])]);\n await tx.run(\"ROLLBACK\");\n }).catch(() => { commitFailed = true; });\n check(commitFailed, \"COMMIT after rollback must fail\");\n check(await first.get(\"SELECT key FROM conformance WHERE key = ?\", [\"failed-commit\"]) === undefined, \"failed COMMIT leaves no row\");\n await first.transaction((tx) => tx.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"after-failed-commit\", new Uint8Array([8])]));\n check(await first.get(\"SELECT key FROM conformance WHERE key = ?\", [\"after-failed-commit\"]) !== undefined, \"next transaction after failed COMMIT succeeds\");\n checks.push(\"body-ended transaction rejects and next transaction succeeds\");\n let release!: () => void;\n let reached!: () => void;\n let committed = false;\n let interleaved = false;\n const barrier = new Promise<void>((resolve) => { release = resolve; });\n const atBarrier = new Promise<void>((resolve) => { reached = resolve; });\n const write = first.transaction(async (tx) => {\n await tx.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"barrier\", new Uint8Array([2])]);\n reached();\n await barrier;\n return 42;\n }).then((value) => { committed = true; return value; });\n await atBarrier;\n const competing = second.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"competing\", new Uint8Array([3])])\n .then(() => { if (!committed) interleaved = true; }, (error) => {\n check(/busy|locked/i.test(String(error)), `competing writer must busy or wait: ${error}`);\n });\n // Also prove that an outer operation on this driver cannot enter its suspended transaction.\n let outerFinished = false;\n const outer = first.run(\"INSERT INTO conformance VALUES (?, ?)\", [\"outer\", new Uint8Array([4])]).then(() => { outerFinished = true; });\n await new Promise((resolve) => setTimeout(resolve, 25));\n const prematureOuter = outerFinished;\n release();\n check(await write === 42, \"transaction result\");\n await Promise.all([competing, outer]);\n check(!interleaved && !prematureOuter, \"late beforeCommit holds transaction\");\n checks.push(\"late beforeCommit holds transaction\", \"transaction result\");\n return { journalMode: readback!.journal_mode, checks };\n } finally {\n await first.close();\n await second.close();\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["ir_contract_default", "I53_MAX", "I53_MIN", "CURRENT_VALUE_MODEL_VERSION", "ir_contract_default", "twinLastDecline", "isSnapbackRefusal", "runSqliteDriverConformance", "open", "first", "second", "checks", "check", "condition", "message", "mode", "readback", "length", "bytes", "index", "held", "value", "keys", "key", "absent", "marker", "tx", "error", "commitFailed", "release", "reached", "committed", "interleaved", "barrier", "resolve", "atBarrier", "write", "competing", "outerFinished", "outer", "prematureOuter", "witnessControl", "source", "url", "app", "client", "closed", "socket", "retry", "barrier", "releases", "send", "value", "hooks", "event", "held", "resolve", "construct", "args", "run", "verb", "release", "connect", "message", "id", "request", "result", "error", "isSnapbackRefusal", "runSqliteDriverConformance", "open"]
7
+ }
package/dist/expo.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./react-core.js";
2
+ export { AssetImage, AssetVideo, useAssetFile } from "./react-native.js";
3
+ export type { AssetImageProps, AssetVideoProps } from "./react-native.js";
4
+ export * from "./expo/index.js";
package/dist/expo.mjs ADDED
@@ -0,0 +1,9 @@
1
+ var Ce=Object.defineProperty;var ut=Object.getOwnPropertyDescriptor;var lt=Object.getOwnPropertyNames;var ft=Object.prototype.hasOwnProperty;var yt=(t,e)=>{for(var r in e)Ce(t,r,{get:e[r],enumerable:!0})},je=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of lt(e))!ft.call(t,s)&&s!==r&&Ce(t,s,{get:()=>e[s],enumerable:!(n=ut(e,s))||n.enumerable});return t},Q=(t,e,r)=>(je(t,e,"default"),r&&je(r,e,"default"));export*from"snapback2/react-core";var W={};yt(W,{AssetImage:()=>ve,AssetVideo:()=>be,useAssetFile:()=>we});Q(W,cr);import*as cr from"snapback2/react-core";import U,{useEffect as se,useMemo as He,useRef as ze,useState as Ge}from"react";import{Image as Rt,PixelRatio as kt,View as At}from"react-native";import{VideoView as xt,useVideoPlayer as Pt}from"expo-video";import{useSnapbackClient as Et,useSnapbackSnapshot as Ot}from"snapback2/react-core";import{SnapbackRefusal as Xe}from"snapback2/client";import{useEffect as re,useMemo as ne,useRef as me,useState as Ue,useSyncExternalStore as wt}from"react";import{useSnapbackClient as We,useSnapbackSnapshot as Ve}from"snapback2/react-core";import{isSnapbackRefusal as ht,SnapbackRefusal as vt}from"snapback2/client";var ee=/^s2id-[0-9a-f]{32}-[0-9a-f]{16}$/;function $(t,e){return e===null?t:`${t}.w${e}`}function pt(t){return`${t.table}\0${t.id}`}function Z(t){return typeof t=="number"&&Number.isSafeInteger(t)&&t>0}function gt(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=t,r=Object.keys(e).sort().join(",");if(!(r!=="height,id,width"&&r!=="duration,height,id,width")&&!(typeof e.id!="string"||!ee.test(e.id))&&!(!Z(e.width)||!Z(e.height))&&!("duration"in e&&!Z(e.duration)))return e.id}function C(t,e=new Set){let r=gt(t);if(r!==void 0)e.add(r);else if(Array.isArray(t))for(let n of t)C(n,e);else if(t&&typeof t=="object")for(let n of Object.values(t))C(n,e);return e}function Be(t,e){let r=new Set;for(let n of t)C(n.row,r);for(let n of e)n.data!==void 0&&C(n.data,r);return r}function pe(t){let e=new Set;for(let r of Object.values(t.viewed))for(let n of r.assets)e.add(n);if(e.size===0)return e;for(let[r,n]of Object.entries(t.rows))if(!t.viewed[r])for(let s of C(n.row))e.delete(s);return e}function mt(t){if(!t||typeof t!="object"||typeof t.assetId!="string"||!ee.test(t.assetId))throw new TypeError("byte cache put must name an engine-minted asset id");if(t.width!==null&&!Z(t.width))throw new TypeError("byte cache put width must be null or a positive integer");if(!(t.bytes instanceof Uint8Array))throw new TypeError("byte cache put bytes must be a Uint8Array");if(typeof t.type!="string")throw new TypeError("byte cache put type must be a string")}function Le(t,e,r){let n={put:[],remove:[]};for(let a of e.viewed??[]){if(!a||typeof a.table!="string"||typeof a.id!="string")throw new TypeError("viewed rows must be row references");let c=pt(a),u=new Set(t.viewed[c]?.assets??[]);C(t.rows[c]?.row,u),t.viewed[c]={at:Date.now(),assets:[...u]}}let s=pe(t);for(let[a,c]of Object.entries(t.assets))r.has(c.assetId)&&!s.has(c.assetId)||(delete t.assets[a],n.remove.push(a));for(let a of e.bytes?.touch??[]){let c=t.assets[a];c&&(c.lastRead=++t.clock)}let o=e.bytes?.ceiling??Number.POSITIVE_INFINITY;if(!(o>0))throw new TypeError("byte cache ceiling must be positive");for(let a of e.bytes?.put??[]){if(mt(a),!r.has(a.assetId)||s.has(a.assetId))continue;let c=a.bytes.byteLength;if(c===0||c>o)continue;let u=$(a.assetId,a.width);t.assets[u]={assetId:a.assetId,width:a.width,size:c,type:a.type,lastRead:++t.clock},n.put.push({key:u,bytes:a.bytes})}let d=Object.values(t.assets).reduce((a,c)=>a+c.size,0);for(;d>o;){let[a,c]=Object.entries(t.assets).reduce((u,h)=>h[1].lastRead<u[1].lastRead?h:u);delete t.assets[a],n.remove.push(a),d-=c.size}return n.put=n.put.filter(a=>t.assets[a.key]!==void 0),n.remove=[...new Set(n.remove)].filter(a=>t.assets[a]===void 0),n}var Ne=Symbol.for("snapback2.native.assetFiles");function Fe(t,e){Object.defineProperty(t,Ne,{value:e})}function Me(t){return t.assetCache?.store?.[Ne]}function ge(t,e){return`${t.replace(/\/$/,"")}/${encodeURIComponent(e).replace(/\./g,"%2E")}/`}var te=class{constructor(e){this.fs=e}partition;state;revision=0;custody=0;assetRevisions=new Map;listeners=new Set;materialized=new Set;tail=Promise.resolve();getRevision=()=>this.revision;getAssetRevision=e=>`${this.custody}:${this.assetRevisions.get(e)??0}`;getCustody=()=>this.custody;available=()=>this.partition!==void 0&&!this.state?.sealed;subscribe=e=>(this.listeners.add(e),()=>{this.listeners.delete(e)});changed(e=!1){this.revision++,e&&this.custody++;for(let r of this.listeners)try{r()}catch{}}enqueue(e){let r=this.tail.then(e);return this.tail=r.catch(()=>{}),r}remove(e){return this.materialized.delete(e),this.fs.remove(e).catch(()=>{})}path(e,r=this.partition){return r===void 0?void 0:ge(this.fs.root,r)+e}eligible(e,r){return this.partition!==void 0&&!this.state?.sealed&&this.state?.assets[$(e,r)]!==void 0}async get(e,r,n){if(!ee.test(r))return;if(n!==null&&(!Number.isSafeInteger(n)||n<=0))throw new TypeError("asset width must be a positive integer");let s=this.getAssetRevision(r),o=this.path($(r,n));if(!o)return;let a=await e.viewOnce(r)||await e.viewed(r)?void 0:await e.getBytes(r,n);return this.enqueue(async()=>{if(!a||s!==this.getAssetRevision(r)||!this.eligible(r,n)||await e.viewOnce(r)||await e.viewed(r)){await this.remove(o);return}if((!this.materialized.has(o)||!await this.fs.exists?.(o))&&await this.fs.write(o,a.bytes),s!==this.getAssetRevision(r)||!this.eligible(r,n)||await e.viewOnce(r)||await e.viewed(r)){await this.remove(o);return}return this.materialized.add(o),o})}async opened(e,r){this.materialized.clear(),this.partition=e,this.state=r,this.changed(!0);let n=ge(this.fs.root,e);await this.enqueue(async()=>{for(let s of await this.fs.list(n))r?.assets[s]||await this.remove(n+s)})}async replaced(e,r){let n=this.state?.sealed!==e.sealed,s=Ie(this.state),o=Ie(e),d=new Set([...s.keys(),...o.keys()].filter(c=>s.get(c)!==o.get(c)));for(let c of[...r.remove,...r.put.map(u=>u.key)])d.add(c.split(".w")[0]);for(let c of d)this.assetRevisions.set(c,(this.assetRevisions.get(c)??0)+1);this.state=e,this.changed(n);let a=this.partition;this.enqueue(async()=>{for(let c of[...r.remove,...r.put.map(u=>u.key)]){let u=this.path(c,a);u&&await this.remove(u)}}).catch(()=>{})}async wiped(e){this.partition===e&&this.closed(),await this.enqueue(()=>this.remove(ge(this.fs.root,e)))}closed(){this.materialized.clear(),this.partition=void 0,this.state=void 0,this.changed(!0)}};function Ie(t){let e=new Map;for(let[r,n]of Object.entries(t?.rows??{}))for(let s of C(n.row)){let o=e.get(s)??[];o.push(JSON.stringify([r,n.row,!!t?.viewed[r]])),e.set(s,o)}return new Map([...e].map(([r,n])=>[r,n.sort().join(`
2
+ `)]))}function _e(t,e){return{carrier:t.carrier,async open(r){let n=await t.open(r);try{await e.opened(r,n)}catch(s){throw e.closed(),await t.close(),s}return n},async replace(r,n){await t.replace(r,n),await e.replaced(r,n)},readBytes:t.readBytes?r=>t.readBytes(r):void 0,async wipe(r){await t.wipe(r),await e.wiped(r)},async close(){e.closed(),await t.close()}}}var De=()=>"0",bt=()=>()=>{},$e=t=>ht(t)?t:new vt({code:"E_OFFLINE_CARRIER",family:"offline",message:String(t),site:"useAssetFile",rewrite:"open a writable Expo cache directory and retry",guide:"offline"});function St(t){let e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r=[];for(let n=0;n<t.bytes.length;n+=3){let s=t.bytes[n],o=t.bytes[n+1],d=t.bytes[n+2],a=s<<16|(o??0)<<8|(d??0);r.push(e[a>>>18],e[a>>>12&63],o===void 0?"=":e[a>>>6&63],d===void 0?"=":e[a&63])}return`data:${t.type.replace(/[;,\r\n]/g,"")};base64,${r.join("")}`}function Ke(t,e){let r=We(),n=Ve(),s=Me(r),o=ne(()=>s&&t?()=>s.getAssetRevision(t.id):De,[s,t?.id]),d=wt(s?.subscribe??bt,o,De),a=ne(()=>({client:r,id:t?.id,width:e,auth:n.authGeneration,files:s,revision:d}),[r,t?.id,e,n.authGeneration,s,d]),[c,u]=Ue();return re(()=>{let h=!0;if(!t||!s||!r.assetCache){u({identity:a});return}return s.get(r.assetCache,t.id,e??null).then(w=>{h&&u({identity:a,uri:w})},w=>{h&&u({identity:a,error:w})}),()=>{h=!1}},[a]),{files:s,revision:d,custody:s?.getCustody()??0,checked:c?.identity===a,uri:c?.identity===a?c.uri:void 0,error:c?.identity===a?c.error:void 0}}function we(t,e={}){return Ke(t,e.width).uri}function he(t,e={}){let r=We(),n=Ve(),s=Ke(t,e.width),o=ne(()=>s.error===void 0?void 0:$e(s.error),[s.error]),d=me({}).current,a=ne(()=>({client:r,id:t?.id,auth:n.authGeneration,custody:s.custody}),[r,t?.id,n.authGeneration,s.custody]),[c,u]=Ue(),h=me(void 0),w=me(a);return w.current=a,re(()=>(w.current=a,()=>{w.current=void 0}),[a]),re(()=>{if(!t||e.enabled===!1||!s.checked||o||s.uri||h.current?.identity===a&&(h.current.width===e.width||c?.identity!==a)||s.files&&!s.files.available())return;h.current={identity:a,width:e.width};let v=()=>w.current===a&&r.snapshot.authGeneration===a.auth&&(s.files?.getCustody()??0)===a.custody;(async()=>{if(await r.ready,!v())return;if(await r.assetCache?.viewed(t.id)){v()&&c?.identity!==a&&u({identity:a,revision:s.files?.getAssetRevision(t.id)??"0"});return}if(!v())return;let k=await r.assetUrl(t,{width:e.width});if(!k||!v())return;let P=await r.assetBytes(k,{viewing:d});v()&&(r.assetCache&&!await r.assetCache.canServe(t.id,d)||v()&&u({identity:a,revision:s.files?.getAssetRevision(t.id)??"0",uri:St(P)}))})().catch(k=>{if(w.current!==a)return;let P=$e(k);u({identity:a,revision:s.files?.getAssetRevision(t.id)??"0",refusal:P})})},[a,c,s.checked,s.uri,e.width,e.enabled,o]),re(()=>{if(!t||!s.files||c?.identity!==a||!c.uri||c.revision===s.revision)return;let v=!0,k=s.custody;return r.assetCache?.canServe(t.id,d).then(P=>{v&&w.current===a&&s.files?.getCustody()===k&&u({...c,revision:s.revision,uri:P?c.uri:void 0})}).catch(()=>{v&&w.current===a&&u({...c,revision:s.revision,uri:void 0})}),()=>{v=!1}},[c,a,s.revision]),{settled:s.checked&&(!!s.uri||c?.identity===a),uri:s.uri??(c?.identity===a&&c.revision===s.revision?c.uri:void 0),refusal:o??(c?.identity===a?c.refusal:void 0)}}function ve({asset:t,width:e,height:r,style:n,onLayout:s,onRefusal:o,onSourceChange:d,...a}){let[c,u]=Ge(),h=Math.max(1,Math.ceil((c??e??t.width)*kt.get())),{uri:w,refusal:v,settled:k}=he(t,{width:h}),P=ze(o);return P.current=o,U.useLayoutEffect(()=>{d?.(w,k)},[w,k,d]),se(()=>{v&&P.current?.(v)},[v]),U.createElement(Rt,{...a,onLayout:q=>{q.nativeEvent.layout.width>0&&u(q.nativeEvent.layout.width),s?.(q)},source:w?{uri:w}:void 0,style:[{width:e??t.width,height:r??(e===void 0?t.height:void 0),aspectRatio:t.width/t.height},n]})}function Tt({uri:t,asset:e,play:r,onPlay:n,onRefusal:s,onSourceChange:o,autoPlay:d,onPlaybackStart:a,...c}){let u=Pt(t);return se(()=>{let h=u.addListener("statusChange",({status:v,error:k})=>{v==="error"&&s?.(new Xe({code:"E_ASSET_TYPE",family:"input",site:"AssetVideo",message:k?.message??"native video decoder refused the source",rewrite:"use a video source supported by the native player",guide:"assets/client"}))}),w=u.addListener("playingChange",({isPlaying:v})=>{v&&n()});return r&&u.play(),()=>{w.remove(),h.remove()}},[u,n,r]),U.createElement(xt,{...c,player:u,nativeControls:c.nativeControls??!0})}function be({asset:t,width:e,height:r,style:n,onRefusal:s,onSourceChange:o,onPlaybackStart:d,autoPlay:a=!1,...c}){let u=Et(),h=Ot(),w=He(()=>({client:u,id:t.id,auth:h.authGeneration}),[u,t.id,h.authGeneration]),[v,k]=Ge(),P=ze(s);P.current=s,se(()=>{let l=!0;return(async()=>{await u.ready;let f=!!(await u.assetCache?.viewOnce(t.id)||await u.assetCache?.viewed(t.id));l&&k({identity:w,once:f})})().catch(f=>{l&&P.current?.(f)}),()=>{l=!1}},[w]);let O=v?.identity===w?v.once:void 0,q=He(()=>O?new Xe({code:"E_ASSET_TYPE",family:"input",site:"AssetVideo",message:"view-once video is not played on native in v1: the native player cannot open an in-memory source and a view-once clip never touches disk",rewrite:"send the clip as an ordinary attachment, or wait for the native view-once video ruling",guide:"assets/client"}):void 0,[O]),{uri:N,refusal:M,settled:_}=he(O===!1?t:null,{enabled:O===!1});U.useLayoutEffect(()=>{o?.(N,_)},[N,_,o]),se(()=>{(q??M)&&P.current?.(q??M)},[q,M]);let i=[{width:e??t.width,height:r??(e===void 0?t.height:void 0),aspectRatio:t.width/t.height},n];return N?U.createElement(Tt,{...c,asset:t,uri:N,onRefusal:s,play:a,onPlay:d??(()=>{}),style:i}):U.createElement(At,{testID:c.testID,style:i})}import{fetch as Wt}from"expo/fetch";import{createClient as Vt}from"snapback2/client";import{defaultDatabaseDirectory as qt,openDatabaseAsync as jt}from"expo-sqlite";function Ye(){let t=Promise.resolve();return e=>{let r=t.then(e);return t=r.then(()=>{},()=>{}),r}}function ie(t){return{async run(e,r=[]){await t.runAsync(e,r)},async get(e,r=[]){return await t.getFirstAsync(e,r)??void 0},async all(e,r=[]){return t.getAllAsync(e,r)}}}var Ct=/^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;function Se(t="snapback2.db"){if(!Ct.test(t))throw new TypeError(`expoSqliteDriver: the database name must be a plain file name (letters, digits, ., _, -), not a path: ${JSON.stringify(t)}`);let e,r=()=>e??=jt(t,{useNewConnection:!0}),n=Ye();return{identity:`expo-sqlite:${qt.replace(/\/*$/,"")}/${t}`,run:(s,o)=>n(async()=>ie(await r()).run(s,o)),get:(s,o)=>n(async()=>ie(await r()).get(s,o)),all:(s,o)=>n(async()=>ie(await r()).all(s,o)),exec:s=>n(async()=>(await r()).execAsync(s)),transaction:s=>n(async()=>{let o;return await(await r()).withExclusiveTransactionAsync(async d=>{o=await s(ie(d))}),o}),close:()=>n(async()=>{let s=e;e=void 0,s&&await(await s).closeAsync()})}}import*as Bt from"expo-secure-store";function Re(t="default",e=Bt){if(!/^[A-Za-z0-9._-]+$/.test(t))throw new TypeError("expoSecureTokenStore name must contain only A-Z, a-z, 0-9, '.', '_', or '-'");let r={token:`snapback2.${t}.token`,principal:`snapback2.${t}.principal`},n={},s=Promise.resolve();function o(c){let u=s.then(c);return s=u.then(()=>{},()=>{}),u}let d=c=>o(async()=>(n[c]===void 0&&(n[c]=await e.getItemAsync(r[c])),n[c])),a=(c,u)=>o(async()=>{if(u!==null&&new TextEncoder().encode(u).byteLength>2048)throw new TypeError("expoSecureTokenStore values must fit in 2 KB");if(u===null&&c==="token"){delete n.token,delete n.principal;let w=(await Promise.allSettled(Object.values(r).map(v=>e.deleteItemAsync(v)))).find(v=>v.status==="rejected");if(w?.status==="rejected")throw w.reason;n.token=n.principal=null}else delete n[c],u===null?await e.deleteItemAsync(r[c]):await e.setItemAsync(r[c],u),n[c]=u});return{load:()=>d("token"),save:c=>a("token",c),loadPrincipal:()=>d("principal"),savePrincipal:c=>a("principal",c)}}function g(t){return t===void 0?"undefined":t===null||typeof t!="object"?JSON.stringify(t):Array.isArray(t)?`[${t.map(g).join(",")}]`:`{${Object.keys(t).sort().map(e=>`${JSON.stringify(e)}:${g(t[e])}`).join(",")}}`}function ke(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function oe(t,e){if(t.op!==e.op||!ke(t.args)||!ke(e.args))return;let r=t.cursorArg??e.cursorArg,n={...e.args},s={...t.args};if(delete n[Object.hasOwn(n,e.cursorArg)?e.cursorArg:r],delete s[r],g(n)!==g(s))return;let o=t.args[r];return o===null||typeof o=="string"?o:void 0}function Je(t,e,r){let n=new Map;for(let a of t){if((a.nextSeq??a.seq)<r||typeof a.next!="string")continue;let c=oe(a,e);c!==void 0&&n.set(c,n.has(c)&&n.get(c)!==a.next?null:a.next)}let s=new Set([e.watermark]),o=[],d=e.watermark;for(;n.has(d);){let a=n.get(d);if(a==null||s.has(a))break;s.add(a),o.push(a),d=a}return o}function Qe(t,e,r){if(!e||oe({...e,args:ke(e.args)?{...e.args,[e.cursorArg??t.cursorArg]:e.watermark}:e.args},t)===void 0||typeof e.watermark!="string"||(t.consumed??[]).includes(e.watermark))return!1;let n=r.indexOf(e.watermark);t.progress!==void 0&&!r.includes(t.progress)&&(t.progress=[...t.consumed??[]].reverse().find(o=>r.includes(o)));let s=t.progress===void 0?-1:r.indexOf(t.progress);return n<0||n<=s?!1:(t.progress=e.watermark,t.consumed=[...new Set([...t.consumed??[],...r.slice(0,n+1)])],!0)}var T=class extends Error{constructor(r,n,s){super(n);this.code=r;this.rewrite=s;this.name="OfflineStoreFailure"}},Ze={put:[],remove:[]};function V(){return{version:1,sealed:!1,clock:0,rows:{},answers:{},watermarks:{},outbox:{},grants:{},overlay:{},overlayMisses:{},overlayWatermarks:{},overlayNegatives:{},bounds:{},programs:{},negative:{},rowSeq:{},assets:{},viewed:{}}}function y(t){return structuredClone(t)}function L(t,e){return`${t}\0${g(e===void 0?e:G(e))}`}function b(t){return`${t.table}\0${t.id}`}function ae(t){let e=t.indexOf("\0");return{table:t.slice(0,e),id:t.slice(e+1)}}function ce(t,e){let r=b(e);(t.get(r)?.seq??-1)<=e.seq&&t.set(r,e)}function de(t){if(!t||typeof t!="object"||Array.isArray(t))return;let e=t.$row;return e!==null&&typeof e=="object"&&!Array.isArray(e)&&Object.keys(e).sort().join(",")==="id,table"&&typeof e.table=="string"&&typeof e.id=="string"?{table:e.table,id:e.id}:void 0}function I(t,e=new Map){let r=de(t);if(r)e.set(b(r),r);else if(Array.isArray(t))for(let n of t)I(n,e);else if(t&&typeof t=="object")for(let n of Object.values(t))I(n,e);return[...e.values()]}function Ae(t,e,r){let n=de(t);return n?r?.[b(n)]?null:e[b(n)]?.row??null:Array.isArray(t)?t.map(s=>Ae(s,e,r)):!t||typeof t!="object"?t:Object.fromEntries(Object.entries(t).map(([s,o])=>[s,Ae(o,e,r)]))}function xe(t,e,r=new Map){if(e==null){for(let s of I(t))r.set(b(s),s);return[...r.values()]}let n=de(t);if(n&&e===null)r.set(b(n),n);else if(Array.isArray(t)&&Array.isArray(e))for(let s=0;s<t.length;s+=1)xe(t[s],s<e.length?e[s]:void 0,r);else if(t&&e&&typeof t=="object"&&typeof e=="object"&&!Array.isArray(t)&&!Array.isArray(e))for(let s of Object.keys(t))xe(t[s],Object.prototype.hasOwnProperty.call(e,s)?e[s]:void 0,r);return[...r.values()]}function Pe(t,e,r){let n=de(t);if(n&&e==null&&r.has(b(n)))return t;if(Array.isArray(t)&&Array.isArray(e)){let s=e.map((o,d)=>d<t.length?Pe(t[d],o,r):o);for(let o=e.length;o<t.length;o+=1)I(t[o]).some(d=>r.has(b(d)))&&s.push(y(t[o]));return s}if(t&&e&&typeof t=="object"&&typeof e=="object"&&!Array.isArray(t)&&!Array.isArray(e)){let s=y(e);for(let o of Object.keys(t)){let d=t[o];Object.prototype.hasOwnProperty.call(s,o)?s[o]=Pe(d,s[o],r):I(d).some(a=>r.has(b(a)))&&(s[o]=y(d))}return s}return e==null&&I(t).some(s=>r.has(b(s)))?y(t):e}function Lt(t,e){return t.site===e.site&&t.table===e.table&&t.index===e.index&&g(t.prefix)===g(e.prefix)&&t.order===e.order&&g(t.after)===g(e.after)&&g(t.before)===g(e.before)&&g(t.gt)===g(e.gt)&&g(t.gte)===g(e.gte)&&g(t.lt)===g(e.lt)&&g(t.lte)===g(e.lte)&&t.ruleHash===e.ruleHash}function z(t,e){if(t===e)return 0;if(typeof t=="number"&&typeof e=="number"||typeof t=="string"&&typeof e=="string")return t<e?-1:1;if(typeof t=="boolean"&&typeof e=="boolean")return t?1:-1;let r=g(t),n=g(e);return r<n?-1:r>n?1:0}function B(t,e){for(let r=0;r<Math.min(t.length,e.length);r+=1){let n=z(t[r],e[r]);if(n!==0)return n}return t.length-e.length}function et(t,e,r){let n=t?.tables?.[e.table]?.indexes?.[e.index]?.columns;if(!r||!Array.isArray(n)||!n.every(o=>typeof o=="string"))return;let s=n.map(o=>r.row[o]);return s.some(o=>o===void 0)?void 0:s}function tt(t,e){if(t!=null)return Array.isArray(t)?t.length===e?t:void 0:e===1?[t]:void 0}function It(t,e){if(t.length<e.prefix.length||B(t.slice(0,e.prefix.length),e.prefix)!==0)return!1;let r=t.slice(e.prefix.length),n=tt(e.after,r.length),s=tt(e.before,r.length);if(n&&(e.order==="Asc"?B(r,n)<=0:B(r,n)>=0)||s&&(e.order==="Asc"?B(r,s)>=0:B(r,s)<=0))return!1;let o=r[0];return!(e.gt!==null&&e.gt!==void 0&&z(o,e.gt)<=0||e.gte!==null&&e.gte!==void 0&&z(o,e.gte)<0||e.lt!==null&&e.lt!==void 0&&z(o,e.lt)>=0||e.lte!==null&&e.lte!==void 0&&z(o,e.lte)>0)}function Nt(t,e,r,n,s){let o=et(s,e,n[b({table:e.table,id:t})]);if(!o)return r.stop==="Exhausted";if(!It(o,r))return!1;if(r.stop==="Exhausted")return!0;let d=r.rowIds.map(u=>et(s,r,n[b({table:r.table,id:u})])).filter(Array.isArray);if(d.length===0)return!1;let a=d.reduce((u,h)=>B(u,h)<=0?u:h),c=d.reduce((u,h)=>B(u,h)>=0?u:h);return B(o,a)>=0&&B(o,c)<=0}function G(t){return JSON.parse(JSON.stringify(t))}function rt(t){if(!t)return V();if(t=G(t),t.version!==1||typeof t.rows!="object"||typeof t.answers!="object")throw new T("E_OFFLINE_CARRIER","range store contains an unsupported format");return{...t,watermarks:t.watermarks??{},outbox:t.outbox??{},grants:t.grants??{},overlay:t.overlay??{},overlayMisses:t.overlayMisses??{},overlayWatermarks:t.overlayWatermarks??{},overlayNegatives:t.overlayNegatives??{},bounds:t.bounds??{},programs:t.programs??{},negative:t.negative??{},rowSeq:t.rowSeq??{},assets:t.assets??{},viewed:t.viewed??{}}}function nt(t){return[...t].sort((e,r)=>(e.order??Number.MAX_SAFE_INTEGER)-(r.order??Number.MAX_SAFE_INTEGER)||e.updatedAt-r.updatedAt||L(e.op,e.args).localeCompare(L(r.op,r.args)))[0]}var Ft=2;function Mt(t){return Object.values(t).sort((e,r)=>(e.order??e.createdAt)-(r.order??r.createdAt)||e.createdAt-r.createdAt||(e.intent<r.intent?-1:1))}var K=class{constructor(e){this.backend=e}state=V();partition;tail=Promise.resolve();outbox={all:async()=>this.read(()=>(this.ensureUnsealed(),y(Mt(this.state.outbox)))),get:async e=>this.read(()=>(this.ensureUnsealed(),this.state.outbox[e]&&y(this.state.outbox[e]))),put:async e=>this.commit({outbox:{put:[e]}}),remove:async e=>this.commit({outbox:{remove:[e]}}),grant:async()=>this.read(()=>{this.ensureUnsealed();let e=this.state.currentGrant===void 0?void 0:this.state.grants[this.state.currentGrant];return e&&y(e)}),grants:async()=>this.read(()=>(this.ensureUnsealed(),y(Object.values(this.state.grants)))),bounds:async()=>this.read(()=>(this.ensureUnsealed(),y(this.state.bounds))),overlay:async()=>this.read(()=>(this.ensureUnsealed(),y(this.state.overlay))),programs:async()=>this.read(()=>(this.ensureUnsealed(),y(this.state.programs)))};async open(e){if(typeof e!="string"||e.length===0)throw new T("E_OFFLINE_CARRIER","range store partition must be a principal id");await this.enqueue(async()=>{this.partition!==e&&(this.partition!==void 0&&this.partition!==e&&await this.backend.close(),this.partition=e,this.state=rt(await this.backend.open(e)))})}async unseal(){await this.enqueue(async()=>{if(this.ensureOpen(),!this.state.sealed)return;let e={...y(this.state),sealed:!1};await this.backend.replace(e,Ze),this.state=e})}async seal(){await this.enqueue(async()=>{this.ensureOpen();let e={...y(this.state),sealed:!0};await this.backend.replace(e,Ze),this.state=e})}async wipe(e){await this.enqueue(async()=>{await this.backend.wipe(e),this.partition===e&&(this.partition=void 0,this.state=V())})}async answer(e,r){return this.read(()=>{if(this.state.sealed)return;let n=L(e,r),s=this.state.answers[n];if(!s)return;let o=this.state.watermarks[n],{refs:d,order:a,fingerprint:c,cursorArg:u,data:h,...w}=y(s);return{...w,...h===void 0?{}:{data:Ae(h,this.state.rows,this.state.viewed)},...o===void 0?{}:{caughtUp:!0,watermark:o.watermark}}})}async coverage(){return this.read(()=>{if(this.state.sealed)return{rows:[],ranges:[],absences:[],verdicts:[],watermarks:[],viewed:[],evicted:[]};let e=new Map,r=new Map,n=new Map;for(let d of Object.values(this.state.answers)){for(let a of d.receipt?.ranges??[])e.set(`${a.site}\0${g(a.prefix)}\0${g([a.order,a.after,a.before,a.gt,a.gte,a.lt,a.lte])}`,y(a));for(let a of d.receipt?.absences??[])r.set(g(a),y(a));for(let a of d.receipt?.verdicts??[])n.set(g(a),y(a))}let s=Object.keys(this.state.viewed).map(ae);for(let d of s){let a=[d.table,"byId",[d.id]];r.set(g(a),a)}let o;for(let d of Object.values(this.state.answers))(o===void 0||d.deliveredAt>o)&&(o=d.deliveredAt);return{rows:y(Object.entries(this.state.rows).filter(([d])=>!this.state.viewed[d]).map(([,d])=>d)),ranges:[...e.values()],absences:[...r.values()],verdicts:[...n.values()],watermarks:y(Object.values(this.state.watermarks)),viewed:s,evicted:Object.keys(this.state.negative).map(ae),...this.state.facts===void 0?{}:{facts:y(this.state.facts)},...this.state.superseded===void 0?{}:{superseded:y(this.state.superseded)},...o===void 0?{}:{deliveredAt:o}}})}async commit(e){await this.enqueue(async()=>{this.ensureOpen();let{bytes:r,...n}=e,s={put:[],remove:[]},o=rt(this.applySwap(y(this.state),{...G(n),...r===void 0?{}:{bytes:r}},s));await this.backend.replace(o,s),this.state=o})}async readBytes(e,r){return this.read(async()=>{if(this.state.sealed)return;let n=$(e,r),s=this.state.assets[n];if(!s)return;let o=await this.backend.readBytes?.(n);return o&&o.byteLength===s.size?{bytes:o,type:s.type}:void 0})}async bytes(){return this.read(()=>{if(this.state.sealed)return{entries:[],totalBytes:0,viewed:[]};let e=y(Object.values(this.state.assets));return{entries:e,totalBytes:e.reduce((r,n)=>r+n.size,0),viewed:Object.keys(this.state.viewed).map(ae)}})}async rowsReferencing(e){return this.read(()=>this.state.sealed?[]:y(Object.values(this.state.rows).filter(r=>C(r.row).has(e))))}async viewedAsset(e){return this.read(()=>!this.state.sealed&&pe(this.state).has(e))}async viewedRows(){return this.read(()=>this.state.sealed?[]:Object.keys(this.state.viewed).map(ae))}async touchBytes(e){await this.enqueue(async()=>{if(!(this.partition===void 0||this.state.sealed))for(let r of e){let n=this.state.assets[r];n&&(n.lastRead=++this.state.clock)}})}async close(){await this.enqueue(async()=>{this.partition=void 0,this.state=V(),await this.backend.close()})}applySwap(e,r,n){if(e.sealed)throw new T("E_OFFLINE_CARRIER","range store is sealed");if(r.resetCoverage&&(e.rows={},e.rowSeq={},e.negative={},e.answers={},e.watermarks={},e.overlayWatermarks=Object.fromEntries(Object.keys(e.overlay).map(i=>[i,null])),e.overlayMisses={},delete e.facts),r.superseded===null&&delete e.superseded,r.resumeSeq!==void 0&&(!Number.isSafeInteger(r.resumeSeq)||r.resumeSeq<0))throw new TypeError("store resume seq must be a non-negative safe integer");r.facts&&(e.facts=y(r.facts)),r.superseded&&(e.superseded=y(r.superseded));let s=new Map,o=new Map,d=(i,l)=>{let f=b(i);(o.get(f)?.seq??-1)<=i.seq&&o.set(f,{seq:i.seq,source:l}),ce(s,i)},a=i=>{for(let[l,f,p]of i.receipt?.absences??[]){if(f!=="byId"||p.length!==1)throw new TypeError("store receipts may evict only a primary byId absence");typeof p[0]=="string"&&d({table:l,id:p[0],seq:i.seq},"absence")}};for(let i of r.dropAnswers??[])delete e.answers[i];for(let i of r.refreshes??[]){let l=e.answers[L(i.op,i.args)];if(!(!l||i.seq<l.seq)){if(l.validator!==void 0&&l.validator!==i.validator)throw new TypeError(`conditional validator changed for ${i.op}`);i.seq!==l.seq&&(l.nextSeq??=l.seq,l.seq=i.seq,l.validator=i.validator,l.deliveredAt=i.deliveredAt??Date.now(),l.order=++e.clock,a(l))}}let c=y(e.answers),u=[];for(let i of r.tombstones??[])d(i,"tombstone");for(let i of r.deliveries??[]){if(!Number.isSafeInteger(i.seq)||i.seq<0)throw new TypeError("store delivery seq must be a non-negative safe integer");let l=L(i.op,i.args),f=c[l],p=g({op:i.op,args:i.args,state:i.state,data:i.data,next:i.next,seq:i.seq,validator:i.validator,receipt:i.receipt,rows:i.rows,reason:i.reason,rule:i.rule});if(f&&i.seq<f.seq)continue;if(f&&i.seq===f.seq){if(f.fingerprint===p)continue;throw new TypeError(`conflicting deliveries for ${i.op} at seq ${i.seq}`)}for(let m of i.rows??[]){if(!m.table||!m.id||!m.row||typeof m.row!="object")throw new TypeError("store delivery row is malformed");let E=b(m);if((e.negative[E]??-1)>=i.seq){ce(s,{table:m.table,id:m.id,seq:e.negative[E]});continue}if(e.rowSeq[E]===i.seq&&g(e.rows[E])!==g(m))throw new TypeError(`conflicting row ${m.table}/${m.id} at seq ${i.seq}`);(e.rowSeq[E]??-1)>i.seq||(e.rows[E]=y(m),e.rowSeq[E]=i.seq)}let S=y(i.data),x=i.state,A=!1,H=i.state==="denied"&&f?f.refs:f?xe(f.data,S):[],D=new Set;for(let m of H)(r.retention?.[m.table]??"until-revoked")==="delivered-history"?(D.add(b(m)),A=!0):ce(s,{...m,seq:i.seq});i.state==="denied"&&f&&D.size>0?(S=y(f.data),x=f.state):f&&D.size>0&&(S=Pe(f.data,S,D));let F=I(S);for(let m of i.receipt?.ranges??[])for(let E of[...m.rowIds,...m.haloId?[m.haloId]:[]])F.some(J=>J.table===m.table&&J.id===E)||F.push({table:m.table,id:E});for(let m of i.rows??[])F.some(E=>E.table===m.table&&E.id===m.id)||F.push({table:m.table,id:m.id});let le={op:i.op,...i.cursorArg===void 0?{}:{cursorArg:i.cursorArg},args:y(i.args),state:x,...S===void 0?{}:{data:S},...i.next===void 0?{}:{next:i.next},nextSeq:i.seq,seq:i.seq,...i.validator===void 0?{}:{validator:i.validator},...i.receipt===void 0?{}:{receipt:y(i.receipt)},deliveredAt:i.deliveredAt??Date.now(),...A?{retained:!0}:{},...i.reason===void 0?{}:{reason:i.reason},...i.rule===void 0?{}:{rule:i.rule},refs:F,order:++e.clock,fingerprint:p};e.answers[l]=le,u.push(...(i.receipt?.ranges??[]).map(m=>({range:m,seq:i.seq}))),a(le)}let h=Object.values(c).flatMap(i=>i.receipt?.ranges??[]);for(let i of h){let l=u.filter(({range:p})=>Lt(i,p));if(l.length===0)continue;let f=new Set(l.flatMap(({range:p})=>[...p.rowIds,...p.haloId?[p.haloId]:[]]));for(let p of i.rowIds){if(f.has(p))continue;let S=l.filter(({range:x})=>Nt(p,i,x,e.rows,r.schema)).reduce((x,A)=>Math.max(x,A.seq),-1);S>=0&&ce(s,{table:i.table,id:p,seq:S})}}let w=new Set;for(let[i,l]of s)if(!((e.negative[i]??-1)>l.seq)&&!((e.rowSeq[i]??-1)>l.seq)){e.negative[i]=l.seq,w.add(i),delete e.rows[i],delete e.rowSeq[i];for(let f of Object.values(e.answers)){for(let p of f.receipt?.ranges??[]){if(p.table!==l.table)continue;let S=l.id;p.rowIds.includes(S)&&(p.rowIds=p.rowIds.filter(x=>x!==S),p.evictedIds=[...p.evictedIds??[],S]),p.haloId===S&&delete p.haloId}f.refs.some(p=>b(p)===i)&&(f.refs=f.refs.filter(p=>b(p)!==i),!f.retained&&f.state!=="denied"&&(f.state="partial",f.reason="evicted",delete f.next))}}for(let i of Object.values(e.answers))i.receipt&&(i.receipt.absences=i.receipt.absences.filter(([l,f,p])=>{let S=f==="byId"&&p.length===1?p[0]:void 0,x=typeof S=="string"?b({table:l,id:S}):void 0;return x===void 0||(e.rowSeq[x]??-1)<0||w.has(x)}));let v=new Set(Object.values(e.answers).flatMap(i=>i.refs.map(b)));for(let i of Object.keys(e.rows))v.has(i)||(delete e.rows[i],delete e.rowSeq[i]);let k=Le(e,r,Be(Object.values(e.rows),Object.values(e.answers)));n.put.push(...k.put),n.remove.push(...k.remove);let P=nt(Object.values(e.watermarks));if(r.watermark){let i=L(r.watermark.op,r.watermark.args),l=e.watermarks[i]?.order??++e.clock;e.watermarks[i]={...y(r.watermark),order:l}}let O=nt(Object.values(e.watermarks));for(let i of r.outbox?.remove??[])delete e.outbox[i],delete e.overlayMisses[i],delete e.overlay[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i];let q=new Set;for(let i of r.outbox?.put??[]){if(typeof i.intent!="string"||i.intent.length===0)throw new TypeError("outbox entry needs an intent");let l=e.outbox[i.intent];i.state==="committed"&&l?.state!=="committed"&&q.add(i.intent);let f=l?.order??i.order??++e.clock;e.outbox[i.intent]={...y(i),order:f}}r.bounds&&(e.bounds=y(r.bounds)),r.programs&&(e.programs=y(r.programs));for(let i of r.grants?.remove??[])delete e.grants[i],e.currentGrant===i&&delete e.currentGrant;for(let i of r.grants?.put??[])e.grants[i.id]=y(i);if(r.grants?.current===null)delete e.currentGrant;else if(typeof r.grants?.current=="string"){if(!e.grants[r.grants.current])throw new TypeError("current grant must be a held grant");e.currentGrant=r.grants.current}for(let i of r.overlay?.remove??[])delete e.overlay[i],delete e.overlayMisses[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i];for(let[i,l]of Object.entries(r.overlay?.put??{}))e.overlay[i]=y(l),delete e.overlayMisses[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i];let N=Object.keys(e.overlay).some(i=>e.outbox[i]?.state==="committed")?Object.values(e.answers):[],M=N.filter(i=>{let l=Array.isArray(i.data)?i.data.length>0:I(i.data).length>0;return(i.state==="complete"||i.state==="capped")&&i.receipt!==void 0&&!i.receipt.ranges.some(f=>(f.evictedIds?.length??0)>0)&&l&&i.refs.length>0&&i.refs.every(f=>e.rows[b(f)]!==void 0)}),_=new Map;for(let i of Object.keys(e.overlay)){let l=e.outbox[i];if(l===void 0||l.state==="rejected"||l.state==="expired"){delete e.overlay[i],delete e.overlayMisses[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i],l&&delete l.predicted;continue}for(let R of e.overlay[i]){let j=b(R),ye=o.get(j);ye!==void 0&&ye.seq>=(e.overlayNegatives[i]?.[j]?.seq??-1)&&((e.overlayNegatives[i]??={})[j]=ye)}if(l.state!=="committed")continue;if(e.overlayWatermarks[i]==null&&O!==void 0){let R=q.has(i)&&e.overlayWatermarks[i]!==null?P??O:O;e.overlayWatermarks[i]={op:R.op,args:y(R.args),cursorArg:R.cursorArg??"c",watermark:R.watermark}}let f=l.response?.seq;if(!Number.isSafeInteger(f)){delete e.overlay[i],delete e.overlayMisses[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i],delete l.predicted;continue}let p=e.overlay[i],S=Number(f),x=new Set(p.filter(R=>!R.removed).map(b)),A=e.overlayWatermarks[i]??void 0,H=A?g({op:A.op,args:A.args,cursorArg:A.cursorArg,watermark:A.watermark,seq:S}):"";if(!_.has(H)){let R=A?Je(M,A,S):[];_.set(H,{chain:R,reached:new Set(R)})}let{chain:D,reached:F}=_.get(H),m=p.some(R=>{let j=e.overlayNegatives[i]?.[b(R)];return j!==void 0&&j.seq>=S&&(e.rowSeq[b(R)]??-1)<=j.seq})||N.some(R=>R.seq>=S&&!R.retained&&R.state!=="denied"&&R.refs.some(j=>x.has(b(j))&&e.rows[b(j)]!==void 0))||x.size>0&&A!==void 0&&M.some(R=>R.seq>=S&&F.has(oe(R,A))),J=A!==void 0&&r.watermark!==void 0&&O!==void 0&&L(O.op,O.args)===L(r.watermark.op,r.watermark.args)&&Qe(A,O,D)||r.resumeSeq!==void 0&&r.resumeSeq>S,fe=m?0:(e.overlayMisses[i]??0)+(J&&!q.has(i)?1:0);if(!m&&fe<Ft){fe>0&&(e.overlayMisses[i]=fe);continue}delete e.overlay[i],delete e.overlayMisses[i],delete e.overlayWatermarks[i],delete e.overlayNegatives[i],delete l.predicted}return e}ensureOpen(){if(this.partition===void 0)throw new T("E_OFFLINE_CARRIER",`${this.backend.carrier} range store is not open`)}ensureUnsealed(){if(this.state.sealed)throw new T("E_OFFLINE_CARRIER","range store is sealed")}enqueue(e){let r=this.tail.then(e);return this.tail=r.then(()=>{},()=>{}),r}async read(e){return await this.tail,this.ensureOpen(),e()}};var _t=Symbol.for("snapback2.offline.storeLocks"),Ee=globalThis[_t]??=new Map;function ue(t="default"){return{async request(e,r,n){let s=Ee.get(t)??new Map;if(s.has(e))return n(null);Ee.set(t,s);let o={name:e};s.set(e,o);try{return await n(o)}finally{s.get(e)===o&&s.delete(e),s.size===0&&Ee.delete(t)}}}}async function Oe(t,e){let r,n,s,o=new Promise((c,u)=>{r=c,n=u}),d=new Promise(c=>{s=c}),a=t.request(`snapback2:${e}`,{ifAvailable:!0},async c=>{if(!c){n(new T("E_OFFLINE_LOCK","this account is open in another client; close it and try again"));return}r(),await d});return a.catch(n),await o,async()=>{s(),await a}}function st(t){let{rows:e={},answers:r={},outbox:n={},...s}=G(t),o=d=>Object.entries(d).map(([a,c])=>({key:a,value:c}));return{rows:o(e),answers:o(r),outbox:o(n),meta:[{key:"state",value:s}]}}function it(t){let e=t.meta.find(({key:n})=>n==="state");if(!e&&!t.rows.length&&!t.answers.length&&!t.outbox.length)return;let r=n=>Object.fromEntries(n.map(({key:s,value:o})=>[s,o]));return{...V(),...e?.value,rows:r(t.rows),answers:r(t.answers),outbox:r(t.outbox)}}function X(t,e){let r=e?t.indexOf("\0"):-1;return JSON.stringify(r<0?[t]:[t.slice(0,r),t.slice(r+1)])}function ot(t){let e=JSON.parse(t);if(!Array.isArray(e)||e.length<1||e.length>2||e.some(r=>typeof r!="string"))throw new Error("invalid sqlite state key");return e.join("\0")}var ct=["state","rows","answers","outbox"],at=[...ct,"bytes"],Dt=`
3
+ CREATE TABLE IF NOT EXISTS state (partition TEXT NOT NULL PRIMARY KEY, value TEXT NOT NULL);
4
+ CREATE TABLE IF NOT EXISTS rows (partition TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (partition, key));
5
+ CREATE TABLE IF NOT EXISTS answers (partition TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (partition, key));
6
+ CREATE TABLE IF NOT EXISTS outbox (partition TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (partition, key));
7
+ CREATE TABLE IF NOT EXISTS bytes (partition TEXT NOT NULL, key TEXT NOT NULL, value BLOB NOT NULL, PRIMARY KEY (partition, key));
8
+ `,Y=class{constructor(e,r={}){this.source=e;this.options=r}carrier="SQLite";driver;partition;release;locks;async guard(e){try{return await e()}catch(r){if(r instanceof T)throw r;let n=r instanceof Error?r.message:String(r),o=/SQLITE_BUSY|SQLITE_LOCKED|database (?:is )?(?:busy|locked)/i.test(n)?"another writer holds the store; close it or wait":"open a writable database with expo-sqlite ^16";throw new T("E_OFFLINE_CARRIER",`SQLite carrier: ${n}; ${o}`,o)}}async open(e){return await this.close(),this.guard(async()=>{let r=typeof this.source=="function"?await this.source():this.source;try{let n=r.identity;if(typeof n!="string"||!n.trim())throw new T("E_OFFLINE_CARRIER","driver has no database identity","supply a stable database identity on the SQLite driver");this.locks=this.options.locks??ue(n),this.release=await Oe(this.locks,e)}catch(n){throw typeof this.source=="function"&&await r.close().catch(()=>{}),n}try{this.driver=r,this.partition=e;let n=await this.driver.get("PRAGMA journal_mode=WAL");if(n?.journal_mode.toLowerCase()!=="wal")throw new Error(`journal_mode is ${n?.journal_mode??"missing"}, expected wal`);return await this.driver.exec(Dt),await this.driver.transaction(async s=>{let o=await s.get("SELECT value FROM state WHERE partition = ?",[e]),d=async a=>(await s.all(`SELECT key, value FROM ${a} WHERE partition = ?`,[e])).map(({key:c,value:u})=>({key:ot(c),value:JSON.parse(u)}));return it({rows:await d("rows"),answers:await d("answers"),outbox:await d("outbox"),meta:o?[{key:"state",value:JSON.parse(o.value)}]:[]})})}catch(n){throw await this.close().catch(()=>{}),n}})}async replace(e,r){return this.guard(async()=>{if(!this.driver||this.partition===void 0)throw new Error("not open");let n=this.partition,s=st(e);await this.driver.transaction(async o=>{for(let d of ct)await o.run(`DELETE FROM ${d} WHERE partition = ?`,[n]);await o.run("INSERT INTO state (partition, value) VALUES (?, ?)",[n,JSON.stringify(s.meta[0].value)]);for(let d of["rows","answers","outbox"])for(let{key:a,value:c}of s[d])await o.run(`INSERT INTO ${d} (partition, key, value) VALUES (?, ?, ?)`,[n,X(a,d!=="outbox"),JSON.stringify(c)]);for(let d of r.put)await o.run("INSERT OR REPLACE INTO bytes (partition, key, value) VALUES (?, ?, ?)",[n,X(d.key,!1),d.bytes]);for(let d of r.remove)await o.run("DELETE FROM bytes WHERE partition = ? AND key = ?",[n,X(d,!1)]);await this.options.beforeCommit?.(e,r)})})}async readBytes(e){return this.guard(async()=>!this.driver||this.partition===void 0?void 0:(await this.driver.get("SELECT value FROM bytes WHERE partition = ? AND key = ?",[this.partition,X(e,!1)]))?.value)}async wipe(e){if(!this.driver)return await this.open(e),this.wipe(e);if(this.partition!==e){let r=await Oe(this.locks,e);try{await this.guard(()=>this.driver.transaction(async n=>{for(let s of at)await n.run(`DELETE FROM ${s} WHERE partition = ?`,[e])}))}finally{await r()}return}await this.guard(async()=>{await this.driver.transaction(async r=>{for(let n of at)await r.run(`DELETE FROM ${n} WHERE partition = ?`,[e])})}),await this.close()}async close(){let e=this.driver,r=this.release;this.driver=void 0,this.partition=void 0,this.release=void 0;try{e&&await this.guard(()=>e.close())}finally{await r?.()}}};function $t(t,e={}){let r=(()=>new K(new Y(t,e)));return Object.defineProperty(r,"carrier",{value:"SQLite"}),r}import{Directory as Te,File as qe,Paths as Ut}from"expo-file-system";function dt(){return{root:new Te(Ut.cache,"snapback2").uri,async exists(t){return new qe(t).exists},async list(t){let e=new Te(t);return e.exists?e.list().map(r=>r.name):[]},async write(t,e){let r=new qe(t);r.parentDirectory.create({idempotent:!0,intermediates:!0}),r.write(e)},async remove(t){let e=t.endsWith("/")?new Te(t):new qe(t);e.exists&&e.delete()}}}function Kt(t="snapback2.db",e={}){let r=(()=>{let n=new te(dt()),s=new K(_e(new Y(()=>Se(t),e),n));return Fe(s,n),s});return Object.defineProperty(r,"carrier",{value:"SQLite"}),r}function Ur(t){return Vt({...t,fetch:t.fetch??Wt,offline:{...t.offline,store:t.offline?.store??Kt()},...t.as!==void 0?{}:{tokenStore:t.tokenStore??Re()}})}export{ve as AssetImage,be as AssetVideo,Ur as createExpoClient,Re as expoSecureTokenStore,Se as expoSqliteDriver,Kt as expoSqliteStore,ue as inProcessLocks,$t as sqliteRangeStore,we as useAssetFile};
9
+ //# sourceMappingURL=expo.mjs.map