syncorejs 0.2.2 → 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 (140) hide show
  1. package/dist/_vendor/cli/app.d.mts.map +1 -1
  2. package/dist/_vendor/cli/app.mjs +8 -5
  3. package/dist/_vendor/cli/app.mjs.map +1 -1
  4. package/dist/_vendor/cli/context.mjs.map +1 -1
  5. package/dist/_vendor/cli/dev-session.mjs.map +1 -1
  6. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  7. package/dist/_vendor/cli/errors.mjs.map +1 -1
  8. package/dist/_vendor/cli/help.mjs.map +1 -1
  9. package/dist/_vendor/cli/index.mjs +9 -2
  10. package/dist/_vendor/cli/index.mjs.map +1 -1
  11. package/dist/_vendor/cli/messages.mjs.map +1 -1
  12. package/dist/_vendor/cli/preflight.mjs.map +1 -1
  13. package/dist/_vendor/cli/project.mjs +20 -20
  14. package/dist/_vendor/cli/project.mjs.map +1 -1
  15. package/dist/_vendor/cli/render.mjs.map +1 -1
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +8 -2
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +238 -64
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs.map +1 -1
  22. package/dist/_vendor/core/runtime/components.d.mts.map +1 -1
  23. package/dist/_vendor/core/runtime/components.mjs.map +1 -1
  24. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  25. package/dist/_vendor/core/runtime/devtools.mjs +130 -23
  26. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  27. package/dist/_vendor/core/runtime/functions.d.mts +388 -6
  28. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  29. package/dist/_vendor/core/runtime/functions.mjs +72 -1
  30. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  31. package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/id.mjs.map +1 -1
  33. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +11 -5
  34. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
  35. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +123 -20
  36. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -1
  37. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +56 -8
  38. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
  39. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +49 -14
  40. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -1
  41. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +4 -7
  42. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -1
  43. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +76 -1
  44. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
  45. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
  47. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +4 -3
  48. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
  49. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -1
  50. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -1
  51. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +4 -0
  52. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -1
  53. package/dist/_vendor/core/runtime/runtime.d.mts +1040 -9
  54. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  55. package/dist/_vendor/core/runtime/runtime.mjs +63 -0
  56. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  57. package/dist/_vendor/core/transport.d.mts +2 -0
  58. package/dist/_vendor/core/transport.d.mts.map +1 -1
  59. package/dist/_vendor/core/transport.mjs +33 -24
  60. package/dist/_vendor/core/transport.mjs.map +1 -1
  61. package/dist/_vendor/devtools-protocol/index.d.ts +149 -4
  62. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  63. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  64. package/dist/_vendor/next/config.d.ts +3 -4
  65. package/dist/_vendor/next/config.d.ts.map +1 -1
  66. package/dist/_vendor/next/config.js +37 -19
  67. package/dist/_vendor/next/config.js.map +1 -1
  68. package/dist/_vendor/next/index.d.ts +109 -29
  69. package/dist/_vendor/next/index.d.ts.map +1 -1
  70. package/dist/_vendor/next/index.js +77 -17
  71. package/dist/_vendor/next/index.js.map +1 -1
  72. package/dist/_vendor/platform-expo/index.d.ts +146 -27
  73. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  74. package/dist/_vendor/platform-expo/index.js +76 -10
  75. package/dist/_vendor/platform-expo/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/react.js.map +1 -1
  77. package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
  78. package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
  79. package/dist/_vendor/platform-node/index.d.mts +173 -9
  80. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  81. package/dist/_vendor/platform-node/index.mjs +225 -94
  82. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  83. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  84. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  86. package/dist/_vendor/platform-web/external-change.d.ts +41 -0
  87. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  88. package/dist/_vendor/platform-web/external-change.js +30 -0
  89. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  90. package/dist/_vendor/platform-web/index.d.ts +307 -35
  91. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  92. package/dist/_vendor/platform-web/index.js +189 -23
  93. package/dist/_vendor/platform-web/index.js.map +1 -1
  94. package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
  95. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/indexeddb.js +10 -0
  97. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  98. package/dist/_vendor/platform-web/opfs.d.ts +13 -0
  99. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/opfs.js +12 -0
  101. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  102. package/dist/_vendor/platform-web/persistence.d.ts +54 -0
  103. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  104. package/dist/_vendor/platform-web/persistence.js +15 -0
  105. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  106. package/dist/_vendor/platform-web/react.d.ts +1 -2
  107. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  108. package/dist/_vendor/platform-web/react.js +2 -4
  109. package/dist/_vendor/platform-web/react.js.map +1 -1
  110. package/dist/_vendor/platform-web/sqljs.js +10 -1
  111. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  112. package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
  113. package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
  114. package/dist/_vendor/platform-web/worker.d.ts +60 -9
  115. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  116. package/dist/_vendor/platform-web/worker.js +37 -4
  117. package/dist/_vendor/platform-web/worker.js.map +1 -1
  118. package/dist/_vendor/react/index.d.ts +196 -13
  119. package/dist/_vendor/react/index.d.ts.map +1 -1
  120. package/dist/_vendor/react/index.js +208 -17
  121. package/dist/_vendor/react/index.js.map +1 -1
  122. package/dist/_vendor/schema/definition.d.ts +129 -0
  123. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  124. package/dist/_vendor/schema/definition.js +99 -0
  125. package/dist/_vendor/schema/definition.js.map +1 -1
  126. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  127. package/dist/_vendor/schema/planner.js.map +1 -1
  128. package/dist/_vendor/schema/validators.d.ts +180 -4
  129. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  130. package/dist/_vendor/schema/validators.js +35 -1
  131. package/dist/_vendor/schema/validators.js.map +1 -1
  132. package/dist/_vendor/svelte/index.d.ts +205 -7
  133. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  134. package/dist/_vendor/svelte/index.js +199 -6
  135. package/dist/_vendor/svelte/index.js.map +1 -1
  136. package/dist/browser.d.ts.map +1 -1
  137. package/dist/cli.js +3 -1
  138. package/dist/cli.js.map +1 -1
  139. package/dist/index.d.ts +1 -1
  140. package/package.json +24 -21
