@fedify/fedify 2.1.12 → 2.1.14

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 (96) hide show
  1. package/dist/{builder-D9hlyCx5.mjs → builder-BBifZZ5G.mjs} +3 -3
  2. package/dist/{chunk-nlSIicah.js → chunk-CRNNMoPX.js} +2 -2
  3. package/dist/{client-z-8dc-e1.d.cts → client-CAM_bQXx.d.cts} +1 -0
  4. package/dist/{client-AtlibPOU.d.ts → client-CSddvgWN.d.ts} +1 -2
  5. package/dist/compat/mod.d.cts +2 -1
  6. package/dist/compat/mod.d.ts +2 -3
  7. package/dist/compat/mod.js +3 -3
  8. package/dist/compat/transformers.test.mjs +1 -1
  9. package/dist/{context-C0fssarc.d.ts → context-_l9t359k.d.ts} +53 -12
  10. package/dist/{context-LmrZPwti.d.cts → context-o22vjzS2.d.cts} +53 -10
  11. package/dist/{deno-Ddb3P7G7.mjs → deno-bqnW1QlD.mjs} +1 -1
  12. package/dist/{docloader-CK8tewt1.mjs → docloader-D-Fu0kBF.mjs} +2 -2
  13. package/dist/federation/builder.test.mjs +1 -1
  14. package/dist/federation/handler.test.mjs +1361 -41
  15. package/dist/federation/idempotency.test.mjs +2 -2
  16. package/dist/federation/inbox.test.mjs +1 -1
  17. package/dist/federation/middleware.test.mjs +1588 -84
  18. package/dist/federation/mod.cjs +1 -1
  19. package/dist/federation/mod.d.cts +4 -3
  20. package/dist/federation/mod.d.ts +4 -5
  21. package/dist/federation/mod.js +2 -2
  22. package/dist/federation/retry.test.mjs +1 -1
  23. package/dist/federation/send.test.mjs +3 -3
  24. package/dist/federation/temporal.test.d.mts +2 -0
  25. package/dist/federation/temporal.test.mjs +71 -0
  26. package/dist/federation/webfinger.test.mjs +1 -1
  27. package/dist/{http-CrGuipxe.d.cts → http-BDZeS5om.d.ts} +2 -1
  28. package/dist/{http-aQzN9Ayi.d.ts → http-C87EWkO0.d.cts} +2 -3
  29. package/dist/{http-CmDErAnD.cjs → http-CEXtUHix.cjs} +57 -9
  30. package/dist/{http-qKyJKSnF.js → http-Cw1EorUA.js} +58 -10
  31. package/dist/{http-DRDpStn2.mjs → http-DvhFwy2E.mjs} +58 -10
  32. package/dist/{inbox-DTuPJEma.mjs → inbox-7b3VYnec.mjs} +7 -4
  33. package/dist/{key-WSedbAA-.mjs → key-B3OBz6A_.mjs} +1 -1
  34. package/dist/{kv-CbLNp3zQ.d.cts → kv-D6hNiMTK.d.ts} +1 -0
  35. package/dist/{kv-cache-9cdQDeT0.cjs → kv-cache-BIapFiSV.cjs} +2 -2
  36. package/dist/{kv-cache-DNNRFY8u.js → kv-cache-C6nynGhh.js} +2 -2
  37. package/dist/{kv-cache-B01V7s3h.mjs → kv-cache-FsT1ljhs.mjs} +1 -1
  38. package/dist/{kv-GFYnFoOl.d.ts → kv-gJ8LYbxX.d.cts} +1 -3
  39. package/dist/ld-DTxYKZTT.mjs +550 -0
  40. package/dist/{middleware-UThUK30x.js → middleware-0vceVN10.js} +407 -82
  41. package/dist/{middleware-DFcgN1cH.cjs → middleware-BcbU5lKS.cjs} +406 -80
  42. package/dist/{middleware-8P6Z15Wq.cjs → middleware-DUT--Qen.cjs} +1 -1
  43. package/dist/{middleware-DDsnRvFC.mjs → middleware-DmT9t0BJ.mjs} +317 -84
  44. package/dist/{middleware-C2c1rlmE.mjs → middleware-Li_g_vqz.mjs} +1 -1
  45. package/dist/{mod-CnwZoDzx.d.ts → mod-0dF0OeGw.d.cts} +2 -3
  46. package/dist/{mod-B505FZBC.d.cts → mod-BTWFtZwa.d.ts} +4 -3
  47. package/dist/{mod-Cgpc49fA.d.cts → mod-Bv1elC-b.d.ts} +2 -1
  48. package/dist/{mod-DoJBjjnO.d.cts → mod-CjiK2zrW.d.ts} +3 -2
  49. package/dist/{mod-DvxszxXC.d.ts → mod-D5YlzzP3.d.cts} +3 -4
  50. package/dist/{mod-D7PAuO6k.d.ts → mod-dxOnKp2Y.d.cts} +4 -5
  51. package/dist/mod.cjs +4 -4
  52. package/dist/mod.d.cts +9 -8
  53. package/dist/mod.d.ts +9 -10
  54. package/dist/mod.js +9 -9
  55. package/dist/nodeinfo/handler.test.mjs +1 -1
  56. package/dist/nodeinfo/mod.d.cts +2 -1
  57. package/dist/nodeinfo/mod.d.ts +2 -3
  58. package/dist/nodeinfo/mod.js +3 -3
  59. package/dist/otel/mod.d.cts +2 -1
  60. package/dist/otel/mod.d.ts +2 -3
  61. package/dist/otel/mod.js +2 -2
  62. package/dist/{owner-CptqhsOy.d.cts → owner-CnngXDNJ.d.ts} +2 -1
  63. package/dist/{owner-74ARJ5TL.d.ts → owner-DEvZuyOE.d.cts} +2 -3
  64. package/dist/{owner-BceqCVOs.mjs → owner-DzcnuWYo.mjs} +2 -2
  65. package/dist/{proof-4NUQlbQV.js → proof-CtTT8A7y.js} +299 -5
  66. package/dist/{proof-BojC8L94.mjs → proof-DmVGi7mI.mjs} +2 -2
  67. package/dist/{proof-kuTIVfyZ.cjs → proof-JEoW3ODq.cjs} +345 -3
  68. package/dist/runtime/mod.d.cts +1 -0
  69. package/dist/runtime/mod.d.ts +1 -2
  70. package/dist/runtime/mod.js +3 -3
  71. package/dist/{send-Buz5Gy8x.mjs → send-5ulOKVCW.mjs} +2 -2
  72. package/dist/sig/http.test.mjs +120 -2
  73. package/dist/sig/key.test.mjs +1 -1
  74. package/dist/sig/ld.test.mjs +558 -2
  75. package/dist/sig/mod.cjs +2 -2
  76. package/dist/sig/mod.d.cts +4 -3
  77. package/dist/sig/mod.d.ts +4 -5
  78. package/dist/sig/mod.js +4 -4
  79. package/dist/sig/owner.test.mjs +1 -1
  80. package/dist/sig/proof.test.mjs +1 -1
  81. package/dist/temporal-CmJkd-25.mjs +95 -0
  82. package/dist/testing/mod.d.mts +48 -6
  83. package/dist/{transformers-ve6e2xcg.js → transformers-BGMIq1cs.js} +2 -2
  84. package/dist/{types-hvL8ElAs.js → types-CAY3OdLq.js} +2 -2
  85. package/dist/utils/docloader.test.mjs +2 -2
  86. package/dist/utils/kv-cache.test.mjs +1 -1
  87. package/dist/utils/mod.cjs +1 -1
  88. package/dist/utils/mod.d.cts +2 -1
  89. package/dist/utils/mod.d.ts +2 -3
  90. package/dist/utils/mod.js +3 -3
  91. package/dist/vocab/mod.d.cts +1 -0
  92. package/dist/vocab/mod.d.ts +1 -2
  93. package/dist/vocab/mod.js +2 -2
  94. package/package.json +7 -7
  95. package/dist/ld-OBpaLfHR.mjs +0 -256
  96. /package/dist/{retry-B_E3V_Dx.mjs → retry-CJL0poaU.mjs} +0 -0
