syncorejs 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +330 -46
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/dev-session.mjs.map +1 -1
  8. package/dist/_vendor/cli/doctor.mjs +513 -46
  9. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  10. package/dist/_vendor/cli/errors.mjs.map +1 -1
  11. package/dist/_vendor/cli/help.mjs.map +1 -1
  12. package/dist/_vendor/cli/index.mjs +9 -2
  13. package/dist/_vendor/cli/index.mjs.map +1 -1
  14. package/dist/_vendor/cli/messages.mjs +5 -4
  15. package/dist/_vendor/cli/messages.mjs.map +1 -1
  16. package/dist/_vendor/cli/preflight.mjs.map +1 -1
  17. package/dist/_vendor/cli/project.mjs +125 -27
  18. package/dist/_vendor/cli/project.mjs.map +1 -1
  19. package/dist/_vendor/cli/render.mjs +57 -9
  20. package/dist/_vendor/cli/render.mjs.map +1 -1
  21. package/dist/_vendor/cli/targets.mjs +4 -3
  22. package/dist/_vendor/cli/targets.mjs.map +1 -1
  23. package/dist/_vendor/core/cli.d.mts +20 -4
  24. package/dist/_vendor/core/cli.d.mts.map +1 -1
  25. package/dist/_vendor/core/cli.mjs +458 -133
  26. package/dist/_vendor/core/cli.mjs.map +1 -1
  27. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  28. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  29. package/dist/_vendor/core/index.d.mts +5 -3
  30. package/dist/_vendor/core/index.mjs +22 -2
  31. package/dist/_vendor/core/index.mjs.map +1 -1
  32. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  33. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  34. package/dist/_vendor/core/runtime/components.mjs +186 -0
  35. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  36. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +178 -60
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.d.mts +398 -16
  41. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  42. package/dist/_vendor/core/runtime/functions.mjs +74 -3
  43. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  44. package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
  45. package/dist/_vendor/core/runtime/id.mjs.map +1 -1
  46. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +83 -0
  47. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +720 -0
  49. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +234 -0
  51. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +255 -0
  53. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +200 -0
  55. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +252 -0
  57. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +145 -0
  59. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +221 -0
  61. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  62. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  63. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  64. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  65. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  66. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +41 -0
  67. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  68. package/dist/_vendor/core/runtime/runtime.d.mts +1187 -202
  69. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  70. package/dist/_vendor/core/runtime/runtime.mjs +73 -1365
  71. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  72. package/dist/_vendor/core/transport.d.mts +113 -0
  73. package/dist/_vendor/core/transport.d.mts.map +1 -0
  74. package/dist/_vendor/core/transport.mjs +428 -0
  75. package/dist/_vendor/core/transport.mjs.map +1 -0
  76. package/dist/_vendor/devtools-protocol/index.d.ts +187 -4
  77. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  78. package/dist/_vendor/devtools-protocol/index.js +25 -9
  79. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  80. package/dist/_vendor/next/config.d.ts +3 -4
  81. package/dist/_vendor/next/config.d.ts.map +1 -1
  82. package/dist/_vendor/next/config.js +37 -19
  83. package/dist/_vendor/next/config.js.map +1 -1
  84. package/dist/_vendor/next/index.d.ts +109 -29
  85. package/dist/_vendor/next/index.d.ts.map +1 -1
  86. package/dist/_vendor/next/index.js +104 -26
  87. package/dist/_vendor/next/index.js.map +1 -1
  88. package/dist/_vendor/platform-expo/index.d.ts +156 -37
  89. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  90. package/dist/_vendor/platform-expo/index.js +80 -12
  91. package/dist/_vendor/platform-expo/index.js.map +1 -1
  92. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  93. package/dist/_vendor/platform-expo/react.js +11 -10
  94. package/dist/_vendor/platform-expo/react.js.map +1 -1
  95. package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
  96. package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
  97. package/dist/_vendor/platform-node/index.d.mts +192 -24
  98. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  99. package/dist/_vendor/platform-node/index.mjs +236 -97
  100. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  101. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  102. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  103. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  104. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  105. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  106. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  107. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  108. package/dist/_vendor/platform-web/external-change.d.ts +43 -1
  109. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  110. package/dist/_vendor/platform-web/external-change.js +32 -1
  111. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  112. package/dist/_vendor/platform-web/index.d.ts +323 -51
  113. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  114. package/dist/_vendor/platform-web/index.js +233 -30
  115. package/dist/_vendor/platform-web/index.js.map +1 -1
  116. package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
  117. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
  118. package/dist/_vendor/platform-web/indexeddb.js +10 -0
  119. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  120. package/dist/_vendor/platform-web/opfs.d.ts +13 -0
  121. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  122. package/dist/_vendor/platform-web/opfs.js +12 -0
  123. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  124. package/dist/_vendor/platform-web/persistence.d.ts +54 -0
  125. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  126. package/dist/_vendor/platform-web/persistence.js +15 -0
  127. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  128. package/dist/_vendor/platform-web/react.d.ts +1 -2
  129. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  130. package/dist/_vendor/platform-web/react.js +27 -13
  131. package/dist/_vendor/platform-web/react.js.map +1 -1
  132. package/dist/_vendor/platform-web/sqljs.js +10 -1
  133. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  134. package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
  135. package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
  136. package/dist/_vendor/platform-web/worker.d.ts +71 -44
  137. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  138. package/dist/_vendor/platform-web/worker.js +40 -271
  139. package/dist/_vendor/platform-web/worker.js.map +1 -1
  140. package/dist/_vendor/react/index.d.ts +222 -23
  141. package/dist/_vendor/react/index.d.ts.map +1 -1
  142. package/dist/_vendor/react/index.js +476 -63
  143. package/dist/_vendor/react/index.js.map +1 -1
  144. package/dist/_vendor/schema/definition.d.ts +151 -37
  145. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  146. package/dist/_vendor/schema/definition.js +102 -20
  147. package/dist/_vendor/schema/definition.js.map +1 -1
  148. package/dist/_vendor/schema/index.d.ts +4 -4
  149. package/dist/_vendor/schema/index.js +2 -2
  150. package/dist/_vendor/schema/planner.d.ts +19 -2
  151. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  152. package/dist/_vendor/schema/planner.js +79 -3
  153. package/dist/_vendor/schema/planner.js.map +1 -1
  154. package/dist/_vendor/schema/validators.d.ts +279 -83
  155. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  156. package/dist/_vendor/schema/validators.js +330 -38
  157. package/dist/_vendor/schema/validators.js.map +1 -1
  158. package/dist/_vendor/svelte/index.d.ts +245 -19
  159. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  160. package/dist/_vendor/svelte/index.js +443 -20
  161. package/dist/_vendor/svelte/index.js.map +1 -1
  162. package/dist/browser.d.ts.map +1 -1
  163. package/dist/cli.js +3 -1
  164. package/dist/cli.js.map +1 -1
  165. package/dist/components.d.ts +2 -0
  166. package/dist/components.js +2 -0
  167. package/dist/index.d.ts +3 -2
  168. package/dist/index.js +2 -1
  169. package/package.json +29 -21
@@ -1,1381 +1,105 @@
1
- import { generateId } from "./id.mjs";
2
- import { createSchemaSnapshot, describeValidator, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateSearchIndexStatement, renderMigrationSql, searchIndexTableName } from "../../schema/index.js";
3
- import { fromZonedTime, toZonedTime } from "date-fns-tz";
1
+ import { RuntimeKernel } from "./internal/runtimeKernel.mjs";
4
2
  //#region src/runtime/runtime.ts
