@proseql/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. package/LICENSE +21 -0
  2. package/dist/errors/crud-errors.d.ts +98 -0
  3. package/dist/errors/crud-errors.d.ts.map +1 -0
  4. package/dist/errors/crud-errors.js +23 -0
  5. package/dist/errors/crud-errors.js.map +1 -0
  6. package/dist/errors/index.d.ts +16 -0
  7. package/dist/errors/index.d.ts.map +1 -0
  8. package/dist/errors/index.js +12 -0
  9. package/dist/errors/index.js.map +1 -0
  10. package/dist/errors/migration-errors.d.ts +22 -0
  11. package/dist/errors/migration-errors.d.ts.map +1 -0
  12. package/dist/errors/migration-errors.js +14 -0
  13. package/dist/errors/migration-errors.js.map +1 -0
  14. package/dist/errors/plugin-errors.d.ts +15 -0
  15. package/dist/errors/plugin-errors.d.ts.map +1 -0
  16. package/dist/errors/plugin-errors.js +11 -0
  17. package/dist/errors/plugin-errors.js.map +1 -0
  18. package/dist/errors/query-errors.d.ts +31 -0
  19. package/dist/errors/query-errors.d.ts.map +1 -0
  20. package/dist/errors/query-errors.js +11 -0
  21. package/dist/errors/query-errors.js.map +1 -0
  22. package/dist/errors/storage-errors.d.ts +30 -0
  23. package/dist/errors/storage-errors.d.ts.map +1 -0
  24. package/dist/errors/storage-errors.js +11 -0
  25. package/dist/errors/storage-errors.js.map +1 -0
  26. package/dist/factories/crud-factory-with-relationships.d.ts +28 -0
  27. package/dist/factories/crud-factory-with-relationships.d.ts.map +1 -0
  28. package/dist/factories/crud-factory-with-relationships.js +8 -0
  29. package/dist/factories/crud-factory-with-relationships.js.map +1 -0
  30. package/dist/factories/crud-factory.d.ts +25 -0
  31. package/dist/factories/crud-factory.d.ts.map +1 -0
  32. package/dist/factories/crud-factory.js +8 -0
  33. package/dist/factories/crud-factory.js.map +1 -0
  34. package/dist/factories/database-effect.d.ts +241 -0
  35. package/dist/factories/database-effect.d.ts.map +1 -0
  36. package/dist/factories/database-effect.js +859 -0
  37. package/dist/factories/database-effect.js.map +1 -0
  38. package/dist/hooks/hook-runner.d.ts +60 -0
  39. package/dist/hooks/hook-runner.d.ts.map +1 -0
  40. package/dist/hooks/hook-runner.js +107 -0
  41. package/dist/hooks/hook-runner.js.map +1 -0
  42. package/dist/index.d.ts +84 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +110 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/indexes/index-lookup.d.ts +33 -0
  47. package/dist/indexes/index-lookup.d.ts.map +1 -0
  48. package/dist/indexes/index-lookup.js +180 -0
  49. package/dist/indexes/index-lookup.js.map +1 -0
  50. package/dist/indexes/index-manager.d.ts +118 -0
  51. package/dist/indexes/index-manager.d.ts.map +1 -0
  52. package/dist/indexes/index-manager.js +345 -0
  53. package/dist/indexes/index-manager.js.map +1 -0
  54. package/dist/indexes/search-index.d.ts +179 -0
  55. package/dist/indexes/search-index.d.ts.map +1 -0
  56. package/dist/indexes/search-index.js +405 -0
  57. package/dist/indexes/search-index.js.map +1 -0
  58. package/dist/migrations/migration-runner.d.ts +70 -0
  59. package/dist/migrations/migration-runner.d.ts.map +1 -0
  60. package/dist/migrations/migration-runner.js +271 -0
  61. package/dist/migrations/migration-runner.js.map +1 -0
  62. package/dist/migrations/migration-types.d.ts +63 -0
  63. package/dist/migrations/migration-types.d.ts.map +1 -0
  64. package/dist/migrations/migration-types.js +5 -0
  65. package/dist/migrations/migration-types.js.map +1 -0
  66. package/dist/operations/crud/create-with-relationships.d.ts +44 -0
  67. package/dist/operations/crud/create-with-relationships.d.ts.map +1 -0
  68. package/dist/operations/crud/create-with-relationships.js +483 -0
  69. package/dist/operations/crud/create-with-relationships.js.map +1 -0
  70. package/dist/operations/crud/create.d.ts +48 -0
  71. package/dist/operations/crud/create.d.ts.map +1 -0
  72. package/dist/operations/crud/create.js +333 -0
  73. package/dist/operations/crud/create.js.map +1 -0
  74. package/dist/operations/crud/delete-with-relationships.d.ts +63 -0
  75. package/dist/operations/crud/delete-with-relationships.d.ts.map +1 -0
  76. package/dist/operations/crud/delete-with-relationships.js +395 -0
  77. package/dist/operations/crud/delete-with-relationships.js.map +1 -0
  78. package/dist/operations/crud/delete.d.ts +58 -0
  79. package/dist/operations/crud/delete.d.ts.map +1 -0
  80. package/dist/operations/crud/delete.js +267 -0
  81. package/dist/operations/crud/delete.js.map +1 -0
  82. package/dist/operations/crud/unique-check.d.ts +114 -0
  83. package/dist/operations/crud/unique-check.d.ts.map +1 -0
  84. package/dist/operations/crud/unique-check.js +383 -0
  85. package/dist/operations/crud/unique-check.js.map +1 -0
  86. package/dist/operations/crud/update-with-relationships.d.ts +45 -0
  87. package/dist/operations/crud/update-with-relationships.d.ts.map +1 -0
  88. package/dist/operations/crud/update-with-relationships.js +516 -0
  89. package/dist/operations/crud/update-with-relationships.js.map +1 -0
  90. package/dist/operations/crud/update.d.ts +91 -0
  91. package/dist/operations/crud/update.d.ts.map +1 -0
  92. package/dist/operations/crud/update.js +505 -0
  93. package/dist/operations/crud/update.js.map +1 -0
  94. package/dist/operations/crud/upsert.d.ts +52 -0
  95. package/dist/operations/crud/upsert.d.ts.map +1 -0
  96. package/dist/operations/crud/upsert.js +386 -0
  97. package/dist/operations/crud/upsert.js.map +1 -0
  98. package/dist/operations/query/aggregate.d.ts +30 -0
  99. package/dist/operations/query/aggregate.d.ts.map +1 -0
  100. package/dist/operations/query/aggregate.js +227 -0
  101. package/dist/operations/query/aggregate.js.map +1 -0
  102. package/dist/operations/query/cursor-stream.d.ts +18 -0
  103. package/dist/operations/query/cursor-stream.d.ts.map +1 -0
  104. package/dist/operations/query/cursor-stream.js +199 -0
  105. package/dist/operations/query/cursor-stream.js.map +1 -0
  106. package/dist/operations/query/filter-stream.d.ts +12 -0
  107. package/dist/operations/query/filter-stream.d.ts.map +1 -0
  108. package/dist/operations/query/filter-stream.js +167 -0
  109. package/dist/operations/query/filter-stream.js.map +1 -0
  110. package/dist/operations/query/filter.d.ts +13 -0
  111. package/dist/operations/query/filter.d.ts.map +1 -0
  112. package/dist/operations/query/filter.js +267 -0
  113. package/dist/operations/query/filter.js.map +1 -0
  114. package/dist/operations/query/paginate-stream.d.ts +11 -0
  115. package/dist/operations/query/paginate-stream.d.ts.map +1 -0
  116. package/dist/operations/query/paginate-stream.js +22 -0
  117. package/dist/operations/query/paginate-stream.js.map +1 -0
  118. package/dist/operations/query/query-helpers.d.ts +14 -0
  119. package/dist/operations/query/query-helpers.d.ts.map +1 -0
  120. package/dist/operations/query/query-helpers.js +22 -0
  121. package/dist/operations/query/query-helpers.js.map +1 -0
  122. package/dist/operations/query/resolve-computed.d.ts +142 -0
  123. package/dist/operations/query/resolve-computed.d.ts.map +1 -0
  124. package/dist/operations/query/resolve-computed.js +197 -0
  125. package/dist/operations/query/resolve-computed.js.map +1 -0
  126. package/dist/operations/query/search.d.ts +110 -0
  127. package/dist/operations/query/search.d.ts.map +1 -0
  128. package/dist/operations/query/search.js +188 -0
  129. package/dist/operations/query/search.js.map +1 -0
  130. package/dist/operations/query/select-stream.d.ts +27 -0
  131. package/dist/operations/query/select-stream.d.ts.map +1 -0
  132. package/dist/operations/query/select-stream.js +88 -0
  133. package/dist/operations/query/select-stream.js.map +1 -0
  134. package/dist/operations/query/select.d.ts +54 -0
  135. package/dist/operations/query/select.d.ts.map +1 -0
  136. package/dist/operations/query/select.js +159 -0
  137. package/dist/operations/query/select.js.map +1 -0
  138. package/dist/operations/query/sort-stream.d.ts +46 -0
  139. package/dist/operations/query/sort-stream.d.ts.map +1 -0
  140. package/dist/operations/query/sort-stream.js +158 -0
  141. package/dist/operations/query/sort-stream.js.map +1 -0
  142. package/dist/operations/query/sort.d.ts +9 -0
  143. package/dist/operations/query/sort.d.ts.map +1 -0
  144. package/dist/operations/query/sort.js +58 -0
  145. package/dist/operations/query/sort.js.map +1 -0
  146. package/dist/operations/relationships/populate-stream.d.ts +29 -0
  147. package/dist/operations/relationships/populate-stream.d.ts.map +1 -0
  148. package/dist/operations/relationships/populate-stream.js +159 -0
  149. package/dist/operations/relationships/populate-stream.js.map +1 -0
  150. package/dist/operations/relationships/populate.d.ts +15 -0
  151. package/dist/operations/relationships/populate.d.ts.map +1 -0
  152. package/dist/operations/relationships/populate.js +228 -0
  153. package/dist/operations/relationships/populate.js.map +1 -0
  154. package/dist/plugins/plugin-hooks.d.ts +25 -0
  155. package/dist/plugins/plugin-hooks.d.ts.map +1 -0
  156. package/dist/plugins/plugin-hooks.js +64 -0
  157. package/dist/plugins/plugin-hooks.js.map +1 -0
  158. package/dist/plugins/plugin-registry.d.ts +26 -0
  159. package/dist/plugins/plugin-registry.d.ts.map +1 -0
  160. package/dist/plugins/plugin-registry.js +150 -0
  161. package/dist/plugins/plugin-registry.js.map +1 -0
  162. package/dist/plugins/plugin-types.d.ts +95 -0
  163. package/dist/plugins/plugin-types.d.ts.map +1 -0
  164. package/dist/plugins/plugin-types.js +6 -0
  165. package/dist/plugins/plugin-types.js.map +1 -0
  166. package/dist/plugins/plugin-validation.d.ts +49 -0
  167. package/dist/plugins/plugin-validation.d.ts.map +1 -0
  168. package/dist/plugins/plugin-validation.js +295 -0
  169. package/dist/plugins/plugin-validation.js.map +1 -0
  170. package/dist/reactive/change-event.d.ts +44 -0
  171. package/dist/reactive/change-event.d.ts.map +1 -0
  172. package/dist/reactive/change-event.js +49 -0
  173. package/dist/reactive/change-event.js.map +1 -0
  174. package/dist/reactive/change-pubsub.d.ts +32 -0
  175. package/dist/reactive/change-pubsub.d.ts.map +1 -0
  176. package/dist/reactive/change-pubsub.js +31 -0
  177. package/dist/reactive/change-pubsub.js.map +1 -0
  178. package/dist/reactive/evaluate-query.d.ts +62 -0
  179. package/dist/reactive/evaluate-query.d.ts.map +1 -0
  180. package/dist/reactive/evaluate-query.js +57 -0
  181. package/dist/reactive/evaluate-query.js.map +1 -0
  182. package/dist/reactive/watch-by-id.d.ts +53 -0
  183. package/dist/reactive/watch-by-id.d.ts.map +1 -0
  184. package/dist/reactive/watch-by-id.js +55 -0
  185. package/dist/reactive/watch-by-id.js.map +1 -0
  186. package/dist/reactive/watch.d.ts +78 -0
  187. package/dist/reactive/watch.d.ts.map +1 -0
  188. package/dist/reactive/watch.js +133 -0
  189. package/dist/reactive/watch.js.map +1 -0
  190. package/dist/serializers/codecs/hjson.d.ts +33 -0
  191. package/dist/serializers/codecs/hjson.d.ts.map +1 -0
  192. package/dist/serializers/codecs/hjson.js +40 -0
  193. package/dist/serializers/codecs/hjson.js.map +1 -0
  194. package/dist/serializers/codecs/json.d.ts +22 -0
  195. package/dist/serializers/codecs/json.d.ts.map +1 -0
  196. package/dist/serializers/codecs/json.js +28 -0
  197. package/dist/serializers/codecs/json.js.map +1 -0
  198. package/dist/serializers/codecs/json5.d.ts +26 -0
  199. package/dist/serializers/codecs/json5.d.ts.map +1 -0
  200. package/dist/serializers/codecs/json5.js +33 -0
  201. package/dist/serializers/codecs/json5.js.map +1 -0
  202. package/dist/serializers/codecs/jsonc.d.ts +29 -0
  203. package/dist/serializers/codecs/jsonc.d.ts.map +1 -0
  204. package/dist/serializers/codecs/jsonc.js +38 -0
  205. package/dist/serializers/codecs/jsonc.js.map +1 -0
  206. package/dist/serializers/codecs/jsonl.d.ts +17 -0
  207. package/dist/serializers/codecs/jsonl.d.ts.map +1 -0
  208. package/dist/serializers/codecs/jsonl.js +31 -0
  209. package/dist/serializers/codecs/jsonl.js.map +1 -0
  210. package/dist/serializers/codecs/prose.d.ts +419 -0
  211. package/dist/serializers/codecs/prose.d.ts.map +1 -0
  212. package/dist/serializers/codecs/prose.js +1060 -0
  213. package/dist/serializers/codecs/prose.js.map +1 -0
  214. package/dist/serializers/codecs/toml.d.ts +23 -0
  215. package/dist/serializers/codecs/toml.d.ts.map +1 -0
  216. package/dist/serializers/codecs/toml.js +66 -0
  217. package/dist/serializers/codecs/toml.js.map +1 -0
  218. package/dist/serializers/codecs/toon.d.ts +20 -0
  219. package/dist/serializers/codecs/toon.d.ts.map +1 -0
  220. package/dist/serializers/codecs/toon.js +33 -0
  221. package/dist/serializers/codecs/toon.js.map +1 -0
  222. package/dist/serializers/codecs/yaml.d.ts +24 -0
  223. package/dist/serializers/codecs/yaml.d.ts.map +1 -0
  224. package/dist/serializers/codecs/yaml.js +31 -0
  225. package/dist/serializers/codecs/yaml.js.map +1 -0
  226. package/dist/serializers/format-codec.d.ts +53 -0
  227. package/dist/serializers/format-codec.d.ts.map +1 -0
  228. package/dist/serializers/format-codec.js +148 -0
  229. package/dist/serializers/format-codec.js.map +1 -0
  230. package/dist/serializers/presets.d.ts +48 -0
  231. package/dist/serializers/presets.d.ts.map +1 -0
  232. package/dist/serializers/presets.js +72 -0
  233. package/dist/serializers/presets.js.map +1 -0
  234. package/dist/serializers/serializer-service.d.ts +11 -0
  235. package/dist/serializers/serializer-service.d.ts.map +1 -0
  236. package/dist/serializers/serializer-service.js +4 -0
  237. package/dist/serializers/serializer-service.js.map +1 -0
  238. package/dist/state/collection-state.d.ts +19 -0
  239. package/dist/state/collection-state.d.ts.map +1 -0
  240. package/dist/state/collection-state.js +15 -0
  241. package/dist/state/collection-state.js.map +1 -0
  242. package/dist/state/state-operations.d.ts +38 -0
  243. package/dist/state/state-operations.d.ts.map +1 -0
  244. package/dist/state/state-operations.js +65 -0
  245. package/dist/state/state-operations.js.map +1 -0
  246. package/dist/storage/in-memory-adapter-layer.d.ts +16 -0
  247. package/dist/storage/in-memory-adapter-layer.d.ts.map +1 -0
  248. package/dist/storage/in-memory-adapter-layer.js +81 -0
  249. package/dist/storage/in-memory-adapter-layer.js.map +1 -0
  250. package/dist/storage/persistence-effect.d.ts +244 -0
  251. package/dist/storage/persistence-effect.d.ts.map +1 -0
  252. package/dist/storage/persistence-effect.js +551 -0
  253. package/dist/storage/persistence-effect.js.map +1 -0
  254. package/dist/storage/storage-service.d.ts +22 -0
  255. package/dist/storage/storage-service.d.ts.map +1 -0
  256. package/dist/storage/storage-service.js +4 -0
  257. package/dist/storage/storage-service.js.map +1 -0
  258. package/dist/storage/transforms.d.ts +183 -0
  259. package/dist/storage/transforms.d.ts.map +1 -0
  260. package/dist/storage/transforms.js +263 -0
  261. package/dist/storage/transforms.js.map +1 -0
  262. package/dist/transactions/transaction.d.ts +87 -0
  263. package/dist/transactions/transaction.d.ts.map +1 -0
  264. package/dist/transactions/transaction.js +240 -0
  265. package/dist/transactions/transaction.js.map +1 -0
  266. package/dist/types/aggregate-types.d.ts +73 -0
  267. package/dist/types/aggregate-types.d.ts.map +1 -0
  268. package/dist/types/aggregate-types.js +14 -0
  269. package/dist/types/aggregate-types.js.map +1 -0
  270. package/dist/types/computed-types.d.ts +71 -0
  271. package/dist/types/computed-types.d.ts.map +1 -0
  272. package/dist/types/computed-types.js +8 -0
  273. package/dist/types/computed-types.js.map +1 -0
  274. package/dist/types/crud-relationship-types.d.ts +180 -0
  275. package/dist/types/crud-relationship-types.d.ts.map +1 -0
  276. package/dist/types/crud-relationship-types.js +17 -0
  277. package/dist/types/crud-relationship-types.js.map +1 -0
  278. package/dist/types/crud-types.d.ts +343 -0
  279. package/dist/types/crud-types.d.ts.map +1 -0
  280. package/dist/types/crud-types.js +43 -0
  281. package/dist/types/crud-types.js.map +1 -0
  282. package/dist/types/cursor-types.d.ts +52 -0
  283. package/dist/types/cursor-types.d.ts.map +1 -0
  284. package/dist/types/cursor-types.js +2 -0
  285. package/dist/types/cursor-types.js.map +1 -0
  286. package/dist/types/database-config-types.d.ts +196 -0
  287. package/dist/types/database-config-types.d.ts.map +1 -0
  288. package/dist/types/database-config-types.js +11 -0
  289. package/dist/types/database-config-types.js.map +1 -0
  290. package/dist/types/hook-types.d.ts +158 -0
  291. package/dist/types/hook-types.d.ts.map +1 -0
  292. package/dist/types/hook-types.js +6 -0
  293. package/dist/types/hook-types.js.map +1 -0
  294. package/dist/types/index-types.d.ts +42 -0
  295. package/dist/types/index-types.d.ts.map +1 -0
  296. package/dist/types/index-types.js +8 -0
  297. package/dist/types/index-types.js.map +1 -0
  298. package/dist/types/operators.d.ts +5 -0
  299. package/dist/types/operators.d.ts.map +1 -0
  300. package/dist/types/operators.js +297 -0
  301. package/dist/types/operators.js.map +1 -0
  302. package/dist/types/query-overloads.d.ts +54 -0
  303. package/dist/types/query-overloads.d.ts.map +1 -0
  304. package/dist/types/query-overloads.js +3 -0
  305. package/dist/types/query-overloads.js.map +1 -0
  306. package/dist/types/reactive-types.d.ts +75 -0
  307. package/dist/types/reactive-types.d.ts.map +1 -0
  308. package/dist/types/reactive-types.js +7 -0
  309. package/dist/types/reactive-types.js.map +1 -0
  310. package/dist/types/schema-types.d.ts +56 -0
  311. package/dist/types/schema-types.d.ts.map +1 -0
  312. package/dist/types/schema-types.js +8 -0
  313. package/dist/types/schema-types.js.map +1 -0
  314. package/dist/types/search-types.d.ts +82 -0
  315. package/dist/types/search-types.d.ts.map +1 -0
  316. package/dist/types/search-types.js +110 -0
  317. package/dist/types/search-types.js.map +1 -0
  318. package/dist/types/types.d.ts +286 -0
  319. package/dist/types/types.d.ts.map +1 -0
  320. package/dist/types/types.js +2 -0
  321. package/dist/types/types.js.map +1 -0
  322. package/dist/utils/id-generator.d.ts +97 -0
  323. package/dist/utils/id-generator.d.ts.map +1 -0
  324. package/dist/utils/id-generator.js +247 -0
  325. package/dist/utils/id-generator.js.map +1 -0
  326. package/dist/utils/nested-path.d.ts +56 -0
  327. package/dist/utils/nested-path.d.ts.map +1 -0
  328. package/dist/utils/nested-path.js +119 -0
  329. package/dist/utils/nested-path.js.map +1 -0
  330. package/dist/utils/path.d.ts +16 -0
  331. package/dist/utils/path.d.ts.map +1 -0
  332. package/dist/utils/path.js +24 -0
  333. package/dist/utils/path.js.map +1 -0
  334. package/dist/validators/foreign-key.d.ts +49 -0
  335. package/dist/validators/foreign-key.d.ts.map +1 -0
  336. package/dist/validators/foreign-key.js +153 -0
  337. package/dist/validators/foreign-key.js.map +1 -0
  338. package/dist/validators/schema-validator.d.ts +19 -0
  339. package/dist/validators/schema-validator.d.ts.map +1 -0
  340. package/dist/validators/schema-validator.js +34 -0
  341. package/dist/validators/schema-validator.js.map +1 -0
  342. package/package.json +57 -0
