syncorejs 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +330 -46
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/dev-session.mjs.map +1 -1
  8. package/dist/_vendor/cli/doctor.mjs +513 -46
  9. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  10. package/dist/_vendor/cli/errors.mjs.map +1 -1
  11. package/dist/_vendor/cli/help.mjs.map +1 -1
  12. package/dist/_vendor/cli/index.mjs +9 -2
  13. package/dist/_vendor/cli/index.mjs.map +1 -1
  14. package/dist/_vendor/cli/messages.mjs +5 -4
  15. package/dist/_vendor/cli/messages.mjs.map +1 -1
  16. package/dist/_vendor/cli/preflight.mjs.map +1 -1
  17. package/dist/_vendor/cli/project.mjs +125 -27
  18. package/dist/_vendor/cli/project.mjs.map +1 -1
  19. package/dist/_vendor/cli/render.mjs +57 -9
  20. package/dist/_vendor/cli/render.mjs.map +1 -1
  21. package/dist/_vendor/cli/targets.mjs +4 -3
  22. package/dist/_vendor/cli/targets.mjs.map +1 -1
  23. package/dist/_vendor/core/cli.d.mts +20 -4
  24. package/dist/_vendor/core/cli.d.mts.map +1 -1
  25. package/dist/_vendor/core/cli.mjs +458 -133
  26. package/dist/_vendor/core/cli.mjs.map +1 -1
  27. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  28. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  29. package/dist/_vendor/core/index.d.mts +5 -3
  30. package/dist/_vendor/core/index.mjs +22 -2
  31. package/dist/_vendor/core/index.mjs.map +1 -1
  32. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  33. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  34. package/dist/_vendor/core/runtime/components.mjs +186 -0
  35. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  36. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +178 -60
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.d.mts +398 -16
  41. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  42. package/dist/_vendor/core/runtime/functions.mjs +74 -3
  43. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  44. package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
  45. package/dist/_vendor/core/runtime/id.mjs.map +1 -1
  46. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +83 -0
  47. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +720 -0
  49. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +234 -0
  51. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +255 -0
  53. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +200 -0
  55. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +252 -0
  57. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +145 -0
  59. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +221 -0
  61. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  62. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  63. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  64. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  65. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  66. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +41 -0
  67. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  68. package/dist/_vendor/core/runtime/runtime.d.mts +1187 -202
  69. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  70. package/dist/_vendor/core/runtime/runtime.mjs +73 -1365
  71. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  72. package/dist/_vendor/core/transport.d.mts +113 -0
  73. package/dist/_vendor/core/transport.d.mts.map +1 -0
  74. package/dist/_vendor/core/transport.mjs +428 -0
  75. package/dist/_vendor/core/transport.mjs.map +1 -0
  76. package/dist/_vendor/devtools-protocol/index.d.ts +187 -4
  77. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  78. package/dist/_vendor/devtools-protocol/index.js +25 -9
  79. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  80. package/dist/_vendor/next/config.d.ts +3 -4
  81. package/dist/_vendor/next/config.d.ts.map +1 -1
  82. package/dist/_vendor/next/config.js +37 -19
  83. package/dist/_vendor/next/config.js.map +1 -1
  84. package/dist/_vendor/next/index.d.ts +109 -29
  85. package/dist/_vendor/next/index.d.ts.map +1 -1
  86. package/dist/_vendor/next/index.js +104 -26
  87. package/dist/_vendor/next/index.js.map +1 -1
  88. package/dist/_vendor/platform-expo/index.d.ts +156 -37
  89. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  90. package/dist/_vendor/platform-expo/index.js +80 -12
  91. package/dist/_vendor/platform-expo/index.js.map +1 -1
  92. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  93. package/dist/_vendor/platform-expo/react.js +11 -10
  94. package/dist/_vendor/platform-expo/react.js.map +1 -1
  95. package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
  96. package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
  97. package/dist/_vendor/platform-node/index.d.mts +192 -24
  98. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  99. package/dist/_vendor/platform-node/index.mjs +236 -97
  100. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  101. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  102. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  103. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  104. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  105. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  106. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  107. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  108. package/dist/_vendor/platform-web/external-change.d.ts +43 -1
  109. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  110. package/dist/_vendor/platform-web/external-change.js +32 -1
  111. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  112. package/dist/_vendor/platform-web/index.d.ts +323 -51
  113. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  114. package/dist/_vendor/platform-web/index.js +233 -30
  115. package/dist/_vendor/platform-web/index.js.map +1 -1
  116. package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
  117. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
  118. package/dist/_vendor/platform-web/indexeddb.js +10 -0
  119. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  120. package/dist/_vendor/platform-web/opfs.d.ts +13 -0
  121. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  122. package/dist/_vendor/platform-web/opfs.js +12 -0
  123. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  124. package/dist/_vendor/platform-web/persistence.d.ts +54 -0
  125. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  126. package/dist/_vendor/platform-web/persistence.js +15 -0
  127. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  128. package/dist/_vendor/platform-web/react.d.ts +1 -2
  129. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  130. package/dist/_vendor/platform-web/react.js +27 -13
  131. package/dist/_vendor/platform-web/react.js.map +1 -1
  132. package/dist/_vendor/platform-web/sqljs.js +10 -1
  133. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  134. package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
  135. package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
  136. package/dist/_vendor/platform-web/worker.d.ts +71 -44
  137. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  138. package/dist/_vendor/platform-web/worker.js +40 -271
  139. package/dist/_vendor/platform-web/worker.js.map +1 -1
  140. package/dist/_vendor/react/index.d.ts +222 -23
  141. package/dist/_vendor/react/index.d.ts.map +1 -1
  142. package/dist/_vendor/react/index.js +476 -63
  143. package/dist/_vendor/react/index.js.map +1 -1
  144. package/dist/_vendor/schema/definition.d.ts +151 -37
  145. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  146. package/dist/_vendor/schema/definition.js +102 -20
  147. package/dist/_vendor/schema/definition.js.map +1 -1
  148. package/dist/_vendor/schema/index.d.ts +4 -4
  149. package/dist/_vendor/schema/index.js +2 -2
  150. package/dist/_vendor/schema/planner.d.ts +19 -2
  151. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  152. package/dist/_vendor/schema/planner.js +79 -3
  153. package/dist/_vendor/schema/planner.js.map +1 -1
  154. package/dist/_vendor/schema/validators.d.ts +279 -83
  155. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  156. package/dist/_vendor/schema/validators.js +330 -38
  157. package/dist/_vendor/schema/validators.js.map +1 -1
  158. package/dist/_vendor/svelte/index.d.ts +245 -19
  159. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  160. package/dist/_vendor/svelte/index.js +443 -20
  161. package/dist/_vendor/svelte/index.js.map +1 -1
  162. package/dist/browser.d.ts.map +1 -1
  163. package/dist/cli.js +3 -1
  164. package/dist/cli.js.map +1 -1
  165. package/dist/components.d.ts +2 -0
  166. package/dist/components.js +2 -0
  167. package/dist/index.d.ts +3 -2
  168. package/dist/index.js +2 -1
  169. package/package.json +29 -21
@@ -1,27 +1,62 @@
1
- import { FunctionArgsFromDefinition, FunctionKindFromDefinition, FunctionReference, FunctionResultFromDefinition, MisfirePolicy, RecurringJobDefinition, RecurringSchedule, SyncoreFunctionKind } from "./functions.mjs";
2
- import { InferDocument, InferTableInput, SyncoreSchema, Validator } from "../../schema/index.d.ts";
3
- import { SyncoreActiveQueryInfo, SyncoreDevtoolsEvent, SyncoreDevtoolsEventOrigin, SyncoreRuntimeSummary } from "../../devtools-protocol/index.d.ts";
1
+ import { FunctionArgs, FunctionArgsFromDefinition, FunctionKindFromDefinition, FunctionReference, FunctionResultFromDefinition, MisfirePolicy, RecurringJobDefinition, RecurringSchedule, SyncoreFunctionKind } from "./functions.mjs";
2
+ import { ResolvedSyncoreComponent, SyncoreComponentFunctionMetadata } from "./components.mjs";
3
+ import { AnyTableDefinition, InferDocument, InferTableInput, SyncoreSchemaDefinition, TableIndexFields, TableIndexNames, TableSearchIndexConfig, TableSearchIndexNames, Validator } from "../../schema/index.d.ts";
4
+ import { DocumentChangePreview, SyncoreActiveQueryInfo, SyncoreDevtoolsEvent, SyncoreDevtoolsEventOrigin, SyncoreRuntimeSummary } from "../../devtools-protocol/index.d.ts";
4
5
 
5
6
  //#region src/runtime/runtime.d.ts
7
+ /**
8
+ * A registered Syncore function ready for execution by the runtime.
9
+ *
10
+ * This is the shape stored in the function registry after Syncore processes a
11
+ * definition exported from `syncore/functions/`. You rarely interact with this
12
+ * directly — prefer the generated `api` object and the high-level function
13
+ * builders ({@link query}, {@link mutation}, {@link action}).
14
+ */
6
15
  interface RegisteredSyncoreFunction {
7
16
  kind: SyncoreFunctionKind;
8
- argsValidator: Validator<unknown>;
9
- returnsValidator?: Validator<unknown>;
17
+ argsValidator: Validator<unknown, unknown, string>;
18
+ returnsValidator?: Validator<unknown, unknown, string>;
10
19
  handler: RegisteredSyncoreHandler;
20
+ __syncoreComponent?: SyncoreComponentFunctionMetadata;
11
21
  }
22
+ /**
23
+ * A map from function path strings (e.g. `"tasks/create"`) to their registered
24
+ * definitions.
25
+ *
26
+ * This is the type of the second argument to {@link SyncoreRuntimeOptions} and
27
+ * is produced by `npx syncorejs codegen` in `syncore/_generated/functions.ts`.
28
+ * You should not need to implement this interface manually.
29
+ */
12
30
  interface SyncoreFunctionRegistry {
13
31
  readonly [name: string]: RegisteredSyncoreFunction | undefined;
14
32
  }
33
+ /** @internal Bivariant function handler type used to avoid TypeScript strictness issues with contravariant function parameters. */
15
34
  type RegisteredSyncoreHandler = {
16
35
  bivarianceHack(ctx: unknown, args: unknown): unknown;
17
36
  }["bivarianceHack"];
37
+ /** A plain JSON-serialisable object. Used for function arguments and scheduler payloads. */
18
38
  type JsonObject = Record<string, unknown>;
39
+ /** A SQL-style comparison operator used when building index range queries. */
19
40
  type ComparisonOperator = "=" | ">" | ">=" | "<" | "<=";
41
+ /**
42
+ * A single field comparison used inside a query filter or index range.
43
+ *
44
+ * You typically get `QueryCondition` values from the builder callbacks passed
45
+ * to {@link IndexRangeBuilder} or {@link FilterBuilder} — you do not construct
46
+ * them manually.
47
+ */
20
48
  type QueryCondition = {
21
49
  field: string;
22
50
  operator: ComparisonOperator;
23
51
  value: unknown;
24
52
  };
