@vibesdotdev/storage 0.0.2 → 1.0.0-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1462) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +78 -1
  3. package/dist/adapters/cloud-api/cloud-api.adapter.descriptor.d.ts +10 -0
  4. package/dist/adapters/cloud-api/cloud-api.adapter.descriptor.d.ts.map +1 -0
  5. package/dist/adapters/cloud-api/cloud-api.adapter.descriptor.js +40 -0
  6. package/dist/adapters/cloud-api/cloud-api.adapter.descriptor.js.map +1 -0
  7. package/dist/adapters/cloud-api/cloud-api.adapter.impl.d.ts +93 -0
  8. package/dist/adapters/cloud-api/cloud-api.adapter.impl.d.ts.map +1 -0
  9. package/dist/adapters/cloud-api/cloud-api.adapter.impl.js +437 -0
  10. package/dist/adapters/cloud-api/cloud-api.adapter.impl.js.map +1 -0
  11. package/dist/adapters/cloud-api/cloud-api.fetch-client.d.ts +59 -0
  12. package/dist/adapters/cloud-api/cloud-api.fetch-client.d.ts.map +1 -0
  13. package/dist/adapters/cloud-api/cloud-api.fetch-client.js +357 -0
  14. package/dist/adapters/cloud-api/cloud-api.fetch-client.js.map +1 -0
  15. package/dist/adapters/cloud-api/cloud-api.record.d.ts +85 -0
  16. package/dist/adapters/cloud-api/cloud-api.record.d.ts.map +1 -0
  17. package/dist/adapters/cloud-api/cloud-api.record.helpers.d.ts +71 -0
  18. package/dist/adapters/cloud-api/cloud-api.record.helpers.d.ts.map +1 -0
  19. package/dist/adapters/cloud-api/cloud-api.record.helpers.js +270 -0
  20. package/dist/adapters/cloud-api/cloud-api.record.helpers.js.map +1 -0
  21. package/dist/adapters/cloud-api/cloud-api.record.js +351 -0
  22. package/dist/adapters/cloud-api/cloud-api.record.js.map +1 -0
  23. package/dist/adapters/cloud-api/cloud-api.test-utils.d.ts +38 -0
  24. package/dist/adapters/cloud-api/cloud-api.test-utils.d.ts.map +1 -0
  25. package/dist/adapters/cloud-api/cloud-api.test-utils.js +139 -0
  26. package/dist/adapters/cloud-api/cloud-api.test-utils.js.map +1 -0
  27. package/dist/adapters/cloud-api/index.d.ts +10 -0
  28. package/dist/adapters/cloud-api/index.d.ts.map +1 -0
  29. package/dist/adapters/cloud-api/index.js +10 -0
  30. package/dist/adapters/cloud-api/index.js.map +1 -0
  31. package/dist/adapters/cloud-api/storage-adapter-cloud-api.plugin.d.ts +9 -0
  32. package/dist/adapters/cloud-api/storage-adapter-cloud-api.plugin.d.ts.map +1 -0
  33. package/dist/adapters/cloud-api/storage-adapter-cloud-api.plugin.js +21 -0
  34. package/dist/adapters/cloud-api/storage-adapter-cloud-api.plugin.js.map +1 -0
  35. package/dist/adapters/drizzle/batch-type-proof.d.ts +21 -0
  36. package/dist/adapters/drizzle/batch-type-proof.d.ts.map +1 -0
  37. package/dist/adapters/drizzle/batch-type-proof.js +2 -0
  38. package/dist/adapters/drizzle/batch-type-proof.js.map +1 -0
  39. package/dist/adapters/drizzle/canonical.adapter.descriptor.d.ts +4 -0
  40. package/dist/adapters/drizzle/canonical.adapter.descriptor.d.ts.map +1 -0
  41. package/dist/adapters/drizzle/canonical.adapter.descriptor.js +29 -0
  42. package/dist/adapters/drizzle/canonical.adapter.descriptor.js.map +1 -0
  43. package/dist/adapters/drizzle/contracts.d.ts +24 -0
  44. package/dist/adapters/drizzle/contracts.d.ts.map +1 -0
  45. package/dist/adapters/drizzle/contracts.js +5 -0
  46. package/dist/adapters/drizzle/contracts.js.map +1 -0
  47. package/dist/adapters/drizzle/docs/storage.adapters.drizzle.docs.descriptor.d.ts +4 -0
  48. package/dist/adapters/drizzle/docs/storage.adapters.drizzle.docs.descriptor.d.ts.map +1 -0
  49. package/dist/adapters/drizzle/docs/storage.adapters.drizzle.docs.descriptor.js +187 -0
  50. package/dist/adapters/drizzle/docs/storage.adapters.drizzle.docs.descriptor.js.map +1 -0
  51. package/dist/adapters/drizzle/drizzle-journal.migration-format.impl.d.ts +15 -0
  52. package/dist/adapters/drizzle/drizzle-journal.migration-format.impl.d.ts.map +1 -0
  53. package/dist/adapters/drizzle/drizzle-journal.migration-format.impl.js +59 -0
  54. package/dist/adapters/drizzle/drizzle-journal.migration-format.impl.js.map +1 -0
  55. package/dist/adapters/drizzle/drizzle-journal.migration-format.plugin.d.ts +5 -0
  56. package/dist/adapters/drizzle/drizzle-journal.migration-format.plugin.d.ts.map +1 -0
  57. package/dist/adapters/drizzle/drizzle-journal.migration-format.plugin.js +27 -0
  58. package/dist/adapters/drizzle/drizzle-journal.migration-format.plugin.js.map +1 -0
  59. package/dist/adapters/drizzle/drizzle.adapter.descriptor.d.ts +2 -0
  60. package/dist/adapters/drizzle/drizzle.adapter.descriptor.d.ts.map +1 -0
  61. package/dist/adapters/drizzle/drizzle.adapter.descriptor.js +3 -0
  62. package/dist/adapters/drizzle/drizzle.adapter.descriptor.js.map +1 -0
  63. package/dist/adapters/drizzle/drizzle.adapter.impl.d.ts +37 -0
  64. package/dist/adapters/drizzle/drizzle.adapter.impl.d.ts.map +1 -0
  65. package/dist/adapters/drizzle/drizzle.adapter.impl.js +1163 -0
  66. package/dist/adapters/drizzle/drizzle.adapter.impl.js.map +1 -0
  67. package/dist/adapters/drizzle/extensions.d.ts +5 -0
  68. package/dist/adapters/drizzle/extensions.d.ts.map +1 -0
  69. package/dist/adapters/drizzle/extensions.js +30 -0
  70. package/dist/adapters/drizzle/extensions.js.map +1 -0
  71. package/dist/adapters/drizzle/index.d.ts +12 -0
  72. package/dist/adapters/drizzle/index.d.ts.map +1 -0
  73. package/dist/adapters/drizzle/index.js +12 -0
  74. package/dist/adapters/drizzle/index.js.map +1 -0
  75. package/dist/adapters/drizzle/libsql-migrator.d.ts +23 -0
  76. package/dist/adapters/drizzle/libsql-migrator.d.ts.map +1 -0
  77. package/dist/adapters/drizzle/libsql-migrator.js +67 -0
  78. package/dist/adapters/drizzle/libsql-migrator.js.map +1 -0
  79. package/dist/adapters/drizzle/libsql.d.ts +15 -0
  80. package/dist/adapters/drizzle/libsql.d.ts.map +1 -0
  81. package/dist/adapters/drizzle/libsql.js +19 -0
  82. package/dist/adapters/drizzle/libsql.js.map +1 -0
  83. package/dist/adapters/drizzle/migration-files.d.ts +15 -0
  84. package/dist/adapters/drizzle/migration-files.d.ts.map +1 -0
  85. package/dist/adapters/drizzle/migration-files.js +54 -0
  86. package/dist/adapters/drizzle/migration-files.js.map +1 -0
  87. package/dist/adapters/drizzle/migration-statements.d.ts +7 -0
  88. package/dist/adapters/drizzle/migration-statements.d.ts.map +1 -0
  89. package/dist/adapters/drizzle/migration-statements.js +14 -0
  90. package/dist/adapters/drizzle/migration-statements.js.map +1 -0
  91. package/dist/adapters/drizzle/persistence-errors.d.ts +33 -0
  92. package/dist/adapters/drizzle/persistence-errors.d.ts.map +1 -0
  93. package/dist/adapters/drizzle/persistence-errors.js +75 -0
  94. package/dist/adapters/drizzle/persistence-errors.js.map +1 -0
  95. package/dist/adapters/drizzle/persistence-factory.d.ts +100 -0
  96. package/dist/adapters/drizzle/persistence-factory.d.ts.map +1 -0
  97. package/dist/adapters/drizzle/persistence-factory.js +391 -0
  98. package/dist/adapters/drizzle/persistence-factory.js.map +1 -0
  99. package/dist/adapters/drizzle/persistence.client.d.ts +73 -0
  100. package/dist/adapters/drizzle/persistence.client.d.ts.map +1 -0
  101. package/dist/adapters/drizzle/persistence.client.js +238 -0
  102. package/dist/adapters/drizzle/persistence.client.js.map +1 -0
  103. package/dist/adapters/drizzle/providers.d.ts +19 -0
  104. package/dist/adapters/drizzle/providers.d.ts.map +1 -0
  105. package/dist/adapters/drizzle/providers.js +62 -0
  106. package/dist/adapters/drizzle/providers.js.map +1 -0
  107. package/dist/adapters/drizzle/record-ddl.d.ts +13 -0
  108. package/dist/adapters/drizzle/record-ddl.d.ts.map +1 -0
  109. package/dist/adapters/drizzle/record-ddl.js +70 -0
  110. package/dist/adapters/drizzle/record-ddl.js.map +1 -0
  111. package/dist/adapters/drizzle/record-table.d.ts +59 -0
  112. package/dist/adapters/drizzle/record-table.d.ts.map +1 -0
  113. package/dist/adapters/drizzle/record-table.js +87 -0
  114. package/dist/adapters/drizzle/record-table.js.map +1 -0
  115. package/dist/adapters/drizzle/schemas/record-migration.d.ts +30 -0
  116. package/dist/adapters/drizzle/schemas/record-migration.d.ts.map +1 -0
  117. package/dist/adapters/drizzle/schemas/record-migration.js +12 -0
  118. package/dist/adapters/drizzle/schemas/record-migration.js.map +1 -0
  119. package/dist/adapters/drizzle/storage-adapter-drizzle.plugin.d.ts +26 -0
  120. package/dist/adapters/drizzle/storage-adapter-drizzle.plugin.d.ts.map +1 -0
  121. package/dist/adapters/drizzle/storage-adapter-drizzle.plugin.js +90 -0
  122. package/dist/adapters/drizzle/storage-adapter-drizzle.plugin.js.map +1 -0
  123. package/dist/adapters/drizzle/storage-drizzle-db.context.d.ts +53 -0
  124. package/dist/adapters/drizzle/storage-drizzle-db.context.d.ts.map +1 -0
  125. package/dist/adapters/drizzle/storage-drizzle-db.context.js +8 -0
  126. package/dist/adapters/drizzle/storage-drizzle-db.context.js.map +1 -0
  127. package/dist/adapters/drizzle/test.d.ts +60 -0
  128. package/dist/adapters/drizzle/test.d.ts.map +1 -0
  129. package/dist/adapters/drizzle/test.js +49 -0
  130. package/dist/adapters/drizzle/test.js.map +1 -0
  131. package/dist/adapters/drizzle/transforms.d.ts +42 -0
  132. package/dist/adapters/drizzle/transforms.d.ts.map +1 -0
  133. package/dist/adapters/drizzle/transforms.js +147 -0
  134. package/dist/adapters/drizzle/transforms.js.map +1 -0
  135. package/dist/adapters/filesystem/filesystem.adapter.descriptor.d.ts +13 -0
  136. package/dist/adapters/filesystem/filesystem.adapter.descriptor.d.ts.map +1 -0
  137. package/dist/adapters/filesystem/filesystem.adapter.descriptor.js +34 -0
  138. package/dist/adapters/filesystem/filesystem.adapter.descriptor.js.map +1 -0
  139. package/dist/adapters/filesystem/filesystem.adapter.impl.d.ts +57 -0
  140. package/dist/adapters/filesystem/filesystem.adapter.impl.d.ts.map +1 -0
  141. package/dist/adapters/filesystem/filesystem.adapter.impl.js +257 -0
  142. package/dist/adapters/filesystem/filesystem.adapter.impl.js.map +1 -0
  143. package/dist/adapters/filesystem/index.d.ts +10 -0
  144. package/dist/adapters/filesystem/index.d.ts.map +1 -0
  145. package/dist/adapters/filesystem/index.js +10 -0
  146. package/dist/adapters/filesystem/index.js.map +1 -0
  147. package/dist/adapters/filesystem/storage-adapter-filesystem.plugin.d.ts +11 -0
  148. package/dist/adapters/filesystem/storage-adapter-filesystem.plugin.d.ts.map +1 -0
  149. package/dist/adapters/filesystem/storage-adapter-filesystem.plugin.js +25 -0
  150. package/dist/adapters/filesystem/storage-adapter-filesystem.plugin.js.map +1 -0
  151. package/dist/adapters/fixture/fixture.adapter.descriptor.d.ts +14 -0
  152. package/dist/adapters/fixture/fixture.adapter.descriptor.d.ts.map +1 -0
  153. package/dist/adapters/fixture/fixture.adapter.descriptor.js +35 -0
  154. package/dist/adapters/fixture/fixture.adapter.descriptor.js.map +1 -0
  155. package/dist/adapters/fixture/fixture.adapter.impl.d.ts +41 -0
  156. package/dist/adapters/fixture/fixture.adapter.impl.d.ts.map +1 -0
  157. package/dist/adapters/fixture/fixture.adapter.impl.js +162 -0
  158. package/dist/adapters/fixture/fixture.adapter.impl.js.map +1 -0
  159. package/dist/adapters/fixture/index.d.ts +9 -0
  160. package/dist/adapters/fixture/index.d.ts.map +1 -0
  161. package/dist/adapters/fixture/index.js +9 -0
  162. package/dist/adapters/fixture/index.js.map +1 -0
  163. package/dist/adapters/fixture/storage-adapter-fixture.plugin.d.ts +10 -0
  164. package/dist/adapters/fixture/storage-adapter-fixture.plugin.d.ts.map +1 -0
  165. package/dist/adapters/fixture/storage-adapter-fixture.plugin.js +21 -0
  166. package/dist/adapters/fixture/storage-adapter-fixture.plugin.js.map +1 -0
  167. package/dist/adapters/idb/idb-backend.d.ts +33 -0
  168. package/dist/adapters/idb/idb-backend.d.ts.map +1 -0
  169. package/dist/adapters/idb/idb-backend.js +217 -0
  170. package/dist/adapters/idb/idb-backend.js.map +1 -0
  171. package/dist/adapters/idb/idb.adapter.descriptor.d.ts +14 -0
  172. package/dist/adapters/idb/idb.adapter.descriptor.d.ts.map +1 -0
  173. package/dist/adapters/idb/idb.adapter.descriptor.js +35 -0
  174. package/dist/adapters/idb/idb.adapter.descriptor.js.map +1 -0
  175. package/dist/adapters/idb/idb.adapter.impl.d.ts +56 -0
  176. package/dist/adapters/idb/idb.adapter.impl.d.ts.map +1 -0
  177. package/dist/adapters/idb/idb.adapter.impl.js +227 -0
  178. package/dist/adapters/idb/idb.adapter.impl.js.map +1 -0
  179. package/dist/adapters/idb/index.d.ts +10 -0
  180. package/dist/adapters/idb/index.d.ts.map +1 -0
  181. package/dist/adapters/idb/index.js +10 -0
  182. package/dist/adapters/idb/index.js.map +1 -0
  183. package/dist/adapters/idb/storage-adapter-idb.plugin.d.ts +11 -0
  184. package/dist/adapters/idb/storage-adapter-idb.plugin.d.ts.map +1 -0
  185. package/dist/adapters/idb/storage-adapter-idb.plugin.js +22 -0
  186. package/dist/adapters/idb/storage-adapter-idb.plugin.js.map +1 -0
  187. package/dist/adapters/json/filesystem-binding.d.ts +14 -0
  188. package/dist/adapters/json/filesystem-binding.d.ts.map +1 -0
  189. package/dist/adapters/json/filesystem-binding.js +7 -0
  190. package/dist/adapters/json/filesystem-binding.js.map +1 -0
  191. package/dist/adapters/json/index.d.ts +10 -0
  192. package/dist/adapters/json/index.d.ts.map +1 -0
  193. package/dist/adapters/json/index.js +10 -0
  194. package/dist/adapters/json/index.js.map +1 -0
  195. package/dist/adapters/json/json.adapter.descriptor.d.ts +10 -0
  196. package/dist/adapters/json/json.adapter.descriptor.d.ts.map +1 -0
  197. package/dist/adapters/json/json.adapter.descriptor.js +38 -0
  198. package/dist/adapters/json/json.adapter.descriptor.js.map +1 -0
  199. package/dist/adapters/json/json.adapter.impl.d.ts +59 -0
  200. package/dist/adapters/json/json.adapter.impl.d.ts.map +1 -0
  201. package/dist/adapters/json/json.adapter.impl.js +395 -0
  202. package/dist/adapters/json/json.adapter.impl.js.map +1 -0
  203. package/dist/adapters/json/storage-adapter-json.plugin.d.ts +9 -0
  204. package/dist/adapters/json/storage-adapter-json.plugin.d.ts.map +1 -0
  205. package/dist/adapters/json/storage-adapter-json.plugin.js +21 -0
  206. package/dist/adapters/json/storage-adapter-json.plugin.js.map +1 -0
  207. package/dist/adapters/localdb/index.d.ts +9 -0
  208. package/dist/adapters/localdb/index.d.ts.map +1 -0
  209. package/dist/adapters/localdb/index.js +9 -0
  210. package/dist/adapters/localdb/index.js.map +1 -0
  211. package/dist/adapters/localdb/localdb.adapter.descriptor.d.ts +10 -0
  212. package/dist/adapters/localdb/localdb.adapter.descriptor.d.ts.map +1 -0
  213. package/dist/adapters/localdb/localdb.adapter.descriptor.js +37 -0
  214. package/dist/adapters/localdb/localdb.adapter.descriptor.js.map +1 -0
  215. package/dist/adapters/localdb/localdb.adapter.impl.d.ts +237 -0
  216. package/dist/adapters/localdb/localdb.adapter.impl.d.ts.map +1 -0
  217. package/dist/adapters/localdb/localdb.adapter.impl.js +1370 -0
  218. package/dist/adapters/localdb/localdb.adapter.impl.js.map +1 -0
  219. package/dist/adapters/localdb/localdb.adapter.loader.consumer.d.ts +2 -0
  220. package/dist/adapters/localdb/localdb.adapter.loader.consumer.d.ts.map +1 -0
  221. package/dist/adapters/localdb/localdb.adapter.loader.consumer.js +2 -0
  222. package/dist/adapters/localdb/localdb.adapter.loader.consumer.js.map +1 -0
  223. package/dist/adapters/localdb/localdb.inputs.d.ts +3 -0
  224. package/dist/adapters/localdb/localdb.inputs.d.ts.map +1 -0
  225. package/dist/adapters/localdb/localdb.inputs.js +3 -0
  226. package/dist/adapters/localdb/localdb.inputs.js.map +1 -0
  227. package/dist/adapters/localdb/migration-failure.d.ts +3 -0
  228. package/dist/adapters/localdb/migration-failure.d.ts.map +1 -0
  229. package/dist/adapters/localdb/migration-failure.js +16 -0
  230. package/dist/adapters/localdb/migration-failure.js.map +1 -0
  231. package/dist/adapters/localdb/outcome.d.ts +13 -0
  232. package/dist/adapters/localdb/outcome.d.ts.map +1 -0
  233. package/dist/adapters/localdb/outcome.js +12 -0
  234. package/dist/adapters/localdb/outcome.js.map +1 -0
  235. package/dist/adapters/localdb/repository-query.d.ts +19 -0
  236. package/dist/adapters/localdb/repository-query.d.ts.map +1 -0
  237. package/dist/adapters/localdb/repository-query.js +127 -0
  238. package/dist/adapters/localdb/repository-query.js.map +1 -0
  239. package/dist/adapters/localdb/storage-adapter-localdb.plugin.d.ts +14 -0
  240. package/dist/adapters/localdb/storage-adapter-localdb.plugin.d.ts.map +1 -0
  241. package/dist/adapters/localdb/storage-adapter-localdb.plugin.js +30 -0
  242. package/dist/adapters/localdb/storage-adapter-localdb.plugin.js.map +1 -0
  243. package/dist/adapters/memory/index.d.ts +9 -0
  244. package/dist/adapters/memory/index.d.ts.map +1 -0
  245. package/dist/adapters/memory/index.js +9 -0
  246. package/dist/adapters/memory/index.js.map +1 -0
  247. package/dist/adapters/memory/memory-root-store.d.ts +20 -0
  248. package/dist/adapters/memory/memory-root-store.d.ts.map +1 -0
  249. package/dist/adapters/memory/memory-root-store.js +49 -0
  250. package/dist/adapters/memory/memory-root-store.js.map +1 -0
  251. package/dist/adapters/memory/memory.adapter.impl.d.ts +61 -0
  252. package/dist/adapters/memory/memory.adapter.impl.d.ts.map +1 -0
  253. package/dist/adapters/memory/memory.adapter.impl.js +718 -0
  254. package/dist/adapters/memory/memory.adapter.impl.js.map +1 -0
  255. package/dist/adapters/memory/memory.adapter.storage-adapter-memory.descriptor.d.ts +10 -0
  256. package/dist/adapters/memory/memory.adapter.storage-adapter-memory.descriptor.d.ts.map +1 -0
  257. package/dist/adapters/memory/memory.adapter.storage-adapter-memory.descriptor.js +37 -0
  258. package/dist/adapters/memory/memory.adapter.storage-adapter-memory.descriptor.js.map +1 -0
  259. package/dist/adapters/memory/storage-adapter-memory.plugin.d.ts +9 -0
  260. package/dist/adapters/memory/storage-adapter-memory.plugin.d.ts.map +1 -0
  261. package/dist/adapters/memory/storage-adapter-memory.plugin.js +44 -0
  262. package/dist/adapters/memory/storage-adapter-memory.plugin.js.map +1 -0
  263. package/dist/adapters/parse-adapter-input.d.ts +22 -0
  264. package/dist/adapters/parse-adapter-input.d.ts.map +1 -0
  265. package/dist/adapters/parse-adapter-input.js +36 -0
  266. package/dist/adapters/parse-adapter-input.js.map +1 -0
  267. package/dist/adapters/select-adapter.d.ts +36 -0
  268. package/dist/adapters/select-adapter.d.ts.map +1 -0
  269. package/dist/adapters/select-adapter.js +88 -0
  270. package/dist/adapters/select-adapter.js.map +1 -0
  271. package/dist/adapters/validate-for-persist.d.ts +21 -0
  272. package/dist/adapters/validate-for-persist.d.ts.map +1 -0
  273. package/dist/adapters/validate-for-persist.js +36 -0
  274. package/dist/adapters/validate-for-persist.js.map +1 -0
  275. package/dist/backup/backup.plugin.d.ts +276 -0
  276. package/dist/backup/backup.plugin.d.ts.map +1 -0
  277. package/dist/backup/backup.plugin.js +72 -0
  278. package/dist/backup/backup.plugin.js.map +1 -0
  279. package/dist/backup/channels/backup.channel.d.ts +21 -0
  280. package/dist/backup/channels/backup.channel.d.ts.map +1 -0
  281. package/dist/backup/channels/backup.channel.js +89 -0
  282. package/dist/backup/channels/backup.channel.js.map +1 -0
  283. package/dist/backup/config/storage.backup.config.descriptor.d.ts +57 -0
  284. package/dist/backup/config/storage.backup.config.descriptor.d.ts.map +1 -0
  285. package/dist/backup/config/storage.backup.config.descriptor.js +53 -0
  286. package/dist/backup/config/storage.backup.config.descriptor.js.map +1 -0
  287. package/dist/backup/core/hash.d.ts +8 -0
  288. package/dist/backup/core/hash.d.ts.map +1 -0
  289. package/dist/backup/core/hash.js +10 -0
  290. package/dist/backup/core/hash.js.map +1 -0
  291. package/dist/backup/core/homes.d.ts +13 -0
  292. package/dist/backup/core/homes.d.ts.map +1 -0
  293. package/dist/backup/core/homes.js +54 -0
  294. package/dist/backup/core/homes.js.map +1 -0
  295. package/dist/backup/core/index.d.ts +17 -0
  296. package/dist/backup/core/index.d.ts.map +1 -0
  297. package/dist/backup/core/index.js +12 -0
  298. package/dist/backup/core/index.js.map +1 -0
  299. package/dist/backup/core/io.d.ts +76 -0
  300. package/dist/backup/core/io.d.ts.map +1 -0
  301. package/dist/backup/core/io.js +177 -0
  302. package/dist/backup/core/io.js.map +1 -0
  303. package/dist/backup/core/paths.d.ts +50 -0
  304. package/dist/backup/core/paths.d.ts.map +1 -0
  305. package/dist/backup/core/paths.js +75 -0
  306. package/dist/backup/core/paths.js.map +1 -0
  307. package/dist/backup/core/readiness.d.ts +9 -0
  308. package/dist/backup/core/readiness.d.ts.map +1 -0
  309. package/dist/backup/core/readiness.js +104 -0
  310. package/dist/backup/core/readiness.js.map +1 -0
  311. package/dist/backup/core/recovery.d.ts +11 -0
  312. package/dist/backup/core/recovery.d.ts.map +1 -0
  313. package/dist/backup/core/recovery.js +75 -0
  314. package/dist/backup/core/recovery.js.map +1 -0
  315. package/dist/backup/core/registry.d.ts +13 -0
  316. package/dist/backup/core/registry.d.ts.map +1 -0
  317. package/dist/backup/core/registry.js +18 -0
  318. package/dist/backup/core/registry.js.map +1 -0
  319. package/dist/backup/core/restore.d.ts +36 -0
  320. package/dist/backup/core/restore.d.ts.map +1 -0
  321. package/dist/backup/core/restore.js +280 -0
  322. package/dist/backup/core/restore.js.map +1 -0
  323. package/dist/backup/core/rotate.d.ts +19 -0
  324. package/dist/backup/core/rotate.d.ts.map +1 -0
  325. package/dist/backup/core/rotate.js +153 -0
  326. package/dist/backup/core/rotate.js.map +1 -0
  327. package/dist/backup/core/rotation.d.ts +19 -0
  328. package/dist/backup/core/rotation.d.ts.map +1 -0
  329. package/dist/backup/core/rotation.js +100 -0
  330. package/dist/backup/core/rotation.js.map +1 -0
  331. package/dist/backup/core/snapshot.d.ts +45 -0
  332. package/dist/backup/core/snapshot.d.ts.map +1 -0
  333. package/dist/backup/core/snapshot.js +217 -0
  334. package/dist/backup/core/snapshot.js.map +1 -0
  335. package/dist/backup/docs/backup.disaster-recovery.docs.descriptor.d.ts +19 -0
  336. package/dist/backup/docs/backup.disaster-recovery.docs.descriptor.d.ts.map +1 -0
  337. package/dist/backup/docs/backup.disaster-recovery.docs.descriptor.js +42 -0
  338. package/dist/backup/docs/backup.disaster-recovery.docs.descriptor.js.map +1 -0
  339. package/dist/backup/index.d.ts +19 -0
  340. package/dist/backup/index.d.ts.map +1 -0
  341. package/dist/backup/index.js +16 -0
  342. package/dist/backup/index.js.map +1 -0
  343. package/dist/backup/jobs/backup.snapshot.descriptor.d.ts +95 -0
  344. package/dist/backup/jobs/backup.snapshot.descriptor.d.ts.map +1 -0
  345. package/dist/backup/jobs/backup.snapshot.descriptor.js +33 -0
  346. package/dist/backup/jobs/backup.snapshot.descriptor.js.map +1 -0
  347. package/dist/backup/jobs/backup.snapshot.impl.d.ts +59 -0
  348. package/dist/backup/jobs/backup.snapshot.impl.d.ts.map +1 -0
  349. package/dist/backup/jobs/backup.snapshot.impl.js +393 -0
  350. package/dist/backup/jobs/backup.snapshot.impl.js.map +1 -0
  351. package/dist/backup/jobs/backup.snapshot.schema.d.ts +3 -0
  352. package/dist/backup/jobs/backup.snapshot.schema.d.ts.map +1 -0
  353. package/dist/backup/jobs/backup.snapshot.schema.js +3 -0
  354. package/dist/backup/jobs/backup.snapshot.schema.js.map +1 -0
  355. package/dist/backup/kinds/backup-preflight.kind.d.ts +16 -0
  356. package/dist/backup/kinds/backup-preflight.kind.d.ts.map +1 -0
  357. package/dist/backup/kinds/backup-preflight.kind.js +27 -0
  358. package/dist/backup/kinds/backup-preflight.kind.js.map +1 -0
  359. package/dist/backup/kinds/backup-recovery-plan.kind.d.ts +11 -0
  360. package/dist/backup/kinds/backup-recovery-plan.kind.d.ts.map +1 -0
  361. package/dist/backup/kinds/backup-recovery-plan.kind.js +16 -0
  362. package/dist/backup/kinds/backup-recovery-plan.kind.js.map +1 -0
  363. package/dist/backup/kinds/backup-target.kind.d.ts +16 -0
  364. package/dist/backup/kinds/backup-target.kind.d.ts.map +1 -0
  365. package/dist/backup/kinds/backup-target.kind.js +21 -0
  366. package/dist/backup/kinds/backup-target.kind.js.map +1 -0
  367. package/dist/backup/schemas/core.d.ts +169 -0
  368. package/dist/backup/schemas/core.d.ts.map +1 -0
  369. package/dist/backup/schemas/core.js +117 -0
  370. package/dist/backup/schemas/core.js.map +1 -0
  371. package/dist/backup/schemas/disaster-recovery.d.ts +512 -0
  372. package/dist/backup/schemas/disaster-recovery.d.ts.map +1 -0
  373. package/dist/backup/schemas/disaster-recovery.js +410 -0
  374. package/dist/backup/schemas/disaster-recovery.js.map +1 -0
  375. package/dist/backup/schemas/index.d.ts +10 -0
  376. package/dist/backup/schemas/index.d.ts.map +1 -0
  377. package/dist/backup/schemas/index.js +10 -0
  378. package/dist/backup/schemas/index.js.map +1 -0
  379. package/dist/backup/schemas/job.d.ts +102 -0
  380. package/dist/backup/schemas/job.d.ts.map +1 -0
  381. package/dist/backup/schemas/job.js +47 -0
  382. package/dist/backup/schemas/job.js.map +1 -0
  383. package/dist/backup/schemas/preflight.d.ts +38 -0
  384. package/dist/backup/schemas/preflight.d.ts.map +1 -0
  385. package/dist/backup/schemas/preflight.js +21 -0
  386. package/dist/backup/schemas/preflight.js.map +1 -0
  387. package/dist/backup/schemas/snapshot.d.ts +110 -0
  388. package/dist/backup/schemas/snapshot.d.ts.map +1 -0
  389. package/dist/backup/schemas/snapshot.js +84 -0
  390. package/dist/backup/schemas/snapshot.js.map +1 -0
  391. package/dist/backup/schemas/target.d.ts +95 -0
  392. package/dist/backup/schemas/target.d.ts.map +1 -0
  393. package/dist/backup/schemas/target.js +105 -0
  394. package/dist/backup/schemas/target.js.map +1 -0
  395. package/dist/backup/storage/snapshot.record.descriptor.d.ts +10 -0
  396. package/dist/backup/storage/snapshot.record.descriptor.d.ts.map +1 -0
  397. package/dist/backup/storage/snapshot.record.descriptor.js +41 -0
  398. package/dist/backup/storage/snapshot.record.descriptor.js.map +1 -0
  399. package/dist/backup/targets/awareness-bus.target.descriptor.d.ts +10 -0
  400. package/dist/backup/targets/awareness-bus.target.descriptor.d.ts.map +1 -0
  401. package/dist/backup/targets/awareness-bus.target.descriptor.js +32 -0
  402. package/dist/backup/targets/awareness-bus.target.descriptor.js.map +1 -0
  403. package/dist/backup/targets/presence-identity.target.descriptor.d.ts +10 -0
  404. package/dist/backup/targets/presence-identity.target.descriptor.d.ts.map +1 -0
  405. package/dist/backup/targets/presence-identity.target.descriptor.js +40 -0
  406. package/dist/backup/targets/presence-identity.target.descriptor.js.map +1 -0
  407. package/dist/backup/targets/presence-inner.target.descriptor.d.ts +10 -0
  408. package/dist/backup/targets/presence-inner.target.descriptor.d.ts.map +1 -0
  409. package/dist/backup/targets/presence-inner.target.descriptor.js +36 -0
  410. package/dist/backup/targets/presence-inner.target.descriptor.js.map +1 -0
  411. package/dist/backup/targets/storage-records.target.descriptor.d.ts +16 -0
  412. package/dist/backup/targets/storage-records.target.descriptor.d.ts.map +1 -0
  413. package/dist/backup/targets/storage-records.target.descriptor.js +38 -0
  414. package/dist/backup/targets/storage-records.target.descriptor.js.map +1 -0
  415. package/dist/bridge/index.d.ts +2 -2
  416. package/dist/bridge/index.d.ts.map +1 -1
  417. package/dist/bridge/index.js +1 -1
  418. package/dist/bridge/index.js.map +1 -1
  419. package/dist/bridge/storage-kind-bridge.d.ts +13 -2
  420. package/dist/bridge/storage-kind-bridge.d.ts.map +1 -1
  421. package/dist/bridge/storage-kind-bridge.js +72 -69
  422. package/dist/bridge/storage-kind-bridge.js.map +1 -1
  423. package/dist/bridge/types.d.ts +4 -3
  424. package/dist/bridge/types.d.ts.map +1 -1
  425. package/dist/cli/backup/backup.cli-group.descriptor.d.ts +9 -0
  426. package/dist/cli/backup/backup.cli-group.descriptor.d.ts.map +1 -0
  427. package/dist/cli/backup/backup.cli-group.descriptor.js +16 -0
  428. package/dist/cli/backup/backup.cli-group.descriptor.js.map +1 -0
  429. package/dist/cli/backup/commands/backup.now.cli-command.descriptor.d.ts +4 -0
  430. package/dist/cli/backup/commands/backup.now.cli-command.descriptor.d.ts.map +1 -0
  431. package/dist/cli/backup/commands/backup.now.cli-command.descriptor.js +24 -0
  432. package/dist/cli/backup/commands/backup.now.cli-command.descriptor.js.map +1 -0
  433. package/dist/cli/backup/commands/backup.now.cli-command.impl.d.ts +26 -0
  434. package/dist/cli/backup/commands/backup.now.cli-command.impl.d.ts.map +1 -0
  435. package/dist/cli/backup/commands/backup.now.cli-command.impl.js +61 -0
  436. package/dist/cli/backup/commands/backup.now.cli-command.impl.js.map +1 -0
  437. package/dist/cli/backup/commands/backup.restore.cli-command.descriptor.d.ts +4 -0
  438. package/dist/cli/backup/commands/backup.restore.cli-command.descriptor.d.ts.map +1 -0
  439. package/dist/cli/backup/commands/backup.restore.cli-command.descriptor.js +24 -0
  440. package/dist/cli/backup/commands/backup.restore.cli-command.descriptor.js.map +1 -0
  441. package/dist/cli/backup/commands/backup.restore.cli-command.impl.d.ts +23 -0
  442. package/dist/cli/backup/commands/backup.restore.cli-command.impl.d.ts.map +1 -0
  443. package/dist/cli/backup/commands/backup.restore.cli-command.impl.js +50 -0
  444. package/dist/cli/backup/commands/backup.restore.cli-command.impl.js.map +1 -0
  445. package/dist/cli/backup/commands/backup.status.cli-command.descriptor.d.ts +4 -0
  446. package/dist/cli/backup/commands/backup.status.cli-command.descriptor.d.ts.map +1 -0
  447. package/dist/cli/backup/commands/backup.status.cli-command.descriptor.js +19 -0
  448. package/dist/cli/backup/commands/backup.status.cli-command.descriptor.js.map +1 -0
  449. package/dist/cli/backup/commands/backup.status.cli-command.impl.d.ts +21 -0
  450. package/dist/cli/backup/commands/backup.status.cli-command.impl.d.ts.map +1 -0
  451. package/dist/cli/backup/commands/backup.status.cli-command.impl.js +76 -0
  452. package/dist/cli/backup/commands/backup.status.cli-command.impl.js.map +1 -0
  453. package/dist/cli/backup/commands/backup.verify.cli-command.descriptor.d.ts +4 -0
  454. package/dist/cli/backup/commands/backup.verify.cli-command.descriptor.d.ts.map +1 -0
  455. package/dist/cli/backup/commands/backup.verify.cli-command.descriptor.js +20 -0
  456. package/dist/cli/backup/commands/backup.verify.cli-command.descriptor.js.map +1 -0
  457. package/dist/cli/backup/commands/backup.verify.cli-command.impl.d.ts +20 -0
  458. package/dist/cli/backup/commands/backup.verify.cli-command.impl.d.ts.map +1 -0
  459. package/dist/cli/backup/commands/backup.verify.cli-command.impl.js +48 -0
  460. package/dist/cli/backup/commands/backup.verify.cli-command.impl.js.map +1 -0
  461. package/dist/cli/backup/plugin.d.ts +5 -0
  462. package/dist/cli/backup/plugin.d.ts.map +1 -0
  463. package/dist/cli/backup/plugin.js +30 -0
  464. package/dist/cli/backup/plugin.js.map +1 -0
  465. package/dist/cli/commands/list/storage.list.cli-command.descriptor.d.ts +1 -1
  466. package/dist/cli/commands/list/storage.list.cli-command.descriptor.d.ts.map +1 -1
  467. package/dist/cli/commands/list/storage.list.cli-command.impl.d.ts +8 -1
  468. package/dist/cli/commands/list/storage.list.cli-command.impl.d.ts.map +1 -1
  469. package/dist/cli/commands/list/storage.list.cli-command.impl.js +17 -8
  470. package/dist/cli/commands/list/storage.list.cli-command.impl.js.map +1 -1
  471. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.d.ts +1 -1
  472. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.d.ts.map +1 -1
  473. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.d.ts +9 -1
  474. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.d.ts.map +1 -1
  475. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.js +110 -76
  476. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.js.map +1 -1
  477. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.d.ts +1 -1
  478. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.d.ts.map +1 -1
  479. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.d.ts +9 -1
  480. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.d.ts.map +1 -1
  481. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.js +131 -108
  482. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.js.map +1 -1
  483. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.d.ts +1 -1
  484. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.d.ts.map +1 -1
  485. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.d.ts +12 -1
  486. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.d.ts.map +1 -1
  487. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.js +34 -26
  488. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.js.map +1 -1
  489. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.d.ts +1 -1
  490. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.d.ts.map +1 -1
  491. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.d.ts +11 -1
  492. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.d.ts.map +1 -1
  493. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.js +89 -42
  494. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.js.map +1 -1
  495. package/dist/cli/commands/sync/sync-command-input.d.ts +20 -0
  496. package/dist/cli/commands/sync/sync-command-input.d.ts.map +1 -0
  497. package/dist/cli/commands/sync/sync-command-input.js +20 -0
  498. package/dist/cli/commands/sync/sync-command-input.js.map +1 -0
  499. package/dist/cli/gc/commands/gc.run.cli-command.descriptor.d.ts +4 -0
  500. package/dist/cli/gc/commands/gc.run.cli-command.descriptor.d.ts.map +1 -0
  501. package/dist/cli/gc/commands/gc.run.cli-command.descriptor.js +20 -0
  502. package/dist/cli/gc/commands/gc.run.cli-command.descriptor.js.map +1 -0
  503. package/dist/cli/gc/commands/gc.run.cli-command.impl.d.ts +24 -0
  504. package/dist/cli/gc/commands/gc.run.cli-command.impl.d.ts.map +1 -0
  505. package/dist/cli/gc/commands/gc.run.cli-command.impl.js +104 -0
  506. package/dist/cli/gc/commands/gc.run.cli-command.impl.js.map +1 -0
  507. package/dist/cli/gc/commands/gc.status.cli-command.descriptor.d.ts +4 -0
  508. package/dist/cli/gc/commands/gc.status.cli-command.descriptor.d.ts.map +1 -0
  509. package/dist/cli/gc/commands/gc.status.cli-command.descriptor.js +19 -0
  510. package/dist/cli/gc/commands/gc.status.cli-command.descriptor.js.map +1 -0
  511. package/dist/cli/gc/commands/gc.status.cli-command.impl.d.ts +21 -0
  512. package/dist/cli/gc/commands/gc.status.cli-command.impl.d.ts.map +1 -0
  513. package/dist/cli/gc/commands/gc.status.cli-command.impl.js +74 -0
  514. package/dist/cli/gc/commands/gc.status.cli-command.impl.js.map +1 -0
  515. package/dist/cli/gc/gc.cli-group.descriptor.d.ts +4 -0
  516. package/dist/cli/gc/gc.cli-group.descriptor.d.ts.map +1 -0
  517. package/dist/cli/gc/gc.cli-group.descriptor.js +11 -0
  518. package/dist/cli/gc/gc.cli-group.descriptor.js.map +1 -0
  519. package/dist/cli/gc/plugin.d.ts +5 -0
  520. package/dist/cli/gc/plugin.d.ts.map +1 -0
  521. package/dist/cli/gc/plugin.js +28 -0
  522. package/dist/cli/gc/plugin.js.map +1 -0
  523. package/dist/cli/plugin.d.ts +17 -0
  524. package/dist/cli/plugin.d.ts.map +1 -0
  525. package/dist/cli/plugin.js +51 -0
  526. package/dist/cli/plugin.js.map +1 -0
  527. package/dist/cli/storage.cli-group.descriptor.d.ts +1 -1
  528. package/dist/cli/storage.cli-group.descriptor.d.ts.map +1 -1
  529. package/dist/cli/storage.sync.cli-group.descriptor.d.ts +1 -1
  530. package/dist/cli/storage.sync.cli-group.descriptor.d.ts.map +1 -1
  531. package/dist/cli/types.d.ts +4 -20
  532. package/dist/cli/types.d.ts.map +1 -1
  533. package/dist/cli/types.js +3 -4
  534. package/dist/cli/types.js.map +1 -1
  535. package/dist/client/browse-adapter.d.ts +49 -0
  536. package/dist/client/browse-adapter.d.ts.map +1 -0
  537. package/dist/client/browse-adapter.js +99 -0
  538. package/dist/client/browse-adapter.js.map +1 -0
  539. package/dist/client/diagnose-adapter.d.ts +9 -0
  540. package/dist/client/diagnose-adapter.d.ts.map +1 -0
  541. package/dist/client/diagnose-adapter.js +9 -0
  542. package/dist/client/diagnose-adapter.js.map +1 -0
  543. package/dist/client/index.d.ts +7 -1
  544. package/dist/client/index.d.ts.map +1 -1
  545. package/dist/client/index.js +5 -1
  546. package/dist/client/index.js.map +1 -1
  547. package/dist/client/list-browsable-adapters.d.ts +4 -3
  548. package/dist/client/list-browsable-adapters.d.ts.map +1 -1
  549. package/dist/client/list-browsable-adapters.js +3 -8
  550. package/dist/client/list-browsable-adapters.js.map +1 -1
  551. package/dist/client/object-adapter.d.ts +33 -0
  552. package/dist/client/object-adapter.d.ts.map +1 -0
  553. package/dist/client/object-adapter.js +45 -0
  554. package/dist/client/object-adapter.js.map +1 -0
  555. package/dist/client/storage-explorer-capability.d.ts +81 -0
  556. package/dist/client/storage-explorer-capability.d.ts.map +1 -0
  557. package/dist/client/storage-explorer-capability.impl.worker.d.ts +23 -0
  558. package/dist/client/storage-explorer-capability.impl.worker.d.ts.map +1 -0
  559. package/dist/client/storage-explorer-capability.impl.worker.js +124 -0
  560. package/dist/client/storage-explorer-capability.impl.worker.js.map +1 -0
  561. package/dist/client/storage-explorer-capability.js +125 -0
  562. package/dist/client/storage-explorer-capability.js.map +1 -0
  563. package/dist/client/storage-failure.d.ts +43 -0
  564. package/dist/client/storage-failure.d.ts.map +1 -0
  565. package/dist/client/storage-failure.js +100 -0
  566. package/dist/client/storage-failure.js.map +1 -0
  567. package/dist/daemon/commands/browse.d.ts +29 -0
  568. package/dist/daemon/commands/browse.d.ts.map +1 -0
  569. package/dist/daemon/commands/browse.js +91 -0
  570. package/dist/daemon/commands/browse.js.map +1 -0
  571. package/dist/daemon/commands/sync.d.ts +1 -1
  572. package/dist/daemon/commands/sync.d.ts.map +1 -1
  573. package/dist/daemon/commands/sync.js +60 -52
  574. package/dist/daemon/commands/sync.js.map +1 -1
  575. package/dist/docs/records.docs.descriptor.d.ts +1 -1
  576. package/dist/docs/records.docs.descriptor.d.ts.map +1 -1
  577. package/dist/docs/records.docs.descriptor.js +136 -151
  578. package/dist/docs/records.docs.descriptor.js.map +1 -1
  579. package/dist/docs/storage.adapters.docs.descriptor.d.ts +4 -0
  580. package/dist/docs/storage.adapters.docs.descriptor.d.ts.map +1 -0
  581. package/dist/docs/storage.adapters.docs.descriptor.js +175 -0
  582. package/dist/docs/storage.adapters.docs.descriptor.js.map +1 -0
  583. package/dist/docs/storage.browse.docs.descriptor.d.ts +4 -0
  584. package/dist/docs/storage.browse.docs.descriptor.d.ts.map +1 -0
  585. package/dist/docs/storage.browse.docs.descriptor.js +74 -0
  586. package/dist/docs/storage.browse.docs.descriptor.js.map +1 -0
  587. package/dist/docs/storage.docs.descriptor.d.ts +4 -0
  588. package/dist/docs/storage.docs.descriptor.d.ts.map +1 -0
  589. package/dist/docs/storage.docs.descriptor.js +51 -0
  590. package/dist/docs/storage.docs.descriptor.js.map +1 -0
  591. package/dist/docs/storage.manifests.docs.descriptor.d.ts +4 -0
  592. package/dist/docs/storage.manifests.docs.descriptor.d.ts.map +1 -0
  593. package/dist/docs/{manifests.docs.descriptor.js → storage.manifests.docs.descriptor.js} +69 -34
  594. package/dist/docs/storage.manifests.docs.descriptor.js.map +1 -0
  595. package/dist/docs/sync.docs.descriptor.d.ts +1 -1
  596. package/dist/docs/sync.docs.descriptor.d.ts.map +1 -1
  597. package/dist/docs/sync.docs.descriptor.js +140 -150
  598. package/dist/docs/sync.docs.descriptor.js.map +1 -1
  599. package/dist/docs/transform.docs.descriptor.d.ts +4 -0
  600. package/dist/docs/transform.docs.descriptor.d.ts.map +1 -0
  601. package/dist/docs/transform.docs.descriptor.js +134 -0
  602. package/dist/docs/transform.docs.descriptor.js.map +1 -0
  603. package/dist/host-files.d.ts +35 -0
  604. package/dist/host-files.d.ts.map +1 -0
  605. package/dist/host-files.js +60 -0
  606. package/dist/host-files.js.map +1 -0
  607. package/dist/index.d.ts +15 -13
  608. package/dist/index.d.ts.map +1 -1
  609. package/dist/index.js +10 -10
  610. package/dist/index.js.map +1 -1
  611. package/dist/interfaces/adapter.d.ts +166 -47
  612. package/dist/interfaces/adapter.d.ts.map +1 -1
  613. package/dist/interfaces/document-store.d.ts +41 -1
  614. package/dist/interfaces/document-store.d.ts.map +1 -1
  615. package/dist/interfaces/index.d.ts +8 -7
  616. package/dist/interfaces/index.d.ts.map +1 -1
  617. package/dist/interfaces/index.js +9 -7
  618. package/dist/interfaces/index.js.map +1 -1
  619. package/dist/interfaces/object-store.d.ts +54 -0
  620. package/dist/interfaces/object-store.d.ts.map +1 -0
  621. package/dist/interfaces/object-store.js +13 -0
  622. package/dist/interfaces/object-store.js.map +1 -0
  623. package/dist/interfaces/query-builder.d.ts +2 -30
  624. package/dist/interfaces/query-builder.d.ts.map +1 -1
  625. package/dist/interfaces/query-builder.js +0 -9
  626. package/dist/interfaces/query-builder.js.map +1 -1
  627. package/dist/interfaces/record/index.d.ts +1 -1
  628. package/dist/interfaces/record/index.d.ts.map +1 -1
  629. package/dist/interfaces/record/index.js +1 -1
  630. package/dist/interfaces/record/index.js.map +1 -1
  631. package/dist/interfaces/record/interface.d.ts +30 -9
  632. package/dist/interfaces/record/interface.d.ts.map +1 -1
  633. package/dist/interfaces/schema/index.d.ts +2 -2
  634. package/dist/interfaces/schema/index.d.ts.map +1 -1
  635. package/dist/interfaces/schema/index.js +2 -2
  636. package/dist/interfaces/schema/index.js.map +1 -1
  637. package/dist/interfaces/schema/schema-interface.d.ts +9 -8
  638. package/dist/interfaces/schema/schema-interface.d.ts.map +1 -1
  639. package/dist/interfaces/schema/table-interface.d.ts +4 -3
  640. package/dist/interfaces/schema/table-interface.d.ts.map +1 -1
  641. package/dist/interfaces/storage-consumer-fs.d.ts +25 -27
  642. package/dist/interfaces/storage-consumer-fs.d.ts.map +1 -1
  643. package/dist/interfaces/storage-consumer-fs.js +29 -30
  644. package/dist/interfaces/storage-consumer-fs.js.map +1 -1
  645. package/dist/manifest/base.d.ts +104 -22
  646. package/dist/manifest/base.d.ts.map +1 -1
  647. package/dist/manifest/base.js +393 -151
  648. package/dist/manifest/base.js.map +1 -1
  649. package/dist/manifest/cloud-sync.browser.d.ts +3 -0
  650. package/dist/manifest/cloud-sync.browser.d.ts.map +1 -0
  651. package/dist/manifest/cloud-sync.browser.js +3 -0
  652. package/dist/manifest/cloud-sync.browser.js.map +1 -0
  653. package/dist/manifest/cloud-sync.consumer.d.ts +2 -6
  654. package/dist/manifest/cloud-sync.consumer.d.ts.map +1 -1
  655. package/dist/manifest/cloud-sync.consumer.js +2 -81
  656. package/dist/manifest/cloud-sync.consumer.js.map +1 -1
  657. package/dist/manifest/cloud-sync.core.d.ts +30 -0
  658. package/dist/manifest/cloud-sync.core.d.ts.map +1 -0
  659. package/dist/manifest/cloud-sync.core.js +67 -0
  660. package/dist/manifest/cloud-sync.core.js.map +1 -0
  661. package/dist/manifest/cloud-sync.d.ts +3 -0
  662. package/dist/manifest/cloud-sync.d.ts.map +1 -0
  663. package/dist/manifest/cloud-sync.js +8 -0
  664. package/dist/manifest/cloud-sync.js.map +1 -0
  665. package/dist/manifest/index.d.ts +1 -1
  666. package/dist/manifest/index.d.ts.map +1 -1
  667. package/dist/manifest/index.js +1 -1
  668. package/dist/manifest/index.js.map +1 -1
  669. package/dist/manifest/source-policy.d.ts +21 -4
  670. package/dist/manifest/source-policy.d.ts.map +1 -1
  671. package/dist/manifest/source-policy.js +48 -50
  672. package/dist/manifest/source-policy.js.map +1 -1
  673. package/dist/migration-format/index.d.ts +3 -0
  674. package/dist/migration-format/index.d.ts.map +1 -0
  675. package/dist/migration-format/index.js +3 -0
  676. package/dist/migration-format/index.js.map +1 -0
  677. package/dist/migration-format/migration-format.kind.d.ts +5 -0
  678. package/dist/migration-format/migration-format.kind.d.ts.map +1 -0
  679. package/dist/migration-format/migration-format.kind.js +18 -0
  680. package/dist/migration-format/migration-format.kind.js.map +1 -0
  681. package/dist/path-format.d.ts +4 -0
  682. package/dist/path-format.d.ts.map +1 -0
  683. package/dist/path-format.js +71 -0
  684. package/dist/path-format.js.map +1 -0
  685. package/dist/path-policy.d.ts +28 -0
  686. package/dist/path-policy.d.ts.map +1 -0
  687. package/dist/path-policy.js +123 -0
  688. package/dist/path-policy.js.map +1 -0
  689. package/dist/paths.d.ts +14 -5
  690. package/dist/paths.d.ts.map +1 -1
  691. package/dist/paths.js +17 -150
  692. package/dist/paths.js.map +1 -1
  693. package/dist/policy/context.d.ts +3 -2
  694. package/dist/policy/context.d.ts.map +1 -1
  695. package/dist/policy/context.js +13 -16
  696. package/dist/policy/context.js.map +1 -1
  697. package/dist/policy/index.d.ts +3 -3
  698. package/dist/policy/index.d.ts.map +1 -1
  699. package/dist/policy/index.js +3 -3
  700. package/dist/policy/index.js.map +1 -1
  701. package/dist/policy/overrides.d.ts +5 -39
  702. package/dist/policy/overrides.d.ts.map +1 -1
  703. package/dist/policy/overrides.js +19 -24
  704. package/dist/policy/overrides.js.map +1 -1
  705. package/dist/policy/persistence.d.ts +2 -35
  706. package/dist/policy/persistence.d.ts.map +1 -1
  707. package/dist/policy/persistence.js +2 -12
  708. package/dist/policy/persistence.js.map +1 -1
  709. package/dist/policy/types.d.ts +5 -5
  710. package/dist/policy/types.d.ts.map +1 -1
  711. package/dist/record/array-fields.d.ts +15 -0
  712. package/dist/record/array-fields.d.ts.map +1 -0
  713. package/dist/record/array-fields.js +80 -0
  714. package/dist/record/array-fields.js.map +1 -0
  715. package/dist/record/atomic.d.ts +5 -0
  716. package/dist/record/atomic.d.ts.map +1 -0
  717. package/dist/record/atomic.js +10 -0
  718. package/dist/record/atomic.js.map +1 -0
  719. package/dist/record/base.d.ts +177 -39
  720. package/dist/record/base.d.ts.map +1 -1
  721. package/dist/record/base.js +1708 -525
  722. package/dist/record/base.js.map +1 -1
  723. package/dist/record/columns.d.ts +23 -0
  724. package/dist/record/columns.d.ts.map +1 -0
  725. package/dist/record/columns.js +161 -0
  726. package/dist/record/columns.js.map +1 -0
  727. package/dist/record/delivery.d.ts +57 -0
  728. package/dist/record/delivery.d.ts.map +1 -0
  729. package/dist/record/delivery.js +262 -0
  730. package/dist/record/delivery.js.map +1 -0
  731. package/dist/record/events.js +1 -1
  732. package/dist/record/events.js.map +1 -1
  733. package/dist/record/extensions.d.ts +17 -14
  734. package/dist/record/extensions.d.ts.map +1 -1
  735. package/dist/record/extensions.js +79 -53
  736. package/dist/record/extensions.js.map +1 -1
  737. package/dist/record/failure-condition.d.ts +14 -0
  738. package/dist/record/failure-condition.d.ts.map +1 -0
  739. package/dist/record/failure-condition.js +17 -0
  740. package/dist/record/failure-condition.js.map +1 -0
  741. package/dist/record/index.d.ts +5 -1
  742. package/dist/record/index.d.ts.map +1 -1
  743. package/dist/record/index.js +5 -1
  744. package/dist/record/index.js.map +1 -1
  745. package/dist/record/merge.d.ts +21 -0
  746. package/dist/record/merge.d.ts.map +1 -0
  747. package/dist/record/merge.js +147 -0
  748. package/dist/record/merge.js.map +1 -0
  749. package/dist/record/relations.d.ts +23 -0
  750. package/dist/record/relations.d.ts.map +1 -0
  751. package/dist/record/relations.js +164 -0
  752. package/dist/record/relations.js.map +1 -0
  753. package/dist/record/revision.d.ts +5 -0
  754. package/dist/record/revision.d.ts.map +1 -0
  755. package/dist/record/revision.js +11 -0
  756. package/dist/record/revision.js.map +1 -0
  757. package/dist/record/schema-coordination.client.d.ts +16 -0
  758. package/dist/record/schema-coordination.client.d.ts.map +1 -0
  759. package/dist/record/schema-coordination.client.js +59 -0
  760. package/dist/record/schema-coordination.client.js.map +1 -0
  761. package/dist/record/storage.record-delivery.reconcile.descriptor.d.ts +74 -0
  762. package/dist/record/storage.record-delivery.reconcile.descriptor.d.ts.map +1 -0
  763. package/dist/record/storage.record-delivery.reconcile.descriptor.js +54 -0
  764. package/dist/record/storage.record-delivery.reconcile.descriptor.js.map +1 -0
  765. package/dist/record/storage.record-delivery.reconcile.impl.d.ts +22 -0
  766. package/dist/record/storage.record-delivery.reconcile.impl.d.ts.map +1 -0
  767. package/dist/record/storage.record-delivery.reconcile.impl.js +52 -0
  768. package/dist/record/storage.record-delivery.reconcile.impl.js.map +1 -0
  769. package/dist/resolver/storage-backed-resolver.d.ts +12 -5
  770. package/dist/resolver/storage-backed-resolver.d.ts.map +1 -1
  771. package/dist/resolver/storage-backed-resolver.js +129 -48
  772. package/dist/resolver/storage-backed-resolver.js.map +1 -1
  773. package/dist/retention/capacity.d.ts +37 -0
  774. package/dist/retention/capacity.d.ts.map +1 -0
  775. package/dist/retention/capacity.js +71 -0
  776. package/dist/retention/capacity.js.map +1 -0
  777. package/dist/retention/catalog-chunks.d.ts +11 -0
  778. package/dist/retention/catalog-chunks.d.ts.map +1 -0
  779. package/dist/retention/catalog-chunks.js +62 -0
  780. package/dist/retention/catalog-chunks.js.map +1 -0
  781. package/dist/retention/reclaim.d.ts +59 -0
  782. package/dist/retention/reclaim.d.ts.map +1 -0
  783. package/dist/retention/reclaim.js +259 -0
  784. package/dist/retention/reclaim.js.map +1 -0
  785. package/dist/retention/roots.d.ts +141 -0
  786. package/dist/retention/roots.d.ts.map +1 -0
  787. package/dist/retention/roots.js +223 -0
  788. package/dist/retention/roots.js.map +1 -0
  789. package/dist/retention/scratch-custody.d.ts +40 -0
  790. package/dist/retention/scratch-custody.d.ts.map +1 -0
  791. package/dist/retention/scratch-custody.js +15 -0
  792. package/dist/retention/scratch-custody.js.map +1 -0
  793. package/dist/retention/startup-sweep.d.ts +57 -0
  794. package/dist/retention/startup-sweep.d.ts.map +1 -0
  795. package/dist/retention/startup-sweep.js +137 -0
  796. package/dist/retention/startup-sweep.js.map +1 -0
  797. package/dist/retention/storage.retention-sweep.descriptor.d.ts +82 -0
  798. package/dist/retention/storage.retention-sweep.descriptor.d.ts.map +1 -0
  799. package/dist/retention/storage.retention-sweep.descriptor.js +42 -0
  800. package/dist/retention/storage.retention-sweep.descriptor.js.map +1 -0
  801. package/dist/retention/storage.retention-sweep.impl.d.ts +22 -0
  802. package/dist/retention/storage.retention-sweep.impl.d.ts.map +1 -0
  803. package/dist/retention/storage.retention-sweep.impl.js +78 -0
  804. package/dist/retention/storage.retention-sweep.impl.js.map +1 -0
  805. package/dist/retention/storage.retention-sweep.schema.d.ts +3 -0
  806. package/dist/retention/storage.retention-sweep.schema.d.ts.map +1 -0
  807. package/dist/retention/storage.retention-sweep.schema.js +3 -0
  808. package/dist/retention/storage.retention-sweep.schema.js.map +1 -0
  809. package/dist/retention/sweep.d.ts +31 -0
  810. package/dist/retention/sweep.d.ts.map +1 -0
  811. package/dist/retention/sweep.js +190 -0
  812. package/dist/retention/sweep.js.map +1 -0
  813. package/dist/retention/tmp-scratch.d.ts +35 -0
  814. package/dist/retention/tmp-scratch.d.ts.map +1 -0
  815. package/dist/retention/tmp-scratch.js +149 -0
  816. package/dist/retention/tmp-scratch.js.map +1 -0
  817. package/dist/schemas/adapter-config.d.ts +42 -0
  818. package/dist/schemas/adapter-config.d.ts.map +1 -0
  819. package/dist/schemas/adapter-config.js +21 -0
  820. package/dist/schemas/adapter-config.js.map +1 -0
  821. package/dist/schemas/adapter-configuration-diagnostic.d.ts +35 -0
  822. package/dist/schemas/adapter-configuration-diagnostic.d.ts.map +1 -0
  823. package/dist/schemas/adapter-configuration-diagnostic.js +30 -0
  824. package/dist/schemas/adapter-configuration-diagnostic.js.map +1 -0
  825. package/dist/schemas/adapter-descriptor.d.ts +215 -5
  826. package/dist/schemas/adapter-descriptor.d.ts.map +1 -1
  827. package/dist/schemas/adapter-descriptor.js +158 -4
  828. package/dist/schemas/adapter-descriptor.js.map +1 -1
  829. package/dist/schemas/cloud-api.d.ts +99 -0
  830. package/dist/schemas/cloud-api.d.ts.map +1 -0
  831. package/dist/schemas/cloud-api.js +55 -0
  832. package/dist/schemas/cloud-api.js.map +1 -0
  833. package/dist/schemas/cloud-endpoints.d.ts +25 -0
  834. package/dist/schemas/cloud-endpoints.d.ts.map +1 -0
  835. package/dist/schemas/cloud-endpoints.js +38 -0
  836. package/dist/schemas/cloud-endpoints.js.map +1 -0
  837. package/dist/schemas/document-identity.d.ts +40 -0
  838. package/dist/schemas/document-identity.d.ts.map +1 -0
  839. package/dist/schemas/document-identity.js +21 -0
  840. package/dist/schemas/document-identity.js.map +1 -0
  841. package/dist/schemas/document-revision.d.ts +37 -0
  842. package/dist/schemas/document-revision.d.ts.map +1 -0
  843. package/dist/schemas/document-revision.js +14 -0
  844. package/dist/schemas/document-revision.js.map +1 -0
  845. package/dist/schemas/drizzle/config.d.ts +83 -0
  846. package/dist/schemas/drizzle/config.d.ts.map +1 -0
  847. package/dist/schemas/drizzle/config.js +55 -0
  848. package/dist/schemas/drizzle/config.js.map +1 -0
  849. package/dist/schemas/drizzle/connection.d.ts +41 -0
  850. package/dist/schemas/drizzle/connection.d.ts.map +1 -0
  851. package/dist/schemas/drizzle/connection.js +23 -0
  852. package/dist/schemas/drizzle/connection.js.map +1 -0
  853. package/dist/schemas/drizzle/journal-read.d.ts +11 -0
  854. package/dist/schemas/drizzle/journal-read.d.ts.map +1 -0
  855. package/dist/schemas/drizzle/journal-read.js +2 -0
  856. package/dist/schemas/drizzle/journal-read.js.map +1 -0
  857. package/dist/schemas/drizzle/persistence-config.d.ts +39 -0
  858. package/dist/schemas/drizzle/persistence-config.d.ts.map +1 -0
  859. package/dist/schemas/drizzle/persistence-config.js +71 -0
  860. package/dist/schemas/drizzle/persistence-config.js.map +1 -0
  861. package/dist/schemas/drizzle/persistence.d.ts +102 -0
  862. package/dist/schemas/drizzle/persistence.d.ts.map +1 -0
  863. package/dist/schemas/drizzle/persistence.js +28 -0
  864. package/dist/schemas/drizzle/persistence.js.map +1 -0
  865. package/dist/schemas/drizzle/ports.d.ts +39 -0
  866. package/dist/schemas/drizzle/ports.d.ts.map +1 -0
  867. package/dist/schemas/drizzle/ports.js +2 -0
  868. package/dist/schemas/drizzle/ports.js.map +1 -0
  869. package/dist/schemas/drizzle/providers.d.ts +64 -0
  870. package/dist/schemas/drizzle/providers.d.ts.map +1 -0
  871. package/dist/schemas/drizzle/providers.js +19 -0
  872. package/dist/schemas/drizzle/providers.js.map +1 -0
  873. package/dist/schemas/drizzle/raw-sql.d.ts +33 -0
  874. package/dist/schemas/drizzle/raw-sql.d.ts.map +1 -0
  875. package/dist/schemas/drizzle/raw-sql.js +34 -0
  876. package/dist/schemas/drizzle/raw-sql.js.map +1 -0
  877. package/dist/schemas/explorer/capability.d.ts +41 -0
  878. package/dist/schemas/explorer/capability.d.ts.map +1 -0
  879. package/dist/schemas/explorer/capability.js +14 -0
  880. package/dist/schemas/explorer/capability.js.map +1 -0
  881. package/dist/schemas/explorer/overview.d.ts +41 -0
  882. package/dist/schemas/explorer/overview.d.ts.map +1 -0
  883. package/dist/schemas/explorer/overview.js +45 -0
  884. package/dist/schemas/explorer/overview.js.map +1 -0
  885. package/dist/schemas/explorer/refs.d.ts +38 -0
  886. package/dist/schemas/explorer/refs.d.ts.map +1 -0
  887. package/dist/schemas/explorer/refs.js +21 -0
  888. package/dist/schemas/explorer/refs.js.map +1 -0
  889. package/dist/schemas/explorer/rpc.d.ts +211 -0
  890. package/dist/schemas/explorer/rpc.d.ts.map +1 -0
  891. package/dist/schemas/explorer/rpc.js +103 -0
  892. package/dist/schemas/explorer/rpc.js.map +1 -0
  893. package/dist/schemas/explorer/views.d.ts +99 -0
  894. package/dist/schemas/explorer/views.d.ts.map +1 -0
  895. package/dist/schemas/explorer/views.js +84 -0
  896. package/dist/schemas/explorer/views.js.map +1 -0
  897. package/dist/schemas/index.d.ts +16 -8
  898. package/dist/schemas/index.d.ts.map +1 -1
  899. package/dist/schemas/index.js +12 -4
  900. package/dist/schemas/index.js.map +1 -1
  901. package/dist/schemas/localdb/inputs.d.ts +80 -0
  902. package/dist/schemas/localdb/inputs.d.ts.map +1 -0
  903. package/dist/schemas/localdb/inputs.js +99 -0
  904. package/dist/schemas/localdb/inputs.js.map +1 -0
  905. package/dist/schemas/localdb/query.d.ts +28 -0
  906. package/dist/schemas/localdb/query.d.ts.map +1 -0
  907. package/dist/schemas/localdb/query.js +27 -0
  908. package/dist/schemas/localdb/query.js.map +1 -0
  909. package/dist/schemas/manifest-descriptor.d.ts +15 -5
  910. package/dist/schemas/manifest-descriptor.d.ts.map +1 -1
  911. package/dist/schemas/manifest-descriptor.js +2 -2
  912. package/dist/schemas/manifest-descriptor.js.map +1 -1
  913. package/dist/schemas/memory-store.d.ts +8 -0
  914. package/dist/schemas/memory-store.d.ts.map +1 -0
  915. package/dist/schemas/memory-store.js +7 -0
  916. package/dist/schemas/memory-store.js.map +1 -0
  917. package/dist/schemas/migration-format.d.ts +50 -0
  918. package/dist/schemas/migration-format.d.ts.map +1 -0
  919. package/dist/schemas/migration-format.js +17 -0
  920. package/dist/schemas/migration-format.js.map +1 -0
  921. package/dist/schemas/policy/overrides.d.ts +41 -0
  922. package/dist/schemas/policy/overrides.d.ts.map +1 -0
  923. package/dist/schemas/policy/overrides.js +14 -0
  924. package/dist/schemas/policy/overrides.js.map +1 -0
  925. package/dist/schemas/policy/persistence.d.ts +62 -0
  926. package/dist/schemas/policy/persistence.d.ts.map +1 -0
  927. package/dist/schemas/policy/persistence.js +24 -0
  928. package/dist/schemas/policy/persistence.js.map +1 -0
  929. package/dist/schemas/record/atomic.d.ts +41 -0
  930. package/dist/schemas/record/atomic.d.ts.map +1 -0
  931. package/dist/schemas/record/atomic.js +12 -0
  932. package/dist/schemas/record/atomic.js.map +1 -0
  933. package/dist/schemas/record/batch.d.ts +53 -0
  934. package/dist/schemas/record/batch.d.ts.map +1 -0
  935. package/dist/schemas/record/batch.js +36 -0
  936. package/dist/schemas/record/batch.js.map +1 -0
  937. package/dist/schemas/record/columns.d.ts +44 -0
  938. package/dist/schemas/record/columns.d.ts.map +1 -0
  939. package/dist/schemas/record/columns.js +25 -0
  940. package/dist/schemas/record/columns.js.map +1 -0
  941. package/dist/schemas/record/delivery.d.ts +54 -0
  942. package/dist/schemas/record/delivery.d.ts.map +1 -0
  943. package/dist/schemas/record/delivery.js +23 -0
  944. package/dist/schemas/record/delivery.js.map +1 -0
  945. package/dist/schemas/record/descriptor.d.ts +153 -51
  946. package/dist/schemas/record/descriptor.d.ts.map +1 -1
  947. package/dist/schemas/record/descriptor.js +61 -11
  948. package/dist/schemas/record/descriptor.js.map +1 -1
  949. package/dist/schemas/record/index.d.ts +8 -2
  950. package/dist/schemas/record/index.d.ts.map +1 -1
  951. package/dist/schemas/record/index.js +5 -1
  952. package/dist/schemas/record/index.js.map +1 -1
  953. package/dist/schemas/record/query-values.d.ts +62 -0
  954. package/dist/schemas/record/query-values.d.ts.map +1 -0
  955. package/dist/schemas/record/query-values.js +33 -0
  956. package/dist/schemas/record/query-values.js.map +1 -0
  957. package/dist/schemas/record/relation.d.ts +1 -1
  958. package/dist/schemas/record/relation.d.ts.map +1 -1
  959. package/dist/schemas/record/relation.js +8 -4
  960. package/dist/schemas/record/relation.js.map +1 -1
  961. package/dist/schemas/record/revision.d.ts +92 -0
  962. package/dist/schemas/record/revision.d.ts.map +1 -0
  963. package/dist/schemas/record/revision.js +39 -0
  964. package/dist/schemas/record/revision.js.map +1 -0
  965. package/dist/schemas/retention/jobs.d.ts +103 -0
  966. package/dist/schemas/retention/jobs.d.ts.map +1 -0
  967. package/dist/schemas/retention/jobs.js +72 -0
  968. package/dist/schemas/retention/jobs.js.map +1 -0
  969. package/dist/schemas/retention/results.d.ts +153 -0
  970. package/dist/schemas/retention/results.d.ts.map +1 -0
  971. package/dist/schemas/retention/results.js +84 -0
  972. package/dist/schemas/retention/results.js.map +1 -0
  973. package/dist/schemas/schema/descriptor.d.ts +14 -4
  974. package/dist/schemas/schema/descriptor.d.ts.map +1 -1
  975. package/dist/schemas/schema/descriptor.js +3 -3
  976. package/dist/schemas/schema/descriptor.js.map +1 -1
  977. package/dist/schemas/schema/index.d.ts +1 -1
  978. package/dist/schemas/schema/index.d.ts.map +1 -1
  979. package/dist/schemas/schema/index.js +1 -1
  980. package/dist/schemas/schema/index.js.map +1 -1
  981. package/dist/schemas/storage-provider.d.ts +79 -0
  982. package/dist/schemas/storage-provider.d.ts.map +1 -0
  983. package/dist/schemas/storage-provider.js +54 -0
  984. package/dist/schemas/storage-provider.js.map +1 -0
  985. package/dist/schemas/storage-ref.d.ts +39 -0
  986. package/dist/schemas/storage-ref.d.ts.map +1 -0
  987. package/dist/schemas/storage-ref.js +64 -0
  988. package/dist/schemas/storage-ref.js.map +1 -0
  989. package/dist/schemas/sync/command.d.ts +15 -0
  990. package/dist/schemas/sync/command.d.ts.map +1 -0
  991. package/dist/schemas/sync/command.js +10 -0
  992. package/dist/schemas/sync/command.js.map +1 -0
  993. package/dist/schemas/sync/crud.d.ts +26 -0
  994. package/dist/schemas/sync/crud.d.ts.map +1 -0
  995. package/dist/schemas/sync/crud.js +16 -0
  996. package/dist/schemas/sync/crud.js.map +1 -0
  997. package/dist/schemas/sync/document.d.ts +4 -0
  998. package/dist/schemas/sync/document.d.ts.map +1 -0
  999. package/dist/schemas/sync/document.js +4 -0
  1000. package/dist/schemas/sync/document.js.map +1 -0
  1001. package/dist/schemas/sync/local-first.d.ts +26 -0
  1002. package/dist/schemas/sync/local-first.d.ts.map +1 -0
  1003. package/dist/schemas/sync/local-first.js +14 -0
  1004. package/dist/schemas/sync/local-first.js.map +1 -0
  1005. package/dist/schemas/sync/results.d.ts +61 -0
  1006. package/dist/schemas/sync/results.d.ts.map +1 -0
  1007. package/dist/schemas/sync/results.js +47 -0
  1008. package/dist/schemas/sync/results.js.map +1 -0
  1009. package/dist/schemas/sync-bridge-descriptor.d.ts +34 -15
  1010. package/dist/schemas/sync-bridge-descriptor.d.ts.map +1 -1
  1011. package/dist/schemas/sync-bridge-descriptor.js +9 -2
  1012. package/dist/schemas/sync-bridge-descriptor.js.map +1 -1
  1013. package/dist/schemas/sync-memory.d.ts +7 -0
  1014. package/dist/schemas/sync-memory.d.ts.map +1 -0
  1015. package/dist/schemas/sync-memory.js +4 -0
  1016. package/dist/schemas/sync-memory.js.map +1 -0
  1017. package/dist/schemas/tools/views.d.ts +83 -0
  1018. package/dist/schemas/tools/views.d.ts.map +1 -0
  1019. package/dist/schemas/tools/views.js +57 -0
  1020. package/dist/schemas/tools/views.js.map +1 -0
  1021. package/dist/schemas/tools/widgets.d.ts +41 -0
  1022. package/dist/schemas/tools/widgets.d.ts.map +1 -0
  1023. package/dist/schemas/tools/widgets.js +13 -0
  1024. package/dist/schemas/tools/widgets.js.map +1 -0
  1025. package/dist/schemas/transform.d.ts +32 -0
  1026. package/dist/schemas/transform.d.ts.map +1 -0
  1027. package/dist/schemas/transform.js +18 -0
  1028. package/dist/schemas/transform.js.map +1 -0
  1029. package/dist/schemas/zod-schema-value.d.ts +8 -0
  1030. package/dist/schemas/zod-schema-value.d.ts.map +1 -0
  1031. package/dist/schemas/zod-schema-value.js +8 -0
  1032. package/dist/schemas/zod-schema-value.js.map +1 -0
  1033. package/dist/server/manifest-route.d.ts +18 -10
  1034. package/dist/server/manifest-route.d.ts.map +1 -1
  1035. package/dist/server/manifest-route.js +88 -90
  1036. package/dist/server/manifest-route.js.map +1 -1
  1037. package/dist/storage-explorer.consumer.plugin.d.ts +10 -0
  1038. package/dist/storage-explorer.consumer.plugin.d.ts.map +1 -0
  1039. package/dist/storage-explorer.consumer.plugin.js +24 -0
  1040. package/dist/storage-explorer.consumer.plugin.js.map +1 -0
  1041. package/dist/storage-policy.context.descriptor.js +1 -1
  1042. package/dist/storage-policy.context.descriptor.js.map +1 -1
  1043. package/dist/storage-policy.context.server.d.ts +7 -9
  1044. package/dist/storage-policy.context.server.d.ts.map +1 -1
  1045. package/dist/storage-policy.context.server.js +7 -20
  1046. package/dist/storage-policy.context.server.js.map +1 -1
  1047. package/dist/storage.consumer.plugin.d.ts +3 -0
  1048. package/dist/storage.consumer.plugin.d.ts.map +1 -0
  1049. package/dist/storage.consumer.plugin.js +15 -0
  1050. package/dist/storage.consumer.plugin.js.map +1 -0
  1051. package/dist/storage.node.plugin.d.ts +3 -0
  1052. package/dist/storage.node.plugin.d.ts.map +1 -0
  1053. package/dist/storage.node.plugin.js +12 -0
  1054. package/dist/storage.node.plugin.js.map +1 -0
  1055. package/dist/storage.plugin.d.ts +10 -16
  1056. package/dist/storage.plugin.d.ts.map +1 -1
  1057. package/dist/storage.plugin.js +150 -91
  1058. package/dist/storage.plugin.js.map +1 -1
  1059. package/dist/svelte/components/ConnectStorageProviderDialog.svelte +345 -0
  1060. package/dist/svelte/components/ConnectStorageProviderDialog.svelte.d.ts +17 -0
  1061. package/dist/svelte/components/ConnectStorageProviderDialog.svelte.d.ts.map +1 -0
  1062. package/dist/svelte/components/PlatformStorageExplorer.svelte +57 -0
  1063. package/dist/svelte/components/PlatformStorageExplorer.svelte.d.ts +23 -0
  1064. package/dist/svelte/components/PlatformStorageExplorer.svelte.d.ts.map +1 -0
  1065. package/dist/svelte/components/StorageBrowser.svelte +436 -0
  1066. package/dist/svelte/components/StorageBrowser.svelte.d.ts +51 -0
  1067. package/dist/svelte/components/StorageBrowser.svelte.d.ts.map +1 -0
  1068. package/dist/svelte/components/StorageBrowserPage.svelte +71 -0
  1069. package/dist/svelte/components/StorageBrowserPage.svelte.d.ts +23 -0
  1070. package/dist/svelte/components/StorageBrowserPage.svelte.d.ts.map +1 -0
  1071. package/dist/svelte/components/StorageCollectionManager.svelte +193 -0
  1072. package/dist/svelte/components/StorageCollectionManager.svelte.d.ts +14 -0
  1073. package/dist/svelte/components/StorageCollectionManager.svelte.d.ts.map +1 -0
  1074. package/dist/svelte/components/StorageDetailsPane.svelte +160 -0
  1075. package/dist/svelte/components/StorageDetailsPane.svelte.d.ts +11 -0
  1076. package/dist/svelte/components/StorageDetailsPane.svelte.d.ts.map +1 -0
  1077. package/dist/svelte/components/StorageDropzone.svelte +76 -0
  1078. package/dist/svelte/components/StorageDropzone.svelte.d.ts +9 -0
  1079. package/dist/svelte/components/StorageDropzone.svelte.d.ts.map +1 -0
  1080. package/dist/svelte/components/StorageExplorer.svelte +954 -0
  1081. package/dist/svelte/components/StorageExplorer.svelte.d.ts +48 -0
  1082. package/dist/svelte/components/StorageExplorer.svelte.d.ts.map +1 -0
  1083. package/dist/svelte/components/StorageFileBrowser.svelte +201 -0
  1084. package/dist/svelte/components/StorageFileBrowser.svelte.d.ts +18 -0
  1085. package/dist/svelte/components/StorageFileBrowser.svelte.d.ts.map +1 -0
  1086. package/dist/svelte/components/StorageFileCard.svelte +162 -0
  1087. package/dist/svelte/components/StorageFileCard.svelte.d.ts +10 -0
  1088. package/dist/svelte/components/StorageFileCard.svelte.d.ts.map +1 -0
  1089. package/dist/svelte/components/StoragePathBar.svelte +104 -0
  1090. package/dist/svelte/components/StoragePathBar.svelte.d.ts +19 -0
  1091. package/dist/svelte/components/StoragePathBar.svelte.d.ts.map +1 -0
  1092. package/dist/svelte/components/StorageProviderSelect.svelte +163 -0
  1093. package/dist/svelte/components/StorageProviderSelect.svelte.d.ts +20 -0
  1094. package/dist/svelte/components/StorageProviderSelect.svelte.d.ts.map +1 -0
  1095. package/dist/svelte/components/StorageProviderSidebar.svelte +187 -0
  1096. package/dist/svelte/components/StorageProviderSidebar.svelte.d.ts +30 -0
  1097. package/dist/svelte/components/StorageProviderSidebar.svelte.d.ts.map +1 -0
  1098. package/dist/svelte/components/StorageReadinessNotice.svelte +181 -0
  1099. package/dist/svelte/components/StorageReadinessNotice.svelte.d.ts +32 -0
  1100. package/dist/svelte/components/StorageReadinessNotice.svelte.d.ts.map +1 -0
  1101. package/dist/svelte/components/StorageSearchBox.svelte +106 -0
  1102. package/dist/svelte/components/StorageSearchBox.svelte.d.ts +19 -0
  1103. package/dist/svelte/components/StorageSearchBox.svelte.d.ts.map +1 -0
  1104. package/dist/svelte/components/StorageToolbar.svelte +160 -0
  1105. package/dist/svelte/components/StorageToolbar.svelte.d.ts +26 -0
  1106. package/dist/svelte/components/StorageToolbar.svelte.d.ts.map +1 -0
  1107. package/dist/svelte/components/StorageUploadChip.svelte +116 -0
  1108. package/dist/svelte/components/StorageUploadChip.svelte.d.ts +13 -0
  1109. package/dist/svelte/components/StorageUploadChip.svelte.d.ts.map +1 -0
  1110. package/dist/svelte/components/index.d.ts +14 -0
  1111. package/dist/svelte/components/index.d.ts.map +1 -0
  1112. package/dist/svelte/components/index.js +12 -0
  1113. package/dist/svelte/components/storage-media.d.ts +14 -0
  1114. package/dist/svelte/components/storage-media.d.ts.map +1 -0
  1115. package/dist/svelte/components/storage-media.js +13 -0
  1116. package/dist/svelte/index.d.ts +14 -0
  1117. package/dist/svelte/index.d.ts.map +1 -0
  1118. package/dist/svelte/index.js +13 -0
  1119. package/dist/svelte/note-store-failure.d.ts +15 -0
  1120. package/dist/svelte/note-store-failure.d.ts.map +1 -0
  1121. package/dist/svelte/note-store-failure.js +21 -0
  1122. package/dist/svelte/platform-storage-port.d.ts +86 -0
  1123. package/dist/svelte/platform-storage-port.d.ts.map +1 -0
  1124. package/dist/svelte/platform-storage-port.js +71 -0
  1125. package/dist/svelte/storage-browse-runtime.d.ts +65 -0
  1126. package/dist/svelte/storage-browse-runtime.d.ts.map +1 -0
  1127. package/dist/svelte/storage-browse-runtime.js +68 -0
  1128. package/dist/svelte/storage-connection.d.ts +23 -0
  1129. package/dist/svelte/storage-connection.d.ts.map +1 -0
  1130. package/dist/svelte/storage-connection.js +40 -0
  1131. package/dist/svelte/storage-display.d.ts +6 -0
  1132. package/dist/svelte/storage-display.d.ts.map +1 -0
  1133. package/dist/svelte/storage-display.js +58 -0
  1134. package/dist/svelte/storage-explorer-runtime.d.ts +120 -0
  1135. package/dist/svelte/storage-explorer-runtime.d.ts.map +1 -0
  1136. package/dist/svelte/storage-explorer-runtime.js +251 -0
  1137. package/dist/svelte/storage-explorer.load.d.ts +22 -0
  1138. package/dist/svelte/storage-explorer.load.d.ts.map +1 -0
  1139. package/dist/svelte/storage-explorer.load.js +37 -0
  1140. package/dist/svelte/storage-refs-runtime.d.ts +36 -0
  1141. package/dist/svelte/storage-refs-runtime.d.ts.map +1 -0
  1142. package/dist/svelte/storage-refs-runtime.js +47 -0
  1143. package/dist/svelte/storage-runtime.d.ts +30 -0
  1144. package/dist/svelte/storage-runtime.d.ts.map +1 -0
  1145. package/dist/svelte/storage-runtime.js +39 -0
  1146. package/dist/svelte/storage-svelte.plugin.d.ts +40 -0
  1147. package/dist/svelte/storage-svelte.plugin.d.ts.map +1 -0
  1148. package/dist/svelte/storage-svelte.plugin.js +50 -0
  1149. package/dist/svelte/storage-upload-runtime.d.ts +8 -0
  1150. package/dist/svelte/storage-upload-runtime.d.ts.map +1 -0
  1151. package/dist/svelte/storage-upload-runtime.js +12 -0
  1152. package/dist/svelte/stores/index.d.ts +4 -0
  1153. package/dist/svelte/stores/index.d.ts.map +1 -0
  1154. package/dist/svelte/stores/index.js +3 -0
  1155. package/dist/svelte/stores/storage-browse.svelte.d.ts +70 -0
  1156. package/dist/svelte/stores/storage-browse.svelte.d.ts.map +1 -0
  1157. package/dist/svelte/stores/storage-browse.svelte.js +133 -0
  1158. package/dist/svelte/stores/storage-providers.svelte.d.ts +32 -0
  1159. package/dist/svelte/stores/storage-providers.svelte.d.ts.map +1 -0
  1160. package/dist/svelte/stores/storage-providers.svelte.js +73 -0
  1161. package/dist/svelte/stores/storage-refs.svelte.d.ts +57 -0
  1162. package/dist/svelte/stores/storage-refs.svelte.d.ts.map +1 -0
  1163. package/dist/svelte/stores/storage-refs.svelte.js +90 -0
  1164. package/dist/svelte/styles.css +14 -0
  1165. package/dist/svelte/test-assets.d.ts +8 -0
  1166. package/dist/svelte/tool-widgets/StorageBrowseToolCard.svelte +109 -0
  1167. package/dist/svelte/tool-widgets/StorageBrowseToolCard.svelte.d.ts +9 -0
  1168. package/dist/svelte/tool-widgets/StorageBrowseToolCard.svelte.d.ts.map +1 -0
  1169. package/dist/svelte/tool-widgets/StorageCopyToolCard.svelte +87 -0
  1170. package/dist/svelte/tool-widgets/StorageCopyToolCard.svelte.d.ts +9 -0
  1171. package/dist/svelte/tool-widgets/StorageCopyToolCard.svelte.d.ts.map +1 -0
  1172. package/dist/svelte/tool-widgets/StorageSearchToolCard.svelte +94 -0
  1173. package/dist/svelte/tool-widgets/StorageSearchToolCard.svelte.d.ts +9 -0
  1174. package/dist/svelte/tool-widgets/StorageSearchToolCard.svelte.d.ts.map +1 -0
  1175. package/dist/svelte/tool-widgets/format.d.ts +15 -0
  1176. package/dist/svelte/tool-widgets/format.d.ts.map +1 -0
  1177. package/dist/svelte/tool-widgets/format.js +32 -0
  1178. package/dist/svelte/tool-widgets/index.d.ts +2 -0
  1179. package/dist/svelte/tool-widgets/index.d.ts.map +1 -0
  1180. package/dist/svelte/tool-widgets/index.js +12 -0
  1181. package/dist/svelte/tool-widgets/runtime.d.ts +9 -0
  1182. package/dist/svelte/tool-widgets/runtime.d.ts.map +1 -0
  1183. package/dist/svelte/tool-widgets/runtime.js +42 -0
  1184. package/dist/sync/cloud-document.d.ts +24 -0
  1185. package/dist/sync/cloud-document.d.ts.map +1 -0
  1186. package/dist/sync/cloud-document.js +47 -0
  1187. package/dist/sync/cloud-document.js.map +1 -0
  1188. package/dist/sync/cloud-pull.d.ts +9 -3
  1189. package/dist/sync/cloud-pull.d.ts.map +1 -1
  1190. package/dist/sync/cloud-pull.js +85 -86
  1191. package/dist/sync/cloud-pull.js.map +1 -1
  1192. package/dist/sync/cloud-record-crud-config.d.ts +7 -17
  1193. package/dist/sync/cloud-record-crud-config.d.ts.map +1 -1
  1194. package/dist/sync/cloud-record-crud-config.js +17 -9
  1195. package/dist/sync/cloud-record-crud-config.js.map +1 -1
  1196. package/dist/sync/local-first.d.ts +61 -0
  1197. package/dist/sync/local-first.d.ts.map +1 -0
  1198. package/dist/sync/local-first.js +121 -0
  1199. package/dist/sync/local-first.js.map +1 -0
  1200. package/dist/sync/record-sync-bridge.d.ts +35 -3
  1201. package/dist/sync/record-sync-bridge.d.ts.map +1 -1
  1202. package/dist/sync/record-sync-bridge.js +50 -46
  1203. package/dist/sync/record-sync-bridge.js.map +1 -1
  1204. package/dist/sync/resolve-sync-target.d.ts +16 -17
  1205. package/dist/sync/resolve-sync-target.d.ts.map +1 -1
  1206. package/dist/sync/resolve-sync-target.js +18 -43
  1207. package/dist/sync/resolve-sync-target.js.map +1 -1
  1208. package/dist/sync/runtime-sync-state.d.ts +18 -0
  1209. package/dist/sync/runtime-sync-state.d.ts.map +1 -0
  1210. package/dist/sync/runtime-sync-state.js +12 -0
  1211. package/dist/sync/runtime-sync-state.js.map +1 -0
  1212. package/dist/sync/schemas/cloud-pull.d.ts +9 -5
  1213. package/dist/sync/schemas/cloud-pull.d.ts.map +1 -1
  1214. package/dist/sync/schemas/index.d.ts +3 -3
  1215. package/dist/sync/schemas/index.d.ts.map +1 -1
  1216. package/dist/sync/schemas/index.js +3 -3
  1217. package/dist/sync/schemas/index.js.map +1 -1
  1218. package/dist/sync/schemas/sync-helpers.d.ts +4 -2
  1219. package/dist/sync/schemas/sync-helpers.d.ts.map +1 -1
  1220. package/dist/sync/schemas/sync-state.d.ts +7 -5
  1221. package/dist/sync/schemas/sync-state.d.ts.map +1 -1
  1222. package/dist/sync/schemas/sync-state.js +1 -0
  1223. package/dist/sync/schemas/sync-state.js.map +1 -1
  1224. package/dist/sync/sync-bridge.d.ts +39 -31
  1225. package/dist/sync/sync-bridge.d.ts.map +1 -1
  1226. package/dist/sync/sync-bridge.js +427 -196
  1227. package/dist/sync/sync-bridge.js.map +1 -1
  1228. package/dist/sync/sync-manifest.d.ts +7 -19
  1229. package/dist/sync/sync-manifest.d.ts.map +1 -1
  1230. package/dist/sync/sync-manifest.js +125 -84
  1231. package/dist/sync/sync-manifest.js.map +1 -1
  1232. package/dist/sync/sync-state.browser.d.ts +37 -0
  1233. package/dist/sync/sync-state.browser.d.ts.map +1 -0
  1234. package/dist/sync/sync-state.browser.js +136 -0
  1235. package/dist/sync/sync-state.browser.js.map +1 -0
  1236. package/dist/sync/sync-state.client.d.ts +71 -0
  1237. package/dist/sync/sync-state.client.d.ts.map +1 -0
  1238. package/dist/sync/sync-state.client.js +78 -0
  1239. package/dist/sync/sync-state.client.js.map +1 -0
  1240. package/dist/sync/sync-state.consumer.d.ts +44 -17
  1241. package/dist/sync/sync-state.consumer.d.ts.map +1 -1
  1242. package/dist/sync/sync-state.consumer.js +161 -87
  1243. package/dist/sync/sync-state.consumer.js.map +1 -1
  1244. package/dist/sync/sync-state.core.d.ts +40 -0
  1245. package/dist/sync/sync-state.core.d.ts.map +1 -0
  1246. package/dist/sync/sync-state.core.js +130 -0
  1247. package/dist/sync/sync-state.core.js.map +1 -0
  1248. package/dist/sync/sync-state.descriptor.d.ts +39 -0
  1249. package/dist/sync/sync-state.descriptor.d.ts.map +1 -0
  1250. package/dist/sync/sync-state.descriptor.js +23 -0
  1251. package/dist/sync/sync-state.descriptor.js.map +1 -0
  1252. package/dist/testing.d.ts +31 -0
  1253. package/dist/testing.d.ts.map +1 -0
  1254. package/dist/testing.js +82 -0
  1255. package/dist/testing.js.map +1 -0
  1256. package/dist/tools/descriptors.d.ts +312 -0
  1257. package/dist/tools/descriptors.d.ts.map +1 -0
  1258. package/dist/tools/descriptors.js +98 -0
  1259. package/dist/tools/descriptors.js.map +1 -0
  1260. package/dist/tools/impl.d.ts +72 -0
  1261. package/dist/tools/impl.d.ts.map +1 -0
  1262. package/dist/tools/impl.js +572 -0
  1263. package/dist/tools/impl.js.map +1 -0
  1264. package/dist/tools/index.d.ts +3 -0
  1265. package/dist/tools/index.d.ts.map +1 -0
  1266. package/dist/tools/index.js +3 -0
  1267. package/dist/tools/index.js.map +1 -0
  1268. package/dist/tools/schemas.d.ts +136 -0
  1269. package/dist/tools/schemas.d.ts.map +1 -0
  1270. package/dist/tools/schemas.js +113 -0
  1271. package/dist/tools/schemas.js.map +1 -0
  1272. package/dist/tools/widget-views.d.ts +9 -0
  1273. package/dist/tools/widget-views.d.ts.map +1 -0
  1274. package/dist/tools/widget-views.js +119 -0
  1275. package/dist/tools/widget-views.js.map +1 -0
  1276. package/dist/transform/index.d.ts +7 -18
  1277. package/dist/transform/index.d.ts.map +1 -1
  1278. package/dist/transform/index.js +16 -14
  1279. package/dist/transform/index.js.map +1 -1
  1280. package/dist/transform/transform-descriptor.d.ts +1 -21
  1281. package/dist/transform/transform-descriptor.d.ts.map +1 -1
  1282. package/dist/transform/transform-descriptor.js +1 -29
  1283. package/dist/transform/transform-descriptor.js.map +1 -1
  1284. package/dist/tui/plugin.d.ts +26 -0
  1285. package/dist/tui/plugin.d.ts.map +1 -0
  1286. package/dist/tui/plugin.js +107 -0
  1287. package/dist/tui/plugin.js.map +1 -0
  1288. package/dist/tui/storage-explorer.descriptor.d.ts +25 -4
  1289. package/dist/tui/storage-explorer.descriptor.d.ts.map +1 -1
  1290. package/dist/tui/storage-explorer.descriptor.js +7 -3
  1291. package/dist/tui/storage-explorer.descriptor.js.map +1 -1
  1292. package/dist/tui/storage-explorer.impl.consumer.d.ts +23 -6
  1293. package/dist/tui/storage-explorer.impl.consumer.d.ts.map +1 -1
  1294. package/dist/tui/storage-explorer.impl.consumer.js +231 -8
  1295. package/dist/tui/storage-explorer.impl.consumer.js.map +1 -1
  1296. package/dist/tui/storage-open.descriptor.d.ts +16 -3
  1297. package/dist/tui/storage-open.descriptor.d.ts.map +1 -1
  1298. package/dist/tui/storage-open.descriptor.js +5 -1
  1299. package/dist/tui/storage-open.descriptor.js.map +1 -1
  1300. package/dist/tui/storage-open.impl.consumer.d.ts +6 -1
  1301. package/dist/tui/storage-open.impl.consumer.d.ts.map +1 -1
  1302. package/dist/tui/storage-open.impl.consumer.js.map +1 -1
  1303. package/dist/tui/storage-signed-out-ui.d.ts +21 -0
  1304. package/dist/tui/storage-signed-out-ui.d.ts.map +1 -0
  1305. package/dist/tui/storage-signed-out-ui.js +34 -0
  1306. package/dist/tui/storage-signed-out-ui.js.map +1 -0
  1307. package/dist/tui/storage.tui-state.descriptor.d.ts +61 -0
  1308. package/dist/tui/storage.tui-state.descriptor.d.ts.map +1 -0
  1309. package/dist/tui/storage.tui-state.descriptor.js +52 -0
  1310. package/dist/tui/storage.tui-state.descriptor.js.map +1 -0
  1311. package/dist/tui/storage.tui-state.impl.consumer.d.ts +29 -0
  1312. package/dist/tui/storage.tui-state.impl.consumer.d.ts.map +1 -0
  1313. package/dist/tui/storage.tui-state.impl.consumer.js +207 -0
  1314. package/dist/tui/storage.tui-state.impl.consumer.js.map +1 -0
  1315. package/dist/tui/storage.tui-state.key.d.ts +27 -0
  1316. package/dist/tui/storage.tui-state.key.d.ts.map +1 -0
  1317. package/dist/tui/storage.tui-state.key.js +12 -0
  1318. package/dist/tui/storage.tui-state.key.js.map +1 -0
  1319. package/dist/tui/tool-widgets/StorageBrowseToolWidget.impl.consumer.d.ts +14 -0
  1320. package/dist/tui/tool-widgets/StorageBrowseToolWidget.impl.consumer.d.ts.map +1 -0
  1321. package/dist/tui/tool-widgets/StorageBrowseToolWidget.impl.consumer.js +28 -0
  1322. package/dist/tui/tool-widgets/StorageBrowseToolWidget.impl.consumer.js.map +1 -0
  1323. package/dist/tui/tool-widgets/StorageCopyToolWidget.impl.consumer.d.ts +14 -0
  1324. package/dist/tui/tool-widgets/StorageCopyToolWidget.impl.consumer.d.ts.map +1 -0
  1325. package/dist/tui/tool-widgets/StorageCopyToolWidget.impl.consumer.js +21 -0
  1326. package/dist/tui/tool-widgets/StorageCopyToolWidget.impl.consumer.js.map +1 -0
  1327. package/dist/tui/tool-widgets/StorageSearchToolWidget.impl.consumer.d.ts +14 -0
  1328. package/dist/tui/tool-widgets/StorageSearchToolWidget.impl.consumer.d.ts.map +1 -0
  1329. package/dist/tui/tool-widgets/StorageSearchToolWidget.impl.consumer.js +22 -0
  1330. package/dist/tui/tool-widgets/StorageSearchToolWidget.impl.consumer.js.map +1 -0
  1331. package/dist/tui/tool-widgets/format.d.ts +19 -0
  1332. package/dist/tui/tool-widgets/format.d.ts.map +1 -0
  1333. package/dist/tui/tool-widgets/format.js +53 -0
  1334. package/dist/tui/tool-widgets/format.js.map +1 -0
  1335. package/dist/tui/tool-widgets/storage.browse.tool-widget.descriptor.d.ts +14 -0
  1336. package/dist/tui/tool-widgets/storage.browse.tool-widget.descriptor.d.ts.map +1 -0
  1337. package/dist/tui/tool-widgets/storage.browse.tool-widget.descriptor.js +14 -0
  1338. package/dist/tui/tool-widgets/storage.browse.tool-widget.descriptor.js.map +1 -0
  1339. package/dist/tui/tool-widgets/storage.copy.tool-widget.descriptor.d.ts +14 -0
  1340. package/dist/tui/tool-widgets/storage.copy.tool-widget.descriptor.d.ts.map +1 -0
  1341. package/dist/tui/tool-widgets/storage.copy.tool-widget.descriptor.js +14 -0
  1342. package/dist/tui/tool-widgets/storage.copy.tool-widget.descriptor.js.map +1 -0
  1343. package/dist/tui/tool-widgets/storage.search.tool-widget.descriptor.d.ts +14 -0
  1344. package/dist/tui/tool-widgets/storage.search.tool-widget.descriptor.d.ts.map +1 -0
  1345. package/dist/tui/tool-widgets/storage.search.tool-widget.descriptor.js +14 -0
  1346. package/dist/tui/tool-widgets/storage.search.tool-widget.descriptor.js.map +1 -0
  1347. package/dist/utils/caller-bearer.d.ts +13 -0
  1348. package/dist/utils/caller-bearer.d.ts.map +1 -0
  1349. package/dist/utils/caller-bearer.js +17 -0
  1350. package/dist/utils/caller-bearer.js.map +1 -0
  1351. package/dist/utils/error-context.d.ts +9 -1
  1352. package/dist/utils/error-context.d.ts.map +1 -1
  1353. package/dist/utils/error-context.js +7 -14
  1354. package/dist/utils/error-context.js.map +1 -1
  1355. package/dist/utils/failure-of.d.ts +20 -0
  1356. package/dist/utils/failure-of.d.ts.map +1 -0
  1357. package/dist/utils/failure-of.js +35 -0
  1358. package/dist/utils/failure-of.js.map +1 -0
  1359. package/dist/workers-port.d.ts +37 -0
  1360. package/dist/workers-port.d.ts.map +1 -0
  1361. package/dist/workers-port.js +60 -0
  1362. package/dist/workers-port.js.map +1 -0
  1363. package/package.json +641 -144
  1364. package/dist/adapters/index.d.ts +0 -14
  1365. package/dist/adapters/index.d.ts.map +0 -1
  1366. package/dist/adapters/index.js +0 -15
  1367. package/dist/adapters/index.js.map +0 -1
  1368. package/dist/cli/context.consumer.d.ts +0 -26
  1369. package/dist/cli/context.consumer.d.ts.map +0 -1
  1370. package/dist/cli/context.consumer.js +0 -164
  1371. package/dist/cli/context.consumer.js.map +0 -1
  1372. package/dist/docs/adapters.docs.descriptor.d.ts +0 -4
  1373. package/dist/docs/adapters.docs.descriptor.d.ts.map +0 -1
  1374. package/dist/docs/adapters.docs.descriptor.js +0 -223
  1375. package/dist/docs/adapters.docs.descriptor.js.map +0 -1
  1376. package/dist/docs/manifests.docs.descriptor.d.ts +0 -4
  1377. package/dist/docs/manifests.docs.descriptor.d.ts.map +0 -1
  1378. package/dist/docs/manifests.docs.descriptor.js.map +0 -1
  1379. package/src/adapters/index.ts +0 -13
  1380. package/src/bridge/index.ts +0 -2
  1381. package/src/bridge/storage-kind-bridge.ts +0 -131
  1382. package/src/bridge/types.ts +0 -46
  1383. package/src/cli/commands/list/storage.list.cli-command.descriptor.ts +0 -20
  1384. package/src/cli/commands/list/storage.list.cli-command.impl.ts +0 -72
  1385. package/src/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.ts +0 -28
  1386. package/src/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.ts +0 -171
  1387. package/src/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.ts +0 -28
  1388. package/src/cli/commands/sync/push/storage.sync.push.cli-command.impl.ts +0 -213
  1389. package/src/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.ts +0 -22
  1390. package/src/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.ts +0 -129
  1391. package/src/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.ts +0 -17
  1392. package/src/cli/commands/sync/status/storage.sync.status.cli-command.impl.ts +0 -192
  1393. package/src/cli/context.consumer.ts +0 -195
  1394. package/src/cli/storage.cli-group.descriptor.ts +0 -13
  1395. package/src/cli/storage.sync.cli-group.descriptor.ts +0 -14
  1396. package/src/cli/types.ts +0 -25
  1397. package/src/client/index.ts +0 -10
  1398. package/src/client/list-browsable-adapters.ts +0 -42
  1399. package/src/daemon/commands/sync.ts +0 -225
  1400. package/src/docs/adapters.docs.descriptor.ts +0 -225
  1401. package/src/docs/manifests.docs.descriptor.ts +0 -318
  1402. package/src/docs/records.docs.descriptor.ts +0 -271
  1403. package/src/docs/sync.docs.descriptor.ts +0 -281
  1404. package/src/index.ts +0 -109
  1405. package/src/interfaces/adapter.ts +0 -451
  1406. package/src/interfaces/document-store.ts +0 -74
  1407. package/src/interfaces/index.ts +0 -117
  1408. package/src/interfaces/key-value.ts +0 -112
  1409. package/src/interfaces/query-builder.ts +0 -31
  1410. package/src/interfaces/record/index.ts +0 -58
  1411. package/src/interfaces/record/interface.ts +0 -723
  1412. package/src/interfaces/repository.ts +0 -300
  1413. package/src/interfaces/schema/index.ts +0 -37
  1414. package/src/interfaces/schema/schema-interface.ts +0 -586
  1415. package/src/interfaces/schema/table-interface.ts +0 -314
  1416. package/src/interfaces/storage-consumer-fs.ts +0 -66
  1417. package/src/manifest/base.ts +0 -594
  1418. package/src/manifest/cloud-sync.consumer.ts +0 -120
  1419. package/src/manifest/index.ts +0 -7
  1420. package/src/manifest/source-policy.ts +0 -74
  1421. package/src/paths.ts +0 -171
  1422. package/src/policy/context.ts +0 -30
  1423. package/src/policy/index.ts +0 -20
  1424. package/src/policy/overrides.ts +0 -46
  1425. package/src/policy/persistence.ts +0 -117
  1426. package/src/policy/types.ts +0 -28
  1427. package/src/record/base.ts +0 -1872
  1428. package/src/record/events.ts +0 -56
  1429. package/src/record/extensions.ts +0 -308
  1430. package/src/record/index.ts +0 -7
  1431. package/src/resolver/storage-backed-resolver.ts +0 -255
  1432. package/src/schemas/adapter-descriptor.ts +0 -128
  1433. package/src/schemas/index.ts +0 -104
  1434. package/src/schemas/manifest-descriptor.ts +0 -143
  1435. package/src/schemas/record/descriptor.ts +0 -621
  1436. package/src/schemas/record/index.ts +0 -60
  1437. package/src/schemas/record/relation.ts +0 -49
  1438. package/src/schemas/schema/descriptor.ts +0 -278
  1439. package/src/schemas/schema/index.ts +0 -14
  1440. package/src/schemas/sync-bridge-descriptor.ts +0 -143
  1441. package/src/server/manifest-route.ts +0 -319
  1442. package/src/storage-policy.context.descriptor.ts +0 -17
  1443. package/src/storage-policy.context.server.ts +0 -34
  1444. package/src/storage.plugin.ts +0 -264
  1445. package/src/sync/cloud-pull.ts +0 -202
  1446. package/src/sync/cloud-record-crud-config.ts +0 -41
  1447. package/src/sync/record-sync-bridge.ts +0 -183
  1448. package/src/sync/resolve-sync-target.ts +0 -57
  1449. package/src/sync/schemas/cloud-pull.ts +0 -26
  1450. package/src/sync/schemas/index.ts +0 -18
  1451. package/src/sync/schemas/sync-helpers.ts +0 -18
  1452. package/src/sync/schemas/sync-state.ts +0 -41
  1453. package/src/sync/sync-bridge.ts +0 -763
  1454. package/src/sync/sync-manifest.ts +0 -212
  1455. package/src/sync/sync-state.consumer.ts +0 -144
  1456. package/src/transform/index.ts +0 -130
  1457. package/src/transform/transform-descriptor.ts +0 -53
  1458. package/src/tui/storage-explorer.descriptor.ts +0 -32
  1459. package/src/tui/storage-explorer.impl.consumer.ts +0 -14
  1460. package/src/tui/storage-open.descriptor.ts +0 -31
  1461. package/src/tui/storage-open.impl.consumer.ts +0 -14
  1462. package/src/utils/error-context.ts +0 -19
