@objectstack/metadata-fs 5.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.
package/dist/index.js ADDED
@@ -0,0 +1,588 @@
1
+ // src/repository.ts
2
+ import fs2 from "fs/promises";
3
+ import { existsSync as existsSync2 } from "fs";
4
+ import path3 from "path";
5
+ import chokidar from "chokidar";
6
+ import {
7
+ hashSpec,
8
+ ConflictError,
9
+ refKey
10
+ } from "@objectstack/metadata-core";
11
+
12
+ // src/layout.ts
13
+ import path from "path";
14
+ function itemPath(layout, type, name) {
15
+ return path.join(layout.root, type, `${name}.json`);
16
+ }
17
+ function typeDir(layout, type) {
18
+ return path.join(layout.root, type);
19
+ }
20
+ function logDir(layout) {
21
+ return path.join(layout.root, ".objectstack", ".log");
22
+ }
23
+ function logFile(layout) {
24
+ return path.join(logDir(layout), `main.jsonl`);
25
+ }
26
+ function parseItemPath(layout, absPath) {
27
+ const rel = path.relative(layout.root, absPath);
28
+ if (rel.startsWith("..") || rel.startsWith(".objectstack")) return null;
29
+ const segments = rel.split(path.sep);
30
+ if (segments.length !== 2) return null;
31
+ const type = segments[0];
32
+ const file = segments[1];
33
+ if (!file.endsWith(".json")) return null;
34
+ const name = file.slice(0, -".json".length);
35
+ return { type, name };
36
+ }
37
+
38
+ // src/jsonl-log.ts
39
+ import fs from "fs/promises";
40
+ import path2 from "path";
41
+ import readline from "readline";
42
+ import { createReadStream, existsSync } from "fs";
43
+ var JsonlLog = class {
44
+ constructor(file) {
45
+ this.file = file;
46
+ }
47
+ async append(evt) {
48
+ await fs.mkdir(path2.dirname(this.file), { recursive: true });
49
+ await fs.appendFile(this.file, JSON.stringify(evt) + "\n", "utf8");
50
+ }
51
+ /** Read all events in seq order (i.e. file order). */
52
+ async *readAll() {
53
+ if (!existsSync(this.file)) return;
54
+ const rl = readline.createInterface({
55
+ input: createReadStream(this.file, { encoding: "utf8" }),
56
+ crlfDelay: Infinity
57
+ });
58
+ try {
59
+ for await (const line of rl) {
60
+ if (!line.trim()) continue;
61
+ try {
62
+ yield JSON.parse(line);
63
+ } catch {
64
+ }
65
+ }
66
+ } finally {
67
+ rl.close();
68
+ }
69
+ }
70
+ /** Return the highest seq number in the log, or 0 if empty. */
71
+ async highestSeq() {
72
+ let max = 0;
73
+ for await (const evt of this.readAll()) {
74
+ if (typeof evt.seq === "number" && evt.seq > max) max = evt.seq;
75
+ }
76
+ return max;
77
+ }
78
+ };
79
+
80
+ // src/sync.ts
81
+ var KeyedMutex = class {
82
+ constructor() {
83
+ this.tails = /* @__PURE__ */ new Map();
84
+ }
85
+ async run(key, fn) {
86
+ const prev = this.tails.get(key) ?? Promise.resolve();
87
+ const next = prev.then(fn, fn);
88
+ const swallowed = next.catch(() => void 0);
89
+ this.tails.set(key, swallowed);
90
+ try {
91
+ return await next;
92
+ } finally {
93
+ if (this.tails.get(key) === swallowed) {
94
+ this.tails.delete(key);
95
+ }
96
+ }
97
+ }
98
+ };
99
+ function createBroker(matches) {
100
+ const subs = /* @__PURE__ */ new Set();
101
+ return {
102
+ subscribe: (s) => {
103
+ subs.add(s);
104
+ },
105
+ unsubscribe: (s) => {
106
+ subs.delete(s);
107
+ },
108
+ publish: (evt) => {
109
+ for (const s of subs) {
110
+ if (s.closed) continue;
111
+ if (!matches(evt, s.filter)) continue;
112
+ s.push(evt);
113
+ }
114
+ }
115
+ };
116
+ }
117
+
118
+ // src/watch-iterable.ts
119
+ function createWatchIterable(args) {
120
+ const queue = [];
121
+ let waiter = null;
122
+ let closed = false;
123
+ const delivered = /* @__PURE__ */ new Set();
124
+ const evtKey = (e) => `${args.branchKeyOf(e)}#${e.seq}`;
125
+ const subscriber = {
126
+ filter: args.filter,
127
+ closed: false,
128
+ push: (evt) => {
129
+ if (subscriber.closed) return;
130
+ const k = evtKey(evt);
131
+ if (delivered.has(k)) return;
132
+ if (waiter) {
133
+ delivered.add(k);
134
+ const w = waiter;
135
+ waiter = null;
136
+ w({ value: clone(evt), done: false });
137
+ } else {
138
+ queue.push(evt);
139
+ }
140
+ }
141
+ };
142
+ args.broker.subscribe(subscriber);
143
+ const replay = [...args.replay].sort((a, b) => a.seq - b.seq);
144
+ let replayIdx = 0;
145
+ const drain = () => {
146
+ while (replayIdx < replay.length) {
147
+ const evt = replay[replayIdx++];
148
+ if (typeof args.since === "number" && evt.seq <= args.since) continue;
149
+ const k = evtKey(evt);
150
+ if (delivered.has(k)) continue;
151
+ delivered.add(k);
152
+ return { value: clone(evt), done: false };
153
+ }
154
+ while (queue.length > 0) {
155
+ const evt = queue.shift();
156
+ const k = evtKey(evt);
157
+ if (delivered.has(k)) continue;
158
+ delivered.add(k);
159
+ return { value: clone(evt), done: false };
160
+ }
161
+ return null;
162
+ };
163
+ const close = () => {
164
+ if (!closed) {
165
+ closed = true;
166
+ subscriber.closed = true;
167
+ args.broker.unsubscribe(subscriber);
168
+ if (waiter) {
169
+ const w = waiter;
170
+ waiter = null;
171
+ w({ value: void 0, done: true });
172
+ }
173
+ }
174
+ return { value: void 0, done: true };
175
+ };
176
+ const iterator = {
177
+ next: () => {
178
+ if (closed) return Promise.resolve({ value: void 0, done: true });
179
+ const immediate = drain();
180
+ if (immediate) return Promise.resolve(immediate);
181
+ return new Promise((resolve) => {
182
+ waiter = resolve;
183
+ });
184
+ },
185
+ return: () => Promise.resolve(close()),
186
+ throw: (err) => {
187
+ close();
188
+ return Promise.reject(err);
189
+ }
190
+ };
191
+ return { [Symbol.asyncIterator]: () => iterator };
192
+ }
193
+ function clone(value) {
194
+ return JSON.parse(JSON.stringify(value));
195
+ }
196
+
197
+ // src/repository.ts
198
+ var matchRefFilter = (ref, filter) => {
199
+ if (filter.org && filter.org !== ref.org) return false;
200
+ if (filter.type && filter.type !== ref.type) return false;
201
+ if (filter.name && filter.name !== ref.name) return false;
202
+ return true;
203
+ };
204
+ var matchEvent = (evt, filter) => matchRefFilter(evt.ref, filter);
205
+ var FileSystemRepository = class {
206
+ constructor(opts) {
207
+ this.mutex = new KeyedMutex();
208
+ this.broker = createBroker(matchEvent);
209
+ /** In-memory index: refKey → current hash (HEAD). */
210
+ this.heads = /* @__PURE__ */ new Map();
211
+ /** Next seq counter, hydrated from the log on `start()`. */
212
+ this.nextSeq = 1;
213
+ /** Paths we wrote ourselves; suppress the resulting chokidar event. */
214
+ this.selfWrites = /* @__PURE__ */ new Set();
215
+ this.watcher = null;
216
+ this.started = false;
217
+ this.org = opts.org;
218
+ this.fsActor = opts.fsActor ?? "fs";
219
+ this.disableWatch = opts.disableWatch ?? false;
220
+ this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
221
+ this.layout = { root: path3.resolve(opts.root) };
222
+ this.log = new JsonlLog(logFile(this.layout));
223
+ }
224
+ // ── Lifecycle ───────────────────────────────────────────────────────
225
+ async start() {
226
+ if (this.started) return;
227
+ this.started = true;
228
+ await fs2.mkdir(this.layout.root, { recursive: true });
229
+ await fs2.mkdir(logDir(this.layout), { recursive: true });
230
+ await this.scanHeads();
231
+ const highest = await this.log.highestSeq();
232
+ this.nextSeq = highest + 1;
233
+ if (!this.disableWatch) this.startWatcher();
234
+ }
235
+ async close() {
236
+ if (this.watcher) {
237
+ await this.watcher.close();
238
+ this.watcher = null;
239
+ }
240
+ this.started = false;
241
+ }
242
+ // ── Read API ────────────────────────────────────────────────────────
243
+ async get(ref) {
244
+ this.assertScope(ref);
245
+ const file = itemPath(this.layout, ref.type, ref.name);
246
+ if (!existsSync2(file)) return null;
247
+ const body = await readJson(file);
248
+ if (!body) return null;
249
+ const hash = hashSpec(body);
250
+ if (ref.version && ref.version !== hash) return null;
251
+ const meta = await this.findMetaForHash(ref, hash);
252
+ return {
253
+ ref: { ...ref, version: void 0 },
254
+ body,
255
+ hash,
256
+ parentHash: meta?.parentHash ?? null,
257
+ authoredBy: meta?.actor ?? this.fsActor,
258
+ authoredAt: meta?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
259
+ message: meta?.message,
260
+ seq: meta?.seq ?? 0
261
+ };
262
+ }
263
+ async *list(filter) {
264
+ const limit = filter.limit ?? Infinity;
265
+ let yielded = 0;
266
+ for (const [key, hash] of this.heads) {
267
+ const ref = parseRefKey(key);
268
+ if (!ref) continue;
269
+ if (!matchRefFilter(ref, filter)) continue;
270
+ if (filter.nameContains && !ref.name.includes(filter.nameContains)) continue;
271
+ const meta = await this.findMetaForHash(ref, hash);
272
+ const header = {
273
+ ref: { ...ref, version: void 0 },
274
+ hash,
275
+ parentHash: meta?.parentHash ?? null,
276
+ authoredBy: meta?.actor ?? this.fsActor,
277
+ authoredAt: meta?.ts ?? (/* @__PURE__ */ new Date(0)).toISOString(),
278
+ message: meta?.message,
279
+ seq: meta?.seq ?? 0
280
+ };
281
+ yield header;
282
+ if (++yielded >= limit) return;
283
+ }
284
+ }
285
+ async *history(ref, opts = {}) {
286
+ this.assertScope(ref);
287
+ const since = opts.sinceSeq ?? -1;
288
+ const limit = opts.limit ?? Infinity;
289
+ let yielded = 0;
290
+ for await (const evt of this.log.readAll()) {
291
+ if (evt.seq <= since) continue;
292
+ if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;
293
+ if (evt.ref.org !== ref.org) continue;
294
+ yield evt;
295
+ if (++yielded >= limit) return;
296
+ }
297
+ }
298
+ watch(filter, since) {
299
+ const replay = [];
300
+ const promise = (async () => {
301
+ for await (const evt of this.log.readAll()) {
302
+ if (matchEvent(evt, filter)) replay.push(evt);
303
+ }
304
+ })();
305
+ return deferredIterable(promise.then(
306
+ () => createWatchIterable({
307
+ filter,
308
+ since,
309
+ replay,
310
+ broker: this.broker,
311
+ matches: matchEvent,
312
+ branchKeyOf: (e) => e.ref.org
313
+ })
314
+ ));
315
+ }
316
+ // ── Write API ───────────────────────────────────────────────────────
317
+ put(ref, spec, opts) {
318
+ this.assertScope(ref);
319
+ return this.mutex.run(refKey(ref), async () => {
320
+ const key = refKey(ref);
321
+ const currentHead = this.heads.get(key) ?? null;
322
+ if ((opts.parentVersion ?? null) !== currentHead) {
323
+ throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);
324
+ }
325
+ const hash = hashSpec(spec);
326
+ if (currentHead === hash) {
327
+ const meta = await this.findMetaForHash(ref, hash);
328
+ return {
329
+ version: hash,
330
+ seq: meta?.seq ?? 0,
331
+ item: {
332
+ ref: { ...ref, version: void 0 },
333
+ body: spec,
334
+ hash,
335
+ parentHash: meta?.parentHash ?? null,
336
+ authoredBy: meta?.actor ?? this.fsActor,
337
+ authoredAt: meta?.ts ?? this.now().toISOString(),
338
+ message: meta?.message,
339
+ seq: meta?.seq ?? 0
340
+ }
341
+ };
342
+ }
343
+ const seq = this.nextSeq++;
344
+ const ts = this.now().toISOString();
345
+ const file = itemPath(this.layout, ref.type, ref.name);
346
+ await fs2.mkdir(typeDir(this.layout, ref.type), { recursive: true });
347
+ this.selfWrites.add(file);
348
+ try {
349
+ await writeJsonAtomic(file, spec);
350
+ } finally {
351
+ setTimeout(() => this.selfWrites.delete(file), 200);
352
+ }
353
+ this.heads.set(key, hash);
354
+ const evt = {
355
+ seq,
356
+ op: currentHead ? "update" : "create",
357
+ ref: { ...ref, version: void 0 },
358
+ hash,
359
+ parentHash: currentHead,
360
+ actor: opts.actor,
361
+ message: opts.message,
362
+ ts,
363
+ source: opts.source ?? "fs"
364
+ };
365
+ await this.log.append(evt);
366
+ this.broker.publish(evt);
367
+ return {
368
+ version: hash,
369
+ seq,
370
+ item: {
371
+ ref: { ...ref, version: void 0 },
372
+ body: spec,
373
+ hash,
374
+ parentHash: currentHead,
375
+ authoredBy: opts.actor,
376
+ authoredAt: ts,
377
+ message: opts.message,
378
+ seq
379
+ }
380
+ };
381
+ });
382
+ }
383
+ delete(ref, opts) {
384
+ this.assertScope(ref);
385
+ return this.mutex.run(refKey(ref), async () => {
386
+ const key = refKey(ref);
387
+ const currentHead = this.heads.get(key) ?? null;
388
+ if (currentHead !== opts.parentVersion) {
389
+ throw new ConflictError(ref, opts.parentVersion, currentHead);
390
+ }
391
+ const file = itemPath(this.layout, ref.type, ref.name);
392
+ this.selfWrites.add(file);
393
+ try {
394
+ if (existsSync2(file)) await fs2.unlink(file);
395
+ } finally {
396
+ setTimeout(() => this.selfWrites.delete(file), 200);
397
+ }
398
+ this.heads.delete(key);
399
+ const seq = this.nextSeq++;
400
+ const ts = this.now().toISOString();
401
+ const evt = {
402
+ seq,
403
+ op: "delete",
404
+ ref: { ...ref, version: void 0 },
405
+ hash: null,
406
+ parentHash: currentHead,
407
+ actor: opts.actor,
408
+ message: opts.message,
409
+ ts,
410
+ source: opts.source ?? "fs"
411
+ };
412
+ await this.log.append(evt);
413
+ this.broker.publish(evt);
414
+ return { seq };
415
+ });
416
+ }
417
+ // ── Internals ───────────────────────────────────────────────────────
418
+ assertScope(ref) {
419
+ if (ref.org !== this.org) {
420
+ throw new Error(
421
+ `FileSystemRepository scope mismatch: expected org=${this.org}, got org=${ref.org}`
422
+ );
423
+ }
424
+ }
425
+ async scanHeads() {
426
+ this.heads.clear();
427
+ let entries = [];
428
+ try {
429
+ entries = await fs2.readdir(this.layout.root, { withFileTypes: true });
430
+ } catch {
431
+ return;
432
+ }
433
+ for (const entry of entries) {
434
+ if (!entry.isDirectory()) continue;
435
+ if (entry.name.startsWith(".")) continue;
436
+ const type = entry.name;
437
+ const dir = path3.join(this.layout.root, type);
438
+ let files = [];
439
+ try {
440
+ files = await fs2.readdir(dir);
441
+ } catch {
442
+ continue;
443
+ }
444
+ for (const file of files) {
445
+ if (!file.endsWith(".json")) continue;
446
+ const name = file.slice(0, -".json".length);
447
+ const ref = {
448
+ org: this.org,
449
+ type,
450
+ name
451
+ };
452
+ const body = await readJson(path3.join(dir, file));
453
+ if (!body) continue;
454
+ this.heads.set(refKey(ref), hashSpec(body));
455
+ }
456
+ }
457
+ }
458
+ async findMetaForHash(ref, hash) {
459
+ let last = null;
460
+ for await (const evt of this.log.readAll()) {
461
+ if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;
462
+ if (evt.ref.org !== ref.org) continue;
463
+ if (evt.hash === hash) last = evt;
464
+ }
465
+ return last;
466
+ }
467
+ startWatcher() {
468
+ const w = chokidar.watch(this.layout.root, {
469
+ ignored: [/(^|[\\/])\../],
470
+ // skip dotfiles incl. .objectstack
471
+ ignoreInitial: true,
472
+ depth: 2,
473
+ awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },
474
+ // Use polling to avoid `fs.watch` EMFILE on macOS / busy dev hosts.
475
+ // The depth-2 recursion would otherwise wire native watches across
476
+ // the entire customization tree.
477
+ usePolling: true,
478
+ interval: 1e3,
479
+ binaryInterval: 2e3
480
+ });
481
+ w.on("add", (p) => void this.handleFsChange(p, "add"));
482
+ w.on("change", (p) => void this.handleFsChange(p, "change"));
483
+ w.on("unlink", (p) => void this.handleFsChange(p, "unlink"));
484
+ this.watcher = w;
485
+ }
486
+ async handleFsChange(absPath, kind) {
487
+ if (this.selfWrites.has(absPath)) return;
488
+ const parsed = parseItemPath(this.layout, absPath);
489
+ if (!parsed) return;
490
+ const ref = {
491
+ org: this.org,
492
+ type: parsed.type,
493
+ name: parsed.name
494
+ };
495
+ const key = refKey(ref);
496
+ await this.mutex.run(key, async () => {
497
+ if (kind === "unlink") {
498
+ const currentHead2 = this.heads.get(key) ?? null;
499
+ if (!currentHead2) return;
500
+ this.heads.delete(key);
501
+ const seq2 = this.nextSeq++;
502
+ const evt2 = {
503
+ seq: seq2,
504
+ op: "delete",
505
+ ref: { ...ref, version: void 0 },
506
+ hash: null,
507
+ parentHash: currentHead2,
508
+ actor: this.fsActor,
509
+ ts: this.now().toISOString(),
510
+ source: "fs"
511
+ };
512
+ await this.log.append(evt2);
513
+ this.broker.publish(evt2);
514
+ return;
515
+ }
516
+ const body = await readJson(absPath);
517
+ if (!body) return;
518
+ const hash = hashSpec(body);
519
+ const currentHead = this.heads.get(key) ?? null;
520
+ if (currentHead === hash) return;
521
+ this.heads.set(key, hash);
522
+ const seq = this.nextSeq++;
523
+ const evt = {
524
+ seq,
525
+ op: currentHead ? "update" : "create",
526
+ ref: { ...ref, version: void 0 },
527
+ hash,
528
+ parentHash: currentHead,
529
+ actor: this.fsActor,
530
+ ts: this.now().toISOString(),
531
+ source: "fs"
532
+ };
533
+ await this.log.append(evt);
534
+ this.broker.publish(evt);
535
+ });
536
+ }
537
+ };
538
+ async function readJson(file) {
539
+ try {
540
+ const text = await fs2.readFile(file, "utf8");
541
+ return JSON.parse(text);
542
+ } catch {
543
+ return null;
544
+ }
545
+ }
546
+ async function writeJsonAtomic(file, body) {
547
+ const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
548
+ await fs2.writeFile(tmp, JSON.stringify(body, null, 2) + "\n", "utf8");
549
+ await fs2.rename(tmp, file);
550
+ }
551
+ function parseRefKey(key) {
552
+ const parts = key.split("/");
553
+ if (parts.length !== 3) return null;
554
+ return {
555
+ org: parts[0],
556
+ type: parts[1],
557
+ name: parts[2]
558
+ };
559
+ }
560
+ function deferredIterable(promise) {
561
+ return {
562
+ [Symbol.asyncIterator]() {
563
+ let inner = null;
564
+ return {
565
+ async next() {
566
+ if (!inner) {
567
+ const iterable = await promise;
568
+ inner = iterable[Symbol.asyncIterator]();
569
+ }
570
+ return inner.next();
571
+ },
572
+ async return(value) {
573
+ if (!inner) {
574
+ const iterable = await promise;
575
+ inner = iterable[Symbol.asyncIterator]();
576
+ }
577
+ if (inner.return) return inner.return(value);
578
+ return { value: void 0, done: true };
579
+ }
580
+ };
581
+ }
582
+ };
583
+ }
584
+ export {
585
+ FileSystemRepository,
586
+ JsonlLog
587
+ };
588
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/repository.ts","../src/layout.ts","../src/jsonl-log.ts","../src/sync.ts","../src/watch-iterable.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * `FileSystemRepository` — Node-only implementation of\n * `MetadataRepository` backed by JSON files plus a JSONL change log.\n *\n * See `README.md` for the on-disk layout and ADR-0008 §10 PR-4 for the\n * design rationale.\n *\n * Invariants\n * ──────────\n * - All `put` / `delete` ops serialize per-key via `KeyedMutex`.\n * - The change-log JSONL is the durable source of `seq`. On boot we\n * scan the log to learn the next seq value.\n * - Body files (`<type>/<name>.json`) are the source of truth; the\n * log is a denormalised history index.\n * - chokidar-driven external edits are translated into MetadataEvents\n * by hashing the new content and comparing to the last-known hash.\n */\n\nimport fs from 'node:fs/promises';\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport type { FSWatcher } from 'chokidar';\nimport chokidar from 'chokidar';\nimport {\n type MetadataRepository,\n type MetaRef,\n type MetadataItem,\n type MetadataItemHeader,\n type MetadataEvent,\n type PutOptions,\n type PutResult,\n type DeleteOptions,\n type DeleteResult,\n type ListFilter,\n type WatchFilter,\n type HistoryOptions,\n type MetadataType,\n hashSpec,\n ConflictError,\n refKey,\n} from '@objectstack/metadata-core';\nimport {\n type FsLayout,\n itemPath,\n parseItemPath,\n typeDir,\n logDir,\n logFile,\n} from './layout.js';\nimport { JsonlLog } from './jsonl-log.js';\nimport { KeyedMutex, createBroker, type EventBroker } from './sync.js';\nimport { createWatchIterable } from './watch-iterable.js';\n\nexport interface FileSystemRepositoryOptions {\n /** Absolute path to the metadata root directory. */\n root: string;\n /** Tenant/org. */\n org: string;\n /** Identity reported in events that originate from external FS edits. */\n fsActor?: string;\n /** Disable chokidar watcher (e.g. for read-only contexts). */\n disableWatch?: boolean;\n /** Optional clock injection for deterministic tests. */\n now?: () => Date;\n}\n\nconst matchRefFilter = (\n ref: MetaRef,\n filter: { org?: string; type?: MetadataType; name?: string },\n): boolean => {\n if (filter.org && filter.org !== ref.org) return false;\n if (filter.type && filter.type !== ref.type) return false;\n if (filter.name && filter.name !== ref.name) return false;\n return true;\n};\n\nconst matchEvent = (evt: MetadataEvent, filter: WatchFilter): boolean => matchRefFilter(evt.ref, filter);\n\nexport class FileSystemRepository implements MetadataRepository {\n private readonly layout: FsLayout;\n private readonly org: string;\n private readonly fsActor: string;\n private readonly disableWatch: boolean;\n private readonly now: () => Date;\n private readonly log: JsonlLog;\n private readonly mutex = new KeyedMutex();\n private readonly broker: EventBroker = createBroker(matchEvent);\n\n /** In-memory index: refKey → current hash (HEAD). */\n private readonly heads = new Map<string, string>();\n /** Next seq counter, hydrated from the log on `start()`. */\n private nextSeq = 1;\n /** Paths we wrote ourselves; suppress the resulting chokidar event. */\n private readonly selfWrites = new Set<string>();\n private watcher: FSWatcher | null = null;\n private started = false;\n\n constructor(opts: FileSystemRepositoryOptions) {\n this.org = opts.org;\n this.fsActor = opts.fsActor ?? 'fs';\n this.disableWatch = opts.disableWatch ?? false;\n this.now = opts.now ?? (() => new Date());\n this.layout = { root: path.resolve(opts.root) };\n this.log = new JsonlLog(logFile(this.layout));\n }\n\n // ── Lifecycle ───────────────────────────────────────────────────────\n\n async start(): Promise<void> {\n if (this.started) return;\n this.started = true;\n await fs.mkdir(this.layout.root, { recursive: true });\n await fs.mkdir(logDir(this.layout), { recursive: true });\n\n // 1) Scan body files to build the head index.\n await this.scanHeads();\n\n // 2) Hydrate nextSeq from the existing log.\n const highest = await this.log.highestSeq();\n this.nextSeq = highest + 1;\n\n // 3) Start the watcher (unless disabled).\n if (!this.disableWatch) this.startWatcher();\n }\n\n async close(): Promise<void> {\n if (this.watcher) {\n await this.watcher.close();\n this.watcher = null;\n }\n this.started = false;\n }\n\n // ── Read API ────────────────────────────────────────────────────────\n\n async get(ref: MetaRef): Promise<MetadataItem | null> {\n this.assertScope(ref);\n const file = itemPath(this.layout, ref.type, ref.name);\n if (!existsSync(file)) return null;\n const body = await readJson(file);\n if (!body) return null;\n const hash = hashSpec(body);\n if (ref.version && ref.version !== hash) return null;\n // Walk back through the log to populate parent/authoredBy/seq.\n const meta = await this.findMetaForHash(ref, hash);\n return {\n ref: { ...ref, version: undefined },\n body: body as Record<string, unknown>,\n hash,\n parentHash: meta?.parentHash ?? null,\n authoredBy: meta?.actor ?? this.fsActor,\n authoredAt: meta?.ts ?? new Date(0).toISOString(),\n message: meta?.message,\n seq: meta?.seq ?? 0,\n };\n }\n\n async *list(filter: ListFilter): AsyncIterable<MetadataItemHeader> {\n const limit = filter.limit ?? Infinity;\n let yielded = 0;\n for (const [key, hash] of this.heads) {\n const ref = parseRefKey(key);\n if (!ref) continue;\n if (!matchRefFilter(ref, filter)) continue;\n if (filter.nameContains && !ref.name.includes(filter.nameContains)) continue;\n const meta = await this.findMetaForHash(ref, hash);\n const header: MetadataItemHeader = {\n ref: { ...ref, version: undefined },\n hash,\n parentHash: meta?.parentHash ?? null,\n authoredBy: meta?.actor ?? this.fsActor,\n authoredAt: meta?.ts ?? new Date(0).toISOString(),\n message: meta?.message,\n seq: meta?.seq ?? 0,\n };\n yield header;\n if (++yielded >= limit) return;\n }\n }\n\n async *history(ref: MetaRef, opts: HistoryOptions = {}): AsyncIterable<MetadataEvent> {\n this.assertScope(ref);\n const since = opts.sinceSeq ?? -1;\n const limit = opts.limit ?? Infinity;\n let yielded = 0;\n for await (const evt of this.log.readAll()) {\n if (evt.seq <= since) continue;\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n if (evt.ref.org !== ref.org) continue;\n yield evt;\n if (++yielded >= limit) return;\n }\n }\n\n watch(filter: WatchFilter, since?: number): AsyncIterable<MetadataEvent> {\n // Eagerly snapshot the existing log for replay; new events route via broker.\n const replay: MetadataEvent[] = [];\n const promise = (async () => {\n for await (const evt of this.log.readAll()) {\n if (matchEvent(evt, filter)) replay.push(evt);\n }\n })();\n // We must await replay before returning, but the public API is\n // sync-returning AsyncIterable. Wrap in a deferred iterable.\n return deferredIterable(promise.then(() =>\n createWatchIterable({\n filter,\n since,\n replay,\n broker: this.broker,\n matches: matchEvent,\n branchKeyOf: (e) => e.ref.org,\n }),\n ));\n }\n\n // ── Write API ───────────────────────────────────────────────────────\n\n put(ref: MetaRef, spec: unknown, opts: PutOptions): Promise<PutResult> {\n this.assertScope(ref);\n return this.mutex.run(refKey(ref), async () => {\n const key = refKey(ref);\n const currentHead = this.heads.get(key) ?? null;\n if ((opts.parentVersion ?? null) !== currentHead) {\n throw new ConflictError(ref, opts.parentVersion ?? null, currentHead);\n }\n const hash = hashSpec(spec);\n if (currentHead === hash) {\n // No-op write — same content.\n const meta = await this.findMetaForHash(ref, hash);\n return {\n version: hash,\n seq: meta?.seq ?? 0,\n item: {\n ref: { ...ref, version: undefined },\n body: spec as Record<string, unknown>,\n hash,\n parentHash: meta?.parentHash ?? null,\n authoredBy: meta?.actor ?? this.fsActor,\n authoredAt: meta?.ts ?? this.now().toISOString(),\n message: meta?.message,\n seq: meta?.seq ?? 0,\n },\n };\n }\n\n const seq = this.nextSeq++;\n const ts = this.now().toISOString();\n const file = itemPath(this.layout, ref.type, ref.name);\n await fs.mkdir(typeDir(this.layout, ref.type), { recursive: true });\n this.selfWrites.add(file);\n try {\n await writeJsonAtomic(file, spec);\n } finally {\n // Hold the suppression until chokidar has had a chance to emit;\n // we keep it in selfWrites for one debounce tick.\n setTimeout(() => this.selfWrites.delete(file), 200);\n }\n this.heads.set(key, hash);\n\n const evt: MetadataEvent = {\n seq,\n op: currentHead ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'fs',\n };\n await this.log.append(evt);\n this.broker.publish(evt);\n\n return {\n version: hash,\n seq,\n item: {\n ref: { ...ref, version: undefined },\n body: spec as Record<string, unknown>,\n hash,\n parentHash: currentHead,\n authoredBy: opts.actor,\n authoredAt: ts,\n message: opts.message,\n seq,\n },\n };\n });\n }\n\n delete(ref: MetaRef, opts: DeleteOptions): Promise<DeleteResult> {\n this.assertScope(ref);\n return this.mutex.run(refKey(ref), async () => {\n const key = refKey(ref);\n const currentHead = this.heads.get(key) ?? null;\n if (currentHead !== opts.parentVersion) {\n throw new ConflictError(ref, opts.parentVersion, currentHead);\n }\n const file = itemPath(this.layout, ref.type, ref.name);\n this.selfWrites.add(file);\n try {\n if (existsSync(file)) await fs.unlink(file);\n } finally {\n setTimeout(() => this.selfWrites.delete(file), 200);\n }\n this.heads.delete(key);\n const seq = this.nextSeq++;\n const ts = this.now().toISOString();\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: opts.actor,\n message: opts.message,\n ts,\n source: opts.source ?? 'fs',\n };\n await this.log.append(evt);\n this.broker.publish(evt);\n return { seq };\n });\n }\n\n // ── Internals ───────────────────────────────────────────────────────\n\n private assertScope(ref: MetaRef): void {\n if (ref.org !== this.org) {\n throw new Error(\n `FileSystemRepository scope mismatch: expected org=${this.org}, got org=${ref.org}`,\n );\n }\n }\n\n private async scanHeads(): Promise<void> {\n this.heads.clear();\n // Walk one level deep: <root>/<type>/<name>.json\n let entries: import('node:fs').Dirent[] = [];\n try {\n entries = await fs.readdir(this.layout.root, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (entry.name.startsWith('.')) continue;\n const type = entry.name;\n const dir = path.join(this.layout.root, type);\n let files: string[] = [];\n try {\n files = await fs.readdir(dir);\n } catch {\n continue;\n }\n for (const file of files) {\n if (!file.endsWith('.json')) continue;\n const name = file.slice(0, -'.json'.length);\n const ref: MetaRef = {\n org: this.org,\n type: type as MetadataType,\n name,\n };\n const body = await readJson(path.join(dir, file));\n if (!body) continue;\n this.heads.set(refKey(ref), hashSpec(body));\n }\n }\n }\n\n private async findMetaForHash(\n ref: MetaRef,\n hash: string,\n ): Promise<MetadataEvent | null> {\n let last: MetadataEvent | null = null;\n for await (const evt of this.log.readAll()) {\n if (evt.ref.type !== ref.type || evt.ref.name !== ref.name) continue;\n if (evt.ref.org !== ref.org) continue;\n if (evt.hash === hash) last = evt;\n }\n return last;\n }\n\n private startWatcher(): void {\n const w = chokidar.watch(this.layout.root, {\n ignored: [/(^|[\\\\/])\\../], // skip dotfiles incl. .objectstack\n ignoreInitial: true,\n depth: 2,\n awaitWriteFinish: { stabilityThreshold: 50, pollInterval: 20 },\n // Use polling to avoid `fs.watch` EMFILE on macOS / busy dev hosts.\n // The depth-2 recursion would otherwise wire native watches across\n // the entire customization tree.\n usePolling: true,\n interval: 1000,\n binaryInterval: 2000,\n });\n w.on('add', (p) => void this.handleFsChange(p, 'add'));\n w.on('change', (p) => void this.handleFsChange(p, 'change'));\n w.on('unlink', (p) => void this.handleFsChange(p, 'unlink'));\n this.watcher = w;\n }\n\n private async handleFsChange(absPath: string, kind: 'add' | 'change' | 'unlink'): Promise<void> {\n if (this.selfWrites.has(absPath)) return; // Suppress our own writes.\n const parsed = parseItemPath(this.layout, absPath);\n if (!parsed) return;\n const ref: MetaRef = {\n org: this.org,\n type: parsed.type as MetadataType,\n name: parsed.name,\n };\n const key = refKey(ref);\n await this.mutex.run(key, async () => {\n if (kind === 'unlink') {\n const currentHead = this.heads.get(key) ?? null;\n if (!currentHead) return;\n this.heads.delete(key);\n const seq = this.nextSeq++;\n const evt: MetadataEvent = {\n seq,\n op: 'delete',\n ref: { ...ref, version: undefined },\n hash: null,\n parentHash: currentHead,\n actor: this.fsActor,\n ts: this.now().toISOString(),\n source: 'fs',\n };\n await this.log.append(evt);\n this.broker.publish(evt);\n return;\n }\n const body = await readJson(absPath);\n if (!body) return;\n const hash = hashSpec(body);\n const currentHead = this.heads.get(key) ?? null;\n if (currentHead === hash) return; // No content change.\n this.heads.set(key, hash);\n const seq = this.nextSeq++;\n const evt: MetadataEvent = {\n seq,\n op: currentHead ? 'update' : 'create',\n ref: { ...ref, version: undefined },\n hash,\n parentHash: currentHead,\n actor: this.fsActor,\n ts: this.now().toISOString(),\n source: 'fs',\n };\n await this.log.append(evt);\n this.broker.publish(evt);\n });\n }\n}\n\n// ── Utilities ─────────────────────────────────────────────────────────\n\nasync function readJson(file: string): Promise<unknown | null> {\n try {\n const text = await fs.readFile(file, 'utf8');\n return JSON.parse(text);\n } catch {\n return null;\n }\n}\n\nasync function writeJsonAtomic(file: string, body: unknown): Promise<void> {\n const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;\n await fs.writeFile(tmp, JSON.stringify(body, null, 2) + '\\n', 'utf8');\n await fs.rename(tmp, file);\n}\n\nfunction parseRefKey(key: string): MetaRef | null {\n const parts = key.split('/');\n if (parts.length !== 3) return null;\n return {\n org: parts[0]!,\n type: parts[1]! as MetadataType,\n name: parts[2]!,\n };\n}\n\n/**\n * Wrap a Promise<AsyncIterable<T>> as a sync-returning AsyncIterable<T>.\n * The first `.next()` awaits the promise.\n */\nfunction deferredIterable<T>(promise: Promise<AsyncIterable<T>>): AsyncIterable<T> {\n return {\n [Symbol.asyncIterator]() {\n let inner: AsyncIterator<T> | null = null;\n return {\n async next() {\n if (!inner) {\n const iterable = await promise;\n inner = iterable[Symbol.asyncIterator]();\n }\n return inner.next();\n },\n async return(value?: unknown) {\n if (!inner) {\n const iterable = await promise;\n inner = iterable[Symbol.asyncIterator]();\n }\n if (inner.return) return inner.return(value);\n return { value: undefined, done: true };\n },\n } as AsyncIterator<T>;\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Disk layout helpers — see ADR-0008 §10 PR-4 / packages/metadata-fs README.\n *\n * <root>/<type>/<name>.json — canonical body\n * <root>/.objectstack/.log/main.jsonl — append-only change log\n */\n\nimport path from 'node:path';\nimport type { MetadataType } from '@objectstack/metadata-core';\n\nexport interface FsLayout {\n /** Absolute path to the metadata root. */\n root: string;\n}\n\nexport function itemPath(layout: FsLayout, type: MetadataType, name: string): string {\n return path.join(layout.root, type, `${name}.json`);\n}\n\nexport function typeDir(layout: FsLayout, type: MetadataType): string {\n return path.join(layout.root, type);\n}\n\nexport function logDir(layout: FsLayout): string {\n return path.join(layout.root, '.objectstack', '.log');\n}\n\nexport function logFile(layout: FsLayout): string {\n // Single change log per filesystem root (branching is a Git concern,\n // not a metadata-layer concern).\n return path.join(logDir(layout), `main.jsonl`);\n}\n\n/** Parse a path like \".../view/case_grid.json\" into {type, name}. */\nexport function parseItemPath(\n layout: FsLayout,\n absPath: string,\n): { type: string; name: string } | null {\n const rel = path.relative(layout.root, absPath);\n if (rel.startsWith('..') || rel.startsWith('.objectstack')) return null;\n const segments = rel.split(path.sep);\n if (segments.length !== 2) return null;\n const type = segments[0]!;\n const file = segments[1]!;\n if (!file.endsWith('.json')) return null;\n const name = file.slice(0, -'.json'.length);\n return { type, name };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Append-only JSONL change log writer / reader. Each line is a single\n * `MetadataEvent` serialized via `JSON.stringify`.\n *\n * Durability strategy\n * ───────────────────\n * - Append with `O_APPEND` semantics (Node's `fs.appendFile` is\n * atomic for sub-PIPE_BUF-sized writes; events are well under 4 KiB).\n * - Read by streaming the file line-by-line and JSON.parse-ing each.\n * - On a corrupt line we skip and continue — the body files are the\n * source of truth; the log is a denormalised history index.\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport readline from 'node:readline';\nimport { createReadStream, existsSync } from 'node:fs';\nimport type { MetadataEvent } from '@objectstack/metadata-core';\n\nexport class JsonlLog {\n constructor(private readonly file: string) {}\n\n async append(evt: MetadataEvent): Promise<void> {\n await fs.mkdir(path.dirname(this.file), { recursive: true });\n await fs.appendFile(this.file, JSON.stringify(evt) + '\\n', 'utf8');\n }\n\n /** Read all events in seq order (i.e. file order). */\n async *readAll(): AsyncIterable<MetadataEvent> {\n if (!existsSync(this.file)) return;\n const rl = readline.createInterface({\n input: createReadStream(this.file, { encoding: 'utf8' }),\n crlfDelay: Infinity,\n });\n try {\n for await (const line of rl) {\n if (!line.trim()) continue;\n try {\n yield JSON.parse(line) as MetadataEvent;\n } catch {\n // Skip corrupt line.\n }\n }\n } finally {\n rl.close();\n }\n }\n\n /** Return the highest seq number in the log, or 0 if empty. */\n async highestSeq(): Promise<number> {\n let max = 0;\n for await (const evt of this.readAll()) {\n if (typeof evt.seq === 'number' && evt.seq > max) max = evt.seq;\n }\n return max;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Mutex / event-broker primitives used by FileSystemRepository.\n *\n * `KeyedMutex` serializes operations on the same key (refKey). The\n * broker re-uses the same manual-AsyncIterator pattern as\n * InMemoryRepository so that consumer `return()` reliably unblocks.\n */\n\nimport type { MetadataEvent, WatchFilter } from '@objectstack/metadata-core';\n\nexport class KeyedMutex {\n private readonly tails = new Map<string, Promise<unknown>>();\n\n async run<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prev = this.tails.get(key) ?? Promise.resolve();\n const next = prev.then(fn, fn);\n // Save the swallowed-error tail so successive runs don't reject on\n // an unrelated prior failure.\n const swallowed = next.catch(() => undefined);\n this.tails.set(key, swallowed);\n try {\n return await next;\n } finally {\n // Best-effort cleanup: drop the entry if nothing newer was queued.\n if (this.tails.get(key) === swallowed) {\n this.tails.delete(key);\n }\n }\n }\n}\n\nexport interface BrokerSubscriber {\n filter: WatchFilter;\n closed: boolean;\n push(evt: MetadataEvent): void;\n}\n\nexport interface EventBroker {\n subscribe(sub: BrokerSubscriber): void;\n unsubscribe(sub: BrokerSubscriber): void;\n publish(evt: MetadataEvent): void;\n}\n\nexport function createBroker(matches: (evt: MetadataEvent, filter: WatchFilter) => boolean): EventBroker {\n const subs = new Set<BrokerSubscriber>();\n return {\n subscribe: (s) => { subs.add(s); },\n unsubscribe: (s) => { subs.delete(s); },\n publish: (evt) => {\n for (const s of subs) {\n if (s.closed) continue;\n if (!matches(evt, s.filter)) continue;\n s.push(evt);\n }\n },\n };\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Manual `AsyncIterator` factory for `repo.watch()`. Mirrors the\n * pattern used in `@objectstack/metadata-core`'s `InMemoryRepository`:\n * async generators do NOT run `finally` when paused on an unresolved\n * `await`, so we cannot use them to implement `watch()`.\n */\n\nimport type { MetadataEvent, WatchFilter } from '@objectstack/metadata-core';\nimport { type EventBroker, type BrokerSubscriber } from './sync.js';\n\nexport interface CreateWatchIteratorArgs {\n filter: WatchFilter;\n since: number | undefined;\n replay: MetadataEvent[];\n broker: EventBroker;\n /** Returns true if `evt.ref` matches `filter`. */\n matches: (evt: MetadataEvent, filter: WatchFilter) => boolean;\n branchKeyOf: (evt: MetadataEvent) => string;\n}\n\nexport function createWatchIterable(\n args: CreateWatchIteratorArgs,\n): AsyncIterable<MetadataEvent> {\n const queue: MetadataEvent[] = [];\n let waiter: ((evt: IteratorResult<MetadataEvent>) => void) | null = null;\n let closed = false;\n const delivered = new Set<string>();\n const evtKey = (e: MetadataEvent) => `${args.branchKeyOf(e)}#${e.seq}`;\n\n const subscriber: BrokerSubscriber = {\n filter: args.filter,\n closed: false,\n push: (evt) => {\n if (subscriber.closed) return;\n const k = evtKey(evt);\n if (delivered.has(k)) return;\n if (waiter) {\n delivered.add(k);\n const w = waiter;\n waiter = null;\n w({ value: clone(evt), done: false });\n } else {\n queue.push(evt);\n }\n },\n };\n args.broker.subscribe(subscriber);\n\n const replay = [...args.replay].sort((a, b) => a.seq - b.seq);\n let replayIdx = 0;\n\n const drain = (): IteratorResult<MetadataEvent> | null => {\n while (replayIdx < replay.length) {\n const evt = replay[replayIdx++]!;\n if (typeof args.since === 'number' && evt.seq <= args.since) continue;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n while (queue.length > 0) {\n const evt = queue.shift()!;\n const k = evtKey(evt);\n if (delivered.has(k)) continue;\n delivered.add(k);\n return { value: clone(evt), done: false };\n }\n return null;\n };\n\n const close = (): IteratorResult<MetadataEvent> => {\n if (!closed) {\n closed = true;\n subscriber.closed = true;\n args.broker.unsubscribe(subscriber);\n if (waiter) {\n const w = waiter;\n waiter = null;\n w({ value: undefined, done: true });\n }\n }\n return { value: undefined, done: true };\n };\n\n const iterator: AsyncIterator<MetadataEvent> = {\n next: () => {\n if (closed) return Promise.resolve({ value: undefined, done: true });\n const immediate = drain();\n if (immediate) return Promise.resolve(immediate);\n return new Promise<IteratorResult<MetadataEvent>>((resolve) => {\n waiter = resolve;\n });\n },\n return: () => Promise.resolve(close()),\n throw: (err) => {\n close();\n return Promise.reject(err);\n },\n };\n return { [Symbol.asyncIterator]: () => iterator };\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n"],"mappings":"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","names":["fs","existsSync","path","path","path","fs","existsSync","currentHead","seq","evt"]}