@graphrefly/ts 0.0.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 (210) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/index.cjs +9015 -0
  3. package/dist/adapters/index.cjs.map +1 -0
  4. package/dist/adapters/index.d.cts +678 -0
  5. package/dist/adapters/index.d.ts +678 -0
  6. package/dist/adapters/index.js +2894 -0
  7. package/dist/adapters/index.js.map +1 -0
  8. package/dist/adapters/observe-storage.cjs +260 -0
  9. package/dist/adapters/observe-storage.cjs.map +1 -0
  10. package/dist/adapters/observe-storage.d.cts +77 -0
  11. package/dist/adapters/observe-storage.d.ts +77 -0
  12. package/dist/adapters/observe-storage.js +161 -0
  13. package/dist/adapters/observe-storage.js.map +1 -0
  14. package/dist/append-log-DisvEReb.d.cts +189 -0
  15. package/dist/append-log-DzKMmjxx.d.ts +189 -0
  16. package/dist/chunk-2ILL7HQI.js +1189 -0
  17. package/dist/chunk-2ILL7HQI.js.map +1 -0
  18. package/dist/chunk-4VLAQHZI.js +330 -0
  19. package/dist/chunk-4VLAQHZI.js.map +1 -0
  20. package/dist/chunk-6PHB5CJS.js +229 -0
  21. package/dist/chunk-6PHB5CJS.js.map +1 -0
  22. package/dist/chunk-COHO25CZ.js +2449 -0
  23. package/dist/chunk-COHO25CZ.js.map +1 -0
  24. package/dist/chunk-D6BG7U6A.js +161 -0
  25. package/dist/chunk-D6BG7U6A.js.map +1 -0
  26. package/dist/chunk-DJC4CHLQ.js +818 -0
  27. package/dist/chunk-DJC4CHLQ.js.map +1 -0
  28. package/dist/chunk-DK554R7P.js +67 -0
  29. package/dist/chunk-DK554R7P.js.map +1 -0
  30. package/dist/chunk-DKP56CSW.js +322 -0
  31. package/dist/chunk-DKP56CSW.js.map +1 -0
  32. package/dist/chunk-G4M3NHRJ.js +688 -0
  33. package/dist/chunk-G4M3NHRJ.js.map +1 -0
  34. package/dist/chunk-GAJW5I5P.js +1 -0
  35. package/dist/chunk-GAJW5I5P.js.map +1 -0
  36. package/dist/chunk-GDL26N2T.js +282 -0
  37. package/dist/chunk-GDL26N2T.js.map +1 -0
  38. package/dist/chunk-GTPCOEOZ.js +741 -0
  39. package/dist/chunk-GTPCOEOZ.js.map +1 -0
  40. package/dist/chunk-H2HTHFVN.js +1 -0
  41. package/dist/chunk-H2HTHFVN.js.map +1 -0
  42. package/dist/chunk-HDRL4VQQ.js +218 -0
  43. package/dist/chunk-HDRL4VQQ.js.map +1 -0
  44. package/dist/chunk-I3IHOHCJ.js +2267 -0
  45. package/dist/chunk-I3IHOHCJ.js.map +1 -0
  46. package/dist/chunk-JIMUDQN3.js +965 -0
  47. package/dist/chunk-JIMUDQN3.js.map +1 -0
  48. package/dist/chunk-JR4IKCR2.js +462 -0
  49. package/dist/chunk-JR4IKCR2.js.map +1 -0
  50. package/dist/chunk-K3FXTC3I.js +165 -0
  51. package/dist/chunk-K3FXTC3I.js.map +1 -0
  52. package/dist/chunk-OOEN33FB.js +65 -0
  53. package/dist/chunk-OOEN33FB.js.map +1 -0
  54. package/dist/chunk-PKVBHGBM.js +554 -0
  55. package/dist/chunk-PKVBHGBM.js.map +1 -0
  56. package/dist/chunk-RIIOUTGM.js +1857 -0
  57. package/dist/chunk-RIIOUTGM.js.map +1 -0
  58. package/dist/chunk-SCRFRCGR.js +33 -0
  59. package/dist/chunk-SCRFRCGR.js.map +1 -0
  60. package/dist/chunk-SWTLEQL6.js +53 -0
  61. package/dist/chunk-SWTLEQL6.js.map +1 -0
  62. package/dist/chunk-SZAETNNA.js +1 -0
  63. package/dist/chunk-SZAETNNA.js.map +1 -0
  64. package/dist/chunk-TEW47IVR.js +49 -0
  65. package/dist/chunk-TEW47IVR.js.map +1 -0
  66. package/dist/chunk-Y3ZLRGHC.js +1482 -0
  67. package/dist/chunk-Y3ZLRGHC.js.map +1 -0
  68. package/dist/chunk-Z2ANQ6JW.js +149 -0
  69. package/dist/chunk-Z2ANQ6JW.js.map +1 -0
  70. package/dist/codec-BB7p8fJc.d.cts +38 -0
  71. package/dist/codec-BB7p8fJc.d.ts +38 -0
  72. package/dist/composition/index.cjs +2505 -0
  73. package/dist/composition/index.cjs.map +1 -0
  74. package/dist/composition/index.d.cts +95 -0
  75. package/dist/composition/index.d.ts +95 -0
  76. package/dist/composition/index.js +19 -0
  77. package/dist/composition/index.js.map +1 -0
  78. package/dist/core/index.cjs +2475 -0
  79. package/dist/core/index.cjs.map +1 -0
  80. package/dist/core/index.d.cts +25 -0
  81. package/dist/core/index.d.ts +25 -0
  82. package/dist/core/index.js +74 -0
  83. package/dist/core/index.js.map +1 -0
  84. package/dist/cqrs/index.cjs +595 -0
  85. package/dist/cqrs/index.cjs.map +1 -0
  86. package/dist/cqrs/index.d.cts +200 -0
  87. package/dist/cqrs/index.d.ts +200 -0
  88. package/dist/cqrs/index.js +557 -0
  89. package/dist/cqrs/index.js.map +1 -0
  90. package/dist/data-structures/index.cjs +4277 -0
  91. package/dist/data-structures/index.cjs.map +1 -0
  92. package/dist/data-structures/index.d.cts +22 -0
  93. package/dist/data-structures/index.d.ts +22 -0
  94. package/dist/data-structures/index.js +31 -0
  95. package/dist/data-structures/index.js.map +1 -0
  96. package/dist/describe-BZb7AS2y.d.ts +57 -0
  97. package/dist/describe-Ciu8SUnN.d.cts +57 -0
  98. package/dist/graph/index.cjs +7832 -0
  99. package/dist/graph/index.cjs.map +1 -0
  100. package/dist/graph/index.d.cts +251 -0
  101. package/dist/graph/index.d.ts +251 -0
  102. package/dist/graph/index.js +131 -0
  103. package/dist/graph/index.js.map +1 -0
  104. package/dist/graph-DfdF9zy4.d.ts +666 -0
  105. package/dist/graph-s23RbbFO.d.cts +666 -0
  106. package/dist/index-CAEmLCYF.d.cts +166 -0
  107. package/dist/index-SnzZu3x7.d.ts +166 -0
  108. package/dist/index.cjs +15409 -0
  109. package/dist/index.cjs.map +1 -0
  110. package/dist/index.d.cts +23 -0
  111. package/dist/index.d.ts +23 -0
  112. package/dist/index.js +582 -0
  113. package/dist/index.js.map +1 -0
  114. package/dist/messages-BZqrGGm1.d.cts +55 -0
  115. package/dist/messages-BZqrGGm1.d.ts +55 -0
  116. package/dist/messaging/index.cjs +818 -0
  117. package/dist/messaging/index.cjs.map +1 -0
  118. package/dist/messaging/index.d.cts +235 -0
  119. package/dist/messaging/index.d.ts +235 -0
  120. package/dist/messaging/index.js +767 -0
  121. package/dist/messaging/index.js.map +1 -0
  122. package/dist/observe-event-log-0SVsx7JG.d.cts +88 -0
  123. package/dist/observe-event-log-Dnm76owp.d.ts +88 -0
  124. package/dist/operators/index.cjs +3774 -0
  125. package/dist/operators/index.cjs.map +1 -0
  126. package/dist/operators/index.d.cts +308 -0
  127. package/dist/operators/index.d.ts +308 -0
  128. package/dist/operators/index.js +112 -0
  129. package/dist/operators/index.js.map +1 -0
  130. package/dist/orchestration/index.cjs +3541 -0
  131. package/dist/orchestration/index.cjs.map +1 -0
  132. package/dist/orchestration/index.d.cts +302 -0
  133. package/dist/orchestration/index.d.ts +302 -0
  134. package/dist/orchestration/index.js +1004 -0
  135. package/dist/orchestration/index.js.map +1 -0
  136. package/dist/patterns/index.cjs +1045 -0
  137. package/dist/patterns/index.cjs.map +1 -0
  138. package/dist/patterns/index.d.cts +39 -0
  139. package/dist/patterns/index.d.ts +39 -0
  140. package/dist/patterns/index.js +33 -0
  141. package/dist/patterns/index.js.map +1 -0
  142. package/dist/reactive-collection-BxBqtIOE.d.cts +344 -0
  143. package/dist/reactive-collection-Cb1II1Fi.d.ts +344 -0
  144. package/dist/reactive-collection-storage-BNiEc-Gd.d.ts +217 -0
  145. package/dist/reactive-collection-storage-D-KmjE0J.d.cts +217 -0
  146. package/dist/reactive-map-BcB4KsQR.d.ts +256 -0
  147. package/dist/reactive-map-D-g3dv8I.d.cts +256 -0
  148. package/dist/read-through-B1CYPfq_.d.cts +82 -0
  149. package/dist/read-through-DwyJhGs1.d.ts +82 -0
  150. package/dist/render/index.cjs +250 -0
  151. package/dist/render/index.cjs.map +1 -0
  152. package/dist/render/index.d.cts +58 -0
  153. package/dist/render/index.d.ts +58 -0
  154. package/dist/render/index.js +20 -0
  155. package/dist/render/index.js.map +1 -0
  156. package/dist/resilience-Dig3Q7n0.d.cts +45 -0
  157. package/dist/resilience-Dig3Q7n0.d.ts +45 -0
  158. package/dist/semantic-memory-graph-BYoIlP35.d.cts +356 -0
  159. package/dist/semantic-memory-graph-CxyXLms3.d.ts +356 -0
  160. package/dist/solutions/index.cjs +3249 -0
  161. package/dist/solutions/index.cjs.map +1 -0
  162. package/dist/solutions/index.d.cts +520 -0
  163. package/dist/solutions/index.d.ts +520 -0
  164. package/dist/solutions/index.js +31 -0
  165. package/dist/solutions/index.js.map +1 -0
  166. package/dist/sources/browser.cjs +3095 -0
  167. package/dist/sources/browser.cjs.map +1 -0
  168. package/dist/sources/browser.d.cts +39 -0
  169. package/dist/sources/browser.d.ts +39 -0
  170. package/dist/sources/browser.js +137 -0
  171. package/dist/sources/browser.js.map +1 -0
  172. package/dist/sources/index.cjs +3021 -0
  173. package/dist/sources/index.cjs.map +1 -0
  174. package/dist/sources/index.d.cts +7 -0
  175. package/dist/sources/index.d.ts +7 -0
  176. package/dist/sources/index.js +63 -0
  177. package/dist/sources/index.js.map +1 -0
  178. package/dist/sources/node.cjs +675 -0
  179. package/dist/sources/node.cjs.map +1 -0
  180. package/dist/sources/node.d.cts +109 -0
  181. package/dist/sources/node.d.ts +109 -0
  182. package/dist/sources/node.js +639 -0
  183. package/dist/sources/node.js.map +1 -0
  184. package/dist/storage/browser.cjs +777 -0
  185. package/dist/storage/browser.cjs.map +1 -0
  186. package/dist/storage/browser.d.cts +40 -0
  187. package/dist/storage/browser.d.ts +40 -0
  188. package/dist/storage/browser.js +275 -0
  189. package/dist/storage/browser.js.map +1 -0
  190. package/dist/storage/index.cjs +1989 -0
  191. package/dist/storage/index.cjs.map +1 -0
  192. package/dist/storage/index.d.cts +92 -0
  193. package/dist/storage/index.d.ts +92 -0
  194. package/dist/storage/index.js +161 -0
  195. package/dist/storage/index.js.map +1 -0
  196. package/dist/storage/node.cjs +898 -0
  197. package/dist/storage/node.cjs.map +1 -0
  198. package/dist/storage/node.d.cts +66 -0
  199. package/dist/storage/node.d.ts +66 -0
  200. package/dist/storage/node.js +402 -0
  201. package/dist/storage/node.js.map +1 -0
  202. package/dist/testing/index.cjs +95 -0
  203. package/dist/testing/index.cjs.map +1 -0
  204. package/dist/testing/index.d.cts +20 -0
  205. package/dist/testing/index.d.ts +20 -0
  206. package/dist/testing/index.js +8 -0
  207. package/dist/testing/index.js.map +1 -0
  208. package/dist/types-bNMSWKqF.d.cts +732 -0
  209. package/dist/types-h12mNYbq.d.ts +732 -0
  210. package/package.json +250 -0