@@ -16,16 +16,248 @@
16
16
  * });
17
17
  * ```
18
18
  */
19
+ import * as z from 'zod/v4';
20
+ import { isRecordTableMissing } from "./failure-condition.js";
19
21
  import { getLogger } from '@vibesdotdev/logging';
20
- import { toErrorContext } from '../utils/error-context';
21
- const logger = getLogger('storage:record');
22
- import { resolveHomePath, resolveScopePathTemplate, resolveWorkspacePath } from '../paths';
23
- import { getVibesRuntime } from '@vibesdotdev/runtime/api/runtime';
24
- import { createTracer } from '@vibesdotdev/runtime/debug';
25
- function resolveTimestampField(timestamps, field) {
26
- return typeof timestamps === 'object' && typeof timestamps?.[field] === 'string'
27
- ? timestamps[field]
28
- : field;
22
+ import { CapabilityError } from '@vibesdotdev/kernel';
23
+ import { JsonObjectSchema, JsonValueSchema } from '@vibesdotdev/kernel/schemas/json';
24
+ import { toErrorContext } from "../utils/error-context.js";
25
+ import { failureOfSync, outcomeOf } from "../utils/failure-of.js";
26
+ import { RecordFilterOperatorSchema } from "../schemas/record/query-values.js";
27
+ import { StorageAdapterConfigSchema } from "../schemas/adapter-config.js";
28
+ import { ZodSchemaValueSchema } from "../schemas/zod-schema-value.js";
29
+ import { resolveScopePathTemplate, resolveVibesBaseDir, resolveWorkspacePathWithPolicy } from "../path-policy.js";
30
+ import { createTracer } from '@vibesdotdev/kernel/debug';
31
+ import { getPathRoots } from '@vibesdotdev/paths';
32
+ import { selectStorageAdapter } from "../adapters/select-adapter.js";
33
+ import { parseAdapterInput } from "../adapters/parse-adapter-input.js";
34
+ import { extractCloudEndpoint, registerCloudEndpointIfSupported } from "../schemas/cloud-endpoints.js";
35
+ import { enqueueRecordDelivery } from "./delivery.js";
36
+ import { applySetAdditions, assertArrayField, assertArrayMembers, updatePatch } from "./array-fields.js";
37
+ import { resolveSchemaCoordination } from "./schema-coordination.client.js";
38
+ import { loadRecordRelations } from "./relations.js";
39
+ import { assertRecordRevisionSchema, recordIndexName, resolveTimestampField, softDeleteColumn, timestampColumn } from "./columns.js";
40
+ import { RECORD_REVISION_COLUMN, RecordRevisionMetadataSchema, RecordRevisionQuerySchema } from "../schemas/record/revision.js";
41
+ import { RecordAtomicMutationsSchema } from "../schemas/record/atomic.js";
42
+ /** Whether a value is a field map: an object that is not an array (every stored row's JSON is one). */
43
+ function isFieldMap(value) {
44
+ return value instanceof Object && !Array.isArray(value);
45
+ }
46
+ /** A record value seen as its field map: the same object, never a copy, so a hook's or loader's change lands on it. */
47
+ const RecordFieldsViewSchema = z.custom((value) => isFieldMap(value));
48
+ const RecordLifecycleHookSchema = z.custom((value) => value instanceof Function);
49
+ /** An inline migration's `up` step: SQL text, or a function run against the record's database. */
50
+ const MigrationSqlSchema = z.string();
51
+ const MigrationFunctionSchema = z.custom((value) => value instanceof Function);
52
+ /** A descriptor's custom id generator (`autoId.strategy: 'custom'`) and the id it produces. */
53
+ const IdGeneratorSchema = z.custom((value) => value instanceof Function);
54
+ const GeneratedIdSchema = z.string();
55
+ /** A descriptor's per-scope storage path templates (`scopePaths`), when it declares them. */
56
+ const ScopePathTemplatesSchema = z.object({ scopePaths: z.record(z.string(), z.string().optional()).optional() });
57
+ /** What a SQL database connection must offer this record: prepared statements and statement execution. */
58
+ const SqlHandleMethodsSchema = z.object({ prepare: z.function(), exec: z.function() });
59
+ const SqlDatabaseHandleSchema = z.custom((value) => SqlHandleMethodsSchema.safeParse(value).success);
60
+ const SyncTransactionMethodsSchema = z.object({ transaction: z.function(), inTransaction: z.boolean() });
61
+ const SyncTransactionDatabaseSchema = z.custom(value => SyncTransactionMethodsSchema.safeParse(value).success);
62
+ /** Where a record-scoped database opens: the record's resolved base path and its adapter's database file name. */
63
+ const RecordDatabaseTargetSchema = z.object({ basePath: z.string(), dbName: z.string().optional() });
64
+ /** The workspace a record's kind context names (`runtime/workspace-path`), when it names one. */
65
+ const ContextWorkspacePathSchema = z.string();
66
+ /** A hierarchy query's id path (`json_array` of the primary keys from the root). */
67
+ const HierarchyPathSchema = z.array(z.union([z.string(), z.number()]).transform(String));
68
+ /**
69
+ * A filter's fields, seen as the caller's own object (never a copy, so no own
70
+ * key is dropped): each field's condition, an operator object or an equality value.
71
+ */
72
+ const FilterFieldsViewSchema = z.custom((value) => isFieldMap(value));
73
+ const JsonFilterOperandSchema = z.union([JsonObjectSchema, z.array(JsonValueSchema)]);
74
+ /**
75
+ * Whether a field condition is an operator object (`{ eq, in, isNull, … }`):
76
+ * any object, null-prototype ones included, except a Date (a Date, a function
77
+ * or a primitive is an equality value). The condition is read in place.
78
+ */
79
+ function isOperatorObject(value) {
80
+ if (value === null || value === undefined || Array.isArray(value))
81
+ return false;
82
+ if (value instanceof Object)
83
+ return !(value instanceof Date) && !(value instanceof Function);
84
+ return Object.getPrototypeOf(value) === null;
85
+ }
86
+ const FilterOperatorsSchema = z.custom((value) => isOperatorObject(value));
87
+ /**
88
+ * Whether a descriptor's schema is a zod schema. The record's row type `T` is
89
+ * the one its registered descriptor declares: the schema it carries produces it.
90
+ */
91
+ function isRecordSchema(schema) {
92
+ return ZodSchemaValueSchema.safeParse(schema).success;
93
+ }
94
+ const connectionTransactions = new WeakMap();
95
+ const signalConnectionStates = new WeakMap();
96
+ function getConnectionTransactionState(db) {
97
+ let state = connectionTransactions.get(db);
98
+ if (!state) {
99
+ state = {
100
+ leased: false,
101
+ waiters: [],
102
+ framesBySignal: new Map(),
103
+ fallbackFrame: null,
104
+ savepointSeq: 0
105
+ };
106
+ connectionTransactions.set(db, state);
107
+ }
108
+ return state;
109
+ }
110
+ function currentInvocationSignal(runtime) {
111
+ let signal;
112
+ const failure = failureOfSync(() => {
113
+ signal = runtime.invocationSignal;
114
+ });
115
+ return failure ? undefined : signal;
116
+ }
117
+ function getActiveTransactionFrame(db, runtime) {
118
+ const state = connectionTransactions.get(db);
119
+ if (!state || !state.leased)
120
+ return null;
121
+ const signal = currentInvocationSignal(runtime);
122
+ if (signal !== undefined) {
123
+ return state.framesBySignal.get(signal) ?? null;
124
+ }
125
+ return state.fallbackFrame;
126
+ }
127
+ function isConnectionTransactionActive(db) {
128
+ const state = connectionTransactions.get(db);
129
+ return Boolean(state?.leased);
130
+ }
131
+ async function waitForConnectionLease(db, runtime) {
132
+ const state = connectionTransactions.get(db);
133
+ if (!state)
134
+ return;
135
+ while (state.leased && getActiveTransactionFrame(db, runtime) === null) {
136
+ await new Promise((resolve) => {
137
+ state.waiters.push(resolve);
138
+ });
139
+ }
140
+ }
141
+ async function withTransactionFrame(runtime, state, parentFrame, operation) {
142
+ const parentSignal = currentInvocationSignal(runtime);
143
+ const controller = new AbortController();
144
+ const runWithFrame = async (ownedSignal) => {
145
+ const frame = {
146
+ state,
147
+ parent: parentFrame,
148
+ signal: ownedSignal,
149
+ childLeased: false,
150
+ childWaiters: [],
151
+ onRollback: []
152
+ };
153
+ const inheritedStates = [];
154
+ if (ownedSignal !== undefined) {
155
+ state.framesBySignal.set(ownedSignal, frame);
156
+ const ownedSet = new Set([state]);
157
+ if (parentSignal !== undefined) {
158
+ const parentSet = signalConnectionStates.get(parentSignal);
159
+ if (parentSet) {
160
+ for (const otherState of parentSet) {
161
+ ownedSet.add(otherState);
162
+ if (otherState !== state) {
163
+ const otherFrame = otherState.framesBySignal.get(parentSignal);
164
+ if (otherFrame) {
165
+ otherState.framesBySignal.set(ownedSignal, otherFrame);
166
+ inheritedStates.push(otherState);
167
+ }
168
+ }
169
+ }
170
+ }
171
+ }
172
+ signalConnectionStates.set(ownedSignal, ownedSet);
173
+ }
174
+ else {
175
+ state.fallbackFrame = frame;
176
+ }
177
+ try {
178
+ return await operation(frame);
179
+ }
180
+ finally {
181
+ if (ownedSignal !== undefined) {
182
+ state.framesBySignal.delete(ownedSignal);
183
+ for (const otherState of inheritedStates) {
184
+ otherState.framesBySignal.delete(ownedSignal);
185
+ }
186
+ signalConnectionStates.delete(ownedSignal);
187
+ }
188
+ else {
189
+ state.fallbackFrame = parentFrame;
190
+ }
191
+ }
192
+ };
193
+ return runtime.runInvocation(() => runWithFrame(currentInvocationSignal(runtime)), { signal: controller.signal });
194
+ }
195
+ function flushRollbackCallbacks(frame) {
196
+ for (let i = frame.onRollback.length - 1; i >= 0; i--) {
197
+ frame.onRollback[i]();
198
+ }
199
+ }
200
+ async function runInSavepoint(runtime, parentFrame, db, operation) {
201
+ while (parentFrame.childLeased) {
202
+ await new Promise((resolve) => {
203
+ parentFrame.childWaiters.push(resolve);
204
+ });
205
+ }
206
+ parentFrame.childLeased = true;
207
+ const savepoint = `vibes_tx_sp_${++parentFrame.state.savepointSeq}`;
208
+ try {
209
+ return await withTransactionFrame(runtime, parentFrame.state, parentFrame, async (childFrame) => {
210
+ db.exec(`SAVEPOINT ${savepoint}`);
211
+ try {
212
+ const result = await operation(childFrame);
213
+ db.exec(`RELEASE SAVEPOINT ${savepoint}`);
214
+ parentFrame.onRollback.push(...childFrame.onRollback);
215
+ return result;
216
+ }
217
+ catch (error) {
218
+ db.exec(`ROLLBACK TO SAVEPOINT ${savepoint}`);
219
+ db.exec(`RELEASE SAVEPOINT ${savepoint}`);
220
+ flushRollbackCallbacks(childFrame);
221
+ throw error;
222
+ }
223
+ });
224
+ }
225
+ finally {
226
+ parentFrame.childLeased = false;
227
+ const waiters = parentFrame.childWaiters.splice(0);
228
+ for (const wake of waiters) {
229
+ wake();
230
+ }
231
+ }
232
+ }
233
+ async function withImmediateSchemaTransaction(runtime, db, operation) {
234
+ const state = getConnectionTransactionState(db);
235
+ while (state.leased) {
236
+ await new Promise((resolve) => {
237
+ state.waiters.push(resolve);
238
+ });
239
+ }
240
+ state.leased = true;
241
+ try {
242
+ await withTransactionFrame(runtime, state, null, async () => {
243
+ db.exec('BEGIN IMMEDIATE');
244
+ try {
245
+ await operation();
246
+ db.exec('COMMIT');
247
+ }
248
+ catch (error) {
249
+ db.exec('ROLLBACK');
250
+ throw error;
251
+ }
252
+ });
253
+ }
254
+ finally {
255
+ state.leased = false;
256
+ const waiters = state.waiters.splice(0);
257
+ for (const wake of waiters) {
258
+ wake();
259
+ }
260
+ }
29
261
  }
30
262
  export function applyCreateTimestamps(record, timestamps, now) {
31
263
  if (!timestamps) {
@@ -49,6 +281,93 @@ export function applyUpdateTimestamp(record, update, timestamps, now) {
49
281
  record[updatedAtField] = now;
50
282
  }
51
283
  }
284
+ /**
285
+ * Split a multi-statement SQL string into individual statements.
286
+ *
287
+ * Unlike a naive `split(';')`, this respects:
288
+ * - single/double-quoted string literals (including `''` escapes),
289
+ * - line comments (--) and block comments,
290
+ * - `CREATE TRIGGER ... BEGIN ... END` bodies, whose internal `;`s must not
291
+ * terminate the statement (a `CASE ... END` inside the body is balanced too).
292
+ */
293
+ export function splitSqlStatements(sql) {
294
+ const statements = [];
295
+ let current = '';
296
+ let triggerDepth = 0;
297
+ let i = 0;
298
+ const isWordChar = (c) => /[A-Za-z0-9_]/.test(c);
299
+ while (i < sql.length) {
300
+ const c = sql[i];
301
+ const next = sql[i + 1];
302
+ if (c === '-' && next === '-') {
303
+ const end = sql.indexOf('\n', i);
304
+ const stop = end === -1 ? sql.length : end + 1;
305
+ current += sql.slice(i, stop);
306
+ i = stop;
307
+ continue;
308
+ }
309
+ if (c === '/' && next === '*') {
310
+ const end = sql.indexOf('*/', i + 2);
311
+ const stop = end === -1 ? sql.length : end + 2;
312
+ current += sql.slice(i, stop);
313
+ i = stop;
314
+ continue;
315
+ }
316
+ if (c === "'" || c === '"') {
317
+ let j = i + 1;
318
+ while (j < sql.length) {
319
+ if (sql[j] === c) {
320
+ // '' inside a single-quoted literal is an escaped quote
321
+ if (c === "'" && sql[j + 1] === "'") {
322
+ j += 2;
323
+ continue;
324
+ }
325
+ j++;
326
+ break;
327
+ }
328
+ j++;
329
+ }
330
+ current += sql.slice(i, j);
331
+ i = j;
332
+ continue;
333
+ }
334
+ if (c === ';') {
335
+ if (triggerDepth > 0) {
336
+ current += c;
337
+ i++;
338
+ continue;
339
+ }
340
+ if (current.trim())
341
+ statements.push(current.trim());
342
+ current = '';
343
+ i++;
344
+ continue;
345
+ }
346
+ if (isWordChar(c)) {
347
+ let j = i;
348
+ while (j < sql.length && isWordChar(sql[j]))
349
+ j++;
350
+ const word = sql.slice(i, j).toUpperCase();
351
+ if (triggerDepth > 0) {
352
+ if (word === 'CASE')
353
+ triggerDepth++;
354
+ else if (word === 'END')
355
+ triggerDepth--;
356
+ }
357
+ else if (word === 'BEGIN' && /\bCREATE\s+(TEMP(ORARY)?\s+)?TRIGGER\b/i.test(current)) {
358
+ triggerDepth = 1;
359
+ }
360
+ current += sql.slice(i, j);
361
+ i = j;
362
+ continue;
363
+ }
364
+ current += c;
365
+ i++;
366
+ }
367
+ if (current.trim())
368
+ statements.push(current.trim());
369
+ return statements;
370
+ }
52
371
  /**
53
372
  * StorageRecord Implementation
54
373
  *
@@ -58,22 +377,188 @@ export function applyUpdateTimestamp(record, update, timestamps, now) {
58
377
  * Uses runtime query API to resolve the appropriate adapter based on
59
378
  * the descriptor's allowedAdapters and defaultAdapter settings.
60
379
  */
380
+ /**
381
+ * Thrown by `update()` when an `UpdateOptions.where` precondition does not
382
+ * match the record's current state at write time — an optimistic-lock
383
+ * conflict. The precondition is enforced ATOMICALLY inside the UPDATE
384
+ * statement's WHERE (single SQL statement, evaluated against the live row), so
385
+ * this is a genuine compare-and-set with no read-check-write TOCTOU window:
386
+ * of two concurrent writers passing `{ where: { updatedAt: <read> } }`, the
387
+ * second sees 0 rows matched and gets this error instead of clobbering the
388
+ * first. Callers catch it to detect a concurrent write and retry.
389
+ *
390
+ * It is a `CapabilityError` (`CONFLICT`, reason `record-precondition-conflict`)
391
+ * named `OptimisticLockError`. Its message and details are value-free (the
392
+ * table only); the record id and the precondition travel on its own fields.
393
+ */
394
+ export class OptimisticLockError extends CapabilityError {
395
+ recordId;
396
+ tableName;
397
+ where;
398
+ constructor(tableName, recordId, where, cause) {
399
+ super(`Optimistic-lock conflict updating a ${tableName} record: it no longer matches its update precondition (concurrent write).`, 'CONFLICT', { reason: 'record-precondition-conflict', retryable: true, details: { tableName }, cause });
400
+ this.name = 'OptimisticLockError';
401
+ this.tableName = tableName;
402
+ this.recordId = recordId;
403
+ this.where = where;
404
+ }
405
+ }
61
406
  export class StorageRecord {
62
407
  tableName;
63
408
  primaryKey;
64
- schema;
65
409
  relations;
410
+ supportsConditionalWrites = true;
411
+ get revisions() {
412
+ if (!this.descriptor.versioning)
413
+ return undefined;
414
+ return {
415
+ metadata: id => this.revisionMetadata(id),
416
+ list: options => this.revisionList(options),
417
+ readAtRevision: (id, revision) => this.readAtRevision(id, revision)
418
+ };
419
+ }
420
+ revisionLivePredicate() {
421
+ const deletedAt = this.softDeleteField();
422
+ if (!deletedAt)
423
+ return '';
424
+ const path = `$.${JSON.stringify(deletedAt)}`.replaceAll("'", "''");
425
+ return ` AND json_extract(data, '${path}') IS NULL`;
426
+ }
427
+ async revisionMetadata(id) {
428
+ await this.ensureInitialized();
429
+ const row = this.db.prepare(`SELECT CAST(${this.primaryKey} AS TEXT) AS id, ${RECORD_REVISION_COLUMN} AS revision FROM ${this.tableName} WHERE ${this.primaryKey} = ?${this.revisionLivePredicate()}`).get(id);
430
+ return row ? RecordRevisionMetadataSchema.parse(row) : null;
431
+ }
432
+ async readAtRevision(id, revision) {
433
+ await this.ensureInitialized();
434
+ const row = this.db.prepare(`SELECT data, ${RECORD_REVISION_COLUMN} AS revision FROM ${this.tableName} WHERE ${this.primaryKey} = ? AND ${RECORD_REVISION_COLUMN} = ?${this.revisionLivePredicate()}`).get(id, revision);
435
+ if (row)
436
+ return { status: 'read', revision: row.revision, value: this.decodeRecord(row.data) };
437
+ const current = await this.revisionMetadata(id);
438
+ return current ? { status: 'changed', revision: current.revision } : { status: 'not-found' };
439
+ }
440
+ async revisionList(options) {
441
+ await this.ensureInitialized();
442
+ const query = RecordRevisionQuerySchema.parse(options ?? {});
443
+ const filter = this.buildWhereClause(query.where ?? {});
444
+ const predicate = `${filter.clause || '1 = 1'}${this.revisionLivePredicate()}`;
445
+ const params = [...filter.values];
446
+ const projection = `CAST(${this.primaryKey} AS TEXT) AS id, ${RECORD_REVISION_COLUMN} AS revision`;
447
+ let sql = `SELECT ${projection} FROM ${this.tableName} WHERE ${predicate}`;
448
+ const order = Array.isArray(query.orderBy) ? query.orderBy : Object.entries(query.orderBy ?? {}).map(([field, direction]) => ({ field, direction }));
449
+ const ordering = order.map(item => `${this.filterColumn(item.field)} ${item.direction}`);
450
+ if (!order.some(item => item.field === this.primaryKey))
451
+ ordering.push(this.primaryKey);
452
+ sql += ` ORDER BY ${ordering.join(', ')}`;
453
+ if (query.limit !== undefined) {
454
+ sql += ' LIMIT ?';
455
+ params.push(query.limit + 1);
456
+ }
457
+ else if (query.offset !== undefined)
458
+ sql += ' LIMIT -1';
459
+ if (query.offset !== undefined) {
460
+ sql += ' OFFSET ?';
461
+ params.push(query.offset);
462
+ }
463
+ const rows = this.db.prepare(sql).all(...params);
464
+ const hasMore = query.limit !== undefined && rows.length > query.limit;
465
+ const items = (hasMore ? rows.slice(0, query.limit) : rows).map(row => RecordRevisionMetadataSchema.parse(row));
466
+ const page = { items, hasMore };
467
+ if (query.limit === undefined || query.includeTotal) {
468
+ page.total = this.db.prepare(`SELECT COUNT(*) AS count FROM ${this.tableName} WHERE ${predicate}`).get(...filter.values)?.count ?? 0;
469
+ }
470
+ return page;
471
+ }
66
472
  descriptor;
67
473
  context;
68
474
  adapterCache = null;
475
+ adapterCacheId = null;
476
+ adapterBasePathSemantics = 'unused';
69
477
  db = null;
70
478
  initialized = false;
71
- asMutableRecord(record) {
72
- return record;
479
+ initialization = null;
480
+ nativeRecords = new WeakMap();
481
+ /** This record's row type: a stored field map, the same object (rows are written after this record's schema accepted them). */
482
+ recordValueSchema = z.custom((value) => isFieldMap(value));
483
+ /** The runtime this record was handed; there is no ambient default. */
484
+ getRuntime() {
485
+ const runtime = this.context.runtime;
486
+ if (!runtime) {
487
+ throw new CapabilityError(`Storage record '${this.descriptor.id}' was built without a runtime.`, 'INVALID_INPUT', { reason: 'storage-runtime-missing', retryable: false });
488
+ }
489
+ return runtime;
490
+ }
491
+ /** This record's Logging: the handed runtime's, never an ambient default. */
492
+ get logger() {
493
+ return getLogger('storage:record', this.getRuntime());
494
+ }
495
+ /**
496
+ * This record's zod schema, as its descriptor declares it. The record's
497
+ * row type `T` is the one its owner declares: the schema it carries produces it.
498
+ */
499
+ get schema() {
500
+ const schema = this.descriptor.schema;
501
+ if (!isRecordSchema(schema)) {
502
+ throw new CapabilityError(`Storage record '${this.descriptor.id}' declares no zod schema.`, 'INVALID_INPUT', {
503
+ reason: 'storage-record-schema-missing',
504
+ retryable: false,
505
+ details: { recordKind: this.descriptor.id }
506
+ });
507
+ }
508
+ return schema;
509
+ }
510
+ /** A record value's field map: the same object, refused typed when it is not one. */
511
+ fieldsOf(value) {
512
+ const fields = parseAdapterInput(RecordFieldsViewSchema, value, {
513
+ message: 'A storage record value must be a field map.',
514
+ code: 'INVALID_INPUT',
515
+ reason: 'record-value-invalid',
516
+ details: { recordKind: this.descriptor.id }
517
+ });
518
+ if (this.descriptor.versioning && Object.hasOwn(fields, RECORD_REVISION_COLUMN))
519
+ throw new CapabilityError('Storage revision metadata cannot be written as record data.', 'INVALID_INPUT', {
520
+ reason: 'record-revision-field-reserved', retryable: false
521
+ });
522
+ return fields;
523
+ }
524
+ /** A field map as this record's row type: the same object. */
525
+ recordOf(fields) {
526
+ this.fieldsOf(fields);
527
+ return parseAdapterInput(this.recordValueSchema, fields, {
528
+ message: 'A storage record value must be a field map.',
529
+ code: 'INVALID_INPUT',
530
+ reason: 'record-value-invalid',
531
+ details: { recordKind: this.descriptor.id }
532
+ });
533
+ }
534
+ /** One stored row's JSON `data` column as its field map; a row that is not a JSON object is refused typed. */
535
+ decodeFields(data) {
536
+ return parseAdapterInput(RecordFieldsViewSchema, JSON.parse(data), {
537
+ message: `A stored ${this.tableName} row is not a JSON object.`,
538
+ code: 'INTERNAL',
539
+ reason: 'record-row-invalid',
540
+ details: { recordKind: this.descriptor.id }
541
+ });
542
+ }
543
+ /** One stored row's JSON `data` column as this record's row type. */
544
+ decodeRecord(data) {
545
+ return this.recordOf(this.decodeFields(data));
546
+ }
547
+ /** A hierarchy query's id path, each id as text. */
548
+ decodePath(path) {
549
+ return parseAdapterInput(HierarchyPathSchema, JSON.parse(path), {
550
+ message: `A ${this.tableName} hierarchy path is not a list of ids.`,
551
+ code: 'INTERNAL',
552
+ reason: 'record-hierarchy-path-invalid',
553
+ details: { recordKind: this.descriptor.id }
554
+ });
73
555
  }
74
556
  readStringField(record, field) {
75
- const value = this.asMutableRecord(record)[field];
76
- return typeof value === 'string' ? value : null;
557
+ const fields = RecordFieldsViewSchema.safeParse(record);
558
+ if (!fields.success)
559
+ return null;
560
+ const value = z.string().safeParse(fields.data[field]);
561
+ return value.success ? value.data : null;
77
562
  }
78
563
  readPrimaryKey(record) {
79
564
  const id = this.readStringField(record, this.primaryKey);
@@ -82,33 +567,196 @@ export class StorageRecord {
82
567
  }
83
568
  return id;
84
569
  }
570
+ /** Run one descriptor lifecycle hook, when the descriptor declares it, with the row's fields; it must be a function. */
571
+ async runHook(name, fields) {
572
+ const declared = this.descriptor[name];
573
+ if (!declared)
574
+ return;
575
+ const hook = parseAdapterInput(RecordLifecycleHookSchema, declared, {
576
+ message: `Storage record '${this.descriptor.id}' declares a ${name} hook that is not a function.`,
577
+ code: 'INVALID_INPUT',
578
+ reason: 'record-hook-invalid',
579
+ details: { recordKind: this.descriptor.id, hook: name }
580
+ });
581
+ await hook(fields);
582
+ }
85
583
  /**
86
- * Emit a lifecycle event via the runtime HookBus.
87
- * Fire-and-forget: never blocks CRUD, silently catches errors.
584
+ * Emit a lifecycle event via the handed runtime's HookBus. A failing
585
+ * subscriber never blocks the CRUD that already happened; it is reported.
88
586
  */
89
- emitLifecycle(phase, record) {
90
- try {
91
- const runtime = getVibesRuntime();
92
- const id = record[this.primaryKey];
93
- runtime.hooks('storage/record').emit(phase, {
94
- recordId: id,
95
- recordKind: this.descriptor.id,
96
- tableName: this.tableName,
97
- data: record
587
+ emitLifecycle(phase, fields) {
588
+ const runtime = this.getRuntime();
589
+ const payload = {
590
+ recordId: fields[this.primaryKey],
591
+ recordKind: this.descriptor.id,
592
+ tableName: this.tableName,
593
+ data: fields
594
+ };
595
+ void outcomeOf(() => runtime.hooks('storage/record').emit(phase, payload)).then(outcome => {
596
+ if (!outcome.ok)
597
+ this.logger.warn('storage/record lifecycle hook failed', {
598
+ phase, recordKind: this.descriptor.id, error: outcome.failure
599
+ });
600
+ });
601
+ }
602
+ atomic = { commit: mutations => this.commitAtomic(mutations) };
603
+ /** Prepare complete row mutations, then commit without yielding to another record or reader. */
604
+ async commitAtomic(input) {
605
+ const runtime = this.getRuntime();
606
+ const lifecycle = runtime.lifecycle;
607
+ const assertLive = () => {
608
+ if (lifecycle.phase === 'live' && !lifecycle.signal.aborted && runtime.lifecycle === lifecycle)
609
+ return;
610
+ throw new CapabilityError('A prepared commit cannot outlive its admitted runtime.', 'CANCELLED', {
611
+ reason: 'runtime-disposed', retryable: false, cause: lifecycle.signal.reason
98
612
  });
99
- }
100
- catch {
101
- // Runtime may not be bootstrapped in tests — never block CRUD
613
+ };
614
+ assertLive();
615
+ if (this.constructor !== StorageRecord)
616
+ throw new CapabilityError('A custom record cannot initiate a native prepared commit.', 'UNAVAILABLE', {
617
+ reason: 'record-atomic-target-unsupported', retryable: false
618
+ });
619
+ if (!this.initialized || !this.db)
620
+ throw new CapabilityError('Prepared commits require explicitly initialized native records.', 'UNAVAILABLE', {
621
+ reason: 'record-atomic-not-initialized', retryable: false
622
+ });
623
+ const db = this.db;
624
+ const transactionDb = parseAdapterInput(SyncTransactionDatabaseSchema, db, {
625
+ message: 'The selected connection has no synchronous transaction capability.', code: 'UNAVAILABLE',
626
+ reason: 'record-atomic-transaction-unsupported'
627
+ });
628
+ const assertNoTransaction = () => {
629
+ if (transactionDb.inTransaction || isConnectionTransactionActive(db)) {
630
+ throw new CapabilityError('A prepared commit cannot join an existing transaction.', 'CONFLICT', {
631
+ reason: 'record-atomic-transaction-active', retryable: true
632
+ });
633
+ }
634
+ };
635
+ assertNoTransaction();
636
+ const mutations = parseAdapterInput(RecordAtomicMutationsSchema, input, {
637
+ message: 'Prepared record mutations are invalid.', code: 'INVALID_INPUT',
638
+ reason: 'record-atomic-input-invalid'
639
+ });
640
+ // Snapshot only row data, never selected record handles. The bytes and their
641
+ // validation/event drafts belong to this admission before preparation yields.
642
+ const snapshots = mutations.map(mutation => mutation.type === 'delete'
643
+ ? undefined : JSON.stringify(mutation.data));
644
+ const targets = mutations.map(mutation => {
645
+ const record = mutation.record;
646
+ if (!(record instanceof StorageRecord) || record.constructor !== StorageRecord || record.getRuntime() !== runtime) {
647
+ throw new CapabilityError('Prepared commits require native records from the same runtime.', 'UNAVAILABLE', {
648
+ reason: 'record-atomic-target-unsupported', retryable: false
649
+ });
650
+ }
651
+ if (!record.initialized || !record.db)
652
+ throw new CapabilityError('Prepared commits require explicitly initialized native records.', 'UNAVAILABLE', {
653
+ reason: 'record-atomic-not-initialized', retryable: false,
654
+ details: { recordKind: record.descriptor.id }
655
+ });
656
+ if (record.db !== db)
657
+ throw new CapabilityError('Prepared records must share one physical connection.', 'UNAVAILABLE', {
658
+ reason: 'record-atomic-connection-mismatch', retryable: false
659
+ });
660
+ const descriptor = record.descriptor;
661
+ const hooks = ['beforeCreate', 'afterCreate', 'beforeUpdate', 'afterUpdate', 'beforeDelete', 'afterDelete'];
662
+ if (descriptor.autoId || descriptor.timestamps || descriptor.versioning || descriptor.softDelete ||
663
+ descriptor.audit || descriptor.changeTracking || descriptor.delivery || descriptor.sideEffects || descriptor.validate !== undefined ||
664
+ hooks.some(hook => descriptor[hook] !== undefined)) {
665
+ throw new CapabilityError('This record requires behavior outside the prepared commit contract.', 'UNAVAILABLE', {
666
+ reason: 'record-atomic-descriptor-unsupported', retryable: false,
667
+ details: { recordKind: descriptor.id }
668
+ });
669
+ }
670
+ const condition = mutation.type === 'create' || !mutation.where
671
+ ? { clause: '', values: [] } : record.buildWhereClause(mutation.where);
672
+ return { record, descriptor, condition };
673
+ });
674
+ const prepared = [];
675
+ for (const [index, mutation] of mutations.entries()) {
676
+ const { record, descriptor, condition } = targets[index];
677
+ const serialized = snapshots[index];
678
+ const fields = serialized === undefined ? undefined : record.decodeFields(serialized);
679
+ if (fields && String(fields[record.primaryKey]) !== mutation.id) {
680
+ throw new CapabilityError('A prepared row must carry its exact primary key.', 'INVALID_INPUT', {
681
+ reason: 'record-primary-key-immutable', retryable: false
682
+ });
683
+ }
684
+ if (fields && descriptor.validateOnWrite !== false) {
685
+ const validation = await record.validate(fields);
686
+ if (!validation.success)
687
+ throw new CapabilityError('Prepared record validation failed.', 'INVALID_INPUT', {
688
+ reason: 'record-atomic-validation-failed', retryable: false, details: { recordKind: descriptor.id }
689
+ });
690
+ }
691
+ const predicate = `${record.primaryKey} = ?${condition.clause ? ` AND (${condition.clause})` : ''}`;
692
+ const parameters = [mutation.id, ...condition.values];
693
+ const sql = mutation.type === 'create'
694
+ ? `INSERT INTO ${record.tableName} (${record.primaryKey}, data) VALUES (?, ?)`
695
+ : mutation.type === 'replace'
696
+ ? `UPDATE ${record.tableName} SET data = ? WHERE ${predicate}`
697
+ : `DELETE FROM ${record.tableName} WHERE ${predicate} RETURNING data`;
698
+ const statement = db.prepare(sql);
699
+ prepared.push({ record, type: mutation.type, run: () => {
700
+ if (mutation.type === 'delete') {
701
+ const deleted = statement.get(...parameters);
702
+ if (deleted)
703
+ return record.decodeFields(deleted.data);
704
+ }
705
+ else {
706
+ const result = mutation.type === 'create'
707
+ ? statement.run(mutation.id, serialized) : statement.run(serialized, ...parameters);
708
+ if (result.changes === 1 && serialized !== undefined)
709
+ return record.decodeFields(serialized);
710
+ }
711
+ throw new CapabilityError('A prepared record precondition no longer holds.', 'CONFLICT', {
712
+ reason: 'record-atomic-precondition-failed', retryable: false, details: { index }
713
+ });
714
+ } });
715
+ }
716
+ assertLive();
717
+ assertNoTransaction();
718
+ const committed = transactionDb.transaction(() => prepared.map(operation => ({ ...operation, fields: operation.run() })));
719
+ for (const operation of committed) {
720
+ const phase = operation.type === 'create' ? 'afterCreate' : operation.type === 'replace' ? 'afterUpdate' : 'afterDelete';
721
+ operation.record.emitLifecycle(phase, operation.fields);
102
722
  }
103
723
  }
104
724
  constructor(descriptor, context) {
725
+ assertRecordRevisionSchema(descriptor);
105
726
  this.descriptor = descriptor;
106
727
  this.context = context;
107
728
  this.tableName = descriptor.tableName;
108
729
  this.primaryKey = descriptor.primaryKey || 'id';
109
- this.schema = descriptor.schema;
110
730
  this.relations = descriptor.relations || {};
111
731
  }
732
+ /** Default records use the selected adapter's native record implementation. */
733
+ static async resolveValue(implementation) {
734
+ if (!(implementation instanceof StorageRecord) || implementation.constructor !== StorageRecord) {
735
+ return implementation;
736
+ }
737
+ return implementation.resolveNativeRecord();
738
+ }
739
+ async resolveNativeRecord() {
740
+ const adapter = await this.getAdapter();
741
+ const config = await this.getAdapterConfig(this.adapterCacheId ?? adapter.id, this.adapterBasePathSemantics);
742
+ // Non-filesystem adapters require no workspace; their selected owner remains
743
+ // the identity. Filesystem records retain the Kernel-resolved target at selection.
744
+ const key = this.adapterBasePathSemantics === 'filesystem'
745
+ ? JSON.stringify([config.basePath, config.dbName]) : '';
746
+ let records = this.nativeRecords.get(adapter);
747
+ if (!records) {
748
+ records = new Map();
749
+ this.nativeRecords.set(adapter, records);
750
+ }
751
+ const existing = records.get(key);
752
+ if (existing)
753
+ return existing;
754
+ const context = this.adapterBasePathSemantics === 'filesystem'
755
+ ? { ...this.context, 'runtime/workspace-path': config.workspacePath } : this.context;
756
+ const record = adapter.record(this.descriptor, context);
757
+ records.set(key, record);
758
+ return record;
759
+ }
112
760
  /**
113
761
  * Resolve the adapter for this record using runtime query API.
114
762
  *
@@ -117,62 +765,73 @@ export class StorageRecord {
117
765
  */
118
766
  async getAdapter() {
119
767
  if (this.adapterCache) {
768
+ const adapterId = this.adapterCacheId ?? this.adapterCache.id;
769
+ const config = await this.getAdapterConfig(adapterId, this.adapterBasePathSemantics);
770
+ this.adapterCache.configure(config);
120
771
  return this.adapterCache;
121
772
  }
122
773
  const adapterTracer = createTracer(`adapter:${this.descriptor.id}`);
123
774
  adapterTracer.start();
124
- const runtime = getVibesRuntime();
125
- let adapter = null;
126
- let adapterId = '';
127
- if (this.descriptor.defaultAdapter &&
128
- this.descriptor.allowedAdapters.includes(this.descriptor.defaultAdapter)) {
129
- adapterId = this.descriptor.defaultAdapter;
130
- adapter = await runtime
131
- .query('storage/adapter')
132
- .withId(adapterId)
133
- .enabled()
134
- .resolve()
135
- .catch(() => null);
136
- }
137
- if (!adapter) {
138
- for (const allowedId of this.descriptor.allowedAdapters) {
139
- adapter = await runtime
140
- .query('storage/adapter')
141
- .withId(allowedId)
142
- .enabled()
143
- .resolve()
144
- .catch(() => null);
145
- if (adapter) {
146
- adapterId = allowedId;
147
- break;
148
- }
775
+ const runtime = this.getRuntime();
776
+ // A default adapter that is offered but fails is an error — never a
777
+ // silent reroute to another allowed adapter (RC06 U6).
778
+ const request = {
779
+ ownerKind: 'record',
780
+ ownerId: this.descriptor.id,
781
+ allowedAdapters: this.descriptor.allowedAdapters
782
+ };
783
+ if (this.descriptor.defaultAdapter) {
784
+ request.preferred = { adapterId: this.descriptor.defaultAdapter, source: 'descriptor' };
785
+ }
786
+ const { adapter, adapterId, descriptor: adapterDescriptor } = await selectStorageAdapter(runtime, request);
787
+ // Storage adapters are runtime singletons, while records can target different
788
+ // files/scopes through descriptor adapterConfig. Re-apply this record's config
789
+ // before opening the connection so one record cannot inherit another's DB.
790
+ const basePathSemantics = adapterDescriptor.basePathSemantics ?? 'unused';
791
+ const config = await this.getAdapterConfig(adapterId, basePathSemantics);
792
+ adapter.configure(config);
793
+ // Register per-record cloud endpoint overrides so cloud-api uses
794
+ // descriptor endpoints instead of the namespace fallback.
795
+ if (adapterId === 'storage.cloud-api') {
796
+ const cloudEndpoint = extractCloudEndpoint(this.descriptor.adapterConfig);
797
+ if (cloudEndpoint) {
798
+ registerCloudEndpointIfSupported(adapter, this.descriptor.namespace ?? this.descriptor.id, cloudEndpoint);
149
799
  }
150
800
  }
151
- if (!adapter || !adapterId) {
152
- throw new Error(`No available adapter for record '${this.descriptor.id}'. Checked: ${this.descriptor.allowedAdapters.join(', ')}`);
153
- }
154
- if (!adapter.isConfigured()) {
155
- const config = this.getAdapterConfig(adapterId);
156
- adapter.configure(config);
157
- }
158
801
  this.adapterCache = adapter;
802
+ this.adapterCacheId = adapterId;
803
+ this.adapterBasePathSemantics = basePathSemantics;
159
804
  adapterTracer.end('resolved');
160
805
  return adapter;
161
806
  }
162
807
  /**
163
808
  * Get adapter configuration based on record scope.
164
809
  */
165
- getAdapterConfig(adapterId) {
166
- const workspacePath = resolveWorkspacePath(this.context);
167
- const config = {
168
- basePath: this.getBasePath(workspacePath),
169
- workspacePath
170
- };
810
+ async getAdapterConfig(adapterId, basePathSemantics = this.adapterBasePathSemantics) {
811
+ const runtime = this.getRuntime();
812
+ // Every adapter call runs on this record's runtime: an adapter that
813
+ // resolves origins or credentials (cloud-api) uses it, never an ambient one.
814
+ const config = { runtime };
815
+ if (basePathSemantics === 'filesystem') {
816
+ // The storage workspace policy the manifest uses (SPEC): the record's own
817
+ // kind context (`runtime/workspace-path`) first, else the host's bound
818
+ // workspace. Only a record whose context names none asks the runtime,
819
+ // whose reader refuses `workspace-unbound` for an unbound host — before
820
+ // any path is resolved or file opened, and with no home-directory fallback.
821
+ const hostWorkspace = ContextWorkspacePathSchema.safeParse(this.context['runtime/workspace-path']).success
822
+ ? undefined
823
+ : await runtime.context('runtime/workspace-path');
824
+ const roots = getPathRoots(runtime);
825
+ const workspacePath = resolveWorkspacePathWithPolicy(this.context, undefined, roots, undefined, undefined, hostWorkspace);
826
+ config.basePath = this.getBasePath(workspacePath, roots);
827
+ config.workspacePath = workspacePath;
828
+ config.roots = roots;
829
+ }
171
830
  if (this.descriptor.adapterConfig) {
172
831
  const selectedAdapterId = adapterId || this.descriptor.defaultAdapter || this.descriptor.allowedAdapters[0];
173
- const adapterSpecificConfig = this.descriptor.adapterConfig[selectedAdapterId];
174
- if (adapterSpecificConfig && typeof adapterSpecificConfig === 'object') {
175
- Object.assign(config, adapterSpecificConfig);
832
+ const adapterSpecificConfig = StorageAdapterConfigSchema.safeParse(this.descriptor.adapterConfig[selectedAdapterId]);
833
+ if (adapterSpecificConfig.success) {
834
+ Object.assign(config, adapterSpecificConfig.data);
176
835
  }
177
836
  }
178
837
  return config;
@@ -181,24 +840,21 @@ export class StorageRecord {
181
840
  * Get base path for storage based on scope.
182
841
  *
183
842
  * Scope mapping:
184
- * - global: ~/.vibes/ (user-level, shared across projects)
843
+ * - global: canonical Vibes home selected by runtime/environment
185
844
  * - project: ./.vibes/ (project-level, within .vibes dir)
186
845
  * - local: ./.vibes/ (alias for project)
187
846
  * - auto: ./.vibes/ (defaults to project scope)
188
847
  */
189
- getBasePath(workspacePath = resolveWorkspacePath(this.context)) {
848
+ getBasePath(workspacePath, roots) {
190
849
  const scope = this.descriptor.scope || 'project';
191
- const homePath = resolveHomePath();
192
- const descriptorWithScopePaths = this.descriptor;
193
- if (descriptorWithScopePaths.scopePaths) {
194
- const customPath = descriptorWithScopePaths.scopePaths[scope];
195
- if (customPath) {
196
- return resolveScopePathTemplate(customPath, workspacePath);
197
- }
850
+ const declared = ScopePathTemplatesSchema.safeParse(this.descriptor);
851
+ const customPath = declared.success ? declared.data.scopePaths?.[scope] : undefined;
852
+ if (customPath) {
853
+ return resolveScopePathTemplate(customPath, workspacePath, roots);
198
854
  }
199
855
  switch (scope) {
200
856
  case 'global':
201
- return `${homePath}/.vibes`;
857
+ return resolveVibesBaseDir(roots);
202
858
  case 'local':
203
859
  case 'project':
204
860
  case 'auto':
@@ -206,6 +862,15 @@ export class StorageRecord {
206
862
  return `${workspacePath}/.vibes`;
207
863
  }
208
864
  }
865
+ /** The field soft deletion stamps, or null when the record does not soft-delete. */
866
+ softDeleteField() {
867
+ return softDeleteColumn(this.descriptor.softDelete);
868
+ }
869
+ /** The descriptor's `autoId` options, when it declares them as an object. */
870
+ autoIdOptions() {
871
+ const autoId = this.descriptor.autoId;
872
+ return autoId === true || autoId === false ? undefined : autoId;
873
+ }
209
874
  /**
210
875
  * Initialize the storage record
211
876
  * - Creates table if it doesn't exist
@@ -213,30 +878,80 @@ export class StorageRecord {
213
878
  * - Creates indexes
214
879
  */
215
880
  async initialize() {
881
+ if (this.initialized)
882
+ return;
883
+ const runtime = this.getRuntime();
884
+ const db = this.db ?? (await this.getDatabase());
885
+ this.db = db;
886
+ await waitForConnectionLease(db, runtime);
887
+ if (this.initialized)
888
+ return;
889
+ if (this.initialization)
890
+ return this.initialization;
891
+ const initialization = this.initializeOnce();
892
+ this.initialization = initialization;
893
+ try {
894
+ await initialization;
895
+ }
896
+ finally {
897
+ if (this.initialization === initialization) {
898
+ this.initialization = null;
899
+ }
900
+ }
901
+ }
902
+ async initializeOnce() {
216
903
  if (this.initialized)
217
904
  return;
218
905
  // Get database connection from adapter
219
- this.db = await this.getDatabase();
220
- // Create table if using inline migrations
221
- if (this.descriptor.migrations && this.descriptor.migrations.length > 0) {
222
- await this.runMigrations();
223
- // Inline migrations may create normalized column schemas.
224
- // StorageRecord CRUD operations expect a `data` TEXT column for JSON storage.
225
- // If the migrated table lacks a `data` column and is empty, recreate it
226
- // with the minimal (primaryKey, data) schema that StorageRecord requires.
227
- await this.ensureDataColumn();
228
- }
229
- else if (this.descriptor.migrationKey) {
230
- // Migrations should be pre-registered by the module
231
- // The adapter will handle them
906
+ const runtime = this.getRuntime();
907
+ const db = this.db ?? (await this.getDatabase());
908
+ this.db = db;
909
+ const initializeSchema = async () => {
910
+ // Create table if using inline migrations
911
+ if (this.descriptor.migrations && this.descriptor.migrations.length > 0) {
912
+ await this.runMigrations();
913
+ // Inline migrations may create normalized column schemas.
914
+ // StorageRecord CRUD operations expect a `data` TEXT column for JSON storage.
915
+ // If the migrated table lacks a `data` column and is empty, recreate it
916
+ // with the minimal (primaryKey, data) schema that StorageRecord requires.
917
+ await this.ensureDataColumn();
918
+ }
919
+ else if (this.descriptor.migrationKey) {
920
+ // Migrations should be pre-registered by the module
921
+ // The adapter will handle them
922
+ }
923
+ else {
924
+ // Auto-create table based on schema
925
+ await this.createTableFromSchema();
926
+ }
927
+ if (this.descriptor.versioning) {
928
+ const columns = this.db.prepare(`PRAGMA table_info(${this.tableName})`).all();
929
+ if (!columns.some(column => column.name === RECORD_REVISION_COLUMN)) {
930
+ this.db.exec(`ALTER TABLE ${this.tableName} ADD COLUMN ${RECORD_REVISION_COLUMN} TEXT`);
931
+ }
932
+ this.db.exec(`UPDATE ${this.tableName} SET ${RECORD_REVISION_COLUMN} = lower(hex(randomblob(16))) WHERE ${RECORD_REVISION_COLUMN} IS NULL`);
933
+ }
934
+ // Create indexes
935
+ if (this.descriptor.indexes && this.descriptor.indexes.length > 0) {
936
+ await this.createIndexes();
937
+ }
938
+ await this.createFtsArtifacts();
939
+ };
940
+ const activeFrame = getActiveTransactionFrame(db, runtime);
941
+ if (activeFrame) {
942
+ await runInSavepoint(runtime, activeFrame, db, async (savepointFrame) => {
943
+ await initializeSchema();
944
+ savepointFrame.onRollback.push(() => {
945
+ this.initialized = false;
946
+ });
947
+ });
232
948
  }
233
- else {
234
- // Auto-create table based on schema
235
- await this.createTableFromSchema();
949
+ else if (this.adapterCacheId === 'storage.localdb') {
950
+ const coordination = await resolveSchemaCoordination(runtime);
951
+ await coordination.initializeSchema(db, () => withImmediateSchemaTransaction(runtime, db, initializeSchema));
236
952
  }
237
- // Create indexes
238
- if (this.descriptor.indexes && this.descriptor.indexes.length > 0) {
239
- await this.createIndexes();
953
+ else {
954
+ await initializeSchema();
240
955
  }
241
956
  this.initialized = true;
242
957
  }
@@ -251,15 +966,15 @@ export class StorageRecord {
251
966
  async ensureDataColumn() {
252
967
  if (!this.db)
253
968
  throw new Error('Database not initialized');
254
- const cols = this.db.query(`PRAGMA table_info(${this.tableName})`).all();
969
+ const cols = this.db.prepare(`PRAGMA table_info(${this.tableName})`).all();
255
970
  if (cols.some((c) => c.name === 'data'))
256
971
  return;
257
972
  // Table lacks a data column. Only recreate if it is empty (no data to lose).
258
973
  const row = this.db
259
- .query(`SELECT COUNT(*) as n FROM ${this.tableName}`)
974
+ .prepare(`SELECT COUNT(*) as n FROM ${this.tableName}`)
260
975
  .get();
261
976
  if (!row || row.n > 0) {
262
- logger.warn(`[StorageRecord] Table '${this.tableName}' has no 'data' column and contains rows. ` +
977
+ this.logger.warn(`[StorageRecord] Table '${this.tableName}' has no 'data' column and contains rows. ` +
263
978
  `CRUD operations may fail. Consider adding a migration to add a 'data TEXT' column.`);
264
979
  return;
265
980
  }
@@ -269,16 +984,69 @@ export class StorageRecord {
269
984
  }
270
985
  /**
271
986
  * Get database connection from adapter.
272
- * Uses the typed ensureReady() / getDatabaseConnection() interface.
987
+ *
988
+ * Prefers the record-scoped connection when the adapter supports one
989
+ * (`getRecordDatabaseConnection`). The adapter instance is a runtime
990
+ * singleton shared by every record that resolves it, while `configure()`
991
+ * mutates that shared instance's `this.database`/`this.config` in place —
992
+ * two records with different descriptors (different `scope`/`dbName`)
993
+ * resolving concurrently can otherwise race each other's `configure()` +
994
+ * `ensureReady()` + `getDatabaseConnection()` sequence and hand back a
995
+ * connection open on the WRONG descriptor's physical database (live
996
+ * forensics: stray `brain_state`/`session_state` tables inside
997
+ * `awareness.db`, frozen partial `awareness_*` tables inside
998
+ * `sessions.db`).
999
+ *
1000
+ * `getRecordDatabaseConnection({ basePath, dbName })` resolves from a
1001
+ * dbPath-keyed pool instead, keyed off THIS call's own `getAdapterConfig()`
1002
+ * output — computed purely from `this.descriptor`/`this.context` (never
1003
+ * from the adapter's shared mutable fields) — so it never depends on that
1004
+ * shared state surviving to the end of this call. This is the record
1005
+ * analogue of the fix already applied to the document-store path (see the
1006
+ * localdb adapter's `getScopedDb` for that incident writeup); it is a
1007
+ * DIFFERENT method (not the same `getScopedDb` pool) because a record's
1008
+ * scope→path mapping (`getBasePath()` below) is its own and does not match
1009
+ * a manifest's scope→path mapping — reusing `getScopedDb` here would
1010
+ * silently retarget every existing local record store (e.g.
1011
+ * `~/.vibes/awareness.db`) to a different, empty file (verified live: for
1012
+ * a global-scope record, `getScopedDb`'s manifest resolution lands on
1013
+ * `resolveGlobalStorageBasePath()` — a different absolute path than this
1014
+ * record's own configured basePath). Adapters that don't implement
1015
+ * `getRecordDatabaseConnection` (memory, a single remote connection, etc.)
1016
+ * fall back to the plain ensureReady() + getDatabaseConnection() interface.
273
1017
  */
274
1018
  async getDatabase() {
275
1019
  const adapter = await this.getAdapter();
276
- await adapter.ensureReady();
277
- const db = adapter.getDatabaseConnection();
278
- if (!db) {
279
- throw new Error(`Adapter '${adapter.id}' returned null from getDatabaseConnection() for record '${this.descriptor.id}'`);
1020
+ if (adapter.getRecordDatabaseConnection) {
1021
+ // Computed purely from this record's own (immutable per-instance)
1022
+ // descriptor/context — not from the adapter's shared mutable
1023
+ // `this.config` — so it can't be clobbered by a sibling record's
1024
+ // concurrent configure() call.
1025
+ const config = await this.getAdapterConfig(this.adapterCacheId ?? adapter.id, this.adapterBasePathSemantics);
1026
+ const target = parseAdapterInput(RecordDatabaseTargetSchema, config, {
1027
+ message: `Storage record '${this.descriptor.id}' has no database location for its adapter.`,
1028
+ code: 'INVALID_INPUT',
1029
+ reason: 'storage-record-database-location-invalid',
1030
+ details: { recordKind: this.descriptor.id, adapterId: adapter.id }
1031
+ });
1032
+ const db = await adapter.getRecordDatabaseConnection({
1033
+ basePath: target.basePath,
1034
+ dbName: target.dbName,
1035
+ runtime: this.getRuntime()
1036
+ });
1037
+ return this.sqlHandleOf(db, adapter.id);
280
1038
  }
281
- return db;
1039
+ await adapter.ensureReady();
1040
+ return this.sqlHandleOf(adapter.getDatabaseConnection(), adapter.id);
1041
+ }
1042
+ /** An adapter's database connection as a SQL handle; anything else (no connection) is refused typed. */
1043
+ sqlHandleOf(connection, adapterId) {
1044
+ return parseAdapterInput(SqlDatabaseHandleSchema, connection, {
1045
+ message: `Adapter '${adapterId}' returned no SQL database connection for record '${this.descriptor.id}'.`,
1046
+ code: 'INTERNAL',
1047
+ reason: 'storage-record-database-unavailable',
1048
+ details: { recordKind: this.descriptor.id, adapterId }
1049
+ });
282
1050
  }
283
1051
  /**
284
1052
  * Create table from schema definition
@@ -288,8 +1056,8 @@ export class StorageRecord {
288
1056
  throw new Error('Database not initialized');
289
1057
  // Build column definitions from schema
290
1058
  const columns = this.buildColumnDefinitions();
291
- const columnDefs = Object.entries(columns)
292
- .map(([name, def]) => `${name} ${def}`)
1059
+ const columnDefs = [...columns]
1060
+ .map(([name, def]) => `"${name.replaceAll('"', '""')}" ${def}`)
293
1061
  .join(',\n ');
