@vibesdotdev/storage 0.0.1

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 (423) hide show
  1. package/README.md +75 -0
  2. package/SPEC.md +60 -0
  3. package/dist/adapters/index.d.ts +14 -0
  4. package/dist/adapters/index.d.ts.map +1 -0
  5. package/dist/adapters/index.js +15 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/bridge/index.d.ts +3 -0
  8. package/dist/bridge/index.d.ts.map +1 -0
  9. package/dist/bridge/index.js +2 -0
  10. package/dist/bridge/index.js.map +1 -0
  11. package/dist/bridge/storage-kind-bridge.d.ts +27 -0
  12. package/dist/bridge/storage-kind-bridge.d.ts.map +1 -0
  13. package/dist/bridge/storage-kind-bridge.js +110 -0
  14. package/dist/bridge/storage-kind-bridge.js.map +1 -0
  15. package/dist/bridge/types.d.ts +45 -0
  16. package/dist/bridge/types.d.ts.map +1 -0
  17. package/dist/bridge/types.js +8 -0
  18. package/dist/bridge/types.js.map +1 -0
  19. package/dist/cli/commands/list/storage.list.cli-command.descriptor.d.ts +4 -0
  20. package/dist/cli/commands/list/storage.list.cli-command.descriptor.d.ts.map +1 -0
  21. package/dist/cli/commands/list/storage.list.cli-command.descriptor.js +18 -0
  22. package/dist/cli/commands/list/storage.list.cli-command.descriptor.js.map +1 -0
  23. package/dist/cli/commands/list/storage.list.cli-command.impl.d.ts +11 -0
  24. package/dist/cli/commands/list/storage.list.cli-command.impl.d.ts.map +1 -0
  25. package/dist/cli/commands/list/storage.list.cli-command.impl.js +48 -0
  26. package/dist/cli/commands/list/storage.list.cli-command.impl.js.map +1 -0
  27. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.d.ts +4 -0
  28. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.d.ts.map +1 -0
  29. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.js +26 -0
  30. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.js.map +1 -0
  31. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.d.ts +13 -0
  32. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.d.ts.map +1 -0
  33. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.js +136 -0
  34. package/dist/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.js.map +1 -0
  35. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.d.ts +4 -0
  36. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.d.ts.map +1 -0
  37. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.js +26 -0
  38. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.js.map +1 -0
  39. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.d.ts +13 -0
  40. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.d.ts.map +1 -0
  41. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.js +165 -0
  42. package/dist/cli/commands/sync/push/storage.sync.push.cli-command.impl.js.map +1 -0
  43. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.d.ts +4 -0
  44. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.d.ts.map +1 -0
  45. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.js +20 -0
  46. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.js.map +1 -0
  47. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.d.ts +12 -0
  48. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.d.ts.map +1 -0
  49. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.js +95 -0
  50. package/dist/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.js.map +1 -0
  51. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.d.ts +4 -0
  52. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.d.ts.map +1 -0
  53. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.js +15 -0
  54. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.js.map +1 -0
  55. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.d.ts +13 -0
  56. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.d.ts.map +1 -0
  57. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.js +150 -0
  58. package/dist/cli/commands/sync/status/storage.sync.status.cli-command.impl.js.map +1 -0
  59. package/dist/cli/context.consumer.d.ts +26 -0
  60. package/dist/cli/context.consumer.d.ts.map +1 -0
  61. package/dist/cli/context.consumer.js +164 -0
  62. package/dist/cli/context.consumer.js.map +1 -0
  63. package/dist/cli/storage.cli-group.descriptor.d.ts +4 -0
  64. package/dist/cli/storage.cli-group.descriptor.d.ts.map +1 -0
  65. package/dist/cli/storage.cli-group.descriptor.js +11 -0
  66. package/dist/cli/storage.cli-group.descriptor.js.map +1 -0
  67. package/dist/cli/storage.sync.cli-group.descriptor.d.ts +4 -0
  68. package/dist/cli/storage.sync.cli-group.descriptor.d.ts.map +1 -0
  69. package/dist/cli/storage.sync.cli-group.descriptor.js +12 -0
  70. package/dist/cli/storage.sync.cli-group.descriptor.js.map +1 -0
  71. package/dist/cli/types.d.ts +26 -0
  72. package/dist/cli/types.d.ts.map +1 -0
  73. package/dist/cli/types.js +11 -0
  74. package/dist/cli/types.js.map +1 -0
  75. package/dist/client/index.d.ts +10 -0
  76. package/dist/client/index.d.ts.map +1 -0
  77. package/dist/client/index.js +10 -0
  78. package/dist/client/index.js.map +1 -0
  79. package/dist/client/list-browsable-adapters.d.ts +31 -0
  80. package/dist/client/list-browsable-adapters.d.ts.map +1 -0
  81. package/dist/client/list-browsable-adapters.js +40 -0
  82. package/dist/client/list-browsable-adapters.js.map +1 -0
  83. package/dist/daemon/commands/sync.d.ts +23 -0
  84. package/dist/daemon/commands/sync.d.ts.map +1 -0
  85. package/dist/daemon/commands/sync.js +169 -0
  86. package/dist/daemon/commands/sync.js.map +1 -0
  87. package/dist/docs/adapters.docs.descriptor.d.ts +4 -0
  88. package/dist/docs/adapters.docs.descriptor.d.ts.map +1 -0
  89. package/dist/docs/adapters.docs.descriptor.js +223 -0
  90. package/dist/docs/adapters.docs.descriptor.js.map +1 -0
  91. package/dist/docs/manifests.docs.descriptor.d.ts +4 -0
  92. package/dist/docs/manifests.docs.descriptor.d.ts.map +1 -0
  93. package/dist/docs/manifests.docs.descriptor.js +316 -0
  94. package/dist/docs/manifests.docs.descriptor.js.map +1 -0
  95. package/dist/docs/records.docs.descriptor.d.ts +4 -0
  96. package/dist/docs/records.docs.descriptor.d.ts.map +1 -0
  97. package/dist/docs/records.docs.descriptor.js +269 -0
  98. package/dist/docs/records.docs.descriptor.js.map +1 -0
  99. package/dist/docs/sync.docs.descriptor.d.ts +4 -0
  100. package/dist/docs/sync.docs.descriptor.d.ts.map +1 -0
  101. package/dist/docs/sync.docs.descriptor.js +279 -0
  102. package/dist/docs/sync.docs.descriptor.js.map +1 -0
  103. package/dist/index.d.ts +27 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +29 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/interfaces/adapter.d.ts +382 -0
  108. package/dist/interfaces/adapter.d.ts.map +1 -0
  109. package/dist/interfaces/adapter.js +11 -0
  110. package/dist/interfaces/adapter.js.map +1 -0
  111. package/dist/interfaces/document-store.d.ts +62 -0
  112. package/dist/interfaces/document-store.d.ts.map +1 -0
  113. package/dist/interfaces/document-store.js +8 -0
  114. package/dist/interfaces/document-store.js.map +1 -0
  115. package/dist/interfaces/index.d.ts +17 -0
  116. package/dist/interfaces/index.d.ts.map +1 -0
  117. package/dist/interfaces/index.js +24 -0
  118. package/dist/interfaces/index.js.map +1 -0
  119. package/dist/interfaces/key-value.d.ts +95 -0
  120. package/dist/interfaces/key-value.d.ts.map +1 -0
  121. package/dist/interfaces/key-value.js +8 -0
  122. package/dist/interfaces/key-value.js.map +1 -0
  123. package/dist/interfaces/query-builder.d.ts +31 -0
  124. package/dist/interfaces/query-builder.d.ts.map +1 -0
  125. package/dist/interfaces/query-builder.js +11 -0
  126. package/dist/interfaces/query-builder.js.map +1 -0
  127. package/dist/interfaces/record/index.d.ts +7 -0
  128. package/dist/interfaces/record/index.d.ts.map +1 -0
  129. package/dist/interfaces/record/index.js +7 -0
  130. package/dist/interfaces/record/index.js.map +1 -0
  131. package/dist/interfaces/record/interface.d.ts +593 -0
  132. package/dist/interfaces/record/interface.d.ts.map +1 -0
  133. package/dist/interfaces/record/interface.js +49 -0
  134. package/dist/interfaces/record/interface.js.map +1 -0
  135. package/dist/interfaces/repository.d.ts +262 -0
  136. package/dist/interfaces/repository.d.ts.map +1 -0
  137. package/dist/interfaces/repository.js +8 -0
  138. package/dist/interfaces/repository.js.map +1 -0
  139. package/dist/interfaces/schema/index.d.ts +8 -0
  140. package/dist/interfaces/schema/index.d.ts.map +1 -0
  141. package/dist/interfaces/schema/index.js +10 -0
  142. package/dist/interfaces/schema/index.js.map +1 -0
  143. package/dist/interfaces/schema/schema-interface.d.ts +516 -0
  144. package/dist/interfaces/schema/schema-interface.d.ts.map +1 -0
  145. package/dist/interfaces/schema/schema-interface.js +31 -0
  146. package/dist/interfaces/schema/schema-interface.js.map +1 -0
  147. package/dist/interfaces/schema/table-interface.d.ts +250 -0
  148. package/dist/interfaces/schema/table-interface.d.ts.map +1 -0
  149. package/dist/interfaces/schema/table-interface.js +31 -0
  150. package/dist/interfaces/schema/table-interface.js.map +1 -0
  151. package/dist/interfaces/storage-consumer-fs.d.ts +40 -0
  152. package/dist/interfaces/storage-consumer-fs.d.ts.map +1 -0
  153. package/dist/interfaces/storage-consumer-fs.js +42 -0
  154. package/dist/interfaces/storage-consumer-fs.js.map +1 -0
  155. package/dist/manifest/base.d.ts +187 -0
  156. package/dist/manifest/base.d.ts.map +1 -0
  157. package/dist/manifest/base.js +516 -0
  158. package/dist/manifest/base.js.map +1 -0
  159. package/dist/manifest/cloud-sync.consumer.d.ts +7 -0
  160. package/dist/manifest/cloud-sync.consumer.d.ts.map +1 -0
  161. package/dist/manifest/cloud-sync.consumer.js +82 -0
  162. package/dist/manifest/cloud-sync.consumer.js.map +1 -0
  163. package/dist/manifest/index.d.ts +7 -0
  164. package/dist/manifest/index.d.ts.map +1 -0
  165. package/dist/manifest/index.js +7 -0
  166. package/dist/manifest/index.js.map +1 -0
  167. package/dist/manifest/source-policy.d.ts +8 -0
  168. package/dist/manifest/source-policy.d.ts.map +1 -0
  169. package/dist/manifest/source-policy.js +62 -0
  170. package/dist/manifest/source-policy.js.map +1 -0
  171. package/dist/paths.d.ts +6 -0
  172. package/dist/paths.d.ts.map +1 -0
  173. package/dist/paths.js +152 -0
  174. package/dist/paths.js.map +1 -0
  175. package/dist/policy/context.d.ts +9 -0
  176. package/dist/policy/context.d.ts.map +1 -0
  177. package/dist/policy/context.js +29 -0
  178. package/dist/policy/context.js.map +1 -0
  179. package/dist/policy/index.d.ts +4 -0
  180. package/dist/policy/index.d.ts.map +1 -0
  181. package/dist/policy/index.js +4 -0
  182. package/dist/policy/index.js.map +1 -0
  183. package/dist/policy/overrides.d.ts +40 -0
  184. package/dist/policy/overrides.d.ts.map +1 -0
  185. package/dist/policy/overrides.js +37 -0
  186. package/dist/policy/overrides.js.map +1 -0
  187. package/dist/policy/persistence.d.ts +38 -0
  188. package/dist/policy/persistence.d.ts.map +1 -0
  189. package/dist/policy/persistence.js +65 -0
  190. package/dist/policy/persistence.js.map +1 -0
  191. package/dist/policy/types.d.ts +21 -0
  192. package/dist/policy/types.d.ts.map +1 -0
  193. package/dist/policy/types.js +7 -0
  194. package/dist/policy/types.js.map +1 -0
  195. package/dist/record/base.d.ts +186 -0
  196. package/dist/record/base.d.ts.map +1 -0
  197. package/dist/record/base.js +1437 -0
  198. package/dist/record/base.js.map +1 -0
  199. package/dist/record/events.d.ts +71 -0
  200. package/dist/record/events.d.ts.map +1 -0
  201. package/dist/record/events.js +52 -0
  202. package/dist/record/events.js.map +1 -0
  203. package/dist/record/extensions.d.ts +51 -0
  204. package/dist/record/extensions.d.ts.map +1 -0
  205. package/dist/record/extensions.js +222 -0
  206. package/dist/record/extensions.js.map +1 -0
  207. package/dist/record/index.d.ts +7 -0
  208. package/dist/record/index.d.ts.map +1 -0
  209. package/dist/record/index.js +7 -0
  210. package/dist/record/index.js.map +1 -0
  211. package/dist/resolver/storage-backed-resolver.d.ts +23 -0
  212. package/dist/resolver/storage-backed-resolver.d.ts.map +1 -0
  213. package/dist/resolver/storage-backed-resolver.js +187 -0
  214. package/dist/resolver/storage-backed-resolver.js.map +1 -0
  215. package/dist/schemas/adapter-descriptor.d.ts +108 -0
  216. package/dist/schemas/adapter-descriptor.d.ts.map +1 -0
  217. package/dist/schemas/adapter-descriptor.js +101 -0
  218. package/dist/schemas/adapter-descriptor.js.map +1 -0
  219. package/dist/schemas/index.d.ts +14 -0
  220. package/dist/schemas/index.d.ts.map +1 -0
  221. package/dist/schemas/index.js +12 -0
  222. package/dist/schemas/index.js.map +1 -0
  223. package/dist/schemas/manifest-descriptor.d.ts +110 -0
  224. package/dist/schemas/manifest-descriptor.d.ts.map +1 -0
  225. package/dist/schemas/manifest-descriptor.js +114 -0
  226. package/dist/schemas/manifest-descriptor.js.map +1 -0
  227. package/dist/schemas/record/descriptor.d.ts +570 -0
  228. package/dist/schemas/record/descriptor.d.ts.map +1 -0
  229. package/dist/schemas/record/descriptor.js +389 -0
  230. package/dist/schemas/record/descriptor.js.map +1 -0
  231. package/dist/schemas/record/index.d.ts +8 -0
  232. package/dist/schemas/record/index.d.ts.map +1 -0
  233. package/dist/schemas/record/index.js +7 -0
  234. package/dist/schemas/record/index.js.map +1 -0
  235. package/dist/schemas/record/relation.d.ts +36 -0
  236. package/dist/schemas/record/relation.d.ts.map +1 -0
  237. package/dist/schemas/record/relation.js +37 -0
  238. package/dist/schemas/record/relation.js.map +1 -0
  239. package/dist/schemas/schema/descriptor.d.ts +231 -0
  240. package/dist/schemas/schema/descriptor.d.ts.map +1 -0
  241. package/dist/schemas/schema/descriptor.js +205 -0
  242. package/dist/schemas/schema/descriptor.js.map +1 -0
  243. package/dist/schemas/schema/index.d.ts +7 -0
  244. package/dist/schemas/schema/index.d.ts.map +1 -0
  245. package/dist/schemas/schema/index.js +7 -0
  246. package/dist/schemas/schema/index.js.map +1 -0
  247. package/dist/schemas/sync-bridge-descriptor.d.ts +124 -0
  248. package/dist/schemas/sync-bridge-descriptor.d.ts.map +1 -0
  249. package/dist/schemas/sync-bridge-descriptor.js +67 -0
  250. package/dist/schemas/sync-bridge-descriptor.js.map +1 -0
  251. package/dist/server/manifest-route.d.ts +92 -0
  252. package/dist/server/manifest-route.d.ts.map +1 -0
  253. package/dist/server/manifest-route.js +226 -0
  254. package/dist/server/manifest-route.js.map +1 -0
  255. package/dist/storage-policy.context.descriptor.d.ts +16 -0
  256. package/dist/storage-policy.context.descriptor.d.ts.map +1 -0
  257. package/dist/storage-policy.context.descriptor.js +16 -0
  258. package/dist/storage-policy.context.descriptor.js.map +1 -0
  259. package/dist/storage-policy.context.server.d.ts +14 -0
  260. package/dist/storage-policy.context.server.d.ts.map +1 -0
  261. package/dist/storage-policy.context.server.js +27 -0
  262. package/dist/storage-policy.context.server.js.map +1 -0
  263. package/dist/storage.plugin.d.ts +38 -0
  264. package/dist/storage.plugin.d.ts.map +1 -0
  265. package/dist/storage.plugin.js +204 -0
  266. package/dist/storage.plugin.js.map +1 -0
  267. package/dist/sync/cloud-pull.d.ts +15 -0
  268. package/dist/sync/cloud-pull.d.ts.map +1 -0
  269. package/dist/sync/cloud-pull.js +130 -0
  270. package/dist/sync/cloud-pull.js.map +1 -0
  271. package/dist/sync/cloud-record-crud-config.d.ts +19 -0
  272. package/dist/sync/cloud-record-crud-config.d.ts.map +1 -0
  273. package/dist/sync/cloud-record-crud-config.js +22 -0
  274. package/dist/sync/cloud-record-crud-config.js.map +1 -0
  275. package/dist/sync/record-sync-bridge.d.ts +5 -0
  276. package/dist/sync/record-sync-bridge.d.ts.map +1 -0
  277. package/dist/sync/record-sync-bridge.js +121 -0
  278. package/dist/sync/record-sync-bridge.js.map +1 -0
  279. package/dist/sync/resolve-sync-target.d.ts +23 -0
  280. package/dist/sync/resolve-sync-target.d.ts.map +1 -0
  281. package/dist/sync/resolve-sync-target.js +45 -0
  282. package/dist/sync/resolve-sync-target.js.map +1 -0
  283. package/dist/sync/schemas/cloud-pull.d.ts +25 -0
  284. package/dist/sync/schemas/cloud-pull.d.ts.map +1 -0
  285. package/dist/sync/schemas/cloud-pull.js +7 -0
  286. package/dist/sync/schemas/cloud-pull.js.map +1 -0
  287. package/dist/sync/schemas/index.d.ts +9 -0
  288. package/dist/sync/schemas/index.d.ts.map +1 -0
  289. package/dist/sync/schemas/index.js +9 -0
  290. package/dist/sync/schemas/index.js.map +1 -0
  291. package/dist/sync/schemas/sync-helpers.d.ts +17 -0
  292. package/dist/sync/schemas/sync-helpers.d.ts.map +1 -0
  293. package/dist/sync/schemas/sync-helpers.js +7 -0
  294. package/dist/sync/schemas/sync-helpers.js.map +1 -0
  295. package/dist/sync/schemas/sync-state.d.ts +65 -0
  296. package/dist/sync/schemas/sync-state.d.ts.map +1 -0
  297. package/dist/sync/schemas/sync-state.js +32 -0
  298. package/dist/sync/schemas/sync-state.js.map +1 -0
  299. package/dist/sync/sync-bridge.d.ts +80 -0
  300. package/dist/sync/sync-bridge.d.ts.map +1 -0
  301. package/dist/sync/sync-bridge.js +617 -0
  302. package/dist/sync/sync-bridge.js.map +1 -0
  303. package/dist/sync/sync-manifest.d.ts +20 -0
  304. package/dist/sync/sync-manifest.d.ts.map +1 -0
  305. package/dist/sync/sync-manifest.js +140 -0
  306. package/dist/sync/sync-manifest.js.map +1 -0
  307. package/dist/sync/sync-state.consumer.d.ts +23 -0
  308. package/dist/sync/sync-state.consumer.d.ts.map +1 -0
  309. package/dist/sync/sync-state.consumer.js +111 -0
  310. package/dist/sync/sync-state.consumer.js.map +1 -0
  311. package/dist/transform/index.d.ts +75 -0
  312. package/dist/transform/index.d.ts.map +1 -0
  313. package/dist/transform/index.js +81 -0
  314. package/dist/transform/index.js.map +1 -0
  315. package/dist/transform/transform-descriptor.d.ts +51 -0
  316. package/dist/transform/transform-descriptor.d.ts.map +1 -0
  317. package/dist/transform/transform-descriptor.js +30 -0
  318. package/dist/transform/transform-descriptor.js.map +1 -0
  319. package/dist/tui/storage-explorer.descriptor.d.ts +14 -0
  320. package/dist/tui/storage-explorer.descriptor.d.ts.map +1 -0
  321. package/dist/tui/storage-explorer.descriptor.js +29 -0
  322. package/dist/tui/storage-explorer.descriptor.js.map +1 -0
  323. package/dist/tui/storage-explorer.impl.consumer.d.ts +11 -0
  324. package/dist/tui/storage-explorer.impl.consumer.d.ts.map +1 -0
  325. package/dist/tui/storage-explorer.impl.consumer.js +13 -0
  326. package/dist/tui/storage-explorer.impl.consumer.js.map +1 -0
  327. package/dist/tui/storage-open.descriptor.d.ts +14 -0
  328. package/dist/tui/storage-open.descriptor.d.ts.map +1 -0
  329. package/dist/tui/storage-open.descriptor.js +28 -0
  330. package/dist/tui/storage-open.descriptor.js.map +1 -0
  331. package/dist/tui/storage-open.impl.consumer.d.ts +11 -0
  332. package/dist/tui/storage-open.impl.consumer.d.ts.map +1 -0
  333. package/dist/tui/storage-open.impl.consumer.js +13 -0
  334. package/dist/tui/storage-open.impl.consumer.js.map +1 -0
  335. package/dist/utils/error-context.d.ts +2 -0
  336. package/dist/utils/error-context.d.ts.map +1 -0
  337. package/dist/utils/error-context.js +17 -0
  338. package/dist/utils/error-context.js.map +1 -0
  339. package/package.json +241 -0
  340. package/src/adapters/index.ts +13 -0
  341. package/src/bridge/index.ts +2 -0
  342. package/src/bridge/storage-kind-bridge.ts +131 -0
  343. package/src/bridge/types.ts +46 -0
  344. package/src/cli/commands/list/storage.list.cli-command.descriptor.ts +20 -0
  345. package/src/cli/commands/list/storage.list.cli-command.impl.ts +72 -0
  346. package/src/cli/commands/sync/pull/storage.sync.pull.cli-command.descriptor.ts +28 -0
  347. package/src/cli/commands/sync/pull/storage.sync.pull.cli-command.impl.ts +171 -0
  348. package/src/cli/commands/sync/push/storage.sync.push.cli-command.descriptor.ts +28 -0
  349. package/src/cli/commands/sync/push/storage.sync.push.cli-command.impl.ts +213 -0
  350. package/src/cli/commands/sync/resolve/storage.sync.resolve.cli-command.descriptor.ts +22 -0
  351. package/src/cli/commands/sync/resolve/storage.sync.resolve.cli-command.impl.ts +129 -0
  352. package/src/cli/commands/sync/status/storage.sync.status.cli-command.descriptor.ts +17 -0
  353. package/src/cli/commands/sync/status/storage.sync.status.cli-command.impl.ts +192 -0
  354. package/src/cli/context.consumer.ts +195 -0
  355. package/src/cli/storage.cli-group.descriptor.ts +13 -0
  356. package/src/cli/storage.sync.cli-group.descriptor.ts +14 -0
  357. package/src/cli/types.ts +25 -0
  358. package/src/client/index.ts +10 -0
  359. package/src/client/list-browsable-adapters.ts +42 -0
  360. package/src/daemon/commands/sync.ts +225 -0
  361. package/src/docs/adapters.docs.descriptor.ts +225 -0
  362. package/src/docs/manifests.docs.descriptor.ts +318 -0
  363. package/src/docs/records.docs.descriptor.ts +271 -0
  364. package/src/docs/sync.docs.descriptor.ts +281 -0
  365. package/src/index.ts +109 -0
  366. package/src/interfaces/adapter.ts +451 -0
  367. package/src/interfaces/document-store.ts +74 -0
  368. package/src/interfaces/index.ts +117 -0
  369. package/src/interfaces/key-value.ts +112 -0
  370. package/src/interfaces/query-builder.ts +31 -0
  371. package/src/interfaces/record/index.ts +58 -0
  372. package/src/interfaces/record/interface.ts +723 -0
  373. package/src/interfaces/repository.ts +300 -0
  374. package/src/interfaces/schema/index.ts +37 -0
  375. package/src/interfaces/schema/schema-interface.ts +586 -0
  376. package/src/interfaces/schema/table-interface.ts +314 -0
  377. package/src/interfaces/storage-consumer-fs.ts +66 -0
  378. package/src/manifest/base.ts +594 -0
  379. package/src/manifest/cloud-sync.consumer.ts +120 -0
  380. package/src/manifest/index.ts +7 -0
  381. package/src/manifest/source-policy.ts +74 -0
  382. package/src/paths.ts +171 -0
  383. package/src/policy/context.ts +30 -0
  384. package/src/policy/index.ts +20 -0
  385. package/src/policy/overrides.ts +46 -0
  386. package/src/policy/persistence.ts +117 -0
  387. package/src/policy/types.ts +28 -0
  388. package/src/record/base.ts +1872 -0
  389. package/src/record/events.ts +56 -0
  390. package/src/record/extensions.ts +308 -0
  391. package/src/record/index.ts +7 -0
  392. package/src/resolver/storage-backed-resolver.ts +255 -0
  393. package/src/schemas/adapter-descriptor.ts +128 -0
  394. package/src/schemas/index.ts +104 -0
  395. package/src/schemas/manifest-descriptor.ts +143 -0
  396. package/src/schemas/record/descriptor.ts +621 -0
  397. package/src/schemas/record/index.ts +60 -0
  398. package/src/schemas/record/relation.ts +49 -0
  399. package/src/schemas/schema/descriptor.ts +278 -0
  400. package/src/schemas/schema/index.ts +14 -0
  401. package/src/schemas/sync-bridge-descriptor.ts +143 -0
  402. package/src/server/manifest-route.ts +319 -0
  403. package/src/storage-policy.context.descriptor.ts +17 -0
  404. package/src/storage-policy.context.server.ts +34 -0
  405. package/src/storage.plugin.ts +264 -0
  406. package/src/sync/cloud-pull.ts +202 -0
  407. package/src/sync/cloud-record-crud-config.ts +41 -0
  408. package/src/sync/record-sync-bridge.ts +183 -0
  409. package/src/sync/resolve-sync-target.ts +57 -0
  410. package/src/sync/schemas/cloud-pull.ts +26 -0
  411. package/src/sync/schemas/index.ts +18 -0
  412. package/src/sync/schemas/sync-helpers.ts +18 -0
  413. package/src/sync/schemas/sync-state.ts +41 -0
  414. package/src/sync/sync-bridge.ts +763 -0
  415. package/src/sync/sync-manifest.ts +212 -0
  416. package/src/sync/sync-state.consumer.ts +144 -0
  417. package/src/transform/index.ts +130 -0
  418. package/src/transform/transform-descriptor.ts +53 -0
  419. package/src/tui/storage-explorer.descriptor.ts +32 -0
  420. package/src/tui/storage-explorer.impl.consumer.ts +14 -0
  421. package/src/tui/storage-open.descriptor.ts +31 -0
  422. package/src/tui/storage-open.impl.consumer.ts +14 -0
  423. package/src/utils/error-context.ts +19 -0
