@worker-protocol/hono 0.1.0

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.
@@ -0,0 +1,169 @@
1
+ import type { activityState, alertSeverity, eventsEntry, eventTypeDeclaration, health } from "@worker-protocol/schemas";
2
+ import type * as z from "zod";
3
+ import type { ActionFacts } from "./actions.ts";
4
+ import type { ErrorCode } from "./codes.ts";
5
+ import type { MetricFacts } from "./metrics.ts";
6
+ import type { TaskFacts, TaskTypes } from "./tasks.ts";
7
+ /**
8
+ * What a Worker author implements, and the whole of it.
9
+ *
10
+ * **Everything this protocol fixes is `mount()`'s.** The addresses, the verbs, the two headers on
11
+ * every response, the error envelope, the page envelope and its cursor, the refusal for a version
12
+ * this Worker cannot speak or a filter it does not know, the bucket boundaries cut in a declared
13
+ * zone, the idempotency window. None of it is a decision a Worker gets to make, so none of it is
14
+ * asked for here.
15
+ *
16
+ * What is left is what only the Worker knows, and it is a short list: who it is, whether a
17
+ * credential is good, how it is doing, which Tasks' conditions hold, how much of something
18
+ * happened, what an Action does. That division is the measure this package is held to —
19
+ * `examples/minimal-worker` is a conformant Worker in under 150 lines, and anything above that
20
+ * line is a rule `mount()` should have carried.
21
+ *
22
+ * A Capability left `undefined` is one the Descriptor does not declare (DESC-2), and its address is
23
+ * not served: a Descriptor naming a Capability that answers nothing is DESC-18's fault, and a
24
+ * Worker built through this interface cannot produce one.
25
+ */
26
+ export type Worker = {
27
+ /**
28
+ * The Worker's own id. DESC-6 and DESC-27: not the URL, and not derived from it — so it is a
29
+ * constant a Worker is deployed with, never anything read from the address at boot.
30
+ */
31
+ id: string;
32
+ /** The edition this Worker speaks (DESC-23). Defaults to the one `@worker-protocol/schemas` encodes. */
33
+ edition?: string;
34
+ /**
35
+ * TASK-31. The Task types this Worker answers, which IS its Skill, keyed by type.
36
+ *
37
+ * Beside the id rather than inside `tasks`, because a Skill is served at no address: it is what
38
+ * this Worker is, and a Capability is what it serves. A Worker that only ANSWERS Tasks declares
39
+ * this and no `tasks` Capability at all.
40
+ *
41
+ * Each entry says what this Worker NEEDS to receive to answer a Task of that type — which is the
42
+ * other half of what the owner's `raises` says it sends, and what a Tower compares to answer
43
+ * "can this Worker take that one's Tasks?" before any work changes hands.
44
+ */
45
+ skills?: Record<string, SkillDeclaration>;
46
+ /**
47
+ * Whether a presented credential is good, on every address this protocol defines (REG-21).
48
+ *
49
+ * `token` is what followed `Bearer ` (REG-3), or `undefined` where nothing readable was
50
+ * presented. `unauthenticated` is `401`, `forbidden` is `403` (ENDP-29), and how the Worker
51
+ * decides is its own. Left out, the Worker reads openly, which `spec/registration.md` permits.
52
+ *
53
+ * It may answer a promise, and it has to: `spec/registration.md` names *a Worker that validates
54
+ * an API key against an identity provider* as the first example of what REG-3 admits, and that
55
+ * is a network call. A signature that could not await it forbade the case the rule was written
56
+ * around, and left a Worker comparing against a secret it was deployed with as the only kind
57
+ * this package could serve.
58
+ */
59
+ authenticate?: (token: string | undefined) => "accepted" | "unauthenticated" | "forbidden" | Promise<"accepted" | "unauthenticated" | "forbidden">;
60
+ /** `health`: the answer to a poll (HLTH-2). HLTH-5 makes it `200` whatever it reports. */
61
+ health?: () => z.infer<typeof health> | Promise<z.infer<typeof health>>;
62
+ /** `metrics`: what the entry declares (MET-1 to MET-6), and the Worker's own values. */
63
+ metrics?: MetricFacts;
64
+ /** `actions`: the Actions this Worker accepts, each with its input and what it does. */
65
+ actions?: ActionFacts;
66
+ /** `alerts`: the Alerts whose conditions hold (ALRT-2). `mount()` orders, serializes and pages. */
67
+ alerts?: () => Alert[] | Promise<Alert[]>;
68
+ /**
69
+ * `activity`: what this Worker is doing and has undertaken to do (ACTV-2).
70
+ *
71
+ * The Worker answers its own domain — an id, a state, when it entered it, a line for a person —
72
+ * and `mount()` carries the rest: the instant's format, the order ENDP-23 requires, the page
73
+ * envelope. It is the consumer's own Fact about its work and not a Claim; `spec/activity.md`
74
+ * holds the argument.
75
+ */
76
+ activity?: () => Activity[] | Promise<Activity[]>;
77
+ /**
78
+ * `nudges`: told that there is work of a Task type this Worker answers (NDG-2).
79
+ *
80
+ * Everything about the call is `mount()`'s, and more of it than usual: the body's shape is NDG-2's
81
+ * rather than this Worker's, a type it declares no Skill for is refused `404` before this is
82
+ * reached (NDG-3), and the answer is `204` because there is nothing to say. What is left is the
83
+ * one thing only the Worker knows — that it should go and read that work sooner than its next
84
+ * sweep would have.
85
+ *
86
+ * It is handed the type and nothing else, and TASK-15 is why: the owner is authoritative over
87
+ * whether the condition still holds, so a Task that travelled here would be a claim that may
88
+ * already be false. The Worker reads, and what it reads is true when it reads it.
89
+ *
90
+ * Declaring it is optional and what it buys is latency. Without it this Worker is told nothing and
91
+ * works from its own schedule, which is slower and never wrong (TASK-19).
92
+ */
93
+ nudges?: (type: string) => void | Promise<void>;
94
+ /**
95
+ * `events`: the entry and nothing else, because there is no address to serve (EVT-11).
96
+ *
97
+ * Each event type's `data` is a Zod object, as an Action's input and a Task's payload are.
98
+ * `mount()` writes the JSON Schema the Descriptor carries, so a Worker declares one shape.
99
+ */
100
+ events?: Omit<z.infer<typeof eventsEntry>, "version" | "address" | "publishes"> & {
101
+ publishes: Record<string, Omit<z.infer<typeof eventTypeDeclaration>, "data"> & {
102
+ data: z.ZodType;
103
+ }>;
104
+ };
105
+ /** `tasks`: what the entry declares (TASK-27, TASK-2 to TASK-4), and which conditions hold. */
106
+ tasks?: {
107
+ /** TASK-2. Every Task type this Worker raises, with its payload schema and answering Actions. */
108
+ raises: TaskTypes;
109
+ } & TaskFacts;
110
+ };
111
+ /**
112
+ * TASK-31. What a Worker declares about one Skill: the payload it needs to receive to answer one.
113
+ *
114
+ * A Zod object, as an Action's input and a Task's payload are, and `mount()` writes the JSON Schema
115
+ * the Descriptor carries. It is this Worker's own requirement — NAME-6 judges it against what an
116
+ * owner sends, and a Tower holding both knows at enrollment whether the work can be read.
117
+ *
118
+ * Optional: `{}` claims the type and says nothing about what it needs, which is what a Worker that
119
+ * takes whatever arrives should say. It costs the check, and nothing else.
120
+ */
121
+ export type SkillDeclaration = {
122
+ /** What this Worker needs to RECEIVE in order to answer one. */
123
+ payload?: z.ZodType;
124
+ /** What this Worker PRODUCES in answer, judged against the owner's answering Action. */
125
+ produces?: z.ZodType;
126
+ };
127
+ /** What a Worker says about a condition an operator should see: the domain, and no more (ALRT-3). */
128
+ export type Alert = {
129
+ /** ALRT-3. The Worker's own id for this Alert. Opaque to everyone else. */
130
+ id: string;
131
+ /** ALRT-4. `warning` or `critical`, and this edition defines no third value. */
132
+ severity: z.infer<typeof alertSeverity>;
133
+ /**
134
+ * ALRT-3. When the condition began — a `Date`, which `mount()` writes as the instant the rule
135
+ * fixes. It is what lets a console tell `this is new` from `this is the same as yesterday`.
136
+ */
137
+ since: Date;
138
+ /** ALRT-3. Human-readable, and parsed by nothing. */
139
+ summary: string;
140
+ /** ALRT-3, ALRT-7. The Actions this Alert offers, by the names the `actions` entry holds. */
141
+ actions: string[];
142
+ };
143
+ /** What a Worker says about one thing it holds: the domain, and the whole of it (ACTV-3). */
144
+ export type Activity = {
145
+ /** ACTV-3. The Worker's own id, opaque to everyone else. */
146
+ id: string;
147
+ /** ACTV-4. `scheduled`, `pending` or `running`. */
148
+ state: z.infer<typeof activityState>;
149
+ /**
150
+ * ACTV-3. When it entered its current state — began running, joined the queue, was undertaken.
151
+ * A Worker that answers `new Date()` here is answering *now* and telling an operator nothing.
152
+ */
153
+ since: Date;
154
+ /** ACTV-3. For a person. Nothing parses it. */
155
+ summary: string;
156
+ };
157
+ /**
158
+ * A refusal. The code fixes the status and the class (ENDP-26), so only the code is chosen and a
159
+ * Worker cannot answer a code under a status the vocabulary does not give it.
160
+ */
161
+ export type Refusal = {
162
+ code: ErrorCode;
163
+ message: string;
164
+ };
165
+ /** A success. `body: null` is no body at all — not the four bytes `null` (ACT-10, ACT-11). */
166
+ export type Answer = {
167
+ status: number;
168
+ body: unknown;
169
+ };
package/dist/worker.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@worker-protocol/hono",
3
+ "version": "0.1.0",
4
+ "workerProtocolEdition": "0.1",
5
+ "description": "The protocol's surface as Hono routes, and mount(): a Worker author implements an interface and gets every address, header and refusal this protocol fixes",
6
+ "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/rowing-tech/worker-protocol.git",
10
+ "directory": "packages/hono"
11
+ },
12
+ "homepage": "https://github.com/rowing-tech/worker-protocol#readme",
13
+ "bugs": "https://github.com/rowing-tech/worker-protocol/issues",
14
+ "publishConfig": {
15
+ "access": "public"
16
+ },
17
+ "type": "module",
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "default": "./dist/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "LICENSE",
29
+ "NOTICE"
30
+ ],
31
+ "dependencies": {
32
+ "@hono/zod-openapi": "1.6.3",
33
+ "@worker-protocol/schemas": "0.1.0"
34
+ },
35
+ "peerDependencies": {
36
+ "hono": "^4.13.7",
37
+ "zod": "^4.5.4"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "26.5.1",
41
+ "hono": "4.13.7",
42
+ "typescript": "7.0.2",
43
+ "vitest": "5.0.0",
44
+ "zod": "4.5.4"
45
+ },
46
+ "scripts": {
47
+ "typecheck": "tsc -p tsconfig.json",
48
+ "build": "tsc -p tsconfig.build.json",
49
+ "test": "vitest run"
50
+ }
51
+ }