@bjornpagen/bumbledb 1.3.0 → 2.0.0-rc.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 (290) hide show
  1. package/COOKBOOK.md +224 -95
  2. package/README.md +114 -45
  3. package/dist/alternatives.d.ts +5 -6
  4. package/dist/alternatives.d.ts.map +1 -1
  5. package/dist/alternatives.js +5 -6
  6. package/dist/alternatives.js.map +1 -1
  7. package/dist/bin.d.ts +3 -0
  8. package/dist/bin.d.ts.map +1 -0
  9. package/dist/bin.js +85 -0
  10. package/dist/bin.js.map +1 -0
  11. package/dist/capacity.d.ts +14 -17
  12. package/dist/capacity.d.ts.map +1 -1
  13. package/dist/capacity.js +7 -7
  14. package/dist/capacity.js.map +1 -1
  15. package/dist/changes.d.ts +32 -41
  16. package/dist/changes.d.ts.map +1 -1
  17. package/dist/changes.js +86 -110
  18. package/dist/changes.js.map +1 -1
  19. package/dist/closed.d.ts +2 -2
  20. package/dist/closed.d.ts.map +1 -1
  21. package/dist/closed.js +5 -5
  22. package/dist/closed.js.map +1 -1
  23. package/dist/codec.d.ts +20 -27
  24. package/dist/codec.d.ts.map +1 -1
  25. package/dist/codec.js +48 -22
  26. package/dist/codec.js.map +1 -1
  27. package/dist/compile.d.ts +26 -50
  28. package/dist/compile.d.ts.map +1 -1
  29. package/dist/compile.js +75 -64
  30. package/dist/compile.js.map +1 -1
  31. package/dist/database/database.d.ts +98 -0
  32. package/dist/database/database.d.ts.map +1 -0
  33. package/dist/database/database.js +249 -0
  34. package/dist/database/database.js.map +1 -0
  35. package/dist/database/driver.d.ts +40 -0
  36. package/dist/database/driver.d.ts.map +1 -0
  37. package/dist/database/driver.js +68 -0
  38. package/dist/database/driver.js.map +1 -0
  39. package/dist/database/fs.d.ts +12 -0
  40. package/dist/database/fs.d.ts.map +1 -0
  41. package/dist/database/fs.js +120 -0
  42. package/dist/database/fs.js.map +1 -0
  43. package/dist/database/generate.d.ts +26 -0
  44. package/dist/database/generate.d.ts.map +1 -0
  45. package/dist/database/generate.js +102 -0
  46. package/dist/database/generate.js.map +1 -0
  47. package/dist/database/hosted.d.ts +32 -0
  48. package/dist/database/hosted.d.ts.map +1 -0
  49. package/dist/database/hosted.js +44 -0
  50. package/dist/database/hosted.js.map +1 -0
  51. package/dist/database/io.d.ts +94 -0
  52. package/dist/database/io.d.ts.map +1 -0
  53. package/dist/database/io.js +35 -0
  54. package/dist/database/io.js.map +1 -0
  55. package/dist/database/mem.d.ts +26 -0
  56. package/dist/database/mem.d.ts.map +1 -0
  57. package/dist/database/mem.js +67 -0
  58. package/dist/database/mem.js.map +1 -0
  59. package/dist/database/migration.d.ts +55 -0
  60. package/dist/database/migration.d.ts.map +1 -0
  61. package/dist/database/migration.js +46 -0
  62. package/dist/database/migration.js.map +1 -0
  63. package/dist/database/s3.d.ts +36 -0
  64. package/dist/database/s3.d.ts.map +1 -0
  65. package/dist/database/s3.js +141 -0
  66. package/dist/database/s3.js.map +1 -0
  67. package/dist/db.d.ts +44 -128
  68. package/dist/db.d.ts.map +1 -1
  69. package/dist/db.js +163 -287
  70. package/dist/db.js.map +1 -1
  71. package/dist/engine.d.ts +7 -0
  72. package/dist/engine.d.ts.map +1 -0
  73. package/dist/engine.js +2 -0
  74. package/dist/engine.js.map +1 -0
  75. package/dist/errors.d.ts +152 -46
  76. package/dist/errors.d.ts.map +1 -1
  77. package/dist/errors.js +107 -19
  78. package/dist/errors.js.map +1 -1
  79. package/dist/face.d.ts +5 -5
  80. package/dist/face.d.ts.map +1 -1
  81. package/dist/face.js +6 -6
  82. package/dist/face.js.map +1 -1
  83. package/dist/fields.d.ts +4 -5
  84. package/dist/fields.d.ts.map +1 -1
  85. package/dist/fields.js +3 -3
  86. package/dist/fields.js.map +1 -1
  87. package/dist/index.d.ts +60 -58
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +27 -29
  90. package/dist/index.js.map +1 -1
  91. package/dist/judgment.d.ts +1 -1
  92. package/dist/judgment.js +1 -1
  93. package/dist/law.d.ts +16 -68
  94. package/dist/law.d.ts.map +1 -1
  95. package/dist/law.js +3 -3
  96. package/dist/law.js.map +1 -1
  97. package/dist/lower.d.ts +3 -3
  98. package/dist/lower.d.ts.map +1 -1
  99. package/dist/lower.js +35 -39
  100. package/dist/lower.js.map +1 -1
  101. package/dist/native/addon.d.ts +23 -0
  102. package/dist/native/addon.d.ts.map +1 -0
  103. package/dist/native/addon.js +15 -0
  104. package/dist/native/addon.js.map +1 -0
  105. package/dist/native/binding.d.ts +996 -0
  106. package/dist/native/json.d.ts +14 -0
  107. package/dist/native/json.d.ts.map +1 -0
  108. package/dist/native/json.js +73 -0
  109. package/dist/native/json.js.map +1 -0
  110. package/dist/native/load.d.ts +9 -0
  111. package/dist/native/load.d.ts.map +1 -0
  112. package/dist/native/load.js +30 -0
  113. package/dist/native/load.js.map +1 -0
  114. package/dist/native/op.d.ts +29 -0
  115. package/dist/native/op.d.ts.map +1 -0
  116. package/dist/native/op.js +50 -0
  117. package/dist/native/op.js.map +1 -0
  118. package/dist/query/atom.d.ts +13 -14
  119. package/dist/query/atom.d.ts.map +1 -1
  120. package/dist/query/atom.js +2 -2
  121. package/dist/query/atom.js.map +1 -1
  122. package/dist/query/compute.d.ts +7 -7
  123. package/dist/query/compute.d.ts.map +1 -1
  124. package/dist/query/compute.js +7 -16
  125. package/dist/query/compute.js.map +1 -1
  126. package/dist/query/find.d.ts +8 -8
  127. package/dist/query/find.d.ts.map +1 -1
  128. package/dist/query/find.js +1 -1
  129. package/dist/query/lower.d.ts +31 -17
  130. package/dist/query/lower.d.ts.map +1 -1
  131. package/dist/query/lower.js +260 -368
  132. package/dist/query/lower.js.map +1 -1
  133. package/dist/query/run.d.ts +5 -5
  134. package/dist/query/run.d.ts.map +1 -1
  135. package/dist/query/run.js +32 -15
  136. package/dist/query/run.js.map +1 -1
  137. package/dist/query/scope.d.ts +20 -28
  138. package/dist/query/scope.d.ts.map +1 -1
  139. package/dist/query/scope.js +13 -8
  140. package/dist/query/scope.js.map +1 -1
  141. package/dist/query/segments.d.ts +2 -2
  142. package/dist/query/segments.d.ts.map +1 -1
  143. package/dist/query/segments.js +3 -3
  144. package/dist/query/segments.js.map +1 -1
  145. package/dist/relation.d.ts +1 -1
  146. package/dist/relation.d.ts.map +1 -1
  147. package/dist/relation.js +4 -4
  148. package/dist/relation.js.map +1 -1
  149. package/dist/result.d.ts +15 -22
  150. package/dist/result.d.ts.map +1 -1
  151. package/dist/result.js +30 -39
  152. package/dist/result.js.map +1 -1
  153. package/dist/rows.d.ts +9 -33
  154. package/dist/rows.d.ts.map +1 -1
  155. package/dist/rows.js +8 -12
  156. package/dist/rows.js.map +1 -1
  157. package/dist/runtime.d.ts +17 -43
  158. package/dist/runtime.d.ts.map +1 -1
  159. package/dist/runtime.js +41 -241
  160. package/dist/runtime.js.map +1 -1
  161. package/dist/scalar.d.ts +36 -106
  162. package/dist/scalar.d.ts.map +1 -1
  163. package/dist/scalar.js +36 -185
  164. package/dist/scalar.js.map +1 -1
  165. package/dist/schema.d.ts +7 -18
  166. package/dist/schema.d.ts.map +1 -1
  167. package/dist/schema.js +18 -228
  168. package/dist/schema.js.map +1 -1
  169. package/dist/selection.d.ts +4 -4
  170. package/dist/selection.d.ts.map +1 -1
  171. package/dist/selection.js +5 -5
  172. package/dist/selection.js.map +1 -1
  173. package/dist/shape.d.ts +5 -5
  174. package/dist/shape.d.ts.map +1 -1
  175. package/dist/spec.d.ts +7 -79
  176. package/dist/spec.d.ts.map +1 -1
  177. package/dist/spec.js.map +1 -1
  178. package/dist/statements.d.ts +3 -3
  179. package/dist/statements.d.ts.map +1 -1
  180. package/dist/statements.js +10 -10
  181. package/dist/statements.js.map +1 -1
  182. package/dist/uuid.d.ts +1 -1
  183. package/dist/uuid.d.ts.map +1 -1
  184. package/dist/uuid.js +1 -1
  185. package/dist/uuid.js.map +1 -1
  186. package/dist/values.d.ts +2 -2
  187. package/dist/values.d.ts.map +1 -1
  188. package/dist/values.js +2 -2
  189. package/dist/values.js.map +1 -1
  190. package/package.json +18 -18
  191. package/src/alternatives.ts +7 -8
  192. package/src/bin.ts +82 -0
  193. package/src/capacity.ts +20 -23
  194. package/src/changes.ts +134 -197
  195. package/src/closed.ts +5 -5
  196. package/src/codec.ts +77 -51
  197. package/src/compile.ts +91 -113
  198. package/src/database/database.ts +413 -0
  199. package/src/database/driver.ts +134 -0
  200. package/src/database/fs.ts +119 -0
  201. package/src/database/generate.ts +116 -0
  202. package/src/database/hosted.ts +102 -0
  203. package/src/database/io.ts +134 -0
  204. package/src/database/mem.ts +113 -0
  205. package/src/database/migration.ts +107 -0
  206. package/src/database/s3.ts +231 -0
  207. package/src/db.ts +290 -547
  208. package/src/engine.ts +14 -0
  209. package/src/errors.ts +118 -39
  210. package/src/face.ts +9 -9
  211. package/src/fields.ts +6 -7
  212. package/src/index.ts +102 -109
  213. package/src/judgment.ts +1 -1
  214. package/src/law.ts +18 -70
  215. package/src/lower.ts +61 -72
  216. package/src/native/addon.ts +54 -0
  217. package/src/native/binding.d.ts +996 -0
  218. package/src/native/json.ts +100 -0
  219. package/src/native/load.ts +31 -0
  220. package/src/native/op.ts +73 -0
  221. package/src/query/atom.ts +15 -16
  222. package/src/query/compute.ts +13 -22
  223. package/src/query/find.ts +8 -8
  224. package/src/query/lower.ts +323 -414
  225. package/src/query/run.ts +42 -21
  226. package/src/query/scope.ts +37 -38
  227. package/src/query/segments.ts +4 -4
  228. package/src/relation.ts +4 -4
  229. package/src/result.ts +53 -80
  230. package/src/rows.ts +18 -46
  231. package/src/runtime.ts +50 -346
  232. package/src/scalar.ts +76 -336
  233. package/src/schema.ts +23 -284
  234. package/src/selection.ts +6 -6
  235. package/src/shape.ts +5 -5
  236. package/src/spec.ts +7 -85
  237. package/src/statements.ts +11 -11
  238. package/src/uuid.ts +1 -1
  239. package/src/values.ts +4 -4
  240. package/dist/close.d.ts +0 -24
  241. package/dist/close.d.ts.map +0 -1
  242. package/dist/close.js +0 -34
  243. package/dist/close.js.map +0 -1
  244. package/dist/db-native.d.ts +0 -196
  245. package/dist/db-native.d.ts.map +0 -1
  246. package/dist/db-native.js +0 -13
  247. package/dist/db-native.js.map +0 -1
  248. package/dist/internal/log.d.ts +0 -26
  249. package/dist/internal/log.d.ts.map +0 -1
  250. package/dist/internal/log.js +0 -12
  251. package/dist/internal/log.js.map +0 -1
  252. package/dist/migration.d.ts +0 -31
  253. package/dist/migration.d.ts.map +0 -1
  254. package/dist/migration.js +0 -47
  255. package/dist/migration.js.map +0 -1
  256. package/dist/native.d.ts +0 -562
  257. package/dist/native.d.ts.map +0 -1
  258. package/dist/native.js +0 -123
  259. package/dist/native.js.map +0 -1
  260. package/dist/query/description.d.ts +0 -41
  261. package/dist/query/description.d.ts.map +0 -1
  262. package/dist/query/description.js +0 -311
  263. package/dist/query/description.js.map +0 -1
  264. package/dist/query/parse-ir.d.ts +0 -10
  265. package/dist/query/parse-ir.d.ts.map +0 -1
  266. package/dist/query/parse-ir.js +0 -280
  267. package/dist/query/parse-ir.js.map +0 -1
  268. package/dist/runtime-codes.d.ts +0 -2
  269. package/dist/runtime-codes.d.ts.map +0 -1
  270. package/dist/runtime-codes.js +0 -18
  271. package/dist/runtime-codes.js.map +0 -1
  272. package/dist/runtime-errors.d.ts +0 -118
  273. package/dist/runtime-errors.d.ts.map +0 -1
  274. package/dist/runtime-errors.js +0 -98
  275. package/dist/runtime-errors.js.map +0 -1
  276. package/dist/runtime-native.d.ts +0 -141
  277. package/dist/runtime-native.d.ts.map +0 -1
  278. package/dist/runtime-native.js +0 -5
  279. package/dist/runtime-native.js.map +0 -1
  280. package/pack-provenance.json +0 -7
  281. package/src/close.ts +0 -53
  282. package/src/db-native.ts +0 -243
  283. package/src/internal/log.ts +0 -48
  284. package/src/migration.ts +0 -61
  285. package/src/native.ts +0 -736
  286. package/src/query/description.ts +0 -424
  287. package/src/query/parse-ir.ts +0 -291
  288. package/src/runtime-codes.ts +0 -17
  289. package/src/runtime-errors.ts +0 -109
  290. package/src/runtime-native.ts +0 -189