@@ -0,0 +1,594 @@
1
+ /**
2
+ * Storage Manifest Base Class
3
+ *
4
+ * Base class for working with stored data. Modules can extend this
5
+ * for custom methods while getting load/save for free.
6
+ *
7
+ * Uses runtime query API to resolve the appropriate adapter.
8
+ */
9
+
10
+ import type { z } from 'zod/v4';
11
+ import { getLogger } from '@vibesdotdev/logging';
12
+ import { getVibesRuntime } from '@vibesdotdev/runtime/api/runtime';
13
+ import type { KindContext } from '@vibesdotdev/runtime/schemas/kind';
14
+ import type { StorageAdapterInterface } from '../interfaces/adapter';
15
+ import type { StorageAdapterDescriptor } from '../schemas/adapter-descriptor';
16
+ import type { StorageManifestDescriptor } from '../schemas/manifest-descriptor';
17
+ import type { DocumentChangeCallback, Unsubscribe } from '../interfaces/document-store';
18
+ import { createTracer } from '@vibesdotdev/runtime/debug';
19
+ import { isBuildingEnvironment } from '@vibesdotdev/runtime';
20
+ import { resolveHomePath, resolveScopePathTemplate, resolveWorkspacePath } from '../paths';
21
+ import { toErrorContext } from '../utils/error-context';
22
+
23
+ const logger = getLogger('storage:manifest');
24
+
25
+ type RuntimeLike = ReturnType<typeof getVibesRuntime>;
26
+
27
+ /**
28
+ * Base class for storage manifests.
29
+ *
30
+ * Provides document-style operations (load, save, exists, delete) using
31
+ * the runtime to resolve the appropriate adapter based on scope and configuration.
32
+ *
33
+ * Includes caching and subscription support for reactive updates.
34
+ *
35
+ * @template T - The type of data being stored
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * // Runtime instantiation (via descriptor)
40
+ * // The runtime will instantiate this with the descriptor
41
+ *
42
+ * // Manual instantiation by ID
43
+ * const storage = StorageManifest.fromId<AIProvider[]>('ai.providers');
44
+ * const providers = await storage.load();
45
+ *
46
+ * // Extended class with custom methods
47
+ * class AIProvidersStorage extends StorageManifest<AIProvider[]> {
48
+ * async getEnabled(): Promise<AIProvider[]> {
49
+ * const all = await this.load() ?? [];
50
+ * return all.filter(p => p.enabled);
51
+ * }
52
+ * }
53
+ * ```
54
+ */
55
+ export class StorageManifest<T> {
56
+ protected descriptor!: StorageManifestDescriptor;
57
+ protected context!: KindContext;
58
+ private adapterCache: StorageAdapterInterface | null = null;
59
+
60
+ // Lazy resolution
61
+ private pendingId: string | null = null;
62
+ private pendingContext: KindContext | undefined;
63
+ private resolved = false;
64
+
65
+ // Caching
66
+ private cachedData: T | null = null;
67
+ private cacheValid = false;
68
+
69
+ // Subscriptions
70
+ private subscribers = new Set<(data: T | null) => void>();
71
+ private storeUnsubscribe: Unsubscribe | null = null;
72
+ private subscriptionStarted = false;
73
+
74
+ private getRuntime(): RuntimeLike {
75
+ return (this.context.runtime as RuntimeLike | undefined) ?? getVibesRuntime();
76
+ }
77
+
78
+ /**
79
+ * Create a storage manifest instance.
80
+ *
81
+ * @param descriptorOrId - Either the full descriptor (for runtime instantiation)
82
+ * or the manifest ID string (for manual instantiation)
83
+ * @param context - Runtime context (optional for manual instantiation, required for runtime)
84
+ */
85
+ constructor(descriptorOrId: StorageManifestDescriptor | string, context?: KindContext) {
86
+ if (typeof descriptorOrId === 'string') {
87
+ // Defer descriptor resolution so module-level singletons don't crash
88
+ // when imported before the runtime is bootstrapped (e.g. SSR imports).
89
+ this.pendingId = descriptorOrId;
90
+ this.pendingContext = context;
91
+ // Set stub descriptor so property accesses before resolve don't throw
92
+ this.descriptor = {
93
+ id: descriptorOrId,
94
+ kind: 'storage/manifest',
95
+ namespace: descriptorOrId
96
+ } as StorageManifestDescriptor;
97
+ this.context = context ?? { runtime: getVibesRuntime() };
98
+ } else {
99
+ // Direct descriptor (runtime instantiation)
100
+ this.descriptor = descriptorOrId;
101
+ this.context = context ?? { runtime: getVibesRuntime() };
102
+ this.resolved = true;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Resolve the descriptor from the runtime on first data access.
108
+ * No-op if already resolved or constructed with a direct descriptor.
109
+ */
110
+ private ensureResolved(): void {
111
+ if (this.resolved || !this.pendingId) return;
112
+
113
+ const runtime =
114
+ (this.pendingContext?.runtime as RuntimeLike | undefined) ??
115
+ (this.context.runtime as RuntimeLike | undefined) ??
116
+ getVibesRuntime();
117
+ const hasKind = runtime.hasKind('storage/manifest');
118
+
119
+ if (!hasKind) {
120
+ // Runtime not yet bootstrapped — keep the stub descriptor.
121
+ // It will be re-resolved on next access after bootstrap.
122
+ return;
123
+ }
124
+
125
+ const descriptor = runtime
126
+ .query<StorageManifestDescriptor, unknown>('storage/manifest')
127
+ .filter((d: StorageManifestDescriptor) => d.id === this.pendingId)
128
+ .firstDescriptor();
129
+
130
+ if (!descriptor) {
131
+ if (isBuildingEnvironment()) return;
132
+ throw new Error(`Storage manifest '${this.pendingId}' not found. Is it registered?`);
133
+ }
134
+
135
+ this.descriptor = descriptor;
136
+ this.context = this.pendingContext ?? { runtime };
137
+ this.resolved = true;
138
+ this.pendingId = null;
139
+ this.pendingContext = undefined;
140
+ }
141
+
142
+ /**
143
+ * Create a storage manifest by looking up its ID.
144
+ * Convenience static method for manual instantiation.
145
+ */
146
+ static fromId<T>(manifestId: string): StorageManifest<T> {
147
+ return new StorageManifest<T>(manifestId);
148
+ }
149
+
150
+ /**
151
+ * Get the schema for this manifest.
152
+ */
153
+ get schema(): z.ZodType<T> {
154
+ this.ensureResolved();
155
+ return this.descriptor.schema as z.ZodType<T>;
156
+ }
157
+
158
+ /**
159
+ * Get the namespace for this manifest.
160
+ */
161
+ get namespace(): string {
162
+ this.ensureResolved();
163
+ return this.descriptor.namespace;
164
+ }
165
+
166
+ /**
167
+ * Resolve the adapter for this manifest using runtime query API.
168
+ *
169
+ * Uses the manifest's allowed adapters list and current scope to
170
+ * select the best available adapter.
171
+ */
172
+ protected async getAdapter(): Promise<StorageAdapterInterface> {
173
+ this.ensureResolved();
174
+
175
+ if (!this.resolved) {
176
+ throw new Error(
177
+ `Storage manifest '${this.descriptor.id}' is not yet resolved. ` +
178
+ `The runtime may not be bootstrapped. Ensure 'storage/manifest' kind is registered before accessing storage.`
179
+ );
180
+ }
181
+
182
+ // Adapters are runtime singletons, so manifest-specific configuration
183
+ // must be re-applied on every access before reuse.
184
+ if (this.adapterCache) {
185
+ this.adapterCache.configure(this.getAdapterConfig());
186
+ return this.adapterCache;
187
+ }
188
+
189
+ const adapterTracer = createTracer(`adapter:${this.descriptor.id}`);
190
+ adapterTracer.start();
191
+
192
+ const runtime = this.getRuntime();
193
+ let adapter: StorageAdapterInterface | null = null;
194
+ let adapterId: string = '';
195
+
196
+ // Prefer explicit default adapter when available for current hardware.
197
+ if (
198
+ this.descriptor.defaultAdapter &&
199
+ this.descriptor.allowedAdapters.includes(this.descriptor.defaultAdapter)
200
+ ) {
201
+ adapterId = this.descriptor.defaultAdapter;
202
+ try {
203
+ adapter = await runtime
204
+ .query<StorageAdapterDescriptor, StorageAdapterInterface>('storage/adapter')
205
+ .withId(adapterId)
206
+ .enabled()
207
+ .resolve();
208
+ } catch {
209
+ // Default adapter not registered or unavailable — fall through to allowed adapters
210
+ adapter = null;
211
+ adapterId = '';
212
+ }
213
+ }
214
+
215
+ // Fallback to first available adapter
216
+ if (!adapter) {
217
+ for (const allowedId of this.descriptor.allowedAdapters) {
218
+ try {
219
+ adapter = await runtime
220
+ .query<StorageAdapterDescriptor, StorageAdapterInterface>('storage/adapter')
221
+ .withId(allowedId)
222
+ .enabled()
223
+ .resolve();
224
+ adapterId = allowedId;
225
+ break;
226
+ } catch {
227
+ continue;
228
+ }
229
+ }
230
+ }
231
+
232
+ if (!adapter || !adapterId) {
233
+ throw new Error(
234
+ `No available adapter for manifest '${this.descriptor.id}'. Checked: ${this.descriptor.allowedAdapters.join(', ')}`
235
+ );
236
+ }
237
+
238
+ // Adapters are shared across manifests. Always apply this manifest's
239
+ // configuration before use so filename/basePath overrides stay isolated.
240
+ const config = this.getAdapterConfig(adapterId);
241
+ adapter.configure(config);
242
+
243
+ // Register per-manifest cloud endpoint overrides so cloud-api uses
244
+ // descriptor endpoints (e.g. /api/storage/chat/rooms) instead of the
245
+ // namespace fallback (/api/storage/chat.rooms).
246
+ if (adapterId === 'storage.cloud-api') {
247
+ const cloudEndpoint = this.getCloudApiEndpoint();
248
+ if (cloudEndpoint && this.hasCloudEndpointRegistrar(adapter)) {
249
+ adapter.registerEndpoint(this.descriptor.namespace, cloudEndpoint);
250
+ }
251
+ }
252
+
253
+ // Cache adapter but don't initialize yet
254
+ // Initialization will happen on first data access
255
+ this.adapterCache = adapter;
256
+ adapterTracer.end('resolved');
257
+ return adapter;
258
+ }
259
+
260
+ /**
261
+ * Get adapter configuration based on manifest scope.
262
+ * Override in subclasses for custom configuration.
263
+ */
264
+ protected getAdapterConfig(adapterId?: string): Record<string, unknown> {
265
+ const workspacePath = resolveWorkspacePath(this.context);
266
+ const config: Record<string, unknown> = {
267
+ basePath: this.getBasePath(workspacePath),
268
+ workspacePath
269
+ };
270
+
271
+ // Pass through descriptor-level flags that adapters need
272
+ if (this.descriptor.customTables) {
273
+ config.customTables = true;
274
+ }
275
+ if (this.descriptor.migrationKey) {
276
+ config.migrationKey = this.descriptor.migrationKey;
277
+ }
278
+
279
+ // Pass through adapter-specific config from descriptor
280
+ if (this.descriptor.adapterConfig) {
281
+ const selectedAdapterId =
282
+ adapterId || this.descriptor.defaultAdapter || this.descriptor.allowedAdapters[0];
283
+ const adapterSpecificConfig = this.descriptor.adapterConfig[selectedAdapterId];
284
+ if (adapterSpecificConfig && typeof adapterSpecificConfig === 'object') {
285
+ Object.assign(config, adapterSpecificConfig);
286
+ }
287
+
288
+ // Backward-compatible normalization for cloud adapter manifests.
289
+ // Only promote endpoint -> apiEndpoint when endpoint is a full URL.
290
+ if (
291
+ selectedAdapterId === 'storage.cloud-api' &&
292
+ typeof config.endpoint === 'string' &&
293
+ typeof config.apiEndpoint !== 'string' &&
294
+ config.endpoint.startsWith('http')
295
+ ) {
296
+ config.apiEndpoint = config.endpoint;
297
+ }
298
+ }
299
+
300
+ return config;
301
+ }
302
+
303
+ private getCloudApiEndpoint(): string | null {
304
+ const raw = this.descriptor.adapterConfig?.['storage.cloud-api'];
305
+ if (!raw || typeof raw !== 'object') return null;
306
+ const endpoint = (raw as Record<string, unknown>).endpoint;
307
+ return typeof endpoint === 'string' && endpoint.length > 0 ? endpoint : null;
308
+ }
309
+
310
+ private hasCloudEndpointRegistrar(
311
+ adapter: StorageAdapterInterface
312
+ ): adapter is StorageAdapterInterface & {
313
+ registerEndpoint(namespace: string, endpoint: string): void;
314
+ } {
315
+ return typeof (adapter as unknown as Record<string, unknown>).registerEndpoint === 'function';
316
+ }
317
+
318
+ /**
319
+ * Get base path for storage based on scope.
320
+ * Uses descriptor's scopePaths if provided, otherwise defaults.
321
+ */
322
+ protected getBasePath(workspacePath = resolveWorkspacePath(this.context)): string {
323
+ const home = resolveHomePath();
324
+ const scope = this.descriptor.scope;
325
+
326
+ // Use custom scope paths if provided
327
+ if (this.descriptor.scopePaths) {
328
+ const customPath = this.descriptor.scopePaths[scope as 'global' | 'project' | 'local'];
329
+ if (customPath) {
330
+ return resolveScopePathTemplate(customPath, workspacePath);
331
+ }
332
+ }
333
+
334
+ // Default path resolution
335
+ switch (scope) {
336
+ case 'global':
337
+ return `${home}/.vibes/storage`;
338
+ case 'project':
339
+ case 'local':
340
+ return `${workspacePath}/.vibes/storage`;
341
+ case 'auto':
342
+ default:
343
+ return `${workspacePath}/.vibes/storage`;
344
+ }
345
+ }
346
+
347
+ // Caching
348
+
349
+ /**
350
+ * Invalidate the cache, forcing the next load to hit storage.
351
+ */
352
+ invalidateCache(): void {
353
+ this.cacheValid = false;
354
+ this.cachedData = null;
355
+ }
356
+
357
+ /**
358
+ * Get cached data if available.
359
+ * @returns Cached data or undefined if not cached
360
+ */
361
+ getCachedData(): T | undefined {
362
+ if (this.cacheValid) {
363
+ return this.cachedData ?? undefined;
364
+ }
365
+ return undefined;
366
+ }
367
+
368
+ /**
369
+ * Check if data is currently cached.
370
+ */
371
+ isCached(): boolean {
372
+ return this.cacheValid;
373
+ }
374
+
375
+ // Document Operations
376
+
377
+ /**
378
+ * Load data from storage.
379
+ *
380
+ * @returns The stored data, or null if not found
381
+ */
382
+ async load(): Promise<T | null> {
383
+ // Return cached data if valid
384
+ if (this.cacheValid) {
385
+ return this.cachedData;
386
+ }
387
+
388
+ const adapter = await this.getAdapter();
389
+ const store = adapter.documentStore(this.descriptor.namespace, this.schema, this.descriptor);
390
+ const data = await store.load();
391
+
392
+ // Update cache
393
+ this.cachedData = data;
394
+ this.cacheValid = true;
395
+
396
+ // Start subscription if adapter supports it and not already started
397
+ if (!this.subscriptionStarted && store.subscribe) {
398
+ this.startSubscription(store.subscribe.bind(store));
399
+ }
400
+
401
+ return data;
402
+ }
403
+
404
+ /**
405
+ * Save data to storage.
406
+ *
407
+ * @param data - The data to save
408
+ */
409
+ async save(data: T): Promise<void> {
410
+ const adapter = await this.getAdapter();
411
+ const store = adapter.documentStore(this.descriptor.namespace, this.schema, this.descriptor);
412
+ await store.save(data);
413
+
414
+ // Update cache
415
+ this.cachedData = data;
416
+ this.cacheValid = true;
417
+
418
+ // Notify subscribers
419
+ this.notifySubscribers(data);
420
+
421
+ // Non-blocking cloud push when a consumer-local save needs to mirror to cloud.
422
+ this.triggerCloudSync(adapter.id, data);
423
+ }
424
+
425
+ /**
426
+ * Trigger a non-blocking cloud push after save.
427
+ * Only fires if the manifest has cloud-api in allowedAdapters.
428
+ *
429
+ * Consumer modules are loaded via opaque dynamic import to prevent Vite
430
+ * from tracing them into the client bundle — they contain Node.js APIs
431
+ * and must only run server-side.
432
+ */
433
+ private triggerCloudSync(activeAdapterId: string, data: T): void {
434
+ if (!this.descriptor.allowedAdapters.includes('storage.cloud-api')) return;
435
+ if (activeAdapterId === 'storage.cloud-api') return;
436
+ if (this.getRuntime().scope.hardware === 'cloud') return;
437
+
438
+ const consumerSyncModule = './cloud-sync.consumer';
439
+
440
+ void import(/* @vite-ignore */ consumerSyncModule)
441
+ .then(({ pushManifestToCloudAfterLocalSave }) =>
442
+ pushManifestToCloudAfterLocalSave({
443
+ descriptor: this.descriptor,
444
+ data,
445
+ activeAdapterId
446
+ })
447
+ )
448
+ .catch(() => {
449
+ // Non-blocking: don't fail the save operation on sync errors.
450
+ });
451
+ }
452
+
453
+ /**
454
+ * Check if data exists in storage.
455
+ */
456
+ async exists(): Promise<boolean> {
457
+ const adapter = await this.getAdapter();
458
+ const store = adapter.documentStore(this.descriptor.namespace, this.schema, this.descriptor);
459
+ return store.exists();
460
+ }
461
+
462
+ /**
463
+ * Delete data from storage.
464
+ */
465
+ async delete(): Promise<void> {
466
+ const adapter = await this.getAdapter();
467
+ const store = adapter.documentStore(this.descriptor.namespace, this.schema, this.descriptor);
468
+ await store.delete();
469
+
470
+ // Clear cache
471
+ this.cachedData = null;
472
+ this.cacheValid = false;
473
+
474
+ // Notify subscribers
475
+ this.notifySubscribers(null);
476
+ }
477
+
478
+ /**
479
+ * Load data or return default value.
480
+ *
481
+ * @param defaultValue - Value to return if no data exists
482
+ */
483
+ async loadOrDefault(defaultValue: T): Promise<T> {
484
+ const data = await this.load();
485
+ return data ?? defaultValue;
486
+ }
487
+
488
+ /**
489
+ * Update data using a transform function.
490
+ *
491
+ * @param transform - Function that receives current data and returns updated data
492
+ */
493
+ async update(transform: (current: T | null) => T): Promise<void> {
494
+ const current = await this.load();
495
+ const updated = transform(current);
496
+ await this.save(updated);
497
+ }
498
+
499
+ /**
500
+ * Validate data against the manifest's schema.
501
+ */
502
+ validate(data: unknown): z.ZodSafeParseResult<T> {
503
+ return this.schema.safeParse(data) as z.ZodSafeParseResult<T>;
504
+ }
505
+
506
+ // Subscriptions
507
+
508
+ /**
509
+ * Subscribe to changes for this manifest.
510
+ * Returns an unsubscribe function.
511
+ *
512
+ * @param callback - Function called when data changes
513
+ * @returns Unsubscribe function
514
+ */
515
+ subscribe(callback: (data: T | null) => void): Unsubscribe {
516
+ this.subscribers.add(callback);
517
+
518
+ // Start storage-level subscription if not already started
519
+ if (!this.subscriptionStarted) {
520
+ this.initSubscription();
521
+ }
522
+
523
+ // Return unsubscribe function
524
+ return () => {
525
+ this.subscribers.delete(callback);
526
+ // Clean up storage subscription if no more subscribers
527
+ if (this.subscribers.size === 0) {
528
+ this.stopSubscription();
529
+ }
530
+ };
531
+ }
532
+
533
+ /**
534
+ * Initialize subscription to storage adapter.
535
+ */
536
+ private async initSubscription(): Promise<void> {
537
+ if (this.subscriptionStarted) return;
538
+
539
+ const adapter = await this.getAdapter();
540
+ const store = adapter.documentStore(this.descriptor.namespace, this.schema, this.descriptor);
541
+
542
+ if (store.subscribe) {
543
+ this.startSubscription(store.subscribe.bind(store));
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Start subscription with the given subscribe function.
549
+ */
550
+ private startSubscription(subscribeFn: (cb: DocumentChangeCallback<T>) => Unsubscribe): void {
551
+ if (this.storeUnsubscribe) return;
552
+
553
+ this.storeUnsubscribe = subscribeFn((data) => {
554
+ // Update cache from external change
555
+ this.cachedData = data;
556
+ this.cacheValid = true;
557
+ // Notify our subscribers
558
+ this.notifySubscribers(data);
559
+ });
560
+
561
+ this.subscriptionStarted = true;
562
+ }
563
+
564
+ /**
565
+ * Stop storage-level subscription.
566
+ */
567
+ private stopSubscription(): void {
568
+ if (this.storeUnsubscribe) {
569
+ this.storeUnsubscribe();
570
+ this.storeUnsubscribe = null;
571
+ }
572
+ this.subscriptionStarted = false;
573
+ }
574
+
575
+ /**
576
+ * Notify all subscribers of a change.
577
+ */
578
+ protected notifySubscribers(data: T | null): void {
579
+ for (const callback of this.subscribers) {
580
+ try {
581
+ callback(data);
582
+ } catch (error) {
583
+ logger.error('Error in manifest subscriber:', toErrorContext(error));
584
+ }
585
+ }
586
+ }
587
+
588
+ /**
589
+ * Get the number of active subscribers.
590
+ */
591
+ getSubscriberCount(): number {
592
+ return this.subscribers.size;
593
+ }
594
+ }
@@ -0,0 +1,120 @@
1
+ import { getVibesRuntime } from '@vibesdotdev/runtime/api/runtime';
2
+ import type { z } from 'zod/v4';
3
+ import type { StorageAdapterInterface } from '../interfaces/adapter';
4
+ import type { StorageAdapterDescriptor } from '../schemas/adapter-descriptor';
5
+ import type { StorageManifestDescriptor } from '../schemas/manifest-descriptor';
6
+ import {
7
+ computeHash,
8
+ loadSyncState,
9
+ markError,
10
+ markLocalAhead,
11
+ markSynced,
12
+ saveSyncState
13
+ } from '../sync/sync-state.consumer';
14
+
15
+ const REMOTE_ADAPTER_IDS = new Set(['storage.cloud-api', 'storage.drizzle']);
16
+
17
+ function getCloudEndpoint(descriptor: StorageManifestDescriptor): string | null {
18
+ const raw = descriptor.adapterConfig?.['storage.cloud-api'];
19
+ if (!raw || typeof raw !== 'object') return null;
20
+ const endpoint = (raw as Record<string, unknown>).endpoint;
21
+ return typeof endpoint === 'string' && endpoint.length > 0 ? endpoint : null;
22
+ }
23
+
24
+ function hasEndpointRegistrar(
25
+ adapter: StorageAdapterInterface
26
+ ): adapter is StorageAdapterInterface & {
27
+ registerEndpoint(namespace: string, endpoint: string): void;
28
+ } {
29
+ return 'registerEndpoint' in adapter && typeof adapter.registerEndpoint === 'function';
30
+ }
31
+
32
+ function isZodSchema<T>(schema: object | null): schema is z.ZodType<T> {
33
+ return schema !== null && '_zod' in schema;
34
+ }
35
+
36
+ async function isCloudSyncEnabled(): Promise<boolean> {
37
+ const runtime = getVibesRuntime();
38
+ if (!runtime.hasKind('config/manifest')) {
39
+ return false;
40
+ }
41
+
42
+ try {
43
+ const manifest = (await runtime
44
+ .query('config/manifest')
45
+ .withId('ai.core')
46
+ .enabled()
47
+ .resolve()) as { load(): Promise<Record<string, unknown> | null> } | null;
48
+ if (!manifest) {
49
+ return false;
50
+ }
51
+
52
+ const config = await manifest.load();
53
+ return config?.syncToCloud === true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ export async function pushManifestToCloudAfterLocalSave<T>({
60
+ descriptor,
61
+ data,
62
+ activeAdapterId
63
+ }: {
64
+ descriptor: StorageManifestDescriptor;
65
+ data: T;
66
+ activeAdapterId: string;
67
+ }): Promise<void> {
68
+ if (!descriptor.allowedAdapters.includes('storage.cloud-api')) {
69
+ return;
70
+ }
71
+
72
+ if (REMOTE_ADAPTER_IDS.has(activeAdapterId)) {
73
+ return;
74
+ }
75
+
76
+ const runtime = getVibesRuntime();
77
+ if (runtime.scope?.surface === 'worker' || runtime.scope?.purpose === 'worker') {
78
+ return;
79
+ }
80
+
81
+ if (!(await isCloudSyncEnabled())) {
82
+ return;
83
+ }
84
+
85
+ const localHash = computeHash(data);
86
+ const syncState = await loadSyncState();
87
+
88
+ try {
89
+ const cloudAdapter = await runtime
90
+ .query<StorageAdapterDescriptor, StorageAdapterInterface>('storage/adapter')
91
+ .withId('storage.cloud-api')
92
+ .enabled()
93
+ .resolve();
94
+
95
+ const endpoint = getCloudEndpoint(descriptor);
96
+ if (endpoint && hasEndpointRegistrar(cloudAdapter)) {
97
+ cloudAdapter.registerEndpoint(descriptor.namespace, endpoint);
98
+ }
99
+
100
+ await cloudAdapter.ensureReady();
101
+ const schema =
102
+ typeof descriptor.schema === 'object' && isZodSchema<T>(descriptor.schema)
103
+ ? descriptor.schema
104
+ : null;
105
+ if (!schema) {
106
+ throw new Error(`Storage manifest '${descriptor.id}' is missing a valid schema`);
107
+ }
108
+
109
+ const store = cloudAdapter.documentStore(descriptor.namespace, schema, descriptor);
110
+ await store.save(data);
111
+
112
+ markSynced(syncState, descriptor.id, localHash, 'push');
113
+ } catch (error) {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ markLocalAhead(syncState, descriptor.id, localHash);
116
+ markError(syncState, descriptor.id, message);
117
+ }
118
+
119
+ await saveSyncState(syncState);
120
+ }