syncorejs 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/dist/{core/src/cli.d.ts → _vendor/cli/app.d.mts} +4 -2
  2. package/dist/_vendor/cli/app.d.mts.map +1 -0
  3. package/dist/_vendor/cli/app.mjs +997 -0
  4. package/dist/_vendor/cli/app.mjs.map +1 -0
  5. package/dist/_vendor/cli/context.mjs +180 -0
  6. package/dist/_vendor/cli/context.mjs.map +1 -0
  7. package/dist/_vendor/cli/dev-session.mjs +49 -0
  8. package/dist/_vendor/cli/dev-session.mjs.map +1 -0
  9. package/dist/_vendor/cli/doctor.mjs +80 -0
  10. package/dist/_vendor/cli/doctor.mjs.map +1 -0
  11. package/dist/_vendor/cli/errors.mjs +22 -0
  12. package/dist/_vendor/cli/errors.mjs.map +1 -0
  13. package/dist/_vendor/cli/help.mjs +26 -0
  14. package/dist/_vendor/cli/help.mjs.map +1 -0
  15. package/dist/_vendor/cli/index.d.mts +2 -0
  16. package/dist/_vendor/cli/index.mjs +23 -0
  17. package/dist/_vendor/cli/index.mjs.map +1 -0
  18. package/dist/_vendor/cli/messages.mjs +32 -0
  19. package/dist/_vendor/cli/messages.mjs.map +1 -0
  20. package/dist/_vendor/cli/preflight.mjs +35 -0
  21. package/dist/_vendor/cli/preflight.mjs.map +1 -0
  22. package/dist/_vendor/cli/project.mjs +583 -0
  23. package/dist/_vendor/cli/project.mjs.map +1 -0
  24. package/dist/_vendor/cli/render.mjs +133 -0
  25. package/dist/_vendor/cli/render.mjs.map +1 -0
  26. package/dist/_vendor/cli/targets.mjs +87 -0
  27. package/dist/_vendor/cli/targets.mjs.map +1 -0
  28. package/dist/_vendor/core/cli.d.mts +59 -1
  29. package/dist/_vendor/core/cli.d.mts.map +1 -1
  30. package/dist/_vendor/core/cli.mjs +528 -75
  31. package/dist/_vendor/core/cli.mjs.map +1 -1
  32. package/dist/_vendor/core/index.d.mts +12 -4
  33. package/dist/_vendor/core/index.d.mts.map +1 -0
  34. package/dist/_vendor/core/index.mjs +4 -3
  35. package/dist/_vendor/core/index.mjs.map +1 -1
  36. package/dist/_vendor/core/runtime/devtools.d.mts +32 -6
  37. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  38. package/dist/_vendor/core/runtime/devtools.mjs +397 -182
  39. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  40. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  41. package/dist/_vendor/core/runtime/runtime.d.mts +89 -7
  42. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  43. package/dist/_vendor/core/runtime/runtime.mjs +303 -32
  44. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  45. package/dist/_vendor/devtools-protocol/index.d.ts +189 -82
  46. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  47. package/dist/_vendor/devtools-protocol/index.js +39 -0
  48. package/dist/_vendor/devtools-protocol/index.js.map +1 -0
  49. package/dist/_vendor/next/config.d.ts.map +1 -1
  50. package/dist/_vendor/next/config.js +2 -5
  51. package/dist/_vendor/next/config.js.map +1 -1
  52. package/dist/_vendor/platform-expo/index.d.ts +15 -5
  53. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  54. package/dist/_vendor/platform-expo/index.js +33 -3
  55. package/dist/_vendor/platform-expo/index.js.map +1 -1
  56. package/dist/_vendor/platform-expo/react.js.map +1 -1
  57. package/dist/_vendor/platform-node/index.d.mts +10 -5
  58. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  59. package/dist/_vendor/platform-node/index.mjs +145 -35
  60. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  61. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  62. package/dist/_vendor/platform-web/external-change.d.ts +39 -0
  63. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -0
  64. package/dist/_vendor/platform-web/external-change.js +61 -0
  65. package/dist/_vendor/platform-web/external-change.js.map +1 -0
  66. package/dist/_vendor/platform-web/index.d.ts +27 -5
  67. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  68. package/dist/_vendor/platform-web/index.js +310 -44
  69. package/dist/_vendor/platform-web/index.js.map +1 -1
  70. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  71. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  72. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  73. package/dist/_vendor/platform-web/react.js.map +1 -1
  74. package/dist/_vendor/platform-web/sqljs.js +22 -2
  75. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  76. package/dist/_vendor/schema/definition.js.map +1 -1
  77. package/dist/_vendor/schema/planner.js.map +1 -1
  78. package/dist/_vendor/schema/validators.js.map +1 -1
  79. package/dist/browser-react.d.ts +1 -1
  80. package/dist/browser-react.js +1 -1
  81. package/dist/browser.d.ts +6 -7
  82. package/dist/browser.d.ts.map +1 -1
  83. package/dist/browser.js +4 -5
  84. package/dist/browser.js.map +1 -1
  85. package/dist/cli.d.ts +1 -1
  86. package/dist/cli.js +12 -3
  87. package/dist/cli.js.map +1 -1
  88. package/dist/expo-react.d.ts +1 -1
  89. package/dist/expo-react.js +1 -1
  90. package/dist/expo.d.ts +1 -2
  91. package/dist/expo.js +1 -2
  92. package/dist/index.d.ts +3 -7
  93. package/dist/index.js +3 -8
  94. package/dist/next-config.d.ts +1 -2
  95. package/dist/next-config.js +1 -2
  96. package/dist/next.d.ts +1 -3
  97. package/dist/next.js +1 -3
  98. package/dist/node-ipc-react.d.ts +1 -1
  99. package/dist/node-ipc-react.js +1 -1
  100. package/dist/node-ipc.d.ts +1 -2
  101. package/dist/node-ipc.js +1 -2
  102. package/dist/node.d.ts +1 -4
  103. package/dist/node.js +1 -3
  104. package/dist/react.d.ts +1 -2
  105. package/dist/react.js +1 -2
  106. package/dist/svelte.d.ts +1 -2
  107. package/dist/svelte.js +1 -2
  108. package/package.json +6 -3
  109. package/dist/core/src/cli.d.ts.map +0 -1
  110. package/dist/core/src/cli.js +0 -1196
  111. package/dist/core/src/cli.js.map +0 -1
  112. package/dist/core/src/index.js +0 -7
  113. package/dist/core/src/runtime/devtools.d.ts +0 -7
  114. package/dist/core/src/runtime/devtools.d.ts.map +0 -1
  115. package/dist/core/src/runtime/devtools.js +0 -300
  116. package/dist/core/src/runtime/devtools.js.map +0 -1
  117. package/dist/core/src/runtime/functions.d.ts +0 -123
  118. package/dist/core/src/runtime/functions.d.ts.map +0 -1
  119. package/dist/core/src/runtime/functions.js +0 -71
  120. package/dist/core/src/runtime/functions.js.map +0 -1
  121. package/dist/core/src/runtime/id.d.ts +0 -13
  122. package/dist/core/src/runtime/id.d.ts.map +0 -1
  123. package/dist/core/src/runtime/id.js +0 -28
  124. package/dist/core/src/runtime/id.js.map +0 -1
  125. package/dist/core/src/runtime/runtime.d.ts +0 -371
  126. package/dist/core/src/runtime/runtime.d.ts.map +0 -1
  127. package/dist/core/src/runtime/runtime.js +0 -1143
  128. package/dist/core/src/runtime/runtime.js.map +0 -1
  129. package/dist/devtools-protocol/src/index.d.ts +0 -201
  130. package/dist/devtools-protocol/src/index.d.ts.map +0 -1
  131. package/dist/next/src/config.d.ts +0 -17
  132. package/dist/next/src/config.d.ts.map +0 -1
  133. package/dist/next/src/config.js +0 -73
  134. package/dist/next/src/config.js.map +0 -1
  135. package/dist/next/src/index.d.ts +0 -80
  136. package/dist/next/src/index.d.ts.map +0 -1
  137. package/dist/next/src/index.js +0 -82
  138. package/dist/next/src/index.js.map +0 -1
  139. package/dist/platform-expo/src/index.d.ts +0 -96
  140. package/dist/platform-expo/src/index.d.ts.map +0 -1
  141. package/dist/platform-expo/src/index.js +0 -198
  142. package/dist/platform-expo/src/index.js.map +0 -1
  143. package/dist/platform-expo/src/react.d.ts +0 -26
  144. package/dist/platform-expo/src/react.d.ts.map +0 -1
  145. package/dist/platform-expo/src/react.js +0 -30
  146. package/dist/platform-expo/src/react.js.map +0 -1
  147. package/dist/platform-node/src/index.d.ts +0 -145
  148. package/dist/platform-node/src/index.d.ts.map +0 -1
  149. package/dist/platform-node/src/index.js +0 -407
  150. package/dist/platform-node/src/index.js.map +0 -1
  151. package/dist/platform-node/src/ipc-react.d.ts +0 -25
  152. package/dist/platform-node/src/ipc-react.d.ts.map +0 -1
  153. package/dist/platform-node/src/ipc-react.js +0 -21
  154. package/dist/platform-node/src/ipc-react.js.map +0 -1
  155. package/dist/platform-node/src/ipc.d.ts +0 -76
  156. package/dist/platform-node/src/ipc.d.ts.map +0 -1
  157. package/dist/platform-node/src/ipc.js +0 -344
  158. package/dist/platform-node/src/ipc.js.map +0 -1
  159. package/dist/platform-web/src/index.d.ts +0 -106
  160. package/dist/platform-web/src/index.d.ts.map +0 -1
  161. package/dist/platform-web/src/index.js +0 -311
  162. package/dist/platform-web/src/index.js.map +0 -1
  163. package/dist/platform-web/src/indexeddb.js +0 -125
  164. package/dist/platform-web/src/indexeddb.js.map +0 -1
  165. package/dist/platform-web/src/opfs.js +0 -146
  166. package/dist/platform-web/src/opfs.js.map +0 -1
  167. package/dist/platform-web/src/persistence.d.ts +0 -20
  168. package/dist/platform-web/src/persistence.d.ts.map +0 -1
  169. package/dist/platform-web/src/persistence.js +0 -23
  170. package/dist/platform-web/src/persistence.js.map +0 -1
  171. package/dist/platform-web/src/react.d.ts +0 -35
  172. package/dist/platform-web/src/react.d.ts.map +0 -1
  173. package/dist/platform-web/src/react.js +0 -42
  174. package/dist/platform-web/src/react.js.map +0 -1
  175. package/dist/platform-web/src/sqljs.js +0 -133
  176. package/dist/platform-web/src/sqljs.js.map +0 -1
  177. package/dist/platform-web/src/worker.d.ts +0 -79
  178. package/dist/platform-web/src/worker.d.ts.map +0 -1
  179. package/dist/platform-web/src/worker.js +0 -308
  180. package/dist/platform-web/src/worker.js.map +0 -1
  181. package/dist/react/src/index.d.ts +0 -59
  182. package/dist/react/src/index.d.ts.map +0 -1
  183. package/dist/react/src/index.js +0 -151
  184. package/dist/react/src/index.js.map +0 -1
  185. package/dist/schema/src/definition.d.ts +0 -98
  186. package/dist/schema/src/definition.d.ts.map +0 -1
  187. package/dist/schema/src/definition.js +0 -84
  188. package/dist/schema/src/definition.js.map +0 -1
  189. package/dist/schema/src/planner.d.ts +0 -42
  190. package/dist/schema/src/planner.d.ts.map +0 -1
  191. package/dist/schema/src/planner.js +0 -131
  192. package/dist/schema/src/planner.js.map +0 -1
  193. package/dist/schema/src/validators.d.ts +0 -194
  194. package/dist/schema/src/validators.d.ts.map +0 -1
  195. package/dist/schema/src/validators.js +0 -158
  196. package/dist/schema/src/validators.js.map +0 -1
  197. package/dist/svelte/src/index.d.ts +0 -44
  198. package/dist/svelte/src/index.d.ts.map +0 -1
  199. package/dist/svelte/src/index.js +0 -75
  200. package/dist/svelte/src/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import type { SyncoreWebPersistence, StoredWebFile } from \"./persistence.js\";\r\n\r\nexport interface IndexedDbPersistenceOptions {\r\n databaseName?: string;\r\n}\r\n\r\ntype StoredDatabaseRecord = {\r\n key: string;\r\n bytes: ArrayBuffer;\r\n updatedAt: number;\r\n};\r\n\r\ntype StoredFileRecord = {\r\n key: string;\r\n bytes: ArrayBuffer;\r\n contentType: string | null;\r\n size: number;\r\n updatedAt: number;\r\n};\r\n\r\nexport class SyncoreIndexedDbPersistence implements SyncoreWebPersistence {\r\n readonly storageProtocol = \"idb\" as const;\r\n private readonly databaseName: string;\r\n\r\n constructor(options?: IndexedDbPersistenceOptions) {\r\n this.databaseName = options?.databaseName ?? \"syncore-web\";\r\n }\r\n\r\n async loadDatabase(key: string): Promise<Uint8Array | null> {\r\n const record = await this.getRecord<StoredDatabaseRecord>(\"databases\", key);\r\n if (!record) {\r\n return null;\r\n }\r\n return new Uint8Array(record.bytes);\r\n }\r\n\r\n async saveDatabase(key: string, bytes: Uint8Array): Promise<void> {\r\n await this.putRecord<StoredDatabaseRecord>(\"databases\", {\r\n key,\r\n bytes: sliceToArrayBuffer(bytes),\r\n updatedAt: Date.now()\r\n });\r\n }\r\n\r\n async getFile(\r\n namespace: string,\r\n id: string\r\n ): Promise<StoredWebFile | null> {\r\n const record = await this.getRecord<StoredFileRecord>(\r\n \"files\",\r\n createNamespacedKey(namespace, id)\r\n );\r\n if (!record) {\r\n return null;\r\n }\r\n return {\r\n id,\r\n bytes: new Uint8Array(record.bytes),\r\n contentType: record.contentType,\r\n size: record.size\r\n };\r\n }\r\n\r\n async putFile(\r\n namespace: string,\r\n id: string,\r\n bytes: Uint8Array,\r\n contentType: string | null\r\n ): Promise<void> {\r\n await this.putRecord<StoredFileRecord>(\"files\", {\r\n key: createNamespacedKey(namespace, id),\r\n bytes: sliceToArrayBuffer(bytes),\r\n contentType,\r\n size: bytes.byteLength,\r\n updatedAt: Date.now()\r\n });\r\n }\r\n\r\n async deleteFile(namespace: string, id: string): Promise<void> {\r\n await this.deleteRecord(\"files\", createNamespacedKey(namespace, id));\r\n }\r\n\r\n async listFiles(namespace: string): Promise<StoredWebFile[]> {\r\n const prefix = `${namespace}:`;\r\n const records = await this.listRecords<StoredFileRecord>(\"files\");\r\n return records\r\n .filter((record) => record.key.startsWith(prefix))\r\n .map((record) => ({\r\n id: record.key.slice(prefix.length),\r\n bytes: new Uint8Array(record.bytes),\r\n contentType: record.contentType,\r\n size: record.size\r\n }));\r\n }\r\n\r\n private async getDatabase(): Promise<IDBDatabase> {\r\n const indexedDb = globalThis.indexedDB;\r\n if (!indexedDb) {\r\n throw new Error(\"IndexedDB is not available in this environment.\");\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDb.open(this.databaseName, 1);\r\n request.onupgradeneeded = () => {\r\n const database = request.result;\r\n if (!database.objectStoreNames.contains(\"databases\")) {\r\n database.createObjectStore(\"databases\", { keyPath: \"key\" });\r\n }\r\n if (!database.objectStoreNames.contains(\"files\")) {\r\n database.createObjectStore(\"files\", { keyPath: \"key\" });\r\n }\r\n };\r\n request.onsuccess = () => resolve(request.result);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(\"Failed to open IndexedDB.\"));\r\n });\r\n }\r\n\r\n private async getRecord<TRecord>(\r\n storeName: \"databases\" | \"files\",\r\n key: string\r\n ): Promise<TRecord | null> {\r\n const database = await this.getDatabase();\r\n try {\r\n return await new Promise<TRecord | null>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readonly\");\r\n const request = transaction.objectStore(storeName).get(key);\r\n request.onsuccess = () => resolve((request.result as TRecord | undefined) ?? null);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(`Failed to read ${storeName}/${key}.`));\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async putRecord<TRecord extends { key: string }>(\r\n storeName: \"databases\" | \"files\",\r\n record: TRecord\r\n ): Promise<void> {\r\n const database = await this.getDatabase();\r\n try {\r\n await new Promise<void>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readwrite\");\r\n transaction.oncomplete = () => resolve();\r\n transaction.onerror = () =>\r\n reject(transaction.error ?? new Error(`Failed to write ${storeName}/${record.key}.`));\r\n transaction.objectStore(storeName).put(record);\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async deleteRecord(\r\n storeName: \"databases\" | \"files\",\r\n key: string\r\n ): Promise<void> {\r\n const database = await this.getDatabase();\r\n try {\r\n await new Promise<void>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readwrite\");\r\n transaction.oncomplete = () => resolve();\r\n transaction.onerror = () =>\r\n reject(transaction.error ?? new Error(`Failed to delete ${storeName}/${key}.`));\r\n transaction.objectStore(storeName).delete(key);\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n\r\n private async listRecords<TRecord>(\r\n storeName: \"databases\" | \"files\"\r\n ): Promise<TRecord[]> {\r\n const database = await this.getDatabase();\r\n try {\r\n return await new Promise<TRecord[]>((resolve, reject) => {\r\n const transaction = database.transaction(storeName, \"readonly\");\r\n const request = transaction.objectStore(storeName).getAll();\r\n request.onsuccess = () => resolve((request.result as TRecord[] | undefined) ?? []);\r\n request.onerror = () =>\r\n reject(request.error ?? new Error(`Failed to list records from ${storeName}.`));\r\n });\r\n } finally {\r\n database.close();\r\n }\r\n }\r\n}\r\n\r\nfunction createNamespacedKey(namespace: string, id: string): string {\r\n return `${namespace}:${id}`;\r\n}\r\n\r\nfunction sliceToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\r\n return bytes.buffer.slice(\r\n bytes.byteOffset,\r\n bytes.byteOffset + bytes.byteLength\r\n ) as ArrayBuffer;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"indexeddb.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import type { SyncoreWebPersistence, StoredWebFile } from \"./persistence.js\";\n\nexport interface IndexedDbPersistenceOptions {\n databaseName?: string;\n}\n\ntype StoredDatabaseRecord = {\n key: string;\n bytes: ArrayBuffer;\n updatedAt: number;\n};\n\ntype StoredFileRecord = {\n key: string;\n bytes: ArrayBuffer;\n contentType: string | null;\n size: number;\n updatedAt: number;\n};\n\nexport class SyncoreIndexedDbPersistence implements SyncoreWebPersistence {\n readonly storageProtocol = \"idb\" as const;\n private readonly databaseName: string;\n\n constructor(options?: IndexedDbPersistenceOptions) {\n this.databaseName = options?.databaseName ?? \"syncore-web\";\n }\n\n async loadDatabase(key: string): Promise<Uint8Array | null> {\n const record = await this.getRecord<StoredDatabaseRecord>(\"databases\", key);\n if (!record) {\n return null;\n }\n return new Uint8Array(record.bytes);\n }\n\n async saveDatabase(key: string, bytes: Uint8Array): Promise<void> {\n await this.putRecord<StoredDatabaseRecord>(\"databases\", {\n key,\n bytes: sliceToArrayBuffer(bytes),\n updatedAt: Date.now()\n });\n }\n\n async getFile(\n namespace: string,\n id: string\n ): Promise<StoredWebFile | null> {\n const record = await this.getRecord<StoredFileRecord>(\n \"files\",\n createNamespacedKey(namespace, id)\n );\n if (!record) {\n return null;\n }\n return {\n id,\n bytes: new Uint8Array(record.bytes),\n contentType: record.contentType,\n size: record.size\n };\n }\n\n async putFile(\n namespace: string,\n id: string,\n bytes: Uint8Array,\n contentType: string | null\n ): Promise<void> {\n await this.putRecord<StoredFileRecord>(\"files\", {\n key: createNamespacedKey(namespace, id),\n bytes: sliceToArrayBuffer(bytes),\n contentType,\n size: bytes.byteLength,\n updatedAt: Date.now()\n });\n }\n\n async deleteFile(namespace: string, id: string): Promise<void> {\n await this.deleteRecord(\"files\", createNamespacedKey(namespace, id));\n }\n\n async listFiles(namespace: string): Promise<StoredWebFile[]> {\n const prefix = `${namespace}:`;\n const records = await this.listRecords<StoredFileRecord>(\"files\");\n return records\n .filter((record) => record.key.startsWith(prefix))\n .map((record) => ({\n id: record.key.slice(prefix.length),\n bytes: new Uint8Array(record.bytes),\n contentType: record.contentType,\n size: record.size\n }));\n }\n\n private async getDatabase(): Promise<IDBDatabase> {\n const indexedDb = globalThis.indexedDB;\n if (!indexedDb) {\n throw new Error(\"IndexedDB is not available in this environment.\");\n }\n\n return new Promise((resolve, reject) => {\n const request = indexedDb.open(this.databaseName, 1);\n request.onupgradeneeded = () => {\n const database = request.result;\n if (!database.objectStoreNames.contains(\"databases\")) {\n database.createObjectStore(\"databases\", { keyPath: \"key\" });\n }\n if (!database.objectStoreNames.contains(\"files\")) {\n database.createObjectStore(\"files\", { keyPath: \"key\" });\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () =>\n reject(request.error ?? new Error(\"Failed to open IndexedDB.\"));\n });\n }\n\n private async getRecord<TRecord>(\n storeName: \"databases\" | \"files\",\n key: string\n ): Promise<TRecord | null> {\n const database = await this.getDatabase();\n try {\n return await new Promise<TRecord | null>((resolve, reject) => {\n const transaction = database.transaction(storeName, \"readonly\");\n const request = transaction.objectStore(storeName).get(key);\n request.onsuccess = () => resolve((request.result as TRecord | undefined) ?? null);\n request.onerror = () =>\n reject(request.error ?? new Error(`Failed to read ${storeName}/${key}.`));\n });\n } finally {\n database.close();\n }\n }\n\n private async putRecord<TRecord extends { key: string }>(\n storeName: \"databases\" | \"files\",\n record: TRecord\n ): Promise<void> {\n const database = await this.getDatabase();\n try {\n await new Promise<void>((resolve, reject) => {\n const transaction = database.transaction(storeName, \"readwrite\");\n transaction.oncomplete = () => resolve();\n transaction.onerror = () =>\n reject(transaction.error ?? new Error(`Failed to write ${storeName}/${record.key}.`));\n transaction.objectStore(storeName).put(record);\n });\n } finally {\n database.close();\n }\n }\n\n private async deleteRecord(\n storeName: \"databases\" | \"files\",\n key: string\n ): Promise<void> {\n const database = await this.getDatabase();\n try {\n await new Promise<void>((resolve, reject) => {\n const transaction = database.transaction(storeName, \"readwrite\");\n transaction.oncomplete = () => resolve();\n transaction.onerror = () =>\n reject(transaction.error ?? new Error(`Failed to delete ${storeName}/${key}.`));\n transaction.objectStore(storeName).delete(key);\n });\n } finally {\n database.close();\n }\n }\n\n private async listRecords<TRecord>(\n storeName: \"databases\" | \"files\"\n ): Promise<TRecord[]> {\n const database = await this.getDatabase();\n try {\n return await new Promise<TRecord[]>((resolve, reject) => {\n const transaction = database.transaction(storeName, \"readonly\");\n const request = transaction.objectStore(storeName).getAll();\n request.onsuccess = () => resolve((request.result as TRecord[] | undefined) ?? []);\n request.onerror = () =>\n reject(request.error ?? new Error(`Failed to list records from ${storeName}.`));\n });\n } finally {\n database.close();\n }\n }\n}\n\nfunction createNamespacedKey(namespace: string, id: string): string {\n return `${namespace}:${id}`;\n}\n\nfunction sliceToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength\n ) as ArrayBuffer;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"opfs.js","names":[],"sources":["../src/opfs.ts"],"sourcesContent":["import type { SyncoreWebPersistence, StoredWebFile } from \"./persistence.js\";\r\n\r\nexport interface OpfsPersistenceOptions {\r\n rootDirectoryName?: string;\r\n}\r\n\r\ntype StoredFileMetadata = {\r\n contentType: string | null;\r\n};\r\n\r\ntype OpfsStorageManager = StorageManager & {\r\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\r\n};\r\n\r\nexport class SyncoreOpfsPersistence implements SyncoreWebPersistence {\r\n readonly storageProtocol = \"opfs\" as const;\r\n private rootDirectoryPromise: Promise<FileSystemDirectoryHandle> | undefined;\r\n\r\n constructor(private readonly options: OpfsPersistenceOptions = {}) {}\r\n\r\n async loadDatabase(key: string): Promise<Uint8Array | null> {\r\n const handle = await this.getOptionalFileHandle(\r\n [\"databases\"],\r\n `${encodePathComponent(key)}.sqlite`\r\n );\r\n if (!handle) {\r\n return null;\r\n }\r\n return readFileBytes(handle);\r\n }\r\n\r\n async saveDatabase(key: string, bytes: Uint8Array): Promise<void> {\r\n const directory = await this.ensureDirectory([\"databases\"]);\r\n await writeBytes(\r\n await directory.getFileHandle(`${encodePathComponent(key)}.sqlite`, {\r\n create: true\r\n }),\r\n bytes\r\n );\r\n }\r\n\r\n async getFile(namespace: string, id: string): Promise<StoredWebFile | null> {\r\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\r\n if (!directory) {\r\n return null;\r\n }\r\n\r\n const fileName = `${encodePathComponent(id)}.bin`;\r\n const metadataName = `${encodePathComponent(id)}.meta.json`;\r\n const fileHandle = await this.getOptionalFileHandleFromDirectory(directory, fileName);\r\n if (!fileHandle) {\r\n return null;\r\n }\r\n\r\n const [bytes, metadata] = await Promise.all([\r\n readFileBytes(fileHandle),\r\n this.readMetadata(directory, metadataName)\r\n ]);\r\n\r\n return {\r\n id,\r\n bytes,\r\n size: bytes.byteLength,\r\n contentType: metadata?.contentType ?? null\r\n };\r\n }\r\n\r\n async putFile(\r\n namespace: string,\r\n id: string,\r\n bytes: Uint8Array,\r\n contentType: string | null\r\n ): Promise<void> {\r\n const directory = await this.ensureDirectory([\"files\", encodePathComponent(namespace)]);\r\n const encodedId = encodePathComponent(id);\r\n\r\n await writeBytes(\r\n await directory.getFileHandle(`${encodedId}.bin`, { create: true }),\r\n bytes\r\n );\r\n await writeText(\r\n await directory.getFileHandle(`${encodedId}.meta.json`, { create: true }),\r\n JSON.stringify({ contentType } satisfies StoredFileMetadata)\r\n );\r\n }\r\n\r\n async deleteFile(namespace: string, id: string): Promise<void> {\r\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\r\n if (!directory) {\r\n return;\r\n }\r\n\r\n const encodedId = encodePathComponent(id);\r\n await removeEntryIfExists(directory, `${encodedId}.bin`);\r\n await removeEntryIfExists(directory, `${encodedId}.meta.json`);\r\n }\r\n\r\n async listFiles(namespace: string): Promise<StoredWebFile[]> {\r\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\r\n if (!directory) {\r\n return [];\r\n }\r\n\r\n const files: StoredWebFile[] = [];\r\n const iterableDirectory = directory as FileSystemDirectoryHandle & {\r\n entries(): AsyncIterable<[string, FileSystemHandle]>;\r\n };\r\n for await (const [name, handle] of iterableDirectory.entries()) {\r\n if (handle.kind !== \"file\" || !name.endsWith(\".bin\")) {\r\n continue;\r\n }\r\n const encodedId = name.slice(0, -4);\r\n const id = decodeURIComponent(encodedId);\r\n const bytes = await readFileBytes(handle as FileSystemFileHandle);\r\n const metadata = await this.readMetadata(directory, `${encodedId}.meta.json`);\r\n files.push({\r\n id,\r\n bytes,\r\n size: bytes.byteLength,\r\n contentType: metadata?.contentType ?? null\r\n });\r\n }\r\n return files;\r\n }\r\n\r\n private async ensureDirectory(\r\n pathSegments: string[]\r\n ): Promise<FileSystemDirectoryHandle> {\r\n let directory = await this.getRootDirectory();\r\n for (const segment of pathSegments) {\r\n directory = await directory.getDirectoryHandle(segment, { create: true });\r\n }\r\n return directory;\r\n }\r\n\r\n private async getOptionalDirectory(\r\n pathSegments: string[]\r\n ): Promise<FileSystemDirectoryHandle | null> {\r\n try {\r\n let directory = await this.getRootDirectory();\r\n for (const segment of pathSegments) {\r\n directory = await directory.getDirectoryHandle(segment);\r\n }\r\n return directory;\r\n } catch (error) {\r\n if (isNotFoundError(error)) {\r\n return null;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n private async getOptionalFileHandle(\r\n pathSegments: string[],\r\n fileName: string\r\n ): Promise<FileSystemFileHandle | null> {\r\n const directory = await this.getOptionalDirectory(pathSegments);\r\n if (!directory) {\r\n return null;\r\n }\r\n return this.getOptionalFileHandleFromDirectory(directory, fileName);\r\n }\r\n\r\n private async getOptionalFileHandleFromDirectory(\r\n directory: FileSystemDirectoryHandle,\r\n fileName: string\r\n ): Promise<FileSystemFileHandle | null> {\r\n try {\r\n return await directory.getFileHandle(fileName);\r\n } catch (error) {\r\n if (isNotFoundError(error)) {\r\n return null;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n private async readMetadata(\r\n directory: FileSystemDirectoryHandle,\r\n fileName: string\r\n ): Promise<StoredFileMetadata | null> {\r\n const handle = await this.getOptionalFileHandleFromDirectory(directory, fileName);\r\n if (!handle) {\r\n return null;\r\n }\r\n\r\n const bytes = await readFileBytes(handle);\r\n return JSON.parse(new TextDecoder().decode(bytes)) as StoredFileMetadata;\r\n }\r\n\r\n private async getRootDirectory(): Promise<FileSystemDirectoryHandle> {\r\n if (!this.rootDirectoryPromise) {\r\n this.rootDirectoryPromise = (async () => {\r\n const storageManager = getOpfsStorageManager();\r\n if (!storageManager?.getDirectory) {\r\n throw new Error(\"OPFS is not available in this environment.\");\r\n }\r\n const root = await storageManager.getDirectory();\r\n return root.getDirectoryHandle(\r\n this.options.rootDirectoryName ?? \"syncore\",\r\n { create: true }\r\n );\r\n })();\r\n }\r\n return this.rootDirectoryPromise;\r\n }\r\n}\r\n\r\nasync function readFileBytes(handle: FileSystemFileHandle): Promise<Uint8Array> {\r\n const file = await handle.getFile();\r\n return new Uint8Array(await file.arrayBuffer());\r\n}\r\n\r\nasync function writeBytes(\r\n handle: FileSystemFileHandle,\r\n bytes: Uint8Array\r\n): Promise<void> {\r\n const writable = await handle.createWritable();\r\n try {\r\n await writable.write(sliceToArrayBuffer(bytes));\r\n await writable.truncate(bytes.byteLength);\r\n } finally {\r\n await writable.close();\r\n }\r\n}\r\n\r\nasync function writeText(\r\n handle: FileSystemFileHandle,\r\n value: string\r\n): Promise<void> {\r\n await writeBytes(handle, new TextEncoder().encode(value));\r\n}\r\n\r\nasync function removeEntryIfExists(\r\n directory: FileSystemDirectoryHandle,\r\n name: string\r\n): Promise<void> {\r\n try {\r\n await directory.removeEntry(name);\r\n } catch (error) {\r\n if (!isNotFoundError(error)) {\r\n throw error;\r\n }\r\n }\r\n}\r\n\r\nfunction encodePathComponent(value: string): string {\r\n return encodeURIComponent(value);\r\n}\r\n\r\nfunction sliceToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\r\n return bytes.buffer.slice(\r\n bytes.byteOffset,\r\n bytes.byteOffset + bytes.byteLength\r\n ) as ArrayBuffer;\r\n}\r\n\r\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\r\n if (typeof navigator === \"undefined\") {\r\n return undefined;\r\n }\r\n return navigator.storage as OpfsStorageManager | undefined;\r\n}\r\n\r\nfunction isNotFoundError(error: unknown): boolean {\r\n return (\r\n typeof error === \"object\" &&\r\n error !== null &&\r\n \"name\" in error &&\r\n error.name === \"NotFoundError\"\r\n );\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"opfs.js","names":[],"sources":["../src/opfs.ts"],"sourcesContent":["import type { SyncoreWebPersistence, StoredWebFile } from \"./persistence.js\";\n\nexport interface OpfsPersistenceOptions {\n rootDirectoryName?: string;\n}\n\ntype StoredFileMetadata = {\n contentType: string | null;\n};\n\ntype OpfsStorageManager = StorageManager & {\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\n};\n\nexport class SyncoreOpfsPersistence implements SyncoreWebPersistence {\n readonly storageProtocol = \"opfs\" as const;\n private rootDirectoryPromise: Promise<FileSystemDirectoryHandle> | undefined;\n\n constructor(private readonly options: OpfsPersistenceOptions = {}) {}\n\n async loadDatabase(key: string): Promise<Uint8Array | null> {\n const handle = await this.getOptionalFileHandle(\n [\"databases\"],\n `${encodePathComponent(key)}.sqlite`\n );\n if (!handle) {\n return null;\n }\n return readFileBytes(handle);\n }\n\n async saveDatabase(key: string, bytes: Uint8Array): Promise<void> {\n const directory = await this.ensureDirectory([\"databases\"]);\n await writeBytes(\n await directory.getFileHandle(`${encodePathComponent(key)}.sqlite`, {\n create: true\n }),\n bytes\n );\n }\n\n async getFile(namespace: string, id: string): Promise<StoredWebFile | null> {\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\n if (!directory) {\n return null;\n }\n\n const fileName = `${encodePathComponent(id)}.bin`;\n const metadataName = `${encodePathComponent(id)}.meta.json`;\n const fileHandle = await this.getOptionalFileHandleFromDirectory(directory, fileName);\n if (!fileHandle) {\n return null;\n }\n\n const [bytes, metadata] = await Promise.all([\n readFileBytes(fileHandle),\n this.readMetadata(directory, metadataName)\n ]);\n\n return {\n id,\n bytes,\n size: bytes.byteLength,\n contentType: metadata?.contentType ?? null\n };\n }\n\n async putFile(\n namespace: string,\n id: string,\n bytes: Uint8Array,\n contentType: string | null\n ): Promise<void> {\n const directory = await this.ensureDirectory([\"files\", encodePathComponent(namespace)]);\n const encodedId = encodePathComponent(id);\n\n await writeBytes(\n await directory.getFileHandle(`${encodedId}.bin`, { create: true }),\n bytes\n );\n await writeText(\n await directory.getFileHandle(`${encodedId}.meta.json`, { create: true }),\n JSON.stringify({ contentType } satisfies StoredFileMetadata)\n );\n }\n\n async deleteFile(namespace: string, id: string): Promise<void> {\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\n if (!directory) {\n return;\n }\n\n const encodedId = encodePathComponent(id);\n await removeEntryIfExists(directory, `${encodedId}.bin`);\n await removeEntryIfExists(directory, `${encodedId}.meta.json`);\n }\n\n async listFiles(namespace: string): Promise<StoredWebFile[]> {\n const directory = await this.getOptionalDirectory([\"files\", encodePathComponent(namespace)]);\n if (!directory) {\n return [];\n }\n\n const files: StoredWebFile[] = [];\n const iterableDirectory = directory as FileSystemDirectoryHandle & {\n entries(): AsyncIterable<[string, FileSystemHandle]>;\n };\n for await (const [name, handle] of iterableDirectory.entries()) {\n if (handle.kind !== \"file\" || !name.endsWith(\".bin\")) {\n continue;\n }\n const encodedId = name.slice(0, -4);\n const id = decodeURIComponent(encodedId);\n const bytes = await readFileBytes(handle as FileSystemFileHandle);\n const metadata = await this.readMetadata(directory, `${encodedId}.meta.json`);\n files.push({\n id,\n bytes,\n size: bytes.byteLength,\n contentType: metadata?.contentType ?? null\n });\n }\n return files;\n }\n\n private async ensureDirectory(\n pathSegments: string[]\n ): Promise<FileSystemDirectoryHandle> {\n let directory = await this.getRootDirectory();\n for (const segment of pathSegments) {\n directory = await directory.getDirectoryHandle(segment, { create: true });\n }\n return directory;\n }\n\n private async getOptionalDirectory(\n pathSegments: string[]\n ): Promise<FileSystemDirectoryHandle | null> {\n try {\n let directory = await this.getRootDirectory();\n for (const segment of pathSegments) {\n directory = await directory.getDirectoryHandle(segment);\n }\n return directory;\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n private async getOptionalFileHandle(\n pathSegments: string[],\n fileName: string\n ): Promise<FileSystemFileHandle | null> {\n const directory = await this.getOptionalDirectory(pathSegments);\n if (!directory) {\n return null;\n }\n return this.getOptionalFileHandleFromDirectory(directory, fileName);\n }\n\n private async getOptionalFileHandleFromDirectory(\n directory: FileSystemDirectoryHandle,\n fileName: string\n ): Promise<FileSystemFileHandle | null> {\n try {\n return await directory.getFileHandle(fileName);\n } catch (error) {\n if (isNotFoundError(error)) {\n return null;\n }\n throw error;\n }\n }\n\n private async readMetadata(\n directory: FileSystemDirectoryHandle,\n fileName: string\n ): Promise<StoredFileMetadata | null> {\n const handle = await this.getOptionalFileHandleFromDirectory(directory, fileName);\n if (!handle) {\n return null;\n }\n\n const bytes = await readFileBytes(handle);\n return JSON.parse(new TextDecoder().decode(bytes)) as StoredFileMetadata;\n }\n\n private async getRootDirectory(): Promise<FileSystemDirectoryHandle> {\n if (!this.rootDirectoryPromise) {\n this.rootDirectoryPromise = (async () => {\n const storageManager = getOpfsStorageManager();\n if (!storageManager?.getDirectory) {\n throw new Error(\"OPFS is not available in this environment.\");\n }\n const root = await storageManager.getDirectory();\n return root.getDirectoryHandle(\n this.options.rootDirectoryName ?? \"syncore\",\n { create: true }\n );\n })();\n }\n return this.rootDirectoryPromise;\n }\n}\n\nasync function readFileBytes(handle: FileSystemFileHandle): Promise<Uint8Array> {\n const file = await handle.getFile();\n return new Uint8Array(await file.arrayBuffer());\n}\n\nasync function writeBytes(\n handle: FileSystemFileHandle,\n bytes: Uint8Array\n): Promise<void> {\n const writable = await handle.createWritable();\n try {\n await writable.write(sliceToArrayBuffer(bytes));\n await writable.truncate(bytes.byteLength);\n } finally {\n await writable.close();\n }\n}\n\nasync function writeText(\n handle: FileSystemFileHandle,\n value: string\n): Promise<void> {\n await writeBytes(handle, new TextEncoder().encode(value));\n}\n\nasync function removeEntryIfExists(\n directory: FileSystemDirectoryHandle,\n name: string\n): Promise<void> {\n try {\n await directory.removeEntry(name);\n } catch (error) {\n if (!isNotFoundError(error)) {\n throw error;\n }\n }\n}\n\nfunction encodePathComponent(value: string): string {\n return encodeURIComponent(value);\n}\n\nfunction sliceToArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength\n ) as ArrayBuffer;\n}\n\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\n if (typeof navigator === \"undefined\") {\n return undefined;\n }\n return navigator.storage as OpfsStorageManager | undefined;\n}\n\nfunction isNotFoundError(error: unknown): boolean {\n return (\n typeof error === \"object\" &&\n error !== null &&\n \"name\" in error &&\n error.name === \"NotFoundError\"\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.js","names":[],"sources":["../src/persistence.ts"],"sourcesContent":["import { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\r\nimport { SyncoreOpfsPersistence } from \"./opfs.js\";\r\n\r\nexport interface StoredWebFile {\r\n id: string;\r\n bytes: Uint8Array;\r\n contentType: string | null;\r\n size: number;\r\n}\r\n\r\nexport interface SyncoreWebPersistence {\r\n readonly storageProtocol: \"idb\" | \"opfs\";\r\n loadDatabase(key: string): Promise<Uint8Array | null>;\r\n saveDatabase(key: string, bytes: Uint8Array): Promise<void>;\r\n getFile(namespace: string, id: string): Promise<StoredWebFile | null>;\r\n putFile(\r\n namespace: string,\r\n id: string,\r\n bytes: Uint8Array,\r\n contentType: string | null\r\n ): Promise<void>;\r\n deleteFile(namespace: string, id: string): Promise<void>;\r\n listFiles(namespace: string): Promise<StoredWebFile[]>;\r\n}\r\n\r\nexport type WebPersistenceMode = \"auto\" | \"indexeddb\" | \"opfs\";\r\n\r\nexport interface CreateWebPersistenceOptions {\r\n mode?: WebPersistenceMode;\r\n indexedDbDatabaseName?: string;\r\n opfsRootDirectoryName?: string;\r\n}\r\n\r\nexport async function createWebPersistence(\r\n options: CreateWebPersistenceOptions = {}\r\n): Promise<SyncoreWebPersistence> {\r\n const mode = options.mode ?? \"auto\";\r\n\r\n if (mode === \"opfs\") {\r\n if (!isOpfsAvailable()) {\r\n throw new Error(\"OPFS is not available in this environment.\");\r\n }\r\n return new SyncoreOpfsPersistence(\r\n options.opfsRootDirectoryName\r\n ? { rootDirectoryName: options.opfsRootDirectoryName }\r\n : undefined\r\n );\r\n }\r\n\r\n if (mode === \"auto\" && isOpfsAvailable()) {\r\n return new SyncoreOpfsPersistence(\r\n options.opfsRootDirectoryName\r\n ? { rootDirectoryName: options.opfsRootDirectoryName }\r\n : undefined\r\n );\r\n }\r\n\r\n return new SyncoreIndexedDbPersistence(\r\n options.indexedDbDatabaseName\r\n ? { databaseName: options.indexedDbDatabaseName }\r\n : undefined\r\n );\r\n}\r\n\r\nexport function isOpfsAvailable(): boolean {\r\n return Boolean(getOpfsStorageManager()?.getDirectory);\r\n}\r\n\r\ntype OpfsStorageManager = StorageManager & {\r\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\r\n};\r\n\r\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\r\n if (typeof navigator === \"undefined\") {\r\n return undefined;\r\n }\r\n return navigator.storage as OpfsStorageManager | undefined;\r\n}\r\n"],"mappings":";;;AAiCA,eAAsB,qBACpB,UAAuC,EAAE,EACT;CAChC,MAAM,OAAO,QAAQ,QAAQ;AAE7B,KAAI,SAAS,QAAQ;AACnB,MAAI,CAAC,iBAAiB,CACpB,OAAM,IAAI,MAAM,6CAA6C;AAE/D,SAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,uBAAuB,GACpD,KAAA,EACL;;AAGH,KAAI,SAAS,UAAU,iBAAiB,CACtC,QAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,uBAAuB,GACpD,KAAA,EACL;AAGH,QAAO,IAAI,4BACT,QAAQ,wBACJ,EAAE,cAAc,QAAQ,uBAAuB,GAC/C,KAAA,EACL;;AAGH,SAAgB,kBAA2B;AACzC,QAAO,QAAQ,uBAAuB,EAAE,aAAa;;AAOvD,SAAS,wBAAwD;AAC/D,KAAI,OAAO,cAAc,YACvB;AAEF,QAAO,UAAU"}
