@fedify/testing 2.4.0-pr.936.41 → 2.5.0-dev.2271

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mod.cjs CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  let _fedify_fedify_sig = require("@fedify/fedify/sig");
4
4
  let _fedify_vocab = require("@fedify/vocab");
5
+ let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
5
6
  let _fedify_fedify_federation = require("@fedify/fedify/federation");
6
7
  let es_toolkit = require("es-toolkit");
7
8
  let node_assert_strict = require("node:assert/strict");
@@ -38,7 +39,7 @@ const noopMeterProvider$1 = { getMeter: () => ({
38
39
  removeBatchObservableCallback: () => void 0
39
40
  }) };
40
41
  function createContext(values) {
41
- const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, meterProvider, tracerProvider, clone, getNodeInfoUri, getActorUri, getObjectUri, getCollectionUri, getOutboxUri, getMediaUploaderUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject, traverseCollection, lookupNodeInfo, lookupWebFinger, sendActivity, routeActivity, enqueueTask, enqueueTaskMany } = values;
42
+ const { federation, url = new URL("http://example.com/"), canonicalOrigin, data, documentLoader, contextLoader, verifyPortableObject, meterProvider, tracerProvider, clone, getNodeInfoUri, getActorUri, getPortableActorUri, getObjectUri, getPortableObjectUri, getPortableInboxUri, getPortableOutboxUri, getPortableFollowingUri, getPortableFollowersUri, getPortableLikedUri, getPortableFeaturedUri, getPortableFeaturedTagsUri, getPortableCollectionUri, getCollectionUri, getOutboxUri, getMediaUploaderUri, getInboxUri, getFollowingUri, getFollowersUri, getLikedUri, getFeaturedUri, getFeaturedTagsUri, parseUri, getActorKeyPairs, getDocumentLoader, lookupObject, traverseCollection, lookupNodeInfo, lookupWebFinger, sendActivity, routeActivity, enqueueTask, enqueueTaskMany } = values;
42
43
  function throwRouterError() {
43
44
  throw new _fedify_fedify_federation.RouterError("Not implemented");
44
45
  }
