syncorejs 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/_vendor/core/cli.d.mts.map +1 -1
  2. package/dist/_vendor/core/cli.mjs +272 -7
  3. package/dist/_vendor/core/cli.mjs.map +1 -1
  4. package/dist/_vendor/core/index.d.mts +3 -3
  5. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  6. package/dist/_vendor/core/runtime/devtools.mjs +131 -0
  7. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  8. package/dist/_vendor/core/runtime/functions.d.mts +3 -3
  9. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  10. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +1 -1
  11. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
  12. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +1 -1
  13. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
  14. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +5 -1
  15. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
  16. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +99 -13
  17. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
  18. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +38 -4
  19. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
  20. package/dist/_vendor/core/runtime/runtime.d.mts +65 -8
  21. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  22. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  23. package/dist/_vendor/core/transport.d.mts.map +1 -1
  24. package/dist/_vendor/core/transport.mjs +30 -5
  25. package/dist/_vendor/core/transport.mjs.map +1 -1
  26. package/dist/_vendor/devtools-protocol/index.d.ts +75 -1
  27. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  28. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  29. package/dist/_vendor/next/index.js +9 -1
  30. package/dist/_vendor/next/index.js.map +1 -1
  31. package/dist/_vendor/platform-expo/index.d.ts +1 -1
  32. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  33. package/dist/_vendor/platform-expo/index.js +6 -1
  34. package/dist/_vendor/platform-expo/index.js.map +1 -1
  35. package/dist/_vendor/platform-node/index.d.mts +2 -1
  36. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  37. package/dist/_vendor/platform-node/index.mjs +27 -2
  38. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  39. package/dist/_vendor/platform-node/ipc-react.mjs +4 -0
  40. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  41. package/dist/_vendor/platform-web/external-change.d.ts +2 -2
  42. package/dist/_vendor/platform-web/external-change.js +2 -2
  43. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  44. package/dist/_vendor/platform-web/index.d.ts +13 -10
  45. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  46. package/dist/_vendor/platform-web/index.js +66 -10
  47. package/dist/_vendor/platform-web/index.js.map +1 -1
  48. package/dist/_vendor/platform-web/indexeddb.d.ts +3 -3
  49. package/dist/_vendor/platform-web/indexeddb.js +3 -3
  50. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  51. package/dist/_vendor/platform-web/opfs.d.ts +3 -1
  52. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  53. package/dist/_vendor/platform-web/opfs.js +29 -3
  54. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  55. package/dist/_vendor/platform-web/persistence.d.ts +31 -1
  56. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  57. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  58. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  59. package/dist/_vendor/platform-web/react.js +9 -1
  60. package/dist/_vendor/platform-web/react.js.map +1 -1
  61. package/dist/_vendor/react/index.d.ts +6 -5
  62. package/dist/_vendor/react/index.d.ts.map +1 -1
  63. package/dist/_vendor/react/index.js +6 -5
  64. package/dist/_vendor/react/index.js.map +1 -1
  65. package/dist/_vendor/svelte/index.d.ts +8 -6
  66. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  67. package/dist/_vendor/svelte/index.js +7 -5
  68. package/dist/_vendor/svelte/index.js.map +1 -1
  69. package/package.json +1 -1
@@ -72,7 +72,7 @@ var DevtoolsEngine = class {
72
72
  function toDevtoolsScopes(scopes) {
73
73
  const resolved = [];
74
74
  for (const scope of scopes) {
75
- if (scope.startsWith("row:") || !(scope.startsWith("table:") || scope.startsWith("storage:")) && scope !== "runtime.summary" && scope !== "runtime.activeQueries" && scope !== "schema.tables" && scope !== "scheduler.jobs") continue;
75
+ if (scope.startsWith("row:") || !(scope.startsWith("table:") || scope.startsWith("storage:")) && scope !== "runtime.summary" && scope !== "runtime.activeQueries" && scope !== "schema.tables" && scope !== "scheduler.jobs" && scope !== "storage.objects") continue;
76
76
  resolved.push(scope);
77
77
  }
78
78
  return resolved.length > 0 ? resolved : ["all"];
@@ -1 +1 @@
1
- {"version":3,"file":"devtoolsEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/devtoolsEngine.ts"],"sourcesContent":["import type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\nimport type {\n ImpactScope,\n DevtoolsLiveQueryScope,\n DevtoolsLiveQuerySnapshot,\n DevtoolsSink\n} from \"../../runtime.js\";\nimport { devtoolsScopesForEvent, type DevtoolsEventMeta } from \"./shared.js\";\n\ntype DevtoolsEngineDeps = {\n runtimeId: string;\n platform: string;\n sink?: DevtoolsSink;\n getActiveQueryInfos: () => SyncoreActiveQueryInfo[];\n getSchemaTables: () => Promise<DevtoolsLiveQuerySnapshot[\"schemaTables\"]>;\n};\n\nexport class DevtoolsEngine {\n private readonly recentEvents: SyncoreDevtoolsEvent[] = [];\n private readonly listeners = new Set<(event: SyncoreDevtoolsEvent) => void>();\n private readonly invalidationListeners = new Set<\n (scopes: Set<DevtoolsLiveQueryScope>) => void\n >();\n private nextSequence = 1;\n\n constructor(private readonly deps: DevtoolsEngineDeps) {}\n\n getRuntimeSummary(): SyncoreRuntimeSummary {\n return {\n runtimeId: this.deps.runtimeId,\n platform: this.deps.platform,\n connectedAt: Date.now(),\n activeQueryCount: this.deps.getActiveQueryInfos().length,\n recentEventCount: this.recentEvents.length\n };\n }\n\n getActiveQueryInfos(): SyncoreActiveQueryInfo[] {\n return this.deps.getActiveQueryInfos();\n }\n\n async getLiveSnapshot(): Promise<DevtoolsLiveQuerySnapshot> {\n return {\n summary: this.getRuntimeSummary(),\n activeQueries: this.getActiveQueryInfos(),\n schemaTables: await this.deps.getSchemaTables()\n };\n }\n\n subscribeEvents(listener: (event: SyncoreDevtoolsEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n subscribeInvalidations(\n listener: (scopes: Set<DevtoolsLiveQueryScope>) => void\n ): () => void {\n this.invalidationListeners.add(listener);\n return () => {\n this.invalidationListeners.delete(listener);\n };\n }\n\n notifyScopes(scopes: Iterable<DevtoolsLiveQueryScope>): void {\n const scopeSet = new Set(scopes);\n for (const listener of this.invalidationListeners) {\n listener(scopeSet);\n }\n }\n\n emit(event: SyncoreDevtoolsEvent): void {\n const sequencedEvent = {\n ...event,\n sequence: event.sequence ?? this.nextSequence++\n };\n this.recentEvents.push(sequencedEvent);\n this.recentEvents.splice(0, Math.max(0, this.recentEvents.length - 24));\n this.deps.sink?.emit(sequencedEvent);\n this.notifyScopes(devtoolsScopesForEvent(sequencedEvent));\n for (const listener of this.listeners) {\n listener(sequencedEvent);\n }\n }\n\n async forceRefresh(\n reason: string,\n handlers: {\n refreshQueriesForScopes: (\n scopes: Set<ImpactScope>,\n reason: string\n ) => Promise<unknown>;\n },\n meta: DevtoolsEventMeta = {},\n scopes: Iterable<ImpactScope> = []\n ): Promise<void> {\n const scopeSet = new Set(scopes);\n if (scopeSet.size > 0) {\n await handlers.refreshQueriesForScopes(scopeSet, reason);\n }\n this.notifyScopes(\n scopeSet.size > 0 ? toDevtoolsScopes(scopeSet) : [\"all\"]\n );\n this.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"info\",\n message: reason,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n }\n}\n\nfunction toDevtoolsScopes(\n scopes: Iterable<ImpactScope>\n): DevtoolsLiveQueryScope[] {\n const resolved: DevtoolsLiveQueryScope[] = [];\n for (const scope of scopes) {\n if (\n scope.startsWith(\"row:\") ||\n !(scope.startsWith(\"table:\") || scope.startsWith(\"storage:\")) &&\n scope !== \"runtime.summary\" &&\n scope !== \"runtime.activeQueries\" &&\n scope !== \"schema.tables\" &&\n scope !== \"scheduler.jobs\"\n ) {\n continue;\n }\n resolved.push(scope as DevtoolsLiveQueryScope);\n }\n return resolved.length > 0 ? resolved : [\"all\"];\n}\n"],"mappings":";;AAqBA,IAAa,iBAAb,MAA4B;CAQG;CAP7B,eAAwD,CAAC;CACzD,4BAA6B,IAAI,IAA2C;CAC5E,wCAAyC,IAAI,IAE3C;CACF,eAAuB;CAEvB,YAAY,MAA2C;EAA1B,KAAA,OAAA;CAA2B;CAExD,oBAA2C;EACzC,OAAO;GACL,WAAW,KAAK,KAAK;GACrB,UAAU,KAAK,KAAK;GACpB,aAAa,KAAK,IAAI;GACtB,kBAAkB,KAAK,KAAK,oBAAoB,EAAE;GAClD,kBAAkB,KAAK,aAAa;EACtC;CACF;CAEA,sBAAgD;EAC9C,OAAO,KAAK,KAAK,oBAAoB;CACvC;CAEA,MAAM,kBAAsD;EAC1D,OAAO;GACL,SAAS,KAAK,kBAAkB;GAChC,eAAe,KAAK,oBAAoB;GACxC,cAAc,MAAM,KAAK,KAAK,gBAAgB;EAChD;CACF;CAEA,gBAAgB,UAA6D;EAC3E,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACX,KAAK,UAAU,OAAO,QAAQ;EAChC;CACF;CAEA,uBACE,UACY;EACZ,KAAK,sBAAsB,IAAI,QAAQ;EACvC,aAAa;GACX,KAAK,sBAAsB,OAAO,QAAQ;EAC5C;CACF;CAEA,aAAa,QAAgD;EAC3D,MAAM,WAAW,IAAI,IAAI,MAAM;EAC/B,KAAK,MAAM,YAAY,KAAK,uBAC1B,SAAS,QAAQ;CAErB;CAEA,KAAK,OAAmC;EACtC,MAAM,iBAAiB;GACrB,GAAG;GACH,UAAU,MAAM,YAAY,KAAK;EACnC;EACA,KAAK,aAAa,KAAK,cAAc;EACrC,KAAK,aAAa,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,aAAa,SAAS,EAAE,CAAC;EACtE,KAAK,KAAK,MAAM,KAAK,cAAc;EACnC,KAAK,aAAa,uBAAuB,cAAc,CAAC;EACxD,KAAK,MAAM,YAAY,KAAK,WAC1B,SAAS,cAAc;CAE3B;CAEA,MAAM,aACJ,QACA,UAMA,OAA0B,CAAC,GAC3B,SAAgC,CAAC,GAClB;EACf,MAAM,WAAW,IAAI,IAAI,MAAM;EAC/B,IAAI,SAAS,OAAO,GAClB,MAAM,SAAS,wBAAwB,UAAU,MAAM;EAEzD,KAAK,aACH,SAAS,OAAO,IAAI,iBAAiB,QAAQ,IAAI,CAAC,KAAK,CACzD;EACA,KAAK,KAAK;GACR,MAAM;GACN,WAAW,KAAK,KAAK;GACrB,OAAO;GACP,SAAS;GACT,WAAW,KAAK,IAAI;GACpB,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC/C,CAAC;CACH;AACF;AAEA,SAAS,iBACP,QAC0B;CAC1B,MAAM,WAAqC,CAAC;CAC5C,KAAK,MAAM,SAAS,QAAQ;EAC1B,IACE,MAAM,WAAW,MAAM,KACvB,EAAE,MAAM,WAAW,QAAQ,KAAK,MAAM,WAAW,UAAU,MACzD,UAAU,qBACV,UAAU,2BACV,UAAU,mBACV,UAAU,kBAEZ;EAEF,SAAS,KAAK,KAA+B;CAC/C;CACA,OAAO,SAAS,SAAS,IAAI,WAAW,CAAC,KAAK;AAChD"}
1
+ {"version":3,"file":"devtoolsEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/devtoolsEngine.ts"],"sourcesContent":["import type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent,\n SyncoreRuntimeSummary\n} from \"@syncore/devtools-protocol\";\nimport type {\n ImpactScope,\n DevtoolsLiveQueryScope,\n DevtoolsLiveQuerySnapshot,\n DevtoolsSink\n} from \"../../runtime.js\";\nimport { devtoolsScopesForEvent, type DevtoolsEventMeta } from \"./shared.js\";\n\ntype DevtoolsEngineDeps = {\n runtimeId: string;\n platform: string;\n sink?: DevtoolsSink;\n getActiveQueryInfos: () => SyncoreActiveQueryInfo[];\n getSchemaTables: () => Promise<DevtoolsLiveQuerySnapshot[\"schemaTables\"]>;\n};\n\nexport class DevtoolsEngine {\n private readonly recentEvents: SyncoreDevtoolsEvent[] = [];\n private readonly listeners = new Set<(event: SyncoreDevtoolsEvent) => void>();\n private readonly invalidationListeners = new Set<\n (scopes: Set<DevtoolsLiveQueryScope>) => void\n >();\n private nextSequence = 1;\n\n constructor(private readonly deps: DevtoolsEngineDeps) {}\n\n getRuntimeSummary(): SyncoreRuntimeSummary {\n return {\n runtimeId: this.deps.runtimeId,\n platform: this.deps.platform,\n connectedAt: Date.now(),\n activeQueryCount: this.deps.getActiveQueryInfos().length,\n recentEventCount: this.recentEvents.length\n };\n }\n\n getActiveQueryInfos(): SyncoreActiveQueryInfo[] {\n return this.deps.getActiveQueryInfos();\n }\n\n async getLiveSnapshot(): Promise<DevtoolsLiveQuerySnapshot> {\n return {\n summary: this.getRuntimeSummary(),\n activeQueries: this.getActiveQueryInfos(),\n schemaTables: await this.deps.getSchemaTables()\n };\n }\n\n subscribeEvents(listener: (event: SyncoreDevtoolsEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n subscribeInvalidations(\n listener: (scopes: Set<DevtoolsLiveQueryScope>) => void\n ): () => void {\n this.invalidationListeners.add(listener);\n return () => {\n this.invalidationListeners.delete(listener);\n };\n }\n\n notifyScopes(scopes: Iterable<DevtoolsLiveQueryScope>): void {\n const scopeSet = new Set(scopes);\n for (const listener of this.invalidationListeners) {\n listener(scopeSet);\n }\n }\n\n emit(event: SyncoreDevtoolsEvent): void {\n const sequencedEvent = {\n ...event,\n sequence: event.sequence ?? this.nextSequence++\n };\n this.recentEvents.push(sequencedEvent);\n this.recentEvents.splice(0, Math.max(0, this.recentEvents.length - 24));\n this.deps.sink?.emit(sequencedEvent);\n this.notifyScopes(devtoolsScopesForEvent(sequencedEvent));\n for (const listener of this.listeners) {\n listener(sequencedEvent);\n }\n }\n\n async forceRefresh(\n reason: string,\n handlers: {\n refreshQueriesForScopes: (\n scopes: Set<ImpactScope>,\n reason: string\n ) => Promise<unknown>;\n },\n meta: DevtoolsEventMeta = {},\n scopes: Iterable<ImpactScope> = []\n ): Promise<void> {\n const scopeSet = new Set(scopes);\n if (scopeSet.size > 0) {\n await handlers.refreshQueriesForScopes(scopeSet, reason);\n }\n this.notifyScopes(\n scopeSet.size > 0 ? toDevtoolsScopes(scopeSet) : [\"all\"]\n );\n this.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"info\",\n message: reason,\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n }\n}\n\nfunction toDevtoolsScopes(\n scopes: Iterable<ImpactScope>\n): DevtoolsLiveQueryScope[] {\n const resolved: DevtoolsLiveQueryScope[] = [];\n for (const scope of scopes) {\n if (\n scope.startsWith(\"row:\") ||\n !(scope.startsWith(\"table:\") || scope.startsWith(\"storage:\")) &&\n scope !== \"runtime.summary\" &&\n scope !== \"runtime.activeQueries\" &&\n scope !== \"schema.tables\" &&\n scope !== \"scheduler.jobs\" &&\n scope !== \"storage.objects\"\n ) {\n continue;\n }\n resolved.push(scope as DevtoolsLiveQueryScope);\n }\n return resolved.length > 0 ? resolved : [\"all\"];\n}\n"],"mappings":";;AAqBA,IAAa,iBAAb,MAA4B;CAQG;CAP7B,eAAwD,CAAC;CACzD,4BAA6B,IAAI,IAA2C;CAC5E,wCAAyC,IAAI,IAE3C;CACF,eAAuB;CAEvB,YAAY,MAA2C;EAA1B,KAAA,OAAA;CAA2B;CAExD,oBAA2C;EACzC,OAAO;GACL,WAAW,KAAK,KAAK;GACrB,UAAU,KAAK,KAAK;GACpB,aAAa,KAAK,IAAI;GACtB,kBAAkB,KAAK,KAAK,oBAAoB,EAAE;GAClD,kBAAkB,KAAK,aAAa;EACtC;CACF;CAEA,sBAAgD;EAC9C,OAAO,KAAK,KAAK,oBAAoB;CACvC;CAEA,MAAM,kBAAsD;EAC1D,OAAO;GACL,SAAS,KAAK,kBAAkB;GAChC,eAAe,KAAK,oBAAoB;GACxC,cAAc,MAAM,KAAK,KAAK,gBAAgB;EAChD;CACF;CAEA,gBAAgB,UAA6D;EAC3E,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACX,KAAK,UAAU,OAAO,QAAQ;EAChC;CACF;CAEA,uBACE,UACY;EACZ,KAAK,sBAAsB,IAAI,QAAQ;EACvC,aAAa;GACX,KAAK,sBAAsB,OAAO,QAAQ;EAC5C;CACF;CAEA,aAAa,QAAgD;EAC3D,MAAM,WAAW,IAAI,IAAI,MAAM;EAC/B,KAAK,MAAM,YAAY,KAAK,uBAC1B,SAAS,QAAQ;CAErB;CAEA,KAAK,OAAmC;EACtC,MAAM,iBAAiB;GACrB,GAAG;GACH,UAAU,MAAM,YAAY,KAAK;EACnC;EACA,KAAK,aAAa,KAAK,cAAc;EACrC,KAAK,aAAa,OAAO,GAAG,KAAK,IAAI,GAAG,KAAK,aAAa,SAAS,EAAE,CAAC;EACtE,KAAK,KAAK,MAAM,KAAK,cAAc;EACnC,KAAK,aAAa,uBAAuB,cAAc,CAAC;EACxD,KAAK,MAAM,YAAY,KAAK,WAC1B,SAAS,cAAc;CAE3B;CAEA,MAAM,aACJ,QACA,UAMA,OAA0B,CAAC,GAC3B,SAAgC,CAAC,GAClB;EACf,MAAM,WAAW,IAAI,IAAI,MAAM;EAC/B,IAAI,SAAS,OAAO,GAClB,MAAM,SAAS,wBAAwB,UAAU,MAAM;EAEzD,KAAK,aACH,SAAS,OAAO,IAAI,iBAAiB,QAAQ,IAAI,CAAC,KAAK,CACzD;EACA,KAAK,KAAK;GACR,MAAM;GACN,WAAW,KAAK,KAAK;GACrB,OAAO;GACP,SAAS;GACT,WAAW,KAAK,IAAI;GACpB,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;EAC/C,CAAC;CACH;AACF;AAEA,SAAS,iBACP,QAC0B;CAC1B,MAAM,WAAqC,CAAC;CAC5C,KAAK,MAAM,SAAS,QAAQ;EAC1B,IACE,MAAM,WAAW,MAAM,KACvB,EAAE,MAAM,WAAW,QAAQ,KAAK,MAAM,WAAW,UAAU,MACzD,UAAU,qBACV,UAAU,2BACV,UAAU,mBACV,UAAU,oBACV,UAAU,mBAEZ;EAEF,SAAS,KAAK,KAA+B;CAC/C;CACA,OAAO,SAAS,SAAS,IAAI,WAAW,CAAC,KAAK;AAChD"}
@@ -216,7 +216,7 @@ function toDevtoolsScopes(scopes) {
216
216
  if (tableName) resolved.add(`table:${tableName}`);
217
217
  continue;
218
218
  }