1
+ {"version":3,"file":"persistence.js","names":[],"sources":["../src/persistence.ts"],"sourcesContent":["import { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport { SyncoreOpfsPersistence } from \"./opfs.js\";\n\nexport interface StoredWebFile {\n id: string;\n bytes: Uint8Array;\n contentType: string | null;\n size: number;\n}\n\nexport interface SyncoreWebPersistence {\n readonly storageProtocol: \"idb\" | \"opfs\";\n loadDatabase(key: string): Promise<Uint8Array | null>;\n saveDatabase(key: string, bytes: Uint8Array): Promise<void>;\n getFile(namespace: string, id: string): Promise<StoredWebFile | null>;\n putFile(\n namespace: string,\n id: string,\n bytes: Uint8Array,\n contentType: string | null\n ): Promise<void>;\n deleteFile(namespace: string, id: string): Promise<void>;\n listFiles(namespace: string): Promise<StoredWebFile[]>;\n}\n\nexport type WebPersistenceMode = \"auto\" | \"indexeddb\" | \"opfs\";\n\nexport interface CreateWebPersistenceOptions {\n mode?: WebPersistenceMode;\n indexedDbDatabaseName?: string;\n opfsRootDirectoryName?: string;\n}\n\nexport async function createWebPersistence(\n options: CreateWebPersistenceOptions = {}\n): Promise<SyncoreWebPersistence> {\n const mode = options.mode ?? \"auto\";\n\n if (mode === \"opfs\") {\n if (!isOpfsAvailable()) {\n throw new Error(\"OPFS is not available in this environment.\");\n }\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n if (mode === \"auto\" && isOpfsAvailable()) {\n return new SyncoreOpfsPersistence(\n options.opfsRootDirectoryName\n ? { rootDirectoryName: options.opfsRootDirectoryName }\n : undefined\n );\n }\n\n return new SyncoreIndexedDbPersistence(\n options.indexedDbDatabaseName\n ? { databaseName: options.indexedDbDatabaseName }\n : undefined\n );\n}\n\nexport function isOpfsAvailable(): boolean {\n return Boolean(getOpfsStorageManager()?.getDirectory);\n}\n\ntype OpfsStorageManager = StorageManager & {\n getDirectory?: () => Promise<FileSystemDirectoryHandle>;\n};\n\nfunction getOpfsStorageManager(): OpfsStorageManager | undefined {\n if (typeof navigator === \"undefined\") {\n return undefined;\n }\n return navigator.storage as OpfsStorageManager | undefined;\n}\n"],"mappings":";;;AAiCA,eAAsB,qBACpB,UAAuC,EAAE,EACT;CAChC,MAAM,OAAO,QAAQ,QAAQ;AAE7B,KAAI,SAAS,QAAQ;AACnB,MAAI,CAAC,iBAAiB,CACpB,OAAM,IAAI,MAAM,6CAA6C;AAE/D,SAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,uBAAuB,GACpD,KAAA,EACL;;AAGH,KAAI,SAAS,UAAU,iBAAiB,CACtC,QAAO,IAAI,uBACT,QAAQ,wBACJ,EAAE,mBAAmB,QAAQ,uBAAuB,GACpD,KAAA,EACL;AAGH,QAAO,IAAI,4BACT,QAAQ,wBACJ,EAAE,cAAc,QAAQ,uBAAuB,GAC/C,KAAA,EACL;;AAGH,SAAgB,kBAA2B;AACzC,QAAO,QAAQ,uBAAuB,EAAE,aAAa;;AAOvD,SAAS,wBAAwD;AAC/D,KAAI,OAAO,cAAc,YACvB;AAEF,QAAO,UAAU"}
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":[],"sources":["../src/react.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\r\nimport type { ReactNode } from \"react\";\r\nimport { SyncoreProvider } from \"@syncore/react\";\r\nimport {\r\n createSyncoreWebWorkerClient,\r\n type CreateWebWorkerClientProviderOptions,\r\n type ManagedWebWorkerClient\r\n} from \"./worker.js\";\r\n\r\n/**\r\n * Props for {@link SyncoreWebProvider}.\r\n */\r\nexport interface SyncoreWebProviderProps extends CreateWebWorkerClientProviderOptions {\r\n /** The React subtree that should receive the Syncore client. */\r\n children: ReactNode;\r\n\r\n /** Optional fallback content rendered before the worker client is ready. */\r\n fallback?: ReactNode;\r\n}\r\n\r\n/**\r\n * Props for {@link SyncoreBrowserProvider}.\r\n */\r\nexport type SyncoreBrowserProviderProps = SyncoreWebProviderProps;\r\n\r\n/**\r\n * Start a worker-backed Syncore client and provide it to React descendants.\r\n */\r\nexport function SyncoreWebProvider({\r\n children,\r\n workerUrl,\r\n workerType,\r\n workerName,\r\n fallback = null\r\n}: SyncoreWebProviderProps): ReactNode {\r\n const [managedClient, setManagedClient] =\r\n useState<ManagedWebWorkerClient | null>(null);\r\n\r\n useEffect(() => {\r\n const nextClient = createSyncoreWebWorkerClient({\r\n workerUrl,\r\n ...(workerType ? { workerType } : {}),\r\n ...(workerName ? { workerName } : {})\r\n });\r\n setManagedClient(nextClient);\r\n\r\n return () => {\r\n nextClient.dispose();\r\n setManagedClient(null);\r\n };\r\n }, [workerName, workerType, workerUrl]);\r\n\r\n if (!managedClient) {\r\n return fallback;\r\n }\r\n\r\n return (\r\n <SyncoreProvider client={managedClient.client}>{children}</SyncoreProvider>\r\n );\r\n}\r\n\r\n/**\r\n * Start a worker-backed Syncore client and provide it to React descendants.\r\n */\r\nexport function SyncoreBrowserProvider(props: SyncoreBrowserProviderProps) {\r\n return <SyncoreWebProvider {...props} />;\r\n}\r\n"],"mappings":";;;;;;;;AA4BA,SAAgB,mBAAmB,EACjC,UACA,WACA,YACA,YACA,WAAW,QAC0B;CACrC,MAAM,CAAC,eAAe,oBACpB,SAAwC,KAAK;AAE/C,iBAAgB;EACd,MAAM,aAAa,6BAA6B;GAC9C;GACA,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;GACpC,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;GACrC,CAAC;AACF,mBAAiB,WAAW;AAE5B,eAAa;AACX,cAAW,SAAS;AACpB,oBAAiB,KAAK;;IAEvB;EAAC;EAAY;EAAY;EAAU,CAAC;AAEvC,KAAI,CAAC,cACH,QAAO;AAGT,QACE,oBAAC,iBAAD;EAAiB,QAAQ,cAAc;EAAS;EAA2B,CAAA;;;;;AAO/E,SAAgB,uBAAuB,OAAoC;AACzE,QAAO,oBAAC,oBAAD,EAAoB,GAAI,OAAS,CAAA"}
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 {\n createSyncoreWebWorkerClient,\n type CreateWebWorkerClientProviderOptions,\n type ManagedWebWorkerClient\n} from \"./worker.js\";\n\n/**\n * Props for {@link SyncoreWebProvider}.\n */\nexport interface SyncoreWebProviderProps extends CreateWebWorkerClientProviderOptions {\n /** The React subtree that should receive the Syncore client. */\n children: ReactNode;\n\n /** Optional fallback content rendered before the worker client is ready. */\n fallback?: ReactNode;\n}\n\n/**\n * Props for {@link SyncoreBrowserProvider}.\n */\nexport type SyncoreBrowserProviderProps = SyncoreWebProviderProps;\n\n/**\n * Start a worker-backed Syncore client and provide it to React descendants.\n */\nexport function SyncoreWebProvider({\n children,\n workerUrl,\n workerType,\n workerName,\n fallback = null\n}: SyncoreWebProviderProps): ReactNode {\n const [managedClient, setManagedClient] =\n useState<ManagedWebWorkerClient | null>(null);\n\n useEffect(() => {\n const nextClient = createSyncoreWebWorkerClient({\n workerUrl,\n ...(workerType ? { workerType } : {}),\n ...(workerName ? { workerName } : {})\n });\n setManagedClient(nextClient);\n\n return () => {\n nextClient.dispose();\n setManagedClient(null);\n };\n }, [workerName, workerType, workerUrl]);\n\n if (!managedClient) {\n return fallback;\n }\n\n return (\n <SyncoreProvider client={managedClient.client}>{children}</SyncoreProvider>\n );\n}\n\n/**\n * Start a worker-backed Syncore client and provide it to React descendants.\n */\nexport function SyncoreBrowserProvider(props: SyncoreBrowserProviderProps) {\n return <SyncoreWebProvider {...props} />;\n}\n"],"mappings":";;;;;;;;AA4BA,SAAgB,mBAAmB,EACjC,UACA,WACA,YACA,YACA,WAAW,QAC0B;CACrC,MAAM,CAAC,eAAe,oBACpB,SAAwC,KAAK;AAE/C,iBAAgB;EACd,MAAM,aAAa,6BAA6B;GAC9C;GACA,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;GACpC,GAAI,aAAa,EAAE,YAAY,GAAG,EAAE;GACrC,CAAC;AACF,mBAAiB,WAAW;AAE5B,eAAa;AACX,cAAW,SAAS;AACpB,oBAAiB,KAAK;;IAEvB;EAAC;EAAY;EAAY;EAAU,CAAC;AAEvC,KAAI,CAAC,cACH,QAAO;AAGT,QACE,oBAAC,iBAAD;EAAiB,QAAQ,cAAc;EAAS;EAA2B,CAAA;;;;;AAO/E,SAAgB,uBAAuB,OAAoC;AACzE,QAAO,oBAAC,oBAAD,EAAoB,GAAI,OAAS,CAAA"}
@@ -4,16 +4,17 @@ import initSqlJs from "sql.js";
4
4
  var SqlJsDriver = class SqlJsDriver {
5
5
  transactionDepth = 0;
6
6
  closed = false;
7
- constructor(database, persistence, databaseName) {
7
+ constructor(database, persistence, databaseName, createDatabase) {
8
8
  this.database = database;
9
9
  this.persistence = persistence;
10
10
  this.databaseName = databaseName;
11
+ this.createDatabase = createDatabase;
11
12
  }
12
13
  static async create(options) {
13
14
  const persistence = options.persistence ?? new SyncoreIndexedDbPersistence();
14
15
  const SQL = await initSqlJs(typeof window === "undefined" && !options.locateFile && !options.wasmUrl ? void 0 : { locateFile: options.locateFile ?? (() => options.wasmUrl ?? "/sql-wasm.wasm") });
15
16
  const existingBytes = await persistence.loadDatabase(options.databaseName);
16
- return new SqlJsDriver(existingBytes ? new SQL.Database(existingBytes) : new SQL.Database(), persistence, options.databaseName);
17
+ return new SqlJsDriver(existingBytes ? new SQL.Database(existingBytes) : new SQL.Database(), persistence, options.databaseName, (bytes) => bytes ? new SQL.Database(bytes) : new SQL.Database());
17
18
  }
18
19
  async exec(sql) {
19
20
  this.ensureOpen();
@@ -99,6 +100,19 @@ var SqlJsDriver = class SqlJsDriver {
99
100
  this.database.close();
100
101
  this.closed = true;
101
102
  }
103
+ async reloadFromPersistence() {
104
+ this.ensureOpen();
105
+ const bytes = await this.persistence.loadDatabase(this.databaseName);
106
+ if (!bytes) return false;
107
+ const nextDatabase = this.createDatabase(bytes);
108
+ const previousDatabase = this.database;
109
+ this.database = nextDatabase;
110
+ previousDatabase.close();
111
+ return true;
112
+ }
113
+ createDatabaseFromBytes(bytes) {
114
+ return this.createDatabase(bytes);
115
+ }
102
116
  async persistIfNeeded() {
103
117
  if (this.transactionDepth === 0) await this.persistNow();
104
118
  }
@@ -109,6 +123,12 @@ var SqlJsDriver = class SqlJsDriver {
109
123
  ensureOpen() {
110
124
  if (this.closed) throw new Error("The sql.js driver is already closed.");
111
125
  }
126
+ replaceDatabase(database) {
127
+ this.ensureOpen();
128
+ const previousDatabase = this.database;
129
+ this.database = database;
130
+ previousDatabase.close();
131
+ }
112
132
  };
113
133
  function normalizeParams(values) {
114
134
  return values.map((value) => {
@@ -1 +1 @@
1
- {"version":3,"file":"sqljs.js","names":[],"sources":["../src/sqljs.ts"],"sourcesContent":["import initSqlJs from \"sql.js\";\r\nimport type { RunResult, SyncoreSqlDriver } from \"@syncore/core\";\r\nimport { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\r\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\r\n\r\ntype SqlJsDatabase = initSqlJs.Database;\r\ntype SqlJsValue = initSqlJs.SqlValue;\r\n\r\nexport interface CreateSqlJsDriverOptions {\r\n databaseName: string;\r\n persistence?: SyncoreWebPersistence;\r\n wasmUrl?: string;\r\n locateFile?: (fileName: string) => string;\r\n}\r\n\r\nexport class SqlJsDriver implements SyncoreSqlDriver {\r\n private transactionDepth = 0;\r\n private closed = false;\r\n\r\n constructor(\r\n private readonly database: SqlJsDatabase,\r\n private readonly persistence: SyncoreWebPersistence,\r\n private readonly databaseName: string\r\n ) {}\r\n\r\n static async create(options: CreateSqlJsDriverOptions): Promise<SqlJsDriver> {\r\n const persistence =\r\n options.persistence ?? new SyncoreIndexedDbPersistence();\r\n const SQL = await initSqlJs(\r\n typeof window === \"undefined\" && !options.locateFile && !options.wasmUrl\r\n ? undefined\r\n : {\r\n locateFile:\r\n options.locateFile ?? (() => options.wasmUrl ?? \"/sql-wasm.wasm\")\r\n }\r\n );\r\n const existingBytes = await persistence.loadDatabase(options.databaseName);\r\n const database = existingBytes\r\n ? new SQL.Database(existingBytes)\r\n : new SQL.Database();\r\n return new SqlJsDriver(database, persistence, options.databaseName);\r\n }\r\n\r\n async exec(sql: string): Promise<void> {\r\n this.ensureOpen();\r\n this.database.run(sql);\r\n await this.persistIfNeeded();\r\n }\r\n\r\n async run(sql: string, params: unknown[] = []): Promise<RunResult> {\r\n this.ensureOpen();\r\n const statement = this.database.prepare(sql);\r\n try {\r\n statement.run(normalizeParams(params));\r\n const lastInsertRowid = readScalarNumber(\r\n this.database,\r\n \"SELECT last_insert_rowid()\"\r\n );\r\n const result = {\r\n changes: this.database.getRowsModified(),\r\n ...(lastInsertRowid !== null ? { lastInsertRowid } : {})\r\n };\r\n await this.persistIfNeeded();\r\n return result;\r\n } finally {\r\n statement.free();\r\n }\r\n }\r\n\r\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\r\n this.ensureOpen();\r\n const statement = this.database.prepare(sql);\r\n try {\r\n statement.bind(normalizeParams(params));\r\n if (!statement.step()) {\r\n return undefined;\r\n }\r\n return statement.getAsObject() as T;\r\n } finally {\r\n statement.free();\r\n }\r\n }\r\n\r\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\r\n this.ensureOpen();\r\n const statement = this.database.prepare(sql);\r\n try {\r\n statement.bind(normalizeParams(params));\r\n const rows: T[] = [];\r\n while (statement.step()) {\r\n rows.push(statement.getAsObject() as T);\r\n }\r\n return rows;\r\n } finally {\r\n statement.free();\r\n }\r\n }\r\n\r\n async withTransaction<T>(callback: () => Promise<T>): Promise<T> {\r\n this.ensureOpen();\r\n if (this.transactionDepth > 0) {\r\n return this.withSavepoint(`nested_${this.transactionDepth}`, callback);\r\n }\r\n\r\n this.transactionDepth += 1;\r\n this.database.run(\"BEGIN IMMEDIATE\");\r\n try {\r\n const result = await callback();\r\n this.database.run(\"COMMIT\");\r\n await this.persistNow();\r\n return result;\r\n } catch (error) {\r\n this.database.run(\"ROLLBACK\");\r\n throw error;\r\n } finally {\r\n this.transactionDepth -= 1;\r\n }\r\n }\r\n\r\n async withSavepoint<T>(name: string, callback: () => Promise<T>): Promise<T> {\r\n this.ensureOpen();\r\n const safeName = name.replaceAll(/[^a-zA-Z0-9_]/g, \"_\");\r\n this.database.run(`SAVEPOINT ${safeName}`);\r\n this.transactionDepth += 1;\r\n try {\r\n const result = await callback();\r\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\r\n return result;\r\n } catch (error) {\r\n this.database.run(`ROLLBACK TO SAVEPOINT ${safeName}`);\r\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\r\n throw error;\r\n } finally {\r\n this.transactionDepth -= 1;\r\n }\r\n }\r\n\r\n async close(): Promise<void> {\r\n if (this.closed) {\r\n return;\r\n }\r\n await this.persistNow();\r\n this.database.close();\r\n this.closed = true;\r\n }\r\n\r\n private async persistIfNeeded(): Promise<void> {\r\n if (this.transactionDepth === 0) {\r\n await this.persistNow();\r\n }\r\n }\r\n\r\n private async persistNow(): Promise<void> {\r\n this.ensureOpen();\r\n await this.persistence.saveDatabase(\r\n this.databaseName,\r\n this.database.export()\r\n );\r\n }\r\n\r\n private ensureOpen(): void {\r\n if (this.closed) {\r\n throw new Error(\"The sql.js driver is already closed.\");\r\n }\r\n }\r\n}\r\n\r\nfunction normalizeParams(values: unknown[]): SqlJsValue[] {\r\n return values.map((value) => {\r\n if (typeof value === \"boolean\") {\r\n return value ? 1 : 0;\r\n }\r\n if (\r\n value === null ||\r\n typeof value === \"number\" ||\r\n typeof value === \"string\" ||\r\n value instanceof Uint8Array\r\n ) {\r\n return value;\r\n }\r\n if (value instanceof ArrayBuffer) {\r\n return new Uint8Array(value);\r\n }\r\n return JSON.stringify(value);\r\n });\r\n}\r\n\r\nfunction readScalarNumber(database: SqlJsDatabase, sql: string): number | null {\r\n const rows = database.exec(sql);\r\n const firstSet = rows[0];\r\n const firstRow = firstSet?.values[0];\r\n const firstValue = firstRow?.[0];\r\n if (typeof firstValue === \"number\") {\r\n return firstValue;\r\n }\r\n if (typeof firstValue === \"string\") {\r\n const parsed = Number(firstValue);\r\n return Number.isNaN(parsed) ? null : parsed;\r\n }\r\n return null;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"sqljs.js","names":[],"sources":["../src/sqljs.ts"],"sourcesContent":["import initSqlJs from \"sql.js\";\nimport type { RunResult, SyncoreSqlDriver } from \"@syncore/core\";\nimport { SyncoreIndexedDbPersistence } from \"./indexeddb.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\ntype SqlJsValue = initSqlJs.SqlValue;\n\nexport interface CreateSqlJsDriverOptions {\n databaseName: string;\n persistence?: SyncoreWebPersistence;\n wasmUrl?: string;\n locateFile?: (fileName: string) => string;\n}\n\nexport class SqlJsDriver implements SyncoreSqlDriver {\n private transactionDepth = 0;\n private closed = false;\n\n constructor(\n private database: SqlJsDatabase,\n private readonly persistence: SyncoreWebPersistence,\n private readonly databaseName: string,\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase\n ) {}\n\n static async create(options: CreateSqlJsDriverOptions): Promise<SqlJsDriver> {\n const persistence =\n options.persistence ?? new SyncoreIndexedDbPersistence();\n const SQL = await initSqlJs(\n typeof window === \"undefined\" && !options.locateFile && !options.wasmUrl\n ? undefined\n : {\n locateFile:\n options.locateFile ?? (() => options.wasmUrl ?? \"/sql-wasm.wasm\")\n }\n );\n const existingBytes = await persistence.loadDatabase(options.databaseName);\n const database = existingBytes\n ? new SQL.Database(existingBytes)\n : new SQL.Database();\n return new SqlJsDriver(\n database,\n persistence,\n options.databaseName,\n (bytes) => (bytes ? new SQL.Database(bytes) : new SQL.Database())\n );\n }\n\n async exec(sql: string): Promise<void> {\n this.ensureOpen();\n this.database.run(sql);\n await this.persistIfNeeded();\n }\n\n async run(sql: string, params: unknown[] = []): Promise<RunResult> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.run(normalizeParams(params));\n const lastInsertRowid = readScalarNumber(\n this.database,\n \"SELECT last_insert_rowid()\"\n );\n const result = {\n changes: this.database.getRowsModified(),\n ...(lastInsertRowid !== null ? { lastInsertRowid } : {})\n };\n await this.persistIfNeeded();\n return result;\n } finally {\n statement.free();\n }\n }\n\n async get<T>(sql: string, params: unknown[] = []): Promise<T | undefined> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n if (!statement.step()) {\n return undefined;\n }\n return statement.getAsObject() as T;\n } finally {\n statement.free();\n }\n }\n\n async all<T>(sql: string, params: unknown[] = []): Promise<T[]> {\n this.ensureOpen();\n const statement = this.database.prepare(sql);\n try {\n statement.bind(normalizeParams(params));\n const rows: T[] = [];\n while (statement.step()) {\n rows.push(statement.getAsObject() as T);\n }\n return rows;\n } finally {\n statement.free();\n }\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 this.database.run(\"BEGIN IMMEDIATE\");\n try {\n const result = await callback();\n this.database.run(\"COMMIT\");\n await this.persistNow();\n return result;\n } catch (error) {\n this.database.run(\"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.database.run(`SAVEPOINT ${safeName}`);\n this.transactionDepth += 1;\n try {\n const result = await callback();\n this.database.run(`RELEASE SAVEPOINT ${safeName}`);\n return result;\n } catch (error) {\n this.database.run(`ROLLBACK TO SAVEPOINT ${safeName}`);\n this.database.run(`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.persistNow();\n this.database.close();\n this.closed = true;\n }\n\n async reloadFromPersistence(): Promise<boolean> {\n this.ensureOpen();\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (!bytes) {\n return false;\n }\n const nextDatabase = this.createDatabase(bytes);\n const previousDatabase = this.database;\n this.database = nextDatabase;\n previousDatabase.close();\n return true;\n }\n\n createDatabaseFromBytes(bytes?: Uint8Array): SqlJsDatabase {\n return this.createDatabase(bytes);\n }\n\n private async persistIfNeeded(): Promise<void> {\n if (this.transactionDepth === 0) {\n await this.persistNow();\n }\n }\n\n private async persistNow(): Promise<void> {\n this.ensureOpen();\n await this.persistence.saveDatabase(\n this.databaseName,\n this.database.export()\n );\n }\n\n private ensureOpen(): void {\n if (this.closed) {\n throw new Error(\"The sql.js driver is already closed.\");\n }\n }\n\n replaceDatabase(database: SqlJsDatabase): void {\n this.ensureOpen();\n const previousDatabase = this.database;\n this.database = database;\n previousDatabase.close();\n }\n}\n\nfunction normalizeParams(values: unknown[]): SqlJsValue[] {\n return values.map((value) => {\n if (typeof value === \"boolean\") {\n return value ? 1 : 0;\n }\n if (\n value === null ||\n typeof value === \"number\" ||\n typeof value === \"string\" ||\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 readScalarNumber(database: SqlJsDatabase, sql: string): number | null {\n const rows = database.exec(sql);\n const firstSet = rows[0];\n const firstRow = firstSet?.values[0];\n const firstValue = firstRow?.[0];\n if (typeof firstValue === \"number\") {\n return firstValue;\n }\n if (typeof firstValue === \"string\") {\n const parsed = Number(firstValue);\n return Number.isNaN(parsed) ? null : parsed;\n }\n return null;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"definition.js","names":[],"sources":["../src/definition.ts"],"sourcesContent":["import {\r\n ensureObjectValidator,\r\n type Infer,\r\n type ObjectValidatorShape,\r\n type Validator\r\n} from \"./validators.js\";\r\nimport type { ObjectValidator } from \"./validators.js\";\r\n\r\nexport interface IndexDefinition {\r\n name: string;\r\n fields: string[];\r\n}\r\n\r\nexport interface SearchIndexDefinition {\r\n name: string;\r\n searchField: string;\r\n filterFields: string[];\r\n}\r\n\r\nexport interface TableDefinitionOptions {\r\n tableName?: string;\r\n}\r\n\r\nexport interface TableDocumentSystemFields {\r\n _id: string;\r\n _creationTime: number;\r\n}\r\n\r\n/**\r\n * Describes a Syncore table and its indexes.\r\n *\r\n * Create tables with {@link defineTable} and then chain index helpers to make\r\n * queries faster and more expressive.\r\n */\r\nexport class TableDefinition<TValidator extends Validator<unknown>> {\r\n readonly indexes: IndexDefinition[] = [];\r\n readonly searchIndexes: SearchIndexDefinition[] = [];\r\n readonly options: TableDefinitionOptions;\r\n\r\n constructor(\r\n public readonly validator: TValidator,\r\n options?: TableDefinitionOptions\r\n ) {\r\n this.options = options ?? {};\r\n }\r\n\r\n /**\r\n * Add a named index for querying a table by one or more fields.\r\n *\r\n * @param name - The index name used from `ctx.db.query(...).withIndex(...)`.\r\n * @param fields - The fields that participate in the index.\r\n * @returns The same table definition for chaining.\r\n */\r\n index(name: string, fields: string[]): this {\r\n this.indexes.push({ name, fields });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add a search index for text search.\r\n *\r\n * @param name - The search index name used from `withSearchIndex(...)`.\r\n * @param config - The indexed search field and optional filter fields.\r\n * @returns The same table definition for chaining.\r\n */\r\n searchIndex(\r\n name: string,\r\n config: { searchField: string; filterFields?: string[] }\r\n ): this {\r\n this.searchIndexes.push({\r\n name,\r\n searchField: config.searchField,\r\n filterFields: config.filterFields ?? []\r\n });\r\n return this;\r\n }\r\n}\r\n\r\nexport type AnyTableDefinition = TableDefinition<Validator<unknown>>;\r\n\r\nexport type InferDocument<TTable extends AnyTableDefinition> = Infer<\r\n TTable[\"validator\"]\r\n> &\r\n TableDocumentSystemFields;\r\n\r\nexport type InferTableInput<TTable extends AnyTableDefinition> = Omit<\r\n InferDocument<TTable>,\r\n keyof TableDocumentSystemFields\r\n>;\r\n\r\n/**\r\n * Define a table in a Syncore schema.\r\n *\r\n * Pass an object of validators describing the document fields stored in the\r\n * table. Chain `.index(...)` or `.searchIndex(...)` to add query helpers.\r\n *\r\n * @example\r\n * ```ts\r\n * const tasks = defineTable({\r\n * text: v.string(),\r\n * done: v.boolean()\r\n * }).index(\"by_done\", [\"done\"]);\r\n * ```\r\n */\r\nexport function defineTable<TShape extends ObjectValidatorShape>(\r\n validator: TShape\r\n): TableDefinition<ObjectValidator<TShape>>;\r\nexport function defineTable<TValidator extends Validator<unknown>>(\r\n validator: TValidator\r\n): TableDefinition<TValidator>;\r\nexport function defineTable<TShape extends ObjectValidatorShape>(\r\n validator: TShape | Validator<unknown>\r\n): TableDefinition<Validator<unknown>> {\r\n return new TableDefinition(ensureObjectValidator(validator));\r\n}\r\n\r\nexport interface SyncoreSchemaDefinition {\r\n [tableName: string]: AnyTableDefinition;\r\n}\r\n\r\nexport class SyncoreSchema<TTables extends SyncoreSchemaDefinition> {\r\n constructor(public readonly tables: TTables) {}\r\n\r\n getTable<TTableName extends Extract<keyof TTables, string>>(\r\n tableName: TTableName\r\n ): TTables[TTableName] {\r\n const table = this.tables[tableName];\r\n if (!table) {\r\n throw new Error(`Unknown table \"${tableName}\".`);\r\n }\r\n return table;\r\n }\r\n\r\n tableNames(): Array<Extract<keyof TTables, string>> {\r\n return Object.keys(this.tables) as Array<Extract<keyof TTables, string>>;\r\n }\r\n}\r\n\r\n/**\r\n * Define the tables that make up your Syncore app.\r\n *\r\n * The returned schema is used by runtimes, code generation, and type inference.\r\n *\r\n * @example\r\n * ```ts\r\n * export default defineSchema({\r\n * tasks: defineTable({\r\n * text: v.string(),\r\n * done: v.boolean()\r\n * })\r\n * });\r\n * ```\r\n */\r\nexport function defineSchema<TTables extends SyncoreSchemaDefinition>(\r\n tables: TTables\r\n): SyncoreSchema<TTables> {\r\n return new SyncoreSchema(tables);\r\n}\r\n"],"mappings":";;;;;;;;AAkCA,IAAa,kBAAb,MAAoE;CAClE,UAAsC,EAAE;CACxC,gBAAkD,EAAE;CACpD;CAEA,YACE,WACA,SACA;AAFgB,OAAA,YAAA;AAGhB,OAAK,UAAU,WAAW,EAAE;;;;;;;;;CAU9B,MAAM,MAAc,QAAwB;AAC1C,OAAK,QAAQ,KAAK;GAAE;GAAM;GAAQ,CAAC;AACnC,SAAO;;;;;;;;;CAUT,YACE,MACA,QACM;AACN,OAAK,cAAc,KAAK;GACtB;GACA,aAAa,OAAO;GACpB,cAAc,OAAO,gBAAgB,EAAE;GACxC,CAAC;AACF,SAAO;;;AAoCX,SAAgB,YACd,WACqC;AACrC,QAAO,IAAI,gBAAgB,sBAAsB,UAAU,CAAC;;AAO9D,IAAa,gBAAb,MAAoE;CAClE,YAAY,QAAiC;AAAjB,OAAA,SAAA;;CAE5B,SACE,WACqB;EACrB,MAAM,QAAQ,KAAK,OAAO;AAC1B,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kBAAkB,UAAU,IAAI;AAElD,SAAO;;CAGT,aAAoD;AAClD,SAAO,OAAO,KAAK,KAAK,OAAO;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,aACd,QACwB;AACxB,QAAO,IAAI,cAAc,OAAO"}
1
+ {"version":3,"file":"definition.js","names":[],"sources":["../src/definition.ts"],"sourcesContent":["import {\n ensureObjectValidator,\n type Infer,\n type ObjectValidatorShape,\n type Validator\n} from \"./validators.js\";\nimport type { ObjectValidator } from \"./validators.js\";\n\nexport interface IndexDefinition {\n name: string;\n fields: string[];\n}\n\nexport interface SearchIndexDefinition {\n name: string;\n searchField: string;\n filterFields: string[];\n}\n\nexport interface TableDefinitionOptions {\n tableName?: string;\n}\n\nexport interface TableDocumentSystemFields {\n _id: string;\n _creationTime: number;\n}\n\n/**\n * Describes a Syncore table and its indexes.\n *\n * Create tables with {@link defineTable} and then chain index helpers to make\n * queries faster and more expressive.\n */\nexport class TableDefinition<TValidator extends Validator<unknown>> {\n readonly indexes: IndexDefinition[] = [];\n readonly searchIndexes: SearchIndexDefinition[] = [];\n readonly options: TableDefinitionOptions;\n\n constructor(\n public readonly validator: TValidator,\n options?: TableDefinitionOptions\n ) {\n this.options = options ?? {};\n }\n\n /**\n * Add a named index for querying a table by one or more fields.\n *\n * @param name - The index name used from `ctx.db.query(...).withIndex(...)`.\n * @param fields - The fields that participate in the index.\n * @returns The same table definition for chaining.\n */\n index(name: string, fields: string[]): this {\n this.indexes.push({ name, fields });\n return this;\n }\n\n /**\n * Add a search index for text search.\n *\n * @param name - The search index name used from `withSearchIndex(...)`.\n * @param config - The indexed search field and optional filter fields.\n * @returns The same table definition for chaining.\n */\n searchIndex(\n name: string,\n config: { searchField: string; filterFields?: string[] }\n ): this {\n this.searchIndexes.push({\n name,\n searchField: config.searchField,\n filterFields: config.filterFields ?? []\n });\n return this;\n }\n}\n\nexport type AnyTableDefinition = TableDefinition<Validator<unknown>>;\n\nexport type InferDocument<TTable extends AnyTableDefinition> = Infer<\n TTable[\"validator\"]\n> &\n TableDocumentSystemFields;\n\nexport type InferTableInput<TTable extends AnyTableDefinition> = Omit<\n InferDocument<TTable>,\n keyof TableDocumentSystemFields\n>;\n\n/**\n * Define a table in a Syncore schema.\n *\n * Pass an object of validators describing the document fields stored in the\n * table. Chain `.index(...)` or `.searchIndex(...)` to add query helpers.\n *\n * @example\n * ```ts\n * const tasks = defineTable({\n * text: v.string(),\n * done: v.boolean()\n * }).index(\"by_done\", [\"done\"]);\n * ```\n */\nexport function defineTable<TShape extends ObjectValidatorShape>(\n validator: TShape\n): TableDefinition<ObjectValidator<TShape>>;\nexport function defineTable<TValidator extends Validator<unknown>>(\n validator: TValidator\n): TableDefinition<TValidator>;\nexport function defineTable<TShape extends ObjectValidatorShape>(\n validator: TShape | Validator<unknown>\n): TableDefinition<Validator<unknown>> {\n return new TableDefinition(ensureObjectValidator(validator));\n}\n\nexport interface SyncoreSchemaDefinition {\n [tableName: string]: AnyTableDefinition;\n}\n\nexport class SyncoreSchema<TTables extends SyncoreSchemaDefinition> {\n constructor(public readonly tables: TTables) {}\n\n getTable<TTableName extends Extract<keyof TTables, string>>(\n tableName: TTableName\n ): TTables[TTableName] {\n const table = this.tables[tableName];\n if (!table) {\n throw new Error(`Unknown table \"${tableName}\".`);\n }\n return table;\n }\n\n tableNames(): Array<Extract<keyof TTables, string>> {\n return Object.keys(this.tables) as Array<Extract<keyof TTables, string>>;\n }\n}\n\n/**\n * Define the tables that make up your Syncore app.\n *\n * The returned schema is used by runtimes, code generation, and type inference.\n *\n * @example\n * ```ts\n * export default defineSchema({\n * tasks: defineTable({\n * text: v.string(),\n * done: v.boolean()\n * })\n * });\n * ```\n */\nexport function defineSchema<TTables extends SyncoreSchemaDefinition>(\n tables: TTables\n): SyncoreSchema<TTables> {\n return new SyncoreSchema(tables);\n}\n"],"mappings":";;;;;;;;AAkCA,IAAa,kBAAb,MAAoE;CAClE,UAAsC,EAAE;CACxC,gBAAkD,EAAE;CACpD;CAEA,YACE,WACA,SACA;AAFgB,OAAA,YAAA;AAGhB,OAAK,UAAU,WAAW,EAAE;;;;;;;;;CAU9B,MAAM,MAAc,QAAwB;AAC1C,OAAK,QAAQ,KAAK;GAAE;GAAM;GAAQ,CAAC;AACnC,SAAO;;;;;;;;;CAUT,YACE,MACA,QACM;AACN,OAAK,cAAc,KAAK;GACtB;GACA,aAAa,OAAO;GACpB,cAAc,OAAO,gBAAgB,EAAE;GACxC,CAAC;AACF,SAAO;;;AAoCX,SAAgB,YACd,WACqC;AACrC,QAAO,IAAI,gBAAgB,sBAAsB,UAAU,CAAC;;AAO9D,IAAa,gBAAb,MAAoE;CAClE,YAAY,QAAiC;AAAjB,OAAA,SAAA;;CAE5B,SACE,WACqB;EACrB,MAAM,QAAQ,KAAK,OAAO;AAC1B,MAAI,CAAC,MACH,OAAM,IAAI,MAAM,kBAAkB,UAAU,IAAI;AAElD,SAAO;;CAGT,aAAoD;AAClD,SAAO,OAAO,KAAK,KAAK,OAAO;;;;;;;;;;;;;;;;;;AAmBnC,SAAgB,aACd,QACwB;AACxB,QAAO,IAAI,cAAc,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"planner.js","names":[],"sources":["../src/planner.ts"],"sourcesContent":["import {\r\n type SearchIndexDefinition,\r\n type SyncoreSchemaDefinition,\r\n type SyncoreSchema\r\n} from \"./definition.js\";\r\nimport {\r\n describeValidator,\r\n type ValidatorDescription\r\n} from \"./validators.js\";\r\n\r\nexport interface TableSnapshot {\r\n name: string;\r\n validator: ValidatorDescription;\r\n indexes: Array<{\r\n name: string;\r\n fields: string[];\r\n }>;\r\n searchIndexes: Array<{\r\n name: string;\r\n searchField: string;\r\n filterFields: string[];\r\n }>;\r\n}\r\n\r\nexport interface SchemaSnapshot {\r\n version: 1;\r\n tables: TableSnapshot[];\r\n hash: string;\r\n}\r\n\r\nexport interface SchemaMigrationPlan {\r\n previousHash: string | null;\r\n nextHash: string;\r\n statements: string[];\r\n warnings: string[];\r\n destructiveChanges: string[];\r\n}\r\n\r\nexport function createSchemaSnapshot<TTables extends SyncoreSchemaDefinition>(\r\n schema: SyncoreSchema<TTables>\r\n): SchemaSnapshot {\r\n const tables = schema\r\n .tableNames()\r\n .sort((left, right) => left.localeCompare(right))\r\n .map((tableName) => {\r\n const table = schema.getTable(tableName);\r\n return {\r\n name: tableName,\r\n validator: describeValidator(table.validator),\r\n indexes: table.indexes\r\n .map((index) => ({\r\n name: index.name,\r\n fields: [...index.fields]\r\n }))\r\n .sort((left, right) => left.name.localeCompare(right.name)),\r\n searchIndexes: table.searchIndexes\r\n .map((index) => ({\r\n name: index.name,\r\n searchField: index.searchField,\r\n filterFields: [...index.filterFields]\r\n }))\r\n .sort((left, right) => left.name.localeCompare(right.name))\r\n };\r\n });\r\n\r\n const base = {\r\n version: 1 as const,\r\n tables\r\n };\r\n\r\n return {\r\n ...base,\r\n hash: createSchemaHash(base)\r\n };\r\n}\r\n\r\nexport function diffSchemaSnapshots(\r\n previousSnapshot: SchemaSnapshot | null | undefined,\r\n nextSnapshot: SchemaSnapshot\r\n): SchemaMigrationPlan {\r\n const statements: string[] = [];\r\n const warnings: string[] = [];\r\n const destructiveChanges: string[] = [];\r\n\r\n const previousTables = new Map(\r\n (previousSnapshot?.tables ?? []).map((table) => [table.name, table])\r\n );\r\n const nextTables = new Map(nextSnapshot.tables.map((table) => [table.name, table]));\r\n\r\n for (const table of nextSnapshot.tables) {\r\n const previousTable = previousTables.get(table.name);\r\n if (!previousTable) {\r\n statements.push(renderCreateTableStatement(table.name));\r\n for (const index of table.indexes) {\r\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\r\n }\r\n for (const searchIndex of table.searchIndexes) {\r\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\r\n }\r\n continue;\r\n }\r\n\r\n if (stableStringify(previousTable.validator) !== stableStringify(table.validator)) {\r\n warnings.push(\r\n `Validator changed for table \"${table.name}\". Existing rows are not rewritten automatically.`\r\n );\r\n }\r\n\r\n const previousIndexes = new Map(\r\n previousTable.indexes.map((index) => [index.name, index])\r\n );\r\n const nextIndexes = new Map(table.indexes.map((index) => [index.name, index]));\r\n\r\n for (const index of table.indexes) {\r\n const previousIndex = previousIndexes.get(index.name);\r\n if (!previousIndex) {\r\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\r\n continue;\r\n }\r\n if (stableStringify(previousIndex.fields) !== stableStringify(index.fields)) {\r\n destructiveChanges.push(\r\n `Index \"${table.name}.${index.name}\" changed fields and requires a manual migration.`\r\n );\r\n }\r\n }\r\n\r\n for (const previousIndex of previousTable.indexes) {\r\n if (!nextIndexes.has(previousIndex.name)) {\r\n destructiveChanges.push(\r\n `Index \"${table.name}.${previousIndex.name}\" was removed and requires a manual migration.`\r\n );\r\n }\r\n }\r\n\r\n const previousSearchIndexes = new Map(\r\n previousTable.searchIndexes.map((index) => [index.name, index])\r\n );\r\n const nextSearchIndexes = new Map(\r\n table.searchIndexes.map((index) => [index.name, index])\r\n );\r\n\r\n for (const searchIndex of table.searchIndexes) {\r\n const previousSearchIndex = previousSearchIndexes.get(searchIndex.name);\r\n if (!previousSearchIndex) {\r\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\r\n continue;\r\n }\r\n if (stableStringify(previousSearchIndex) !== stableStringify(searchIndex)) {\r\n destructiveChanges.push(\r\n `Search index \"${table.name}.${searchIndex.name}\" changed and requires a manual migration.`\r\n );\r\n }\r\n }\r\n\r\n for (const previousSearchIndex of previousTable.searchIndexes) {\r\n if (!nextSearchIndexes.has(previousSearchIndex.name)) {\r\n destructiveChanges.push(\r\n `Search index \"${table.name}.${previousSearchIndex.name}\" was removed and requires a manual migration.`\r\n );\r\n }\r\n }\r\n }\r\n\r\n for (const previousTable of previousSnapshot?.tables ?? []) {\r\n if (!nextTables.has(previousTable.name)) {\r\n destructiveChanges.push(\r\n `Table \"${previousTable.name}\" was removed and requires a manual migration.`\r\n );\r\n }\r\n }\r\n\r\n return {\r\n previousHash: previousSnapshot?.hash ?? null,\r\n nextHash: nextSnapshot.hash,\r\n statements,\r\n warnings,\r\n destructiveChanges\r\n };\r\n}\r\n\r\nexport function renderMigrationSql(\r\n plan: SchemaMigrationPlan,\r\n options?: { title?: string }\r\n): string {\r\n const lines: string[] = [];\r\n\r\n lines.push(`-- ${options?.title ?? \"Syncore migration\"}`);\r\n lines.push(`-- previous: ${plan.previousHash ?? \"none\"}`);\r\n lines.push(`-- next: ${plan.nextHash}`);\r\n\r\n for (const warning of plan.warnings) {\r\n lines.push(`-- warning: ${warning}`);\r\n }\r\n\r\n if (plan.destructiveChanges.length > 0) {\r\n for (const destructiveChange of plan.destructiveChanges) {\r\n lines.push(`-- destructive: ${destructiveChange}`);\r\n }\r\n }\r\n\r\n if (plan.statements.length > 0) {\r\n lines.push(\"\");\r\n for (const statement of plan.statements) {\r\n lines.push(statement);\r\n }\r\n } else {\r\n lines.push(\"\");\r\n lines.push(\"-- no-op\");\r\n }\r\n\r\n return `${lines.join(\"\\n\")}\\n`;\r\n}\r\n\r\nexport function parseSchemaSnapshot(source: string): SchemaSnapshot {\r\n const parsed = JSON.parse(source) as SchemaSnapshot;\r\n if (parsed.version !== 1 || !Array.isArray(parsed.tables) || typeof parsed.hash !== \"string\") {\r\n throw new Error(\"Invalid schema snapshot file.\");\r\n }\r\n return parsed;\r\n}\r\n\r\nexport function renderCreateTableStatement(tableName: string): string {\r\n return `\r\nCREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\r\n _id TEXT PRIMARY KEY,\r\n _creationTime INTEGER NOT NULL,\r\n _json TEXT NOT NULL\r\n);`.trim();\r\n}\r\n\r\nexport function renderCreateIndexStatement(\r\n tableName: string,\r\n indexName: string,\r\n fields: string[]\r\n): string {\r\n const expressions = fields\r\n .map((field) => `json_extract(_json, '$.${field}')`)\r\n .join(\", \");\r\n return `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(\r\n `idx_${tableName}_${indexName}`\r\n )} ON ${quoteIdentifier(tableName)} (${expressions});`;\r\n}\r\n\r\nexport function renderCreateSearchIndexStatement(\r\n tableName: string,\r\n searchIndex: SearchIndexDefinition | TableSnapshot[\"searchIndexes\"][number]\r\n): string {\r\n return `CREATE VIRTUAL TABLE IF NOT EXISTS ${quoteIdentifier(\r\n searchIndexTableName(tableName, searchIndex.name)\r\n )} USING fts5(_id UNINDEXED, search_value);`;\r\n}\r\n\r\nexport function searchIndexTableName(tableName: string, indexName: string): string {\r\n return `fts_${tableName}_${indexName}`;\r\n}\r\n\r\nfunction createSchemaHash(value: Omit<SchemaSnapshot, \"hash\">): string {\r\n return stableStringify(value);\r\n}\r\n\r\nfunction quoteIdentifier(identifier: string): string {\r\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\r\n}\r\n\r\nfunction stableStringify(value: unknown): string {\r\n return JSON.stringify(sortValue(value));\r\n}\r\n\r\nfunction sortValue(value: unknown): unknown {\r\n if (Array.isArray(value)) {\r\n return value.map(sortValue);\r\n }\r\n if (value && typeof value === \"object\") {\r\n return Object.fromEntries(\r\n Object.entries(value as Record<string, unknown>)\r\n .sort(([left], [right]) => left.localeCompare(right))\r\n .map(([key, nested]) => [key, sortValue(nested)])\r\n );\r\n }\r\n return value;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"planner.js","names":[],"sources":["../src/planner.ts"],"sourcesContent":["import {\n type SearchIndexDefinition,\n type SyncoreSchemaDefinition,\n type SyncoreSchema\n} from \"./definition.js\";\nimport {\n describeValidator,\n type ValidatorDescription\n} from \"./validators.js\";\n\nexport interface TableSnapshot {\n name: string;\n validator: ValidatorDescription;\n indexes: Array<{\n name: string;\n fields: string[];\n }>;\n searchIndexes: Array<{\n name: string;\n searchField: string;\n filterFields: string[];\n }>;\n}\n\nexport interface SchemaSnapshot {\n version: 1;\n tables: TableSnapshot[];\n hash: string;\n}\n\nexport interface SchemaMigrationPlan {\n previousHash: string | null;\n nextHash: string;\n statements: string[];\n warnings: string[];\n destructiveChanges: string[];\n}\n\nexport function createSchemaSnapshot<TTables extends SyncoreSchemaDefinition>(\n schema: SyncoreSchema<TTables>\n): SchemaSnapshot {\n const tables = schema\n .tableNames()\n .sort((left, right) => left.localeCompare(right))\n .map((tableName) => {\n const table = schema.getTable(tableName);\n return {\n name: tableName,\n validator: describeValidator(table.validator),\n indexes: table.indexes\n .map((index) => ({\n name: index.name,\n fields: [...index.fields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name)),\n searchIndexes: table.searchIndexes\n .map((index) => ({\n name: index.name,\n searchField: index.searchField,\n filterFields: [...index.filterFields]\n }))\n .sort((left, right) => left.name.localeCompare(right.name))\n };\n });\n\n const base = {\n version: 1 as const,\n tables\n };\n\n return {\n ...base,\n hash: createSchemaHash(base)\n };\n}\n\nexport function diffSchemaSnapshots(\n previousSnapshot: SchemaSnapshot | null | undefined,\n nextSnapshot: SchemaSnapshot\n): SchemaMigrationPlan {\n const statements: string[] = [];\n const warnings: string[] = [];\n const destructiveChanges: string[] = [];\n\n const previousTables = new Map(\n (previousSnapshot?.tables ?? []).map((table) => [table.name, table])\n );\n const nextTables = new Map(nextSnapshot.tables.map((table) => [table.name, table]));\n\n for (const table of nextSnapshot.tables) {\n const previousTable = previousTables.get(table.name);\n if (!previousTable) {\n statements.push(renderCreateTableStatement(table.name));\n for (const index of table.indexes) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n }\n for (const searchIndex of table.searchIndexes) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n }\n continue;\n }\n\n if (stableStringify(previousTable.validator) !== stableStringify(table.validator)) {\n warnings.push(\n `Validator changed for table \"${table.name}\". Existing rows are not rewritten automatically.`\n );\n }\n\n const previousIndexes = new Map(\n previousTable.indexes.map((index) => [index.name, index])\n );\n const nextIndexes = new Map(table.indexes.map((index) => [index.name, index]));\n\n for (const index of table.indexes) {\n const previousIndex = previousIndexes.get(index.name);\n if (!previousIndex) {\n statements.push(renderCreateIndexStatement(table.name, index.name, index.fields));\n continue;\n }\n if (stableStringify(previousIndex.fields) !== stableStringify(index.fields)) {\n destructiveChanges.push(\n `Index \"${table.name}.${index.name}\" changed fields and requires a manual migration.`\n );\n }\n }\n\n for (const previousIndex of previousTable.indexes) {\n if (!nextIndexes.has(previousIndex.name)) {\n destructiveChanges.push(\n `Index \"${table.name}.${previousIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n const previousSearchIndexes = new Map(\n previousTable.searchIndexes.map((index) => [index.name, index])\n );\n const nextSearchIndexes = new Map(\n table.searchIndexes.map((index) => [index.name, index])\n );\n\n for (const searchIndex of table.searchIndexes) {\n const previousSearchIndex = previousSearchIndexes.get(searchIndex.name);\n if (!previousSearchIndex) {\n statements.push(renderCreateSearchIndexStatement(table.name, searchIndex));\n continue;\n }\n if (stableStringify(previousSearchIndex) !== stableStringify(searchIndex)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${searchIndex.name}\" changed and requires a manual migration.`\n );\n }\n }\n\n for (const previousSearchIndex of previousTable.searchIndexes) {\n if (!nextSearchIndexes.has(previousSearchIndex.name)) {\n destructiveChanges.push(\n `Search index \"${table.name}.${previousSearchIndex.name}\" was removed and requires a manual migration.`\n );\n }\n }\n }\n\n for (const previousTable of previousSnapshot?.tables ?? []) {\n if (!nextTables.has(previousTable.name)) {\n destructiveChanges.push(\n `Table \"${previousTable.name}\" was removed and requires a manual migration.`\n );\n }\n }\n\n return {\n previousHash: previousSnapshot?.hash ?? null,\n nextHash: nextSnapshot.hash,\n statements,\n warnings,\n destructiveChanges\n };\n}\n\nexport function renderMigrationSql(\n plan: SchemaMigrationPlan,\n options?: { title?: string }\n): string {\n const lines: string[] = [];\n\n lines.push(`-- ${options?.title ?? \"Syncore migration\"}`);\n lines.push(`-- previous: ${plan.previousHash ?? \"none\"}`);\n lines.push(`-- next: ${plan.nextHash}`);\n\n for (const warning of plan.warnings) {\n lines.push(`-- warning: ${warning}`);\n }\n\n if (plan.destructiveChanges.length > 0) {\n for (const destructiveChange of plan.destructiveChanges) {\n lines.push(`-- destructive: ${destructiveChange}`);\n }\n }\n\n if (plan.statements.length > 0) {\n lines.push(\"\");\n for (const statement of plan.statements) {\n lines.push(statement);\n }\n } else {\n lines.push(\"\");\n lines.push(\"-- no-op\");\n }\n\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function parseSchemaSnapshot(source: string): SchemaSnapshot {\n const parsed = JSON.parse(source) as SchemaSnapshot;\n if (parsed.version !== 1 || !Array.isArray(parsed.tables) || typeof parsed.hash !== \"string\") {\n throw new Error(\"Invalid schema snapshot file.\");\n }\n return parsed;\n}\n\nexport function renderCreateTableStatement(tableName: string): string {\n return `\nCREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\n _id TEXT PRIMARY KEY,\n _creationTime INTEGER NOT NULL,\n _json TEXT NOT NULL\n);`.trim();\n}\n\nexport function renderCreateIndexStatement(\n tableName: string,\n indexName: string,\n fields: string[]\n): string {\n const expressions = fields\n .map((field) => `json_extract(_json, '$.${field}')`)\n .join(\", \");\n return `CREATE INDEX IF NOT EXISTS ${quoteIdentifier(\n `idx_${tableName}_${indexName}`\n )} ON ${quoteIdentifier(tableName)} (${expressions});`;\n}\n\nexport function renderCreateSearchIndexStatement(\n tableName: string,\n searchIndex: SearchIndexDefinition | TableSnapshot[\"searchIndexes\"][number]\n): string {\n return `CREATE VIRTUAL TABLE IF NOT EXISTS ${quoteIdentifier(\n searchIndexTableName(tableName, searchIndex.name)\n )} USING fts5(_id UNINDEXED, search_value);`;\n}\n\nexport function searchIndexTableName(tableName: string, indexName: string): string {\n return `fts_${tableName}_${indexName}`;\n}\n\nfunction createSchemaHash(value: Omit<SchemaSnapshot, \"hash\">): string {\n return stableStringify(value);\n}\n\nfunction quoteIdentifier(identifier: string): string {\n return `\"${identifier.replaceAll('\"', '\"\"')}\"`;\n}\n\nfunction stableStringify(value: unknown): string {\n return JSON.stringify(sortValue(value));\n}\n\nfunction sortValue(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map(sortValue);\n }\n if (value && typeof value === \"object\") {\n return Object.fromEntries(\n Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, sortValue(nested)])\n );\n }\n return value;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"validators.js","names":[],"sources":["../src/validators.ts"],"sourcesContent":["export type ValidatorKind =\r\n | \"string\"\r\n | \"number\"\r\n | \"boolean\"\r\n | \"literal\"\r\n | \"array\"\r\n | \"object\"\r\n | \"id\"\r\n | \"optional\"\r\n | \"any\"\r\n | \"null\";\r\n\r\n/**\r\n * Validates unknown input at runtime and carries its parsed TypeScript type.\r\n *\r\n * Syncore uses validators for function arguments, return values, and table\r\n * definitions. Most apps create validators through {@link v} instead of\r\n * instantiating validator classes directly.\r\n */\r\nexport interface Validator<TValue> {\r\n readonly kind: ValidatorKind;\r\n\r\n /**\r\n * Parse and validate an unknown value.\r\n *\r\n * @param value - The value to validate.\r\n * @param path - A human-readable path used in validation errors.\r\n * @returns The parsed value when validation succeeds.\r\n */\r\n parse(value: unknown, path?: string): TValue;\r\n}\r\n\r\nexport type ValidatorDescription =\r\n | { kind: \"string\" }\r\n | { kind: \"number\" }\r\n | { kind: \"boolean\" }\r\n | { kind: \"null\" }\r\n | { kind: \"any\" }\r\n | { kind: \"literal\"; value: string | number | boolean | null }\r\n | { kind: \"array\"; item: ValidatorDescription }\r\n | { kind: \"object\"; shape: Record<string, ValidatorDescription> }\r\n | { kind: \"id\"; tableName: string }\r\n | { kind: \"optional\"; inner: ValidatorDescription };\r\n\r\nexport interface ObjectValidatorShape {\r\n [key: string]: Validator<unknown>;\r\n}\r\n\r\nexport class StringValidator implements Validator<string> {\r\n readonly kind = \"string\" as const;\r\n\r\n parse(value: unknown, path = \"value\"): string {\r\n if (typeof value !== \"string\") {\r\n throw new Error(`${path} must be a string.`);\r\n }\r\n return value;\r\n }\r\n}\r\n\r\nexport class NumberValidator implements Validator<number> {\r\n readonly kind = \"number\" as const;\r\n\r\n parse(value: unknown, path = \"value\"): number {\r\n if (typeof value !== \"number\" || Number.isNaN(value)) {\r\n throw new Error(`${path} must be a number.`);\r\n }\r\n return value;\r\n }\r\n}\r\n\r\nexport class BooleanValidator implements Validator<boolean> {\r\n readonly kind = \"boolean\" as const;\r\n\r\n parse(value: unknown, path = \"value\"): boolean {\r\n if (typeof value !== \"boolean\") {\r\n throw new Error(`${path} must be a boolean.`);\r\n }\r\n return value;\r\n }\r\n}\r\n\r\nexport class NullValidator implements Validator<null> {\r\n readonly kind = \"null\" as const;\r\n\r\n parse(value: unknown, path = \"value\"): null {\r\n if (value !== null) {\r\n throw new Error(`${path} must be null.`);\r\n }\r\n return null;\r\n }\r\n}\r\n\r\nexport class AnyValidator implements Validator<unknown> {\r\n readonly kind = \"any\" as const;\r\n\r\n parse(value: unknown): unknown {\r\n return value;\r\n }\r\n}\r\n\r\nexport class LiteralValidator<\r\n TValue extends string | number | boolean | null\r\n> implements Validator<TValue> {\r\n readonly kind = \"literal\" as const;\r\n\r\n constructor(public readonly literalValue: TValue) {}\r\n\r\n parse(value: unknown, path = \"value\"): TValue {\r\n if (value !== this.literalValue) {\r\n throw new Error(`${path} must equal ${String(this.literalValue)}.`);\r\n }\r\n return this.literalValue;\r\n }\r\n}\r\n\r\nexport class ArrayValidator<TItem> implements Validator<TItem[]> {\r\n readonly kind = \"array\" as const;\r\n\r\n constructor(public readonly itemValidator: Validator<TItem>) {}\r\n\r\n parse(value: unknown, path = \"value\"): TItem[] {\r\n if (!Array.isArray(value)) {\r\n throw new Error(`${path} must be an array.`);\r\n }\r\n return value.map((item, index) =>\r\n this.itemValidator.parse(item, `${path}[${index}]`)\r\n );\r\n }\r\n}\r\n\r\nexport class ObjectValidator<\r\n TShape extends ObjectValidatorShape\r\n> implements Validator<{ [TKey in keyof TShape]: Infer<TShape[TKey]> }> {\r\n readonly kind = \"object\" as const;\r\n\r\n constructor(public readonly shape: TShape) {}\r\n\r\n parse(\r\n value: unknown,\r\n path = \"value\"\r\n ): { [TKey in keyof TShape]: Infer<TShape[TKey]> } {\r\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\r\n throw new Error(`${path} must be an object.`);\r\n }\r\n\r\n const source = value as Record<string, unknown>;\r\n const parsed: Record<string, unknown> = {};\r\n\r\n for (const [key, validator] of Object.entries(this.shape)) {\r\n const optional =\r\n validator.kind === \"optional\" && source[key] === undefined;\r\n if (optional) {\r\n continue;\r\n }\r\n parsed[key] = validator.parse(source[key], `${path}.${key}`);\r\n }\r\n\r\n return parsed as { [TKey in keyof TShape]: Infer<TShape[TKey]> };\r\n }\r\n}\r\n\r\nexport class IdValidator<\r\n TTableName extends string\r\n> implements Validator<string> {\r\n readonly kind = \"id\" as const;\r\n\r\n constructor(public readonly tableName: TTableName) {}\r\n\r\n parse(value: unknown, path = \"value\"): string {\r\n if (typeof value !== \"string\" || value.length === 0) {\r\n throw new Error(`${path} must be a non-empty id string.`);\r\n }\r\n return value;\r\n }\r\n}\r\n\r\nexport class OptionalValidator<TValue> implements Validator<\r\n TValue | undefined\r\n> {\r\n readonly kind = \"optional\" as const;\r\n\r\n constructor(public readonly inner: Validator<TValue>) {}\r\n\r\n parse(value: unknown, path = \"value\"): TValue | undefined {\r\n if (value === undefined) {\r\n return undefined;\r\n }\r\n return this.inner.parse(value, path);\r\n }\r\n}\r\n\r\nexport type Infer<TValidator> =\r\n TValidator extends Validator<infer TValue> ? TValue : never;\r\n\r\nexport type ValidatorMap = Record<string, Validator<unknown>>;\r\n\r\n/**\r\n * The public validator builder API.\r\n *\r\n * Hover each property in your editor to see what it validates and how to use it.\r\n */\r\nexport interface ValidatorBuilderApi {\r\n /**\r\n * Validate a string value.\r\n *\r\n * @returns A validator that accepts JavaScript strings.\r\n */\r\n string(): StringValidator;\r\n\r\n /**\r\n * Validate a number value.\r\n *\r\n * @returns A validator that accepts finite JavaScript numbers.\r\n */\r\n number(): NumberValidator;\r\n\r\n /**\r\n * Validate a boolean value.\r\n *\r\n * @returns A validator that accepts `true` and `false`.\r\n */\r\n boolean(): BooleanValidator;\r\n\r\n /**\r\n * Validate the literal value `null`.\r\n *\r\n * @returns A validator that only accepts `null`.\r\n */\r\n null(): NullValidator;\r\n\r\n /**\r\n * Accept any value without validation.\r\n *\r\n * Use this sparingly for escape hatches when you do not want Syncore to\r\n * enforce a more specific runtime shape.\r\n */\r\n any(): AnyValidator;\r\n\r\n /**\r\n * Validate a single literal value.\r\n *\r\n * @param literalValue - The exact value that must be provided.\r\n * @returns A validator that only accepts that one value.\r\n */\r\n literal<TValue extends string | number | boolean | null>(\r\n literalValue: TValue\r\n ): LiteralValidator<TValue>;\r\n\r\n /**\r\n * Validate an array whose items all use the same validator.\r\n *\r\n * @param itemValidator - The validator for each item in the array.\r\n * @returns A validator for arrays of the provided item type.\r\n */\r\n array<TItem>(itemValidator: Validator<TItem>): ArrayValidator<TItem>;\r\n\r\n /**\r\n * Validate an object with a fixed property shape.\r\n *\r\n * @param shape - The validators for each property on the object.\r\n * @returns A validator for objects matching that shape.\r\n */\r\n object<TShape extends ObjectValidatorShape>(\r\n shape: TShape\r\n ): ObjectValidator<TShape>;\r\n\r\n /**\r\n * Validate an identifier string that points at a table.\r\n *\r\n * Use this for document ids that come from Syncore tables.\r\n *\r\n * @param tableName - The name of the referenced table.\r\n * @returns A validator for ids belonging to that table.\r\n */\r\n id<TTableName extends string>(tableName: TTableName): IdValidator<TTableName>;\r\n\r\n /**\r\n * Make another validator optional.\r\n *\r\n * @param inner - The validator for the defined case.\r\n * @returns A validator that accepts `undefined` or the inner value.\r\n */\r\n optional<TValue>(inner: Validator<TValue>): OptionalValidator<TValue>;\r\n}\r\n\r\nfunction isValidator(\r\n value: Validator<unknown> | ValidatorMap\r\n): value is Validator<unknown> {\r\n return typeof (value as Validator<unknown>).parse === \"function\";\r\n}\r\n\r\nexport function ensureObjectValidator(\r\n value: Validator<unknown> | ValidatorMap\r\n): Validator<unknown> {\r\n if (isValidator(value)) {\r\n return value;\r\n }\r\n return new ObjectValidator(value);\r\n}\r\n\r\n/**\r\n * Build runtime validators for schemas, function args, and return values.\r\n *\r\n * @example\r\n * ```ts\r\n * defineTable({\r\n * text: v.string(),\r\n * done: v.boolean(),\r\n * ownerId: v.optional(v.id(\"users\"))\r\n * });\r\n * ```\r\n */\r\nexport const v: ValidatorBuilderApi = {\r\n string: () => new StringValidator(),\r\n number: () => new NumberValidator(),\r\n boolean: () => new BooleanValidator(),\r\n null: () => new NullValidator(),\r\n any: () => new AnyValidator(),\r\n literal: <TValue extends string | number | boolean | null>(\r\n literalValue: TValue\r\n ) => new LiteralValidator(literalValue),\r\n array: <TItem>(itemValidator: Validator<TItem>) =>\r\n new ArrayValidator(itemValidator),\r\n object: <TShape extends ObjectValidatorShape>(shape: TShape) =>\r\n new ObjectValidator(shape),\r\n id: <TTableName extends string>(tableName: TTableName) =>\r\n new IdValidator(tableName),\r\n optional: <TValue>(inner: Validator<TValue>) => new OptionalValidator(inner)\r\n};\r\n\r\nexport function describeValidator(\r\n validator: Validator<unknown>\r\n): ValidatorDescription {\r\n switch (validator.kind) {\r\n case \"string\":\r\n case \"number\":\r\n case \"boolean\":\r\n case \"null\":\r\n case \"any\":\r\n return { kind: validator.kind };\r\n case \"literal\":\r\n return {\r\n kind: \"literal\",\r\n value: (validator as LiteralValidator<string | number | boolean | null>)\r\n .literalValue\r\n };\r\n case \"array\":\r\n return {\r\n kind: \"array\",\r\n item: describeValidator(\r\n (validator as ArrayValidator<unknown>).itemValidator\r\n )\r\n };\r\n case \"object\": {\r\n const objectValidator =\r\n validator as ObjectValidator<ObjectValidatorShape>;\r\n return {\r\n kind: \"object\",\r\n shape: Object.fromEntries(\r\n Object.entries(objectValidator.shape).map(([key, nested]) => [\r\n key,\r\n describeValidator(nested)\r\n ])\r\n )\r\n };\r\n }\r\n case \"id\":\r\n return {\r\n kind: \"id\",\r\n tableName: (validator as IdValidator<string>).tableName\r\n };\r\n case \"optional\":\r\n return {\r\n kind: \"optional\",\r\n inner: describeValidator(\r\n (validator as OptionalValidator<unknown>).inner\r\n )\r\n };\r\n }\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"validators.js","names":[],"sources":["../src/validators.ts"],"sourcesContent":["export type ValidatorKind =\n | \"string\"\n | \"number\"\n | \"boolean\"\n | \"literal\"\n | \"array\"\n | \"object\"\n | \"id\"\n | \"optional\"\n | \"any\"\n | \"null\";\n\n/**\n * Validates unknown input at runtime and carries its parsed TypeScript type.\n *\n * Syncore uses validators for function arguments, return values, and table\n * definitions. Most apps create validators through {@link v} instead of\n * instantiating validator classes directly.\n */\nexport interface Validator<TValue> {\n readonly kind: ValidatorKind;\n\n /**\n * Parse and validate an unknown value.\n *\n * @param value - The value to validate.\n * @param path - A human-readable path used in validation errors.\n * @returns The parsed value when validation succeeds.\n */\n parse(value: unknown, path?: string): TValue;\n}\n\nexport type ValidatorDescription =\n | { kind: \"string\" }\n | { kind: \"number\" }\n | { kind: \"boolean\" }\n | { kind: \"null\" }\n | { kind: \"any\" }\n | { kind: \"literal\"; value: string | number | boolean | null }\n | { kind: \"array\"; item: ValidatorDescription }\n | { kind: \"object\"; shape: Record<string, ValidatorDescription> }\n | { kind: \"id\"; tableName: string }\n | { kind: \"optional\"; inner: ValidatorDescription };\n\nexport interface ObjectValidatorShape {\n [key: string]: Validator<unknown>;\n}\n\nexport class StringValidator implements Validator<string> {\n readonly kind = \"string\" as const;\n\n parse(value: unknown, path = \"value\"): string {\n if (typeof value !== \"string\") {\n throw new Error(`${path} must be a string.`);\n }\n return value;\n }\n}\n\nexport class NumberValidator implements Validator<number> {\n readonly kind = \"number\" as const;\n\n parse(value: unknown, path = \"value\"): number {\n if (typeof value !== \"number\" || Number.isNaN(value)) {\n throw new Error(`${path} must be a number.`);\n }\n return value;\n }\n}\n\nexport class BooleanValidator implements Validator<boolean> {\n readonly kind = \"boolean\" as const;\n\n parse(value: unknown, path = \"value\"): boolean {\n if (typeof value !== \"boolean\") {\n throw new Error(`${path} must be a boolean.`);\n }\n return value;\n }\n}\n\nexport class NullValidator implements Validator<null> {\n readonly kind = \"null\" as const;\n\n parse(value: unknown, path = \"value\"): null {\n if (value !== null) {\n throw new Error(`${path} must be null.`);\n }\n return null;\n }\n}\n\nexport class AnyValidator implements Validator<unknown> {\n readonly kind = \"any\" as const;\n\n parse(value: unknown): unknown {\n return value;\n }\n}\n\nexport class LiteralValidator<\n TValue extends string | number | boolean | null\n> implements Validator<TValue> {\n readonly kind = \"literal\" as const;\n\n constructor(public readonly literalValue: TValue) {}\n\n parse(value: unknown, path = \"value\"): TValue {\n if (value !== this.literalValue) {\n throw new Error(`${path} must equal ${String(this.literalValue)}.`);\n }\n return this.literalValue;\n }\n}\n\nexport class ArrayValidator<TItem> implements Validator<TItem[]> {\n readonly kind = \"array\" as const;\n\n constructor(public readonly itemValidator: Validator<TItem>) {}\n\n parse(value: unknown, path = \"value\"): TItem[] {\n if (!Array.isArray(value)) {\n throw new Error(`${path} must be an array.`);\n }\n return value.map((item, index) =>\n this.itemValidator.parse(item, `${path}[${index}]`)\n );\n }\n}\n\nexport class ObjectValidator<\n TShape extends ObjectValidatorShape\n> implements Validator<{ [TKey in keyof TShape]: Infer<TShape[TKey]> }> {\n readonly kind = \"object\" as const;\n\n constructor(public readonly shape: TShape) {}\n\n parse(\n value: unknown,\n path = \"value\"\n ): { [TKey in keyof TShape]: Infer<TShape[TKey]> } {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new Error(`${path} must be an object.`);\n }\n\n const source = value as Record<string, unknown>;\n const parsed: Record<string, unknown> = {};\n\n for (const [key, validator] of Object.entries(this.shape)) {\n const optional =\n validator.kind === \"optional\" && source[key] === undefined;\n if (optional) {\n continue;\n }\n parsed[key] = validator.parse(source[key], `${path}.${key}`);\n }\n\n return parsed as { [TKey in keyof TShape]: Infer<TShape[TKey]> };\n }\n}\n\nexport class IdValidator<\n TTableName extends string\n> implements Validator<string> {\n readonly kind = \"id\" as const;\n\n constructor(public readonly tableName: TTableName) {}\n\n parse(value: unknown, path = \"value\"): string {\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(`${path} must be a non-empty id string.`);\n }\n return value;\n }\n}\n\nexport class OptionalValidator<TValue> implements Validator<\n TValue | undefined\n> {\n readonly kind = \"optional\" as const;\n\n constructor(public readonly inner: Validator<TValue>) {}\n\n parse(value: unknown, path = \"value\"): TValue | undefined {\n if (value === undefined) {\n return undefined;\n }\n return this.inner.parse(value, path);\n }\n}\n\nexport type Infer<TValidator> =\n TValidator extends Validator<infer TValue> ? TValue : never;\n\nexport type ValidatorMap = Record<string, Validator<unknown>>;\n\n/**\n * The public validator builder API.\n *\n * Hover each property in your editor to see what it validates and how to use it.\n */\nexport interface ValidatorBuilderApi {\n /**\n * Validate a string value.\n *\n * @returns A validator that accepts JavaScript strings.\n */\n string(): StringValidator;\n\n /**\n * Validate a number value.\n *\n * @returns A validator that accepts finite JavaScript numbers.\n */\n number(): NumberValidator;\n\n /**\n * Validate a boolean value.\n *\n * @returns A validator that accepts `true` and `false`.\n */\n boolean(): BooleanValidator;\n\n /**\n * Validate the literal value `null`.\n *\n * @returns A validator that only accepts `null`.\n */\n null(): NullValidator;\n\n /**\n * Accept any value without validation.\n *\n * Use this sparingly for escape hatches when you do not want Syncore to\n * enforce a more specific runtime shape.\n */\n any(): AnyValidator;\n\n /**\n * Validate a single literal value.\n *\n * @param literalValue - The exact value that must be provided.\n * @returns A validator that only accepts that one value.\n */\n literal<TValue extends string | number | boolean | null>(\n literalValue: TValue\n ): LiteralValidator<TValue>;\n\n /**\n * Validate an array whose items all use the same validator.\n *\n * @param itemValidator - The validator for each item in the array.\n * @returns A validator for arrays of the provided item type.\n */\n array<TItem>(itemValidator: Validator<TItem>): ArrayValidator<TItem>;\n\n /**\n * Validate an object with a fixed property shape.\n *\n * @param shape - The validators for each property on the object.\n * @returns A validator for objects matching that shape.\n */\n object<TShape extends ObjectValidatorShape>(\n shape: TShape\n ): ObjectValidator<TShape>;\n\n /**\n * Validate an identifier string that points at a table.\n *\n * Use this for document ids that come from Syncore tables.\n *\n * @param tableName - The name of the referenced table.\n * @returns A validator for ids belonging to that table.\n */\n id<TTableName extends string>(tableName: TTableName): IdValidator<TTableName>;\n\n /**\n * Make another validator optional.\n *\n * @param inner - The validator for the defined case.\n * @returns A validator that accepts `undefined` or the inner value.\n */\n optional<TValue>(inner: Validator<TValue>): OptionalValidator<TValue>;\n}\n\nfunction isValidator(\n value: Validator<unknown> | ValidatorMap\n): value is Validator<unknown> {\n return typeof (value as Validator<unknown>).parse === \"function\";\n}\n\nexport function ensureObjectValidator(\n value: Validator<unknown> | ValidatorMap\n): Validator<unknown> {\n if (isValidator(value)) {\n return value;\n }\n return new ObjectValidator(value);\n}\n\n/**\n * Build runtime validators for schemas, function args, and return values.\n *\n * @example\n * ```ts\n * defineTable({\n * text: v.string(),\n * done: v.boolean(),\n * ownerId: v.optional(v.id(\"users\"))\n * });\n * ```\n */\nexport const v: ValidatorBuilderApi = {\n string: () => new StringValidator(),\n number: () => new NumberValidator(),\n boolean: () => new BooleanValidator(),\n null: () => new NullValidator(),\n any: () => new AnyValidator(),\n literal: <TValue extends string | number | boolean | null>(\n literalValue: TValue\n ) => new LiteralValidator(literalValue),\n array: <TItem>(itemValidator: Validator<TItem>) =>\n new ArrayValidator(itemValidator),\n object: <TShape extends ObjectValidatorShape>(shape: TShape) =>\n new ObjectValidator(shape),\n id: <TTableName extends string>(tableName: TTableName) =>\n new IdValidator(tableName),\n optional: <TValue>(inner: Validator<TValue>) => new OptionalValidator(inner)\n};\n\nexport function describeValidator(\n validator: Validator<unknown>\n): ValidatorDescription {\n switch (validator.kind) {\n case \"string\":\n case \"number\":\n case \"boolean\":\n case \"null\":\n case \"any\":\n return { kind: validator.kind };\n case \"literal\":\n return {\n kind: \"literal\",\n value: (validator as LiteralValidator<string | number | boolean | null>)\n .literalValue\n };\n case \"array\":\n return {\n kind: \"array\",\n item: describeValidator(\n (validator as ArrayValidator<unknown>).itemValidator\n )\n };\n case \"object\": {\n const objectValidator =\n validator as ObjectValidator<ObjectValidatorShape>;\n return {\n kind: \"object\",\n shape: Object.fromEntries(\n Object.entries(objectValidator.shape).map(([key, nested]) => [\n key,\n describeValidator(nested)\n ])\n )\n };\n }\n case \"id\":\n return {\n kind: \"id\",\n tableName: (validator as IdValidator<string>).tableName\n };\n case \"optional\":\n return {\n kind: \"optional\",\n inner: describeValidator(\n (validator as OptionalValidator<unknown>).inner\n )\n };\n }\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { SyncoreBrowserProvider, SyncoreBrowserProviderProps, SyncoreWebProvider, SyncoreWebProviderProps } from "./platform-web/src/react.js";
1
+ import { SyncoreBrowserProvider, SyncoreBrowserProviderProps, SyncoreWebProvider, SyncoreWebProviderProps } from "./_vendor/platform-web/react.d.ts";
2
2
  export { SyncoreBrowserProvider, type SyncoreBrowserProviderProps, SyncoreWebProvider, type SyncoreWebProviderProps };
@@ -1,2 +1,2 @@
1
- import { SyncoreBrowserProvider, SyncoreWebProvider } from "./platform-web/src/react.js";
1
+ import { SyncoreBrowserProvider, SyncoreWebProvider } from "./_vendor/platform-web/react.js";
2
2
  export { SyncoreBrowserProvider, SyncoreWebProvider };
package/dist/browser.d.ts CHANGED
@@ -1,12 +1,11 @@
1
- import { AttachWebWorkerRuntimeOptions, AttachedWebWorkerRuntime, CreateWebWorkerClientProviderOptions, ManagedWebWorkerClient, SyncoreWebWorkerClient, SyncoreWorkerMessageEndpoint, WorkerQueryWatch, attachWebWorkerRuntime, createManagedWebWorkerClient, createSyncoreWebWorkerClient, createWebWorkerClient } from "./platform-web/src/worker.js";
2
- import { BrowserFileStorageAdapter, BrowserSyncoreSchema, CreateBrowserRuntimeOptions, CreateBrowserWorkerRuntimeOptions, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerRuntime, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerRuntime } from "./platform-web/src/index.js";
1
+ import { AttachWebWorkerRuntimeOptions, AttachedWebWorkerRuntime, BrowserFileStorageAdapter, BrowserSyncoreSchema, CreateBrowserRuntimeOptions, CreateBrowserWorkerRuntimeOptions, CreateWebWorkerClientProviderOptions, CreateWebWorkerClientProviderOptions as CreateWebWorkerClientProviderOptions$1, ManagedWebWorkerClient, ManagedWebWorkerClient as ManagedWebWorkerClient$1, SyncoreWebWorkerClient, SyncoreWorkerMessageEndpoint, WorkerQueryWatch, attachWebWorkerRuntime, attachWebWorkerRuntime as attachWebWorkerRuntime$1, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerRuntime, createManagedWebWorkerClient, createManagedWebWorkerClient as createManagedWebWorkerClient$1, createSyncoreWebWorkerClient, createSyncoreWebWorkerClient as createSyncoreWebWorkerClient$1, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerClient, createWebWorkerRuntime } from "./_vendor/platform-web/index.d.ts";
3
2
 
4
3
  //#region src/browser.d.ts
5
- type CreateBrowserWorkerClientOptions = CreateWebWorkerClientProviderOptions;
6
- type ManagedBrowserWorkerClient = ManagedWebWorkerClient;
7
- declare const attachBrowserWorkerRuntime: typeof attachWebWorkerRuntime;
8
- declare const createBrowserWorkerClient: typeof createSyncoreWebWorkerClient;
9
- declare const createManagedBrowserWorkerClient: typeof createManagedWebWorkerClient;
4
+ type CreateBrowserWorkerClientOptions = CreateWebWorkerClientProviderOptions$1;
5
+ type ManagedBrowserWorkerClient = ManagedWebWorkerClient$1;
6
+ declare const attachBrowserWorkerRuntime: typeof attachWebWorkerRuntime$1;
7
+ declare const createBrowserWorkerClient: typeof createSyncoreWebWorkerClient$1;
8
+ declare const createManagedBrowserWorkerClient: typeof createManagedWebWorkerClient$1;
10
9
  //#endregion
11
10
  export { type AttachWebWorkerRuntimeOptions, type AttachedWebWorkerRuntime, BrowserFileStorageAdapter, type BrowserSyncoreSchema, type CreateBrowserRuntimeOptions, CreateBrowserWorkerClientOptions, type CreateBrowserWorkerRuntimeOptions, type CreateWebWorkerClientProviderOptions, ManagedBrowserWorkerClient, type ManagedWebWorkerClient, type SyncoreWebWorkerClient, type SyncoreWorkerMessageEndpoint, type WorkerQueryWatch, attachBrowserWorkerRuntime, attachWebWorkerRuntime, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerClient, createBrowserWorkerRuntime, createManagedBrowserWorkerClient, createManagedWebWorkerClient, createSyncoreWebWorkerClient, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerClient, createWebWorkerRuntime };
12
11
  //# sourceMappingURL=browser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","names":[],"sources":["../src/browser.ts"],"mappings":";;;;KAsCY,gCAAA,GACV,oCAAA;AAAA,KACU,0BAAA,GAA6B,sBAAA;AAAA,cAE5B,0BAAA,SAA0B,sBAAA;AAAA,cAC1B,yBAAA,SAAyB,4BAAA;AAAA,cACzB,gCAAA,SAAgC,4BAAA"}
1
+ {"version":3,"file":"browser.d.ts","names":[],"sources":["../src/browser.ts"],"mappings":";;;KAsCY,gCAAA,GACV,sCAAA;AAAA,KACU,0BAAA,GAA6B,wBAAA;AAAA,cAE5B,0BAAA,SAA0B,wBAAA;AAAA,cAC1B,yBAAA,SAAyB,8BAAA;AAAA,cACzB,gCAAA,SAAgC,8BAAA"}
package/dist/browser.js CHANGED
@@ -1,9 +1,8 @@
1
- import { attachWebWorkerRuntime, createManagedWebWorkerClient, createSyncoreWebWorkerClient, createWebWorkerClient } from "./platform-web/src/worker.js";
2
- import { BrowserFileStorageAdapter, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerRuntime, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerRuntime } from "./platform-web/src/index.js";
1
+ import { BrowserFileStorageAdapter, attachWebWorkerRuntime, attachWebWorkerRuntime as attachWebWorkerRuntime$1, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerRuntime, createManagedWebWorkerClient, createManagedWebWorkerClient as createManagedWebWorkerClient$1, createSyncoreWebWorkerClient, createSyncoreWebWorkerClient as createSyncoreWebWorkerClient$1, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerClient, createWebWorkerRuntime } from "./_vendor/platform-web/index.js";
3
2
  //#region src/browser.ts
4
- const attachBrowserWorkerRuntime = attachWebWorkerRuntime;
5
- const createBrowserWorkerClient = createSyncoreWebWorkerClient;
6
- const createManagedBrowserWorkerClient = createManagedWebWorkerClient;
3
+ const attachBrowserWorkerRuntime = attachWebWorkerRuntime$1;
4
+ const createBrowserWorkerClient = createSyncoreWebWorkerClient$1;
5
+ const createManagedBrowserWorkerClient = createManagedWebWorkerClient$1;
7
6
  //#endregion
8
7
  export { BrowserFileStorageAdapter, attachBrowserWorkerRuntime, attachWebWorkerRuntime, createBrowserSyncoreClient, createBrowserSyncoreRuntime, createBrowserWorkerClient, createBrowserWorkerRuntime, createManagedBrowserWorkerClient, createManagedWebWorkerClient, createSyncoreWebWorkerClient, createWebSyncoreClient, createWebSyncoreRuntime, createWebWorkerClient, createWebWorkerRuntime };
9
8
 
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","names":[],"sources":["../src/browser.ts"],"sourcesContent":["export {\r\n BrowserFileStorageAdapter,\r\n createBrowserSyncoreClient,\r\n createBrowserSyncoreRuntime,\r\n createBrowserWorkerRuntime,\r\n type BrowserSyncoreSchema,\r\n type CreateBrowserRuntimeOptions,\r\n type CreateBrowserWorkerRuntimeOptions,\r\n createWebSyncoreClient,\r\n createWebSyncoreRuntime,\r\n createWebWorkerRuntime\r\n} from \"@syncore/platform-web\";\r\nexport type {\r\n AttachWebWorkerRuntimeOptions,\r\n AttachedWebWorkerRuntime,\r\n CreateWebWorkerClientProviderOptions,\r\n ManagedWebWorkerClient,\r\n SyncoreWorkerMessageEndpoint,\r\n SyncoreWebWorkerClient,\r\n WorkerQueryWatch\r\n} from \"@syncore/platform-web\";\r\nexport {\r\n attachWebWorkerRuntime,\r\n createManagedWebWorkerClient,\r\n createSyncoreWebWorkerClient,\r\n createWebWorkerClient\r\n} from \"@syncore/platform-web\";\r\n\r\nimport {\r\n attachWebWorkerRuntime,\r\n createManagedWebWorkerClient,\r\n createSyncoreWebWorkerClient\r\n} from \"@syncore/platform-web\";\r\nimport type {\r\n CreateWebWorkerClientProviderOptions,\r\n ManagedWebWorkerClient\r\n} from \"@syncore/platform-web\";\r\n\r\nexport type CreateBrowserWorkerClientOptions =\r\n CreateWebWorkerClientProviderOptions;\r\nexport type ManagedBrowserWorkerClient = ManagedWebWorkerClient;\r\n\r\nexport const attachBrowserWorkerRuntime = attachWebWorkerRuntime;\r\nexport const createBrowserWorkerClient = createSyncoreWebWorkerClient;\r\nexport const createManagedBrowserWorkerClient = createManagedWebWorkerClient;\r\n"],"mappings":";;;AA0CA,MAAa,6BAA6B;AAC1C,MAAa,4BAA4B;AACzC,MAAa,mCAAmC"}
1
+ {"version":3,"file":"browser.js","names":["attachWebWorkerRuntime","createSyncoreWebWorkerClient","createManagedWebWorkerClient"],"sources":["../src/browser.ts"],"sourcesContent":["export {\n BrowserFileStorageAdapter,\n createBrowserSyncoreClient,\n createBrowserSyncoreRuntime,\n createBrowserWorkerRuntime,\n type BrowserSyncoreSchema,\n type CreateBrowserRuntimeOptions,\n type CreateBrowserWorkerRuntimeOptions,\n createWebSyncoreClient,\n createWebSyncoreRuntime,\n createWebWorkerRuntime\n} from \"@syncore/platform-web\";\nexport type {\n AttachWebWorkerRuntimeOptions,\n AttachedWebWorkerRuntime,\n CreateWebWorkerClientProviderOptions,\n ManagedWebWorkerClient,\n SyncoreWorkerMessageEndpoint,\n SyncoreWebWorkerClient,\n WorkerQueryWatch\n} from \"@syncore/platform-web\";\nexport {\n attachWebWorkerRuntime,\n createManagedWebWorkerClient,\n createSyncoreWebWorkerClient,\n createWebWorkerClient\n} from \"@syncore/platform-web\";\n\nimport {\n attachWebWorkerRuntime,\n createManagedWebWorkerClient,\n createSyncoreWebWorkerClient\n} from \"@syncore/platform-web\";\nimport type {\n CreateWebWorkerClientProviderOptions,\n ManagedWebWorkerClient\n} from \"@syncore/platform-web\";\n\nexport type CreateBrowserWorkerClientOptions =\n CreateWebWorkerClientProviderOptions;\nexport type ManagedBrowserWorkerClient = ManagedWebWorkerClient;\n\nexport const attachBrowserWorkerRuntime = attachWebWorkerRuntime;\nexport const createBrowserWorkerClient = createSyncoreWebWorkerClient;\nexport const createManagedBrowserWorkerClient = createManagedWebWorkerClient;\n"],"mappings":";;AA0CA,MAAa,6BAA6BA;AAC1C,MAAa,4BAA4BC;AACzC,MAAa,mCAAmCC"}
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { runSyncoreCli } from "./core/src/cli.js";
1
+ import { runSyncoreCli } from "./_vendor/cli/index.d.mts";
2
2
  export { runSyncoreCli };
package/dist/cli.js CHANGED
@@ -1,10 +1,19 @@
1
1
  #!/usr/bin/env node
2
- import { runSyncoreCli } from "./core/src/cli.js";
2
+ import { runSyncoreCli } from "./_vendor/cli/index.mjs";
3
+ import { realpathSync } from "node:fs";
3
4
  import path from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
- import { realpathSync } from "node:fs";
6
6
  //#region src/cli.ts
7
- if (process.argv[1] && realpathSync(path.resolve(process.argv[1])) === realpathSync(path.resolve(fileURLToPath(import.meta.url)))) await runSyncoreCli();
7
+ function isDirectInvocation(moduleUrl) {
8
+ const invokedPath = process.argv[1];
9
+ if (invokedPath === void 0) return false;
10
+ return realpathSync(path.resolve(invokedPath)) === realpathSync(path.resolve(fileURLToPath(moduleUrl)));
11
+ }
12
+ if (isDirectInvocation(import.meta.url)) runSyncoreCli().catch((error) => {
13
+ process.nextTick(() => {
14
+ throw error;
15
+ });
16
+ });
8
17
  //#endregion
9
18
  export { runSyncoreCli };
10
19
 
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { runSyncoreCli } from \"@syncore/core/cli\";\r\nimport { realpathSync } from \"node:fs\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nexport { runSyncoreCli };\r\n\r\nif (\r\n process.argv[1] &&\r\n realpathSync(path.resolve(process.argv[1])) ===\r\n realpathSync(path.resolve(fileURLToPath(import.meta.url)))\r\n) {\r\n await runSyncoreCli();\r\n}\r\n"],"mappings":";;;;;AAOA,IACE,QAAQ,KAAK,MACb,aAAa,KAAK,QAAQ,QAAQ,KAAK,GAAG,CAAC,KACzC,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC,CAAC,CAE5D,OAAM,eAAe"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { runSyncoreCli } from \"@syncore/cli\";\nimport { realpathSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport { runSyncoreCli };\n\nfunction isDirectInvocation(moduleUrl: string): boolean {\n const invokedPath = process.argv[1];\n if (invokedPath === undefined) {\n return false;\n }\n\n return (\n realpathSync(path.resolve(invokedPath)) ===\n realpathSync(path.resolve(fileURLToPath(moduleUrl)))\n );\n}\n\nif (isDirectInvocation(import.meta.url)) {\n void runSyncoreCli().catch((error) => {\n process.nextTick(() => {\n throw error;\n });\n });\n}\n"],"mappings":";;;;;AAOA,SAAS,mBAAmB,WAA4B;CACtD,MAAM,cAAc,QAAQ,KAAK;AACjC,KAAI,gBAAgB,KAAA,EAClB,QAAO;AAGT,QACE,aAAa,KAAK,QAAQ,YAAY,CAAC,KACvC,aAAa,KAAK,QAAQ,cAAc,UAAU,CAAC,CAAC;;AAIxD,IAAI,mBAAmB,OAAO,KAAK,IAAI,CAChC,gBAAe,CAAC,OAAO,UAAU;AACpC,SAAQ,eAAe;AACrB,QAAM;GACN;EACF"}
@@ -1,2 +1,2 @@
1
- import { SyncoreExpoProvider, SyncoreExpoProviderProps } from "./platform-expo/src/react.js";
1
+ import { SyncoreExpoProvider, SyncoreExpoProviderProps } from "./_vendor/platform-expo/react.d.ts";
2
2
  export { SyncoreExpoProvider, type SyncoreExpoProviderProps };
@@ -1,2 +1,2 @@
1
- import { SyncoreExpoProvider } from "./platform-expo/src/react.js";
1
+ import { SyncoreExpoProvider } from "./_vendor/platform-expo/react.js";
2
2
  export { SyncoreExpoProvider };
package/dist/expo.d.ts CHANGED
@@ -1,2 +1 @@
1
- import { CreateExpoRuntimeOptions, ExpoFileStorageAdapter, ExpoSqliteDriver, ExpoSyncoreBootstrap, ExpoSyncoreSchema, createExpoSyncoreBootstrap, createExpoSyncoreClient, createExpoSyncoreRuntime } from "./platform-expo/src/index.js";
2
- export { CreateExpoRuntimeOptions, ExpoFileStorageAdapter, ExpoSqliteDriver, ExpoSyncoreBootstrap, ExpoSyncoreSchema, createExpoSyncoreBootstrap, createExpoSyncoreClient, createExpoSyncoreRuntime };
1
+ export * from "./_vendor/platform-expo/index.d.ts";
package/dist/expo.js CHANGED
@@ -1,2 +1 @@
1
- import { ExpoFileStorageAdapter, ExpoSqliteDriver, createExpoSyncoreBootstrap, createExpoSyncoreClient, createExpoSyncoreRuntime } from "./platform-expo/src/index.js";
2
- export { ExpoFileStorageAdapter, ExpoSqliteDriver, createExpoSyncoreBootstrap, createExpoSyncoreClient, createExpoSyncoreRuntime };
1
+ export * from "./_vendor/platform-expo/index.js";
package/dist/index.d.ts CHANGED
@@ -1,7 +1,3 @@
1
- import { AnyValidator, ArrayValidator, BooleanValidator, IdValidator, Infer, LiteralValidator, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, StringValidator, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, describeValidator, ensureObjectValidator, v } from "./schema/src/validators.js";
2
- import { AnyTableDefinition, IndexDefinition, InferDocument, InferTableInput, SearchIndexDefinition, SyncoreSchema, SyncoreSchemaDefinition, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, defineSchema, defineTable } from "./schema/src/definition.js";
3
- import { SchemaMigrationPlan, SchemaSnapshot, TableSnapshot, createSchemaSnapshot, diffSchemaSnapshots, parseSchemaSnapshot, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName } from "./schema/src/planner.js";
4
- import { CronJobs, EmptyArgs, FunctionArgs, FunctionArgsFromDefinition, FunctionConfig, FunctionKindFromDefinition, FunctionReference, FunctionReferenceFor, FunctionResult, FunctionResultFromDefinition, InferArgs, MisfirePolicy, RecurringDailySchedule, RecurringIntervalSchedule, RecurringJobDefinition, RecurringSchedule, RecurringWeeklySchedule, SyncoreFunctionDefinition, SyncoreFunctionKind, action, cronJobs, mutation, query } from "./core/src/runtime/functions.js";
5
- import { ActionCtx, AnySyncoreSchema, ComparisonOperator, DevtoolsSink, DocumentForTable, FilterBuilder, IndexRangeBuilder, InsertValueForTable, JsonObject, MutationCtx, PaginationOptions, PaginationResult, QueryBuilder, QueryCondition, QueryCtx, QueryExpression, RegisteredSyncoreFunction, RegisteredSyncoreHandler, RunResult, SchedulerApi, SchedulerOptions, SearchIndexBuilder, SearchQuery, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreClient, SyncoreDatabaseReader, SyncoreDatabaseWriter, SyncoreExperimentalPlugin, SyncoreExperimentalPluginContext, SyncoreFunctionRegistry, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter, SyncoreStorageApi, SyncoreWatch, TableNames, createFunctionReference, createFunctionReferenceFor } from "./core/src/runtime/runtime.js";
6
- import { generateId } from "./core/src/runtime/id.js";
7
- export { type ActionCtx, type AnySyncoreSchema, AnyTableDefinition, AnyValidator, ArrayValidator, BooleanValidator, type ComparisonOperator, type CronJobs, type DevtoolsSink, type DocumentForTable, type EmptyArgs, type FilterBuilder, type FunctionArgs, type FunctionArgsFromDefinition, type FunctionConfig, type FunctionKindFromDefinition, type FunctionReference, type FunctionReferenceFor, type FunctionResult, type FunctionResultFromDefinition, IdValidator, IndexDefinition, type IndexRangeBuilder, Infer, type InferArgs, InferDocument, InferTableInput, type InsertValueForTable, type JsonObject, LiteralValidator, type MisfirePolicy, type MutationCtx, NullValidator, NumberValidator, ObjectValidator, ObjectValidatorShape, OptionalValidator, type PaginationOptions, type PaginationResult, type QueryBuilder, type QueryCondition, type QueryCtx, type QueryExpression, type RecurringDailySchedule, type RecurringIntervalSchedule, type RecurringJobDefinition, type RecurringSchedule, type RecurringWeeklySchedule, type RegisteredSyncoreFunction, type RegisteredSyncoreHandler, type RunResult, type SchedulerApi, type SchedulerOptions, SchemaMigrationPlan, SchemaSnapshot, type SearchIndexBuilder, SearchIndexDefinition, type SearchQuery, type StorageObject, type StorageWriteInput, StringValidator, type SyncoreCapabilities, type SyncoreClient, type SyncoreDatabaseReader, type SyncoreDatabaseWriter, type SyncoreExperimentalPlugin, type SyncoreExperimentalPluginContext, type SyncoreFunctionDefinition, type SyncoreFunctionKind, type SyncoreFunctionRegistry, SyncoreRuntime, type SyncoreRuntimeOptions, SyncoreSchema, SyncoreSchemaDefinition, type SyncoreSqlDriver, type SyncoreStorageAdapter, type SyncoreStorageApi, type SyncoreWatch, TableDefinition, TableDefinitionOptions, TableDocumentSystemFields, type TableNames, TableSnapshot, Validator, ValidatorBuilderApi, ValidatorDescription, ValidatorKind, ValidatorMap, action, createFunctionReference, createFunctionReferenceFor, createSchemaSnapshot, cronJobs, defineSchema, defineTable, describeValidator, diffSchemaSnapshots, ensureObjectValidator, generateId, mutation, parseSchemaSnapshot, query, renderCreateIndexStatement, renderCreateSearchIndexStatement, renderCreateTableStatement, renderMigrationSql, searchIndexTableName, v };
1
+ import { ActionCtx, AnySyncoreSchema, ComparisonOperator, CronJobs, DevtoolsSink, DocumentForTable, EmptyArgs, FilterBuilder, FunctionArgs, FunctionArgsFromDefinition, FunctionConfig, FunctionKindFromDefinition, FunctionReference, FunctionReferenceFor, FunctionResult, FunctionResultFromDefinition, IndexRangeBuilder, InferArgs, InsertValueForTable, JsonObject, MisfirePolicy, MutationCtx, PaginationOptions, PaginationResult, QueryBuilder, QueryCondition, QueryCtx, QueryExpression, RecurringDailySchedule, RecurringIntervalSchedule, RecurringJobDefinition, RecurringSchedule, RecurringWeeklySchedule, RegisteredSyncoreFunction, RegisteredSyncoreHandler, RunResult, SchedulerApi, SchedulerOptions, SearchIndexBuilder, SearchQuery, StorageObject, StorageWriteInput, SyncoreCapabilities, SyncoreClient, SyncoreDatabaseReader, SyncoreDatabaseWriter, SyncoreExperimentalPlugin, SyncoreExperimentalPluginContext, SyncoreFunctionDefinition, SyncoreFunctionKind, SyncoreFunctionRegistry, SyncoreRuntime, SyncoreRuntimeOptions, SyncoreSqlDriver, SyncoreStorageAdapter, SyncoreStorageApi, SyncoreWatch, TableNames, action, createFunctionReference, createFunctionReferenceFor, cronJobs, generateId, mutation, query } from "./_vendor/core/index.d.mts";
2
+ export * from "./_vendor/schema/index.d.ts";
3
+ export { type ActionCtx, type AnySyncoreSchema, type ComparisonOperator, type CronJobs, type DevtoolsSink, type DocumentForTable, type EmptyArgs, type FilterBuilder, type FunctionArgs, type FunctionArgsFromDefinition, type FunctionConfig, type FunctionKindFromDefinition, type FunctionReference, type FunctionReferenceFor, type FunctionResult, type FunctionResultFromDefinition, type IndexRangeBuilder, type InferArgs, type InsertValueForTable, type JsonObject, type MisfirePolicy, type MutationCtx, type PaginationOptions, type PaginationResult, type QueryBuilder, type QueryCondition, type QueryCtx, type QueryExpression, type RecurringDailySchedule, type RecurringIntervalSchedule, type RecurringJobDefinition, type RecurringSchedule, type RecurringWeeklySchedule, type RegisteredSyncoreFunction, type RegisteredSyncoreHandler, type RunResult, type SchedulerApi, type SchedulerOptions, type SearchIndexBuilder, type SearchQuery, type StorageObject, type StorageWriteInput, type SyncoreCapabilities, type SyncoreClient, type SyncoreDatabaseReader, type SyncoreDatabaseWriter, type SyncoreExperimentalPlugin, type SyncoreExperimentalPluginContext, type SyncoreFunctionDefinition, type SyncoreFunctionKind, type SyncoreFunctionRegistry, SyncoreRuntime, type SyncoreRuntimeOptions, type SyncoreSqlDriver, type SyncoreStorageAdapter, type SyncoreStorageApi, type SyncoreWatch, type TableNames, action, createFunctionReference, createFunctionReferenceFor, cronJobs, generateId, mutation, query };