@hasna/mementos 0.14.74 → 0.14.78

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 (68) hide show
  1. package/bun.lock +93 -0
  2. package/dist/cli/commands/agent.d.ts.map +1 -1
  3. package/dist/cli/commands/info-context.d.ts.map +1 -1
  4. package/dist/cli/commands/info-stale.d.ts.map +1 -1
  5. package/dist/cli/commands/io-export.d.ts.map +1 -1
  6. package/dist/cli/commands/memory-cmd-crud.d.ts.map +1 -1
  7. package/dist/cli/commands/memory-cmd-list.d.ts.map +1 -1
  8. package/dist/cli/commands/memory-cmd-recall.d.ts.map +1 -1
  9. package/dist/cli/commands/memory-cmd-search.d.ts.map +1 -1
  10. package/dist/cli/commands/memory-cmd-tail.d.ts.map +1 -1
  11. package/dist/cli/commands/project.d.ts.map +1 -1
  12. package/dist/cli/commands/system-watch.d.ts.map +1 -1
  13. package/dist/cli/helpers.d.ts +38 -0
  14. package/dist/cli/helpers.d.ts.map +1 -1
  15. package/dist/cli/index.js +2077 -299
  16. package/dist/db/__fixtures__/fail-closed-stub-server.d.ts.map +1 -1
  17. package/dist/db/agents.d.ts +6 -0
  18. package/dist/db/agents.d.ts.map +1 -1
  19. package/dist/db/audit.d.ts +14 -0
  20. package/dist/db/audit.d.ts.map +1 -1
  21. package/dist/db/memory-project-link.d.ts +13 -0
  22. package/dist/db/memory-project-link.d.ts.map +1 -0
  23. package/dist/db/migrations.d.ts.map +1 -1
  24. package/dist/db/pg-migrations.d.ts +0 -6
  25. package/dist/db/pg-migrations.d.ts.map +1 -1
  26. package/dist/db/projects.d.ts +22 -1
  27. package/dist/db/projects.d.ts.map +1 -1
  28. package/dist/index.d.ts +5 -2
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +2909 -173
  31. package/dist/lib/package-version.d.ts +3 -0
  32. package/dist/lib/package-version.d.ts.map +1 -0
  33. package/dist/mcp/index.js +524 -10
  34. package/dist/memory-project-link/index.d.ts +2 -0
  35. package/dist/memory-project-link/index.d.ts.map +1 -0
  36. package/dist/memory-project-link/schema.d.ts +5 -0
  37. package/dist/memory-project-link/schema.d.ts.map +1 -0
  38. package/dist/project-registration/authority.d.ts +39 -0
  39. package/dist/project-registration/authority.d.ts.map +1 -0
  40. package/dist/project-registration/http.d.ts +29 -0
  41. package/dist/project-registration/http.d.ts.map +1 -0
  42. package/dist/project-registration/index.d.ts +8 -0
  43. package/dist/project-registration/index.d.ts.map +1 -0
  44. package/dist/project-registration/project-references.d.ts +71 -0
  45. package/dist/project-registration/project-references.d.ts.map +1 -0
  46. package/dist/project-registration/schema.d.ts +5 -0
  47. package/dist/project-registration/schema.d.ts.map +1 -0
  48. package/dist/project-registration/types.d.ts +168 -0
  49. package/dist/project-registration/types.d.ts.map +1 -0
  50. package/dist/project-registration.d.ts +2 -0
  51. package/dist/project-registration.d.ts.map +1 -0
  52. package/dist/project-registration.js +1496 -0
  53. package/dist/sdk/index.d.ts +127 -0
  54. package/dist/sdk/index.d.ts.map +1 -1
  55. package/dist/sdk/index.js +74 -0
  56. package/dist/server/index.d.ts +1 -0
  57. package/dist/server/index.d.ts.map +1 -1
  58. package/dist/server/index.js +2667 -149
  59. package/dist/server/routes/memories.d.ts +1 -0
  60. package/dist/server/routes/memories.d.ts.map +1 -1
  61. package/dist/server/routes/memory-project-link.d.ts +2 -0
  62. package/dist/server/routes/memory-project-link.d.ts.map +1 -0
  63. package/dist/server/routes/project-registration.d.ts +2 -0
  64. package/dist/server/routes/project-registration.d.ts.map +1 -0
  65. package/dist/types/index.d.ts +119 -0
  66. package/dist/types/index.d.ts.map +1 -1
  67. package/package.json +9 -3
  68. package/dashboard/bun.lock +0 -519
