@fedify/interaction-controls 2.5.0-dev.2339 → 2.5.0-dev.2355

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { Accept, Announce, AnnounceAuthorization, AnnounceRequest, Application, Article, ChatMessage, Create, Delete, FeatureAuthorization, FeatureRequest, FeaturedCollection, Group, Like, LikeAuthorization, LikeRequest, Mention, Note, Object as Object$1, Organization, PUBLIC_COLLECTION, Person, Question, QuoteAuthorization, QuoteRequest, Reject, ReplyAuthorization, ReplyRequest, Service, isActor } from "@fedify/vocab";
3
- import { getFe34Origin } from "@fedify/vocab-runtime";
3
+ import { FetchError, getFe34Origin, isTransientFetchError, parseIri } from "@fedify/vocab-runtime";
4
4
  //#region src/control.ts
5
5
  function createInteractionControl(config) {
6
6
  return {
@@ -29,7 +29,7 @@ function createInteractionControl(config) {
29
29
  createRevocation: (options) => new Delete({
30
30
  id: options.id,
31
31
  actor: options.actor,
32
- object: getRequiredId(options.authorization, "authorization"),
32
+ object: options.embedAuthorization && !(options.authorization instanceof URL) ? createEmbeddedAuthorization(options.authorization, config) : getRequiredId(options.authorization, "authorization"),
33
33
  ...audience(options)
34
34
  }),
35
35
  recognizeImpolite: config.recognizeImpolite,
@@ -58,6 +58,17 @@ function getRequiredId(value, name) {
58
58
  if (value.id == null) throw new TypeError(`The ${name} must have an id.`);
59
59
  return value.id;
60
60
  }
61
+ function createEmbeddedAuthorization(authorization, config) {
62
+ const { interactingObjectId, interactionTargetId } = authorization;
63
+ if (interactingObjectId == null) throw new TypeError("The authorization's interactingObject must have an id.");
64
+ if (interactionTargetId == null) throw new TypeError("The authorization's interactionTarget must have an id.");
65
+ return new config.authorizationClass({
66
+ id: getRequiredId(authorization, "authorization"),
67
+ attributions: authorization.attributionIds,
68
+ interactingObject: interactingObjectId,
69
+ interactionTarget: interactionTargetId
70
+ });
71
+ }
61
72
  function getId(value) {
62
73
  return value instanceof URL ? value : value.id;
63
74
  }
@@ -69,8 +80,9 @@ function getTypeId(object) {
69
80
  }
70
81
  async function verifyRequest(context, options, config) {
71
82
  const documentLoader = options.documentLoader ?? context.documentLoader;
83
+ const contextLoader = options.contextLoader ?? documentLoader;
72
84
  const expectedRequestId = options.request instanceof URL ? options.request : null;
73
- const requestResult = await materialize(options.request, config.requestClass, documentLoader);
85
+ const requestResult = await materialize(options.request, config.requestClass, documentLoader, contextLoader);
74
86
  if (!requestResult.ok) return {
75
87
  verified: false,
76
88
  failure: requestResult.failure
@@ -106,12 +118,17 @@ async function verifyRequest(context, options, config) {
106
118
  actual: request.id
107
119
  }
108
120
  };
109
- const dereferenceOptions = {
110
- documentLoader,
111
- contextLoader: documentLoader,
112
- suppressError: true
121
+ const targetResult = options.resolvedInteractionTarget != null && request.objectId != null ? {
122
+ ok: true,
123
+ value: options.resolvedInteractionTarget
124
+ } : await dereferenceField(request, request.objectId, config.getInteractionTarget, documentLoader, contextLoader);
125
+ if (!targetResult.ok) return {
126
+ verified: false,
127
+ request,
128
+ requestId: request.id,
129
+ failure: targetResult.failure
113
130
  };
114
- const interactionTarget = await config.getInteractionTarget(request, dereferenceOptions);
131
+ const interactionTarget = targetResult.value;
115
132
  if (interactionTarget == null) return {
116
133
  verified: false,
117
134
  request,
@@ -132,7 +149,17 @@ async function verifyRequest(context, options, config) {
132
149
  actualTypes: [getTypeId(interactionTarget)]
133
150
  }
134
151
  };
135
- const interactingObject = await config.getInteractingObject(request, dereferenceOptions);
152
+ const interactingResult = options.resolvedInteractingObject != null && request.instrumentId != null ? {
153
+ ok: true,
154
+ value: options.resolvedInteractingObject
155
+ } : await dereferenceField(request, request.instrumentId, config.getInteractingObject, documentLoader, contextLoader);
156
+ if (!interactingResult.ok) return {
157
+ verified: false,
158
+ request,
159
+ requestId: request.id,
160
+ failure: interactingResult.failure
161
+ };
162
+ const interactingObject = interactingResult.value;
136
163
  if (interactingObject == null) return {
137
164
  verified: false,
138
165
  request,
@@ -183,7 +210,7 @@ async function verifyRequest(context, options, config) {
183
210
  type: "missingObjectId"
184
211
  }
185
212
  };
186
- const validation = config.validateRequest(request, interactingObject, interactionTarget, requester);
213
+ const validation = config.validateRequest(request, interactingObject, interactionTarget, requester, options);
187
214
  if (validation != null) return {
188
215
  verified: false,
189
216
  request,
@@ -212,23 +239,35 @@ async function verifyRequest(context, options, config) {
212
239
  };
213
240
  }
214
241
  async function verifyAuthorization(context, options, config) {
215
- const embeddedAuthorization = !(options.authorization instanceof URL);
216
- const expectedAuthorizationId = options.authorization instanceof URL ? options.authorization : null;
242
+ const authorizationUrl = options.authorization instanceof URL ? options.authorization : null;
243
+ const embeddedAuthorization = authorizationUrl == null;
244
+ const expectedAuthorizationId = options.authorizationId ?? authorizationUrl;
245
+ const checkOrigin = !options.allowOffOrigin || options.verifyAuthenticity == null;
217
246
  const expectedAttribution = options.attributedTo ?? (!(options.interactionTarget instanceof URL) ? config.getSelfActor(options.interactionTarget) : null);
218
- if (expectedAuthorizationId != null && expectedAttribution == null) return {
247
+ if (authorizationUrl != null && options.authorizationId != null && !idsEqual(authorizationUrl, options.authorizationId)) return {
248
+ verified: false,
249
+ authorizationId: authorizationUrl,
250
+ failure: {
251
+ category: "unauthorized",
252
+ type: "idMismatch",
253
+ expected: options.authorizationId,
254
+ actual: authorizationUrl
255
+ }
256
+ };
257
+ if (authorizationUrl != null && expectedAttribution == null) return {
219
258
  verified: false,
220
- authorizationId: expectedAuthorizationId,
259
+ authorizationId: authorizationUrl,
221
260
  failure: {
222
261
  category: "unauthorized",
223
262
  type: "missingAttribution"
224
263
  }
225
264
  };
226
- if (expectedAuthorizationId != null && expectedAttribution != null) {
265
+ if (checkOrigin && authorizationUrl != null && expectedAttribution != null) {
227
266
  const expectedOrigin = getAuthorizationOrigin(expectedAttribution);
228
- const actualOrigin = getAuthorizationOrigin(expectedAuthorizationId);
267
+ const actualOrigin = getAuthorizationOrigin(authorizationUrl);
229
268
  if (expectedOrigin === "null" || actualOrigin === "null" || expectedOrigin !== actualOrigin) return {
230
269
  verified: false,
231
- authorizationId: expectedAuthorizationId,
270
+ authorizationId: authorizationUrl,
232
271
  failure: {
233
272
  category: "unauthorized",
234
273
  type: "originMismatch",
@@ -237,7 +276,8 @@ async function verifyAuthorization(context, options, config) {
237
276
  }
238
277
  };
239
278
  }
240
- const authorizationResult = await materialize(options.authorization, config.authorizationClass, options.documentLoader ?? context.documentLoader);
279
+ const documentLoader = options.documentLoader ?? context.documentLoader;
280
+ const authorizationResult = await materialize(options.authorization, config.authorizationClass, documentLoader, options.contextLoader ?? documentLoader);
241
281
  if (!authorizationResult.ok) return {
242
282
  verified: false,
243
283
  failure: authorizationResult.failure
@@ -361,7 +401,7 @@ async function verifyAuthorization(context, options, config) {
361
401
  };
362
402
  const expectedOrigin = getAuthorizationOrigin(expectedAttribution);
363
403
  const actualOrigin = getAuthorizationOrigin(authorization.id);
364
- if (expectedOrigin === "null" || actualOrigin === "null" || expectedOrigin !== actualOrigin) return {
404
+ if (checkOrigin && (expectedOrigin === "null" || actualOrigin === "null" || expectedOrigin !== actualOrigin)) return {
365
405
  verified: false,
366
406
  authorization,
367
407
  authorizationId: authorization.id,
@@ -414,60 +454,163 @@ async function verifyAuthorization(context, options, config) {
414
454
  authorizationId: authorization.id
415
455
  };
416
456
  }
417
- async function materialize(value, constructor, documentLoader) {
418
- if (!(value instanceof URL)) return {
419
- ok: true,
420
- object: value
457
+ /**
458
+ * Wraps the loaders for a single dereferencing operation so that errors they
459
+ * throw can be told apart from other errors afterwards. Parsed objects keep
460
+ * the loaders they were given, so the wrappers stop recording and pass calls
461
+ * through once the operation is released.
462
+ */
463
+ function trackLoaders(documentLoader, contextLoader) {
464
+ const records = /* @__PURE__ */ new Map();
465
+ let released = false;
466
+ const wrap = (loader) => {
467
+ if (loader == null) return void 0;
468
+ return async (url, options) => {
469
+ if (released) return await loader(url, options);
470
+ let remoteDocument;
471
+ try {
472
+ remoteDocument = await loader(url, options);
473
+ } catch (error) {
474
+ if (!released) records.set(error, url);
475
+ throw error;
476
+ }
477
+ if (remoteDocument == null) {
478
+ const message = "The document loader returned no document.";
479
+ let error;
480
+ try {
481
+ error = new FetchError(parseIri(url), message);
482
+ } catch {
483
+ error = /* @__PURE__ */ new Error(`${url}: ${message}`);
484
+ }
485
+ if (!released) records.set(error, url);
486
+ throw error;
487
+ }
488
+ return remoteDocument;
489
+ };
421
490
  };
422
- const loader = documentLoader;
423
- if (loader == null) return {
424
- ok: false,
425
- failure: {
426
- category: "unverifiable",
427
- type: "notDereferenceable",
428
- url: value
491
+ const wrappedDocumentLoader = wrap(documentLoader);
492
+ return {
493
+ documentLoader: wrappedDocumentLoader,
494
+ contextLoader: contextLoader === documentLoader ? wrappedDocumentLoader : wrap(contextLoader),
495
+ records,
496
+ release: () => {
497
+ released = true;
498
+ records.clear();
499
+ }
500
+ };
501
+ }
502
+ const MAX_CAUSE_DEPTH = 8;
503
+ function collectCauses(error, causes = [], depth = 0) {
504
+ if (depth > MAX_CAUSE_DEPTH || causes.includes(error)) return causes;
505
+ causes.push(error);
506
+ if (typeof error !== "object" || error == null) return causes;
507
+ if (error instanceof AggregateError) for (const e of error.errors) collectCauses(e, causes, depth + 1);
508
+ if ("cause" in error) collectCauses(error.cause, causes, depth + 1);
509
+ const details = error.details;
510
+ if (typeof details === "object" && details != null && "cause" in details) collectCauses(details.cause, causes, depth + 1);
511
+ return causes;
512
+ }
513
+ /**
514
+ * Classifies the error thrown by a dereferencing operation. It is a fetch
515
+ * failure only if a loader error is among its causes; errors that loaders
516
+ * threw during attempts the operation recovered from are ignored.
517
+ */
518
+ function classifyFailure(error, tracker) {
519
+ const loaderErrors = collectCauses(error).filter((e) => tracker.records.has(e));
520
+ const transientIndex = loaderErrors.findIndex(isTransientFetchError);
521
+ const selected = loaderErrors[transientIndex < 0 ? 0 : transientIndex];
522
+ const url = loaderErrors.length < 1 ? null : getFailedUrl(selected, tracker.records.get(selected));
523
+ if (url == null) return {
524
+ category: "unverifiable",
525
+ type: "invalidJsonLd",
526
+ cause: error,
527
+ transient: false
528
+ };
529
+ return {
530
+ category: "unverifiable",
531
+ type: "notDereferenceable",
532
+ url,
533
+ cause: loaderErrors.length === 1 ? selected : error,
534
+ transient: transientIndex >= 0
535
+ };
536
+ }
537
+ function getFailedUrl(error, requestedUrl) {
538
+ const url = error?.url;
539
+ if (url instanceof URL) return url;
540
+ for (const candidate of [url, requestedUrl]) {
541
+ if (typeof candidate !== "string") continue;
542
+ try {
543
+ return parseIri(candidate);
544
+ } catch {
545
+ continue;
429
546
  }
547
+ }
548
+ return null;
549
+ }
550
+ function notDereferenceable(url) {
551
+ return {
552
+ category: "unverifiable",
553
+ type: "notDereferenceable",
554
+ url,
555
+ transient: false
430
556
  };
431
- let remoteDocument;
557
+ }
558
+ /**
559
+ * Dereferences a field of the request, such as its `object` or `instrument`.
560
+ * Returns `null` if the request has no such field at all.
561
+ */
562
+ async function dereferenceField(request, reference, dereference, documentLoader, contextLoader) {
563
+ const tracker = trackLoaders(documentLoader, contextLoader);
432
564
  try {
433
- remoteDocument = await loader(value.href);
434
- } catch (cause) {
565
+ const value = await dereference(request, {
566
+ documentLoader: tracker.documentLoader,
567
+ contextLoader: tracker.contextLoader,
568
+ suppressError: false
569
+ });
570
+ if (value != null || reference == null) return {
571
+ ok: true,
572
+ value
573
+ };
435
574
  return {
436
575
  ok: false,
437
- failure: {
438
- category: "unverifiable",
439
- type: "notDereferenceable",
440
- url: value,
441
- cause
442
- }
576
+ failure: notDereferenceable(reference)
443
577
  };
578
+ } catch (error) {
579
+ return {
580
+ ok: false,
581
+ failure: classifyFailure(error, tracker)
582
+ };
583
+ } finally {
584
+ tracker.release();
444
585
  }
445
- if (remoteDocument == null) return {
586
+ }
587
+ async function materialize(value, constructor, documentLoader, contextLoader) {
588
+ if (!(value instanceof URL)) return {
589
+ ok: true,
590
+ object: value
591
+ };
592
+ if (documentLoader == null) return {
446
593
  ok: false,
447
- failure: {
448
- category: "unverifiable",
449
- type: "notDereferenceable",
450
- url: value
451
- }
594
+ failure: notDereferenceable(value)
452
595
  };
596
+ const tracker = trackLoaders(documentLoader, contextLoader);
453
597
  try {
598
+ const remoteDocument = await tracker.documentLoader(value.href);
454
599
  return {
455
600
  ok: true,
456
601
  object: await constructor.fromJsonLd(remoteDocument.document, {
457
- documentLoader: loader,
458
- contextLoader: loader,
602
+ documentLoader: tracker.documentLoader,
603
+ contextLoader: tracker.contextLoader,
459
604
  baseUrl: value
460
605
  })
461
606
  };
462
- } catch (cause) {
607
+ } catch (error) {
463
608
  return {
464
609
  ok: false,
465
- failure: {
466
- category: "unverifiable",
467
- type: "invalidJsonLd",
468
- cause
469
- }
610
+ failure: classifyFailure(error, tracker)
470
611
  };
612
+ } finally {
613
+ tracker.release();
471
614
  }
472
615
  }
473
616
  async function evaluatePolicy(context, options, config) {
@@ -484,30 +627,60 @@ async function evaluatePolicy(context, options, config) {
484
627
  reason: implicitAutomatic
485
628
  };
486
629
  const policy = options.subject.interactionPolicy;
487
- if (policy == null) return missingPolicyDecision(config);
488
- const rule = policy[config.policyProperty];
489
- if (rule == null) return missingRuleDecision(config);
490
- const automaticApprovals = rule.automaticApprovals ?? [];
491
- const manualApprovals = rule.manualApprovals ?? [];
492
- if (automaticApprovals.length < 1 && manualApprovals.length < 1) return missingRuleDecision(config);
493
- const automatic = await matchRule(automaticApprovals, options.requester, context, options.matchesApprovalCollection, { actorOnly: true });
630
+ let rule = getApprovalRule(policy?.[config.policyProperty]);
631
+ if (rule == null) {
632
+ rule = getApprovalRule(options.fallbackRule);
633
+ if (rule == null) return policy == null ? missingPolicyDecision(config) : missingRuleDecision(config);
634
+ }
635
+ const { automaticApprovals, manualApprovals } = rule;
636
+ const matchOptions = {
637
+ requester: options.requester,
638
+ context,
639
+ matchesApprovalCollection: options.matchesApprovalCollection,
640
+ throwCollectionErrors: options.collectionErrors === "throw"
641
+ };
642
+ if (options.precedence === "automatic") {
643
+ const automatic = await matchRule(automaticApprovals, matchOptions);
644
+ if (automatic?.result === "matched") return {
645
+ result: "automatic",
646
+ reason: automatic.reason
647
+ };
648
+ else if (automatic?.result === "unverifiableCollection") return deniedUnverifiableCollection(automatic);
649
+ const manual = await matchRule(manualApprovals, matchOptions);
650
+ if (manual?.result === "matched") return {
651
+ result: "manual",
652
+ reason: manual.reason
653
+ };
654
+ else if (manual?.result === "unverifiableCollection") return deniedUnverifiableCollection(manual);
655
+ return {
656
+ result: "denied",
657
+ reason: { type: "noMatch" }
658
+ };
659
+ }
660
+ const automatic = await matchRule(automaticApprovals, {
661
+ ...matchOptions,
662
+ actorOnly: true
663
+ });
494
664
  if (automatic?.result === "matched") return {
495
665
  result: "automatic",
496
666
  reason: automatic.reason
497
667
  };
498
- const manual = await matchRule(manualApprovals, options.requester, context, options.matchesApprovalCollection, { actorOnly: true });
668
+ const manual = await matchRule(manualApprovals, {
669
+ ...matchOptions,
670
+ actorOnly: true
671
+ });
499
672
  if (manual?.result === "matched") return {
500
673
  result: "manual",
501
674
  reason: manual.reason
502
675
  };
503
- const broadAutomatic = await matchRule(automaticApprovals, options.requester, context, options.matchesApprovalCollection);
504
- const unverifiableAutomatic = broadAutomatic?.result === "unverifiableCollection" ? broadAutomatic.collection : null;
676
+ const broadAutomatic = await matchRule(automaticApprovals, matchOptions);
677
+ const unverifiableAutomatic = broadAutomatic?.result === "unverifiableCollection" ? broadAutomatic : null;
505
678
  if (broadAutomatic?.result === "matched") return {
506
679
  result: "automatic",
507
680
  reason: broadAutomatic.reason
508
681
  };
509
- const broadManual = await matchRule(manualApprovals, options.requester, context, options.matchesApprovalCollection);
510
- if (broadManual?.result === "unverifiableCollection") return deniedUnverifiableCollection(broadManual.collection);
682
+ const broadManual = await matchRule(manualApprovals, matchOptions);
683
+ if (broadManual?.result === "unverifiableCollection") return deniedUnverifiableCollection(broadManual);
511
684
  else if (broadManual?.result === "matched") return {
512
685
  result: "manual",
513
686
  reason: broadManual.reason
@@ -518,6 +691,16 @@ async function evaluatePolicy(context, options, config) {
518
691
  reason: { type: "noMatch" }
519
692
  };
520
693
  }
694
+ function getApprovalRule(rule) {
695
+ if (rule == null) return null;
696
+ const automaticApprovals = rule.automaticApprovals ?? [];
697
+ const manualApprovals = rule.manualApprovals ?? [];
698
+ if (automaticApprovals.length < 1 && manualApprovals.length < 1) return null;
699
+ return {
700
+ automaticApprovals,
701
+ manualApprovals
702
+ };
703
+ }
521
704
  async function matchImplicitAutomaticActors(subject, requester, options, config) {
522
705
  if (config.getImplicitAutomaticActors == null) return null;
523
706
  for await (const actor of config.getImplicitAutomaticActors(subject, options)) if (idsEqual(actor, requester)) return {
@@ -552,7 +735,8 @@ function missingRuleDecision(config) {
552
735
  reason: { type: "missingRule" }
553
736
  };
554
737
  }
555
- async function matchRule(entries, requester, context, matchesApprovalCollection, options = {}) {
738
+ async function matchRule(entries, options) {
739
+ const { requester, matchesApprovalCollection } = options;
556
740
  for (const entry of entries) if (entry.href === requester.href) return {
557
741
  result: "matched",
558
742
  reason: {
@@ -566,14 +750,18 @@ async function matchRule(entries, requester, context, matchesApprovalCollection,
566
750
  reason: { type: "public" }
567
751
  };
568
752
  if (matchesApprovalCollection != null) {
569
- let unverifiableCollection = null;
753
+ let unverifiable = null;
570
754
  for (const entry of entries) {
571
755
  if (entry.href === PUBLIC_COLLECTION.href || entry.href === requester.href) continue;
572
756
  let matched;
573
757
  try {
574
- matched = await matchesApprovalCollection(entry, requester, context);
575
- } catch {
576
- unverifiableCollection ??= entry;
758
+ matched = await matchesApprovalCollection(entry, requester, options.context);
759
+ } catch (cause) {
760
+ if (options.throwCollectionErrors) throw cause;
761
+ unverifiable ??= {
762
+ collection: entry,
763
+ cause
764
+ };
577
765
  continue;
578
766
  }
579
767
  if (matched) return {
@@ -584,19 +772,20 @@ async function matchRule(entries, requester, context, matchesApprovalCollection,
584
772
  }
585
773
  };
586
774
  }
587
- if (unverifiableCollection != null) return {
775
+ if (unverifiable != null) return {
588
776
  result: "unverifiableCollection",
589
- collection: unverifiableCollection
777
+ ...unverifiable
590
778
  };
591
779
  }
592
780
  return null;
593
781
  }
594
- function deniedUnverifiableCollection(collection) {
782
+ function deniedUnverifiableCollection(failure) {
595
783
  return {
596
784
  result: "denied",
597
785
  reason: {
598
786
  type: "unverifiableCollection",
599
- collection
787
+ collection: failure.collection,
788
+ cause: failure.cause
600
789
  }
601
790
  };
602
791
  }
@@ -797,20 +986,23 @@ const quoteInteraction = createInteractionControl({
797
986
  ChatMessage.typeId
798
987
  ],
799
988
  getInteractionTarget: (request, options) => request.getObject(options),
800
- validateRequest: (_request, quote, target, requester) => {
989
+ validateRequest: (_request, quote, target, requester, options) => {
801
990
  const targetId = getRequiredId(target, "interactionTarget");
802
- if (quote.quoteId != null && quote.quoteUrl != null && !idsEqual(quote.quoteUrl, quote.quoteId)) return {
991
+ const quoteReference = options.quoteReference ?? "strict";
992
+ if (quoteReference === "strict" && quote.quoteId != null && quote.quoteUrl != null && !idsEqual(quote.quoteUrl, quote.quoteId)) return {
803
993
  type: "objectMismatch",
804
994
  expected: quote.quoteId,
805
995
  actual: quote.quoteUrl
806
996
  };
807
997
  const quoteTargetId = getQuoteTargetId(quote);
808
- if (!idsEqual(quoteTargetId, targetId)) return {
998
+ if (!(quoteReference === "any" ? idsEqual(quote.quoteId, targetId) || idsEqual(quote.quoteUrl, targetId) : idsEqual(quoteTargetId, targetId))) return {
809
999
  type: "objectMismatch",
810
1000
  expected: targetId,
811
1001
  actual: quoteTargetId ?? void 0
812
1002
  };
813
- if (!idsEqual(quote.attributionId, requester)) return {
1003
+ const attributionIds = quote.attributionIds;
1004
+ if (attributionIds.length < 1 && options.missingAttribution === "requester") return null;
1005
+ if (!(options.attribution === "any" ? attributionIds.some((id) => idsEqual(id, requester)) : idsEqual(quote.attributionId, requester))) return {
814
1006
  type: "requesterMismatch",
815
1007
  expected: requester,
816
1008
  actual: quote.attributionId ?? void 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/interaction-controls",
3
- "version": "2.5.0-dev.2339+5e786861",
3
+ "version": "2.5.0-dev.2355+c92a1cb1",
4
4
  "description": "Interaction control helpers for Fedify",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -47,12 +47,12 @@
47
47
  "README.md"
48
48
  ],
49
49
  "peerDependencies": {
50
- "@fedify/fedify": "^2.5.0-dev.2339+5e786861"
50
+ "@fedify/fedify": "^2.5.0-dev.2355+c92a1cb1"
51
51
  },
52
52
  "dependencies": {
53
53
  "temporal-polyfill": "^1.0.1",
54
- "@fedify/vocab": "2.5.0-dev.2339+5e786861",
55
- "@fedify/vocab-runtime": "^2.5.0-dev.2339+5e786861"
54
+ "@fedify/vocab": "2.5.0-dev.2355+c92a1cb1",
55
+ "@fedify/vocab-runtime": "^2.5.0-dev.2355+c92a1cb1"
56
56
  },
57
57
  "devDependencies": {
58
58
  "tsdown": "^0.22.0",