@@ -4,13 +4,15 @@ globalThis.addEventListener = () => {};
4
4
  import { n as createRequestContext, t as createInboxContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-Duiq_YC9.mjs";
7
+ import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
7
8
  import { t as assert } from "../assert-ddO5KLpe.mjs";
8
9
  import { r as parseAcceptSignature } from "../accept-Dd__NiUL.mjs";
9
- import { s as signRequest } from "../http-DRDpStn2.mjs";
10
+ import { s as signRequest } from "../http-DvhFwy2E.mjs";
10
11
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
12
+ import { a as compactJsonLd, f as signJsonLd } from "../ld-DTxYKZTT.mjs";
11
13
  import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
12
- import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-DDsnRvFC.mjs";
13
- import { t as InboxListenerSet } from "../inbox-DTuPJEma.mjs";
14
+ import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-DmT9t0BJ.mjs";
15
+ import { t as InboxListenerSet } from "../inbox-7b3VYnec.mjs";
14
16
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
15
17
  import { Create, Note, Person } from "@fedify/vocab";
16
18
  import { FetchError } from "@fedify/vocab-runtime";
@@ -885,6 +887,11 @@ test("handleInbox()", async () => {
885
887
  if (identifier !== "someone") return null;
886
888
  return new Person({ name: "Someone" });
887
889
  };
890
+ const restrictiveContextLoader = async (resource) => {
891
+ const url = new URL(resource).href;
892
+ if (url === "https://www.w3.org/ns/activitystreams" || url === "https://w3id.org/identity/v1") return await mockDocumentLoader(url);
893
+ throw new Error(`Unexpected context: ${url}`);
894
+ };
888
895
  const inboxOptions = {
889
896
  kv: new MemoryKvStore(),
890
897
  kvPrefixes: {
@@ -954,6 +961,49 @@ test("handleInbox()", async () => {
954
961
  });
955
962
  assertEquals(onNotFoundCalled, null);
956
963
  assertEquals(response.status, 401);
964
+ const malformedProofCreatedRequest = new Request("https://example.com/", {
965
+ method: "POST",
966
+ body: JSON.stringify({
967
+ "@context": ["https://www.w3.org/ns/activitystreams", "https://w3id.org/security/data-integrity/v1"],
968
+ id: "https://example.com/activities/invalid-proof-created",
969
+ type: "Create",
970
+ actor: "https://example.com/person2",
971
+ object: {
972
+ id: "https://example.com/notes/invalid-proof-created",
973
+ type: "Note",
974
+ attributedTo: "https://example.com/person2",
975
+ content: "Hello, world!"
976
+ },
977
+ proof: {
978
+ type: "DataIntegrityProof",
979
+ cryptosuite: "eddsa-jcs-2022",
980
+ verificationMethod: "https://example.com/person2#main-key",
981
+ proofPurpose: "assertionMethod",
982
+ created: { "@value": "not-a-date" },
983
+ proofValue: "zLaewdp4H9kqtwyrLatK4cjY5oRHwVcw4gibPSUDYDMhi4M49v8pcYk3ZB6D69dNpAPbUmY8ocuJ3m9KhKJEEg7z"
984
+ }
985
+ })
986
+ });
987
+ const malformedProofCreatedContext = createRequestContext({
988
+ federation,
989
+ request: malformedProofCreatedRequest,
990
+ url: new URL(malformedProofCreatedRequest.url),
991
+ data: void 0,
992
+ documentLoader: mockDocumentLoader,
993
+ contextLoader: mockDocumentLoader
994
+ });
995
+ response = await handleInbox(malformedProofCreatedRequest, {
996
+ recipient: null,
997
+ context: malformedProofCreatedContext,
998
+ inboxContextFactory(_activity) {
999
+ return createInboxContext({
1000
+ ...malformedProofCreatedContext,
1001
+ clone: void 0
1002
+ });
1003
+ },
1004
+ ...inboxOptions
1005
+ });
1006
+ assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
957
1007
  onNotFoundCalled = null;
958
1008
  const signedRequest = await signRequest(unsignedRequest.clone(), rsaPrivateKey3, rsaPublicKey3.id);
959
1009
  const signedContext = createRequestContext({
@@ -976,85 +1026,1355 @@ test("handleInbox()", async () => {
976
1026
  });
977
1027
  assertEquals(onNotFoundCalled, null);
978
1028
  assertEquals([response.status, await response.text()], [202, ""]);
979
- response = await handleInbox(signedRequest, {
980
- recipient: "someone",
981
- context: signedContext,
1029
+ const ldSignedRequest = new Request("https://example.com/", {
1030
+ method: "POST",
1031
+ body: JSON.stringify(await signJsonLd({
1032
+ "@context": [
1033
+ "https://www.w3.org/ns/activitystreams",
1034
+ "https://w3id.org/identity/v1",
1035
+ "https://w3id.org/security/v1",
1036
+ "https://w3id.org/security/data-integrity/v1"
1037
+ ],
1038
+ id: "https://example.com/activities/ld-signed",
1039
+ type: "Create",
1040
+ actor: "https://example.com/person2",
1041
+ object: {
1042
+ id: "https://example.com/notes/ld-signed",
1043
+ type: "Note",
1044
+ attributedTo: "https://example.com/person2",
1045
+ content: "Hello, world!"
1046
+ }
1047
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
1048
+ });
1049
+ const ldSignedContext = createRequestContext({
1050
+ federation,
1051
+ request: ldSignedRequest,
1052
+ url: new URL(ldSignedRequest.url),
1053
+ data: void 0,
1054
+ documentLoader: mockDocumentLoader,
1055
+ contextLoader: restrictiveContextLoader
1056
+ });
1057
+ response = await handleInbox(ldSignedRequest, {
1058
+ recipient: null,
1059
+ context: ldSignedContext,
982
1060
  inboxContextFactory(_activity) {
983
1061
  return createInboxContext({
984
- ...unsignedContext,
985
- clone: void 0,
986
- recipient: "someone"
1062
+ ...ldSignedContext,
1063
+ clone: void 0
987
1064
  });
988
1065
  },
989
1066
  ...inboxOptions
990
1067
  });
991
1068
  assertEquals(onNotFoundCalled, null);
992
1069
  assertEquals([response.status, await response.text()], [202, ""]);
993
- response = await handleInbox(unsignedRequest, {
1070
+ const remoteContextUrl = "https://remote.example/contexts/ext";
1071
+ let failRemoteContextOnce = true;
1072
+ const flakyContextLoader = async (resource) => {
1073
+ const url = new URL(resource).href;
1074
+ if (url === remoteContextUrl) {
1075
+ if (failRemoteContextOnce) {
1076
+ failRemoteContextOnce = false;
1077
+ throw new Error(`Unexpected context: ${url}`);
1078
+ }
1079
+ return {
1080
+ contextUrl: null,
1081
+ documentUrl: url,
1082
+ document: { "@context": { ext: "https://example.com/ext" } }
1083
+ };
1084
+ }
1085
+ return await mockDocumentLoader(url);
1086
+ };
1087
+ const httpSignedLdBody = {
1088
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1089
+ id: "https://example.com/activities/http-signed-ld",
1090
+ type: "Create",
1091
+ actor: "https://example.com/person2",
1092
+ ext: "preserve-me",
1093
+ object: {
1094
+ id: "https://example.com/notes/http-signed-ld",
1095
+ type: "Note",
1096
+ attributedTo: "https://example.com/person2",
1097
+ content: "Hello, world!"
1098
+ },
1099
+ signature: {
1100
+ type: "RsaSignature2017",
1101
+ creator: rsaPublicKey3.id.href,
1102
+ created: "2024-01-01T00:00:00Z",
1103
+ signatureValue: "bogus"
1104
+ }
1105
+ };
1106
+ const httpSignedLdRequest = await signRequest(new Request("https://example.com/", {
1107
+ method: "POST",
1108
+ body: JSON.stringify(httpSignedLdBody)
1109
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1110
+ const httpSignedLdContext = createRequestContext({
1111
+ federation,
1112
+ request: httpSignedLdRequest,
1113
+ url: new URL(httpSignedLdRequest.url),
1114
+ data: void 0,
1115
+ documentLoader: mockDocumentLoader,
1116
+ contextLoader: flakyContextLoader
1117
+ });
1118
+ response = await handleInbox(httpSignedLdRequest, {
994
1119
  recipient: null,
995
- context: unsignedContext,
1120
+ context: httpSignedLdContext,
996
1121
  inboxContextFactory(_activity) {
997
1122
  return createInboxContext({
998
- ...unsignedContext,
1123
+ ...httpSignedLdContext,
999
1124
  clone: void 0
1000
1125
  });
1001
1126
  },
1002
- ...inboxOptions,
1003
- skipSignatureVerification: true
1127
+ ...inboxOptions
1004
1128
  });
1005
1129
  assertEquals(onNotFoundCalled, null);
1006
- assertEquals(response.status, 202);
1007
- response = await handleInbox(unsignedRequest, {
1008
- recipient: "someone",
1009
- context: unsignedContext,
1130
+ assertEquals([response.status, await response.text()], [202, ""]);
1131
+ const ldSignedOnlyBody = await signJsonLd({
1132
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1133
+ id: "https://example.com/activities/ld-only-transient",
1134
+ type: "Create",
1135
+ actor: "https://example.com/person2",
1136
+ ext: "preserve-me",
1137
+ object: {
1138
+ id: "https://example.com/notes/ld-only-transient",
1139
+ type: "Note",
1140
+ attributedTo: "https://example.com/person2",
1141
+ content: "Hello, world!"
1142
+ }
1143
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
1144
+ const url = new URL(resource).href;
1145
+ if (url === remoteContextUrl) return {
1146
+ contextUrl: null,
1147
+ documentUrl: url,
1148
+ document: { "@context": { ext: "https://example.com/ext" } }
1149
+ };
1150
+ return await mockDocumentLoader(url);
1151
+ } });
1152
+ const malformedTemporalLdSignedBody = await signJsonLd({
1153
+ "@context": "https://www.w3.org/ns/activitystreams",
1154
+ id: "https://example.com/activities/ld-only-invalid-published",
1155
+ type: "Create",
1156
+ actor: "https://example.com/person2",
1157
+ published: { "@value": "not-a-date" },
1158
+ object: {
1159
+ id: "https://example.com/notes/ld-only-invalid-published",
1160
+ type: "Note",
1161
+ attributedTo: "https://example.com/person2",
1162
+ content: "Hello, world!"
1163
+ }
1164
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
1165
+ const malformedTemporalLdSignedRequest = new Request("https://example.com/", {
1166
+ method: "POST",
1167
+ body: JSON.stringify(malformedTemporalLdSignedBody)
1168
+ });
1169
+ const malformedTemporalLdSignedContext = createRequestContext({
1170
+ federation,
1171
+ request: malformedTemporalLdSignedRequest,
1172
+ url: new URL(malformedTemporalLdSignedRequest.url),
1173
+ data: void 0,
1174
+ documentLoader: mockDocumentLoader,
1175
+ contextLoader: mockDocumentLoader
1176
+ });
1177
+ response = await handleInbox(malformedTemporalLdSignedRequest, {
1178
+ recipient: null,
1179
+ context: malformedTemporalLdSignedContext,
1010
1180
  inboxContextFactory(_activity) {
1011
1181
  return createInboxContext({
1012
- ...unsignedContext,
1013
- clone: void 0,
1014
- recipient: "someone"
1182
+ ...malformedTemporalLdSignedContext,
1183
+ clone: void 0
1015
1184
  });
1016
1185
  },
1017
- ...inboxOptions,
1018
- skipSignatureVerification: true
1186
+ ...inboxOptions
1019
1187
  });
1020
- assertEquals(onNotFoundCalled, null);
1021
- assertEquals(response.status, 202);
1022
- const signedInvalidRequest = await signRequest(new Request("https://example.com/", {
1188
+ assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
1189
+ const malformedClosedLdSignedBody = await signJsonLd({
1190
+ "@context": "https://www.w3.org/ns/activitystreams",
1191
+ id: "https://example.com/questions/ld-only-invalid-closed",
1192
+ type: "Question",
1193
+ closed: "2024-02-31T00:00:00Z"
1194
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader });
1195
+ const malformedClosedLdSignedRequest = new Request("https://example.com/", {
1196
+ method: "POST",
1197
+ body: JSON.stringify(malformedClosedLdSignedBody)
1198
+ });
1199
+ const malformedClosedLdSignedContext = createRequestContext({
1200
+ federation,
1201
+ request: malformedClosedLdSignedRequest,
1202
+ url: new URL(malformedClosedLdSignedRequest.url),
1203
+ data: void 0,
1204
+ documentLoader: mockDocumentLoader,
1205
+ contextLoader: mockDocumentLoader
1206
+ });
1207
+ response = await handleInbox(malformedClosedLdSignedRequest, {
1208
+ recipient: null,
1209
+ context: malformedClosedLdSignedContext,
1210
+ inboxContextFactory(_activity) {
1211
+ return createInboxContext({
1212
+ ...malformedClosedLdSignedContext,
1213
+ clone: void 0
1214
+ });
1215
+ },
1216
+ ...inboxOptions
1217
+ });
1218
+ assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
1219
+ const malformedIriHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1023
1220
  method: "POST",
1024
1221
  body: JSON.stringify({
1025
- "@context": [
1026
- "https://www.w3.org/ns/activitystreams",
1027
- true,
1028
- 23
1029
- ],
1222
+ "@context": "https://www.w3.org/ns/activitystreams",
1223
+ id: "http://[",
1030
1224
  type: "Create",
1225
+ actor: "https://example.com/person2",
1031
1226
  object: {
1227
+ id: "https://example.com/notes/http-signed-invalid-iri",
1032
1228
  type: "Note",
1229
+ attributedTo: "https://example.com/person2",
1033
1230
  content: "Hello, world!"
1034
- },
1035
- actor: "https://example.com/users/alice"
1231
+ }
1036
1232
  })
1037
1233
  }), rsaPrivateKey3, rsaPublicKey3.id);
1038
- const signedInvalidContext = createRequestContext({
1234
+ const malformedIriHttpSignedContext = createRequestContext({
1039
1235
  federation,
1040
- request: signedInvalidRequest,
1041
- url: new URL(signedInvalidRequest.url),
1236
+ request: malformedIriHttpSignedRequest,
1237
+ url: new URL(malformedIriHttpSignedRequest.url),
1042
1238
  data: void 0,
1043
- documentLoader: mockDocumentLoader
1239
+ documentLoader: mockDocumentLoader,
1240
+ contextLoader: mockDocumentLoader
1044
1241
  });
1045
- response = await handleInbox(signedInvalidRequest, {
1242
+ response = await handleInbox(malformedIriHttpSignedRequest, {
1046
1243
  recipient: null,
1047
- context: signedContext,
1244
+ context: malformedIriHttpSignedContext,
1048
1245
  inboxContextFactory(_activity) {
1049
1246
  return createInboxContext({
1050
- ...signedInvalidContext,
1247
+ ...malformedIriHttpSignedContext,
1248
+ clone: void 0
1249
+ });
1250
+ },
1251
+ ...inboxOptions
1252
+ });
1253
+ assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
1254
+ const ldSignedOnlyRequest = new Request("https://example.com/", {
1255
+ method: "POST",
1256
+ body: JSON.stringify(ldSignedOnlyBody)
1257
+ });
1258
+ const ldSignedOnlyContext = createRequestContext({
1259
+ federation,
1260
+ request: ldSignedOnlyRequest,
1261
+ url: new URL(ldSignedOnlyRequest.url),
1262
+ data: void 0,
1263
+ documentLoader: mockDocumentLoader,
1264
+ contextLoader: async (resource) => {
1265
+ const url = new URL(resource).href;
1266
+ if (url === remoteContextUrl) throw new Error(`Unexpected context: ${url}`);
1267
+ return await mockDocumentLoader(url);
1268
+ }
1269
+ });
1270
+ await assertRejects(() => handleInbox(ldSignedOnlyRequest, {
1271
+ recipient: null,
1272
+ context: ldSignedOnlyContext,
1273
+ inboxContextFactory(_activity) {
1274
+ return createInboxContext({
1275
+ ...ldSignedOnlyContext,
1276
+ clone: void 0
1277
+ });
1278
+ },
1279
+ ...inboxOptions
1280
+ }), Error);
1281
+ failRemoteContextOnce = true;
1282
+ const invalidHttpFallbackRequest = new Request("https://example.com/", {
1283
+ method: "POST",
1284
+ body: JSON.stringify(ldSignedOnlyBody),
1285
+ headers: { Signature: "bogus" }
1286
+ });
1287
+ const invalidHttpFallbackContext = createRequestContext({
1288
+ federation,
1289
+ request: invalidHttpFallbackRequest,
1290
+ url: new URL(invalidHttpFallbackRequest.url),
1291
+ data: void 0,
1292
+ documentLoader: mockDocumentLoader,
1293
+ contextLoader: flakyContextLoader
1294
+ });
1295
+ await assertRejects(() => handleInbox(invalidHttpFallbackRequest, {
1296
+ recipient: null,
1297
+ context: invalidHttpFallbackContext,
1298
+ inboxContextFactory(_activity) {
1299
+ return createInboxContext({
1300
+ ...invalidHttpFallbackContext,
1301
+ clone: void 0
1302
+ });
1303
+ },
1304
+ ...inboxOptions
1305
+ }), Error);
1306
+ const transientKeyContextUrl = "https://remote.example/contexts/key";
1307
+ const transientCreatorUrl = "https://remote.example/keys/transient#main-key";
1308
+ const verificationFailureLdSignedBody = await signJsonLd({
1309
+ "@context": "https://www.w3.org/ns/activitystreams",
1310
+ id: "https://example.com/activities/ld-key-fetch-transient",
1311
+ type: "Create",
1312
+ actor: "https://example.com/person2",
1313
+ object: {
1314
+ id: "https://example.com/notes/ld-key-fetch-transient",
1315
+ type: "Note",
1316
+ attributedTo: "https://example.com/person2",
1317
+ content: "Hello, world!"
1318
+ }
1319
+ }, rsaPrivateKey3, new URL(transientCreatorUrl), { contextLoader: mockDocumentLoader });
1320
+ const verificationFailureLdSignedRequest = new Request("https://example.com/", {
1321
+ method: "POST",
1322
+ body: JSON.stringify(verificationFailureLdSignedBody),
1323
+ headers: { Signature: "bogus" }
1324
+ });
1325
+ const verificationFailureLdSignedContext = createRequestContext({
1326
+ federation,
1327
+ request: verificationFailureLdSignedRequest,
1328
+ url: new URL(verificationFailureLdSignedRequest.url),
1329
+ data: void 0,
1330
+ documentLoader: async (resource) => {
1331
+ if (resource === transientCreatorUrl) return {
1332
+ contextUrl: null,
1333
+ documentUrl: resource,
1334
+ document: {
1335
+ "@context": [transientKeyContextUrl],
1336
+ id: resource
1337
+ }
1338
+ };
1339
+ return await mockDocumentLoader(new URL(resource).href);
1340
+ },
1341
+ contextLoader: async (resource) => {
1342
+ if (resource === transientKeyContextUrl) throw new Error(`Transient key context failure: ${resource}`);
1343
+ return await mockDocumentLoader(new URL(resource).href);
1344
+ }
1345
+ });
1346
+ response = await handleInbox(verificationFailureLdSignedRequest, {
1347
+ recipient: null,
1348
+ context: verificationFailureLdSignedContext,
1349
+ inboxContextFactory(_activity) {
1350
+ return createInboxContext({
1351
+ ...verificationFailureLdSignedContext,
1352
+ clone: void 0
1353
+ });
1354
+ },
1355
+ ...inboxOptions
1356
+ });
1357
+ assertEquals([response.status, await response.text()], [401, "Failed to verify the request signature."]);
1358
+ failRemoteContextOnce = true;
1359
+ const deferredMalformedTemporalLdSignedBody = await signJsonLd({
1360
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1361
+ id: "https://example.com/activities/deferred-invalid-published",
1362
+ type: "Create",
1363
+ actor: "https://example.com/person2",
1364
+ ext: "preserve-me",
1365
+ published: { "@value": "not-a-date" },
1366
+ object: {
1367
+ id: "https://example.com/notes/deferred-invalid-published",
1368
+ type: "Note",
1369
+ attributedTo: "https://example.com/person2",
1370
+ content: "Hello, world!"
1371
+ }
1372
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: async (resource) => {
1373
+ const url = new URL(resource).href;
1374
+ if (url === remoteContextUrl) return {
1375
+ contextUrl: null,
1376
+ documentUrl: url,
1377
+ document: { "@context": { ext: "https://example.com/ext" } }
1378
+ };
1379
+ return await mockDocumentLoader(url);
1380
+ } });
1381
+ const deferredMalformedTemporalLdSignedRequest = new Request("https://example.com/", {
1382
+ method: "POST",
1383
+ body: JSON.stringify(deferredMalformedTemporalLdSignedBody),
1384
+ headers: { Signature: "bogus" }
1385
+ });
1386
+ const deferredMalformedTemporalLdSignedContext = createRequestContext({
1387
+ federation,
1388
+ request: deferredMalformedTemporalLdSignedRequest,
1389
+ url: new URL(deferredMalformedTemporalLdSignedRequest.url),
1390
+ data: void 0,
1391
+ documentLoader: mockDocumentLoader,
1392
+ contextLoader: flakyContextLoader
1393
+ });
1394
+ response = await handleInbox(deferredMalformedTemporalLdSignedRequest, {
1395
+ recipient: null,
1396
+ context: deferredMalformedTemporalLdSignedContext,
1397
+ inboxContextFactory(_activity) {
1398
+ return createInboxContext({
1399
+ ...deferredMalformedTemporalLdSignedContext,
1400
+ clone: void 0
1401
+ });
1402
+ },
1403
+ ...inboxOptions
1404
+ });
1405
+ assertEquals([response.status, await response.text()], [400, "Invalid activity."]);
1406
+ const malformedLdSignedRequest = new Request("https://example.com/", {
1407
+ method: "POST",
1408
+ body: JSON.stringify({
1409
+ ...ldSignedOnlyBody,
1410
+ "@context": ["not a url", "https://www.w3.org/ns/activitystreams"]
1411
+ })
1412
+ });
1413
+ const malformedLdSignedContext = createRequestContext({
1414
+ federation,
1415
+ request: malformedLdSignedRequest,
1416
+ url: new URL(malformedLdSignedRequest.url),
1417
+ data: void 0,
1418
+ documentLoader: mockDocumentLoader,
1419
+ contextLoader: mockDocumentLoader
1420
+ });
1421
+ response = await handleInbox(malformedLdSignedRequest, {
1422
+ recipient: null,
1423
+ context: malformedLdSignedContext,
1424
+ inboxContextFactory(_activity) {
1425
+ return createInboxContext({
1426
+ ...malformedLdSignedContext,
1427
+ clone: void 0
1428
+ });
1429
+ },
1430
+ ...inboxOptions
1431
+ });
1432
+ assertEquals([response.status, await response.text()], [400, "Invalid JSON-LD."]);
1433
+ const dualSignedInvalidCreatorRequest = await signRequest(new Request("https://example.com/", {
1434
+ method: "POST",
1435
+ body: JSON.stringify({
1436
+ ...httpSignedLdBody,
1437
+ signature: {
1438
+ ...httpSignedLdBody.signature,
1439
+ creator: "not a url"
1440
+ }
1441
+ })
1442
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1443
+ const dualSignedInvalidCreatorContext = createRequestContext({
1444
+ federation,
1445
+ request: dualSignedInvalidCreatorRequest,
1446
+ url: new URL(dualSignedInvalidCreatorRequest.url),
1447
+ data: void 0,
1448
+ documentLoader: mockDocumentLoader,
1449
+ contextLoader: flakyContextLoader
1450
+ });
1451
+ response = await handleInbox(dualSignedInvalidCreatorRequest, {
1452
+ recipient: null,
1453
+ context: dualSignedInvalidCreatorContext,
1454
+ inboxContextFactory(_activity) {
1455
+ return createInboxContext({
1456
+ ...dualSignedInvalidCreatorContext,
1051
1457
  clone: void 0
1052
1458
  });
1053
1459
  },
1054
1460
  ...inboxOptions
1055
1461
  });
1056
1462
  assertEquals(onNotFoundCalled, null);
1057
- assertEquals(response.status, 400);
1463
+ assertEquals([response.status, await response.text()], [202, ""]);
1464
+ const invalidUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1465
+ method: "POST",
1466
+ body: JSON.stringify({
1467
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1468
+ id: "https://example.com/activities/http-signed-invalid-context",
1469
+ type: "Create",
1470
+ actor: "https://example.com/person2",
1471
+ ext: "preserve-me",
1472
+ object: {
1473
+ id: "https://example.com/notes/http-signed-invalid-context",
1474
+ type: "Note",
1475
+ attributedTo: "https://example.com/person2",
1476
+ content: "Hello, world!"
1477
+ }
1478
+ })
1479
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1480
+ const invalidUrlHttpSignedContext = createRequestContext({
1481
+ federation,
1482
+ request: invalidUrlHttpSignedRequest,
1483
+ url: new URL(invalidUrlHttpSignedRequest.url),
1484
+ data: void 0,
1485
+ documentLoader: mockDocumentLoader,
1486
+ contextLoader: async (resource) => {
1487
+ const url = new URL(resource).href;
1488
+ if (url === remoteContextUrl) {
1489
+ const error = /* @__PURE__ */ new Error(`Transient remote context failure: ${url}`);
1490
+ error.name = "jsonld.InvalidUrl";
1491
+ error.details = {
1492
+ code: "loading remote context failed",
1493
+ url
1494
+ };
1495
+ throw error;
1496
+ }
1497
+ return await mockDocumentLoader(url);
1498
+ }
1499
+ });
1500
+ await assertRejects(() => handleInbox(invalidUrlHttpSignedRequest, {
1501
+ recipient: null,
1502
+ context: invalidUrlHttpSignedContext,
1503
+ inboxContextFactory(_activity) {
1504
+ return createInboxContext({
1505
+ ...invalidUrlHttpSignedContext,
1506
+ clone: void 0
1507
+ });
1508
+ },
1509
+ ...inboxOptions
1510
+ }), Error);
1511
+ const opaqueContextIdHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1512
+ method: "POST",
1513
+ body: JSON.stringify({
1514
+ "@context": ["app-context", "https://www.w3.org/ns/activitystreams"],
1515
+ id: "https://example.com/activities/http-signed-opaque-context",
1516
+ type: "Create",
1517
+ actor: "https://example.com/person2",
1518
+ object: {
1519
+ id: "https://example.com/notes/http-signed-opaque-context",
1520
+ type: "Note",
1521
+ attributedTo: "https://example.com/person2",
1522
+ content: "Hello, world!"
1523
+ }
1524
+ })
1525
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1526
+ const opaqueContextIdHttpSignedContext = createRequestContext({
1527
+ federation,
1528
+ request: opaqueContextIdHttpSignedRequest,
1529
+ url: new URL(opaqueContextIdHttpSignedRequest.url),
1530
+ data: void 0,
1531
+ documentLoader: mockDocumentLoader,
1532
+ contextLoader: async (resource) => {
1533
+ if (resource === "app-context") {
1534
+ const error = /* @__PURE__ */ new Error(`Opaque context backend is unavailable: ${resource}`);
1535
+ error.name = "jsonld.InvalidUrl";
1536
+ error.details = {
1537
+ code: "loading remote context failed",
1538
+ url: resource
1539
+ };
1540
+ throw error;
1541
+ }
1542
+ return await mockDocumentLoader(new URL(resource).href);
1543
+ }
1544
+ });
1545
+ await assertRejects(() => handleInbox(opaqueContextIdHttpSignedRequest, {
1546
+ recipient: null,
1547
+ context: opaqueContextIdHttpSignedContext,
1548
+ inboxContextFactory(_activity) {
1549
+ return createInboxContext({
1550
+ ...opaqueContextIdHttpSignedContext,
1551
+ clone: void 0
1552
+ });
1553
+ },
1554
+ ...inboxOptions
1555
+ }), Error);
1556
+ const opaqueContextTypeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1557
+ method: "POST",
1558
+ body: JSON.stringify({
1559
+ "@context": ["app:context", "https://www.w3.org/ns/activitystreams"],
1560
+ id: "https://example.com/activities/http-signed-opaque-typeerror",
1561
+ type: "Create",
1562
+ actor: "https://example.com/person2",
1563
+ object: {
1564
+ id: "https://example.com/notes/http-signed-opaque-typeerror",
1565
+ type: "Note",
1566
+ attributedTo: "https://example.com/person2",
1567
+ content: "Hello, world!"
1568
+ }
1569
+ })
1570
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1571
+ const opaqueContextTypeErrorHttpSignedContext = createRequestContext({
1572
+ federation,
1573
+ request: opaqueContextTypeErrorHttpSignedRequest,
1574
+ url: new URL(opaqueContextTypeErrorHttpSignedRequest.url),
1575
+ data: void 0,
1576
+ documentLoader: mockDocumentLoader,
1577
+ contextLoader: async (resource) => {
1578
+ if (resource === "app:context") throw new TypeError(`Invalid URL: ${resource}`);
1579
+ return await mockDocumentLoader(new URL(resource).href);
1580
+ }
1581
+ });
1582
+ await assertRejects(() => handleInbox(opaqueContextTypeErrorHttpSignedRequest, {
1583
+ recipient: null,
1584
+ context: opaqueContextTypeErrorHttpSignedContext,
1585
+ inboxContextFactory(_activity) {
1586
+ return createInboxContext({
1587
+ ...opaqueContextTypeErrorHttpSignedContext,
1588
+ clone: void 0
1589
+ });
1590
+ },
1591
+ ...inboxOptions
1592
+ }), Error);
1593
+ const networkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1594
+ method: "POST",
1595
+ body: JSON.stringify({
1596
+ "@context": ["//cdn.example/ctx", "https://www.w3.org/ns/activitystreams"],
1597
+ id: "https://example.com/activities/http-signed-network-path-context",
1598
+ type: "Create",
1599
+ actor: "https://example.com/person2",
1600
+ object: {
1601
+ id: "https://example.com/notes/http-signed-network-path-context",
1602
+ type: "Note",
1603
+ attributedTo: "https://example.com/person2",
1604
+ content: "Hello, world!"
1605
+ }
1606
+ })
1607
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1608
+ const networkPathContextHttpSignedContext = createRequestContext({
1609
+ federation,
1610
+ request: networkPathContextHttpSignedRequest,
1611
+ url: new URL(networkPathContextHttpSignedRequest.url),
1612
+ data: void 0,
1613
+ documentLoader: mockDocumentLoader,
1614
+ contextLoader: async (resource) => {
1615
+ if (resource === "//cdn.example/ctx") {
1616
+ const error = /* @__PURE__ */ new Error(`Network-path context backend is unavailable: ${resource}`);
1617
+ error.name = "jsonld.InvalidUrl";
1618
+ error.details = {
1619
+ code: "loading remote context failed",
1620
+ url: resource
1621
+ };
1622
+ throw error;
1623
+ }
1624
+ return await mockDocumentLoader(new URL(resource).href);
1625
+ }
1626
+ });
1627
+ await assertRejects(() => handleInbox(networkPathContextHttpSignedRequest, {
1628
+ recipient: null,
1629
+ context: networkPathContextHttpSignedContext,
1630
+ inboxContextFactory(_activity) {
1631
+ return createInboxContext({
1632
+ ...networkPathContextHttpSignedContext,
1633
+ clone: void 0
1634
+ });
1635
+ },
1636
+ ...inboxOptions
1637
+ }), Error);
1638
+ const malformedNetworkPathContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1639
+ method: "POST",
1640
+ body: JSON.stringify({
1641
+ "@context": ["//[", "https://www.w3.org/ns/activitystreams"],
1642
+ id: "https://example.com/activities/http-signed-malformed-network-path-context",
1643
+ type: "Create",
1644
+ actor: "https://example.com/person2",
1645
+ object: {
1646
+ id: "https://example.com/notes/http-signed-malformed-network-path-context",
1647
+ type: "Note",
1648
+ attributedTo: "https://example.com/person2",
1649
+ content: "Hello, world!"
1650
+ }
1651
+ })
1652
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1653
+ const malformedNetworkPathContextHttpSignedContext = createRequestContext({
1654
+ federation,
1655
+ request: malformedNetworkPathContextHttpSignedRequest,
1656
+ url: new URL(malformedNetworkPathContextHttpSignedRequest.url),
1657
+ data: void 0,
1658
+ documentLoader: mockDocumentLoader,
1659
+ contextLoader: async (resource) => {
1660
+ if (resource === "//[") {
1661
+ const error = /* @__PURE__ */ new Error(`Malformed network-path context: ${resource}`);
1662
+ error.name = "jsonld.InvalidUrl";
1663
+ error.details = {
1664
+ code: "loading remote context failed",
1665
+ url: resource
1666
+ };
1667
+ throw error;
1668
+ }
1669
+ return await mockDocumentLoader(new URL(resource).href);
1670
+ }
1671
+ });
1672
+ response = await handleInbox(malformedNetworkPathContextHttpSignedRequest, {
1673
+ recipient: null,
1674
+ context: malformedNetworkPathContextHttpSignedContext,
1675
+ inboxContextFactory(_activity) {
1676
+ return createInboxContext({
1677
+ ...malformedNetworkPathContextHttpSignedContext,
1678
+ clone: void 0
1679
+ });
1680
+ },
1681
+ ...inboxOptions
1682
+ });
1683
+ assertEquals(response.status, 400);
1684
+ const malformedUrlLikeContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1685
+ method: "POST",
1686
+ body: JSON.stringify({
1687
+ "@context": ["http://[", "https://www.w3.org/ns/activitystreams"],
1688
+ id: "https://example.com/activities/http-signed-malformed-url-like-context",
1689
+ type: "Create",
1690
+ actor: "https://example.com/person2",
1691
+ object: {
1692
+ id: "https://example.com/notes/http-signed-malformed-url-like-context",
1693
+ type: "Note",
1694
+ attributedTo: "https://example.com/person2",
1695
+ content: "Hello, world!"
1696
+ }
1697
+ })
1698
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1699
+ const malformedUrlLikeContextHttpSignedContext = createRequestContext({
1700
+ federation,
1701
+ request: malformedUrlLikeContextHttpSignedRequest,
1702
+ url: new URL(malformedUrlLikeContextHttpSignedRequest.url),
1703
+ data: void 0,
1704
+ documentLoader: mockDocumentLoader,
1705
+ contextLoader: async (resource) => {
1706
+ if (resource === "http://[") {
1707
+ const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
1708
+ error.name = "jsonld.InvalidUrl";
1709
+ error.details = {
1710
+ code: "loading remote context failed",
1711
+ url: resource
1712
+ };
1713
+ throw error;
1714
+ }
1715
+ return await mockDocumentLoader(new URL(resource).href);
1716
+ }
1717
+ });
1718
+ response = await handleInbox(malformedUrlLikeContextHttpSignedRequest, {
1719
+ recipient: null,
1720
+ context: malformedUrlLikeContextHttpSignedContext,
1721
+ inboxContextFactory(_activity) {
1722
+ return createInboxContext({
1723
+ ...malformedUrlLikeContextHttpSignedContext,
1724
+ clone: void 0
1725
+ });
1726
+ },
1727
+ ...inboxOptions
1728
+ });
1729
+ assertEquals(response.status, 400);
1730
+ const malformedContextUrlHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1731
+ method: "POST",
1732
+ body: JSON.stringify({
1733
+ "@context": ["not a url", "https://www.w3.org/ns/activitystreams"],
1734
+ id: "https://example.com/activities/http-signed-malformed-context",
1735
+ type: "Create",
1736
+ actor: "https://example.com/person2",
1737
+ object: {
1738
+ id: "https://example.com/notes/http-signed-malformed-context",
1739
+ type: "Note",
1740
+ attributedTo: "https://example.com/person2",
1741
+ content: "Hello, world!"
1742
+ }
1743
+ })
1744
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1745
+ const malformedContextUrlHttpSignedContext = createRequestContext({
1746
+ federation,
1747
+ request: malformedContextUrlHttpSignedRequest,
1748
+ url: new URL(malformedContextUrlHttpSignedRequest.url),
1749
+ data: void 0,
1750
+ documentLoader: mockDocumentLoader,
1751
+ contextLoader: async (resource) => {
1752
+ if (resource === "not a url") {
1753
+ const error = /* @__PURE__ */ new Error(`Invalid remote context URL: ${resource}`);
1754
+ error.name = "jsonld.InvalidUrl";
1755
+ error.details = {
1756
+ code: "loading remote context failed",
1757
+ url: resource
1758
+ };
1759
+ throw error;
1760
+ }
1761
+ return await mockDocumentLoader(new URL(resource).href);
1762
+ }
1763
+ });
1764
+ response = await handleInbox(malformedContextUrlHttpSignedRequest, {
1765
+ recipient: null,
1766
+ context: malformedContextUrlHttpSignedContext,
1767
+ inboxContextFactory(_activity) {
1768
+ return createInboxContext({
1769
+ ...malformedContextUrlHttpSignedContext,
1770
+ clone: void 0
1771
+ });
1772
+ },
1773
+ ...inboxOptions
1774
+ });
1775
+ assertEquals(response.status, 400);
1776
+ const invalidRemoteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1777
+ method: "POST",
1778
+ body: JSON.stringify({
1779
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1780
+ id: "https://example.com/activities/http-signed-invalid-remote-context",
1781
+ type: "Create",
1782
+ actor: "https://example.com/person2",
1783
+ object: {
1784
+ id: "https://example.com/notes/http-signed-invalid-remote-context",
1785
+ type: "Note",
1786
+ attributedTo: "https://example.com/person2",
1787
+ content: "Hello, world!"
1788
+ }
1789
+ })
1790
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1791
+ const invalidRemoteContextHttpSignedContext = createRequestContext({
1792
+ federation,
1793
+ request: invalidRemoteContextHttpSignedRequest,
1794
+ url: new URL(invalidRemoteContextHttpSignedRequest.url),
1795
+ data: void 0,
1796
+ documentLoader: mockDocumentLoader,
1797
+ contextLoader: async (resource) => {
1798
+ const url = new URL(resource).href;
1799
+ if (url === remoteContextUrl) return {
1800
+ contextUrl: null,
1801
+ documentUrl: url,
1802
+ document: [
1803
+ "not",
1804
+ "an",
1805
+ "object"
1806
+ ]
1807
+ };
1808
+ return await mockDocumentLoader(url);
1809
+ }
1810
+ });
1811
+ response = await handleInbox(invalidRemoteContextHttpSignedRequest, {
1812
+ recipient: null,
1813
+ context: invalidRemoteContextHttpSignedContext,
1814
+ inboxContextFactory(_activity) {
1815
+ return createInboxContext({
1816
+ ...invalidRemoteContextHttpSignedContext,
1817
+ clone: void 0
1818
+ });
1819
+ },
1820
+ ...inboxOptions
1821
+ });
1822
+ assertEquals(response.status, 400);
1823
+ const invalidUrlAbsoluteContextHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1824
+ method: "POST",
1825
+ body: JSON.stringify({
1826
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1827
+ id: "https://example.com/activities/http-signed-invalid-url-context",
1828
+ type: "Create",
1829
+ actor: "https://example.com/person2",
1830
+ ext: "preserve-me",
1831
+ object: {
1832
+ id: "https://example.com/notes/http-signed-invalid-url-context",
1833
+ type: "Note",
1834
+ attributedTo: "https://example.com/person2",
1835
+ content: "Hello, world!"
1836
+ }
1837
+ })
1838
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1839
+ const invalidUrlAbsoluteContextHttpSignedContext = createRequestContext({
1840
+ federation,
1841
+ request: invalidUrlAbsoluteContextHttpSignedRequest,
1842
+ url: new URL(invalidUrlAbsoluteContextHttpSignedRequest.url),
1843
+ data: void 0,
1844
+ documentLoader: mockDocumentLoader,
1845
+ contextLoader: async (resource) => {
1846
+ const url = new URL(resource).href;
1847
+ if (url === remoteContextUrl) throw new TypeError(`Invalid URL: ${url}`);
1848
+ return await mockDocumentLoader(url);
1849
+ }
1850
+ });
1851
+ await assertRejects(() => handleInbox(invalidUrlAbsoluteContextHttpSignedRequest, {
1852
+ recipient: null,
1853
+ context: invalidUrlAbsoluteContextHttpSignedContext,
1854
+ inboxContextFactory(_activity) {
1855
+ return createInboxContext({
1856
+ ...invalidUrlAbsoluteContextHttpSignedContext,
1857
+ clone: void 0
1858
+ });
1859
+ },
1860
+ ...inboxOptions
1861
+ }), Error);
1862
+ const typeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1863
+ method: "POST",
1864
+ body: JSON.stringify({
1865
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1866
+ id: "https://example.com/activities/http-signed-typeerror-context",
1867
+ type: "Create",
1868
+ actor: "https://example.com/person2",
1869
+ ext: "preserve-me",
1870
+ object: {
1871
+ id: "https://example.com/notes/http-signed-typeerror-context",
1872
+ type: "Note",
1873
+ attributedTo: "https://example.com/person2",
1874
+ content: "Hello, world!"
1875
+ }
1876
+ })
1877
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1878
+ const typeErrorHttpSignedContext = createRequestContext({
1879
+ federation,
1880
+ request: typeErrorHttpSignedRequest,
1881
+ url: new URL(typeErrorHttpSignedRequest.url),
1882
+ data: void 0,
1883
+ documentLoader: mockDocumentLoader,
1884
+ contextLoader: async (resource) => {
1885
+ const url = new URL(resource).href;
1886
+ if (url === remoteContextUrl) throw new TypeError(`The remote context host timed out: ${url}`);
1887
+ return await mockDocumentLoader(url);
1888
+ }
1889
+ });
1890
+ await assertRejects(() => handleInbox(typeErrorHttpSignedRequest, {
1891
+ recipient: null,
1892
+ context: typeErrorHttpSignedContext,
1893
+ inboxContextFactory(_activity) {
1894
+ return createInboxContext({
1895
+ ...typeErrorHttpSignedContext,
1896
+ clone: void 0
1897
+ });
1898
+ },
1899
+ ...inboxOptions
1900
+ }), Error);
1901
+ const rangeErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1902
+ method: "POST",
1903
+ body: JSON.stringify({
1904
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1905
+ id: "https://example.com/activities/http-signed-rangeerror-context",
1906
+ type: "Create",
1907
+ actor: "https://example.com/person2",
1908
+ ext: "preserve-me",
1909
+ object: {
1910
+ id: "https://example.com/notes/http-signed-rangeerror-context",
1911
+ type: "Note",
1912
+ attributedTo: "https://example.com/person2",
1913
+ content: "Hello, world!"
1914
+ }
1915
+ })
1916
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1917
+ const rangeErrorHttpSignedContext = createRequestContext({
1918
+ federation,
1919
+ request: rangeErrorHttpSignedRequest,
1920
+ url: new URL(rangeErrorHttpSignedRequest.url),
1921
+ data: void 0,
1922
+ documentLoader: mockDocumentLoader,
1923
+ contextLoader: async (resource) => {
1924
+ const url = new URL(resource).href;
1925
+ if (url === remoteContextUrl) throw new RangeError(`Temporary remote context cache window exceeded: ${url}`);
1926
+ return await mockDocumentLoader(url);
1927
+ }
1928
+ });
1929
+ await assertRejects(() => handleInbox(rangeErrorHttpSignedRequest, {
1930
+ recipient: null,
1931
+ context: rangeErrorHttpSignedContext,
1932
+ inboxContextFactory(_activity) {
1933
+ return createInboxContext({
1934
+ ...rangeErrorHttpSignedContext,
1935
+ clone: void 0
1936
+ });
1937
+ },
1938
+ ...inboxOptions
1939
+ }), Error);
1940
+ const syntaxErrorHttpSignedRequest = await signRequest(new Request("https://example.com/", {
1941
+ method: "POST",
1942
+ body: JSON.stringify({
1943
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
1944
+ id: "https://example.com/activities/http-signed-syntax-context",
1945
+ type: "Create",
1946
+ actor: "https://example.com/person2",
1947
+ ext: "preserve-me",
1948
+ object: {
1949
+ id: "https://example.com/notes/http-signed-syntax-context",
1950
+ type: "Note",
1951
+ attributedTo: "https://example.com/person2",
1952
+ content: "Hello, world!"
1953
+ }
1954
+ })
1955
+ }), rsaPrivateKey3, rsaPublicKey3.id);
1956
+ const syntaxErrorHttpSignedContext = createRequestContext({
1957
+ federation,
1958
+ request: syntaxErrorHttpSignedRequest,
1959
+ url: new URL(syntaxErrorHttpSignedRequest.url),
1960
+ data: void 0,
1961
+ documentLoader: mockDocumentLoader,
1962
+ contextLoader: async (resource) => {
1963
+ const url = new URL(resource).href;
1964
+ if (url === remoteContextUrl) {
1965
+ const error = /* @__PURE__ */ new Error(`Transient syntax failure: ${url}`);
1966
+ error.name = "jsonld.SyntaxError";
1967
+ error.details = { code: "loading remote context failed" };
1968
+ throw error;
1969
+ }
1970
+ return await mockDocumentLoader(url);
1971
+ }
1972
+ });
1973
+ await assertRejects(() => handleInbox(syntaxErrorHttpSignedRequest, {
1974
+ recipient: null,
1975
+ context: syntaxErrorHttpSignedContext,
1976
+ inboxContextFactory(_activity) {
1977
+ return createInboxContext({
1978
+ ...syntaxErrorHttpSignedContext,
1979
+ clone: void 0
1980
+ });
1981
+ },
1982
+ ...inboxOptions
1983
+ }), Error);
1984
+ response = await handleInbox(signedRequest, {
1985
+ recipient: "someone",
1986
+ context: signedContext,
1987
+ inboxContextFactory(_activity) {
1988
+ return createInboxContext({
1989
+ ...unsignedContext,
1990
+ clone: void 0,
1991
+ recipient: "someone"
1992
+ });
1993
+ },
1994
+ ...inboxOptions
1995
+ });
1996
+ assertEquals(onNotFoundCalled, null);
1997
+ assertEquals([response.status, await response.text()], [202, ""]);
1998
+ response = await handleInbox(unsignedRequest, {
1999
+ recipient: null,
2000
+ context: unsignedContext,
2001
+ inboxContextFactory(_activity) {
2002
+ return createInboxContext({
2003
+ ...unsignedContext,
2004
+ clone: void 0
2005
+ });
2006
+ },
2007
+ ...inboxOptions,
2008
+ skipSignatureVerification: true
2009
+ });
2010
+ assertEquals(onNotFoundCalled, null);
2011
+ assertEquals(response.status, 202);
2012
+ response = await handleInbox(unsignedRequest, {
2013
+ recipient: "someone",
2014
+ context: unsignedContext,
2015
+ inboxContextFactory(_activity) {
2016
+ return createInboxContext({
2017
+ ...unsignedContext,
2018
+ clone: void 0,
2019
+ recipient: "someone"
2020
+ });
2021
+ },
2022
+ ...inboxOptions,
2023
+ skipSignatureVerification: true
2024
+ });
2025
+ assertEquals(onNotFoundCalled, null);
2026
+ assertEquals(response.status, 202);
2027
+ const unsafeJson = {
2028
+ "@context": ["https://www.w3.org/ns/activitystreams", { rev: "@reverse" }],
2029
+ id: "https://example.com/activities/unsafe",
2030
+ type: "Announce",
2031
+ actor: "https://example.com/person2",
2032
+ object: "https://example.com/notes/1",
2033
+ rev: { object: {
2034
+ id: "https://example.com/activities/undo",
2035
+ type: "Undo",
2036
+ actor: "https://example.com/person2"
2037
+ } }
2038
+ };
2039
+ const unsafeRequest = await signRequest(new Request("https://example.com/", {
2040
+ method: "POST",
2041
+ body: JSON.stringify(unsafeJson)
2042
+ }), rsaPrivateKey3, rsaPublicKey3.id);
2043
+ const unsafeContext = createRequestContext({
2044
+ federation,
2045
+ request: unsafeRequest,
2046
+ url: new URL(unsafeRequest.url),
2047
+ data: void 0,
2048
+ documentLoader: mockDocumentLoader
2049
+ });
2050
+ response = await handleInbox(unsafeRequest, {
2051
+ recipient: null,
2052
+ context: unsafeContext,
2053
+ inboxContextFactory(_activity) {
2054
+ return createInboxContext({
2055
+ ...unsafeContext,
2056
+ clone: void 0
2057
+ });
2058
+ },
2059
+ ...inboxOptions
2060
+ });
2061
+ assertEquals(response.status, 202);
2062
+ const unsafeLdRequest = new Request("https://example.com/", {
2063
+ method: "POST",
2064
+ body: JSON.stringify(await signJsonLd(unsafeJson, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: mockDocumentLoader }))
2065
+ });
2066
+ const unsafeLdContext = createRequestContext({
2067
+ federation,
2068
+ request: unsafeLdRequest,
2069
+ url: new URL(unsafeLdRequest.url),
2070
+ data: void 0,
2071
+ documentLoader: mockDocumentLoader
2072
+ });
2073
+ response = await handleInbox(unsafeLdRequest, {
2074
+ recipient: null,
2075
+ context: unsafeLdContext,
2076
+ inboxContextFactory(_activity) {
2077
+ return createInboxContext({
2078
+ ...unsafeLdContext,
2079
+ clone: void 0
2080
+ });
2081
+ },
2082
+ ...inboxOptions
2083
+ });
2084
+ assertEquals(response.status, 400);
2085
+ const signedInvalidRequest = await signRequest(new Request("https://example.com/", {
2086
+ method: "POST",
2087
+ body: JSON.stringify({
2088
+ "@context": [
2089
+ "https://www.w3.org/ns/activitystreams",
2090
+ true,
2091
+ 23
2092
+ ],
2093
+ type: "Create",
2094
+ object: {
2095
+ type: "Note",
2096
+ content: "Hello, world!"
2097
+ },
2098
+ actor: "https://example.com/users/alice"
2099
+ })
2100
+ }), rsaPrivateKey3, rsaPublicKey3.id);
2101
+ const signedInvalidContext = createRequestContext({
2102
+ federation,
2103
+ request: signedInvalidRequest,
2104
+ url: new URL(signedInvalidRequest.url),
2105
+ data: void 0,
2106
+ documentLoader: mockDocumentLoader
2107
+ });
2108
+ response = await handleInbox(signedInvalidRequest, {
2109
+ recipient: null,
2110
+ context: signedContext,
2111
+ inboxContextFactory(_activity) {
2112
+ return createInboxContext({
2113
+ ...signedInvalidContext,
2114
+ clone: void 0
2115
+ });
2116
+ },
2117
+ ...inboxOptions
2118
+ });
2119
+ assertEquals(onNotFoundCalled, null);
2120
+ assertEquals(response.status, 400);
2121
+ });
2122
+ test("handleInbox() preserves the raw signed payload for inboxContextFactory", async () => {
2123
+ const federation = createFederation({ kv: new MemoryKvStore() });
2124
+ const remoteContextUrl = "https://remote.example/contexts/ext";
2125
+ const sourceContextLoader = async (resource) => {
2126
+ const url = new URL(resource).href;
2127
+ if (url === remoteContextUrl) return {
2128
+ contextUrl: null,
2129
+ documentUrl: url,
2130
+ document: { "@context": { ext: "https://example.com/ext" } }
2131
+ };
2132
+ return await mockDocumentLoader(url);
2133
+ };
2134
+ const signed = await signJsonLd({
2135
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
2136
+ id: "https://example.com/activities/preserve-raw",
2137
+ type: "Create",
2138
+ actor: "https://example.com/person2",
2139
+ ext: "preserve-me",
2140
+ object: {
2141
+ id: "https://example.com/notes/preserve-raw",
2142
+ type: "Note",
2143
+ attributedTo: "https://example.com/person2",
2144
+ content: "Hello, world!"
2145
+ }
2146
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
2147
+ const request = new Request("https://example.com/", {
2148
+ method: "POST",
2149
+ body: JSON.stringify(signed)
2150
+ });
2151
+ const context = createRequestContext({
2152
+ federation,
2153
+ request,
2154
+ url: new URL(request.url),
2155
+ data: void 0,
2156
+ documentLoader: mockDocumentLoader,
2157
+ contextLoader: sourceContextLoader
2158
+ });
2159
+ let receivedRaw = null;
2160
+ let receivedTyped = null;
2161
+ const inboxListeners = new InboxListenerSet();
2162
+ inboxListeners.add(Create, (ctx, activity) => {
2163
+ receivedRaw = ctx.activity;
2164
+ receivedTyped = activity;
2165
+ });
2166
+ const response = await handleInbox(request, {
2167
+ recipient: "someone",
2168
+ context,
2169
+ inboxContextFactory(recipient, activity, activityId, activityType) {
2170
+ return {
2171
+ ...createInboxContext({
2172
+ ...context,
2173
+ clone: void 0,
2174
+ recipient
2175
+ }),
2176
+ activity,
2177
+ activityId,
2178
+ activityType
2179
+ };
2180
+ },
2181
+ kv: new MemoryKvStore(),
2182
+ kvPrefixes: {
2183
+ activityIdempotence: ["_fedify", "activityIdempotence"],
2184
+ publicKey: ["_fedify", "publicKey"],
2185
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
2186
+ },
2187
+ actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
2188
+ inboxListeners,
2189
+ onNotFound: () => new Response("Not found", { status: 404 }),
2190
+ signatureTimeWindow: { minutes: 5 },
2191
+ skipSignatureVerification: false
2192
+ });
2193
+ assertEquals([response.status, await response.text()], [202, ""]);
2194
+ assertEquals(receivedRaw, signed);
2195
+ const delivered = receivedTyped;
2196
+ assert(delivered != null);
2197
+ assertEquals(delivered.id?.href, "https://example.com/activities/preserve-raw");
2198
+ });
2199
+ test("handleInbox() enqueues normalizedActivity for LD-signed inbox work", async () => {
2200
+ const remoteContextUrl = "https://remote.example/contexts/ext";
2201
+ const sourceContextLoader = async (resource) => {
2202
+ const url = new URL(resource).href;
2203
+ if (url === remoteContextUrl) return {
2204
+ contextUrl: null,
2205
+ documentUrl: url,
2206
+ document: { "@context": { ext: "https://example.com/ext" } }
2207
+ };
2208
+ return await mockDocumentLoader(url);
2209
+ };
2210
+ let queuedMessage = null;
2211
+ const queue = {
2212
+ enqueue(message) {
2213
+ queuedMessage = message;
2214
+ return Promise.resolve();
2215
+ },
2216
+ async listen() {}
2217
+ };
2218
+ const federation = createFederation({
2219
+ kv: new MemoryKvStore(),
2220
+ queue
2221
+ });
2222
+ const signed = await signJsonLd({
2223
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
2224
+ id: "https://example.com/activities/enqueued-normalized",
2225
+ type: "Create",
2226
+ actor: "https://example.com/person2",
2227
+ ext: "preserve-me",
2228
+ object: {
2229
+ id: "https://example.com/notes/enqueued-normalized",
2230
+ type: "Note",
2231
+ attributedTo: "https://example.com/person2",
2232
+ content: "Hello, world!"
2233
+ }
2234
+ }, rsaPrivateKey3, rsaPublicKey3.id, { contextLoader: sourceContextLoader });
2235
+ const request = new Request("https://example.com/", {
2236
+ method: "POST",
2237
+ body: JSON.stringify(signed)
2238
+ });
2239
+ const context = createRequestContext({
2240
+ federation,
2241
+ request,
2242
+ url: new URL(request.url),
2243
+ data: void 0,
2244
+ documentLoader: mockDocumentLoader,
2245
+ contextLoader: sourceContextLoader
2246
+ });
2247
+ const response = await handleInbox(request, {
2248
+ recipient: "someone",
2249
+ context,
2250
+ inboxContextFactory(recipient, activity, activityId, activityType) {
2251
+ return {
2252
+ ...createInboxContext({
2253
+ ...context,
2254
+ clone: void 0,
2255
+ recipient
2256
+ }),
2257
+ activity,
2258
+ activityId,
2259
+ activityType
2260
+ };
2261
+ },
2262
+ kv: new MemoryKvStore(),
2263
+ kvPrefixes: {
2264
+ activityIdempotence: ["_fedify", "activityIdempotence"],
2265
+ publicKey: ["_fedify", "publicKey"],
2266
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
2267
+ },
2268
+ queue,
2269
+ actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
2270
+ onNotFound: () => new Response("Not found", { status: 404 }),
2271
+ signatureTimeWindow: { minutes: 5 },
2272
+ skipSignatureVerification: false
2273
+ });
2274
+ assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
2275
+ const enqueued = queuedMessage;
2276
+ assert(enqueued != null);
2277
+ const inboxMessage = enqueued;
2278
+ assertEquals(inboxMessage.activity, signed);
2279
+ assertEquals(inboxMessage.normalizedActivity, await compactJsonLd(signed, sourceContextLoader));
2280
+ assertEquals(inboxMessage.ldSignatureVerified, true);
2281
+ });
2282
+ test("handleInbox() caches normalizedActivity for queued signature-bearing fallback traffic", async () => {
2283
+ const remoteContextUrl = "https://remote.example/contexts/ext";
2284
+ let queuedMessage = null;
2285
+ const queue = {
2286
+ enqueue(message) {
2287
+ queuedMessage = message;
2288
+ return Promise.resolve();
2289
+ },
2290
+ async listen() {}
2291
+ };
2292
+ const federation = createFederation({
2293
+ kv: new MemoryKvStore(),
2294
+ queue
2295
+ });
2296
+ const sourceContextLoader = async (resource) => {
2297
+ const url = new URL(resource).href;
2298
+ if (url === remoteContextUrl) return {
2299
+ contextUrl: null,
2300
+ documentUrl: url,
2301
+ document: { "@context": { ext: "https://example.com/ext" } }
2302
+ };
2303
+ return await mockDocumentLoader(url);
2304
+ };
2305
+ const unsignedBody = {
2306
+ "@context": [remoteContextUrl, "https://www.w3.org/ns/activitystreams"],
2307
+ id: "https://example.com/activities/non-lds-queued-signature",
2308
+ type: "Create",
2309
+ actor: "https://example.com/person2",
2310
+ ext: "preserve-me",
2311
+ object: {
2312
+ id: "https://example.com/notes/non-lds-queued-signature",
2313
+ type: "Note",
2314
+ attributedTo: "https://example.com/person2",
2315
+ content: "Hello, world!"
2316
+ },
2317
+ signature: {
2318
+ type: "RsaSignature2017",
2319
+ creator: "not a url",
2320
+ created: "2024-09-12T16:50:46Z",
2321
+ signatureValue: "Zm9v"
2322
+ }
2323
+ };
2324
+ const request = await signRequest(new Request("https://example.com/", {
2325
+ method: "POST",
2326
+ body: JSON.stringify(unsignedBody)
2327
+ }), rsaPrivateKey3, rsaPublicKey3.id);
2328
+ const context = createRequestContext({
2329
+ federation,
2330
+ request,
2331
+ url: new URL(request.url),
2332
+ data: void 0,
2333
+ documentLoader: mockDocumentLoader,
2334
+ contextLoader: sourceContextLoader
2335
+ });
2336
+ const response = await handleInbox(request, {
2337
+ recipient: "someone",
2338
+ context,
2339
+ inboxContextFactory(recipient, activity, activityId, activityType) {
2340
+ return {
2341
+ ...createInboxContext({
2342
+ ...context,
2343
+ clone: void 0,
2344
+ recipient
2345
+ }),
2346
+ activity,
2347
+ activityId,
2348
+ activityType
2349
+ };
2350
+ },
2351
+ kv: new MemoryKvStore(),
2352
+ kvPrefixes: {
2353
+ activityIdempotence: ["_fedify", "activityIdempotence"],
2354
+ publicKey: ["_fedify", "publicKey"],
2355
+ acceptSignatureNonce: ["_fedify", "acceptSignatureNonce"]
2356
+ },
2357
+ queue,
2358
+ actorDispatcher: (_ctx, identifier) => identifier === "someone" ? new Person({ name: "Someone" }) : null,
2359
+ onNotFound: () => new Response("Not found", { status: 404 }),
2360
+ signatureTimeWindow: { minutes: 5 },
2361
+ skipSignatureVerification: false
2362
+ });
2363
+ assertEquals([response.status, await response.text()], [202, "Activity is enqueued."]);
2364
+ if (queuedMessage == null) throw new Error("Inbox message not queued.");
2365
+ const inboxMessage = queuedMessage;
2366
+ assertEquals(inboxMessage, {
2367
+ type: "inbox",
2368
+ id: inboxMessage.id,
2369
+ baseUrl: "https://example.com",
2370
+ activity: unsignedBody,
2371
+ normalizedActivity: await compactJsonLd(unsignedBody, sourceContextLoader),
2372
+ ldSignatureVerified: false,
2373
+ started: inboxMessage.started,
2374
+ attempt: 0,
2375
+ identifier: "someone",
2376
+ traceContext: inboxMessage.traceContext
2377
+ });
1058
2378
  });
1059
2379
  test("respondWithObject()", async () => {
1060
2380
  const response = await respondWithObject(new Note({