syncorejs 0.2.0 → 0.2.2

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 (135) hide show
  1. package/README.md +2 -1
  2. package/dist/_vendor/cli/app.d.mts.map +1 -1
  3. package/dist/_vendor/cli/app.mjs +323 -42
  4. package/dist/_vendor/cli/app.mjs.map +1 -1
  5. package/dist/_vendor/cli/context.mjs +27 -9
  6. package/dist/_vendor/cli/context.mjs.map +1 -1
  7. package/dist/_vendor/cli/doctor.mjs +513 -46
  8. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  9. package/dist/_vendor/cli/messages.mjs +5 -4
  10. package/dist/_vendor/cli/messages.mjs.map +1 -1
  11. package/dist/_vendor/cli/project.mjs +110 -12
  12. package/dist/_vendor/cli/project.mjs.map +1 -1
  13. package/dist/_vendor/cli/render.mjs +57 -9
  14. package/dist/_vendor/cli/render.mjs.map +1 -1
  15. package/dist/_vendor/cli/targets.mjs +4 -3
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +13 -3
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +242 -91
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs +60 -0
  22. package/dist/_vendor/core/devtools-auth.mjs.map +1 -0
  23. package/dist/_vendor/core/index.d.mts +5 -3
  24. package/dist/_vendor/core/index.mjs +22 -2
  25. package/dist/_vendor/core/index.mjs.map +1 -1
  26. package/dist/_vendor/core/runtime/components.d.mts +111 -0
  27. package/dist/_vendor/core/runtime/components.d.mts.map +1 -0
  28. package/dist/_vendor/core/runtime/components.mjs +186 -0
  29. package/dist/_vendor/core/runtime/components.mjs.map +1 -0
  30. package/dist/_vendor/core/runtime/devtools.d.mts +4 -4
  31. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/devtools.mjs +52 -41
  33. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  34. package/dist/_vendor/core/runtime/functions.d.mts +10 -10
  35. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  36. package/dist/_vendor/core/runtime/functions.mjs +2 -2
  37. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  38. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +77 -0
  39. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -0
  40. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +617 -0
  41. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -0
  42. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +186 -0
  43. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -0
  44. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +220 -0
  45. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +203 -0
  47. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -0
  48. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +177 -0
  49. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -0
  50. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +144 -0
  51. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -0
  52. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +220 -0
  53. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -0
  54. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs +32 -0
  55. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -0
  56. package/dist/_vendor/core/runtime/internal/systemMeta.mjs +61 -0
  57. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -0
  58. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +37 -0
  59. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -0
  60. package/dist/_vendor/core/runtime/runtime.d.mts +159 -205
  61. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  62. package/dist/_vendor/core/runtime/runtime.mjs +16 -1371
  63. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  64. package/dist/_vendor/core/transport.d.mts +111 -0
  65. package/dist/_vendor/core/transport.d.mts.map +1 -0
  66. package/dist/_vendor/core/transport.mjs +419 -0
  67. package/dist/_vendor/core/transport.mjs.map +1 -0
  68. package/dist/_vendor/devtools-protocol/index.d.ts +39 -1
  69. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  70. package/dist/_vendor/devtools-protocol/index.js +25 -9
  71. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  72. package/dist/_vendor/next/index.d.ts +1 -1
  73. package/dist/_vendor/next/index.d.ts.map +1 -1
  74. package/dist/_vendor/next/index.js +31 -13
  75. package/dist/_vendor/next/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/index.d.ts +12 -12
  77. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  78. package/dist/_vendor/platform-expo/index.js +4 -2
  79. package/dist/_vendor/platform-expo/index.js.map +1 -1
  80. package/dist/_vendor/platform-expo/react.d.ts.map +1 -1
  81. package/dist/_vendor/platform-expo/react.js +11 -10
  82. package/dist/_vendor/platform-expo/react.js.map +1 -1
  83. package/dist/_vendor/platform-node/index.d.mts +23 -19
  84. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/index.mjs +13 -5
  86. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  87. package/dist/_vendor/platform-node/ipc-react.d.mts.map +1 -1
  88. package/dist/_vendor/platform-node/ipc-react.mjs +15 -2
  89. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  90. package/dist/_vendor/platform-node/ipc.d.mts +11 -35
  91. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  92. package/dist/_vendor/platform-node/ipc.mjs +3 -273
  93. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  94. package/dist/_vendor/platform-web/external-change.d.ts +2 -1
  95. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/external-change.js +2 -1
  97. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  98. package/dist/_vendor/platform-web/index.d.ts +21 -21
  99. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/index.js +44 -7
  101. package/dist/_vendor/platform-web/index.js.map +1 -1
  102. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  103. package/dist/_vendor/platform-web/react.js +29 -13
  104. package/dist/_vendor/platform-web/react.js.map +1 -1
  105. package/dist/_vendor/platform-web/worker.d.ts +11 -35
  106. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  107. package/dist/_vendor/platform-web/worker.js +3 -267
  108. package/dist/_vendor/platform-web/worker.js.map +1 -1
  109. package/dist/_vendor/react/index.d.ts +36 -20
  110. package/dist/_vendor/react/index.d.ts.map +1 -1
  111. package/dist/_vendor/react/index.js +279 -57
  112. package/dist/_vendor/react/index.js.map +1 -1
  113. package/dist/_vendor/schema/definition.d.ts +48 -63
  114. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  115. package/dist/_vendor/schema/definition.js +22 -39
  116. package/dist/_vendor/schema/definition.js.map +1 -1
  117. package/dist/_vendor/schema/index.d.ts +4 -4
  118. package/dist/_vendor/schema/index.js +2 -2
  119. package/dist/_vendor/schema/planner.d.ts +19 -2
  120. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  121. package/dist/_vendor/schema/planner.js +79 -3
  122. package/dist/_vendor/schema/planner.js.map +1 -1
  123. package/dist/_vendor/schema/validators.d.ts +141 -121
  124. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  125. package/dist/_vendor/schema/validators.js +300 -42
  126. package/dist/_vendor/schema/validators.js.map +1 -1
  127. package/dist/_vendor/svelte/index.d.ts +47 -19
  128. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  129. package/dist/_vendor/svelte/index.js +250 -20
  130. package/dist/_vendor/svelte/index.js.map +1 -1
  131. package/dist/components.d.ts +2 -0
  132. package/dist/components.js +2 -0
  133. package/dist/index.d.ts +3 -2
  134. package/dist/index.js +2 -1
  135. package/package.json +8 -3
@@ -1,13 +1,28 @@
1
1
  //#region src/index.ts
2
+ const SYNCORE_DEVTOOLS_PROTOCOL_VERSION = 1;
3
+ const SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION = 1;
4
+ const SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION = 1;
5
+ function isCompatibleVersionHandshake(handshake) {
6
+ return handshake.maxSupportedProtocolVersion >= 1 && handshake.minSupportedProtocolVersion <= 1 && handshake.protocolVersion >= handshake.minSupportedProtocolVersion && handshake.protocolVersion <= handshake.maxSupportedProtocolVersion;
7
+ }
2
8
  function createBasePublicId(input) {
3
9
  return stablePublicId(input, 0);
4
10
  }
