syncorejs 0.1.0 → 0.2.0

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 (200) hide show
  1. package/dist/{core/src/cli.d.ts → _vendor/cli/app.d.mts} +4 -2
  2. package/dist/_vendor/cli/app.d.mts.map +1 -0
  3. package/dist/_vendor/cli/app.mjs +997 -0
  4. package/dist/_vendor/cli/app.mjs.map +1 -0
  5. package/dist/_vendor/cli/context.mjs +180 -0
  6. package/dist/_vendor/cli/context.mjs.map +1 -0
  7. package/dist/_vendor/cli/dev-session.mjs +49 -0
  8. package/dist/_vendor/cli/dev-session.mjs.map +1 -0
  9. package/dist/_vendor/cli/doctor.mjs +80 -0
  10. package/dist/_vendor/cli/doctor.mjs.map +1 -0
  11. package/dist/_vendor/cli/errors.mjs +22 -0
  12. package/dist/_vendor/cli/errors.mjs.map +1 -0
  13. package/dist/_vendor/cli/help.mjs +26 -0
  14. package/dist/_vendor/cli/help.mjs.map +1 -0
  15. package/dist/_vendor/cli/index.d.mts +2 -0
  16. package/dist/_vendor/cli/index.mjs +23 -0
  17. package/dist/_vendor/cli/index.mjs.map +1 -0
  18. package/dist/_vendor/cli/messages.mjs +32 -0
  19. package/dist/_vendor/cli/messages.mjs.map +1 -0
  20. package/dist/_vendor/cli/preflight.mjs +35 -0
  21. package/dist/_vendor/cli/preflight.mjs.map +1 -0
  22. package/dist/_vendor/cli/project.mjs +583 -0
  23. package/dist/_vendor/cli/project.mjs.map +1 -0
  24. package/dist/_vendor/cli/render.mjs +133 -0
  25. package/dist/_vendor/cli/render.mjs.map +1 -0
  26. package/dist/_vendor/cli/targets.mjs +87 -0
  27. package/dist/_vendor/cli/targets.mjs.map +1 -0
  28. package/dist/_vendor/core/cli.d.mts +59 -1
  29. package/dist/_vendor/core/cli.d.mts.map +1 -1
  30. package/dist/_vendor/core/cli.mjs +528 -75
  31. package/dist/_vendor/core/cli.mjs.map +1 -1
  32. package/dist/_vendor/core/index.d.mts +12 -4
  33. package/dist/_vendor/core/index.d.mts.map +1 -0
  34. package/dist/_vendor/core/index.mjs +4 -3
  35. package/dist/_vendor/core/index.mjs.map +1 -1
  36. package/dist/_vendor/core/runtime/devtools.d.mts +32 -6
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +397 -182
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  41. package/dist/_vendor/core/runtime/runtime.d.mts +89 -7
  42. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  43. package/dist/_vendor/core/runtime/runtime.mjs +303 -32
  44. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  45. package/dist/_vendor/devtools-protocol/index.d.ts +189 -82
  46. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  47. package/dist/_vendor/devtools-protocol/index.js +39 -0
  48. package/dist/_vendor/devtools-protocol/index.js.map +1 -0
  49. package/dist/_vendor/next/config.d.ts.map +1 -1
  50. package/dist/_vendor/next/config.js +2 -5
  51. package/dist/_vendor/next/config.js.map +1 -1
  52. package/dist/_vendor/platform-expo/index.d.ts +15 -5
  53. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  54. package/dist/_vendor/platform-expo/index.js +33 -3
  55. package/dist/_vendor/platform-expo/index.js.map +1 -1
  56. package/dist/_vendor/platform-expo/react.js.map +1 -1
  57. package/dist/_vendor/platform-node/index.d.mts +10 -5
  58. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  59. package/dist/_vendor/platform-node/index.mjs +145 -35
  60. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  61. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  62. package/dist/_vendor/platform-web/external-change.d.ts +39 -0
  63. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -0
  64. package/dist/_vendor/platform-web/external-change.js +61 -0
  65. package/dist/_vendor/platform-web/external-change.js.map +1 -0
  66. package/dist/_vendor/platform-web/index.d.ts +27 -5
  67. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  68. package/dist/_vendor/platform-web/index.js +310 -44
  69. package/dist/_vendor/platform-web/index.js.map +1 -1
  70. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  71. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  72. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  73. package/dist/_vendor/platform-web/react.js.map +1 -1
  74. package/dist/_vendor/platform-web/sqljs.js +22 -2
  75. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  76. package/dist/_vendor/schema/definition.js.map +1 -1
  77. package/dist/_vendor/schema/planner.js.map +1 -1
  78. package/dist/_vendor/schema/validators.js.map +1 -1
  79. package/dist/browser-react.d.ts +1 -1
  80. package/dist/browser-react.js +1 -1
  81. package/dist/browser.d.ts +6 -7
  82. package/dist/browser.d.ts.map +1 -1
  83. package/dist/browser.js +4 -5
  84. package/dist/browser.js.map +1 -1
  85. package/dist/cli.d.ts +1 -1
  86. package/dist/cli.js +12 -3
  87. package/dist/cli.js.map +1 -1
  88. package/dist/expo-react.d.ts +1 -1
  89. package/dist/expo-react.js +1 -1
  90. package/dist/expo.d.ts +1 -2
  91. package/dist/expo.js +1 -2
  92. package/dist/index.d.ts +3 -7
  93. package/dist/index.js +3 -8
  94. package/dist/next-config.d.ts +1 -2
  95. package/dist/next-config.js +1 -2
  96. package/dist/next.d.ts +1 -3
  97. package/dist/next.js +1 -3
  98. package/dist/node-ipc-react.d.ts +1 -1
  99. package/dist/node-ipc-react.js +1 -1
  100. package/dist/node-ipc.d.ts +1 -2
  101. package/dist/node-ipc.js +1 -2
  102. package/dist/node.d.ts +1 -4
  103. package/dist/node.js +1 -3
  104. package/dist/react.d.ts +1 -2
  105. package/dist/react.js +1 -2
  106. package/dist/svelte.d.ts +1 -2
  107. package/dist/svelte.js +1 -2
  108. package/package.json +6 -3
  109. package/dist/core/src/cli.d.ts.map +0 -1
  110. package/dist/core/src/cli.js +0 -1196
  111. package/dist/core/src/cli.js.map +0 -1
  112. package/dist/core/src/index.js +0 -7
  113. package/dist/core/src/runtime/devtools.d.ts +0 -7
  114. package/dist/core/src/runtime/devtools.d.ts.map +0 -1
  115. package/dist/core/src/runtime/devtools.js +0 -300
  116. package/dist/core/src/runtime/devtools.js.map +0 -1
  117. package/dist/core/src/runtime/functions.d.ts +0 -123
  118. package/dist/core/src/runtime/functions.d.ts.map +0 -1
  119. package/dist/core/src/runtime/functions.js +0 -71
  120. package/dist/core/src/runtime/functions.js.map +0 -1
  121. package/dist/core/src/runtime/id.d.ts +0 -13
  122. package/dist/core/src/runtime/id.d.ts.map +0 -1
  123. package/dist/core/src/runtime/id.js +0 -28
  124. package/dist/core/src/runtime/id.js.map +0 -1
  125. package/dist/core/src/runtime/runtime.d.ts +0 -371
  126. package/dist/core/src/runtime/runtime.d.ts.map +0 -1
  127. package/dist/core/src/runtime/runtime.js +0 -1143
  128. package/dist/core/src/runtime/runtime.js.map +0 -1
  129. package/dist/devtools-protocol/src/index.d.ts +0 -201
  130. package/dist/devtools-protocol/src/index.d.ts.map +0 -1
  131. package/dist/next/src/config.d.ts +0 -17
  132. package/dist/next/src/config.d.ts.map +0 -1
  133. package/dist/next/src/config.js +0 -73
  134. package/dist/next/src/config.js.map +0 -1
  135. package/dist/next/src/index.d.ts +0 -80
  136. package/dist/next/src/index.d.ts.map +0 -1
  137. package/dist/next/src/index.js +0 -82
  138. package/dist/next/src/index.js.map +0 -1
  139. package/dist/platform-expo/src/index.d.ts +0 -96
  140. package/dist/platform-expo/src/index.d.ts.map +0 -1
  141. package/dist/platform-expo/src/index.js +0 -198
  142. package/dist/platform-expo/src/index.js.map +0 -1
  143. package/dist/platform-expo/src/react.d.ts +0 -26
  144. package/dist/platform-expo/src/react.d.ts.map +0 -1
  145. package/dist/platform-expo/src/react.js +0 -30
  146. package/dist/platform-expo/src/react.js.map +0 -1
  147. package/dist/platform-node/src/index.d.ts +0 -145
  148. package/dist/platform-node/src/index.d.ts.map +0 -1
  149. package/dist/platform-node/src/index.js +0 -407
  150. package/dist/platform-node/src/index.js.map +0 -1
  151. package/dist/platform-node/src/ipc-react.d.ts +0 -25
  152. package/dist/platform-node/src/ipc-react.d.ts.map +0 -1
  153. package/dist/platform-node/src/ipc-react.js +0 -21
  154. package/dist/platform-node/src/ipc-react.js.map +0 -1
  155. package/dist/platform-node/src/ipc.d.ts +0 -76
  156. package/dist/platform-node/src/ipc.d.ts.map +0 -1
  157. package/dist/platform-node/src/ipc.js +0 -344
  158. package/dist/platform-node/src/ipc.js.map +0 -1
  159. package/dist/platform-web/src/index.d.ts +0 -106
  160. package/dist/platform-web/src/index.d.ts.map +0 -1
  161. package/dist/platform-web/src/index.js +0 -311
  162. package/dist/platform-web/src/index.js.map +0 -1
  163. package/dist/platform-web/src/indexeddb.js +0 -125
  164. package/dist/platform-web/src/indexeddb.js.map +0 -1
  165. package/dist/platform-web/src/opfs.js +0 -146
  166. package/dist/platform-web/src/opfs.js.map +0 -1
  167. package/dist/platform-web/src/persistence.d.ts +0 -20
  168. package/dist/platform-web/src/persistence.d.ts.map +0 -1
  169. package/dist/platform-web/src/persistence.js +0 -23
  170. package/dist/platform-web/src/persistence.js.map +0 -1
  171. package/dist/platform-web/src/react.d.ts +0 -35
  172. package/dist/platform-web/src/react.d.ts.map +0 -1
  173. package/dist/platform-web/src/react.js +0 -42
  174. package/dist/platform-web/src/react.js.map +0 -1
  175. package/dist/platform-web/src/sqljs.js +0 -133
  176. package/dist/platform-web/src/sqljs.js.map +0 -1
  177. package/dist/platform-web/src/worker.d.ts +0 -79
  178. package/dist/platform-web/src/worker.d.ts.map +0 -1
  179. package/dist/platform-web/src/worker.js +0 -308
  180. package/dist/platform-web/src/worker.js.map +0 -1
  181. package/dist/react/src/index.d.ts +0 -59
  182. package/dist/react/src/index.d.ts.map +0 -1
  183. package/dist/react/src/index.js +0 -151
  184. package/dist/react/src/index.js.map +0 -1
  185. package/dist/schema/src/definition.d.ts +0 -98
  186. package/dist/schema/src/definition.d.ts.map +0 -1
  187. package/dist/schema/src/definition.js +0 -84
  188. package/dist/schema/src/definition.js.map +0 -1
  189. package/dist/schema/src/planner.d.ts +0 -42
  190. package/dist/schema/src/planner.d.ts.map +0 -1
  191. package/dist/schema/src/planner.js +0 -131
  192. package/dist/schema/src/planner.js.map +0 -1
  193. package/dist/schema/src/validators.d.ts +0 -194
  194. package/dist/schema/src/validators.d.ts.map +0 -1
  195. package/dist/schema/src/validators.js +0 -158
  196. package/dist/schema/src/validators.js.map +0 -1
  197. package/dist/svelte/src/index.d.ts +0 -44
  198. package/dist/svelte/src/index.d.ts.map +0 -1
  199. package/dist/svelte/src/index.js +0 -75
  200. package/dist/svelte/src/index.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { generateId } from "./id.mjs";