@@ -51,6 +52,7 @@ function createContext(values) {
51
52
  hostname: url.hostname,
52
53
  documentLoader: documentLoader ?? mockDocumentLoader,
53
54
  contextLoader: contextLoader ?? mockDocumentLoader,
55
+ ...verifyPortableObject == null ? {} : { verifyPortableObject },
54
56
  meterProvider: meterProvider ?? noopMeterProvider$1,
55
57
  tracerProvider: tracerProvider ?? noopTracerProvider$1,
56
58
  clone: clone ?? ((data) => createContext({
@@ -59,7 +61,17 @@ function createContext(values) {
59
61
  })),
60
62
  getNodeInfoUri: getNodeInfoUri ?? throwRouterError,
61
63
  getActorUri: getActorUri ?? throwRouterError,
64
+ getPortableActorUri: getPortableActorUri ?? throwRouterError,
62
65
  getObjectUri: getObjectUri ?? throwRouterError,
66
+ getPortableObjectUri: getPortableObjectUri ?? throwRouterError,
67
+ getPortableInboxUri: getPortableInboxUri ?? throwRouterError,
68
+ getPortableOutboxUri: getPortableOutboxUri ?? throwRouterError,
69
+ getPortableFollowingUri: getPortableFollowingUri ?? throwRouterError,
70
+ getPortableFollowersUri: getPortableFollowersUri ?? throwRouterError,
71
+ getPortableLikedUri: getPortableLikedUri ?? throwRouterError,
72
+ getPortableFeaturedUri: getPortableFeaturedUri ?? throwRouterError,
73
+ getPortableFeaturedTagsUri: getPortableFeaturedTagsUri ?? throwRouterError,
74
+ getPortableCollectionUri: getPortableCollectionUri ?? throwRouterError,
63
75
  getCollectionUri: getCollectionUri ?? throwRouterError,
64
76
  getOutboxUri: getOutboxUri ?? throwRouterError,
65
77
  getMediaUploaderUri: getMediaUploaderUri ?? throwRouterError,
@@ -79,13 +91,15 @@ function createContext(values) {
79
91
  lookupObject: lookupObject ?? ((uri, options = {}) => {
80
92
  return (0, _fedify_vocab.lookupObject)(uri, {
81
93
  documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
82
- contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
94
+ contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader,
95
+ verifyPortableObject: options.verifyPortableObject ?? verifyPortableObject
83
96
  });
84
97
  }),
85
98
  traverseCollection: traverseCollection ?? ((collection, options = {}) => {
86
99
  return (0, _fedify_vocab.traverseCollection)(collection, {
87
100
  documentLoader: options.documentLoader ?? documentLoader ?? mockDocumentLoader,
88
- contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader
101
+ contextLoader: options.contextLoader ?? contextLoader ?? mockDocumentLoader,
102
+ verifyPortableObject: options.verifyPortableObject ?? verifyPortableObject
89
103
  });
90
104
  }),
91
105
  lookupNodeInfo: lookupNodeInfo ?? ((_params) => {
@@ -124,15 +138,59 @@ function createRequestContext(args) {
124
138
  })),
125
139
  request: args.request ?? new Request(args.url),
126
140
  url: args.url,
141
+ portableRequest: args.portableRequest,
127
142
  getActor: args.getActor ?? (() => Promise.resolve(null)),
128
143
  getObject: args.getObject ?? (() => Promise.resolve(null)),
129
144
  getSignedKey: args.getSignedKey ?? (() => Promise.resolve(null)),
130
145
  getSignedKeyOwner: args.getSignedKeyOwner ?? (() => Promise.resolve(null)),
146
+ isSignedByAudience: args.isSignedByAudience ?? ((object, options) => isSignedByAudience(() => args.getSignedKeyOwner == null ? Promise.resolve(null) : args.getSignedKeyOwner(options ?? {}), object, options?.isMember)),
131
147
  sendActivity: args.sendActivity ?? ((_params) => {
132
148
  throw new Error("Not implemented");
133
149
  })
134
150
  };
135
151
  }
152
+ const PUBLIC_IDS = /* @__PURE__ */ new Set([
153
+ _fedify_vocab.PUBLIC_COLLECTION.href,
154
+ "as:Public",
155
+ "Public"
156
+ ]);
157
+ function getPortableIri(id) {
158
+ try {
159
+ if (id.protocol === "ap:" || id.protocol === "ap+ef61:") return (0, _fedify_vocab_runtime.formatIri)(id);
160
+ const portable = (0, _fedify_vocab_runtime.fromCompatibleEf61Id)(id);
161
+ return portable == null ? null : (0, _fedify_vocab_runtime.formatIri)(portable);
162
+ } catch (error) {
163
+ if (error instanceof TypeError) return null;
164
+ throw error;
165
+ }
166
+ }
167
+ function isSameActorId(a, b) {
168
+ const portableA = getPortableIri(a);
169
+ const portableB = getPortableIri(b);
170
+ if (portableA != null || portableB != null) return portableA != null && portableB != null && (0, _fedify_vocab_runtime.arePortableUrisEqual)(portableA, portableB);
171
+ return a.href === b.href;
172
+ }
173
+ /**
174
+ * The default implementation of `RequestContext.isSignedByAudience()` for
175
+ * testing, which checks the audience against `getSignedKeyOwner()`.
176
+ */
177
+ async function isSignedByAudience(getSignedKeyOwner, object, isMember) {
178
+ const addressees = [
179
+ ...object.toIds,
180
+ ...object.ccIds,
181
+ ...object.btoIds,
182
+ ...object.bccIds,
183
+ ...object.audienceIds
184
+ ];
185
+ if (addressees.some((id) => PUBLIC_IDS.has(id.href))) return true;
186
+ const actor = await getSignedKeyOwner();
187
+ const actorId = actor?.id;
188
+ if (actor == null || actorId == null) return false;
189
+ if (addressees.some((id) => isSameActorId(id, actorId))) return true;
190
+ if (isMember == null) return false;
191
+ for (const addressee of addressees) if (await isMember(addressee, actor)) return true;
192
+ return false;
193
+ }
136
194
  /**
137
195
  * Creates an InboxContext for testing purposes.
138
196
  * Not exported - used internally only. Public API is in mock.ts
@@ -226,6 +284,63 @@ function expandUriTemplate(template, values) {
226
284
  }
227
285
  });
228
286
  }
287
+ /**
288
+ * Builds an FEP-ef61 portable ID from a DID authority and a path, validating
289
+ * the authority the same way as `@fedify/fedify` does.
290
+ */
291
+ function buildPortableUri(authority, path) {
292
+ if (typeof authority !== "string" || !/^did:[a-z0-9]+:[^/?#]+$/i.test(authority)) throw new TypeError("The authority of a portable ID must be a DID without a path, query, or fragment.");
293
+ const did = (0, _fedify_vocab_runtime.getFe34Origin)(authority);
294
+ if (!isBase58BtcDidKey(did)) throw new TypeError("The did:key authority of a portable ID must be encoded in base58-btc, i.e., start with z.");
295
+ return (0, _fedify_vocab_runtime.parseIri)(`ap+ef61://${did}${path}`);
296
+ }
297
+ /**
298
+ * Checks that a normalized DID, if it is a `did:key` DID, is encoded in
299
+ * base58-btc, as `@fedify/fedify` requires of portable IDs.
300
+ */
301
+ function isBase58BtcDidKey(did) {
302
+ return !did.startsWith("did:key:") || /^z[1-9A-HJ-NP-Za-km-z]+$/.test(did.slice(8));
303
+ }
304
+ /**
305
+ * Recognizes an FEP-ef61 portable ID, i.e., an `ap:` or `ap+ef61:` URI, or
306
+ * a compatible identifier on any gateway, validating it the same way as
307
+ * `Context.parseUri()` of `@fedify/fedify` does.
308
+ * @returns The DID and the path of the portable ID, `null` if it is
309
+ * malformed, or `undefined` if the URI is not a portable ID.
310
+ */
311
+ function parseMockPortableId(uri) {
312
+ const portable = uri.protocol === "ap:" || uri.protocol === "ap+ef61:";
313
+ if (!portable && !((uri.protocol === "http:" || uri.protocol === "https:") && /^\/\.well-known\/apgateway\/did(?::|%3A)/i.test(uri.pathname))) return;
314
+ if (uri.username !== "" || uri.password !== "") return null;
315
+ try {
316
+ const id = portable ? (0, _fedify_vocab_runtime.parseIri)(uri) : (0, _fedify_vocab_runtime.fromCompatibleEf61Id)(uri.origin + uri.pathname);
317
+ if (id == null) return null;
318
+ (0, _fedify_vocab_runtime.canonicalizePortableUri)((0, _fedify_vocab_runtime.formatIri)(id));
319
+ const authority = (0, _fedify_vocab_runtime.getFe34Origin)(id);
320
+ if (!isBase58BtcDidKey(authority)) return null;
321
+ return {
322
+ authority,
323
+ path: id.pathname
324
+ };
325
+ } catch (error) {
326
+ if (error instanceof TypeError) return null;
327
+ throw error;
328
+ }
329
+ }
330
+ /**
331
+ * Parses a path in the way {@link MockContext.parseUri} does, i.e., only
332
+ * `/users/{identifier}` and its subpaths are recognized, as actors.
333
+ */
334
+ function parseMockPath(path) {
335
+ if (path.startsWith("/users/")) {
336
+ const parts = path.split("/");
337
+ if (parts.length >= 3) return {
338
+ type: "actor",
339
+ identifier: parts[2]
340
+ };
341
+ }
342
+ return null;
343
+ }
229
344
  function validateOutboxListenerPath(path, dispatcherPath) {
230
345
  if (!path.startsWith("/")) throw new TypeError("Path must start with a slash.");
231
346
  if (dispatcherPath != null && dispatcherPath !== path) throw new TypeError("Outbox listener path and outbox dispatcher path must match.");
@@ -302,6 +417,7 @@ var MockFederation = class {
302
417
  outboxListenerErrorHandler;
303
418
  mediaUploaderCallback;
304
419
  mediaUploaderAuthorizePredicate;
420
+ hashlinkMediaDispatcher;
305
421
  followingDispatcher;
306
422
  followersDispatcher;
307
423
  likedDispatcher;
@@ -339,93 +455,103 @@ var MockFederation = class {
339
455
  setActorDispatcher(path, dispatcher) {
340
456
  this.actorDispatchers.set(path, dispatcher);
341
457
  this.actorPath = path;
342
- return {
458
+ const setters = {
343
459
  setKeyPairsDispatcher: (keyPairsDispatcher) => {
344
460
  this.actorKeyPairsDispatcher = keyPairsDispatcher;
345
- return this;
461
+ return setters;
346
462
  },
347
- mapHandle: () => this,
348
- mapAlias: () => this,
349
- authorize: () => this
463
+ mapHandle: () => setters,
464
+ mapAlias: () => setters,
465
+ mapPortableActorId: () => setters,
466
+ authorize: () => setters
350
467
  };
468
+ return setters;
351
469
  }
352
470
  setObjectDispatcher(cls, path, dispatcher) {
353
471
  this.objectDispatchers.set(path, dispatcher);
354
472
  this.objectPaths.set(cls.typeId.href, path);
355
- return { authorize: () => this };
473
+ const setters = { authorize: () => setters };
474
+ return setters;
356
475
  }
357
476
  setInboxDispatcher(_path, dispatcher) {
358
477
  this.inboxDispatcher = dispatcher;
359
- return {
360
- setCounter: () => this,
361
- setFirstCursor: () => this,
362
- setLastCursor: () => this,
363
- authorize: () => this
478
+ const setters = {
479
+ setCounter: () => setters,
480
+ setFirstCursor: () => setters,
481
+ setLastCursor: () => setters,
482
+ authorize: () => setters
364
483
  };
484
+ return setters;
365
485
  }
366
486
  setOutboxDispatcher(path, dispatcher) {
367
487
  validateOutboxListenerPath(path, this.outboxListenersInitialized ? this.outboxPath : void 0);
368
488
  this.outboxDispatcher = dispatcher;
369
489
  this.outboxPath = path;
370
- return {
371
- setCounter: () => this,
372
- setFirstCursor: () => this,
373
- setLastCursor: () => this,
490
+ const setters = {
491
+ setCounter: () => setters,
492
+ setFirstCursor: () => setters,
493
+ setLastCursor: () => setters,
374
494
  authorize: (predicate) => {
375
495
  this.outboxDispatcherAuthorizePredicate = predicate;
376
- return this;
496
+ return setters;
377
497
  }
378
498
  };
499
+ return setters;
379
500
  }
380
501
  setFollowingDispatcher(path, dispatcher) {
381
502
  this.followingDispatcher = dispatcher;
382
503
  this.followingPath = path;
383
- return {
384
- setCounter: () => this,
385
- setFirstCursor: () => this,
386
- setLastCursor: () => this,
387
- authorize: () => this
504
+ const setters = {
505
+ setCounter: () => setters,
506
+ setFirstCursor: () => setters,
507
+ setLastCursor: () => setters,
508
+ authorize: () => setters
388
509
  };
510
+ return setters;
389
511
  }
390
512
  setFollowersDispatcher(path, dispatcher) {
391
513
  this.followersDispatcher = dispatcher;
392
514
  this.followersPath = path;
393
- return {
394
- setCounter: () => this,
395
- setFirstCursor: () => this,
396
- setLastCursor: () => this,
397
- authorize: () => this
515
+ const setters = {
516
+ setCounter: () => setters,
517
+ setFirstCursor: () => setters,
518
+ setLastCursor: () => setters,
519
+ authorize: () => setters
398
520
  };
521
+ return setters;
399
522
  }
400
523
  setLikedDispatcher(path, dispatcher) {
401
524
  this.likedDispatcher = dispatcher;
402
525
  this.likedPath = path;
403
- return {
404
- setCounter: () => this,
405
- setFirstCursor: () => this,
406
- setLastCursor: () => this,
407
- authorize: () => this
526
+ const setters = {
527
+ setCounter: () => setters,
528
+ setFirstCursor: () => setters,
529
+ setLastCursor: () => setters,
530
+ authorize: () => setters
408
531
  };
532
+ return setters;
409
533
  }
410
534
  setFeaturedDispatcher(path, dispatcher) {
411
535
  this.featuredDispatcher = dispatcher;
412
536
  this.featuredPath = path;
413
- return {
414
- setCounter: () => this,
415
- setFirstCursor: () => this,
416
- setLastCursor: () => this,
417
- authorize: () => this
537
+ const setters = {
538
+ setCounter: () => setters,
539
+ setFirstCursor: () => setters,
540
+ setLastCursor: () => setters,
541
+ authorize: () => setters
418
542
  };
543
+ return setters;
419
544
  }
420
545
  setFeaturedTagsDispatcher(path, dispatcher) {
421
546
  this.featuredTagsDispatcher = dispatcher;
422
547
  this.featuredTagsPath = path;
423
- return {
424
- setCounter: () => this,
425
- setFirstCursor: () => this,
426
- setLastCursor: () => this,
427
- authorize: () => this
548
+ const setters = {
549
+ setCounter: () => setters,
550
+ setFirstCursor: () => setters,
551
+ setLastCursor: () => setters,
552
+ authorize: () => setters
428
553
  };
554
+ return setters;
429
555
  }
430
556
  setInboxListeners(inboxPath, sharedInboxPath) {
431
557
  this.inboxPath = inboxPath;
@@ -444,6 +570,9 @@ var MockFederation = class {
444
570
  onUnverifiedActivity() {
445
571
  return this;
446
572
  },
573
+ onRequestFinished() {
574
+ return this;
575
+ },
447
576
  setSharedKeyDispatcher() {
448
577
  return this;
449
578
  },
@@ -484,6 +613,10 @@ var MockFederation = class {
484
613
  return this;
485
614
  } };
486
615
  }
616
+ setHashlinkMediaDispatcher(dispatcher) {
617
+ if (this.hashlinkMediaDispatcher != null) throw new TypeError("Hashlink media dispatcher already set.");
618
+ this.hashlinkMediaDispatcher = dispatcher;
619
+ }
487
620
  setOutboxPermanentFailureHandler(_handler) {}
488
621
  async startQueue(contextData, options) {
489
622
  this.contextData = contextData;
@@ -499,7 +632,7 @@ var MockFederation = class {
499
632
  async processQueuedTask(contextData, _message) {
500
633
  this.contextData = contextData;
501
634
  }
502
- createContext(baseUrlOrRequest, contextData) {
635
+ createContext(baseUrlOrRequest, contextData, options = {}) {
503
636
  const mockFederation = this;
504
637
  const request = baseUrlOrRequest instanceof Request ? baseUrlOrRequest : null;
505
638
  return new MockContext({
@@ -508,10 +641,63 @@ var MockFederation = class {
508
641
  data: contextData,
509
642
  federation: mockFederation,
510
643
  meterProvider: this.options.meterProvider,
511
- tracerProvider: this.options.tracerProvider
644
+ tracerProvider: this.options.tracerProvider,
645
+ documentLoader: this.options.documentLoader,
646
+ contextLoader: this.options.contextLoader,
647
+ verifyPortableObject: this.options.verifyPortableObject,
648
+ portableRequest: options.portableRequest
512
649
  });
513
650
  }
514
651
  async fetch(request, options) {
652
+ if (this.hashlinkMediaDispatcher != null) {
653
+ const url = new URL(request.url);
654
+ if ((url.protocol === "http:" || url.protocol === "https:") && url.pathname.startsWith("/.well-known/apgateway/")) {
655
+ const encoded = url.pathname.slice(23);
656
+ if (/^hl(?::|%3A)/i.test(encoded)) {
657
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response("Method not allowed.", {
658
+ status: 405,
659
+ headers: {
660
+ Allow: "GET, HEAD",
661
+ "Content-Type": "text/plain; charset=utf-8"
662
+ }
663
+ });
664
+ let media;
665
+ try {
666
+ let hashlink;
667
+ try {
668
+ hashlink = decodeURIComponent(encoded);
669
+ } catch (error) {
670
+ throw new TypeError("Invalid percent-encoding in the hashlink.", { cause: error });
671
+ }
672
+ const { digestMultibase } = (0, _fedify_vocab_runtime.parseHashlink)(hashlink);
673
+ const { digest } = (0, _fedify_vocab_runtime.parseDigestMultibase)(digestMultibase);
674
+ media = globalThis.Object.freeze({
675
+ hashlink: `hl:${digestMultibase}`,
676
+ digestMultibase,
677
+ algorithm: "sha2-256",
678
+ digest,
679
+ multihash: (0, _fedify_vocab_runtime.decodeMultibase)(digestMultibase)
680
+ });
681
+ } catch (error) {
682
+ if (!(error instanceof TypeError)) throw error;
683
+ return new Response(request.method === "HEAD" ? null : "Malformed hashlink.", {
684
+ status: 400,
685
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
686
+ });
687
+ }
688
+ const context = this.createContext(request, options.contextData);
689
+ const response = await this.hashlinkMediaDispatcher(context, media);
690
+ if (response == null) return options.onNotFound == null ? new Response("Not Found", { status: 404 }) : await options.onNotFound(request);
691
+ if (request.method !== "HEAD" || response.body == null) return response;
692
+ response.body.cancel().catch(() => {});
693
+ return new Response(null, {
694
+ status: response.status,
695
+ statusText: response.statusText,
696
+ headers: response.headers
697
+ });
698
+ }
699
+ }
700
+ }
515
701
  if (options.onNotFound) return options.onNotFound(request);
516
702
  return new Response("Not Found", { status: 404 });
517
703
  }
@@ -619,20 +805,24 @@ var MockFederation = class {
619
805
  this.sentActivities = [];
620
806
  }
621
807
  setCollectionDispatcher(_name, _itemType, _path, _dispatcher) {
622
- return {
623
- setCounter: () => this,
624
- setFirstCursor: () => this,
625
- setLastCursor: () => this,
626
- authorize: () => this
808
+ const setters = {
809
+ setCounter: () => setters,
810
+ setFirstCursor: () => setters,
811
+ setLastCursor: () => setters,
812
+ authorize: () => setters,
813
+ mapPortableOwner: () => setters
627
814
  };
815
+ return setters;
628
816
  }
629
817
  setOrderedCollectionDispatcher(_name, _itemType, _path, _dispatcher) {
630
- return {
631
- setCounter: () => this,
632
- setFirstCursor: () => this,
633
- setLastCursor: () => this,
634
- authorize: () => this
818
+ const setters = {
819
+ setCounter: () => setters,
820
+ setFirstCursor: () => setters,
821
+ setLastCursor: () => setters,
822
+ authorize: () => setters,
823
+ mapPortableOwner: () => setters
635
824
  };
825
+ return setters;
636
826
  }
637
827
  };
638
828
  /**
@@ -730,6 +920,10 @@ var MockContext = class MockContext {
730
920
  tracerProvider;
731
921
  request;
732
922
  url;
923
+ portableRequest;
924
+ verifyPortableObject;
925
+ hasDocumentLoader;
926
+ hasContextLoader;
733
927
  sentActivities = [];
734
928
  constructor(options) {
735
929
  const url = options.url ?? new URL("https://example.com");
@@ -739,6 +933,10 @@ var MockContext = class MockContext {
739
933
  this.hostname = url.hostname;
740
934
  this.url = url;
741
935
  this.request = options.request ?? new Request(url);
936
+ this.portableRequest = options.portableRequest;
937
+ this.verifyPortableObject = options.verifyPortableObject;
938
+ this.hasDocumentLoader = options.hasDocumentLoader ?? options.documentLoader != null;
939
+ this.hasContextLoader = options.hasContextLoader ?? options.contextLoader != null;
742
940
  this.data = options.data;
743
941
  this.federation = options.federation;
744
942
  this.documentLoader = options.documentLoader ?? (async (url) => ({
@@ -757,12 +955,16 @@ var MockContext = class MockContext {
757
955
  }
758
956
  return null;
759
957
  }
760
- async getObject(cls, values) {
958
+ async getObject(cls, values, options) {
761
959
  if (this.federation instanceof MockFederation) {
762
960
  const path = this.federation.objectPaths.get(cls.typeId.href);
763
961
  if (path) {
764
962
  const dispatcher = this.federation.objectDispatchers.get(path);
765
- if (dispatcher) return await dispatcher(this, values);
963
+ if (dispatcher) {
964
+ const object = await dispatcher(this, values);
965
+ if (object instanceof _fedify_vocab.Tombstone && !(object instanceof cls) && options?.tombstone !== "passthrough") return null;
966
+ return object;
967
+ }
766
968
  }
767
969
  }
768
970
  return null;
@@ -773,6 +975,9 @@ var MockContext = class MockContext {
773
975
  getSignedKeyOwner() {
774
976
  return Promise.resolve(null);
775
977
  }
978
+ isSignedByAudience(object, options) {
979
+ return isSignedByAudience(() => this.getSignedKeyOwner(), object, options?.isMember);
980
+ }
776
981
  #resolveTaskDefinition(task) {
777
982
  if (!(this.federation instanceof MockFederation)) throw new TypeError("No task definitions are available.");
778
983
  const def = this.federation.taskDefinitions.get(task.name);
@@ -796,10 +1001,15 @@ var MockContext = class MockContext {
796
1001
  clone(data) {
797
1002
  return new MockContext({
798
1003
  url: this.url,
1004
+ request: this.request,
799
1005
  data,
800
1006
  federation: this.federation,
801
1007
  documentLoader: this.documentLoader,
802
1008
  contextLoader: this.contextLoader,
1009
+ verifyPortableObject: this.verifyPortableObject,
1010
+ portableRequest: this.portableRequest,
1011
+ hasDocumentLoader: this.hasDocumentLoader,
1012
+ hasContextLoader: this.hasContextLoader,
803
1013
  meterProvider: this.meterProvider,
804
1014
  tracerProvider: this.tracerProvider
805
1015
  });
@@ -826,6 +1036,46 @@ var MockContext = class MockContext {
826
1036
  const path = globalThis.Object.entries(values).map(([key, value]) => `${key}/${value}`).join("/");
827
1037
  return new URL(`/objects/${cls.name.toLowerCase()}/${path}`, this.origin);
828
1038
  }
1039
+ getPortableActorUri(identifier, authority) {
1040
+ const { pathname } = this.getActorUri(identifier);
1041
+ return buildPortableUri(authority, pathname);
1042
+ }
1043
+ getPortableObjectUri(cls, values, authority) {
1044
+ const { pathname } = this.getObjectUri(cls, values);
1045
+ return buildPortableUri(authority, pathname);
1046
+ }
1047
+ getPortableInboxUri(identifier, authority) {
1048
+ const { pathname } = this.getInboxUri(identifier);
1049
+ return buildPortableUri(authority, pathname);
1050
+ }
1051
+ getPortableOutboxUri(identifier, authority) {
1052
+ const { pathname } = this.getOutboxUri(identifier);
1053
+ return buildPortableUri(authority, pathname);
1054
+ }
1055
+ getPortableFollowingUri(identifier, authority) {
1056
+ const { pathname } = this.getFollowingUri(identifier);
1057
+ return buildPortableUri(authority, pathname);
1058
+ }
1059
+ getPortableFollowersUri(identifier, authority) {
1060
+ const { pathname } = this.getFollowersUri(identifier);
1061
+ return buildPortableUri(authority, pathname);
1062
+ }
1063
+ getPortableLikedUri(identifier, authority) {
1064
+ const { pathname } = this.getLikedUri(identifier);
1065
+ return buildPortableUri(authority, pathname);
1066
+ }
1067
+ getPortableFeaturedUri(identifier, authority) {
1068
+ const { pathname } = this.getFeaturedUri(identifier);
1069
+ return buildPortableUri(authority, pathname);
1070
+ }
1071
+ getPortableFeaturedTagsUri(identifier, authority) {
1072
+ const { pathname } = this.getFeaturedTagsUri(identifier);
1073
+ return buildPortableUri(authority, pathname);
1074
+ }
1075
+ getPortableCollectionUri(name, values, authority) {
1076
+ const { pathname } = this.getCollectionUri(name, values);
1077
+ return buildPortableUri(authority, pathname);
1078
+ }
829
1079
  getOutboxUri(identifier) {
830
1080
  if (this.federation instanceof MockFederation && this.federation.outboxPath) {
831
1081
  const path = expandUriTemplate(this.federation.outboxPath, { identifier });
@@ -890,15 +1140,16 @@ var MockContext = class MockContext {
890
1140
  const path = globalThis.Object.entries(values).map(([key, value]) => `${key}/${value}`).join("/");
891
1141
  return new URL(`/collections/${String(_name)}/${path}`, this.origin);
892
1142
  }
893
- parseUri(uri) {
894
- if (uri.pathname.startsWith("/users/")) {
895
- const parts = uri.pathname.split("/");
896
- if (parts.length >= 3) return {
897
- type: "actor",
898
- identifier: parts[2]
899
- };
900
- }
901
- return null;
1143
+ parseUri(uri, options = {}) {
1144
+ if (uri == null) return null;
1145
+ const portable = parseMockPortableId(uri);
1146
+ if (portable === void 0) return parseMockPath(uri.pathname);
1147
+ if (portable === null || !options.portable) return null;
1148
+ const result = parseMockPath(portable.path);
1149
+ return result == null ? null : {
1150
+ ...result,
1151
+ authority: portable.authority
1152
+ };
902
1153
  }
903
1154
  async getActorKeyPairs(identifier) {
904
1155
  if (this.federation instanceof MockFederation && this.federation.actorKeyPairsDispatcher) {
@@ -924,8 +1175,21 @@ var MockContext = class MockContext {
924
1175
  if ("keyId" in params) return this.documentLoader;
925
1176
  return Promise.resolve(this.documentLoader);
926
1177
  }
927
- lookupObject(_uri, _options) {
928
- return Promise.resolve(null);
1178
+ lookupObject(uri, options = {}) {
1179
+ if (!this.hasDocumentLoader && options.documentLoader == null || (options.verifyPortableObject ?? this.verifyPortableObject) == null) return Promise.resolve(null);
1180
+ const value = typeof uri === "string" ? uri : (0, _fedify_vocab_runtime.formatIri)(uri);
1181
+ if (!/^ap(?:\+ef61)?:\/\//i.test(value)) try {
1182
+ if ((0, _fedify_vocab_runtime.fromCompatibleEf61Id)(value) == null) return Promise.resolve(null);
1183
+ } catch (error) {
1184
+ if (error instanceof TypeError) return Promise.resolve(null);
1185
+ throw error;
1186
+ }
1187
+ return (0, _fedify_vocab.lookupObject)(uri, {
1188
+ ...options,
1189
+ documentLoader: options.documentLoader ?? this.documentLoader,
1190
+ contextLoader: options.contextLoader ?? (this.hasContextLoader ? this.contextLoader : options.documentLoader ?? this.contextLoader),
1191
+ verifyPortableObject: options.verifyPortableObject ?? this.verifyPortableObject
1192
+ });
929
1193
  }
930
1194
  traverseCollection(_collection, _options) {
931
1195
  return { async *[Symbol.asyncIterator]() {} };
@@ -1129,7 +1393,7 @@ function parseInt0(s) {
1129
1393
  function padNumber(digits, num) {
1130
1394
  return String(num).padStart(digits, "0");
1131
1395
  }
1132
- const padNumber2 = /* @__PURE__ */ bindArgs(padNumber, 2);
1396
+ const padNumber2 = /*@__PURE__*/ bindArgs(padNumber, 2);
1133
1397
  function compareNumbers(a, b) {
1134
1398
  return Math.sign(a - b);
1135
1399
  }
@@ -1197,7 +1461,7 @@ const unitNameMap = {
1197
1461
  month: 8,
1198
1462
  year: 9
1199
1463
  };
1200
- const unitNamesAsc = /* @__PURE__ */ Object.keys(unitNameMap);
1464
+ const unitNamesAsc = /*@__PURE__*/ Object.keys(unitNameMap);
1201
1465
  const nanoInMicro = nanoInMicro$1;
1202
1466
  const nanoInMilli = nanoInMilli$1;
1203
1467
  const nanoInSec = nanoInSec$1;
@@ -1213,19 +1477,19 @@ const unitNanoMap = [
1213
1477
  nanoInHour,
1214
1478
  nanoInUtcDay
1215
1479
  ];
1216
- const bigNanoInMicro = /* @__PURE__ */ BigInt(nanoInMicro);
1217
- const bigNanoInMilli = /* @__PURE__ */ BigInt(nanoInMilli);
1218
- const bigNanoInSec = /* @__PURE__ */ BigInt(nanoInSec);
1219
- const bigNanoInMinute = /* @__PURE__ */ BigInt(nanoInMinute);
1220
- const bigNanoInHour = /* @__PURE__ */ BigInt(nanoInHour);
1221
- const bigNanoInUtcDay = /* @__PURE__ */ BigInt(nanoInUtcDay);
1480
+ const bigNanoInMicro = /*@__PURE__*/ BigInt(nanoInMicro);
1481
+ const bigNanoInMilli = /*@__PURE__*/ BigInt(nanoInMilli);
1482
+ const bigNanoInSec = /*@__PURE__*/ BigInt(nanoInSec);
1483
+ const bigNanoInMinute = /*@__PURE__*/ BigInt(nanoInMinute);
1484
+ const bigNanoInHour = /*@__PURE__*/ BigInt(nanoInHour);
1485
+ const bigNanoInUtcDay = /*@__PURE__*/ BigInt(nanoInUtcDay);
1222
1486
  function divideBigNanoToExactNumber(bigNano, divisorNano) {
1223
1487
  const days = Number(bigNano / bigNanoInUtcDay);
1224
1488
  const timeNano = Number(bigNano % bigNanoInUtcDay);
1225
1489
  return days * (nanoInUtcDay / divisorNano) + (Math.trunc(timeNano / divisorNano) + timeNano % divisorNano / divisorNano);
1226
1490
  }
1227
- const timeFieldNamesAsc = /* @__PURE__ */ unitNamesAsc.slice(0, 6);
1228
- const timeGetters = /* @__PURE__ */ createPropGetters(timeFieldNamesAsc);
1491
+ const timeFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.slice(0, 6);
1492
+ const timeGetters = /*@__PURE__*/ createPropGetters(timeFieldNamesAsc);
1229
1493
  const yearFieldNamesAsc = ["year"];
1230
1494
  const dayFieldNamesAsc = ["day"];
1231
1495
  const calendarDateFieldNamesAsc = [
@@ -1247,24 +1511,24 @@ const monthDayFieldNames = [
1247
1511
  "month",
1248
1512
  "monthCode"
1249
1513
  ];
1250
- const timeFieldNamesAlpha = /* @__PURE__ */ sortStrings(timeFieldNamesAsc);
1251
- const yearFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearFieldNamesAsc);
1252
- const yearMonthFieldNamesAlpha = /* @__PURE__ */ sortStrings(monthFieldNames, yearFieldNamesAsc);
1253
- const yearMonthFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearMonthFieldNamesAlpha);
1254
- const yearMonthCodeFieldNamesAlpha = /* @__PURE__ */ sortStrings(["monthCode"], yearFieldNamesAsc);
1255
- const yearMonthCodeFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
1256
- const monthCodeDayFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, ["monthCode"]);
1257
- const dateFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, yearMonthFieldNamesAlpha);
1258
- const dateFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthFieldNamesAlpha);
1259
- const dateTimeFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc);
1260
- const dateTimeFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc);
1261
- const dateTimeAndOffsetFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames);
1262
- const dateTimeAndOffsetFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames);
1263
- const dateTimeAndZoneFieldNamesAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
1264
- const dateTimeAndZoneFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
1265
- const yearMonthCodeDayFieldNamesAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, yearMonthCodeFieldNamesAlpha);
1266
- const yearMonthCodeDayFieldNamesWithEraAlpha = /* @__PURE__ */ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
1267
- const timeFieldDefaults = /* @__PURE__ */ zipPropsConst(timeFieldNamesAsc, 0);
1514
+ const timeFieldNamesAlpha = /*@__PURE__*/ sortStrings(timeFieldNamesAsc);
1515
+ const yearFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearFieldNamesAsc);
1516
+ const yearMonthFieldNamesAlpha = /*@__PURE__*/ sortStrings(monthFieldNames, yearFieldNamesAsc);
1517
+ const yearMonthFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthFieldNamesAlpha);
1518
+ const yearMonthCodeFieldNamesAlpha = /*@__PURE__*/ sortStrings(["monthCode"], yearFieldNamesAsc);
1519
+ const yearMonthCodeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
1520
+ const monthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, ["monthCode"]);
1521
+ const dateFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthFieldNamesAlpha);
1522
+ const dateFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthFieldNamesAlpha);
1523
+ const dateTimeFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc);
1524
+ const dateTimeFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc);
1525
+ const dateTimeAndOffsetFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames);
1526
+ const dateTimeAndOffsetFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames);
1527
+ const dateTimeAndZoneFieldNamesAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
1528
+ const dateTimeAndZoneFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dateFieldNamesWithEraAlpha, timeFieldNamesAsc, offsetFieldNames, timeZoneFieldNames);
1529
+ const yearMonthCodeDayFieldNamesAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, yearMonthCodeFieldNamesAlpha);
1530
+ const yearMonthCodeDayFieldNamesWithEraAlpha = /*@__PURE__*/ sortStrings(dayFieldNamesAsc, eraYearFieldNames, yearMonthCodeFieldNamesAlpha);
1531
+ const timeFieldDefaults = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, 0);
1268
1532
  function validateTimeFields(timeFields) {
1269
1533
  return constrainTimeFields(timeFields, 1), timeFields;
1270
1534
  }