5
- const DEFAULT_MISFIRE_POLICY = { type: "catch_up" };
6
- var RuntimeFilterBuilder = class {
7
- eq(field, value) {
8
- return {
9
- type: "condition",
10
- condition: {
11
- field,
12
- operator: "=",
13
- value
14
- }
15
- };
16
- }
17
- gt(field, value) {
18
- return {
19
- type: "condition",
20
- condition: {
21
- field,
22
- operator: ">",
23
- value
24
- }
25
- };
26
- }
27
- gte(field, value) {
28
- return {
29
- type: "condition",
30
- condition: {
31
- field,
32
- operator: ">=",
33
- value
34
- }
35
- };
36
- }
37
- lt(field, value) {
38
- return {
39
- type: "condition",
40
- condition: {
41
- field,
42
- operator: "<",
43
- value
44
- }
45
- };
46
- }
47
- lte(field, value) {
48
- return {
49
- type: "condition",
50
- condition: {
51
- field,
52
- operator: "<=",
53
- value
54
- }
55
- };
56
- }
57
- and(...expressions) {
58
- return {
59
- type: "and",
60
- expressions
61
- };
62
- }
63
- or(...expressions) {
64
- return {
65
- type: "or",
66
- expressions
67
- };
68
- }
69
- };
70
- var RuntimeIndexRangeBuilder = class {
71
- conditions = [];
72
- eq(field, value) {
73
- this.conditions.push({
74
- field,
75
- operator: "=",
76
- value
77
- });
78
- return this;
79
- }
80
- gt(field, value) {
81
- this.conditions.push({
82
- field,
83
- operator: ">",
84
- value
85
- });
86
- return this;
87
- }
88
- gte(field, value) {
89
- this.conditions.push({
90
- field,
91
- operator: ">=",
92
- value
93
- });
94
- return this;
95
- }
96
- lt(field, value) {
97
- this.conditions.push({
98
- field,
99
- operator: "<",
100
- value
101
- });
102
- return this;
103
- }
104
- lte(field, value) {
105
- this.conditions.push({
106
- field,
107
- operator: "<=",
108
- value
109
- });
110
- return this;
111
- }
112
- build() {
113
- return [...this.conditions];
114
- }
115
- };
116
- var RuntimeSearchIndexBuilder = class {
117
- searchField;
118
- searchText;
119
- filters = [];
120
- search(field, value) {
121
- this.searchField = field;
122
- this.searchText = value;
123
- return this;
124
- }
125
- eq(field, value) {
126
- this.filters.push({
127
- field,
128
- operator: "=",
129
- value
130
- });
131
- return this;
132
- }
133
- build() {
134
- if (!this.searchField || !this.searchText) throw new Error("Search queries require a search field and search text.");
135
- return {
136
- searchField: this.searchField,
137
- searchText: this.searchText,
138
- filters: [...this.filters]
139
- };
140
- }
141
- };
142
- var RuntimeQueryBuilder = class {
143
- orderDirection = "asc";
144
- source = { type: "table" };
145
- filterExpression;
146
- constructor(executeQuery, tableName, dependencyCollector) {
147
- this.executeQuery = executeQuery;
148
- this.tableName = tableName;
149
- this.dependencyCollector = dependencyCollector;
150
- }
151
- withIndex(indexName, builder) {
152
- this.source = {
153
- type: "index",
154
- name: indexName,
155
- range: builder?.(new RuntimeIndexRangeBuilder()).build() ?? []
156
- };
157
- return this;
158
- }
159
- withSearchIndex(indexName, builder) {
160
- this.source = {
161
- type: "search",
162
- name: indexName,
163
- query: builder(new RuntimeSearchIndexBuilder()).build()
164
- };
165
- return this;
166
- }
167
- order(order) {
168
- this.orderDirection = order;
169
- return this;
170
- }
171
- filter(builder) {
172
- this.filterExpression = builder(new RuntimeFilterBuilder());
173
- return this;
174
- }
175
- async collect() {
176
- return this.execute();
177
- }
178
- async take(count) {
179
- return this.execute({ limit: count });
180
- }
181
- async first() {
182
- return (await this.execute({ limit: 1 }))[0] ?? null;
183
- }
184
- async unique() {
185
- const results = await this.execute({ limit: 2 });
186
- if (results.length > 1) throw new Error("Expected a unique result but found multiple rows.");
187
- return results[0] ?? null;
188
- }
189
- async paginate(options) {
190
- const offset = options.cursor ? Number.parseInt(options.cursor, 10) : 0;
191
- const page = await this.execute({
192
- limit: options.numItems,
193
- offset
194
- });
195
- const nextCursor = page.length < options.numItems ? null : String(offset + page.length);
196
- return {
197
- page,
198
- cursor: nextCursor,
199
- isDone: nextCursor === null
200
- };
201
- }
202
- async execute(options) {
203
- this.dependencyCollector?.add(`table:${this.tableName}`);
204
- const queryOptions = {
205
- tableName: this.tableName,
206
- source: this.source,
207
- filterExpression: this.filterExpression,
208
- orderDirection: this.orderDirection
209
- };
210
- if (this.dependencyCollector) queryOptions.dependencyCollector = this.dependencyCollector;
211
- if (options?.limit !== void 0) queryOptions.limit = options.limit;
212
- if (options?.offset !== void 0) queryOptions.offset = options.offset;
213
- return this.executeQuery(queryOptions);
214
- }
215
- };
216
3
  /**
217
- * The local Syncore runtime that owns the database, storage, scheduler, and function execution.
4
+ * Local-first Syncore runtime that hosts your schema, functions, and storage.
5
+ *
6
+ * `SyncoreRuntime` is the central engine of every Syncore app. It owns the
7
+ * SQLite driver, the storage adapter, the reactivity engine, and the
8
+ * background scheduler. Platform-specific factory functions
9
+ * (`createNodeSyncoreRuntime`, `createWebSyncoreRuntime`, etc.) wrap it with
10
+ * environment-appropriate defaults so you rarely need to instantiate it
11
+ * directly.
12
+ *
13
+ * **Lifecycle**
14
+ * 1. Construct the runtime (schema migration is deferred until first use).
15
+ * 2. Call `await runtime.start()` to apply the schema, start the scheduler,
16
+ * and connect to devtools. The runtime emits `"runtime.connected"` when ready.
17
+ * 3. Call `runtime.createClient()` to get a {@link SyncoreClient} for
18
+ * invoking functions and subscribing to reactive queries.
19
+ * 4. Call `await runtime.stop()` on shutdown to flush pending jobs and close
20
+ * the database.
21
+ *
22
+ * ```ts
23
+ * const runtime = new SyncoreRuntime({
24
+ * schema,
25
+ * functions,
26
+ * driver: new NodeSqliteDriver("./db.sqlite"),
27
+ * storage: new NodeFileStorageAdapter("./storage"),
28
+ * });
29
+ * await runtime.start();
30
+ * const client = runtime.createClient();
31
+ * ```
218
32
  */