294
1062
  const sql = `
295
1063
  CREATE TABLE IF NOT EXISTS ${this.tableName} (
@@ -299,36 +1067,33 @@ export class StorageRecord {
299
1067
  this.db.exec(sql);
300
1068
  }
301
1069
  /**
302
- * Build column definitions from schema and descriptor
1070
+ * Build column definitions (name to SQL definition, in declaration order)
1071
+ * from schema and descriptor
303
1072
  */
304
1073
  buildColumnDefinitions() {
305
- const columns = {};
1074
+ const columns = new Map();
306
1075
  // Primary key
307
1076
  const pkType = this.descriptor.fieldConfig?.[this.primaryKey]?.type || 'TEXT';
308
- columns[this.primaryKey] = `${pkType} PRIMARY KEY`;
1077
+ columns.set(this.primaryKey, `${pkType} PRIMARY KEY`);
309
1078
  // Auto-managed fields
310
1079
  if (this.descriptor.timestamps) {
311
- const createdAt = typeof this.descriptor.timestamps === 'object'
312
- ? this.descriptor.timestamps.createdAt
313
- : 'createdAt';
314
- const updatedAt = typeof this.descriptor.timestamps === 'object'
315
- ? this.descriptor.timestamps.updatedAt
316
- : 'updatedAt';
317
- if (createdAt) {
318
- const createdAtField = typeof createdAt === 'string' ? createdAt : 'createdAt';
319
- columns[createdAtField] = 'TEXT DEFAULT CURRENT_TIMESTAMP';
1080
+ const createdAtField = timestampColumn(this.descriptor.timestamps, 'createdAt');
1081
+ const updatedAtField = timestampColumn(this.descriptor.timestamps, 'updatedAt');
1082
+ if (createdAtField) {
1083
+ columns.set(createdAtField, 'TEXT DEFAULT CURRENT_TIMESTAMP');
320
1084
  }
321
- if (updatedAt) {
322
- const updatedAtField = typeof updatedAt === 'string' ? updatedAt : 'updatedAt';
323
- columns[updatedAtField] = 'TEXT DEFAULT CURRENT_TIMESTAMP';
1085
+ if (updatedAtField) {
1086
+ columns.set(updatedAtField, 'TEXT DEFAULT CURRENT_TIMESTAMP');
324
1087
  }
325
1088
  }
326
- if (this.descriptor.softDelete) {
327
- const deletedAtField = typeof this.descriptor.softDelete === 'string' ? this.descriptor.softDelete : 'deletedAt';
328
- columns[deletedAtField] = 'TEXT';
1089
+ const deletedAtField = this.softDeleteField();
1090
+ if (deletedAtField) {
1091
+ columns.set(deletedAtField, 'TEXT');
329
1092
  }
330
1093
  // Schema introspection is not yet wired; all non-meta fields live in a JSON `data` blob.
331
- columns['data'] = 'TEXT';
1094
+ columns.set('data', 'TEXT');
1095
+ if (this.descriptor.versioning)
1096
+ columns.set(RECORD_REVISION_COLUMN, 'TEXT');
332
1097
  return columns;
333
1098
  }
334
1099
  /**
@@ -337,12 +1102,13 @@ export class StorageRecord {
337
1102
  async createIndexes() {
338
1103
  if (!this.db)
339
1104
  throw new Error('Database not initialized');
1105
+ const db = this.db;
340
1106
  // Detect whether the table uses JSON blob storage (has a 'data' column).
341
1107
  // ensureDataColumn() may have recreated the table as (primaryKey, data TEXT),
342
1108
  // so direct column references from the descriptor won't exist. Use json_extract
343
1109
  // expressions instead so indexes still function and unique constraints are enforced.
344
- const tableInfo = this.db
345
- .query(`PRAGMA table_info(${this.tableName})`)
1110
+ const tableInfo = db
1111
+ .prepare(`PRAGMA table_info(${this.tableName})`)
346
1112
  .all();
347
1113
  const hasDataColumn = tableInfo.some((c) => c.name === 'data');
348
1114
  const toSnakeCase = (value) => value
@@ -351,13 +1117,14 @@ export class StorageRecord {
351
1117
  .toLowerCase();
352
1118
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
353
1119
  for (const index of this.descriptor.indexes || []) {
354
- const indexName = index.name || `idx_${this.tableName}_${index.fields.join('_')}`;
1120
+ const indexName = recordIndexName(this.tableName, index);
355
1121
  const unique = index.unique ? 'UNIQUE' : '';
356
1122
  let fieldExpr;
357
1123
  let whereExpr = '';
358
1124
  if (hasDataColumn) {
359
- // JSON blob mode: reference fields via json_extract so indexes work
360
- fieldExpr = index.fields.map((f) => `json_extract(data, '$.${f}')`).join(', ');
1125
+ // JSON blob mode: reference fields as queries compile them (`filterColumn`:
1126
+ // the primary-key column, else json_extract) so an index serves its query.
1127
+ fieldExpr = index.fields.map((f) => this.filterColumn(f)).join(', ');
361
1128
  if (index.where) {
362
1129
  // Replace field name references in the WHERE clause with json_extract.
363
1130
  // Some descriptors still use snake_case predicates (legacy migration SQL style).
@@ -367,7 +1134,7 @@ export class StorageRecord {
367
1134
  // which would corrupt an already-substituted json_extract expression.
368
1135
  const aliases = [...new Set([f, toSnakeCase(f)])];
369
1136
  for (const alias of aliases) {
370
- w = w.replace(new RegExp(`\\b${escapeRegExp(alias)}\\b`, 'g'), `json_extract(data, '$.${f}')`);
1137
+ w = w.replace(new RegExp(`\\b${escapeRegExp(alias)}\\b`, 'g'), this.filterColumn(f));
371
1138
  }
372
1139
  }
373
1140
  whereExpr = `WHERE ${w}`;
@@ -378,31 +1145,155 @@ export class StorageRecord {
378
1145
  fieldExpr = index.fields.join(', ');
379
1146
  whereExpr = index.where ? `WHERE ${index.where}` : '';
380
1147
  }
381
- const sql = `CREATE ${unique} INDEX IF NOT EXISTS ${indexName} ON ${this.tableName} (${fieldExpr}) ${whereExpr}`;
382
- try {
383
- this.db.query(sql).run();
384
- }
385
- catch (error) {
386
- if (index.unique) {
387
- // Unique index failures must be fatal — they protect data integrity
388
- throw error;
1148
+ const indexIdentifier = /^[A-Za-z_][A-Za-z0-9_]*$/.test(indexName) ? indexName : `"${indexName.replace(/"/g, '""')}"`;
1149
+ const sql = `CREATE ${unique} INDEX IF NOT EXISTS ${indexIdentifier} ON ${this.tableName} (${fieldExpr}) ${whereExpr}`;
1150
+ const existing = db.prepare("SELECT sql, tbl_name FROM sqlite_master WHERE type = 'index' AND name = ?").all(indexName)[0];
1151
+ if (existing) {
1152
+ if (existing.tbl_name !== this.tableName || !existing.sql) {
1153
+ throw new CapabilityError('The declared record index belongs to another schema.', 'CONFLICT', {
1154
+ reason: 'record-index-owner-conflict'
1155
+ });
389
1156
  }
390
- // Non-unique index creation can fail if descriptor field names don't match
391
- // actual column names (e.g. camelCase vs snake_case mismatch). Log and continue.
392
- logger.warn(`[StorageRecord] Skipping index '${indexName}' on '${this.tableName}': ${error instanceof Error ? error.message : String(error)}`);
1157
+ // SQLite omits IF NOT EXISTS from stored DDL. Preserve quoted values
1158
+ // while ignoring statement whitespace, so unchanged indexes stay intact.
1159
+ const definition = (statement) => statement
1160
+ .replace(/^(\s*CREATE\s+(?:UNIQUE\s+)?INDEX\s+)IF\s+NOT\s+EXISTS\s+/i, '$1')
1161
+ .replace(/'(?:[^']|'')*'|"(?:[^"]|"")*"|\s+/g, (token) => /^\s+$/.test(token) ? ' ' : token)
1162
+ .trim().replace(/;$/, '');
1163
+ if (definition(existing.sql) === definition(sql))
1164
+ continue;
1165
+ // Initialization holds BEGIN IMMEDIATE: a failed replacement rolls
1166
+ // back the drop, preserving the previous index and unique constraint.
1167
+ db.exec(`DROP INDEX "${indexName.replace(/"/g, '""')}"`);
1168
+ db.prepare(sql).run();
1169
+ continue;
1170
+ }
1171
+ if (index.unique) {
1172
+ // Unique index failures must be fatal — they protect data integrity.
1173
+ db.prepare(sql).run();
1174
+ continue;
1175
+ }
1176
+ // Non-unique index creation can fail if descriptor field names don't match
1177
+ // actual column names (e.g. camelCase vs snake_case mismatch). Log and continue.
1178
+ const failure = failureOfSync(() => {
1179
+ db.prepare(sql).run();
1180
+ });
1181
+ if (failure) {
1182
+ this.logger.warn('[StorageRecord] Skipping a non-unique index that could not be created', {
1183
+ index: indexName,
1184
+ table: this.tableName,
1185
+ error: failure
1186
+ });
393
1187
  }