@@ -1493,20 +1757,20 @@ function computeCalendarWeekOfYear(calendar, isoDate) {
1493
1757
  function computeCalendarYearOfWeek(calendar, isoDate) {
1494
1758
  return calendar === void 0 ? computeIsoWeekFields(isoDate).yearOfWeek : void 0;
1495
1759
  }
1496
- const durationFieldNamesAsc = /* @__PURE__ */ unitNamesAsc.map((unitName) => unitName + "s");
1497
- const durationGetters = /* @__PURE__ */ createPropGetters(durationFieldNamesAsc);
1498
- const durationFieldNamesAlpha = /* @__PURE__ */ sortStrings(durationFieldNamesAsc);
1499
- const durationTimeFieldNamesAsc = /* @__PURE__ */ durationFieldNamesAsc.slice(0, 6);
1500
- const durationDateFieldNamesAsc = /* @__PURE__ */ durationFieldNamesAsc.slice(6);
1501
- const durationCalendarFieldNamesAsc = /* @__PURE__ */ durationDateFieldNamesAsc.slice(1);
1502
- const durationFieldIndexes = /* @__PURE__ */ durationFieldNamesAsc.reduce((indexes, fieldName, i) => (indexes[fieldName] = i, indexes), {});
1503
- const durationFieldDefaults = /* @__PURE__ */ zipPropsConst(durationFieldNamesAsc, 0);
1504
- const durationTimeFieldDefaults = /* @__PURE__ */ zipPropsConst(durationTimeFieldNamesAsc, 0);
1505
- const clearDurationFields = /* @__PURE__ */ bindArgs(zeroOutProps, durationFieldNamesAsc);
1760
+ const durationFieldNamesAsc = /*@__PURE__*/ unitNamesAsc.map((unitName) => unitName + "s");
1761
+ const durationGetters = /*@__PURE__*/ createPropGetters(durationFieldNamesAsc);
1762
+ const durationFieldNamesAlpha = /*@__PURE__*/ sortStrings(durationFieldNamesAsc);
1763
+ const durationTimeFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(0, 6);
1764
+ const durationDateFieldNamesAsc = /*@__PURE__*/ durationFieldNamesAsc.slice(6);
1765
+ const durationCalendarFieldNamesAsc = /*@__PURE__*/ durationDateFieldNamesAsc.slice(1);
1766
+ const durationFieldIndexes = /*@__PURE__*/ durationFieldNamesAsc.reduce((indexes, fieldName, i) => (indexes[fieldName] = i, indexes), {});
1767
+ const durationFieldDefaults = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, 0);
1768
+ const durationTimeFieldDefaults = /*@__PURE__*/ zipPropsConst(durationTimeFieldNamesAsc, 0);
1769
+ const clearDurationFields = /*@__PURE__*/ bindArgs(zeroOutProps, durationFieldNamesAsc);
1506
1770
  function requirePropDefined(optionName, optionVal) {
1507
1771
  return optionVal ?? throwRangeError(missingField(optionName)), optionVal;
1508
1772
  }
