@fedify/fedify 2.4.0-dev.1490 → 2.4.0-dev.1508

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 (109) hide show
  1. package/README.md +7 -5
  2. package/dist/{assert-OguE97r2.mjs → assert-DvVHoIZ0.mjs} +1 -1
  3. package/dist/{assert_instance_of-DBC5X09g.mjs → assert_instance_of-CxoC8bVq.mjs} +1 -1
  4. package/dist/{assert_not_equals-DkVK8oqV.mjs → assert_not_equals-C8vcTTA3.mjs} +1 -1
  5. package/dist/{assert_rejects-DN60FHPX.mjs → assert_rejects-DAdaaIWM.mjs} +2 -2
  6. package/dist/{assert_strict_equals-XEgZAlrj.mjs → assert_strict_equals-BzqyKuYL.mjs} +1 -1
  7. package/dist/{assert_throws-BOkhLGYc.mjs → assert_throws-zN79lgIC.mjs} +1 -1
  8. package/dist/{builder-CwwQRArg.mjs → builder-DlWCek6Z.mjs} +23 -3
  9. package/dist/codec-CmlPqjNX.mjs +103 -0
  10. package/dist/compat/mod.d.cts +1 -1
  11. package/dist/compat/mod.d.ts +1 -1
  12. package/dist/compat/outgoing-jsonld.test.mjs +3 -3
  13. package/dist/compat/public-audience.test.mjs +3 -3
  14. package/dist/compat/transformers.test.mjs +5 -5
  15. package/dist/{context-DVoTs_wM.mjs → context-BBRTgkrs.mjs} +8 -2
  16. package/dist/{context-BU6jSQdo.d.ts → context-BF5B4ZDk.d.ts} +529 -259
  17. package/dist/{context-BBVLF7lx.d.cts → context-CYF8X0ft.d.cts} +530 -259
  18. package/dist/{deno-Yohf1ISx.mjs → deno-nexucTAX.mjs} +1 -1
  19. package/dist/{docloader-B6DFWEOx.mjs → docloader-DAdEN549.mjs} +3 -3
  20. package/dist/federation/builder.test.mjs +5 -5
  21. package/dist/federation/circuit-breaker.test.mjs +5 -5
  22. package/dist/federation/collection.test.mjs +3 -3
  23. package/dist/federation/handler.test.mjs +13 -13
  24. package/dist/federation/idempotency.test.mjs +6 -6
  25. package/dist/federation/inbox.test.mjs +3 -3
  26. package/dist/federation/keycache.test.mjs +5 -5
  27. package/dist/federation/kv.test.mjs +3 -3
  28. package/dist/federation/metrics.test.mjs +4 -4
  29. package/dist/federation/middleware.test.mjs +200 -19
  30. package/dist/federation/mod.cjs +3 -259
  31. package/dist/federation/mod.d.cts +4 -4
  32. package/dist/federation/mod.d.ts +4 -4
  33. package/dist/federation/mod.js +1 -257
  34. package/dist/federation/mq.test.mjs +103 -261
  35. package/dist/federation/negotiation.test.mjs +4 -4
  36. package/dist/federation/retry.test.mjs +3 -3
  37. package/dist/federation/router.test.mjs +5 -5
  38. package/dist/federation/send.test.mjs +10 -10
  39. package/dist/federation/tasks/codec.test.d.mts +2 -0
  40. package/dist/federation/tasks/codec.test.mjs +292 -0
  41. package/dist/federation/tasks/enqueue.test.d.mts +2 -0
  42. package/dist/federation/tasks/enqueue.test.mjs +992 -0
  43. package/dist/federation/tasks/tasks.test.d.mts +2 -0
  44. package/dist/federation/tasks/tasks.test.mjs +555 -0
  45. package/dist/federation/temporal.test.mjs +4 -4
  46. package/dist/federation/webfinger.test.mjs +6 -6
  47. package/dist/{http-C3eJp_Ha.cjs → http-BCj9dlA_.cjs} +1 -1
  48. package/dist/{http-BqSm-f2G.js → http-Dv3147TL.js} +1 -1
  49. package/dist/{http-BeDlAH97.mjs → http-inQqS3yx.mjs} +4 -4
  50. package/dist/{key-DNBTcjuV.mjs → key-CGrr6-I2.mjs} +2 -2
  51. package/dist/{kv-cache-Crq8nvSV.js → kv-cache-C2oTEXyb.js} +1 -1
  52. package/dist/{kv-cache-b2HaCLC-.mjs → kv-cache-C3qkFkbt.mjs} +1 -1
  53. package/dist/{kv-cache-CGJMNSs-.cjs → kv-cache-c8X6svwE.cjs} +1 -1
  54. package/dist/{ld-CQlFhsU6.mjs → ld-fnWaNT8y.mjs} +3 -3
  55. package/dist/{metrics-B6RKtTLt.mjs → metrics-C5hMINAa.mjs} +1 -1
  56. package/dist/{middleware-CQygcRYE.mjs → middleware-CQlq6ykI.mjs} +1 -1
  57. package/dist/{middleware-CBM9vGYg.cjs → middleware-DeVzbg1I.cjs} +690 -30
  58. package/dist/{middleware-CsxoclmN.mjs → middleware-Dl2nyVgB.mjs} +306 -46
  59. package/dist/{middleware-B3vFLcCh.js → middleware-vQ_ZpgcX.js} +679 -31
  60. package/dist/{mod-vPYVoa5n.d.ts → mod-4WGAuR6X.d.ts} +1 -1
  61. package/dist/{mod-C0F6kvgS.d.cts → mod-BHEB3xhE.d.cts} +1 -1
  62. package/dist/mod.cjs +6 -6
  63. package/dist/mod.d.cts +5 -5
  64. package/dist/mod.d.ts +5 -5
  65. package/dist/mod.js +5 -5
  66. package/dist/mq-03vS-C9P.mjs +276 -0
  67. package/dist/{mq-D8uSFzxe.d.cts → mq-B5_hohs7.d.ts} +34 -0
  68. package/dist/{mq-D-nlpY04.d.ts → mq-DXMt_qCY.d.cts} +34 -0
  69. package/dist/nodeinfo/client.test.mjs +3 -3
  70. package/dist/nodeinfo/handler.test.mjs +5 -5
  71. package/dist/nodeinfo/types.test.mjs +4 -4
  72. package/dist/otel/exporter.test.mjs +3 -3
  73. package/dist/{outgoing-jsonld-L_DbOaFe.mjs → outgoing-jsonld-BNL8AC14.mjs} +1 -1
  74. package/dist/{owner-B1FnbZ1w.mjs → owner-Bq2eZyO_.mjs} +2 -2
  75. package/dist/{proof-ChmZL5rY.cjs → proof-Buz7I8wj.cjs} +1 -1
  76. package/dist/{proof-BhNTn0HE.js → proof-CTG9azxN.js} +1 -1
  77. package/dist/{proof-BUuvq0ia.mjs → proof-DZCjcFCd.mjs} +5 -5
  78. package/dist/{send-Dq-Ofquk.mjs → send-B253S5gN.mjs} +3 -3
  79. package/dist/sig/accept.test.mjs +1 -1
  80. package/dist/sig/http.test.mjs +8 -8
  81. package/dist/sig/key.test.mjs +6 -6
  82. package/dist/sig/ld.test.mjs +7 -7
  83. package/dist/sig/mod.cjs +2 -2
  84. package/dist/sig/mod.js +2 -2
  85. package/dist/sig/owner.test.mjs +6 -6
  86. package/dist/sig/proof.test.mjs +8 -8
  87. package/dist/{std__assert-BBjXFNOb.mjs → std__assert-Dh6uLf_q.mjs} +4 -4
  88. package/dist/{temporal-C-vrfOld.mjs → temporal-d6TshGjq.mjs} +1 -1
  89. package/dist/testing/mod.d.mts +1037 -8
  90. package/dist/testing/mod.mjs +3 -2
  91. package/dist/testing-Dy-WhqpN.mjs +77 -0
  92. package/dist/utils/docloader.test.mjs +6 -6
  93. package/dist/utils/kv-cache.test.mjs +1 -1
  94. package/dist/utils/mod.cjs +1 -1
  95. package/dist/utils/mod.js +1 -1
  96. package/package.json +9 -7
  97. /package/dist/{accept-CPkZzmGN.mjs → accept-CgDcxvjV.mjs} +0 -0
  98. /package/dist/{activity-listener-tztVvlNb.mjs → activity-listener-BeTGV3wc.mjs} +0 -0
  99. /package/dist/{assert_equals-C-ZRDbaf.mjs → assert_equals-PBVKNMJd.mjs} +0 -0
  100. /package/dist/{circuit-breaker-CSWsyoef.mjs → circuit-breaker-hJBB6jwA.mjs} +0 -0
  101. /package/dist/{client-ByXmQhYD.mjs → client-B_A6mfn3.mjs} +0 -0
  102. /package/dist/{collection-Cc3DVAhE.mjs → collection-Dp5ky45w.mjs} +0 -0
  103. /package/dist/{keycache-BeU0LCII.mjs → keycache-CaOR6NYg.mjs} +0 -0
  104. /package/dist/{keys-DGu1NFwu.mjs → keys-CSYsOMFG.mjs} +0 -0
  105. /package/dist/{kv-rV3vodCc.mjs → kv-CIvR3Non.mjs} +0 -0
  106. /package/dist/{negotiation-DDstyBvc.mjs → negotiation-m_L2nyS3.mjs} +0 -0
  107. /package/dist/{public-audience-Cvbr2Gzt.mjs → public-audience-c9zmYKgA.mjs} +0 -0
  108. /package/dist/{retry-CXg_MBI-.mjs → retry-DI4O-zYl.mjs} +0 -0
  109. /package/dist/{types-J53Kw7so.mjs → types-BFowWFTT.mjs} +0 -0
