@fedify/fedify 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.
Files changed (244) hide show
  1. package/README.md +50 -39
  2. package/dist/{assert-CJ1KXcvv.mjs → assert-D1Eafmd_.mjs} +1 -1
  3. package/dist/{assert_equals-BV7oLMxm.mjs → assert_equals-Z6IcEbWU.mjs} +2 -28
  4. package/dist/assert_exists-cffSpJ7X.mjs +28 -0
  5. package/dist/assert_false-CPdRnc3E.mjs +23 -0
  6. package/dist/assert_greater_or_equal-BlafOF4-.mjs +31 -0
  7. package/dist/{assert_instance_of-Q9KTBnrf.mjs → assert_instance_of-mrXWWNY-.mjs} +1 -1
  8. package/dist/{assert_not_equals-EPJPoxuf.mjs → assert_not_equals-CpPK2t89.mjs} +2 -1
  9. package/dist/assert_rejects-CTptdYck.mjs +39 -0
  10. package/dist/{assert_strict_equals-Ec20AUwa.mjs → assert_strict_equals-CYvBmkpC.mjs} +2 -1
  11. package/dist/{assert_throws-NiFfqbnH.mjs → assert_throws-BYITOvPO.mjs} +2 -1
  12. package/dist/assertion_error-B-cx0fFA.mjs +31 -0
  13. package/dist/audience-CHQ-FlLD.mjs +95 -0
  14. package/dist/{builder-DonuxRl_.mjs → builder-Bda6LK7A.mjs} +169 -14
  15. package/dist/{circuit-breaker-CBGyz5F7.mjs → circuit-breaker-XJ53md4S.mjs} +224 -17
  16. package/dist/circuit-breaker-test-utils-DW6YC_Pe.mjs +14 -0
  17. package/dist/{client-CjF5IYQS.mjs → client-CEnNz8O7.mjs} +144 -8
  18. package/dist/{codec-BBga-FWI.mjs → codec-C6rTrpIp.mjs} +26 -3
  19. package/dist/{collection-BIhlldqK.mjs → collection-CnnP1UAv.mjs} +1 -1
  20. package/dist/compat/mod.cjs +1 -1
  21. package/dist/compat/mod.d.cts +1 -1
  22. package/dist/compat/mod.d.ts +1 -1
  23. package/dist/compat/mod.js +1 -1
  24. package/dist/compat/outgoing-jsonld.test.mjs +71 -3
  25. package/dist/compat/public-audience.test.mjs +14 -5
  26. package/dist/compat/secured-document.test.d.mts +2 -0
  27. package/dist/compat/secured-document.test.mjs +58 -0
  28. package/dist/compat/transformers.test.mjs +5 -5
  29. package/dist/compound-proof-B-9mXxFW.mjs +989 -0
  30. package/dist/{context-Zii1f5xS.d.ts → context-7NyiqIcg.d.ts} +1600 -25
  31. package/dist/{context-BJXxPElm.mjs → context-8Tu9kea0.mjs} +21 -63
  32. package/dist/{context-lxX6I7as.d.cts → context-tsYPJ2Ew.d.cts} +1600 -25
  33. package/dist/{deno-BHzKn9Ig.mjs → deno-oR5aJKpU.mjs} +1 -1
  34. package/dist/dist-DJQM4u7i.mjs +189 -0
  35. package/dist/docloader-DBoYbvYW.mjs +87 -0
  36. package/dist/{esm-yYQfPhIY.mjs → esm-BHXXx4Wx.mjs} +13 -6
  37. package/dist/federation/audience.test.d.mts +2 -0
  38. package/dist/federation/audience.test.mjs +130 -0
  39. package/dist/federation/builder.test.mjs +54 -5
  40. package/dist/federation/circuit-breaker.test.mjs +838 -7
  41. package/dist/federation/collection.test.mjs +3 -3
  42. package/dist/federation/compound-fanout.test.d.mts +2 -0
  43. package/dist/federation/compound-fanout.test.mjs +228 -0
  44. package/dist/federation/handler-compound-proof.test.d.mts +2 -0
  45. package/dist/federation/handler-compound-proof.test.mjs +384 -0
  46. package/dist/federation/handler.test.mjs +385 -15
  47. package/dist/federation/hashlink-media.test.d.mts +2 -0
  48. package/dist/federation/hashlink-media.test.mjs +422 -0
  49. package/dist/federation/idempotency.test.mjs +6 -6
  50. package/dist/federation/inbox-report.test.d.mts +2 -0
  51. package/dist/federation/inbox-report.test.mjs +475 -0
  52. package/dist/federation/inbox.test.mjs +3 -3
  53. package/dist/federation/keycache.test.mjs +215 -14
  54. package/dist/federation/kv.test.mjs +3 -3
  55. package/dist/federation/metrics.test.mjs +5 -5
  56. package/dist/federation/middleware.test.mjs +967 -35
  57. package/dist/federation/mod.cjs +18 -261
  58. package/dist/federation/mod.d.cts +5 -5
  59. package/dist/federation/mod.d.ts +5 -5
  60. package/dist/federation/mod.js +279 -1
  61. package/dist/federation/mq.test.mjs +123 -47
  62. package/dist/federation/negotiation.test.mjs +4 -4
  63. package/dist/federation/outgoing-proof.test.d.mts +2 -0
  64. package/dist/federation/outgoing-proof.test.mjs +931 -0
  65. package/dist/federation/portable-actor.test.d.mts +2 -0
  66. package/dist/federation/portable-actor.test.mjs +496 -0
  67. package/dist/federation/portable-chain.test.d.mts +2 -0
  68. package/dist/federation/portable-chain.test.mjs +370 -0
  69. package/dist/federation/portable-collection.test.d.mts +2 -0
  70. package/dist/federation/portable-collection.test.mjs +832 -0
  71. package/dist/federation/portable-delivery.test.d.mts +2 -0
  72. package/dist/federation/portable-delivery.test.mjs +746 -0
  73. package/dist/federation/portable-gateway-keys.test.d.mts +2 -0
  74. package/dist/federation/portable-gateway-keys.test.mjs +686 -0
  75. package/dist/federation/portable-inbox.test.d.mts +2 -0
  76. package/dist/federation/portable-inbox.test.mjs +1535 -0
  77. package/dist/federation/portable-parse-uri.test.d.mts +2 -0
  78. package/dist/federation/portable-parse-uri.test.mjs +348 -0
  79. package/dist/federation/portable-route-activity.test.d.mts +2 -0
  80. package/dist/federation/portable-route-activity.test.mjs +259 -0
  81. package/dist/federation/portable.test.d.mts +2 -0
  82. package/dist/federation/portable.test.mjs +1072 -0
  83. package/dist/federation/retry.test.mjs +4 -3
  84. package/dist/federation/router.test.mjs +9 -159
  85. package/dist/federation/send.test.mjs +245 -27
  86. package/dist/federation/tasks/codec.test.mjs +4 -4
  87. package/dist/federation/tasks/enqueue.test.mjs +42 -4
  88. package/dist/federation/tasks/tasks.test.mjs +7 -6
  89. package/dist/federation/temporal.test.mjs +4 -4
  90. package/dist/federation/webfinger.test.mjs +275 -12
  91. package/dist/{middleware-FrSMAYEx.cjs → federation-CkZiAcWY.cjs} +8640 -3722
  92. package/dist/http-B5JT115n.cjs +10630 -0
  93. package/dist/http-DydWCB7R.js +6527 -0
  94. package/dist/{http-CPVMUgsW.mjs → http-siciWkmx.mjs} +397 -168
  95. package/dist/key-BspPiXqn.mjs +951 -0
  96. package/dist/key-C1nv7_jD.d.ts +1125 -0
  97. package/dist/key-DYxuyd-s.d.cts +1125 -0
  98. package/dist/key-owner-evidence-APB6NpSk.mjs +108 -0
  99. package/dist/keycache-Da8A8mZa.mjs +200 -0
  100. package/dist/{kv-D6hNiMTK.d.ts → kv-B2mzK5tl.d.cts} +3 -2
  101. package/dist/{kv-gJ8LYbxX.d.cts → kv-B2mzK5tl.d.ts} +3 -2
  102. package/dist/kv-DBRWVx3Q.mjs +104 -0
  103. package/dist/{kv-cache-KyGqJqSp.mjs → kv-cache-BF1hnW65.mjs} +1 -1
  104. package/dist/{kv-cache-5bnX3-Jq.js → kv-cache-CTQr1tbn.js} +54 -16
  105. package/dist/{kv-cache-DQ29C4XL.cjs → kv-cache-DCac1nEU.cjs} +55 -19
  106. package/dist/{ld-Cj9F_5AP.mjs → ld-CzaP-hJl.mjs} +44 -12
  107. package/dist/map-local-context-conflict-DOYEEJ3u.mjs +204 -0
  108. package/dist/map-local-create-note-B0SeXJms.mjs +214 -0
  109. package/dist/{metrics-DMk-nTxI.mjs → metrics-CYg9yxBL.mjs} +3 -2
  110. package/dist/metrics-DVKYY6K1.d.cts +36 -0
  111. package/dist/metrics-DoRtI0qU.d.ts +36 -0
  112. package/dist/{middleware-Y-kpsp6S.js → middleware-CsDDXKuL.js} +5493 -1103
  113. package/dist/{middleware-CvwTUgnP.mjs → middleware-D7_LO-Vs.mjs} +3157 -272
  114. package/dist/{middleware-CEctxBN6.mjs → middleware-DWjrfpcz.mjs} +1 -1
  115. package/dist/{mod-gOlPDuT8.d.cts → mod-BojP2Bw3.d.cts} +1 -1
  116. package/dist/mod-CavhAgLw.d.cts +320 -0
  117. package/dist/mod-CtpF2BU5.d.ts +320 -0
  118. package/dist/{mod-BtEaIEcD.d.cts → mod-DEZXwV6J.d.ts} +10 -4
  119. package/dist/{mod-UMmveiO0.d.ts → mod-Dw-nPxQh.d.cts} +10 -4
  120. package/dist/{mod-DLc2Ot6f.d.ts → mod-Xj5P4s4d.d.ts} +1 -1
  121. package/dist/mod.cjs +40 -39
  122. package/dist/mod.d.cts +10 -11
  123. package/dist/mod.d.ts +10 -11
  124. package/dist/mod.js +9 -9
  125. package/dist/{mq-llHKivST.mjs → mq-Dm1Wmtm-.mjs} +6 -0
  126. package/dist/nodeinfo/client.test.mjs +139 -4
  127. package/dist/nodeinfo/handler.test.mjs +5 -5
  128. package/dist/nodeinfo/mod.cjs +1 -1
  129. package/dist/nodeinfo/mod.d.cts +1 -1
  130. package/dist/nodeinfo/mod.d.ts +1 -1
  131. package/dist/nodeinfo/mod.js +1 -1
  132. package/dist/nodeinfo/types.test.mjs +4 -4
  133. package/dist/otel/exporter.test.mjs +3 -3
  134. package/dist/otel/mod.cjs +0 -1
  135. package/dist/otel/mod.d.cts +1 -1
  136. package/dist/otel/mod.d.ts +1 -1
  137. package/dist/{outgoing-jsonld-URuqsICz.mjs → outgoing-jsonld-4IJqPYos.mjs} +12 -10
  138. package/dist/{owner-DEvZuyOE.d.cts → owner-BF0L9cMt.d.cts} +11 -0
  139. package/dist/{owner-CnngXDNJ.d.ts → owner-BSzr5-I8.d.ts} +11 -0
  140. package/dist/owner-DYgfTFHy.mjs +267 -0
  141. package/dist/portable-collection-BpsVsRov.js +670 -0
  142. package/dist/portable-collection-C74P1vtC.mjs +418 -0
  143. package/dist/portable-collection-DZd2bm7M.cjs +686 -0
  144. package/dist/portable-key-BIMLrAP4.mjs +549 -0
  145. package/dist/portable-key-id-Ca30-hOa.mjs +248 -0
  146. package/dist/proof-Dxy6ByqI.mjs +1299 -0
  147. package/dist/{public-audience-Cr5MsnMq.mjs → public-audience--klBIOk1.mjs} +15 -10
  148. package/dist/router-u5O5KCQr.mjs +156 -0
  149. package/dist/runtime/mod.cjs +1 -1
  150. package/dist/runtime/mod.js +1 -1
  151. package/dist/secured-document-CxzZZ9nL.mjs +35 -0
  152. package/dist/send-C4dDpAxm.mjs +495 -0
  153. package/dist/sig/accept.test.mjs +1 -1
  154. package/dist/sig/compound-proof-discovery.test.d.mts +2 -0
  155. package/dist/sig/compound-proof-discovery.test.mjs +331 -0
  156. package/dist/sig/compound-proof-producer.test.d.mts +2 -0
  157. package/dist/sig/compound-proof-producer.test.mjs +107 -0
  158. package/dist/sig/compound-proof-verification.test.d.mts +2 -0
  159. package/dist/sig/compound-proof-verification.test.mjs +833 -0
  160. package/dist/sig/compound-proof.test.d.mts +2 -0
  161. package/dist/sig/compound-proof.test.mjs +411 -0
  162. package/dist/sig/http.test.mjs +702 -31
  163. package/dist/sig/key-owner-evidence.test.d.mts +2 -0
  164. package/dist/sig/key-owner-evidence.test.mjs +100 -0
  165. package/dist/sig/key.test.mjs +220 -8
  166. package/dist/sig/ld.test.mjs +65 -7
  167. package/dist/sig/mitra-receive-interop.test.d.mts +2 -0
  168. package/dist/sig/mitra-receive-interop.test.mjs +260 -0
  169. package/dist/sig/mitra-send-interop.test.d.mts +2 -0
  170. package/dist/sig/mitra-send-interop.test.mjs +461 -0
  171. package/dist/sig/mod.cjs +18 -16
  172. package/dist/sig/mod.d.cts +4 -4
  173. package/dist/sig/mod.d.ts +4 -4
  174. package/dist/sig/mod.js +3 -3
  175. package/dist/sig/owner.test.mjs +108 -9
  176. package/dist/sig/portable-collection.test.d.mts +2 -0
  177. package/dist/sig/portable-collection.test.mjs +697 -0
  178. package/dist/sig/portable-interop.test.d.mts +2 -0
  179. package/dist/sig/portable-interop.test.mjs +156 -0
  180. package/dist/sig/portable-key-id.test.d.mts +2 -0
  181. package/dist/sig/portable-key-id.test.mjs +67 -0
  182. package/dist/sig/portable-key.test.d.mts +2 -0
  183. package/dist/sig/portable-key.test.mjs +1439 -0
  184. package/dist/sig/proof.test.mjs +1753 -16
  185. package/dist/sig/tootik-request-interop.test.d.mts +2 -0
  186. package/dist/sig/tootik-request-interop.test.mjs +190 -0
  187. package/dist/sig/tootik-send-interop.test.d.mts +2 -0
  188. package/dist/sig/tootik-send-interop.test.mjs +331 -0
  189. package/dist/sig/verification.test.d.mts +2 -0
  190. package/dist/sig/verification.test.mjs +310 -0
  191. package/dist/std__assert-DvMDRPes.mjs +8 -0
  192. package/dist/{testing-CO7ftO8A.mjs → tasks-IG3pHfvZ.mjs} +2 -2
  193. package/dist/{temporal-fsXO5IvD.mjs → temporal-D0KJI4NF.mjs} +3 -3
  194. package/dist/testing/context.test.d.mts +2 -0
  195. package/dist/testing/context.test.mjs +19 -0
  196. package/dist/testing/kv.test.d.mts +2 -0
  197. package/dist/testing/kv.test.mjs +101 -0
  198. package/dist/testing/mod.d.mts +1617 -48
  199. package/dist/testing/mod.mjs +6 -3
  200. package/dist/testing-t8xDeJZ5.mjs +8 -0
  201. package/dist/{transformers-DGwl-hD1.cjs → transformers-BalCrSsr.cjs} +0 -1
  202. package/dist/{types-DC1pNgJA.js → types-_S2_MeLF.js} +144 -8
  203. package/dist/{types-CPym83zc.cjs → types-poIao40f.cjs} +160 -7
  204. package/dist/utils/docloader.test.mjs +530 -8
  205. package/dist/utils/kv-cache.test.mjs +2 -2
  206. package/dist/utils/mod.cjs +1 -1
  207. package/dist/utils/mod.d.cts +1 -1
  208. package/dist/utils/mod.d.ts +1 -1
  209. package/dist/utils/mod.js +1 -1
  210. package/dist/verification-C3954hQh.mjs +5 -0
  211. package/dist/verification-Dpfcw8ml.mjs +105 -0
  212. package/dist/vocab/cjs.test.mjs +1 -1
  213. package/package.json +8 -8
  214. package/dist/assert_rejects-EgstcQy5.mjs +0 -82
  215. package/dist/docloader-e1khNUKy.mjs +0 -49
  216. package/dist/http-CsmQ-BQ4.js +0 -2483
  217. package/dist/http-VyDTd4G3.d.cts +0 -553
  218. package/dist/http-hlIjmewS.cjs +0 -2694
  219. package/dist/http-lf8Hsd91.d.ts +0 -553
  220. package/dist/key-DfPsX8hv.mjs +0 -453
  221. package/dist/keycache-_DHqz5z_.mjs +0 -97
  222. package/dist/metrics-CoUJLQhZ.d.cts +0 -276
  223. package/dist/metrics-Dc4jR-IC.d.ts +0 -276
  224. package/dist/mod-B0hW12_O.d.cts +0 -307
  225. package/dist/mod-COIAjwRS.d.ts +0 -307
  226. package/dist/owner-Br9YYzn_.mjs +0 -120
  227. package/dist/proof-B4ngoaNQ.mjs +0 -316
  228. package/dist/proof-B6E0XUXJ.js +0 -1415
  229. package/dist/proof-NN4xCQGL.cjs +0 -1562
  230. package/dist/send-CxHKUNzv.mjs +0 -239
  231. package/dist/std__assert-B2NnY9bq.mjs +0 -79
  232. package/dist/temporal-polyfill-BCdTHexd.cjs +0 -3565
  233. /package/dist/{accept-8PiolDP7.mjs → accept-CPVqki78.mjs} +0 -0
  234. /package/dist/{activity-listener-BtTSxZWA.mjs → activity-listener-C-BSbKI7.mjs} +0 -0
  235. /package/dist/{client-CAM_bQXx.d.cts → client-D-XP_Ccb.d.cts} +0 -0
  236. /package/dist/{client-CSddvgWN.d.ts → client-D-XP_Ccb.d.ts} +0 -0
  237. /package/dist/{keys-Dbyd7bhl.mjs → keys-DIJNHW-Y.mjs} +0 -0
  238. /package/dist/{kv-CXBAok3k.mjs → kv-DCHkaJ9-.mjs} +0 -0
  239. /package/dist/{negotiation-RuwrPFqi.mjs → negotiation-CCocWT_N.mjs} +0 -0
  240. /package/dist/{retry-DgFqeS5k.mjs → retry-zX5xw5e0.mjs} +0 -0
  241. /package/dist/{chunk-D5IdezsZ.js → rolldown-runtime-BC-Eo5Mj.js} +0 -0
  242. /package/dist/{chunk-Czpeue4M.cjs → rolldown-runtime-Dmm3t92W.cjs} +0 -0
  243. /package/dist/{transformers-DXze6FUB.js → transformers-DAtXl_Kc.js} +0 -0
  244. /package/dist/{types-CrtdVuDP.mjs → types-DE_LV1JB.mjs} +0 -0