394
1188
  }
395
1189
  }
1190
+ sqlString(value) {
1191
+ return `'${value.replace(/'/g, "''")}'`;
1192
+ }
1193
+ jsonPathForField(field) {
1194
+ return `$."${field.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
1195
+ }
1196
+ validateTokenizer(tokenizer) {
1197
+ if (!/^[A-Za-z0-9_\s]+$/.test(tokenizer)) {
1198
+ throw new Error(`Invalid FTS tokenizer for ${this.tableName}: ${tokenizer}`);
1199
+ }
1200
+ return tokenizer.trim() || 'unicode61';
1201
+ }
1202
+ async createFtsArtifacts() {
1203
+ if (!this.db || !this.descriptor.fts)
1204
+ return;
1205
+ const fields = this.descriptor.fts.fields.map((field) => String(field));
1206
+ if (fields.length === 0)
1207
+ return;
1208
+ const tableInfo = this.db
1209
+ .prepare(`PRAGMA table_info(${this.tableName})`)
1210
+ .all();
1211
+ const hasDataColumn = tableInfo.some((column) => column.name === 'data');
1212
+ const ftsRawTableName = this.descriptor.fts.tableName || `${this.tableName}_fts`;
1213
+ const tableName = this.quoteIdentifier(this.tableName);
1214
+ const ftsTableName = this.quoteIdentifier(ftsRawTableName);
1215
+ const insertTriggerName = this.quoteIdentifier(`${ftsRawTableName}_ai`);
1216
+ const deleteTriggerName = this.quoteIdentifier(`${ftsRawTableName}_ad`);
1217
+ const updateTriggerName = this.quoteIdentifier(`${ftsRawTableName}_au`);
1218
+ const quotedFields = fields.map((field) => this.quoteIdentifier(field));
1219
+ const fieldList = quotedFields.join(', ');
1220
+ const tokenizer = this.validateTokenizer(this.descriptor.fts.tokenizer || 'unicode61');
1221
+ const valueExpression = (source, field) => {
1222
+ if (hasDataColumn) {
1223
+ const dataRef = source ? `${source}.data` : 'data';
1224
+ return `json_extract(${dataRef}, ${this.sqlString(this.jsonPathForField(field))})`;
1225
+ }
1226
+ const tableRef = source ? `${source}.` : '';
1227
+ return `${tableRef}${this.quoteIdentifier(field)}`;
1228
+ };
1229
+ const newValues = fields.map((field) => valueExpression('NEW', field)).join(', ');
1230
+ const selectValues = fields.map((field) => valueExpression(null, field)).join(', ');
1231
+ const triggerNames = new Set([
1232
+ `${ftsRawTableName}_ai`,
1233
+ `${ftsRawTableName}_ad`,
1234
+ `${ftsRawTableName}_au`,
1235
+ `${this.tableName}_fts_insert`,
1236
+ `${this.tableName}_fts_delete`,
1237
+ `${this.tableName}_fts_update`
1238
+ ]);
1239
+ for (const row of this.db
1240
+ .prepare(`SELECT name FROM sqlite_master WHERE type = 'trigger' AND tbl_name = ? AND instr(sql, ?) > 0`)
1241
+ .all(this.tableName, ftsRawTableName)) {
1242
+ triggerNames.add(row.name);
1243
+ }
1244
+ for (const triggerName of triggerNames) {
1245
+ this.db.exec(`DROP TRIGGER IF EXISTS ${this.quoteIdentifier(triggerName)}`);
1246
+ }
1247
+ this.db.exec(`DROP TABLE IF EXISTS ${ftsTableName}`);
1248
+ this.db.exec(`
1249
+ CREATE VIRTUAL TABLE IF NOT EXISTS ${ftsTableName} USING fts5(
1250
+ ${fieldList},
1251
+ tokenize=${this.sqlString(tokenizer)}
1252
+ )
1253
+ `);
1254
+ this.execStatements(`
1255
+ CREATE TRIGGER IF NOT EXISTS ${insertTriggerName} AFTER INSERT ON ${tableName}
1256
+ BEGIN
1257
+ INSERT INTO ${ftsTableName}(rowid, ${fieldList})
1258
+ VALUES (NEW.rowid, ${newValues});
1259
+ END
1260
+ `);
1261
+ this.execStatements(`
1262
+ CREATE TRIGGER IF NOT EXISTS ${deleteTriggerName} AFTER DELETE ON ${tableName}
1263
+ BEGIN
1264
+ DELETE FROM ${ftsTableName} WHERE rowid = OLD.rowid;
1265
+ END
1266
+ `);
1267
+ this.execStatements(`
1268
+ CREATE TRIGGER IF NOT EXISTS ${updateTriggerName} AFTER UPDATE ON ${tableName}
1269
+ BEGIN
1270
+ DELETE FROM ${ftsTableName} WHERE rowid = OLD.rowid;
1271
+ INSERT INTO ${ftsTableName}(rowid, ${fieldList})
1272
+ VALUES (NEW.rowid, ${newValues});
1273
+ END
1274
+ `);
1275
+ this.db
1276
+ .prepare(`
1277
+ INSERT INTO ${ftsTableName}(rowid, ${fieldList})
1278
+ SELECT rowid, ${selectValues}
1279
+ FROM ${tableName}
1280
+ `)
1281
+ .run();
1282
+ }
396
1283
  /**
397
1284
  * Execute one or more SQL statements, splitting on `;` and running each
398
1285
  * via the prepared-statement path so bun:sqlite does not misinterpret `$`
399
1286
  * tokens inside string literals (e.g. json_extract paths).
1287
+ *
1288
+ * Splitting is statement-aware: `;` inside string literals, comments, or a
1289
+ * `CREATE TRIGGER ... BEGIN ... END` body does NOT terminate a statement —
1290
+ * a naive split truncates trigger bodies and SQLite throws "incomplete input".
400
1291
  */
401
1292
  execStatements(sql) {
402
1293
  if (!this.db)
403
1294
  throw new Error('Database not initialized');
404
- for (const stmt of sql.split(';').map((s) => s.trim()).filter(Boolean)) {
405
- this.db.query(stmt).run();
1295
+ for (const stmt of splitSqlStatements(sql)) {
1296
+ this.db.prepare(stmt).run();
406
1297
  }
407
1298
  }
408
1299
  /**
@@ -411,9 +1302,10 @@ export class StorageRecord {
411
1302
  async runMigrations() {
412
1303
  if (!this.db)
413
1304
  throw new Error('Database not initialized');
1305
+ const db = this.db;
414
1306
  const migrations = this.descriptor.migrations || [];
415
1307
  // Create migrations tracking table if not exists
416
- this.db.exec(`
1308
+ db.exec(`
417
1309
  CREATE TABLE IF NOT EXISTS _migrations_${this.tableName} (
418
1310
  version INTEGER PRIMARY KEY,
419
1311
  name TEXT,
@@ -421,8 +1313,8 @@ export class StorageRecord {
421
1313
  )
422
1314
  `);
423
1315
  // Get applied migrations
424
- const applied = this.db
425
- .query(`SELECT version FROM _migrations_${this.tableName}`)
1316
+ const applied = db
1317
+ .prepare(`SELECT version FROM _migrations_${this.tableName}`)
426
1318
  .all()
427
1319
  .map((r) => r.version);
428
1320
  // Run pending migrations
@@ -430,17 +1322,21 @@ export class StorageRecord {
430
1322
  if (applied.includes(migration.version))
431
1323
  continue;
432
1324
  try {
433
- if (typeof migration.up === 'string') {
434
- this.execStatements(migration.up);
1325
+ // The `up` step: SQL text runs statement by statement; a function runs against this database.
1326
+ const upSql = MigrationSqlSchema.safeParse(migration.up);
1327
+ if (upSql.success) {
1328
+ this.execStatements(upSql.data);
435
1329
  }
436
- else if (typeof migration.up === 'function') {
437
- await migration.up(this.db);
1330
+ else {
1331
+ const upFunction = MigrationFunctionSchema.safeParse(migration.up);
1332
+ if (upFunction.success)
1333
+ await upFunction.data(db);
438
1334
  }
439
1335
  // Record migration
440
- this.db
441
- .query(`INSERT INTO _migrations_${this.tableName} (version, name) VALUES (?, ?)`)
1336
+ db
1337
+ .prepare(`INSERT INTO _migrations_${this.tableName} (version, name) VALUES (?, ?)`)
442
1338
  .run(migration.version, migration.name || '');
443
- logger.debug(`[StorageRecord] Applied migration ${migration.version} for ${this.tableName}`);
1339
+ this.logger.debug(`[StorageRecord] Applied migration ${migration.version} for ${this.tableName}`);
444
1340
  }
445
1341
  catch (error) {
446
1342
  // A "no such table" failure typically means the consumer is
@@ -449,13 +1345,12 @@ export class StorageRecord {
449
1345
  // layer catches the throw and renders empty state; demote the
450
1346
  // log so the spurious noise doesn't reach the user. Real
451
1347
  // migration failures still surface at warn level + the throw.
452
- const message = error instanceof Error ? error.message : String(error);
453
- const isTableMissing = /no such table/i.test(message);
1348
+ const isTableMissing = error instanceof Error && isRecordTableMissing(error);
454
1349
  if (isTableMissing) {
455
- logger.debug(`[StorageRecord] Migration ${migration.version} skipped for ${this.tableName} (table not yet initialized)`);
1350
+ this.logger.debug(`[StorageRecord] Migration ${migration.version} skipped for ${this.tableName} (table not yet initialized)`);
456
1351
  }
457
1352
  else {
458
- logger.error(`[StorageRecord] Migration ${migration.version} failed for ${this.tableName}:`, toErrorContext(error));
1353
+ this.logger.error(`[StorageRecord] Migration ${migration.version} failed for ${this.tableName}:`, toErrorContext(error));
459
1354
  }
460
1355
  throw error;
461
1356
  }
@@ -463,14 +1358,14 @@ export class StorageRecord {
463
1358
  }
464
1359
  // CREATE
465
1360
  async create(data) {
1361
+ return this.createFields(this.fieldsOf({ ...data }));
1362
+ }
1363
+ /** Create one row from its own (already copied) field map. */
1364
+ async createFields(record) {
466
1365
  await this.ensureInitialized();
467
- // Prepare record
468
- const record = { ...data };
469
1366
  // Auto-generate ID (must happen before validation so the schema check passes)
470
1367
  if (this.descriptor.autoId) {
471
- const idField = typeof this.descriptor.autoId === 'object' && this.descriptor.autoId.field
472
- ? this.descriptor.autoId.field
473
- : this.primaryKey;
1368
+ const idField = this.autoIdOptions()?.field || this.primaryKey;
474
1369
  if (!record[idField]) {
475
1370
  record[idField] = this.generateId();
476
1371
  }
@@ -481,9 +1376,8 @@ export class StorageRecord {
481
1376
  applyCreateTimestamps(record, this.descriptor.timestamps, now);
482
1377
  }
483
1378
  // Execute beforeCreate hook (may set additional required fields)
484
- if (this.descriptor.beforeCreate) {
485
- await this.descriptor.beforeCreate(record);
486
- }
1379
+ await this.runHook('beforeCreate', record);
1380
+ this.fieldsOf(record);
487
1381
  // Validate after all auto-fields are populated
488
1382
  if (this.descriptor.validateOnWrite !== false) {
489
1383
  const validation = await this.validate(record);
@@ -494,13 +1388,52 @@ export class StorageRecord {
494
1388
  // Insert into database
495
1389
  const id = record[this.primaryKey];
496
1390
  const dataJson = JSON.stringify(record);
497
- this.db.query(`INSERT INTO ${this.tableName} (${this.primaryKey}, data) VALUES (?, ?)`).run(id, dataJson);
498
- // Execute afterCreate hook
499
- if (this.descriptor.afterCreate) {
500
- await this.descriptor.afterCreate(record);
1391
+ const db = await this.acquireConnection();
1392
+ const inserted = this.descriptor.versioning
1393
+ ? db.prepare(`INSERT INTO ${this.tableName} (${this.primaryKey}, data, ${RECORD_REVISION_COLUMN}) VALUES (?, ?, ?) ON CONFLICT (${this.primaryKey}) DO NOTHING`).run(id, dataJson, crypto.randomUUID())
1394
+ : db.prepare(`INSERT INTO ${this.tableName} (${this.primaryKey}, data) VALUES (?, ?) ON CONFLICT (${this.primaryKey}) DO NOTHING`).run(id, dataJson);
1395
+ if (inserted.changes === 0) {
1396
+ throw new CapabilityError(`Record with duplicate ${this.primaryKey} already exists in ${this.tableName}.`, 'CONFLICT', {
1397
+ reason: 'record-identity-conflict',
1398
+ retryable: true,
1399
+ details: { tableName: this.tableName, primaryKey: this.primaryKey }
1400
+ });
501
1401
  }
1402
+ // Execute afterCreate hook
1403
+ await this.runHook('afterCreate', record);
1404
+ // In-process notification only (D-RC11): the hook bus dies with this
1405
+ // runtime and never delivers across processes.
502
1406
  this.emitLifecycle('afterCreate', record);
503
- return record;
1407
+ // Durable delivery the record kind declares: a Workers job keyed by this
1408
+ // record's id, enqueued before create() returns. A gated kind is claimed
1409
+ // by the reconcile once its gate admits the pass (one eligibility decision
1410
+ // per pass, not per row); the row itself is the durable intent.
1411
+ if (this.descriptor.delivery && !this.descriptor.delivery.gate) {
1412
+ await this.enqueueDelivery(String(id));
1413
+ }
1414
+ return this.recordOf(record);
1415
+ }
1416
+ /**
1417
+ * Enqueue this record kind's declared delivery job for one created row. The
1418
+ * row is already persisted, so a failed enqueue does not fail the write: it
1419
+ * is logged at error with its value-free facts, the row stays pending, and
1420
+ * the Workers consumer enqueues it again when it starts
1421
+ * (`requeuePendingDeliveries`).
1422
+ */
1423
+ async enqueueDelivery(recordId) {
1424
+ const delivery = this.descriptor.delivery;
1425
+ if (!delivery)
1426
+ return;
1427
+ const enqueued = await outcomeOf(() => enqueueRecordDelivery(this.getRuntime(), this.descriptor.id, delivery, recordId));
1428
+ if (enqueued.ok)
1429
+ return;
1430
+ this.logger.error('Record delivery could not be enqueued; the record stays pending until the Workers consumer re-enqueues it', {
1431
+ reason: 'storage-record-delivery-enqueue-failed',
1432
+ recordKind: this.descriptor.id,
1433
+ recordId,
1434
+ job: delivery.job,
1435
+ error: enqueued.failure
1436
+ });
504
1437
  }
505
1438
  async createMany(data, options) {
506
1439
  const results = [];
@@ -515,19 +1448,22 @@ export class StorageRecord {
515
1448
  // READ
516
1449
  async get(id, options) {
517
1450
  await this.ensureInitialized();
518
- const row = this.db.query(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
1451
+ const row = this.db.prepare(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
519
1452
  if (!row)
520
1453
  return null;
521
- const record = JSON.parse(row.data);
1454
+ const record = this.decodeRecord(row.data);
522
1455
  // Load relations if requested
523
1456
  if (options?.include) {
524
- await this.loadRelations(record, options.include);
1457
+ await loadRecordRelations(this.relationOwner, record, options.include);
525
1458
  }
526
1459
  // Validate on read if configured
527
1460
  if (this.descriptor.validateOnRead) {
528
1461
  const validation = await this.validate(record);
529
1462
  if (!validation.success) {
530
- logger.warn(`[StorageRecord] Validation failed on read:`, toErrorContext(validation.errors));
1463
+ this.logger.warn('[StorageRecord] Validation failed on read', {
1464
+ recordKind: this.descriptor.id,
1465
+ issues: validation.errors?.issues.length ?? 0
1466
+ });
531
1467
  }
532
1468
  }
533
1469
  return record;
@@ -544,15 +1480,20 @@ export class StorageRecord {
544
1480
  if (ids.length === 0)
545
1481
  return [];
546
1482
  const placeholders = ids.map(() => '?').join(', ');
547
- const rows = this.db.query(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} IN (${placeholders})`).all(...ids);
548
- let records = rows.map((row) => JSON.parse(row.data));
1483
+ const rows = this.db.prepare(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} IN (${placeholders})`).all(...ids);
1484
+ let records = rows.map((row) => this.decodeRecord(row.data));
549
1485
  // Load relations if requested
550
- if (options?.include) {
551
- records = await Promise.all(records.map((record) => this.loadRelations(record, options.include)));
1486
+ const include = options?.include;
1487
+ if (include) {
1488
+ records = await Promise.all(records.map((record) => loadRecordRelations(this.relationOwner, record, include)));
552
1489
  }
553
1490
  return records;
554
1491
  }
555
1492
  async list(options) {
1493
+ return this.listRecords(options);
1494
+ }
1495
+ /** `list()` over any relation names: the bulk paths (stream, updateMany, deleteMany) list through it. */
1496
+ async listRecords(options) {
556
1497
  await this.ensureInitialized();
557
1498
  // Build query
558
1499
  let sql = `SELECT data FROM ${this.tableName}`;
@@ -568,19 +1509,23 @@ export class StorageRecord {
568
1509
  }
569
1510
  }
570
1511
  // Soft delete filter
571
- if (this.descriptor.softDelete && !options?.includeDeleted) {
572
- const deletedAtField = typeof this.descriptor.softDelete === 'string' ? this.descriptor.softDelete : 'deletedAt';
1512
+ const deletedAtField = this.softDeleteField();
1513
+ if (deletedAtField && !options?.includeDeleted) {
573
1514
  sql += hasWhereConditions ? ` AND ` : ` WHERE `;
574
1515
  sql += `json_extract(data, '$.${deletedAtField}') IS NULL`;
575
1516
  }
1517
+ // A bounded page does not scan the entire match set merely to report
1518
+ // metadata. Callers that need it explicitly request the independent count.
1519
+ let total;
1520
+ if (options?.limit === undefined || options.includeTotal === true) {
1521
+ const countSql = sql.replace('SELECT data', 'SELECT COUNT(*) as count');
1522
+ const countRow = this.db.prepare(countSql).get(...params);
1523
+ total = countRow?.count ?? 0;
1524
+ }
576
1525
  // ORDER BY
577
1526
  if (options?.orderBy) {
578
1527
  sql += ` ORDER BY ${this.buildOrderBy(options.orderBy)}`;
579
1528
  }
580
- // COUNT query for total
581
- const countSql = sql.replace('SELECT data', 'SELECT COUNT(*) as count');
582
- const countRow = this.db.query(countSql).get(...params);
583
- const total = countRow?.count || 0;
584
1529
  // LIMIT and OFFSET
585
1530
  if (options?.limit) {
586
1531
  sql += ` LIMIT ?`;
@@ -591,17 +1536,20 @@ export class StorageRecord {
591
1536
  params.push(options.offset);
592
1537
  }
593
1538
  // Execute query
594
- const rows = this.db.query(sql).all(...params);
595
- let items = rows.map((row) => JSON.parse(row.data));
1539
+ const rows = this.db.prepare(sql).all(...params);
1540
+ let items = rows.map((row) => this.decodeRecord(row.data));
596
1541
  // Load relations if requested
597
- if (options?.include) {
598
- items = await Promise.all(items.map((item) => this.loadRelations(item, options.include)));
1542
+ const include = options?.include;
1543
+ if (include) {
1544
+ items = await Promise.all(items.map((item) => loadRecordRelations(this.relationOwner, item, include)));
599
1545
  }
600
- return {
1546
+ const result = {
601
1547
  items,
602
- total,
603
1548
  hasMore: options?.limit ? items.length === options.limit : false
604
1549
  };
1550
+ if (total !== undefined)
1551
+ result.total = total;
1552
+ return result;
605
1553
  }
606
1554
  async findFirst(options) {
607
1555
  const result = await this.list({ ...options, limit: 1 });
@@ -616,7 +1564,7 @@ export class StorageRecord {
616
1564
  }
617
1565
  async exists(id) {
618
1566
  await this.ensureInitialized();
619
- const row = this.db.query(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
1567
+ const row = this.db.prepare(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
620
1568
  return (row?.count || 0) > 0;
621
1569
  }
622
1570
  async count(where) {
@@ -633,24 +1581,46 @@ export class StorageRecord {
633
1581
  }
634
1582
  }
635
1583
  // Soft delete filter
636
- if (this.descriptor.softDelete) {
637
- const deletedAtField = typeof this.descriptor.softDelete === 'string' ? this.descriptor.softDelete : 'deletedAt';
1584
+ const deletedAtField = this.softDeleteField();
1585
+ if (deletedAtField) {
638
1586
  sql += hasWhereConditions ? ` AND ` : ` WHERE `;
639
1587
  sql += `json_extract(data, '$.${deletedAtField}') IS NULL`;
640
1588
  }
641
- const row = this.db.query(sql).get(...params);
1589
+ const row = this.db.prepare(sql).get(...params);
642
1590
  return row?.count || 0;
643
1591
  }
644
1592
  // UPDATE
645
- async update(id, data, _options) {
1593
+ async update(id, data, options) {
1594
+ return this.updateFields(id, data, options);
1595
+ }
1596
+ /** One update of a row's fields; a `where` precondition that no longer holds throws its OptimisticLockError. */
1597
+ async updateFields(id, data, options) {
1598
+ const updated = await this.updateWithConditions(id, data, options);
1599
+ if (updated instanceof OptimisticLockError)
1600
+ throw updated;
1601
+ return updated;
1602
+ }
1603
+ /**
1604
+ * One conditional update: the updated record, or the optimistic-lock
1605
+ * conflict when its precondition no longer holds (the drizzle record's
1606
+ * same contract), so a guarded bulk write skips a conflicting row without
1607
+ * catching anything.
1608
+ */
1609
+ async updateWithConditions(id, data, options, bulkWhere) {
646
1610
  await this.ensureInitialized();
647
1611
  // Get existing record
648
1612
  const existing = await this.get(id);
649
1613
  if (!existing) {
650
- throw new Error(`Record with ${this.primaryKey}=${id} not found in ${this.tableName}`);
1614
+ throw new CapabilityError(`Record with ${this.primaryKey}=${id} not found in ${this.tableName}`, 'NOT_FOUND', {
1615
+ reason: 'record-absent',
1616
+ retryable: false,
1617
+ details: { tableName: this.tableName, id }
1618
+ });
651
1619
  }
652
1620
  // Merge updates
653
- const updated = { ...existing, ...data };
1621
+ const snapshot = this.fieldsOf(structuredClone(existing));
1622
+ const updated = this.fieldsOf({ ...existing, ...data });
1623
+ const additions = applySetAdditions(updated, data, options);
654
1624
  // Auto-manage timestamps
655
1625
  if (this.descriptor.timestamps) {
656
1626
  applyUpdateTimestamp(updated, data, this.descriptor.timestamps, new Date().toISOString());
@@ -663,34 +1633,105 @@ export class StorageRecord {
663
1633
  }
664
1634
  }
665
1635
  // Execute beforeUpdate hook
666
- if (this.descriptor.beforeUpdate) {
667
- await this.descriptor.beforeUpdate(updated);
1636
+ await this.runHook('beforeUpdate', updated);
1637
+ this.fieldsOf(updated);
1638
+ // A record's identity is its key column, and the stored JSON carries the same id; a filter
1639
+ // on the primary key reads the column. An update (or a beforeUpdate hook) naming another
1640
+ // primary key is refused, so the two never diverge.
1641
+ const nextKey = updated[this.primaryKey];
1642
+ if (nextKey !== undefined && nextKey !== null && String(nextKey) !== id) {
1643
+ throw new CapabilityError('A record update cannot change its primary key.', 'INVALID_INPUT', {
1644
+ reason: 'record-primary-key-immutable',
1645
+ retryable: false,
1646
+ details: { recordKind: this.descriptor.id, field: this.primaryKey }
1647
+ });
668
1648
  }
669
- // Update in database
670
- const dataJson = JSON.stringify(updated);
671
- this.db.query(`UPDATE ${this.tableName} SET data = ? WHERE ${this.primaryKey} = ?`).run(dataJson, id);
672
- // Execute afterUpdate hook
673
- if (this.descriptor.afterUpdate) {
674
- await this.descriptor.afterUpdate(updated);
1649
+ // Update in database. Honor the declared `where` precondition
1650
+ // (UpdateOptions.where — "update only if conditions match") ATOMICALLY:
1651
+ // the precondition rides in the UPDATE's WHERE, so SQLite evaluates it
1652
+ // against the current row at write time in a single statement. If a
1653
+ // concurrent writer moved the row between our get() and here, 0 rows
1654
+ // match → changes === 0 → conflict. This is a real compare-and-set, not
1655
+ // a read-check-write (which would leave a TOCTOU window — two writers
1656
+ // both passing an in-memory check before either writes). Single-record
1657
+ // update() previously ignored this param entirely (silent unconditional
1658
+ // write); the lost-update hazard it exists to prevent (TF-05) is now
1659
+ // actually closed for every record type.
1660
+ const patch = updatePatch(snapshot, updated, data, additions);
1661
+ const argumentsSql = [];
1662
+ const params = [];
1663
+ const removals = [];
1664
+ for (const [field, value] of Object.entries(patch)) {
1665
+ const path = `$.${JSON.stringify(field)}`;
1666
+ if (value === undefined) {
1667
+ removals.push(path);
1668
+ continue;
1669
+ }
1670
+ argumentsSql.push('?, json(?)');
1671
+ params.push(path, JSON.stringify(value));
1672
+ }
1673
+ for (const [field, value] of Object.entries(additions)) {
1674
+ const path = `$.${field}`;
1675
+ argumentsSql.push(`?, json(CASE WHEN EXISTS (
1676
+ SELECT 1 FROM json_each(COALESCE(json_extract(data, ?), '[]'))
1677
+ WHERE type = 'text' AND value = ?
1678
+ ) THEN COALESCE(json_extract(data, ?), '[]')
1679
+ ELSE json_insert(COALESCE(json_extract(data, ?), '[]'), '$[#]', ?) END)`);
1680
+ params.push(path, path, value, path, path, value);
1681
+ }
1682
+ let expression = argumentsSql.length ? `json_set(data, ${argumentsSql.join(', ')})` : 'data';
1683
+ if (removals.length) {
1684
+ expression = `json_remove(${expression}, ${removals.map(() => '?').join(', ')})`;
1685
+ params.push(...removals);
1686
+ }
1687
+ let revisionAssignment = '';
1688
+ if (this.descriptor.versioning) {
1689
+ revisionAssignment = `, ${RECORD_REVISION_COLUMN} = ?`;
1690
+ params.push(crypto.randomUUID());
1691
+ }
1692
+ let sql = `UPDATE ${this.tableName} SET data = ${expression}${revisionAssignment} WHERE ${this.primaryKey} = ?`;
1693
+ params.push(id);
1694
+ // Build the bulk and additional preconditions independently: overlapping
1695
+ // fields must both match the live row, never overwrite each other.
1696
+ for (const where of [bulkWhere, options?.where]) {
1697
+ if (!where)
1698
+ continue;
1699
+ const { clause, values } = this.buildWhereClause(where);
1700
+ if (clause) {
1701
+ sql += ` AND (${clause})`;
1702
+ params.push(...values);
1703
+ }
675
1704
  }
676
- this.emitLifecycle('afterUpdate', updated);
677
- return updated;
1705
+ const db = await this.acquireConnection();
1706
+ const result = db.prepare(`${sql} RETURNING data`).get(...params);
1707
+ const precondition = options?.where ?? bulkWhere;
1708
+ if (precondition && !result) {
1709
+ // The row exists (get() above) but the precondition no longer holds.
1710
+ return new OptimisticLockError(this.tableName, String(id), precondition);
1711
+ }
1712
+ if (!result)
1713
+ throw new Error(`Record with ${this.primaryKey}=${id} not found in ${this.tableName}`);
1714
+ // RETURNING reads the same JSON record just patched by SQLite.
1715
+ const persisted = this.decodeFields(result.data);
1716
+ // Execute afterUpdate hook
1717
+ await this.runHook('afterUpdate', persisted);
1718
+ this.emitLifecycle('afterUpdate', persisted);
1719
+ return this.recordOf(persisted);
678
1720
  }
679
1721
  async updateMany(where, data, options) {
680
1722
  await this.ensureInitialized();
681
1723
  // Find matching records
682
- const result = await this.list({ where, select: [this.primaryKey] });
1724
+ const result = await this.listRecords({ where });
683
1725
  const ids = result.items.map((item) => this.readPrimaryKey(item));
684
- // Update each record
1726
+ // Update each record under the same filter, so a row that stopped
1727
+ // matching between the list and its update (another writer claimed it)
1728
+ // is skipped instead of overwritten: updateMany is a guarded write.
685
1729
  let count = 0;
686
1730
  for (const id of ids) {
687
- try {
688
- await this.update(id, data, options);
689
- count++;
690
- }
691
- catch (error) {
692
- logger.error(`[StorageRecord] Failed to update record ${id}:`, toErrorContext(error));
693
- }
1731
+ const updated = await this.updateWithConditions(id, data, options, where);
1732
+ if (updated instanceof OptimisticLockError)
1733
+ continue;
1734
+ count++;
694
1735
  }
695
1736
  return { count, success: count > 0 };
696
1737
  }
@@ -700,75 +1741,131 @@ export class StorageRecord {
700
1741
  return this.update(id, update);
701
1742
  }
702
1743
  else {
703
- return this.create({ ...create, [this.primaryKey]: id });
1744
+ return this.createFields(this.fieldsOf({ ...create, [this.primaryKey]: id }));
704
1745
  }
705
1746
  }
706
1747
  // DELETE
707
1748
  async delete(id, options) {
1749
+ return this.deleteWithConditions(id, options);
1750
+ }
1751
+ async deleteWithConditions(id, options, bulkWhere) {
708
1752
  await this.ensureInitialized();
1753
+ const predicates = [`${this.primaryKey} = ?`];
1754
+ const values = [id];
1755
+ for (const where of [bulkWhere, options?.where]) {
1756
+ if (where === undefined)
1757
+ continue;
1758
+ const condition = this.buildDeleteWhereClause(where);
1759
+ if (condition.clause) {
1760
+ predicates.push(`(${condition.clause})`);
1761
+ values.push(...condition.values);
1762
+ }
1763
+ }
709
1764
  // Check if exists
710
- let existing = await this.get(id, { include: undefined });
1765
+ let existing = null;
1766
+ const live = await this.get(id, { include: undefined });
1767
+ if (live)
1768
+ existing = this.fieldsOf(live);
711
1769
  if (!existing && this.descriptor.softDelete && options?.force) {
712
- const row = this.db.query(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
713
- existing = row ? JSON.parse(row.data) : null;
1770
+ const forceDb = await this.acquireConnection();
1771
+ const row = forceDb.prepare(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
1772
+ existing = row ? this.decodeFields(row.data) : null;
714
1773
  }
715
1774
  if (!existing) {
716
1775
  return { count: 0, success: false };
717
1776
  }
718
1777
  // Execute beforeDelete hook
719
- if (this.descriptor.beforeDelete) {
720
- await this.descriptor.beforeDelete(existing);
721
- }
722
- // Soft delete or hard delete
723
- if (this.descriptor.softDelete && !options?.force) {
724
- const deletedAtField = typeof this.descriptor.softDelete === 'string' ? this.descriptor.softDelete : 'deletedAt';
725
- const deleted = {
726
- ...existing,
727
- [deletedAtField]: new Date().toISOString()
728
- };
729
- const dataJson = JSON.stringify(deleted);
730
- this.db.query(`UPDATE ${this.tableName} SET data = ? WHERE ${this.primaryKey} = ?`).run(dataJson, id);
1778
+ await this.runHook('beforeDelete', existing);
1779
+ // Patch only deletion fields against live JSON; a before hook may yield to a peer.
1780
+ let sql;
1781
+ let parameters;
1782
+ const deletedAtField = this.softDeleteField();
1783
+ if (deletedAtField && !options?.force) {
1784
+ const now = new Date().toISOString();
1785
+ const deleted = { [deletedAtField]: now };
1786
+ applyUpdateTimestamp(deleted, {}, this.descriptor.timestamps, now);
1787
+ const assignments = Object.entries(deleted);
1788
+ sql = `UPDATE ${this.tableName} SET data = json_set(data, ${assignments.map(() => '?, json(?)').join(', ')})`;
1789
+ parameters = assignments.flatMap(([field, value]) => [`$.${JSON.stringify(field)}`, JSON.stringify(value)]);
1790
+ if (this.descriptor.versioning) {
1791
+ sql += `, ${RECORD_REVISION_COLUMN} = ?`;
1792
+ parameters.push(crypto.randomUUID());
1793
+ }
731
1794
  }
732
1795
  else {
733
- this.db.query(`DELETE FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).run(id);
1796
+ sql = `DELETE FROM ${this.tableName}`;
1797
+ parameters = [];
734
1798
  }
1799
+ const db = await this.acquireConnection();
1800
+ const result = db.prepare(`${sql} WHERE ${predicates.join(' AND ')} RETURNING data`).get(...parameters, ...values);
1801
+ if (!result)
1802
+ return { count: 0, success: false };
1803
+ // RETURNING carries the same JSON row actually mutated.
1804
+ const persisted = this.decodeFields(result.data);
735
1805
  // Execute afterDelete hook
736
- if (this.descriptor.afterDelete) {
737
- await this.descriptor.afterDelete(existing);
738
- }
739
- this.emitLifecycle('afterDelete', existing);
1806
+ await this.runHook('afterDelete', persisted);
1807
+ this.emitLifecycle('afterDelete', persisted);
740
1808
  return { count: 1, success: true };
741
1809
  }
742
1810
  async deleteMany(where, options) {
743
1811
  await this.ensureInitialized();
1812
+ const predicates = [];
1813
+ const values = [];
1814
+ for (const filter of [where, options?.where]) {
1815
+ if (filter === undefined)
1816
+ continue;
1817
+ const condition = this.buildDeleteWhereClause(filter);
1818
+ if (condition.clause) {
1819
+ predicates.push(`(${condition.clause})`);
1820
+ values.push(...condition.values);
1821
+ }
1822
+ }
1823
+ const physicalDelete = !this.descriptor.softDelete || Boolean(options?.force);
1824
+ // A forced delete must be able to see rows that are already soft-deleted.
1825
+ // `list()` appends `deletedAt IS NULL` unless told otherwise, so without
1826
+ // this a soft-deleted row can never be physically removed in bulk — soft
1827
+ // delete becomes a one-way trip and the table grows forever. Retention
1828
+ // that stamps at one window and reaps at a longer one depends on this.
1829
+ const includeDeleted = Boolean(options?.force);
1830
+ // Set-based fast path. The per-row loop below exists to run
1831
+ // beforeDelete/afterDelete hooks and emit per-record lifecycle events; a
1832
+ // descriptor with neither needs none of that, and paying one SELECT plus
1833
+ // one statement per row is ruinous at scale (a real retention sweep hit
1834
+ // ~500k rows, i.e. ~1M statements and 500k hook emissions, one
1835
+ // transaction each). Hook-bearing descriptors keep the old semantics.
1836
+ const hasDeleteHooks = Boolean(this.descriptor.beforeDelete || this.descriptor.afterDelete);
1837
+ if (physicalDelete && !hasDeleteHooks) {
1838
+ const clause = predicates.join(' AND ');
1839
+ const sql = clause
1840
+ ? `DELETE FROM ${this.tableName} WHERE ${clause}`
1841
+ : `DELETE FROM ${this.tableName}`;
1842
+ const result = this.db.prepare(sql).run(...values);
1843
+ const count = Number(result?.changes ?? 0);
1844
+ return { count, success: count > 0 };
1845
+ }
744
1846
  // Find matching records
745
- const result = await this.list({ where, select: [this.primaryKey] });
1847
+ const result = await this.listRecords({ where, includeDeleted });
746
1848
  const ids = result.items.map((item) => this.readPrimaryKey(item));
747
1849
  // Delete each record
748
1850
  let count = 0;
749
1851
  for (const id of ids) {
750
- try {
751
- const deleteResult = await this.delete(id, options);
752
- count += deleteResult.count;
753
- }
754
- catch (error) {
755
- logger.error(`[StorageRecord] Failed to delete record ${id}:`, toErrorContext(error));
756
- }
1852
+ const deleteResult = await this.deleteWithConditions(id, options, where);
1853
+ count += deleteResult.count;
757
1854
  }
758
1855
  return { count, success: count > 0 };
759
1856
  }
760
1857
  async restore(id) {
761
1858
  await this.ensureInitialized();
762
- if (!this.descriptor.softDelete) {
1859
+ const deletedAtField = this.softDeleteField();
1860
+ if (!deletedAtField) {
763
1861
  throw new Error(`Soft delete is not enabled for ${this.tableName}`);
764
1862
  }
765
- const deletedAtField = typeof this.descriptor.softDelete === 'string' ? this.descriptor.softDelete : 'deletedAt';
766
1863
  // Get record (including soft-deleted)
767
- const row = this.db.query(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
1864
+ const row = this.db.prepare(`SELECT data FROM ${this.tableName} WHERE ${this.primaryKey} = ?`).get(id);
768
1865
  if (!row) {
769
1866
  throw new Error(`Record with ${this.primaryKey}=${id} not found in ${this.tableName}`);
770
1867
  }
771
- const record = JSON.parse(row.data);
1868
+ const record = this.decodeFields(row.data);
772
1869
  if (!record[deletedAtField]) {
773
1870
  throw new Error(`Record ${id} is not soft-deleted`);
774
1871
  }
@@ -776,15 +1873,16 @@ export class StorageRecord {
776
1873
  delete record[deletedAtField];
777
1874
  // Update timestamp
778
1875
  if (this.descriptor.timestamps) {
779
- const updatedAtField = typeof this.descriptor.timestamps === 'object' &&
780
- typeof this.descriptor.timestamps.updatedAt === 'string'
781
- ? this.descriptor.timestamps.updatedAt
782
- : 'updatedAt';
783
- record[updatedAtField] = new Date().toISOString();
1876
+ record[resolveTimestampField(this.descriptor.timestamps, 'updatedAt')] = new Date().toISOString();
784
1877
  }
785
1878
  const dataJson = JSON.stringify(record);
786
- this.db.query(`UPDATE ${this.tableName} SET data = ? WHERE ${this.primaryKey} = ?`).run(dataJson, id);
787
- return record;
1879
+ if (this.descriptor.versioning) {
1880
+ this.db.prepare(`UPDATE ${this.tableName} SET data = ?, ${RECORD_REVISION_COLUMN} = ? WHERE ${this.primaryKey} = ?`).run(dataJson, crypto.randomUUID(), id);
1881
+ }
1882
+ else {
1883
+ this.db.prepare(`UPDATE ${this.tableName} SET data = ? WHERE ${this.primaryKey} = ?`).run(dataJson, id);
1884
+ }
1885
+ return this.recordOf(record);
788
1886
  }
789
1887
  // AGGREGATION
790
1888
  async aggregate(options) {
@@ -834,36 +1932,72 @@ export class StorageRecord {
834
1932
  .join(', ');
835
1933
  sql += ` GROUP BY ${groupByExpr}`;
836
1934
  }
837
- const rows = this.db.query(sql).all(...params);
1935
+ const rows = this.db.prepare(sql).all(...params);
838
1936
  return rows.map((row) => ({
839
1937
  value: row.value || 0
840
1938
  }));
841
1939
  }
842
1940
  // TRANSACTIONS
843
1941
  async transaction(fn) {
844
- await this.ensureInitialized();
845
- if (!this.db) {
846
- throw new Error('Database not initialized');
1942
+ const runtime = this.getRuntime();
1943
+ const db = this.db ?? (await this.getDatabase());
1944
+ this.db = db;
1945
+ const existingFrame = getActiveTransactionFrame(db, runtime);
1946
+ if (existingFrame) {
1947
+ await this.ensureInitialized();
1948
+ return runInSavepoint(runtime, existingFrame, db, async () => {
1949
+ const tx = {
1950
+ id: `tx-${Date.now()}-${Math.random().toString(36).slice(2)}`,
1951
+ startTime: Date.now(),
1952
+ isActive: true
1953
+ };
1954
+ try {
1955
+ return await fn(tx);
1956
+ }
1957
+ finally {
1958
+ tx.isActive = false;
1959
+ }
1960
+ });
847
1961
  }
848
- // Begin transaction
849
- this.db.exec('BEGIN TRANSACTION');
850
- const tx = {
851
- id: `tx-${Date.now()}-${Math.random().toString(36).slice(2)}`,
852
- startTime: Date.now(),
853
- isActive: true
854
- };
1962
+ const state = getConnectionTransactionState(db);
1963
+ while (state.leased || !this.initialized) {
1964
+ if (state.leased) {
1965
+ await new Promise((resolve) => {
1966
+ state.waiters.push(resolve);
1967
+ });
1968
+ continue;
1969
+ }
1970
+ await this.initialize();
1971
+ }
1972
+ state.leased = true;
855
1973
  try {
856
- const result = await fn(tx);
857
- // Commit transaction
858
- this.db.exec('COMMIT');
859
- tx.isActive = false;
860
- return result;
1974
+ return await withTransactionFrame(runtime, state, null, async (frame) => {
1975
+ const tx = {
1976
+ id: `tx-${Date.now()}-${Math.random().toString(36).slice(2)}`,
1977
+ startTime: Date.now(),
1978
+ isActive: true
1979
+ };
1980
+ db.exec('BEGIN TRANSACTION');
1981
+ try {
1982
+ const result = await fn(tx);
1983
+ db.exec('COMMIT');
1984
+ tx.isActive = false;
1985
+ return result;
1986
+ }
1987
+ catch (error) {
1988
+ db.exec('ROLLBACK');
1989
+ tx.isActive = false;
1990
+ flushRollbackCallbacks(frame);
1991
+ throw error;
1992
+ }
1993
+ });
861
1994
  }
862
- catch (error) {
863
- // Rollback transaction
864
- this.db.exec('ROLLBACK');
865
- tx.isActive = false;
866
- throw error;
1995
+ finally {
1996
+ state.leased = false;
1997
+ const waiters = state.waiters.splice(0);
1998
+ for (const wake of waiters) {
1999
+ wake();
2000
+ }
867
2001
  }
868
2002
  }
869
2003
  // STREAMING
@@ -871,11 +2005,12 @@ export class StorageRecord {
871
2005
  await this.ensureInitialized();
872
2006
  const batchSize = options?.batchSize || 100;
873
2007
  let offset = options?.offset || 0;
2008
+ // StreamOptions extends QueryOptions but adds batchSize; `batchSize` is
2009
+ // stream-only — strip it before passing the rest to list().
2010
+ const streamOptions = options ?? {};
2011
+ const { batchSize: _strip, ...listOptions } = streamOptions;
874
2012
  while (true) {
875
- // StreamOptions extends QueryOptions but adds batchSize/offset; both belong on QueryOptions
876
- // for the list() call below, but `batchSize` is stream-only — strip it before passing along.
877
- const { batchSize: _strip, ...listOptions } = (options ?? {});
878
- const result = await this.list({
2013
+ const result = await this.listRecords({
879
2014
  ...listOptions,
880
2015
  limit: batchSize,
881
2016
  offset
@@ -900,12 +2035,12 @@ export class StorageRecord {
900
2035
  let result;
901
2036
  switch (op.type) {
902
2037
  case 'create':
903
- result = await this.create(op.data);
2038
+ result = await this.createFields({ ...this.fieldsOf(op.data) });
904
2039
  break;
905
2040
  case 'update':
906
2041
  if (!op.id)
907
2042
  throw new Error('ID required for update');
908
- result = await this.update(op.id, op.data);
2043
+ result = await this.updateFields(op.id, this.fieldsOf(op.data));
909
2044
  break;
910
2045
  case 'delete':
911
2046
  if (!op.id)
@@ -920,16 +2055,12 @@ export class StorageRecord {
920
2055
  }
921
2056
  // UTILITIES
922
2057
  async validate(data) {
923
- try {
924
- const result = this.schema.parse(data);
925
- return { success: true, data: result };
926
- }
927
- catch (error) {
928
- if (error instanceof Error && 'issues' in error) {
929
- return { success: false, errors: error };
930
- }
931
- return { success: false, errors: undefined };
932
- }
2058
+ // A schema refusal is the validation result; any other failure while
2059
+ // validating (a refinement that throws) is not a result and propagates.
2060
+ const result = this.schema.safeParse(data);
2061
+ if (result.success)
2062
+ return { success: true, data: result.data };
2063
+ return { success: false, errors: result.error };
933
2064
  }
934
2065
  getAdapterId() {
935
2066
  if (this.adapterCache) {
@@ -943,12 +2074,30 @@ export class StorageRecord {
943
2074
  }
944
2075
  // PRIVATE HELPERS
945
2076
  async ensureInitialized() {
946
- if (!this.initialized) {
2077
+ const runtime = this.getRuntime();
2078
+ const db = this.db ?? (await this.getDatabase());
2079
+ this.db = db;
2080
+ const state = connectionTransactions.get(db);
2081
+ while ((state?.leased && getActiveTransactionFrame(db, runtime) === null) || !this.initialized) {
2082
+ if (state?.leased && getActiveTransactionFrame(db, runtime) === null) {
2083
+ await new Promise((resolve) => {
2084
+ state.waiters.push(resolve);
2085
+ });
2086
+ continue;
2087
+ }
947
2088
  await this.initialize();
948
2089
  }
949
2090
  }
2091
+ async acquireConnection() {
2092
+ if (!this.db) {
2093
+ throw new Error('Database not initialized');
2094
+ }
2095
+ await waitForConnectionLease(this.db, this.getRuntime());
2096
+ return this.db;
2097
+ }
950
2098
  generateId() {
951
- const strategy = typeof this.descriptor.autoId === 'object' ? this.descriptor.autoId.strategy : 'uuid';
2099
+ const autoId = this.autoIdOptions();
2100
+ const strategy = autoId ? autoId.strategy : 'uuid';
952
2101
  if (strategy === 'uuid') {
953
2102
  return crypto.randomUUID();
954
2103
  }
@@ -961,66 +2110,241 @@ export class StorageRecord {
961
2110
  }
962
2111
  return id;
963
2112
  }
964
- else if (strategy === 'custom' && typeof this.descriptor.autoId === 'object') {
965
- if (this.descriptor.autoId.generator) {
966
- return this.descriptor.autoId.generator();
967
- }
2113
+ else if (strategy === 'custom' && autoId?.generator) {
2114
+ const refusal = {
2115
+ message: `Storage record '${this.descriptor.id}' declares a custom id generator that does not produce a string id.`,
2116
+ code: 'INVALID_INPUT',
2117
+ reason: 'record-id-generator-invalid',
2118
+ details: { recordKind: this.descriptor.id }
2119
+ };
2120
+ const generator = parseAdapterInput(IdGeneratorSchema, autoId.generator, refusal);
2121
+ return parseAdapterInput(GeneratedIdSchema, generator(), refusal);
968
2122
  }
969
2123
  return crypto.randomUUID();
970
2124
  }
2125
+ buildDeleteWhereClause(where) {
2126
+ const scalarSchema = z.union([z.string(), z.number(), z.boolean(), z.null(), z.date()]);
2127
+ const filter = z.record(z.string(), z.unknown()).safeParse(where);
2128
+ if (!filter.success) {
2129
+ throw new CapabilityError('Deletion filters must be field objects.', 'INVALID_INPUT', { reason: 'record-delete-filter-invalid', retryable: false });
2130
+ }
2131
+ for (const [field, condition] of Object.entries(filter.data)) {
2132
+ const invalid = () => { throw new CapabilityError(`Invalid deletion filter for '${field}'.`, 'INVALID_INPUT', { reason: 'record-delete-filter-invalid', retryable: false }); };
2133
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(field) || field === '__proto__')
2134
+ invalid();
2135
+ if (scalarSchema.safeParse(condition).success)
2136
+ continue;
2137
+ const comparison = z.record(z.string(), z.unknown()).safeParse(condition);
2138
+ if (!comparison.success)
2139
+ return invalid();
2140
+ const operators = Object.entries(comparison.data);
2141
+ if (!operators.length)
2142
+ invalid();
2143
+ for (const [operator, value] of operators) {
2144
+ if (!RecordFilterOperatorSchema.safeParse(operator).success)
2145
+ invalid();
2146
+ if (operator === 'hasAll' && !z.array(z.string()).min(1).safeParse(value).success)
2147
+ invalid();
2148
+ if (['eq', 'ne', 'gt', 'gte', 'lt', 'lte'].includes(operator) && !scalarSchema.safeParse(value).success)
2149
+ invalid();
2150
+ if (['like', 'ilike', 'contains', 'startsWith', 'endsWith'].includes(operator) && !z.string().safeParse(value).success)
2151
+ invalid();
2152
+ if ((operator === 'in' || operator === 'notIn') && !z.array(scalarSchema).safeParse(value).success)
2153
+ invalid();
2154
+ if (operator === 'between' && !z.tuple([scalarSchema, scalarSchema]).safeParse(value).success)
2155
+ invalid();
2156
+ if ((operator === 'isNull' || operator === 'isNotNull') && !z.boolean().safeParse(value).success)
2157
+ invalid();
2158
+ }
2159
+ }
2160
+ return this.buildWhereClause(where);
2161
+ }
2162
+ encodeDateFilterOperand(value) {
2163
+ if (!(value instanceof Date))
2164
+ return value;
2165
+ const date = z.date().safeParse(value);
2166
+ if (!date.success) {
2167
+ throw new CapabilityError('Date filter operands must be valid dates.', 'INVALID_INPUT', {
2168
+ reason: 'record-filter-date-invalid', retryable: false
2169
+ });
2170
+ }
2171
+ return date.data.toISOString();
2172
+ }
2173
+ /** json_extract returns object/array fields as their persisted JSON text. */
2174
+ encodeEqualityFilterOperand(value) {
2175
+ const structured = JsonFilterOperandSchema.safeParse(value);
2176
+ if (structured.success)
2177
+ return JSON.stringify(structured.data);
2178
+ return this.encodeDateFilterOperand(value);
2179
+ }
2180
+ /**
2181
+ * The SQL a filter on `field` reads. The primary key is the table's own key
2182
+ * column, which always equals the stored record's id, so filtering on it is an
2183
+ * index lookup; every other field is read from the stored JSON. Before this,
2184
+ * an id filter scanned the whole table through json_extract, which made a
2185
+ * per-row guarded update quadratic in the table size.
2186
+ */
2187
+ filterColumn(field) {
2188
+ const path = `$.${field}`.replaceAll("'", "''");
2189
+ return field === this.primaryKey ? this.primaryKey : `json_extract(data, '${path}')`;
2190
+ }
2191
+ /** The SQL type of `field` in the stored JSON (`NULL` when the key is absent, `'null'` when JSON `null`). */
2192
+ filterFieldType(field) {
2193
+ const path = `$.${field}`.replaceAll("'", "''");
2194
+ return field === this.primaryKey ? `typeof(${this.primaryKey})` : `json_type(data, '${path}')`;
2195
+ }
971
2196
  buildWhereClause(where) {
972
2197
  const conditions = [];
973
2198
  const values = [];
974
- for (const [field, condition] of Object.entries(where)) {
975
- if (typeof condition === 'object' && condition !== null) {
2199
+ const filter = parseAdapterInput(FilterFieldsViewSchema, where, {
2200
+ message: 'Record filters must be field objects.',
2201
+ code: 'INVALID_INPUT',
2202
+ reason: 'record-filter-invalid',
2203
+ details: { recordKind: this.descriptor.id }
2204
+ });
2205
+ for (const [field, condition] of Object.entries(filter)) {
2206
+ // An operator object (`{ eq, in, … }`) is a complex condition; a Date or any other value is equality.
2207
+ const operators = FilterOperatorsSchema.safeParse(condition);
2208
+ if (operators.success) {
976
2209
  // Complex condition
977
- const cond = condition;
2210
+ const cond = operators.data;
2211
+ const refuse = (check) => {
2212
+ throw new CapabilityError('Record filter clause cannot be enforced.', 'INVALID_INPUT', {
2213
+ reason: 'record-filter-invalid',
2214
+ retryable: false,
2215
+ details: { recordKind: this.descriptor.id, field, check }
2216
+ });
2217
+ };
2218
+ const operatorKeys = Object.keys(cond);
2219
+ // An operator object with no operator constrains nothing: refused, never read as "every row".
2220
+ if (!operatorKeys.length)
2221
+ refuse('empty-comparison');
2222
+ for (const operator of operatorKeys) {
2223
+ if (!RecordFilterOperatorSchema.safeParse(operator).success)
2224
+ refuse('unknown-operator');
2225
+ }
2226
+ if ('in' in cond && !Array.isArray(cond.in))
2227
+ refuse('array-operand-required');
2228
+ if ('notIn' in cond && !Array.isArray(cond.notIn))
2229
+ refuse('array-operand-required');
2230
+ if ('between' in cond && !(Array.isArray(cond.between) && cond.between.length === 2))
2231
+ refuse('two-bounds-required');
2232
+ if ('hasAll' in cond) {
2233
+ for (const member of assertArrayMembers(field, cond.hasAll)) {
2234
+ conditions.push(`EXISTS (SELECT 1 FROM json_each(COALESCE(json_extract(data, ?), '[]')) WHERE type = 'text' AND value = ?)`);
2235
+ values.push(`$.${field}`, member);
2236
+ }
2237
+ }
2238
+ if ('hasNot' in cond) {
2239
+ assertArrayField(field, cond.hasNot);
2240
+ conditions.push(`NOT EXISTS (SELECT 1 FROM json_each(COALESCE(json_extract(data, ?), '[]')) WHERE type = 'text' AND value = ?)`);
2241
+ values.push(`$.${field}`, cond.hasNot);
2242
+ }
978
2243
  if ('eq' in cond) {
979
- conditions.push(`json_extract(data, '$.${field}') = ?`);
980
- values.push(cond.eq);
2244
+ if (cond.eq === undefined) {
2245
+ conditions.push(`${this.filterFieldType(field)} IS NULL`);
2246
+ }
2247
+ else {
2248
+ conditions.push(`${this.filterColumn(field)} ${cond.eq === null ? 'IS NULL' : '= ?'}`);
2249
+ if (cond.eq !== null)
2250
+ values.push(this.encodeEqualityFilterOperand(cond.eq));
2251
+ }
981
2252
  }
982
2253
  if ('ne' in cond) {
983
- conditions.push(`json_extract(data, '$.${field}') != ?`);
984
- values.push(cond.ne);
2254
+ if (cond.ne === undefined) {
2255
+ conditions.push(`${this.filterFieldType(field)} IS NOT NULL`);
2256
+ }
2257
+ else {
2258
+ conditions.push(`${this.filterColumn(field)} ${cond.ne === null ? 'IS NOT NULL' : '!= ?'}`);
2259
+ if (cond.ne !== null)
2260
+ values.push(this.encodeEqualityFilterOperand(cond.ne));
2261
+ }
985
2262
  }
986
2263
  if ('gt' in cond) {
987
- conditions.push(`json_extract(data, '$.${field}') > ?`);
988
- values.push(cond.gt);
2264
+ conditions.push(`${this.filterColumn(field)} > ?`);
2265
+ values.push(this.encodeDateFilterOperand(cond.gt));
989
2266
  }
990
2267
  if ('gte' in cond) {
991
- conditions.push(`json_extract(data, '$.${field}') >= ?`);
992
- values.push(cond.gte);
2268
+ conditions.push(`${this.filterColumn(field)} >= ?`);
2269
+ values.push(this.encodeDateFilterOperand(cond.gte));
993
2270
  }
994
2271
  if ('lt' in cond) {
995
- conditions.push(`json_extract(data, '$.${field}') < ?`);
996
- values.push(cond.lt);
2272
+ conditions.push(`${this.filterColumn(field)} < ?`);
2273
+ values.push(this.encodeDateFilterOperand(cond.lt));
997
2274
  }
998
2275
  if ('lte' in cond) {
999
- conditions.push(`json_extract(data, '$.${field}') <= ?`);
1000
- values.push(cond.lte);
2276
+ conditions.push(`${this.filterColumn(field)} <= ?`);
2277
+ values.push(this.encodeDateFilterOperand(cond.lte));
1001
2278
  }
1002
2279
  if ('in' in cond && Array.isArray(cond.in)) {
1003
2280
  const placeholders = cond.in.map(() => '?').join(', ');
1004
- conditions.push(`json_extract(data, '$.${field}') IN (${placeholders})`);
1005
- values.push(...cond.in);
2281
+ conditions.push(`${this.filterColumn(field)} IN (${placeholders})`);
2282
+ values.push(...cond.in.map(value => this.encodeEqualityFilterOperand(value)));
1006
2283
  }
1007
2284
  if ('like' in cond) {
1008
- conditions.push(`json_extract(data, '$.${field}') LIKE ?`);
2285
+ conditions.push(`${this.filterColumn(field)} LIKE ?`);
1009
2286
  values.push(cond.like);
1010
2287
  }
1011
2288
  if ('isNull' in cond) {
1012
2289
  if (cond.isNull) {
1013
- conditions.push(`json_extract(data, '$.${field}') IS NULL`);
2290
+ conditions.push(`${this.filterColumn(field)} IS NULL`);
2291
+ }
2292
+ else {
2293
+ conditions.push(`${this.filterColumn(field)} IS NOT NULL`);
2294
+ }
2295
+ }
2296
+ // Operators below were declared by WhereFilter but never built, so a
2297
+ // query using them was silently UNFILTERED — no error, just wrong
2298
+ // rows. Found when a retention sweep's `{ processedAt: { isNotNull:
2299
+ // true } }` guard was dropped and the delete matched 328,232 rows
2300
+ // instead of the ~148k processed ones, i.e. it would have removed
2301
+ // 178,639 unprocessed rows the caller had explicitly excluded.
2302
+ // A typed operator that no builder honours is worse than a missing
2303
+ // one: it reads as a guarantee.
2304
+ if ('isNotNull' in cond) {
2305
+ if (cond.isNotNull) {
2306
+ conditions.push(`${this.filterColumn(field)} IS NOT NULL`);
1014
2307
  }
1015
2308
  else {
1016
- conditions.push(`json_extract(data, '$.${field}') IS NOT NULL`);
2309
+ conditions.push(`${this.filterColumn(field)} IS NULL`);
1017
2310
  }
1018
2311
  }
2312
+ if ('notIn' in cond && Array.isArray(cond.notIn)) {
2313
+ const placeholders = cond.notIn.map(() => '?').join(', ');
2314
+ conditions.push(`${this.filterColumn(field)} NOT IN (${placeholders})`);
2315
+ values.push(...cond.notIn.map(value => this.encodeEqualityFilterOperand(value)));
2316
+ }
2317
+ if ('ilike' in cond) {
2318
+ // SQLite LIKE is case-insensitive for ASCII by default; lower()
2319
+ // both sides so the intent holds regardless of collation.
2320
+ conditions.push(`lower(${this.filterColumn(field)}) LIKE lower(?)`);
2321
+ values.push(cond.ilike);
2322
+ }
2323
+ if ('contains' in cond) {
2324
+ conditions.push(`${this.filterColumn(field)} LIKE ?`);
2325
+ values.push(`%${String(cond.contains)}%`);
2326
+ }
2327
+ if ('startsWith' in cond) {
2328
+ conditions.push(`${this.filterColumn(field)} LIKE ?`);
2329
+ values.push(`${String(cond.startsWith)}%`);
2330
+ }
2331
+ if ('endsWith' in cond) {
2332
+ conditions.push(`${this.filterColumn(field)} LIKE ?`);
2333
+ values.push(`%${String(cond.endsWith)}`);
2334
+ }
2335
+ if ('between' in cond && Array.isArray(cond.between) && cond.between.length === 2) {
2336
+ conditions.push(`${this.filterColumn(field)} BETWEEN ? AND ?`);
2337
+ values.push(this.encodeDateFilterOperand(cond.between[0]), this.encodeDateFilterOperand(cond.between[1]));
2338
+ }
2339
+ }
2340
+ else if (condition === undefined) {
2341
+ conditions.push(`${this.filterFieldType(field)} IS NULL`);
1019
2342
  }
1020
2343
  else {
1021
- // Simple equality
1022
- conditions.push(`json_extract(data, '$.${field}') = ?`);
1023
- values.push(condition);
2344
+ // SQL equality with NULL never matches; nullable fields use the null predicate.
2345
+ conditions.push(`${this.filterColumn(field)} ${condition === null ? 'IS NULL' : '= ?'}`);
2346
+ if (condition !== null)
2347
+ values.push(this.encodeEqualityFilterOperand(condition));
1024
2348
  }
1025
2349
  }
1026
2350
  return {
@@ -1031,181 +2355,19 @@ export class StorageRecord {
1031
2355
  buildOrderBy(orderBy) {
1032
2356
  if (Array.isArray(orderBy)) {
1033
2357
  return orderBy
1034
- .map((o) => `json_extract(data, '$.${String(o.field)}') ${o.direction}`)
2358
+ .map((o) => `${this.filterColumn(String(o.field))} ${o.direction}`)
1035
2359
  .join(', ');
1036
2360
  }
1037
2361
  else {
1038
2362
  const fields = Object.entries(orderBy).map(([field, direction]) => {
1039
- return `json_extract(data, '$.${field}') ${direction}`;
2363
+ return `${this.filterColumn(field)} ${direction}`;
1040
2364
  });
1041
2365
  return fields.join(', ');
1042
2366
  }
1043
2367
  }
1044
- async loadRelations(record, include) {
1045
- if (!include || Object.keys(this.relations).length === 0) {
1046
- return record;
1047
- }
1048
- const relationNames = Array.isArray(include) ? include : Object.keys(include);
1049
- for (const relationName of relationNames) {
1050
- const relation = this.relations[relationName];
1051
- if (!relation)
1052
- continue;
1053
- const related = await this.loadRelation(record, relation);
1054
- this.asMutableRecord(record)[relationName] = related;
1055
- }
1056
- return record;
1057
- }
1058
- async loadRelation(record, relation) {
1059
- // For now, we'll implement basic relation loading
1060
- // This would be enhanced with proper relation resolution
1061
- switch (relation.type) {
1062
- case 'belongs_to': {
1063
- const foreignKey = relation.foreignKey;
1064
- if (!foreignKey)
1065
- return null;
1066
- const foreignId = this.readStringField(record, foreignKey);
1067
- if (!foreignId)
1068
- return null;
1069
- // Resolving belongs_to requires runtime access to the target storage; unimplemented.
1070
- return null;
1071
- }
1072
- case 'has_many': {
1073
- // Resolving has_many requires runtime access to the target storage; unimplemented.
1074
- return [];
1075
- }
1076
- case 'has_one': {
1077
- // Resolving has_one requires runtime access to the target storage; unimplemented.
1078
- return null;
1079
- }
1080
- case 'many_to_many': {
1081
- // Implement many-to-many through junction table
1082
- return await this.loadManyToManyRelation(record, relation);
1083
- }
1084
- default:
1085
- return null;
1086
- }
1087
- }
1088
- /**
1089
- * Load many-to-many relation through junction table
1090
- */
1091
- async loadManyToManyRelation(record, relation) {
1092
- if (!relation.through) {
1093
- logger.warn(`Many-to-many relation '${relation.as || 'unknown'}' has no 'through' property`);
1094
- return [];
1095
- }
1096
- try {
1097
- const runtime = getVibesRuntime();
1098
- if (!runtime) {
1099
- logger.warn('Runtime not available for relation loading');
1100
- return [];
1101
- }
1102
- // Resolve the junction table storage interface
1103
- const junctionStorage = await runtime
1104
- .query('storage/record')
1105
- .withId(relation.through)
1106
- .enabled()
1107
- .resolve()
1108
- .catch((error) => {
1109
- logger.warn(`Failed to resolve junction table '${relation.through}':`, toErrorContext(error));
1110
- return null;
1111
- });
1112
- if (!junctionStorage) {
1113
- return [];
1114
- }
1115
- // Resolve the target table storage interface
1116
- const targetStorage = await runtime
1117
- .query('storage/record')
1118
- .withId(relation.target)
1119
- .enabled()
1120
- .resolve()
1121
- .catch((error) => {
1122
- logger.warn(`Failed to resolve target table '${relation.target}':`, toErrorContext(error));
1123
- return null;
1124
- });
1125
- if (!targetStorage) {
1126
- return [];
1127
- }
1128
- // Get the primary key for this record
1129
- const thisPrimaryKey = this.readStringField(record, this.primaryKey);
1130
- if (!thisPrimaryKey) {
1131
- return [];
1132
- }
1133
- const junctionTable = this.quoteIdentifier(junctionStorage.tableName);
1134
- const thisFkColumn = await this.findJunctionForeignKeyColumn(junctionStorage, thisPrimaryKey, junctionTable);
1135
- if (!thisFkColumn) {
1136
- return [];
1137
- }
1138
- // Query junction rows by this record id
1139
- const junctionResults = await junctionStorage.raw(`SELECT * FROM ${junctionTable} WHERE ${this.quoteIdentifier(thisFkColumn)} = ?`, thisPrimaryKey);
1140
- if (junctionResults.length === 0) {
1141
- return [];
1142
- }
1143
- // Extract target foreign keys from junction results
1144
- const targetIds = this.extractJunctionTargetIds(junctionResults, thisFkColumn, targetStorage.primaryKey);
1145
- if (targetIds.length === 0) {
1146
- return [];
1147
- }
1148
- return await targetStorage.getMany(targetIds);
1149
- }
1150
- catch (error) {
1151
- logger.error(`Error loading many-to-many relation: ${error}`);
1152
- return [];
1153
- }
1154
- }
1155
- /**
1156
- * Find a junction table column that stores this record's primary key.
1157
- */
1158
- async findJunctionForeignKeyColumn(junctionStorage, recordId, quotedJunctionTable) {
1159
- let tableInfo;
1160
- try {
1161
- tableInfo = await junctionStorage.raw(`PRAGMA table_info(${quotedJunctionTable})`);
1162
- }
1163
- catch {
1164
- return null;
1165
- }
1166
- const candidateColumns = tableInfo
1167
- .map((column) => column.name)
1168
- .filter((name) => this.isLikelyIdColumn(name))
1169
- .filter((name) => name !== 'id' && name !== junctionStorage.primaryKey)
1170
- .filter((name) => name !== 'data');
1171
- for (const column of candidateColumns) {
1172
- const rows = await junctionStorage.raw(`SELECT COUNT(*) as count FROM ${quotedJunctionTable} WHERE ${this.quoteIdentifier(column)} = ?`, recordId);
1173
- if ((rows[0]?.count ?? 0) > 0) {
1174
- return column;
1175
- }
1176
- }
1177
- return null;
1178
- }
1179
- /**
1180
- * Extract target IDs from junction rows.
1181
- */
1182
- extractJunctionTargetIds(junctionRows, thisFkColumn, targetPrimaryKey) {
1183
- const targetIds = new Set();
1184
- for (const row of junctionRows) {
1185
- for (const [field, value] of Object.entries(row)) {
1186
- if (field === thisFkColumn || field === targetPrimaryKey) {
1187
- continue;
1188
- }
1189
- if (!this.isLikelyIdColumn(field) || typeof value !== 'string') {
1190
- continue;
1191
- }
1192
- if (value.length > 0) {
1193
- targetIds.add(value);
1194
- }
1195
- }
1196
- }
1197
- return [...targetIds];
1198
- }
1199
- /**
1200
- * True when a field name is likely an identifier / foreign key field.
1201
- */
1202
- isLikelyIdColumn(name) {
1203
- const lower = name.toLowerCase();
1204
- return (lower === 'id' ||
1205
- lower === 'data' ||
1206
- /(^|[._])id$/i.test(name) ||
1207
- /_id$/.test(lower) ||
1208
- /Id$/.test(name));
2368
+ /** What relation loading reads of this record: its kind, primary key, declared relations and runtime. */
2369
+ get relationOwner() {
2370
+ return { recordKind: this.descriptor.id, primaryKey: this.primaryKey, relations: this.relations, runtime: this.context.runtime };
1209
2371
  }
1210
2372
  /**
1211
2373
  * Escape SQL identifier safely for query fragments.
@@ -1223,6 +2385,9 @@ export class StorageRecord {
1223
2385
  throw new Error(`FTS is not configured for ${this.tableName}`);
1224
2386
  }
1225
2387
  const ftsTable = this.descriptor.fts.tableName || `${this.tableName}_fts`;
2388
+ const baseTableName = this.quoteIdentifier(this.tableName);
2389
+ const primaryKeyName = this.quoteIdentifier(this.primaryKey);
2390
+ const ftsTableName = this.quoteIdentifier(ftsTable);
1226
2391
  const ftsFields = this.descriptor.fts.fields;
1227
2392
  // Split into words and AND them for multi-word queries.
1228
2393
  // Each word is quoted to escape FTS5 special characters.
@@ -1230,23 +2395,42 @@ export class StorageRecord {
1230
2395
  const ftsQuery = words.length <= 1
1231
2396
  ? `"${query.replace(/"/g, '""')}"`
1232
2397
  : words.map((w) => `"${w.replace(/"/g, '""')}"`).join(' AND ');
1233
- const params = [ftsQuery];
1234
- let sql = `
1235
- SELECT
1236
- ${this.tableName}.${this.primaryKey},
1237
- ${this.tableName}.data,
1238
- ${ftsTable}.rank as _rank
1239
- FROM ${this.tableName}
1240
- JOIN ${ftsTable} ON ${ftsTable}.rowid = ${this.tableName}.rowid
1241
- WHERE ${ftsTable} MATCH ?
2398
+ // MATCH + where fragment shared by the page query and the COUNT query so
2399
+ // the two can never drift.
2400
+ let fromWhere = `
2401
+ FROM ${baseTableName}
2402
+ JOIN ${ftsTableName} ON ${ftsTableName}.rowid = ${baseTableName}.rowid
2403
+ WHERE ${ftsTableName} MATCH ?
1242
2404
  `;
2405
+ const filterParams = [ftsQuery];
1243
2406
  if (options?.where) {
1244
2407
  const { clause, values } = this.buildWhereClause(options.where);
1245
2408
  if (clause) {
1246
- sql += ` AND ${clause}`;
1247
- params.push(...values);
2409
+ fromWhere += ` AND ${clause}`;
2410
+ filterParams.push(...values);
1248
2411
  }
1249
2412
  }
2413
+ // Soft delete filter — search honors softDelete exactly like list()/count()
2414
+ // (includeDeleted opts out). Callers must never re-implement this per-lane.
2415
+ const deletedAtField = this.softDeleteField();
2416
+ if (deletedAtField && !options?.includeDeleted) {
2417
+ fromWhere += ` AND json_extract(data, '$.${deletedAtField}') IS NULL`;
2418
+ }
2419
+ // True total: COUNT over the same MATCH + where BEFORE limit/offset.
2420
+ // Returning the page length as `total` hid every match past the cap.
2421
+ // (minScore is applied per-row after fetch, so the total counts matches
2422
+ // before that post-filter.)
2423
+ const countRow = this.db.prepare(`SELECT COUNT(*) as count ${fromWhere}`).get(...filterParams);
2424
+ const total = countRow?.count || 0;
2425
+ const params = [...filterParams];
2426
+ let sql = `
2427
+ SELECT
2428
+ ${baseTableName}.${primaryKeyName},
2429
+ ${baseTableName}.data,
2430
+ ${ftsTableName}.rowid as _fts_rowid,
2431
+ ${ftsTableName}.rank as _rank
2432
+ ${fromWhere}
2433
+ `;
1250
2434
  if (options?.orderBy) {
1251
2435
  sql += ` ORDER BY ${this.buildOrderBy(options.orderBy)}`;
1252
2436
  }
@@ -1261,12 +2445,12 @@ export class StorageRecord {
1261
2445
  sql += ` OFFSET ?`;
1262
2446
  params.push(options.offset);
1263
2447
  }
1264
- const rows = this.db.query(sql).all(...params);
2448
+ const rows = this.db.prepare(sql).all(...params);
1265
2449
  const items = [];
1266
2450
  const scores = [];
1267
2451
  const highlights = new Map();
1268
2452
  for (const row of rows) {
1269
- const record = JSON.parse(row.data);
2453
+ const record = this.decodeRecord(row.data);
1270
2454
  items.push(record);
1271
2455
  scores.push(row._rank);
1272
2456
  if (options?.highlight) {
@@ -1274,13 +2458,13 @@ export class StorageRecord {
1274
2458
  const suffix = options.highlightSuffix || '</mark>';
1275
2459
  const id = this.readPrimaryKey(record);
1276
2460
  const fieldHighlights = [];
1277
- for (const field of ftsFields) {
2461
+ for (const fieldIndex of ftsFields.keys()) {
1278
2462
  const highlightSql = `
1279
- SELECT highlight(${ftsTable}, ?, ?, ?) as highlighted
1280
- WHERE ${ftsTable} MATCH ? AND rowid = ?
2463
+ SELECT highlight(${ftsTableName}, ?, ?, ?) as highlighted
2464
+ FROM ${ftsTableName}
2465
+ WHERE ${ftsTableName} MATCH ? AND rowid = ?
1281
2466
  `;
1282
- const fieldIndex = ftsFields.indexOf(field);
1283
- const highlightRow = this.db.query(highlightSql).get(fieldIndex, prefix, suffix, ftsQuery, rows.indexOf(row) + 1);
2467
+ const highlightRow = this.db.prepare(highlightSql).get(fieldIndex, prefix, suffix, ftsQuery, row._fts_rowid);
1284
2468
  if (highlightRow?.highlighted) {
1285
2469
  fieldHighlights.push(highlightRow.highlighted);
1286
2470
  }
@@ -1294,10 +2478,10 @@ export class StorageRecord {
1294
2478
  }
1295
2479
  return {
1296
2480
  items,
1297
- total: items.length,
2481
+ total,
1298
2482
  scores,
1299
2483
  highlights: highlights.size > 0 ? highlights : undefined,
1300
- hasMore: options?.limit ? items.length === options.limit : false
2484
+ hasMore: (options?.offset ?? 0) + items.length < total
1301
2485
  };
1302
2486
  }
1303
2487
  // HIERARCHY
@@ -1330,12 +2514,11 @@ export class StorageRecord {
1330
2514
  )
1331
2515
  SELECT * FROM hierarchy ORDER BY depth, ${this.primaryKey}
1332
2516
  `;
1333
- const rows = this.db.query(sql).all(id, maxDepth);
2517
+ const rows = this.db.prepare(sql).all(id, maxDepth);
1334
2518
  const items = rows.map((row) => {
1335
- const record = JSON.parse(row.data);
1336
- record.depth = row.depth;
2519
+ const record = Object.assign(this.decodeRecord(row.data), { depth: row.depth });
1337
2520
  if (includePath && row.path) {
1338
- record.path = JSON.parse(row.path);
2521
+ return Object.assign(record, { path: this.decodePath(row.path) });
1339
2522
  }
1340
2523
  return record;
1341
2524
  });
@@ -1363,8 +2546,8 @@ export class StorageRecord {
1363
2546
  )
1364
2547
  SELECT * FROM ancestors ORDER BY depth DESC
1365
2548
  `;
1366
- const rows = this.db.query(sql).all(id);
1367
- return rows.map((row) => JSON.parse(row.data));
2549
+ const rows = this.db.prepare(sql).all(id);
2550
+ return rows.map((row) => this.decodeRecord(row.data));
1368
2551
  }
1369
2552
  async getTree(options) {
1370
2553
  await this.ensureInitialized();
@@ -1397,18 +2580,18 @@ export class StorageRecord {
1397
2580
  )
1398
2581
  SELECT * FROM tree ORDER BY depth, ${this.primaryKey}
1399
2582
  `;
1400
- const rows = this.db.query(sql).all(...whereClause.values, maxDepth);
2583
+ const rows = this.db.prepare(sql).all(...whereClause.values, maxDepth);
1401
2584
  const nodeMap = new Map();
1402
2585
  const rootNodes = [];
1403
2586
  for (const row of rows) {
1404
- const record = JSON.parse(row.data);
2587
+ const record = this.decodeRecord(row.data);
1405
2588
  const recordId = this.readPrimaryKey(record);
1406
2589
  const parentId = this.readStringField(record, parentField);
1407
2590
  const node = {
1408
2591
  record,
1409
2592
  children: [],
1410
2593
  depth: row.depth,
1411
- path: JSON.parse(row.path)
2594
+ path: this.decodePath(row.path)
1412
2595
  };
1413
2596
  nodeMap.set(recordId, node);
1414
2597
  if (parentId && nodeMap.has(parentId)) {
@@ -1423,11 +2606,11 @@ export class StorageRecord {
1423
2606
  // RAW QUERIES
1424
2607
  async raw(sql, ...params) {
1425
2608
  await this.ensureInitialized();
1426
- return this.db.query(sql).all(...params);
2609
+ return this.db.prepare(sql).all(...params);
1427
2610
  }
1428
2611
  async queryRaw(options) {
1429
2612
  await this.ensureInitialized();
1430
- const rows = this.db.query(options.sql).all(...(options.params || []));
2613
+ const rows = this.db.prepare(options.sql).all(...(options.params || []));
1431
2614
  if (options.schema) {
1432
2615
  return rows.map((row) => options.schema.parse(row));
1433
2616
  }