2
- import { createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateSearchIndexStatement, renderMigrationSql, searchIndexTableName } from "../../schema/index.js";
2
+ import { createSchemaSnapshot, describeValidator, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateSearchIndexStatement, renderMigrationSql, searchIndexTableName } from "../../schema/index.js";
3
3
  import { fromZonedTime, toZonedTime } from "date-fns-tz";
4
4
  //#region src/runtime/runtime.ts
5
5
  const DEFAULT_MISFIRE_POLICY = { type: "catch_up" };
@@ -224,9 +224,17 @@ var SyncoreRuntime = class {
224
224
  activeQueries = /* @__PURE__ */ new Map();
225
225
  disabledSearchIndexes = /* @__PURE__ */ new Set();
226
226
  recentEvents = [];
227
+ devtoolsListeners = /* @__PURE__ */ new Set();
228
+ devtoolsInvalidationListeners = /* @__PURE__ */ new Set();
229
+ externalChangeSourceId = generateId();
230
+ detachExternalChangeListener;
231
+ pendingExternalChangePromise;
232
+ queuedExternalChange;
227
233
  schedulerTimer;
228
234
  recurringJobs;
229
235
  schedulerPollIntervalMs;
236
+ driverDatabasePath;
237
+ prepared = false;
230
238
  started = false;
231
239
  constructor(options) {
232
240
  this.options = options;
@@ -235,17 +243,19 @@ var SyncoreRuntime = class {
235
243
  this.recurringJobs = options.scheduler?.recurringJobs ?? [];
236
244
  this.schedulerPollIntervalMs = options.scheduler?.pollIntervalMs ?? 1e3;
237
245
  this.capabilities = Object.freeze(this.buildCapabilities());
246
+ this.driverDatabasePath = inferDriverDatabasePath(options.driver);
247
+ this.options.devtools?.attachRuntime?.(this);
238
248
  }
239
249
  /**
240
250
  * Start the local Syncore runtime.
241
251
  */
242
252
  async start() {
243
253
  if (this.started) return;
244
- await this.ensureSystemTables();
245
- await this.reconcileStorageState();
246
- await this.applySchema();
247
- await this.syncRecurringJobs();
254
+ await this.prepareForDirectAccess();
248
255
  await this.runPluginHook("onStart");
256
+ this.detachExternalChangeListener = this.options.externalChangeSignal?.subscribe((event) => {
257
+ this.handleExternalChangeEvent(event);
258
+ });
249
259
  this.schedulerTimer = setInterval(() => {
250
260
  this.processDueJobs();
251
261
  }, this.schedulerPollIntervalMs);
@@ -257,6 +267,14 @@ var SyncoreRuntime = class {
257
267
  timestamp: Date.now()
258
268
  });
259
269
  }
270
+ 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;
277
+ }
260
278
  /**
261
279
  * Stop the local Syncore runtime and release any open resources.
262
280
  */