@@ -0,0 +1,989 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { a as getRawCanonicalPortableId, l as isPortableId, n as getCanonicalPortableId } from "./portable-key-id-Ca30-hOa.mjs";
5
+ import { a as verificationObservation, t as observeAttempt } from "./verification-Dpfcw8ml.mjs";
6
+ import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
7
+ import { c as getNormalizationContextLoader } from "./ld-CzaP-hJl.mjs";
8
+ import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-Dxy6ByqI.mjs";
9
+ import { parseIri, preloadedContexts } from "@fedify/vocab-runtime";
10
+ import jsonld from "@fedify/vocab-runtime/jsonld";
11
+ //#region src/sig/compound-proof.ts
12
+ const textEncoder = new TextEncoder();
13
+ /**
14
+ * Checks whether a literal `id` or `@id` value makes a map a portable object:
15
+ * an `ap:` or `ap+ef61:` URI, or anything that looks like an FEP-ef61
16
+ * compatible identifier, even a malformed one, which then fails the policy.
17
+ */
18
+ function isPortableIdValue(value) {
19
+ return typeof value === "string" && isPortableId(value);
20
+ }
21
+ function isJsonMap(value) {
22
+ return typeof value === "object" && value != null && !Array.isArray(value);
23
+ }
24
+ function isCompoundProofJsonObject(value) {
25
+ return typeof value === "object" && value != null && !Array.isArray(value);
26
+ }
27
+ function hasOnlyDataProperties(value) {
28
+ try {
29
+ return Reflect.ownKeys(value).every((key) => {
30
+ if (typeof key !== "string") return false;
31
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
32
+ return descriptor != null && "value" in descriptor && descriptor.enumerable;
33
+ });
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+ function hasOnlyJsonArrayProperties(value) {
39
+ try {
40
+ const keys = Reflect.ownKeys(value);
41
+ if (keys.length !== value.length + 1) return false;
42
+ for (const key of keys) {
43
+ if (key === "length") continue;
44
+ if (typeof key !== "string") return false;
45
+ const index = Number(key);
46
+ if (!Number.isInteger(index) || index < 0 || index >= value.length || String(index) !== key) return false;
47
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
48
+ if (descriptor == null || !descriptor.enumerable || !("value" in descriptor)) return false;
49
+ }
50
+ return true;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ function escapeJsonPointerSegment(segment) {
56
+ return segment.replaceAll("~", "~0").replaceAll("/", "~1");
57
+ }
58
+ function childPath(path, segment) {
59
+ return `${path}/${escapeJsonPointerSegment(segment)}`;
60
+ }
61
+ function comparePaths(left, right) {
62
+ return left < right ? -1 : left > right ? 1 : 0;
63
+ }
64
+ function encodedLength(value) {
65
+ return textEncoder.encode(value).byteLength;
66
+ }
67
+ function validateLimits(limits) {
68
+ for (const [name, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name} must be a non-negative safe integer.`);
69
+ }
70
+ function limitExceeded(limit, maximum, actual, path) {
71
+ return {
72
+ status: "unsupported",
73
+ reason: {
74
+ type: "limitExceeded",
75
+ limit,
76
+ maximum,
77
+ actual,
78
+ path
79
+ }
80
+ };
81
+ }
82
+ function addBytes(statistics, bytes, limits, path) {
83
+ statistics.byteLength += bytes;
84
+ if (statistics.byteLength > limits.maxBytes) return limitExceeded("bytes", limits.maxBytes, statistics.byteLength, path);
85
+ }
86
+ function inspectJsonTree(root, limits) {
87
+ if (!isJsonMap(root)) return {
88
+ status: "unsupported",
89
+ reason: {
90
+ type: "invalidJsonTree",
91
+ path: ""
92
+ }
93
+ };
94
+ const statistics = {
95
+ byteLength: 0,
96
+ mapCount: 0,
97
+ proofCount: 0,
98
+ maxDepth: 0
99
+ };
100
+ const seen = /* @__PURE__ */ new Set();
101
+ const pending = [{
102
+ value: root,
103
+ path: "",
104
+ depth: 0,
105
+ insideProof: false,
106
+ insideContext: false
107
+ }];
108
+ while (pending.length > 0) {
109
+ const current = pending.pop();
110
+ statistics.maxDepth = Math.max(statistics.maxDepth, current.depth);
111
+ if (current.depth > limits.maxDepth) return limitExceeded("depth", limits.maxDepth, current.depth, current.path);
112
+ const value = current.value;
113
+ if (value === null) {
114
+ const failure = addBytes(statistics, 4, limits, current.path);
115
+ if (failure != null) return failure;
116
+ } else if (typeof value === "boolean") {
117
+ const failure = addBytes(statistics, value ? 4 : 5, limits, current.path);
118
+ if (failure != null) return failure;
119
+ } else if (typeof value === "string") {
120
+ const failure = addBytes(statistics, encodedLength(JSON.stringify(value)), limits, current.path);
121
+ if (failure != null) return failure;
122
+ } else if (typeof value === "number") {
123
+ if (!Number.isFinite(value)) return {
124
+ status: "unsupported",
125
+ reason: {
126
+ type: "invalidJsonTree",
127
+ path: current.path
128
+ }
129
+ };
130
+ const failure = addBytes(statistics, encodedLength(JSON.stringify(value)), limits, current.path);
131
+ if (failure != null) return failure;
132
+ } else if (Array.isArray(value)) {
133
+ if (seen.has(value) || !hasOnlyJsonArrayProperties(value)) return {
134
+ status: "unsupported",
135
+ reason: {
136
+ type: "invalidJsonTree",
137
+ path: current.path
138
+ }
139
+ };
140
+ seen.add(value);
141
+ const failure = addBytes(statistics, 2 + Math.max(0, value.length - 1), limits, current.path);
142
+ if (failure != null) return failure;
143
+ for (let index = value.length - 1; index >= 0; index--) pending.push({
144
+ value: value[index],
145
+ path: childPath(current.path, String(index)),
146
+ depth: current.depth + 1,
147
+ insideProof: current.insideProof,
148
+ insideContext: current.insideContext
149
+ });
150
+ } else if (isJsonMap(value)) {
151
+ const prototype = Object.getPrototypeOf(value);
152
+ if (seen.has(value) || !hasOnlyDataProperties(value) || prototype !== Object.prototype && prototype !== null) return {
153
+ status: "unsupported",
154
+ reason: {
155
+ type: "invalidJsonTree",
156
+ path: current.path
157
+ }
158
+ };
159
+ seen.add(value);
160
+ statistics.mapCount++;
161
+ if (statistics.mapCount > limits.maxMaps) return limitExceeded("maps", limits.maxMaps, statistics.mapCount, current.path);
162
+ const entries = Object.entries(value).sort(([left], [right]) => comparePaths(left, right));
163
+ let objectBytes = 2 + Math.max(0, entries.length - 1);
164
+ for (const [key] of entries) objectBytes += encodedLength(JSON.stringify(key)) + 1;
165
+ const failure = addBytes(statistics, objectBytes, limits, current.path);
166
+ if (failure != null) return failure;
167
+ const hasDirectProof = !current.insideProof && !current.insideContext && Object.hasOwn(value, "proof");
168
+ if (hasDirectProof) {
169
+ statistics.proofCount++;
170
+ if (statistics.proofCount > limits.maxProofs) return limitExceeded("proofs", limits.maxProofs, statistics.proofCount, childPath(current.path, "proof"));
171
+ if (!isJsonMap(value.proof)) return {
172
+ status: "unsupported",
173
+ reason: {
174
+ type: "unsupportedProofShape",
175
+ path: childPath(current.path, "proof")
176
+ }
177
+ };
178
+ }
179
+ for (let index = entries.length - 1; index >= 0; index--) {
180
+ const [key, child] = entries[index];
181
+ pending.push({
182
+ value: child,
183
+ path: childPath(current.path, key),
184
+ depth: current.depth + 1,
185
+ insideProof: current.insideProof || hasDirectProof && key === "proof",
186
+ insideContext: current.insideContext || key === "@context"
187
+ });
188
+ }
189
+ } else return {
190
+ status: "unsupported",
191
+ reason: {
192
+ type: "invalidJsonTree",
193
+ path: current.path
194
+ }
195
+ };
196
+ }
197
+ return statistics;
198
+ }
199
+ function freezeJsonTree(root) {
200
+ const pending = [root];
201
+ while (pending.length > 0) {
202
+ const value = pending.pop();
203
+ if (typeof value !== "object" || value == null) continue;
204
+ if (Array.isArray(value)) for (const child of value) pending.push(child);
205
+ else for (const child of Object.values(value)) pending.push(child);
206
+ Object.freeze(value);
207
+ }
208
+ }
209
+ function collectDocuments(snapshot) {
210
+ const documents = [];
211
+ const pending = [{
212
+ value: snapshot,
213
+ path: "",
214
+ depth: 0
215
+ }];
216
+ while (pending.length > 0) {
217
+ const current = pending.pop();
218
+ if (Array.isArray(current.value)) {
219
+ for (let index = current.value.length - 1; index >= 0; index--) pending.push({
220
+ value: current.value[index],
221
+ path: childPath(current.path, String(index)),
222
+ depth: current.depth + 1
223
+ });
224
+ continue;
225
+ }
226
+ if (!isCompoundProofJsonObject(current.value)) continue;
227
+ const document = current.value;
228
+ if (Object.hasOwn(document, "proof")) {
229
+ const proof = document.proof;
230
+ const id = typeof document.id === "string" ? document.id : typeof document["@id"] === "string" ? document["@id"] : void 0;
231
+ documents.push(Object.freeze({
232
+ path: current.path,
233
+ id,
234
+ depth: current.depth,
235
+ securedDocument: document,
236
+ proof
237
+ }));
238
+ }
239
+ const entries = Object.entries(document).sort(([left], [right]) => comparePaths(left, right));
240
+ for (let index = entries.length - 1; index >= 0; index--) {
241
+ const [key, value] = entries[index];
242
+ if (key === "proof" || key === "@context") continue;
243
+ pending.push({
244
+ value,
245
+ path: childPath(current.path, key),
246
+ depth: current.depth + 1
247
+ });
248
+ }
249
+ }
250
+ documents.sort((left, right) => right.depth - left.depth || comparePaths(left.path, right.path));
251
+ return documents;
252
+ }
253
+ function collectPortableObjects(snapshot) {
254
+ const objects = [];
255
+ const pending = [{
256
+ value: snapshot,
257
+ path: "",
258
+ depth: 0
259
+ }];
260
+ while (pending.length > 0) {
261
+ const current = pending.pop();
262
+ if (Array.isArray(current.value)) {
263
+ for (let index = current.value.length - 1; index >= 0; index--) pending.push({
264
+ value: current.value[index],
265
+ path: childPath(current.path, String(index)),
266
+ depth: current.depth + 1,
267
+ keyOf: current.keyOf
268
+ });
269
+ continue;
270
+ }
271
+ if (!isCompoundProofJsonObject(current.value)) continue;
272
+ const document = current.value;
273
+ const id = [document.id, document["@id"]].find(isPortableIdValue);
274
+ if (typeof id === "string") objects.push(Object.freeze({
275
+ path: current.path,
276
+ id,
277
+ depth: current.depth,
278
+ document,
279
+ ...current.keyOf == null ? {} : { keyOf: current.keyOf }
280
+ }));
281
+ const entries = Object.entries(document).sort(([left], [right]) => comparePaths(left, right));
282
+ for (let index = entries.length - 1; index >= 0; index--) {
283
+ const [key, value] = entries[index];
284
+ if (key === "proof" || key === "@context") continue;
285
+ pending.push({
286
+ value,
287
+ path: childPath(current.path, key),
288
+ depth: current.depth + 1,
289
+ ...typeof id === "string" && (key === "publicKey" || key === "assertionMethod") ? { keyOf: {
290
+ path: current.path,
291
+ member: key
292
+ } } : {}
293
+ });
294
+ }
295
+ }
296
+ objects.sort((left, right) => right.depth - left.depth || comparePaths(left.path, right.path));
297
+ return objects;
298
+ }
299
+ /**
300
+ * Determines whether a received JSON tree contains a portable object ID, i.e.,
301
+ * an `ap:` or `ap+ef61:` URI or an FEP-ef61 compatible identifier, outside
302
+ * a proof or context definition, without exceeding discovery limits.
303
+ *
304
+ * This is only an applicability check. It does not authenticate the input;
305
+ * callers must still use {@link verifyCompoundPortableObjectProofs} to obtain
306
+ * an atomic result from an immutable snapshot. `indeterminate` means the
307
+ * applicability scan itself reached a limit, so a caller must not treat the
308
+ * document as outside the compound profile.
309
+ *
310
+ * @internal
311
+ */
312
+ function inspectCompoundPortableObjectApplicability(json, limits) {
313
+ validateLimits(limits);
314
+ let byteLength = 0;
315
+ let mapCount = 0;
316
+ const pending = [{
317
+ iterator: [json].values(),
318
+ depth: 0
319
+ }];
320
+ const addByteLength = (length) => {
321
+ byteLength += length;
322
+ return byteLength <= limits.maxBytes;
323
+ };
324
+ while (pending.length > 0) {
325
+ const frame = pending[pending.length - 1];
326
+ const next = frame.iterator.next();
327
+ if (next.done) {
328
+ pending.pop();
329
+ if (byteLength > limits.maxBytes) return "indeterminate";
330
+ continue;
331
+ }
332
+ const value = next.value;
333
+ if (frame.depth > limits.maxDepth) return "indeterminate";
334
+ if (value === null) {
335
+ if (!addByteLength(4)) return "indeterminate";
336
+ } else if (typeof value === "boolean") {
337
+ if (!addByteLength(value ? 4 : 5)) return "indeterminate";
338
+ } else if (typeof value === "string" || typeof value === "number") {
339
+ if (!addByteLength(encodedLength(JSON.stringify(value)))) return "indeterminate";
340
+ } else if (Array.isArray(value)) {
341
+ if (!addByteLength(2 + Math.max(0, value.length - 1))) return "indeterminate";
342
+ pending.push({
343
+ iterator: value.values(),
344
+ depth: frame.depth + 1
345
+ });
346
+ } else if (isJsonMap(value)) {
347
+ mapCount++;
348
+ if (mapCount > limits.maxMaps) return "indeterminate";
349
+ if ([value.id, value["@id"]].some(isPortableIdValue)) return "present";
350
+ if (!addByteLength(2)) return "indeterminate";
351
+ const children = function* () {
352
+ let first = true;
353
+ for (const key in value) {
354
+ if (!Object.hasOwn(value, key)) continue;
355
+ if (key === "proof" || key === "@context") continue;
356
+ if (!first && !addByteLength(1)) return;
357
+ first = false;
358
+ if (!addByteLength(encodedLength(JSON.stringify(key)) + 1)) return;
359
+ yield value[key];
360
+ }
361
+ }();
362
+ pending.push({
363
+ iterator: children,
364
+ depth: frame.depth + 1
365
+ });
366
+ }
367
+ if (byteLength > limits.maxBytes) return "indeterminate";
368
+ }
369
+ return "absent";
370
+ }
371
+ /**
372
+ * Walks every JSON map in a finite, already-materialized JSON tree, depth
373
+ * first in insertion order, skipping the values of `proof` and `@context`
374
+ * members. A map reached a second time through a repeated reference is not
375
+ * visited again, so a cyclic input terminates. The walk stops as soon as
376
+ * the visitor returns a value other than `undefined`.
377
+ */
378
+ function findInJsonMaps(json, visit) {
379
+ const seen = /* @__PURE__ */ new Set();
380
+ const pending = [{
381
+ value: json,
382
+ path: ""
383
+ }];
384
+ while (pending.length > 0) {
385
+ const { value, path } = pending.pop();
386
+ if (typeof value !== "object" || value == null || seen.has(value)) continue;
387
+ seen.add(value);
388
+ if (Array.isArray(value)) {
389
+ for (let index = value.length - 1; index >= 0; index--) pending.push({
390
+ value: value[index],
391
+ path: childPath(path, String(index))
392
+ });
393
+ continue;
394
+ }
395
+ const map = value;
396
+ const result = visit(map, path);
397
+ if (result !== void 0) return result;
398
+ const keys = Object.keys(map);
399
+ for (let index = keys.length - 1; index >= 0; index--) {
400
+ const key = keys[index];
401
+ if (key === "proof" || key === "@context") continue;
402
+ pending.push({
403
+ value: map[key],
404
+ path: childPath(path, key)
405
+ });
406
+ }
407
+ }
408
+ }
409
+ /**
410
+ * Determines whether an outgoing JSON tree contains portable content that
411
+ * requires producer-side proof-shape checks: a map, outside proof and context
412
+ * values, whose direct `id` or `@id` is a portable ActivityPub URI or an
413
+ * FEP-ef61 compatible identifier.
414
+ *
415
+ * This deliberately differs from
416
+ * {@link inspectCompoundPortableObjectApplicability}: it applies no resource
417
+ * limits, so a portable map or an unsupported proof beyond the inbox limits
418
+ * is still found, and a large document without portable maps stays outside
419
+ * the compound-proof profile. The input must be a finite JSON tree that has
420
+ * already been materialized, such as a compact JSON-LD document about to be
421
+ * sent.
422
+ *
423
+ * @internal
424
+ */
425
+ function containsCompoundPortableObject(json) {
426
+ return findInJsonMaps(json, (map) => [map.id, map["@id"]].some(isPortableIdValue) ? true : void 0) ?? false;
427
+ }
428
+ /**
429
+ * Finds the first direct `proof` member whose value the map-local
430
+ * compound-proof profile does not accept, i.e., anything other than a single
431
+ * JSON map: a proof set, `null`, or a scalar. Like the inbox discovery, it
432
+ * does not look inside proof or context values.
433
+ *
434
+ * The input must be a finite, already-materialized JSON tree; see
435
+ * {@link containsCompoundPortableObject}.
436
+ *
437
+ * @returns The RFC 6901 JSON Pointer to the unsupported `proof` member, or
438
+ * `null` if every direct proof is a single JSON map.
439
+ * @internal
440
+ */
441
+ function findUnsupportedCompoundProofShape(json) {
442
+ return findInJsonMaps(json, (map, path) => Object.hasOwn(map, "proof") && !isJsonMap(map.proof) ? childPath(path, "proof") : void 0) ?? null;
443
+ }
444
+ /**
445
+ * Finds the first embedded map that carries a direct `proof` but not its own
446
+ * `@context`. The map-local compound-proof profile verifies each proof-bearing
447
+ * map as a self-contained document, so the inbox rejects such a map with
448
+ * `missingContext` whatever its proof. An object embedded with its captured
449
+ * signed representation, e.g., by {@link signObject}, always keeps its own
450
+ * `@context`; one rebuilt under its parent's context, e.g., an object parsed
451
+ * from a received document, never does, and its proof no longer covers what
452
+ * it has become anyway.
453
+ *
454
+ * The input must be a finite, already-materialized JSON tree; see
455
+ * {@link containsCompoundPortableObject}.
456
+ *
457
+ * @returns The RFC 6901 JSON Pointer to the map, or `null` if every embedded
458
+ * proof-bearing map has its own `@context`.
459
+ * @internal
460
+ */
461
+ function findEmbeddedProofWithoutContext(json) {
462
+ return findInJsonMaps(json, (map, path) => path !== "" && Object.hasOwn(map, "proof") && !Object.hasOwn(map, "@context") ? path : void 0) ?? null;
463
+ }
464
+ /**
465
+ * Discovers direct literal proof-bearing maps in an immutable JSON snapshot.
466
+ *
467
+ * Discovery is bounded and atomic. Unsupported proof shapes, non-JSON input,
468
+ * or a resource-limit failure return no partial candidate list. Proof
469
+ * configuration subtrees are validated and counted as JSON, but are not
470
+ * themselves searched for secured documents.
471
+ */
472
+ function discoverCompoundProofDocuments(json, limits) {
473
+ validateLimits(limits);
474
+ let inspected;
475
+ try {
476
+ inspected = inspectJsonTree(json, limits);
477
+ } catch {
478
+ return {
479
+ status: "unsupported",
480
+ reason: {
481
+ type: "invalidJsonTree",
482
+ path: ""
483
+ }
484
+ };
485
+ }
486
+ if ("status" in inspected) return inspected;
487
+ let snapshot;
488
+ try {
489
+ snapshot = structuredClone(json);
490
+ } catch {
491
+ return {
492
+ status: "unsupported",
493
+ reason: {
494
+ type: "invalidJsonTree",
495
+ path: ""
496
+ }
497
+ };
498
+ }
499
+ freezeJsonTree(snapshot);
500
+ return {
501
+ status: "ok",
502
+ snapshot,
503
+ documents: Object.freeze(collectDocuments(snapshot)),
504
+ statistics: Object.freeze({ ...inspected })
505
+ };
506
+ }
507
+ /**
508
+ * Verifies every direct literal proof found in one immutable JSON snapshot.
509
+ *
510
+ * Each current map is verified independently. Only its literal `proof`
511
+ * member is removed from its JCS input, so descendant proofs and proof aliases
512
+ * remain unchanged. This is the lower-level compound-document mechanism; it
513
+ * does not apply portable-object policy or authenticate an inbox activity.
514
+ *
515
+ * @internal
516
+ */
517
+ async function verifyCompoundProofDocuments(json, limits, options = {}) {
518
+ const discovered = discoverCompoundProofDocuments(json, limits);
519
+ if (discovered.status !== "ok") return discovered;
520
+ const documents = await verifyDiscoveredProofDocuments(discovered, options);
521
+ return {
522
+ status: "ok",
523
+ verified: documents.length > 0 && documents.every((document) => document.verified),
524
+ snapshot: discovered.snapshot,
525
+ documents,
526
+ statistics: discovered.statistics
527
+ };
528
+ }
529
+ async function verifyDiscoveredProofDocuments(discovered, options) {
530
+ const documents = await Promise.all(discovered.documents.map(async (document) => {
531
+ const metadata = {
532
+ path: document.path,
533
+ ...document.id == null ? {} : { id: document.id },
534
+ depth: document.depth
535
+ };
536
+ if (document.depth > 0 && !Object.hasOwn(document.securedDocument, "@context")) return Object.freeze({
537
+ ...metadata,
538
+ verified: false,
539
+ reason: { type: "missingContext" }
540
+ });
541
+ const inherited = options[verificationObservation];
542
+ let subjectId = null;
543
+ if (inherited != null && document.id != null) try {
544
+ subjectId = parseIri(document.id);
545
+ } catch {}
546
+ const key = await observeAttempt({ [verificationObservation]: inherited == null ? void 0 : {
547
+ ...inherited,
548
+ attempt: void 0,
549
+ check: void 0,
550
+ subject: {
551
+ id: subjectId,
552
+ pointer: document.path
553
+ }
554
+ } }, "objectIntegrity", (observation) => verifyMapLocalProof(document.securedDocument, {
555
+ ...options,
556
+ [verificationObservation]: inherited == null ? void 0 : observation
557
+ }), (key) => key != null);
558
+ return key == null ? Object.freeze({
559
+ ...metadata,
560
+ verified: false,
561
+ reason: { type: "invalidProof" }
562
+ }) : Object.freeze({
563
+ ...metadata,
564
+ verified: true,
565
+ key
566
+ });
567
+ }));
568
+ return Object.freeze(documents);
569
+ }
570
+ /**
571
+ * Applies FEP-ef61 portable-object policy to every portable map in a compound
572
+ * snapshot without changing the map-local cryptographic inputs.
573
+ *
574
+ * @internal
575
+ */
576
+ async function verifyCompoundPortableObjectProofs(json, limits, options = {}) {
577
+ const discovered = discoverCompoundProofDocuments(json, limits);
578
+ if (discovered.status !== "ok") return discovered;
579
+ const proofs = await verifyDiscoveredProofDocuments(discovered, options);
580
+ const proofByPath = new Map(proofs.map((proof) => [proof.path, proof]));
581
+ const verifyPortableObject = async (object) => {
582
+ const metadata = {
583
+ path: object.path,
584
+ id: object.id,
585
+ depth: object.depth
586
+ };
587
+ if (object.depth > 0 && !Object.hasOwn(object.document, "@context")) return Object.freeze({
588
+ ...metadata,
589
+ verified: false,
590
+ reason: { type: "missingContext" }
591
+ });
592
+ const proof = proofByPath.get(object.path);
593
+ const key = proof?.verified === true ? proof.key : null;
594
+ try {
595
+ const policy = await verifyPortableObjectProofPolicy(object.document, key, options);
596
+ return policy.verified ? Object.freeze({
597
+ ...metadata,
598
+ id: policy.objectId,
599
+ verified: true,
600
+ keys: policy.keys
601
+ }) : Object.freeze({
602
+ ...metadata,
603
+ verified: false,
604
+ reason: policy.reason
605
+ });
606
+ } catch {
607
+ return Object.freeze({
608
+ ...metadata,
609
+ verified: false,
610
+ reason: { type: "invalidPortableObject" }
611
+ });
612
+ }
613
+ };
614
+ const collected = collectPortableObjects(discovered.snapshot);
615
+ const objectByPath = new Map(collected.map((object) => [object.path, object]));
616
+ const isKeyCandidate = (object) => object.keyOf != null && !Object.hasOwn(object.document, "proof");
617
+ const results = /* @__PURE__ */ new Map();
618
+ for (const result of await Promise.all(collected.filter((object) => !isKeyCandidate(object)).map(verifyPortableObject))) results.set(result.path, result);
619
+ let projections = 0;
620
+ for (const object of collected) {
621
+ if (!isKeyCandidate(object)) continue;
622
+ const { path, member } = object.keyOf;
623
+ const parent = results.get(path);
624
+ const parentObject = objectByPath.get(path);
625
+ if (parent?.verified === true && parentObject != null && projections++ < MAX_KEY_PROJECTIONS && await isEmbeddedPortableKey(parentObject.document, member, object.document, parent.id)) continue;
626
+ results.set(object.path, await verifyPortableObject(object));
627
+ }
628
+ const portableObjects = Object.freeze(collected.flatMap((object) => {
629
+ const result = results.get(object.path);
630
+ return result == null ? [] : [result];
631
+ }));
632
+ return {
633
+ status: "ok",
634
+ verified: portableObjects.length > 0 && proofs.every((proof) => proof.verified) && portableObjects.every((object) => object.verified),
635
+ snapshot: discovered.snapshot,
636
+ proofs,
637
+ portableObjects,
638
+ statistics: discovered.statistics
639
+ };
640
+ }
641
+ /**
642
+ * The maximum number of embedded keys per compound document that are checked
643
+ * by {@link isEmbeddedPortableKey}, each of which expands a projection of its
644
+ * parent. Keys beyond it have to carry their own proofs.
645
+ */
646
+ const MAX_KEY_PROJECTIONS = 64;
647
+ const SECURITY = "https://w3id.org/security#";
648
+ const KEY_TYPES = /* @__PURE__ */ new Set([`${SECURITY}Key`, `${SECURITY}Multikey`]);
649
+ const KEY_PROPERTIES = /* @__PURE__ */ new Set([
650
+ "@id",
651
+ "@type",
652
+ `${SECURITY}owner`,
653
+ `${SECURITY}controller`,
654
+ `${SECURITY}publicKeyPem`,
655
+ `${SECURITY}publicKeyMultibase`
656
+ ]);
657
+ /**
658
+ * Tells whether an unsigned map embedded in a verified portable map is just
659
+ * a key of the portable object the parent is, e.g., a gateway key of
660
+ * a portable actor, which the parent's proof covers.
661
+ *
662
+ * The key's meaning depends on the contexts active where it sits, so it is
663
+ * decided on a projection of the parent that keeps only what those contexts
664
+ * depend on: the parent's `@context`, its types and ID, and the member that
665
+ * holds the key. That holds only if nothing else in the parent can change
666
+ * them, so both documents may only use contexts that define no keyword
667
+ * aliases and no scoped contexts; Fedify's preloaded contexts, the only
668
+ * remote ones the compound policy loads, alias just `id` and `type`.
669
+ *
670
+ * The projection must expand to the parent's verified ID with the key as the
671
+ * single value of `publicKey` or `assertionMethod`, and the key must be
672
+ * nothing but a `CryptographicKey` or `Multikey` whose ID is the parent's
673
+ * `ap:` or `ap+ef61:` URI, or its compatible identifier, plus a fragment,
674
+ * and whose owner or controller, if any, is the parent.
675
+ */
676
+ async function isEmbeddedPortableKey(parent, member, key, parentId) {
677
+ const parentPortableId = getCanonicalPortableIdOf(parentId);
678
+ if (parentPortableId == null) return false;
679
+ if (!isSimpleContext(parent["@context"]) || Object.hasOwn(key, "@context") && !isSimpleContext(key["@context"])) return false;
680
+ const literalIds = [key.id, key["@id"]].filter((id) => id !== void 0);
681
+ if (literalIds.length < 1 || literalIds.some((id) => id !== literalIds[0]) || typeof literalIds[0] !== "string" || !isKeyIdOf(literalIds[0], parentPortableId)) return false;
682
+ const projection = {};
683
+ for (const property of [
684
+ "@context",
685
+ "type",
686
+ "@type",
687
+ "id",
688
+ "@id"
689
+ ]) if (Object.hasOwn(parent, property)) projection[property] = parent[property];
690
+ projection[member] = key;
691
+ let expanded;
692
+ try {
693
+ expanded = await jsonld.expand(structuredClone(projection), {
694
+ documentLoader: getNormalizationContextLoader(preloadedOnlyDocumentLoader),
695
+ keepFreeFloatingNodes: true
696
+ });
697
+ } catch {
698
+ return false;
699
+ }
700
+ if (expanded.length !== 1 || hasGraphKeyword(expanded)) return false;
701
+ const node = expanded[0];
702
+ if (!isJsonMap(node) || typeof node["@id"] !== "string" || getCanonicalPortableIdOf(node["@id"]) !== parentPortableId) return false;
703
+ const properties = Object.keys(node).filter((property) => property !== "@id" && property !== "@type");
704
+ if (properties.length !== 1 || properties[0] !== `${SECURITY}${member}`) return false;
705
+ const values = node[properties[0]];
706
+ if (!Array.isArray(values) || values.length !== 1) return false;
707
+ const expandedKey = values[0];
708
+ if (!isJsonMap(expandedKey) || expandedKey["@id"] !== literalIds[0] || Object.keys(expandedKey).some((property) => !KEY_PROPERTIES.has(property))) return false;
709
+ const types = expandedKey["@type"];
710
+ if (types !== void 0 && (!Array.isArray(types) || types.some((type) => typeof type !== "string" || !KEY_TYPES.has(type)))) return false;
711
+ const pem = expandedKey[`${SECURITY}publicKeyPem`];
712
+ const multibase = expandedKey[`${SECURITY}publicKeyMultibase`];
713
+ if (pem === void 0 && multibase === void 0) return false;
714
+ if (pem !== void 0 && !isSingleStringValue(pem)) return false;
715
+ if (multibase !== void 0 && !isSingleStringValue(multibase, `${SECURITY}multibase`)) return false;
716
+ for (const property of [`${SECURITY}owner`, `${SECURITY}controller`]) {
717
+ const owners = expandedKey[property];
718
+ if (owners === void 0) continue;
719
+ if (!Array.isArray(owners) || owners.length !== 1 || !isJsonMap(owners[0]) || Object.keys(owners[0]).length !== 1 || typeof owners[0]["@id"] !== "string" || getCanonicalPortableIdOf(owners[0]["@id"]) !== parentPortableId) return false;
720
+ }
721
+ return true;
722
+ }
723
+ function getCanonicalPortableIdOf(id) {
724
+ try {
725
+ return getCanonicalPortableId(parseIri(id));
726
+ } catch (error) {
727
+ if (error instanceof TypeError) return null;
728
+ throw error;
729
+ }
730
+ }
731
+ /**
732
+ * Checks whether an ID is the given portable object's `ap:` or `ap+ef61:`
733
+ * URI, or its compatible identifier, plus a non-empty fragment. The ID is
734
+ * compared as it is written, so an ID that URL parsing would turn into
735
+ * the portable object's, e.g., through dot segments, is not one of its keys.
736
+ */
737
+ function isKeyIdOf(id, portableId) {
738
+ const canonical = getRawCanonicalPortableId(id);
739
+ if (canonical == null) return false;
740
+ const hash = canonical.indexOf("#");
741
+ return hash >= 0 && hash < canonical.length - 1 && canonical.slice(0, hash) === portableId;
742
+ }
743
+ /**
744
+ * Checks whether a `@context` value defines no keyword aliases and no scoped
745
+ * contexts: only references to contexts and term definitions that map terms
746
+ * to IRIs, optionally with an `@id` or `@vocab` type coercion or a datatype.
747
+ */
748
+ function isSimpleContext(context) {
749
+ if (typeof context === "string") return true;
750
+ if (Array.isArray(context)) return context.every(isSimpleContext);
751
+ if (context === void 0 || !isCompoundProofJsonObject(context)) return false;
752
+ return Object.entries(context).every(([term, definition]) => {
753
+ if (term.startsWith("@")) return false;
754
+ if (typeof definition === "string") return !definition.startsWith("@");
755
+ if (!isCompoundProofJsonObject(definition)) return false;
756
+ const { "@id": id, "@type": type, ...rest } = definition;
757
+ return Object.keys(rest).length < 1 && typeof id === "string" && !id.startsWith("@") && (type === void 0 || type === "@id" || type === "@vocab" || typeof type === "string" && !type.startsWith("@"));
758
+ });
759
+ }
760
+ function hasGraphKeyword(value) {
761
+ if (Array.isArray(value)) return value.some(hasGraphKeyword);
762
+ if (!isJsonMap(value)) return false;
763
+ return Object.entries(value).some(([key, child]) => key === "@graph" || key === "@included" || key === "@reverse" || hasGraphKeyword(child));
764
+ }
765
+ /**
766
+ * Checks whether expanded values are a single plain string, optionally with
767
+ * the given datatype.
768
+ */
769
+ function isSingleStringValue(values, datatype) {
770
+ if (!Array.isArray(values) || values.length !== 1) return false;
771
+ const value = values[0];
772
+ if (!isJsonMap(value) || typeof value["@value"] !== "string") return false;
773
+ const keys = Object.keys(value);
774
+ return keys.length === 1 || datatype != null && keys.length === 2 && value["@type"] === datatype;
775
+ }
776
+ /**
777
+ * FEP-2277 core types that FEP-ef61 does not require to carry proofs.
778
+ */
779
+ const UNSECURED_CORE_TYPES = /* @__PURE__ */ new Set([
780
+ "collection",
781
+ "link",
782
+ "verificationMethod",
783
+ "publicKey"
784
+ ]);
785
+ /**
786
+ * Applies the FEP-ef61 proof policy, under the map-local compound-proof
787
+ * profile, to every portable object embedded in a document that a gateway is
788
+ * about to serve, e.g., the items of a portable collection page. The root
789
+ * map itself is not checked, as the caller built it.
790
+ *
791
+ * Each map below the root whose direct `id` or `@id` is a portable ID or
792
+ * a compatible identifier is checked independently of the others:
793
+ *
794
+ * - A map with a direct `proof` is a separately secured document: the proof
795
+ * has to be a single map, the map has to carry its own `@context`, and
796
+ * the proof has to verify over the map exactly as it is served, with only
797
+ * its own proof removed, and be made with a key of the DID in the map's
798
+ * ID, as the map-local compound-proof profile requires.
799
+ * - A map without a proof has to be a collection, a link, or a key, which
800
+ * FEP-ef61 does not require to be secured. It is classified under its
801
+ * effective context, i.e., the `@context` values of its ancestors and
802
+ * itself, which therefore may only use the simple, preloaded contexts
803
+ * that the compound-proof policy accepts.
804
+ *
805
+ * Maps that merely refer to a portable object, i.e., have no member other
806
+ * than `id` or `@id`, and maps with non-portable IDs, are neither checked nor
807
+ * verified. Every `@context` in the document, however, has to be simple, and
808
+ * every portable object that the expanded document describes has to be
809
+ * a map found this way, so that no context can hide a portable object from
810
+ * this check, e.g., by aliasing `@id` or redefining `proof`. The input must be a finite, already-materialized JSON tree, such
811
+ * as the compact JSON-LD document that is about to be served.
812
+ *
813
+ * @param json The document to check.
814
+ * @param options The options for verifying the proofs. Proofs whose
815
+ * verification methods are not `did:key` DID URLs may make
816
+ * the document loader fetch DID documents.
817
+ * @returns The result, which names the first refused map, if any.
818
+ * @internal
819
+ */
820
+ async function verifyServedPortableObjects(json, options = {}) {
821
+ json = JSON.parse(JSON.stringify(json));
822
+ const collected = collectServedPortableMaps(json);
823
+ if (!("maps" in collected)) return collected;
824
+ const maps = collected.maps;
825
+ const discovered = /* @__PURE__ */ new Map();
826
+ const ids = maps.map((candidate) => candidate.id);
827
+ const rootId = isJsonMap(json) ? [json.id, json["@id"]].find(isPortableIdValue) : void 0;
828
+ if (rootId != null) ids.push(rootId);
829
+ for (const id of ids) discovered.set(id, (discovered.get(id) ?? 0) + 1);
830
+ let hidden;
831
+ try {
832
+ hidden = findHiddenPortableObject(await jsonld.expand(json, { documentLoader: preloadedOnlyDocumentLoader }), discovered);
833
+ } catch {
834
+ return {
835
+ verified: false,
836
+ path: "",
837
+ id: "",
838
+ reason: "unsupportedContext"
839
+ };
840
+ }
841
+ if (hidden != null) return {
842
+ verified: false,
843
+ path: "",
844
+ id: hidden,
845
+ reason: "hiddenPortableObject"
846
+ };
847
+ for (const candidate of maps) {
848
+ const reason = await checkServedPortableMap(candidate, options);
849
+ if (reason != null) return {
850
+ verified: false,
851
+ path: candidate.path,
852
+ id: candidate.id,
853
+ reason
854
+ };
855
+ }
856
+ return { verified: true };
857
+ }
858
+ /**
859
+ * Finds a portable object that an expanded JSON-LD document describes, i.e.,
860
+ * a node with a portable `@id` and other members, more times than the given
861
+ * numbers of maps found for its ID.
862
+ */
863
+ function findHiddenPortableObject(expanded, discovered) {
864
+ const remaining = new Map(discovered);
865
+ const pending = [expanded];
866
+ while (pending.length > 0) {
867
+ const value = pending.pop();
868
+ if (Array.isArray(value)) {
869
+ pending.push(...value);
870
+ continue;
871
+ }
872
+ if (!isJsonMap(value)) continue;
873
+ const id = value["@id"];
874
+ if (isPortableIdValue(id) && Object.keys(value).some((key) => key !== "@id")) {
875
+ const count = remaining.get(id) ?? 0;
876
+ if (count < 1) return id;
877
+ remaining.set(id, count - 1);
878
+ }
879
+ pending.push(...Object.values(value));
880
+ }
881
+ }
882
+ /**
883
+ * Checks whether a context uses only preloaded remote contexts and simple
884
+ * term definitions, so that the context in effect for a map can be told from
885
+ * the contexts of its ancestors.
886
+ */
887
+ function isCheckableContext(context) {
888
+ return (Array.isArray(context) ? context : [context]).every((value) => typeof value === "string" ? Object.hasOwn(preloadedContexts, value) : !Array.isArray(value) && isSimpleContext(value));
889
+ }
890
+ /**
891
+ * Checks whether every remote context that a context refers to is preloaded,
892
+ * so that all of its definitions are known.
893
+ */
894
+ function hasOnlyPreloadedRemoteContexts(context) {
895
+ return (Array.isArray(context) ? context : [context]).every((value) => typeof value === "string" ? Object.hasOwn(preloadedContexts, value) : !Array.isArray(value));
896
+ }
897
+ /**
898
+ * Checks whether a map or any map below it, outside proof and context values,
899
+ * has a portable `id` or `@id` and other members.
900
+ */
901
+ function hasLiteralPortableObject(json) {
902
+ return findInJsonMaps(json, (map) => [map.id, map["@id"]].some(isPortableIdValue) && Object.keys(map).some((key) => key !== "id" && key !== "@id") ? true : void 0) ?? false;
903
+ }
904
+ function collectServedPortableMaps(json) {
905
+ const maps = [];
906
+ const seen = /* @__PURE__ */ new Set();
907
+ const pending = [{
908
+ value: json,
909
+ path: "",
910
+ depth: 0,
911
+ contexts: []
912
+ }];
913
+ while (pending.length > 0) {
914
+ const { value, path, depth, contexts } = pending.pop();
915
+ if (typeof value !== "object" || value == null || seen.has(value)) continue;
916
+ seen.add(value);
917
+ if (Array.isArray(value)) {
918
+ for (let index = value.length - 1; index >= 0; index--) pending.push({
919
+ value: value[index],
920
+ path: childPath(path, String(index)),
921
+ depth: depth + 1,
922
+ contexts
923
+ });
924
+ continue;
925
+ }
926
+ const map = value;
927
+ const hasContext = Object.hasOwn(map, "@context");
928
+ if (hasContext && !isCheckableContext(map["@context"])) {
929
+ if (hasOnlyPreloadedRemoteContexts(map["@context"]) && !hasLiteralPortableObject(map)) continue;
930
+ const id = [map.id, map["@id"]].find((v) => typeof v === "string");
931
+ return {
932
+ verified: false,
933
+ path,
934
+ id: typeof id === "string" ? id : "",
935
+ reason: "unsupportedContext"
936
+ };
937
+ }
938
+ const mapContexts = hasContext ? [...contexts, map["@context"]] : contexts;
939
+ const id = [map.id, map["@id"]].find(isPortableIdValue);
940
+ if (depth > 0 && id != null && Object.keys(map).some((key) => key !== "id" && key !== "@id")) maps.push({
941
+ path,
942
+ id,
943
+ map,
944
+ contexts: mapContexts
945
+ });
946
+ const keys = Object.keys(map);
947
+ for (let index = keys.length - 1; index >= 0; index--) {
948
+ const key = keys[index];
949
+ if (key === "proof" || key === "@context") continue;
950
+ pending.push({
951
+ value: map[key],
952
+ path: childPath(path, key),
953
+ depth: depth + 1,
954
+ contexts: mapContexts
955
+ });
956
+ }
957
+ }
958
+ maps.sort((left, right) => comparePaths(left.path, right.path));
959
+ return { maps };
960
+ }
961
+ async function checkServedPortableMap({ map, contexts }, options) {
962
+ if (Object.hasOwn(map, "proof")) {
963
+ if (!isJsonMap(map.proof)) return "unsupportedProofShape";
964
+ if (!Object.hasOwn(map, "@context")) return "missingContext";
965
+ try {
966
+ const policy = await verifyPortableObjectProofPolicy(map, await verifyMapLocalProof(map, options), options);
967
+ return policy.verified ? null : policy.reason.type;
968
+ } catch (error) {
969
+ if (error instanceof TypeError) return "invalidPortableObject";
970
+ throw error;
971
+ }
972
+ }
973
+ const flattened = contexts.flatMap((context) => Array.isArray(context) ? context : [context]);
974
+ if (flattened.length < 1 || !isSimpleContext(flattened)) return "unsupportedContext";
975
+ let expanded;
976
+ try {
977
+ expanded = await jsonld.expand({
978
+ ...map,
979
+ "@context": flattened
980
+ }, { documentLoader: preloadedOnlyDocumentLoader });
981
+ } catch {
982
+ return "unsupportedContext";
983
+ }
984
+ if (expanded.length !== 1 || !isJsonMap(expanded[0])) return "invalidPortableObject";
985
+ const type = classifyFep2277CoreType(expanded[0]);
986
+ return UNSECURED_CORE_TYPES.has(type) ? null : "missingProof";
987
+ }
988
+ //#endregion
989
+ export { inspectCompoundPortableObjectApplicability as a, verifyServedPortableObjects as c, findUnsupportedCompoundProofShape as i, discoverCompoundProofDocuments as n, verifyCompoundPortableObjectProofs as o, findEmbeddedProofWithoutContext as r, verifyCompoundProofDocuments as s, containsCompoundPortableObject as t };