1509
- const requireString = /* @__PURE__ */ bindArgs(requireType, "string");
1773
+ const requireString = /*@__PURE__*/ bindArgs(requireType, "string");
1510
1774
  function requireType(typeName, arg, entityName = typeName) {
1511
1775
  return typeof arg !== typeName && throwTypeError(invalidEntity(entityName, arg)), arg;
1512
1776
  }
@@ -1618,17 +1882,17 @@ function coerceChoiceOption(optionName, enumNameMap, options, defaultChoice = 0)
1618
1882
  const enumNum = enumNameMap[enumStr];
1619
1883
  return void 0 === enumNum && throwRangeError(invalidChoice(optionName, enumStr, enumNameMap)), enumNum;
1620
1884
  }
1621
- const coerceSmallestUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, smallestUnitStr);
1622
- const coerceLargestUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, "largestUnit");
1623
- const coerceTotalUnit = /* @__PURE__ */ bindArgs(coerceUnitOption, "unit");
1624
- const coerceOverflow = /* @__PURE__ */ bindArgs(coerceChoiceOption, "overflow", overflowMap);
1625
- const coerceEpochDisambig = /* @__PURE__ */ bindArgs(coerceChoiceOption, "disambiguation", epochDisambigMap);
1626
- const coerceOffsetDisambig = /* @__PURE__ */ bindArgs(coerceChoiceOption, "offset", offsetDisambigMap);
1627
- const coerceCalendarDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "calendarName", calendarDisplayMap);
1628
- const coerceTimeZoneDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "timeZoneName", timeZoneDisplayMap);
1629
- const coerceOffsetDisplay = /* @__PURE__ */ bindArgs(coerceChoiceOption, "offset", offsetDisplayMap);
1630
- const coerceRoundingMode = /* @__PURE__ */ bindArgs(coerceChoiceOption, "roundingMode", roundingModeMap);
1631
- const coerceDirection = /* @__PURE__ */ bindArgs(coerceChoiceOption, "direction", directionMap);
1885
+ const coerceSmallestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, smallestUnitStr);
1886
+ const coerceLargestUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "largestUnit");
1887
+ const coerceTotalUnit = /*@__PURE__*/ bindArgs(coerceUnitOption, "unit");
1888
+ const coerceOverflow = /*@__PURE__*/ bindArgs(coerceChoiceOption, "overflow", overflowMap);
1889
+ const coerceEpochDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "disambiguation", epochDisambigMap);
1890
+ const coerceOffsetDisambig = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisambigMap);
1891
+ const coerceCalendarDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "calendarName", calendarDisplayMap);
1892
+ const coerceTimeZoneDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "timeZoneName", timeZoneDisplayMap);
1893
+ const coerceOffsetDisplay = /*@__PURE__*/ bindArgs(coerceChoiceOption, "offset", offsetDisplayMap);
1894
+ const coerceRoundingMode = /*@__PURE__*/ bindArgs(coerceChoiceOption, "roundingMode", roundingModeMap);
1895
+ const coerceDirection = /*@__PURE__*/ bindArgs(coerceChoiceOption, "direction", directionMap);
1632
1896
  function validateRoundingInc(roundingInc, smallestUnit, allowManyLargeUnits, solarMode) {
1633
1897
  const upUnitNano = solarMode ? nanoInUtcDay : unitNanoMap[smallestUnit + 1];
1634
1898
  if (upUnitNano) {
@@ -1682,8 +1946,8 @@ function refineZonedFieldOptions(options, defaultOffsetDisambig = 0) {
1682
1946
  epochDisambig
1683
1947
  ];
1684
1948
  }
1685
- const epochNanoMax = /* @__PURE__ */ BigInt(1e8) * bigNanoInUtcDay;
1686
- const epochNanoMin = /* @__PURE__ */ BigInt(-1e8) * bigNanoInUtcDay;
1949
+ const epochNanoMax = /*@__PURE__*/ BigInt(1e8) * bigNanoInUtcDay;
1950
+ const epochNanoMin = /*@__PURE__*/ BigInt(-1e8) * bigNanoInUtcDay;
1687
1951
  const plainDateEpochNanoMin = epochNanoMin - bigNanoInUtcDay;
1688
1952
  const isoYearMonthIndexMin = -3261848;
1689
1953
  function checkIsoYearMonthInBounds(isoDate) {
@@ -1954,11 +2218,12 @@ function nudgeRelativeDuration(sign, durationFields, endEpochNano, _largestUnit,
1954
2218
  }
1955
2219
  function getTimeZoneTransitionEpochNanoseconds(slots, options) {
1956
2220
  return slots.timeZone.U(slots.epochNanoseconds, ((options) => {
1957
- const res = coerceDirection(normalizeOptionsOrString(options, "direction"), 0);
2221
+ const normalizedOptions = normalizeOptionsOrString(options, "direction");
2222
+ const res = coerceDirection(normalizedOptions, 0);
1958
2223
  return res || throwRangeError(invalidEntity("direction", res)), res;
1959
2224
  })(options));
1960
2225
  }
1961
- const zonedEpochSlotsToIso = /* @__PURE__ */ memoize(_zonedEpochSlotsToIso, WeakMap);
2226
+ const zonedEpochSlotsToIso = /*@__PURE__*/ memoize(_zonedEpochSlotsToIso, WeakMap);
1962
2227
  function _zonedEpochSlotsToIso(slots) {
1963
2228
  const { epochNanoseconds, timeZone } = slots;
1964
2229
  const offsetNanoseconds = timeZone.C(epochNanoseconds);
@@ -2524,7 +2789,7 @@ function resolveTimeFields(fields, overflow) {
2524
2789
  ...fields
2525
2790
  }), overflow);
2526
2791
  }