5
11
  function createPublicId(key, keys) {
12
+ return createPublicIdWithFormatter(key, keys, stablePublicId);
13
+ }
14
+ function createPublicRuntimeId(runtimeId, runtimeIds) {
15
+ return runtimeIds ? createPublicIdWithFormatter(runtimeId, runtimeIds, stableRuntimePublicId) : stableRuntimePublicId(runtimeId, 0);
16
+ }
17
+ function createPublicTargetId(targetKey, targetKeys) {
18
+ return createPublicId(targetKey, targetKeys);
19
+ }
20
+ function createPublicIdWithFormatter(key, keys, formatPublicId) {
6
21
  const used = /* @__PURE__ */ new Set();
7
22
  for (const existingKey of [...keys].sort()) {
8
23
  let attempt = 0;
9
24
  while (true) {
10
- const candidate = stablePublicId(existingKey, attempt);
25
+ const candidate = formatPublicId(existingKey, attempt);
11
26
  if (existingKey === key && !used.has(candidate)) return candidate;
12
27
  if (!used.has(candidate)) {
13
28
  used.add(candidate);
@@ -16,24 +31,25 @@ function createPublicId(key, keys) {
16
31
  attempt += 1;
17
32
  }
18
33
  }
19
- return createBasePublicId(key);
34
+ return formatPublicId(key, 0);
20
35
  }
21
- function createPublicRuntimeId(runtimeId, runtimeIds) {
22
- return runtimeIds ? createPublicId(runtimeId, runtimeIds) : createBasePublicId(runtimeId);
36
+ function stablePublicId(input, salt) {
37
+ return (stableHash(input, salt) % 1e5).toString().padStart(5, "0");
23
38
  }
24
- function createPublicTargetId(targetKey, targetKeys) {
25
- return createPublicId(targetKey, targetKeys);
39
+ function stableRuntimePublicId(input, salt) {
40
+ const value = stableHash(input, salt);
41
+ return `${String.fromCharCode(65 + value % 26)}${(Math.floor(value / 26) % 1e3).toString().padStart(3, "0")}`;
26
42
  }
27
- function stablePublicId(input, salt) {
43
+ function stableHash(input, salt) {
28
44
  const hashInput = salt === 0 ? input : `${input}#${salt}`;
29
45
  let hash = 2166136261;
30
46
  for (let index = 0; index < hashInput.length; index += 1) {
31
47
  hash ^= hashInput.charCodeAt(index);
32
48
  hash = Math.imul(hash, 16777619);
33
49
  }
34
- return ((hash >>> 0) % 1e5).toString().padStart(5, "0");
50
+ return hash >>> 0;
35
51
  }
36
52
  //#endregion
37
- export { createBasePublicId, createPublicId, createPublicRuntimeId, createPublicTargetId };
53
+ export { SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION, SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION, SYNCORE_DEVTOOLS_PROTOCOL_VERSION, createBasePublicId, createPublicId, createPublicRuntimeId, createPublicTargetId, isCompatibleVersionHandshake };
38
54
 
39
55
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type SyncoreDevtoolsEventOrigin = \"runtime\" | \"dashboard\";\n\ntype SyncoreDevtoolsEventBase = {\n runtimeId: string;\n timestamp: number;\n origin?: SyncoreDevtoolsEventOrigin;\n};\n\nexport type SyncoreDevtoolsEvent =\n | (SyncoreDevtoolsEventBase & {\n type: \"runtime.connected\";\n platform: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"runtime.disconnected\";\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"query.executed\";\n queryId: string;\n functionName: string;\n dependencies: string[];\n durationMs: number;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"query.invalidated\";\n queryId: string;\n reason: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"mutation.committed\";\n mutationId: string;\n functionName: string;\n changedTables: string[];\n durationMs: number;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"action.completed\";\n actionId: string;\n functionName: string;\n durationMs: number;\n error?: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"scheduler.tick\";\n executedJobIds: string[];\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"storage.updated\";\n storageId: string;\n operation: \"put\" | \"delete\";\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"log\";\n level: \"info\" | \"warn\" | \"error\";\n message: string;\n });\n\nexport interface SyncoreActiveQueryInfo {\n id: string;\n functionName: string;\n dependencyKeys: string[];\n lastRunAt: number;\n}\n\nexport interface SyncoreRuntimeSummary {\n runtimeId: string;\n platform: string;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n storageProtocol?: string;\n databaseLabel?: string;\n storageIdentity?: string;\n connectedAt: number;\n activeQueryCount: number;\n recentEventCount: number;\n}\n\nexport function createBasePublicId(input: string): string {\n return stablePublicId(input, 0);\n}\n\nexport function createPublicId(\n key: string,\n keys: Iterable<string>\n): string {\n const used = new Set<string>();\n for (const existingKey of [...keys].sort()) {\n let attempt = 0;\n while (true) {\n const candidate = stablePublicId(existingKey, attempt);\n if (existingKey === key && !used.has(candidate)) {\n return candidate;\n }\n if (!used.has(candidate)) {\n used.add(candidate);\n break;\n }\n attempt += 1;\n }\n }\n return createBasePublicId(key);\n}\n\nexport function createPublicRuntimeId(\n runtimeId: string,\n runtimeIds?: Iterable<string>\n): string {\n return runtimeIds ? createPublicId(runtimeId, runtimeIds) : createBasePublicId(runtimeId);\n}\n\nexport function createPublicTargetId(\n targetKey: string,\n targetKeys: Iterable<string>\n): string {\n return createPublicId(targetKey, targetKeys);\n}\n\nfunction stablePublicId(input: string, salt: number): string {\n const hashInput = salt === 0 ? input : `${input}#${salt}`;\n let hash = 2166136261;\n for (let index = 0; index < hashInput.length; index += 1) {\n hash ^= hashInput.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n const value = (hash >>> 0) % 100000;\n return value.toString().padStart(5, \"0\");\n}\n\n/* ------------------------------------------------------------------ */\n/* Runtime → Dashboard messages */\n/* ------------------------------------------------------------------ */\n\nexport type SyncoreDevtoolsMessage =\n | {\n type: \"hello\";\n runtimeId: string;\n platform: string;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n storageProtocol?: string;\n databaseLabel?: string;\n storageIdentity?: string;\n }\n | { type: \"event\"; event: SyncoreDevtoolsEvent }\n | {\n type: \"event.batch\";\n runtimeId: string;\n events: SyncoreDevtoolsEvent[];\n }\n | { type: \"ping\" }\n | { type: \"pong\" }\n | {\n type: \"command.result\";\n commandId: string;\n runtimeId: string;\n payload: SyncoreDevtoolsCommandResultPayload;\n }\n | {\n type: \"subscription.data\";\n subscriptionId: string;\n runtimeId: string;\n payload: SyncoreDevtoolsSubscriptionResultPayload;\n }\n | {\n type: \"subscription.error\";\n subscriptionId: string;\n runtimeId: string;\n error: string;\n };\n\n/* ------------------------------------------------------------------ */\n/* Dashboard → Runtime requests */\n/* ------------------------------------------------------------------ */\n\nexport interface SyncoreDevtoolsCommand {\n type: \"command\";\n commandId: string;\n targetRuntimeId: string;\n payload: SyncoreDevtoolsCommandPayload;\n}\n\nexport interface SyncoreDevtoolsSubscribe {\n type: \"subscribe\";\n subscriptionId: string;\n targetRuntimeId: string;\n payload: SyncoreDevtoolsSubscriptionPayload;\n}\n\nexport interface SyncoreDevtoolsUnsubscribe {\n type: \"unsubscribe\";\n subscriptionId: string;\n targetRuntimeId: string;\n}\n\nexport type SyncoreDevtoolsClientMessage =\n | { type: \"ping\" }\n | SyncoreDevtoolsCommand\n | SyncoreDevtoolsSubscribe\n | SyncoreDevtoolsUnsubscribe;\n\nexport type SyncoreDevtoolsCommandPayload =\n /* Functions */\n | {\n kind: \"fn.run\";\n functionName: string;\n functionType: \"query\" | \"mutation\" | \"action\";\n args: Record<string, unknown>;\n }\n | { kind: \"data.insert\"; table: string; document: Record<string, unknown> }\n | {\n kind: \"data.patch\";\n table: string;\n id: string;\n fields: Record<string, unknown>;\n }\n | { kind: \"data.delete\"; table: string; id: string }\n /* SQL */\n | { kind: \"sql.read\"; query: string }\n | { kind: \"sql.write\"; query: string }\n /* Scheduler */\n | { kind: \"scheduler.cancel\"; jobId: string }\n | {\n kind: \"scheduler.update\";\n jobId: string;\n schedule: SchedulerRecurringSchedule;\n args: Record<string, unknown>;\n misfirePolicy: SchedulerMisfirePolicy;\n runAt?: number;\n };\n\nexport type SyncoreDevtoolsSubscriptionPayload =\n | { kind: \"runtime.summary\" }\n | { kind: \"runtime.activeQueries\" }\n | { kind: \"schema.tables\" }\n | {\n kind: \"fn.watch\";\n functionName: string;\n functionType: \"query\";\n args: Record<string, unknown>;\n }\n | {\n kind: \"data.table\";\n table: string;\n filters?: DataFilter[];\n limit?: number;\n cursor?: string;\n }\n | { kind: \"scheduler.jobs\" }\n | { kind: \"functions.catalog\" }\n | { kind: \"sql.watch\"; query: string };\n\nexport interface DataFilter {\n field: string;\n operator:\n | \"eq\"\n | \"neq\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"contains\"\n | \"startsWith\";\n value: unknown;\n}\n\n/* ------------------------------------------------------------------ */\n/* Response payloads */\n/* ------------------------------------------------------------------ */\n\nexport type SyncoreDevtoolsCommandResultPayload =\n | {\n kind: \"fn.run.result\";\n result?: unknown;\n error?: string;\n durationMs: number;\n }\n | {\n kind: \"data.mutate.result\";\n success: boolean;\n id?: string;\n error?: string;\n }\n | {\n kind: \"sql.read.result\";\n columns: string[];\n rows: unknown[][];\n error?: string;\n }\n | {\n kind: \"sql.write.result\";\n rowsAffected: number;\n error?: string;\n invalidationScopes: string[];\n }\n | {\n kind: \"scheduler.cancel.result\";\n success: boolean;\n cancelled: boolean;\n error?: string;\n }\n | {\n kind: \"scheduler.update.result\";\n success: boolean;\n updated: boolean;\n error?: string;\n job?: SchedulerJob;\n }\n | { kind: \"error\"; message: string };\n\nexport type SyncoreDevtoolsSubscriptionResultPayload =\n | { kind: \"runtime.summary.result\"; summary: SyncoreRuntimeSummary }\n | {\n kind: \"runtime.activeQueries.result\";\n activeQueries: SyncoreActiveQueryInfo[];\n }\n | {\n kind: \"fn.watch.result\";\n result?: unknown;\n error?: string;\n }\n | { kind: \"schema.tables.result\"; tables: TableSchema[] }\n | {\n kind: \"data.table.result\";\n rows: Record<string, unknown>[];\n totalCount: number;\n cursor?: string;\n }\n | { kind: \"scheduler.jobs.result\"; jobs: SchedulerJob[] }\n | { kind: \"functions.catalog.result\"; functions: FunctionDefinition[] }\n | {\n kind: \"sql.watch.result\";\n columns: string[];\n rows: unknown[][];\n observedTables: string[];\n };\n\n/* ------------------------------------------------------------------ */\n/* Shared data shapes */\n/* ------------------------------------------------------------------ */\n\nexport interface FunctionDefinition {\n name: string;\n type: \"query\" | \"mutation\" | \"action\";\n file: string;\n /** Argument validator schema (JSON Schema-like), if available */\n args?: Record<string, unknown>;\n /** Return validator schema, if available */\n returns?: Record<string, unknown>;\n}\n\nexport interface TableSchema {\n name: string;\n fields: TableField[];\n indexes: TableIndex[];\n documentCount: number;\n}\n\nexport interface TableField {\n name: string;\n type: string;\n optional: boolean;\n}\n\nexport interface TableIndex {\n name: string;\n fields: string[];\n unique: boolean;\n}\n\nexport interface SchedulerJob {\n id: string;\n functionName: string;\n args: Record<string, unknown>;\n scheduledAt: number;\n runAt: number;\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"cancelled\";\n completedAt?: number;\n result?: unknown;\n error?: string;\n durationMs?: number;\n recurringName?: string;\n schedule?: SchedulerRecurringSchedule;\n scheduleLabel?: string;\n misfirePolicy?: SchedulerMisfirePolicy;\n timezone?: string;\n lastRunAt?: number;\n updatedAt?: number;\n /** Compatibility label for older UI code. */\n cronSchedule?: string;\n}\n\nexport interface SchedulerRecurringIntervalSchedule {\n type: \"interval\";\n seconds?: number;\n minutes?: number;\n hours?: number;\n}\n\nexport interface SchedulerRecurringDailySchedule {\n type: \"daily\";\n hour: number;\n minute: number;\n timezone?: string;\n}\n\nexport interface SchedulerRecurringWeeklySchedule {\n type: \"weekly\";\n dayOfWeek:\n | \"sunday\"\n | \"monday\"\n | \"tuesday\"\n | \"wednesday\"\n | \"thursday\"\n | \"friday\"\n | \"saturday\";\n hour: number;\n minute: number;\n timezone?: string;\n}\n\nexport type SchedulerRecurringSchedule =\n | SchedulerRecurringIntervalSchedule\n | SchedulerRecurringDailySchedule\n | SchedulerRecurringWeeklySchedule;\n\nexport type SchedulerMisfirePolicy =\n | { type: \"catch_up\" }\n | { type: \"skip\" }\n | { type: \"run_once_if_missed\" }\n | { type: \"windowed\"; windowMs: number };\n"],"mappings":";AA+EA,SAAgB,mBAAmB,OAAuB;AACxD,QAAO,eAAe,OAAO,EAAE;;AAGjC,SAAgB,eACd,KACA,MACQ;CACR,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,eAAe,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE;EAC1C,IAAI,UAAU;AACd,SAAO,MAAM;GACX,MAAM,YAAY,eAAe,aAAa,QAAQ;AACtD,OAAI,gBAAgB,OAAO,CAAC,KAAK,IAAI,UAAU,CAC7C,QAAO;AAET,OAAI,CAAC,KAAK,IAAI,UAAU,EAAE;AACxB,SAAK,IAAI,UAAU;AACnB;;AAEF,cAAW;;;AAGf,QAAO,mBAAmB,IAAI;;AAGhC,SAAgB,sBACd,WACA,YACQ;AACR,QAAO,aAAa,eAAe,WAAW,WAAW,GAAG,mBAAmB,UAAU;;AAG3F,SAAgB,qBACd,WACA,YACQ;AACR,QAAO,eAAe,WAAW,WAAW;;AAG9C,SAAS,eAAe,OAAe,MAAsB;CAC3D,MAAM,YAAY,SAAS,IAAI,QAAQ,GAAG,MAAM,GAAG;CACnD,IAAI,OAAO;AACX,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS,GAAG;AACxD,UAAQ,UAAU,WAAW,MAAM;AACnC,SAAO,KAAK,KAAK,MAAM,SAAS;;AAGlC,UADe,SAAS,KAAK,KAChB,UAAU,CAAC,SAAS,GAAG,IAAI"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type SyncoreDevtoolsEventOrigin = \"runtime\" | \"dashboard\";\n\nexport const SYNCORE_DEVTOOLS_PROTOCOL_VERSION = 1;\nexport const SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION = 1;\nexport const SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION = 1;\n\nexport interface VersionHandshake {\n protocolVersion: number;\n minSupportedProtocolVersion: number;\n maxSupportedProtocolVersion: number;\n runtimeVersion?: string;\n}\n\nexport function isCompatibleVersionHandshake(\n handshake: Pick<\n VersionHandshake,\n \"protocolVersion\" | \"minSupportedProtocolVersion\" | \"maxSupportedProtocolVersion\"\n >\n): boolean {\n return (\n handshake.maxSupportedProtocolVersion >=\n SYNCORE_DEVTOOLS_MIN_SUPPORTED_PROTOCOL_VERSION &&\n handshake.minSupportedProtocolVersion <=\n SYNCORE_DEVTOOLS_MAX_SUPPORTED_PROTOCOL_VERSION &&\n handshake.protocolVersion >= handshake.minSupportedProtocolVersion &&\n handshake.protocolVersion <= handshake.maxSupportedProtocolVersion\n );\n}\n\ntype SyncoreDevtoolsEventBase = {\n runtimeId: string;\n timestamp: number;\n origin?: SyncoreDevtoolsEventOrigin;\n};\n\nexport type SyncoreDevtoolsEvent =\n | (SyncoreDevtoolsEventBase & {\n type: \"runtime.connected\";\n platform: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"runtime.disconnected\";\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"query.executed\";\n queryId: string;\n functionName: string;\n componentPath?: string;\n componentName?: string;\n dependencies: string[];\n durationMs: number;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"query.invalidated\";\n queryId: string;\n componentPath?: string;\n componentName?: string;\n reason: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"mutation.committed\";\n mutationId: string;\n functionName: string;\n componentPath?: string;\n componentName?: string;\n changedTables: string[];\n durationMs: number;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"action.completed\";\n actionId: string;\n functionName: string;\n componentPath?: string;\n componentName?: string;\n durationMs: number;\n error?: string;\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"scheduler.tick\";\n executedJobIds: string[];\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"storage.updated\";\n storageId: string;\n componentPath?: string;\n operation: \"put\" | \"delete\";\n })\n | (SyncoreDevtoolsEventBase & {\n type: \"log\";\n level: \"info\" | \"warn\" | \"error\";\n message: string;\n });\n\nexport interface SyncoreActiveQueryInfo {\n id: string;\n functionName: string;\n owner?: \"root\" | \"component\";\n componentPath?: string;\n componentName?: string;\n dependencyKeys: string[];\n lastRunAt: number;\n}\n\nexport interface SyncoreRuntimeSummary {\n runtimeId: string;\n platform: string;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n storageProtocol?: string;\n databaseLabel?: string;\n storageIdentity?: string;\n connectedAt: number;\n activeQueryCount: number;\n recentEventCount: number;\n}\n\nexport function createBasePublicId(input: string): string {\n return stablePublicId(input, 0);\n}\n\nexport function createPublicId(\n key: string,\n keys: Iterable<string>\n): string {\n return createPublicIdWithFormatter(key, keys, stablePublicId);\n}\n\nexport function createPublicRuntimeId(\n runtimeId: string,\n runtimeIds?: Iterable<string>\n): string {\n return runtimeIds\n ? createPublicIdWithFormatter(runtimeId, runtimeIds, stableRuntimePublicId)\n : stableRuntimePublicId(runtimeId, 0);\n}\n\nexport function createPublicTargetId(\n targetKey: string,\n targetKeys: Iterable<string>\n): string {\n return createPublicId(targetKey, targetKeys);\n}\n\nfunction createPublicIdWithFormatter(\n key: string,\n keys: Iterable<string>,\n formatPublicId: (input: string, salt: number) => string\n): string {\n const used = new Set<string>();\n for (const existingKey of [...keys].sort()) {\n let attempt = 0;\n while (true) {\n const candidate = formatPublicId(existingKey, attempt);\n if (existingKey === key && !used.has(candidate)) {\n return candidate;\n }\n if (!used.has(candidate)) {\n used.add(candidate);\n break;\n }\n attempt += 1;\n }\n }\n return formatPublicId(key, 0);\n}\n\nfunction stablePublicId(input: string, salt: number): string {\n const value = stableHash(input, salt) % 100000;\n return value.toString().padStart(5, \"0\");\n}\n\nfunction stableRuntimePublicId(input: string, salt: number): string {\n const value = stableHash(input, salt);\n const letter = String.fromCharCode(65 + (value % 26));\n const digits = Math.floor(value / 26) % 1000;\n return `${letter}${digits.toString().padStart(3, \"0\")}`;\n}\n\nfunction stableHash(input: string, salt: number): number {\n const hashInput = salt === 0 ? input : `${input}#${salt}`;\n let hash = 2166136261;\n for (let index = 0; index < hashInput.length; index += 1) {\n hash ^= hashInput.charCodeAt(index);\n hash = Math.imul(hash, 16777619);\n }\n return hash >>> 0;\n}\n\n/* ------------------------------------------------------------------ */\n/* Runtime → Dashboard messages */\n/* ------------------------------------------------------------------ */\n\nexport type SyncoreDevtoolsMessage =\n | {\n type: \"hello\";\n protocolVersion?: number;\n minSupportedProtocolVersion?: number;\n maxSupportedProtocolVersion?: number;\n runtimeVersion?: string;\n runtimeId: string;\n platform: string;\n appName?: string;\n origin?: string;\n sessionLabel?: string;\n targetKind?: \"client\" | \"project\";\n storageProtocol?: string;\n databaseLabel?: string;\n storageIdentity?: string;\n }\n | { type: \"event\"; event: SyncoreDevtoolsEvent }\n | {\n type: \"event.batch\";\n runtimeId: string;\n events: SyncoreDevtoolsEvent[];\n }\n | { type: \"ping\" }\n | { type: \"pong\" }\n | {\n type: \"command.result\";\n commandId: string;\n runtimeId: string;\n payload: SyncoreDevtoolsCommandResultPayload;\n }\n | {\n type: \"subscription.data\";\n subscriptionId: string;\n runtimeId: string;\n payload: SyncoreDevtoolsSubscriptionResultPayload;\n }\n | {\n type: \"subscription.error\";\n subscriptionId: string;\n runtimeId: string;\n error: string;\n };\n\n/* ------------------------------------------------------------------ */\n/* Dashboard → Runtime requests */\n/* ------------------------------------------------------------------ */\n\nexport interface SyncoreDevtoolsCommand {\n type: \"command\";\n commandId: string;\n targetRuntimeId: string;\n payload: SyncoreDevtoolsCommandPayload;\n}\n\nexport interface SyncoreDevtoolsSubscribe {\n type: \"subscribe\";\n subscriptionId: string;\n targetRuntimeId: string;\n payload: SyncoreDevtoolsSubscriptionPayload;\n}\n\nexport interface SyncoreDevtoolsUnsubscribe {\n type: \"unsubscribe\";\n subscriptionId: string;\n targetRuntimeId: string;\n}\n\nexport type SyncoreDevtoolsClientMessage =\n | { type: \"ping\" }\n | SyncoreDevtoolsCommand\n | SyncoreDevtoolsSubscribe\n | SyncoreDevtoolsUnsubscribe;\n\nexport type SyncoreDevtoolsCommandPayload =\n /* Functions */\n | {\n kind: \"fn.run\";\n functionName: string;\n functionType: \"query\" | \"mutation\" | \"action\";\n args: Record<string, unknown>;\n }\n | { kind: \"data.insert\"; table: string; document: Record<string, unknown> }\n | {\n kind: \"data.patch\";\n table: string;\n id: string;\n fields: Record<string, unknown>;\n }\n | { kind: \"data.delete\"; table: string; id: string }\n /* SQL */\n | { kind: \"sql.read\"; query: string }\n | { kind: \"sql.write\"; query: string }\n /* Scheduler */\n | { kind: \"scheduler.cancel\"; jobId: string }\n | {\n kind: \"scheduler.update\";\n jobId: string;\n schedule: SchedulerRecurringSchedule;\n args: Record<string, unknown>;\n misfirePolicy: SchedulerMisfirePolicy;\n runAt?: number;\n };\n\nexport type SyncoreDevtoolsSubscriptionPayload =\n | { kind: \"runtime.summary\" }\n | { kind: \"runtime.activeQueries\" }\n | { kind: \"schema.tables\" }\n | {\n kind: \"fn.watch\";\n functionName: string;\n functionType: \"query\";\n args: Record<string, unknown>;\n }\n | {\n kind: \"data.table\";\n table: string;\n filters?: DataFilter[];\n limit?: number;\n cursor?: string;\n }\n | { kind: \"scheduler.jobs\" }\n | { kind: \"functions.catalog\" }\n | { kind: \"sql.watch\"; query: string };\n\nexport interface DataFilter {\n field: string;\n operator:\n | \"eq\"\n | \"neq\"\n | \"gt\"\n | \"gte\"\n | \"lt\"\n | \"lte\"\n | \"contains\"\n | \"startsWith\";\n value: unknown;\n}\n\n/* ------------------------------------------------------------------ */\n/* Response payloads */\n/* ------------------------------------------------------------------ */\n\nexport type SyncoreDevtoolsCommandResultPayload =\n | {\n kind: \"fn.run.result\";\n result?: unknown;\n error?: string;\n durationMs: number;\n }\n | {\n kind: \"data.mutate.result\";\n success: boolean;\n id?: string;\n error?: string;\n }\n | {\n kind: \"sql.read.result\";\n columns: string[];\n rows: unknown[][];\n error?: string;\n }\n | {\n kind: \"sql.write.result\";\n rowsAffected: number;\n error?: string;\n invalidationScopes: string[];\n }\n | {\n kind: \"scheduler.cancel.result\";\n success: boolean;\n cancelled: boolean;\n error?: string;\n }\n | {\n kind: \"scheduler.update.result\";\n success: boolean;\n updated: boolean;\n error?: string;\n job?: SchedulerJob;\n }\n | { kind: \"error\"; message: string };\n\nexport type SyncoreDevtoolsSubscriptionResultPayload =\n | { kind: \"runtime.summary.result\"; summary: SyncoreRuntimeSummary }\n | {\n kind: \"runtime.activeQueries.result\";\n activeQueries: SyncoreActiveQueryInfo[];\n }\n | {\n kind: \"fn.watch.result\";\n result?: unknown;\n error?: string;\n }\n | { kind: \"schema.tables.result\"; tables: TableSchema[] }\n | {\n kind: \"data.table.result\";\n rows: Record<string, unknown>[];\n totalCount: number;\n cursor?: string;\n }\n | { kind: \"scheduler.jobs.result\"; jobs: SchedulerJob[] }\n | { kind: \"functions.catalog.result\"; functions: FunctionDefinition[] }\n | {\n kind: \"sql.watch.result\";\n columns: string[];\n rows: unknown[][];\n observedTables: string[];\n };\n\n/* ------------------------------------------------------------------ */\n/* Shared data shapes */\n/* ------------------------------------------------------------------ */\n\nexport interface FunctionDefinition {\n name: string;\n type: \"query\" | \"mutation\" | \"action\";\n file: string;\n owner?: \"root\" | \"component\";\n componentPath?: string;\n componentName?: string;\n visibility?: \"public\" | \"internal\";\n localName?: string;\n /** Argument validator schema (JSON Schema-like), if available */\n args?: Record<string, unknown>;\n /** Return validator schema, if available */\n returns?: Record<string, unknown>;\n}\n\nexport interface TableSchema {\n name: string;\n displayName?: string;\n owner?: \"root\" | \"component\";\n componentPath?: string;\n componentName?: string;\n fields: TableField[];\n indexes: TableIndex[];\n documentCount: number;\n}\n\nexport interface TableField {\n name: string;\n type: string;\n optional: boolean;\n}\n\nexport interface TableIndex {\n name: string;\n fields: string[];\n unique: boolean;\n}\n\nexport interface SchedulerJob {\n id: string;\n functionName: string;\n owner?: \"root\" | \"component\";\n componentPath?: string;\n componentName?: string;\n args: Record<string, unknown>;\n scheduledAt: number;\n runAt: number;\n status: \"pending\" | \"running\" | \"completed\" | \"failed\" | \"cancelled\";\n completedAt?: number;\n result?: unknown;\n error?: string;\n durationMs?: number;\n recurringName?: string;\n schedule?: SchedulerRecurringSchedule;\n scheduleLabel?: string;\n misfirePolicy?: SchedulerMisfirePolicy;\n timezone?: string;\n lastRunAt?: number;\n updatedAt?: number;\n /** Compatibility label for older UI code. */\n cronSchedule?: string;\n}\n\nexport interface SchedulerRecurringIntervalSchedule {\n type: \"interval\";\n seconds?: number;\n minutes?: number;\n hours?: number;\n}\n\nexport interface SchedulerRecurringDailySchedule {\n type: \"daily\";\n hour: number;\n minute: number;\n timezone?: string;\n}\n\nexport interface SchedulerRecurringWeeklySchedule {\n type: \"weekly\";\n dayOfWeek:\n | \"sunday\"\n | \"monday\"\n | \"tuesday\"\n | \"wednesday\"\n | \"thursday\"\n | \"friday\"\n | \"saturday\";\n hour: number;\n minute: number;\n timezone?: string;\n}\n\nexport type SchedulerRecurringSchedule =\n | SchedulerRecurringIntervalSchedule\n | SchedulerRecurringDailySchedule\n | SchedulerRecurringWeeklySchedule;\n\nexport type SchedulerMisfirePolicy =\n | { type: \"catch_up\" }\n | { type: \"skip\" }\n | { type: \"run_once_if_missed\" }\n | { type: \"windowed\"; windowMs: number };\n"],"mappings":";AAEA,MAAa,oCAAoC;AACjD,MAAa,kDAAkD;AAC/D,MAAa,kDAAkD;AAS/D,SAAgB,6BACd,WAIS;AACT,QACE,UAAU,+BAAA,KAEV,UAAU,+BAAA,KAEV,UAAU,mBAAmB,UAAU,+BACvC,UAAU,mBAAmB,UAAU;;AA6F3C,SAAgB,mBAAmB,OAAuB;AACxD,QAAO,eAAe,OAAO,EAAE;;AAGjC,SAAgB,eACd,KACA,MACQ;AACR,QAAO,4BAA4B,KAAK,MAAM,eAAe;;AAG/D,SAAgB,sBACd,WACA,YACQ;AACR,QAAO,aACH,4BAA4B,WAAW,YAAY,sBAAsB,GACzE,sBAAsB,WAAW,EAAE;;AAGzC,SAAgB,qBACd,WACA,YACQ;AACR,QAAO,eAAe,WAAW,WAAW;;AAG9C,SAAS,4BACP,KACA,MACA,gBACQ;CACR,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,eAAe,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE;EAC1C,IAAI,UAAU;AACd,SAAO,MAAM;GACX,MAAM,YAAY,eAAe,aAAa,QAAQ;AACtD,OAAI,gBAAgB,OAAO,CAAC,KAAK,IAAI,UAAU,CAC7C,QAAO;AAET,OAAI,CAAC,KAAK,IAAI,UAAU,EAAE;AACxB,SAAK,IAAI,UAAU;AACnB;;AAEF,cAAW;;;AAGf,QAAO,eAAe,KAAK,EAAE;;AAG/B,SAAS,eAAe,OAAe,MAAsB;AAE3D,SADc,WAAW,OAAO,KAAK,GAAG,KAC3B,UAAU,CAAC,SAAS,GAAG,IAAI;;AAG1C,SAAS,sBAAsB,OAAe,MAAsB;CAClE,MAAM,QAAQ,WAAW,OAAO,KAAK;AAGrC,QAAO,GAFQ,OAAO,aAAa,KAAM,QAAQ,GAAI,IACtC,KAAK,MAAM,QAAQ,GAAG,GAAG,KACd,UAAU,CAAC,SAAS,GAAG,IAAI;;AAGvD,SAAS,WAAW,OAAe,MAAsB;CACvD,MAAM,YAAY,SAAS,IAAI,QAAQ,GAAG,MAAM,GAAG;CACnD,IAAI,OAAO;AACX,MAAK,IAAI,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS,GAAG;AACxD,UAAQ,UAAU,WAAW,MAAM;AACnC,SAAO,KAAK,KAAK,MAAM,SAAS;;AAElC,QAAO,SAAS"}
@@ -74,7 +74,7 @@ declare function SyncoreNextProvider({
74
74
  serviceWorkerUrl?: string; /** Optional explicit module URL for an already-public worker asset. */
75
75
  workerUrl?: URL | string; /** Optional public worker asset path for production builds. */
76
76
  workerAssetUrl?: string;
77
- }): react_jsx_runtime0.JSX.Element | null;
77
+ }): react_jsx_runtime0.JSX.Element;
78
78
  //#endregion
