syncorejs 0.2.2 → 0.2.4

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