@@ -266,6 +284,8 @@ var SyncoreRuntime = class {
266
284
  this.schedulerTimer = void 0;
267
285
  }
268
286
  if (this.started) await this.runPluginHook("onStop");
287
+ this.detachExternalChangeListener?.();
288
+ this.detachExternalChangeListener = void 0;
269
289
  await this.options.driver.close?.();
270
290
  if (this.started) this.emitDevtools({
271
291
  type: "runtime.disconnected",
@@ -285,31 +305,140 @@ var SyncoreRuntime = class {
285
305
  watchQuery: (reference, ...args) => this.watchQuery(reference, normalizeOptionalArgs(args))
286
306
  };
287
307
  }
288
- getDevtoolsSnapshot() {
308
+ async getDevtoolsLiveQuerySnapshot() {
309
+ return {
310
+ summary: this.getRuntimeSummary(),
311
+ activeQueries: this.getActiveQueryInfos(),
312
+ schemaTables: await this.getSchemaTablesForDevtools()
313
+ };
314
+ }
315
+ getRuntimeSummary() {
289
316
  return {
290
317
  runtimeId: this.runtimeId,
291
318
  platform: this.platform,
292
319
  connectedAt: Date.now(),
293
- activeQueries: [...this.activeQueries.values()].map((query) => ({
294
- id: query.id,
295
- functionName: query.functionName,
296
- dependencyKeys: [...query.dependencyKeys],
297
- lastRunAt: query.lastRunAt
298
- })),
299
- pendingJobs: [],
300
- recentEvents: [...this.recentEvents]
320
+ activeQueryCount: this.activeQueries.size,
321
+ recentEventCount: this.recentEvents.length
301
322
  };
302
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;
367
+ }
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
+ }
303
431
  getRuntimeId() {
304
432
  return this.runtimeId;
305
433
  }
306
- async runQuery(reference, args = {}) {
434
+ async runQuery(reference, args = {}, meta = {}) {
307
435
  const definition = this.resolveFunction(reference, "query");
308
436
  const dependencyCollector = /* @__PURE__ */ new Set();
309
437
  const startedAt = Date.now();
310
438
  const result = await this.invokeFunction(definition, args, {
311
439
  mutationDepth: 0,
312
440
  changedTables: /* @__PURE__ */ new Set(),
441
+ storageChanges: [],
313
442
  dependencyCollector
314
443
  });
315
444
  this.emitDevtools({
@@ -319,20 +448,30 @@ var SyncoreRuntime = class {
319
448
  functionName: reference.name,
320
449
  dependencies: [...dependencyCollector],
321
450
  durationMs: Date.now() - startedAt,
322
- timestamp: Date.now()
451
+ timestamp: Date.now(),
452
+ ...meta.origin ? { origin: meta.origin } : {}
323
453
  });
324
454
  return result;
325
455
  }
326
- async runMutation(reference, args = {}) {
456
+ async runMutation(reference, args = {}, meta = {}) {
327
457
  const definition = this.resolveFunction(reference, "mutation");
328
458
  const mutationId = generateId();
329
459
  const startedAt = Date.now();
330
460
  const changedTables = /* @__PURE__ */ new Set();
461
+ const storageChanges = [];
331
462
  const result = await this.options.driver.withTransaction(async () => this.invokeFunction(definition, args, {
332
463
  mutationDepth: 1,
333
- changedTables
464
+ changedTables,
465
+ storageChanges
334
466
  }));
335
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);
336
475
  this.emitDevtools({
337
476
  type: "mutation.committed",
338
477
  runtimeId: this.runtimeId,
@@ -340,18 +479,20 @@ var SyncoreRuntime = class {
340
479
  functionName: reference.name,
341
480
  changedTables: [...changedTables],
342
481
  durationMs: Date.now() - startedAt,
343
- timestamp: Date.now()
482
+ timestamp: Date.now(),
483
+ ...meta.origin ? { origin: meta.origin } : {}
344
484
  });
345
485
  return result;
346
486
  }
347
- async runAction(reference, args = {}) {
487
+ async runAction(reference, args = {}, meta = {}) {
348
488
  const definition = this.resolveFunction(reference, "action");
349
489
  const actionId = generateId();
350
490
  const startedAt = Date.now();
351
491
  try {
352
492
  const result = await this.invokeFunction(definition, args, {
353
493
  mutationDepth: 0,
354
- changedTables: /* @__PURE__ */ new Set()
494
+ changedTables: /* @__PURE__ */ new Set(),
495
+ storageChanges: []
355
496
  });
356
497
  this.emitDevtools({
357
498
  type: "action.completed",
@@ -359,7 +500,8 @@ var SyncoreRuntime = class {
359
500
  actionId,
360
501
  functionName: reference.name,
361
502
  durationMs: Date.now() - startedAt,
362
- timestamp: Date.now()
503
+ timestamp: Date.now(),
504
+ ...meta.origin ? { origin: meta.origin } : {}
363
505
  });
364
506
  return result;
365
507
  } catch (error) {
@@ -370,6 +512,7 @@ var SyncoreRuntime = class {
370
512
  functionName: reference.name,
371
513
  durationMs: Date.now() - startedAt,
372
514
  timestamp: Date.now(),
515
+ ...meta.origin ? { origin: meta.origin } : {},
373
516
  error: error instanceof Error ? error.message : String(error)
374
517
  });
375
518
  throw error;
@@ -470,7 +613,7 @@ var SyncoreRuntime = class {
470
613
  }
471
614
  createContext(kind, state) {
472
615
  const db = kind === "mutation" ? this.createDatabaseWriter(state) : this.createDatabaseReader(state);
473
- const storage = this.createStorageApi();
616
+ const storage = this.createStorageApi(state);
474
617
  const scheduler = this.createSchedulerApi();
475
618
  return {
476
619
  db,
@@ -482,7 +625,8 @@ var SyncoreRuntime = class {
482
625
  const normalizedArgs = normalizeOptionalArgs(args);
483
626
  if (kind === "mutation") return this.options.driver.withSavepoint(`sp_${generateId().replace(/-/g, "_")}`, () => this.invokeFunction(this.resolveFunction(reference, "mutation"), normalizedArgs, {
484
627
  mutationDepth: state.mutationDepth + 1,
485
- changedTables: state.changedTables
628
+ changedTables: state.changedTables,
629
+ storageChanges: state.storageChanges
486
630
  }));
487
631
  return this.runMutation(reference, normalizedArgs);
488
632
  },
@@ -552,7 +696,7 @@ var SyncoreRuntime = class {
552
696
  }
553
697
  };
554
698
  }
555
- createStorageApi() {
699
+ createStorageApi(state) {
556
700
  return {
557
701
  put: async (input) => {
558
702
  const id = generateId();
@@ -582,6 +726,10 @@ var SyncoreRuntime = class {
582
726
  operation: "put",
583
727
  timestamp: Date.now()
584
728
  });
729
+ state.storageChanges.push({
730
+ storageId: id,
731
+ reason: "storage-put"
732
+ });
585
733
  return id;
586
734
  },
587
735
  get: async (id) => {
@@ -611,6 +759,10 @@ var SyncoreRuntime = class {
611
759
  operation: "delete",
612
760
  timestamp: Date.now()
613
761
  });
762
+ state.storageChanges.push({
763
+ storageId: id,
764
+ reason: "storage-delete"
765
+ });
614
766
  }
615
767
  };
616
768
  }
@@ -630,10 +782,13 @@ var SyncoreRuntime = class {
630
782
  return this.scheduleJob(value, reference, functionArgs, misfirePolicy);
631
783
  },
632
784
  cancel: async (id) => {
633
- await this.options.driver.run(`UPDATE "_scheduled_functions" SET status = 'cancelled', updated_at = ? WHERE id = ?`, [Date.now(), id]);
785
+ await this.cancelScheduledJob(id);
634
786
  }
635
787
  };
636
788
  }
789
+ notifySchedulerJobsChanged() {
790
+ this.notifyDevtoolsScopes(["scheduler.jobs"]);
791
+ }
637
792
  async ensureSystemTables() {
638
793
  await this.options.driver.exec(`
639
794
  CREATE TABLE IF NOT EXISTS "_syncore_migrations" (
@@ -802,6 +957,7 @@ var SyncoreRuntime = class {
802
957
  misfirePolicy.type,
803
958
  misfirePolicy.type === "windowed" ? misfirePolicy.windowMs : null
804
959
  ]);
960
+ this.notifySchedulerJobsChanged();
805
961
  return id;
806
962
  }
807
963
  async syncRecurringJobs() {
@@ -825,6 +981,7 @@ var SyncoreRuntime = class {
825
981
  job.misfirePolicy.type,
826
982
  job.misfirePolicy.type === "windowed" ? job.misfirePolicy.windowMs : null
827
983
  ]);
984
+ this.notifySchedulerJobsChanged();
828
985
  }
829
986
  }
830
987
  async processDueJobs() {
@@ -850,6 +1007,7 @@ var SyncoreRuntime = class {
850
1007
  await this.advanceOrFinalizeJob(job, "completed", now);
851
1008
  } catch (error) {
852
1009
  await this.options.driver.run(`UPDATE "_scheduled_functions" SET status = 'failed', updated_at = ? WHERE id = ?`, [Date.now(), job.id]);
1010
+ this.notifySchedulerJobsChanged();
853
1011
  this.emitDevtools({
854
1012
  type: "log",
855
1013
  runtimeId: this.runtimeId,
@@ -859,12 +1017,15 @@ var SyncoreRuntime = class {
859
1017
  });
860
1018
  }
861
1019
  }
862
- if (executedJobIds.length > 0) this.emitDevtools({
863
- type: "scheduler.tick",
864
- runtimeId: this.runtimeId,
865
- executedJobIds,
866
- timestamp: Date.now()
867
- });
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
+ }
868
1029
  }
869
1030
  async advanceOrFinalizeJob(job, terminalStatus, executedAt) {
870
1031
  if (!job.recurring_name || !job.schedule_json) {
@@ -874,6 +1035,7 @@ var SyncoreRuntime = class {
874
1035
  executedAt,
875
1036
  job.id
876
1037
  ]);
1038
+ this.notifySchedulerJobsChanged();
877
1039
  return;
878
1040
  }
879
1041
  const nextRunAt = computeNextRun(JSON.parse(job.schedule_json), executedAt + 1);
@@ -885,6 +1047,7 @@ var SyncoreRuntime = class {
885
1047
  executedAt,
886
1048
  job.id
887
1049
  ]);
1050
+ this.notifySchedulerJobsChanged();
888
1051
  }
889
1052
  async refreshInvalidatedQueries(changedTables, mutationId) {
890
1053
  for (const query of this.activeQueries.values()) {
@@ -914,6 +1077,94 @@ var SyncoreRuntime = class {
914
1077
  }
915
1078
  for (const listener of record.listeners) listener();
916
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
+ }
917
1168
  async collectQueryDependencies(functionName, args) {
918
1169
  const definition = this.resolveFunction({
919
1170
  kind: "query",
@@ -923,6 +1174,7 @@ var SyncoreRuntime = class {
923
1174
  await this.invokeFunction(definition, args, {
924
1175
  mutationDepth: 0,
925
1176
  changedTables: /* @__PURE__ */ new Set(),
1177
+ storageChanges: [],
926
1178
  dependencyCollector
927
1179
  });
928
1180
  return dependencyCollector;
@@ -979,6 +1231,8 @@ var SyncoreRuntime = class {
979
1231
  this.recentEvents.unshift(event);
980
1232
  this.recentEvents.splice(24);
981
1233
  this.options.devtools?.emit(event);
1234
+ this.notifyDevtoolsScopes(devtoolsScopesForEvent(event));
1235
+ for (const listener of this.devtoolsListeners) listener(event);
982
1236
  }
983
1237
  createPluginContext() {
984
1238
  return {
@@ -1037,6 +1291,23 @@ function sortValue(value) {
1037
1291
  if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nested]) => [key, sortValue(nested)]));
1038
1292
  return value;
1039
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
+ }
1040
1311
  function omitSystemFields(document) {
1041
1312
  const clone = { ...document };
1042
1313
  delete clone._id;