@arki/journal 0.0.0-stage → 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 (121) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +541 -2
  3. package/dist/api.d.ts +336 -0
  4. package/dist/api.d.ts.map +1 -0
  5. package/dist/api.js +2 -0
  6. package/dist/api.js.map +1 -0
  7. package/dist/command.d.ts +27 -0
  8. package/dist/command.d.ts.map +1 -0
  9. package/dist/command.js +157 -0
  10. package/dist/command.js.map +1 -0
  11. package/dist/debug.d.ts +7 -0
  12. package/dist/debug.d.ts.map +1 -0
  13. package/dist/debug.js +8 -0
  14. package/dist/debug.js.map +1 -0
  15. package/dist/dot.d.ts +102 -0
  16. package/dist/dot.d.ts.map +1 -0
  17. package/dist/dot.js +159 -0
  18. package/dist/dot.js.map +1 -0
  19. package/dist/entity.d.ts +56 -0
  20. package/dist/entity.d.ts.map +1 -0
  21. package/dist/entity.js +174 -0
  22. package/dist/entity.js.map +1 -0
  23. package/dist/errors.d.ts +47 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +112 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/event.d.ts +9 -0
  28. package/dist/event.d.ts.map +1 -0
  29. package/dist/event.js +19 -0
  30. package/dist/event.js.map +1 -0
  31. package/dist/fold.d.ts +29 -0
  32. package/dist/fold.d.ts.map +1 -0
  33. package/dist/fold.js +49 -0
  34. package/dist/fold.js.map +1 -0
  35. package/dist/handlers.d.ts +26 -0
  36. package/dist/handlers.d.ts.map +1 -0
  37. package/dist/handlers.js +130 -0
  38. package/dist/handlers.js.map +1 -0
  39. package/dist/history.d.ts +49 -0
  40. package/dist/history.d.ts.map +1 -0
  41. package/dist/history.js +259 -0
  42. package/dist/history.js.map +1 -0
  43. package/dist/hooks.d.ts +14 -0
  44. package/dist/hooks.d.ts.map +1 -0
  45. package/dist/hooks.js +67 -0
  46. package/dist/hooks.js.map +1 -0
  47. package/dist/id.d.ts +18 -0
  48. package/dist/id.d.ts.map +1 -0
  49. package/dist/id.js +41 -0
  50. package/dist/id.js.map +1 -0
  51. package/dist/index.d.ts +12 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +5 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/journal.d.ts +51 -0
  56. package/dist/journal.d.ts.map +1 -0
  57. package/dist/journal.js +181 -0
  58. package/dist/journal.js.map +1 -0
  59. package/dist/listen.d.ts +37 -0
  60. package/dist/listen.d.ts.map +1 -0
  61. package/dist/listen.js +90 -0
  62. package/dist/listen.js.map +1 -0
  63. package/dist/orpc.d.ts +21 -0
  64. package/dist/orpc.d.ts.map +1 -0
  65. package/dist/orpc.js +57 -0
  66. package/dist/orpc.js.map +1 -0
  67. package/dist/pg.d.ts +537 -0
  68. package/dist/pg.d.ts.map +1 -0
  69. package/dist/pg.js +80 -0
  70. package/dist/pg.js.map +1 -0
  71. package/dist/storage.d.ts +52 -0
  72. package/dist/storage.d.ts.map +1 -0
  73. package/dist/storage.js +221 -0
  74. package/dist/storage.js.map +1 -0
  75. package/dist/testing.d.ts +19 -0
  76. package/dist/testing.d.ts.map +1 -0
  77. package/dist/testing.js +23 -0
  78. package/dist/testing.js.map +1 -0
  79. package/dist/transaction-guard.d.ts +2 -0
  80. package/dist/transaction-guard.d.ts.map +1 -0
  81. package/dist/transaction-guard.js +39 -0
  82. package/dist/transaction-guard.js.map +1 -0
  83. package/dist/types.d.ts +414 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +2 -0
  86. package/dist/types.js.map +1 -0
  87. package/dist/worker.d.ts +109 -0
  88. package/dist/worker.d.ts.map +1 -0
  89. package/dist/worker.js +459 -0
  90. package/dist/worker.js.map +1 -0
  91. package/dist/write.d.ts +39 -0
  92. package/dist/write.d.ts.map +1 -0
  93. package/dist/write.js +287 -0
  94. package/dist/write.js.map +1 -0
  95. package/docs/agent-guide.md +324 -0
  96. package/llms.txt +40 -0
  97. package/package.json +99 -4
  98. package/src/api.ts +465 -0
  99. package/src/command.ts +242 -0
  100. package/src/debug.ts +8 -0
  101. package/src/dot.ts +296 -0
  102. package/src/entity.ts +377 -0
  103. package/src/errors.ts +134 -0
  104. package/src/event.ts +26 -0
  105. package/src/fold.ts +97 -0
  106. package/src/handlers.ts +262 -0
  107. package/src/history.ts +390 -0
  108. package/src/hooks.ts +85 -0
  109. package/src/id.ts +54 -0
  110. package/src/index.ts +76 -0
  111. package/src/journal.ts +256 -0
  112. package/src/listen.ts +109 -0
  113. package/src/orpc.ts +59 -0
  114. package/src/pg.ts +107 -0
  115. package/src/storage.ts +277 -0
  116. package/src/test-support.ts +82 -0
  117. package/src/testing.ts +23 -0
  118. package/src/transaction-guard.ts +41 -0
  119. package/src/types.ts +439 -0
  120. package/src/worker.ts +610 -0
  121. package/src/write.ts +434 -0
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The entity row and stored values: load and save the row, exact-id checks, value
3
+ * comparison, the JSON round-trip check, freezing, and the root-database guard.
4
+ * Imports only leaf files (`types`, `errors`). Must not import `journal`, `api`,
5
+ * `write`, `command`, `history` or `worker`.
6
+ */
7
+ import type { z } from 'zod';
8
+ import type { AnyEntity, AnyEventDef, DbHandle, EntityDef, EntityId, EntityTable, ReducerRow, RootDb, Row } from './types.js';
9
+ /** A select row has every column of its table. The check is real; the guard narrows. */
10
+ export declare function isRowOf<Tbl extends EntityTable>(table: Tbl, value: object): value is Row<Tbl>;
11
+ type EntityRowSource = Pick<AnyEntity, 'table' | 'keyColumn'>;
12
+ export declare function loadRow(db: DbHandle, entity: EntityRowSource, id: string, lock: boolean): Promise<object | null>;
13
+ /**
14
+ * With no row to compare against (a create and delete in one write, a replay of a missing row),
15
+ * `assertStoredId` sees nothing, so the id is checked up front, before any database access: not
16
+ * empty, within the key's length limit in code points (the column or the log would cut it), and,
17
+ * for a `uuid` key, lower-case and hyphenated.
18
+ */
19
+ export declare function assertExactId(entity: Pick<AnyEntity, 'kind' | 'uuidKey' | 'keyLimit'>, id: string): void;
20
+ export declare function assertStoredId(entity: Pick<AnyEntity, 'kind' | 'key'>, row: object | null, id: string): void;
21
+ /** Insert, update or delete the row so it matches `next`. Returns the stored row. */
22
+ export declare function saveReplayed<Tbl extends EntityTable, Key extends string>(db: DbHandle, entity: EntityDef<string, Tbl, AnyEventDef, unknown, Key>, id: EntityId<Tbl, Key>, current: object | null, next: ReducerRow<Tbl, Key> | null): Promise<Row<Tbl> | null>;
23
+ export declare function toRecord(value: object): Record<string, unknown>;
24
+ export declare function showValue(value: unknown): string;
25
+ /**
26
+ * The row the database returned must hold, for each of `columns`, the value the reducers produced.
27
+ * Replay recomputes from reducer output, so a value the database rounds, trims or alters would make
28
+ * replay and the table drift apart. Uses the same comparison as `verify`.
29
+ */
30
+ export declare function assertStoredValues(entity: Pick<AnyEntity, 'kind'>, id: string, produced: object, saved: object, columns: Iterable<string>): void;
31
+ /**
32
+ * Freeze a plain object or array and everything inside it. A `Date` is frozen
33
+ * itself; its internals are not touched. Anything else is left alone.
34
+ */
35
+ export declare function deepFreeze<T>(value: T): T;
36
+ /**
37
+ * Deep equality for stored rows: `verify` compares what replay produced with what the table holds.
38
+ * Strict: primitives, `Date`, value objects, arrays and plain objects only. A value object is a class
39
+ * instance with an `equals` method, such as a Temporal value from a custom column: it equals another
40
+ * instance of the same class when its `equals` says so. A `Map`, `Set`, other class instance or typed
41
+ * array is never equal to anything, because JSON storage would lose its contents.
42
+ */
43
+ export declare function sameValue(a: unknown, b: unknown): boolean;
44
+ /**
45
+ * Throws `UNSTABLE_SCHEMA` unless the value survives JSON storage and a second parse
46
+ * unchanged. Replay depends on this. A value JSON cannot encode (a `bigint`, a cycle)
47
+ * is the same failure, with the serializer's error as `cause`.
48
+ */
49
+ export declare function assertRoundTrips(schema: z.ZodType, value: unknown, kind: string, id: string, what: string): void;
50
+ export declare function assertRootDb(db: RootDb, verb: string): void;
51
+ export {};
52
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,UAAU,EACV,MAAM,EACN,GAAG,EACJ,MAAM,YAAY,CAAC;AAGpB,wFAAwF;AACxF,wBAAgB,OAAO,CAAC,GAAG,SAAS,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,CAE7F;AAED,KAAK,eAAe,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,WAAW,CAAC,CAAC;AAE9D,wBAAsB,OAAO,CAC3B,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,eAAe,EACvB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AASD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAuBxG;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAW5G;AAED,qFAAqF;AACrF,wBAAsB,YAAY,CAAC,GAAG,SAAS,WAAW,EAAE,GAAG,SAAS,MAAM,EAC5E,EAAE,EAAE,QAAQ,EACZ,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,EACzD,EAAE,EAAE,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,EACtB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,IAAI,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,GAChC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAe1B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAE/D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWhD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,EAC/B,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,GACxB,IAAI,CAaN;AAoBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAWzC;AAYD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,CAQzD;AAwBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAoBhH;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM3D"}
@@ -0,0 +1,221 @@
1
+ import { eq, getTableColumns, is } from 'drizzle-orm';
2
+ import { PgAsyncTransaction } from 'drizzle-orm/pg-core';
3
+ import { JournalError } from './errors.js';
4
+ /** A select row has every column of its table. The check is real; the guard narrows. */
5
+ export function isRowOf(table, value) {
6
+ return Object.keys(getTableColumns(table)).every(column => column in value);
7
+ }
8
+ export async function loadRow(db, entity, id, lock) {
9
+ const query = db.select().from(entity.table).where(eq(entity.keyColumn, id)).limit(1);
10
+ const rows = lock ? await query.for('update') : await query;
11
+ return rows[0] ?? null;
12
+ }
13
+ /**
14
+ * The id the caller passes must be exactly the key the database stores. A database can treat
15
+ * two spellings as one key (a `uuid` ignores case), but the log is keyed by the raw string,
16
+ * so a second spelling would split one entity's history in two.
17
+ */
18
+ const CANONICAL_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
19
+ /**
20
+ * With no row to compare against (a create and delete in one write, a replay of a missing row),
21
+ * `assertStoredId` sees nothing, so the id is checked up front, before any database access: not
22
+ * empty, within the key's length limit in code points (the column or the log would cut it), and,
23
+ * for a `uuid` key, lower-case and hyphenated.
24
+ */
25
+ export function assertExactId(entity, id) {
26
+ const fail = (reason) => {
27
+ throw new JournalError('ID_MISMATCH', entity.kind, id, reason);
28
+ };
29
+ if (id === '')
30
+ fail('the id is empty');
31
+ // (Surrogates are U+D800 to U+DFFF.) A lone surrogate is encoded by the driver as U+FFFD, so it would reach the history of another id.
32
+ // Iterating by code point yields a paired surrogate as one astral character, never as a surrogate.
33
+ for (const ch of id) {
34
+ const point = ch.codePointAt(0) ?? 0;
35
+ if (point >= 55_296 && point <= 57_343) {
36
+ fail('the id contains an unpaired surrogate, so it is not well-formed text and the database would store a different key');
37
+ }
38
+ }
39
+ const length = [...id].length;
40
+ if (length > entity.keyLimit.max) {
41
+ const owner = entity.keyLimit.source === 'column' ? "the key column's" : "the journal's";
42
+ fail(`the id is ${length} characters but the limit is ${entity.keyLimit.max}, ${owner} limit`);
43
+ }
44
+ if (entity.uuidKey && !CANONICAL_UUID.test(id)) {
45
+ fail(`${JSON.stringify(id)} is not a canonical uuid; uuid keys must be lower-case and hyphenated`);
46
+ }
47
+ }
48
+ export function assertStoredId(entity, row, id) {
49
+ if (row === null)
50
+ return;
51
+ const stored = Reflect.get(row, entity.key);
52
+ if (stored !== id) {
53
+ throw new JournalError('ID_MISMATCH', entity.kind, id, `the stored key is ${JSON.stringify(stored)} but the id given is ${JSON.stringify(id)}; use the stored form`);
54
+ }
55
+ }
56
+ /** Insert, update or delete the row so it matches `next`. Returns the stored row. */
57
+ export async function saveReplayed(db, entity, id, current, next) {
58
+ const erased = entity.table;
59
+ if (next === null) {
60
+ if (current !== null)
61
+ await db.delete(erased).where(eq(entity.keyColumn, id));
62
+ return null;
63
+ }
64
+ const saved = current === null
65
+ ? await db.insert(erased).values(next).returning()
66
+ : await db.update(erased).set(next).where(eq(entity.keyColumn, id)).returning();
67
+ const row = saved[0];
68
+ if (row === undefined || !isRowOf(entity.table, row)) {
69
+ throw new Error(`journal: saving ${id} returned no complete row`);
70
+ }
71
+ return row;
72
+ }
73
+ export function toRecord(value) {
74
+ return { ...value };
75
+ }
76
+ export function showValue(value) {
77
+ if (value instanceof Date)
78
+ return Number.isNaN(value.getTime()) ? 'Invalid Date' : value.toISOString();
79
+ if (typeof value === 'bigint')
80
+ return `${value}n`;
81
+ if (typeof value === 'object' && value !== null) {
82
+ try {
83
+ return JSON.stringify(value);
84
+ }
85
+ catch {
86
+ return Object.prototype.toString.call(value);
87
+ }
88
+ }
89
+ return typeof value === 'string' ? JSON.stringify(value) : String(value);
90
+ }
91
+ /**
92
+ * The row the database returned must hold, for each of `columns`, the value the reducers produced.
93
+ * Replay recomputes from reducer output, so a value the database rounds, trims or alters would make
94
+ * replay and the table drift apart. Uses the same comparison as `verify`.
95
+ */
96
+ export function assertStoredValues(entity, id, produced, saved, columns) {
97
+ const want = toRecord(produced);
98
+ const have = toRecord(saved);
99
+ for (const column of columns) {
100
+ if (!sameValue(want[column], have[column])) {
101
+ throw new JournalError('VALUE_MISMATCH', entity.kind, id, `column "${column}": the reducer produced ${showValue(want[column])} but the database stored ${showValue(have[column])}`);
102
+ }
103
+ }
104
+ }
105
+ /** Same own enumerable keys and same values. A key holding `undefined` is the same as a missing key, as in JSON. */
106
+ function sameKeys(left, right, same) {
107
+ const keys = Object.keys(left).filter(key => left[key] !== undefined);
108
+ const rightKeys = Object.keys(right).filter(key => right[key] !== undefined);
109
+ return keys.length === rightKeys.length && keys.every(key => same(left[key], right[key]));
110
+ }
111
+ /** An object literal shape: prototype `Object.prototype` or `null`. A `Set`, `Map`, `Date` or class instance is not one. */
112
+ function isPlainObject(value) {
113
+ if (typeof value !== 'object' || value === null)
114
+ return false;
115
+ const proto = Object.getPrototypeOf(value);
116
+ return proto === Object.prototype || proto === null;
117
+ }
118
+ /**
119
+ * Freeze a plain object or array and everything inside it. A `Date` is frozen
120
+ * itself; its internals are not touched. Anything else is left alone.
121
+ */
122
+ export function deepFreeze(value) {
123
+ if (Array.isArray(value)) {
124
+ for (const item of value)
125
+ deepFreeze(item);
126
+ Object.freeze(value);
127
+ }
128
+ else if (isPlainObject(value)) {
129
+ for (const item of Object.values(value))
130
+ deepFreeze(item);
131
+ Object.freeze(value);
132
+ }
133
+ else if (value instanceof Date) {
134
+ Object.freeze(value);
135
+ }
136
+ return value;
137
+ }
138
+ /** Same length, and every index from 0 up compared. A hole is not equal to a value, `null` and `undefined` included. */
139
+ function sameArray(a, b, same) {
140
+ if (a.length !== b.length)
141
+ return false;
142
+ for (let index = 0; index < a.length; index++) {
143
+ if (index in a !== index in b)
144
+ return false;
145
+ if (!same(a[index], b[index]))
146
+ return false;
147
+ }
148
+ return true;
149
+ }
150
+ /**
151
+ * Deep equality for stored rows: `verify` compares what replay produced with what the table holds.
152
+ * Strict: primitives, `Date`, value objects, arrays and plain objects only. A value object is a class
153
+ * instance with an `equals` method, such as a Temporal value from a custom column: it equals another
154
+ * instance of the same class when its `equals` says so. A `Map`, `Set`, other class instance or typed
155
+ * array is never equal to anything, because JSON storage would lose its contents.
156
+ */
157
+ export function sameValue(a, b) {
158
+ if (typeof a === 'function' || typeof a === 'symbol')
159
+ return false;
160
+ if (typeof a !== 'object' || a === null)
161
+ return Object.is(a, b);
162
+ if (a instanceof Date)
163
+ return b instanceof Date && a.getTime() === b.getTime();
164
+ if (Array.isArray(a))
165
+ return Array.isArray(b) && sameArray(a, b, sameValue);
166
+ if (isValueObject(a))
167
+ return Object.getPrototypeOf(a) === Object.getPrototypeOf(b) && a.equals(b) === true;
168
+ if (isPlainObject(a) && isPlainObject(b))
169
+ return sameKeys(a, b, sameValue);
170
+ return false;
171
+ }
172
+ /**
173
+ * A class instance that defines its own equality, like Temporal's `Instant` or `PlainDate`. A column
174
+ * whose driver rebuilds the same class on read round-trips it; a lossy column hands back another type
175
+ * or another value, and `equals` then reports the difference.
176
+ */
177
+ function isValueObject(value) {
178
+ return !isPlainObject(value) && 'equals' in value && typeof value.equals === 'function';
179
+ }
180
+ /**
181
+ * Deep equality for a value that must survive JSON storage: primitives, arrays and
182
+ * plain objects only. A `Set` has no enumerable keys and would otherwise equal `{}`.
183
+ */
184
+ function sameJson(a, b) {
185
+ if (Object.is(a, b))
186
+ return true;
187
+ if (Array.isArray(a) && Array.isArray(b)) {
188
+ return sameArray(a, b, sameJson);
189
+ }
190
+ if (isPlainObject(a) && isPlainObject(b))
191
+ return sameKeys(a, b, sameJson);
192
+ return false;
193
+ }
194
+ /**
195
+ * Throws `UNSTABLE_SCHEMA` unless the value survives JSON storage and a second parse
196
+ * unchanged. Replay depends on this. A value JSON cannot encode (a `bigint`, a cycle)
197
+ * is the same failure, with the serializer's error as `cause`.
198
+ */
199
+ export function assertRoundTrips(schema, value, kind, id, what) {
200
+ let stable;
201
+ try {
202
+ // Not structuredClone: the point is what JSON storage does to the value (Dates become strings).
203
+ const text = JSON.stringify(value);
204
+ const stored = JSON.parse(text);
205
+ const again = schema.safeParse(stored);
206
+ stable = again.success && sameJson(again.data, value);
207
+ }
208
+ catch (error) {
209
+ throw new JournalError('UNSTABLE_SCHEMA', kind, id, `${what}: the value cannot be stored as JSON`, {
210
+ cause: error,
211
+ });
212
+ }
213
+ if (!stable)
214
+ throw new JournalError('UNSTABLE_SCHEMA', kind, id, `${what}: the schema must accept its own stored output unchanged`);
215
+ }
216
+ export function assertRootDb(db, verb) {
217
+ if (is(db, PgAsyncTransaction)) {
218
+ throw new Error(`journal: ${verb} needs the root database, not a transaction: effects would run before the outer transaction commits`);
219
+ }
220
+ }
221
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Test helper: an in-memory PGlite with a schema pushed, the fixture every
3
+ * journal test rebuilds by hand. `@electric-sql/pglite` and `drizzle-kit` are
4
+ * optional peers. Import this module only from tests. This is the one file
5
+ * that may import them.
6
+ */
7
+ import { PGlite } from '@electric-sql/pglite';
8
+ /**
9
+ * Open a fresh in-memory database with `schema` (the journal tables and the
10
+ * app's tables) pushed. Returns the Drizzle database and the PGlite client;
11
+ * call `client.close()` when the test file ends.
12
+ */
13
+ export declare function openJournalDb(schema: Record<string, unknown>): Promise<{
14
+ db: import("drizzle-orm/pglite").PgliteDatabase<import("drizzle-orm").EmptyRelations> & {
15
+ $client: PGlite;
16
+ };
17
+ client: PGlite;
18
+ }>;
19
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAI9C;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;GAOlE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Test helper: an in-memory PGlite with a schema pushed, the fixture every
3
+ * journal test rebuilds by hand. `@electric-sql/pglite` and `drizzle-kit` are
4
+ * optional peers. Import this module only from tests. This is the one file
5
+ * that may import them.
6
+ */
7
+ import { PGlite } from '@electric-sql/pglite';
8
+ import { pushSchema } from 'drizzle-kit/api-postgres';
9
+ import { drizzle } from 'drizzle-orm/pglite';
10
+ /**
11
+ * Open a fresh in-memory database with `schema` (the journal tables and the
12
+ * app's tables) pushed. Returns the Drizzle database and the PGlite client;
13
+ * call `client.close()` when the test file ends.
14
+ */
15
+ export async function openJournalDb(schema) {
16
+ const client = new PGlite('memory://');
17
+ await client.waitReady;
18
+ const db = drizzle({ client });
19
+ const { apply } = await pushSchema(schema, db);
20
+ await apply();
21
+ return { db, client };
22
+ }
23
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAA+B;IACjE,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC;IACvC,MAAM,MAAM,CAAC,SAAS,CAAC;IACvB,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/B,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,MAAM,KAAK,EAAE,CAAC;IACd,OAAO,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC;AACxB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function oneAtATime<T>(handle: object, what: string, work: () => Promise<T>): Promise<T>;
2
+ //# sourceMappingURL=transaction-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-guard.d.ts","sourceRoot":"","sources":["../src/transaction-guard.ts"],"names":[],"mappings":"AA0BA,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAc9F"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The guard that allows one journal operation per transaction at a time.
3
+ * Its `WeakMap` is the only module-level state in the library, and it must stay
4
+ * module-scoped: see the comment on it.
5
+ * Imports nothing from this package. `write` and `command` import it.
6
+ */
7
+ import { is } from 'drizzle-orm';
8
+ import { PgAsyncTransaction } from 'drizzle-orm/pg-core';
9
+ /** Passed to `then` twice: waits for a promise to settle, whether it resolves or rejects. */
10
+ const ignoreSettlement = () => undefined;
11
+ /**
12
+ * Savepoints are a stack. Two journal operations open on one transaction at the
13
+ * same time release each other's savepoint, so a caught failure of one could
14
+ * erase what the other wrote. A transaction therefore holds one operation at a
15
+ * time. A second one is refused: it waits for the first to settle (so the
16
+ * caller's rollback undoes it whole), then rejects. A child runs on its own
17
+ * savepoint handle, so nesting is fine. The root database opens a separate
18
+ * transaction per operation and is never busy.
19
+ *
20
+ * The map is module-scoped on purpose. Two journals can run on one transaction,
21
+ * and then they share its savepoint stack, so they must share this guard too.
22
+ * Do not move it into the journal factory.
23
+ */
24
+ const inFlight = new WeakMap();
25
+ export function oneAtATime(handle, what, work) {
26
+ if (!is(handle, PgAsyncTransaction))
27
+ return work();
28
+ const busy = inFlight.get(handle);
29
+ if (busy !== undefined) {
30
+ return busy.then(ignoreSettlement, ignoreSettlement).then(() => {
31
+ throw new Error(`journal: ${what} started on a transaction that already has a journal operation in flight; await operations on one transaction one at a time`);
32
+ });
33
+ }
34
+ // eslint-disable-next-line drizzle/enforce-delete-with-where -- WeakMap#delete, not a Drizzle query
35
+ const running = work().finally(() => inFlight.delete(handle));
36
+ inFlight.set(handle, running);
37
+ return running;
38
+ }
39
+ //# sourceMappingURL=transaction-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction-guard.js","sourceRoot":"","sources":["../src/transaction-guard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,6FAA6F;AAC7F,MAAM,gBAAgB,GAAG,GAAc,EAAE,CAAC,SAAS,CAAC;AAEpD;;;;;;;;;;;;GAYG;AACH,MAAM,QAAQ,GAAG,IAAI,OAAO,EAA4B,CAAC;AACzD,MAAM,UAAU,UAAU,CAAI,MAAc,EAAE,IAAY,EAAE,IAAsB;IAChF,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC;QAAE,OAAO,IAAI,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;YAC7D,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,6HAA6H,CAC9I,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IACD,oGAAoG;IACpG,MAAM,OAAO,GAAG,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9D,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,OAAO,OAAO,CAAC;AACjB,CAAC"}