@@ -1,9 +1,17 @@
1
1
  import { FunctionArgs, FunctionArgsFromDefinition, FunctionKindFromDefinition, FunctionReference, FunctionResultFromDefinition, MisfirePolicy, RecurringJobDefinition, RecurringSchedule, SyncoreFunctionKind } from "./functions.mjs";
2
2
  import { ResolvedSyncoreComponent, SyncoreComponentFunctionMetadata } from "./components.mjs";
3
3
  import { AnyTableDefinition, InferDocument, InferTableInput, SyncoreSchemaDefinition, TableIndexFields, TableIndexNames, TableSearchIndexConfig, TableSearchIndexNames, Validator } from "../../schema/index.d.ts";
4
- import { SyncoreActiveQueryInfo, SyncoreDevtoolsEvent, SyncoreDevtoolsEventOrigin, SyncoreRuntimeSummary } from "../../devtools-protocol/index.d.ts";
4
+ import { DocumentChangePreview, SyncoreActiveQueryInfo, SyncoreDevtoolsEvent, SyncoreDevtoolsEventOrigin, SyncoreRuntimeSummary } from "../../devtools-protocol/index.d.ts";
5
5
 
6
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
+ */
7
15
  interface RegisteredSyncoreFunction {
8
16
  kind: SyncoreFunctionKind;
9
17
  argsValidator: Validator<unknown, unknown, string>;
@@ -11,19 +19,44 @@ interface RegisteredSyncoreFunction {
11
19
  handler: RegisteredSyncoreHandler;
12
20
  __syncoreComponent?: SyncoreComponentFunctionMetadata;
13
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
+ */
14
30
  interface SyncoreFunctionRegistry {
15
31
  readonly [name: string]: RegisteredSyncoreFunction | undefined;
16
32
  }
33
+ /** @internal Bivariant function handler type used to avoid TypeScript strictness issues with contravariant function parameters. */
17
34
  type RegisteredSyncoreHandler = {
18
35
  bivarianceHack(ctx: unknown, args: unknown): unknown;
19
36
  }["bivarianceHack"];
37
+ /** A plain JSON-serialisable object. Used for function arguments and scheduler payloads. */
20
38
  type JsonObject = Record<string, unknown>;
39
+ /** A SQL-style comparison operator used when building index range queries. */
21
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
+ */
22
48
  type QueryCondition = {
23
49
  field: string;
24
50
  operator: ComparisonOperator;
25
51
  value: unknown;
26
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
+ */
27
60
  type QueryExpression = {
28
61
  type: "condition";
29
62
  condition: QueryCondition;
@@ -34,27 +67,71 @@ type QueryExpression = {
34
67
  type: "or";
35
68
  expressions: QueryExpression[];
36
69
  };
70
+ /**
71
+ * The arguments Syncore passes to its SQLite FTS5 full-text search layer when
72
+ * a query uses `.withSearchIndex()`.
73
+ */
37
74
  type SearchQuery = {
38
75
  searchField: string;
39
76
  searchText: string;
40
77
  filters: QueryCondition[];
41
78
  };
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
+ */
42
85
  interface RunResult {
86
+ /** Number of rows affected by the statement. */
43
87
  changes: number;
88
+ /** The rowid of the last inserted row, if applicable. */
44
89
  lastInsertRowid?: number | string;
45
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
+ */
46
98
  interface CapabilityDescriptor {
99
+ /** Unique capability name. Must match the key used in `SyncoreCapabilities`. */
47
100
  name: string;
101
+ /** Semantic version number for the capability’s interface contract. */
48
102
  version: number;
103
+ /** Optional feature flags exposed by this capability. */
49
104
  features?: string[];
105
+ /** Arbitrary metadata for introspection or devtools display. */
50
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
+ */
51
111
  optional?: boolean;
52
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
+ */
53
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. */
54
130
  type ImpactSet = ReadonlySet<ImpactScope>;
55
131
  interface ExecutionResult<TResult = unknown> {
56
132
  result: TResult;
57
133
  changedTables: Set<string>;
134
+ documentChanges: DocumentChangePreview[];
58
135
  storageChanges: Array<{
59
136
  storageId: string;
60
137
  reason: Extract<SyncoreExternalChangeReason, "storage-put" | "storage-delete">;
@@ -67,32 +144,134 @@ interface ExecutionResult<TResult = unknown> {
67
144
  changedScopes: ImpactScope[];
68
145
  }>;
69
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
+ */
70
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
+ */
71
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
+ */
72
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
+ */
73
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
+ */
74
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
+ */
75
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
+ */
76
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
+ */
77
194
  close?(): Promise<void>;
78
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
+ */
79
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
+ */
80
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
+ */
81
224
  interface SyncoreExternalChangeEvent {
225
+ /** Identifies the runtime instance that published this event. */
82
226
  sourceId: string;
227
+ /** Which category of state changed. */
83
228
  scope: SyncoreExternalChangeScope;
229
+ /** Why the change occurred. */
84
230
  reason: SyncoreExternalChangeReason;
231
+ /** Unix timestamp (milliseconds) when the change was published. */
85
232
  timestamp: number;
233
+ /** Optional opaque string used to detect duplicate or out-of-order events. */
86
234
  revision?: string;
235
+ /** Specific impact scopes that were affected (subset of `scope`). */
87
236
  changedScopes?: ImpactScope[];
237
+ /** Table names that were written to, when `scope` includes `"database"`. */
88
238
  changedTables?: string[];
239
+ /** Storage object IDs that were affected, when `scope` includes `"storage"`. */
89
240
  storageIds?: string[];
90
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
+ */
91
255
  interface SyncoreExternalChangeSignal {
256
+ /**
257
+ * Register a listener for incoming change events.
258
+ * @returns A cleanup function that removes the listener when called.
259
+ */
92
260
  subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void;
261
+ /** Publish an outgoing change event to other subscribers. */
93
262
  publish(event: SyncoreExternalChangeEvent): void | Promise<void>;
263
+ /** Optional cleanup called when the runtime shuts down. */
94
264
  close?(): void | Promise<void>;
95
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
+ */
96
275
  interface SyncoreExternalChangeApplier {
97
276
  applyExternalChange(event: SyncoreExternalChangeEvent): Promise<{
98
277
  databaseChanged: boolean;
@@ -102,30 +281,116 @@ interface SyncoreExternalChangeApplier {
102
281
  }
103
282
  /**
104
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
+ * ```
105
296
  */
106
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
+ */
107
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
+ */
108
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
+ */
109
314
  fileName?: string;
110
315
  }
111
316
  /**
112
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.
113
322
  */
114
323
  interface StorageObject {
324
+ /** Opaque identifier. Store this in a database document to reference the object. */
115
325
  id: string;
326
+ /** Absolute path or key used by the storage backend (filesystem path, OPFS key, etc.). */
116
327
  path: string;
328
+ /** Size of the stored data in bytes. */
117
329
  size: number;
330
+ /** MIME type provided at write time, or `null` if none was specified. */
118
331
  contentType: string | null;
119
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
+ */
120
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
+ */
121
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
+ */
122
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
+ */
123
360
  read(id: string): Promise<Uint8Array | null>;
361
+ /**
362
+ * Permanently remove a stored object. A no-op if the object does not exist.
363
+ */
124
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
+ */
125
369
  list?(): Promise<StorageObject[]>;
126
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
+ */
127
387
  interface DevtoolsSink {
388
+ /** Called synchronously every time the runtime emits a new event. */
128
389
  emit(event: SyncoreDevtoolsEvent): 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
+ */
129
394
  attachRuntime?(runtime: SyncoreRuntime<SyncoreDataModel>): void;
130
395
  }
131
396
  interface DevtoolsLiveQuerySnapshot {
@@ -151,64 +416,289 @@ interface DevtoolsLiveQuerySnapshot {
151
416
  }>;
152
417
  }
153
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
+ */
154
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
+ */
155
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
+ */
156
451
  recurringJobs?: RecurringJobDefinition[];
157
452
  }
158
453
  type SyncoreResolvedComponents = readonly ResolvedSyncoreComponent[];
159
454
  interface UpdateScheduledJobOptions {
160
455
  id: string;
161
- schedule: RecurringSchedule;
456
+ schedule?: RecurringSchedule;
162
457
  args: JsonObject;
163
- misfirePolicy: MisfirePolicy;
458
+ misfirePolicy?: MisfirePolicy;
164
459
  runAt?: number;
165
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
+ */
166
485
  interface SyncoreCapabilities {
167
486
  [name: string]: unknown;
168
487
  }
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
+ */
169
502
  interface SyncoreDataModel<TTables extends SyncoreSchemaDefinition = SyncoreSchemaDefinition> {
170
503
  readonly tables: TTables;
171
504
  getTable(tableName: Extract<keyof TTables, string>): TTables[Extract<keyof TTables, string>];
172
505
  tableNames(): Array<Extract<keyof TTables, string>>;
173
506
  }
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
+ */
174
518
  interface SyncoreRuntimeOptions<TSchema extends SyncoreDataModel> {
519
+ /** The data model that defines the available tables, indexes, and schemas. */
175
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
+ */
176
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
+ */
177
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
+ */
178
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
+ */
179
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
+ */
180
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
+ */
181
564
  externalChangeApplier?: SyncoreExternalChangeApplier;
565
+ /**
566
+ * Platform-specific capabilities injected into `ctx.capabilities` inside
567
+ * every function handler. See {@link SyncoreCapabilities}.
568
+ */
182
569
  capabilities?: SyncoreCapabilities;
570
+ /** Structured capability descriptors validated at start-up. */
183
571
  capabilityDescriptors?: CapabilityDescriptor[];
572
+ /**
573
+ * Label reported to devtools to identify the runtime’s environment
574
+ * (e.g. `"node"`, `"browser"`, `"expo"`, `"electron-main"`).
575
+ */
184
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
+ */
185
583
  devtools?: DevtoolsSink;
584
+ /** Scheduler configuration for background and recurring jobs. */
186
585
  scheduler?: SchedulerOptions;
187
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
+ */
188
609
  interface PaginationOptions {
610
+ /**
611
+ * The cursor returned by the previous page, or `null` / `undefined` for the
612
+ * first page.
613
+ */
189
614
  cursor?: string | null;
615
+ /** Maximum number of items to return in this page. */
190
616
  numItems: number;
191
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
+ */
192
624
  interface PaginationResult<TItem> {
625
+ /** The items in this page. May be fewer than `numItems` if `isDone` is `true`. */
193
626
  page: TItem[];
627
+ /**
628
+ * Opaque cursor to pass as `PaginationOptions.cursor` in the next call.
629
+ * `null` when `isDone` is `true`.
630
+ */
194
631
  cursor: string | null;
632
+ /** `true` when this is the last page and no more results exist. */
195
633
  isDone: boolean;
196
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
+ */
197
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
+ */
198
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
+ */
199
663
  interface SyncoreRuntimeStatus {
664
+ /** Coarse lifecycle phase. */
200
665
  kind: SyncoreRuntimeStatusKind;
666
+ /** Machine-readable reason for a non-`"ready"` state. */
201
667
  reason?: SyncoreRuntimeStatusReason;
668
+ /** The underlying error when `kind` is `"error"`. */
202
669
  error?: Error;
203
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
+ */
204
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
+ */
205
688
  interface SyncoreQueryState<TData> {
689
+ /** The most recent result from the query, or `undefined` while loading. */
206
690
  data: TData | undefined;
691
+ /** The error thrown by the last execution, or `undefined` on success. */
207
692
  error: Error | undefined;
693
+ /** Fine-grained subscription lifecycle status. */
208
694
  status: SyncoreQueryStatus;
695
+ /** Current lifecycle status of the underlying runtime. */
209
696
  runtimeStatus: SyncoreRuntimeStatus;
697
+ /** `true` while waiting for the first result. Equivalent to `status === "loading"`. */
210
698
  isLoading: boolean;
699
+ /** `true` when the last execution threw an error. */
211
700
  isError: boolean;
701
+ /** `true` when `data` is available and the runtime is ready. */
212
702
  isReady: boolean;
213
703
  }
214
704
  type SyncoreQueryRequest<TReference extends FunctionReference<"query"> = FunctionReference<"query">> = (Record<never, never> extends FunctionArgs<TReference> ? {
@@ -221,23 +711,90 @@ type SyncoreQueryRequest<TReference extends FunctionReference<"query"> = Functio
221
711
  skip?: boolean;
222
712
  };
223
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
+ */
224
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
+ */
225
732
  interface UsePaginatedQueryResult<TItem> {
733
+ /** All items loaded so far, across all fetched pages. */
226
734
  results: TItem[];
735
+ /** Raw page results in order, one entry per fetched page. */
227
736
  pages: PaginationResult<TItem>[];
737
+ /** Current lifecycle phase of the paginated query. */
228
738
  status: SyncorePaginatedQueryStatus;
739
+ /** The error thrown by the last failed page load, or `undefined`. */
229
740
  error: Error | undefined;
741
+ /** `true` while waiting for the first page. */
230
742
  isLoading: boolean;
743
+ /** `true` while a `loadMore()` request is in progress. */
231
744
  isLoadingMore: boolean;
745
+ /** `true` when there is a next page available to load. */
232
746
  hasMore: boolean;
747
+ /** Cursor to pass to the next page request. `null` when `isDone` is `true`. */
233
748
  cursor: string | null;
749
+ /** Current lifecycle status of the underlying runtime. */
234
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
+ */
235
758
  loadMore(numItems?: number): Promise<void> | void;
236
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
+ */
237
782
  interface SyncoreWatch<TValue> {
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
+ */
238
787
  onUpdate(callback: () => void): () => void;
788
+ /** Return the latest available query result, or `undefined` if not yet loaded. */
239
789
  localQueryResult(): TValue | undefined;
790
+ /** Return the error from the last failed execution, or `undefined` on success. */
240
791
  localQueryError(): Error | undefined;
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
+ */
241
798
  dispose?(): void;
242
799
  }
243
800
  interface FilterBuilder {
@@ -269,72 +826,399 @@ type OptionalPropertyNames<TValue> = TValue extends object ? { [TKey in keyof TV
269
826
  type PatchValue<TValue> = TValue extends object ? { [TKey in keyof TValue]?: TKey extends OptionalPropertyNames<TValue> ? TValue[TKey] | undefined : TValue[TKey] } : never;
270
827
  type PatchValueForTable<TSchema extends SyncoreDataModel, TTableName extends TableNames<TSchema>> = PatchValue<InsertValueForTable<TSchema, TTableName>>;
271
828
  type OptionalArgsTuple<TArgs> = Record<never, never> extends TArgs ? [args?: TArgs] : [args: TArgs];
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
+ */
272
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
+ */
273
860
  get<TTableName extends TableNames<TSchema>>(table: TTableName, id: string): Promise<DocumentForTable<TSchema, TTableName> | null>;
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
+ */
274
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
+ */
275
875
  raw<TValue = unknown>(sql: string, params?: unknown[]): Promise<TValue[]>;
276
876
  }
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
+ */
277
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
+ */
278
903
  insert<TTableName extends TableNames<TSchema>>(table: TTableName, value: InsertValueForTable<TSchema, TTableName>): Promise<string>;
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
+ */
279
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
+ */
280
918
  replace<TTableName extends TableNames<TSchema>>(table: TTableName, id: string, value: InsertValueForTable<TSchema, TTableName>): Promise<void>;
919
+ /**
920
+ * Permanently delete the document with the given `id`.
921
+ *
922
+ * A no-op if the document does not exist.
923
+ */
281
924
  delete<TTableName extends TableNames<TSchema>>(table: TTableName, id: string): Promise<void>;
282
925
  }
283
926
  /**
284
- * Storage operations exposed to Syncore functions and clients.
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
+ * ```
285
945
  */
286
946
  interface SyncoreStorageApi {
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
+ */
287
952
  put(input: StorageWriteInput): Promise<string>;
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
+ */
288
957
  get(id: string): Promise<StorageObject | null>;
958
+ /**
959
+ * Return the raw bytes of the stored object, or `null` if it does not exist.
960
+ */
289
961
  read(id: string): Promise<Uint8Array | null>;
962
+ /**
963
+ * Permanently delete the stored object. A no-op if the object does not exist.
964
+ */
290
965
  delete(id: string): Promise<void>;
291
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
+ */
292
990
  interface SchedulerApi {
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
+ */
293
1000
  runAfter<TArgs, TResult>(delayMs: number, functionReference: FunctionReference<"mutation" | "action", TArgs, TResult>, ...args: [...OptionalArgsTuple<TArgs>, misfirePolicy?: MisfirePolicy]): Promise<string>;
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
+ */
294
1011
  runAt<TArgs, TResult>(timestamp: number | Date, functionReference: FunctionReference<"mutation" | "action", TArgs, TResult>, ...args: [...OptionalArgsTuple<TArgs>, misfirePolicy?: MisfirePolicy]): Promise<string>;
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
+ */
295
1017
  cancel(id: string): Promise<void>;
296
1018
  }
297
1019
  /**
298
- * Context available inside Syncore query handlers.
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
+ * ```
299
1045
  */
300
1046
  interface QueryCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> {
1047
+ /** Read-only access to the local SQLite database. */
301
1048
  db: SyncoreDatabaseReader<TSchema>;
1049
+ /** Blob storage access for reading files and images. */
302
1050
  storage: SyncoreStorageApi;
1051
+ /**
1052
+ * Platform capabilities injected at runtime setup (e.g. push notifications,
1053
+ * biometrics). `undefined` when no capabilities were configured.
1054
+ */
303
1055
  capabilities?: Readonly<SyncoreCapabilities>;
1056
+ /** Structured descriptors for the registered capabilities. */
304
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
+ */
305
1062
  component?: {
306
1063
  path: string;
307
1064
  name: string;
308
1065
  version: string;
309
1066
  capabilities: readonly string[];
310
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
+ */
311
1074
  runQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
312
1075
  }
313
1076
  /**
314
- * Context available inside Syncore mutation handlers.
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
+ * ```
315
1104
  */
316
1105
  interface MutationCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> extends QueryCtx<TSchema> {
1106
+ /** Read-write database access. Changes are committed atomically on handler return. */
317
1107
  db: SyncoreDatabaseWriter<TSchema>;
1108
+ /** Schedule mutations and actions to run outside the current transaction. */
318
1109
  scheduler: SchedulerApi;
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
+ */
319
1116
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
1117
+ /**
1118
+ * Launch an action from within a mutation. The action runs asynchronously
1119
+ * in a separate context **after** the mutation commits.
1120
+ */
320
1121
  runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
321
1122
  }
322
1123
  /**
323
- * Context available inside Syncore action handlers.
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
+ * ```
324
1147
  */
325
1148
  interface ActionCtx<TSchema extends SyncoreDataModel = SyncoreDataModel> extends QueryCtx<TSchema> {
1149
+ /** Schedule mutations and actions to run at a later time. */
326
1150
  scheduler: SchedulerApi;
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
+ */
327
1158
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
1159
+ /** Call another action from within this action. */
328
1160
  runAction<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
329
1161
  }
330
1162
  /**
331
- * Client API for invoking Syncore queries, mutations, actions, and watches.
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
+ * ```
332
1187
  */
333
1188
  interface SyncoreClient {
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
+ */
334
1195
  query<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
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
+ */
335
1202
  mutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
1203
+ /**
1204
+ * Execute an action and return its result.
1205
+ *
1206
+ * Actions are non-transactional and may take arbitrarily long to complete.
1207
+ */
336
1208
  action<TArgs, TResult>(reference: FunctionReference<"action", TArgs, TResult>, ...args: OptionalArgsTuple<TArgs>): Promise<TResult>;
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
+ */
337
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
+ */
338
1222
  watchRuntimeStatus(): SyncoreWatch<SyncoreRuntimeStatus>;
339
1223
  }
340
1224
  interface SyncoreRuntimeAdmin<TSchema extends SyncoreDataModel = SyncoreDataModel> {
@@ -359,32 +1243,179 @@ interface SyncoreRuntimeAdmin<TSchema extends SyncoreDataModel = SyncoreDataMode
359
1243
  }
360
1244
  type DevtoolsEventMeta = {
361
1245
  origin?: SyncoreDevtoolsEventOrigin;
1246
+ executionId?: string;
1247
+ parentExecutionId?: string;
1248
+ schedulerJobId?: string;
1249
+ schedulerRun?: boolean;
362
1250
  };
363
1251
  /**
364
- * Chainable query builder returned by `ctx.db.query(...)`.
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
+ * ```
365
1278
  */
366
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
+ */
367
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
+ */
368
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
+ */
369
1304
  order(order: "asc" | "desc"): this;
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
+ */
370
1314
  filter(builder: (filter: FilterBuilder) => QueryExpression): this;
1315
+ /** Execute the query and return all matching documents as an array. */
371
1316
  collect(): Promise<TDocument[]>;
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
+ */
372
1323
  take(count: number): Promise<TDocument[]>;
1324
+ /**
1325
+ * Execute the query and return the first matching document, or `null` if
1326
+ * there are no results.
1327
+ */
373
1328
  first(): Promise<TDocument | null>;
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
+ */
374
1336
  unique(): Promise<TDocument | null>;
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
+ */
375
1344
  paginate(options: PaginationOptions): Promise<PaginationResult<TDocument>>;
376
1345
  }
377
1346
  /**
378
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
+ * ```
379
1375
  */
380
1376
  declare class SyncoreRuntime<TSchema extends SyncoreDataModel> {
381
1377
  private readonly options;
382
1378
  private readonly kernel;
383
1379
  constructor(options: SyncoreRuntimeOptions<TSchema>);
1380
+ /**
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.
1386
+ */
384
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
+ */
385
1394
  prepareForDirectAccess(): Promise<void>;
1395
+ /**
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.
1401
+ */
386
1402
  stop(): Promise<void>;
1403
+ /**
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.
1409
+ */
387
1410
  createClient(): SyncoreClient;
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
+ */
388
1419
  getAdmin(): SyncoreRuntimeAdmin<TSchema>;
389
1420
  runQuery<TArgs, TResult>(reference: FunctionReference<"query", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;
390
1421
  runMutation<TArgs, TResult>(reference: FunctionReference<"mutation", TArgs, TResult>, args?: JsonObject, meta?: DevtoolsEventMeta): Promise<TResult>;