53
+ /**
54
+ * A composable predicate tree used by {@link FilterBuilder}.
55
+ *
56
+ * Leaf nodes are single {@link QueryCondition} values; branch nodes combine
57
+ * conditions with `and` / `or` semantics. The runtime evaluates these trees
58
+ * against documents during a query scan.
59
+ */
25
60
  type QueryExpression = {
26
61
  type: "condition";
27
62
  condition: QueryCondition;
@@ -32,82 +67,341 @@ type QueryExpression = {
32
67
  type: "or";
33
68
  expressions: QueryExpression[];
34
69
  };
70
+ /**
71
+ * The arguments Syncore passes to its SQLite FTS5 full-text search layer when
72
+ * a query uses `.withSearchIndex()`.
73
+ */
35
74
  type SearchQuery = {
36
75
  searchField: string;
37
76
  searchText: string;
38
77
  filters: QueryCondition[];
39
78
  };
40
- type DevtoolsEventMeta = {
41
- origin?: SyncoreDevtoolsEventOrigin;
42
- };
79
+ /**
80
+ * Result metadata returned by a single SQL `run` call.
81
+ *
82
+ * Wraps the driver's raw result so callers get consistent change-count and
83
+ * last-insert-rowid values regardless of the underlying SQLite binding.
84
+ */
43
85
  interface RunResult {
86
+ /** Number of rows affected by the statement. */
44
87
  changes: number;
88
+ /** The rowid of the last inserted row, if applicable. */
45
89
  lastInsertRowid?: number | string;
46
90
  }
91
+ /**
92
+ * Describes an optional runtime capability that functions can read from `ctx.capabilities`.
93
+ *
94
+ * Capabilities let platform adapters expose platform-specific services (e.g.
95
+ * push notifications, biometrics) to Syncore functions in a portable way. The
96
+ * runtime validates capabilities against their descriptors at start-up.
97
+ */
98
+ interface CapabilityDescriptor {
99
+ /** Unique capability name. Must match the key used in `SyncoreCapabilities`. */
100
+ name: string;
101
+ /** Semantic version number for the capability’s interface contract. */
102
+ version: number;
103
+ /** Optional feature flags exposed by this capability. */
104
+ features?: string[];
105
+ /** Arbitrary metadata for introspection or devtools display. */
106
+ metadata?: Record<string, unknown>;
107
+ /**
108
+ * When `true`, the runtime starts even if the capability is absent. Useful
109
+ * for progressively-enhanced features that degrade gracefully.
110
+ */
111
+ optional?: boolean;
112
+ }
113
+ /**
114
+ * Identifies a subset of the runtime state that a query depends on or that a
115
+ * mutation has changed.
116
+ *
117
+ * The reactivity engine uses impact scopes to efficiently determine which
118
+ * active queries need to be re-executed after a mutation commits. Scopes are
119
+ * additive: a query subscribed to `"table:tasks"` will be invalidated by any
120
+ * mutation that writes to the `tasks` table.
121
+ *
122
+ * - `"runtime.summary"` / `"runtime.activeQueries"` / `"schema.tables"` /
123
+ * `"scheduler.jobs"` — runtime-level state consumed by devtools.
124
+ * - `` `table:${string}` `` — every document in a specific table.
125
+ * - `` `row:${string}:${string}` `` — a single document (table + id).
126
+ * - `` `storage:${string}` `` — a specific storage object.
127
+ */
128
+ type ImpactScope = "runtime.summary" | "runtime.activeQueries" | "schema.tables" | "scheduler.jobs" | `table:${string}` | `row:${string}:${string}` | `storage:${string}`;
129
+ /** A frozen set of {@link ImpactScope} values. */
130
+ type ImpactSet = ReadonlySet<ImpactScope>;
131
+ interface ExecutionResult<TResult = unknown> {
132
+ result: TResult;
133
+ changedTables: Set<string>;
134
+ documentChanges: DocumentChangePreview[];
135
+ storageChanges: Array<{
136
+ storageId: string;
137
+ reason: Extract<SyncoreExternalChangeReason, "storage-put" | "storage-delete">;
138
+ }>;
139
+ scheduledJobs: string[];
140
+ devtoolsEvents: SyncoreDevtoolsEvent[];
141
+ externalChangeRequests: Array<{
142
+ scope: SyncoreExternalChangeScope;
143
+ reason: SyncoreExternalChangeReason;
144
+ changedScopes: ImpactScope[];
145
+ }>;
146
+ }
147
+ /**
148
+ * Low-level interface that Syncore uses to communicate with a SQLite database.
149
+ *
150
+ * The runtime ships concrete implementations for every supported environment
151
+ * (`NodeSqliteDriver`, `SqlJsDriver`, `ExpoSqliteDriver`). Implement this
152
+ * interface only if you need to integrate a custom SQLite binding.
153
+ *
154
+ * All methods must be safe to call concurrently — the runtime serialises
155
+ * concurrent writes through the driver’s own transaction mechanism.
156
+ */
47
157
  interface SyncoreSqlDriver {
158
+ /**
159
+ * Execute one or more SQL statements that produce no result rows (e.g.
160
+ * `CREATE TABLE`, `PRAGMA journal_mode = WAL`).
161
+ */
48
162
  exec(sql: string): Promise<void>;
163
+ /**
164
+ * Execute a single parameterised statement and return change metadata.
165
+ * Typically used for `INSERT`, `UPDATE`, and `DELETE`.
166
+ */
49
167
  run(sql: string, params?: unknown[]): Promise<RunResult>;
168
+ /**
169
+ * Execute a query and return the first row, or `undefined` if there are no
170
+ * results. Useful for `SELECT … LIMIT 1` lookups.
171
+ */
50
172
  get<T>(sql: string, params?: unknown[]): Promise<T | undefined>;
173
+ /**
174
+ * Execute a query and return all matching rows as an array. Returns an empty
175
+ * array when there are no results.
176
+ */
51
177
  all<T>(sql: string, params?: unknown[]): Promise<T[]>;
178
+ /**
179
+ * Run `callback` inside an atomic SQLite transaction.
180
+ *
181
+ * If the callback throws, the transaction is rolled back automatically.
182
+ * Implementations should support nested calls by using `SAVEPOINT`.
183
+ */
52
184
  withTransaction<T>(callback: () => Promise<T>): Promise<T>;
185
+ /**
186
+ * Run `callback` inside a named SQLite savepoint, allowing partial rollback
187
+ * within an outer transaction.
188
+ */
53
189
  withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T>;
190
+ /**
191
+ * Release the underlying database handle. Called by the runtime during
192
+ * shutdown. Implementations that hold no persistent resources may omit this.
193
+ */
54
194
  close?(): Promise<void>;
55
195
  }
196
+ /**
197
+ * Identifies which category of local state an external change event affects.
198
+ *
199
+ * - `"database"` — One or more SQLite tables were written by an external
200
+ * source (e.g. another Syncore instance sharing the same database file).
201
+ * - `"storage"` — One or more blob/file storage objects were added or removed.
202
+ * - `"all"` — Both the database and storage should be treated as changed.
203
+ */
56
204
  type SyncoreExternalChangeScope = "database" | "storage" | "all";
205
+ /**
206
+ * Why an external change event was emitted.
207
+ *
208
+ * - `"commit"` — A write transaction was committed by another runtime instance.
209
+ * - `"storage-put"` — A new storage object was written.
210
+ * - `"storage-delete"` — A storage object was removed.
211
+ * - `"reconcile"` — The runtime is re-synchronising with the underlying store
212
+ * after a reconnect or restart.
213
+ */
57
214
  type SyncoreExternalChangeReason = "commit" | "storage-put" | "storage-delete" | "reconcile";
215
+ /**
216
+ * A message that notifies a Syncore runtime that state has changed in a source
217
+ * it does not own — for example, a write made by a shared Node process that
218
+ * the browser tab needs to reflect.
219
+ *
220
+ * The runtime subscribes to these events through a
221
+ * {@link SyncoreExternalChangeSignal} and uses them to invalidate and refresh
222
+ * affected queries without polling.
223
+ */
58
224
  interface SyncoreExternalChangeEvent {
225
+ /** Identifies the runtime instance that published this event. */
59
226
  sourceId: string;
227
+ /** Which category of state changed. */
60
228
  scope: SyncoreExternalChangeScope;
229
+ /** Why the change occurred. */
61
230
  reason: SyncoreExternalChangeReason;
231
+ /** Unix timestamp (milliseconds) when the change was published. */
62
232
  timestamp: number;
233
+ /** Optional opaque string used to detect duplicate or out-of-order events. */
63
234
  revision?: string;
235
+ /** Specific impact scopes that were affected (subset of `scope`). */
236
+ changedScopes?: ImpactScope[];
237
+ /** Table names that were written to, when `scope` includes `"database"`. */
64
238
  changedTables?: string[];
239
+ /** Storage object IDs that were affected, when `scope` includes `"storage"`. */
65
240
  storageIds?: string[];
66
241
  }
242
+ /**
243
+ * A pub/sub channel for cross-instance change notifications.
244
+ *
245
+ * Provide an implementation to `SyncoreRuntimeOptions.externalChangeSignal`
246
+ * when multiple Syncore runtimes share the same underlying database (e.g. an
247
+ * Electron main process and renderer, or multiple browser tabs). The runtime
248
+ * will publish events after its own commits and react to events published by
249
+ * other instances.
250
+ *
251
+ * Platform adapters ship ready-made implementations:
252
+ * - `BroadcastChannelExternalChangeSignal` (browser, shared workers)
253
+ * - Node IPC signal (Electron main ↔ renderer)
254
+ */
67
255
  interface SyncoreExternalChangeSignal {
256
+ /**
257
+ * Register a listener for incoming change events.
258
+ * @returns A cleanup function that removes the listener when called.
259
+ */
68
260
  subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void;
261
+ /** Publish an outgoing change event to other subscribers. */
69
262
  publish(event: SyncoreExternalChangeEvent): void | Promise<void>;
263
+ /** Optional cleanup called when the runtime shuts down. */
70
264
  close?(): void | Promise<void>;
71
265
  }
266
+ /**
267
+ * Applies an incoming {@link SyncoreExternalChangeEvent} to a local SQL
268
+ * driver, reconciling the local state with a remote source.
269
+ *
270
+ * Typically provided by the same adapter that supplies
271
+ * {@link SyncoreExternalChangeSignal} (e.g. `SqlJsExternalChangeApplier` for
272
+ * browser runtimes). Only necessary when the local driver needs to pull in
273
+ * changes made to the database file on disk by another process.
274
+ */
72
275
  interface SyncoreExternalChangeApplier {
73
276
  applyExternalChange(event: SyncoreExternalChangeEvent): Promise<{
74
277
  databaseChanged: boolean;
75
278
  storageChanged: boolean;
279
+ changedScopes: ImpactScope[];
76
280
  }>;
77
281
  }
78
282
  /**
79
- * The binary or text payload written through Syncore storage APIs.
283
+ * The payload used when writing a new object through Syncore storage APIs.
284
+ *
285
+ * Pass this to `ctx.storage.put()` inside a mutation or action to persist a
286
+ * binary blob alongside your database documents.
287
+ *
288
+ * ```ts
289
+ * const id = await ctx.storage.put({
290
+ * data: new Uint8Array(imageBytes),
291
+ * contentType: "image/png",
292
+ * fileName: "avatar.png",
293
+ * });
294
+ * // Store `id` in the database to reference the object later.
295
+ * ```
80
296
  */
81
297
  interface StorageWriteInput {
298
+ /**
299
+ * The raw data to store. Accepts `Uint8Array`, `ArrayBuffer`, or a UTF-8
300
+ * string (the string is encoded to bytes automatically).
301
+ */
82
302
  data: Uint8Array | ArrayBuffer | string;
303
+ /**
304
+ * MIME type hint stored alongside the object (e.g. `"image/png"`,
305
+ * `"application/pdf"`). Not validated or enforced by Syncore; purely
306
+ * informational for downstream consumers.
307
+ */
83
308
  contentType?: string;
309
+ /**
310
+ * Optional human-readable filename hint. Stored as metadata and surfaced in
311
+ * devtools but not used for addressing the object (the auto-generated `id` is
312
+ * used for that).
313
+ */
84
314
  fileName?: string;
85
315
  }
86
316
  /**
87
- * Metadata about an object stored through Syncore storage APIs.
317
+ * Metadata describing a stored object managed by the Syncore storage adapter.
318
+ *
319
+ * Returned by `ctx.storage.get()` and `SyncoreStorageAdapter.list()`. The
320
+ * `id` field is the opaque string you store in the database to reference this
321
+ * object; use it with `ctx.storage.read()` to fetch the bytes.
88
322
  */
89
323
  interface StorageObject {
324
+ /** Opaque identifier. Store this in a database document to reference the object. */
90
325
  id: string;
326
+ /** Absolute path or key used by the storage backend (filesystem path, OPFS key, etc.). */
91
327
  path: string;
328
+ /** Size of the stored data in bytes. */
92
329
  size: number;
330
+ /** MIME type provided at write time, or `null` if none was specified. */
93
331
  contentType: string | null;
94
332
  }
333
+ /**
334
+ * Low-level interface for persisting and retrieving binary blobs alongside the
335
+ * Syncore database.
336
+ *
337
+ * The runtime ships concrete implementations for every supported environment
338
+ * (`NodeFileStorageAdapter`, `BrowserFileStorageAdapter`,
339
+ * `ExpoFileStorageAdapter`). Implement this interface only if you need a custom
340
+ * storage backend (e.g. an in-memory store for tests or an S3-compatible
341
+ * remote).
342
+ */
95
343
  interface SyncoreStorageAdapter {
344
+ /**
345
+ * Write a blob and return its metadata.
346
+ *
347
+ * @param id - The opaque identifier Syncore assigns to this object. Use
348
+ * the same value to retrieve or delete the object later.
349
+ * @param input - The data and optional metadata to persist.
350
+ */
96
351
  put(id: string, input: StorageWriteInput): Promise<StorageObject>;
352
+ /**
353
+ * Return the metadata for a stored object, or `null` if it does not exist.
354
+ * Does **not** return the raw bytes — use {@link read} for that.
355
+ */
97
356
  get(id: string): Promise<StorageObject | null>;
357
+ /**
358
+ * Return the raw bytes of a stored object, or `null` if it does not exist.
359
+ */
98
360
  read(id: string): Promise<Uint8Array | null>;
361
+ /**
362
+ * Permanently remove a stored object. A no-op if the object does not exist.
363
+ */
99
364
  delete(id: string): Promise<void>;
365
+ /**
366
+ * Enumerate all stored objects. Used by devtools and migration tooling.
367
+ * Optional — omit for backends that don’t support listing.
368
+ */
100
369
  list?(): Promise<StorageObject[]>;
101
370
  }
371
+ /**
372
+ * Receives structured devtools events emitted by the runtime.
373
+ *
374
+ * In development the platform adapters automatically connect a WebSocket sink
375
+ * that forwards events to the Syncore devtools dashboard. You can also supply
376
+ * a custom sink for testing, logging, or building your own observability layer:
377
+ *
378
+ * ```ts
379
+ * const sink: DevtoolsSink = {
380
+ * emit(event) { console.log("[syncore]", event.type); },
381
+ * };
382
+ * ```
383
+ *
384
+ * Pass `devtools: false` to the runtime options to disable devtools entirely
385
+ * (recommended for production builds).
386
+ */
102
387
  interface DevtoolsSink {
388
+ /** Called synchronously every time the runtime emits a new event. */
103
389
  emit(event: SyncoreDevtoolsEvent): void;
104
- attachRuntime?(runtime: SyncoreRuntime<AnySyncoreSchema>): void;
390
+ /**
391
+ * Optional hook called once after the runtime is constructed so the sink can
392
+ * hold a reference to it (e.g. to call `runtime.getAdmin()`).
393
+ */
394
+ attachRuntime?(runtime: SyncoreRuntime<SyncoreDataModel>): void;
105
395
  }
106
396
  interface DevtoolsLiveQuerySnapshot {
107
397
  summary: SyncoreRuntimeSummary;
108
398
  activeQueries: SyncoreActiveQueryInfo[];
109
399
  schemaTables: Array<{
110
400
  name: string;
401
+ displayName?: string;
402
+ owner: "root" | "component";
403
+ componentPath?: string;
404
+ componentName?: string;
111
405
  fields: Array<{
112
406
  name: string;
113
407
  type: string;
@@ -121,332 +415,1023 @@ interface DevtoolsLiveQuerySnapshot {
121
415
  documentCount: number;
122
416
  }>;
123
417
  }
124
- type DevtoolsLiveQueryScope = "all" | "runtime.summary" | "runtime.activeQueries" | "schema.tables" | "scheduler.jobs" | `table:${string}`;
418
+ type DevtoolsLiveQueryScope = "all" | "runtime.summary" | "runtime.activeQueries" | "schema.tables" | "scheduler.jobs" | `table:${string}` | `storage:${string}`;
419
+ /**
420
+ * Configuration for the Syncore built-in scheduler.
421
+ *
422
+ * Pass this to `SyncoreRuntimeOptions.scheduler` to enable background job
423
+ * processing. The scheduler polls for pending one-off jobs (created via
424
+ * `ctx.scheduler.runAfter` / `ctx.scheduler.runAt`) and for recurring jobs
425
+ * defined with {@link CronJobs}.
426
+ *
427
+ * ```ts
428
+ * import crons from "./syncore/crons";
429
+ *
430
+ * createNodeSyncoreRuntime({
431
+ * ...,
432
+ * scheduler: {
433
+ * pollIntervalMs: 500,
434
+ * recurringJobs: crons.jobs,
435
+ * },
436
+ * });
437
+ * ```
438
+ */
125
439
  interface SchedulerOptions {
440
+ /**
441
+ * How often the scheduler checks for jobs that are due, in milliseconds.
442
+ * Defaults to `1000` (1 second). Lower values increase responsiveness at
443
+ * the cost of more frequent SQLite reads.
444
+ */
126
445
  pollIntervalMs?: number;
446
+ /**
447
+ * Static list of recurring job definitions to register when the runtime
448
+ * starts. Build this with the {@link CronJobs} helper and a call to
449
+ * {@link cronJobs}.
450
+ */
127
451
  recurringJobs?: RecurringJobDefinition[];
128
452
  }
453
+ type SyncoreResolvedComponents = readonly ResolvedSyncoreComponent[];
129
454
  interface UpdateScheduledJobOptions {
130
455
  id: string;
131
- schedule: RecurringSchedule;
456
+ schedule?: RecurringSchedule;
132
457
  args: JsonObject;
133
- misfirePolicy: MisfirePolicy;
458
+ misfirePolicy?: MisfirePolicy;
134
459
  runAt?: number;
135
460
  }
461
+ /**
462
+ * An open-ended bag of platform-specific capabilities exposed to Syncore
463
+ * function handlers via `ctx.capabilities`.
464
+ *
465
+ * Use capabilities to inject platform services (push notifications, camera
466
+ * access, native storage, etc.) that should be available inside your
467
+ * functions without hard-coding platform imports:
468
+ *
469
+ * ```ts
470
+ * // Runtime setup (platform-specific)
471
+ * createExpoSyncoreRuntime({
472
+ * ...,
473
+ * capabilities: { pushNotifications: Notifications },
474
+ * });
475
+ *
476
+ * // Inside a mutation
477
+ * export const notify = mutation({
478
+ * args: { message: s.string() },
479
+ * handler: async (ctx, { message }) => {
480
+ * await ctx.capabilities?.pushNotifications?.scheduleAsync({ body: message });
481
+ * },
482
+ * });
483
+ * ```
484
+ */
136
485
  interface SyncoreCapabilities {
137
486
  [name: string]: unknown;
138
487
  }
139
- interface SyncoreExperimentalPluginContext<TSchema extends AnySyncoreSchema> {
140
- runtimeId: string;
141
- platform: string;
142
- schema: TSchema;
143
- driver: SyncoreSqlDriver;
144
- storage: SyncoreStorageAdapter;
145
- scheduler?: SchedulerOptions;
146
- devtools?: DevtoolsSink;
147
- emitDevtools(event: SyncoreDevtoolsEvent): void;
148
- }
149
- interface SyncoreExperimentalPlugin<TSchema extends AnySyncoreSchema> {
150
- name: string;
151
- capabilities?: SyncoreCapabilities | ((context: SyncoreExperimentalPluginContext<TSchema>) => SyncoreCapabilities | void);
152
- onStart?(context: SyncoreExperimentalPluginContext<TSchema>): Promise<void> | void;
153
- onStop?(context: SyncoreExperimentalPluginContext<TSchema>): Promise<void> | void;
488
+ /**
489
+ * The typed data model that backs a Syncore runtime.
490
+ *
491
+ * `SyncoreDataModel` is the shape of the value returned by
492
+ * {@link defineSchema}. It is the type parameter you see on
493
+ * {@link SyncoreRuntime}, {@link QueryCtx}, {@link MutationCtx}, and the
494
+ * generated server context types. Application code typically gets this from
495
+ * the schema file rather than constructing it directly:
496
+ *
497
+ * ```ts
498
+ * import schema from "../syncore/schema";
499
+ * type MySchema = typeof schema;
500
+ * ```
501
+ */
502
+ interface SyncoreDataModel<TTables extends SyncoreSchemaDefinition = SyncoreSchemaDefinition> {
503
+ readonly tables: TTables;
504
+ getTable(tableName: Extract<keyof TTables, string>): TTables[Extract<keyof TTables, string>];
505
+ tableNames(): Array<Extract<keyof TTables, string>>;
154
506
  }
155
- interface SyncoreRuntimeOptions<TSchema extends AnySyncoreSchema> {
507
+ /**
508
+ * Low-level options for constructing a {@link SyncoreRuntime} directly.
509
+ *
510
+ * Most applications should use a platform-specific factory function instead
511
+ * (`createNodeSyncoreRuntime`, `createWebSyncoreRuntime`,
512
+ * `createExpoSyncoreRuntime`, etc.), which fill in sensible defaults for the
513
+ * driver, storage adapter, and devtools connection.
514
+ *
515
+ * Only reach for `SyncoreRuntimeOptions` when you need full control over the
516
+ * underlying SQLite driver or storage backend.
517
+ */
518
+ interface SyncoreRuntimeOptions<TSchema extends SyncoreDataModel> {
519
+ /** The data model that defines the available tables, indexes, and schemas. */
156
520
  schema: TSchema;
521
+ /**
522
+ * The registered functions Syncore can invoke. In practice this is always
523
+ * the generated `functions` export from `syncore/_generated/functions.ts`.
524
+ */
157
525
  functions: SyncoreFunctionRegistry;
526
+ /**
527
+ * Resolved Syncore component instances to mount alongside the root app
528
+ * functions. Only required when your app installs Syncore components.
529
+ */
530
+ components?: SyncoreResolvedComponents;
531
+ /**
532
+ * The SQLite driver Syncore will use for all database operations.
533
+ *
534
+ * Use one of the platform-specific drivers shipped by Syncore
535
+ * (`NodeSqliteDriver`, `SqlJsDriver`, `ExpoSqliteDriver`) or provide a
536
+ * custom implementation of {@link SyncoreSqlDriver}.
537
+ */
158
538
  driver: SyncoreSqlDriver;
539
+ /**
540
+ * The blob storage adapter used for `ctx.storage.put()` and related APIs.
541
+ *
542
+ * Use one of the platform-specific adapters
543
+ * (`NodeFileStorageAdapter`, `BrowserFileStorageAdapter`,
544
+ * `ExpoFileStorageAdapter`) or a custom implementation of
545
+ * {@link SyncoreStorageAdapter}.
546
+ */
159
547
  storage: SyncoreStorageAdapter;
548
+ /**
549
+ * A pub/sub channel that lets this runtime receive change notifications
550
+ * published by other Syncore instances sharing the same data source.
551
+ *
552
+ * Required when running Syncore across multiple contexts that share a
553
+ * database (e.g. Electron main + renderer, or multiple browser tabs).
554
+ * Platform adapters provide ready-made implementations.
555
+ */
160
556
  externalChangeSignal?: SyncoreExternalChangeSignal;
557
+ /**
558
+ * Applies incoming external change events to the local SQLite driver,
559
+ * reconciling the local state with changes written by another process.
560
+ *
561
+ * Usually paired with `externalChangeSignal`. Only required for drivers
562
+ * that hold an in-memory copy of the database (e.g. SQL.js in the browser).
563
+ */
161
564
  externalChangeApplier?: SyncoreExternalChangeApplier;
565
+ /**
566
+ * Platform-specific capabilities injected into `ctx.capabilities` inside
567
+ * every function handler. See {@link SyncoreCapabilities}.
568
+ */
162
569
  capabilities?: SyncoreCapabilities;
163
- experimentalPlugins?: Array<SyncoreExperimentalPlugin<TSchema>>;
570
+ /** Structured capability descriptors validated at start-up. */
571
+ capabilityDescriptors?: CapabilityDescriptor[];
572
+ /**
573
+ * Label reported to devtools to identify the runtime’s environment
574
+ * (e.g. `"node"`, `"browser"`, `"expo"`, `"electron-main"`).
575
+ */
164
576
  platform?: string;
577
+ /**
578
+ * Devtools event sink used during development.
579
+ *
580
+ * Pass `false` to disable devtools entirely (recommended for production).
581
+ * Omit to use the platform adapter’s default auto-connect behaviour.
582
+ */
165
583
  devtools?: DevtoolsSink;
584
+ /** Scheduler configuration for background and recurring jobs. */
166
585
  scheduler?: SchedulerOptions;
167
586
  }
587
+ /**
588
+ * Arguments for a paginated Syncore query.
589
+ *
590
+ * Add `paginationOpts: s.object({ cursor: s.nullable(s.string()), numItems: s.number() })`
591
+ * to your query’s `args` schema, then accept a `PaginationOptions` value in the
592
+ * handler to enable cursor-based pagination:
593
+ *
594
+ * ```ts
595
+ * export const listTasks = query({
596
+ * args: {
597
+ * paginationOpts: s.object({
598
+ * cursor: s.nullable(s.string()),
599
+ * numItems: s.number(),
600
+ * }),
601
+ * },
602
+ * handler: async (ctx, { paginationOpts }) =>
603
+ * ctx.db.query("tasks").paginate(paginationOpts),
604
+ * });
605
+ * ```
606
+ *
607
+ * In React, use {@link usePaginatedQuery} which manages the cursor for you.
608
+ */
168
609
  interface PaginationOptions {
610
+ /**
611
+ * The cursor returned by the previous page, or `null` / `undefined` for the
612
+ * first page.
613
+ */
169
614
  cursor?: string | null;
615
+ /** Maximum number of items to return in this page. */
170
616
  numItems: number;
171
617
  }
618
+ /**
619
+ * The value returned by `ctx.db.query(…).paginate()`.
620
+ *
621
+ * Store the `cursor` field and pass it back in the next call to fetch the
622
+ * following page. `isDone` is `true` when there are no more results.
623
+ */
172
624
  interface PaginationResult<TItem> {
173
- /** The current page of results. */
625
+ /** The items in this page. May be fewer than `numItems` if `isDone` is `true`. */
174
626
  page: TItem[];
175
- /** The cursor to pass to the next page request, or `null` when finished. */
627
+ /**
628
+ * Opaque cursor to pass as `PaginationOptions.cursor` in the next call.
629
+ * `null` when `isDone` is `true`.
630
+ */
176
631
  cursor: string | null;
177
- /** Whether there are no more pages to read. */
632
+ /** `true` when this is the last page and no more results exist. */
178
633
  isDone: boolean;
179
634
  }
635
+ /**
636
+ * Coarse lifecycle phase of a Syncore runtime.
637
+ *
638
+ * - `"starting"` — The runtime is initialising (applying schema, loading driver).
639
+ * - `"ready"` — The runtime is fully started and accepting function calls.
640
+ * - `"recovering"` — A transient error occurred; the runtime is attempting recovery.
641
+ * - `"unavailable"` — The runtime is unreachable (worker not started, IPC down, etc.).
642
+ * - `"error"` — The runtime encountered an unrecoverable error.
643
+ */
644
+ type SyncoreRuntimeStatusKind = "starting" | "ready" | "recovering" | "unavailable" | "error";
645
+ /**
646
+ * Explains why the runtime entered its current non-`"ready"` state.
647
+ *
648
+ * Useful for rendering descriptive loading or error messages in the UI.
649
+ */
650
+ type SyncoreRuntimeStatusReason = "booting" | "rehydrating" | "worker-restarting" | "worker-unavailable" | "ipc-unavailable" | "runtime-unavailable" | "disposed";
651
+ /**
652
+ * Snapshot of the runtime’s current lifecycle state.
653
+ *
654
+ * Subscribe to changes with `client.watchRuntimeStatus()` or the
655
+ * `useSyncoreStatus()` React hook to adapt the UI while the runtime is
656
+ * starting up or recovering.
657
+ *
658
+ * ```ts
659
+ * const status = useSyncoreStatus();
660
+ * if (status.kind !== "ready") return <LoadingSpinner />;
661
+ * ```
662
+ */
663
+ interface SyncoreRuntimeStatus {
664
+ /** Coarse lifecycle phase. */
665
+ kind: SyncoreRuntimeStatusKind;
666
+ /** Machine-readable reason for a non-`"ready"` state. */
667
+ reason?: SyncoreRuntimeStatusReason;
668
+ /** The underlying error when `kind` is `"error"`. */
669
+ error?: Error;
670
+ }
671
+ /**
672
+ * Lifecycle status of an individual reactive query subscription.
673
+ *
674
+ * - `"loading"` — The query has never produced a result (first load).
675
+ * - `"success"` — The query has data and no error.
676
+ * - `"error"` — The last execution threw an error. `data` may still hold a
677
+ * stale value from a prior successful run.
678
+ * - `"skipped"` — The subscription was suppressed with the `skip` sentinel.
679
+ */
680
+ type SyncoreQueryStatus = "loading" | "success" | "error" | "skipped";
681
+ /**
682
+ * The full reactive state of a Syncore query subscription.
683
+ *
684
+ * Returned by {@link useQueryState} and Svelte’s `createQueryStore`. For most
685
+ * components you only need the `data` field — use {@link useQuery} for that
686
+ * simpler shape.
687
+ */
688
+ interface SyncoreQueryState<TData> {
689
+ /** The most recent result from the query, or `undefined` while loading. */
690
+ data: TData | undefined;
691
+ /** The error thrown by the last execution, or `undefined` on success. */
692
+ error: Error | undefined;
693
+ /** Fine-grained subscription lifecycle status. */
694
+ status: SyncoreQueryStatus;
695
+ /** Current lifecycle status of the underlying runtime. */
696
+ runtimeStatus: SyncoreRuntimeStatus;
697
+ /** `true` while waiting for the first result. Equivalent to `status === "loading"`. */
698
+ isLoading: boolean;
699
+ /** `true` when the last execution threw an error. */
700
+ isError: boolean;
701
+ /** `true` when `data` is available and the runtime is ready. */
702
+ isReady: boolean;
703
+ }
704
+ type SyncoreQueryRequest<TReference extends FunctionReference<"query"> = FunctionReference<"query">> = (Record<never, never> extends FunctionArgs<TReference> ? {
705
+ query: TReference;
706
+ args?: FunctionArgs<TReference>;
707
+ } : {
708
+ query: TReference;
709
+ args: FunctionArgs<TReference>;
710
+ }) & {
711
+ skip?: boolean;
712
+ };
713
+ type SyncoreQueriesRequest = Record<string, SyncoreQueryRequest>;
714
+ /**
715
+ * Lifecycle status of a paginated Syncore query subscription.
716
+ *
717
+ * - `"loading"` — Waiting for the first page to arrive.
718
+ * - `"ready"` — At least one page has loaded and more pages are available.
719
+ * - `"loadingMore"` — A `loadMore()` call is in progress.
720
+ * - `"exhausted"` — All pages have been loaded (`isDone` is `true` on the
721
+ * last page). `loadMore()` is a no-op in this state.
722
+ * - `"error"` — The last page load failed. `error` contains the thrown error.
723
+ */
724
+ type SyncorePaginatedQueryStatus = "loading" | "ready" | "loadingMore" | "exhausted" | "error";
725
+ /**
726
+ * The result object returned by {@link usePaginatedQuery} and
727
+ * `createPaginatedQueryStore`.
728
+ *
729
+ * Contains the accumulated items, pagination metadata, and a `loadMore`
730
+ * callback for fetching the next page.
731
+ */
732
+ interface UsePaginatedQueryResult<TItem> {
733
+ /** All items loaded so far, across all fetched pages. */
734
+ results: TItem[];
735
+ /** Raw page results in order, one entry per fetched page. */
736
+ pages: PaginationResult<TItem>[];
737
+ /** Current lifecycle phase of the paginated query. */
738
+ status: SyncorePaginatedQueryStatus;
739
+ /** The error thrown by the last failed page load, or `undefined`. */
740
+ error: Error | undefined;
741
+ /** `true` while waiting for the first page. */
742
+ isLoading: boolean;
743
+ /** `true` while a `loadMore()` request is in progress. */
744
+ isLoadingMore: boolean;
745
+ /** `true` when there is a next page available to load. */
746
+ hasMore: boolean;
747
+ /** Cursor to pass to the next page request. `null` when `isDone` is `true`. */
748
+ cursor: string | null;
749
+ /** Current lifecycle status of the underlying runtime. */
750
+ runtimeStatus: SyncoreRuntimeStatus;
751
+ /**
752
+ * Fetch the next page of results.
753
+ *
754
+ * @param numItems - Number of items to request. Defaults to `initialNumItems`.
755
+ * A no-op when `hasMore` is `false`, `isLoadingMore` is `true`, or an error
756
+ * occurred.
757
+ */
758
+ loadMore(numItems?: number): Promise<void> | void;
759
+ }
760
+ /**
761
+ * A live, cancellable subscription to a reactive Syncore value.
762
+ *
763
+ * The runtime keeps the watched value up-to-date by re-running the underlying
764
+ * query whenever its data dependencies change. `onUpdate` is called each time a
765
+ * fresh result is available so the subscriber can read the new value with
766
+ * `localQueryResult()`.
767
+ *
768
+ * React’s `useQuery` and Svelte’s `createQueryStore` are built on top of this
769
+ * interface — you only need `SyncoreWatch` directly when integrating with
770
+ * frameworks outside the first-party adapters.
771
+ *
772
+ * ```ts
773
+ * const watch = client.watchQuery(api.tasks.list);
774
+ * const unsubscribe = watch.onUpdate(() => {
775
+ * console.log(watch.localQueryResult());
776
+ * });
777
+ * // Later:
778
+ * unsubscribe();
779
+ * watch.dispose?.();
780
+ * ```
781
+ */
180
782
  interface SyncoreWatch<TValue> {
181
- /** Subscribe to updates for this query watch. */
783
+ /**
784
+ * Register a callback to be called whenever the watched value changes.
785
+ * @returns An `unsubscribe` function; call it to stop receiving updates.
786
+ */
182
787
  onUpdate(callback: () => void): () => void;
183
- /** Read the latest local query result, if one is available. */
788
+ /** Return the latest available query result, or `undefined` if not yet loaded. */
184
789
  localQueryResult(): TValue | undefined;
185
- /** Read the latest local query error, if one is available. */
790
+ /** Return the error from the last failed execution, or `undefined` on success. */
186
791
  localQueryError(): Error | undefined;
187
- /** Dispose the watch if the implementation exposes explicit cleanup. */
792
+ /**
793
+ * Release all resources held by this watch handle.
794
+ *
795
+ * Call this when the subscriber is unmounted or the watch is no longer needed
796
+ * to prevent memory leaks. Framework adapters call this automatically.
797
+ */
188
798
  dispose?(): void;
189
799
  }
190
800
  interface FilterBuilder {
191
- /** Match documents whose field is exactly equal to a value. */
192
801
  eq(field: string, value: unknown): QueryExpression;
193
- /** Match documents whose field is greater than a value. */
194
802
  gt(field: string, value: unknown): QueryExpression;
195
- /** Match documents whose field is greater than or equal to a value. */
196
803
  gte(field: string, value: unknown): QueryExpression;
197
- /** Match documents whose field is less than a value. */
198
804
  lt(field: string, value: unknown): QueryExpression;
199
- /** Match documents whose field is less than or equal to a value. */
200
805
  lte(field: string, value: unknown): QueryExpression;
201
- /** Combine several filter expressions with logical AND. */
202
806
  and(...expressions: QueryExpression[]): QueryExpression;
203
- /** Combine several filter expressions with logical OR. */
204
807
  or(...expressions: QueryExpression[]): QueryExpression;
205
808
  }
206
- interface IndexRangeBuilder {
207
- /** Constrain an indexed field to an exact value. */
208
- eq(field: string, value: unknown): IndexRangeBuilder;
209
- /** Constrain an indexed field to values greater than a value. */
210
- gt(field: string, value: unknown): IndexRangeBuilder;
211
- /** Constrain an indexed field to values greater than or equal to a value. */
212
- gte(field: string, value: unknown): IndexRangeBuilder;
213
- /** Constrain an indexed field to values less than a value. */
214
- lt(field: string, value: unknown): IndexRangeBuilder;
215
- /** Constrain an indexed field to values less than or equal to a value. */
216
- lte(field: string, value: unknown): IndexRangeBuilder;
217
- /** Finish building the index range. */
809
+ interface IndexRangeBuilder<TFieldName extends string = string> {
810
+ eq(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName>;
811
+ gt(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName>;
812
+ gte(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName>;
813
+ lt(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName>;
814
+ lte(field: TFieldName, value: unknown): IndexRangeBuilder<TFieldName>;
218
815
  build(): QueryCondition[];
219
816
  }
220
- interface SearchIndexBuilder {
221
- /** Set the text field and text to search for. */
222
- search(field: string, value: string): SearchIndexBuilder;
223
- /** Add an equality filter alongside the text search. */
224
- eq(field: string, value: unknown): SearchIndexBuilder;
225
- /** Finish building the search query. */
817
+ interface SearchIndexBuilder<TSearchField extends string = string, TFilterField extends string = string> {
818
+ search(field: TSearchField, value: string): SearchIndexBuilder<TSearchField, TFilterField>;
819
+ eq(field: TFilterField, value: unknown): SearchIndexBuilder<TSearchField, TFilterField>;
226
820
  build(): SearchQuery;
227
821
  }
228
- type AnySyncoreSchema = SyncoreSchema<any>;
229
- type TableNames<TSchema extends AnySyncoreSchema> = Extract<keyof TSchema["tables"], string>;
230
- type DocumentForTable<TSchema extends AnySyncoreSchema, TTableName extends TableNames<TSchema>> = InferDocument<TSchema["tables"][TTableName]>;
231
- type InsertValueForTable<TSchema extends AnySyncoreSchema, TTableName extends TableNames<TSchema>> = InferTableInput<TSchema["tables"][TTableName]>;
822
+ type TableNames<TSchema extends SyncoreDataModel> = Extract<keyof TSchema["tables"], string>;
823
+ type DocumentForTable<TSchema extends SyncoreDataModel, TTableName extends TableNames<TSchema>> = InferDocument<TSchema["tables"][TTableName]>;
824
+ type InsertValueForTable<TSchema extends SyncoreDataModel, TTableName extends TableNames<TSchema>> = InferTableInput<TSchema["tables"][TTableName]>;
825
+ type OptionalPropertyNames<TValue> = TValue extends object ? { [TKey in keyof TValue]-?: Omit<TValue, TKey> extends TValue ? TKey : never }[keyof TValue] : never;
826
+ type PatchValue<TValue> = TValue extends object ? { [TKey in keyof TValue]?: TKey extends OptionalPropertyNames<TValue> ? TValue[TKey] | undefined : TValue[TKey] } : never;
827
+ type PatchValueForTable<TSchema extends SyncoreDataModel, TTableName extends TableNames<TSchema>> = PatchValue<InsertValueForTable<TSchema, TTableName>>;
232
828
  type OptionalArgsTuple<TArgs> = Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];
233
- interface SyncoreDatabaseReader<TSchema extends AnySyncoreSchema = AnySyncoreSchema> {
234
- /** Read a single document by table name and id. */
829
+ /**
830
+ * Read-only database API available inside Syncore query (and mutation/action)
831
+ * handlers via `ctx.db`.
832
+ *
833
+ * All methods are fully typed against your schema — the table names and
834
+ * returned document shapes are inferred from the `TSchema` type parameter:
835
+ *
836
+ * ```ts
837
+ * // Fetch by ID
838
+ * const task = await ctx.db.get("tasks", taskId);
839
+ *
840
+ * // Chainable query builder
841
+ * const todos = await ctx.db
842
+ * .query("tasks")
843
+ * .withIndex("by_status", (q) => q.eq("status", "todo"))
844
+ * .order("asc")
845
+ * .take(20);
846
+ *
847
+ * // Raw SQL escape hatch (use sparingly — bypasses type safety)
848
+ * const rows = await ctx.db.raw<{ count: number }>(
849
+ * "SELECT COUNT(*) AS count FROM tasks"
850
+ * );
851
+ * ```
852
+ */
853
+ interface SyncoreDatabaseReader<TSchema extends SyncoreDataModel = SyncoreDataModel> {
854
+ /**
855
+ * Fetch a single document by its `_id`, or `null` if it does not exist.
856
+ *
857
+ * @param table - The table to look in.
858
+ * @param id - The document’s `_id` string.
859
+ */
235
860
  get<TTableName extends TableNames<TSchema>>(table: TTableName, id: string): Promise<DocumentForTable<TSchema, TTableName> | null>;
236
- /** Start building a table query. */
237
- query<TTableName extends TableNames<TSchema>>(table: TTableName): QueryBuilder<DocumentForTable<TSchema, TTableName>>;
238
- /** Run raw SQL against the local Syncore database. */
861
+ /**
862
+ * Start a chainable {@link QueryBuilder} for the given table.
863
+ *
864
+ * Chain `.withIndex()` or `.withSearchIndex()` to use an index, `.filter()`
865
+ * for additional predicates, `.order()` to control direction, and then a
866
+ * terminal method (`.collect()`, `.first()`, `.take()`, `.paginate()`).
867
+ */
868
+ query<TTableName extends TableNames<TSchema>>(table: TTableName): QueryBuilder<TSchema["tables"][TTableName], DocumentForTable<TSchema, TTableName>>;
869
+ /**
870
+ * Execute a raw SQL `SELECT` statement and return the results.
871
+ *
872
+ * Prefer the typed query builder whenever possible. Use `raw` as an escape
873
+ * hatch for complex aggregations or joins that the builder cannot express.
874
+ */
239
875
  raw<TValue = unknown>(sql: string, params?: unknown[]): Promise<TValue[]>;
240
876
  }
241
- interface SyncoreDatabaseWriter<TSchema extends AnySyncoreSchema = AnySyncoreSchema> extends SyncoreDatabaseReader<TSchema> {
242
- /** Insert a new document into a table and return its generated id. */
877
+ /**
878
+ * Read-write database API available inside Syncore mutation handlers via
879
+ * `ctx.db`. Extends {@link SyncoreDatabaseReader} with write methods that
880
+ * execute atomically within the mutation’s transaction.
881
+ *
882
+ * ```ts
883
+ * // Insert a new document and get its generated _id
884
+ * const id = await ctx.db.insert("tasks", { title: "Buy milk", status: "todo", projectId: null });
885
+ *
886
+ * // Merge a partial update (other fields are preserved)
887
+ * await ctx.db.patch("tasks", id, { status: "done" });
888
+ *
889
+ * // Replace the entire document (all fields must be provided)
890
+ * await ctx.db.replace("tasks", id, { title: "Buy oat milk", status: "todo", projectId: null });
891
+ *
892
+ * // Delete a document
893
+ * await ctx.db.delete("tasks", id);
894
+ * ```
895
+ */
896
+ interface SyncoreDatabaseWriter<TSchema extends SyncoreDataModel = SyncoreDataModel> extends SyncoreDatabaseReader<TSchema> {
897
+ /**
898
+ * Insert a new document and return its generated `_id`.
899
+ *
900
+ * The value must satisfy the table’s validator schema. System fields
901
+ * (`_id`, `_creationTime`) are set automatically.
902
+ */
243
903
  insert<TTableName extends TableNames<TSchema>>(table: TTableName, value: InsertValueForTable<TSchema, TTableName>): Promise<string>;
244
- /** Apply a partial update to an existing document. */
245
- patch<TTableName extends TableNames<TSchema>>(table: TTableName, id: string, value: Partial<InsertValueForTable<TSchema, TTableName>>): Promise<void>;
246
- /** Replace an existing document with a full new value. */
904
+ /**
905
+ * Merge `value` into the existing document at `id`.
906
+ *
907
+ * Only the keys present in `value` are updated; all other fields retain their
908
+ * current values. Equivalent to a SQL `UPDATE … SET …` for specific columns.
909
+ */
910
+ patch<TTableName extends TableNames<TSchema>>(table: TTableName, id: string, value: PatchValueForTable<TSchema, TTableName>): Promise<void>;
911
+ /**
912
+ * Overwrite the entire document at `id` with `value`.
913
+ *
914
+ * The `_id` and `_creationTime` system fields are preserved; all other
915
+ * fields are replaced. Use `patch` when you only want to change a subset of
916
+ * fields.
917
+ */
247
918
  replace<TTableName extends TableNames<TSchema>>(table: TTableName, id: string, value: InsertValueForTable<TSchema, TTableName>): Promise<void>;
248
- /** Delete a document from a table. */
919
+ /**
920
+ * Permanently delete the document with the given `id`.
921
+ *
922
+ * A no-op if the document does not exist.
923
+ */
249
924
  delete<TTableName extends TableNames<TSchema>>(table: TTableName, id: string): Promise<void>;
250
925
  }
251
926
  /**
252
- * The storage API exposed inside Syncore runtime contexts.
927
+ * Blob storage operations exposed to Syncore function handlers via
928
+ * `ctx.storage`.
929
+ *
930
+ * Store large binary objects (images, PDFs, audio files) separately from the
931
+ * SQLite database. Each object gets an opaque `id` that you can persist in a
932
+ * document field for later retrieval.
933
+ *
934
+ * ```ts
935
+ * // In a mutation
936
+ * const id = await ctx.storage.put({
937
+ * data: imageBuffer,
938
+ * contentType: "image/png",
939
+ * });
940
+ * await ctx.db.patch("users", userId, { avatarId: id });
941
+ *
942
+ * // In a query
943
+ * const bytes = await ctx.storage.read(user.avatarId);
944
+ * ```
253
945
  */
254
946
  interface SyncoreStorageApi {
255
- /** Store a file-like payload locally and return its generated id. */
947
+ /**
948
+ * Persist a binary blob and return its auto-generated opaque `id`.
949
+ *
950
+ * Store the returned `id` in a database document to reference the object.
951
+ */
256
952
  put(input: StorageWriteInput): Promise<string>;
257
- /** Read metadata for a stored object. */
953
+ /**
954
+ * Return metadata for the stored object, or `null` if it does not exist.
955
+ * Does not fetch the raw bytes — use `read` for that.
956
+ */
258
957
  get(id: string): Promise<StorageObject | null>;
259
- /** Read the stored bytes for an object. */
958
+ /**
959
+ * Return the raw bytes of the stored object, or `null` if it does not exist.
960
+ */
260
961
  read(id: string): Promise<Uint8Array | null>;
261
- /** Delete a stored object. */
962
+ /**
963
+ * Permanently delete the stored object. A no-op if the object does not exist.
964
+ */
262
965
  delete(id: string): Promise<void>;
263
966
  }
967
+ /**
968
+ * Job scheduling API available to mutation and action handlers via
969
+ * `ctx.scheduler`.
970
+ *
971
+ * Use `runAfter` and `runAt` to enqueue a mutation or action that runs outside
972
+ * the current transaction — ideal for sending notifications, retrying
973
+ * failed operations, or breaking long workflows into steps.
974
+ *
975
+ * ```ts
976
+ * // Run a cleanup job 24 hours from now
977
+ * await ctx.scheduler.runAfter(
978
+ * 24 * 60 * 60_000,
979
+ * api.cleanup.deleteExpiredSessions,
980
+ * );
981
+ *
982
+ * // Run at a specific timestamp
983
+ * const tomorrow = Date.now() + 86_400_000;
984
+ * const jobId = await ctx.scheduler.runAt(tomorrow, api.email.sendDigest, { userId });
985
+ *
986
+ * // Cancel if the user opts out before the job fires
987
+ * await ctx.scheduler.cancel(jobId);
988
+ * ```
989
+ */
264
990
  interface SchedulerApi {
265
- /** Schedule a mutation or action to run after a delay. */
991
+ /**
992
+ * Enqueue a mutation or action to run after `delayMs` milliseconds.
993
+ *
994
+ * @param delayMs - Delay from now in milliseconds.
995
+ * @param functionReference - The mutation or action to execute.
996
+ * @param args - Arguments forwarded to the function.
997
+ * @param misfirePolicy - Optional policy for missed executions.
998
+ * @returns An opaque job `id` you can pass to `cancel`.
999
+ */
266
1000
  runAfter<TArgs, TResult>(delayMs: number, functionReference: FunctionReference<"mutation" | "action", TArgs, TResult>, ...args: [...OptionalArgsTuple<TArgs>, misfirePolicy?: MisfirePolicy]): Promise<string>;
267
- /** Schedule a mutation or action to run at a specific time. */
1001
+ /**
1002
+ * Enqueue a mutation or action to run at a specific Unix timestamp (or
1003
+ * `Date` object).
1004
+ *
1005
+ * @param timestamp - When to run the job (ms since epoch, or a Date).
1006
+ * @param functionReference - The mutation or action to execute.
1007
+ * @param args - Arguments forwarded to the function.
1008
+ * @param misfirePolicy - Optional policy for missed executions.
1009
+ * @returns An opaque job `id` you can pass to `cancel`.
1010
+ */
268
1011
  runAt<TArgs, TResult>(timestamp: number | Date, functionReference: FunctionReference<"mutation" | "action", TArgs, TResult>, ...args: [...OptionalArgsTuple<TArgs>, misfirePolicy?: MisfirePolicy]): Promise<string>;
269
- /** Cancel a previously scheduled job. */
1012
+ /**
1013
+ * Cancel a pending scheduled job by its `id`.
1014
+ *
1015
+ * A no-op if the job has already executed or was already cancelled.
1016
+ */
270
1017
  cancel(id: string): Promise<void>;
271
1018
  }
272
1019
  /**
273
- * The context object available inside Syncore queries.
1020
+ * Execution context injected into every Syncore **query** handler.
1021
+ *
1022
+ * `ctx` is the first argument of every `query()` handler. It provides
1023
+ * read-only database access, storage access, platform capabilities, and the
1024
+ * ability to call other queries. The type is generic over the app schema so
1025
+ * that `ctx.db` is fully typed against your tables.
1026
+ *
1027
+ * The generated `QueryCtx` in `syncore/_generated/server.ts` is always
1028
+ * preferred over the base type because it is pre-bound to your app schema:
1029
+ *
1030
+ * ```ts
1031
+ * import type { QueryCtx } from "../_generated/server";
1032
+ *
1033
+ * export const list = query({
1034
+ * args: { projectId: s.optional(s.id("projects")) },
1035
+ * handler: async (ctx: QueryCtx, { projectId }) => {
1036
+ * return ctx.db
1037
+ * .query("tasks")
1038
+ * .withIndex("by_project", (q) =>
1039
+ * projectId ? q.eq("projectId", projectId) : q
1040
+ * )
1041
+ * .collect();
1042
+ * },
1043
+ * });
1044
+ * ```
274
1045
  */
275
- interface QueryCtx<TSchema extends AnySyncoreSchema = AnySyncoreSchema> {
276
- /** Read-only database access for this query. */
1046
+ interface QueryCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> {
1047
+ /** Read-only access to the local SQLite database. */
277
1048
  db: SyncoreDatabaseReader<TSchema>;
278
- /** Local file/blob storage for this runtime. */
1049
+ /** Blob storage access for reading files and images. */
279
1050
  storage: SyncoreStorageApi;
280
- /** Optional adapter or plugin capabilities exposed by the runtime. */
1051
+ /**
1052
+ * Platform capabilities injected at runtime setup (e.g. push notifications,
1053
+ * biometrics). `undefined` when no capabilities were configured.
1054
+ */
281
1055
  capabilities?: Readonly<SyncoreCapabilities>;
282
- /** Call another Syncore query from inside this query. */
1056
+ /** Structured descriptors for the registered capabilities. */
1057
+ capabilityDescriptors?: ReadonlyArray<CapabilityDescriptor>;
1058
+ /**
1059
+ * Metadata about the Syncore component this function belongs to, if it was
1060
+ * installed as part of a component package rather than the root app.
1061
+ */
1062
+ component?: {
1063
+ path: string;
1064
+ name: string;
1065
+ version: string;
1066
+ capabilities: readonly string[];
1067
+ };
1068
+ /**
1069
+ * Call another Syncore query inside this handler.
1070
+ *
1071
+ * The callee’s read-set is merged into the current query’s dependencies, so
1072
+ * any change that would invalidate the callee also invalidates this query.
1073
+ */
283
1074
  runQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
284
1075
  }
285
1076
  /**
286
- * The context object available inside Syncore mutations.
1077
+ * Execution context injected into every Syncore **mutation** handler.
1078
+ *
1079
+ * Extends {@link QueryCtx} with a writable database (`ctx.db`), a scheduler,
1080
+ * and the ability to call mutations or actions. Everything runs inside a single
1081
+ * atomic SQLite transaction that is committed when the handler returns or
1082
+ * rolled back if it throws.
1083
+ *
1084
+ * Use the generated `MutationCtx` from `syncore/_generated/server.ts` so that
1085
+ * `ctx.db` is typed to your specific schema:
1086
+ *
1087
+ * ```ts
1088
+ * import type { MutationCtx } from "../_generated/server";
1089
+ *
1090
+ * export const create = mutation({
1091
+ * args: { title: s.string() },
1092
+ * handler: async (ctx: MutationCtx, { title }) => {
1093
+ * const id = await ctx.db.insert("tasks", {
1094
+ * title,
1095
+ * status: "todo",
1096
+ * projectId: null,
1097
+ * });
1098
+ * // Schedule a follow-up action without blocking the transaction
1099
+ * await ctx.scheduler.runAfter(0, api.tasks.notifyCreated, { id });
1100
+ * return id;
1101
+ * },
1102
+ * });
1103
+ * ```
287
1104
  */
288
- interface MutationCtx<TSchema extends AnySyncoreSchema = AnySyncoreSchema> extends QueryCtx<TSchema> {
1105
+ interface MutationCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> extends QueryCtx<TSchema> {
1106
+ /** Read-write database access. Changes are committed atomically on handler return. */
289
1107
  db: SyncoreDatabaseWriter<TSchema>;
290
- /** Schedule future work from this mutation. */
1108
+ /** Schedule mutations and actions to run outside the current transaction. */
291
1109
  scheduler: SchedulerApi;
292
- /** Call another mutation from inside this mutation. */
1110
+ /**
1111
+ * Call another mutation inside this handler’s transaction.
1112
+ *
1113
+ * The callee shares the current transaction context, so its writes are part
1114
+ * of the same atomic commit.
1115
+ */
293
1116
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
294
- /** Call an action from this mutation. */
1117
+ /**
1118
+ * Launch an action from within a mutation. The action runs asynchronously
1119
+ * in a separate context **after** the mutation commits.
1120
+ */
295
1121
  runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
296
1122
  }
297
1123
  /**
298
- * The context object available inside Syncore actions.
1124
+ * Execution context injected into every Syncore **action** handler.
1125
+ *
1126
+ * Extends {@link QueryCtx} with a scheduler and the ability to call mutations
1127
+ * and other actions. Unlike mutations, actions run **outside** of any
1128
+ * transaction, so they can perform long-running or async work (HTTP requests,
1129
+ * file I/O, etc.) and delegate writes to mutations.
1130
+ *
1131
+ * Use the generated `ActionCtx` from `syncore/_generated/server.ts` so the
1132
+ * types are bound to your app schema:
1133
+ *
1134
+ * ```ts
1135
+ * import type { ActionCtx } from "../_generated/server";
1136
+ *
1137
+ * export const importFromApi = action({
1138
+ * args: { projectId: s.id("projects") },
1139
+ * handler: async (ctx: ActionCtx, { projectId }) => {
1140
+ * const data = await fetch("https://api.example.com/tasks").then((r) => r.json());
1141
+ * for (const item of data) {
1142
+ * await ctx.runMutation(api.tasks.create, { title: item.title });
1143
+ * }
1144
+ * },
1145
+ * });
1146
+ * ```
299
1147
  */
300
- interface ActionCtx<TSchema extends AnySyncoreSchema = AnySyncoreSchema> extends QueryCtx<TSchema> {
301
- /** Schedule future work from this action. */
1148
+ interface ActionCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> extends QueryCtx<TSchema> {
1149
+ /** Schedule mutations and actions to run at a later time. */
302
1150
  scheduler: SchedulerApi;
303
- /** Call a mutation from this action. */
1151
+ /**
1152
+ * Call a mutation from within this action.
1153
+ *
1154
+ * Because actions are non-transactional, each `runMutation` call creates its
1155
+ * own transaction. If the action fails partway through, earlier mutations are
1156
+ * **not** rolled back automatically — design accordingly.
1157
+ */
304
1158
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
305
- /** Call another action from this action. */
1159
+ /** Call another action from within this action. */
306
1160
  runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
307
1161
  }
308
1162
  /**
309
- * The typed client API exposed by a Syncore runtime.
1163
+ * The client-facing API for calling Syncore functions and subscribing to
1164
+ * reactive query results.
1165
+ *
1166
+ * You obtain a `SyncoreClient` by calling `runtime.createClient()` or, for
1167
+ * worker-based browser setups, via the platform adapter’s
1168
+ * `createWebWorkerClient()` / `createManagedWebWorkerClient()` helpers.
1169
+ *
1170
+ * In React, the client is provided to the component tree via
1171
+ * {@link SyncoreProvider} and consumed through hooks (`useQuery`,
1172
+ * `useMutation`, etc.) — you rarely call these methods directly.
1173
+ *
1174
+ * ```ts
1175
+ * const client = runtime.createClient();
1176
+ *
1177
+ * // One-shot query
1178
+ * const tasks = await client.query(api.tasks.list);
1179
+ *
1180
+ * // One-shot mutation
1181
+ * await client.mutation(api.tasks.create, { title: "Buy milk" });
1182
+ *
1183
+ * // Reactive subscription
1184
+ * const watch = client.watchQuery(api.tasks.list);
1185
+ * watch.onUpdate(() => console.log(watch.localQueryResult()));
1186
+ * ```
310
1187
  */
311
1188
  interface SyncoreClient {
312
- /** Fetch a query result once. */
1189
+ /**
1190
+ * Execute a query and return its result.
1191
+ *
1192
+ * Unlike a reactive subscription, this is a one-shot call that does not
1193
+ * stay up to date. Use `watchQuery` or the `useQuery` hook for live data.
1194
+ */
313
1195
  query<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
314
- /** Execute a mutation. */
1196
+ /**
1197
+ * Execute a mutation and return its result.
1198
+ *
1199
+ * The mutation runs atomically and all affected queries are automatically
1200
+ * re-executed after the commit.
1201
+ */
315
1202
  mutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
316
- /** Execute an action. */
1203
+ /**
1204
+ * Execute an action and return its result.
1205
+ *
1206
+ * Actions are non-transactional and may take arbitrarily long to complete.
1207
+ */
317
1208
  action<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
318
- /** Subscribe to a query and receive reactive updates. */
1209
+ /**
1210
+ * Subscribe to a reactive query and return a {@link SyncoreWatch} handle.
1211
+ *
1212
+ * The watch delivers a new result every time the underlying data changes.
1213
+ * Call `watch.onUpdate()` to listen and `watch.dispose()` when done.
1214
+ */
319
1215
  watchQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): SyncoreWatch<TResult>;
1216
+ /**
1217
+ * Subscribe to the runtime’s lifecycle status.
1218
+ *
1219
+ * Useful for showing loading or error states in the UI while the runtime is
1220
+ * starting or recovering.
1221
+ */
1222
+ watchRuntimeStatus(): SyncoreWatch<SyncoreRuntimeStatus>;
1223
+ }
1224
+ interface SyncoreRuntimeAdmin<TSchema extends SyncoreDataModel = SyncoreDataModel> {
1225
+ prepareForDirectAccess(): Promise<void>;
1226
+ createClient(): SyncoreClient;
1227
+ runQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
1228
+ runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
1229
+ runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
1230
+ runDevtoolsMutation<TResult>(callback: (ctx: {
1231
+ db: SyncoreDatabaseWriter<TSchema>;
1232
+ }) => Promise<TResult>, meta?: DevtoolsEventMeta): Promise<TResult>;
1233
+ getRuntimeSummary(): SyncoreRuntimeSummary;
1234
+ getActiveQueryInfos(): SyncoreActiveQueryInfo[];
1235
+ getRuntimeId(): string;
1236
+ getDriverDatabasePath(): string | undefined;
1237
+ subscribeToDevtoolsEvents(listener: (event: SyncoreDevtoolsEvent) => void): () => void;
1238
+ subscribeToDevtoolsInvalidations(listener: (scopes: Set<DevtoolsLiveQueryScope>) => void): () => void;
1239
+ notifyDevtoolsScopes(scopes: Iterable<DevtoolsLiveQueryScope>): void;
1240
+ forceRefreshDevtools(reason: string, scopes?: Iterable<ImpactScope>, meta?: DevtoolsEventMeta): Promise<void>;
1241
+ cancelScheduledJob(id: string): Promise<boolean>;
1242
+ updateScheduledJob(options: UpdateScheduledJobOptions): Promise<boolean>;
320
1243
  }
1244
+ type DevtoolsEventMeta = {
1245
+ origin?: SyncoreDevtoolsEventOrigin;
1246
+ executionId?: string;
1247
+ parentExecutionId?: string;
1248
+ schedulerJobId?: string;
1249
+ schedulerRun?: boolean;
1250
+ };
321
1251
  /**
322
- * A composable builder for Syncore table queries.
1252
+ * Chainable query builder returned by `ctx.db.query(tableName)`.
1253
+ *
1254
+ * Chain methods in this order to build up a query:
1255
+ *
1256
+ * 1. **Optional** — `.withIndex()` or `.withSearchIndex()` to use an index.
1257
+ * 2. **Optional** — `.filter()` to add arbitrary field predicates.
1258
+ * 3. **Optional** — `.order()` to control sort direction (defaults to `"asc"`).
1259
+ * 4. **Required terminal** — `.collect()`, `.take()`, `.first()`, `.unique()`,
1260
+ * or `.paginate()`.
1261
+ *
1262
+ * ```ts
1263
+ * // Fetch all tasks in a project, newest first, limited to 10
1264
+ * const tasks = await ctx.db
1265
+ * .query("tasks")
1266
+ * .withIndex("by_project", (q) => q.eq("projectId", id))
1267
+ * .order("desc")
1268
+ * .take(10);
1269
+ *
1270
+ * // Full-text search with a filter
1271
+ * const results = await ctx.db
1272
+ * .query("tasks")
1273
+ * .withSearchIndex("search_title", (q) =>
1274
+ * q.search("title", searchText).eq("status", "todo")
1275
+ * )
1276
+ * .collect();
1277
+ * ```
323
1278
  */
324
- interface QueryBuilder<TDocument> {
325
- /** Query through a named index instead of scanning the whole table. */
326
- withIndex(indexName: string, builder?: (range: IndexRangeBuilder) => IndexRangeBuilder): this;
327
- /** Query through a named search index for text search. */
328
- withSearchIndex(indexName: string, builder: (search: SearchIndexBuilder) => SearchIndexBuilder): this;
329
- /** Set the result ordering. */
1279
+ interface QueryBuilder<TTable extends AnyTableDefinition, TDocument = InferDocument<TTable>> {
1280
+ /**
1281
+ * Restrict the query to documents matching an index range.
1282
+ *
1283
+ * @param indexName - The name of the index to use (must be registered via `defineTable().index()`).
1284
+ * @param builder - Optional callback that receives an `IndexRangeBuilder` and returns it
1285
+ * after chaining `eq`, `gt`, `gte`, `lt`, `lte` calls. Omit to return all documents in
1286
+ * index order.
1287
+ */
1288
+ withIndex<TIndexName extends TableIndexNames<TTable>>(indexName: TIndexName, builder?: (range: IndexRangeBuilder<TableIndexFields<TTable, TIndexName>[number]>) => IndexRangeBuilder<TableIndexFields<TTable, TIndexName>[number]>): this;
1289
+ /**
1290
+ * Restrict the query to documents matching a full-text search index.
1291
+ *
1292
+ * @param indexName - The name of the search index (must be registered via `defineTable().searchIndex()`).
1293
+ * @param builder - Callback that calls `.search(field, text)` and optionally chains
1294
+ * `.eq(filterField, value)` conditions.
1295
+ */
1296
+ withSearchIndex<TIndexName extends TableSearchIndexNames<TTable>>(indexName: TIndexName, builder: (search: SearchIndexBuilder<TableSearchIndexConfig<TTable, TIndexName>["searchField"], TableSearchIndexConfig<TTable, TIndexName>["filterFields"]>) => SearchIndexBuilder<TableSearchIndexConfig<TTable, TIndexName>["searchField"], TableSearchIndexConfig<TTable, TIndexName>["filterFields"]>): this;
1297
+ /**
1298
+ * Set the iteration order for this query. Defaults to `"asc"`.
1299
+ *
1300
+ * When used with `withIndex`, the order applies to the index's primary sort key.
1301
+ * When used without an index (full table scan), `"asc"` and `"desc"` refer to
1302
+ * insertion order.
1303
+ */
330
1304
  order(order: "asc" | "desc"): this;
331
- /** Add a filter expression to the query. */
1305
+ /**
1306
+ * Add an additional in-memory predicate that is applied after index
1307
+ * evaluation.
1308
+ *
1309
+ * Use this for conditions that cannot be expressed as an index range (e.g.
1310
+ * checking a field not covered by the active index). Heavy use of `filter`
1311
+ * on large tables causes a full index scan — prefer dedicated indexes for
1312
+ * frequently filtered fields.
1313
+ */
332
1314
  filter(builder: (filter: FilterBuilder) => QueryExpression): this;
333
- /** Collect all matching documents. */
1315
+ /** Execute the query and return all matching documents as an array. */
334
1316
  collect(): Promise<TDocument[]>;
335
- /** Collect up to a fixed number of matching documents. */
1317
+ /**
1318
+ * Execute the query and return at most `count` documents.
1319
+ *
1320
+ * More efficient than `collect()` when you only need a limited number of
1321
+ * results.
1322
+ */
336
1323
  take(count: number): Promise<TDocument[]>;
337
- /** Return the first matching document, or `null` if none exist. */
1324
+ /**
1325
+ * Execute the query and return the first matching document, or `null` if
1326
+ * there are no results.
1327
+ */
338
1328
  first(): Promise<TDocument | null>;
339
- /** Return one matching document and throw if multiple rows match. */
1329
+ /**
1330
+ * Execute the query and return the single matching document, or `null` if
1331
+ * there are no results. Throws if more than one document matches.
1332
+ *
1333
+ * Use when you expect exactly zero or one result (e.g. a unique index
1334
+ * lookup).
1335
+ */
340
1336
  unique(): Promise<TDocument | null>;
341
- /** Read a paginated slice of documents using a cursor. */
1337
+ /**
1338
+ * Execute the query with cursor-based pagination.
1339
+ *
1340
+ * Pass `PaginationOptions` (a `cursor` and `numItems`) to fetch one page at
1341
+ * a time. The returned `PaginationResult` contains the page items, the next
1342
+ * cursor, and an `isDone` flag.
1343
+ */
342
1344
  paginate(options: PaginationOptions): Promise<PaginationResult<TDocument>>;
343
1345
  }
344
1346
  /**
345
- * The local Syncore runtime that owns the database, storage, scheduler, and function execution.
1347
+ * Local-first Syncore runtime that hosts your schema, functions, and storage.
1348
+ *
1349
+ * `SyncoreRuntime` is the central engine of every Syncore app. It owns the
1350
+ * SQLite driver, the storage adapter, the reactivity engine, and the
1351
+ * background scheduler. Platform-specific factory functions
1352
+ * (`createNodeSyncoreRuntime`, `createWebSyncoreRuntime`, etc.) wrap it with
1353
+ * environment-appropriate defaults so you rarely need to instantiate it
1354
+ * directly.
1355
+ *
1356
+ * **Lifecycle**
1357
+ * 1. Construct the runtime (schema migration is deferred until first use).
1358
+ * 2. Call `await runtime.start()` to apply the schema, start the scheduler,
1359
+ * and connect to devtools. The runtime emits `"runtime.connected"` when ready.
1360
+ * 3. Call `runtime.createClient()` to get a {@link SyncoreClient} for
1361
+ * invoking functions and subscribing to reactive queries.
1362
+ * 4. Call `await runtime.stop()` on shutdown to flush pending jobs and close
1363
+ * the database.
1364
+ *
1365
+ * ```ts
1366
+ * const runtime = new SyncoreRuntime({
1367
+ * schema,
1368
+ * functions,
1369
+ * driver: new NodeSqliteDriver("./db.sqlite"),
1370
+ * storage: new NodeFileStorageAdapter("./storage"),
1371
+ * });
1372
+ * await runtime.start();
1373
+ * const client = runtime.createClient();
1374
+ * ```
346
1375
  */
347
- declare class SyncoreRuntime<TSchema extends AnySyncoreSchema> {
1376
+ declare class SyncoreRuntime<TSchema extends SyncoreDataModel> {
348
1377
  private readonly options;
349
- private readonly runtimeId;
350
- private readonly platform;
351
- private readonly capabilities;
352
- private readonly experimentalPlugins;
353
- private readonly activeQueries;
354
- private readonly disabledSearchIndexes;
355
- private readonly recentEvents;
356
- private readonly devtoolsListeners;
357
- private readonly devtoolsInvalidationListeners;
358
- private readonly externalChangeSourceId;
359
- private detachExternalChangeListener;
360
- private pendingExternalChangePromise;
361
- private queuedExternalChange;
362
- private schedulerTimer;
363
- private readonly recurringJobs;
364
- private readonly schedulerPollIntervalMs;
365
- private readonly driverDatabasePath;
366
- private prepared;
367
- private started;
1378
+ private readonly kernel;
368
1379
  constructor(options: SyncoreRuntimeOptions<TSchema>);
369
1380
  /**
370
- * Start the local Syncore runtime.
1381
+ * Start the runtime: apply the schema migration, initialise the scheduler,
1382
+ * and connect to devtools (if configured).
1383
+ *
1384
+ * Must be called and awaited before using the client. Calling `start()`
1385
+ * a second time is a no-op.
371
1386
  */
372
1387
  start(): Promise<void>;
1388
+ /**
1389
+ * Prepare the runtime for direct (synchronous) access patterns used by
1390
+ * devtools and migration tooling.
1391
+ *
1392
+ * You do not need to call this in normal application code.
1393
+ */
373
1394
  prepareForDirectAccess(): Promise<void>;
374
1395
  /**
375
- * Stop the local Syncore runtime and release any open resources.
1396
+ * Stop the runtime gracefully.
1397
+ *
1398
+ * Flushes any pending scheduler jobs, closes the SQLite driver, and
1399
+ * disconnects from devtools. Call this when your application is shutting
1400
+ * down to avoid database file corruption.
376
1401
  */
377
1402
  stop(): Promise<void>;
378
1403
  /**
379
- * Create a typed client for calling this runtime from the same process.
1404
+ * Create a new {@link SyncoreClient} bound to this runtime.
1405
+ *
1406
+ * Multiple clients can be created from the same runtime — they all share
1407
+ * the same underlying database and reactivity engine. Usually you only need
1408
+ * one client per runtime instance.
380
1409
  */
381
1410
  createClient(): SyncoreClient;
382
- getDevtoolsLiveQuerySnapshot(): Promise<DevtoolsLiveQuerySnapshot>;
383
- getRuntimeSummary(): SyncoreRuntimeSummary;
384
- getActiveQueryInfos(): SyncoreActiveQueryInfo[];
385
- getDriverDatabasePath(): string | undefined;
386
- cancelScheduledJob(id: string): Promise<boolean>;
387
- updateScheduledJob(options: UpdateScheduledJobOptions): Promise<boolean>;
388
- subscribeToDevtoolsEvents(listener: (event: SyncoreDevtoolsEvent) => void): () => void;
389
- subscribeToDevtoolsInvalidations(listener: (scopes: Set<DevtoolsLiveQueryScope>) => void): () => void;
390
- notifyDevtoolsScopes(scopes: Iterable<DevtoolsLiveQueryScope>): void;
391
- runDevtoolsMutation<TResult>(callback: (ctx: {
392
- db: SyncoreDatabaseWriter<TSchema>;
393
- }) => Promise<TResult>, meta?: DevtoolsEventMeta): Promise<TResult>;
394
- forceRefreshDevtools(reason: string, meta?: DevtoolsEventMeta): Promise<void>;
395
- getRuntimeId(): string;
1411
+ /**
1412
+ * Return the low-level admin API.
1413
+ *
1414
+ * The admin API exposes devtools introspection, direct query/mutation
1415
+ * execution, and scheduler management. It is used by the devtools
1416
+ * dashboard and integration tooling — most application code should use
1417
+ * the regular `SyncoreClient` instead.
1418
+ */
1419
+ getAdmin(): SyncoreRuntimeAdmin<TSchema>;
396
1420
  runQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
397
1421
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
398
1422
  runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
399
1423
  watchQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, args?: JsonObject): SyncoreWatch<TResult>;
400
- private executeQueryBuilder;
401
- private invokeFunction;
402
- private createContext;
403
- private createDatabaseReader;
404
- private createDatabaseWriter;
405
- private createStorageApi;
406
- private createSchedulerApi;
407
- private notifySchedulerJobsChanged;
408
- private ensureSystemTables;
409
- private reconcileStorageState;
410
- private applySchema;
411
- private scheduleJob;
412
- private syncRecurringJobs;
413
- private processDueJobs;
414
- private advanceOrFinalizeJob;
415
- private refreshInvalidatedQueries;
416
- private rerunActiveQuery;
417
- private handleExternalChangeEvent;
418
- private processExternalChangeResult;
419
- private publishExternalChange;
420
- private publishStorageChanges;
421
- private refreshAllActiveQueries;
422
- private getSchemaTablesForDevtools;
423
- private collectQueryDependencies;
424
- private resolveFunction;
425
- private validateDocument;
426
- private deserializeDocument;
427
- private syncSearchIndexes;
428
- private removeSearchIndexes;
429
- private renderExpression;
430
- private renderCondition;
431
- private createActiveQueryKey;
432
- private emitDevtools;
433
- private createPluginContext;
434
- private buildCapabilities;
435
- private runPluginHook;
436
- private findSearchIndexKeyForStatement;
437
- private getTableDefinition;
438
1424
  }
439
1425
  declare function createFunctionReference<TKind extends SyncoreFunctionKind, TArgs = Record<never, never>, TResult = unknown>(kind: TKind, name: string): FunctionReference<TKind, TArgs, TResult>;
440
1426
  /**
441
- * Create a typed function reference from a concrete Syncore function definition.
442
- *
443
- * Generated code uses this helper to preserve function arg and result inference.
1427
+ * Create a function reference from an existing Syncore function definition
1428
+ * while preserving its inferred args and result types.
444
1429
  */
445
1430
  declare function createFunctionReferenceFor<TDefinition extends {
446
1431
  kind: SyncoreFunctionKind;
447
- argsValidator: Validator<unknown>;
448
- returnsValidator?: Validator<unknown>;
1432
+ argsValidator: Validator<unknown, unknown, string>;
1433
+ returnsValidator?: Validator<unknown, unknown, string>;
449
1434
  }>(kind: FunctionKindFromDefinition<TDefinition>, name: string): FunctionReference<FunctionKindFromDefinition<TDefinition>, FunctionArgsFromDefinition<TDefinition>, FunctionResultFromDefinition<TDefinition>>;
450
1435
  //#endregion
451
- export { ActionCtx, AnySyncoreSchema, ComparisonOperator, DevtoolsLiveQueryScope, DevtoolsLiveQuerySnapshot, DevtoolsSink, DocumentForTable, FilterBuilder, IndexRangeBuilder, InsertValueForTable, JsonObject, MutationCtx, PaginationOptions, PaginationResult, QueryBuilder, QueryCondition, QueryCtx, QueryExpression, RegisteredSyncoreFunction, RegisteredSyncoreHandler, RunResult, SchedulerApi, SchedulerOptions, SearchIndexBuilder, SearchQuery, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreClient, SyncoreDatabaseReader, SyncoreDatabaseWriter, SyncoreExperimentalPlugin, SyncoreExperimentalPluginContext, SyncoreExternalChangeApplier, SyncoreExternalChangeEvent, SyncoreExternalChangeReason, SyncoreExternalChangeScope, SyncoreExternalChangeSignal, SyncoreFunctionRegistry, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter, SyncoreStorageApi, SyncoreWatch, TableNames, UpdateScheduledJobOptions, createFunctionReference, createFunctionReferenceFor };
1436
+ export { ActionCtx, CapabilityDescriptor, ComparisonOperator, DevtoolsLiveQueryScope, DevtoolsLiveQuerySnapshot, DevtoolsSink, DocumentForTable, ExecutionResult, FilterBuilder, ImpactScope, ImpactSet, IndexRangeBuilder, InsertValueForTable, JsonObject, MutationCtx, PaginationOptions, PaginationResult, PatchValueForTable, QueryBuilder, QueryCondition, QueryCtx, QueryExpression, RegisteredSyncoreFunction, RegisteredSyncoreHandler, RunResult, SchedulerApi, SchedulerOptions, SearchIndexBuilder, SearchQuery, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreClient, SyncoreDataModel, SyncoreDatabaseReader, SyncoreDatabaseWriter, SyncoreExternalChangeApplier, SyncoreExternalChangeEvent, SyncoreExternalChangeReason, SyncoreExternalChangeScope, SyncoreExternalChangeSignal, SyncoreFunctionRegistry, SyncorePaginatedQueryStatus, SyncoreQueriesRequest, SyncoreQueryRequest, SyncoreQueryState, SyncoreQueryStatus, SyncoreResolvedComponents, SyncoreRuntime, SyncoreRuntimeAdmin, SyncoreRuntimeOptions, SyncoreRuntimeStatus, SyncoreRuntimeStatusKind, SyncoreRuntimeStatusReason, SyncoreSqlDriver, SyncoreStorageAdapter, SyncoreStorageApi, SyncoreWatch, TableNames, UpdateScheduledJobOptions, UsePaginatedQueryResult, createFunctionReference, createFunctionReferenceFor };
452
1437
  //# sourceMappingURL=runtime.d.mts.map