Skip to content

Commit e095d6c

Browse files
committed
Polish task payload docs and cover Temporal
Three documentation points raised on the task API review, plus a regression test backing the Temporal claim: - The payload codec round-trips devalue's built-in Temporal types with no extra code, but the supported-payload list omitted them. List `Temporal` (with `Temporal.Instant` / `Temporal.Duration` examples) and add a serialize/deserialize round-trip test so the documented support stays covered. - The vocab import example used the compatibility path `@fedify/fedify/vocab`. Switch it to `@fedify/vocab`, matching the surrounding docs and the current package boundary so copied code does not bind to a path slated for removal. - Task payloads now cross durable queue storage and can hold arbitrary application data. Add a trust-boundary security note to the queue isolation section: treat the backend and payloads as internal trusted storage, pass identifiers the worker resolves rather than long-lived secrets, and use a dedicated task queue with `taskQueueResolution: "strict"` when isolation is required. #803 (comment) #803 (comment) #803 (comment) Assisted-by: Claude Code:claude-opus-4-8
1 parent 94633f1 commit e095d6c

2 files changed

Lines changed: 28 additions & 3 deletions

File tree

docs/manual/tasks.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,17 @@ Task payloads cross a message queue, so they are serialized on enqueue and
5757
deserialized on dispatch. Fedify owns this codec—applications never encode
5858
payloads themselves. The codec is built on [devalue], which means payloads
5959
are not limited to JSON: `Date`, `Map`, `Set`, `URL`, `RegExp`, `bigint`,
60-
typed arrays, circular references, and repeated references all round-trip
61-
faithfully.
60+
typed arrays, `Temporal` values (e.g., `Temporal.Instant`,
61+
`Temporal.Duration`), circular references, and repeated references all
62+
round-trip faithfully.
6263

6364
Activity Vocabulary objects (`Note`, `Create`, `Person`, `Link`, and so on)
6465
are also supported as payload values. Each vocabulary object is bridged
6566
through expanded JSON-LD on the wire and comes back as a real instance, so
6667
the handler can call its methods and getters as usual:
6768

6869
~~~~ typescript
69-
import { Note } from "@fedify/fedify/vocab";
70+
import { Note } from "@fedify/vocab";
7071
import { z } from "zod";
7172

7273
const indexNote = federation.defineTask("indexNote", {
@@ -247,6 +248,14 @@ A task that falls back to the outbox queue needs no dedicated worker; the
247248
outbox worker dispatches every message by its type regardless of which queue
248249
delivered it.
249250

251+
> [!CAUTION]
252+
> Task payloads cross durable queue storage, so treat the queue backend and
253+
> its payloads as internal, trusted storage. Do not place long-lived secrets
254+
> or credentials directly in a task payload; pass an identifier that the
255+
> worker resolves from your application storage instead. When task workloads
256+
> must stay isolated from ActivityPub delivery, give them a dedicated task
257+
> queue and set `taskQueueResolution: "strict"`.
258+
250259

251260
Limitations
252261
-----------

packages/fedify/src/federation/tasks/codec.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,22 @@ test("TaskCodec.serialize() / deserialize()", async (t) => {
210210
strictEqual(decoded.big[length - 1], length - 1);
211211
},
212212
);
213+
214+
await t.step("round-trips Temporal values", async () => {
215+
const payload = {
216+
instant: Temporal.Instant.from("2026-01-02T03:04:05Z"),
217+
duration: Temporal.Duration.from({ hours: 1, minutes: 30 }),
218+
};
219+
const encoded = await codec.serialize(payload);
220+
const decoded = await codec.deserialize(encoded) as {
221+
instant: Temporal.Instant;
222+
duration: Temporal.Duration;
223+
};
224+
ok(decoded.instant instanceof Temporal.Instant);
225+
ok(decoded.instant.equals(payload.instant));
226+
ok(decoded.duration instanceof Temporal.Duration);
227+
strictEqual(decoded.duration.toString(), payload.duration.toString());
228+
});
213229
});
214230

215231
test("TaskCodec (one instance reused across decodes)", async (t) => {

0 commit comments

Comments
 (0)