@crvouga/mockingbird-service-llamacloud 1.7.0 → 1.8.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/CHANGELOG.md CHANGED
@@ -1,14 +1,31 @@
1
1
  # Changelog — @crvouga/mockingbird-service-llamacloud
2
2
 
3
- ## 1.7.0 (2026-10-01)
3
+ ## 1.8.0 (2026-10-02)
4
4
 
5
5
  ### Features
6
6
 
7
- - record SQL timelines and serve the admin explorer ([8ced17a](https://github.com/crvouga/mockingbird/commit/8ced17ac398e386ef4e9d7e01fc7744c34b95d07))
7
+ - coordinate HTTP and protocol mocks ([3168653](https://github.com/crvouga/mockingbird/commit/31686534c55a9823fdaa92afc9cc456cf741b809))
8
+
9
+ ### Fixes and improvements
8
10
 
9
- ### Dependencies
11
+ - decouple smoke types from built packages ([a4b31c6](https://github.com/crvouga/mockingbird/commit/a4b31c6205d92aa0c6f7c9de3518d5ae44d38829))
12
+ - protect child admin endpoints ([5410b94](https://github.com/crvouga/mockingbird/commit/5410b948a30dcedc21e9a70e44fd415c1ea25394))
13
+ - harden protocol startup and state ([6f5b557](https://github.com/crvouga/mockingbird/commit/6f5b557e87b48b31985949e04f3b5ef58f51d295))
14
+ - preserve checkpoints and lifecycle ([50e8c32](https://github.com/crvouga/mockingbird/commit/50e8c322feded2f7f45afce9d8cdef43c37ab9ab))
15
+ - clean branch state on reset ([3b714d4](https://github.com/crvouga/mockingbird/commit/3b714d4a5df8e75fee0bb31e98d7c7566e164381))
16
+ - type signal listener cleanup ([5279852](https://github.com/crvouga/mockingbird/commit/5279852e56ea60262ccf17aa6e46b3c31091a34a))
17
+
18
+ ## 1.7.1 (2026-10-01)
19
+
20
+ ### Fixes and improvements
10
21
 
11
- - `@crvouga/mockingbird-service-sqlite`
22
+ - freeze clock before setting time ([a054a1d](https://github.com/crvouga/mockingbird/commit/a054a1defad62539d719b1d31c15107afea623bd))
23
+
24
+ ## 1.7.0 (2026-09-30)
25
+
26
+ ### Features
27
+
28
+ - record SQL timelines and serve the admin explorer ([8ced17a](https://github.com/crvouga/mockingbird/commit/8ced17ac398e386ef4e9d7e01fc7744c34b95d07))
12
29
 
13
30
  ## 1.6.1 (2026-09-30)
14
31
 
@@ -183,29 +183,30 @@ var createControlPlane = (context) => {
183
183
  return json(200, { status: "ok", reset: target === "*" ? context.namespaces() : [target] });
184
184
  },
185
185
  "GET /namespaces": () => json(200, { default: context.defaultNamespace, namespaces: context.namespaces() }),
186
- "GET /clock": () => json(200, context.clock.state()),
187
- "POST /clock": ({ body }) => {
186
+ "GET /clock": ({ namespace }) => json(200, (context.namespaceClock?.(namespace) ?? context.clock).state()),
187
+ "POST /clock": ({ body, namespace }) => {
188
+ const clock = context.namespaceClock?.(namespace) ?? context.clock;
188
189
  if (!isRecord(body))
189
190
  return adminError(400, "expected a JSON object");
190
191
  if (body.reset === true)
191
- context.clock.reset();
192
+ clock.reset();
193
+ if (body.freeze === true)
194
+ clock.freeze();
192
195
  if (body.set !== void 0) {
193
196
  const instant = parseInstant(body.set);
194
197
  if (instant === void 0)
195
198
  return adminError(400, "set: expected epoch ms or ISO-8601");
196
- context.clock.set(instant);
199
+ clock.set(instant);
197
200
  }
198
201
  if (body.advance !== void 0) {
199
202
  const delta = parseDuration(body.advance);
200
203
  if (delta === void 0)
201
204
  return adminError(400, 'advance: expected ms or "15m"-style');
202
- context.clock.advance(delta);
205
+ clock.advance(delta);
203
206
  }
204
- if (body.freeze === true)
205
- context.clock.freeze();
206
207
  if (body.freeze === false)
207
- context.clock.unfreeze();
208
- return json(200, context.clock.state());
208
+ clock.unfreeze();
209
+ return json(200, clock.state());
209
210
  },
210
211
  "GET /faults": () => json(200, { faults: context.faults.list() }),
211
212
  "POST /faults": ({ body, namespace }) => {
@@ -474,7 +475,33 @@ var faultResponse = (rule) => {
474
475
  };
475
476
  var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) => {
476
477
  const entries = [];
478
+ let isolated = false;
479
+ const scopedRngs = /* @__PURE__ */ new Map();
480
+ const random = (namespace) => {
481
+ if (!isolated || namespace === "default")
482
+ return rng;
483
+ let scoped = scopedRngs.get(namespace);
484
+ if (!scoped) {
485
+ scoped = createRng(rng.seed);
486
+ scopedRngs.set(namespace, scoped);
487
+ }
488
+ return scoped;
489
+ };
477
490
  return {
491
+ isolateNamespaces() {
492
+ isolated = true;
493
+ },
494
+ snapshot(namespace) {
495
+ return {
496
+ rules: structuredClone(entries.map((entry, position) => ({
497
+ ...entry.rule,
498
+ remaining: entry.remaining,
499
+ hits: entry.hits,
500
+ position
501
+ })).filter((entry) => entry.namespace === namespace)),
502
+ rngState: random(namespace).state()
503
+ };
504
+ },
478
505
  add(rule) {
479
506
  const existing = entries.findIndex((e) => e.rule.id === rule.id);
480
507
  const entry = { rule, remaining: rule.count ?? null, hits: 0 };
@@ -494,6 +521,16 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
494
521
  },
495
522
  clear() {
496
523
  entries.length = 0;
524
+ rng.reset();
525
+ scopedRngs.clear();
526
+ },
527
+ restore(namespace, state) {
528
+ for (let i = entries.length - 1; i >= 0; i--)
529
+ if (entries[i]?.rule.namespace === namespace)
530
+ entries.splice(i, 1);
531
+ for (const { remaining, hits, position, ...rule } of state.rules)
532
+ entries.splice(Math.min(position, entries.length), 0, { rule, remaining, hits });
533
+ random(namespace).setState(state.rngState);
497
534
  },
498
535
  async take(candidate) {
499
536
  const hits = [];
@@ -503,7 +540,7 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
503
540
  if (!matches(entry.rule, candidate))
504
541
  continue;
505
542
  const rate = entry.rule.rate ?? 1;
506
- if (rng.next() >= rate)
543
+ if (random(candidate.namespace).next() >= rate)
507
544
  continue;
508
545
  entry.hits++;
509
546
  if (entry.remaining !== null)
@@ -1052,7 +1089,8 @@ var annotateResponse = (response, extra) => {
1052
1089
  var responseNotes = (response) => notes.get(response);
1053
1090
 
1054
1091
  // ../core/dist/metrics.js
1055
- var createMetrics = () => {
1092
+ var createMetrics = (partition = true) => {
1093
+ const namespaces = /* @__PURE__ */ new Map();
1056
1094
  let requests = 0;
1057
1095
  let faults = 0;
1058
1096
  let totalDurationMs = 0;
@@ -1060,6 +1098,14 @@ var createMetrics = () => {
1060
1098
  const unmatched = /* @__PURE__ */ new Map();
1061
1099
  return {
1062
1100
  record(entry) {
1101
+ if (partition) {
1102
+ let scoped = namespaces.get(entry.namespace);
1103
+ if (!scoped) {
1104
+ scoped = createMetrics(false);
1105
+ namespaces.set(entry.namespace, scoped);
1106
+ }
1107
+ scoped.record(entry);
1108
+ }
1063
1109
  requests++;
1064
1110
  totalDurationMs += entry.durationMs;
1065
1111
  if (entry.faultId !== void 0)
@@ -1071,7 +1117,7 @@ var createMetrics = () => {
1071
1117
  unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
1072
1118
  }
1073
1119
  },
1074
- report: () => ({
1120
+ report: (namespace) => namespace !== void 0 ? (namespaces.get(namespace) ?? createMetrics(false)).report() : {
1075
1121
  requests,
1076
1122
  byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
1077
1123
  unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
@@ -1080,8 +1126,13 @@ var createMetrics = () => {
1080
1126
  }),
1081
1127
  faults,
1082
1128
  totalDurationMs
1083
- }),
1084
- reset() {
1129
+ },
1130
+ reset(namespace) {
1131
+ if (namespace !== void 0) {
1132
+ namespaces.delete(namespace);
1133
+ return;
1134
+ }
1135
+ namespaces.clear();
1085
1136
  requests = 0;
1086
1137
  faults = 0;
1087
1138
  totalDurationMs = 0;
@@ -4053,7 +4104,7 @@ var stateAdminRoutes = (routes) => {
4053
4104
  };
4054
4105
 
4055
4106
  // ../core/dist/version.js
4056
- var PACKAGE_VERSION = true ? "1.7.0" : UNRELEASED_VERSION;
4107
+ var PACKAGE_VERSION = true ? "1.8.0" : UNRELEASED_VERSION;
4057
4108
 
4058
4109
  // ../core/dist/webhooks.js
4059
4110
  var json4 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
@@ -4219,6 +4270,20 @@ var createRuntime = (options) => {
4219
4270
  const instances = /* @__PURE__ */ new Map();
4220
4271
  const publicNamespaces = /* @__PURE__ */ new Set();
4221
4272
  const branchRngs = /* @__PURE__ */ new Map();
4273
+ const namespaceClocks = /* @__PURE__ */ new Map();
4274
+ const namespaceForStorage = /* @__PURE__ */ new Map();
4275
+ let isolatedNamespaces = false;
4276
+ const namespaceClock = (name = DEFAULT_NAMESPACE) => {
4277
+ if (!isolatedNamespaces || name === DEFAULT_NAMESPACE)
4278
+ return clock;
4279
+ let found = namespaceClocks.get(name);
4280
+ if (!found) {
4281
+ found = createClock();
4282
+ namespaceClocks.set(name, found);
4283
+ }
4284
+ return found;
4285
+ };
4286
+ const fleetPoints = /* @__PURE__ */ new WeakMap();
4222
4287
  const timelines = /* @__PURE__ */ new Map();
4223
4288
  const branchStorage = /* @__PURE__ */ new Map();
4224
4289
  const captured = /* @__PURE__ */ new Map();
@@ -4232,17 +4297,21 @@ var createRuntime = (options) => {
4232
4297
  if (!NAMESPACE_PATTERN.test(key) || !NAMESPACE_PATTERN.test(publicNamespace)) {
4233
4298
  throw new RangeError(`namespace must match ${NAMESPACE_PATTERN}: ${JSON.stringify(publicNamespace)}`);
4234
4299
  }
4300
+ const createdContextRng = isolatedRng ?? (isolatedNamespaces && key !== DEFAULT_NAMESPACE ? createRng(options.seed ?? 0) : rng);
4235
4301
  const created = options.create({
4236
4302
  namespace: storageNamespace(key),
4237
4303
  publicNamespace,
4238
4304
  sqlite,
4239
- clock,
4240
- rng: isolatedRng ?? rng
4305
+ clock: namespaceClock(publicNamespace),
4306
+ rng: createdContextRng
4241
4307
  });
4242
4308
  instances.set(key, created);
4309
+ namespaceForStorage.set(key, publicNamespace);
4243
4310
  publicNamespaces.add(publicNamespace);
4244
4311
  if (isolatedRng)
4245
4312
  branchRngs.set(key, isolatedRng);
4313
+ else if (isolatedNamespaces && key !== DEFAULT_NAMESPACE)
4314
+ branchRngs.set(key, createdContextRng);
4246
4315
  return created;
4247
4316
  };
4248
4317
  const instance = (name = DEFAULT_NAMESPACE) => instanceFor(name);
@@ -4260,7 +4329,7 @@ var createRuntime = (options) => {
4260
4329
  captured.set(storage, snapshot2);
4261
4330
  return Object.freeze({
4262
4331
  snapshot: snapshot2,
4263
- clock: Object.freeze(clock.state()),
4332
+ clock: Object.freeze(namespaceClock(namespaceForStorage.get(storage) ?? storage).state()),
4264
4333
  rngState: (branchRngs.get(storage) ?? rng).state()
4265
4334
  });
4266
4335
  };
@@ -4270,7 +4339,7 @@ var createRuntime = (options) => {
4270
4339
  return found;
4271
4340
  instance(name);
4272
4341
  found = new Timeline({
4273
- now: clock.now,
4342
+ now: namespaceClock(name).now,
4274
4343
  ...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
4275
4344
  });
4276
4345
  found.commit(capture(name));
@@ -4297,12 +4366,13 @@ var createRuntime = (options) => {
4297
4366
  const point = history2.checkout("main", at);
4298
4367
  restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
4299
4368
  captured.set(namespace, point.value.snapshot);
4300
- rng.setState(point.value.rngState);
4301
- clock.set(point.value.clock.now);
4369
+ (branchRngs.get(namespace) ?? rng).setState(point.value.rngState);
4370
+ const selectedClock = namespaceClock(namespace);
4371
+ selectedClock.set(point.value.clock.now);
4302
4372
  if (point.value.clock.frozen)
4303
- clock.freeze();
4373
+ selectedClock.freeze();
4304
4374
  else
4305
- clock.unfreeze();
4375
+ selectedClock.unfreeze();
4306
4376
  }
4307
4377
  return namespace;
4308
4378
  }
@@ -4360,11 +4430,12 @@ var createRuntime = (options) => {
4360
4430
  const storage = ensureBranch(namespace, branchName);
4361
4431
  restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
4362
4432
  captured.set(storage, point.value.snapshot);
4363
- clock.set(point.value.clock.now);
4433
+ const selectedClock = namespaceClock(namespace);
4434
+ selectedClock.set(point.value.clock.now);
4364
4435
  if (point.value.clock.frozen)
4365
- clock.freeze();
4436
+ selectedClock.freeze();
4366
4437
  else
4367
- clock.unfreeze();
4438
+ selectedClock.unfreeze();
4368
4439
  (branchRngs.get(storage) ?? rng).setState(point.value.rngState);
4369
4440
  };
4370
4441
  const reset = async (name = DEFAULT_NAMESPACE) => {
@@ -4372,10 +4443,21 @@ var createRuntime = (options) => {
4372
4443
  options.webhooks?.clear();
4373
4444
  for (const each of instances.values())
4374
4445
  await each.reset();
4446
+ for (const each of branchRngs.values())
4447
+ each.reset();
4375
4448
  timelines.clear();
4449
+ for (const storage of branchStorage.values()) {
4450
+ instances.delete(storage);
4451
+ branchRngs.delete(storage);
4452
+ namespaceForStorage.delete(storage);
4453
+ }
4376
4454
  branchStorage.clear();
4377
- branchRngs.clear();
4378
4455
  captured.clear();
4456
+ faults.clear();
4457
+ clock.reset();
4458
+ rng.reset();
4459
+ for (const each of namespaceClocks.values())
4460
+ each.reset();
4379
4461
  return;
4380
4462
  }
4381
4463
  options.webhooks?.clear(name);
@@ -4393,11 +4475,18 @@ var createRuntime = (options) => {
4393
4475
  else
4394
4476
  clearNamespace(sqlite, storageNamespace(storage));
4395
4477
  branchStorage.delete(mapping);
4478
+ instances.delete(storage);
4479
+ namespaceForStorage.delete(storage);
4396
4480
  branchRngs.delete(storage);
4397
4481
  captured.delete(storage);
4398
4482
  }
4399
4483
  timelines.delete(name);
4400
4484
  captured.delete(name);
4485
+ faults.restore(name, { rules: [], rngState: createRng(options.seed ?? 0).state() });
4486
+ namespaceClock(name).reset();
4487
+ (branchRngs.get(name) ?? rng).reset();
4488
+ journal.clear(name);
4489
+ metrics.reset(name);
4401
4490
  };
4402
4491
  const snapshot = (name = DEFAULT_NAMESPACE) => {
4403
4492
  return checkpoint(name, "main").value.snapshot;
@@ -4435,6 +4524,53 @@ var createRuntime = (options) => {
4435
4524
  rng,
4436
4525
  credentials,
4437
4526
  webhooks: options.webhooks,
4527
+ isolateNamespaces() {
4528
+ if (publicNamespaces.size > 1)
4529
+ throw new Error("enable namespace isolation before creating named namespaces");
4530
+ isolatedNamespaces = true;
4531
+ faults.isolateNamespaces();
4532
+ },
4533
+ namespaceClock,
4534
+ namespaceOf(request) {
4535
+ const url = new URL(request.url);
4536
+ const prefixed = PATH_PREFIX.exec(url.pathname);
4537
+ const selected = request.headers.get(NAMESPACE_HEADER) ?? (prefixed ? decodeURIComponent(prefixed[1]) : void 0);
4538
+ const path = prefixed ? prefixed[2] ?? "/" : url.pathname;
4539
+ if (path === "/__admin" || path.startsWith("/__admin/"))
4540
+ return url.searchParams.get("namespace") ?? selected ?? DEFAULT_NAMESPACE;
4541
+ if (selected !== void 0)
4542
+ return selected;
4543
+ const credential = options.credential?.(request);
4544
+ return (credential !== void 0 ? credentials.get(credential) : void 0) ?? DEFAULT_NAMESPACE;
4545
+ },
4546
+ fleetSnapshot(namespace) {
4547
+ instance(namespace);
4548
+ const handle = {};
4549
+ fleetPoints.set(handle, {
4550
+ namespace,
4551
+ state: capture(namespace),
4552
+ faults: faults.snapshot(namespace),
4553
+ webhooks: options.webhooks?.snapshot(namespace)
4554
+ });
4555
+ return handle;
4556
+ },
4557
+ fleetRestore(handle, namespace) {
4558
+ const point = typeof handle === "object" && handle !== null ? fleetPoints.get(handle) : void 0;
4559
+ if (!point || point.namespace !== namespace)
4560
+ throw new Error("invalid fleet checkpoint");
4561
+ restoreNamespace(sqlite, storageNamespace(namespace), point.state.snapshot);
4562
+ captured.set(namespace, point.state.snapshot);
4563
+ const selectedClock = namespaceClock(namespace);
4564
+ selectedClock.freeze();
4565
+ selectedClock.set(point.state.clock.now);
4566
+ if (!point.state.clock.frozen)
4567
+ selectedClock.unfreeze();
4568
+ (branchRngs.get(namespace) ?? rng).setState(point.state.rngState);
4569
+ faults.restore(namespace, point.faults);
4570
+ if (point.webhooks !== void 0)
4571
+ options.webhooks?.restore(namespace, point.webhooks);
4572
+ timelines.delete(namespace);
4573
+ },
4438
4574
  applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
4439
4575
  const preset = options.presets?.[name];
4440
4576
  if (!preset)
@@ -4610,6 +4746,7 @@ var createRuntime = (options) => {
4610
4746
  startedAt: wallNow(),
4611
4747
  wallNow,
4612
4748
  clock,
4749
+ namespaceClock,
4613
4750
  faults,
4614
4751
  metrics,
4615
4752
  journal,
@@ -5277,6 +5414,8 @@ var LlamaCloudAPI = class {
5277
5414
  };
5278
5415
 
5279
5416
  export {
5417
+ createClock,
5418
+ parseDuration,
5280
5419
  document2 as document,
5281
5420
  operationIds,
5282
5421
  supportedOperationIds,
@@ -5292,4 +5431,4 @@ export {
5292
5431
  LLAMACLOUD_NAMESPACE,
5293
5432
  LlamaCloudAPI
5294
5433
  };
5295
- //# sourceMappingURL=chunk-D7EKGZUI.js.map
5434
+ //# sourceMappingURL=chunk-RWVJQZGU.js.map