79
79
  export { SyncoreNextOptions, SyncoreNextProvider, SyncoreServiceWorker, SyncoreServiceWorkerRegistration, createNextSyncoreClient, createSyncoreNextWorkerUrl, getSyncoreWorkerUrl, registerSyncoreServiceWorker, resolveSqlJsWasmUrl };
80
80
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;;;;;UAYiB,kBAAA;;EAEf,gBAAA;EAFiC;EAKjC,YAAA;EALiC;EAQjC,cAAA;AAAA;;;;UAMe,gCAAA;EAAgC;EAE/C,UAAA,IAAc,OAAA;EAAA;EAGd,MAAA,IAAU,OAAA,CAAQ,yBAAA;AAAA;;;;iBAME,4BAAA,CACpB,OAAA,GAAU,kBAAA,GACT,OAAA,CAAQ,gCAAA;;;;iBAkBK,mBAAA,CAAoB,OAAA,GAAU,kBAAA;;AApB9C;;iBA2BgB,uBAAA,CAAwB,OAAA;EA1B5B;;;;EA+BV,YAAA,SAAqB,MAAA,EA/BX;EAkCV,SAAA,GAAY,GAAA,WAjCX;EAoCD,cAAA;AAAA,IACE,sBAAA;;AAnBJ;;iBAmCgB,oBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA;AAAA;EAEA,QAAA,EAAU,SAAA;EACV,gBAAA;EACA,YAAA,IAAgB,YAAA,EAAc,yBAAA;AAAA,IAC/B,SAAA;;;;;;;iBAsBe,mBAAA,CAAA;EACd,QAAA;EACA,YAAA;EACA,gBAAA;EACA,SAAA;EACA;AAAA;EA1DqB,gEA6DrB,QAAA,EAAU,SAAA;EA1DE;;;EA+DZ,YAAA,SAAqB,MAAA,EA3DG;EA8DxB,gBAAA,WA9Cc;EAiDd,SAAA,GAAY,GAAA;EAGZ,cAAA;AAAA,IACD,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;;;;;UAsBiB,kBAAA;;EAEf,gBAAA;EAFiC;EAKjC,YAAA;EALiC;EAQjC,cAAA;AAAA;;;;UAMe,gCAAA;EAAgC;EAE/C,UAAA,IAAc,OAAA;EAAA;EAGd,MAAA,IAAU,OAAA,CAAQ,yBAAA;AAAA;;;;iBAME,4BAAA,CACpB,OAAA,GAAU,kBAAA,GACT,OAAA,CAAQ,gCAAA;;;;iBAkBK,mBAAA,CAAoB,OAAA,GAAU,kBAAA;;AApB9C;;iBA2BgB,uBAAA,CAAwB,OAAA;EA1B5B;;;;EA+BV,YAAA,SAAqB,MAAA,EA/BX;EAkCV,SAAA,GAAY,GAAA,WAjCX;EAoCD,cAAA;AAAA,IACE,sBAAA;;AAnBJ;;iBAmCgB,oBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA;AAAA;EAEA,QAAA,EAAU,SAAA;EACV,gBAAA;EACA,YAAA,IAAgB,YAAA,EAAc,yBAAA;AAAA,IAC/B,SAAA;;;;;;;iBAsBe,mBAAA,CAAA;EACd,QAAA;EACA,YAAA;EACA,gBAAA;EACA,SAAA;EACA;AAAA;EA1DqB,gEA6DrB,QAAA,EAAU,SAAA;EA1DE;;;EA+DZ,YAAA,SAAqB,MAAA,EA3DG;EA8DxB,gBAAA,WA9Cc;EAiDd,SAAA,GAAY,GAAA;EAGZ,cAAA;AAAA,IACD,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -1,7 +1,8 @@
1
1
  import { createSyncoreNextWorkerUrl, getSyncoreWorkerUrl } from "./config.js";
