@crvouga/mockingbird-service-google-calendar 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-google-calendar
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 }) => {
@@ -492,7 +493,33 @@ var faultResponse = (rule) => {
492
493
  };
493
494
  var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))) => {
494
495
  const entries = [];
496
+ let isolated = false;
497
+ const scopedRngs = /* @__PURE__ */ new Map();
498
+ const random = (namespace) => {
499
+ if (!isolated || namespace === "default")
500
+ return rng;
501
+ let scoped = scopedRngs.get(namespace);
502
+ if (!scoped) {
503
+ scoped = createRng(rng.seed);
504
+ scopedRngs.set(namespace, scoped);
505
+ }
506
+ return scoped;
507
+ };
495
508
  return {
509
+ isolateNamespaces() {
510
+ isolated = true;
511
+ },
512
+ snapshot(namespace) {
513
+ return {
514
+ rules: structuredClone(entries.map((entry, position) => ({
515
+ ...entry.rule,
516
+ remaining: entry.remaining,
517
+ hits: entry.hits,
518
+ position
519
+ })).filter((entry) => entry.namespace === namespace)),
520
+ rngState: random(namespace).state()
521
+ };
522
+ },
496
523
  add(rule) {
497
524
  const existing = entries.findIndex((e) => e.rule.id === rule.id);
498
525
  const entry = { rule, remaining: rule.count ?? null, hits: 0 };
@@ -512,6 +539,16 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
512
539
  },
513
540
  clear() {
514
541
  entries.length = 0;
542
+ rng.reset();
543
+ scopedRngs.clear();
544
+ },
545
+ restore(namespace, state) {
546
+ for (let i = entries.length - 1; i >= 0; i--)
547
+ if (entries[i]?.rule.namespace === namespace)
548
+ entries.splice(i, 1);
549
+ for (const { remaining, hits, position, ...rule } of state.rules)
550
+ entries.splice(Math.min(position, entries.length), 0, { rule, remaining, hits });
551
+ random(namespace).setState(state.rngState);
515
552
  },
516
553
  async take(candidate) {
517
554
  const hits = [];
@@ -521,7 +558,7 @@ var createFaultRegistry = (rng = createRng(0), sleep = (ms) => new Promise((reso
521
558
  if (!matches(entry.rule, candidate))
522
559
  continue;
523
560
  const rate = entry.rule.rate ?? 1;
524
- if (rng.next() >= rate)
561
+ if (random(candidate.namespace).next() >= rate)
525
562
  continue;
526
563
  entry.hits++;
527
564
  if (entry.remaining !== null)
@@ -1089,7 +1126,8 @@ var annotateResponse = (response, extra) => {
1089
1126
  var responseNotes = (response) => notes.get(response);
1090
1127
 
1091
1128
  // ../core/dist/metrics.js
1092
- var createMetrics = () => {
1129
+ var createMetrics = (partition = true) => {
1130
+ const namespaces = /* @__PURE__ */ new Map();
1093
1131
  let requests = 0;
1094
1132
  let faults = 0;
1095
1133
  let totalDurationMs = 0;
@@ -1097,6 +1135,14 @@ var createMetrics = () => {
1097
1135
  const unmatched = /* @__PURE__ */ new Map();
1098
1136
  return {
1099
1137
  record(entry) {
1138
+ if (partition) {
1139
+ let scoped = namespaces.get(entry.namespace);
1140
+ if (!scoped) {
1141
+ scoped = createMetrics(false);
1142
+ namespaces.set(entry.namespace, scoped);
1143
+ }
1144
+ scoped.record(entry);
1145
+ }
1100
1146
  requests++;
1101
1147
  totalDurationMs += entry.durationMs;
1102
1148
  if (entry.faultId !== void 0)
@@ -1108,7 +1154,7 @@ var createMetrics = () => {
1108
1154
  unmatched.set(route, (unmatched.get(route) ?? 0) + 1);
1109
1155
  }
1110
1156
  },
1111
- report: () => ({
1157
+ report: (namespace) => namespace !== void 0 ? (namespaces.get(namespace) ?? createMetrics(false)).report() : {
1112
1158
  requests,
1113
1159
  byOperation: Object.fromEntries([...byOperation].sort(([a], [b]) => a.localeCompare(b))),
1114
1160
  unmatched: [...unmatched].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([route, count]) => {
@@ -1117,8 +1163,13 @@ var createMetrics = () => {
1117
1163
  }),
1118
1164
  faults,
1119
1165
  totalDurationMs
1120
- }),
1121
- reset() {
1166
+ },
1167
+ reset(namespace) {
1168
+ if (namespace !== void 0) {
1169
+ namespaces.delete(namespace);
1170
+ return;
1171
+ }
1172
+ namespaces.clear();
1122
1173
  requests = 0;
1123
1174
  faults = 0;
1124
1175
  totalDurationMs = 0;
@@ -4090,7 +4141,7 @@ var stateAdminRoutes = (routes) => {
4090
4141
  };
4091
4142
 
4092
4143
  // ../core/dist/version.js
4093
- var PACKAGE_VERSION = true ? "1.7.0" : UNRELEASED_VERSION;
4144
+ var PACKAGE_VERSION = true ? "1.8.0" : UNRELEASED_VERSION;
4094
4145
 
4095
4146
  // ../core/dist/signing.js
4096
4147
  var encoder = new TextEncoder();
@@ -4183,7 +4234,9 @@ var createWebhookHub = (options) => {
4183
4234
  const faults = /* @__PURE__ */ new Map();
4184
4235
  const held = /* @__PURE__ */ new Map();
4185
4236
  const inFlight = /* @__PURE__ */ new Set();
4237
+ const controllers = /* @__PURE__ */ new Map();
4186
4238
  let immediate = Promise.resolve();
4239
+ const snapshots = /* @__PURE__ */ new WeakMap();
4187
4240
  const track = (work) => {
4188
4241
  inFlight.add(work);
4189
4242
  void work.finally(() => inFlight.delete(work));
@@ -4204,6 +4257,7 @@ var createWebhookHub = (options) => {
4204
4257
  responseBody: null
4205
4258
  };
4206
4259
  const controller = new AbortController();
4260
+ controllers.set(delivery.id, controller);
4207
4261
  const timer = scheduleTimer(() => controller.abort(), timeoutMs);
4208
4262
  try {
4209
4263
  const signed = await options.signer({
@@ -4233,6 +4287,7 @@ var createWebhookHub = (options) => {
4233
4287
  } catch (error) {
4234
4288
  record.error = controller.signal.aborted ? `timed out after ${timeoutMs}ms` : error instanceof Error ? error.message : String(error);
4235
4289
  } finally {
4290
+ controllers.delete(delivery.id);
4236
4291
  cancel(timer);
4237
4292
  record.durationMs = now() - started;
4238
4293
  delivery.attempts.push(record);
@@ -4250,6 +4305,8 @@ var createWebhookHub = (options) => {
4250
4305
  if (!payloads.has(delivery.id))
4251
4306
  return;
4252
4307
  await attempt(delivery).then((ok) => {
4308
+ if (!payloads.has(delivery.id))
4309
+ return;
4253
4310
  if (ok)
4254
4311
  delivery.state = "delivered";
4255
4312
  else
@@ -4311,6 +4368,37 @@ var createWebhookHub = (options) => {
4311
4368
  fanOut(message);
4312
4369
  };
4313
4370
  const hub = {
4371
+ pending(namespace) {
4372
+ return inFlight.size + (held.get(namespace)?.length ?? 0) + [...deliveries.values()].filter((d) => d.namespace === namespace && d.state === "pending").length;
4373
+ },
4374
+ snapshot(namespace) {
4375
+ if (hub.pending(namespace) > 0)
4376
+ throw new Error("webhooks are not quiescent");
4377
+ const handle = {};
4378
+ snapshots.set(handle, structuredClone({
4379
+ namespace,
4380
+ endpoints: own.get(namespace) ?? [],
4381
+ messages: messages.filter((m) => m.namespace === namespace),
4382
+ deliveries: [...deliveries].filter(([, d]) => d.namespace === namespace),
4383
+ payloads: [...payloads].filter(([, p]) => p.message.namespace === namespace),
4384
+ faults: faults.get(namespace) ?? []
4385
+ }));
4386
+ return handle;
4387
+ },
4388
+ restore(namespace, handle) {
4389
+ const point = typeof handle === "object" && handle !== null ? snapshots.get(handle) : void 0;
4390
+ if (!point || point.namespace !== namespace || hub.pending(namespace) > 0)
4391
+ throw new Error("invalid webhook restore");
4392
+ hub.clear(namespace);
4393
+ const copied = structuredClone(point);
4394
+ own.set(namespace, copied.endpoints);
4395
+ messages.push(...copied.messages);
4396
+ for (const [id2, delivery] of copied.deliveries)
4397
+ deliveries.set(id2, delivery);
4398
+ for (const [id2, payload] of copied.payloads)
4399
+ payloads.set(id2, payload);
4400
+ faults.set(namespace, copied.faults);
4401
+ },
4314
4402
  publish(input) {
4315
4403
  const contentType = input.contentType ?? (input.form ? "application/x-www-form-urlencoded" : "application/json");
4316
4404
  const body = typeof input.body === "string" ? input.body : input.form ? new URLSearchParams(input.form).toString() : JSON.stringify(input.body);
@@ -4401,6 +4489,7 @@ var createWebhookHub = (options) => {
4401
4489
  for (const [id2, delivery] of deliveries) {
4402
4490
  if (namespace !== void 0 && delivery.namespace !== namespace)
4403
4491
  continue;
4492
+ controllers.get(id2)?.abort();
4404
4493
  const timer = pending.get(id2);
4405
4494
  if (timer !== void 0)
4406
4495
  cancel(timer);
@@ -4588,6 +4677,20 @@ var createRuntime = (options) => {
4588
4677
  const instances = /* @__PURE__ */ new Map();
4589
4678
  const publicNamespaces = /* @__PURE__ */ new Set();
4590
4679
  const branchRngs = /* @__PURE__ */ new Map();
4680
+ const namespaceClocks = /* @__PURE__ */ new Map();
4681
+ const namespaceForStorage = /* @__PURE__ */ new Map();
4682
+ let isolatedNamespaces = false;
4683
+ const namespaceClock = (name = DEFAULT_NAMESPACE) => {
4684
+ if (!isolatedNamespaces || name === DEFAULT_NAMESPACE)
4685
+ return clock;
4686
+ let found = namespaceClocks.get(name);
4687
+ if (!found) {
4688
+ found = createClock();
4689
+ namespaceClocks.set(name, found);
4690
+ }
4691
+ return found;
4692
+ };
4693
+ const fleetPoints = /* @__PURE__ */ new WeakMap();
4591
4694
  const timelines = /* @__PURE__ */ new Map();
4592
4695
  const branchStorage = /* @__PURE__ */ new Map();
4593
4696
  const captured = /* @__PURE__ */ new Map();
@@ -4601,17 +4704,21 @@ var createRuntime = (options) => {
4601
4704
  if (!NAMESPACE_PATTERN.test(key) || !NAMESPACE_PATTERN.test(publicNamespace)) {
4602
4705
  throw new RangeError(`namespace must match ${NAMESPACE_PATTERN}: ${JSON.stringify(publicNamespace)}`);
4603
4706
  }
4707
+ const createdContextRng = isolatedRng ?? (isolatedNamespaces && key !== DEFAULT_NAMESPACE ? createRng(options.seed ?? 0) : rng);
4604
4708
  const created = options.create({
4605
4709
  namespace: storageNamespace(key),
4606
4710
  publicNamespace,
4607
4711
  sqlite,
4608
- clock,
4609
- rng: isolatedRng ?? rng
4712
+ clock: namespaceClock(publicNamespace),
4713
+ rng: createdContextRng
4610
4714
  });
4611
4715
  instances.set(key, created);
4716
+ namespaceForStorage.set(key, publicNamespace);
4612
4717
  publicNamespaces.add(publicNamespace);
4613
4718
  if (isolatedRng)
4614
4719
  branchRngs.set(key, isolatedRng);
4720
+ else if (isolatedNamespaces && key !== DEFAULT_NAMESPACE)
4721
+ branchRngs.set(key, createdContextRng);
4615
4722
  return created;
4616
4723
  };
4617
4724
  const instance = (name = DEFAULT_NAMESPACE) => instanceFor(name);
@@ -4629,7 +4736,7 @@ var createRuntime = (options) => {
4629
4736
  captured.set(storage, snapshot2);
4630
4737
  return Object.freeze({
4631
4738
  snapshot: snapshot2,
4632
- clock: Object.freeze(clock.state()),
4739
+ clock: Object.freeze(namespaceClock(namespaceForStorage.get(storage) ?? storage).state()),
4633
4740
  rngState: (branchRngs.get(storage) ?? rng).state()
4634
4741
  });
4635
4742
  };
@@ -4639,7 +4746,7 @@ var createRuntime = (options) => {
4639
4746
  return found;
4640
4747
  instance(name);
4641
4748
  found = new Timeline({
4642
- now: clock.now,
4749
+ now: namespaceClock(name).now,
4643
4750
  ...options.maxCheckpoints !== void 0 ? { maxCheckpoints: options.maxCheckpoints } : {}
4644
4751
  });
4645
4752
  found.commit(capture(name));
@@ -4666,12 +4773,13 @@ var createRuntime = (options) => {
4666
4773
  const point = history2.checkout("main", at);
4667
4774
  restoreNamespace(sqlite, storageNamespace(namespace), point.value.snapshot);
4668
4775
  captured.set(namespace, point.value.snapshot);
4669
- rng.setState(point.value.rngState);
4670
- clock.set(point.value.clock.now);
4776
+ (branchRngs.get(namespace) ?? rng).setState(point.value.rngState);
4777
+ const selectedClock = namespaceClock(namespace);
4778
+ selectedClock.set(point.value.clock.now);
4671
4779
  if (point.value.clock.frozen)
4672
- clock.freeze();
4780
+ selectedClock.freeze();
4673
4781
  else
4674
- clock.unfreeze();
4782
+ selectedClock.unfreeze();
4675
4783
  }
4676
4784
  return namespace;
4677
4785
  }
@@ -4729,11 +4837,12 @@ var createRuntime = (options) => {
4729
4837
  const storage = ensureBranch(namespace, branchName);
4730
4838
  restoreNamespace(sqlite, storageNamespace(storage), point.value.snapshot);
4731
4839
  captured.set(storage, point.value.snapshot);
4732
- clock.set(point.value.clock.now);
4840
+ const selectedClock = namespaceClock(namespace);
4841
+ selectedClock.set(point.value.clock.now);
4733
4842
  if (point.value.clock.frozen)
4734
- clock.freeze();
4843
+ selectedClock.freeze();
4735
4844
  else
4736
- clock.unfreeze();
4845
+ selectedClock.unfreeze();
4737
4846
  (branchRngs.get(storage) ?? rng).setState(point.value.rngState);
4738
4847
  };
4739
4848
  const reset = async (name = DEFAULT_NAMESPACE) => {
@@ -4741,10 +4850,21 @@ var createRuntime = (options) => {
4741
4850
  options.webhooks?.clear();
4742
4851
  for (const each of instances.values())
4743
4852
  await each.reset();
4853
+ for (const each of branchRngs.values())
4854
+ each.reset();
4744
4855
  timelines.clear();
4856
+ for (const storage of branchStorage.values()) {
4857
+ instances.delete(storage);
4858
+ branchRngs.delete(storage);
4859
+ namespaceForStorage.delete(storage);
4860
+ }
4745
4861
  branchStorage.clear();
4746
- branchRngs.clear();
4747
4862
  captured.clear();
4863
+ faults.clear();
4864
+ clock.reset();
4865
+ rng.reset();
4866
+ for (const each of namespaceClocks.values())
4867
+ each.reset();
4748
4868
  return;
4749
4869
  }
4750
4870
  options.webhooks?.clear(name);
@@ -4762,11 +4882,18 @@ var createRuntime = (options) => {
4762
4882
  else
4763
4883
  clearNamespace(sqlite, storageNamespace(storage));
4764
4884
  branchStorage.delete(mapping);
4885
+ instances.delete(storage);
4886
+ namespaceForStorage.delete(storage);
4765
4887
  branchRngs.delete(storage);
4766
4888
  captured.delete(storage);
4767
4889
  }
4768
4890
  timelines.delete(name);
4769
4891
  captured.delete(name);
4892
+ faults.restore(name, { rules: [], rngState: createRng(options.seed ?? 0).state() });
4893
+ namespaceClock(name).reset();
4894
+ (branchRngs.get(name) ?? rng).reset();
4895
+ journal.clear(name);
4896
+ metrics.reset(name);
4770
4897
  };
4771
4898
  const snapshot = (name = DEFAULT_NAMESPACE) => {
4772
4899
  return checkpoint(name, "main").value.snapshot;
@@ -4804,6 +4931,53 @@ var createRuntime = (options) => {
4804
4931
  rng,
4805
4932
  credentials,
4806
4933
  webhooks: options.webhooks,
4934
+ isolateNamespaces() {
4935
+ if (publicNamespaces.size > 1)
4936
+ throw new Error("enable namespace isolation before creating named namespaces");
4937
+ isolatedNamespaces = true;
4938
+ faults.isolateNamespaces();
4939
+ },
4940
+ namespaceClock,
4941
+ namespaceOf(request) {
4942
+ const url = new URL(request.url);
4943
+ const prefixed = PATH_PREFIX.exec(url.pathname);
4944
+ const selected = request.headers.get(NAMESPACE_HEADER) ?? (prefixed ? decodeURIComponent(prefixed[1]) : void 0);
4945
+ const path = prefixed ? prefixed[2] ?? "/" : url.pathname;
4946
+ if (path === "/__admin" || path.startsWith("/__admin/"))
4947
+ return url.searchParams.get("namespace") ?? selected ?? DEFAULT_NAMESPACE;
4948
+ if (selected !== void 0)
4949
+ return selected;
4950
+ const credential = options.credential?.(request);
4951
+ return (credential !== void 0 ? credentials.get(credential) : void 0) ?? DEFAULT_NAMESPACE;
4952
+ },
4953
+ fleetSnapshot(namespace) {
4954
+ instance(namespace);
4955
+ const handle = {};
4956
+ fleetPoints.set(handle, {
4957
+ namespace,
4958
+ state: capture(namespace),
4959
+ faults: faults.snapshot(namespace),
4960
+ webhooks: options.webhooks?.snapshot(namespace)
4961
+ });
4962
+ return handle;
4963
+ },
4964
+ fleetRestore(handle, namespace) {
4965
+ const point = typeof handle === "object" && handle !== null ? fleetPoints.get(handle) : void 0;
4966
+ if (!point || point.namespace !== namespace)
4967
+ throw new Error("invalid fleet checkpoint");
4968
+ restoreNamespace(sqlite, storageNamespace(namespace), point.state.snapshot);
4969
+ captured.set(namespace, point.state.snapshot);
4970
+ const selectedClock = namespaceClock(namespace);
4971
+ selectedClock.freeze();
4972
+ selectedClock.set(point.state.clock.now);
4973
+ if (!point.state.clock.frozen)
4974
+ selectedClock.unfreeze();
4975
+ (branchRngs.get(namespace) ?? rng).setState(point.state.rngState);
4976
+ faults.restore(namespace, point.faults);
4977
+ if (point.webhooks !== void 0)
4978
+ options.webhooks?.restore(namespace, point.webhooks);
4979
+ timelines.delete(namespace);
4980
+ },
4807
4981
  applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
4808
4982
  const preset = options.presets?.[name];
4809
4983
  if (!preset)
@@ -4979,6 +5153,7 @@ var createRuntime = (options) => {
4979
5153
  startedAt: wallNow(),
4980
5154
  wallNow,
4981
5155
  clock,
5156
+ namespaceClock,
4982
5157
  faults,
4983
5158
  metrics,
4984
5159
  journal,
@@ -6160,6 +6335,8 @@ var GoogleCalendarAPI = class {
6160
6335
  };
6161
6336
 
6162
6337
  export {
6338
+ createClock,
6339
+ parseDuration,
6163
6340
  document2 as document,
6164
6341
  operationIds,
6165
6342
  supportedOperationIds,
@@ -6175,4 +6352,4 @@ export {
6175
6352
  emailFromCode,
6176
6353
  GoogleCalendarAPI
6177
6354
  };
6178
- //# sourceMappingURL=chunk-PTK5HWQN.js.map
6355
+ //# sourceMappingURL=chunk-KZPEFGHP.js.map