219
- if (scope === "runtime.summary" || scope === "runtime.activeQueries" || scope === "schema.tables" || scope === "scheduler.jobs" || scope.startsWith("table:") || scope.startsWith("storage:")) resolved.add(scope);
219
+ if (scope === "runtime.summary" || scope === "runtime.activeQueries" || scope === "schema.tables" || scope === "scheduler.jobs" || scope === "storage.objects" || scope.startsWith("table:") || scope.startsWith("storage:")) resolved.add(scope);
220
220
  }
221
221
  return resolved.size > 0 ? [...resolved] : ["all"];
222
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"reactivityEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/reactivityEngine.ts"],"sourcesContent":["import type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent\n} from \"@syncore/devtools-protocol\";\nimport type { FunctionReference } from \"../../functions.js\";\nimport type {\n DevtoolsLiveQueryScope,\n ImpactScope,\n JsonObject,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal,\n SyncoreWatch\n} from \"../../runtime.js\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport { generateId } from \"../../id.js\";\nimport type {\n ActiveQueryRecord,\n DependencyKey\n} from \"./shared.js\";\nimport { parseCanonicalComponentFunctionName } from \"./shared.js\";\n\ntype ReactivityEngineDeps = {\n runtimeId: string;\n externalChangeSourceId: string;\n externalChangeSignal?: SyncoreExternalChangeSignal;\n externalChangeApplier?: SyncoreExternalChangeApplier;\n devtools: DevtoolsEngine;\n runQuery: <TResult>(\n reference: FunctionReference<\"query\", unknown, TResult>,\n args: JsonObject,\n meta?: { executionId?: string; parentExecutionId?: string }\n ) => Promise<TResult>;\n collectQueryDependencies: (\n functionName: string,\n args: JsonObject\n ) => Promise<Set<DependencyKey>>;\n};\n\nexport class ReactivityEngine {\n private readonly activeQueries = new Map<string, ActiveQueryRecord>();\n private detachExternalChangeListener: (() => void) | undefined;\n private pendingExternalChangePromise: Promise<void> | undefined;\n private queuedExternalChange:\n | {\n changedScopes: Set<ImpactScope>;\n }\n | undefined;\n\n constructor(private readonly deps: ReactivityEngineDeps) {}\n\n start(): void {\n this.detachExternalChangeListener =\n this.deps.externalChangeSignal?.subscribe((event) => {\n void this.handleExternalChangeEvent(event);\n });\n }\n\n stop(): void {\n this.detachExternalChangeListener?.();\n this.detachExternalChangeListener = undefined;\n }\n\n getActiveQueryInfos(): SyncoreActiveQueryInfo[] {\n return [...this.activeQueries.values()].map((query) => {\n const componentFunction = parseCanonicalComponentFunctionName(\n query.functionName\n );\n return {\n id: query.id,\n functionName: query.functionName,\n args: query.args,\n consumers: query.consumers,\n ...(componentFunction\n ? {\n owner: \"component\" as const,\n componentPath: componentFunction.componentPath\n }\n : {\n owner: \"root\" as const\n }),\n dependencyKeys: [...query.dependencyKeys],\n lastRunAt: query.lastRunAt\n };\n });\n }\n\n watchQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject = {}\n ): SyncoreWatch<TResult> {\n const key = this.createActiveQueryKey(reference.name, args);\n let record = this.activeQueries.get(key);\n\n if (!record) {\n record = {\n id: key,\n functionName: reference.name,\n args,\n listeners: new Set<() => void>(),\n consumers: 0,\n dependencyKeys: new Set<DependencyKey>(),\n lastResult: undefined,\n lastError: undefined,\n lastRunAt: 0\n };\n this.activeQueries.set(key, record);\n this.notifyActiveQueriesChanged();\n void this.rerunActiveQuery(record);\n }\n\n const activeRecord = record;\n activeRecord.consumers += 1;\n let disposed = false;\n const ownedListeners = new Set<() => void>();\n\n return {\n onUpdate: (callback) => {\n activeRecord.listeners.add(callback);\n ownedListeners.add(callback);\n queueMicrotask(callback);\n return () => {\n activeRecord.listeners.delete(callback);\n ownedListeners.delete(callback);\n };\n },\n localQueryResult: () => activeRecord.lastResult as TResult | undefined,\n localQueryError: () => activeRecord.lastError,\n dispose: () => {\n if (disposed) {\n return;\n }\n disposed = true;\n for (const callback of ownedListeners) {\n activeRecord.listeners.delete(callback);\n }\n ownedListeners.clear();\n activeRecord.consumers = Math.max(0, activeRecord.consumers - 1);\n if (activeRecord.consumers === 0) {\n this.activeQueries.delete(key);\n this.notifyActiveQueriesChanged();\n }\n }\n };\n }\n\n async refreshInvalidatedQueries(\n changedTables: Set<string>,\n mutationId: string\n ): Promise<void> {\n const impactedScopes = new Set(\n [...changedTables].map((tableName) => `table:${tableName}` as ImpactScope)\n );\n await this.refreshQueriesForScopes(\n impactedScopes,\n `Mutation ${mutationId} changed ${[...changedTables].join(\", \")}`\n );\n }\n\n async refreshQueriesForScopes(\n scopes: Iterable<ImpactScope>,\n reason: string,\n cause?: { executionId?: string }\n ): Promise<string[]> {\n const scopeSet = new Set(scopes);\n if (scopeSet.size === 0) {\n return [];\n }\n const invalidatedQueryIds: string[] = [];\n for (const { query, matchedScopes } of this.getInvalidatedQueriesForScopes(\n scopeSet\n )) {\n const rerunExecutionId = generateReactivityExecutionId();\n this.deps.devtools.emit({\n type: \"query.invalidated\",\n runtimeId: this.deps.runtimeId,\n queryId: query.id,\n ...(parseCanonicalComponentFunctionName(query.functionName)\n ? {\n componentPath: parseCanonicalComponentFunctionName(query.functionName)!\n .componentPath\n }\n : {}),\n reason,\n ...(cause?.executionId ? { causedByExecutionId: cause.executionId } : {}),\n changedScopes: [...scopeSet],\n matchedScopes,\n rerunExecutionId,\n timestamp: Date.now()\n });\n invalidatedQueryIds.push(query.id);\n await this.rerunActiveQuery(query, {\n executionId: rerunExecutionId,\n ...(cause?.executionId ? { parentExecutionId: cause.executionId } : {})\n });\n }\n return invalidatedQueryIds;\n }\n\n getInvalidatedQueryIdsForScopes(scopes: Iterable<ImpactScope>): string[] {\n return this.getInvalidatedQueriesForScopes(new Set(scopes)).map(\n ({ query }) => query.id\n );\n }\n\n async publishExternalChange(\n event: Omit<SyncoreExternalChangeEvent, \"sourceId\" | \"timestamp\">\n ): Promise<void> {\n const changedScopes = resolveChangedScopes(event);\n if (changedScopes.size === 0) {\n throw new Error(\n `Syncore cannot publish external change \"${event.reason}\" without precise impact scopes.`\n );\n }\n await this.deps.externalChangeSignal?.publish({\n ...event,\n changedScopes: [...changedScopes],\n sourceId: this.deps.externalChangeSourceId,\n timestamp: Date.now()\n });\n }\n\n async publishStorageChanges(\n storageChanges: Array<{\n storageId: string;\n reason: \"storage-put\" | \"storage-delete\";\n }>\n ): Promise<void> {\n for (const change of storageChanges) {\n await this.publishExternalChange({\n scope: \"storage\",\n reason: change.reason,\n changedScopes: [`storage:${change.storageId}`],\n storageIds: [change.storageId]\n });\n }\n }\n\n async publishDatabaseReconcile(): Promise<void> {\n throw new Error(\n \"Syncore database reconcile without precise impact scopes is unsupported.\"\n );\n }\n\n private async rerunActiveQuery(\n record: ActiveQueryRecord,\n meta?: { executionId?: string; parentExecutionId?: string }\n ): Promise<void> {\n record.dependencyKeys.clear();\n try {\n const result = await this.deps.runQuery(\n { kind: \"query\", name: record.functionName },\n record.args,\n meta\n );\n record.lastResult = result;\n record.lastError = undefined;\n record.lastRunAt = Date.now();\n record.dependencyKeys = await this.deps.collectQueryDependencies(\n record.functionName,\n record.args\n );\n } catch (error) {\n record.lastError = error as Error;\n record.lastRunAt = Date.now();\n }\n for (const listener of record.listeners) {\n listener();\n }\n this.notifyActiveQueriesChanged();\n }\n\n private notifyActiveQueriesChanged(): void {\n this.deps.devtools.notifyScopes([\"runtime.summary\", \"runtime.activeQueries\"]);\n }\n\n private getInvalidatedQueriesForScopes(scopeSet: Set<ImpactScope>): Array<{\n query: ActiveQueryRecord;\n matchedScopes: ImpactScope[];\n }> {\n return [...this.activeQueries.values()]\n .map((query) => ({\n query,\n matchedScopes: [...scopeSet].filter((scope) =>\n query.dependencyKeys.has(scope)\n )\n }))\n .filter(({ matchedScopes }) => matchedScopes.length > 0);\n }\n\n private async handleExternalChangeEvent(\n event: SyncoreExternalChangeEvent\n ): Promise<void> {\n if (event.sourceId === this.deps.externalChangeSourceId) {\n return;\n }\n const result = this.deps.externalChangeApplier\n ? await this.deps.externalChangeApplier.applyExternalChange(event)\n : {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes: [...resolveChangedScopes(event)]\n };\n await this.processExternalChangeResult(result);\n }\n\n private async processExternalChangeResult(result: {\n changedScopes: ImpactScope[];\n }): Promise<void> {\n const changedScopes = new Set(result.changedScopes);\n if (changedScopes.size === 0) {\n return;\n }\n if (this.pendingExternalChangePromise) {\n this.queuedExternalChange = {\n changedScopes: new Set([\n ...(this.queuedExternalChange?.changedScopes ?? []),\n ...changedScopes\n ])\n };\n return this.pendingExternalChangePromise;\n }\n\n this.pendingExternalChangePromise = (async () => {\n this.deps.devtools.notifyScopes(toDevtoolsScopes(changedScopes));\n await this.refreshQueriesForScopes(\n changedScopes,\n `External change touched ${[...changedScopes].join(\", \")}`\n );\n })();\n\n try {\n await this.pendingExternalChangePromise;\n } finally {\n this.pendingExternalChangePromise = undefined;\n const queued = this.queuedExternalChange;\n this.queuedExternalChange = undefined;\n if (queued) {\n await this.processExternalChangeResult({\n changedScopes: [...queued.changedScopes]\n });\n }\n }\n }\n\n private createActiveQueryKey(name: string, args: JsonObject): string {\n return `${name}:${stableStringify(args)}`;\n }\n}\n\nfunction generateReactivityExecutionId(): string {\n return generateId();\n}\n\nfunction resolveChangedScopes(\n event: Pick<\n SyncoreExternalChangeEvent,\n \"scope\" | \"changedScopes\" | \"changedTables\" | \"storageIds\"\n >\n): Set<ImpactScope> {\n if (Array.isArray(event.changedScopes) && event.changedScopes.length > 0) {\n return new Set(event.changedScopes);\n }\n\n const scopes = new Set<ImpactScope>();\n for (const tableName of event.changedTables ?? []) {\n scopes.add(`table:${tableName}`);\n }\n for (const storageId of event.storageIds ?? []) {\n scopes.add(`storage:${storageId}`);\n }\n\n if (scopes.size === 0 && event.scope !== undefined) {\n throw new Error(\n `Syncore external change scope \"${event.scope}\" did not provide precise impact scopes.`\n );\n }\n\n return scopes;\n}\n\nfunction toDevtoolsScopes(\n scopes: Iterable<ImpactScope>\n): DevtoolsLiveQueryScope[] {\n const resolved = new Set<DevtoolsLiveQueryScope>();\n for (const scope of scopes) {\n if (scope.startsWith(\"row:\")) {\n const [, tableName] = scope.split(\":\");\n if (tableName) {\n resolved.add(`table:${tableName}`);\n }\n continue;\n }\n if (\n scope === \"runtime.summary\" ||\n scope === \"runtime.activeQueries\" ||\n scope === \"schema.tables\" ||\n scope === \"scheduler.jobs\" ||\n scope.startsWith(\"table:\") ||\n scope.startsWith(\"storage:\")\n ) {\n resolved.add(scope as DevtoolsLiveQueryScope);\n }\n }\n return resolved.size > 0 ? [...resolved] : [\"all\"];\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reactivityEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/reactivityEngine.ts"],"sourcesContent":["import type {\n SyncoreActiveQueryInfo,\n SyncoreDevtoolsEvent\n} from \"@syncore/devtools-protocol\";\nimport type { FunctionReference } from \"../../functions.js\";\nimport type {\n DevtoolsLiveQueryScope,\n ImpactScope,\n JsonObject,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal,\n SyncoreWatch\n} from \"../../runtime.js\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport { generateId } from \"../../id.js\";\nimport type {\n ActiveQueryRecord,\n DependencyKey\n} from \"./shared.js\";\nimport { parseCanonicalComponentFunctionName } from \"./shared.js\";\n\ntype ReactivityEngineDeps = {\n runtimeId: string;\n externalChangeSourceId: string;\n externalChangeSignal?: SyncoreExternalChangeSignal;\n externalChangeApplier?: SyncoreExternalChangeApplier;\n devtools: DevtoolsEngine;\n runQuery: <TResult>(\n reference: FunctionReference<\"query\", unknown, TResult>,\n args: JsonObject,\n meta?: { executionId?: string; parentExecutionId?: string }\n ) => Promise<TResult>;\n collectQueryDependencies: (\n functionName: string,\n args: JsonObject\n ) => Promise<Set<DependencyKey>>;\n};\n\nexport class ReactivityEngine {\n private readonly activeQueries = new Map<string, ActiveQueryRecord>();\n private detachExternalChangeListener: (() => void) | undefined;\n private pendingExternalChangePromise: Promise<void> | undefined;\n private queuedExternalChange:\n | {\n changedScopes: Set<ImpactScope>;\n }\n | undefined;\n\n constructor(private readonly deps: ReactivityEngineDeps) {}\n\n start(): void {\n this.detachExternalChangeListener =\n this.deps.externalChangeSignal?.subscribe((event) => {\n void this.handleExternalChangeEvent(event);\n });\n }\n\n stop(): void {\n this.detachExternalChangeListener?.();\n this.detachExternalChangeListener = undefined;\n }\n\n getActiveQueryInfos(): SyncoreActiveQueryInfo[] {\n return [...this.activeQueries.values()].map((query) => {\n const componentFunction = parseCanonicalComponentFunctionName(\n query.functionName\n );\n return {\n id: query.id,\n functionName: query.functionName,\n args: query.args,\n consumers: query.consumers,\n ...(componentFunction\n ? {\n owner: \"component\" as const,\n componentPath: componentFunction.componentPath\n }\n : {\n owner: \"root\" as const\n }),\n dependencyKeys: [...query.dependencyKeys],\n lastRunAt: query.lastRunAt\n };\n });\n }\n\n watchQuery<TArgs, TResult>(\n reference: FunctionReference<\"query\", TArgs, TResult>,\n args: JsonObject = {}\n ): SyncoreWatch<TResult> {\n const key = this.createActiveQueryKey(reference.name, args);\n let record = this.activeQueries.get(key);\n\n if (!record) {\n record = {\n id: key,\n functionName: reference.name,\n args,\n listeners: new Set<() => void>(),\n consumers: 0,\n dependencyKeys: new Set<DependencyKey>(),\n lastResult: undefined,\n lastError: undefined,\n lastRunAt: 0\n };\n this.activeQueries.set(key, record);\n this.notifyActiveQueriesChanged();\n void this.rerunActiveQuery(record);\n }\n\n const activeRecord = record;\n activeRecord.consumers += 1;\n let disposed = false;\n const ownedListeners = new Set<() => void>();\n\n return {\n onUpdate: (callback) => {\n activeRecord.listeners.add(callback);\n ownedListeners.add(callback);\n queueMicrotask(callback);\n return () => {\n activeRecord.listeners.delete(callback);\n ownedListeners.delete(callback);\n };\n },\n localQueryResult: () => activeRecord.lastResult as TResult | undefined,\n localQueryError: () => activeRecord.lastError,\n dispose: () => {\n if (disposed) {\n return;\n }\n disposed = true;\n for (const callback of ownedListeners) {\n activeRecord.listeners.delete(callback);\n }\n ownedListeners.clear();\n activeRecord.consumers = Math.max(0, activeRecord.consumers - 1);\n if (activeRecord.consumers === 0) {\n this.activeQueries.delete(key);\n this.notifyActiveQueriesChanged();\n }\n }\n };\n }\n\n async refreshInvalidatedQueries(\n changedTables: Set<string>,\n mutationId: string\n ): Promise<void> {\n const impactedScopes = new Set(\n [...changedTables].map((tableName) => `table:${tableName}` as ImpactScope)\n );\n await this.refreshQueriesForScopes(\n impactedScopes,\n `Mutation ${mutationId} changed ${[...changedTables].join(\", \")}`\n );\n }\n\n async refreshQueriesForScopes(\n scopes: Iterable<ImpactScope>,\n reason: string,\n cause?: { executionId?: string }\n ): Promise<string[]> {\n const scopeSet = new Set(scopes);\n if (scopeSet.size === 0) {\n return [];\n }\n const invalidatedQueryIds: string[] = [];\n for (const { query, matchedScopes } of this.getInvalidatedQueriesForScopes(\n scopeSet\n )) {\n const rerunExecutionId = generateReactivityExecutionId();\n this.deps.devtools.emit({\n type: \"query.invalidated\",\n runtimeId: this.deps.runtimeId,\n queryId: query.id,\n ...(parseCanonicalComponentFunctionName(query.functionName)\n ? {\n componentPath: parseCanonicalComponentFunctionName(query.functionName)!\n .componentPath\n }\n : {}),\n reason,\n ...(cause?.executionId ? { causedByExecutionId: cause.executionId } : {}),\n changedScopes: [...scopeSet],\n matchedScopes,\n rerunExecutionId,\n timestamp: Date.now()\n });\n invalidatedQueryIds.push(query.id);\n await this.rerunActiveQuery(query, {\n executionId: rerunExecutionId,\n ...(cause?.executionId ? { parentExecutionId: cause.executionId } : {})\n });\n }\n return invalidatedQueryIds;\n }\n\n getInvalidatedQueryIdsForScopes(scopes: Iterable<ImpactScope>): string[] {\n return this.getInvalidatedQueriesForScopes(new Set(scopes)).map(\n ({ query }) => query.id\n );\n }\n\n async publishExternalChange(\n event: Omit<SyncoreExternalChangeEvent, \"sourceId\" | \"timestamp\">\n ): Promise<void> {\n const changedScopes = resolveChangedScopes(event);\n if (changedScopes.size === 0) {\n throw new Error(\n `Syncore cannot publish external change \"${event.reason}\" without precise impact scopes.`\n );\n }\n await this.deps.externalChangeSignal?.publish({\n ...event,\n changedScopes: [...changedScopes],\n sourceId: this.deps.externalChangeSourceId,\n timestamp: Date.now()\n });\n }\n\n async publishStorageChanges(\n storageChanges: Array<{\n storageId: string;\n reason: \"storage-put\" | \"storage-delete\";\n }>\n ): Promise<void> {\n for (const change of storageChanges) {\n await this.publishExternalChange({\n scope: \"storage\",\n reason: change.reason,\n changedScopes: [`storage:${change.storageId}`],\n storageIds: [change.storageId]\n });\n }\n }\n\n async publishDatabaseReconcile(): Promise<void> {\n throw new Error(\n \"Syncore database reconcile without precise impact scopes is unsupported.\"\n );\n }\n\n private async rerunActiveQuery(\n record: ActiveQueryRecord,\n meta?: { executionId?: string; parentExecutionId?: string }\n ): Promise<void> {\n record.dependencyKeys.clear();\n try {\n const result = await this.deps.runQuery(\n { kind: \"query\", name: record.functionName },\n record.args,\n meta\n );\n record.lastResult = result;\n record.lastError = undefined;\n record.lastRunAt = Date.now();\n record.dependencyKeys = await this.deps.collectQueryDependencies(\n record.functionName,\n record.args\n );\n } catch (error) {\n record.lastError = error as Error;\n record.lastRunAt = Date.now();\n }\n for (const listener of record.listeners) {\n listener();\n }\n this.notifyActiveQueriesChanged();\n }\n\n private notifyActiveQueriesChanged(): void {\n this.deps.devtools.notifyScopes([\"runtime.summary\", \"runtime.activeQueries\"]);\n }\n\n private getInvalidatedQueriesForScopes(scopeSet: Set<ImpactScope>): Array<{\n query: ActiveQueryRecord;\n matchedScopes: ImpactScope[];\n }> {\n return [...this.activeQueries.values()]\n .map((query) => ({\n query,\n matchedScopes: [...scopeSet].filter((scope) =>\n query.dependencyKeys.has(scope)\n )\n }))\n .filter(({ matchedScopes }) => matchedScopes.length > 0);\n }\n\n private async handleExternalChangeEvent(\n event: SyncoreExternalChangeEvent\n ): Promise<void> {\n if (event.sourceId === this.deps.externalChangeSourceId) {\n return;\n }\n const result = this.deps.externalChangeApplier\n ? await this.deps.externalChangeApplier.applyExternalChange(event)\n : {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes: [...resolveChangedScopes(event)]\n };\n await this.processExternalChangeResult(result);\n }\n\n private async processExternalChangeResult(result: {\n changedScopes: ImpactScope[];\n }): Promise<void> {\n const changedScopes = new Set(result.changedScopes);\n if (changedScopes.size === 0) {\n return;\n }\n if (this.pendingExternalChangePromise) {\n this.queuedExternalChange = {\n changedScopes: new Set([\n ...(this.queuedExternalChange?.changedScopes ?? []),\n ...changedScopes\n ])\n };\n return this.pendingExternalChangePromise;\n }\n\n this.pendingExternalChangePromise = (async () => {\n this.deps.devtools.notifyScopes(toDevtoolsScopes(changedScopes));\n await this.refreshQueriesForScopes(\n changedScopes,\n `External change touched ${[...changedScopes].join(\", \")}`\n );\n })();\n\n try {\n await this.pendingExternalChangePromise;\n } finally {\n this.pendingExternalChangePromise = undefined;\n const queued = this.queuedExternalChange;\n this.queuedExternalChange = undefined;\n if (queued) {\n await this.processExternalChangeResult({\n changedScopes: [...queued.changedScopes]\n });\n }\n }\n }\n\n private createActiveQueryKey(name: string, args: JsonObject): string {\n return `${name}:${stableStringify(args)}`;\n }\n}\n\nfunction generateReactivityExecutionId(): string {\n return generateId();\n}\n\nfunction resolveChangedScopes(\n event: Pick<\n SyncoreExternalChangeEvent,\n \"scope\" | \"changedScopes\" | \"changedTables\" | \"storageIds\"\n >\n): Set<ImpactScope> {\n if (Array.isArray(event.changedScopes) && event.changedScopes.length > 0) {\n return new Set(event.changedScopes);\n }\n\n const scopes = new Set<ImpactScope>();\n for (const tableName of event.changedTables ?? []) {\n scopes.add(`table:${tableName}`);\n }\n for (const storageId of event.storageIds ?? []) {\n scopes.add(`storage:${storageId}`);\n }\n\n if (scopes.size === 0 && event.scope !== undefined) {\n throw new Error(\n `Syncore external change scope \"${event.scope}\" did not provide precise impact scopes.`\n );\n }\n\n return scopes;\n}\n\nfunction toDevtoolsScopes(\n scopes: Iterable<ImpactScope>\n): DevtoolsLiveQueryScope[] {\n const resolved = new Set<DevtoolsLiveQueryScope>();\n for (const scope of scopes) {\n if (scope.startsWith(\"row:\")) {\n const [, tableName] = scope.split(\":\");\n if (tableName) {\n resolved.add(`table:${tableName}`);\n }\n continue;\n }\n if (\n scope === \"runtime.summary\" ||\n scope === \"runtime.activeQueries\" ||\n scope === \"schema.tables\" ||\n scope === \"scheduler.jobs\" ||\n scope === \"storage.objects\" ||\n scope.startsWith(\"table:\") ||\n scope.startsWith(\"storage:\")\n ) {\n resolved.add(scope as DevtoolsLiveQueryScope);\n }\n }\n return resolved.size > 0 ? [...resolved] : [\"all\"];\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
@@ -175,7 +175,11 @@ function devtoolsScopesForEvent(event) {
175
175
  case "query.invalidated": return new Set(["runtime.summary", "runtime.activeQueries"]);
176
176
  case "mutation.committed": return new Set(["runtime.summary", ...event.changedTables.map((table) => `table:${table}`)]);
177
177
  case "scheduler.tick": return new Set(["scheduler.jobs", "runtime.summary"]);
178
- case "storage.updated": return new Set(["runtime.summary"]);
178
+ case "storage.updated": return new Set([
179
+ "runtime.summary",
180
+ "storage.objects",
181
+ `storage:${event.storageId}`
182
+ ]);
179
183
  case "action.completed":
180
184
  case "log": return new Set(["runtime.summary"]);
181
185
  default: return new Set(["runtime.summary"]);
@@ -1 +1 @@
1
- {"version":3,"file":"shared.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/shared.ts"],"sourcesContent":["import { fromZonedTime, toZonedTime } from \"date-fns-tz\";\nimport type {\n DevtoolsPreview,\n DocumentChangePreview,\n SyncoreDevtoolsEvent,\n SyncoreDevtoolsEventOrigin\n} from \"@syncore/devtools-protocol\";\nimport {\n searchIndexTableName,\n type TableDefinition,\n type Validator\n} from \"@syncore/schema\";\nimport type {\n MisfirePolicy,\n RecurringSchedule,\n SyncoreFunctionKind\n} from \"../../functions.js\";\nimport type { SyncoreComponentFunctionMetadata } from \"../../components.js\";\nimport type {\n DevtoolsLiveQueryScope,\n JsonObject,\n QueryCondition,\n QueryExpression,\n SearchQuery,\n SyncoreDataModel,\n SyncoreExternalChangeReason\n} from \"../../runtime.js\";\n\nexport type DatabaseRow = {\n _id: string;\n _creationTime: number;\n _json: string;\n};\n\nexport type DependencyKey = string;\n\nexport type ActiveQueryRecord = {\n id: string;\n functionName: string;\n args: JsonObject;\n listeners: Set<() => void>;\n consumers: number;\n dependencyKeys: Set<DependencyKey>;\n lastResult: unknown;\n lastError: Error | undefined;\n lastRunAt: number;\n};\n\nexport type DevtoolsEventMeta = {\n origin?: SyncoreDevtoolsEventOrigin;\n executionId?: string;\n parentExecutionId?: string;\n schedulerJobId?: string;\n schedulerRun?: boolean;\n};\n\nexport type ScheduledJobRow = {\n id: string;\n function_name: string;\n function_kind: SyncoreFunctionKind;\n args_json: string;\n status: \"scheduled\" | \"completed\" | \"failed\" | \"cancelled\" | \"skipped\";\n run_at: number;\n created_at: number;\n updated_at: number;\n recurring_name: string | null;\n schedule_json: string | null;\n timezone: string | null;\n misfire_policy: string;\n last_run_at: number | null;\n window_ms: number | null;\n};\n\nexport type StorageMetadataRow = {\n _id: string;\n _creationTime: number;\n file_name: string | null;\n content_type: string | null;\n size: number;\n path: string;\n};\n\nexport type StoragePendingRow = {\n _id: string;\n _creationTime: number;\n file_name: string | null;\n content_type: string | null;\n};\n\nexport type QuerySource =\n | { type: \"table\" }\n | { type: \"index\"; name: string; range: QueryCondition[] }\n | { type: \"search\"; name: string; query: SearchQuery };\n\nexport type ExecuteQueryBuilderOptions = {\n tableName: string;\n source: QuerySource;\n filterExpression: QueryExpression | undefined;\n orderDirection: \"asc\" | \"desc\";\n dependencyCollector?: Set<DependencyKey>;\n limit?: number;\n offset?: number;\n};\n\nexport type RuntimeExecutionState = {\n executionId?: string;\n mutationDepth: number;\n changedTables: Set<string>;\n documentChanges: DocumentChangePreview[];\n storageChanges: Array<{\n storageId: string;\n reason: Extract<\n SyncoreExternalChangeReason,\n \"storage-put\" | \"storage-delete\"\n >;\n }>;\n dependencyCollector?: Set<DependencyKey>;\n componentMetadata?: SyncoreComponentFunctionMetadata | undefined;\n};\n\nconst PREVIEW_MAX_DEPTH = 5;\nconst PREVIEW_MAX_ARRAY_ITEMS = 50;\nconst PREVIEW_MAX_OBJECT_KEYS = 80;\nconst PREVIEW_MAX_STRING_LENGTH = 4000;\n\nexport function createDevtoolsPreview(value: unknown): DevtoolsPreview {\n const seen = new WeakSet<object>();\n let truncated = false;\n\n const preview = (input: unknown, depth: number): unknown => {\n if (typeof input === \"string\") {\n if (input.length > PREVIEW_MAX_STRING_LENGTH) {\n truncated = true;\n return `${input.slice(0, PREVIEW_MAX_STRING_LENGTH)}...`;\n }\n return input;\n }\n if (\n input === null ||\n typeof input === \"number\" ||\n typeof input === \"boolean\"\n ) {\n return input;\n }\n if (typeof input === \"bigint\") {\n return `${input.toString()}n`;\n }\n if (input === undefined) {\n return \"[undefined]\";\n }\n if (typeof input === \"function\") {\n truncated = true;\n return \"[function]\";\n }\n if (typeof input === \"symbol\") {\n truncated = true;\n return input.toString();\n }\n if (input instanceof Error) {\n return {\n name: input.name,\n message: input.message,\n ...(input.stack ? { stack: input.stack } : {})\n };\n }\n if (input instanceof Date) {\n return input.toISOString();\n }\n if (typeof input !== \"object\") {\n return String(input);\n }\n if (seen.has(input)) {\n truncated = true;\n return \"[circular]\";\n }\n if (depth >= PREVIEW_MAX_DEPTH) {\n truncated = true;\n return Array.isArray(input) ? \"[array]\" : \"[object]\";\n }\n\n seen.add(input);\n if (Array.isArray(input)) {\n const items = input\n .slice(0, PREVIEW_MAX_ARRAY_ITEMS)\n .map((item) => preview(item, depth + 1));\n if (input.length > PREVIEW_MAX_ARRAY_ITEMS) {\n truncated = true;\n items.push(`[${input.length - PREVIEW_MAX_ARRAY_ITEMS} more items]`);\n }\n seen.delete(input);\n return items;\n }\n\n const entries = Object.entries(input as Record<string, unknown>);\n const result: Record<string, unknown> = {};\n for (const [key, nested] of entries.slice(0, PREVIEW_MAX_OBJECT_KEYS)) {\n result[key] = preview(nested, depth + 1);\n }\n if (entries.length > PREVIEW_MAX_OBJECT_KEYS) {\n truncated = true;\n result.__truncatedKeys = entries.length - PREVIEW_MAX_OBJECT_KEYS;\n }\n seen.delete(input);\n return result;\n };\n\n try {\n return {\n kind: \"value\",\n value: preview(value, 0),\n ...(truncated ? { truncated: true } : {})\n };\n } catch (error) {\n return {\n kind: \"error\",\n message: error instanceof Error ? error.message : String(error),\n truncated: true\n };\n }\n}\n\nexport function fieldExpression(tableAlias: string, field: string): string {\n const prefix = tableAlias ? `${tableAlias}.` : \"\";\n return `json_extract(${prefix}_json, '$.${field}')`;\n}\n\nexport function quoteIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nexport function sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n\nexport function omitSystemFields<TDocument extends object>(\n document: TDocument\n): JsonObject {\n const clone = { ...(document as Record<string, unknown>) };\n delete clone._id;\n delete clone._creationTime;\n return clone;\n}\n\nexport function toSearchValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n if (value === null || value === undefined) {\n return \"\";\n }\n if (\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n typeof value === \"bigint\"\n ) {\n return String(value);\n }\n return stableStringify(value);\n}\n\nexport function parseMisfirePolicy(\n type: string,\n windowMs: number | null\n): MisfirePolicy {\n if (type === \"windowed\") {\n return { type, windowMs: windowMs ?? 0 };\n }\n if (type === \"skip\" || type === \"run_once_if_missed\") {\n return { type };\n }\n return { type: \"catch_up\" };\n}\n\nexport function shouldRunMissedJob(\n scheduledAt: number,\n now: number,\n policy: MisfirePolicy\n): boolean {\n if (scheduledAt >= now) {\n return true;\n }\n switch (policy.type) {\n case \"catch_up\":\n return true;\n case \"run_once_if_missed\":\n return true;\n case \"skip\":\n return false;\n case \"windowed\":\n return now - scheduledAt <= policy.windowMs;\n }\n}\n\nexport function computeNextRun(\n schedule: RecurringSchedule,\n fromTimestamp: number\n): number {\n switch (schedule.type) {\n case \"interval\":\n return fromTimestamp + intervalToMs(schedule);\n case \"daily\":\n return nextDailyOccurrence(fromTimestamp, schedule);\n case \"weekly\":\n return nextWeeklyOccurrence(fromTimestamp, schedule);\n }\n}\n\nexport function safeReadRecurringSchedule(\n scheduleJson: string | null\n): RecurringSchedule | undefined {\n if (!scheduleJson) {\n return undefined;\n }\n try {\n const parsed = JSON.parse(scheduleJson) as unknown;\n return isRecurringSchedule(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function normalizeOptionalArgs<TArgs>(\n args: [] | [TArgs] | readonly unknown[]\n): TArgs {\n return (args[0] ?? {}) as TArgs;\n}\n\nexport function splitSchedulerArgs(\n args: readonly unknown[]\n): [JsonObject, MisfirePolicy | undefined] {\n if (args.length === 0) {\n return [{}, undefined];\n }\n if (args.length === 1) {\n const [first] = args;\n if (isMisfirePolicy(first)) {\n return [{}, first];\n }\n return [(first ?? {}) as JsonObject, undefined];\n }\n return [(args[0] ?? {}) as JsonObject, args[1] as MisfirePolicy | undefined];\n}\n\nexport function inferDriverDatabasePath(driver: {\n filename?: string;\n databasePath?: string;\n}): string | undefined {\n return driver.databasePath ?? driver.filename;\n}\n\nexport function parseCanonicalComponentFunctionName(functionName: string):\n | {\n componentPath: string;\n visibility: \"public\" | \"internal\";\n localName: string;\n }\n | undefined {\n const match = /^components\\/(.+)\\/(public|internal)\\/(.+)$/.exec(functionName);\n if (!match) {\n return undefined;\n }\n return {\n componentPath: match[1] ?? \"\",\n visibility: (match[2] as \"public\" | \"internal\") ?? \"public\",\n localName: match[3] ?? \"\"\n };\n}\n\nexport function parseComponentScopedIdentifier(\n value: string\n): { componentPath: string; localId: string } | undefined {\n const match = /^component:([^:]+):(.+)$/.exec(value);\n if (!match) {\n return undefined;\n }\n return {\n componentPath: match[1] ?? \"\",\n localId: match[2] ?? \"\"\n };\n}\n\nexport function devtoolsScopesForEvent(\n event: SyncoreDevtoolsEvent\n): Set<DevtoolsLiveQueryScope> {\n switch (event.type) {\n case \"runtime.connected\":\n case \"runtime.disconnected\":\n return new Set([\"runtime.summary\", \"runtime.activeQueries\"]);\n case \"query.executed\":\n case \"query.invalidated\":\n return new Set([\"runtime.summary\", \"runtime.activeQueries\"]);\n case \"mutation.committed\":\n return new Set([\n \"runtime.summary\",\n ...event.changedTables.map((table: string) => `table:${table}` as const)\n ]);\n case \"scheduler.tick\":\n return new Set([\"scheduler.jobs\", \"runtime.summary\"]);\n case \"storage.updated\":\n return new Set([\"runtime.summary\"]);\n case \"action.completed\":\n case \"log\":\n return new Set([\"runtime.summary\"]);\n default:\n return new Set([\"runtime.summary\"]);\n }\n}\n\nexport function getTableDefinition<\n TSchema extends SyncoreDataModel\n>(\n schema: TSchema,\n tableName: string\n): TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>\n> {\n return schema.getTable(tableName as never) as TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>\n >;\n}\n\nexport function searchIndexKey(tableName: string, indexName: string): string {\n return `${tableName}:${indexName}`;\n}\n\nexport function resolveSearchIndexTableName(\n tableName: string,\n indexName: string\n): string {\n return searchIndexTableName(tableName, indexName);\n}\n\nfunction intervalToMs(schedule: {\n seconds?: number;\n minutes?: number;\n hours?: number;\n}): number {\n if (schedule.seconds) {\n return schedule.seconds * 1000;\n }\n if (schedule.minutes) {\n return schedule.minutes * 60 * 1000;\n }\n return (schedule.hours ?? 1) * 60 * 60 * 1000;\n}\n\nfunction nextDailyOccurrence(\n fromTimestamp: number,\n schedule: Extract<RecurringSchedule, { type: \"daily\" }>\n): number {\n const timezone = schedule.timezone ?? \"UTC\";\n const now = new Date(fromTimestamp);\n const zonedNow = toZonedTime(now, timezone);\n const zoned = new Date(zonedNow.getTime());\n zoned.setHours(schedule.hour, schedule.minute, 0, 0);\n if (zoned.getTime() <= zonedNow.getTime()) {\n zoned.setDate(zoned.getDate() + 1);\n }\n return fromZonedTime(zoned, timezone).getTime();\n}\n\nfunction nextWeeklyOccurrence(\n fromTimestamp: number,\n schedule: Extract<RecurringSchedule, { type: \"weekly\" }>\n): number {\n const timezone = schedule.timezone ?? \"UTC\";\n const now = new Date(fromTimestamp);\n const zonedNow = toZonedTime(now, timezone);\n const targetDay = [\n \"sunday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\"\n ].indexOf(schedule.dayOfWeek);\n const zoned = new Date(zonedNow.getTime());\n const delta = (targetDay - zonedNow.getDay() + 7) % 7;\n zoned.setDate(zoned.getDate() + delta);\n zoned.setHours(schedule.hour, schedule.minute, 0, 0);\n if (zoned.getTime() <= zonedNow.getTime()) {\n zoned.setDate(zoned.getDate() + 7);\n }\n return fromZonedTime(zoned, timezone).getTime();\n}\n\nfunction isRecurringSchedule(value: unknown): value is RecurringSchedule {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n switch (value.type) {\n case \"interval\":\n return (\n isOptionalNumber(value.seconds) &&\n isOptionalNumber(value.minutes) &&\n isOptionalNumber(value.hours)\n );\n case \"daily\":\n return (\n typeof value.hour === \"number\" &&\n typeof value.minute === \"number\" &&\n isOptionalString(value.timezone)\n );\n case \"weekly\":\n return (\n isDayOfWeek(value.dayOfWeek) &&\n typeof value.hour === \"number\" &&\n typeof value.minute === \"number\" &&\n isOptionalString(value.timezone)\n );\n default:\n return false;\n }\n}\n\nfunction isMisfirePolicy(value: unknown): value is MisfirePolicy {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"type\" in value &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isOptionalNumber(value: unknown): value is number | undefined {\n return value === undefined || typeof value === \"number\";\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n return value === undefined || typeof value === \"string\";\n}\n\nfunction isDayOfWeek(\n value: unknown\n): value is Extract<RecurringSchedule, { type: \"weekly\" }>[\"dayOfWeek\"] {\n return (\n value === \"sunday\" ||\n value === \"monday\" ||\n value === \"tuesday\" ||\n value === \"wednesday\" ||\n value === \"thursday\" ||\n value === \"friday\" ||\n value === \"saturday\"\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"shared.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/shared.ts"],"sourcesContent":["import { fromZonedTime, toZonedTime } from \"date-fns-tz\";\nimport type {\n DevtoolsPreview,\n DocumentChangePreview,\n SyncoreDevtoolsEvent,\n SyncoreDevtoolsEventOrigin\n} from \"@syncore/devtools-protocol\";\nimport {\n searchIndexTableName,\n type TableDefinition,\n type Validator\n} from \"@syncore/schema\";\nimport type {\n MisfirePolicy,\n RecurringSchedule,\n SyncoreFunctionKind\n} from \"../../functions.js\";\nimport type { SyncoreComponentFunctionMetadata } from \"../../components.js\";\nimport type {\n DevtoolsLiveQueryScope,\n JsonObject,\n QueryCondition,\n QueryExpression,\n SearchQuery,\n SyncoreDataModel,\n SyncoreExternalChangeReason\n} from \"../../runtime.js\";\n\nexport type DatabaseRow = {\n _id: string;\n _creationTime: number;\n _json: string;\n};\n\nexport type DependencyKey = string;\n\nexport type ActiveQueryRecord = {\n id: string;\n functionName: string;\n args: JsonObject;\n listeners: Set<() => void>;\n consumers: number;\n dependencyKeys: Set<DependencyKey>;\n lastResult: unknown;\n lastError: Error | undefined;\n lastRunAt: number;\n};\n\nexport type DevtoolsEventMeta = {\n origin?: SyncoreDevtoolsEventOrigin;\n executionId?: string;\n parentExecutionId?: string;\n schedulerJobId?: string;\n schedulerRun?: boolean;\n};\n\nexport type ScheduledJobRow = {\n id: string;\n function_name: string;\n function_kind: SyncoreFunctionKind;\n args_json: string;\n status: \"scheduled\" | \"completed\" | \"failed\" | \"cancelled\" | \"skipped\";\n run_at: number;\n created_at: number;\n updated_at: number;\n recurring_name: string | null;\n schedule_json: string | null;\n timezone: string | null;\n misfire_policy: string;\n last_run_at: number | null;\n window_ms: number | null;\n};\n\nexport type StorageMetadataRow = {\n _id: string;\n _creationTime: number;\n file_name: string | null;\n content_type: string | null;\n size: number;\n path: string;\n};\n\nexport type StoragePendingRow = {\n _id: string;\n _creationTime: number;\n file_name: string | null;\n content_type: string | null;\n};\n\nexport type QuerySource =\n | { type: \"table\" }\n | { type: \"index\"; name: string; range: QueryCondition[] }\n | { type: \"search\"; name: string; query: SearchQuery };\n\nexport type ExecuteQueryBuilderOptions = {\n tableName: string;\n source: QuerySource;\n filterExpression: QueryExpression | undefined;\n orderDirection: \"asc\" | \"desc\";\n dependencyCollector?: Set<DependencyKey>;\n limit?: number;\n offset?: number;\n};\n\nexport type RuntimeExecutionState = {\n executionId?: string;\n mutationDepth: number;\n changedTables: Set<string>;\n documentChanges: DocumentChangePreview[];\n storageChanges: Array<{\n storageId: string;\n reason: Extract<\n SyncoreExternalChangeReason,\n \"storage-put\" | \"storage-delete\"\n >;\n }>;\n dependencyCollector?: Set<DependencyKey>;\n componentMetadata?: SyncoreComponentFunctionMetadata | undefined;\n};\n\nconst PREVIEW_MAX_DEPTH = 5;\nconst PREVIEW_MAX_ARRAY_ITEMS = 50;\nconst PREVIEW_MAX_OBJECT_KEYS = 80;\nconst PREVIEW_MAX_STRING_LENGTH = 4000;\n\nexport function createDevtoolsPreview(value: unknown): DevtoolsPreview {\n const seen = new WeakSet<object>();\n let truncated = false;\n\n const preview = (input: unknown, depth: number): unknown => {\n if (typeof input === \"string\") {\n if (input.length > PREVIEW_MAX_STRING_LENGTH) {\n truncated = true;\n return `${input.slice(0, PREVIEW_MAX_STRING_LENGTH)}...`;\n }\n return input;\n }\n if (\n input === null ||\n typeof input === \"number\" ||\n typeof input === \"boolean\"\n ) {\n return input;\n }\n if (typeof input === \"bigint\") {\n return `${input.toString()}n`;\n }\n if (input === undefined) {\n return \"[undefined]\";\n }\n if (typeof input === \"function\") {\n truncated = true;\n return \"[function]\";\n }\n if (typeof input === \"symbol\") {\n truncated = true;\n return input.toString();\n }\n if (input instanceof Error) {\n return {\n name: input.name,\n message: input.message,\n ...(input.stack ? { stack: input.stack } : {})\n };\n }\n if (input instanceof Date) {\n return input.toISOString();\n }\n if (typeof input !== \"object\") {\n return String(input);\n }\n if (seen.has(input)) {\n truncated = true;\n return \"[circular]\";\n }\n if (depth >= PREVIEW_MAX_DEPTH) {\n truncated = true;\n return Array.isArray(input) ? \"[array]\" : \"[object]\";\n }\n\n seen.add(input);\n if (Array.isArray(input)) {\n const items = input\n .slice(0, PREVIEW_MAX_ARRAY_ITEMS)\n .map((item) => preview(item, depth + 1));\n if (input.length > PREVIEW_MAX_ARRAY_ITEMS) {\n truncated = true;\n items.push(`[${input.length - PREVIEW_MAX_ARRAY_ITEMS} more items]`);\n }\n seen.delete(input);\n return items;\n }\n\n const entries = Object.entries(input as Record<string, unknown>);\n const result: Record<string, unknown> = {};\n for (const [key, nested] of entries.slice(0, PREVIEW_MAX_OBJECT_KEYS)) {\n result[key] = preview(nested, depth + 1);\n }\n if (entries.length > PREVIEW_MAX_OBJECT_KEYS) {\n truncated = true;\n result.__truncatedKeys = entries.length - PREVIEW_MAX_OBJECT_KEYS;\n }\n seen.delete(input);\n return result;\n };\n\n try {\n return {\n kind: \"value\",\n value: preview(value, 0),\n ...(truncated ? { truncated: true } : {})\n };\n } catch (error) {\n return {\n kind: \"error\",\n message: error instanceof Error ? error.message : String(error),\n truncated: true\n };\n }\n}\n\nexport function fieldExpression(tableAlias: string, field: string): string {\n const prefix = tableAlias ? `${tableAlias}.` : \"\";\n return `json_extract(${prefix}_json, '$.${field}')`;\n}\n\nexport function quoteIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nexport function stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nexport function sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n\nexport function omitSystemFields<TDocument extends object>(\n document: TDocument\n): JsonObject {\n const clone = { ...(document as Record<string, unknown>) };\n delete clone._id;\n delete clone._creationTime;\n return clone;\n}\n\nexport function toSearchValue(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n if (value === null || value === undefined) {\n return \"\";\n }\n if (\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n typeof value === \"bigint\"\n ) {\n return String(value);\n }\n return stableStringify(value);\n}\n\nexport function parseMisfirePolicy(\n type: string,\n windowMs: number | null\n): MisfirePolicy {\n if (type === \"windowed\") {\n return { type, windowMs: windowMs ?? 0 };\n }\n if (type === \"skip\" || type === \"run_once_if_missed\") {\n return { type };\n }\n return { type: \"catch_up\" };\n}\n\nexport function shouldRunMissedJob(\n scheduledAt: number,\n now: number,\n policy: MisfirePolicy\n): boolean {\n if (scheduledAt >= now) {\n return true;\n }\n switch (policy.type) {\n case \"catch_up\":\n return true;\n case \"run_once_if_missed\":\n return true;\n case \"skip\":\n return false;\n case \"windowed\":\n return now - scheduledAt <= policy.windowMs;\n }\n}\n\nexport function computeNextRun(\n schedule: RecurringSchedule,\n fromTimestamp: number\n): number {\n switch (schedule.type) {\n case \"interval\":\n return fromTimestamp + intervalToMs(schedule);\n case \"daily\":\n return nextDailyOccurrence(fromTimestamp, schedule);\n case \"weekly\":\n return nextWeeklyOccurrence(fromTimestamp, schedule);\n }\n}\n\nexport function safeReadRecurringSchedule(\n scheduleJson: string | null\n): RecurringSchedule | undefined {\n if (!scheduleJson) {\n return undefined;\n }\n try {\n const parsed = JSON.parse(scheduleJson) as unknown;\n return isRecurringSchedule(parsed) ? parsed : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function normalizeOptionalArgs<TArgs>(\n args: [] | [TArgs] | readonly unknown[]\n): TArgs {\n return (args[0] ?? {}) as TArgs;\n}\n\nexport function splitSchedulerArgs(\n args: readonly unknown[]\n): [JsonObject, MisfirePolicy | undefined] {\n if (args.length === 0) {\n return [{}, undefined];\n }\n if (args.length === 1) {\n const [first] = args;\n if (isMisfirePolicy(first)) {\n return [{}, first];\n }\n return [(first ?? {}) as JsonObject, undefined];\n }\n return [(args[0] ?? {}) as JsonObject, args[1] as MisfirePolicy | undefined];\n}\n\nexport function inferDriverDatabasePath(driver: {\n filename?: string;\n databasePath?: string;\n}): string | undefined {\n return driver.databasePath ?? driver.filename;\n}\n\nexport function parseCanonicalComponentFunctionName(functionName: string):\n | {\n componentPath: string;\n visibility: \"public\" | \"internal\";\n localName: string;\n }\n | undefined {\n const match = /^components\\/(.+)\\/(public|internal)\\/(.+)$/.exec(functionName);\n if (!match) {\n return undefined;\n }\n return {\n componentPath: match[1] ?? \"\",\n visibility: (match[2] as \"public\" | \"internal\") ?? \"public\",\n localName: match[3] ?? \"\"\n };\n}\n\nexport function parseComponentScopedIdentifier(\n value: string\n): { componentPath: string; localId: string } | undefined {\n const match = /^component:([^:]+):(.+)$/.exec(value);\n if (!match) {\n return undefined;\n }\n return {\n componentPath: match[1] ?? \"\",\n localId: match[2] ?? \"\"\n };\n}\n\nexport function devtoolsScopesForEvent(\n event: SyncoreDevtoolsEvent\n): Set<DevtoolsLiveQueryScope> {\n switch (event.type) {\n case \"runtime.connected\":\n case \"runtime.disconnected\":\n return new Set([\"runtime.summary\", \"runtime.activeQueries\"]);\n case \"query.executed\":\n case \"query.invalidated\":\n return new Set([\"runtime.summary\", \"runtime.activeQueries\"]);\n case \"mutation.committed\":\n return new Set([\n \"runtime.summary\",\n ...event.changedTables.map((table: string) => `table:${table}` as const)\n ]);\n case \"scheduler.tick\":\n return new Set([\"scheduler.jobs\", \"runtime.summary\"]);\n case \"storage.updated\":\n return new Set<DevtoolsLiveQueryScope>([\n \"runtime.summary\",\n \"storage.objects\",\n `storage:${event.storageId}` as const\n ]);\n case \"action.completed\":\n case \"log\":\n return new Set([\"runtime.summary\"]);\n default:\n return new Set([\"runtime.summary\"]);\n }\n}\n\nexport function getTableDefinition<\n TSchema extends SyncoreDataModel\n>(\n schema: TSchema,\n tableName: string\n): TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>\n> {\n return schema.getTable(tableName as never) as TableDefinition<\n Validator<Record<string, unknown>, Record<string, unknown>, string>\n >;\n}\n\nexport function searchIndexKey(tableName: string, indexName: string): string {\n return `${tableName}:${indexName}`;\n}\n\nexport function resolveSearchIndexTableName(\n tableName: string,\n indexName: string\n): string {\n return searchIndexTableName(tableName, indexName);\n}\n\nfunction intervalToMs(schedule: {\n seconds?: number;\n minutes?: number;\n hours?: number;\n}): number {\n if (schedule.seconds) {\n return schedule.seconds * 1000;\n }\n if (schedule.minutes) {\n return schedule.minutes * 60 * 1000;\n }\n return (schedule.hours ?? 1) * 60 * 60 * 1000;\n}\n\nfunction nextDailyOccurrence(\n fromTimestamp: number,\n schedule: Extract<RecurringSchedule, { type: \"daily\" }>\n): number {\n const timezone = schedule.timezone ?? \"UTC\";\n const now = new Date(fromTimestamp);\n const zonedNow = toZonedTime(now, timezone);\n const zoned = new Date(zonedNow.getTime());\n zoned.setHours(schedule.hour, schedule.minute, 0, 0);\n if (zoned.getTime() <= zonedNow.getTime()) {\n zoned.setDate(zoned.getDate() + 1);\n }\n return fromZonedTime(zoned, timezone).getTime();\n}\n\nfunction nextWeeklyOccurrence(\n fromTimestamp: number,\n schedule: Extract<RecurringSchedule, { type: \"weekly\" }>\n): number {\n const timezone = schedule.timezone ?? \"UTC\";\n const now = new Date(fromTimestamp);\n const zonedNow = toZonedTime(now, timezone);\n const targetDay = [\n \"sunday\",\n \"monday\",\n \"tuesday\",\n \"wednesday\",\n \"thursday\",\n \"friday\",\n \"saturday\"\n ].indexOf(schedule.dayOfWeek);\n const zoned = new Date(zonedNow.getTime());\n const delta = (targetDay - zonedNow.getDay() + 7) % 7;\n zoned.setDate(zoned.getDate() + delta);\n zoned.setHours(schedule.hour, schedule.minute, 0, 0);\n if (zoned.getTime() <= zonedNow.getTime()) {\n zoned.setDate(zoned.getDate() + 7);\n }\n return fromZonedTime(zoned, timezone).getTime();\n}\n\nfunction isRecurringSchedule(value: unknown): value is RecurringSchedule {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n switch (value.type) {\n case \"interval\":\n return (\n isOptionalNumber(value.seconds) &&\n isOptionalNumber(value.minutes) &&\n isOptionalNumber(value.hours)\n );\n case \"daily\":\n return (\n typeof value.hour === \"number\" &&\n typeof value.minute === \"number\" &&\n isOptionalString(value.timezone)\n );\n case \"weekly\":\n return (\n isDayOfWeek(value.dayOfWeek) &&\n typeof value.hour === \"number\" &&\n typeof value.minute === \"number\" &&\n isOptionalString(value.timezone)\n );\n default:\n return false;\n }\n}\n\nfunction isMisfirePolicy(value: unknown): value is MisfirePolicy {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"type\" in value &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isOptionalNumber(value: unknown): value is number | undefined {\n return value === undefined || typeof value === \"number\";\n}\n\nfunction isOptionalString(value: unknown): value is string | undefined {\n return value === undefined || typeof value === \"string\";\n}\n\nfunction isDayOfWeek(\n value: unknown\n): value is Extract<RecurringSchedule, { type: \"weekly\" }>[\"dayOfWeek\"] {\n return (\n value === \"sunday\" ||\n value === \"monday\" ||\n value === \"tuesday\" ||\n value === \"wednesday\" ||\n value === \"thursday\" ||\n value === \"friday\" ||\n value === \"saturday\"\n );\n}\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { generateId } from "../../id.mjs";
2
2
  //#region src/runtime/internal/engines/storageEngine.ts
3
+ const STORAGE_RANGE_FALLBACK_LIMIT_BYTES = 8 * 1024 * 1024;
3
4
  var StorageEngine = class {
4
5
  deps;
5
6
  constructor(deps) {
@@ -54,6 +55,74 @@ var StorageEngine = class {
54
55
  });
55
56
  }
56
57
  }
58
+ async listObjects(options = {}) {
59
+ const limit = Math.min(Math.max(options.limit ?? 100, 1), 500);
60
+ const offset = Math.max(options.offset ?? 0, 0);
61
+ const search = options.search?.trim();
62
+ const params = [];
63
+ const whereClauses = [];
64
+ if (search) {
65
+ const pattern = `%${search}%`;
66
+ whereClauses.push(`(_id LIKE ? OR file_name LIKE ? OR content_type LIKE ?)`);
67
+ params.push(pattern, pattern, pattern);
68
+ }
69
+ const whereSql = whereClauses.length > 0 ? ` WHERE ${whereClauses.join(" AND ")}` : "";
70
+ const rows = await this.deps.driver.all(`SELECT _id, _creationTime, file_name, content_type, size, path FROM "_storage"${whereSql} ORDER BY _creationTime DESC LIMIT ? OFFSET ?`, [
71
+ ...params,
72
+ limit,
73
+ offset
74
+ ]);
75
+ const countRow = await this.deps.driver.get(`SELECT COUNT(*) as count FROM "_storage"${whereSql}`, params);
76
+ return {
77
+ entries: rows.map(storageEntryFromRow),
78
+ totalCount: countRow?.count ?? 0
79
+ };
80
+ }
81
+ async getObjectAccessInfo(id) {
82
+ const row = await this.deps.driver.get(`SELECT _id, _creationTime, file_name, content_type, size, path FROM "_storage" WHERE _id = ?`, [id]);
83
+ if (!row) return null;
84
+ return {
85
+ entry: storageEntryFromRow(row),
86
+ supportsRange: storageSupportsRange(this.deps.storage)
87
+ };
88
+ }
89
+ async readObjectRange(id, offset, length) {
90
+ const row = await this.deps.driver.get(`SELECT _id, _creationTime, file_name, content_type, size, path FROM "_storage" WHERE _id = ?`, [id]);
91
+ if (!row) return null;
92
+ const normalizedOffset = Math.max(Math.floor(offset), 0);
93
+ const normalizedLength = Math.max(Math.floor(length), 0);
94
+ const entry = storageEntryFromRow(row);
95
+ const supportsRange = storageSupportsRange(this.deps.storage);
96
+ if (normalizedLength === 0) return {
97
+ entry,
98
+ bytes: new Uint8Array(),
99
+ offset: normalizedOffset,
100
+ bytesRead: 0,
101
+ done: normalizedOffset >= row.size,
102
+ supportsRange
103
+ };
104
+ let bytes;
105
+ if (supportsRange && this.deps.storage.readRange) bytes = await this.deps.storage.readRange(id, normalizedOffset, normalizedLength);
106
+ else {
107
+ if (row.size > STORAGE_RANGE_FALLBACK_LIMIT_BYTES) throw new Error("This storage backend does not support ranged reads for large files.");
108
+ const fullBytes = await this.deps.storage.read(id);
109
+ bytes = fullBytes ? fullBytes.slice(normalizedOffset, normalizedOffset + normalizedLength) : null;
110
+ }
111
+ if (!bytes) return null;
112
+ return {
113
+ entry,
114
+ bytes,
115
+ offset: normalizedOffset,
116
+ bytesRead: bytes.byteLength,
117
+ done: normalizedOffset + bytes.byteLength >= row.size,
118
+ supportsRange
119
+ };
120
+ }
121
+ async deleteObject(id, meta = {}) {
122
+ if (!await this.deps.driver.get(`SELECT _id FROM "_storage" WHERE _id = ?`, [id])) return false;
123
+ await this.deleteCommittedObject(id, void 0, meta);
124
+ return true;
125
+ }
57
126
  createStorageApi(state) {
58
127
  const componentMetadata = state.componentMetadata;
59
128
  const namespacePrefix = componentMetadata ? `component:${componentMetadata.componentPath}:` : "";
@@ -118,19 +187,7 @@ var StorageEngine = class {
118
187
  },
119
188
  delete: async (id) => {
120
189
  ensureStorageCapability();
121
- await this.deps.storage.delete(id);
122
- await this.deps.driver.withTransaction(async () => {
123
- await this.deps.driver.run(`DELETE FROM "_storage" WHERE _id = ?`, [id]);
124
- await this.deps.driver.run(`DELETE FROM "_storage_pending" WHERE _id = ?`, [id]);
125
- });
126
- this.deps.devtools.emit({
127
- type: "storage.updated",
128
- runtimeId: this.deps.runtimeId,
129
- storageId: id,
130
- ...componentMetadata ? { componentPath: componentMetadata.componentPath } : {},
131
- operation: "delete",
132
- timestamp: Date.now()
133
- });
190
+ await this.deleteCommittedObject(id, componentMetadata?.componentPath);
134
191
  state.storageChanges.push({
135
192
  storageId: id,
136
193
  reason: "storage-delete"
@@ -138,7 +195,36 @@ var StorageEngine = class {
138
195
  }
139
196
  };
140
197
  }
198
+ async deleteCommittedObject(id, componentPath, meta = {}) {
199
+ await this.deps.storage.delete(id);
200
+ await this.deps.driver.withTransaction(async () => {
201
+ await this.deps.driver.run(`DELETE FROM "_storage" WHERE _id = ?`, [id]);
202
+ await this.deps.driver.run(`DELETE FROM "_storage_pending" WHERE _id = ?`, [id]);
203
+ });
204
+ this.deps.devtools.emit({
205
+ type: "storage.updated",
206
+ runtimeId: this.deps.runtimeId,
207
+ storageId: id,
208
+ ...componentPath ? { componentPath } : {},
209
+ operation: "delete",
210
+ timestamp: Date.now(),
211
+ ...meta.origin ? { origin: meta.origin } : {}
212
+ });
213
+ }
141
214
  };
215
+ function storageEntryFromRow(row) {
216
+ return {
217
+ id: row._id,
218
+ createdAt: row._creationTime,
219
+ ...row.file_name ? { fileName: row.file_name } : {},
220
+ ...row.content_type ? { contentType: row.content_type } : {},
221
+ size: row.size,
222
+ path: row.path
223
+ };
224
+ }
225
+ function storageSupportsRange(storage) {
226
+ return Boolean(storage.readRange) && storage.supportsRange?.() !== false;
227
+ }
142
228
  //#endregion
143
229
  export { StorageEngine };
144
230
 
@@ -1 +1 @@
1
- {"version":3,"file":"storageEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/storageEngine.ts"],"sourcesContent":["import type {\n StorageObject,\n SyncoreSqlDriver,\n SyncoreStorageAdapter,\n SyncoreStorageApi,\n StorageWriteInput\n} from \"../../runtime.js\";\nimport {\n type RuntimeExecutionState,\n type StorageMetadataRow,\n type StoragePendingRow\n} from \"./shared.js\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport { generateId } from \"../../id.js\";\n\ntype StorageEngineDeps = {\n driver: SyncoreSqlDriver;\n storage: SyncoreStorageAdapter;\n runtimeId: string;\n devtools: DevtoolsEngine;\n};\n\nexport class StorageEngine {\n constructor(private readonly deps: StorageEngineDeps) {}\n\n async prepare(): Promise<void> {\n await this.deps.driver.exec(`\n CREATE TABLE IF NOT EXISTS \"_storage\" (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n file_name TEXT,\n content_type TEXT,\n size INTEGER NOT NULL,\n path TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS \"_storage_pending\" (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n file_name TEXT,\n content_type TEXT\n );\n `);\n }\n\n async reconcile(): Promise<void> {\n const pendingRows = await this.deps.driver.all<StoragePendingRow>(\n `SELECT _id, _creationTime, file_name, content_type FROM \"_storage_pending\"`\n );\n\n for (const pendingRow of pendingRows) {\n const committed = await this.deps.driver.get<\n Pick<StorageMetadataRow, \"_id\">\n >(`SELECT _id FROM \"_storage\" WHERE _id = ?`, [pendingRow._id]);\n if (!committed) {\n await this.deps.storage.delete(pendingRow._id);\n this.deps.devtools.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"warn\",\n message: `Recovered interrupted storage write ${pendingRow._id}.`,\n timestamp: Date.now()\n });\n }\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [pendingRow._id]\n );\n }\n\n if (!this.deps.storage.list) {\n return;\n }\n\n const storedRows = await this.deps.driver.all<Pick<StorageMetadataRow, \"_id\">>(\n `SELECT _id FROM \"_storage\"`\n );\n const knownIds = new Set(storedRows.map((row) => row._id));\n const physicalObjects = await this.deps.storage.list();\n for (const object of physicalObjects) {\n if (knownIds.has(object.id)) {\n continue;\n }\n await this.deps.storage.delete(object.id);\n this.deps.devtools.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"warn\",\n message: `Removed orphaned storage object ${object.id}.`,\n timestamp: Date.now()\n });\n }\n }\n\n createStorageApi(state: RuntimeExecutionState): SyncoreStorageApi {\n const componentMetadata = state.componentMetadata;\n const namespacePrefix = componentMetadata\n ? `component:${componentMetadata.componentPath}:`\n : \"\";\n const ensureStorageCapability = () => {\n if (\n componentMetadata &&\n !componentMetadata.grantedCapabilities.includes(\"storage\")\n ) {\n throw new Error(\n `Component ${JSON.stringify(componentMetadata.componentPath)} is not allowed to use storage.`\n );\n }\n };\n const scopedId = (id: string) => `${namespacePrefix}${id}`;\n\n return {\n put: async (input: StorageWriteInput) => {\n ensureStorageCapability();\n const id = scopedId(generateId());\n const createdAt = Date.now();\n await this.deps.driver.run(\n `INSERT OR REPLACE INTO \"_storage_pending\" (_id, _creationTime, file_name, content_type) VALUES (?, ?, ?, ?)`,\n [id, createdAt, input.fileName ?? null, input.contentType ?? null]\n );\n const object = await this.deps.storage.put(id, input);\n await this.deps.driver.withTransaction(async () => {\n await this.deps.driver.run(\n `INSERT OR REPLACE INTO \"_storage\" (_id, _creationTime, file_name, content_type, size, path) VALUES (?, ?, ?, ?, ?, ?)`,\n [\n id,\n createdAt,\n input.fileName ?? null,\n object.contentType,\n object.size,\n object.path\n ]\n );\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [id]\n );\n });\n this.deps.devtools.emit({\n type: \"storage.updated\",\n runtimeId: this.deps.runtimeId,\n storageId: id,\n ...(componentMetadata\n ? { componentPath: componentMetadata.componentPath }\n : {}),\n operation: \"put\",\n timestamp: Date.now()\n });\n state.storageChanges.push({\n storageId: id,\n reason: \"storage-put\"\n });\n return id;\n },\n get: async (id: string): Promise<StorageObject | null> => {\n ensureStorageCapability();\n state.dependencyCollector?.add(`storage:${id}`);\n const row = await this.deps.driver.get<StorageMetadataRow>(\n `SELECT _id, _creationTime, file_name, content_type, size, path FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n return {\n id: row._id,\n path: row.path,\n size: row.size,\n contentType: row.content_type\n };\n },\n read: async (id: string) => {\n ensureStorageCapability();\n state.dependencyCollector?.add(`storage:${id}`);\n const row = await this.deps.driver.get<Pick<StorageMetadataRow, \"_id\">>(\n `SELECT _id FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n return this.deps.storage.read(id);\n },\n delete: async (id: string) => {\n ensureStorageCapability();\n await this.deps.storage.delete(id);\n await this.deps.driver.withTransaction(async () => {\n await this.deps.driver.run(\n `DELETE FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [id]\n );\n });\n this.deps.devtools.emit({\n type: \"storage.updated\",\n runtimeId: this.deps.runtimeId,\n storageId: id,\n ...(componentMetadata\n ? { componentPath: componentMetadata.componentPath }\n : {}),\n operation: \"delete\",\n timestamp: Date.now()\n });\n state.storageChanges.push({\n storageId: id,\n reason: \"storage-delete\"\n });\n }\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"storageEngine.mjs","names":[],"sources":["../../../../src/runtime/internal/engines/storageEngine.ts"],"sourcesContent":["import type {\n StorageObject,\n SyncoreSqlDriver,\n SyncoreStorageAdapter,\n SyncoreStorageApi,\n StorageWriteInput\n} from \"../../runtime.js\";\nimport type { StorageEntry } from \"@syncore/devtools-protocol\";\nimport {\n type DevtoolsEventMeta,\n type RuntimeExecutionState,\n type StorageMetadataRow,\n type StoragePendingRow\n} from \"./shared.js\";\nimport { DevtoolsEngine } from \"./devtoolsEngine.js\";\nimport { generateId } from \"../../id.js\";\n\ntype StorageEngineDeps = {\n driver: SyncoreSqlDriver;\n storage: SyncoreStorageAdapter;\n runtimeId: string;\n devtools: DevtoolsEngine;\n};\n\nconst STORAGE_RANGE_FALLBACK_LIMIT_BYTES = 8 * 1024 * 1024;\n\nexport class StorageEngine {\n constructor(private readonly deps: StorageEngineDeps) {}\n\n async prepare(): Promise<void> {\n await this.deps.driver.exec(`\n CREATE TABLE IF NOT EXISTS \"_storage\" (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n file_name TEXT,\n content_type TEXT,\n size INTEGER NOT NULL,\n path TEXT NOT NULL\n );\n CREATE TABLE IF NOT EXISTS \"_storage_pending\" (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n file_name TEXT,\n content_type TEXT\n );\n `);\n }\n\n async reconcile(): Promise<void> {\n const pendingRows = await this.deps.driver.all<StoragePendingRow>(\n `SELECT _id, _creationTime, file_name, content_type FROM \"_storage_pending\"`\n );\n\n for (const pendingRow of pendingRows) {\n const committed = await this.deps.driver.get<\n Pick<StorageMetadataRow, \"_id\">\n >(`SELECT _id FROM \"_storage\" WHERE _id = ?`, [pendingRow._id]);\n if (!committed) {\n await this.deps.storage.delete(pendingRow._id);\n this.deps.devtools.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"warn\",\n message: `Recovered interrupted storage write ${pendingRow._id}.`,\n timestamp: Date.now()\n });\n }\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [pendingRow._id]\n );\n }\n\n if (!this.deps.storage.list) {\n return;\n }\n\n const storedRows = await this.deps.driver.all<\n Pick<StorageMetadataRow, \"_id\">\n >(`SELECT _id FROM \"_storage\"`);\n const knownIds = new Set(storedRows.map((row) => row._id));\n const physicalObjects = await this.deps.storage.list();\n for (const object of physicalObjects) {\n if (knownIds.has(object.id)) {\n continue;\n }\n await this.deps.storage.delete(object.id);\n this.deps.devtools.emit({\n type: \"log\",\n runtimeId: this.deps.runtimeId,\n level: \"warn\",\n message: `Removed orphaned storage object ${object.id}.`,\n timestamp: Date.now()\n });\n }\n }\n\n async listObjects(\n options: {\n limit?: number;\n offset?: number;\n search?: string;\n } = {}\n ): Promise<{ entries: StorageEntry[]; totalCount: number }> {\n const limit = Math.min(Math.max(options.limit ?? 100, 1), 500);\n const offset = Math.max(options.offset ?? 0, 0);\n const search = options.search?.trim();\n const params: unknown[] = [];\n const whereClauses: string[] = [];\n\n if (search) {\n const pattern = `%${search}%`;\n whereClauses.push(\n `(_id LIKE ? OR file_name LIKE ? OR content_type LIKE ?)`\n );\n params.push(pattern, pattern, pattern);\n }\n\n const whereSql =\n whereClauses.length > 0 ? ` WHERE ${whereClauses.join(\" AND \")}` : \"\";\n const rows = await this.deps.driver.all<StorageMetadataRow>(\n `SELECT _id, _creationTime, file_name, content_type, size, path FROM \"_storage\"${whereSql} ORDER BY _creationTime DESC LIMIT ? OFFSET ?`,\n [...params, limit, offset]\n );\n const countRow = await this.deps.driver.get<{ count: number }>(\n `SELECT COUNT(*) as count FROM \"_storage\"${whereSql}`,\n params\n );\n\n return {\n entries: rows.map(storageEntryFromRow),\n totalCount: countRow?.count ?? 0\n };\n }\n\n async getObjectAccessInfo(\n id: string\n ): Promise<{ entry: StorageEntry; supportsRange: boolean } | null> {\n const row = await this.deps.driver.get<StorageMetadataRow>(\n `SELECT _id, _creationTime, file_name, content_type, size, path FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n return {\n entry: storageEntryFromRow(row),\n supportsRange: storageSupportsRange(this.deps.storage)\n };\n }\n\n async readObjectRange(\n id: string,\n offset: number,\n length: number\n ): Promise<{\n entry: StorageEntry;\n bytes: Uint8Array;\n offset: number;\n bytesRead: number;\n done: boolean;\n supportsRange: boolean;\n } | null> {\n const row = await this.deps.driver.get<StorageMetadataRow>(\n `SELECT _id, _creationTime, file_name, content_type, size, path FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n\n const normalizedOffset = Math.max(Math.floor(offset), 0);\n const normalizedLength = Math.max(Math.floor(length), 0);\n const entry = storageEntryFromRow(row);\n const supportsRange = storageSupportsRange(this.deps.storage);\n if (normalizedLength === 0) {\n return {\n entry,\n bytes: new Uint8Array(),\n offset: normalizedOffset,\n bytesRead: 0,\n done: normalizedOffset >= row.size,\n supportsRange\n };\n }\n\n let bytes: Uint8Array | null;\n if (supportsRange && this.deps.storage.readRange) {\n bytes = await this.deps.storage.readRange(\n id,\n normalizedOffset,\n normalizedLength\n );\n } else {\n if (row.size > STORAGE_RANGE_FALLBACK_LIMIT_BYTES) {\n throw new Error(\n \"This storage backend does not support ranged reads for large files.\"\n );\n }\n const fullBytes = await this.deps.storage.read(id);\n bytes = fullBytes\n ? fullBytes.slice(normalizedOffset, normalizedOffset + normalizedLength)\n : null;\n }\n if (!bytes) {\n return null;\n }\n return {\n entry,\n bytes,\n offset: normalizedOffset,\n bytesRead: bytes.byteLength,\n done: normalizedOffset + bytes.byteLength >= row.size,\n supportsRange\n };\n }\n\n async deleteObject(\n id: string,\n meta: DevtoolsEventMeta = {}\n ): Promise<boolean> {\n const row = await this.deps.driver.get<Pick<StorageMetadataRow, \"_id\">>(\n `SELECT _id FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return false;\n }\n await this.deleteCommittedObject(id, undefined, meta);\n return true;\n }\n\n createStorageApi(state: RuntimeExecutionState): SyncoreStorageApi {\n const componentMetadata = state.componentMetadata;\n const namespacePrefix = componentMetadata\n ? `component:${componentMetadata.componentPath}:`\n : \"\";\n const ensureStorageCapability = () => {\n if (\n componentMetadata &&\n !componentMetadata.grantedCapabilities.includes(\"storage\")\n ) {\n throw new Error(\n `Component ${JSON.stringify(componentMetadata.componentPath)} is not allowed to use storage.`\n );\n }\n };\n const scopedId = (id: string) => `${namespacePrefix}${id}`;\n\n return {\n put: async (input: StorageWriteInput) => {\n ensureStorageCapability();\n const id = scopedId(generateId());\n const createdAt = Date.now();\n await this.deps.driver.run(\n `INSERT OR REPLACE INTO \"_storage_pending\" (_id, _creationTime, file_name, content_type) VALUES (?, ?, ?, ?)`,\n [id, createdAt, input.fileName ?? null, input.contentType ?? null]\n );\n const object = await this.deps.storage.put(id, input);\n await this.deps.driver.withTransaction(async () => {\n await this.deps.driver.run(\n `INSERT OR REPLACE INTO \"_storage\" (_id, _creationTime, file_name, content_type, size, path) VALUES (?, ?, ?, ?, ?, ?)`,\n [\n id,\n createdAt,\n input.fileName ?? null,\n object.contentType,\n object.size,\n object.path\n ]\n );\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [id]\n );\n });\n this.deps.devtools.emit({\n type: \"storage.updated\",\n runtimeId: this.deps.runtimeId,\n storageId: id,\n ...(componentMetadata\n ? { componentPath: componentMetadata.componentPath }\n : {}),\n operation: \"put\",\n timestamp: Date.now()\n });\n state.storageChanges.push({\n storageId: id,\n reason: \"storage-put\"\n });\n return id;\n },\n get: async (id: string): Promise<StorageObject | null> => {\n ensureStorageCapability();\n state.dependencyCollector?.add(`storage:${id}`);\n const row = await this.deps.driver.get<StorageMetadataRow>(\n `SELECT _id, _creationTime, file_name, content_type, size, path FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n return {\n id: row._id,\n path: row.path,\n size: row.size,\n contentType: row.content_type\n };\n },\n read: async (id: string) => {\n ensureStorageCapability();\n state.dependencyCollector?.add(`storage:${id}`);\n const row = await this.deps.driver.get<Pick<StorageMetadataRow, \"_id\">>(\n `SELECT _id FROM \"_storage\" WHERE _id = ?`,\n [id]\n );\n if (!row) {\n return null;\n }\n return this.deps.storage.read(id);\n },\n delete: async (id: string) => {\n ensureStorageCapability();\n await this.deleteCommittedObject(id, componentMetadata?.componentPath);\n state.storageChanges.push({\n storageId: id,\n reason: \"storage-delete\"\n });\n }\n };\n }\n\n private async deleteCommittedObject(\n id: string,\n componentPath?: string,\n meta: DevtoolsEventMeta = {}\n ): Promise<void> {\n await this.deps.storage.delete(id);\n await this.deps.driver.withTransaction(async () => {\n await this.deps.driver.run(`DELETE FROM \"_storage\" WHERE _id = ?`, [id]);\n await this.deps.driver.run(\n `DELETE FROM \"_storage_pending\" WHERE _id = ?`,\n [id]\n );\n });\n this.deps.devtools.emit({\n type: \"storage.updated\",\n runtimeId: this.deps.runtimeId,\n storageId: id,\n ...(componentPath ? { componentPath } : {}),\n operation: \"delete\",\n timestamp: Date.now(),\n ...(meta.origin ? { origin: meta.origin } : {})\n });\n }\n}\n\nfunction storageEntryFromRow(row: StorageMetadataRow): StorageEntry {\n return {\n id: row._id,\n createdAt: row._creationTime,\n ...(row.file_name ? { fileName: row.file_name } : {}),\n ...(row.content_type ? { contentType: row.content_type } : {}),\n size: row.size,\n path: row.path\n };\n}\n\nfunction storageSupportsRange(storage: SyncoreStorageAdapter): boolean {\n return Boolean(storage.readRange) && storage.supportsRange?.() !== false;\n}\n"],"mappings":"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"}
@@ -17,6 +17,7 @@ var RuntimeKernel = class {
17
17
  externalChangeSourceId = generateId();
18
18
  driverDatabasePath;
19
19
  capabilities;
20
+ runtimeCapabilities;
20
21
  capabilityDescriptors;
21
22
  devtoolsEngine;
22
23
  schemaEngine;
@@ -34,6 +35,10 @@ var RuntimeKernel = class {
34
35
  this.platform = options.platform ?? "node";
35
36
  this.capabilityDescriptors = Object.freeze([...options.capabilityDescriptors ?? []]);
36
37
  this.capabilities = Object.freeze({ ...options.capabilities ?? {} });
38
+ this.runtimeCapabilities = Object.freeze(options.runtimeCapabilities ?? { storage: {
39
+ available: true,
40
+ ...options.storage.supportsRange ? { supportsRange: options.storage.supportsRange() !== false } : {}
41
+ } });
37
42
  this.driverDatabasePath = inferDriverDatabasePath(options.driver);
38
43
  this.devtoolsEngine = new DevtoolsEngine({
39
44
  runtimeId: this.runtimeId,
@@ -57,7 +62,8 @@ var RuntimeKernel = class {
57
62
  this.transactionCoordinator = new TransactionCoordinator(options.driver);
58
63
  this.runtimeStatus = new RuntimeStatusController({
59
64
  kind: "starting",
60
- reason: "booting"
65
+ reason: "booting",
66
+ capabilities: this.runtimeCapabilities
61
67
  });
62
68
  this.schedulerEngine = new SchedulerEngine({
63
69
  driver: options.driver,
@@ -113,6 +119,28 @@ var RuntimeKernel = class {
113
119
  if (resolvedScopes.size > 0) await this.reactivityEngine.refreshQueriesForScopes(resolvedScopes, reason);
114
120
  await this.devtoolsEngine.forceRefresh(reason, { refreshQueriesForScopes: (requestedScopes, refreshReason) => this.reactivityEngine.refreshQueriesForScopes(requestedScopes, refreshReason) }, meta, resolvedScopes);
115
121
  },
122
+ listStorageObjects: async (options) => {
123
+ await this.prepareForDirectAccess();
124
+ return this.storageEngine.listObjects(options);
125
+ },
126
+ getStorageObjectAccessInfo: async (id) => {
127
+ await this.prepareForDirectAccess();
128
+ return this.storageEngine.getObjectAccessInfo(id);
129
+ },
130
+ readStorageObjectRange: async (id, offset, length) => {
131
+ await this.prepareForDirectAccess();
132
+ return this.storageEngine.readObjectRange(id, offset, length);
133
+ },
134
+ deleteStorageObject: async (id, meta) => {
135
+ await this.prepareForDirectAccess();
136
+ const deleted = await this.storageEngine.deleteObject(id, meta);
137
+ if (deleted) this.devtoolsEngine.notifyScopes([
138
+ "runtime.summary",
139
+ "storage.objects",
140
+ `storage:${id}`
141
+ ]);
142
+ return deleted;
143
+ },
116
144
  cancelScheduledJob: async (id) => {
117
145
  await this.prepareForDirectAccess();
118
146
  return this.schedulerEngine.cancelScheduledJob(id);
@@ -128,7 +156,8 @@ var RuntimeKernel = class {
128
156
  if (this.started) return;
129
157
  this.runtimeStatus.setStatus({
130
158
  kind: "starting",
131
- reason: "booting"
159
+ reason: "booting",
160
+ capabilities: this.runtimeCapabilities
132
161
  });
133
162
  await this.prepareForDirectAccess();
134
163
  try {
@@ -136,7 +165,10 @@ var RuntimeKernel = class {
136
165
  this.reactivityEngine.start();
137
166
  this.schedulerEngine.startPolling();
138
167
  this.started = true;
139
- this.runtimeStatus.setStatus({ kind: "ready" });
168
+ this.runtimeStatus.setStatus({
169
+ kind: "ready",
170
+ capabilities: this.runtimeCapabilities
171
+ });
140
172
  this.devtoolsEngine.emit({
141
173
  type: "runtime.connected",
142
174
  runtimeId: this.runtimeId,
@@ -151,6 +183,7 @@ var RuntimeKernel = class {
151
183
  this.runtimeStatus.setStatus({
152
184
  kind: "error",
153
185
  reason: "runtime-unavailable",
186
+ capabilities: this.runtimeCapabilities,
154
187
  ...error instanceof Error ? { error } : {}
155
188
  });
156
189
  throw error;
@@ -185,7 +218,8 @@ var RuntimeKernel = class {
185
218
  this.started = false;
186
219
  this.runtimeStatus.setStatus({
187
220
  kind: "unavailable",
188
- reason: "disposed"
221
+ reason: "disposed",
222
+ capabilities: this.runtimeCapabilities
189
223
  });
190
224
  if (stopError) throw stopError;
191
225
  }