@@ -0,0 +1,19 @@
1
+ import { type Effect, Ref } from "effect";
2
+ /**
3
+ * Entity constraint: must have a readonly string `id` field.
4
+ */
5
+ type HasId = {
6
+ readonly id: string;
7
+ };
8
+ /**
9
+ * Creates a Ref holding a ReadonlyMap<string, T> from an initial array of entities.
10
+ *
11
+ * Each entity is keyed by its `id` field, giving O(1) lookup by ID.
12
+ * If no initial data is provided, the Ref starts with an empty map.
13
+ *
14
+ * @param initialData - Array of entities to seed the collection state
15
+ * @returns Effect producing a Ref containing a ReadonlyMap keyed by entity ID
16
+ */
17
+ export declare const createCollectionState: <T extends HasId>(initialData?: ReadonlyArray<T>) => Effect.Effect<Ref.Ref<ReadonlyMap<string, T>>>;
18
+ export {};
19
+ //# sourceMappingURL=collection-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-state.d.ts","sourceRoot":"","sources":["../../src/state/collection-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE1C;;GAEG;AACH,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,KAAK,EACpD,cAAa,aAAa,CAAC,CAAC,CAAM,KAChC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAK/C,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { Ref } from "effect";
2
+ /**
3
+ * Creates a Ref holding a ReadonlyMap<string, T> from an initial array of entities.
4
+ *
5
+ * Each entity is keyed by its `id` field, giving O(1) lookup by ID.
6
+ * If no initial data is provided, the Ref starts with an empty map.
7
+ *
8
+ * @param initialData - Array of entities to seed the collection state
9
+ * @returns Effect producing a Ref containing a ReadonlyMap keyed by entity ID
10
+ */
11
+ export const createCollectionState = (initialData = []) => {
12
+ const map = new Map(initialData.map((entity) => [entity.id, entity]));
13
+ return Ref.make(map);
14
+ };
15
+ //# sourceMappingURL=collection-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection-state.js","sourceRoot":"","sources":["../../src/state/collection-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,GAAG,EAAE,MAAM,QAAQ,CAAC;AAO1C;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,cAAgC,EAAE,EACe,EAAE;IACnD,MAAM,GAAG,GAA2B,IAAI,GAAG,CAC1C,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC,CAChD,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACtB,CAAC,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { Effect, Option, Ref } from "effect";
2
+ import { NotFoundError } from "../errors/crud-errors.js";
3
+ /**
4
+ * Entity constraint: must have a readonly string `id` field.
5
+ */
6
+ type HasId = {
7
+ readonly id: string;
8
+ };
9
+ /**
10
+ * Gets a single entity by ID from a collection Ref.
11
+ * Returns Option.some if found, Option.none if not.
12
+ */
13
+ export declare const getEntity: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>, id: string) => Effect.Effect<Option.Option<T>>;
14
+ /**
15
+ * Gets a single entity by ID, failing with NotFoundError if not present.
16
+ */
17
+ export declare const getEntityOrFail: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>, id: string, collection: string) => Effect.Effect<T, NotFoundError>;
18
+ /**
19
+ * Gets all entities from a collection Ref as a ReadonlyArray.
20
+ */
21
+ export declare const getAllEntities: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>) => Effect.Effect<ReadonlyArray<T>>;
22
+ /**
23
+ * Sets (creates or replaces) an entity in the collection Ref.
24
+ * The entity is keyed by its `id` field.
25
+ */
26
+ export declare const setEntity: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>, entity: T) => Effect.Effect<void>;
27
+ /**
28
+ * Removes an entity by ID from the collection Ref.
29
+ * Returns true if the entity existed and was removed, false if it was not present.
30
+ */
31
+ export declare const removeEntity: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>, id: string) => Effect.Effect<boolean>;
32
+ /**
33
+ * Atomically updates an entity by ID using an updater function.
34
+ * Returns the updated entity. Fails with NotFoundError if the entity doesn't exist.
35
+ */
36
+ export declare const updateEntity: <T extends HasId>(ref: Ref.Ref<ReadonlyMap<string, T>>, id: string, updater: (entity: T) => T, collection: string) => Effect.Effect<T, NotFoundError>;
37
+ export {};
38
+ //# sourceMappingURL=state-operations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-operations.d.ts","sourceRoot":"","sources":["../../src/state/state-operations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD;;GAEG;AACH,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,KAAK,EACxC,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,IAAI,MAAM,KACR,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CACwC,CAAC;AAE1E;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,KAAK,EAC9C,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,IAAI,MAAM,EACV,YAAY,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,CAe/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,KAAK,EAC7C,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAClC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CACgC,CAAC;AAElE;;;GAGG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,KAAK,EACxC,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,QAAQ,CAAC,KACP,MAAM,CAAC,MAAM,CAAC,IAAI,CAKlB,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,KAAK,EAC3C,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,IAAI,MAAM,KACR,MAAM,CAAC,MAAM,CAAC,OAAO,CAQrB,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,KAAK,EAC3C,KAAK,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EACpC,IAAI,MAAM,EACV,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,EACzB,YAAY,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,aAAa,CAwB/B,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { Effect, Option, Ref } from "effect";
2
+ import { NotFoundError } from "../errors/crud-errors.js";
3
+ /**
4
+ * Gets a single entity by ID from a collection Ref.
5
+ * Returns Option.some if found, Option.none if not.
6
+ */
7
+ export const getEntity = (ref, id) => Ref.get(ref).pipe(Effect.map((map) => Option.fromNullable(map.get(id))));
8
+ /**
9
+ * Gets a single entity by ID, failing with NotFoundError if not present.
10
+ */
11
+ export const getEntityOrFail = (ref, id, collection) => getEntity(ref, id).pipe(Effect.flatMap(Option.match({
12
+ onNone: () => Effect.fail(new NotFoundError({
13
+ collection,
14
+ id,
15
+ message: `Entity with id "${id}" not found in collection "${collection}"`,
16
+ })),
17
+ onSome: Effect.succeed,
18
+ })));
19
+ /**
20
+ * Gets all entities from a collection Ref as a ReadonlyArray.
21
+ */
22
+ export const getAllEntities = (ref) => Ref.get(ref).pipe(Effect.map((map) => Array.from(map.values())));
23
+ /**
24
+ * Sets (creates or replaces) an entity in the collection Ref.
25
+ * The entity is keyed by its `id` field.
26
+ */
27
+ export const setEntity = (ref, entity) => Ref.update(ref, (map) => {
28
+ const next = new Map(map);
29
+ next.set(entity.id, entity);
30
+ return next;
31
+ });
32
+ /**
33
+ * Removes an entity by ID from the collection Ref.
34
+ * Returns true if the entity existed and was removed, false if it was not present.
35
+ */
36
+ export const removeEntity = (ref, id) => Ref.modify(ref, (map) => {
37
+ if (!map.has(id)) {
38
+ return [false, map];
39
+ }
40
+ const next = new Map(map);
41
+ next.delete(id);
42
+ return [true, next];
43
+ });
44
+ /**
45
+ * Atomically updates an entity by ID using an updater function.
46
+ * Returns the updated entity. Fails with NotFoundError if the entity doesn't exist.
47
+ */
48
+ export const updateEntity = (ref, id, updater, collection) => Ref.modify(ref, (map) => {
49
+ const existing = map.get(id);
50
+ if (existing === undefined) {
51
+ return [Option.none(), map];
52
+ }
53
+ const updated = updater(existing);
54
+ const next = new Map(map);
55
+ next.set(id, updated);
56
+ return [Option.some(updated), next];
57
+ }).pipe(Effect.flatMap(Option.match({
58
+ onNone: () => Effect.fail(new NotFoundError({
59
+ collection,
60
+ id,
61
+ message: `Entity with id "${id}" not found in collection "${collection}"`,
62
+ })),
63
+ onSome: Effect.succeed,
64
+ })));
65
+ //# sourceMappingURL=state-operations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-operations.js","sourceRoot":"","sources":["../../src/state/state-operations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * In-memory implementation of StorageAdapter as an Effect Layer.
3
+ * Intended for testing — stores data in a Map<string, string> instead of the filesystem.
4
+ */
5
+ import { Layer } from "effect";
6
+ import { StorageAdapter } from "./storage-service.js";
7
+ /**
8
+ * Creates an InMemoryStorageLayer backed by the provided Map.
9
+ * Pass your own Map to inspect stored data in tests.
10
+ */
11
+ export declare const makeInMemoryStorageLayer: (store?: Map<string, string>) => Layer.Layer<StorageAdapter>;
12
+ /**
13
+ * Default InMemoryStorageLayer with a fresh empty Map.
14
+ */
15
+ export declare const InMemoryStorageLayer: Layer.Layer<StorageAdapter>;
16
+ //# sourceMappingURL=in-memory-adapter-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-adapter-layer.d.ts","sourceRoot":"","sources":["../../src/storage/in-memory-adapter-layer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEvC,OAAO,EAAE,cAAc,EAA4B,MAAM,sBAAsB,CAAC;AAuFhF;;;GAGG;AACH,eAAO,MAAM,wBAAwB,GACpC,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,KACzB,KAAK,CAAC,KAAK,CAAC,cAAc,CAC6B,CAAC;AAE3D;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,KAAK,CAAC,cAAc,CAClC,CAAC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * In-memory implementation of StorageAdapter as an Effect Layer.
3
+ * Intended for testing — stores data in a Map<string, string> instead of the filesystem.
4
+ */
5
+ import { Effect, Layer } from "effect";
6
+ import { StorageError } from "../errors/storage-errors.js";
7
+ import { StorageAdapter } from "./storage-service.js";
8
+ // ============================================================================
9
+ // In-memory storage adapter
10
+ // ============================================================================
11
+ const makeInMemoryAdapter = (store = new Map(), watchers = new Map()) => ({
12
+ read: (path) => Effect.suspend(() => {
13
+ const content = store.get(path);
14
+ if (content === undefined) {
15
+ return Effect.fail(new StorageError({
16
+ path,
17
+ operation: "read",
18
+ message: `File not found: ${path}`,
19
+ }));
20
+ }
21
+ return Effect.succeed(content);
22
+ }),
23
+ write: (path, data) => Effect.sync(() => {
24
+ store.set(path, data);
25
+ // Notify watchers for this path
26
+ const pathWatchers = watchers.get(path);
27
+ if (pathWatchers) {
28
+ for (const cb of pathWatchers) {
29
+ cb();
30
+ }
31
+ }
32
+ }),
33
+ append: (path, data) => Effect.sync(() => {
34
+ const existing = store.get(path) ?? "";
35
+ store.set(path, existing + data);
36
+ // Notify watchers for this path
37
+ const pathWatchers = watchers.get(path);
38
+ if (pathWatchers) {
39
+ for (const cb of pathWatchers) {
40
+ cb();
41
+ }
42
+ }
43
+ }),
44
+ exists: (path) => Effect.sync(() => store.has(path)),
45
+ remove: (path) => Effect.suspend(() => {
46
+ if (!store.has(path)) {
47
+ return Effect.fail(new StorageError({
48
+ path,
49
+ operation: "delete",
50
+ message: `File not found: ${path}`,
51
+ }));
52
+ }
53
+ store.delete(path);
54
+ return Effect.void;
55
+ }),
56
+ ensureDir: (_path) => Effect.void,
57
+ watch: (path, onChange) => Effect.sync(() => {
58
+ const pathWatchers = watchers.get(path) ?? new Set();
59
+ pathWatchers.add(onChange);
60
+ watchers.set(path, pathWatchers);
61
+ return () => {
62
+ pathWatchers.delete(onChange);
63
+ if (pathWatchers.size === 0) {
64
+ watchers.delete(path);
65
+ }
66
+ };
67
+ }),
68
+ });
69
+ // ============================================================================
70
+ // Layer construction
71
+ // ============================================================================
72
+ /**
73
+ * Creates an InMemoryStorageLayer backed by the provided Map.
74
+ * Pass your own Map to inspect stored data in tests.
75
+ */
76
+ export const makeInMemoryStorageLayer = (store) => Layer.succeed(StorageAdapter, makeInMemoryAdapter(store));
77
+ /**
78
+ * Default InMemoryStorageLayer with a fresh empty Map.
79
+ */
80
+ export const InMemoryStorageLayer = makeInMemoryStorageLayer();
81
+ //# sourceMappingURL=in-memory-adapter-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-memory-adapter-layer.js","sourceRoot":"","sources":["../../src/storage/in-memory-adapter-layer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Effect-based persistence functions for loading and saving collection data.
3
+ *
4
+ * Uses StorageAdapter and SerializerRegistry services for I/O and format handling.
5
+ * Data is decoded/encoded through Effect Schema on load/save to ensure type safety.
6
+ * Includes DebouncedWriter for coalescing rapid mutations into single file writes.
7
+ */
8
+ import { Effect, PubSub, Ref, Schema, type Scope } from "effect";
9
+ import { ValidationError } from "../errors/crud-errors.js";
10
+ import { MigrationError } from "../errors/migration-errors.js";
11
+ import { SerializationError, StorageError, type UnsupportedFormatError } from "../errors/storage-errors.js";
12
+ import type { Migration } from "../migrations/migration-types.js";
13
+ import { SerializerRegistry } from "../serializers/serializer-service.js";
14
+ import type { ChangeEvent } from "../types/reactive-types.js";
15
+ import { StorageAdapter } from "./storage-service.js";
16
+ /**
17
+ * Options for loadData.
18
+ */
19
+ export interface LoadDataOptions {
20
+ /**
21
+ * Optional schema version from collection config.
22
+ * When provided, enables version checking and migration support.
23
+ */
24
+ readonly version?: number;
25
+ /**
26
+ * Optional migrations array for automatic data migration.
27
+ * Only used when version is also provided.
28
+ */
29
+ readonly migrations?: ReadonlyArray<Migration>;
30
+ /**
31
+ * Collection name for error messages.
32
+ * Required when version is provided.
33
+ */
34
+ readonly collectionName?: string;
35
+ }
36
+ /**
37
+ * Load collection data from a file, decode each entity through the given Schema.
38
+ *
39
+ * Flow:
40
+ * 1. Check file existence via StorageAdapter
41
+ * 2. Read raw content
42
+ * 3. Deserialize via SerializerRegistry (format determined by file extension)
43
+ * 4. Validate the top-level structure is a Record<string, object>
44
+ * 5. Extract `_version` (default 0 if absent) and remove from entity map
45
+ * 6. Decode each entity value through the Schema
46
+ * 7. Return a ReadonlyMap<string, A> keyed by entity ID
47
+ *
48
+ * If the file does not exist, returns an empty ReadonlyMap.
49
+ *
50
+ * When `options.version` is provided:
51
+ * - Extracts `_version` from the file (defaults to 0 if absent)
52
+ * - Compares file version to config version
53
+ * - If file version < config version: runs migrations (task 5.2)
54
+ * - If file version > config version: fails with MigrationError
55
+ * - If file version === config version: proceeds normally
56
+ */
57
+ export declare const loadData: <A extends {
58
+ readonly id: string;
59
+ }, I, R>(filePath: string, schema: Schema.Schema<A, I, R>, options?: LoadDataOptions) => Effect.Effect<ReadonlyMap<string, A>, StorageError | SerializationError | UnsupportedFormatError | ValidationError | MigrationError, StorageAdapter | SerializerRegistry | R>;
60
+ /**
61
+ * Options for saveData.
62
+ */
63
+ export interface SaveDataOptions {
64
+ /**
65
+ * Optional schema version to stamp into the file.
66
+ * When provided, `_version` is injected at the top level before entities.
67
+ */
68
+ readonly version?: number;
69
+ }
70
+ /**
71
+ * Save collection data to a file, encoding each entity through the given Schema.
72
+ *
73
+ * Flow:
74
+ * 1. Encode each entity through the Schema (Type → Encoded)
75
+ * 2. Build a Record<string, I> keyed by entity ID
76
+ * 3. Optionally inject `_version` at the top level if version is provided
77
+ * 4. Serialize via SerializerRegistry
78
+ * 5. Ensure parent directory exists
79
+ * 6. Write via StorageAdapter
80
+ */
81
+ export declare const saveData: <A extends {
82
+ readonly id: string;
83
+ }, I, R>(filePath: string, schema: Schema.Schema<A, I, R>, data: ReadonlyMap<string, A>, options?: SaveDataOptions) => Effect.Effect<void, StorageError | SerializationError | UnsupportedFormatError | ValidationError, StorageAdapter | SerializerRegistry | R>;
84
+ /**
85
+ * Configuration for loading a single collection from a multi-collection file.
86
+ */
87
+ export interface LoadCollectionConfig {
88
+ readonly name: string;
89
+ readonly schema: Schema.Schema<{
90
+ readonly id: string;
91
+ }, unknown, never>;
92
+ /**
93
+ * Optional schema version from collection config.
94
+ * When provided, enables version checking and migration support.
95
+ */
96
+ readonly version?: number;
97
+ /**
98
+ * Optional migrations array for automatic data migration.
99
+ * Only used when version is also provided.
100
+ */
101
+ readonly migrations?: ReadonlyArray<Migration>;
102
+ }
103
+ /**
104
+ * Load multiple collections from a single file.
105
+ *
106
+ * The file is expected to contain a top-level object where keys are collection names
107
+ * and values are objects keyed by entity ID. Each collection is decoded independently
108
+ * using its own schema.
109
+ *
110
+ * When collections have `version` and `migrations` specified, per-collection migration
111
+ * is applied:
112
+ * - Each collection's `_version` is extracted from its section (default 0 if absent)
113
+ * - If file version < config version: migrations run for that collection
114
+ * - If file version > config version: fails with MigrationError
115
+ * - After any migrations, the entire file is rewritten with all collections at their current versions
116
+ *
117
+ * Returns a Record mapping collection name to ReadonlyMap<string, unknown>.
118
+ */
119
+ export declare const loadCollectionsFromFile: (filePath: string, collections: ReadonlyArray<LoadCollectionConfig>) => Effect.Effect<Record<string, ReadonlyMap<string, {
120
+ readonly id: string;
121
+ }>>, StorageError | SerializationError | UnsupportedFormatError | ValidationError | MigrationError, StorageAdapter | SerializerRegistry>;
122
+ type HasId = {
123
+ readonly id: string;
124
+ };
125
+ /**
126
+ * Configuration for a collection to be saved to a multi-collection file.
127
+ *
128
+ * @template T - The decoded entity type (must have `id` field)
129
+ * @template I - The encoded/serialized type (defaults to T for simple schemas)
130
+ */
131
+ export interface SaveCollectionConfig<T extends HasId = HasId, I = T> {
132
+ readonly name: string;
133
+ readonly schema: Schema.Schema<T, I, never>;
134
+ readonly data: ReadonlyMap<string, T>;
135
+ /**
136
+ * Optional schema version to stamp into this collection's section.
137
+ * When provided, `_version` is injected first in the collection object.
138
+ */
139
+ readonly version?: number;
140
+ }
141
+ /**
142
+ * Save multiple collections to a single file.
143
+ *
144
+ * Encodes each entity in each collection through its schema, then writes
145
+ * the combined data as { collectionName: { _version?, id: encodedEntity, ... }, ... }.
146
+ *
147
+ * If a collection has a `version` specified, `_version` is stamped first
148
+ * in that collection's object for readability.
149
+ */
150
+ export declare function saveCollectionsToFile<T extends HasId, I>(filePath: string, collections: ReadonlyArray<SaveCollectionConfig<T, I>>): Effect.Effect<void, StorageError | SerializationError | UnsupportedFormatError | ValidationError, StorageAdapter | SerializerRegistry>;
151
+ /**
152
+ * Handle returned by `createDebouncedWriter`. Provides methods to schedule
153
+ * debounced writes, flush all pending writes, and query pending state.
154
+ */
155
+ export interface DebouncedWriter {
156
+ /**
157
+ * Schedule a debounced write for the given key. If a write for this key
158
+ * is already pending, it is cancelled and replaced with the new one.
159
+ * The actual write executes after the configured delay unless superseded.
160
+ */
161
+ readonly triggerSave: (key: string, save: Effect.Effect<void, StorageError | SerializationError | UnsupportedFormatError | ValidationError, StorageAdapter | SerializerRegistry>) => Effect.Effect<void, never, StorageAdapter | SerializerRegistry>;
162
+ /**
163
+ * Immediately execute all pending writes, cancelling their debounce timers.
164
+ * Errors from individual saves are collected but do not prevent other saves.
165
+ */
166
+ readonly flush: () => Effect.Effect<void, StorageError | SerializationError | UnsupportedFormatError | ValidationError, StorageAdapter | SerializerRegistry>;
167
+ /**
168
+ * Returns the number of writes currently pending.
169
+ */
170
+ readonly pendingCount: () => Effect.Effect<number>;
171
+ }
172
+ /**
173
+ * Create a DebouncedWriter that coalesces rapid writes into single file operations.
174
+ *
175
+ * Each call to `triggerSave(key, saveEffect)` cancels any pending write for
176
+ * that key and schedules a new one after `delayMs` milliseconds. If another
177
+ * `triggerSave` for the same key arrives before the delay elapses, the timer
178
+ * resets — only the last write within a burst actually hits the filesystem.
179
+ *
180
+ * @param delayMs - Debounce delay in milliseconds (default 100)
181
+ */
182
+ export declare const createDebouncedWriter: (delayMs?: number) => Effect.Effect<DebouncedWriter>;
183
+ /**
184
+ * Handle returned by `createFileWatcher`. Provides the ability to check
185
+ * whether the watcher is active. The watcher is automatically cleaned up
186
+ * when the enclosing Effect Scope closes.
187
+ */
188
+ export interface FileWatcher {
189
+ /**
190
+ * Returns true if the watcher is still active (has not been closed).
191
+ */
192
+ readonly isActive: () => Effect.Effect<boolean>;
193
+ }
194
+ /**
195
+ * Configuration for a single file watcher.
196
+ */
197
+ export interface FileWatcherConfig<A extends {
198
+ readonly id: string;
199
+ }, I, R> {
200
+ /** Path to the file to watch */
201
+ readonly filePath: string;
202
+ /** Schema to decode loaded data through */
203
+ readonly schema: Schema.Schema<A, I, R>;
204
+ /** Ref holding the collection state to update on file change */
205
+ readonly ref: Ref.Ref<ReadonlyMap<string, A>>;
206
+ /** Optional debounce delay in ms for reload after change (default 50) */
207
+ readonly debounceMs?: number;
208
+ /** Optional PubSub to publish reload events to for reactive query subscriptions */
209
+ readonly changePubSub?: PubSub.PubSub<ChangeEvent>;
210
+ /** Collection name (required when changePubSub is provided) */
211
+ readonly collectionName?: string;
212
+ }
213
+ /**
214
+ * Create a managed file watcher using Effect.acquireRelease.
215
+ *
216
+ * The watcher monitors a file for external changes. When a change is detected,
217
+ * it reloads the file through the Schema and updates the collection Ref.
218
+ *
219
+ * The watcher lifecycle is managed by Effect's Scope — it is automatically
220
+ * closed when the Scope finalizes (database shutdown, test cleanup, etc.).
221
+ *
222
+ * Reload is debounced to avoid redundant reloads when editors write
223
+ * multiple change events in quick succession.
224
+ *
225
+ * @param config - File watcher configuration
226
+ * @returns Effect that yields a FileWatcher handle (requires Scope)
227
+ */
228
+ export declare const createFileWatcher: <A extends {
229
+ readonly id: string;
230
+ }, I, R>(config: FileWatcherConfig<A, I, R>) => Effect.Effect<FileWatcher, StorageError, Scope.Scope | StorageAdapter | SerializerRegistry | R>;
231
+ /**
232
+ * Create managed file watchers for multiple files at once.
233
+ *
234
+ * Convenience wrapper that creates a watcher for each config entry.
235
+ * All watchers share the enclosing Scope and are cleaned up together.
236
+ *
237
+ * @param configs - Array of file watcher configurations
238
+ * @returns Effect yielding an array of FileWatcher handles
239
+ */
240
+ export declare const createFileWatchers: <A extends {
241
+ readonly id: string;
242
+ }, I, R>(configs: ReadonlyArray<FileWatcherConfig<A, I, R>>) => Effect.Effect<ReadonlyArray<FileWatcher>, StorageError, Scope.Scope | StorageAdapter | SerializerRegistry | R>;
243
+ export {};
244
+ //# sourceMappingURL=persistence-effect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persistence-effect.d.ts","sourceRoot":"","sources":["../../src/storage/persistence-effect.ts"],"names":[],"mappings":"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"}