2
+ import { createUnavailableSyncoreClient } from "../core/index.mjs";
2
3
  import { createManagedWebWorkerClient, createSyncoreWebWorkerClient } from "../platform-web/index.js";
3
4
  import { SyncoreProvider } from "../react/index.js";
4
- import { useEffect, useState } from "react";
5
+ import { useEffect, useMemo, useRef, useState } from "react";
5
6
  import { jsx } from "react/jsx-runtime";
6
7
  //#region src/index.tsx
7
8
  /**
@@ -48,29 +49,46 @@ function SyncoreServiceWorker({ children, serviceWorkerUrl, onRegistered }) {
48
49
  * fully local in the browser.
49
50
  */
50
51
  function SyncoreNextProvider({ children, createWorker, serviceWorkerUrl, workerUrl, workerAssetUrl }) {
51
- const [managedClient, setManagedClient] = useState(null);
52
+ const createWorkerRef = useRef(createWorker);
53
+ createWorkerRef.current = createWorker;
52
54
  const resolvedWorkerUrl = typeof workerUrl === "string" ? workerUrl : workerUrl?.toString();
55
+ const bootingClient = useMemo(() => createUnavailableSyncoreClient({
56
+ kind: "starting",
57
+ reason: "booting"
58
+ }), []);
59
+ const [client, setClient] = useState(bootingClient);
53
60
  useEffect(() => {
54
- const nextClient = createNextSyncoreClient({
55
- ...createWorker ? { createWorker } : {},
56
- ...resolvedWorkerUrl ? { workerUrl: process.env.NODE_ENV === "production" ? getSyncoreWorkerUrl() : resolvedWorkerUrl } : {},
57
- ...workerAssetUrl ? { workerAssetUrl } : {}
58
- });
59
- setManagedClient(nextClient);
61
+ let disposed = false;
62
+ let managedClient;
63
+ setClient(bootingClient);
64
+ try {
65
+ const workerFactory = createWorkerRef.current;
66
+ managedClient = createNextSyncoreClient({
67
+ ...workerFactory ? { createWorker: () => workerFactory() } : {},
68
+ ...resolvedWorkerUrl ? { workerUrl: process.env.NODE_ENV === "production" ? getSyncoreWorkerUrl() : resolvedWorkerUrl } : {},
69
+ ...workerAssetUrl ? { workerAssetUrl } : {}
70
+ });
71
+ if (!disposed) setClient(managedClient.client);
72
+ } catch (error) {
73
+ if (!disposed) setClient(createUnavailableSyncoreClient({
74
+ kind: "unavailable",
75
+ reason: "worker-unavailable",
76
+ ...error instanceof Error ? { error } : {}
77
+ }));
78
+ }
60
79
  return () => {
61
- nextClient.dispose();
62
- setManagedClient(null);
80
+ disposed = true;
81
+ managedClient?.dispose();
63
82
  };
64
83
  }, [
65
- createWorker,
84
+ bootingClient,
66
85
  resolvedWorkerUrl,
67
86
  workerAssetUrl
68
87
  ]);
69
- if (!managedClient) return null;
70
88
  return /* @__PURE__ */ jsx(SyncoreServiceWorker, {
71
89
  ...serviceWorkerUrl ? { serviceWorkerUrl } : {},
72
90
  children: /* @__PURE__ */ jsx(SyncoreProvider, {
73
- client: managedClient.client,
91
+ client,
74
92
  children
75
93
  })
76
94
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import {\n createManagedWebWorkerClient,\n createSyncoreWebWorkerClient,\n type ManagedWebWorkerClient\n} from \"@syncore/platform-web\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport { useEffect, useState, type ReactNode } from \"react\";\nimport { getSyncoreWorkerUrl } from \"./config.js\";\n\nexport { getSyncoreWorkerUrl } from \"./config.js\";\nexport { createSyncoreNextWorkerUrl } from \"./config.js\";\n\nexport interface SyncoreNextOptions {\n /** Optional service worker URL used to cache the application shell. */\n serviceWorkerUrl?: string;\n\n /** Optional URL for the `sql.js` wasm asset. */\n wasmAssetUrl?: string;\n\n /** Optional URL for the worker asset used by the local Syncore runtime. */\n workerAssetUrl?: string;\n}\n\n/**\n * The result of registering the Syncore service worker in a Next app.\n */\nexport interface SyncoreServiceWorkerRegistration {\n /** Unregister the installed service worker. */\n unregister(): Promise<boolean>;\n\n /** Ask the browser to check for an updated service worker. */\n update(): Promise<ServiceWorkerRegistration>;\n}\n\n/**\n * Register the Syncore service worker used by the Next integration.\n */\nexport async function registerSyncoreServiceWorker(\n options?: SyncoreNextOptions\n): Promise<SyncoreServiceWorkerRegistration | null> {\n if (typeof window === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return null;\n }\n\n const registration = await navigator.serviceWorker.register(\n options?.serviceWorkerUrl ?? \"/sw.js\"\n );\n\n return {\n unregister: () => registration.unregister(),\n update: () => registration.update()\n };\n}\n\n/**\n * Resolve the public URL used by SQL.js to load its wasm file in a Next app.\n */\nexport function resolveSqlJsWasmUrl(options?: SyncoreNextOptions): string {\n return options?.wasmAssetUrl ?? \"/sql-wasm.wasm\";\n}\n\n/**\n * Create a worker-backed Syncore client for a Next app.\n */\nexport function createNextSyncoreClient(options: {\n /**\n * Optional custom worker factory for tests or framework-specific worker\n * bundling patterns such as Next App Router development.\n */\n createWorker?: () => Worker;\n\n /** Optional explicit module URL for an already-public worker asset. */\n workerUrl?: URL | string;\n\n /** Optional public worker asset path for production builds. */\n workerAssetUrl?: string;\n}): ManagedWebWorkerClient {\n if (options.createWorker) {\n return createManagedWebWorkerClient({\n createWorker: options.createWorker\n });\n }\n\n return createSyncoreWebWorkerClient({\n workerUrl:\n options.workerUrl ?? options.workerAssetUrl ?? \"/syncore.worker.js\"\n });\n}\n\n/**\n * Register a service worker while rendering a React subtree.\n */\nexport function SyncoreServiceWorker({\n children,\n serviceWorkerUrl,\n onRegistered\n}: {\n children: ReactNode;\n serviceWorkerUrl?: string;\n onRegistered?: (registration: ServiceWorkerRegistration) => void;\n}) {\n useEffect(() => {\n void (async () => {\n if (typeof window === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return;\n }\n const registration = await navigator.serviceWorker.register(\n serviceWorkerUrl ?? \"/sw.js\"\n );\n onRegistered?.(registration);\n })();\n }, [onRegistered, serviceWorkerUrl]);\n\n return children;\n}\n\n/**\n * Provides a worker-backed Syncore client to a Next React tree.\n *\n * This is the shortest recommended integration for App Router pages that run\n * fully local in the browser.\n */\nexport function SyncoreNextProvider({\n children,\n createWorker,\n serviceWorkerUrl,\n workerUrl,\n workerAssetUrl\n}: {\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /**\n * Optional custom worker factory for tests or Next colocated worker modules.\n */\n createWorker?: () => Worker;\n\n /** Optional service worker URL used to cache the application shell. */\n serviceWorkerUrl?: string;\n\n /** Optional explicit module URL for an already-public worker asset. */\n workerUrl?: URL | string;\n\n /** Optional public worker asset path for production builds. */\n workerAssetUrl?: string;\n}) {\n const [managedClient, setManagedClient] =\n useState<ManagedWebWorkerClient | null>(null);\n const resolvedWorkerUrl =\n typeof workerUrl === \"string\" ? workerUrl : workerUrl?.toString();\n\n useEffect(() => {\n const nextClient = createNextSyncoreClient({\n ...(createWorker ? { createWorker } : {}),\n ...(resolvedWorkerUrl\n ? {\n workerUrl:\n process.env.NODE_ENV === \"production\"\n ? getSyncoreWorkerUrl()\n : resolvedWorkerUrl\n }\n : {}),\n ...(workerAssetUrl ? { workerAssetUrl } : {})\n });\n setManagedClient(nextClient);\n\n return () => {\n nextClient.dispose();\n setManagedClient(null);\n };\n }, [createWorker, resolvedWorkerUrl, workerAssetUrl]);\n\n if (!managedClient) {\n return null;\n }\n\n return (\n <SyncoreServiceWorker {...(serviceWorkerUrl ? { serviceWorkerUrl } : {})}>\n <SyncoreProvider client={managedClient.client}>\n {children}\n </SyncoreProvider>\n </SyncoreServiceWorker>\n );\n}\n"],"mappings":";;;;;;;;;AAqCA,eAAsB,6BACpB,SACkD;AAClD,KAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,WACxD,QAAO;CAGT,MAAM,eAAe,MAAM,UAAU,cAAc,SACjD,SAAS,oBAAoB,SAC9B;AAED,QAAO;EACL,kBAAkB,aAAa,YAAY;EAC3C,cAAc,aAAa,QAAQ;EACpC;;;;;AAMH,SAAgB,oBAAoB,SAAsC;AACxE,QAAO,SAAS,gBAAgB;;;;;AAMlC,SAAgB,wBAAwB,SAYb;AACzB,KAAI,QAAQ,aACV,QAAO,6BAA6B,EAClC,cAAc,QAAQ,cACvB,CAAC;AAGJ,QAAO,6BAA6B,EAClC,WACE,QAAQ,aAAa,QAAQ,kBAAkB,sBAClD,CAAC;;;;;AAMJ,SAAgB,qBAAqB,EACnC,UACA,kBACA,gBAKC;AACD,iBAAgB;AACd,GAAM,YAAY;AAChB,OAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,WACxD;GAEF,MAAM,eAAe,MAAM,UAAU,cAAc,SACjD,oBAAoB,SACrB;AACD,kBAAe,aAAa;MAC1B;IACH,CAAC,cAAc,iBAAiB,CAAC;AAEpC,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,EAClC,UACA,cACA,kBACA,WACA,kBAkBC;CACD,MAAM,CAAC,eAAe,oBACpB,SAAwC,KAAK;CAC/C,MAAM,oBACJ,OAAO,cAAc,WAAW,YAAY,WAAW,UAAU;AAEnE,iBAAgB;EACd,MAAM,aAAa,wBAAwB;GACzC,GAAI,eAAe,EAAE,cAAc,GAAG,EAAE;GACxC,GAAI,oBACA,EACE,WACE,QAAQ,IAAI,aAAa,eACrB,qBAAqB,GACrB,mBACP,GACD,EAAE;GACN,GAAI,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;GAC7C,CAAC;AACF,mBAAiB,WAAW;AAE5B,eAAa;AACX,cAAW,SAAS;AACpB,oBAAiB,KAAK;;IAEvB;EAAC;EAAc;EAAmB;EAAe,CAAC;AAErD,KAAI,CAAC,cACH,QAAO;AAGT,QACE,oBAAC,sBAAD;EAAsB,GAAK,mBAAmB,EAAE,kBAAkB,GAAG,EAAE;YACrE,oBAAC,iBAAD;GAAiB,QAAQ,cAAc;GACpC;GACe,CAAA;EACG,CAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import {\n createUnavailableSyncoreClient,\n type SyncoreClient\n} from \"@syncore/core\";\nimport {\n createManagedWebWorkerClient,\n createSyncoreWebWorkerClient,\n type ManagedWebWorkerClient\n} from \"@syncore/platform-web\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode\n} from \"react\";\nimport { getSyncoreWorkerUrl } from \"./config.js\";\n\nexport { getSyncoreWorkerUrl } from \"./config.js\";\nexport { createSyncoreNextWorkerUrl } from \"./config.js\";\n\nexport interface SyncoreNextOptions {\n /** Optional service worker URL used to cache the application shell. */\n serviceWorkerUrl?: string;\n\n /** Optional URL for the `sql.js` wasm asset. */\n wasmAssetUrl?: string;\n\n /** Optional URL for the worker asset used by the local Syncore runtime. */\n workerAssetUrl?: string;\n}\n\n/**\n * The result of registering the Syncore service worker in a Next app.\n */\nexport interface SyncoreServiceWorkerRegistration {\n /** Unregister the installed service worker. */\n unregister(): Promise<boolean>;\n\n /** Ask the browser to check for an updated service worker. */\n update(): Promise<ServiceWorkerRegistration>;\n}\n\n/**\n * Register the Syncore service worker used by the Next integration.\n */\nexport async function registerSyncoreServiceWorker(\n options?: SyncoreNextOptions\n): Promise<SyncoreServiceWorkerRegistration | null> {\n if (typeof window === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return null;\n }\n\n const registration = await navigator.serviceWorker.register(\n options?.serviceWorkerUrl ?? \"/sw.js\"\n );\n\n return {\n unregister: () => registration.unregister(),\n update: () => registration.update()\n };\n}\n\n/**\n * Resolve the public URL used by SQL.js to load its wasm file in a Next app.\n */\nexport function resolveSqlJsWasmUrl(options?: SyncoreNextOptions): string {\n return options?.wasmAssetUrl ?? \"/sql-wasm.wasm\";\n}\n\n/**\n * Create a worker-backed Syncore client for a Next app.\n */\nexport function createNextSyncoreClient(options: {\n /**\n * Optional custom worker factory for tests or framework-specific worker\n * bundling patterns such as Next App Router development.\n */\n createWorker?: () => Worker;\n\n /** Optional explicit module URL for an already-public worker asset. */\n workerUrl?: URL | string;\n\n /** Optional public worker asset path for production builds. */\n workerAssetUrl?: string;\n}): ManagedWebWorkerClient {\n if (options.createWorker) {\n return createManagedWebWorkerClient({\n createWorker: options.createWorker\n });\n }\n\n return createSyncoreWebWorkerClient({\n workerUrl:\n options.workerUrl ?? options.workerAssetUrl ?? \"/syncore.worker.js\"\n });\n}\n\n/**\n * Register a service worker while rendering a React subtree.\n */\nexport function SyncoreServiceWorker({\n children,\n serviceWorkerUrl,\n onRegistered\n}: {\n children: ReactNode;\n serviceWorkerUrl?: string;\n onRegistered?: (registration: ServiceWorkerRegistration) => void;\n}) {\n useEffect(() => {\n void (async () => {\n if (typeof window === \"undefined\" || !(\"serviceWorker\" in navigator)) {\n return;\n }\n const registration = await navigator.serviceWorker.register(\n serviceWorkerUrl ?? \"/sw.js\"\n );\n onRegistered?.(registration);\n })();\n }, [onRegistered, serviceWorkerUrl]);\n\n return children;\n}\n\n/**\n * Provides a worker-backed Syncore client to a Next React tree.\n *\n * This is the shortest recommended integration for App Router pages that run\n * fully local in the browser.\n */\nexport function SyncoreNextProvider({\n children,\n createWorker,\n serviceWorkerUrl,\n workerUrl,\n workerAssetUrl\n}: {\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /**\n * Optional custom worker factory for tests or Next colocated worker modules.\n */\n createWorker?: () => Worker;\n\n /** Optional service worker URL used to cache the application shell. */\n serviceWorkerUrl?: string;\n\n /** Optional explicit module URL for an already-public worker asset. */\n workerUrl?: URL | string;\n\n /** Optional public worker asset path for production builds. */\n workerAssetUrl?: string;\n}) {\n const createWorkerRef = useRef(createWorker);\n createWorkerRef.current = createWorker;\n const resolvedWorkerUrl =\n typeof workerUrl === \"string\" ? workerUrl : workerUrl?.toString();\n const bootingClient = useMemo(\n () =>\n createUnavailableSyncoreClient({\n kind: \"starting\",\n reason: \"booting\"\n }),\n []\n );\n const [client, setClient] = useState<SyncoreClient>(bootingClient);\n\n useEffect(() => {\n let disposed = false;\n let managedClient: ManagedWebWorkerClient | undefined;\n\n setClient(bootingClient);\n\n try {\n const workerFactory = createWorkerRef.current;\n managedClient = createNextSyncoreClient({\n ...(workerFactory\n ? {\n createWorker: () => workerFactory()\n }\n : {}),\n ...(resolvedWorkerUrl\n ? {\n workerUrl:\n process.env.NODE_ENV === \"production\"\n ? getSyncoreWorkerUrl()\n : resolvedWorkerUrl\n }\n : {}),\n ...(workerAssetUrl ? { workerAssetUrl } : {})\n });\n if (!disposed) {\n setClient(managedClient.client);\n }\n } catch (error) {\n if (!disposed) {\n setClient(\n createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"worker-unavailable\",\n ...(error instanceof Error ? { error } : {})\n })\n );\n }\n }\n\n return () => {\n disposed = true;\n managedClient?.dispose();\n };\n }, [bootingClient, resolvedWorkerUrl, workerAssetUrl]);\n\n return (\n <SyncoreServiceWorker {...(serviceWorkerUrl ? { serviceWorkerUrl } : {})}>\n <SyncoreProvider client={client}>{children}</SyncoreProvider>\n </SyncoreServiceWorker>\n );\n}\n"],"mappings":";;;;;;;;;;AA+CA,eAAsB,6BACpB,SACkD;AAClD,KAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,WACxD,QAAO;CAGT,MAAM,eAAe,MAAM,UAAU,cAAc,SACjD,SAAS,oBAAoB,SAC9B;AAED,QAAO;EACL,kBAAkB,aAAa,YAAY;EAC3C,cAAc,aAAa,QAAQ;EACpC;;;;;AAMH,SAAgB,oBAAoB,SAAsC;AACxE,QAAO,SAAS,gBAAgB;;;;;AAMlC,SAAgB,wBAAwB,SAYb;AACzB,KAAI,QAAQ,aACV,QAAO,6BAA6B,EAClC,cAAc,QAAQ,cACvB,CAAC;AAGJ,QAAO,6BAA6B,EAClC,WACE,QAAQ,aAAa,QAAQ,kBAAkB,sBAClD,CAAC;;;;;AAMJ,SAAgB,qBAAqB,EACnC,UACA,kBACA,gBAKC;AACD,iBAAgB;AACd,GAAM,YAAY;AAChB,OAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,WACxD;GAEF,MAAM,eAAe,MAAM,UAAU,cAAc,SACjD,oBAAoB,SACrB;AACD,kBAAe,aAAa;MAC1B;IACH,CAAC,cAAc,iBAAiB,CAAC;AAEpC,QAAO;;;;;;;;AAST,SAAgB,oBAAoB,EAClC,UACA,cACA,kBACA,WACA,kBAkBC;CACD,MAAM,kBAAkB,OAAO,aAAa;AAC5C,iBAAgB,UAAU;CAC1B,MAAM,oBACJ,OAAO,cAAc,WAAW,YAAY,WAAW,UAAU;CACnE,MAAM,gBAAgB,cAElB,+BAA+B;EAC7B,MAAM;EACN,QAAQ;EACT,CAAC,EACJ,EAAE,CACH;CACD,MAAM,CAAC,QAAQ,aAAa,SAAwB,cAAc;AAElE,iBAAgB;EACd,IAAI,WAAW;EACf,IAAI;AAEJ,YAAU,cAAc;AAExB,MAAI;GACF,MAAM,gBAAgB,gBAAgB;AACtC,mBAAgB,wBAAwB;IACtC,GAAI,gBACA,EACE,oBAAoB,eAAe,EACpC,GACD,EAAE;IACN,GAAI,oBACA,EACE,WACE,QAAQ,IAAI,aAAa,eACrB,qBAAqB,GACrB,mBACP,GACD,EAAE;IACN,GAAI,iBAAiB,EAAE,gBAAgB,GAAG,EAAE;IAC7C,CAAC;AACF,OAAI,CAAC,SACH,WAAU,cAAc,OAAO;WAE1B,OAAO;AACd,OAAI,CAAC,SACH,WACE,+BAA+B;IAC7B,MAAM;IACN,QAAQ;IACR,GAAI,iBAAiB,QAAQ,EAAE,OAAO,GAAG,EAAE;IAC5C,CAAC,CACH;;AAIL,eAAa;AACX,cAAW;AACX,kBAAe,SAAS;;IAEzB;EAAC;EAAe;EAAmB;EAAe,CAAC;AAEtD,QACE,oBAAC,sBAAD;EAAsB,GAAK,mBAAmB,EAAE,kBAAkB,GAAG,EAAE;YACrE,oBAAC,iBAAD;GAAyB;GAAS;GAA2B,CAAA;EACxC,CAAA"}
@@ -1,26 +1,26 @@
1
1
  import { SQLiteDatabase } from "expo-sqlite";