2527
- const offsetRegExp = /* @__PURE__ */ createRegExp("([+-])(\\d{2})(?::?(\\d{2})(?::?(\\d{2})(?:[.,](\\d{1,9}))?)?)?");
2792
+ const offsetRegExp = /*@__PURE__*/ createRegExp("([+-])(\\d{2})(?::?(\\d{2})(?::?(\\d{2})(?:[.,](\\d{1,9}))?)?)?");
2528
2793
  function parseOffsetNano(s) {
2529
2794
  const offsetNano = parseOffsetNanoMaybe(s);
2530
2795
  return void 0 === offsetNano && throwRangeError(failedParse(s)), offsetNano;
@@ -2559,13 +2824,13 @@ const dateFieldRefiners = {
2559
2824
  },
2560
2825
  day: toPositiveIntegerWithTruncation
2561
2826
  };
2562
- const timeFieldRefiners = /* @__PURE__ */ zipPropsConst(timeFieldNamesAsc, toIntegerWithTrunc);
2563
- const durationFieldRefiners = /* @__PURE__ */ zipPropsConst(durationFieldNamesAsc, toStrictInteger);
2827
+ const timeFieldRefiners = /*@__PURE__*/ zipPropsConst(timeFieldNamesAsc, toIntegerWithTrunc);
2828
+ const durationFieldRefiners = /*@__PURE__*/ zipPropsConst(durationFieldNamesAsc, toStrictInteger);
2564
2829
  const offsetFieldRefiners = { offset(offsetString) {
2565
2830
  return parseOffsetNano(toStringViaPrimitive(offsetString));
2566
2831
  } };