@@ -0,0 +1,292 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { t as TaskCodec } from "../../codec-CmlPqjNX.mjs";
5
+ import { Create, Link, Note, Person } from "@fedify/vocab";
6
+ import { mockDocumentLoader, test } from "@fedify/fixture";
7
+ import { deepStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
8
+ //#region src/federation/tasks/codec.test.ts
9
+ const loaders = {
10
+ contextLoader: mockDocumentLoader,
11
+ documentLoader: mockDocumentLoader
12
+ };
13
+ const codec = new TaskCodec(loaders);
14
+ function makeSchema(check) {
15
+ return { "~standard": {
16
+ version: 1,
17
+ vendor: "fedify-test",
18
+ validate(value) {
19
+ return check(value) ? { value } : { issues: [{ message: "Invalid task data." }] };
20
+ }
21
+ } };
22
+ }
23
+ test("TaskCodec.serialize() / deserialize()", async (t) => {
24
+ const note = new Note({
25
+ id: new URL("https://example.com/notes/1"),
26
+ content: "Hello, world!"
27
+ });
28
+ const person = new Person({
29
+ id: new URL("https://example.com/users/alice"),
30
+ name: "Alice"
31
+ });
32
+ const link = new Link({
33
+ href: new URL("https://example.com/"),
34
+ mediaType: "text/html"
35
+ });
36
+ const create = new Create({
37
+ id: new URL("https://example.com/activities/1"),
38
+ actor: new URL("https://example.com/users/alice"),
39
+ object: note
40
+ });
41
+ await t.step("round-trips a mixed payload", async () => {
42
+ const payload = {
43
+ note,
44
+ when: /* @__PURE__ */ new Date("2026-01-02T03:04:05Z"),
45
+ big: 1234567890123456789n,
46
+ url: new URL("https://example.com/some/path"),
47
+ list: [person, link],
48
+ map: new Map([["create", create], ["n", 42]]),
49
+ set: new Set([
50
+ 1,
51
+ 2,
52
+ 3
53
+ ]),
54
+ nested: { create }
55
+ };
56
+ const encoded = await codec.serialize(payload);
57
+ strictEqual(typeof encoded, "string");
58
+ const decoded = await codec.deserialize(encoded);
59
+ ok(decoded.note instanceof Note);
60
+ strictEqual(decoded.note.content?.toString(), "Hello, world!");
61
+ strictEqual(decoded.note.id?.href, "https://example.com/notes/1");
62
+ ok(decoded.when instanceof Date);
63
+ strictEqual(decoded.when.toISOString(), "2026-01-02T03:04:05.000Z");
64
+ strictEqual(decoded.big, 1234567890123456789n);
65
+ ok(decoded.url instanceof URL);
66
+ strictEqual(decoded.url.href, "https://example.com/some/path");
67
+ const list = decoded.list;
68
+ ok(list[0] instanceof Person);
69
+ strictEqual(list[0].name?.toString(), "Alice");
70
+ ok(list[1] instanceof Link);
71
+ strictEqual(list[1].href?.href, "https://example.com/");
72
+ const map = decoded.map;
73
+ ok(map.get("create") instanceof Create);
74
+ strictEqual(map.get("n"), 42);
75
+ deepStrictEqual(decoded.set, new Set([
76
+ 1,
77
+ 2,
78
+ 3
79
+ ]));
80
+ const nested = decoded.nested;
81
+ ok(nested.create instanceof Create);
82
+ const nestedObject = await nested.create.getObject({
83
+ documentLoader: mockDocumentLoader,
84
+ contextLoader: mockDocumentLoader
85
+ });
86
+ ok(nestedObject instanceof Note);
87
+ strictEqual(nestedObject.content?.toString(), "Hello, world!");
88
+ });
89
+ await t.step("encodes vocab objects in expand form (no @context)", async () => {
90
+ ok(!(await codec.serialize({ note })).includes("@context"));
91
+ });
92
+ await t.step("leaves a non-vocab payload untouched", async () => {
93
+ const payload = {
94
+ text: "plain",
95
+ n: 1,
96
+ flag: true,
97
+ nothing: null,
98
+ when: /* @__PURE__ */ new Date("2026-06-10T00:00:00Z"),
99
+ list: [
100
+ 1,
101
+ "two",
102
+ 3n
103
+ ]
104
+ };
105
+ const encoded = await codec.serialize(payload);
106
+ deepStrictEqual(await codec.deserialize(encoded), payload);
107
+ });
108
+ await t.step("throws on a malformed wire string", async () => {
109
+ await rejects(async () => await codec.deserialize("garbage"));
110
+ });
111
+ await t.step("preserves circular and repeated references", async () => {
112
+ const shared = new Note({ content: "shared" });
113
+ const payload = {
114
+ name: "root",
115
+ notes: [shared, shared]
116
+ };
117
+ payload.self = payload;
118
+ const encoded = await codec.serialize(payload);
119
+ const decoded = await codec.deserialize(encoded);
120
+ strictEqual(decoded.self, decoded);
121
+ ok(decoded.notes[0] instanceof Note);
122
+ strictEqual(decoded.notes[0], decoded.notes[1]);
123
+ strictEqual(decoded.notes[0].content?.toString(), "shared");
124
+ });
125
+ await t.step("preserves a cycle through an array", async () => {
126
+ const list = ["head"];
127
+ list.push(list);
128
+ const encoded = await codec.serialize({ list });
129
+ const decoded = await codec.deserialize(encoded);
130
+ strictEqual(decoded.list[0], "head");
131
+ strictEqual(decoded.list[1], decoded.list);
132
+ });
133
+ await t.step("preserves cycles re-entering at a Map and a Set", async () => {
134
+ const set = /* @__PURE__ */ new Set();
135
+ set.add({ set });
136
+ const map = /* @__PURE__ */ new Map();
137
+ map.set("entry", { map });
138
+ const encoded = await codec.serialize({
139
+ set,
140
+ map
141
+ });
142
+ const decoded = await codec.deserialize(encoded);
143
+ const [member] = decoded.set;
144
+ strictEqual(member.set, decoded.set);
145
+ strictEqual(decoded.map.get("entry")?.map, decoded.map);
146
+ });
147
+ await t.step("revives a vocab object nested in a null-prototype object", async () => {
148
+ const nullProto = Object.create(null);
149
+ nullProto.note = note;
150
+ const encoded = await codec.serialize({ wrap: nullProto });
151
+ const decoded = await codec.deserialize(encoded);
152
+ ok(decoded.wrap.note instanceof Note);
153
+ strictEqual(decoded.wrap.note.content?.toString(), "Hello, world!");
154
+ });
155
+ await t.step("revives a payload nested far deeper than any fixed depth cap", async () => {
156
+ const depth = 1e3;
157
+ let payload = new Note({ content: "deep" });
158
+ for (let i = 0; i < depth; i++) payload = i % 2 === 0 ? { inner: payload } : [payload];
159
+ const encoded = await codec.serialize(payload);
160
+ let decoded = await codec.deserialize(encoded);
161
+ for (let i = depth - 1; i >= 0; i--) decoded = i % 2 === 0 ? decoded.inner : decoded[0];
162
+ ok(decoded instanceof Note);
163
+ strictEqual(decoded.content?.toString(), "deep");
164
+ });
165
+ await t.step("round-trips an array too large to spread into push()", async () => {
166
+ const length = 2e5;
167
+ const payload = { big: Array.from({ length }, (_, i) => i) };
168
+ const encoded = await codec.serialize(payload);
169
+ const decoded = await codec.deserialize(encoded);
170
+ strictEqual(decoded.big.length, length);
171
+ strictEqual(decoded.big[0], 0);
172
+ strictEqual(decoded.big[length - 1], length - 1);
173
+ });
174
+ await t.step({
175
+ name: "round-trips Temporal values",
176
+ ignore: "Bun" in globalThis,
177
+ async fn() {
178
+ const payload = {
179
+ instant: Temporal.Instant.from("2026-01-02T03:04:05Z"),
180
+ duration: Temporal.Duration.from({
181
+ hours: 1,
182
+ minutes: 30
183
+ })
184
+ };
185
+ const encoded = await codec.serialize(payload);
186
+ const decoded = await codec.deserialize(encoded);
187
+ ok(decoded.instant instanceof Temporal.Instant);
188
+ ok(decoded.instant.equals(payload.instant));
189
+ ok(decoded.duration instanceof Temporal.Duration);
190
+ strictEqual(decoded.duration.toString(), payload.duration.toString());
191
+ }
192
+ });
193
+ });
194
+ test("TaskCodec (one instance reused across decodes)", async (t) => {
195
+ await t.step("two sequential decodes stay independent", async () => {
196
+ const codec = new TaskCodec(loaders);
197
+ const first = await codec.serialize({ note: new Note({ content: "A" }) });
198
+ const second = await codec.serialize({ note: new Note({ content: "B" }) });
199
+ const a = await codec.deserialize(first);
200
+ const b = await codec.deserialize(second);
201
+ ok(a.note instanceof Note);
202
+ ok(b.note instanceof Note);
203
+ strictEqual(a.note.content?.toString(), "A");
204
+ strictEqual(b.note.content?.toString(), "B");
205
+ });
206
+ });
207
+ test("TaskCodec.validate()", async (t) => {
208
+ const schema = makeSchema((data) => typeof data === "object" && data != null && data.note instanceof Note && typeof data.title === "string");
209
+ await t.step("accepts a payload with a vocab instanceof leaf", async () => {
210
+ const payload = {
211
+ note: new Note({ content: "Hi" }),
212
+ title: "greeting"
213
+ };
214
+ deepStrictEqual(await TaskCodec.validate(schema, payload), payload);
215
+ });
216
+ await t.step("rejects a wrong-shaped payload", async () => {
217
+ await rejects(() => TaskCodec.validate(schema, {
218
+ note: "not a Note",
219
+ title: 42
220
+ }), {
221
+ name: "TypeError",
222
+ message: /Task data failed schema validation/
223
+ });
224
+ });
225
+ await t.step("supports async validation", async () => {
226
+ const asyncSchema = { "~standard": {
227
+ version: 1,
228
+ vendor: "fedify-test",
229
+ validate: (value) => Promise.resolve(typeof value === "number" ? { value } : { issues: [{ message: "not a number" }] })
230
+ } };
231
+ strictEqual(await TaskCodec.validate(asyncSchema, 42), 42);
232
+ await rejects(() => TaskCodec.validate(asyncSchema, "nope"));
233
+ });
234
+ await t.step("round-trip then validate (same schema on both sides)", async () => {
235
+ const payload = {
236
+ note: new Note({ content: "Hi" }),
237
+ title: "greeting"
238
+ };
239
+ const encoded = await codec.serialize(payload);
240
+ const decoded = await codec.deserialize(encoded);
241
+ const validated = await TaskCodec.validate(schema, decoded);
242
+ ok(validated.note instanceof Note);
243
+ strictEqual(validated.title, "greeting");
244
+ strictEqual(validated.note.content?.toString(), "Hi");
245
+ });
246
+ });
247
+ test("TaskCodec.encode() / decode()", async (t) => {
248
+ const schema = makeSchema((data) => typeof data === "object" && data != null && data.note instanceof Note && typeof data.title === "string");
249
+ await t.step("encode() validates then serializes; decode() round-trips", async () => {
250
+ const payload = {
251
+ note: new Note({ content: "Hi" }),
252
+ title: "greeting"
253
+ };
254
+ const wire = await codec.encode(schema, payload);
255
+ strictEqual(typeof wire, "string");
256
+ const back = await codec.decode(schema, wire);
257
+ ok(back.note instanceof Note);
258
+ strictEqual(back.note.content?.toString(), "Hi");
259
+ strictEqual(back.title, "greeting");
260
+ });
261
+ await t.step("encode() rejects a wrong-shaped payload", async () => {
262
+ await rejects(() => codec.encode(schema, {
263
+ note: "nope",
264
+ title: 42
265
+ }), {
266
+ name: "TypeError",
267
+ message: /Task data failed schema validation/
268
+ });
269
+ });
270
+ await t.step("decode() re-validates and rejects a drifted payload", async () => {
271
+ const loose = makeSchema((_data) => true);
272
+ const wire = await codec.encode(loose, { note: "not a note" });
273
+ await rejects(() => codec.decode(schema, wire), {
274
+ name: "TypeError",
275
+ message: /Task data failed schema validation/
276
+ });
277
+ });
278
+ await t.step("a non-idempotent (transforming) schema fails to round-trip", async () => {
279
+ const transforming = { "~standard": {
280
+ version: 1,
281
+ vendor: "fedify-test",
282
+ validate: (value) => typeof value === "string" ? { value: value.length } : { issues: [{ message: "Expected a string." }] }
283
+ } };
284
+ const wire = await codec.encode(transforming, "hello");
285
+ await rejects(() => codec.decode(transforming, wire), {
286
+ name: "TypeError",
287
+ message: /Task data failed schema validation/
288
+ });
289
+ });
290
+ });
291
+ //#endregion
292
+ export {};
@@ -0,0 +1,2 @@
1
+ import { Temporal } from "@js-temporal/polyfill";
2
+ import { URLPattern } from "urlpattern-polyfill";