@@ -0,0 +1,1496 @@
1
+ // @bun
2
+ var __create = Object.create;
3
+ var __getProtoOf = Object.getPrototypeOf;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
12
+ var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
20
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
21
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
22
+ for (let key of __getOwnPropNames(mod))
23
+ if (!__hasOwnProp.call(to, key))
24
+ __defProp(to, key, {
25
+ get: __accessProp.bind(mod, key),
26
+ enumerable: true
27
+ });
28
+ if (canCache)
29
+ cache.set(mod, to);
30
+ return to;
31
+ };
32
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
37
+ var __export = (target, all) => {
38
+ for (var name in all)
39
+ __defProp(target, name, {
40
+ get: all[name],
41
+ enumerable: true,
42
+ configurable: true,
43
+ set: __exportSetter.bind(all, name)
44
+ });
45
+ };
46
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
+ var __require = import.meta.require;
48
+
49
+ // src/project-registration/schema.ts
50
+ function sqliteMementosProjectRegistrationSchemaSql() {
51
+ return `
52
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_receipts (
53
+ receipt_id TEXT PRIMARY KEY,
54
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
55
+ route TEXT NOT NULL,
56
+ package_version TEXT NOT NULL,
57
+ authority_id TEXT NOT NULL,
58
+ tenant_id TEXT NOT NULL,
59
+ corpus_id TEXT NOT NULL,
60
+ operation_id TEXT NOT NULL,
61
+ step_id TEXT NOT NULL,
62
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
63
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
64
+ target_selector TEXT NOT NULL,
65
+ idempotency_key TEXT NOT NULL,
66
+ request_digest TEXT NOT NULL,
67
+ precondition_digest TEXT NOT NULL,
68
+ normalized_call_digest TEXT NOT NULL,
69
+ outcome TEXT NOT NULL CHECK(outcome IN (
70
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
71
+ )),
72
+ reason TEXT,
73
+ target_id TEXT,
74
+ result_revision TEXT,
75
+ result_digest TEXT,
76
+ duplicate_of_receipt_id TEXT,
77
+ accepted_receipt_id TEXT,
78
+ created_by_operation INTEGER NOT NULL CHECK(created_by_operation IN (0, 1)),
79
+ created_at TEXT NOT NULL
80
+ );
81
+
82
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_lookup
83
+ ON mementos_project_registration_receipts (
84
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
85
+ resource_kind, direction, idempotency_key, target_selector
86
+ );
87
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_accepted_step
88
+ ON mementos_project_registration_receipts (
89
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
90
+ resource_kind, direction
91
+ )
92
+ WHERE outcome = 'accepted';
93
+
94
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_bindings (
95
+ authority_id TEXT NOT NULL,
96
+ tenant_id TEXT NOT NULL,
97
+ corpus_id TEXT NOT NULL,
98
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
99
+ target_selector TEXT NOT NULL,
100
+ operation_id TEXT NOT NULL,
101
+ step_id TEXT NOT NULL,
102
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
103
+ idempotency_key TEXT NOT NULL,
104
+ request_digest TEXT NOT NULL,
105
+ precondition_digest TEXT NOT NULL,
106
+ normalized_call_digest TEXT NOT NULL,
107
+ state TEXT NOT NULL CHECK(state IN (
108
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
109
+ )),
110
+ target_id TEXT,
111
+ accepted_receipt_id TEXT,
112
+ result_revision TEXT,
113
+ result_digest TEXT,
114
+ removed_receipt_id TEXT,
115
+ created_at TEXT NOT NULL,
116
+ updated_at TEXT NOT NULL,
117
+ PRIMARY KEY(authority_id, tenant_id, corpus_id, resource_kind, target_selector),
118
+ UNIQUE(accepted_receipt_id)
119
+ );
120
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_binding_target
121
+ ON mementos_project_registration_bindings(
122
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
123
+ )
124
+ WHERE target_id IS NOT NULL;
125
+
126
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_update
127
+ BEFORE UPDATE ON mementos_project_registration_receipts
128
+ BEGIN
129
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
130
+ END;
131
+
132
+ CREATE TRIGGER IF NOT EXISTS mementos_project_registration_receipts_immutable_delete
133
+ BEFORE DELETE ON mementos_project_registration_receipts
134
+ BEGIN
135
+ SELECT RAISE(ABORT, 'mementos project registration receipts are immutable');
136
+ END;
137
+ `;
138
+ }
139
+ function postgresMementosProjectRegistrationSchemaSql() {
140
+ return `
141
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_receipts (
142
+ receipt_id TEXT PRIMARY KEY,
143
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
144
+ route TEXT NOT NULL,
145
+ package_version TEXT NOT NULL,
146
+ authority_id TEXT NOT NULL,
147
+ tenant_id TEXT NOT NULL,
148
+ corpus_id TEXT NOT NULL,
149
+ operation_id TEXT NOT NULL,
150
+ step_id TEXT NOT NULL,
151
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
152
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'inverse')),
153
+ target_selector TEXT NOT NULL,
154
+ idempotency_key TEXT NOT NULL,
155
+ request_digest TEXT NOT NULL,
156
+ precondition_digest TEXT NOT NULL,
157
+ normalized_call_digest TEXT NOT NULL,
158
+ outcome TEXT NOT NULL CHECK(outcome IN (
159
+ 'accepted', 'duplicate_of_accepted', 'terminal_nonacceptance'
160
+ )),
161
+ reason TEXT,
162
+ target_id TEXT,
163
+ result_revision TEXT,
164
+ result_digest TEXT,
165
+ duplicate_of_receipt_id TEXT,
166
+ accepted_receipt_id TEXT,
167
+ created_by_operation BOOLEAN NOT NULL,
168
+ created_at TIMESTAMPTZ NOT NULL
169
+ );
170
+
171
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_lookup
172
+ ON mementos_project_registration_receipts (
173
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
174
+ resource_kind, direction, idempotency_key, target_selector
175
+ );
176
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_receipts_accepted_step
177
+ ON mementos_project_registration_receipts (
178
+ authority_id, tenant_id, corpus_id, operation_id, step_id,
179
+ resource_kind, direction
180
+ )
181
+ WHERE outcome = 'accepted';
182
+
183
+ CREATE TABLE IF NOT EXISTS mementos_project_registration_bindings (
184
+ authority_id TEXT NOT NULL,
185
+ tenant_id TEXT NOT NULL,
186
+ corpus_id TEXT NOT NULL,
187
+ resource_kind TEXT NOT NULL CHECK(resource_kind = 'project'),
188
+ target_selector TEXT NOT NULL,
189
+ operation_id TEXT NOT NULL,
190
+ step_id TEXT NOT NULL,
191
+ direction TEXT NOT NULL CHECK(direction = 'forward'),
192
+ idempotency_key TEXT NOT NULL,
193
+ request_digest TEXT NOT NULL,
194
+ precondition_digest TEXT NOT NULL,
195
+ normalized_call_digest TEXT NOT NULL,
196
+ state TEXT NOT NULL CHECK(state IN (
197
+ 'pending', 'accepted', 'terminal_nonacceptance', 'removed'
198
+ )),
199
+ target_id TEXT,
200
+ accepted_receipt_id TEXT UNIQUE,
201
+ result_revision TEXT,
202
+ result_digest TEXT,
203
+ removed_receipt_id TEXT,
204
+ created_at TIMESTAMPTZ NOT NULL,
205
+ updated_at TIMESTAMPTZ NOT NULL,
206
+ PRIMARY KEY(authority_id, tenant_id, corpus_id, resource_kind, target_selector)
207
+ );
208
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_mementos_project_registration_binding_target
209
+ ON mementos_project_registration_bindings(
210
+ authority_id, tenant_id, corpus_id, resource_kind, target_id
211
+ )
212
+ WHERE target_id IS NOT NULL;
213
+
214
+ CREATE OR REPLACE FUNCTION mementos_project_registration_receipts_immutable()
215
+ RETURNS trigger
216
+ LANGUAGE plpgsql
217
+ AS $$
218
+ BEGIN
219
+ RAISE EXCEPTION 'mementos project registration receipts are immutable';
220
+ END;
221
+ $$;
222
+ DROP TRIGGER IF EXISTS mementos_project_registration_receipts_immutable
223
+ ON mementos_project_registration_receipts;
224
+ CREATE TRIGGER mementos_project_registration_receipts_immutable
225
+ BEFORE UPDATE OR DELETE ON mementos_project_registration_receipts
226
+ FOR EACH ROW EXECUTE FUNCTION mementos_project_registration_receipts_immutable();
227
+ `;
228
+ }
229
+ function sqliteMementosProjectGuardedUpdateSchemaSql() {
230
+ return `
231
+ CREATE TABLE IF NOT EXISTS mementos_project_update_receipts (
232
+ receipt_id TEXT PRIMARY KEY,
233
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
234
+ route TEXT NOT NULL CHECK(route = 'mementos.project-guarded-update.v1'),
235
+ package_version TEXT NOT NULL,
236
+ authority_id TEXT NOT NULL,
237
+ tenant_id TEXT NOT NULL,
238
+ corpus_id TEXT NOT NULL,
239
+ operation_id TEXT NOT NULL,
240
+ step_id TEXT NOT NULL,
241
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
242
+ idempotency_key TEXT NOT NULL,
243
+ request_digest TEXT NOT NULL,
244
+ outcome TEXT NOT NULL CHECK(outcome = 'accepted'),
245
+ target_id TEXT NOT NULL,
246
+ expected_revision TEXT NOT NULL,
247
+ result_revision TEXT NOT NULL,
248
+ result_digest TEXT NOT NULL,
249
+ accepted_receipt_id TEXT,
250
+ before_project_json TEXT NOT NULL,
251
+ after_project_json TEXT NOT NULL,
252
+ created_at TEXT NOT NULL,
253
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
254
+ );
255
+
256
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_update_receipts_target
257
+ ON mementos_project_update_receipts(
258
+ authority_id, tenant_id, corpus_id, target_id, created_at
259
+ );
260
+
261
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_update
262
+ BEFORE UPDATE ON mementos_project_update_receipts
263
+ BEGIN
264
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
265
+ END;
266
+
267
+ CREATE TRIGGER IF NOT EXISTS mementos_project_update_receipts_immutable_delete
268
+ BEFORE DELETE ON mementos_project_update_receipts
269
+ BEGIN
270
+ SELECT RAISE(ABORT, 'mementos project update receipts are immutable');
271
+ END;
272
+ `;
273
+ }
274
+ function postgresMementosProjectGuardedUpdateSchemaSql() {
275
+ return `
276
+ CREATE TABLE IF NOT EXISTS mementos_project_update_receipts (
277
+ receipt_id TEXT PRIMARY KEY,
278
+ authority TEXT NOT NULL CHECK(authority = 'mementos'),
279
+ route TEXT NOT NULL CHECK(route = 'mementos.project-guarded-update.v1'),
280
+ package_version TEXT NOT NULL,
281
+ authority_id TEXT NOT NULL,
282
+ tenant_id TEXT NOT NULL,
283
+ corpus_id TEXT NOT NULL,
284
+ operation_id TEXT NOT NULL,
285
+ step_id TEXT NOT NULL,
286
+ direction TEXT NOT NULL CHECK(direction IN ('forward', 'rollback')),
287
+ idempotency_key TEXT NOT NULL,
288
+ request_digest TEXT NOT NULL,
289
+ outcome TEXT NOT NULL CHECK(outcome = 'accepted'),
290
+ target_id TEXT NOT NULL,
291
+ expected_revision TEXT NOT NULL,
292
+ result_revision TEXT NOT NULL,
293
+ result_digest TEXT NOT NULL,
294
+ accepted_receipt_id TEXT,
295
+ before_project_json JSONB NOT NULL,
296
+ after_project_json JSONB NOT NULL,
297
+ created_at TIMESTAMPTZ NOT NULL,
298
+ UNIQUE(authority_id, tenant_id, corpus_id, direction, idempotency_key)
299
+ );
300
+
301
+ CREATE INDEX IF NOT EXISTS idx_mementos_project_update_receipts_target
302
+ ON mementos_project_update_receipts(
303
+ authority_id, tenant_id, corpus_id, target_id, created_at
304
+ );
305
+
306
+ CREATE OR REPLACE FUNCTION mementos_project_update_receipts_immutable()
307
+ RETURNS trigger
308
+ LANGUAGE plpgsql
309
+ AS $$
310
+ BEGIN
311
+ RAISE EXCEPTION 'mementos project update receipts are immutable';
312
+ END;
313
+ $$;
314
+ DROP TRIGGER IF EXISTS mementos_project_update_receipts_immutable
315
+ ON mementos_project_update_receipts;
316
+ CREATE TRIGGER mementos_project_update_receipts_immutable
317
+ BEFORE UPDATE OR DELETE ON mementos_project_update_receipts
318
+ FOR EACH ROW EXECUTE FUNCTION mementos_project_update_receipts_immutable();
319
+ `;
320
+ }
321
+
322
+ // src/project-registration/authority.ts
323
+ import { createHash } from "crypto";
324
+ import { resolve } from "path";
325
+
326
+ // src/lib/package-version.ts
327
+ import { readFileSync } from "fs";
328
+ import { dirname, join } from "path";
329
+ import { fileURLToPath } from "url";
330
+ function getMementosPackageVersion() {
331
+ const here = dirname(fileURLToPath(import.meta.url));
332
+ for (const candidate of [
333
+ join(here, "..", "..", "package.json"),
334
+ join(here, "..", "package.json")
335
+ ]) {
336
+ try {
337
+ const parsed = JSON.parse(readFileSync(candidate, "utf8"));
338
+ if (typeof parsed.version === "string" && parsed.version.trim())
339
+ return parsed.version;
340
+ } catch {}
341
+ }
342
+ return "0.0.0";
343
+ }
344
+
345
+ // src/project-registration/project-references.ts
346
+ var MEMENTOS_PROJECT_REFERENCE_SURFACES = [
347
+ { key: "memories", table: "memories", column: "project_id" },
348
+ { key: "sessions", table: "sessions", column: "project_id" },
349
+ { key: "entities", table: "entities", column: "project_id" },
350
+ { key: "agents", table: "agents", column: "active_project_id" },
351
+ { key: "tool_events", table: "tool_events", column: "project_id" },
352
+ { key: "tasks", table: "tasks", column: "project_id" },
353
+ {
354
+ key: "memory_consolidation_runs",
355
+ table: "memory_consolidation_runs",
356
+ column: "project_id"
357
+ },
358
+ {
359
+ key: "memory_reflection_runs",
360
+ table: "memory_reflection_runs",
361
+ column: "project_id"
362
+ },
363
+ { key: "memory_acl", table: "memory_acl", column: "project_id" },
364
+ { key: "search_history", table: "search_history", column: "project_id" },
365
+ {
366
+ key: "session_memory_jobs",
367
+ table: "session_memory_jobs",
368
+ column: "project_id"
369
+ },
370
+ { key: "synthesis_events", table: "synthesis_events", column: "project_id" },
371
+ { key: "synthesis_runs", table: "synthesis_runs", column: "project_id" },
372
+ { key: "webhook_hooks", table: "webhook_hooks", column: "project_id" }
373
+ ];
374
+ function quotedIdentifier(value) {
375
+ if (!/^[a-z_][a-z0-9_]*$/.test(value)) {
376
+ throw new Error(`unsafe project-reference identifier: ${value}`);
377
+ }
378
+ return `"${value}"`;
379
+ }
380
+ function mementosProjectReferenceCounts(db, projectId) {
381
+ const projections = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key, table, column }) => `(SELECT COUNT(*) FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?) AS ${quotedIdentifier(key)}`).join(`,
382
+ `);
383
+ const row = db.get(`SELECT
384
+ ${projections}`, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
385
+ return Object.fromEntries(MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ key }) => [key, Number(row[key] ?? 0)]));
386
+ }
387
+ function hasMementosProjectReferences(counts) {
388
+ return MEMENTOS_PROJECT_REFERENCE_SURFACES.some(({ key }) => counts[key] > 0);
389
+ }
390
+ function deleteMementosProjectIfUnreferenced(db, projectId) {
391
+ const guards = MEMENTOS_PROJECT_REFERENCE_SURFACES.map(({ table, column }) => `NOT EXISTS (SELECT 1 FROM ${quotedIdentifier(table)} ` + `WHERE ${quotedIdentifier(column)} = ?)`).join(`
392
+ AND `);
393
+ const result = db.run(`DELETE FROM projects
394
+ WHERE id = ?
395
+ AND ${guards}`, projectId, ...MEMENTOS_PROJECT_REFERENCE_SURFACES.map(() => projectId));
396
+ return result.changes;
397
+ }
398
+
399
+ // src/project-registration/types.ts
400
+ var MEMENTOS_PROJECT_REGISTRATION_ROUTE = "mementos.project-registration.v1";
401
+ var MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE = "projects.full-registration.v1";
402
+ var MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION = 1;
403
+ var MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE = "mementos.project-guarded-update.v1";
404
+
405
+ class MementosProjectRegistrationError extends Error {
406
+ code;
407
+ details;
408
+ constructor(code, message, details = {}) {
409
+ super(message);
410
+ this.code = code;
411
+ this.details = details;
412
+ this.name = "MementosProjectRegistrationError";
413
+ }
414
+ }
415
+
416
+ // src/project-registration/authority.ts
417
+ var WORKSPACE_ID_PATTERN = /^wks_[A-Za-z0-9][A-Za-z0-9_-]{11,}$/;
418
+ var OPERATION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{7,127}$/;
419
+ var STEP_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{2,127}$/;
420
+ var SHA256_PATTERN = /^[0-9a-f]{64}$/;
421
+ var IDEMPOTENCY_PATTERN = /^prk_[0-9a-f]{48}$/;
422
+ var PROJECT_ID_PATTERN = /^mm_project_[0-9a-f]{40}$/;
423
+
424
+ class WriteBoundaryError extends Error {
425
+ point;
426
+ cause;
427
+ constructor(point, cause) {
428
+ super(`Mementos project registration failed at ${point}`);
429
+ this.point = point;
430
+ this.cause = cause;
431
+ }
432
+ }
433
+ function canonicalMementosProjectRegistrationJson(value) {
434
+ return JSON.stringify(canonicalize(value));
435
+ }
436
+ function canonicalize(value) {
437
+ if (Array.isArray(value))
438
+ return value.map(canonicalize);
439
+ if (!value || typeof value !== "object")
440
+ return value;
441
+ const out = {};
442
+ for (const key of Object.keys(value).sort()) {
443
+ const item = value[key];
444
+ if (item !== undefined)
445
+ out[key] = canonicalize(item);
446
+ }
447
+ return out;
448
+ }
449
+ function digestMementosProjectRegistrationValue(value) {
450
+ return createHash("sha256").update(canonicalMementosProjectRegistrationJson(value)).digest("hex");
451
+ }
452
+ function digestOwnedPath(path) {
453
+ return createHash("sha256").update(path).digest("hex");
454
+ }
455
+ function deriveMementosProjectRegistrationIdempotencyKey(input) {
456
+ return `prk_${digestMementosProjectRegistrationValue({
457
+ route: MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
458
+ operation_id: input.operation_id,
459
+ step_id: input.step_id,
460
+ direction: input.direction,
461
+ target_selector: input.target_selector,
462
+ request_digest: input.request_digest,
463
+ precondition_digest: input.precondition_digest
464
+ }).slice(0, 48)}`;
465
+ }
466
+ function responseBytes(value) {
467
+ return Buffer.byteLength(JSON.stringify(value), "utf8");
468
+ }
469
+ function assertBounds(bounds) {
470
+ if (!Number.isSafeInteger(bounds.response_byte_limit) || bounds.response_byte_limit <= 0) {
471
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "response_byte_limit must be a positive integer");
472
+ }
473
+ if (!Number.isSafeInteger(bounds.time_budget_ms) || bounds.time_budget_ms <= 0) {
474
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "time_budget_ms must be a positive integer");
475
+ }
476
+ }
477
+ function assertWithinBounds(value, bounds, startedAt) {
478
+ const bytes = responseBytes(value);
479
+ if (bytes > bounds.response_byte_limit) {
480
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE", `registration response requires ${bytes} bytes but the bound is ${bounds.response_byte_limit}`, { response_bytes: bytes, response_byte_limit: bounds.response_byte_limit });
481
+ }
482
+ const elapsed = Date.now() - startedAt;
483
+ if (elapsed > bounds.time_budget_ms) {
484
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED", `registration call took ${elapsed}ms but the bound is ${bounds.time_budget_ms}ms`, { elapsed_ms: elapsed, time_budget_ms: bounds.time_budget_ms });
485
+ }
486
+ return { response_bytes: bytes, elapsed_ms: elapsed };
487
+ }
488
+ function withResponseControl(receipt, bounds, startedAt) {
489
+ const result = {
490
+ receipt,
491
+ response_control: {
492
+ response_byte_limit: bounds.response_byte_limit,
493
+ time_budget_ms: bounds.time_budget_ms,
494
+ response_bytes: 0,
495
+ elapsed_ms: 0,
496
+ complete: true,
497
+ truncated: false
498
+ }
499
+ };
500
+ for (let attempt = 0;attempt < 8; attempt += 1) {
501
+ const measured2 = assertWithinBounds(result, bounds, startedAt);
502
+ const stable = result.response_control.response_bytes === measured2.response_bytes && result.response_control.elapsed_ms === measured2.elapsed_ms;
503
+ result.response_control.response_bytes = measured2.response_bytes;
504
+ result.response_control.elapsed_ms = measured2.elapsed_ms;
505
+ if (stable)
506
+ break;
507
+ }
508
+ const measured = assertWithinBounds(result, bounds, startedAt);
509
+ result.response_control.response_bytes = measured.response_bytes;
510
+ result.response_control.elapsed_ms = measured.elapsed_ms;
511
+ return result;
512
+ }
513
+ function requireString(value, field, options = {}) {
514
+ const min = options.min ?? 1;
515
+ const max = options.max ?? 512;
516
+ if (typeof value !== "string" || value.length < min || value.length > max || /[\u0000-\u001f]/.test(value) || options.pattern && !options.pattern.test(value)) {
517
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} is not a valid bounded registration identifier`);
518
+ }
519
+ return value;
520
+ }
521
+ function exactKeys(value, expected, field) {
522
+ const actual = Object.keys(value).sort();
523
+ const wanted = [...expected].sort();
524
+ if (actual.length !== wanted.length || actual.some((key, i) => key !== wanted[i])) {
525
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", `${field} must contain exactly: ${wanted.join(", ")}`);
526
+ }
527
+ }
528
+ function ownedPath(target) {
529
+ if (!target || typeof target.withOwnedPath !== "function") {
530
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target must be a package-owned path handle");
531
+ }
532
+ const path = target.withOwnedPath((value) => value);
533
+ requireString(path, "target path", { max: 4096 });
534
+ if (path !== resolve(path)) {
535
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target path must already be canonical and absolute");
536
+ }
537
+ return path;
538
+ }
539
+ function normalizedCallDigest(request) {
540
+ return digestMementosProjectRegistrationValue({
541
+ authority_route: request.authority_route,
542
+ package_version: request.package_version,
543
+ authority_id: request.authority_id,
544
+ tenant_id: request.tenant_id,
545
+ corpus_id: request.corpus_id,
546
+ operation_id: request.operation_id,
547
+ step_id: request.step_id,
548
+ resource_kind: request.resource_kind,
549
+ direction: request.direction,
550
+ target_selector: request.target_selector,
551
+ idempotency_key: request.idempotency_key,
552
+ request_digest: request.request_digest,
553
+ precondition_digest: request.precondition_digest,
554
+ project_id: request.project_id,
555
+ project_slug: request.project_slug,
556
+ project_name: request.project_name,
557
+ desired: request.desired,
558
+ accepted_receipt_id: request.accepted_receipt?.receipt_id ?? null
559
+ });
560
+ }
561
+ function publicReceipt(row) {
562
+ const { target_selector: _selector, normalized_call_digest: _digest, ...receipt } = row;
563
+ return receipt;
564
+ }
565
+ function timestampString(value) {
566
+ return value instanceof Date ? value.toISOString() : String(value);
567
+ }
568
+ function receiptFromRow(row) {
569
+ return {
570
+ ...row,
571
+ authority: "mementos",
572
+ resource_kind: "project",
573
+ created_by_operation: row["created_by_operation"] === true || Number(row["created_by_operation"]) === 1,
574
+ created_at: timestampString(row["created_at"])
575
+ };
576
+ }
577
+ function bindingFromRow(row) {
578
+ return {
579
+ ...row,
580
+ created_at: timestampString(row["created_at"]),
581
+ updated_at: timestampString(row["updated_at"])
582
+ };
583
+ }
584
+ function getStoredReceipt(db, receiptId) {
585
+ const row = db.get("SELECT * FROM mementos_project_registration_receipts WHERE receipt_id = ? LIMIT 1", receiptId);
586
+ return row ? receiptFromRow(row) : null;
587
+ }
588
+ function getAcceptedReceipt(db, request) {
589
+ const row = db.get(`
590
+ SELECT * FROM mementos_project_registration_receipts
591
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
592
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
593
+ AND direction = ? AND outcome = 'accepted'
594
+ ORDER BY created_at ASC, receipt_id ASC
595
+ LIMIT 1
596
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction);
597
+ return row ? receiptFromRow(row) : null;
598
+ }
599
+ function getReceiptForLookup(db, request) {
600
+ const row = db.get(`
601
+ SELECT * FROM mementos_project_registration_receipts
602
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
603
+ AND operation_id = ? AND step_id = ? AND resource_kind = 'project'
604
+ AND direction = ? AND idempotency_key = ? AND target_selector = ?
605
+ ORDER BY CASE outcome
606
+ WHEN 'terminal_nonacceptance' THEN 0
607
+ WHEN 'duplicate_of_accepted' THEN 1
608
+ ELSE 2
609
+ END, created_at DESC, receipt_id DESC
610
+ LIMIT 1
611
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.operation_id, request.step_id, request.direction, request.idempotency_key, request.target_selector);
612
+ return row ? receiptFromRow(row) : null;
613
+ }
614
+ function getBinding(db, capability, targetSelector) {
615
+ const row = db.get(`
616
+ SELECT * FROM mementos_project_registration_bindings
617
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
618
+ AND resource_kind = 'project' AND target_selector = ?
619
+ LIMIT 1
620
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
621
+ return row ? bindingFromRow(row) : null;
622
+ }
623
+ function getProjectByExactId(db, id) {
624
+ const row = db.get("SELECT * FROM projects WHERE id = ? LIMIT 1", id);
625
+ if (!row)
626
+ return null;
627
+ return {
628
+ id: String(row["id"]),
629
+ name: String(row["name"]),
630
+ path: String(row["path"]),
631
+ description: row["description"] === null ? null : String(row["description"]),
632
+ memory_prefix: row["memory_prefix"] === null ? null : String(row["memory_prefix"]),
633
+ created_at: timestampString(row["created_at"]),
634
+ updated_at: timestampString(row["updated_at"])
635
+ };
636
+ }
637
+ function getProjectByPath(db, path) {
638
+ const row = db.get("SELECT * FROM projects WHERE path = ? LIMIT 1", path);
639
+ return row ? getProjectByExactId(db, String(row["id"])) : null;
640
+ }
641
+ function projectRecord(db, project, references = mementosProjectReferenceCounts(db, project.id)) {
642
+ return {
643
+ target_id: project.id,
644
+ revision: project.updated_at,
645
+ digest: digestMementosProjectRegistrationValue({
646
+ id: project.id,
647
+ name: project.name,
648
+ path: project.path,
649
+ description: project.description,
650
+ memory_prefix: project.memory_prefix,
651
+ created_at: project.created_at,
652
+ updated_at: project.updated_at,
653
+ references
654
+ })
655
+ };
656
+ }
657
+ function targetId(capability, selector) {
658
+ return `mm_project_${digestMementosProjectRegistrationValue({
659
+ authority_id: capability.authority_id,
660
+ tenant_id: capability.tenant_id,
661
+ corpus_id: capability.corpus_id,
662
+ selector
663
+ }).slice(0, 40)}`;
664
+ }
665
+ function receiptId(input) {
666
+ return `mmpr_${digestMementosProjectRegistrationValue(input).slice(0, 40)}`;
667
+ }
668
+ function makeReceipt(request, capability, callDigest, now, values) {
669
+ const withoutId = {
670
+ authority: "mementos",
671
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
672
+ package_version: capability.package_version,
673
+ authority_id: capability.authority_id,
674
+ tenant_id: capability.tenant_id,
675
+ corpus_id: capability.corpus_id,
676
+ operation_id: request.operation_id,
677
+ step_id: request.step_id,
678
+ resource_kind: "project",
679
+ direction: request.direction,
680
+ target_selector: values.target_selector ?? request.target_selector,
681
+ idempotency_key: request.idempotency_key,
682
+ request_digest: request.request_digest,
683
+ precondition_digest: request.precondition_digest,
684
+ normalized_call_digest: callDigest,
685
+ outcome: values.outcome,
686
+ reason: values.reason ?? null,
687
+ target_id: values.target_id ?? null,
688
+ result_revision: values.result_revision ?? null,
689
+ result_digest: values.result_digest ?? null,
690
+ duplicate_of_receipt_id: values.duplicate_of_receipt_id ?? null,
691
+ accepted_receipt_id: values.accepted_receipt_id ?? null,
692
+ created_by_operation: values.created_by_operation ?? false
693
+ };
694
+ return {
695
+ receipt_id: receiptId(withoutId),
696
+ ...withoutId,
697
+ created_at: now
698
+ };
699
+ }
700
+ function insertReceipt(db, receipt) {
701
+ db.run(`
702
+ INSERT INTO mementos_project_registration_receipts (
703
+ receipt_id, authority, route, package_version, authority_id, tenant_id,
704
+ corpus_id, operation_id, step_id, resource_kind, direction,
705
+ target_selector, idempotency_key, request_digest, precondition_digest,
706
+ normalized_call_digest, outcome, reason, target_id, result_revision,
707
+ result_digest, duplicate_of_receipt_id, accepted_receipt_id,
708
+ created_by_operation, created_at
709
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
710
+ ON CONFLICT DO NOTHING
711
+ `, receipt.receipt_id, receipt.authority, receipt.route, receipt.package_version, receipt.authority_id, receipt.tenant_id, receipt.corpus_id, receipt.operation_id, receipt.step_id, receipt.resource_kind, receipt.direction, receipt.target_selector, receipt.idempotency_key, receipt.request_digest, receipt.precondition_digest, receipt.normalized_call_digest, receipt.outcome, receipt.reason, receipt.target_id, receipt.result_revision, receipt.result_digest, receipt.duplicate_of_receipt_id, receipt.accepted_receipt_id, receipt.created_by_operation, receipt.created_at);
712
+ const stored = getStoredReceipt(db, receipt.receipt_id);
713
+ const withoutCreatedAt = (value) => {
714
+ const { created_at: _createdAt, ...logical } = value;
715
+ return logical;
716
+ };
717
+ if (!stored || canonicalMementosProjectRegistrationJson(withoutCreatedAt(stored)) !== canonicalMementosProjectRegistrationJson(withoutCreatedAt(receipt))) {
718
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "immutable receipt id resolved to different content");
719
+ }
720
+ return stored;
721
+ }
722
+ function insertBinding(db, request, callDigest, now) {
723
+ const result = db.run(`
724
+ INSERT INTO mementos_project_registration_bindings (
725
+ authority_id, tenant_id, corpus_id, resource_kind, target_selector,
726
+ operation_id, step_id, direction, idempotency_key, request_digest,
727
+ precondition_digest, normalized_call_digest, state, target_id,
728
+ accepted_receipt_id, result_revision, result_digest, removed_receipt_id,
729
+ created_at, updated_at
730
+ ) VALUES (?, ?, ?, 'project', ?, ?, ?, 'forward', ?, ?, ?, ?, 'pending',
731
+ NULL, NULL, NULL, NULL, NULL, ?, ?)
732
+ ON CONFLICT DO NOTHING
733
+ `, request.authority_id, request.tenant_id, request.corpus_id, request.target_selector, request.operation_id, request.step_id, request.idempotency_key, request.request_digest, request.precondition_digest, callDigest, now, now);
734
+ return result.changes === 1;
735
+ }
736
+ function lockBinding(db, capability, targetSelector) {
737
+ const result = db.run(`
738
+ UPDATE mementos_project_registration_bindings
739
+ SET updated_at = updated_at
740
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
741
+ AND resource_kind = 'project' AND target_selector = ?
742
+ `, capability.authority_id, capability.tenant_id, capability.corpus_id, targetSelector);
743
+ return result.changes === 1;
744
+ }
745
+ function setBindingAccepted(db, request, receipt, now) {
746
+ const result = db.run(`
747
+ UPDATE mementos_project_registration_bindings
748
+ SET state = 'accepted', target_id = ?, accepted_receipt_id = ?,
749
+ result_revision = ?, result_digest = ?, updated_at = ?
750
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
751
+ AND resource_kind = 'project' AND target_selector = ? AND state = 'pending'
752
+ `, receipt.target_id, receipt.receipt_id, receipt.result_revision, receipt.result_digest, now, request.authority_id, request.tenant_id, request.corpus_id, request.target_selector);
753
+ if (result.changes !== 1) {
754
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not pending at acceptance");
755
+ }
756
+ }
757
+ function setBindingRemoved(db, accepted, inverseReceiptId, now) {
758
+ const result = db.run(`
759
+ UPDATE mementos_project_registration_bindings
760
+ SET state = 'removed', removed_receipt_id = ?, updated_at = ?
761
+ WHERE authority_id = ? AND tenant_id = ? AND corpus_id = ?
762
+ AND resource_kind = 'project' AND target_selector = ?
763
+ AND state = 'accepted' AND accepted_receipt_id = ?
764
+ `, inverseReceiptId, now, accepted.authority_id, accepted.tenant_id, accepted.corpus_id, accepted.target_selector, accepted.receipt_id);
765
+ if (result.changes !== 1) {
766
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding was not accepted at inverse");
767
+ }
768
+ }
769
+ function capabilityMatches(request, capability) {
770
+ return request.authority_route === capability.route && request.package_version === capability.package_version && request.authority_id === capability.authority_id && request.tenant_id === capability.tenant_id && request.corpus_id === capability.corpus_id;
771
+ }
772
+ function assertCommonRequest(request, capability) {
773
+ assertBounds(request);
774
+ if (request.resource_kind !== "project") {
775
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
776
+ }
777
+ if (request.direction !== "forward" && request.direction !== "inverse") {
778
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "direction must be forward or inverse");
779
+ }
780
+ if (!capabilityMatches(request, capability)) {
781
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "registration request does not match this authority capability identity");
782
+ }
783
+ requireString(request.operation_id, "operation_id", {
784
+ min: 8,
785
+ max: 128,
786
+ pattern: OPERATION_PATTERN
787
+ });
788
+ requireString(request.step_id, "step_id", { min: 3, max: 128, pattern: STEP_PATTERN });
789
+ requireString(request.target_selector, "target_selector", { max: 512 });
790
+ requireString(request.project_id, "project_id", {
791
+ min: 16,
792
+ max: 128,
793
+ pattern: WORKSPACE_ID_PATTERN
794
+ });
795
+ requireString(request.project_name, "project_name", { max: 256 });
796
+ requireString(request.project_slug, "project_slug", { max: 128 });
797
+ requireString(request.request_digest, "request_digest", {
798
+ min: 64,
799
+ max: 64,
800
+ pattern: SHA256_PATTERN
801
+ });
802
+ requireString(request.precondition_digest, "precondition_digest", {
803
+ min: 64,
804
+ max: 64,
805
+ pattern: SHA256_PATTERN
806
+ });
807
+ requireString(request.idempotency_key, "idempotency_key", {
808
+ min: 52,
809
+ max: 52,
810
+ pattern: IDEMPOTENCY_PATTERN
811
+ });
812
+ if (!request.desired || typeof request.desired !== "object" || Array.isArray(request.desired)) {
813
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "desired must be a JSON object");
814
+ }
815
+ if (digestMementosProjectRegistrationValue(request.desired) !== request.request_digest) {
816
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "request_digest does not match the canonical desired payload");
817
+ }
818
+ const expectedKey = deriveMementosProjectRegistrationIdempotencyKey(request);
819
+ if (request.idempotency_key !== expectedKey) {
820
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "idempotency_key does not match the deterministic request", { expected: expectedKey });
821
+ }
822
+ }
823
+ function assertForwardRequest(request, capability) {
824
+ assertCommonRequest(request, capability);
825
+ if (request.direction !== "forward" || request.accepted_receipt !== undefined) {
826
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional create requires a forward request without an accepted receipt");
827
+ }
828
+ exactKeys(request.desired, [
829
+ "name",
830
+ "source_project_id",
831
+ "source_project_slug",
832
+ "target_path_digest"
833
+ ], "forward desired");
834
+ if (request.target_selector !== request.project_id || request.desired["source_project_id"] !== request.project_id || request.desired["source_project_slug"] !== request.project_slug || request.desired["name"] !== request.project_name) {
835
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "forward project identity fields disagree");
836
+ }
837
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
838
+ target_selector: request.target_selector,
839
+ expected: "absent"
840
+ });
841
+ if (request.precondition_digest !== expectedPrecondition) {
842
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "forward precondition must bind expected absence");
843
+ }
844
+ const path = ownedPath(request.target);
845
+ if (request.desired["target_path_digest"] !== digestOwnedPath(path)) {
846
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "target path does not match the package-owned digest");
847
+ }
848
+ return path;
849
+ }
850
+ function sameReceipt(left, right) {
851
+ return canonicalMementosProjectRegistrationJson(publicReceipt(left)) === canonicalMementosProjectRegistrationJson(right);
852
+ }
853
+ function assertInverseRequest(request, capability) {
854
+ assertCommonRequest(request, capability);
855
+ if (request.direction !== "inverse" || !request.accepted_receipt) {
856
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "conditional inverse requires the accepted forward receipt");
857
+ }
858
+ const accepted = request.accepted_receipt;
859
+ if (accepted.authority !== "mementos" || accepted.route !== capability.route || accepted.package_version !== capability.package_version || accepted.authority_id !== capability.authority_id || accepted.tenant_id !== capability.tenant_id || accepted.corpus_id !== capability.corpus_id || accepted.operation_id !== request.operation_id || accepted.step_id !== request.step_id || accepted.resource_kind !== "project" || accepted.direction !== "forward" || accepted.outcome !== "accepted" || !accepted.created_by_operation || !accepted.target_id || !accepted.result_revision || !accepted.result_digest || request.target_selector !== accepted.target_id) {
860
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "accepted receipt is not an exact attempt-created forward receipt");
861
+ }
862
+ exactKeys(request.desired, ["accepted_receipt_id", "target_id"], "inverse desired");
863
+ if (request.desired["accepted_receipt_id"] !== accepted.receipt_id || request.desired["target_id"] !== accepted.target_id) {
864
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse desired payload does not bind the accepted receipt");
865
+ }
866
+ const expectedPrecondition = digestMementosProjectRegistrationValue({
867
+ expected_revision: accepted.result_revision,
868
+ expected_digest: accepted.result_digest
869
+ });
870
+ if (request.precondition_digest !== expectedPrecondition) {
871
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH", "inverse precondition does not bind the accepted object state");
872
+ }
873
+ return accepted;
874
+ }
875
+ function assertLookup(request, capability) {
876
+ assertBounds(request);
877
+ if (request.max_items !== 1) {
878
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS", "terminal receipt lookup requires max_items exactly 1");
879
+ }
880
+ if (request.authority !== "mementos" || request.resource_kind !== "project" || request.authority_route !== capability.route || request.package_version !== capability.package_version || request.authority_id !== capability.authority_id || request.tenant_id !== capability.tenant_id || request.corpus_id !== capability.corpus_id) {
881
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH", "receipt lookup does not match this authority capability identity");
882
+ }
883
+ requireString(request.operation_id, "operation_id", { pattern: OPERATION_PATTERN });
884
+ requireString(request.step_id, "step_id", { pattern: STEP_PATTERN });
885
+ requireString(request.target_selector, "target_selector");
886
+ requireString(request.idempotency_key, "idempotency_key", {
887
+ min: 52,
888
+ max: 52,
889
+ pattern: IDEMPOTENCY_PATTERN
890
+ });
891
+ if (request.target_id !== undefined) {
892
+ requireString(request.target_id, "target_id", {
893
+ min: 51,
894
+ max: 51,
895
+ pattern: PROJECT_ID_PATTERN
896
+ });
897
+ }
898
+ }
899
+
900
+ class PackageOwnedMementosProjectRegistrationAuthority {
901
+ db;
902
+ authority = "mementos";
903
+ capabilityValue;
904
+ now;
905
+ faultInjector;
906
+ constructor(db, options = {}) {
907
+ this.db = db;
908
+ this.now = options.now ?? (() => new Date().toISOString());
909
+ this.faultInjector = options.faultInjector;
910
+ this.capabilityValue = {
911
+ authority: "mementos",
912
+ route: MEMENTOS_PROJECT_REGISTRATION_ROUTE,
913
+ package_version: options.packageVersion ?? getMementosPackageVersion(),
914
+ authority_id: options.authorityId ?? "mementos",
915
+ tenant_id: options.tenantId ?? "default",
916
+ corpus_id: options.corpusId ?? "default",
917
+ supported_resources: ["project"],
918
+ conditional_create: true,
919
+ immutable_receipts: true,
920
+ exact_terminal_lookup: true,
921
+ exact_readback: true,
922
+ conditional_inverse: true,
923
+ ambiguous_outcome_reconciliation: true,
924
+ guarded_update: true,
925
+ no_write_dry_run: true,
926
+ expected_revision_compare_and_swap: true,
927
+ caller_idempotency: true,
928
+ exact_inverse_rollback: true
929
+ };
930
+ }
931
+ fault(point, request) {
932
+ this.faultInjector?.(point, {
933
+ operation_id: request.operation_id,
934
+ step_id: request.step_id,
935
+ resource_kind: "project",
936
+ direction: request.direction
937
+ });
938
+ }
939
+ writeBoundary(point, request) {
940
+ try {
941
+ this.fault(point, request);
942
+ } catch (cause) {
943
+ throw new WriteBoundaryError(point, cause);
944
+ }
945
+ }
946
+ terminal(request, callDigest, reason, values = {}) {
947
+ return insertReceipt(this.db, makeReceipt(request, this.capabilityValue, callDigest, this.now(), {
948
+ outcome: "terminal_nonacceptance",
949
+ reason,
950
+ target_id: values.target_id,
951
+ accepted_receipt_id: values.accepted_receipt_id,
952
+ target_selector: values.target_selector
953
+ }));
954
+ }
955
+ duplicateForwardOrTerminal(request, callDigest, path, accepted) {
956
+ if (accepted.normalized_call_digest !== callDigest) {
957
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "accepted operation/step identity is bound to a different request");
958
+ }
959
+ const project = accepted.target_id ? getProjectByExactId(this.db, accepted.target_id) : null;
960
+ if (!project || project.path !== path) {
961
+ return this.terminal(request, callDigest, "accepted_target_missing_or_replaced", {
962
+ target_id: accepted.target_id
963
+ });
964
+ }
965
+ const record = projectRecord(this.db, project);
966
+ if (record.revision !== accepted.result_revision || record.digest !== accepted.result_digest) {
967
+ return this.terminal(request, callDigest, "accepted_target_drifted", {
968
+ target_id: accepted.target_id
969
+ });
970
+ }
971
+ return insertReceipt(this.db, makeReceipt(request, this.capabilityValue, callDigest, this.now(), {
972
+ outcome: "duplicate_of_accepted",
973
+ target_id: accepted.target_id,
974
+ result_revision: accepted.result_revision,
975
+ result_digest: accepted.result_digest,
976
+ duplicate_of_receipt_id: accepted.receipt_id,
977
+ created_by_operation: false
978
+ }));
979
+ }
980
+ async capability() {
981
+ return {
982
+ ...this.capabilityValue,
983
+ supported_resources: ["project"]
984
+ };
985
+ }
986
+ async create(request) {
987
+ const startedAt = Date.now();
988
+ const path = assertForwardRequest(request, this.capabilityValue);
989
+ const callDigest = normalizedCallDigest(request);
990
+ let stored;
991
+ try {
992
+ stored = this.db.transaction(() => {
993
+ const exact = getReceiptForLookup(this.db, {
994
+ ...request,
995
+ authority: "mementos",
996
+ max_items: 1
997
+ });
998
+ if (exact?.outcome === "terminal_nonacceptance")
999
+ return exact;
1000
+ const accepted = getAcceptedReceipt(this.db, request);
1001
+ if (accepted) {
1002
+ return this.duplicateForwardOrTerminal(request, callDigest, path, accepted);
1003
+ }
1004
+ const existing = getProjectByPath(this.db, path) ?? getProjectByExactId(this.db, targetId(this.capabilityValue, request.target_selector));
1005
+ if (existing) {
1006
+ return this.terminal(request, callDigest, "target_preexists", {
1007
+ target_id: existing.id
1008
+ });
1009
+ }
1010
+ const now = this.now();
1011
+ const insertedBinding = insertBinding(this.db, request, callDigest, now);
1012
+ if (!lockBinding(this.db, this.capabilityValue, request.target_selector)) {
1013
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "registration binding disappeared before it could be locked");
1014
+ }
1015
+ const ownedBinding = getBinding(this.db, this.capabilityValue, request.target_selector);
1016
+ if (!ownedBinding || ownedBinding.normalized_call_digest !== callDigest) {
1017
+ return this.terminal(request, callDigest, "target_selector_preexists", {
1018
+ target_id: ownedBinding?.target_id
1019
+ });
1020
+ }
1021
+ const acceptedAfterBinding = getAcceptedReceipt(this.db, request);
1022
+ if (acceptedAfterBinding) {
1023
+ return this.duplicateForwardOrTerminal(request, callDigest, path, acceptedAfterBinding);
1024
+ }
1025
+ if (!insertedBinding || ownedBinding.state !== "pending") {
1026
+ return this.terminal(request, callDigest, "target_selector_preexists", {
1027
+ target_id: ownedBinding.target_id
1028
+ });
1029
+ }
1030
+ const id = targetId(this.capabilityValue, request.target_selector);
1031
+ this.writeBoundary("before_object_write", request);
1032
+ try {
1033
+ this.db.run(`
1034
+ INSERT INTO projects (
1035
+ id, name, path, description, memory_prefix, created_at, updated_at
1036
+ ) VALUES (?, ?, ?, ?, ?, ?, ?)
1037
+ `, id, request.project_name, path, null, request.project_slug, now, now);
1038
+ } catch (cause) {
1039
+ throw new WriteBoundaryError("before_object_write", cause);
1040
+ }
1041
+ this.writeBoundary("after_object_write", request);
1042
+ const project = getProjectByExactId(this.db, id);
1043
+ if (!project || project.path !== path) {
1044
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "project write did not read back by its full id");
1045
+ }
1046
+ const record = projectRecord(this.db, project);
1047
+ const acceptedReceipt = makeReceipt(request, this.capabilityValue, callDigest, now, {
1048
+ outcome: "accepted",
1049
+ target_id: record.target_id,
1050
+ result_revision: record.revision,
1051
+ result_digest: record.digest,
1052
+ created_by_operation: true
1053
+ });
1054
+ this.writeBoundary("before_receipt_write", request);
1055
+ let inserted;
1056
+ try {
1057
+ inserted = insertReceipt(this.db, acceptedReceipt);
1058
+ } catch (cause) {
1059
+ throw new WriteBoundaryError("before_receipt_write", cause);
1060
+ }
1061
+ this.writeBoundary("after_receipt_write", request);
1062
+ setBindingAccepted(this.db, request, inserted, now);
1063
+ return inserted;
1064
+ });
1065
+ } catch (error) {
1066
+ if (!(error instanceof WriteBoundaryError))
1067
+ throw error;
1068
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`));
1069
+ }
1070
+ this.fault("after_commit", request);
1071
+ const receipt = publicReceipt(stored);
1072
+ assertWithinBounds(receipt, request, startedAt);
1073
+ return receipt;
1074
+ }
1075
+ async readExact(request) {
1076
+ const startedAt = Date.now();
1077
+ assertBounds(request);
1078
+ if (request.resource_kind !== "project") {
1079
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "resource_kind must be project");
1080
+ }
1081
+ requireString(request.target_id, "target_id", {
1082
+ min: 51,
1083
+ max: 51,
1084
+ pattern: PROJECT_ID_PATTERN
1085
+ });
1086
+ const path = ownedPath(request.target);
1087
+ const project = getProjectByExactId(this.db, request.target_id);
1088
+ if (!project || project.path !== path) {
1089
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND", "exact full-id project readback did not resolve the owned path");
1090
+ }
1091
+ const record = projectRecord(this.db, project);
1092
+ assertWithinBounds(record, request, startedAt);
1093
+ return record;
1094
+ }
1095
+ async lookupReceipt(request) {
1096
+ const startedAt = Date.now();
1097
+ assertLookup(request, this.capabilityValue);
1098
+ const receipt = getReceiptForLookup(this.db, request);
1099
+ if (!receipt || request.target_id !== undefined && receipt.target_id !== request.target_id) {
1100
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "exact immutable terminal receipt was not found");
1101
+ }
1102
+ return withResponseControl(publicReceipt(receipt), request, startedAt);
1103
+ }
1104
+ async compensate(request) {
1105
+ const startedAt = Date.now();
1106
+ const acceptedPublic = assertInverseRequest(request, this.capabilityValue);
1107
+ const callDigest = normalizedCallDigest(request);
1108
+ const storedAccepted = getStoredReceipt(this.db, acceptedPublic.receipt_id);
1109
+ if (!storedAccepted || !sameReceipt(storedAccepted, acceptedPublic)) {
1110
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
1111
+ }
1112
+ const path = ownedPath(request.target);
1113
+ let stored;
1114
+ try {
1115
+ stored = this.db.transaction(() => {
1116
+ const exact = getReceiptForLookup(this.db, {
1117
+ ...request,
1118
+ authority: "mementos",
1119
+ max_items: 1
1120
+ });
1121
+ if (exact?.outcome === "terminal_nonacceptance")
1122
+ return exact;
1123
+ if (!lockBinding(this.db, this.capabilityValue, storedAccepted.target_selector)) {
1124
+ return this.terminal(request, callDigest, "accepted_binding_missing", {
1125
+ target_id: storedAccepted.target_id,
1126
+ accepted_receipt_id: storedAccepted.receipt_id
1127
+ });
1128
+ }
1129
+ const binding = getBinding(this.db, this.capabilityValue, storedAccepted.target_selector);
1130
+ if (!binding || binding.accepted_receipt_id !== storedAccepted.receipt_id) {
1131
+ return this.terminal(request, callDigest, "accepted_binding_replaced", {
1132
+ target_id: storedAccepted.target_id,
1133
+ accepted_receipt_id: storedAccepted.receipt_id
1134
+ });
1135
+ }
1136
+ const prior = getAcceptedReceipt(this.db, request);
1137
+ if (prior) {
1138
+ if (prior.normalized_call_digest !== callDigest || prior.accepted_receipt_id !== storedAccepted.receipt_id) {
1139
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH", "inverse operation/step identity is bound to a different request");
1140
+ }
1141
+ return insertReceipt(this.db, makeReceipt(request, this.capabilityValue, callDigest, this.now(), {
1142
+ outcome: "duplicate_of_accepted",
1143
+ target_id: prior.target_id,
1144
+ result_revision: prior.result_revision,
1145
+ result_digest: prior.result_digest,
1146
+ duplicate_of_receipt_id: prior.receipt_id,
1147
+ accepted_receipt_id: storedAccepted.receipt_id
1148
+ }));
1149
+ }
1150
+ if (binding.state !== "accepted") {
1151
+ return this.terminal(request, callDigest, "accepted_binding_not_active", {
1152
+ target_id: storedAccepted.target_id,
1153
+ accepted_receipt_id: storedAccepted.receipt_id
1154
+ });
1155
+ }
1156
+ const project = getProjectByExactId(this.db, storedAccepted.target_id);
1157
+ if (!project) {
1158
+ return this.terminal(request, callDigest, "target_missing", {
1159
+ target_id: storedAccepted.target_id,
1160
+ accepted_receipt_id: storedAccepted.receipt_id
1161
+ });
1162
+ }
1163
+ if (project.path !== path) {
1164
+ return this.terminal(request, callDigest, "target_drifted", {
1165
+ target_id: storedAccepted.target_id,
1166
+ accepted_receipt_id: storedAccepted.receipt_id
1167
+ });
1168
+ }
1169
+ const references = mementosProjectReferenceCounts(this.db, project.id);
1170
+ if (hasMementosProjectReferences(references)) {
1171
+ return this.terminal(request, callDigest, "target_has_dependents", {
1172
+ target_id: storedAccepted.target_id,
1173
+ accepted_receipt_id: storedAccepted.receipt_id
1174
+ });
1175
+ }
1176
+ const current = projectRecord(this.db, project, references);
1177
+ if (current.revision !== storedAccepted.result_revision || current.digest !== storedAccepted.result_digest) {
1178
+ return this.terminal(request, callDigest, "target_drifted", {
1179
+ target_id: storedAccepted.target_id,
1180
+ accepted_receipt_id: storedAccepted.receipt_id
1181
+ });
1182
+ }
1183
+ this.writeBoundary("before_object_write", request);
1184
+ let deleted;
1185
+ try {
1186
+ deleted = deleteMementosProjectIfUnreferenced(this.db, storedAccepted.target_id);
1187
+ } catch (cause) {
1188
+ throw new WriteBoundaryError("before_object_write", cause);
1189
+ }
1190
+ if (deleted !== 1) {
1191
+ const remaining = getProjectByExactId(this.db, storedAccepted.target_id);
1192
+ if (remaining && hasMementosProjectReferences(mementosProjectReferenceCounts(this.db, storedAccepted.target_id))) {
1193
+ return this.terminal(request, callDigest, "target_has_dependents", {
1194
+ target_id: storedAccepted.target_id,
1195
+ accepted_receipt_id: storedAccepted.receipt_id
1196
+ });
1197
+ }
1198
+ throw new WriteBoundaryError("before_object_write", new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "conditional inverse did not delete exactly one unreferenced project"));
1199
+ }
1200
+ this.writeBoundary("after_object_write", request);
1201
+ const inverseRecord = {
1202
+ target_id: storedAccepted.target_id,
1203
+ revision: "absent",
1204
+ digest: digestMementosProjectRegistrationValue({
1205
+ target_id: storedAccepted.target_id,
1206
+ accepted_receipt_id: storedAccepted.receipt_id,
1207
+ absent: true
1208
+ })
1209
+ };
1210
+ const inverseReceipt = makeReceipt(request, this.capabilityValue, callDigest, this.now(), {
1211
+ outcome: "accepted",
1212
+ target_id: inverseRecord.target_id,
1213
+ result_revision: inverseRecord.revision,
1214
+ result_digest: inverseRecord.digest,
1215
+ accepted_receipt_id: storedAccepted.receipt_id,
1216
+ created_by_operation: false
1217
+ });
1218
+ this.writeBoundary("before_receipt_write", request);
1219
+ let inserted;
1220
+ try {
1221
+ inserted = insertReceipt(this.db, inverseReceipt);
1222
+ } catch (cause) {
1223
+ throw new WriteBoundaryError("before_receipt_write", cause);
1224
+ }
1225
+ this.writeBoundary("after_receipt_write", request);
1226
+ setBindingRemoved(this.db, storedAccepted, inserted.receipt_id, this.now());
1227
+ return inserted;
1228
+ });
1229
+ } catch (error) {
1230
+ if (!(error instanceof WriteBoundaryError))
1231
+ throw error;
1232
+ stored = this.db.transaction(() => this.terminal(request, callDigest, `write_failed:${error.point}`, {
1233
+ target_id: storedAccepted.target_id,
1234
+ accepted_receipt_id: storedAccepted.receipt_id
1235
+ }));
1236
+ }
1237
+ this.fault("after_commit", request);
1238
+ const receipt = publicReceipt(stored);
1239
+ assertWithinBounds(receipt, request, startedAt);
1240
+ return receipt;
1241
+ }
1242
+ async verifyInverse(request) {
1243
+ const startedAt = Date.now();
1244
+ const accepted = assertInverseRequest(request, this.capabilityValue);
1245
+ const storedAccepted = getStoredReceipt(this.db, accepted.receipt_id);
1246
+ if (!storedAccepted || !sameReceipt(storedAccepted, accepted)) {
1247
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND", "accepted forward receipt is not stored immutably in this authority");
1248
+ }
1249
+ const inverse = getAcceptedReceipt(this.db, request);
1250
+ if (!inverse || inverse.outcome !== "accepted" || inverse.accepted_receipt_id !== accepted.receipt_id || inverse.result_revision !== "absent" || !inverse.result_digest) {
1251
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND", "accepted conditional inverse receipt was not found");
1252
+ }
1253
+ if (getProjectByExactId(this.db, accepted.target_id)) {
1254
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification found the accepted target still present");
1255
+ }
1256
+ const verification = {
1257
+ target_id: accepted.target_id,
1258
+ accepted_receipt_id: accepted.receipt_id,
1259
+ absent: true,
1260
+ digest: digestMementosProjectRegistrationValue({
1261
+ target_id: accepted.target_id,
1262
+ accepted_receipt_id: accepted.receipt_id,
1263
+ absent: true
1264
+ })
1265
+ };
1266
+ assertWithinBounds(verification, request, startedAt);
1267
+ if (verification.digest !== inverse.result_digest) {
1268
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_CONFLICT", "inverse verification digest does not match its immutable receipt");
1269
+ }
1270
+ return verification;
1271
+ }
1272
+ }
1273
+ function createMementosProjectRegistrationAuthority(db, options = {}) {
1274
+ return new PackageOwnedMementosProjectRegistrationAuthority(db, options);
1275
+ }
1276
+ var createLocalMementosProjectRegistrationAuthority = createMementosProjectRegistrationAuthority;
1277
+ // src/project-registration/http.ts
1278
+ var JSON_HEADERS = { "Content-Type": "application/json" };
1279
+
1280
+ class WirePathHandle {
1281
+ absolutePath;
1282
+ constructor(absolutePath) {
1283
+ this.absolutePath = absolutePath;
1284
+ }
1285
+ withOwnedPath(consumer) {
1286
+ return consumer(this.absolutePath);
1287
+ }
1288
+ }
1289
+ function json(body, status = 200) {
1290
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
1291
+ }
1292
+ function errorStatus(error) {
1293
+ switch (error.code) {
1294
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT":
1295
+ case "MEMENTOS_PROJECT_REGISTRATION_INVALID_BOUNDS":
1296
+ case "MEMENTOS_PROJECT_REGISTRATION_CAPABILITY_MISMATCH":
1297
+ case "MEMENTOS_PROJECT_REGISTRATION_DIGEST_MISMATCH":
1298
+ case "MEMENTOS_PROJECT_REGISTRATION_IDEMPOTENCY_MISMATCH":
1299
+ case "MEMENTOS_PROJECT_REGISTRATION_EXACT_ID_REQUIRED":
1300
+ return 400;
1301
+ case "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND":
1302
+ case "MEMENTOS_PROJECT_REGISTRATION_RECORD_NOT_FOUND":
1303
+ case "MEMENTOS_PROJECT_REGISTRATION_ACCEPTED_RECEIPT_NOT_FOUND":
1304
+ return 404;
1305
+ case "MEMENTOS_PROJECT_REGISTRATION_RESPONSE_TOO_LARGE":
1306
+ return 413;
1307
+ case "MEMENTOS_PROJECT_REGISTRATION_TIME_BUDGET_EXCEEDED":
1308
+ return 408;
1309
+ case "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE":
1310
+ return 503;
1311
+ default:
1312
+ return 409;
1313
+ }
1314
+ }
1315
+ async function readJson(request) {
1316
+ try {
1317
+ const value = await request.json();
1318
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1319
+ } catch {
1320
+ return null;
1321
+ }
1322
+ }
1323
+ function fromWireRequest(body) {
1324
+ const { canonical_path: canonicalPath, ...request } = body;
1325
+ if (typeof canonicalPath !== "string") {
1326
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private registration transport");
1327
+ }
1328
+ return {
1329
+ ...request,
1330
+ target: new WirePathHandle(canonicalPath)
1331
+ };
1332
+ }
1333
+ function fromWireReadRequest(body) {
1334
+ const { canonical_path: canonicalPath, ...request } = body;
1335
+ if (typeof canonicalPath !== "string") {
1336
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "canonical_path is required on the private exact-read transport");
1337
+ }
1338
+ return {
1339
+ ...request,
1340
+ target: new WirePathHandle(canonicalPath)
1341
+ };
1342
+ }
1343
+ async function handleMementosProjectRegistrationHttpRequest(request, url, authority, basePath = "/v1/project-registration") {
1344
+ const path = url.pathname;
1345
+ if (path !== basePath && !path.startsWith(`${basePath}/`))
1346
+ return null;
1347
+ const action = path.slice(basePath.length).split("/").filter(Boolean).join("/");
1348
+ const method = request.method.toUpperCase();
1349
+ try {
1350
+ if ((action === "" || action === "capability") && method === "GET") {
1351
+ return json({ capability: await authority.capability() });
1352
+ }
1353
+ if (method !== "POST")
1354
+ return json({ error: "method not allowed" }, 405);
1355
+ const body = await readJson(request);
1356
+ if (!body) {
1357
+ return json({
1358
+ error: "invalid JSON body",
1359
+ code: "MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT"
1360
+ }, 400);
1361
+ }
1362
+ if (action === "create") {
1363
+ return json({ receipt: await authority.create(fromWireRequest(body)) }, 201);
1364
+ }
1365
+ if (action === "receipts/lookup") {
1366
+ return json(await authority.lookupReceipt(body));
1367
+ }
1368
+ if (action === "read-exact") {
1369
+ return json({ record: await authority.readExact(fromWireReadRequest(body)) });
1370
+ }
1371
+ if (action === "compensate") {
1372
+ return json({ receipt: await authority.compensate(fromWireRequest(body)) }, 201);
1373
+ }
1374
+ if (action === "verify-inverse") {
1375
+ return json({ verification: await authority.verifyInverse(fromWireRequest(body)) });
1376
+ }
1377
+ return json({
1378
+ error: "unknown Mementos project-registration route",
1379
+ code: "MEMENTOS_PROJECT_REGISTRATION_RECEIPT_NOT_FOUND"
1380
+ }, 404);
1381
+ } catch (cause) {
1382
+ if (cause instanceof MementosProjectRegistrationError) {
1383
+ return json({
1384
+ error: cause.message,
1385
+ code: cause.code,
1386
+ details: cause.details,
1387
+ authoritative: true
1388
+ }, errorStatus(cause));
1389
+ }
1390
+ return json({
1391
+ error: cause instanceof Error ? cause.message : "internal registration error",
1392
+ code: "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE"
1393
+ }, 500);
1394
+ }
1395
+ }
1396
+ function extractPath(target) {
1397
+ if (!target || typeof target.withOwnedPath !== "function") {
1398
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_INVALID_INPUT", "target must be a package-owned path handle");
1399
+ }
1400
+ return target.withOwnedPath((absolutePath) => absolutePath);
1401
+ }
1402
+ function toWireRequest(request) {
1403
+ const { target, ...serializable } = request;
1404
+ return {
1405
+ ...serializable,
1406
+ canonical_path: extractPath(target)
1407
+ };
1408
+ }
1409
+
1410
+ class MementosProjectRegistrationHttpClient {
1411
+ authority = "mementos";
1412
+ baseUrl;
1413
+ fetchImpl;
1414
+ headers;
1415
+ constructor(options) {
1416
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1417
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
1418
+ this.headers = {
1419
+ ...options.headers,
1420
+ ...options.apiKey ? { Authorization: `Bearer ${options.apiKey}` } : {},
1421
+ "Content-Type": "application/json"
1422
+ };
1423
+ }
1424
+ async request(action, init = {}) {
1425
+ const response = await this.fetchImpl(`${this.baseUrl}/v1/project-registration${action}`, {
1426
+ ...init,
1427
+ headers: { ...this.headers, ...init.headers ?? {} }
1428
+ });
1429
+ let body;
1430
+ try {
1431
+ body = await response.json();
1432
+ } catch {
1433
+ throw new MementosProjectRegistrationError("MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", `Mementos project registration HTTP ${response.status} returned non-JSON`);
1434
+ }
1435
+ if (!response.ok) {
1436
+ throw new MementosProjectRegistrationError(typeof body["code"] === "string" ? body["code"] : "MEMENTOS_PROJECT_REGISTRATION_ATOMICITY_UNAVAILABLE", typeof body["error"] === "string" ? body["error"] : `Mementos project registration HTTP ${response.status}`, body["details"] && typeof body["details"] === "object" ? body["details"] : {});
1437
+ }
1438
+ return body;
1439
+ }
1440
+ async capability() {
1441
+ const body = await this.request("/capability");
1442
+ return body.capability;
1443
+ }
1444
+ async create(request) {
1445
+ const body = await this.request("/create", { method: "POST", body: JSON.stringify(toWireRequest(request)) });
1446
+ return body.receipt;
1447
+ }
1448
+ async readExact(request) {
1449
+ const { target, ...serializable } = request;
1450
+ const body = await this.request("/read-exact", {
1451
+ method: "POST",
1452
+ body: JSON.stringify({ ...serializable, canonical_path: extractPath(target) })
1453
+ });
1454
+ return body.record;
1455
+ }
1456
+ async lookupReceipt(request) {
1457
+ return this.request("/receipts/lookup", { method: "POST", body: JSON.stringify(request) });
1458
+ }
1459
+ async compensate(request) {
1460
+ const body = await this.request("/compensate", { method: "POST", body: JSON.stringify(toWireRequest(request)) });
1461
+ return body.receipt;
1462
+ }
1463
+ async verifyInverse(request) {
1464
+ const body = await this.request("/verify-inverse", {
1465
+ method: "POST",
1466
+ body: JSON.stringify(toWireRequest(request))
1467
+ });
1468
+ return body.verification;
1469
+ }
1470
+ }
1471
+ function createMementosProjectRegistrationHttpClient(options) {
1472
+ return new MementosProjectRegistrationHttpClient(options);
1473
+ }
1474
+ export {
1475
+ sqliteMementosProjectRegistrationSchemaSql,
1476
+ sqliteMementosProjectGuardedUpdateSchemaSql,
1477
+ postgresMementosProjectRegistrationSchemaSql,
1478
+ postgresMementosProjectGuardedUpdateSchemaSql,
1479
+ mementosProjectReferenceCounts,
1480
+ hasMementosProjectReferences,
1481
+ handleMementosProjectRegistrationHttpRequest,
1482
+ digestMementosProjectRegistrationValue,
1483
+ deriveMementosProjectRegistrationIdempotencyKey,
1484
+ createMementosProjectRegistrationHttpClient,
1485
+ createMementosProjectRegistrationAuthority,
1486
+ createLocalMementosProjectRegistrationAuthority,
1487
+ canonicalMementosProjectRegistrationJson,
1488
+ PackageOwnedMementosProjectRegistrationAuthority,
1489
+ MementosProjectRegistrationHttpClient,
1490
+ MementosProjectRegistrationError,
1491
+ MEMENTOS_PROJECT_REGISTRATION_SCHEMA_VERSION,
1492
+ MEMENTOS_PROJECT_REGISTRATION_ROUTE,
1493
+ MEMENTOS_PROJECT_REGISTRATION_CALLER_ROUTE,
1494
+ MEMENTOS_PROJECT_REFERENCE_SURFACES,
1495
+ MEMENTOS_PROJECT_GUARDED_UPDATE_ROUTE
1496
+ };