@ontrails/store 1.0.0-beta.14 → 1.0.0-beta.15
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.
- package/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +126 -61
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/signal-identity.d.ts +17 -0
- package/dist/internal/signal-identity.d.ts.map +1 -0
- package/dist/internal/signal-identity.js +81 -0
- package/dist/internal/signal-identity.js.map +1 -0
- package/dist/jsonfile/index.d.ts +3 -0
- package/dist/jsonfile/index.d.ts.map +1 -0
- package/dist/jsonfile/index.js +2 -0
- package/dist/jsonfile/index.js.map +1 -0
- package/dist/jsonfile/runtime.d.ts +19 -0
- package/dist/jsonfile/runtime.d.ts.map +1 -0
- package/dist/jsonfile/runtime.js +403 -0
- package/dist/jsonfile/runtime.js.map +1 -0
- package/dist/jsonfile/types.d.ts +40 -0
- package/dist/jsonfile/types.d.ts.map +1 -0
- package/dist/{drizzle → jsonfile}/types.js.map +1 -1
- package/dist/store.d.ts +8 -18
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +152 -94
- package/dist/store.js.map +1 -1
- package/dist/testing.d.ts +28 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +90 -0
- package/dist/testing.js.map +1 -0
- package/dist/trails/crud.d.ts +38 -0
- package/dist/trails/crud.d.ts.map +1 -0
- package/dist/trails/crud.js +112 -0
- package/dist/trails/crud.js.map +1 -0
- package/dist/trails/index.d.ts +7 -0
- package/dist/trails/index.d.ts.map +1 -0
- package/dist/trails/index.js +4 -0
- package/dist/trails/index.js.map +1 -0
- package/dist/trails/reconcile.d.ts +36 -0
- package/dist/trails/reconcile.d.ts.map +1 -0
- package/dist/trails/reconcile.js +142 -0
- package/dist/trails/reconcile.js.map +1 -0
- package/dist/trails/sync.d.ts +25 -0
- package/dist/trails/sync.d.ts.map +1 -0
- package/dist/trails/sync.js +96 -0
- package/dist/trails/sync.js.map +1 -0
- package/dist/trails/utils.d.ts +51 -0
- package/dist/trails/utils.d.ts.map +1 -0
- package/dist/trails/utils.js +65 -0
- package/dist/trails/utils.js.map +1 -0
- package/dist/types.d.ts +205 -35
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -9
- package/src/__tests__/crud.test.ts +346 -0
- package/src/__tests__/jsonfile.test.ts +502 -0
- package/src/__tests__/store.test.ts +239 -33
- package/src/__tests__/sync-reconcile.test.ts +646 -0
- package/src/index.ts +12 -8
- package/src/internal/signal-identity.ts +162 -0
- package/src/jsonfile/index.ts +6 -0
- package/src/jsonfile/runtime.ts +702 -0
- package/src/jsonfile/types.ts +50 -0
- package/src/store.ts +296 -134
- package/src/testing.ts +175 -0
- package/src/trails/crud.ts +355 -0
- package/src/trails/index.ts +10 -0
- package/src/trails/reconcile.ts +281 -0
- package/src/trails/sync.ts +251 -0
- package/src/trails/utils.ts +96 -0
- package/src/types.ts +355 -63
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/drizzle/index.d.ts +0 -3
- package/dist/drizzle/index.d.ts.map +0 -1
- package/dist/drizzle/index.js +0 -2
- package/dist/drizzle/index.js.map +0 -1
- package/dist/drizzle/runtime.d.ts +0 -21
- package/dist/drizzle/runtime.d.ts.map +0 -1
- package/dist/drizzle/runtime.js +0 -458
- package/dist/drizzle/runtime.js.map +0 -1
- package/dist/drizzle/schema.d.ts +0 -15
- package/dist/drizzle/schema.d.ts.map +0 -1
- package/dist/drizzle/schema.js +0 -322
- package/dist/drizzle/schema.js.map +0 -1
- package/dist/drizzle/types.d.ts +0 -40
- package/dist/drizzle/types.d.ts.map +0 -1
- package/src/drizzle/__tests__/drizzle.test.ts +0 -469
- package/src/drizzle/index.ts +0 -17
- package/src/drizzle/runtime.ts +0 -853
- package/src/drizzle/schema.ts +0 -577
- package/src/drizzle/types.ts +0 -70
- /package/dist/{drizzle → jsonfile}/types.js +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import { ConflictError, InternalError, Result, resource } from '@ontrails/core';
|
|
2
|
+
import { bindStoreDefinition } from '../internal/signal-identity.js';
|
|
3
|
+
import { versionFieldName } from '../store.js';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Mutex — single-process in-memory lock
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
/* eslint-disable promise/avoid-new -- Mutex requires manual promise control */
|
|
8
|
+
const createMutexAcquire = (state, queue) => () => {
|
|
9
|
+
if (!state.locked) {
|
|
10
|
+
state.locked = true;
|
|
11
|
+
return Promise.resolve();
|
|
12
|
+
}
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
queue.push(resolve);
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
/* eslint-enable promise/avoid-new */
|
|
18
|
+
const createMutexRelease = (state, queue) => () => {
|
|
19
|
+
const next = queue.shift();
|
|
20
|
+
if (next) {
|
|
21
|
+
next();
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
state.locked = false;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const createMutex = () => {
|
|
28
|
+
const state = { locked: false };
|
|
29
|
+
const queue = [];
|
|
30
|
+
return {
|
|
31
|
+
acquire: createMutexAcquire(state, queue),
|
|
32
|
+
release: createMutexRelease(state, queue),
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Helpers
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
const defaultResourceId = 'store';
|
|
39
|
+
const defaultGenerateIdentity = () => Bun.randomUUIDv7();
|
|
40
|
+
const jsonFilePath = (dir, tableName) => `${dir}/${tableName}.json`;
|
|
41
|
+
const matchesFilters = (entity, filters) => {
|
|
42
|
+
for (const [key, value] of Object.entries(filters)) {
|
|
43
|
+
if (entity[key] !== value) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
};
|
|
49
|
+
const applyPagination = (items, options) => {
|
|
50
|
+
if (options === undefined) {
|
|
51
|
+
return items;
|
|
52
|
+
}
|
|
53
|
+
const offset = options.offset ?? 0;
|
|
54
|
+
const limit = options.limit ?? items.length;
|
|
55
|
+
return items.slice(offset, offset + limit);
|
|
56
|
+
};
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Generated field helpers (defined before use, module-level)
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
const assignIdentity = (payload, identityField, generate) => {
|
|
61
|
+
if (payload[identityField] === undefined) {
|
|
62
|
+
payload[identityField] = generate();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const assignTimestamp = (payload, generatedFields, isNew) => {
|
|
66
|
+
if (generatedFields.has('createdAt') && isNew) {
|
|
67
|
+
payload['createdAt'] = new Date().toISOString();
|
|
68
|
+
}
|
|
69
|
+
if (generatedFields.has('updatedAt')) {
|
|
70
|
+
payload['updatedAt'] = new Date().toISOString();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const resolveNextVersion = (existing) => existing === undefined ? 1 : existing[versionFieldName] + 1;
|
|
74
|
+
const assignVersion = (payload, isVersioned, existing) => {
|
|
75
|
+
if (!isVersioned) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
payload[versionFieldName] = resolveNextVersion(existing);
|
|
79
|
+
};
|
|
80
|
+
const checkVersionConflict = (tableName, isVersioned, input, existing) => {
|
|
81
|
+
if (!isVersioned) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const inputVersion = input[versionFieldName];
|
|
85
|
+
if (inputVersion === undefined) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const currentVersion = existing[versionFieldName];
|
|
89
|
+
if (inputVersion !== currentVersion) {
|
|
90
|
+
throw new ConflictError(`Version conflict on "${tableName}": expected ${String(inputVersion)}, actual ${String(currentVersion)}`);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const buildUpsertPayload = (input, existing, identityField, generateIdentity, generatedFields, isVersioned) => {
|
|
94
|
+
const payload = { ...input };
|
|
95
|
+
assignIdentity(payload, identityField, generateIdentity);
|
|
96
|
+
assignTimestamp(payload, generatedFields, existing === undefined);
|
|
97
|
+
assignVersion(payload, isVersioned, existing);
|
|
98
|
+
return payload;
|
|
99
|
+
};
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// File I/O helpers
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
const loadFromDisk = async (path, identityField, index) => {
|
|
104
|
+
const file = Bun.file(path);
|
|
105
|
+
const exists = await file.exists();
|
|
106
|
+
if (!exists) {
|
|
107
|
+
await Bun.write(path, '[]');
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const text = await file.text();
|
|
111
|
+
const rows = JSON.parse(text);
|
|
112
|
+
for (const row of rows) {
|
|
113
|
+
const id = String(row[identityField]);
|
|
114
|
+
index.set(id, row);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const flushToDisk = async (path, index) => {
|
|
118
|
+
const rows = [...index.values()];
|
|
119
|
+
await Bun.write(path, JSON.stringify(rows, null, 2));
|
|
120
|
+
};
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Upsert core (extracted to stay under max-statements)
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
const resolveExisting = (raw, identityField, index) => {
|
|
125
|
+
const id = String(raw[identityField] ?? '');
|
|
126
|
+
return id ? index.get(id) : undefined;
|
|
127
|
+
};
|
|
128
|
+
const mergeAndBuild = (raw, existing, identityField, generateIdentity, generatedFields, isVersioned) => {
|
|
129
|
+
const merged = existing === undefined ? { ...raw } : { ...existing, ...raw };
|
|
130
|
+
const payload = buildUpsertPayload(merged, existing, identityField, generateIdentity, generatedFields, isVersioned);
|
|
131
|
+
return payload;
|
|
132
|
+
};
|
|
133
|
+
// Flush an upsert to disk then commit to the live index. Extracted to keep
|
|
134
|
+
// executeUpsert under the max-statements limit.
|
|
135
|
+
const flushAndCommitUpsert = async (entity, id, index, path) => {
|
|
136
|
+
// Build the intended state, flush to disk, then update the live index.
|
|
137
|
+
// This ensures the in-memory Map stays consistent with the file even if
|
|
138
|
+
// the write fails.
|
|
139
|
+
const next = new Map([...index, [id, entity]]);
|
|
140
|
+
await flushToDisk(path, next);
|
|
141
|
+
index.set(id, entity);
|
|
142
|
+
return structuredClone(entity);
|
|
143
|
+
};
|
|
144
|
+
const executeUpsert = (input, index, path, cfg) => {
|
|
145
|
+
const raw = input;
|
|
146
|
+
const existing = resolveExisting(raw, cfg.identityField, index);
|
|
147
|
+
if (existing !== undefined) {
|
|
148
|
+
checkVersionConflict(cfg.tableName, cfg.isVersioned, raw, existing);
|
|
149
|
+
}
|
|
150
|
+
const entity = mergeAndBuild(raw, existing, cfg.identityField, cfg.generateIdentity, cfg.generatedFields, cfg.isVersioned);
|
|
151
|
+
const id = String(entity[cfg.identityField]);
|
|
152
|
+
return flushAndCommitUpsert(entity, id, index, path);
|
|
153
|
+
};
|
|
154
|
+
const deriveTableConfig = (options) => ({
|
|
155
|
+
generateIdentity: options.generateIdentity,
|
|
156
|
+
generatedFields: new Set(options.table.generated),
|
|
157
|
+
identityField: options.table.identity,
|
|
158
|
+
isVersioned: options.table.versioned,
|
|
159
|
+
tableName: options.table.name,
|
|
160
|
+
});
|
|
161
|
+
const usesGeneratedIdentity = (config) => config.generatedFields.includes(config.identityField);
|
|
162
|
+
const deriveTableReuseConfig = (options) => {
|
|
163
|
+
const generatedFields = [...options.table.generated].toSorted();
|
|
164
|
+
return {
|
|
165
|
+
generateIdentity: usesGeneratedIdentity({
|
|
166
|
+
generatedFields,
|
|
167
|
+
identityField: options.table.identity,
|
|
168
|
+
})
|
|
169
|
+
? options.generateIdentity
|
|
170
|
+
: undefined,
|
|
171
|
+
generatedFields,
|
|
172
|
+
identityField: options.table.identity,
|
|
173
|
+
isVersioned: options.table.versioned,
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const diffTableReuseConfig = (cached, incoming) => {
|
|
177
|
+
const mismatches = [];
|
|
178
|
+
if (cached.identityField !== incoming.identityField) {
|
|
179
|
+
mismatches.push('identityField');
|
|
180
|
+
}
|
|
181
|
+
if (cached.isVersioned !== incoming.isVersioned) {
|
|
182
|
+
mismatches.push('versioned');
|
|
183
|
+
}
|
|
184
|
+
if (cached.generatedFields.length !== incoming.generatedFields.length ||
|
|
185
|
+
cached.generatedFields.some((field, index) => field !== incoming.generatedFields[index])) {
|
|
186
|
+
mismatches.push('generatedFields');
|
|
187
|
+
}
|
|
188
|
+
if (cached.generateIdentity !== incoming.generateIdentity) {
|
|
189
|
+
mismatches.push('generateIdentity');
|
|
190
|
+
}
|
|
191
|
+
return mismatches;
|
|
192
|
+
};
|
|
193
|
+
const assertCompatibleTableReuse = (path, cached, incoming) => {
|
|
194
|
+
const mismatches = diffTableReuseConfig(cached, incoming);
|
|
195
|
+
if (mismatches.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
throw new ConflictError(`JSON file table reuse conflict for "${path}": ${mismatches.join(', ')} differ across connections. Reuse the same table config for a shared path or isolate the stores to different directories.`);
|
|
199
|
+
};
|
|
200
|
+
const createGetAccessor = (index) => (id) => {
|
|
201
|
+
const entity = index.get(String(id));
|
|
202
|
+
return Promise.resolve(entity === undefined ? null : structuredClone(entity));
|
|
203
|
+
};
|
|
204
|
+
const createListAccessor = (index) => (filters, opts) => {
|
|
205
|
+
const all = [...index.values()];
|
|
206
|
+
const filtered = filters === undefined
|
|
207
|
+
? all
|
|
208
|
+
: all.filter((entity) => matchesFilters(entity, filters));
|
|
209
|
+
return Promise.resolve(applyPagination(filtered, opts).map((e) => structuredClone(e)));
|
|
210
|
+
};
|
|
211
|
+
// Flush a remove to disk then commit to the live index. Extracted to keep
|
|
212
|
+
// the remove closure under the max-statements limit.
|
|
213
|
+
const executeRemove = async (key, index, path) => {
|
|
214
|
+
if (!index.has(key)) {
|
|
215
|
+
return { deleted: false };
|
|
216
|
+
}
|
|
217
|
+
// Build the intended state, flush to disk, then update the live index.
|
|
218
|
+
// This ensures the in-memory Map stays consistent with the file even if
|
|
219
|
+
// the write fails.
|
|
220
|
+
const next = new Map([...index].filter(([k]) => k !== key));
|
|
221
|
+
await flushToDisk(path, next);
|
|
222
|
+
index.delete(key);
|
|
223
|
+
return { deleted: true };
|
|
224
|
+
};
|
|
225
|
+
const tableRegistry = new Map();
|
|
226
|
+
const buildAndRegisterTable = (path, options) => {
|
|
227
|
+
const mutex = createMutex();
|
|
228
|
+
const index = new Map();
|
|
229
|
+
const cfg = deriveTableConfig(options);
|
|
230
|
+
const upsert = async (input) => {
|
|
231
|
+
await mutex.acquire();
|
|
232
|
+
try {
|
|
233
|
+
return await executeUpsert(input, index, path, cfg);
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
mutex.release();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const remove = async (id) => {
|
|
240
|
+
await mutex.acquire();
|
|
241
|
+
try {
|
|
242
|
+
return await executeRemove(String(id), index, path);
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
mutex.release();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const instance = {
|
|
249
|
+
get: createGetAccessor(index),
|
|
250
|
+
list: createListAccessor(index),
|
|
251
|
+
// Skip disk load when the index already has data. This prevents a
|
|
252
|
+
// second `connectJsonFile` call from overwriting in-flight writes
|
|
253
|
+
// (the mutex only guards individual operations, not the full load
|
|
254
|
+
// sequence). It also avoids re-reading stale files on reuse.
|
|
255
|
+
load: async () => {
|
|
256
|
+
if (index.size > 0) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
await loadFromDisk(path, cfg.identityField, index);
|
|
260
|
+
},
|
|
261
|
+
remove,
|
|
262
|
+
upsert,
|
|
263
|
+
};
|
|
264
|
+
tableRegistry.set(path, {
|
|
265
|
+
accessor: instance,
|
|
266
|
+
reuseConfig: deriveTableReuseConfig(options),
|
|
267
|
+
});
|
|
268
|
+
return instance;
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Return a cached or freshly-built table instance for the given path.
|
|
272
|
+
*
|
|
273
|
+
* @remarks
|
|
274
|
+
* Tables are shared per resolved file path so multiple connections stay
|
|
275
|
+
* coherent in-process. Reuse is only allowed when table-affecting config
|
|
276
|
+
* matches exactly; otherwise the connector throws instead of silently
|
|
277
|
+
* inheriting the first connection's runtime semantics.
|
|
278
|
+
*/
|
|
279
|
+
const createJsonFileTable = (options) => {
|
|
280
|
+
const path = jsonFilePath(options.dir, options.table.name);
|
|
281
|
+
const cached = tableRegistry.get(path);
|
|
282
|
+
if (cached === undefined) {
|
|
283
|
+
return buildAndRegisterTable(path, options);
|
|
284
|
+
}
|
|
285
|
+
assertCompatibleTableReuse(path, cached.reuseConfig, deriveTableReuseConfig(options));
|
|
286
|
+
return cached.accessor;
|
|
287
|
+
};
|
|
288
|
+
// Tracks temp directories created by mock factories so `dispose` can clean
|
|
289
|
+
// them up. WeakMap ensures entries are GC'd when the connection is released.
|
|
290
|
+
const mockTmpDirs = new WeakMap();
|
|
291
|
+
const collectMockFixtures = (definition, seed) => {
|
|
292
|
+
const fixturesByTable = new Map();
|
|
293
|
+
for (const tableName of definition.tableNames) {
|
|
294
|
+
const table = definition.tables[tableName];
|
|
295
|
+
if (table === undefined) {
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
const fixtures = seed?.[tableName] ?? table.fixtures;
|
|
299
|
+
if (fixtures.length > 0) {
|
|
300
|
+
fixturesByTable.set(tableName, fixtures);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return fixturesByTable;
|
|
304
|
+
};
|
|
305
|
+
const seedMockTable = async (accessor, fixtures) => {
|
|
306
|
+
for (const fixture of fixtures) {
|
|
307
|
+
await accessor.upsert(fixture);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const seedMockConnection = async (connection, definition, seed) => {
|
|
311
|
+
const fixturesByTable = collectMockFixtures(definition, seed);
|
|
312
|
+
if (fixturesByTable.size === 0) {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const accessors = connection;
|
|
316
|
+
for (const tableName of definition.tableNames) {
|
|
317
|
+
const accessor = accessors[tableName];
|
|
318
|
+
const fixtures = fixturesByTable.get(tableName);
|
|
319
|
+
if (accessor === undefined || fixtures === undefined) {
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
await seedMockTable(accessor, fixtures);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
// Public API
|
|
327
|
+
// ---------------------------------------------------------------------------
|
|
328
|
+
/**
|
|
329
|
+
* Connect to a JSON-file-backed store.
|
|
330
|
+
*
|
|
331
|
+
* Creates one `<tableName>.json` file per table in the target directory.
|
|
332
|
+
* Each file holds a JSON array of entities. The entire dataset is loaded
|
|
333
|
+
* into memory on connect and flushed to disk on every write.
|
|
334
|
+
*/
|
|
335
|
+
export const connectJsonFile = async (definition, options) => {
|
|
336
|
+
const { dir, generateIdentity = defaultGenerateIdentity } = options;
|
|
337
|
+
const connection = {};
|
|
338
|
+
for (const tableName of definition.tableNames) {
|
|
339
|
+
const table = definition.tables[tableName];
|
|
340
|
+
if (table === undefined) {
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
const accessor = createJsonFileTable({
|
|
344
|
+
dir,
|
|
345
|
+
generateIdentity,
|
|
346
|
+
table,
|
|
347
|
+
});
|
|
348
|
+
await accessor.load();
|
|
349
|
+
connection[tableName] = accessor;
|
|
350
|
+
}
|
|
351
|
+
return Object.freeze(connection);
|
|
352
|
+
};
|
|
353
|
+
/**
|
|
354
|
+
* Create a JSON-file-backed store resource.
|
|
355
|
+
*
|
|
356
|
+
* Returns a `Resource` that can be registered in a topo and resolved from
|
|
357
|
+
* trail context via `db.from(ctx)`.
|
|
358
|
+
*/
|
|
359
|
+
export const jsonFile = (definition, options) => {
|
|
360
|
+
const scope = options.id ?? defaultResourceId;
|
|
361
|
+
const store = bindStoreDefinition(definition, scope);
|
|
362
|
+
return resource(scope, {
|
|
363
|
+
create: async () => {
|
|
364
|
+
try {
|
|
365
|
+
return Result.ok(await connectJsonFile(store, options));
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
369
|
+
return Result.err(new InternalError(`JSON file store failed to initialize in "${options.dir}": ${err.message}`, { cause: err }));
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
description: options.description ??
|
|
373
|
+
'JSON-file-backed store bound from an @ontrails/store definition.',
|
|
374
|
+
dispose: async (connection) => {
|
|
375
|
+
const tmpDir = mockTmpDirs.get(connection);
|
|
376
|
+
if (tmpDir !== undefined) {
|
|
377
|
+
const { rm } = await import('node:fs/promises');
|
|
378
|
+
await rm(tmpDir, { force: true, recursive: true });
|
|
379
|
+
mockTmpDirs.delete(connection);
|
|
380
|
+
for (const tableName of store.tableNames) {
|
|
381
|
+
tableRegistry.delete(jsonFilePath(tmpDir, tableName));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
meta: options.meta,
|
|
386
|
+
mock: async () => {
|
|
387
|
+
const { mkdtemp } = await import('node:fs/promises');
|
|
388
|
+
const { join } = await import('node:path');
|
|
389
|
+
const { tmpdir } = await import('node:os');
|
|
390
|
+
const tmpDir = await mkdtemp(join(tmpdir(), 'jsonfile-mock-'));
|
|
391
|
+
const connection = await connectJsonFile(store, {
|
|
392
|
+
...options,
|
|
393
|
+
dir: tmpDir,
|
|
394
|
+
});
|
|
395
|
+
await seedMockConnection(connection, store, options.mockSeed);
|
|
396
|
+
mockTmpDirs.set(connection, tmpDir);
|
|
397
|
+
return connection;
|
|
398
|
+
},
|
|
399
|
+
signals: store.signals,
|
|
400
|
+
});
|
|
401
|
+
};
|
|
402
|
+
export { jsonFile as store };
|
|
403
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/jsonfile/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Resource } from '@ontrails/core';
|
|
2
|
+
import type { AnyStoreDefinition, AnyStoreTable, StoreAccessor, StoreConnectorOptions } from '../types.js';
|
|
3
|
+
/** Connection shape: one StoreAccessor per table name. */
|
|
4
|
+
export type JsonFileConnection<TStore extends Record<string, AnyStoreTable>> = Readonly<{
|
|
5
|
+
[K in keyof TStore]: StoreAccessor<TStore[K]>;
|
|
6
|
+
}>;
|
|
7
|
+
/** Options for creating a jsonfile store. */
|
|
8
|
+
export interface JsonFileStoreOptions<TStore extends AnyStoreDefinition = AnyStoreDefinition> extends StoreConnectorOptions<TStore> {
|
|
9
|
+
/** Directory where JSON files are written. One `<tableName>.json` per table. */
|
|
10
|
+
readonly dir: string;
|
|
11
|
+
/**
|
|
12
|
+
* Optional identity generator. Defaults to `Bun.randomUUIDv7()`.
|
|
13
|
+
*
|
|
14
|
+
* @remarks
|
|
15
|
+
* When a jsonfile table is reused across multiple `connectJsonFile` call
|
|
16
|
+
* sites (same `dir` + table), the runtime enforces that every connection
|
|
17
|
+
* supplies a compatible `generateIdentity`. That compatibility check is
|
|
18
|
+
* intentional reference equality, not a structural or behavioral comparison:
|
|
19
|
+
* two distinct arrow functions that happen to be "logically identical"
|
|
20
|
+
* (e.g. `() => uuid()` written inline in two files) will still be treated
|
|
21
|
+
* as a conflict and surface as a `ConflictError`. Callers that want to
|
|
22
|
+
* share a table across call sites should hoist `generateIdentity` to a
|
|
23
|
+
* stable reference — a module-level `const` or shared utility — and pass
|
|
24
|
+
* that same reference in from every connection, rather than recreating
|
|
25
|
+
* the function inline at each site.
|
|
26
|
+
*
|
|
27
|
+
* The conflict check only fires for custom generators. When no custom
|
|
28
|
+
* generator is provided for an auto-generated-identity store,
|
|
29
|
+
* `deriveTableReuseConfig` normalizes `generateIdentity` to `undefined`
|
|
30
|
+
* regardless of what (if anything) was passed, so two connections that
|
|
31
|
+
* both rely on defaults never conflict on this field. The same
|
|
32
|
+
* normalization also applies when the identity field is not in
|
|
33
|
+
* `generatedFields` — the generator is irrelevant in that case, so the
|
|
34
|
+
* check is skipped.
|
|
35
|
+
*/
|
|
36
|
+
readonly generateIdentity?: () => string;
|
|
37
|
+
}
|
|
38
|
+
/** Resource type for a jsonfile store. */
|
|
39
|
+
export type JsonFileStoreResource<TStore extends Record<string, AnyStoreTable>> = Resource<JsonFileConnection<TStore>>;
|
|
40
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/jsonfile/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,qBAAqB,EACtB,MAAM,aAAa,CAAC;AAErB,0DAA0D;AAC1D,MAAM,MAAM,kBAAkB,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,IACzE,QAAQ,CAAC;KAAG,CAAC,IAAI,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC,CAAC;AAE9D,6CAA6C;AAC7C,MAAM,WAAW,oBAAoB,CACnC,MAAM,SAAS,kBAAkB,GAAG,kBAAkB,CACtD,SAAQ,qBAAqB,CAAC,MAAM,CAAC;IACrC,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1C;AAED,0CAA0C;AAC1C,MAAM,MAAM,qBAAqB,CAC/B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,IAC1C,QAAQ,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/jsonfile/types.ts"],"names":[],"mappings":""}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type { StoreDefinition,
|
|
1
|
+
import type { StoreDefinition, StoreOptions, StoreTablesInput } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Name of the framework-managed version column used by versioned tables for
|
|
4
|
+
* optimistic concurrency control. Exported so connectors can gate their own
|
|
5
|
+
* version-handling logic without duplicating the literal.
|
|
6
|
+
*/
|
|
7
|
+
export declare const versionFieldName = "version";
|
|
2
8
|
/**
|
|
3
9
|
* Declare a connector-agnostic store definition from entity schemas and
|
|
4
10
|
* persistence metadata.
|
|
@@ -6,21 +12,5 @@ import type { StoreDefinition, StoreTable, StoreTablesInput } from './types.js';
|
|
|
6
12
|
* The returned value is a normalized, read-only contract that connectors can
|
|
7
13
|
* bind to a concrete runtime later.
|
|
8
14
|
*/
|
|
9
|
-
export declare const store: <const TTables extends StoreTablesInput>(tables: TTables) => StoreDefinition<TTables>;
|
|
10
|
-
/**
|
|
11
|
-
* Read the full entity schema from a normalized store table.
|
|
12
|
-
*/
|
|
13
|
-
export declare const entitySchemaOf: <TTable extends StoreTable>(table: TTable) => TTable["schema"];
|
|
14
|
-
/**
|
|
15
|
-
* Read the fixture schema from a normalized store table.
|
|
16
|
-
*/
|
|
17
|
-
export declare const fixtureSchemaOf: <TTable extends StoreTable>(table: TTable) => TTable["fixtureSchema"];
|
|
18
|
-
/**
|
|
19
|
-
* Read the insert schema from a normalized store table.
|
|
20
|
-
*/
|
|
21
|
-
export declare const insertSchemaOf: <TTable extends StoreTable>(table: TTable) => TTable["insertSchema"];
|
|
22
|
-
/**
|
|
23
|
-
* Read the update schema from a normalized store table.
|
|
24
|
-
*/
|
|
25
|
-
export declare const updateSchemaOf: <TTable extends StoreTable>(table: TTable) => TTable["updateSchema"];
|
|
15
|
+
export declare const store: <const TTables extends StoreTablesInput>(tables: TTables, options?: StoreOptions) => StoreDefinition<TTables>;
|
|
26
16
|
//# sourceMappingURL=store.d.ts.map
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,eAAe,EAEf,YAAY,EAIZ,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAKpB;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AA4d1C;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,KAAK,CAAC,OAAO,SAAS,gBAAgB,EAC1D,QAAQ,OAAO,EACf,UAAS,YAAiB,KACzB,eAAe,CAAC,OAAO,CAkBzB,CAAC"}
|