@@ -0,0 +1,98 @@
1
+ import type { Scope } from "effect";
2
+ import { Context, Duration, Effect, Layer, Option, Schema } from "effect";
3
+ import type { ChangeSet } from "../changes.ts";
4
+ import type { QueryReader } from "../db.ts";
5
+ import { DbError } from "../errors.ts";
6
+ import type { HeadOut, ReceiptOut, SettledOut } from "../native/binding.d.ts";
7
+ import type { Bumble } from "../runtime.ts";
8
+ import type { AnySchema } from "../schema.ts";
9
+ import type { ExecutorOptions, ObjectStore } from "./io.ts";
10
+ import type { Migrations } from "./migration.ts";
11
+ /** A submission's idempotency key: 32 lowercase hex digits. */
12
+ declare const RequestId: Schema.brand<Schema.String, "RequestId">;
13
+ type RequestId = typeof RequestId.Type;
14
+ /** How fresh a read must be: the cache as it is, the log's tip, or at least a submitted `seq`. */
15
+ type Consistency = "cached" | "latest" | {
16
+ readonly atLeast: bigint;
17
+ };
18
+ /** A decided submission (its receipt), or a refusal. Every refusal but `Unknown` proves the log does not decide it. */
19
+ type SubmitOutcome = Extract<SettledOut, {
20
+ readonly _tag: "Decided" | "Refused";
21
+ }>;
22
+ interface SubmitOptions {
23
+ readonly requestId: RequestId;
24
+ /** Decide only if the database is exactly at this revision. */
25
+ readonly precondition?: bigint;
26
+ }
27
+ /** A snapshot reader, with the log position and revision the cache had reached when it was pinned. */
28
+ interface DatabaseReader<S extends AnySchema> extends QueryReader<S> {
29
+ readonly seq: bigint;
30
+ /** The committed revision; a submit can require it with `precondition`. */
31
+ readonly revision: bigint;
32
+ }
33
+ interface Database<S extends AnySchema> {
34
+ readonly schema: S;
35
+ /** The last decided state this process has seen. */
36
+ readonly head: Effect.Effect<HeadOut>;
37
+ submit(changes: ChangeSet<S>, options: SubmitOptions): Effect.Effect<SubmitOutcome, DbError>;
38
+ read(consistency?: Consistency): Effect.Effect<DatabaseReader<S>, DbError, Scope.Scope>;
39
+ /** The receipt of `requestId`, after catching up with the log. */
40
+ resolve(requestId: RequestId): Effect.Effect<Option.Option<ReceiptOut>, DbError>;
41
+ }
42
+ interface Tuning {
43
+ /** Parallel tail reads while catching up. */
44
+ readonly probeWindow: number;
45
+ /** Log entries between automatic checkpoints. */
46
+ readonly checkpointEvery: bigint;
47
+ readonly checkpointKeep: number;
48
+ readonly maxEntryBytes: number;
49
+ /** Store calls in flight at once. */
50
+ readonly concurrency: number;
51
+ readonly executor: ExecutorOptions;
52
+ /** Stale migration attempts before writers are frozen for one more attempt. */
53
+ readonly migrationAttempts: number;
54
+ readonly freezeLease: Duration.Input;
55
+ }
56
+ interface DatabaseOptions<S extends AnySchema> {
57
+ /** The schema the code reads and writes; the last migration's schema. */
58
+ readonly schema: S;
59
+ readonly migrations: Migrations;
60
+ readonly store: ObjectStore;
61
+ /** Where the disposable local cache lives. */
62
+ readonly cache: {
63
+ readonly directory: string;
64
+ };
65
+ /** `migrate` creates the database and runs pending migrations; `verify` refuses to open with any pending. */
66
+ readonly onOpen: "verify" | "migrate";
67
+ readonly tuning?: Partial<Tuning>;
68
+ }
69
+ /** A service key for one application's database. */
70
+ declare function tag<S extends AnySchema>(key: string): Context.Key<Database<S>, Database<S>>;
71
+ /** Provides `key` with a database opened from `options` for the layer's lifetime. */
72
+ declare function layer<S extends AnySchema>(key: Context.Key<Database<S>, Database<S>>, options: DatabaseOptions<S>): Layer.Layer<Database<S>, DbError, Bumble>;
73
+ interface PoolOptions<S extends AnySchema> extends Omit<DatabaseOptions<S>, "store" | "cache"> {
74
+ readonly store: (tenant: string) => ObjectStore;
75
+ readonly cache: (tenant: string) => {
76
+ readonly directory: string;
77
+ };
78
+ /** How long an unused tenant's database stays open. */
79
+ readonly idleTimeToLive?: Duration.Input;
80
+ }
81
+ /** Databases opened on demand per tenant and closed once idle. */
82
+ interface DatabasePool<S extends AnySchema> {
83
+ get(tenant: string): Effect.Effect<Database<S>, DbError, Scope.Scope>;
84
+ }
85
+ /** A fresh random request id. */
86
+ declare function requestId(): RequestId;
87
+ declare const Database: Readonly<{
88
+ make: <S extends AnySchema>(options: DatabaseOptions<S>) => Effect.Effect<Database<S>, DbError, Bumble | Scope.Scope>;
89
+ layer: typeof layer;
90
+ pool: <S extends AnySchema>(options: PoolOptions<S>) => Effect.Effect<{
91
+ get: (tenant: string) => Effect.Effect<Database<S>, DbError, Scope.Scope>;
92
+ }, never, Bumble | Scope.Scope>;
93
+ tag: typeof tag;
94
+ requestId: typeof requestId;
95
+ }>;
96
+ export type { Consistency, DatabaseOptions, DatabasePool, DatabaseReader, PoolOptions, SubmitOptions, SubmitOutcome, Tuning };
97
+ export { Database, RequestId };
98
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/database/database.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAA;AAChF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAG9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAE3C,OAAO,EAAiB,OAAO,EAAW,MAAM,cAAc,CAAA;AAG9D,OAAO,KAAK,EAAE,OAAO,EAAgB,UAAU,EAAc,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAEvG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAK7C,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,+DAA+D;AAC/D,QAAA,MAAM,SAAS,0CAA0F,CAAA;AACzG,KAAK,SAAS,GAAG,OAAO,SAAS,CAAC,IAAI,CAAA;AAEtC,kGAAkG;AAClG,KAAK,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAErE,uHAAuH;AACvH,KAAK,aAAa,GAAG,OAAO,CAAC,UAAU,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC,CAAA;AAElF,UAAU,aAAa;IACtB,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAC9B;AAED,sGAAsG;AACtG,UAAU,cAAc,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,WAAW,CAAC,CAAC,CAAC;IACnE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CACzB;AAED,UAAU,QAAQ,CAAC,CAAC,SAAS,SAAS;IACrC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAClB,oDAAoD;IACpD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IACrC,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,OAAO,CAAC,CAAA;IAC5F,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACvF,kEAAkE;IAClE,OAAO,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;CAChF;AAED,UAAU,MAAM;IACf,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,iDAAiD;IACjD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,qCAAqC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;IAClC,+EAA+E;IAC/E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAA;CACpC;AAaD,UAAU,eAAe,CAAC,CAAC,SAAS,SAAS;IAC5C,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAA;IAClB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,8CAA8C;IAC9C,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9C,6GAA6G;IAC7G,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAA;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CACjC;AAqQD,oDAAoD;AACpD,iBAAS,GAAG,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAEpF;AAED,qFAAqF;AACrF,iBAAS,KAAK,CAAC,CAAC,SAAS,SAAS,EACjC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAC1C,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,GACzB,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,CAE3C;AAED,UAAU,WAAW,CAAC,CAAC,SAAS,SAAS,CAAE,SAAQ,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC7F,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,WAAW,CAAA;IAC/C,QAAQ,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,uDAAuD;IACvD,QAAQ,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAA;CACxC;AAED,kEAAkE;AAClE,UAAU,YAAY,CAAC,CAAC,SAAS,SAAS;IACzC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;CACrE;AAWD,iCAAiC;AACjC,iBAAS,SAAS,IAAI,SAAS,CAE9B;AAED,QAAA,MAAM,QAAQ;WAhHqC,CAAC,SAAS,SAAS;;WAmGnB,CAAC,SAAS,SAAS;sBAK9C,MAAM;;;;EAQuC,CAAA;AAErE,YAAY,EACX,WAAW,EACX,eAAe,EACf,YAAY,EACZ,cAAc,EACd,WAAW,EACX,aAAa,EACb,aAAa,EACb,MAAM,EACN,CAAA;AACD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAA"}
@@ -0,0 +1,249 @@
1
+ /**
2
+ * A durable database: an immutable log in an object store, read through a local LMDB cache that is
3
+ * only a cache. Writes are submitted commands decided once per request id; reads pin snapshots of
4
+ * the cache at a chosen freshness. Migrations are bundled with the code and run on open (`migrate`)
5
+ * or are required to have run already (`verify`).
6
+ */
7
+ import { randomBytes } from "node:crypto";
8
+ import { Context, Duration, Effect, Layer, Option, RcMap, Schema } from "effect";
9
+ import { ChangeSetLive, ChangeSet as ChangeSets } from "../changes.js";
10
+ import { compiledOf } from "../compile.js";
11
+ import { scopedSnapshot } from "../db.js";
12
+ import { argumentError, DbError, dbError } from "../errors.js";
13
+ import { lower } from "../lower.js";
14
+ import { addon } from "../native/addon.js";
15
+ import { call, scoped } from "../native/op.js";
16
+ import { runtimeHandle } from "../runtime.js";
17
+ import { schemaDescriptor, schemasAgree } from "../schema.js";
18
+ import { Driver } from "./driver.js";
19
+ import { hostedPort } from "./hosted.js";
20
+ import { defaultExecutorOptions } from "./io.js";
21
+ import { populateOf, verifyBundle } from "./migration.js";
22
+ /** A submission's idempotency key: 32 lowercase hex digits. */
23
+ const RequestId = Schema.String.check(Schema.isPattern(/^[0-9a-f]{32}$/)).pipe(Schema.brand("RequestId"));
24
+ const defaultTuning = {
25
+ probeWindow: 32,
26
+ checkpointEvery: 256n,
27
+ checkpointKeep: 3,
28
+ maxEntryBytes: 4 * 1024 * 1024,
29
+ concurrency: 32,
30
+ executor: defaultExecutorOptions,
31
+ migrationAttempts: 3,
32
+ freezeLease: "5 minutes"
33
+ };
34
+ function describeRefusal(refusal) {
35
+ switch (refusal._tag) {
36
+ case "Frozen":
37
+ return `writes are frozen until ${refusal.deadline}`;
38
+ case "MigrationPending":
39
+ return `migration ${refusal.next} has not run`;
40
+ case "MigrationRejected":
41
+ return `migration ${refusal.migration.name} was rejected: ${refusal.evidence.violations.map((violation) => violation.spelling).join("; ")}`;
42
+ case "MigrationsDiverged":
43
+ return `bundled migration ${refusal.index} differs from the one the log applied`;
44
+ case "Stale":
45
+ return `the log moved to ${refusal.head}`;
46
+ case "RequestReused":
47
+ return `request ${refusal.request} was decided for another command`;
48
+ case "Cache":
49
+ return refusal.message;
50
+ case "Corrupt":
51
+ return `log entry ${refusal.seq} is corrupt`;
52
+ default:
53
+ return refusal._tag;
54
+ }
55
+ }
56
+ /** A refusal of the machine, as a `DbError`. */
57
+ function refused(operation, refusal) {
58
+ return new DbError({ operation, reason: { _tag: "Engine", kind: refusal._tag, message: describeRefusal(refusal) } });
59
+ }
60
+ function hex(bytes) {
61
+ return randomBytes(bytes).toString("hex");
62
+ }
63
+ function settledAs(operation, settled, tag) {
64
+ if (settled._tag === tag)
65
+ return Effect.succeed(settled);
66
+ if (settled._tag === "Refused")
67
+ return Effect.fail(refused(operation, settled.refusal));
68
+ return Effect.fail(dbError(operation, { _tag: "Internal" }));
69
+ }
70
+ class DatabaseLive {
71
+ schema;
72
+ #hosted;
73
+ #run;
74
+ #head;
75
+ constructor(schema, hosted, run, head) {
76
+ this.schema = schema;
77
+ this.#hosted = hosted;
78
+ this.#run = run;
79
+ this.#head = head;
80
+ }
81
+ get head() {
82
+ return Effect.suspend(() => {
83
+ const head = this.#head();
84
+ return head === undefined ? Effect.die(dbError("Database.head", { _tag: "Internal" })) : Effect.succeed(head);
85
+ });
86
+ }
87
+ submit(changes, options) {
88
+ const schemaId = compiledOf(this.schema).schemaId;
89
+ const run = this.#run;
90
+ return Effect.gen(function* () {
91
+ const handle = ChangeSetLive.handle(changes, schemaId);
92
+ if (handle === undefined) {
93
+ return yield* Effect.fail(new DbError({ operation: "Database.submit", reason: { _tag: "InvalidArgument" } }));
94
+ }
95
+ const settled = yield* run({
96
+ _tag: "Submit",
97
+ request: options.requestId,
98
+ revision: options.precondition ?? null,
99
+ changes: handle
100
+ });
101
+ if (settled._tag !== "Decided" && settled._tag !== "Refused") {
102
+ return yield* Effect.fail(dbError("Database.submit", { _tag: "Internal" }));
103
+ }
104
+ return settled;
105
+ }).pipe(Effect.withSpan("Database.submit"));
106
+ }
107
+ read(consistency = "cached") {
108
+ const run = this.#run;
109
+ const hosted = this.#hosted;
110
+ const schema = this.schema;
111
+ const seen = this.#head;
112
+ return Effect.gen(function* () {
113
+ const known = seen()?.seq ?? 0n;
114
+ if (consistency === "latest" || (typeof consistency === "object" && known < consistency.atLeast)) {
115
+ yield* Effect.flatMap(run({ _tag: "Sync" }), (settled) => settledAs("Database.read", settled, "Synced"));
116
+ }
117
+ const head = seen();
118
+ const snapshot = yield* scopedSnapshot("Database.read", schema, (done) => addon.hostedSnapshot(hosted, done));
119
+ return {
120
+ seq: head?.seq ?? 0n,
121
+ revision: head?.revision ?? 0n,
122
+ get: snapshot.get.bind(snapshot),
123
+ execute: snapshot.execute.bind(snapshot),
124
+ prepare: snapshot.prepare.bind(snapshot)
125
+ };
126
+ }).pipe(Effect.withSpan("Database.read"));
127
+ }
128
+ resolve(requestId) {
129
+ return this.#run({ _tag: "Resolve", request: requestId }).pipe(Effect.flatMap((settled) => settledAs("Database.resolve", settled, "Resolved")), Effect.map((resolved) => Option.fromNullishOr(resolved.receipt)), Effect.withSpan("Database.resolve"));
130
+ }
131
+ }
132
+ /** Runs bundled migration `step` to completion, freezing writers if it keeps losing to them. */
133
+ const migrateStep = (migrations, step, hosted, run, head, tuning) => Effect.gen(function* () {
134
+ const migration = migrations[step];
135
+ const previous = migrations[step - 1];
136
+ if (migration === undefined || previous === undefined) {
137
+ return yield* Effect.fail(dbError("Database.migrate", { _tag: "Internal" }));
138
+ }
139
+ const attempt = Effect.scoped(Effect.gen(function* () {
140
+ const base = head()?.seq ?? 0n;
141
+ const from = yield* scopedSnapshot("Database.migrate", previous.to, (done) => addon.hostedSnapshot(hosted, done));
142
+ const into = yield* ChangeSets.builder(migration.to);
143
+ const populate = populateOf(migration);
144
+ if (populate !== undefined)
145
+ yield* populate({ from, into });
146
+ const rows = ChangeSetLive.handle(yield* into.finish(), compiledOf(migration.to).schemaId);
147
+ if (rows === undefined)
148
+ return yield* Effect.fail(dbError("Database.migrate", { _tag: "Internal" }));
149
+ const copy = addon.hostedUnchanged(hosted, step);
150
+ return yield* run({ _tag: "Migrate", step, base, copy, rows });
151
+ }));
152
+ let settled = { _tag: "Refused", refusal: { _tag: "NotPending" } };
153
+ for (let tries = 0; tries <= tuning.migrationAttempts; tries++) {
154
+ if (tries === tuning.migrationAttempts) {
155
+ const lease = BigInt(Math.ceil(Duration.toMillis(tuning.freezeLease)));
156
+ yield* Effect.flatMap(run({ _tag: "Freeze", leaseMillis: lease }), (frozen) => settledAs("Database.migrate", frozen, "Frozen"));
157
+ }
158
+ settled = yield* attempt;
159
+ if (settled._tag !== "Refused" || settled.refusal._tag !== "Stale")
160
+ break;
161
+ }
162
+ yield* settledAs("Database.migrate", settled, "Migrated");
163
+ }).pipe(Effect.withSpan("Database.migrate"));
164
+ /**
165
+ * Seeds a database nothing has written to yet with its initial migration's rows, after every
166
+ * migration ran: the seed's relations must still exist unchanged in the current schema. The request
167
+ * id comes from the migration's hash, so racing openers decide the seed once.
168
+ */
169
+ const seedInitial = (database, hash, seed) => Effect.scoped(Effect.gen(function* () {
170
+ const from = yield* database.read("cached");
171
+ const into = yield* ChangeSets.builder(database.schema);
172
+ yield* seed({ from, into });
173
+ const outcome = yield* database.submit(yield* into.finish(), { requestId: RequestId.make(hash.slice(0, 32)) });
174
+ if (outcome._tag === "Refused" && outcome.refusal._tag !== "RequestReused") {
175
+ return yield* Effect.fail(refused("Database.seed", outcome.refusal));
176
+ }
177
+ })).pipe(Effect.withSpan("Database.seed"));
178
+ /** Opens a database for the current scope. */
179
+ const make = Effect.fn("Database.make")(function* (options) {
180
+ const tuning = { ...defaultTuning, ...options.tuning };
181
+ const { migrations } = options;
182
+ const last = migrations[migrations.length - 1];
183
+ const theory = yield* Effect.try({
184
+ try: () => {
185
+ verifyBundle(migrations);
186
+ const theory = schemaDescriptor(options.schema);
187
+ if (last === undefined || !schemasAgree(theory, last.to)) {
188
+ throw new DbError({ operation: "Database.make", reason: { _tag: "InvalidArgument" } });
189
+ }
190
+ return theory;
191
+ },
192
+ catch: (cause) => argumentError("Database.make", cause)
193
+ });
194
+ const opening = {
195
+ bundle: migrations.map((migration) => ({ name: migration.id, hash: migration.hash, schema: lower(migration.to) })),
196
+ seed: hex(16),
197
+ ...(options.onOpen === "migrate" ? { create: hex(16) } : {}),
198
+ probeWindow: tuning.probeWindow,
199
+ checkpointEvery: tuning.checkpointEvery.toString(),
200
+ checkpointKeep: tuning.checkpointKeep,
201
+ maxEntryBytes: tuning.maxEntryBytes
202
+ };
203
+ const runtime = yield* runtimeHandle;
204
+ const directory = yield* scoped("Database.cacheRelease", call("Database.make", (done) => addon.runtimeDirectoryAcquire(runtime, options.cache.directory, done), addon.runtimeDirectoryTake), (owned) => (done) => addon.runtimeDirectoryClose(owned, false, done));
205
+ const hosted = yield* scoped("Database.release", call("Database.make", (done) => addon.hostedOpen(directory, "cache", JSON.stringify(opening), done), addon.hostedTake), (owned) => (done) => addon.hostedClose(owned, done));
206
+ let head;
207
+ const seen = () => head;
208
+ const driver = yield* Driver.make(hostedPort(hosted, (next) => {
209
+ head = next;
210
+ }), options.store, { concurrency: tuning.concurrency, executor: tuning.executor });
211
+ const opened = yield* Effect.flatMap(driver.run({ _tag: "Open" }), (settled) => settledAs("Database.open", settled, "Opened"));
212
+ if (opened.pending > 0 && options.onOpen === "verify") {
213
+ return yield* Effect.fail(refused("Database.open", { _tag: "MigrationPending", next: migrations.length - opened.pending }));
214
+ }
215
+ const fresh = seen()?.revision === 0n;
216
+ for (let step = migrations.length - opened.pending; step < migrations.length; step++) {
217
+ yield* migrateStep(migrations, step, hosted, driver.run, seen, tuning);
218
+ }
219
+ const database = new DatabaseLive(theory, hosted, driver.run, seen);
220
+ const initial = migrations[0];
221
+ const seed = populateOf(initial);
222
+ if (options.onOpen === "migrate" && seed !== undefined && fresh) {
223
+ yield* seedInitial(database, initial.hash, seed);
224
+ }
225
+ return database;
226
+ });
227
+ /** A service key for one application's database. */
228
+ function tag(key) {
229
+ return Context.Service(key);
230
+ }
231
+ /** Provides `key` with a database opened from `options` for the layer's lifetime. */
232
+ function layer(key, options) {
233
+ return Layer.effect(key, make(options));
234
+ }
235
+ /** A pool of per-tenant databases owned by the current scope. */
236
+ const pool = Effect.fn("Database.pool")(function* (options) {
237
+ const map = yield* RcMap.make({
238
+ lookup: (tenant) => make({ ...options, store: options.store(tenant), cache: options.cache(tenant) }),
239
+ idleTimeToLive: options.idleTimeToLive ?? "5 minutes"
240
+ });
241
+ return { get: (tenant) => RcMap.get(map, tenant) };
242
+ });
243
+ /** A fresh random request id. */
244
+ function requestId() {
245
+ return RequestId.make(hex(16));
246
+ }
247
+ const Database = Object.freeze({ make, layer, pool, tag, requestId });
248
+ export { Database, RequestId };
249
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/database/database.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Drives one sans-IO log machine. Requests and store responses go through one mailbox, so the
3
+ * machine steps strictly one input at a time; every `IoRequest` a step emits runs concurrently
4
+ * (bounded) and comes back as another input. Each request carries a ticket that settles exactly
5
+ * once, and closing the driver settles every ticket still open.
6
+ */
7
+ import type { Scope } from "effect";
8
+ import { Effect } from "effect";
9
+ import { DbError } from "../errors.ts";
10
+ import type { ExecutorOptions, IoRequest, IoResponse, ObjectStore } from "./io.ts";
11
+ interface Step<Settled> {
12
+ readonly io: readonly IoRequest[];
13
+ readonly done: ReadonlyArray<{
14
+ readonly ticket: bigint;
15
+ readonly settled: Settled;
16
+ }>;
17
+ }
18
+ /** The machine as the driver sees it. The driver never calls it concurrently. */
19
+ interface MachinePort<Request, Settled> {
20
+ readonly request: (ticket: bigint, request: Request) => Effect.Effect<Step<Settled>, DbError>;
21
+ readonly respond: (response: IoResponse) => Effect.Effect<Step<Settled>, DbError>;
22
+ /** Stops the machine; its step settles every open ticket. */
23
+ readonly close: Effect.Effect<Step<Settled>, DbError>;
24
+ }
25
+ interface DriverOptions {
26
+ /** Store calls in flight at once. */
27
+ readonly concurrency: number;
28
+ readonly executor: ExecutorOptions;
29
+ }
30
+ interface Driver<Request, Settled> {
31
+ /** Submits one request and waits for its settlement. */
32
+ readonly run: (request: Request) => Effect.Effect<Settled, DbError>;
33
+ }
34
+ declare const Driver: Readonly<{
35
+ make: <Request, Settled>(port: MachinePort<Request, Settled>, store: ObjectStore, options?: DriverOptions | undefined) => Effect.Effect<Driver<Request, Settled>, never, Scope.Scope>;
36
+ defaultOptions: DriverOptions;
37
+ }>;
38
+ export type { DriverOptions, MachinePort, Step };
39
+ export { Driver };
40
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/database/driver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAY,MAAM,EAAqC,MAAM,QAAQ,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,KAAK,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAGlF,UAAU,IAAI,CAAC,OAAO;IACrB,QAAQ,CAAC,EAAE,EAAE,SAAS,SAAS,EAAE,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAA;CACpF;AAED,iFAAiF;AACjF,UAAU,WAAW,CAAC,OAAO,EAAE,OAAO;IACrC,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;IAC7F,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;IACjF,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAA;CACrD;AAED,UAAU,aAAa;IACtB,qCAAqC;IACrC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAA;CAClC;AAED,UAAU,MAAM,CAAC,OAAO,EAAE,OAAO;IAChC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CACnE;AAgGD,QAAA,MAAM,MAAM;WAtF8B,OAAO,EAAE,OAAO;;EAsFkB,CAAA;AAE5E,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,IAAI,EAAE,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,CAAA"}
@@ -0,0 +1,68 @@
1
+ import { Deferred, Effect, Fiber, FiberSet, Queue, Semaphore } from "effect";
2
+ import { DbError } from "../errors.js";
3
+ import { defaultExecutorOptions, execute } from "./io.js";
4
+ const defaultDriverOptions = { concurrency: 32, executor: defaultExecutorOptions };
5
+ /** Starts a driver owned by the current scope; closing the scope closes the machine. */
6
+ const make = Effect.fnUntraced(function* (port, store, options = defaultDriverOptions) {
7
+ const mailbox = yield* Queue.unbounded();
8
+ const permits = yield* Semaphore.make(options.concurrency);
9
+ const io = yield* FiberSet.make();
10
+ const tickets = new Map();
11
+ let nextTicket = 0n;
12
+ const closed = new DbError({ operation: "Database", reason: { _tag: "ClosedHandle" } });
13
+ const apply = (step) => Effect.gen(function* () {
14
+ for (const { ticket, settled } of step.done) {
15
+ const waiter = tickets.get(ticket);
16
+ tickets.delete(ticket);
17
+ if (waiter !== undefined)
18
+ yield* Deferred.succeed(waiter, settled);
19
+ }
20
+ for (const request of step.io) {
21
+ yield* FiberSet.run(io, Semaphore.withPermit(permits, execute(store, request, options.executor)).pipe(Effect.flatMap((response) => Queue.offer(mailbox, { _tag: "Response", response }))));
22
+ }
23
+ });
24
+ let stopped;
25
+ /** Ends the driver: later runs fail with `error`, and open tickets fail with it now. */
26
+ const stop = (error) => Effect.suspend(() => {
27
+ stopped ??= error;
28
+ const open = [...tickets.values()];
29
+ tickets.clear();
30
+ return Effect.forEach(open, (waiter) => Deferred.fail(waiter, error), { discard: true });
31
+ });
32
+ const stepFor = (message) => {
33
+ switch (message._tag) {
34
+ case "Request":
35
+ return port.request(message.ticket, message.request);
36
+ case "Response":
37
+ return port.respond(message.response);
38
+ case "Close":
39
+ return port.close;
40
+ }
41
+ };
42
+ const loop = Effect.gen(function* () {
43
+ while (true) {
44
+ const message = yield* Queue.take(mailbox);
45
+ const step = stepFor(message);
46
+ const failed = yield* step.pipe(Effect.flatMap(apply), Effect.as(false), Effect.catch((error) => Effect.as(stop(error), true)));
47
+ if (failed || message._tag === "Close")
48
+ return yield* stop(closed);
49
+ }
50
+ });
51
+ const machine = yield* Effect.forkScoped(loop);
52
+ yield* Effect.addFinalizer(() => Queue.offer(mailbox, { _tag: "Close" }).pipe(Effect.andThen(Fiber.await(machine)), Effect.andThen(FiberSet.clear(io))));
53
+ return {
54
+ run: (request) => Effect.suspend(() => {
55
+ if (stopped !== undefined)
56
+ return Effect.fail(stopped);
57
+ const waiter = Deferred.makeUnsafe();
58
+ const ticket = nextTicket;
59
+ nextTicket += 1n;
60
+ tickets.set(ticket, waiter);
61
+ Queue.offerUnsafe(mailbox, { _tag: "Request", ticket, request });
62
+ return Deferred.await(waiter);
63
+ })
64
+ };
65
+ });
66
+ const Driver = Object.freeze({ make, defaultOptions: defaultDriverOptions });
67
+ export { Driver };
68
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/database/driver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ObjectStore } from "./io.ts";
2
+ /**
3
+ * A store in a local directory, one file per key under `root`. A create writes a temporary file,
4
+ * fsyncs it, hard-links it into place (the link fails if the key exists) and fsyncs the directory,
5
+ * so a created key is durable and never torn. The store clock is the local clock.
6
+ */
7
+ declare function make(root: string): ObjectStore;
8
+ declare const FsStore: Readonly<{
9
+ make: typeof make;
10
+ }>;
11
+ export { FsStore };
12
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/database/fs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAmD,WAAW,EAAiB,MAAM,SAAS,CAAA;AAmC1G;;;;GAIG;AACH,iBAAS,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAqEvC;AAED,QAAA,MAAM,OAAO;;EAA0B,CAAA;AAEvC,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -0,0 +1,120 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import * as path from "node:path";
4
+ import { Effect } from "effect";
5
+ import { DbError } from "../errors.js";
6
+ function code(cause) {
7
+ return typeof cause === "object" && cause !== null && "code" in cause && typeof cause.code === "string"
8
+ ? cause.code
9
+ : undefined;
10
+ }
11
+ function attempt(operation, run) {
12
+ return Effect.tryPromise({
13
+ try: run,
14
+ catch: (cause) => new DbError({ operation, reason: { _tag: "Io", kind: code(cause) ?? "Unknown" } })
15
+ });
16
+ }
17
+ async function syncDirectory(directory) {
18
+ const handle = await fs.open(directory, "r");
19
+ try {
20
+ await handle.sync();
21
+ }
22
+ finally {
23
+ await handle.close();
24
+ }
25
+ }
26
+ async function writeDurably(file, body) {
27
+ if (body._tag === "File")
28
+ await fs.copyFile(body.path, file, fs.constants.COPYFILE_EXCL);
29
+ const handle = await fs.open(file, body._tag === "File" ? "r+" : "wx");
30
+ try {
31
+ if (body._tag === "Bytes")
32
+ await handle.writeFile(body.bytes);
33
+ await handle.sync();
34
+ }
35
+ finally {
36
+ await handle.close();
37
+ }
38
+ }
39
+ /**
40
+ * A store in a local directory, one file per key under `root`. A create writes a temporary file,
41
+ * fsyncs it, hard-links it into place (the link fails if the key exists) and fsyncs the directory,
42
+ * so a created key is durable and never torn. The store clock is the local clock.
43
+ */
44
+ function make(root) {
45
+ const file = (key) => path.join(root, ...key.split("/"));
46
+ const now = () => BigInt(Date.now());
47
+ return {
48
+ get: (_bucket, key, target) => attempt("FsStore.get", async () => {
49
+ try {
50
+ if (target._tag === "Memory") {
51
+ const [bytes, stat] = await Promise.all([fs.readFile(file(key)), fs.stat(file(key), { bigint: true })]);
52
+ return {
53
+ date: now(),
54
+ result: { _tag: "Body", bytes: new Uint8Array(bytes), lastModified: stat.mtimeMs }
55
+ };
56
+ }
57
+ const stat = await fs.stat(file(key), { bigint: true });
58
+ await fs.copyFile(file(key), target.path);
59
+ return { date: now(), result: { _tag: "Saved", lastModified: stat.mtimeMs } };
60
+ }
61
+ catch (cause) {
62
+ if (code(cause) === "ENOENT")
63
+ return { date: now(), result: { _tag: "Missing" } };
64
+ throw cause;
65
+ }
66
+ }),
67
+ putIfAbsent: (_bucket, key, body) => attempt("FsStore.putIfAbsent", async () => {
68
+ const target = file(key);
69
+ const directory = path.dirname(target);
70
+ await fs.mkdir(directory, { recursive: true });
71
+ const temporary = path.join(directory, `.tmp-${randomUUID()}`);
72
+ try {
73
+ await writeDurably(temporary, body);
74
+ try {
75
+ await fs.link(temporary, target);
76
+ }
77
+ catch (cause) {
78
+ if (code(cause) === "EEXIST")
79
+ return { date: now(), result: { _tag: "Occupied" } };
80
+ throw cause;
81
+ }
82
+ }
83
+ finally {
84
+ await fs.rm(temporary, { force: true });
85
+ }
86
+ await syncDirectory(directory);
87
+ return { date: now(), result: { _tag: "Created" } };
88
+ }),
89
+ list: (prefix, startAfter, maxKeys) => attempt("FsStore.list", async () => {
90
+ const directory = path.posix.dirname(`${prefix}_`);
91
+ let names;
92
+ try {
93
+ names = await fs.readdir(path.join(root, ...directory.split("/")));
94
+ }
95
+ catch (cause) {
96
+ if (code(cause) === "ENOENT")
97
+ return { date: now(), result: { _tag: "Keys", keys: [] } };
98
+ throw cause;
99
+ }
100
+ const keys = names
101
+ .filter((name) => !name.startsWith(".tmp-"))
102
+ .map((name) => (directory === "." ? name : `${directory}/${name}`))
103
+ .filter((key) => key.startsWith(prefix) && (startAfter === null || key > startAfter))
104
+ .sort()
105
+ .slice(0, maxKeys);
106
+ return { date: now(), result: { _tag: "Keys", keys } };
107
+ }),
108
+ delete: (key) => attempt("FsStore.delete", async () => {
109
+ await fs.rm(file(key), { force: true });
110
+ await syncDirectory(path.dirname(file(key))).catch((cause) => {
111
+ if (code(cause) !== "ENOENT")
112
+ throw cause;
113
+ });
114
+ return { date: now(), result: { _tag: "Deleted" } };
115
+ })
116
+ };
117
+ }
118
+ const FsStore = Object.freeze({ make });
119
+ export { FsStore };
120
+ //# sourceMappingURL=fs.js.map