@crvouga/mockingbird-service-fullscript 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-fullscript
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)
@@ -1093,7 +1130,8 @@ var annotateResponse = (response, extra) => {
1093
1130
  var responseNotes = (response) => notes.get(response);
1094
1131
 
1095
1132
  // ../core/dist/metrics.js
1096
- var createMetrics = () => {
1133
+ var createMetrics = (partition = true) => {
1134
+ const namespaces = /* @__PURE__ */ new Map();
1097
1135
  let requests = 0;
1098
1136
  let faults = 0;
1099
1137
  let totalDurationMs = 0;
@@ -1101,6 +1139,14 @@ var createMetrics = () => {
1101
1139
  const unmatched = /* @__PURE__ */ new Map();
1102
1140
  return {
1103
1141
  record(entry) {
1142
+ if (partition) {
1143
+ let scoped = namespaces.get(entry.namespace);
1144
+ if (!scoped) {
1145
+ scoped = createMetrics(false);
1146
+ namespaces.set(entry.namespace, scoped);
1147
+ }
1148
+ scoped.record(entry);
1149
+ }
1104
1150
  requests++;
1105
1151
  totalDurationMs += entry.durationMs;
1106
1152
  if (entry.faultId !== void 0)
@@ -1112,7 +1158,7 @@ var createMetrics = () => {
1112
1158
  unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
1113
1159
  }
1114
1160
  },
1115
- report: () => ({
1161
+ report: (namespace) => namespace !== void 0 ? (namespaces.get(namespace) ?? createMetrics(false)).report() : {
1116
1162
  requests,
1117
1163
  byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
1118
1164
  unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
@@ -1121,8 +1167,13 @@ var createMetrics = () => {
1121
1167
  }),
1122
1168
  faults,
1123
1169
  totalDurationMs
1124
- }),
1125
- reset() {
1170
+ },
1171
+ reset(namespace) {
1172
+ if (namespace !== void 0) {
1173
+ namespaces.delete(namespace);
1174
+ return;
1175
+ }
1176
+ namespaces.clear();
1126
1177
  requests = 0;
1127
1178
  faults = 0;
1128
1179
  totalDurationMs = 0;
@@ -4094,7 +4145,7 @@ var stateAdminRoutes = (routes) => {
4094
4145
  };
4095
4146
 
4096
4147
  // ../core/dist/version.js
4097
- var PACKAGE_VERSION = true ? "1.7.0" : UNRELEASED_VERSION;
4148
+ var PACKAGE_VERSION = true ? "1.8.0" : UNRELEASED_VERSION;
4098
4149
 
4099
4150
  // ../core/dist/signing.js
4100
4151
  var encoder = new TextEncoder();
@@ -4187,7 +4238,9 @@ var createWebhookHub = (options) => {
4187
4238
  const faults = /* @__PURE__ */ new Map();
4188
4239
  const held = /* @__PURE__ */ new Map();
4189
4240
  const inFlight = /* @__PURE__ */ new Set();
4241
+ const controllers = /* @__PURE__ */ new Map();
4190
4242
  let immediate = Promise.resolve();
4243
+ const snapshots = /* @__PURE__ */ new WeakMap();
4191
4244
  const track = (work) => {
4192
4245
  inFlight.add(work);
4193
4246
  void work.finally(() => inFlight.delete(work));
@@ -4208,6 +4261,7 @@ var createWebhookHub = (options) => {
4208
4261
  responseBody: null
4209
4262
  };
4210
4263
  const controller = new AbortController();
4264
+ controllers.set(delivery.id, controller);
4211
4265
  const timer = scheduleTimer(() => controller.abort(), timeoutMs);
4212
4266
  try {
4213
4267
  const signed = await options.signer({
@@ -4237,6 +4291,7 @@ var createWebhookHub = (options) => {
4237
4291
  } catch (error) {
4238
4292
  record.error = controller.signal.aborted ? `timed out after ${timeoutMs}ms` : error instanceof Error ? error.message : String(error);
4239
4293
  } finally {
4294
+ controllers.delete(delivery.id);
4240
4295
  cancel(timer);
4241
4296
  record.durationMs = now() - started;
4242
4297
  delivery.attempts.push(record);
@@ -4254,6 +4309,8 @@ var createWebhookHub = (options) => {
4254
4309
  if (!payloads.has(delivery.id))
4255
4310
  return;
4256
4311
  await attempt(delivery).then((ok2) => {
4312
+ if (!payloads.has(delivery.id))
4313
+ return;
4257
4314
  if (ok2)
4258
4315
  delivery.state = "delivered";
4259
4316
  else
@@ -4315,6 +4372,37 @@ var createWebhookHub = (options) => {
4315
4372
  fanOut(message);
4316
4373
  };
4317
4374
  const hub = {
4375
+ pending(namespace) {
4376
+ return inFlight.size + (held.get(namespace)?.length ?? 0) + [...deliveries.values()].filter((d) => d.namespace === namespace && d.state === "pending").length;
4377
+ },
4378
+ snapshot(namespace) {
4379
+ if (hub.pending(namespace) > 0)
4380
+ throw new Error("webhooks are not quiescent");
4381
+ const handle = {};
4382
+ snapshots.set(handle, structuredClone({
4383
+ namespace,
4384
+ endpoints: own.get(namespace) ?? [],
4385
+ messages: messages.filter((m) => m.namespace === namespace),
4386
+ deliveries: [...deliveries].filter(([, d]) => d.namespace === namespace),
4387
+ payloads: [...payloads].filter(([, p]) => p.message.namespace === namespace),
4388
+ faults: faults.get(namespace) ?? []
4389
+ }));
4390
+ return handle;
4391
+ },
4392
+ restore(namespace, handle) {
4393
+ const point = typeof handle === "object" && handle !== null ? snapshots.get(handle) : void 0;
4394
+ if (!point || point.namespace !== namespace || hub.pending(namespace) > 0)
4395
+ throw new Error("invalid webhook restore");
4396
+ hub.clear(namespace);
4397
+ const copied = structuredClone(point);
4398
+ own.set(namespace, copied.endpoints);
4399
+ messages.push(...copied.messages);
4400
+ for (const [id2, delivery] of copied.deliveries)
4401
+ deliveries.set(id2, delivery);
4402
+ for (const [id2, payload] of copied.payloads)
4403
+ payloads.set(id2, payload);
4404
+ faults.set(namespace, copied.faults);
4405
+ },
4318
4406
  publish(input) {
4319
4407
  const contentType = input.contentType ?? (input.form ? "application/x-www-form-urlencoded" : "application/json");
4320
4408
  const body = typeof input.body === "string" ? input.body : input.form ? new URLSearchParams(input.form).toString() : JSON.stringify(input.body);
@@ -4405,6 +4493,7 @@ var createWebhookHub = (options) => {
4405
4493
  for (const [id2, delivery] of deliveries) {
4406
4494
  if (namespace !== void 0 && delivery.namespace !== namespace)
4407
4495
  continue;
4496
+ controllers.get(id2)?.abort();
4408
4497
  const timer = pending.get(id2);
4409
4498
  if (timer !== void 0)
4410
4499
  cancel(timer);
@@ -4592,6 +4681,20 @@ var createRuntime = (options) => {
4592
4681
  const instances = /* @__PURE__ */ new Map();
4593
4682
  const publicNamespaces = /* @__PURE__ */ new Set();
4594
4683
  const branchRngs = /* @__PURE__ */ new Map();
4684
+ const namespaceClocks = /* @__PURE__ */ new Map();
4685
+ const namespaceForStorage = /* @__PURE__ */ new Map();
4686
+ let isolatedNamespaces = false;
4687
+ const namespaceClock = (name = DEFAULT_NAMESPACE) => {
4688
+ if (!isolatedNamespaces || name === DEFAULT_NAMESPACE)
4689
+ return clock;
4690
+ let found = namespaceClocks.get(name);
4691
+ if (!found) {
4692
+ found = createClock();
4693
+ namespaceClocks.set(name, found);
4694
+ }
4695
+ return found;
4696
+ };
4697
+ const fleetPoints = /* @__PURE__ */ new WeakMap();
4595
4698
  const timelines = /* @__PURE__ */ new Map();
4596
4699
  const branchStorage = /* @__PURE__ */ new Map();
4597
4700
  const captured = /* @__PURE__ */ new Map();
@@ -4605,17 +4708,21 @@ var createRuntime = (options) => {
4605
4708
  if (!NAMESPACE_PATTERN.test(key) || !NAMESPACE_PATTERN.test(publicNamespace)) {
4606
4709
  throw new RangeError(`namespace must match ${NAMESPACE_PATTERN}: ${JSON.stringify(publicNamespace)}`);
4607
4710
  }
4711
+ const createdContextRng = isolatedRng ?? (isolatedNamespaces && key !== DEFAULT_NAMESPACE ? createRng(options.seed ?? 0) : rng);
4608
4712
  const created = options.create({
4609
4713
  namespace: storageNamespace(key),
4610
4714
  publicNamespace,
4611
4715
  sqlite,
4612
- clock,
4613
- rng: isolatedRng ?? rng
4716
+ clock: namespaceClock(publicNamespace),
4717
+ rng: createdContextRng
4614
4718
  });
4615
4719
  instances.set(key, created);
4720
+ namespaceForStorage.set(key, publicNamespace);
4616
4721
  publicNamespaces.add(publicNamespace);
4617
4722
  if (isolatedRng)
4618
4723
  branchRngs.set(key, isolatedRng);
4724
+ else if (isolatedNamespaces && key !== DEFAULT_NAMESPACE)
4725
+ branchRngs.set(key, createdContextRng);
4619
4726
  return created;
4620
4727
  };
4621
4728
  const instance = (name = DEFAULT_NAMESPACE) => instanceFor(name);
@@ -4633,7 +4740,7 @@ var createRuntime = (options) => {
4633
4740
  captured.set(storage, snapshot2);
4634
4741
  return Object.freeze({
4635
4742
  snapshot: snapshot2,
4636
- clock: Object.freeze(clock.state()),
4743
+ clock: Object.freeze(namespaceClock(namespaceForStorage.get(storage) ?? storage).state()),
4637
4744
  rngState: (branchRngs.get(storage) ?? rng).state()
4638
4745
  });
4639
4746
  };
@@ -4643,7 +4750,7 @@ var createRuntime = (options) => {
4643
4750
  return found;
4644
4751
  instance(name);
4645
4752
  found = new Timeline({
4646
- now: clock.now,
4753
+ now: namespaceClock(name).now,
4647
4754
  ...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
4648
4755
  });
4649
4756
  found.commit(capture(name));
@@ -4670,12 +4777,13 @@ var createRuntime = (options) => {
4670
4777
  const point = history2.checkout("main", at);
4671
4778
  restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
4672
4779
  captured.set(namespace, point.value.snapshot);
4673
- rng.setState(point.value.rngState);
4674
- clock.set(point.value.clock.now);
4780
+ (branchRngs.get(namespace) ?? rng).setState(point.value.rngState);
4781
+ const selectedClock = namespaceClock(namespace);
4782
+ selectedClock.set(point.value.clock.now);
4675
4783
  if (point.value.clock.frozen)
4676
- clock.freeze();
4784
+ selectedClock.freeze();
4677
4785
  else
4678
- clock.unfreeze();
4786
+ selectedClock.unfreeze();
4679
4787
  }
4680
4788
  return namespace;
4681
4789
  }
@@ -4733,11 +4841,12 @@ var createRuntime = (options) => {
4733
4841
  const storage = ensureBranch(namespace, branchName);
4734
4842
  restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
4735
4843
  captured.set(storage, point.value.snapshot);
4736
- clock.set(point.value.clock.now);
4844
+ const selectedClock = namespaceClock(namespace);
4845
+ selectedClock.set(point.value.clock.now);
4737
4846
  if (point.value.clock.frozen)
4738
- clock.freeze();
4847
+ selectedClock.freeze();
4739
4848
  else
4740
- clock.unfreeze();
4849
+ selectedClock.unfreeze();
4741
4850
  (branchRngs.get(storage) ?? rng).setState(point.value.rngState);
4742
4851
  };
4743
4852
  const reset = async (name = DEFAULT_NAMESPACE) => {
@@ -4745,10 +4854,21 @@ var createRuntime = (options) => {
4745
4854
  options.webhooks?.clear();
4746
4855
  for (const each of instances.values())
4747
4856
  await each.reset();
4857
+ for (const each of branchRngs.values())
4858
+ each.reset();
4748
4859
  timelines.clear();
4860
+ for (const storage of branchStorage.values()) {
4861
+ instances.delete(storage);
4862
+ branchRngs.delete(storage);
4863
+ namespaceForStorage.delete(storage);
4864
+ }
4749
4865
  branchStorage.clear();
4750
- branchRngs.clear();
4751
4866
  captured.clear();
4867
+ faults.clear();
4868
+ clock.reset();
4869
+ rng.reset();
4870
+ for (const each of namespaceClocks.values())
4871
+ each.reset();
4752
4872
  return;
4753
4873
  }
4754
4874
  options.webhooks?.clear(name);
@@ -4766,11 +4886,18 @@ var createRuntime = (options) => {
4766
4886
  else
4767
4887
  clearNamespace(sqlite, storageNamespace(storage));
4768
4888
  branchStorage.delete(mapping);
4889
+ instances.delete(storage);
4890
+ namespaceForStorage.delete(storage);
4769
4891
  branchRngs.delete(storage);
4770
4892
  captured.delete(storage);
4771
4893
  }
4772
4894
  timelines.delete(name);
4773
4895
  captured.delete(name);
4896
+ faults.restore(name, { rules: [], rngState: createRng(options.seed ?? 0).state() });
4897
+ namespaceClock(name).reset();
4898
+ (branchRngs.get(name) ?? rng).reset();
4899
+ journal.clear(name);
4900
+ metrics.reset(name);
4774
4901
  };
4775
4902
  const snapshot = (name = DEFAULT_NAMESPACE) => {
4776
4903
  return checkpoint(name, "main").value.snapshot;
@@ -4808,6 +4935,53 @@ var createRuntime = (options) => {
4808
4935
  rng,
4809
4936
  credentials,
4810
4937
  webhooks: options.webhooks,
4938
+ isolateNamespaces() {
4939
+ if (publicNamespaces.size > 1)
4940
+ throw new Error("enable namespace isolation before creating named namespaces");
4941
+ isolatedNamespaces = true;
4942
+ faults.isolateNamespaces();
4943
+ },
4944
+ namespaceClock,
4945
+ namespaceOf(request) {
4946
+ const url = new URL(request.url);
4947
+ const prefixed = PATH_PREFIX.exec(url.pathname);
4948
+ const selected = request.headers.get(NAMESPACE_HEADER) ?? (prefixed ? decodeURIComponent(prefixed[1]) : void 0);
4949
+ const path = prefixed ? prefixed[2] ?? "/" : url.pathname;
4950
+ if (path === "/__admin" || path.startsWith("/__admin/"))
4951
+ return url.searchParams.get("namespace") ?? selected ?? DEFAULT_NAMESPACE;
4952
+ if (selected !== void 0)
4953
+ return selected;
4954
+ const credential = options.credential?.(request);
4955
+ return (credential !== void 0 ? credentials.get(credential) : void 0) ?? DEFAULT_NAMESPACE;
4956
+ },
4957
+ fleetSnapshot(namespace) {
4958
+ instance(namespace);
4959
+ const handle = {};
4960
+ fleetPoints.set(handle, {
4961
+ namespace,
4962
+ state: capture(namespace),
4963
+ faults: faults.snapshot(namespace),
4964
+ webhooks: options.webhooks?.snapshot(namespace)
4965
+ });
4966
+ return handle;
4967
+ },
4968
+ fleetRestore(handle, namespace) {
4969
+ const point = typeof handle === "object" && handle !== null ? fleetPoints.get(handle) : void 0;
4970
+ if (!point || point.namespace !== namespace)
4971
+ throw new Error("invalid fleet checkpoint");
4972
+ restoreNamespace(sqlite, storageNamespace(namespace), point.state.snapshot);
4973
+ captured.set(namespace, point.state.snapshot);
4974
+ const selectedClock = namespaceClock(namespace);
4975
+ selectedClock.freeze();
4976
+ selectedClock.set(point.state.clock.now);
4977
+ if (!point.state.clock.frozen)
4978
+ selectedClock.unfreeze();
4979
+ (branchRngs.get(namespace) ?? rng).setState(point.state.rngState);
4980
+ faults.restore(namespace, point.faults);
4981
+ if (point.webhooks !== void 0)
4982
+ options.webhooks?.restore(namespace, point.webhooks);
4983
+ timelines.delete(namespace);
4984
+ },
4811
4985
  applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
4812
4986
  const preset = options.presets?.[name];
4813
4987
  if (!preset)
@@ -4983,6 +5157,7 @@ var createRuntime = (options) => {
4983
5157
  startedAt: wallNow(),
4984
5158
  wallNow,
4985
5159
  clock,
5160
+ namespaceClock,
4986
5161
  faults,
4987
5162
  metrics,
4988
5163
  journal,
@@ -6000,6 +6175,8 @@ var FullscriptAPI = class {
6000
6175
  };
6001
6176
 
6002
6177
  export {
6178
+ createClock,
6179
+ parseDuration,
6003
6180
  document2 as document,
6004
6181
  operationIds,
6005
6182
  supportedOperationIds,
@@ -6020,4 +6197,4 @@ export {
6020
6197
  envelope,
6021
6198
  FullscriptAPI
6022
6199
  };
6023
- //# sourceMappingURL=chunk-Q5BBSHKJ.js.map
6200
+ //# sourceMappingURL=chunk-L5A2NSLT.js.map