2
2
  import * as _syncore_core0 from "../core/index.d.mts";
3
- import { AnySyncoreSchema, DevtoolsSink, SchedulerOptions, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreExperimentalPlugin, SyncoreExternalChangeApplier, SyncoreExternalChangeSignal, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter } from "../core/index.d.mts";
3
+ import { DevtoolsSink, SchedulerOptions, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreDataModel, SyncoreExternalChangeApplier, SyncoreExternalChangeSignal, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter } from "../core/index.d.mts";
4
4
 
5
5
  //#region src/index.d.ts
6
- type ExpoSyncoreSchema = AnySyncoreSchema;
6
+ type ExpoSyncoreSchema<TSchema extends SyncoreDataModel = SyncoreDataModel> = TSchema;
7
7
  /**
8
8
  * Options for constructing an Expo Syncore runtime.
9
9
  *
10
10
  * Use this when you want Syncore to persist locally with `expo-sqlite` and the
11
11
  * Expo file system.
12
12
  */
13
- interface CreateExpoRuntimeOptions {
13
+ interface CreateExpoRuntimeOptions<TSchema extends ExpoSyncoreSchema = ExpoSyncoreSchema> {
14
14
  /** The schema for the local Syncore app. */
15
- schema: ExpoSyncoreSchema;
15
+ schema: TSchema;
16
16
  /** The generated function registry for the local Syncore app. */
17
- functions: SyncoreRuntimeOptions<ExpoSyncoreSchema>["functions"];
17
+ functions: SyncoreRuntimeOptions<TSchema>["functions"];
18
+ /** Optional resolved installed components for the local Syncore app. */
19
+ components?: SyncoreRuntimeOptions<TSchema>["components"];
18
20
  /** Optional platform capabilities exposed to function handlers. */
19
21
  capabilities?: SyncoreCapabilities;
20
22
  /** Optional custom SQL driver. Defaults to `expo-sqlite`. */
21
23
  driver?: SyncoreSqlDriver;
22
- /** Optional experimental plugins for runtime hooks. */
23
- experimentalPlugins?: Array<SyncoreExperimentalPlugin<ExpoSyncoreSchema>>;
24
24
  /** Optional custom file/blob storage adapter. */
25
25
  storage?: SyncoreStorageAdapter;
26
26
  /** The SQLite database filename to open locally. */
@@ -43,11 +43,11 @@ interface CreateExpoRuntimeOptions {
43
43
  /**
44
44
  * A reusable bootstrap that lazily creates, starts, and stops an Expo Syncore runtime.
45
45
  */
46
- interface ExpoSyncoreBootstrap {
46
+ interface ExpoSyncoreBootstrap<TSchema extends ExpoSyncoreSchema = ExpoSyncoreSchema> {
47
47
  /** Synchronous access is unavailable; use `getClient()` instead. */
48
48
  getRuntime(): never;
49
49
  /** Start the runtime if needed and return a ready client. */
50
- getClient(): Promise<ReturnType<SyncoreRuntime<ExpoSyncoreSchema>["createClient"]>>;
50
+ getClient(): Promise<ReturnType<SyncoreRuntime<TSchema>["createClient"]>>;
51
51
  /** Stop the current runtime instance if one exists. */
52
52
  stop(): Promise<void>;
53
53
  /** Fully discard the runtime so the next call recreates it. */
@@ -56,15 +56,15 @@ interface ExpoSyncoreBootstrap {
56
56
  /**
57
57
  * Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.
58
58
  */
59
- declare function createExpoSyncoreRuntime(options: CreateExpoRuntimeOptions): SyncoreRuntime<ExpoSyncoreSchema>;
59
+ declare function createExpoSyncoreRuntime<TSchema extends ExpoSyncoreSchema>(options: CreateExpoRuntimeOptions<TSchema>): SyncoreRuntime<TSchema>;
60
60
  /**
61
61
  * Create a same-process Syncore client from a started Expo runtime.
62
62
  */
63
- declare function createExpoSyncoreClient(runtime: SyncoreRuntime<ExpoSyncoreSchema>): _syncore_core0.SyncoreClient;
63
+ declare function createExpoSyncoreClient<TSchema extends ExpoSyncoreSchema>(runtime: SyncoreRuntime<TSchema>): _syncore_core0.SyncoreClient;
64
64
  /**
65
65
  * Create a reusable Expo bootstrap that lazily starts the local runtime.
66
66
  */
67
- declare function createExpoSyncoreBootstrap(options: CreateExpoRuntimeOptions): ExpoSyncoreBootstrap;
67
+ declare function createExpoSyncoreBootstrap<TSchema extends ExpoSyncoreSchema>(options: CreateExpoRuntimeOptions<TSchema>): ExpoSyncoreBootstrap<TSchema>;
68
68
  /**
69
69
  * Syncore SQL driver implementation backed by `expo-sqlite`.
70
70
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;KA0BY,iBAAA,GAAoB,gBAAA;;;AAAhC;;;;UAQiB,wBAAA;EAAA;EAEf,MAAA,EAAQ,iBAAA;;EAGR,SAAA,EAAW,qBAAA,CAAsB,iBAAA;EAAA;EAGjC,YAAA,GAAe,mBAAA;EAAA;EAGf,MAAA,GAAS,gBAAA;EAG6C;EAAtD,mBAAA,GAAsB,KAAA,CAAM,yBAAA,CAA0B,iBAAA;EAAhC;EAGtB,OAAA,GAAU,qBAAA;EAeC;EAZX,YAAA;EAkBuB;EAfvB,iBAAA;EAkBoD;EAfpD,oBAAA;EAxBA;EA2BA,QAAA;EAxBA;EA2BA,QAAA,GAAW,YAAA;EA3BsB;EA8BjC,SAAA,GAAY,gBAAA;EA3BG;EA8Bf,oBAAA,GAAuB,2BAAA;EA3Bd;EA8BT,qBAAA,GAAwB,4BAAA;AAAA;;;;UAMT,oBAAA;EA3Bf;EA6BA,UAAA;EAvBA;EA0BA,SAAA,IAAa,OAAA,CACX,UAAA,CAAW,cAAA,CAAe,iBAAA;EArB5B;EAyBA,IAAA,IAAQ,OAAA;EAtBR;EAyBA,KAAA,IAAS,OAAA;AAAA;;;;iBAMK,wBAAA,CACd,OAAA,EAAS,wBAAA,GACR,cAAA,CAAe,iBAAA;;AArBlB;;iBA0FgB,uBAAA,CACd,OAAA,EAAS,cAAA,CAAe,iBAAA,IAAD,cAAA,CAAmB,aAAA;;;;iBAQ5B,0BAAA,CACd,OAAA,EAAS,wBAAA,GACR,oBAAA;;;;cA8CU,gBAAA,YAA4B,gBAAA;EAAA,QAO7B,QAAA;EAAA,QANF,gBAAA;EAAA,QACA,MAAA;EAAA,iBACS,YAAA;EAAA,iBACA,iBAAA;cAGP,QAAA,EAAU,cAAA,EAClB,OAAA;IACE,YAAA;IACA,iBAAA;EAAA;EAOE,IAAA,CAAK,GAAA,WAAc,OAAA;EAKnB,GAAA,CACJ,GAAA,UACA,MAAA,eACC,OAAA;IAAU,OAAA;IAAiB,eAAA;EAAA;EASxB,GAAA,GAAA,CAAO,GAAA,UAAa,MAAA,eAAyB,OAAA,CAAQ,CAAA;EASrD,GAAA,GAAA,CAAO,GAAA,UAAa,MAAA,eAAyB,OAAA,CAAQ,CAAA;EAKrD,eAAA,GAAA,CAAmB,QAAA,QAAgB,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;EAoBxD,aAAA,GAAA,CAAiB,IAAA,UAAc,QAAA,QAAgB,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;EAkBpE,KAAA,CAAA,GAAS,OAAA;EAAA,QAQP,UAAA;EAMF,MAAA,CAAA,GAAU,OAAA;AAAA;;;;cA4BL,sBAAA,YAAkC,qBAAA;EAAA,iBAC5B,aAAA;cAEL,oBAAA;EAON,GAAA,CAAI,EAAA,UAAY,KAAA,EAAO,iBAAA,GAAoB,OAAA,CAAQ,aAAA;EAenD,GAAA,CAAI,EAAA,WAAa,OAAA,CAAQ,aAAA;EAazB,IAAA,CAAK,EAAA,WAAa,OAAA,CAAQ,UAAA;EAQ1B,MAAA,CAAO,EAAA,WAAa,OAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;KA0BY,iBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;;;AAFJ;;;;UAUiB,wBAAA,iBACC,iBAAA,GAAoB,iBAAA;EATlC;EAYF,MAAA,EAAQ,OAAA;EAZC;EAeT,SAAA,EAAW,qBAAA,CAAsB,OAAA;EAhBjB;EAmBhB,UAAA,GAAa,qBAAA,CAAsB,OAAA;EAlBjC;EAqBF,YAAA,GAAe,mBAAA;EArBN;EAwBT,MAAA,GAAS,gBAAA;EAhB8B;EAmBvC,OAAA,GAAU,qBAAA;EAlBM;EAqBhB,YAAA;EAlBQ;EAqBR,iBAAA;EAlBW;EAqBX,oBAAA;EAlBa;EAqBb,QAAA;EAfS;EAkBT,QAAA,GAAW,YAAA;EAAA;EAGX,SAAA,GAAY,gBAAA;EAGW;EAAvB,oBAAA,GAAuB,2BAAA;EAG6B;EAApD,qBAAA,GAAwB,4BAAA;AAAA;;;;UAMT,oBAAA,iBACC,iBAAA,GAAoB,iBAAA;EA3CpC;EA8CA,UAAA;EA9CiC;EAiDjC,SAAA,IAAa,OAAA,CACX,UAAA,CAAW,cAAA,CAAe,OAAA;EA/Cf;EAmDb,IAAA,IAAQ,OAAA;EAhDR;EAmDA,KAAA,IAAS,OAAA;AAAA;;;;iBAMK,wBAAA,iBACE,iBAAA,CAAA,CAEhB,OAAA,EAAS,wBAAA,CAAyB,OAAA,IACjC,cAAA,CAAe,OAAA;;;;iBAmEF,uBAAA,iBACE,iBAAA,CAAA,CAChB,OAAA,EAAS,cAAA,CAAe,OAAA,IAAD,cAAA,CAAS,aAAA;;;;iBAOlB,0BAAA,iBACE,iBAAA,CAAA,CAEhB,OAAA,EAAS,wBAAA,CAAyB,OAAA,IACjC,oBAAA,CAAqB,OAAA;;;;cA+CX,gBAAA,YAA4B,gBAAA;EAAA,QAO7B,QAAA;EAAA,QANF,gBAAA;EAAA,QACA,MAAA;EAAA,iBACS,YAAA;EAAA,iBACA,iBAAA;cAGP,QAAA,EAAU,cAAA,EAClB,OAAA;IACE,YAAA;IACA,iBAAA;EAAA;EAOE,IAAA,CAAK,GAAA,WAAc,OAAA;EAKnB,GAAA,CACJ,GAAA,UACA,MAAA,eACC,OAAA;IAAU,OAAA;IAAiB,eAAA;EAAA;EASxB,GAAA,GAAA,CAAO,GAAA,UAAa,MAAA,eAAyB,OAAA,CAAQ,CAAA;EASrD,GAAA,GAAA,CAAO,GAAA,UAAa,MAAA,eAAyB,OAAA,CAAQ,CAAA;EAKrD,eAAA,GAAA,CAAmB,QAAA,QAAgB,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;EAoBxD,aAAA,GAAA,CAAiB,IAAA,UAAc,QAAA,QAAgB,OAAA,CAAQ,CAAA,IAAK,OAAA,CAAQ,CAAA;EAkBpE,KAAA,CAAA,GAAS,OAAA;EAAA,QAQP,UAAA;EAMF,MAAA,CAAA,GAAU,OAAA;AAAA;;;;cAuCL,sBAAA,YAAkC,qBAAA;EAAA,iBAC5B,aAAA;cAEL,oBAAA;EAON,GAAA,CAAI,EAAA,UAAY,KAAA,EAAO,iBAAA,GAAoB,OAAA,CAAQ,aAAA;EAenD,GAAA,CAAI,EAAA,WAAa,OAAA,CAAQ,aAAA;EAazB,IAAA,CAAK,EAAA,WAAa,OAAA,CAAQ,UAAA;EAQ1B,MAAA,CAAO,EAAA,WAAa,OAAA;AAAA"}
@@ -19,6 +19,7 @@ function createExpoSyncoreRuntime(options) {
19
19
  return new SyncoreRuntime({
20
20
  schema: options.schema,
21
21
  functions: options.functions,
22
+ ...options.components ? { components: options.components } : {},
22
23
  driver,
23
24
  storage,
24
25
  ...isWebEnvironment && options.externalChangeSignal ? { externalChangeSignal: options.externalChangeSignal } : isWebEnvironment && webExternalChangeSignal ? { externalChangeSignal: webExternalChangeSignal } : {},
@@ -26,7 +27,6 @@ function createExpoSyncoreRuntime(options) {
26
27
  platform: options.platform ?? "expo",
27
28
  ...options.capabilities ? { capabilities: options.capabilities } : {},
28
29
  ...options.devtools ? { devtools: options.devtools } : {},
29
- ...options.experimentalPlugins ? { experimentalPlugins: options.experimentalPlugins } : {},
30
30
  ...options.scheduler ? { scheduler: options.scheduler } : {}
31
31
  });
32
32
  }
@@ -60,6 +60,7 @@ function createExpoSyncoreBootstrap(options) {
60
60
  async stop() {
61
61
  if (!runtime) return;
62
62
  await runtime.stop();
63
+ runtime = null;
63
64
  started = null;
64
65
  },
65
66
  async reset() {
@@ -157,7 +158,8 @@ var ExpoWebExternalChangeApplier = class {
157
158
  if (event.scope === "database" || event.scope === "all") await this.driver.reopen();
158
159
  return {
159
160
  databaseChanged: event.scope === "database" || event.scope === "all",
160
- storageChanged: event.scope === "storage" || event.scope === "all"
161
+ storageChanged: event.scope === "storage" || event.scope === "all",
162
+ changedScopes: event.changedScopes ?? [...(event.changedTables ?? []).map((tableName) => `table:${tableName}`), ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)]
161
163
  };
162
164
  }
163
165
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\nimport {\n defaultDatabaseDirectory,\n openDatabaseSync,\n type SQLiteDatabase\n} from \"expo-sqlite\";\nimport {\n type AnySyncoreSchema,\n type DevtoolsSink,\n type SyncoreExternalChangeApplier,\n type SyncoreExternalChangeSignal,\n SyncoreRuntime,\n type SchedulerOptions,\n type SyncoreCapabilities,\n type SyncoreExperimentalPlugin,\n type SyncoreRuntimeOptions,\n type SyncoreSqlDriver,\n type SyncoreStorageAdapter,\n type StorageObject,\n type StorageWriteInput\n} from \"@syncore/core\";\nimport {\n BroadcastChannelExternalChangeSignal,\n createDefaultSyncChannelName\n} from \"@syncore/platform-web\";\n\nexport type ExpoSyncoreSchema = AnySyncoreSchema;\n\n/**\n * Options for constructing an Expo Syncore runtime.\n *\n * Use this when you want Syncore to persist locally with `expo-sqlite` and the\n * Expo file system.\n */\nexport interface CreateExpoRuntimeOptions {\n /** The schema for the local Syncore app. */\n schema: ExpoSyncoreSchema;\n\n /** The generated function registry for the local Syncore app. */\n functions: SyncoreRuntimeOptions<ExpoSyncoreSchema>[\"functions\"];\n\n /** Optional platform capabilities exposed to function handlers. */\n capabilities?: SyncoreCapabilities;\n\n /** Optional custom SQL driver. Defaults to `expo-sqlite`. */\n driver?: SyncoreSqlDriver;\n\n /** Optional experimental plugins for runtime hooks. */\n experimentalPlugins?: Array<SyncoreExperimentalPlugin<ExpoSyncoreSchema>>;\n\n /** Optional custom file/blob storage adapter. */\n storage?: SyncoreStorageAdapter;\n\n /** The SQLite database filename to open locally. */\n databaseName?: string;\n\n /** Optional directory for the SQLite database file. */\n databaseDirectory?: string;\n\n /** Directory name used for local file/blob storage. */\n storageDirectoryName?: string;\n\n /** Optional runtime platform label shown in devtools snapshots. */\n platform?: string;\n\n /** Optional devtools sink used during development. */\n devtools?: DevtoolsSink;\n\n /** Optional scheduler configuration for jobs and recurring work. */\n scheduler?: SchedulerOptions;\n\n /** Optional shared signal used to synchronize browser instances. */\n externalChangeSignal?: SyncoreExternalChangeSignal;\n\n /** Optional applier used to reconcile browser-side external changes. */\n externalChangeApplier?: SyncoreExternalChangeApplier;\n}\n\n/**\n * A reusable bootstrap that lazily creates, starts, and stops an Expo Syncore runtime.\n */\nexport interface ExpoSyncoreBootstrap {\n /** Synchronous access is unavailable; use `getClient()` instead. */\n getRuntime(): never;\n\n /** Start the runtime if needed and return a ready client. */\n getClient(): Promise<\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\n >;\n\n /** Stop the current runtime instance if one exists. */\n stop(): Promise<void>;\n\n /** Fully discard the runtime so the next call recreates it. */\n reset(): Promise<void>;\n}\n\n/**\n * Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.\n */\nexport function createExpoSyncoreRuntime(\n options: CreateExpoRuntimeOptions\n): SyncoreRuntime<ExpoSyncoreSchema> {\n const databaseDirectory =\n options.databaseDirectory ??\n (typeof defaultDatabaseDirectory === \"string\"\n ? defaultDatabaseDirectory\n : undefined);\n const driver =\n options.driver ??\n new ExpoSqliteDriver(\n openDatabaseSync(\n options.databaseName ?? \"syncore.db\",\n undefined,\n databaseDirectory\n ),\n {\n databaseName: options.databaseName ?? \"syncore.db\",\n ...(databaseDirectory ? { databaseDirectory } : {})\n }\n );\n const storage =\n options.storage ??\n new ExpoFileStorageAdapter(\n options.storageDirectoryName ?? \"syncore-storage\"\n );\n const isWebEnvironment =\n typeof window !== \"undefined\" && typeof document !== \"undefined\";\n const webExternalChangeSignal =\n isWebEnvironment && !options.externalChangeSignal\n ? new BroadcastChannelExternalChangeSignal({\n channelName: createDefaultSyncChannelName(\n options.databaseName ?? \"syncore.db\"\n )\n })\n : undefined;\n const webExternalChangeApplier =\n isWebEnvironment &&\n !options.externalChangeApplier &&\n driver instanceof ExpoSqliteDriver\n ? new ExpoWebExternalChangeApplier(driver)\n : undefined;\n\n return new SyncoreRuntime({\n schema: options.schema,\n functions: options.functions,\n driver,\n storage,\n ...(isWebEnvironment && options.externalChangeSignal\n ? { externalChangeSignal: options.externalChangeSignal }\n : isWebEnvironment && webExternalChangeSignal\n ? { externalChangeSignal: webExternalChangeSignal }\n : {}),\n ...(isWebEnvironment && options.externalChangeApplier\n ? { externalChangeApplier: options.externalChangeApplier }\n : isWebEnvironment && webExternalChangeApplier\n ? { externalChangeApplier: webExternalChangeApplier }\n : {}),\n platform: options.platform ?? \"expo\",\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\n ...(options.devtools ? { devtools: options.devtools } : {}),\n ...(options.experimentalPlugins\n ? { experimentalPlugins: options.experimentalPlugins }\n : {}),\n ...(options.scheduler ? { scheduler: options.scheduler } : {})\n });\n}\n\n/**\n * Create a same-process Syncore client from a started Expo runtime.\n */\nexport function createExpoSyncoreClient(\n runtime: SyncoreRuntime<ExpoSyncoreSchema>\n) {\n return runtime.createClient();\n}\n\n/**\n * Create a reusable Expo bootstrap that lazily starts the local runtime.\n */\nexport function createExpoSyncoreBootstrap(\n options: CreateExpoRuntimeOptions\n): ExpoSyncoreBootstrap {\n let runtime: SyncoreRuntime<ExpoSyncoreSchema> | null = null;\n let started: Promise<\n ReturnType<SyncoreRuntime<ExpoSyncoreSchema>[\"createClient\"]>\n > | null = null;\n\n const ensureRuntime = () => {\n runtime ??= createExpoSyncoreRuntime(options);\n return runtime;\n };\n\n return {\n getRuntime() {\n throw new Error(\n \"createExpoSyncoreBootstrap().getRuntime() is not available synchronously. Use getClient() instead.\"\n );\n },\n async getClient() {\n if (!started) {\n const activeRuntime = ensureRuntime();\n started = activeRuntime\n .start()\n .then(() => activeRuntime.createClient());\n }\n return started;\n },\n async stop() {\n if (!runtime) {\n return;\n }\n await runtime.stop();\n started = null;\n },\n async reset() {\n if (runtime) {\n await runtime.stop();\n }\n runtime = null;\n started = null;\n }\n };\n}\n\n/**\n * Syncore SQL driver implementation backed by `expo-sqlite`.\n */\nexport class ExpoSqliteDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n private readonly databaseName: string;\n private readonly databaseDirectory: string | undefined;\n\n constructor(\n private database: SQLiteDatabase,\n options?: {\n databaseName?: string;\n databaseDirectory?: string;\n }\n ) {\n this.databaseName = options?.databaseName ?? \"syncore.db\";\n this.databaseDirectory = options?.databaseDirectory;\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n await this.database.execAsync(sql);\n }\n\n async run(\n sql: string,\n params: unknown[] = []\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\n this.ensureOpen();\n const result = await this.database.runAsync(sql, normalizeParams(params));\n return {\n changes: result.changes,\n lastInsertRowid: result.lastInsertRowId\n };\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const row = await this.database.getFirstAsync<T>(\n sql,\n normalizeParams(params)\n );\n return row ?? undefined;\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n return this.database.getAllAsync<T>(sql, normalizeParams(params));\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n await this.database.execAsync(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n await this.database.execAsync(\"COMMIT\");\n return result;\n } catch (error) {\n await this.database.execAsync(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.transactionDepth += 1;\n await this.database.execAsync(`SAVEPOINT ${safeName}`);\n try {\n const result = await callback();\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n await this.database.execAsync(`ROLLBACK TO SAVEPOINT ${safeName}`);\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n await this.database.closeAsync();\n this.closed = true;\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The Expo SQLite driver is already closed.\");\n }\n }\n\n async reopen(): Promise<void> {\n this.ensureOpen();\n await this.database.closeAsync();\n this.database = openDatabaseSync(\n this.databaseName,\n undefined,\n this.databaseDirectory\n );\n }\n}\n\nclass ExpoWebExternalChangeApplier implements SyncoreExternalChangeApplier {\n constructor(private readonly driver: ExpoSqliteDriver) {}\n\n async applyExternalChange(event: { scope: \"database\" | \"storage\" | \"all\" }) {\n if (event.scope === \"database\" || event.scope === \"all\") {\n await this.driver.reopen();\n }\n return {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\"\n };\n }\n}\n\n/**\n * Syncore file/blob storage backed by the Expo file system.\n */\nexport class ExpoFileStorageAdapter implements SyncoreStorageAdapter {\n private readonly rootDirectory: Directory;\n\n constructor(storageDirectoryName: string) {\n this.rootDirectory = new Directory(Paths.document, storageDirectoryName);\n if (!this.rootDirectory.exists) {\n this.rootDirectory.create({ idempotent: true, intermediates: true });\n }\n }\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n file.create({ intermediates: true, overwrite: true });\n }\n const bytes = normalizeBinary(input.data);\n file.write(bytes);\n return {\n id,\n path: file.uri,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return {\n id,\n path: file.uri,\n size: file.size,\n contentType: file.type || null\n };\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return file.bytes();\n }\n\n async delete(id: string): Promise<void> {\n const file = new File(this.rootDirectory, id);\n if (file.exists) {\n file.delete();\n }\n }\n}\n\nfunction normalizeParams(\n values: unknown[]\n): Array<string | number | Uint8Array | null> {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n value instanceof Uint8Array\n ) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n return JSON.stringify(value);\n });\n}\n\nfunction normalizeBinary(data: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof data === \"string\") {\n return new TextEncoder().encode(data);\n }\n if (data instanceof Uint8Array) {\n return data;\n }\n return new Uint8Array(data);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Directory, File, Paths } from \"expo-file-system\";\nimport {\n defaultDatabaseDirectory,\n openDatabaseSync,\n type SQLiteDatabase\n} from \"expo-sqlite\";\nimport {\n type ImpactScope,\n type DevtoolsSink,\n type SyncoreExternalChangeApplier,\n type SyncoreExternalChangeSignal,\n SyncoreRuntime,\n type SchedulerOptions,\n type SyncoreCapabilities,\n type SyncoreDataModel,\n type SyncoreRuntimeOptions,\n type SyncoreSqlDriver,\n type SyncoreStorageAdapter,\n type StorageObject,\n type StorageWriteInput\n} from \"@syncore/core\";\nimport {\n BroadcastChannelExternalChangeSignal,\n createDefaultSyncChannelName\n} from \"@syncore/platform-web\";\n\nexport type ExpoSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\n\n/**\n * Options for constructing an Expo Syncore runtime.\n *\n * Use this when you want Syncore to persist locally with `expo-sqlite` and the\n * Expo file system.\n */\nexport interface CreateExpoRuntimeOptions<\n TSchema extends ExpoSyncoreSchema = ExpoSyncoreSchema\n> {\n /** The schema for the local Syncore app. */\n schema: TSchema;\n\n /** The generated function registry for the local Syncore app. */\n functions: SyncoreRuntimeOptions<TSchema>[\"functions\"];\n\n /** Optional resolved installed components for the local Syncore app. */\n components?: SyncoreRuntimeOptions<TSchema>[\"components\"];\n\n /** Optional platform capabilities exposed to function handlers. */\n capabilities?: SyncoreCapabilities;\n\n /** Optional custom SQL driver. Defaults to `expo-sqlite`. */\n driver?: SyncoreSqlDriver;\n\n /** Optional custom file/blob storage adapter. */\n storage?: SyncoreStorageAdapter;\n\n /** The SQLite database filename to open locally. */\n databaseName?: string;\n\n /** Optional directory for the SQLite database file. */\n databaseDirectory?: string;\n\n /** Directory name used for local file/blob storage. */\n storageDirectoryName?: string;\n\n /** Optional runtime platform label shown in devtools snapshots. */\n platform?: string;\n\n /** Optional devtools sink used during development. */\n devtools?: DevtoolsSink;\n\n /** Optional scheduler configuration for jobs and recurring work. */\n scheduler?: SchedulerOptions;\n\n /** Optional shared signal used to synchronize browser instances. */\n externalChangeSignal?: SyncoreExternalChangeSignal;\n\n /** Optional applier used to reconcile browser-side external changes. */\n externalChangeApplier?: SyncoreExternalChangeApplier;\n}\n\n/**\n * A reusable bootstrap that lazily creates, starts, and stops an Expo Syncore runtime.\n */\nexport interface ExpoSyncoreBootstrap<\n TSchema extends ExpoSyncoreSchema = ExpoSyncoreSchema\n> {\n /** Synchronous access is unavailable; use `getClient()` instead. */\n getRuntime(): never;\n\n /** Start the runtime if needed and return a ready client. */\n getClient(): Promise<\n ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>\n >;\n\n /** Stop the current runtime instance if one exists. */\n stop(): Promise<void>;\n\n /** Fully discard the runtime so the next call recreates it. */\n reset(): Promise<void>;\n}\n\n/**\n * Create an Expo Syncore runtime backed by `expo-sqlite` and local file storage.\n */\nexport function createExpoSyncoreRuntime<\n TSchema extends ExpoSyncoreSchema\n>(\n options: CreateExpoRuntimeOptions<TSchema>\n): SyncoreRuntime<TSchema> {\n const databaseDirectory =\n options.databaseDirectory ??\n (typeof defaultDatabaseDirectory === \"string\"\n ? defaultDatabaseDirectory\n : undefined);\n const driver =\n options.driver ??\n new ExpoSqliteDriver(\n openDatabaseSync(\n options.databaseName ?? \"syncore.db\",\n undefined,\n databaseDirectory\n ),\n {\n databaseName: options.databaseName ?? \"syncore.db\",\n ...(databaseDirectory ? { databaseDirectory } : {})\n }\n );\n const storage =\n options.storage ??\n new ExpoFileStorageAdapter(\n options.storageDirectoryName ?? \"syncore-storage\"\n );\n const isWebEnvironment =\n typeof window !== \"undefined\" && typeof document !== \"undefined\";\n const webExternalChangeSignal =\n isWebEnvironment && !options.externalChangeSignal\n ? new BroadcastChannelExternalChangeSignal({\n channelName: createDefaultSyncChannelName(\n options.databaseName ?? \"syncore.db\"\n )\n })\n : undefined;\n const webExternalChangeApplier =\n isWebEnvironment &&\n !options.externalChangeApplier &&\n driver instanceof ExpoSqliteDriver\n ? new ExpoWebExternalChangeApplier(driver)\n : undefined;\n\n return new SyncoreRuntime({\n schema: options.schema,\n functions: options.functions,\n ...(options.components ? { components: options.components } : {}),\n driver,\n storage,\n ...(isWebEnvironment && options.externalChangeSignal\n ? { externalChangeSignal: options.externalChangeSignal }\n : isWebEnvironment && webExternalChangeSignal\n ? { externalChangeSignal: webExternalChangeSignal }\n : {}),\n ...(isWebEnvironment && options.externalChangeApplier\n ? { externalChangeApplier: options.externalChangeApplier }\n : isWebEnvironment && webExternalChangeApplier\n ? { externalChangeApplier: webExternalChangeApplier }\n : {}),\n platform: options.platform ?? \"expo\",\n ...(options.capabilities ? { capabilities: options.capabilities } : {}),\n ...(options.devtools ? { devtools: options.devtools } : {}),\n ...(options.scheduler ? { scheduler: options.scheduler } : {})\n });\n}\n\n/**\n * Create a same-process Syncore client from a started Expo runtime.\n */\nexport function createExpoSyncoreClient<\n TSchema extends ExpoSyncoreSchema\n>(runtime: SyncoreRuntime<TSchema>) {\n return runtime.createClient();\n}\n\n/**\n * Create a reusable Expo bootstrap that lazily starts the local runtime.\n */\nexport function createExpoSyncoreBootstrap<\n TSchema extends ExpoSyncoreSchema\n>(\n options: CreateExpoRuntimeOptions<TSchema>\n): ExpoSyncoreBootstrap<TSchema> {\n let runtime: SyncoreRuntime<TSchema> | null = null;\n let started: Promise<\n ReturnType<SyncoreRuntime<TSchema>[\"createClient\"]>\n > | null = null;\n\n const ensureRuntime = () => {\n runtime ??= createExpoSyncoreRuntime(options);\n return runtime;\n };\n\n return {\n getRuntime() {\n throw new Error(\n \"createExpoSyncoreBootstrap().getRuntime() is not available synchronously. Use getClient() instead.\"\n );\n },\n async getClient() {\n if (!started) {\n const activeRuntime = ensureRuntime();\n started = activeRuntime\n .start()\n .then(() => activeRuntime.createClient());\n }\n return started;\n },\n async stop() {\n if (!runtime) {\n return;\n }\n await runtime.stop();\n runtime = null;\n started = null;\n },\n async reset() {\n if (runtime) {\n await runtime.stop();\n }\n runtime = null;\n started = null;\n }\n };\n}\n\n/**\n * Syncore SQL driver implementation backed by `expo-sqlite`.\n */\nexport class ExpoSqliteDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n private readonly databaseName: string;\n private readonly databaseDirectory: string | undefined;\n\n constructor(\n private database: SQLiteDatabase,\n options?: {\n databaseName?: string;\n databaseDirectory?: string;\n }\n ) {\n this.databaseName = options?.databaseName ?? \"syncore.db\";\n this.databaseDirectory = options?.databaseDirectory;\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n await this.database.execAsync(sql);\n }\n\n async run(\n sql: string,\n params: unknown[] = []\n ): Promise<{ changes: number; lastInsertRowid?: number | string }> {\n this.ensureOpen();\n const result = await this.database.runAsync(sql, normalizeParams(params));\n return {\n changes: result.changes,\n lastInsertRowid: result.lastInsertRowId\n };\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const row = await this.database.getFirstAsync<T>(\n sql,\n normalizeParams(params)\n );\n return row ?? undefined;\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n return this.database.getAllAsync<T>(sql, normalizeParams(params));\n }\n\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n if (this.transactionDepth > 0) {\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\n }\n\n this.transactionDepth += 1;\n await this.database.execAsync(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n await this.database.execAsync(\"COMMIT\");\n return result;\n } catch (error) {\n await this.database.execAsync(\"ROLLBACK\");\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\n this.ensureOpen();\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\n this.transactionDepth += 1;\n await this.database.execAsync(`SAVEPOINT ${safeName}`);\n try {\n const result = await callback();\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n await this.database.execAsync(`ROLLBACK TO SAVEPOINT ${safeName}`);\n await this.database.execAsync(`RELEASE SAVEPOINT ${safeName}`);\n throw error;\n } finally {\n this.transactionDepth -= 1;\n }\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n await this.database.closeAsync();\n this.closed = true;\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The Expo SQLite driver is already closed.\");\n }\n }\n\n async reopen(): Promise<void> {\n this.ensureOpen();\n await this.database.closeAsync();\n this.database = openDatabaseSync(\n this.databaseName,\n undefined,\n this.databaseDirectory\n );\n }\n}\n\nclass ExpoWebExternalChangeApplier implements SyncoreExternalChangeApplier {\n constructor(private readonly driver: ExpoSqliteDriver) {}\n\n async applyExternalChange(event: {\n scope: \"database\" | \"storage\" | \"all\";\n changedScopes?: ImpactScope[];\n changedTables?: string[];\n storageIds?: string[];\n }) {\n if (event.scope === \"database\" || event.scope === \"all\") {\n await this.driver.reopen();\n }\n return {\n databaseChanged: event.scope === \"database\" || event.scope === \"all\",\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes:\n event.changedScopes ??\n ([\n ...(event.changedTables ?? []).map((tableName) => `table:${tableName}`),\n ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)\n ] as ImpactScope[])\n };\n }\n}\n\n/**\n * Syncore file/blob storage backed by the Expo file system.\n */\nexport class ExpoFileStorageAdapter implements SyncoreStorageAdapter {\n private readonly rootDirectory: Directory;\n\n constructor(storageDirectoryName: string) {\n this.rootDirectory = new Directory(Paths.document, storageDirectoryName);\n if (!this.rootDirectory.exists) {\n this.rootDirectory.create({ idempotent: true, intermediates: true });\n }\n }\n\n async put(id: string, input: StorageWriteInput): Promise<StorageObject> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n file.create({ intermediates: true, overwrite: true });\n }\n const bytes = normalizeBinary(input.data);\n file.write(bytes);\n return {\n id,\n path: file.uri,\n size: bytes.byteLength,\n contentType: input.contentType ?? null\n };\n }\n\n async get(id: string): Promise<StorageObject | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return {\n id,\n path: file.uri,\n size: file.size,\n contentType: file.type || null\n };\n }\n\n async read(id: string): Promise<Uint8Array | null> {\n const file = new File(this.rootDirectory, id);\n if (!file.exists) {\n return null;\n }\n return file.bytes();\n }\n\n async delete(id: string): Promise<void> {\n const file = new File(this.rootDirectory, id);\n if (file.exists) {\n file.delete();\n }\n }\n}\n\nfunction normalizeParams(\n values: unknown[]\n): Array<string | number | Uint8Array | null> {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n value instanceof Uint8Array\n ) {\n return value;\n }\n if (value instanceof ArrayBuffer) {\n return new Uint8Array(value);\n }\n return JSON.stringify(value);\n });\n}\n\nfunction normalizeBinary(data: StorageWriteInput[\"data\"]): Uint8Array {\n if (typeof data === \"string\") {\n return new TextEncoder().encode(data);\n }\n if (data instanceof Uint8Array) {\n return data;\n }\n return new Uint8Array(data);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.tsx"],"mappings":";;;;;;AAWA;UAAiB,wBAAA;;EAEf,SAAA,EAAW,oBAAA;EAGD;EAAV,QAAA,EAAU,SAAA;EAGU;EAApB,QAAA,GAAW,SAAA;AAAA;;;;iBAMG,mBAAA,CAAA;EACd,SAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,SAAA"}
1
+ {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.tsx"],"mappings":";;;;;;AAYA;UAAiB,wBAAA;;EAEf,SAAA,EAAW,oBAAA;EAGD;EAAV,QAAA,EAAU,SAAA;EAGU;EAApB,QAAA,GAAW,SAAA;AAAA;;;;iBAMG,mBAAA,CAAA;EACd,SAAA;EACA,QAAA;EACA;AAAA,GACC,wBAAA,GAA2B,SAAA"}
@@ -1,4 +1,5 @@
1
- import { useEffect, useState } from "react";
1
+ import { createDeferredSyncoreClient } from "../core/index.mjs";
2
+ import { useEffect, useMemo } from "react";
2
3
  import { SyncoreProvider } from "../react/index.js";
3
4
  import { jsx } from "react/jsx-runtime";
4
5
  //#region src/react.tsx
@@ -6,22 +7,22 @@ import { jsx } from "react/jsx-runtime";
6
7
  * Start an Expo Syncore bootstrap and provide its client to React descendants.
7
8
  */
8
9
  function SyncoreExpoProvider({ bootstrap, children, fallback = null }) {
9
- const [client, setClient] = useState(null);
10
+ const client = useMemo(() => createDeferredSyncoreClient({
11
+ loadClient: () => bootstrap.getClient(),
12
+ initialStatus: {
13
+ kind: "starting",
14
+ reason: "booting"
15
+ },
16
+ failureReason: "runtime-unavailable"
17
+ }), [bootstrap]);
10
18
  useEffect(() => {
11
- let cancelled = false;
12
- bootstrap.getClient().then((nextClient) => {
13
- if (!cancelled) setClient(nextClient);
14
- });
15
19
  return () => {
16
- cancelled = true;
17
- setClient(null);
18
20
  bootstrap.stop();
19
21
  };
20
22
  }, [bootstrap]);
21
- if (!client) return fallback;
22
23
  return /* @__PURE__ */ jsx(SyncoreProvider, {
23
24
  client,
24
- children
25
+ children: children ?? fallback
25
26
  });
26
27
  }
27
28
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport type { SyncoreClient } from \"@syncore/core\";\nimport type { ExpoSyncoreBootstrap } from \"./index.js\";\n\ntype ExpoSyncoreClient = SyncoreClient;\n\n/**\n * Props for {@link SyncoreExpoProvider}.\n */\nexport interface SyncoreExpoProviderProps {\n /** The bootstrap created with `createExpoSyncoreBootstrap`. */\n bootstrap: ExpoSyncoreBootstrap;\n\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered while the local runtime starts. */\n fallback?: ReactNode;\n}\n\n/**\n * Start an Expo Syncore bootstrap and provide its client to React descendants.\n */\nexport function SyncoreExpoProvider({\n bootstrap,\n children,\n fallback = null\n}: SyncoreExpoProviderProps): ReactNode {\n const [client, setClient] = useState<ExpoSyncoreClient | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n void bootstrap.getClient().then((nextClient: ExpoSyncoreClient) => {\n if (!cancelled) {\n setClient(nextClient);\n }\n });\n\n return () => {\n cancelled = true;\n setClient(null);\n void bootstrap.stop();\n };\n }, [bootstrap]);\n\n if (!client) {\n return fallback;\n }\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;AAyBA,SAAgB,oBAAoB,EAClC,WACA,UACA,WAAW,QAC2B;CACtC,MAAM,CAAC,QAAQ,aAAa,SAAmC,KAAK;AAEpE,iBAAgB;EACd,IAAI,YAAY;AACX,YAAU,WAAW,CAAC,MAAM,eAAkC;AACjE,OAAI,CAAC,UACH,WAAU,WAAW;IAEvB;AAEF,eAAa;AACX,eAAY;AACZ,aAAU,KAAK;AACV,aAAU,MAAM;;IAEtB,CAAC,UAAU,CAAC;AAEf,KAAI,CAAC,OACH,QAAO;AAGT,QAAO,oBAAC,iBAAD;EAAyB;EAAS;EAA2B,CAAA"}
1
+ {"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { createDeferredSyncoreClient } from \"@syncore/core\";\nimport { useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport type { SyncoreClient } from \"@syncore/core\";\nimport type { ExpoSyncoreBootstrap } from \"./index.js\";\n\ntype ExpoSyncoreClient = SyncoreClient;\n\n/**\n * Props for {@link SyncoreExpoProvider}.\n */\nexport interface SyncoreExpoProviderProps {\n /** The bootstrap created with `createExpoSyncoreBootstrap`. */\n bootstrap: ExpoSyncoreBootstrap;\n\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered while the local runtime starts. */\n fallback?: ReactNode;\n}\n\n/**\n * Start an Expo Syncore bootstrap and provide its client to React descendants.\n */\nexport function SyncoreExpoProvider({\n bootstrap,\n children,\n fallback = null\n}: SyncoreExpoProviderProps): ReactNode {\n const client = useMemo(\n () =>\n createDeferredSyncoreClient({\n loadClient: () => bootstrap.getClient(),\n initialStatus: {\n kind: \"starting\",\n reason: \"booting\"\n },\n failureReason: \"runtime-unavailable\"\n }) as ExpoSyncoreClient,\n [bootstrap]\n );\n\n useEffect(() => {\n return () => {\n void bootstrap.stop();\n };\n }, [bootstrap]);\n\n return <SyncoreProvider client={client}>{children ?? fallback}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;;AA0BA,SAAgB,oBAAoB,EAClC,WACA,UACA,WAAW,QAC2B;CACtC,MAAM,SAAS,cAEX,4BAA4B;EAC1B,kBAAkB,UAAU,WAAW;EACvC,eAAe;GACb,MAAM;GACN,QAAQ;GACT;EACD,eAAe;EAChB,CAAC,EACJ,CAAC,UAAU,CACZ;AAED,iBAAgB;AACd,eAAa;AACN,aAAU,MAAM;;IAEtB,CAAC,UAAU,CAAC;AAEf,QAAO,oBAAC,iBAAD;EAAyB;YAAS,YAAY;EAA2B,CAAA"}