2567
- const dateTimeFieldRefiners = /* @__PURE__ */ Object.assign({}, dateFieldRefiners, timeFieldRefiners);
2568
- const zonedDateTimeFieldRefiners = /* @__PURE__ */ Object.assign({}, dateTimeFieldRefiners, offsetFieldRefiners);
2832
+ const dateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateFieldRefiners, timeFieldRefiners);
2833
+ const zonedDateTimeFieldRefiners = /*@__PURE__*/ Object.assign({}, dateTimeFieldRefiners, offsetFieldRefiners);
2569
2834
  function readAndRefineBagFields(bag, validFieldNames, fieldRefiners, requiredFieldNames, disallowEmpty = !requiredFieldNames) {
2570
2835
  const res = {};
2571
2836
  let anyMatching = 0;
@@ -2668,7 +2933,9 @@ const timeZonePeriodDaysByName = {
2668
2933
  const minPossibleTransitionSec = -388152e4;
2669
2934
  function refineTimeDisplayTuple(options, maxSmallestUnit = 4) {
2670
2935
  const subsecDigits = coerceFractionalSecondDigits(options);
2671
- return [coerceRoundingMode(options, 4), ...resolveSmallestUnitAndSubsecDigits(validateUnitRange(smallestUnitStr, coerceSmallestUnit(options), 0, maxSmallestUnit), subsecDigits)];
2936
+ const roundingMode = coerceRoundingMode(options, 4);
2937
+ const smallestUnit = coerceSmallestUnit(options);
2938
+ return [roundingMode, ...resolveSmallestUnitAndSubsecDigits(validateUnitRange(smallestUnitStr, smallestUnit, 0, maxSmallestUnit), subsecDigits)];
2672
2939
  }
2673
2940
  function refineDateDisplayOptions(options) {
2674
2941
  return coerceCalendarDisplay(normalizeOptions(options));
@@ -2843,15 +3110,17 @@ function resolveTimeZoneRecord(rawId) {
2843
3110
  kind: "fixed",
2844
3111
  ...offsetRecord
2845
3112
  };
2846
- return queryNamedTimeZoneRecord("UTC" === upperRawId ? "UTC" : ((rawId) => (badCharactersRegExp.test(rawId) && throwRangeError(invalidTimeZone(rawId)), icuRegExp.test(rawId) && throwRangeError("Forbidden ICU TimeZone"), rawId.toLowerCase().split("/").map((part, partI) => (part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part) ? part.toUpperCase() : part.replace(/baja|dumont|[a-z]+/g, (a, i) => a.length <= 2 && !partI || "in" === a || "chat" === a ? a.toUpperCase() : a.length > 2 || !i ? capitalize(a).replace(/island|noronha|murdo|rivadavia|urville/, capitalize) : a)).join("/")))(rawId));
3113
+ const normId = "UTC" === upperRawId ? "UTC" : ((rawId) => (badCharactersRegExp.test(rawId) && throwRangeError(invalidTimeZone(rawId)), icuRegExp.test(rawId) && throwRangeError("Forbidden ICU TimeZone"), rawId.toLowerCase().split("/").map((part, partI) => (part.length <= 3 || /\d/.test(part)) && !/etc|yap/.test(part) ? part.toUpperCase() : part.replace(/baja|dumont|[a-z]+/g, (a, i) => a.length <= 2 && !partI || "in" === a || "chat" === a ? a.toUpperCase() : a.length > 2 || !i ? capitalize(a).replace(/island|noronha|murdo|rivadavia|urville/, capitalize) : a)).join("/")))(rawId);
3114
+ return queryNamedTimeZoneRecord(normId);
2847
3115
  }
2848
- const queryNamedTimeZoneRecord = /* @__PURE__ */ memoize((normId) => {
3116
+ const queryNamedTimeZoneRecord = /*@__PURE__*/ memoize((normId) => {
2849
3117
  if ("UTC" === normId) return {
2850
3118
  kind: "utc",
2851
3119
  id: normId,
2852
3120
  o: normId
2853
3121
  };
2854
- const format = queryTimeZoneIntlFormat(normId.toUpperCase());
3122
+ const upperNormId = normId.toUpperCase();
3123
+ const format = queryTimeZoneIntlFormat(upperNormId);
2855
3124
  return {
2856
3125
  kind: "named",
2857
3126
  id: normId,
@@ -2859,7 +3128,7 @@ const queryNamedTimeZoneRecord = /* @__PURE__ */ memoize((normId) => {
2859
3128
  o: format.resolvedOptions().timeZone
2860
3129
  };
2861
3130
  });
2862
- const queryTimeZoneIntlFormat = /* @__PURE__ */ memoize((upperNormId) => new RawDateTimeFormat("en-u-hc-h23", {
3131
+ const queryTimeZoneIntlFormat = /*@__PURE__*/ memoize((upperNormId) => new RawDateTimeFormat("en-u-hc-h23", {
2863
3132
  calendar: "iso8601",
2864
3133
  timeZone: upperNormId,
2865
3134
  era: "short",
@@ -2874,7 +3143,7 @@ function queryTimeZone(rawTimeZoneId) {
2874
3143
  const record = resolveTimeZoneRecord(rawTimeZoneId);
2875
3144
  return queryTimeZoneRecord(record.id, record);
2876
3145
  }
2877
- const queryTimeZoneRecord = /* @__PURE__ */ memoize((normTimeZoneId, record) => "named" === record.kind ? new IntlTimeZone(normTimeZoneId, record.o, record.format) : new FixedTimeZone(normTimeZoneId, record.o, "fixed" === record.kind ? record._ : 0));
3146
+ const queryTimeZoneRecord = /*@__PURE__*/ memoize((normTimeZoneId, record) => "named" === record.kind ? new IntlTimeZone(normTimeZoneId, record.o, record.format) : new FixedTimeZone(normTimeZoneId, record.o, "fixed" === record.kind ? record._ : 0));
2878
3147
  var FixedTimeZone = class {
2879
3148
  constructor(id, compareKey, offsetNano) {
2880
3149
  this.id = id, this.o = compareKey, this._ = offsetNano;
@@ -2956,7 +3225,8 @@ var IntlTimeZone = class {
2956
3225
  return void 0 !== intlParts.era && "bce" === normalizeEraName(intlParts.era) ? 1 - year : year;
2957
3226
  })(intlParts), parseInt(intlParts.month), parseInt(intlParts.day)) + 3600 * parseInt(intlParts.hour) + 60 * parseInt(intlParts.minute) + parseInt(intlParts.second) - epochSec;
2958
3227
  })(format), ((timeZoneId) => {
2959
- return timeZonePeriodDaysByName[timeZoneId.split("/").pop()] || 60;
3228
+ const timeZoneName = timeZoneId.split("/").pop();
3229
+ return timeZonePeriodDaysByName[timeZoneName] || 60;
2960
3230
  })(id));
2961
3231
  }
2962
3232
  C(epochNano) {
@@ -3180,12 +3450,12 @@ function offsetRegExpStr(separatorIndex) {
3180
3450
  return "([+-])" + timeRegExpStr(separatorIndex);
3181
3451
  }
3182
3452
  const dateTimeRegExpStr = "(?:(?:([+-])(\\d{6}))|(\\d{4}))(-?)(\\d{2})\\4(\\d{2})(?:[T ]" + timeRegExpStr(8) + "(Z|" + offsetRegExpStr(15) + ")?)?";
3183
- const yearMonthRegExp = /* @__PURE__ */ createRegExp("(?:(?:([+-])(\\d{6}))|(\\d{4}))-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
3184
- const monthDayRegExp = /* @__PURE__ */ createRegExp("(?:--)?(\\d{2})-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
3185
- const dateTimeRegExp = /* @__PURE__ */ createRegExp(dateTimeRegExpStr + "((?:\\[(!?)([^\\]]*)\\]){0,9})");
3186
- const timeRegExp = /* @__PURE__ */ createRegExp("T?" + timeRegExpStr(2) + `(${offsetRegExpStr(9)})?((?:\\[(!?)([^\\]]*)\\]){0,9})`);
3187
- const annotationRegExp = /* @__PURE__ */ new RegExp("\\[(!?)([^\\]]*)\\]", "g");
3188
- const durationRegExp = /* @__PURE__ */ createRegExp("([+-])?P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(?:T(?:(\\d+)(?:[.,](\\d{1,9}))?H)?(?:(\\d+)(?:[.,](\\d{1,9}))?M)?(?:(\\d+)(?:[.,](\\d{1,9}))?S)?)?");
3453
+ const yearMonthRegExp = /*@__PURE__*/ createRegExp("(?:(?:([+-])(\\d{6}))|(\\d{4}))-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
3454
+ const monthDayRegExp = /*@__PURE__*/ createRegExp("(?:--)?(\\d{2})-?(\\d{2})((?:\\[(!?)([^\\]]*)\\]){0,9})");
3455
+ const dateTimeRegExp = /*@__PURE__*/ createRegExp(dateTimeRegExpStr + "((?:\\[(!?)([^\\]]*)\\]){0,9})");
3456
+ const timeRegExp = /*@__PURE__*/ createRegExp("T?" + timeRegExpStr(2) + `(${offsetRegExpStr(9)})?((?:\\[(!?)([^\\]]*)\\]){0,9})`);
3457
+ const annotationRegExp = /*@__PURE__*/ new RegExp("\\[(!?)([^\\]]*)\\]", "g");
3458
+ const durationRegExp = /*@__PURE__*/ createRegExp("([+-])?P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(?:T(?:(\\d+)(?:[.,](\\d{1,9}))?H)?(?:(\\d+)(?:[.,](\\d{1,9}))?M)?(?:(\\d+)(?:[.,](\\d{1,9}))?S)?)?");
3189
3459
  function parseDateTimeLike(s) {
3190
3460
  const parts = dateTimeRegExp.exec(s);
3191
3461
  return parts ? ((parts) => {
@@ -3463,8 +3733,8 @@ const timeDefaultShapeFields = {
3463
3733
  minute: "numeric",
3464
3734
  second: "numeric"
3465
3735
  };
3466
- const dateTimeDefaultShapeFields = /* @__PURE__ */ Object.assign({}, dateDefaultShapeFields, timeDefaultShapeFields);
3467
- const zonedDateTimeDefaultShapeFields = /* @__PURE__ */ Object.assign({}, dateTimeDefaultShapeFields, { timeZoneName: "short" });
3736
+ const dateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateDefaultShapeFields, timeDefaultShapeFields);
3737
+ const zonedDateTimeDefaultShapeFields = /*@__PURE__*/ Object.assign({}, dateTimeDefaultShapeFields, { timeZoneName: "short" });
3468
3738
  const yearMonthDefaultShapeFields = {
3469
3739
  year: "numeric",
3470
3740
  month: "numeric"
@@ -3488,15 +3758,15 @@ const timeShapeFieldNames = [
3488
3758
  "fractionalSecondDigits",
3489
3759
  "timeStyle"
3490
3760
  ];
3491
- const dateTimeShapeFieldNames = /* @__PURE__ */ dateShapeFieldNames.concat(timeShapeFieldNames);
3492
- const yearMonthIgnoredFieldNames = /* @__PURE__ */ ["weekday", "day"].concat(timeShapeFieldNames);
3493
- const monthDayIgnoredFieldNames = /* @__PURE__ */ ["weekday", "year"].concat(timeShapeFieldNames);
3494
- const transformInstantOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], [], dateTimeDefaultShapeFields);
3495
- const transformZonedOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], [], zonedDateTimeDefaultShapeFields);
3496
- const transformDateTimeOptions = /* @__PURE__ */ createOptionsTransformer(dateTimeShapeFieldNames, [], ["timeZoneName"], dateTimeDefaultShapeFields);
3497
- const transformDateOptions = /* @__PURE__ */ createOptionsTransformer(dateShapeFieldNames, timeShapeFieldNames, ["timeZoneName"], dateDefaultShapeFields);
3498
- const transformTimeOptions = /* @__PURE__ */ createOptionsTransformer(timeShapeFieldNames, dateShapeFieldNames, ["timeZoneName", "era"], timeDefaultShapeFields);
3499
- const transformYearMonthOptions = /* @__PURE__ */ createOptionsTransformer([
3761
+ const dateTimeShapeFieldNames = /*@__PURE__*/ dateShapeFieldNames.concat(timeShapeFieldNames);
3762
+ const yearMonthIgnoredFieldNames = /*@__PURE__*/ ["weekday", "day"].concat(timeShapeFieldNames);
3763
+ const monthDayIgnoredFieldNames = /*@__PURE__*/ ["weekday", "year"].concat(timeShapeFieldNames);
3764
+ const transformInstantOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], dateTimeDefaultShapeFields);
3765
+ const transformZonedOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], [], zonedDateTimeDefaultShapeFields);
3766
+ const transformDateTimeOptions = /*@__PURE__*/ createOptionsTransformer(dateTimeShapeFieldNames, [], ["timeZoneName"], dateTimeDefaultShapeFields);
3767
+ const transformDateOptions = /*@__PURE__*/ createOptionsTransformer(dateShapeFieldNames, timeShapeFieldNames, ["timeZoneName"], dateDefaultShapeFields);
3768
+ const transformTimeOptions = /*@__PURE__*/ createOptionsTransformer(timeShapeFieldNames, dateShapeFieldNames, ["timeZoneName", "era"], timeDefaultShapeFields);
3769
+ const transformYearMonthOptions = /*@__PURE__*/ createOptionsTransformer([
3500
3770
  "year",
3501
3771
  "month",
3502
3772
  "dateStyle"
@@ -3518,7 +3788,7 @@ const transformYearMonthOptions = /* @__PURE__ */ createOptionsTransformer([
3518
3788
  month: "numeric"
3519
3789
  }
3520
3790
  });
3521
- const transformMonthDayOptions = /* @__PURE__ */ createOptionsTransformer([
3791
+ const transformMonthDayOptions = /*@__PURE__*/ createOptionsTransformer([
3522
3792
  "month",
3523
3793
  "day",
3524
3794
  "dateStyle"
@@ -3692,8 +3962,8 @@ function resolveBasicCalendarId(rawCalendarId) {
3692
3962
  function resolveBasicCalendarArg(rawCalendarId = isoCalendarId) {
3693
3963
  return resolveBasicCalendarId(rawCalendarId);
3694
3964
  }
3695
- const zonedDateTimeSlotsMap = /* @__PURE__ */ new WeakMap();
3696
- const ZonedDateTime = /* @__PURE__ */ defineTemporalClass(ZonedDateTimeBranding, class {
3965
+ const zonedDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
3966
+ const ZonedDateTime = /*@__PURE__*/ defineTemporalClass(ZonedDateTimeBranding, class {
3697
3967
  constructor(epochNanoseconds, timeZoneId, calendar = void 0) {
3698
3968
  const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
3699
3969
  const timeZone = queryTimeZone(refineTimeZoneId(timeZoneId));
@@ -3841,8 +4111,8 @@ function refineTimeZoneArg(arg) {
3841
4111
  }
3842
4112
  return ((arg) => resolveTimeZoneId(parseTimeZoneId(requireString(arg))))(arg);
3843
4113
  }
3844
- const instantSlotsMap = /* @__PURE__ */ new WeakMap();
3845
- const Instant = /* @__PURE__ */ defineTemporalClass(InstantBranding, class {
4114
+ const instantSlotsMap = /*@__PURE__*/ new WeakMap();
4115
+ const Instant = /*@__PURE__*/ defineTemporalClass(InstantBranding, class {
3846
4116
  constructor(epochNanoseconds) {
3847
4117
  const epochNano = checkEpochNanoInBounds(toBigInt(epochNanoseconds));
3848
4118
  initInstant(this, createEpochNanoSlots(epochNano));
@@ -3927,8 +4197,8 @@ const { toTemporalInstant: toTemporalInstant$1 } = { toTemporalInstant() {
3927
4197
  function initInstant(instance, slots) {
3928
4198
  return instantSlotsMap.set(instance, slots), attachDebugString(instance), instance;
3929
4199
  }
3930
- const plainMonthDaySlotsMap = /* @__PURE__ */ new WeakMap();
3931
- const PlainMonthDay = /* @__PURE__ */ defineTemporalClass(PlainMonthDayBranding, class {
4200
+ const plainMonthDaySlotsMap = /*@__PURE__*/ new WeakMap();
4201
+ const PlainMonthDay = /*@__PURE__*/ defineTemporalClass(PlainMonthDayBranding, class {
3932
4202
  constructor(isoMonth, isoDay, calendar = void 0, referenceIsoYear) {
3933
4203
  const isoMonthInt = toIntegerWithTrunc(isoMonth);
3934
4204
  const isoDayInt = toIntegerWithTrunc(isoDay);
@@ -3992,8 +4262,8 @@ function toPlainMonthDaySlots(arg, options) {
3992
4262
  function initPlainMonthDay(instance, slots) {
3993
4263
  return plainMonthDaySlotsMap.set(instance, slots), attachDebugString(instance), instance;
3994
4264
  }
3995
- const plainYearMonthSlotsMap = /* @__PURE__ */ new WeakMap();
3996
- const PlainYearMonth = /* @__PURE__ */ defineTemporalClass(PlainYearMonthBranding, class {
4265
+ const plainYearMonthSlotsMap = /*@__PURE__*/ new WeakMap();
4266
+ const PlainYearMonth = /*@__PURE__*/ defineTemporalClass(PlainYearMonthBranding, class {
3997
4267
  constructor(isoYear, isoMonth, calendar = void 0, referenceIsoDay) {
3998
4268
  const isoYearInt = toIntegerWithTrunc(isoYear);
3999
4269
  const isoMonthInt = toIntegerWithTrunc(isoMonth);
@@ -4085,8 +4355,8 @@ function getTemporalBrandingAndSlots(obj) {
4085
4355
  function validateBag(bag) {
4086
4356
  return (getTemporalBrandingAndSlots(bag) || void 0 !== bag.calendar || void 0 !== bag.timeZone) && throwTypeError("Invalid bag"), bag;
4087
4357
  }
4088
- const plainTimeSlotsMap = /* @__PURE__ */ new WeakMap();
4089
- const PlainTime = /* @__PURE__ */ defineTemporalClass(PlainTimeBranding, class {
4358
+ const plainTimeSlotsMap = /*@__PURE__*/ new WeakMap();
4359
+ const PlainTime = /*@__PURE__*/ defineTemporalClass(PlainTimeBranding, class {
4090
4360
  constructor(hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0) {
4091
4361
  const fields = validateTimeFields(mapProps(toIntegerWithTrunc, {
4092
4362
  hour,
@@ -4168,8 +4438,8 @@ function optionalToPlainTimeFields(timeArg) {
4168
4438
  function initPlainTime(instance, slots) {
4169
4439
  return plainTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
4170
4440
  }
4171
- const plainDateTimeSlotsMap = /* @__PURE__ */ new WeakMap();
4172
- const PlainDateTime = /* @__PURE__ */ defineTemporalClass(PlainDateTimeBranding, class {
4441
+ const plainDateTimeSlotsMap = /*@__PURE__*/ new WeakMap();
4442
+ const PlainDateTime = /*@__PURE__*/ defineTemporalClass(PlainDateTimeBranding, class {
4173
4443
  constructor(isoYear, isoMonth, isoDay, hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0, calendar = void 0) {
4174
4444
  const fields = checkIsoDateTimeInBounds(validateIsoDateTimeFields(mapProps(toIntegerWithTrunc, {
4175
4445
  year: isoYear,
@@ -4280,8 +4550,8 @@ function toPlainDateTimeSlots(arg, options) {
4280
4550
  function initPlainDateTime(instance, slots) {
4281
4551
  return plainDateTimeSlotsMap.set(instance, slots), attachDebugString(instance), instance;
4282
4552
  }
4283
- const plainDateSlotsMap = /* @__PURE__ */ new WeakMap();
4284
- const PlainDate = /* @__PURE__ */ defineTemporalClass(PlainDateBranding, class {
4553
+ const plainDateSlotsMap = /*@__PURE__*/ new WeakMap();
4554
+ const PlainDate = /*@__PURE__*/ defineTemporalClass(PlainDateBranding, class {
4285
4555
  constructor(isoYear, isoMonth, isoDay, calendar = void 0) {
4286
4556
  const fields = checkIsoDateInBounds(validateIsoDateFields(mapProps(toIntegerWithTrunc, {
4287
4557
  year: isoYear,
@@ -4399,8 +4669,8 @@ function refineCalendarArg(arg) {
4399
4669
  }
4400
4670
  return ((arg) => resolveBasicCalendarId(parseCalendarId(requireString(arg))))(arg);
4401
4671
  }
4402
- const durationSlotsMap = /* @__PURE__ */ new WeakMap();
4403
- const Duration = /* @__PURE__ */ defineTemporalClass(DurationBranding, class {
4672
+ const durationSlotsMap = /*@__PURE__*/ new WeakMap();
4673
+ const Duration = /*@__PURE__*/ defineTemporalClass(DurationBranding, class {
4404
4674
  constructor(years = 0, months = 0, weeks = 0, days = 0, hours = 0, minutes = 0, seconds = 0, milliseconds = 0, microseconds = 0, nanoseconds = 0) {
4405
4675
  const fields = validateDurationFields(mapProps(toStrictInteger, {
4406
4676
  years,
@@ -4493,7 +4763,7 @@ function refinePublicRelativeTo(relativeTo) {
4493
4763
  function initDuration(instance, slots) {
4494
4764
  return durationSlotsMap.set(instance, slots), attachDebugString(instance), instance;
4495
4765
  }
4496
- const Now = /* @__PURE__ */ Object.defineProperties({}, {
4766
+ const Now = /*@__PURE__*/ Object.defineProperties({}, {
4497
4767
  ...createStringTagDescriptors("Temporal.Now"),
4498
4768
  ...createPropDescriptors({
4499
4769
  timeZoneId() {
@@ -4517,7 +4787,7 @@ const Now = /* @__PURE__ */ Object.defineProperties({}, {
4517
4787
  }
4518
4788
  })
4519
4789
  });
4520
- const Temporal$2 = /* @__PURE__ */ Object.defineProperties({}, {
4790
+ const Temporal$3 = /*@__PURE__*/ Object.defineProperties({}, {
4521
4791
  ...createStringTagDescriptors("Temporal"),
4522
4792
  ...createPropDescriptors({
4523
4793
  PlainYearMonth,
@@ -4533,11 +4803,153 @@ const Temporal$2 = /* @__PURE__ */ Object.defineProperties({}, {
4533
4803
  });
4534
4804
  //#endregion
4535
4805
  //#region ../../node_modules/.pnpm/temporal-polyfill@1.0.1/node_modules/temporal-polyfill/index.js
4536
- const Temporal$1 = NativeTemporal || Temporal$2;
4806
+ const Temporal$2 = NativeTemporal || Temporal$3;
4537
4807
  NativeTemporal && Date.prototype.toTemporalInstant;
4538
4808
  //#endregion
4809
+ //#region src/kv-tester.ts
4810
+ const Temporal$1 = Temporal$2;
4811
+ /**
4812
+ * Tests a {@link KvStore} implementation with a standard set of tests.
4813
+ *
4814
+ * The suite covers reads and writes, deletion, structured keys and values,
4815
+ * prefix listing, TTL expiration, and optional compare-and-swap behavior.
4816
+ * Every key is isolated below a random prefix and is deleted after the suite.
4817
+ *
4818
+ * @example
4819
+ * ```typescript ignore
4820
+ * import { test } from "@fedify/fixture";
4821
+ * import { testKvStore } from "@fedify/testing";
4822
+ * import { MyKvStore } from "./my-kv.ts";
4823
+ *
4824
+ * test("MyKvStore", () =>
4825
+ * testKvStore(
4826
+ * () => new MyKvStore(),
4827
+ * async ({ store }) => await store.close(),
4828
+ * )
4829
+ * );
4830
+ * ```
4831
+ *
4832
+ * @param getKvStore A factory that creates the store to test.
4833
+ * @param onFinally A cleanup function called after the suite completes.
4834
+ * @param options Optional configuration for the suite.
4835
+ */
4836
+ async function testKvStore(getKvStore, onFinally, options = {}) {
4837
+ const store = await getKvStore();
4838
+ const prefix = [`fedify_test_${crypto.randomUUID()}`];
4839
+ const key = (...segments) => [...prefix, ...segments];
4840
+ try {
4841
+ (0, node_assert_strict.strictEqual)(await store.get(key("missing")), void 0);
4842
+ for (const [index, value] of [
4843
+ "text",
4844
+ 42,
4845
+ true,
4846
+ null,
4847
+ [
4848
+ 1,
4849
+ "two",
4850
+ false
4851
+ ],
4852
+ { nested: { value: "test" } }
4853
+ ].entries()) {
4854
+ const valueKey = key("values", index.toString());
4855
+ await store.set(valueKey, value);
4856
+ (0, node_assert_strict.deepStrictEqual)(await store.get(valueKey), value);
4857
+ }
4858
+ const overwriteKey = key("overwrite");
4859
+ await store.set(overwriteKey, "before");
4860
+ await store.set(overwriteKey, "after");
4861
+ (0, node_assert_strict.strictEqual)(await store.get(overwriteKey), "after");
4862
+ const deleteKey = key("delete");
4863
+ await store.set(deleteKey, "value");
4864
+ await store.delete(deleteKey);
4865
+ (0, node_assert_strict.strictEqual)(await store.get(deleteKey), void 0);
4866
+ await store.delete(deleteKey);
4867
+ const isolatedKeys = [
4868
+ key("a", "b"),
4869
+ key("a,b"),
4870
+ key("a:b")
4871
+ ];
4872
+ for (const [index, isolatedKey] of isolatedKeys.entries()) await store.set(isolatedKey, index);
4873
+ for (const [index, isolatedKey] of isolatedKeys.entries()) (0, node_assert_strict.strictEqual)(await store.get(isolatedKey), index);
4874
+ if (options.testTtl ?? true) {
4875
+ const expiringKey = key("ttl", "expiring");
4876
+ await store.set(expiringKey, "expired", { ttl: Temporal$1.Duration.from({ milliseconds: 50 }) });
4877
+ await (0, es_toolkit.delay)(100);
4878
+ (0, node_assert_strict.strictEqual)(await store.get(expiringKey), void 0);
4879
+ (0, node_assert_strict.deepStrictEqual)(await collect(store.list(key("ttl"))), []);
4880
+ const refreshedKey = key("ttl", "refreshed");
4881
+ await store.set(refreshedKey, "temporary", { ttl: Temporal$1.Duration.from({ milliseconds: 50 }) });
4882
+ await store.set(refreshedKey, "persistent");
4883
+ await (0, es_toolkit.delay)(100);
4884
+ (0, node_assert_strict.strictEqual)(await store.get(refreshedKey), "persistent");
4885
+ }
4886
+ const listPrefix = key("list");
4887
+ const listEntries = [
4888
+ {
4889
+ key: listPrefix,
4890
+ value: "exact"
4891
+ },
4892
+ {
4893
+ key: key("list", "child"),
4894
+ value: "child"
4895
+ },
4896
+ {
4897
+ key: key("list", "child", "nested"),
4898
+ value: "nested"
4899
+ },
4900
+ {
4901
+ key: key("list-other"),
4902
+ value: "other"
4903
+ }
4904
+ ];
4905
+ for (const entry of listEntries) await store.set(entry.key, entry.value);
4906
+ (0, node_assert_strict.deepStrictEqual)(normalize(await collect(store.list(listPrefix))), normalize(listEntries.slice(0, 3)));
4907
+ const prefixedEntries = await collect(store.list(prefix));
4908
+ (0, node_assert_strict.deepStrictEqual)(normalize(filterByPrefix(await collect(store.list()), prefix)), normalize(prefixedEntries));
4909
+ (0, node_assert_strict.deepStrictEqual)(normalize(filterByPrefix(await collect(store.list([])), prefix)), normalize(prefixedEntries));
4910
+ if (options.testCas ?? store.cas != null) {
4911
+ if (store.cas == null) throw new TypeError("KvStore.cas is not implemented");
4912
+ const cas = store.cas.bind(store);
4913
+ const casKey = key("cas", "value");
4914
+ (0, node_assert_strict.strictEqual)(await cas(casKey, "wrong", "value"), false);
4915
+ (0, node_assert_strict.strictEqual)(await cas(casKey, void 0, "created"), true);
4916
+ (0, node_assert_strict.strictEqual)(await cas(casKey, "wrong", "updated"), false);
4917
+ (0, node_assert_strict.strictEqual)(await cas(casKey, "created", "updated"), true);
4918
+ (0, node_assert_strict.strictEqual)(await store.get(casKey), "updated");
4919
+ (0, node_assert_strict.strictEqual)(await cas(casKey, "updated", void 0), true);
4920
+ (0, node_assert_strict.strictEqual)(await store.get(casKey), void 0);
4921
+ if (options.testTtl ?? true) {
4922
+ const expiredCasKey = key("cas", "expired");
4923
+ await store.set(expiredCasKey, "old", { ttl: Temporal$1.Duration.from({ milliseconds: 50 }) });
4924
+ await (0, es_toolkit.delay)(100);
4925
+ (0, node_assert_strict.strictEqual)(await cas(expiredCasKey, void 0, "replaced"), true);
4926
+ const ttlCasKey = key("cas", "ttl");
4927
+ (0, node_assert_strict.strictEqual)(await cas(ttlCasKey, void 0, "temporary", { ttl: Temporal$1.Duration.from({ milliseconds: 50 }) }), true);
4928
+ await (0, es_toolkit.delay)(100);
4929
+ (0, node_assert_strict.strictEqual)(await store.get(ttlCasKey), void 0);
4930
+ }
4931
+ const concurrentKey = key("cas", "concurrent");
4932
+ (0, node_assert_strict.strictEqual)((await Promise.all(Array.from({ length: 8 }, (_, index) => cas(concurrentKey, void 0, index)))).filter(Boolean).length, 1);
4933
+ }
4934
+ } finally {
4935
+ for await (const entry of store.list(prefix)) await store.delete(entry.key);
4936
+ await onFinally({ store });
4937
+ }
4938
+ }
4939
+ async function collect(entries) {
4940
+ const results = [];
4941
+ for await (const entry of entries) results.push(entry);
4942
+ return results;
4943
+ }
4944
+ function normalize(entries) {
4945
+ return entries.map((entry) => JSON.stringify(entry)).sort();
4946
+ }
4947
+ function filterByPrefix(entries, prefix) {
4948
+ return entries.filter((entry) => prefix.every((segment, index) => entry.key[index] === segment));
4949
+ }
4950
+ //#endregion
4539
4951
  //#region src/mq-tester.ts
4540
- const Temporal = Temporal$1;
4952
+ const Temporal = Temporal$2;
4541
4953
  /**
4542
4954
  * Tests a {@link MessageQueue} implementation with a standard set of tests.
4543
4955
  *
@@ -4723,5 +5135,6 @@ exports.createInboxContext = createInboxContext;
4723
5135
  exports.createOutboxContext = createOutboxContext;
4724
5136
  exports.createRequestContext = createRequestContext;
4725
5137
  exports.getRandomKey = getRandomKey;
5138
+ exports.testKvStore = testKvStore;
4726
5139
  exports.testMessageQueue = testMessageQueue;
4727
5140
  exports.waitFor = waitFor;