@@ -0,0 +1,402 @@
1
+ import {
2
+ appendLogStorage
3
+ } from "../chunk-6PHB5CJS.js";
4
+ import {
5
+ kvStorage
6
+ } from "../chunk-DKP56CSW.js";
7
+ import "../chunk-4VLAQHZI.js";
8
+
9
+ // src/storage/node.ts
10
+ import { randomBytes } from "crypto";
11
+ import {
12
+ closeSync,
13
+ mkdirSync,
14
+ openSync,
15
+ readdirSync,
16
+ readFileSync,
17
+ renameSync,
18
+ unlinkSync,
19
+ writeFileSync
20
+ } from "fs";
21
+ import { createRequire } from "module";
22
+ import { basename, dirname, join } from "path";
23
+ var FILE_STEM_PREFIX = "k-";
24
+ var NAMESPACE_SEPARATOR = "\0";
25
+ var SQLITE_GENERATION = /* @__PURE__ */ Symbol("graphrefly.sqliteBackend.generation");
26
+ var encoder = new TextEncoder();
27
+ var decoder = new TextDecoder("utf-8", { fatal: true });
28
+ var require2 = createRequire("/graphrefly-storage-node.js");
29
+ function isErrno(error, code) {
30
+ return typeof error === "object" && error !== null && "code" in error && error.code === code;
31
+ }
32
+ function keyToStem(key) {
33
+ let out = "";
34
+ for (const ch of key) {
35
+ if (/^[a-zA-Z0-9_-]$/.test(ch)) {
36
+ out += ch;
37
+ continue;
38
+ }
39
+ for (const byte of encoder.encode(ch)) out += `%${byte.toString(16).padStart(2, "0")}`;
40
+ }
41
+ return out;
42
+ }
43
+ function stemToKey(stem) {
44
+ const bytes = [];
45
+ let i = 0;
46
+ while (i < stem.length) {
47
+ const ch = stem[i];
48
+ if (ch === "%" && i + 2 < stem.length) {
49
+ const hex = stem.slice(i + 1, i + 3);
50
+ if (/^[0-9a-f]{2}$/i.test(hex)) {
51
+ bytes.push(Number.parseInt(hex, 16));
52
+ i += 3;
53
+ continue;
54
+ }
55
+ }
56
+ const code = ch.charCodeAt(0);
57
+ if (code > 127) return null;
58
+ bytes.push(code);
59
+ i += 1;
60
+ }
61
+ try {
62
+ return decoder.decode(new Uint8Array(bytes));
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+ function validateExtension(extension) {
68
+ if (extension.length < 2 || !extension.startsWith(".") || extension.includes("..") || /[/\\\0]/.test(extension) || !/^[.A-Za-z0-9_-]+$/.test(extension)) {
69
+ throw new TypeError("fileBackend: extension must be a simple suffix such as .bin");
70
+ }
71
+ return extension;
72
+ }
73
+ function validateNamespace(label, namespace) {
74
+ if (typeof namespace !== "string") {
75
+ throw new TypeError(`${label}: namespace must be a string`);
76
+ }
77
+ if (namespace.includes(NAMESPACE_SEPARATOR)) {
78
+ throw new TypeError(`${label}: namespace must not contain U+0000`);
79
+ }
80
+ return namespace;
81
+ }
82
+ function validateLogicalKey(label, key) {
83
+ if (typeof key !== "string") {
84
+ throw new TypeError(`${label}: key must be a string`);
85
+ }
86
+ if (key.includes(NAMESPACE_SEPARATOR)) {
87
+ throw new TypeError(`${label}: key must not contain U+0000`);
88
+ }
89
+ return key;
90
+ }
91
+ function validateListPrefix(label, prefix) {
92
+ if (typeof prefix !== "string") {
93
+ throw new TypeError(`${label}: list prefix must be a string`);
94
+ }
95
+ if (prefix.includes(NAMESPACE_SEPARATOR)) {
96
+ throw new TypeError(`${label}: list prefix must not contain U+0000`);
97
+ }
98
+ return prefix;
99
+ }
100
+ function validateSqliteTableName(tableName) {
101
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tableName)) {
102
+ throw new TypeError("sqliteBackend: tableName must be a simple SQLite identifier");
103
+ }
104
+ return tableName;
105
+ }
106
+ function sqliteGeneration(storageId, key, rowGeneration, epoch) {
107
+ return Object.freeze({
108
+ [SQLITE_GENERATION]: Object.freeze([storageId, key, rowGeneration, epoch])
109
+ });
110
+ }
111
+ function readSqliteGeneration(generation) {
112
+ if (typeof generation !== "object" || generation === null) return void 0;
113
+ const maybe = generation;
114
+ const token = maybe[SQLITE_GENERATION];
115
+ if (Array.isArray(token) && token.length === 4 && typeof token[0] === "object" && token[0] !== null && typeof token[1] === "string" && (token[2] === null || typeof token[2] === "number") && typeof token[3] === "number") {
116
+ return token;
117
+ }
118
+ return void 0;
119
+ }
120
+ function sqliteChanges(result) {
121
+ return typeof result === "object" && result !== null && "changes" in result && typeof result.changes === "number" ? result.changes : 0;
122
+ }
123
+ function sqliteBytes(value) {
124
+ return Uint8Array.from(value instanceof Uint8Array ? value : new Uint8Array(value));
125
+ }
126
+ function loadSqliteDatabaseSync() {
127
+ try {
128
+ const DatabaseSync = require2("node:sqlite").DatabaseSync;
129
+ if (typeof DatabaseSync !== "function") {
130
+ throw new TypeError("DatabaseSync export is missing");
131
+ }
132
+ return DatabaseSync;
133
+ } catch (error) {
134
+ throw new Error("sqliteBackend: node:sqlite is not available in this Node runtime", {
135
+ cause: error
136
+ });
137
+ }
138
+ }
139
+ function fileBackend(dir, opts = {}) {
140
+ const extension = validateExtension(opts.extension ?? ".bin");
141
+ const namespace = opts.namespace === void 0 ? "" : validateNamespace("fileBackend", opts.namespace);
142
+ const namespacePrefix = namespace.length > 0 ? `${namespace}${NAMESPACE_SEPARATOR}` : "";
143
+ const storageKey = (key) => `${namespacePrefix}${validateLogicalKey("fileBackend", key)}`;
144
+ const pathFor = (key) => join(dir, `${FILE_STEM_PREFIX}${keyToStem(storageKey(key))}${extension}`);
145
+ const keyFromFilename = (filename) => {
146
+ if (!filename.endsWith(extension)) return null;
147
+ const stem = filename.slice(0, -extension.length);
148
+ if (!stem.startsWith(FILE_STEM_PREFIX)) return null;
149
+ const key = stemToKey(stem.slice(FILE_STEM_PREFIX.length));
150
+ if (key === null || !key.startsWith(namespacePrefix)) return null;
151
+ const logical = key.slice(namespacePrefix.length);
152
+ if (logical.includes(NAMESPACE_SEPARATOR)) {
153
+ throw new TypeError("fileBackend: malformed stored key");
154
+ }
155
+ return logical;
156
+ };
157
+ return {
158
+ get(key) {
159
+ try {
160
+ return Uint8Array.from(readFileSync(pathFor(key)));
161
+ } catch (error) {
162
+ if (isErrno(error, "ENOENT")) return void 0;
163
+ throw error;
164
+ }
165
+ },
166
+ put(key, value) {
167
+ mkdirSync(dir, { recursive: true });
168
+ const filePath = pathFor(key);
169
+ const base = basename(filePath);
170
+ const parent = dirname(filePath);
171
+ const tmp = join(parent, `.${base}.${randomBytes(8).toString("hex")}.tmp`);
172
+ try {
173
+ writeFileSync(tmp, value);
174
+ renameSync(tmp, filePath);
175
+ } catch (error) {
176
+ try {
177
+ unlinkSync(tmp);
178
+ } catch {
179
+ }
180
+ throw error;
181
+ }
182
+ },
183
+ putIfAbsent(key, value) {
184
+ mkdirSync(dir, { recursive: true });
185
+ const filePath = pathFor(key);
186
+ let fd;
187
+ try {
188
+ fd = openSync(filePath, "wx");
189
+ writeFileSync(fd, value);
190
+ return true;
191
+ } catch (error) {
192
+ if (isErrno(error, "EEXIST")) return false;
193
+ if (fd !== void 0) {
194
+ try {
195
+ unlinkSync(filePath);
196
+ } catch {
197
+ }
198
+ }
199
+ throw error;
200
+ } finally {
201
+ if (fd !== void 0) closeSync(fd);
202
+ }
203
+ },
204
+ delete(key) {
205
+ try {
206
+ unlinkSync(pathFor(key));
207
+ } catch (error) {
208
+ if (!isErrno(error, "ENOENT")) throw error;
209
+ }
210
+ },
211
+ list(prefix = "") {
212
+ const validatedPrefix = validateListPrefix("fileBackend", prefix);
213
+ let entries;
214
+ try {
215
+ entries = readdirSync(dir);
216
+ } catch (error) {
217
+ if (isErrno(error, "ENOENT")) return [];
218
+ throw error;
219
+ }
220
+ const keys = [];
221
+ for (const entry of entries) {
222
+ if (entry.startsWith(".")) continue;
223
+ const key = keyFromFilename(entry);
224
+ if (key?.startsWith(validatedPrefix)) keys.push(key);
225
+ }
226
+ return keys.sort();
227
+ }
228
+ };
229
+ }
230
+ function fileKv(dir, opts = {}) {
231
+ return kvStorage({
232
+ backend: fileBackend(dir, opts),
233
+ codec: opts.codec
234
+ });
235
+ }
236
+ function fileAppendLog(dir, opts = {}) {
237
+ return appendLogStorage({
238
+ kv: fileKv(dir, opts),
239
+ prefix: opts.prefix ?? "event-log"
240
+ });
241
+ }
242
+ function sqliteBackend(path, opts = {}) {
243
+ const table = validateSqliteTableName(opts.tableName ?? "graphrefly_storage");
244
+ const metaTable = validateSqliteTableName(`${table}_meta`);
245
+ const namespace = opts.namespace === void 0 ? "" : validateNamespace("sqliteBackend", opts.namespace);
246
+ const DatabaseSync = loadSqliteDatabaseSync();
247
+ const namespacePrefix = namespace.length > 0 ? `${namespace}${NAMESPACE_SEPARATOR}` : "";
248
+ const storageKey = (key) => `${namespacePrefix}${validateLogicalKey("sqliteBackend", key)}`;
249
+ const storageId = Object.freeze({});
250
+ const logicalKey = (key) => {
251
+ if (typeof key !== "string") {
252
+ throw new TypeError("sqliteBackend: stored key is corrupt");
253
+ }
254
+ if (!key.startsWith(namespacePrefix)) return void 0;
255
+ const logical = key.slice(namespacePrefix.length);
256
+ if (logical.includes(NAMESPACE_SEPARATOR)) {
257
+ throw new TypeError("sqliteBackend: malformed stored key");
258
+ }
259
+ return logical;
260
+ };
261
+ const db = new DatabaseSync(path);
262
+ db.exec(
263
+ `CREATE TABLE IF NOT EXISTS ${table} (k TEXT PRIMARY KEY, v BLOB NOT NULL, g INTEGER NOT NULL DEFAULT 0)`
264
+ );
265
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all();
266
+ if (!columns.some((column) => column.name === "g")) {
267
+ db.exec(`ALTER TABLE ${table} ADD COLUMN g INTEGER NOT NULL DEFAULT 0`);
268
+ }
269
+ db.exec(`CREATE TABLE IF NOT EXISTS ${metaTable} (k TEXT PRIMARY KEY, v INTEGER NOT NULL)`);
270
+ db.exec(`
271
+ CREATE TRIGGER IF NOT EXISTS ${table}_generation_insert
272
+ AFTER INSERT ON ${table}
273
+ BEGIN
274
+ INSERT INTO ${metaTable} (k, v) VALUES (NEW.k, 1)
275
+ ON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;
276
+ END
277
+ `);
278
+ db.exec(`
279
+ CREATE TRIGGER IF NOT EXISTS ${table}_generation_update
280
+ AFTER UPDATE ON ${table}
281
+ BEGIN
282
+ INSERT INTO ${metaTable} (k, v) VALUES (NEW.k, 1)
283
+ ON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;
284
+ END
285
+ `);
286
+ db.exec(`
287
+ CREATE TRIGGER IF NOT EXISTS ${table}_generation_delete
288
+ AFTER DELETE ON ${table}
289
+ BEGIN
290
+ INSERT INTO ${metaTable} (k, v) VALUES (OLD.k, 1)
291
+ ON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;
292
+ END
293
+ `);
294
+ const keyEpoch = (key) => {
295
+ const row = db.prepare(`SELECT v FROM ${metaTable} WHERE k = ?`).get(key);
296
+ const value = row?.v ?? 0;
297
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
298
+ throw new Error("sqliteBackend: per-key generation epoch is corrupt");
299
+ }
300
+ return value;
301
+ };
302
+ const readRow = (key) => {
303
+ const row = db.prepare(`SELECT v, g FROM ${table} WHERE k = ?`).get(storageKey(key));
304
+ if (row?.v === void 0) return void 0;
305
+ const generation = row.g;
306
+ if (typeof generation !== "number" || !Number.isSafeInteger(generation) || generation < 0) {
307
+ throw new Error("sqliteBackend: row generation is corrupt");
308
+ }
309
+ return { value: sqliteBytes(row.v), generation };
310
+ };
311
+ return {
312
+ name: `sqlite:${path}/${table}`,
313
+ get(key) {
314
+ return readRow(key)?.value;
315
+ },
316
+ put(key, value) {
317
+ db.prepare(
318
+ `INSERT INTO ${table} (k, v, g) VALUES (?, ?, 0) ON CONFLICT(k) DO UPDATE SET v = excluded.v, g = ${table}.g + 1`
319
+ ).run(storageKey(key), value);
320
+ },
321
+ putIfAbsent(key, value) {
322
+ const result = db.prepare(`INSERT OR IGNORE INTO ${table} (k, v, g) VALUES (?, ?, 0)`).run(storageKey(key), value);
323
+ return sqliteChanges(result) === 1;
324
+ },
325
+ getVersioned(key) {
326
+ const row = readRow(key);
327
+ if (row === void 0) {
328
+ return {
329
+ kind: "miss",
330
+ generation: sqliteGeneration(storageId, storageKey(key), null, keyEpoch(storageKey(key)))
331
+ };
332
+ }
333
+ return {
334
+ kind: "hit",
335
+ value: row.value,
336
+ generation: sqliteGeneration(
337
+ storageId,
338
+ storageKey(key),
339
+ row.generation,
340
+ keyEpoch(storageKey(key))
341
+ )
342
+ };
343
+ },
344
+ setIfMatch(key, value, generation) {
345
+ const keyForStorage = storageKey(key);
346
+ const observed = readSqliteGeneration(generation);
347
+ if (observed === void 0 || observed[0] !== storageId || observed[1] !== keyForStorage) {
348
+ return false;
349
+ }
350
+ if (observed[2] === null) {
351
+ return sqliteChanges(
352
+ db.prepare(
353
+ `INSERT INTO ${table} (k, v, g) SELECT ?, ?, 0 WHERE COALESCE((SELECT v FROM ${metaTable} WHERE k = ?), 0) = ? AND NOT EXISTS (SELECT 1 FROM ${table} WHERE k = ?)`
354
+ ).run(keyForStorage, value, keyForStorage, observed[3], keyForStorage)
355
+ ) === 1;
356
+ }
357
+ return sqliteChanges(
358
+ db.prepare(
359
+ `UPDATE ${table} SET v = ?, g = g + 1 WHERE k = ? AND g = ? AND COALESCE((SELECT v FROM ${metaTable} WHERE k = ?), 0) = ?`
360
+ ).run(value, keyForStorage, observed[2], keyForStorage, observed[3])
361
+ ) === 1;
362
+ },
363
+ delete(key) {
364
+ db.prepare(`DELETE FROM ${table} WHERE k = ?`).run(storageKey(key));
365
+ },
366
+ list(prefix = "") {
367
+ const validatedPrefix = validateListPrefix("sqliteBackend", prefix);
368
+ const rows = db.prepare(`SELECT k FROM ${table} ORDER BY k`).all();
369
+ const out = [];
370
+ for (const row of rows) {
371
+ const key = logicalKey(row.k);
372
+ if (key?.startsWith(validatedPrefix)) out.push(key);
373
+ }
374
+ return out.sort();
375
+ },
376
+ close() {
377
+ db.close();
378
+ }
379
+ };
380
+ }
381
+ function sqliteKv(path, opts = {}) {
382
+ const backend = sqliteBackend(path, opts);
383
+ const tier = kvStorage({
384
+ backend,
385
+ codec: opts.codec
386
+ });
387
+ return Object.assign(tier, { close: () => backend.close() });
388
+ }
389
+ function sqliteAppendLog(path, opts = {}) {
390
+ const kv = sqliteKv(path, opts);
391
+ const log = appendLogStorage({ kv, prefix: opts.prefix ?? "event-log" });
392
+ return Object.assign(log, { close: () => kv.close() });
393
+ }
394
+ export {
395
+ fileAppendLog,
396
+ fileBackend,
397
+ fileKv,
398
+ sqliteAppendLog,
399
+ sqliteBackend,
400
+ sqliteKv
401
+ };
402
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/storage/node.ts"],"sourcesContent":["/**\n * Node-only passive storage backends (D103).\n *\n * Import from `@graphrefly/ts/storage/node`; the universal storage barrel stays browser-safe.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n\tcloseSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\treadFileSync,\n\trenameSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { basename, dirname, join } from \"node:path\";\nimport { type AppendLogStorageTier, appendLogStorage } from \"./append-log.js\";\nimport type { StorageBackend, StorageGeneration, StorageNamespaceOptions } from \"./backend.js\";\nimport type { Codec } from \"./codec.js\";\nimport { type KvStorageTier, kvStorage } from \"./kv.js\";\n\nexport interface FileBackendOptions extends StorageNamespaceOptions {\n\t/** File suffix used for stored byte blobs. Defaults to `.bin`. */\n\textension?: string;\n}\n\nexport interface FileKvOptions<T> extends FileBackendOptions {\n\tcodec?: Codec<T>;\n}\n\nexport interface FileAppendLogOptions<T> extends FileKvOptions<T> {\n\t/** Append-log key prefix. Defaults to `event-log`. */\n\tprefix?: string;\n}\n\nexport interface SqliteBackendOptions extends StorageNamespaceOptions {\n\t/** Table name used for passive byte storage. Defaults to `graphrefly_storage`. */\n\ttableName?: string;\n}\n\nexport interface SqliteKvOptions<T> extends SqliteBackendOptions {\n\tcodec?: Codec<T>;\n}\n\nexport interface SqliteAppendLogOptions<T> extends SqliteKvOptions<T> {\n\t/** Append-log key prefix. Defaults to `event-log`. */\n\tprefix?: string;\n}\n\nexport interface ClosableStorageBackend extends StorageBackend {\n\treadonly name: string;\n\tclose(): void;\n}\n\ntype SqliteDatabaseConstructor = new (path: string) => SqliteDatabase;\n\ninterface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\tclose(): void;\n}\n\ninterface SqliteStatement {\n\tget(...params: unknown[]): unknown;\n\trun(...params: unknown[]): unknown;\n\tall(...params: unknown[]): unknown[];\n}\n\nconst FILE_STEM_PREFIX = \"k-\";\nconst NAMESPACE_SEPARATOR = \"\\u0000\";\nconst SQLITE_GENERATION = Symbol(\"graphrefly.sqliteBackend.generation\");\nconst encoder = new TextEncoder();\nconst decoder = new TextDecoder(\"utf-8\", { fatal: true });\nconst require = createRequire(\"/graphrefly-storage-node.js\");\n\ninterface SqliteGeneration {\n\treadonly [SQLITE_GENERATION]: readonly [\n\t\tstorageId: object,\n\t\tkey: string,\n\t\trowGeneration: number | null,\n\t\tepoch: number,\n\t];\n}\n\nfunction isErrno(error: unknown, code: string): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\t(error as { code?: unknown }).code === code\n\t);\n}\n\nfunction keyToStem(key: string): string {\n\tlet out = \"\";\n\tfor (const ch of key) {\n\t\tif (/^[a-zA-Z0-9_-]$/.test(ch)) {\n\t\t\tout += ch;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const byte of encoder.encode(ch)) out += `%${byte.toString(16).padStart(2, \"0\")}`;\n\t}\n\treturn out;\n}\n\nfunction stemToKey(stem: string): string | null {\n\tconst bytes: number[] = [];\n\tlet i = 0;\n\twhile (i < stem.length) {\n\t\tconst ch = stem[i]!;\n\t\tif (ch === \"%\" && i + 2 < stem.length) {\n\t\t\tconst hex = stem.slice(i + 1, i + 3);\n\t\t\tif (/^[0-9a-f]{2}$/i.test(hex)) {\n\t\t\t\tbytes.push(Number.parseInt(hex, 16));\n\t\t\t\ti += 3;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tconst code = ch.charCodeAt(0);\n\t\tif (code > 0x7f) return null;\n\t\tbytes.push(code);\n\t\ti += 1;\n\t}\n\ttry {\n\t\treturn decoder.decode(new Uint8Array(bytes));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction validateExtension(extension: string): string {\n\tif (\n\t\textension.length < 2 ||\n\t\t!extension.startsWith(\".\") ||\n\t\textension.includes(\"..\") ||\n\t\t/[/\\\\\\0]/.test(extension) ||\n\t\t!/^[.A-Za-z0-9_-]+$/.test(extension)\n\t) {\n\t\tthrow new TypeError(\"fileBackend: extension must be a simple suffix such as .bin\");\n\t}\n\treturn extension;\n}\n\nfunction validateNamespace(label: string, namespace: string): string {\n\tif (typeof namespace !== \"string\") {\n\t\tthrow new TypeError(`${label}: namespace must be a string`);\n\t}\n\tif (namespace.includes(NAMESPACE_SEPARATOR)) {\n\t\tthrow new TypeError(`${label}: namespace must not contain U+0000`);\n\t}\n\treturn namespace;\n}\n\nfunction validateLogicalKey(label: string, key: string): string {\n\tif (typeof key !== \"string\") {\n\t\tthrow new TypeError(`${label}: key must be a string`);\n\t}\n\tif (key.includes(NAMESPACE_SEPARATOR)) {\n\t\tthrow new TypeError(`${label}: key must not contain U+0000`);\n\t}\n\treturn key;\n}\n\nfunction validateListPrefix(label: string, prefix: string): string {\n\tif (typeof prefix !== \"string\") {\n\t\tthrow new TypeError(`${label}: list prefix must be a string`);\n\t}\n\tif (prefix.includes(NAMESPACE_SEPARATOR)) {\n\t\tthrow new TypeError(`${label}: list prefix must not contain U+0000`);\n\t}\n\treturn prefix;\n}\n\nfunction validateSqliteTableName(tableName: string): string {\n\tif (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(tableName)) {\n\t\tthrow new TypeError(\"sqliteBackend: tableName must be a simple SQLite identifier\");\n\t}\n\treturn tableName;\n}\n\nfunction sqliteGeneration(\n\tstorageId: object,\n\tkey: string,\n\trowGeneration: number | null,\n\tepoch: number,\n): SqliteGeneration {\n\treturn Object.freeze({\n\t\t[SQLITE_GENERATION]: Object.freeze([storageId, key, rowGeneration, epoch] as const),\n\t});\n}\n\nfunction readSqliteGeneration(\n\tgeneration: StorageGeneration,\n):\n\t| readonly [storageId: object, key: string, rowGeneration: number | null, epoch: number]\n\t| undefined {\n\tif (typeof generation !== \"object\" || generation === null) return undefined;\n\tconst maybe = generation as Partial<SqliteGeneration>;\n\tconst token = maybe[SQLITE_GENERATION];\n\tif (\n\t\tArray.isArray(token) &&\n\t\ttoken.length === 4 &&\n\t\ttypeof token[0] === \"object\" &&\n\t\ttoken[0] !== null &&\n\t\ttypeof token[1] === \"string\" &&\n\t\t(token[2] === null || typeof token[2] === \"number\") &&\n\t\ttypeof token[3] === \"number\"\n\t) {\n\t\treturn token as readonly [object, string, number | null, number];\n\t}\n\treturn undefined;\n}\n\nfunction sqliteChanges(result: unknown): number {\n\treturn typeof result === \"object\" &&\n\t\tresult !== null &&\n\t\t\"changes\" in result &&\n\t\ttypeof (result as { changes?: unknown }).changes === \"number\"\n\t\t? (result as { changes: number }).changes\n\t\t: 0;\n}\n\nfunction sqliteBytes(value: Uint8Array | ArrayBuffer): Uint8Array {\n\treturn Uint8Array.from(value instanceof Uint8Array ? value : new Uint8Array(value));\n}\n\nfunction loadSqliteDatabaseSync(): SqliteDatabaseConstructor {\n\ttry {\n\t\tconst DatabaseSync = (require(\"node:sqlite\") as { DatabaseSync?: unknown }).DatabaseSync;\n\t\tif (typeof DatabaseSync !== \"function\") {\n\t\t\tthrow new TypeError(\"DatabaseSync export is missing\");\n\t\t}\n\t\treturn DatabaseSync as SqliteDatabaseConstructor;\n\t} catch (error) {\n\t\tthrow new Error(\"sqliteBackend: node:sqlite is not available in this Node runtime\", {\n\t\t\tcause: error,\n\t\t});\n\t}\n}\n\n/** Node filesystem byte backend with atomic replace writes and D85 conditional create. */\nexport function fileBackend(dir: string, opts: FileBackendOptions = {}): StorageBackend {\n\tconst extension = validateExtension(opts.extension ?? \".bin\");\n\tconst namespace =\n\t\topts.namespace === undefined ? \"\" : validateNamespace(\"fileBackend\", opts.namespace);\n\tconst namespacePrefix = namespace.length > 0 ? `${namespace}${NAMESPACE_SEPARATOR}` : \"\";\n\tconst storageKey = (key: string) => `${namespacePrefix}${validateLogicalKey(\"fileBackend\", key)}`;\n\tconst pathFor = (key: string) =>\n\t\tjoin(dir, `${FILE_STEM_PREFIX}${keyToStem(storageKey(key))}${extension}`);\n\tconst keyFromFilename = (filename: string): string | null => {\n\t\tif (!filename.endsWith(extension)) return null;\n\t\tconst stem = filename.slice(0, -extension.length);\n\t\tif (!stem.startsWith(FILE_STEM_PREFIX)) return null;\n\t\tconst key = stemToKey(stem.slice(FILE_STEM_PREFIX.length));\n\t\tif (key === null || !key.startsWith(namespacePrefix)) return null;\n\t\tconst logical = key.slice(namespacePrefix.length);\n\t\tif (logical.includes(NAMESPACE_SEPARATOR)) {\n\t\t\tthrow new TypeError(\"fileBackend: malformed stored key\");\n\t\t}\n\t\treturn logical;\n\t};\n\n\treturn {\n\t\tget(key) {\n\t\t\ttry {\n\t\t\t\treturn Uint8Array.from(readFileSync(pathFor(key)));\n\t\t\t} catch (error) {\n\t\t\t\tif (isErrno(error, \"ENOENT\")) return undefined;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tput(key, value) {\n\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\tconst filePath = pathFor(key);\n\t\t\tconst base = basename(filePath);\n\t\t\tconst parent = dirname(filePath);\n\t\t\tconst tmp = join(parent, `.${base}.${randomBytes(8).toString(\"hex\")}.tmp`);\n\t\t\ttry {\n\t\t\t\twriteFileSync(tmp, value);\n\t\t\t\trenameSync(tmp, filePath);\n\t\t\t} catch (error) {\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(tmp);\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore cleanup failures; the original write error is the useful one.\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tputIfAbsent(key, value) {\n\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\tconst filePath = pathFor(key);\n\t\t\tlet fd: number | undefined;\n\t\t\ttry {\n\t\t\t\tfd = openSync(filePath, \"wx\");\n\t\t\t\twriteFileSync(fd, value);\n\t\t\t\treturn true;\n\t\t\t} catch (error) {\n\t\t\t\tif (isErrno(error, \"EEXIST\")) return false;\n\t\t\t\tif (fd !== undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tunlinkSync(filePath);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Best effort cleanup of a partially-created file.\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tif (fd !== undefined) closeSync(fd);\n\t\t\t}\n\t\t},\n\t\tdelete(key) {\n\t\t\ttry {\n\t\t\t\tunlinkSync(pathFor(key));\n\t\t\t} catch (error) {\n\t\t\t\tif (!isErrno(error, \"ENOENT\")) throw error;\n\t\t\t}\n\t\t},\n\t\tlist(prefix = \"\") {\n\t\t\tconst validatedPrefix = validateListPrefix(\"fileBackend\", prefix);\n\t\t\tlet entries: string[];\n\t\t\ttry {\n\t\t\t\tentries = readdirSync(dir);\n\t\t\t} catch (error) {\n\t\t\t\tif (isErrno(error, \"ENOENT\")) return [];\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst keys: string[] = [];\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (entry.startsWith(\".\")) continue;\n\t\t\t\tconst key = keyFromFilename(entry);\n\t\t\t\tif (key?.startsWith(validatedPrefix)) keys.push(key);\n\t\t\t}\n\t\t\treturn keys.sort();\n\t\t},\n\t};\n}\n\n/**\n * Create a typed KV tier over a Node filesystem backend (D106 passive storage).\n */\nexport function fileKv<T>(dir: string, opts: FileKvOptions<T> = {}): KvStorageTier<T> {\n\treturn kvStorage<T>({\n\t\tbackend: fileBackend(dir, opts),\n\t\tcodec: opts.codec,\n\t});\n}\n\n/**\n * Create an append-log tier over a Node filesystem backend (D106 passive storage).\n */\nexport function fileAppendLog<T>(\n\tdir: string,\n\topts: FileAppendLogOptions<T> = {},\n): AppendLogStorageTier<T> {\n\treturn appendLogStorage<T>({\n\t\tkv: fileKv<T>(dir, opts),\n\t\tprefix: opts.prefix ?? \"event-log\",\n\t});\n}\n\n/**\n * Optional Node SQLite byte backend (D106 passive storage).\n *\n * Uses `node:sqlite` when the current Node runtime provides it; otherwise throws a clear\n * runtime error at construction time. The caller owns `close()`.\n */\nexport function sqliteBackend(\n\tpath: string,\n\topts: SqliteBackendOptions = {},\n): ClosableStorageBackend {\n\tconst table = validateSqliteTableName(opts.tableName ?? \"graphrefly_storage\");\n\tconst metaTable = validateSqliteTableName(`${table}_meta`);\n\tconst namespace =\n\t\topts.namespace === undefined ? \"\" : validateNamespace(\"sqliteBackend\", opts.namespace);\n\tconst DatabaseSync = loadSqliteDatabaseSync();\n\tconst namespacePrefix = namespace.length > 0 ? `${namespace}${NAMESPACE_SEPARATOR}` : \"\";\n\tconst storageKey = (key: string) =>\n\t\t`${namespacePrefix}${validateLogicalKey(\"sqliteBackend\", key)}`;\n\tconst storageId = Object.freeze({});\n\tconst logicalKey = (key: string): string | undefined => {\n\t\tif (typeof key !== \"string\") {\n\t\t\tthrow new TypeError(\"sqliteBackend: stored key is corrupt\");\n\t\t}\n\t\tif (!key.startsWith(namespacePrefix)) return undefined;\n\t\tconst logical = key.slice(namespacePrefix.length);\n\t\tif (logical.includes(NAMESPACE_SEPARATOR)) {\n\t\t\tthrow new TypeError(\"sqliteBackend: malformed stored key\");\n\t\t}\n\t\treturn logical;\n\t};\n\tconst db = new DatabaseSync(path);\n\tdb.exec(\n\t\t`CREATE TABLE IF NOT EXISTS ${table} (k TEXT PRIMARY KEY, v BLOB NOT NULL, g INTEGER NOT NULL DEFAULT 0)`,\n\t);\n\tconst columns = db.prepare(`PRAGMA table_info(${table})`).all() as Array<{ name?: string }>;\n\tif (!columns.some((column) => column.name === \"g\")) {\n\t\tdb.exec(`ALTER TABLE ${table} ADD COLUMN g INTEGER NOT NULL DEFAULT 0`);\n\t}\n\tdb.exec(`CREATE TABLE IF NOT EXISTS ${metaTable} (k TEXT PRIMARY KEY, v INTEGER NOT NULL)`);\n\tdb.exec(`\n\t\tCREATE TRIGGER IF NOT EXISTS ${table}_generation_insert\n\t\tAFTER INSERT ON ${table}\n\t\tBEGIN\n\t\t\tINSERT INTO ${metaTable} (k, v) VALUES (NEW.k, 1)\n\t\t\tON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;\n\t\tEND\n\t`);\n\tdb.exec(`\n\t\tCREATE TRIGGER IF NOT EXISTS ${table}_generation_update\n\t\tAFTER UPDATE ON ${table}\n\t\tBEGIN\n\t\t\tINSERT INTO ${metaTable} (k, v) VALUES (NEW.k, 1)\n\t\t\tON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;\n\t\tEND\n\t`);\n\tdb.exec(`\n\t\tCREATE TRIGGER IF NOT EXISTS ${table}_generation_delete\n\t\tAFTER DELETE ON ${table}\n\t\tBEGIN\n\t\t\tINSERT INTO ${metaTable} (k, v) VALUES (OLD.k, 1)\n\t\t\tON CONFLICT(k) DO UPDATE SET v = ${metaTable}.v + 1;\n\t\tEND\n\t`);\n\n\tconst keyEpoch = (key: string): number => {\n\t\tconst row = db.prepare(`SELECT v FROM ${metaTable} WHERE k = ?`).get(key) as\n\t\t\t| { v?: number }\n\t\t\t| undefined;\n\t\tconst value = row?.v ?? 0;\n\t\tif (typeof value !== \"number\" || !Number.isSafeInteger(value) || value < 0) {\n\t\t\tthrow new Error(\"sqliteBackend: per-key generation epoch is corrupt\");\n\t\t}\n\t\treturn value;\n\t};\n\tconst readRow = (\n\t\tkey: string,\n\t):\n\t\t| {\n\t\t\t\treadonly value: Uint8Array;\n\t\t\t\treadonly generation: number;\n\t\t }\n\t\t| undefined => {\n\t\tconst row = db.prepare(`SELECT v, g FROM ${table} WHERE k = ?`).get(storageKey(key)) as\n\t\t\t| { v?: Uint8Array | ArrayBuffer; g?: number }\n\t\t\t| undefined;\n\t\tif (row?.v === undefined) return undefined;\n\t\tconst generation = row.g;\n\t\tif (typeof generation !== \"number\" || !Number.isSafeInteger(generation) || generation < 0) {\n\t\t\tthrow new Error(\"sqliteBackend: row generation is corrupt\");\n\t\t}\n\t\treturn { value: sqliteBytes(row.v), generation };\n\t};\n\n\treturn {\n\t\tname: `sqlite:${path}/${table}`,\n\t\tget(key) {\n\t\t\treturn readRow(key)?.value;\n\t\t},\n\t\tput(key, value) {\n\t\t\tdb.prepare(\n\t\t\t\t`INSERT INTO ${table} (k, v, g) VALUES (?, ?, 0) ON CONFLICT(k) DO UPDATE SET v = excluded.v, g = ${table}.g + 1`,\n\t\t\t).run(storageKey(key), value);\n\t\t},\n\t\tputIfAbsent(key, value) {\n\t\t\tconst result = db\n\t\t\t\t.prepare(`INSERT OR IGNORE INTO ${table} (k, v, g) VALUES (?, ?, 0)`)\n\t\t\t\t.run(storageKey(key), value) as { changes?: number };\n\t\t\treturn sqliteChanges(result) === 1;\n\t\t},\n\t\tgetVersioned(key) {\n\t\t\tconst row = readRow(key);\n\t\t\tif (row === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"miss\",\n\t\t\t\t\tgeneration: sqliteGeneration(storageId, storageKey(key), null, keyEpoch(storageKey(key))),\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tkind: \"hit\",\n\t\t\t\tvalue: row.value,\n\t\t\t\tgeneration: sqliteGeneration(\n\t\t\t\t\tstorageId,\n\t\t\t\t\tstorageKey(key),\n\t\t\t\t\trow.generation,\n\t\t\t\t\tkeyEpoch(storageKey(key)),\n\t\t\t\t),\n\t\t\t};\n\t\t},\n\t\tsetIfMatch(key, value, generation) {\n\t\t\tconst keyForStorage = storageKey(key);\n\t\t\tconst observed = readSqliteGeneration(generation);\n\t\t\tif (observed === undefined || observed[0] !== storageId || observed[1] !== keyForStorage) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (observed[2] === null) {\n\t\t\t\treturn (\n\t\t\t\t\tsqliteChanges(\n\t\t\t\t\t\tdb\n\t\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t\t`INSERT INTO ${table} (k, v, g) SELECT ?, ?, 0 WHERE COALESCE((SELECT v FROM ${metaTable} WHERE k = ?), 0) = ? AND NOT EXISTS (SELECT 1 FROM ${table} WHERE k = ?)`,\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.run(keyForStorage, value, keyForStorage, observed[3], keyForStorage),\n\t\t\t\t\t) === 1\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (\n\t\t\t\tsqliteChanges(\n\t\t\t\t\tdb\n\t\t\t\t\t\t.prepare(\n\t\t\t\t\t\t\t`UPDATE ${table} SET v = ?, g = g + 1 WHERE k = ? AND g = ? AND COALESCE((SELECT v FROM ${metaTable} WHERE k = ?), 0) = ?`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.run(value, keyForStorage, observed[2], keyForStorage, observed[3]),\n\t\t\t\t) === 1\n\t\t\t);\n\t\t},\n\t\tdelete(key) {\n\t\t\tdb.prepare(`DELETE FROM ${table} WHERE k = ?`).run(storageKey(key));\n\t\t},\n\t\tlist(prefix = \"\") {\n\t\t\tconst validatedPrefix = validateListPrefix(\"sqliteBackend\", prefix);\n\t\t\tconst rows = db.prepare(`SELECT k FROM ${table} ORDER BY k`).all() as { k: string }[];\n\t\t\tconst out: string[] = [];\n\t\t\tfor (const row of rows) {\n\t\t\t\tconst key = logicalKey(row.k);\n\t\t\t\tif (key?.startsWith(validatedPrefix)) out.push(key);\n\t\t\t}\n\t\t\treturn out.sort();\n\t\t},\n\t\tclose() {\n\t\t\tdb.close();\n\t\t},\n\t};\n}\n\n/**\n * Create a typed KV tier over an optional Node SQLite backend.\n */\nexport function sqliteKv<T>(\n\tpath: string,\n\topts: SqliteKvOptions<T> = {},\n): KvStorageTier<T> & { close(): void } {\n\tconst backend = sqliteBackend(path, opts);\n\tconst tier = kvStorage<T>({\n\t\tbackend,\n\t\tcodec: opts.codec,\n\t});\n\treturn Object.assign(tier, { close: () => backend.close() });\n}\n\n/**\n * Create an append-log tier over an optional Node SQLite backend.\n */\nexport function sqliteAppendLog<T>(\n\tpath: string,\n\topts: SqliteAppendLogOptions<T> = {},\n): AppendLogStorageTier<T> & { close(): void } {\n\tconst kv = sqliteKv<T>(path, opts);\n\tconst log = appendLogStorage<T>({ kv, prefix: opts.prefix ?? \"event-log\" });\n\treturn Object.assign(log, { close: () => kv.close() });\n}\n"],"mappings":"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","names":["require"]}
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/testing/index.ts
21
+ var testing_exports = {};
22
+ __export(testing_exports, {
23
+ assertDirtyPrecedesTerminalData: () => assertDirtyPrecedesTerminalData
24
+ });
25
+ module.exports = __toCommonJS(testing_exports);
26
+
27
+ // src/protocol/messages.ts
28
+ var TIER_START = 0;
29
+ var TIER_CONTROL = 1;
30
+ var TIER_NOTIFICATION = 2;
31
+ var TIER_VALUE = 3;
32
+ var TIER_SETTLE = 4;
33
+ var TIER_TERMINAL = 5;
34
+ var TIER_TEARDOWN = 6;
35
+ var TIER = {
36
+ START: TIER_START,
37
+ PAUSE: TIER_CONTROL,
38
+ RESUME: TIER_CONTROL,
39
+ DIRTY: TIER_NOTIFICATION,
40
+ DATA: TIER_VALUE,
41
+ RESOLVED: TIER_VALUE,
42
+ INVALIDATE: TIER_SETTLE,
43
+ COMPLETE: TIER_TERMINAL,
44
+ ERROR: TIER_TERMINAL,
45
+ TEARDOWN: TIER_TEARDOWN
46
+ };
47
+ function isValueTier(t) {
48
+ return TIER[t] === TIER_VALUE;
49
+ }
50
+
51
+ // src/testing/assertions.ts
52
+ function assertDirtyPrecedesTerminalData(messages) {
53
+ for (let waveIndex = 0; waveIndex < messages.length; waveIndex += 1) {
54
+ const wave = messages[waveIndex];
55
+ let handshake = false;
56
+ let openedByDirty = false;
57
+ for (let msgIndex = 0; msgIndex < wave.length; msgIndex += 1) {
58
+ const msg = wave[msgIndex];
59
+ const type = msg[0];
60
+ if (msgIndex === 0 && type === "START") {
61
+ handshake = true;
62
+ continue;
63
+ }
64
+ if (handshake && type === "DATA") {
65
+ continue;
66
+ }
67
+ handshake = false;
68
+ if (type === "DIRTY") {
69
+ openedByDirty = true;
70
+ continue;
71
+ }
72
+ if (isValueTier(type)) {
73
+ if (!openedByDirty) {
74
+ throw new Error(
75
+ `assertDirtyPrecedesTerminalData: ${type} at wave ${waveIndex}, index ${msgIndex} arrived without a preceding DIRTY in the same wave (R-dirty-before-data). Wave: ${describeWave(wave, msgIndex)}`
76
+ );
77
+ }
78
+ continue;
79
+ }
80
+ openedByDirty = false;
81
+ }
82
+ }
83
+ }
84
+ function describeWave(wave, around) {
85
+ const parts = [];
86
+ for (let i = 0; i < wave.length; i += 1) {
87
+ parts.push(`${i === around ? "-> " : " "}[${wave[i]?.[0] ?? "?"}]`);
88
+ }
89
+ return parts.join(", ");
90
+ }
91
+ // Annotate the CommonJS export names for ESM import in node:
92
+ 0 && (module.exports = {
93
+ assertDirtyPrecedesTerminalData
94
+ });
95
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/testing/index.ts","../../src/protocol/messages.ts","../../src/testing/assertions.ts"],"sourcesContent":["export {\n\tassertDirtyPrecedesTerminalData,\n\ttype MessageSequence,\n} from \"./assertions.js\";\n","/**\n * Wave-protocol message types and the tier const table.\n *\n * Canonical authority: ~/src/graphrefly/spec/rules.jsonl\n * R-msg-format, R-msg-closed-set, R-tier (D34), R-data-payload, R-sentinel.\n */\n\n/** Opaque pause-lock identifier (R-pause-lockset). */\nexport type LockId = string | symbol;\n\n/**\n * The CLOSED set of 10 message types (R-msg-closed-set / D9 + START handshake).\n * No open set, no user-defined custom types. Adding a type is a spec change.\n */\nexport type Message =\n\t| readonly [\"START\"]\n\t| readonly [\"DIRTY\"]\n\t| readonly [\"DATA\", unknown]\n\t| readonly [\"RESOLVED\"]\n\t| readonly [\"INVALIDATE\"]\n\t| readonly [\"COMPLETE\"]\n\t| readonly [\"ERROR\", unknown]\n\t| readonly [\"TEARDOWN\"]\n\t| readonly [\"PAUSE\", LockId]\n\t| readonly [\"RESUME\", LockId];\n\n/** One array of messages delivered in one call = one Wave (R-wave-boundary). */\nexport type Wave = readonly Message[];\n\n/** The message-type tag (the first tuple element). */\nexport type MessageType = Message[0];\n\n/**\n * SENTINEL = absence-of-DATA (R-sentinel / D16). TS representation is `undefined`.\n * Never a valid DATA payload; `null` IS a valid DATA value (domain-level absence).\n */\nexport const SENTINEL = undefined;\n\n/** R-data-payload: ERROR payloads must not collide with terminal metadata shorthand. */\nexport function isInvalidErrorPayload(v: unknown): boolean {\n\treturn v === SENTINEL || typeof v === \"boolean\";\n}\n\n/** R-data-payload / D78: host-language failures that collide with ctx.terminal shorthand become diagnostics. */\nexport function errorPayload(reason: unknown, fallback = \"error without a valid payload\"): unknown {\n\treturn isInvalidErrorPayload(reason) ? new Error(fallback) : reason;\n}\n\nexport const TIER_START = 0;\nexport const TIER_CONTROL = 1;\nexport const TIER_NOTIFICATION = 2;\nexport const TIER_VALUE = 3;\nexport const TIER_SETTLE = 4;\nexport const TIER_TERMINAL = 5;\nexport const TIER_TEARDOWN = 6;\n\n/**\n * Tier const table (R-tier / D34). Tiers below value dispatch immediately; value\n * and above are batch-deferred. This is a compile-time const table (D18), not runtime config.\n */\nconst TIER: Record<MessageType, number> = {\n\tSTART: TIER_START,\n\tPAUSE: TIER_CONTROL,\n\tRESUME: TIER_CONTROL,\n\tDIRTY: TIER_NOTIFICATION,\n\tDATA: TIER_VALUE,\n\tRESOLVED: TIER_VALUE,\n\tINVALIDATE: TIER_SETTLE,\n\tCOMPLETE: TIER_TERMINAL,\n\tERROR: TIER_TERMINAL,\n\tTEARDOWN: TIER_TEARDOWN,\n};\n\nexport function messageTier(t: MessageType): number {\n\treturn TIER[t];\n}\n\n/** Value and above are deferred inside a batch (DATA/RESOLVED/INVALIDATE/terminal/teardown). */\nexport function isDeferredTier(t: MessageType): boolean {\n\treturn TIER[t] >= TIER_VALUE;\n}\n\n/** Value-tier messages (DATA/RESOLVED) occupy the tier-3 slot. */\nexport function isValueTier(t: MessageType): boolean {\n\treturn TIER[t] === TIER_VALUE;\n}\n\n/** The pause buffer holds the settle slice only: value-tier DATA/RESOLVED plus INVALIDATE. */\nexport function isPauseBufferedTier(t: MessageType): boolean {\n\tconst tier = TIER[t];\n\treturn tier === TIER_VALUE || tier === TIER_SETTLE;\n}\n\n/**\n * A TERMINAL message = tier 5 (COMPLETE | ERROR), R-tier / D34. Detected via the CENTRAL tier\n * table, NOT a per-variant `=== \"COMPLETE\" || === \"ERROR\"` check — so terminal routing stays\n * driven by the one const table (feedback_use_tier_for_signal_routing); discriminate COMPLETE vs\n * ERROR within the tier by the message type only where the handling actually differs.\n */\nexport function isTerminal(t: MessageType): boolean {\n\treturn TIER[t] === TIER_TERMINAL;\n}\n\n/**\n * ctx.up carries control tiers only (R-ctx-up / DR-5): DIRTY, PAUSE, RESUME,\n * INVALIDATE, TEARDOWN. DATA/RESOLVED (tier 3) and COMPLETE/ERROR (tier 5) are\n * down-only.\n */\nexport function isUpAllowed(t: MessageType): boolean {\n\tconst tier = TIER[t];\n\treturn tier !== TIER_VALUE && tier !== TIER_TERMINAL;\n}\n","/**\n * Public testing assertions for clean-slate GraphReFly message streams.\n *\n * These helpers validate captured protocol output; they do not participate in propagation.\n */\n\nimport { isValueTier, type Message, type Wave } from \"../protocol/messages.js\";\n\n/** Captured subscriber messages grouped by wave. START/replay handshake messages occupy one wave. */\nexport type MessageSequence = readonly Wave[];\n\n/**\n * Assert that observed tier-3 settlements (DATA/RESOLVED) are preceded by DIRTY.\n *\n * This encodes R-dirty-before-data for wave-framed subscriber traces. The leading\n * START push-on-subscribe/replay handshake wave is exempt because cached/replay DATA is\n * advertised to a new subscriber without opening a propagation wave.\n */\nexport function assertDirtyPrecedesTerminalData(messages: MessageSequence): void {\n\tfor (let waveIndex = 0; waveIndex < messages.length; waveIndex += 1) {\n\t\tconst wave = messages[waveIndex];\n\t\tlet handshake = false;\n\t\tlet openedByDirty = false;\n\n\t\tfor (let msgIndex = 0; msgIndex < wave.length; msgIndex += 1) {\n\t\t\tconst msg = wave[msgIndex] as Message;\n\t\t\tconst type = msg[0];\n\n\t\t\tif (msgIndex === 0 && type === \"START\") {\n\t\t\t\thandshake = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (handshake && type === \"DATA\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\thandshake = false;\n\n\t\t\tif (type === \"DIRTY\") {\n\t\t\t\topenedByDirty = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (isValueTier(type)) {\n\t\t\t\tif (!openedByDirty) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`assertDirtyPrecedesTerminalData: ${type} at wave ${waveIndex}, index ${msgIndex} arrived without a preceding DIRTY in the same wave (R-dirty-before-data). Wave: ${describeWave(wave, msgIndex)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\topenedByDirty = false;\n\t\t}\n\t}\n}\n\nfunction describeWave(wave: Wave, around: number): string {\n\tconst parts: string[] = [];\n\tfor (let i = 0; i < wave.length; i += 1) {\n\t\tparts.push(`${i === around ? \"-> \" : \" \"}[${wave[i]?.[0] ?? \"?\"}]`);\n\t}\n\treturn parts.join(\", \");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgDO,IAAM,aAAa;AACnB,IAAM,eAAe;AACrB,IAAM,oBAAoB;AAC1B,IAAM,aAAa;AACnB,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAM7B,IAAM,OAAoC;AAAA,EACzC,OAAO;AAAA,EACP,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM;AAAA,EACN,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,OAAO;AAAA,EACP,UAAU;AACX;AAYO,SAAS,YAAY,GAAyB;AACpD,SAAO,KAAK,CAAC,MAAM;AACpB;;;ACnEO,SAAS,gCAAgC,UAAiC;AAChF,WAAS,YAAY,GAAG,YAAY,SAAS,QAAQ,aAAa,GAAG;AACpE,UAAM,OAAO,SAAS,SAAS;AAC/B,QAAI,YAAY;AAChB,QAAI,gBAAgB;AAEpB,aAAS,WAAW,GAAG,WAAW,KAAK,QAAQ,YAAY,GAAG;AAC7D,YAAM,MAAM,KAAK,QAAQ;AACzB,YAAM,OAAO,IAAI,CAAC;AAElB,UAAI,aAAa,KAAK,SAAS,SAAS;AACvC,oBAAY;AACZ;AAAA,MACD;AAEA,UAAI,aAAa,SAAS,QAAQ;AACjC;AAAA,MACD;AACA,kBAAY;AAEZ,UAAI,SAAS,SAAS;AACrB,wBAAgB;AAChB;AAAA,MACD;AAEA,UAAI,YAAY,IAAI,GAAG;AACtB,YAAI,CAAC,eAAe;AACnB,gBAAM,IAAI;AAAA,YACT,oCAAoC,IAAI,YAAY,SAAS,WAAW,QAAQ,oFAAoF,aAAa,MAAM,QAAQ,CAAC;AAAA,UACjM;AAAA,QACD;AACA;AAAA,MACD;AAEA,sBAAgB;AAAA,IACjB;AAAA,EACD;AACD;AAEA,SAAS,aAAa,MAAY,QAAwB;AACzD,QAAM,QAAkB,CAAC;AACzB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACxC,UAAM,KAAK,GAAG,MAAM,SAAS,QAAQ,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG;AAAA,EACrE;AACA,SAAO,MAAM,KAAK,IAAI;AACvB;","names":[]}
@@ -0,0 +1,20 @@
1
+ import { W as Wave } from '../messages-BZqrGGm1.cjs';
2
+
3
+ /**
4
+ * Public testing assertions for clean-slate GraphReFly message streams.
5
+ *
6
+ * These helpers validate captured protocol output; they do not participate in propagation.
7
+ */
8
+
9
+ /** Captured subscriber messages grouped by wave. START/replay handshake messages occupy one wave. */
10
+ type MessageSequence = readonly Wave[];
11
+ /**
12
+ * Assert that observed tier-3 settlements (DATA/RESOLVED) are preceded by DIRTY.
13
+ *
14
+ * This encodes R-dirty-before-data for wave-framed subscriber traces. The leading
15
+ * START push-on-subscribe/replay handshake wave is exempt because cached/replay DATA is
16
+ * advertised to a new subscriber without opening a propagation wave.
17
+ */
18
+ declare function assertDirtyPrecedesTerminalData(messages: MessageSequence): void;
19
+
20
+ export { type MessageSequence, assertDirtyPrecedesTerminalData };
@@ -0,0 +1,20 @@
1
+ import { W as Wave } from '../messages-BZqrGGm1.js';
2
+
3
+ /**
4
+ * Public testing assertions for clean-slate GraphReFly message streams.
5
+ *
6
+ * These helpers validate captured protocol output; they do not participate in propagation.
7
+ */
8
+
9
+ /** Captured subscriber messages grouped by wave. START/replay handshake messages occupy one wave. */
10
+ type MessageSequence = readonly Wave[];
11
+ /**
12
+ * Assert that observed tier-3 settlements (DATA/RESOLVED) are preceded by DIRTY.
13
+ *
14
+ * This encodes R-dirty-before-data for wave-framed subscriber traces. The leading
15
+ * START push-on-subscribe/replay handshake wave is exempt because cached/replay DATA is
16
+ * advertised to a new subscriber without opening a propagation wave.
17
+ */
18
+ declare function assertDirtyPrecedesTerminalData(messages: MessageSequence): void;
19
+
20
+ export { type MessageSequence, assertDirtyPrecedesTerminalData };
@@ -0,0 +1,8 @@
1
+ import {
2
+ assertDirtyPrecedesTerminalData
3
+ } from "../chunk-TEW47IVR.js";
4
+ import "../chunk-DK554R7P.js";
5
+ export {
6
+ assertDirtyPrecedesTerminalData
7
+ };
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}