219
33
  var SyncoreRuntime = class {
220
- runtimeId = generateId();
221
- platform;
222
- capabilities;
223
- experimentalPlugins;
224
- activeQueries = /* @__PURE__ */ new Map();
225
- disabledSearchIndexes = /* @__PURE__ */ new Set();
226
- recentEvents = [];
227
- devtoolsListeners = /* @__PURE__ */ new Set();
228
- devtoolsInvalidationListeners = /* @__PURE__ */ new Set();
229
- externalChangeSourceId = generateId();
230
- detachExternalChangeListener;
231
- pendingExternalChangePromise;
232
- queuedExternalChange;
233
- schedulerTimer;
234
- recurringJobs;
235
- schedulerPollIntervalMs;
236
- driverDatabasePath;
237
- prepared = false;
238
- started = false;
34
+ options;
35
+ kernel;
239
36
  constructor(options) {
240
37
  this.options = options;
241
- this.platform = options.platform ?? "node";
242
- this.experimentalPlugins = options.experimentalPlugins ?? [];
243
- this.recurringJobs = options.scheduler?.recurringJobs ?? [];
244
- this.schedulerPollIntervalMs = options.scheduler?.pollIntervalMs ?? 1e3;
245
- this.capabilities = Object.freeze(this.buildCapabilities());
246
- this.driverDatabasePath = inferDriverDatabasePath(options.driver);
247
- this.options.devtools?.attachRuntime?.(this);
38
+ this.kernel = new RuntimeKernel(options, this);
248
39
  }
249
40
  /**
250
- * Start the local Syncore runtime.
41
+ * Start the runtime: apply the schema migration, initialise the scheduler,
42
+ * and connect to devtools (if configured).
43
+ *
44
+ * Must be called and awaited before using the client. Calling `start()`
45
+ * a second time is a no-op.
251
46
  */
252
47
  async start() {
253
- if (this.started) return;
254
- await this.prepareForDirectAccess();
255
- await this.runPluginHook("onStart");
256
- this.detachExternalChangeListener = this.options.externalChangeSignal?.subscribe((event) => {
257
- this.handleExternalChangeEvent(event);
258
- });
259
- this.schedulerTimer = setInterval(() => {
260
- this.processDueJobs();
261
- }, this.schedulerPollIntervalMs);
262
- this.started = true;
263
- this.emitDevtools({
264
- type: "runtime.connected",
265
- runtimeId: this.runtimeId,
266
- platform: this.platform,
267
- timestamp: Date.now()
268
- });
48
+ await this.kernel.start();
269
49
  }
50
+ /**
51
+ * Prepare the runtime for direct (synchronous) access patterns used by
52
+ * devtools and migration tooling.
53
+ *
54
+ * You do not need to call this in normal application code.
55
+ */
270
56
  async prepareForDirectAccess() {
271
- if (this.prepared) return;
272
- await this.ensureSystemTables();
273
- await this.reconcileStorageState();
274
- await this.applySchema();
275
- await this.syncRecurringJobs();
276
- this.prepared = true;
57
+ await this.kernel.prepareForDirectAccess();
277
58
  }
278
59
  /**
279
- * Stop the local Syncore runtime and release any open resources.
60
+ * Stop the runtime gracefully.
61
+ *
62
+ * Flushes any pending scheduler jobs, closes the SQLite driver, and
63
+ * disconnects from devtools. Call this when your application is shutting
64
+ * down to avoid database file corruption.
280
65
  */
281
66
  async stop() {
282
- if (this.schedulerTimer) {
283
- clearInterval(this.schedulerTimer);
284
- this.schedulerTimer = void 0;
285
- }
286
- if (this.started) await this.runPluginHook("onStop");
287
- this.detachExternalChangeListener?.();
288
- this.detachExternalChangeListener = void 0;
289
- await this.options.driver.close?.();
290
- if (this.started) this.emitDevtools({
291
- type: "runtime.disconnected",
292
- runtimeId: this.runtimeId,
293
- timestamp: Date.now()
294
- });
295
- this.started = false;
67
+ await this.kernel.stop();
296
68
  }
297
69
  /**
298
- * Create a typed client for calling this runtime from the same process.
70
+ * Create a new {@link SyncoreClient} bound to this runtime.
71
+ *
72
+ * Multiple clients can be created from the same runtime — they all share
73
+ * the same underlying database and reactivity engine. Usually you only need
74
+ * one client per runtime instance.
299
75
  */
300
76
  createClient() {
301
- return {
302
- query: (reference, ...args) => this.runQuery(reference, normalizeOptionalArgs(args)),
303
- mutation: (reference, ...args) => this.runMutation(reference, normalizeOptionalArgs(args)),
304
- action: (reference, ...args) => this.runAction(reference, normalizeOptionalArgs(args)),
305
- watchQuery: (reference, ...args) => this.watchQuery(reference, normalizeOptionalArgs(args))
306
- };
307
- }
308
- async getDevtoolsLiveQuerySnapshot() {
309
- return {
310
- summary: this.getRuntimeSummary(),
311
- activeQueries: this.getActiveQueryInfos(),
312
- schemaTables: await this.getSchemaTablesForDevtools()
313
- };
314
- }
315
- getRuntimeSummary() {
316
- return {
317
- runtimeId: this.runtimeId,
318
- platform: this.platform,
319
- connectedAt: Date.now(),
320
- activeQueryCount: this.activeQueries.size,
321
- recentEventCount: this.recentEvents.length
322
- };
323
- }
324
- getActiveQueryInfos() {
325
- return [...this.activeQueries.values()].map((query) => ({
326
- id: query.id,
327
- functionName: query.functionName,
328
- dependencyKeys: [...query.dependencyKeys],
329
- lastRunAt: query.lastRunAt
330
- }));
331
- }
332
- getDriverDatabasePath() {
333
- return this.driverDatabasePath;
334
- }
335
- async cancelScheduledJob(id) {
336
- await this.prepareForDirectAccess();
337
- if (((await this.options.driver.run(`UPDATE "_scheduled_functions"
338
- SET status = 'cancelled', updated_at = ?
339
- WHERE id = ? AND status = 'scheduled'`, [Date.now(), id])).changes ?? 0) > 0) {
340
- this.notifySchedulerJobsChanged();
341
- return true;
342
- }
343
- return false;
344
- }
345
- async updateScheduledJob(options) {
346
- await this.prepareForDirectAccess();
347
- const existing = await this.options.driver.get(`SELECT status, recurring_name FROM "_scheduled_functions" WHERE id = ?`, [options.id]);
348
- if (!existing || existing.status !== "scheduled" || !existing.recurring_name) return false;
349
- const now = Date.now();
350
- const runAt = options.runAt ?? computeNextRun(options.schedule, now);
351
- if (((await this.options.driver.run(`UPDATE "_scheduled_functions"
352
- SET args_json = ?, run_at = ?, updated_at = ?, schedule_json = ?, timezone = ?, misfire_policy = ?, window_ms = ?
353
- WHERE id = ? AND status = 'scheduled' AND recurring_name IS NOT NULL`, [
354
- stableStringify(options.args),
355
- runAt,
356
- now,
357
- stableStringify(options.schedule),
358
- "timezone" in options.schedule ? options.schedule.timezone ?? null : null,
359
- options.misfirePolicy.type,
360
- options.misfirePolicy.type === "windowed" ? options.misfirePolicy.windowMs : null,
361
- options.id
362
- ])).changes ?? 0) > 0) {
363
- this.notifySchedulerJobsChanged();
364
- return true;
365
- }
366
- return false;
77
+ return this.kernel.createClient();
367
78
  }
368
- subscribeToDevtoolsEvents(listener) {
369
- this.devtoolsListeners.add(listener);
370
- return () => {
371
- this.devtoolsListeners.delete(listener);
372
- };
373
- }
374
- subscribeToDevtoolsInvalidations(listener) {
375
- this.devtoolsInvalidationListeners.add(listener);
376
- return () => {
377
- this.devtoolsInvalidationListeners.delete(listener);
378
- };
379
- }
380
- notifyDevtoolsScopes(scopes) {
381
- const scopeSet = new Set(scopes);
382
- if (scopeSet.size === 0) return;
383
- for (const listener of this.devtoolsInvalidationListeners) listener(scopeSet);
384
- }
385
- async runDevtoolsMutation(callback, meta = {}) {
386
- const mutationId = generateId();
387
- const startedAt = Date.now();
388
- const changedTables = /* @__PURE__ */ new Set();
389
- const storageChanges = [];
390
- const result = await this.options.driver.withTransaction(async () => callback({ db: this.createDatabaseWriter({
391
- mutationDepth: 1,
392
- changedTables,
393
- storageChanges
394
- }) }));
395
- await this.refreshInvalidatedQueries(changedTables, mutationId);
396
- if (storageChanges.length > 0) await this.refreshAllActiveQueries();
397
- if (changedTables.size > 0) await this.publishExternalChange({
398
- scope: "database",
399
- reason: "commit",
400
- changedTables: [...changedTables]
401
- });
402
- await this.publishStorageChanges(storageChanges);
403
- this.emitDevtools({
404
- type: "mutation.committed",
405
- runtimeId: this.runtimeId,
406
- mutationId,
407
- functionName: "__devtools__/mutation",
408
- changedTables: [...changedTables],
409
- durationMs: Date.now() - startedAt,
410
- timestamp: Date.now(),
411
- ...meta.origin ? { origin: meta.origin } : {}
412
- });
413
- return result;
414
- }
415
- async forceRefreshDevtools(reason, meta = {}) {
416
- await this.refreshAllActiveQueries();
417
- await this.publishExternalChange({
418
- scope: "database",
419
- reason: "reconcile"
420
- });
421
- this.notifyDevtoolsScopes(["all"]);
422
- this.emitDevtools({
423
- type: "log",
424
- runtimeId: this.runtimeId,
425
- level: "info",
426
- message: reason,
427
- timestamp: Date.now(),
428
- ...meta.origin ? { origin: meta.origin } : {}
429
- });
430
- }
431
- getRuntimeId() {
432
- return this.runtimeId;
79
+ /**
80
+ * Return the low-level admin API.
81
+ *
82
+ * The admin API exposes devtools introspection, direct query/mutation
83
+ * execution, and scheduler management. It is used by the devtools
84
+ * dashboard and integration tooling — most application code should use
85
+ * the regular `SyncoreClient` instead.
86
+ */
87
+ getAdmin() {
88
+ return this.kernel.admin;
433
89
  }
434
90
  async runQuery(reference, args = {}, meta = {}) {
435
- const definition = this.resolveFunction(reference, "query");
436
- const dependencyCollector = /* @__PURE__ */ new Set();
437
- const startedAt = Date.now();
438
- const result = await this.invokeFunction(definition, args, {
439
- mutationDepth: 0,
440
- changedTables: /* @__PURE__ */ new Set(),
441
- storageChanges: [],
442
- dependencyCollector
443
- });
444
- this.emitDevtools({
445
- type: "query.executed",
446
- runtimeId: this.runtimeId,
447
- queryId: reference.name,
448
- functionName: reference.name,
449
- dependencies: [...dependencyCollector],
450
- durationMs: Date.now() - startedAt,
451
- timestamp: Date.now(),
452
- ...meta.origin ? { origin: meta.origin } : {}
453
- });
454
- return result;
91
+ return this.kernel.executionEngine.runQuery(reference, args, meta);
455
92
  }
456
93
  async runMutation(reference, args = {}, meta = {}) {
457
- const definition = this.resolveFunction(reference, "mutation");
458
- const mutationId = generateId();
459
- const startedAt = Date.now();
460
- const changedTables = /* @__PURE__ */ new Set();
461
- const storageChanges = [];
462
- const result = await this.options.driver.withTransaction(async () => this.invokeFunction(definition, args, {
463
- mutationDepth: 1,
464
- changedTables,
465
- storageChanges
466
- }));
467
- await this.refreshInvalidatedQueries(changedTables, mutationId);
468
- if (storageChanges.length > 0) await this.refreshAllActiveQueries();
469
- if (changedTables.size > 0) await this.publishExternalChange({
470
- scope: "database",
471
- reason: "commit",
472
- changedTables: [...changedTables]
473
- });
474
- await this.publishStorageChanges(storageChanges);
475
- this.emitDevtools({
476
- type: "mutation.committed",
477
- runtimeId: this.runtimeId,
478
- mutationId,
479
- functionName: reference.name,
480
- changedTables: [...changedTables],
481
- durationMs: Date.now() - startedAt,
482
- timestamp: Date.now(),
483
- ...meta.origin ? { origin: meta.origin } : {}
484
- });
485
- return result;
94
+ return this.kernel.executionEngine.runMutation(reference, args, meta);
486
95
  }
487
96
  async runAction(reference, args = {}, meta = {}) {
488
- const definition = this.resolveFunction(reference, "action");
489
- const actionId = generateId();
490
- const startedAt = Date.now();
491
- try {
492
- const result = await this.invokeFunction(definition, args, {
493
- mutationDepth: 0,
494
- changedTables: /* @__PURE__ */ new Set(),
495
- storageChanges: []
496
- });
497
- this.emitDevtools({
498
- type: "action.completed",
499
- runtimeId: this.runtimeId,
500
- actionId,
501
- functionName: reference.name,
502
- durationMs: Date.now() - startedAt,
503
- timestamp: Date.now(),
504
- ...meta.origin ? { origin: meta.origin } : {}
505
- });
506
- return result;
507
- } catch (error) {
508
- this.emitDevtools({
509
- type: "action.completed",
510
- runtimeId: this.runtimeId,
511
- actionId,
512
- functionName: reference.name,
513
- durationMs: Date.now() - startedAt,
514
- timestamp: Date.now(),
515
- ...meta.origin ? { origin: meta.origin } : {},
516
- error: error instanceof Error ? error.message : String(error)
517
- });
518
- throw error;
519
- }
97
+ return this.kernel.executionEngine.runAction(reference, args, meta);
520
98
  }
521
99
  watchQuery(reference, args = {}) {
522
- const key = this.createActiveQueryKey(reference.name, args);
523
- let record = this.activeQueries.get(key);
524
- if (!record) {
525
- record = {
526
- id: key,
527
- functionName: reference.name,
528
- args,
529
- listeners: /* @__PURE__ */ new Set(),
530
- consumers: 0,
531
- dependencyKeys: /* @__PURE__ */ new Set(),
532
- lastResult: void 0,
533
- lastError: void 0,
534
- lastRunAt: 0
535
- };
536
- this.activeQueries.set(key, record);
537
- this.rerunActiveQuery(record);
538
- }
539
- const activeRecord = record;
540
- activeRecord.consumers += 1;
541
- let disposed = false;
542
- const ownedListeners = /* @__PURE__ */ new Set();
543
- return {
544
- onUpdate: (callback) => {
545
- activeRecord.listeners.add(callback);
546
- ownedListeners.add(callback);
547
- queueMicrotask(callback);
548
- return () => {
549
- activeRecord.listeners.delete(callback);
550
- ownedListeners.delete(callback);
551
- };
552
- },
553
- localQueryResult: () => activeRecord.lastResult,
554
- localQueryError: () => activeRecord.lastError,
555
- dispose: () => {
556
- if (disposed) return;
557
- disposed = true;
558
- for (const callback of ownedListeners) activeRecord.listeners.delete(callback);
559
- ownedListeners.clear();
560
- activeRecord.consumers = Math.max(0, activeRecord.consumers - 1);
561
- if (activeRecord.consumers === 0) this.activeQueries.delete(key);
562
- }
563
- };
564
- }
565
- async executeQueryBuilder(options) {
566
- const table = this.options.schema.getTable(options.tableName);
567
- const params = [];
568
- const whereClauses = [];
569
- const orderClauses = [];
570
- let joinClause = "";
571
- const source = options.source;
572
- if (source.type === "index") {
573
- const index = table.indexes.find((candidate) => candidate.name === source.name);
574
- if (!index) throw new Error(`Unknown index "${source.name}" on table "${options.tableName}".`);
575
- for (const condition of source.range) whereClauses.push(this.renderCondition("t", condition, params));
576
- const primaryField = index.fields[0];
577
- if (primaryField) orderClauses.push(`${fieldExpression("t", primaryField)} ${options.orderDirection.toUpperCase()}`);
578
- }
579
- if (source.type === "search") {
580
- const searchIndex = table.searchIndexes.find((candidate) => candidate.name === source.name);
581
- if (!searchIndex) throw new Error(`Unknown search index "${source.name}" on table "${options.tableName}".`);
582
- if (searchIndex.searchField !== source.query.searchField) throw new Error(`Search index "${searchIndex.name}" expects field "${searchIndex.searchField}".`);
583
- const searchIndexKey = `${options.tableName}:${searchIndex.name}`;
584
- if (this.disabledSearchIndexes.has(searchIndexKey)) {
585
- whereClauses.push(`${fieldExpression("t", searchIndex.searchField)} LIKE ?`);
586
- params.push(`%${source.query.searchText}%`);
587
- } else {
588
- joinClause = `JOIN ${quoteIdentifier(searchIndexTableName(options.tableName, searchIndex.name))} s ON s._id = t._id`;
589
- whereClauses.push(`s.search_value MATCH ?`);
590
- params.push(source.query.searchText);
591
- }
592
- for (const condition of source.query.filters) whereClauses.push(this.renderCondition("t", condition, params));
593
- }
594
- if (options.filterExpression) whereClauses.push(this.renderExpression("t", options.filterExpression, params));
595
- if (orderClauses.length === 0) orderClauses.push(`t._creationTime ${options.orderDirection.toUpperCase()}`);
596
- orderClauses.push(`t._id ${options.orderDirection.toUpperCase()}`);
597
- const sql = [
598
- `SELECT t._id, t._creationTime, t._json FROM ${quoteIdentifier(options.tableName)} t`,
599
- joinClause,
600
- whereClauses.length > 0 ? `WHERE ${whereClauses.join(" AND ")}` : "",
601
- `ORDER BY ${orderClauses.join(", ")}`,
602
- options.limit !== void 0 ? `LIMIT ${options.limit}` : "",
603
- options.offset !== void 0 ? `OFFSET ${options.offset}` : ""
604
- ].filter(Boolean).join(" ");
605
- return (await this.options.driver.all(sql, params)).map((row) => this.deserializeDocument(options.tableName, row));
606
- }
607
- async invokeFunction(definition, rawArgs, state) {
608
- const args = definition.argsValidator.parse(rawArgs);
609
- const ctx = this.createContext(definition.kind, state);
610
- const result = await definition.handler(ctx, args);
611
- if (definition.returnsValidator) return definition.returnsValidator.parse(result);
612
- return result;
613
- }
614
- createContext(kind, state) {
615
- const db = kind === "mutation" ? this.createDatabaseWriter(state) : this.createDatabaseReader(state);
616
- const storage = this.createStorageApi(state);
617
- const scheduler = this.createSchedulerApi();
618
- return {
619
- db,
620
- storage,
621
- capabilities: this.capabilities,
622
- scheduler,
623
- runQuery: (reference, ...args) => this.runQuery(reference, normalizeOptionalArgs(args)),
624
- runMutation: (reference, ...args) => {
625
- const normalizedArgs = normalizeOptionalArgs(args);
626
- if (kind === "mutation") return this.options.driver.withSavepoint(`sp_${generateId().replace(/-/g, "_")}`, () => this.invokeFunction(this.resolveFunction(reference, "mutation"), normalizedArgs, {
627
- mutationDepth: state.mutationDepth + 1,
628
- changedTables: state.changedTables,
629
- storageChanges: state.storageChanges
630
- }));
631
- return this.runMutation(reference, normalizedArgs);
632
- },
633
- runAction: (reference, ...args) => this.runAction(reference, normalizeOptionalArgs(args))
634
- };
635
- }
636
- createDatabaseReader(state) {
637
- return {
638
- get: async (tableName, id) => {
639
- state.dependencyCollector?.add(`table:${tableName}`);
640
- state.dependencyCollector?.add(`row:${tableName}:${id}`);
641
- const row = await this.options.driver.get(`SELECT _id, _creationTime, _json FROM ${quoteIdentifier(tableName)} WHERE _id = ?`, [id]);
642
- return row ? this.deserializeDocument(tableName, row) : null;
643
- },
644
- query: (tableName) => new RuntimeQueryBuilder((options) => this.executeQueryBuilder(options), tableName, state.dependencyCollector),
645
- raw: (sql, params) => this.options.driver.all(sql, params)
646
- };
647
- }
648
- createDatabaseWriter(state) {
649
- const reader = this.createDatabaseReader(state);
650
- return {
651
- ...reader,
652
- insert: async (tableName, value) => {
653
- const validated = this.validateDocument(tableName, value);
654
- const id = generateId();
655
- const creationTime = Date.now();
656
- const json = stableStringify(validated);
657
- await this.options.driver.run(`INSERT INTO ${quoteIdentifier(tableName)} (_id, _creationTime, _json) VALUES (?, ?, ?)`, [
658
- id,
659
- creationTime,
660
- json
661
- ]);
662
- await this.syncSearchIndexes(tableName, {
663
- _id: id,
664
- _creationTime: creationTime,
665
- _json: json
666
- });
667
- state.changedTables.add(tableName);
668
- return id;
669
- },
670
- patch: async (tableName, id, value) => {
671
- const current = await reader.get(tableName, id);
672
- if (!current) throw new Error(`Document "${id}" does not exist in "${tableName}".`);
673
- const merged = {
674
- ...omitSystemFields(current),
675
- ...value
676
- };
677
- for (const key of Object.keys(merged)) if (merged[key] === void 0) delete merged[key];
678
- const validated = this.validateDocument(tableName, merged);
679
- await this.options.driver.run(`UPDATE ${quoteIdentifier(tableName)} SET _json = ? WHERE _id = ?`, [stableStringify(validated), id]);
680
- const row = await this.options.driver.get(`SELECT _id, _creationTime, _json FROM ${quoteIdentifier(tableName)} WHERE _id = ?`, [id]);
681
- if (row) await this.syncSearchIndexes(tableName, row);
682
- state.changedTables.add(tableName);
683
- },
684
- replace: async (tableName, id, value) => {
685
- const validated = this.validateDocument(tableName, value);
686
- await this.options.driver.run(`UPDATE ${quoteIdentifier(tableName)} SET _json = ? WHERE _id = ?`, [stableStringify(validated), id]);
687
- const row = await this.options.driver.get(`SELECT _id, _creationTime, _json FROM ${quoteIdentifier(tableName)} WHERE _id = ?`, [id]);
688
- if (!row) throw new Error(`Document "${id}" does not exist in "${tableName}".`);
689
- await this.syncSearchIndexes(tableName, row);
690
- state.changedTables.add(tableName);
691
- },
692
- delete: async (tableName, id) => {
693
- await this.options.driver.run(`DELETE FROM ${quoteIdentifier(tableName)} WHERE _id = ?`, [id]);
694
- await this.removeSearchIndexes(tableName, id);
695
- state.changedTables.add(tableName);
696
- }
697
- };
698
- }
699
- createStorageApi(state) {
700
- return {
701
- put: async (input) => {
702
- const id = generateId();
703
- const createdAt = Date.now();
704
- await this.options.driver.run(`INSERT OR REPLACE INTO "_storage_pending" (_id, _creationTime, file_name, content_type) VALUES (?, ?, ?, ?)`, [
705
- id,
706
- createdAt,
707
- input.fileName ?? null,
708
- input.contentType ?? null
709
- ]);
710
- const object = await this.options.storage.put(id, input);
711
- await this.options.driver.withTransaction(async () => {
712
- await this.options.driver.run(`INSERT OR REPLACE INTO "_storage" (_id, _creationTime, file_name, content_type, size, path) VALUES (?, ?, ?, ?, ?, ?)`, [
713
- id,
714
- createdAt,
715
- input.fileName ?? null,
716
- object.contentType,
717
- object.size,
718
- object.path
719
- ]);
720
- await this.options.driver.run(`DELETE FROM "_storage_pending" WHERE _id = ?`, [id]);
721
- });
722
- this.emitDevtools({
723
- type: "storage.updated",
724
- runtimeId: this.runtimeId,
725
- storageId: id,
726
- operation: "put",
727
- timestamp: Date.now()
728
- });
729
- state.storageChanges.push({
730
- storageId: id,
731
- reason: "storage-put"
732
- });
733
- return id;
734
- },
735
- get: async (id) => {
736
- const row = await this.options.driver.get(`SELECT _id, _creationTime, file_name, content_type, size, path FROM "_storage" WHERE _id = ?`, [id]);
737
- if (!row) return null;
738
- return {
739
- id: row._id,
740
- path: row.path,
741
- size: row.size,
742
- contentType: row.content_type
743
- };
744
- },
745
- read: async (id) => {
746
- if (!await this.options.driver.get(`SELECT _id FROM "_storage" WHERE _id = ?`, [id])) return null;
747
- return this.options.storage.read(id);
748
- },
749
- delete: async (id) => {
750
- await this.options.storage.delete(id);
751
- await this.options.driver.withTransaction(async () => {
752
- await this.options.driver.run(`DELETE FROM "_storage" WHERE _id = ?`, [id]);
753
- await this.options.driver.run(`DELETE FROM "_storage_pending" WHERE _id = ?`, [id]);
754
- });
755
- this.emitDevtools({
756
- type: "storage.updated",
757
- runtimeId: this.runtimeId,
758
- storageId: id,
759
- operation: "delete",
760
- timestamp: Date.now()
761
- });
762
- state.storageChanges.push({
763
- storageId: id,
764
- reason: "storage-delete"
765
- });
766
- }
767
- };
768
- }
769
- createSchedulerApi() {
770
- return {
771
- runAfter: async (delayMs, reference, ...args) => {
772
- const schedulerArgs = splitSchedulerArgs(args);
773
- const functionArgs = schedulerArgs[0];
774
- const misfirePolicy = schedulerArgs[1] ?? DEFAULT_MISFIRE_POLICY;
775
- return this.scheduleJob(Date.now() + delayMs, reference, functionArgs, misfirePolicy);
776
- },
777
- runAt: async (timestamp, reference, ...args) => {
778
- const schedulerArgs = splitSchedulerArgs(args);
779
- const functionArgs = schedulerArgs[0];
780
- const misfirePolicy = schedulerArgs[1] ?? DEFAULT_MISFIRE_POLICY;
781
- const value = timestamp instanceof Date ? timestamp.getTime() : timestamp;
782
- return this.scheduleJob(value, reference, functionArgs, misfirePolicy);
783
- },
784
- cancel: async (id) => {
785
- await this.cancelScheduledJob(id);
786
- }
787
- };
788
- }
789
- notifySchedulerJobsChanged() {
790
- this.notifyDevtoolsScopes(["scheduler.jobs"]);
791
- }
792
- async ensureSystemTables() {
793
- await this.options.driver.exec(`
794
- CREATE TABLE IF NOT EXISTS "_syncore_migrations" (
795
- id TEXT PRIMARY KEY,
796
- applied_at INTEGER NOT NULL,
797
- sql TEXT NOT NULL
798
- );
799
- CREATE TABLE IF NOT EXISTS "_syncore_schema_state" (
800
- id TEXT PRIMARY KEY,
801
- schema_hash TEXT NOT NULL,
802
- schema_json TEXT NOT NULL,
803
- updated_at INTEGER NOT NULL
804
- );
805
- CREATE TABLE IF NOT EXISTS "_storage" (
806
- _id TEXT PRIMARY KEY,
807
- _creationTime INTEGER NOT NULL,
808
- file_name TEXT,
809
- content_type TEXT,
810
- size INTEGER NOT NULL,
811
- path TEXT NOT NULL
812
- );
813
- CREATE TABLE IF NOT EXISTS "_storage_pending" (
814
- _id TEXT PRIMARY KEY,
815
- _creationTime INTEGER NOT NULL,
816
- file_name TEXT,
817
- content_type TEXT
818
- );
819
- CREATE TABLE IF NOT EXISTS "_scheduled_functions" (
820
- id TEXT PRIMARY KEY,
821
- function_name TEXT NOT NULL,
822
- function_kind TEXT NOT NULL,
823
- args_json TEXT NOT NULL,
824
- status TEXT NOT NULL,
825
- run_at INTEGER NOT NULL,
826
- created_at INTEGER NOT NULL,
827
- updated_at INTEGER NOT NULL,
828
- recurring_name TEXT,
829
- schedule_json TEXT,
830
- timezone TEXT,
831
- misfire_policy TEXT NOT NULL,
832
- last_run_at INTEGER,
833
- window_ms INTEGER
834
- );
835
- `);
836
- try {
837
- await this.options.driver.exec(`ALTER TABLE "_syncore_schema_state" ADD COLUMN schema_json TEXT NOT NULL DEFAULT '{}'`);
838
- } catch {}
839
- }
840
- async reconcileStorageState() {
841
- const pendingRows = await this.options.driver.all(`SELECT _id, _creationTime, file_name, content_type FROM "_storage_pending"`);
842
- for (const pendingRow of pendingRows) {
843
- if (!await this.options.driver.get(`SELECT _id FROM "_storage" WHERE _id = ?`, [pendingRow._id])) {
844
- await this.options.storage.delete(pendingRow._id);
845
- this.emitDevtools({
846
- type: "log",
847
- runtimeId: this.runtimeId,
848
- level: "warn",
849
- message: `Recovered interrupted storage write ${pendingRow._id}.`,
850
- timestamp: Date.now()
851
- });
852
- }
853
- await this.options.driver.run(`DELETE FROM "_storage_pending" WHERE _id = ?`, [pendingRow._id]);
854
- }
855
- if (!this.options.storage.list) return;
856
- const storedRows = await this.options.driver.all(`SELECT _id FROM "_storage"`);
857
- const knownIds = new Set(storedRows.map((row) => row._id));
858
- const physicalObjects = await this.options.storage.list();
859
- for (const object of physicalObjects) {
860
- if (knownIds.has(object.id)) continue;
861
- await this.options.storage.delete(object.id);
862
- this.emitDevtools({
863
- type: "log",
864
- runtimeId: this.runtimeId,
865
- level: "warn",
866
- message: `Removed orphaned storage object ${object.id}.`,
867
- timestamp: Date.now()
868
- });
869
- }
870
- }
871
- async applySchema() {
872
- const nextSnapshot = createSchemaSnapshot(this.options.schema);
873
- const stateRow = await this.options.driver.get(`SELECT schema_hash, schema_json FROM "_syncore_schema_state" WHERE id = 'current'`);
874
- let previousSnapshot = null;
875
- if (stateRow?.schema_json && stateRow.schema_json !== "{}") try {
876
- previousSnapshot = parseSchemaSnapshot(stateRow.schema_json);
877
- } catch {
878
- previousSnapshot = null;
879
- }
880
- const plan = diffSchemaSnapshots(previousSnapshot, nextSnapshot);
881
- if (plan.destructiveChanges.length > 0) throw new Error(`Syncore detected destructive schema changes that require a manual migration:\n${plan.destructiveChanges.join("\n")}`);
882
- for (const warning of plan.warnings) this.emitDevtools({
883
- type: "log",
884
- runtimeId: this.runtimeId,
885
- level: "warn",
886
- message: warning,
887
- timestamp: Date.now()
888
- });
889
- for (const statement of plan.statements) {
890
- const searchKey = this.findSearchIndexKeyForStatement(statement);
891
- try {
892
- await this.options.driver.exec(statement);
893
- } catch (error) {
894
- if (searchKey) {
895
- this.disabledSearchIndexes.add(searchKey);
896
- this.emitDevtools({
897
- type: "log",
898
- runtimeId: this.runtimeId,
899
- level: "warn",
900
- message: `FTS5 unavailable for ${searchKey}; falling back to LIKE search.`,
901
- timestamp: Date.now()
902
- });
903
- continue;
904
- }
905
- throw error;
906
- }
907
- }
908
- if (plan.statements.length > 0 || plan.warnings.length > 0) {
909
- const migrationSql = renderMigrationSql(plan, { title: "Syncore automatic schema reconciliation" });
910
- await this.options.driver.run(`INSERT OR REPLACE INTO "_syncore_migrations" (id, applied_at, sql) VALUES (?, ?, ?)`, [
911
- nextSnapshot.hash,
912
- Date.now(),
913
- migrationSql
914
- ]);
915
- }
916
- await this.options.driver.run(`INSERT INTO "_syncore_schema_state" (id, schema_hash, schema_json, updated_at)
917
- VALUES ('current', ?, ?, ?)
918
- ON CONFLICT(id) DO UPDATE SET schema_hash = excluded.schema_hash, schema_json = excluded.schema_json, updated_at = excluded.updated_at`, [
919
- nextSnapshot.hash,
920
- stableStringify(nextSnapshot),
921
- Date.now()
922
- ]);
923
- for (const tableName of this.options.schema.tableNames()) {
924
- const table = this.getTableDefinition(tableName);
925
- for (const searchIndex of table.searchIndexes) {
926
- const searchKey = `${tableName}:${searchIndex.name}`;
927
- try {
928
- await this.options.driver.exec(renderCreateSearchIndexStatement(tableName, searchIndex));
929
- this.disabledSearchIndexes.delete(searchKey);
930
- } catch {
931
- const alreadyDisabled = this.disabledSearchIndexes.has(searchKey);
932
- this.disabledSearchIndexes.add(searchKey);
933
- if (!alreadyDisabled) this.emitDevtools({
934
- type: "log",
935
- runtimeId: this.runtimeId,
936
- level: "warn",
937
- message: `FTS5 unavailable for ${searchKey}; falling back to LIKE search.`,
938
- timestamp: Date.now()
939
- });
940
- }
941
- }
942
- }
943
- }
944
- async scheduleJob(runAt, reference, args, misfirePolicy) {
945
- const id = generateId();
946
- const now = Date.now();
947
- await this.options.driver.run(`INSERT INTO "_scheduled_functions"
948
- (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)
949
- VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, NULL, NULL, NULL, ?, NULL, ?)`, [
950
- id,
951
- reference.name,
952
- reference.kind,
953
- stableStringify(args),
954
- runAt,
955
- now,
956
- now,
957
- misfirePolicy.type,
958
- misfirePolicy.type === "windowed" ? misfirePolicy.windowMs : null
959
- ]);
960
- this.notifySchedulerJobsChanged();
961
- return id;
962
- }
963
- async syncRecurringJobs() {
964
- for (const job of this.recurringJobs) {
965
- const id = `recurring:${job.name}`;
966
- if (await this.options.driver.get(`SELECT * FROM "_scheduled_functions" WHERE id = ?`, [id])) continue;
967
- const nextRunAt = computeNextRun(job.schedule, Date.now());
968
- await this.options.driver.run(`INSERT INTO "_scheduled_functions"
969
- (id, function_name, function_kind, args_json, status, run_at, created_at, updated_at, recurring_name, schedule_json, timezone, misfire_policy, last_run_at, window_ms)
970
- VALUES (?, ?, ?, ?, 'scheduled', ?, ?, ?, ?, ?, ?, ?, NULL, ?)`, [
971
- id,
972
- job.function.name,
973
- job.function.kind,
974
- stableStringify(job.args),
975
- nextRunAt,
976
- Date.now(),
977
- Date.now(),
978
- job.name,
979
- stableStringify(job.schedule),
980
- "timezone" in job.schedule ? job.schedule.timezone ?? null : null,
981
- job.misfirePolicy.type,
982
- job.misfirePolicy.type === "windowed" ? job.misfirePolicy.windowMs : null
983
- ]);
984
- this.notifySchedulerJobsChanged();
985
- }
986
- }
987
- async processDueJobs() {
988
- const now = Date.now();
989
- const dueJobs = await this.options.driver.all(`SELECT * FROM "_scheduled_functions" WHERE status = 'scheduled' AND run_at <= ? ORDER BY run_at ASC`, [now]);
990
- const executedJobIds = [];
991
- for (const job of dueJobs) {
992
- const misfirePolicy = parseMisfirePolicy(job.misfire_policy, job.window_ms);
993
- if (!shouldRunMissedJob(job.run_at, now, misfirePolicy)) {
994
- await this.advanceOrFinalizeJob(job, "skipped", now);
995
- continue;
996
- }
997
- try {
998
- if (job.function_kind === "mutation") await this.runMutation({
999
- kind: "mutation",
1000
- name: job.function_name
1001
- }, JSON.parse(job.args_json));
1002
- else await this.runAction({
1003
- kind: "action",
1004
- name: job.function_name
1005
- }, JSON.parse(job.args_json));
1006
- executedJobIds.push(job.id);
1007
- await this.advanceOrFinalizeJob(job, "completed", now);
1008
- } catch (error) {
1009
- await this.options.driver.run(`UPDATE "_scheduled_functions" SET status = 'failed', updated_at = ? WHERE id = ?`, [Date.now(), job.id]);
1010
- this.notifySchedulerJobsChanged();
1011
- this.emitDevtools({
1012
- type: "log",
1013
- runtimeId: this.runtimeId,
1014
- level: "error",
1015
- message: `Scheduled job ${job.id} failed: ${error instanceof Error ? error.message : String(error)}`,
1016
- timestamp: Date.now()
1017
- });
1018
- }
1019
- }
1020
- if (executedJobIds.length > 0) {
1021
- this.emitDevtools({
1022
- type: "scheduler.tick",
1023
- runtimeId: this.runtimeId,
1024
- executedJobIds,
1025
- timestamp: Date.now()
1026
- });
1027
- this.notifySchedulerJobsChanged();
1028
- }
1029
- }
1030
- async advanceOrFinalizeJob(job, terminalStatus, executedAt) {
1031
- if (!job.recurring_name || !job.schedule_json) {
1032
- await this.options.driver.run(`UPDATE "_scheduled_functions" SET status = ?, updated_at = ?, last_run_at = ? WHERE id = ?`, [
1033
- terminalStatus,
1034
- executedAt,
1035
- executedAt,
1036
- job.id
1037
- ]);
1038
- this.notifySchedulerJobsChanged();
1039
- return;
1040
- }
1041
- const nextRunAt = computeNextRun(JSON.parse(job.schedule_json), executedAt + 1);
1042
- await this.options.driver.run(`UPDATE "_scheduled_functions"
1043
- SET status = 'scheduled', run_at = ?, updated_at = ?, last_run_at = ?
1044
- WHERE id = ?`, [
1045
- nextRunAt,
1046
- executedAt,
1047
- executedAt,
1048
- job.id
1049
- ]);
1050
- this.notifySchedulerJobsChanged();
1051
- }
1052
- async refreshInvalidatedQueries(changedTables, mutationId) {
1053
- for (const query of this.activeQueries.values()) {
1054
- if (![...changedTables].some((tableName) => query.dependencyKeys.has(`table:${tableName}`))) continue;
1055
- this.emitDevtools({
1056
- type: "query.invalidated",
1057
- runtimeId: this.runtimeId,
1058
- queryId: query.id,
1059
- reason: `Mutation ${mutationId} changed ${[...changedTables].join(", ")}`,
1060
- timestamp: Date.now()
1061
- });
1062
- await this.rerunActiveQuery(query);
1063
- }
1064
- }
1065
- async rerunActiveQuery(record) {
1066
- record.dependencyKeys.clear();
1067
- try {
1068
- record.lastResult = await this.runQuery({
1069
- kind: "query",
1070
- name: record.functionName
1071
- }, record.args);
1072
- record.lastError = void 0;
1073
- record.lastRunAt = Date.now();
1074
- record.dependencyKeys = await this.collectQueryDependencies(record.functionName, record.args);
1075
- } catch (error) {
1076
- record.lastError = error;
1077
- }
1078
- for (const listener of record.listeners) listener();
1079
- }
1080
- async handleExternalChangeEvent(event) {
1081
- if (event.sourceId === this.externalChangeSourceId) return;
1082
- const result = this.options.externalChangeApplier ? await this.options.externalChangeApplier.applyExternalChange(event) : {
1083
- databaseChanged: event.scope === "database" || event.scope === "all",
1084
- storageChanged: event.scope === "storage" || event.scope === "all"
1085
- };
1086
- await this.processExternalChangeResult(result);
1087
- }
1088
- async processExternalChangeResult(result) {
1089
- if (!result.databaseChanged && !result.storageChanged) return;
1090
- if (this.pendingExternalChangePromise) {
1091
- this.queuedExternalChange = {
1092
- databaseChanged: (this.queuedExternalChange?.databaseChanged ?? false) || result.databaseChanged,
1093
- storageChanged: (this.queuedExternalChange?.storageChanged ?? false) || result.storageChanged
1094
- };
1095
- return this.pendingExternalChangePromise;
1096
- }
1097
- this.pendingExternalChangePromise = (async () => {
1098
- if (result.databaseChanged || result.storageChanged) await this.refreshAllActiveQueries();
1099
- })();
1100
- try {
1101
- await this.pendingExternalChangePromise;
1102
- } finally {
1103
- this.pendingExternalChangePromise = void 0;
1104
- const queued = this.queuedExternalChange;
1105
- this.queuedExternalChange = void 0;
1106
- if (queued) await this.processExternalChangeResult(queued);
1107
- }
1108
- }
1109
- async publishExternalChange(event) {
1110
- await this.options.externalChangeSignal?.publish({
1111
- ...event,
1112
- sourceId: this.externalChangeSourceId,
1113
- timestamp: Date.now()
1114
- });
1115
- }
1116
- async publishStorageChanges(storageChanges) {
1117
- for (const change of storageChanges) await this.publishExternalChange({
1118
- scope: "storage",
1119
- reason: change.reason,
1120
- storageIds: [change.storageId]
1121
- });
1122
- }
1123
- async refreshAllActiveQueries() {
1124
- for (const query of this.activeQueries.values()) await this.rerunActiveQuery(query);
1125
- }
1126
- async getSchemaTablesForDevtools() {
1127
- const tables = [];
1128
- for (const name of this.options.schema.tableNames()) {
1129
- const table = this.getTableDefinition(name);
1130
- const validatorDesc = describeValidator(table.validator);
1131
- const fields = validatorDesc.kind === "object" ? Object.entries(validatorDesc.shape).map(([fieldName, fieldDesc]) => {
1132
- const desc = fieldDesc;
1133
- const optional = desc.kind === "optional";
1134
- return {
1135
- name: fieldName,
1136
- type: optional ? desc.inner?.kind ?? "any" : desc.kind,
1137
- optional
1138
- };
1139
- }) : [];
1140
- fields.unshift({
1141
- name: "_id",
1142
- type: "string",
1143
- optional: false
1144
- }, {
1145
- name: "_creationTime",
1146
- type: "number",
1147
- optional: false
1148
- });
1149
- let documentCount = 0;
1150
- try {
1151
- documentCount = (await this.options.driver.get(`SELECT COUNT(*) as count FROM ${quoteIdentifier(name)}`))?.count ?? 0;
1152
- } catch {
1153
- documentCount = 0;
1154
- }
1155
- tables.push({
1156
- name,
1157
- fields,
1158
- indexes: table.indexes.map((index) => ({
1159
- name: index.name,
1160
- fields: index.fields,
1161
- unique: false
1162
- })),
1163
- documentCount
1164
- });
1165
- }
1166
- return tables;
1167
- }
1168
- async collectQueryDependencies(functionName, args) {
1169
- const definition = this.resolveFunction({
1170
- kind: "query",
1171
- name: functionName
1172
- }, "query");
1173
- const dependencyCollector = /* @__PURE__ */ new Set();
1174
- await this.invokeFunction(definition, args, {
1175
- mutationDepth: 0,
1176
- changedTables: /* @__PURE__ */ new Set(),
1177
- storageChanges: [],
1178
- dependencyCollector
1179
- });
1180
- return dependencyCollector;
1181
- }
1182
- resolveFunction(reference, expectedKind) {
1183
- const definition = this.options.functions[reference.name];
1184
- if (!definition) throw new Error(`Unknown function "${reference.name}".`);
1185
- if (definition.kind !== expectedKind) throw new Error(`Function "${reference.name}" is a ${definition.kind}, expected ${expectedKind}.`);
1186
- return definition;
1187
- }
1188
- validateDocument(tableName, value) {
1189
- return this.getTableDefinition(tableName).validator.parse(value);
1190
- }
1191
- deserializeDocument(tableName, row) {
1192
- const payload = JSON.parse(row._json);
1193
- const document = {
1194
- ...payload,
1195
- _id: row._id,
1196
- _creationTime: row._creationTime
1197
- };
1198
- this.getTableDefinition(tableName).validator.parse(payload);
1199
- return document;
1200
- }
1201
- async syncSearchIndexes(tableName, row) {
1202
- const table = this.getTableDefinition(tableName);
1203
- if (table.searchIndexes.length === 0) return;
1204
- const payload = JSON.parse(row._json);
1205
- for (const searchIndex of table.searchIndexes) {
1206
- if (this.disabledSearchIndexes.has(`${tableName}:${searchIndex.name}`)) continue;
1207
- await this.options.driver.run(`DELETE FROM ${quoteIdentifier(searchIndexTableName(tableName, searchIndex.name))} WHERE _id = ?`, [row._id]);
1208
- await this.options.driver.run(`INSERT INTO ${quoteIdentifier(searchIndexTableName(tableName, searchIndex.name))} (_id, search_value) VALUES (?, ?)`, [row._id, toSearchValue(payload[searchIndex.searchField])]);
1209
- }
1210
- }
1211
- async removeSearchIndexes(tableName, id) {
1212
- const table = this.getTableDefinition(tableName);
1213
- for (const searchIndex of table.searchIndexes) {
1214
- if (this.disabledSearchIndexes.has(`${tableName}:${searchIndex.name}`)) continue;
1215
- await this.options.driver.run(`DELETE FROM ${quoteIdentifier(searchIndexTableName(tableName, searchIndex.name))} WHERE _id = ?`, [id]);
1216
- }
1217
- }
1218
- renderExpression(tableAlias, expression, params) {
1219
- if (expression.type === "condition") return this.renderCondition(tableAlias, expression.condition, params);
1220
- const separator = expression.type === "and" ? " AND " : " OR ";
1221
- return `(${expression.expressions.map((child) => this.renderExpression(tableAlias, child, params)).join(separator)})`;
1222
- }
1223
- renderCondition(tableAlias, condition, params) {
1224
- params.push(condition.value);
1225
- return `${fieldExpression(tableAlias, condition.field)} ${condition.operator} ?`;
1226
- }
1227
- createActiveQueryKey(name, args) {
1228
- return `${name}:${stableStringify(args)}`;
1229
- }
1230
- emitDevtools(event) {
1231
- this.recentEvents.unshift(event);
1232
- this.recentEvents.splice(24);
1233
- this.options.devtools?.emit(event);
1234
- this.notifyDevtoolsScopes(devtoolsScopesForEvent(event));
1235
- for (const listener of this.devtoolsListeners) listener(event);
1236
- }
1237
- createPluginContext() {
1238
- return {
1239
- runtimeId: this.runtimeId,
1240
- platform: this.platform,
1241
- schema: this.options.schema,
1242
- driver: this.options.driver,
1243
- storage: this.options.storage,
1244
- ...this.options.scheduler ? { scheduler: this.options.scheduler } : {},
1245
- ...this.options.devtools ? { devtools: this.options.devtools } : {},
1246
- emitDevtools: (event) => {
1247
- this.emitDevtools(event);
1248
- }
1249
- };
1250
- }
1251
- buildCapabilities() {
1252
- const capabilities = { ...this.options.capabilities ?? {} };
1253
- for (const plugin of this.experimentalPlugins) {
1254
- if (!plugin.capabilities) continue;
1255
- const contributed = typeof plugin.capabilities === "function" ? plugin.capabilities(this.createPluginContext()) : plugin.capabilities;
1256
- if (!contributed) continue;
1257
- Object.assign(capabilities, contributed);
1258
- }
1259
- return capabilities;
1260
- }
1261
- async runPluginHook(hook) {
1262
- const context = this.createPluginContext();
1263
- for (const plugin of this.experimentalPlugins) {
1264
- const handler = plugin[hook];
1265
- if (!handler) continue;
1266
- await handler(context);
1267
- }
1268
- }
1269
- findSearchIndexKeyForStatement(statement) {
1270
- for (const tableName of this.options.schema.tableNames()) {
1271
- const table = this.getTableDefinition(tableName);
1272
- for (const searchIndex of table.searchIndexes) if (statement === renderCreateSearchIndexStatement(tableName, searchIndex)) return `${tableName}:${searchIndex.name}`;
1273
- }
1274
- return null;
1275
- }
1276
- getTableDefinition(tableName) {
1277
- return this.options.schema.getTable(tableName);
100
+ return this.kernel.watchQuery(reference, args);
1278
101
  }
1279
102
  };
1280
- function fieldExpression(tableAlias, field) {
1281
- return `json_extract(${tableAlias ? `${tableAlias}.` : ""}_json, '$.${field}')`;
1282
- }
1283
- function quoteIdentifier(identifier) {
1284
- return `"${identifier.replaceAll("\"", "\"\"")}"`;
1285
- }
1286
- function stableStringify(value) {
1287
- return JSON.stringify(sortValue(value));
1288
- }
1289
- function sortValue(value) {
1290
- if (Array.isArray(value)) return value.map(sortValue);
1291
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
1292
- return value;
1293
- }
1294
- function devtoolsScopesForEvent(event) {
1295
- switch (event.type) {
1296
- case "runtime.connected":
1297
- case "runtime.disconnected": return new Set(["runtime.summary", "runtime.activeQueries"]);
1298
- case "query.executed":
1299
- case "query.invalidated": return new Set(["runtime.summary", "runtime.activeQueries"]);
1300
- case "mutation.committed": return new Set(["runtime.summary", ...event.changedTables.map((table) => `table:${table}`)]);
1301
- case "scheduler.tick": return new Set(["scheduler.jobs", "runtime.summary"]);
1302
- case "storage.updated": return new Set(["runtime.summary"]);
1303
- case "action.completed":
1304
- case "log": return new Set(["runtime.summary"]);
1305
- }
1306
- }
1307
- function inferDriverDatabasePath(driver) {
1308
- const candidate = driver;
1309
- return candidate.databasePath ?? candidate.filename;
1310
- }
1311
- function omitSystemFields(document) {
1312
- const clone = { ...document };
1313
- delete clone._id;
1314
- delete clone._creationTime;
1315
- return clone;
1316
- }
1317
- function toSearchValue(value) {
1318
- if (typeof value === "string") return value;
1319
- if (value === null || value === void 0) return "";
1320
- if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
1321
- return stableStringify(value);
1322
- }
1323
- function parseMisfirePolicy(type, windowMs) {
1324
- if (type === "windowed") return {
1325
- type,
1326
- windowMs: windowMs ?? 0
1327
- };
1328
- if (type === "skip" || type === "run_once_if_missed") return { type };
1329
- return { type: "catch_up" };
1330
- }
1331
- function shouldRunMissedJob(scheduledAt, now, policy) {
1332
- if (scheduledAt >= now) return true;
1333
- switch (policy.type) {
1334
- case "catch_up": return true;
1335
- case "run_once_if_missed": return true;
1336
- case "skip": return false;
1337
- case "windowed": return now - scheduledAt <= policy.windowMs;
1338
- }
1339
- }
1340
- function computeNextRun(schedule, fromTimestamp) {
1341
- switch (schedule.type) {
1342
- case "interval": return fromTimestamp + intervalToMs(schedule);
1343
- case "daily": return nextDailyOccurrence(fromTimestamp, schedule);
1344
- case "weekly": return nextWeeklyOccurrence(fromTimestamp, schedule);
1345
- }
1346
- }
1347
- function intervalToMs(schedule) {
1348
- if (schedule.seconds) return schedule.seconds * 1e3;
1349
- if (schedule.minutes) return schedule.minutes * 60 * 1e3;
1350
- return (schedule.hours ?? 1) * 60 * 60 * 1e3;
1351
- }
1352
- function nextDailyOccurrence(fromTimestamp, schedule) {
1353
- const timezone = schedule.timezone ?? "UTC";
1354
- const zonedNow = toZonedTime(new Date(fromTimestamp), timezone);
1355
- const zoned = new Date(zonedNow.getTime());
1356
- zoned.setHours(schedule.hour, schedule.minute, 0, 0);
1357
- if (zoned.getTime() <= zonedNow.getTime()) zoned.setDate(zoned.getDate() + 1);
1358
- return fromZonedTime(zoned, timezone).getTime();
1359
- }
1360
- function nextWeeklyOccurrence(fromTimestamp, schedule) {
1361
- const timezone = schedule.timezone ?? "UTC";
1362
- const zonedNow = toZonedTime(new Date(fromTimestamp), timezone);
1363
- const targetDay = [
1364
- "sunday",
1365
- "monday",
1366
- "tuesday",
1367
- "wednesday",
1368
- "thursday",
1369
- "friday",
1370
- "saturday"
1371
- ].indexOf(schedule.dayOfWeek);
1372
- const zoned = new Date(zonedNow.getTime());
1373
- const delta = (targetDay - zonedNow.getDay() + 7) % 7;
1374
- zoned.setDate(zoned.getDate() + delta);
1375
- zoned.setHours(schedule.hour, schedule.minute, 0, 0);
1376
- if (zoned.getTime() <= zonedNow.getTime()) zoned.setDate(zoned.getDate() + 7);
1377
- return fromZonedTime(zoned, timezone).getTime();
1378
- }
1379
103
  function createFunctionReference(kind, name) {
1380
104
  return {
1381
105
  kind,
@@ -1383,9 +107,8 @@ function createFunctionReference(kind, name) {
1383
107
  };
1384
108
  }
1385
109
  /**
1386
- * Create a typed function reference from a concrete Syncore function definition.
1387
- *
1388
- * Generated code uses this helper to preserve function arg and result inference.
110
+ * Create a function reference from an existing Syncore function definition
111
+ * while preserving its inferred args and result types.
1389
112
  */
1390
113
  function createFunctionReferenceFor(kind, name) {
1391
114
  return {
@@ -1393,21 +116,6 @@ function createFunctionReferenceFor(kind, name) {
1393
116
  name
1394
117
  };
1395
118
  }
1396
- function normalizeOptionalArgs(args) {
1397
- return args[0] ?? {};
1398
- }
1399
- function splitSchedulerArgs(args) {
1400
- if (args.length === 0) return [{}, void 0];
1401
- if (args.length === 1) {
1402
- const [first] = args;
1403
- if (isMisfirePolicy(first)) return [{}, first];
1404
- return [first ?? {}, void 0];
1405
- }
1406
- return [args[0] ?? {}, args[1]];
1407
- }
1408
- function isMisfirePolicy(value) {
1409
- return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
1410
- }
1411
119
  //#endregion
1412
120
  export { SyncoreRuntime, createFunctionReference, createFunctionReferenceFor };
1413
121