@owlmeans/queue 0.1.18-rc.7 → 0.1.18-rc.9

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 (47) hide show
  1. package/README.md +113 -5
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/queue/SKILL.md +178 -12
  4. package/build/bridge.d.ts +28 -0
  5. package/build/bridge.d.ts.map +1 -0
  6. package/build/bridge.js +97 -0
  7. package/build/bridge.js.map +1 -0
  8. package/build/config.d.ts +22 -0
  9. package/build/config.d.ts.map +1 -0
  10. package/build/config.js +41 -0
  11. package/build/config.js.map +1 -0
  12. package/build/consts.d.ts +33 -0
  13. package/build/consts.d.ts.map +1 -0
  14. package/build/consts.js +35 -0
  15. package/build/consts.js.map +1 -0
  16. package/build/errors.d.ts +59 -0
  17. package/build/errors.d.ts.map +1 -0
  18. package/build/errors.js +90 -0
  19. package/build/errors.js.map +1 -0
  20. package/build/index.d.ts +7 -0
  21. package/build/index.d.ts.map +1 -1
  22. package/build/index.js +7 -1
  23. package/build/index.js.map +1 -1
  24. package/build/transport.d.ts +15 -0
  25. package/build/transport.d.ts.map +1 -0
  26. package/build/transport.js +62 -0
  27. package/build/transport.js.map +1 -0
  28. package/build/types.d.ts +240 -0
  29. package/build/types.d.ts.map +1 -0
  30. package/build/types.js +2 -0
  31. package/build/types.js.map +1 -0
  32. package/build/worker.d.ts +27 -0
  33. package/build/worker.d.ts.map +1 -0
  34. package/build/worker.js +58 -0
  35. package/build/worker.js.map +1 -0
  36. package/package.json +10 -1
  37. package/src/bridge.ts +116 -0
  38. package/src/config.ts +56 -0
  39. package/src/consts.ts +39 -0
  40. package/src/errors.ts +106 -0
  41. package/src/index.ts +7 -0
  42. package/src/transport.ts +82 -0
  43. package/src/types.ts +251 -0
  44. package/src/worker.ts +72 -0
  45. package/tests/bridge.spec.ts +64 -0
  46. package/tests/config.spec.ts +66 -0
  47. package/tsconfig.json +1 -0
package/README.md CHANGED
@@ -1,20 +1,128 @@
1
1
  # @owlmeans/queue
2
2
 
3
- Reserved namespace for queue management. Currently a stub with no exports.
3
+ Job queues as resources, and the route protocol that lets a queued call look like any other call.
4
4
 
5
5
  ## Overview
6
6
 
7
- This package is a placeholder for future queue/task processing functionality within the OwlMeans framework.
7
+ This package is contracts only — it carries no broker code. A driver implements them
8
+ (`@owlmeans/redis-queue` does, over BullMQ); depend on this package from a shared contract
9
+ package, and on the driver only where the application wires itself up.
10
+
11
+ The point of it: a route that names `RouteProtocols.QUEUE` is taken by the queue transport, so the
12
+ caller writes `ep.call(...)` exactly as it would for HTTP. Moving a service-to-service call onto
13
+ the broker is a change to one declaration rather than to every call site, and a call that used to
14
+ die with the process survives a restart.
8
15
 
9
16
  ## Installation
10
17
 
11
18
  ```bash
12
- bun add @owlmeans/queue
19
+ bun add @owlmeans/queue@^0.1.18-rc.8
20
+ ```
21
+
22
+ ## Declaring queues
23
+
24
+ Queues and the job names they accept belong in the shared backend package, so producer and
25
+ consumer read the same list:
26
+
27
+ ```typescript
28
+ import { declareQueue, listenQueues } from '@owlmeans/queue'
29
+
30
+ declareQueue(cfg, AGENT_WORK, ['agent:story:develop', 'agent:story:code'], {
31
+ worker: { concurrency: 3, lockDuration: 60_000 }
32
+ })
33
+ ```
34
+
35
+ Which queues a given process consumes is a separate, process-local statement:
36
+
37
+ ```typescript
38
+ listenQueues(cfg, AGENT_WORK, AGENT_OPS) // absent ⇒ this process only produces
39
+ ```
40
+
41
+ A declaration is an address; `listen` is a deployment fact. Keeping them apart is what lets the
42
+ same binary run as a producer in one deployment and a worker in another.
43
+
44
+ ## Declaring a queued entrypoint
45
+
46
+ ```typescript
47
+ import { job } from '@owlmeans/route'
48
+
49
+ entrypoint(
50
+ route(agent.story.develop, '/:id/develop',
51
+ job({ parent: agent.story.base, service: AGENT, queue: AGENT_WORK, timeout: 30_000 })),
52
+ filter(params(StoryParamsSchema))
53
+ )
54
+ ```
55
+
56
+ It is served with `elevate(...)` and called with `call()` like any backend entrypoint. Add
57
+ `reply: false` to return as soon as the job is accepted — the call resolves `Accepted` with
58
+ `{ id, queue }`, which is what a long pipeline wants.
59
+
60
+ ## Jobs are records
61
+
62
+ ```typescript
63
+ const jobs = ctx.jobs<DevelopInput, DevelopResult>(AGENT_WORK)
64
+
65
+ const queued = await jobs.create({ queue: AGENT_WORK, name: 'agent:story:code', data: input })
66
+ const result = await jobs.wait(queued.id, { timeout: 120_000 })
67
+
68
+ const failed = await jobs.list({ state: JobState.Failed })
69
+ await jobs.take(queued.id) // cancel and return
13
70
  ```
14
71
 
72
+ `QueueResource<D, R>` is a `Resource<JobRecord<D, R>>` composed with `PubSubResource<JobEvent<R>>`,
73
+ so criteria, sorting and paging behave as they do for every other backend. Only what the base
74
+ contract cannot express is added: `wait`, `flow` and `counts`.
75
+
76
+ Deriving `JobOptions.id` from what the job is about (`develop:<storyId>`) makes a duplicate
77
+ enqueue a no-op, which is what makes an admission step safe to retry.
78
+
79
+ ## Processors
80
+
81
+ ```typescript
82
+ worker.process(AGENT_WORK, 'agent:story:code', async job => {
83
+ for (const screen of screens) {
84
+ await job.touch()
85
+ await develop(screen)
86
+ }
87
+ return { ok: true }
88
+ })
89
+ ```
90
+
91
+ `touch()` is not optional in a long loop. The broker judges liveness by the lock, so going quiet
92
+ for longer than `lockDuration` is indistinguishable from a dead worker and the job is re-run
93
+ elsewhere while this one is still going.
94
+
95
+ A child job reports a domain failure by RETURNING `{ ok: false, error }`. Throwing means
96
+ "infrastructure broke, retry me". Parents read `children()` and decide — children never fail
97
+ their parent directly.
98
+
99
+ Retries default to a single attempt: this work mostly talks to a model, and a blind retry
100
+ re-spends the tokens that just failed.
101
+
102
+ ## Job graphs
103
+
104
+ ```typescript
105
+ await jobs.flow({
106
+ name: 'story:finalize', data: ref, children: [
107
+ { name: 'story:gate', data: ref, children: [{ name: 'story:code', data: ref }] }
108
+ ]
109
+ })
110
+ ```
111
+
112
+ Nesting is sequence, siblings are parallel, and a parent reading `children()` is the join.
113
+
114
+ ## Errors
115
+
116
+ A refusal the caller must recognise travels in the reply and is rebuilt on the producing side as
117
+ its own class, so `instanceof` keeps working across the broker. `QueueTimeout` means the WAIT
118
+ ended, not the job — read the job back to learn what became of it.
119
+
15
120
  ## Related Packages
16
121
 
17
- - [`@owlmeans/redis-resource`](../redis-resource) — Redis Streams provide queue-like semantics today
122
+ - [`@owlmeans/redis-queue`](../redis-queue) — the BullMQ driver, and where queue integration tests live
123
+ - [`@owlmeans/route`](../route) — `RouteProtocols.QUEUE` and the `job()` builder
124
+ - [`@owlmeans/entrypoint`](../entrypoint) — declarations and the transport seam
125
+ - [`@owlmeans/resource`](../resource) — the `Resource` contract jobs are read through
18
126
 
19
127
  <!-- owlmeans:agent-guidance:start -->
20
128
  ## Agent guidance
@@ -24,7 +132,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
24
132
  your project's skill store (`.agents/skills/`):
25
133
 
26
134
  ```sh
27
- npx @owlmeans/agent-skills
135
+ npx @owlmeans/agent-skills@^0.1.18-rc.12
28
136
  ```
29
137
 
30
138
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/queue",
4
- "version": "0.1.18-rc.7",
5
- "generatedAt": "2026-09-01T16:28:56.998Z",
4
+ "version": "0.1.18-rc.9",
5
+ "generatedAt": "2026-09-04T22:43:25.454Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: queue
3
- description: How to use @owlmeans/queue — abstract job-queue interface (currently a stub) to be implemented by transport-specific queue services. Auto-invoked when importing queue types.
3
+ description: How to use @owlmeans/queue — job queues as resources, the QUEUE route protocol that makes a queued call indistinguishable from an HTTP one, declaring queues and job names, the producer/consumer split, job graphs, envelope freshness, and the rules a processor must follow. Auto-invoked when importing queue types or declaring a job route.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,24 +8,190 @@ user-invocable: false
8
8
  # @owlmeans/queue
9
9
 
10
10
  **Layer:** Infra
11
- **Install:** `"@owlmeans/queue": "^0.1.18-rc.7"` in `dependencies`
11
+ **Install:** `"@owlmeans/queue": "^0.1.18-rc.9"` in `dependencies`
12
12
 
13
- ## Key Exports
13
+ Contracts only. It carries no broker code — a driver package (`@owlmeans/redis-queue`) implements
14
+ them. Depend on this one from a shared contract package; depend on the driver only where the
15
+ application wires itself up.
14
16
 
15
- This package is currently a stub that defines the queue interface. Concrete implementations (Redis BullMQ, RabbitMQ, etc.) will register a service that satisfies this interface.
17
+ ## The one idea
16
18
 
17
- | Export | Description |
18
- |--------|-------------|
19
- | Queue types (placeholder) | Abstract queue interface |
19
+ A queued call is an ordinary entrypoint call. The route names `RouteProtocols.QUEUE`, the
20
+ transport registered under that protocol takes it, and the caller writes `ep.call(...)` exactly as
21
+ for HTTP. Nothing at a call site says "queue".
22
+
23
+ That is what makes it worth having: moving a service-to-service call onto the broker is a change
24
+ to a declaration, not to the places that call it, and the broker holds the work across a restart of
25
+ either side — which an in-process call cannot.
26
+
27
+ ## Declaring
28
+
29
+ Queues and the jobs they accept live in the SHARED backend package, so producers and consumers
30
+ read one list:
31
+
32
+ ```typescript
33
+ declareQueue(cfg, AGENT_WORK, ['agent:story:develop', 'agent:story:code'], {
34
+ worker: { concurrency: 3, lockDuration: 60_000 }
35
+ })
36
+ ```
37
+
38
+ The job list is enforced, not documentation: enqueueing a name the queue does not declare throws
39
+ `UnknownJobName` instead of parking a job nothing can ever process. Re-declaring a name replaces,
40
+ so a helper that runs once per composed app is safe to call twice.
41
+
42
+ Which queues a process CONSUMES is separate, and lives in that process's own config:
43
+
44
+ ```typescript
45
+ listenQueues(cfg, AGENT_WORK, AGENT_OPS) // absent ⇒ producer only
46
+ ```
47
+
48
+ Keep them apart. A declaration is an address; `listen` is a deployment fact. If a worker bound
49
+ whatever it could serve, every deployment of the same binary would consume everything it imports.
50
+
51
+ A queued entrypoint is declared like any other, with `job()` in place of `backend()`:
52
+
53
+ ```typescript
54
+ entrypoint(
55
+ route(agent.story.develop, '/:id/develop',
56
+ job({ parent: agent.story.base, service: AGENT, queue: AGENT_WORK, timeout: 30_000 })),
57
+ filter(params(StoryParamsSchema))
58
+ )
59
+ ```
60
+
61
+ `reply: false` makes it fire-and-forget: the call resolves `Accepted` with `{ id, queue }` as soon
62
+ as the BROKER has accepted the enqueue — before any worker has picked the job up, so the resolution
63
+ says the job exists and nothing about it having started. That is what a long pipeline wants; watch
64
+ the job by its id for anything further. The default is to wait for the value, for
65
+ `req.timeout ?? route.timeout ?? DEFAULT_JOB_TIMEOUT` (60 s).
66
+
67
+ ## Jobs are records
68
+
69
+ `ctx.jobs<D, R>(queue)` returns a `QueueResource<D, R>` — a `Resource<JobRecord<D, R>>` plus
70
+ pub/sub. So inspecting a queue is the resource contract you already know: `create` enqueues,
71
+ `get`/`load`/`list`/`count` inspect, `delete`/`take` cancel, `purge` bulk-cancels. Only what the
72
+ base contract cannot express is added: `wait(id, { timeout })`, `flow(root)`, `counts()`, and
73
+ `close()` for the connections a producer that waited holds.
74
+
75
+ Called with no queue name it answers the SOLE declared queue and throws `UnknownQueue` once there
76
+ are two — an application that grows a second queue is told to name one rather than quietly served
77
+ the wrong backlog. A queue nothing declared throws the same error at the call site.
20
78
 
21
- ## Usage
79
+ Give `JobOptions.id` a value derived from what the job is ABOUT (`develop:<storyId>`) and a
80
+ duplicate enqueue becomes a no-op returning the job already there — no `RecordExists`. That is
81
+ what makes an admission step safe to retry.
82
+
83
+ ## Writing a processor
22
84
 
23
85
  ```typescript
24
- // Reserve the alias for your queue implementation
25
- import type { Queue } from '@owlmeans/queue'
26
- const queue = ctx.service<Queue>('queue')
86
+ const worker = context.service<QueueWorkerService>(DEFAULT_ALIAS)
87
+
88
+ worker.process(AGENT_WORK, 'agent:story:code', async job => {
89
+ for (const screen of screens) {
90
+ await job.touch() // ← not optional
91
+ await develop(screen)
92
+ }
93
+ return { ok: true }
94
+ })
27
95
  ```
28
96
 
97
+ Registering against a queue this process does not `listen` to throws `QueueNotListening` — the
98
+ question is a deployment one, so it is answered at wiring time rather than when a job arrives.
99
+
100
+ Three rules, each of which has a failure mode attached:
101
+
102
+ - **`touch()` in every long loop.** The broker judges liveness by the lock. Silence for longer
103
+ than `lockDuration` is indistinguishable from a dead worker, so the job is handed to someone
104
+ else while this one is still working — you get two runs, not one. `progress(value)` reports
105
+ without renewing, and the value must be JSON-carryable.
106
+ - **Children report, parents decide.** A child returns `{ ok: false, error }` for a DOMAIN
107
+ failure; it does not throw. Throwing means "infrastructure broke, retry me". A child that
108
+ exhausts its attempts does not fail its parent: the parent runs and reads `children()` /
109
+ `failedChildren()` — both keyed by bare job id — and decides. Give the children of a flow that
110
+ spans queues distinct ids, because that re-keying collapses two ids that are equal across queues.
111
+ - **Retries default to one attempt** (`DEFAULT_ATTEMPTS`). These jobs mostly talk to a model, and
112
+ a blind retry re-spends the tokens that just failed. Raise `attempts` per queue only where jobs
113
+ are cheap and idempotent.
114
+
115
+ `job.signal` is the queue's abort signal, and it is NOT an interrupt. The driver stops by closing
116
+ every worker first — which waits for the jobs in flight to return — and aborts only afterwards, so
117
+ a processor cannot bail out on it and does not need to. An orderly stop drains; an outright kill is
118
+ covered by the rule below.
119
+
120
+ ## Being restartable
121
+
122
+ A worker can die mid-job; the lock expires and the step re-runs. So a processor has to be safe to
123
+ run twice — skip work already recorded, delete the rows a previous attempt created, or accept
124
+ regenerating over a baseline. Decide this per job and say so in a comment; there is no way to make
125
+ it automatic.
126
+
127
+ ## A queued call is still a guarded call
128
+
129
+ `handleJob` rebuilds the request from the envelope and runs the entrypoint as the HTTP boundary
130
+ would: the declared guards are tried in order, the first that matches authorizes, the entity is
131
+ attached, and only then does the handler run. A queued entrypoint with guards therefore needs the
132
+ producer's credentials on the envelope's `headers`, and an unauthorized job answers
133
+ `AuthorizationError`. An alias this process does not serve answers `JobNotServed` — the job was
134
+ taken by a worker that has no handler for it.
135
+
136
+ Set `cfg.queue.envelopeTtl` (seconds) to bound how long a job envelope stays acceptable. The age is
137
+ measured from the envelope's `enqueuedAt` — when the PRODUCER enqueued it, not when a worker picked
138
+ it up — so a captured envelope stops being replayable, and so does a job that sat behind a backlog
139
+ longer than the window: it answers `EnvelopeExpired` at pickup rather than running late. Size the
140
+ window against the longest backlog you are willing to let run, and leave `envelopeTtl` unset — the
141
+ default — for a queue whose jobs may legitimately wait.
142
+
143
+ ## Errors cross the hop as classes
144
+
145
+ A refusal the caller must recognise — `ContentRefused`, `AgentLocked` — is marshalled into the
146
+ reply and rebuilt on the producer side as its own class, so `catch (e) { if (e instanceof ...) }`
147
+ keeps working across the broker. Only broker- and connection-level faults propagate as throws,
148
+ because those are the only ones a retry can fix.
149
+
150
+ ## What runs where
151
+
152
+ `queueWorkerMiddleware()` starts the worker at the **Ready** stage — processors register while the
153
+ application wires up, and binding earlier would take jobs the process cannot yet run. A process
154
+ that listens to nothing registers no worker and pays nothing.
155
+
156
+ `canServeModule` in `@owlmeans/server-api/utils` excludes QUEUE routes, the same way it excludes
157
+ sockets: the worker takes those jobs off the broker, and mounting them on the HTTP server too would
158
+ answer every call twice. `servedJobs` is the mirror image — the entrypoints this process both serves and
159
+ listens to, grouped by queue, which is what a driver binds.
160
+
161
+ ## Key exports
162
+
163
+ | Export | Description |
164
+ |--------|-------------|
165
+ | `declareQueue` / `listenQueues` | Configuration — what exists, and what this process consumes |
166
+ | `queueOf` / `queueOfJob` / `isListening` | Reading it back; `queueOf` throws `UnknownQueue` |
167
+ | `QueueConfig` / `QueueDeclaration` / `QueueWorkerOptions` / `JobOptions` | The configuration shapes |
168
+ | `QueueResource<D, R>` | A queue addressed as a resource, plus `wait` / `flow` / `counts` / `close` |
169
+ | `JobRecord<D, R>` / `JobState` / `isSettled` / `JobEvent` / `JobEventType` | The record shape and lifecycle |
170
+ | `FlowSpec<D>` | A graph node — `name`, optional `queue`, `data`, `opts`, `children` |
171
+ | `QueueWorkerService` | `process(queue, name, fn)`, `start`, `stop`, `listening`, `hooks` |
172
+ | `JobContext<D>` | What a processor gets: `id`, `name`, `queue`, `attempt`, `data`, `signal`, `touch`, `progress`, `children`, `failedChildren` |
173
+ | `QueueHooks` | `wrapHandler`, `onJobResult`, `onJobStalled`, `onJobDead` — the compensation seam |
174
+ | `QueueAppend` | The `ctx.jobs(queue?)` mixin a driver installs; `QueueDriver` is what a driver supplies |
175
+ | `appendQueueTransport` / `makeQueueTransport` | Binds the QUEUE protocol so `call()` routes through the broker |
176
+ | `queueWorkerMiddleware` | Starts the worker at Ready stage |
177
+ | `handleJob` / `servedJobs` / `entrypointProcessor` | The bridge a driver dispatches through |
178
+ | `requestOf` / `assertFresh` / `JobEnvelope` / `JobReply` | The envelope, and rebuilding a request from it |
179
+ | Errors | `QueueError`, `QueueTimeout`, `UnknownJob`, `UnknownJobName`, `UnknownQueue`, `QueueNotListening`, `JobNotServed`, `EnvelopeExpired` |
180
+ | Constants | `DEFAULT_ALIAS` (`queue`), `DEFAULT_JOB_TIMEOUT` (60 000 ms), `DEFAULT_ATTEMPTS` (1) |
181
+
29
182
  ## Depends On
30
183
 
31
- - `@owlmeans/context` — service registration
184
+ - `@owlmeans/context` — service registration and the middleware stage
185
+ - `@owlmeans/entrypoint` — the transport seam and the served-entrypoint dispatch
186
+ - `@owlmeans/route` — `RouteProtocols.QUEUE` and the `job()` builder
187
+ - `@owlmeans/resource` — the `Resource` contract jobs are read through
188
+ - `@owlmeans/auth` / `@owlmeans/auth-common` — guarding a queued call and attaching its entity
189
+ - `@owlmeans/error` — marshalling a refusal across the hop
190
+
191
+ ## Related
192
+
193
+ - `redis-queue` — the BullMQ driver; integration tests for queue behaviour live there
194
+ - `server-job` / `client-job` — exposing a queue's jobs to an application's UI (list, cancel,
195
+ progress over a socket), without touching the contracts here
196
+ - `entrypoint` — declarations, elevation, and the transport lookup
197
+ - `resource` — the criteria and paging semantics `list` follows
@@ -0,0 +1,28 @@
1
+ import type { AbstractRequest } from '@owlmeans/entrypoint';
2
+ import type { BasicContext } from '@owlmeans/context';
3
+ import type { Config, Context, JobContext, JobEnvelope, JobReply } from './types.js';
4
+ /**
5
+ * Rebuild the request the producer described. A queued call is still a call: the same guards, the
6
+ * same filter, the same handler run against it — only the wire is different.
7
+ */
8
+ export declare const requestOf: (envelope: JobEnvelope, path: string, context?: BasicContext<any>) => AbstractRequest;
9
+ /**
10
+ * Freshness is judged from when the job was ENQUEUED, not from now.
11
+ *
12
+ * A signed envelope proves who produced it, and a replay window keeps a captured one from being
13
+ * useful forever. But a job can legitimately sit behind a long backlog, and judging it on pickup
14
+ * would reject exactly the work that a busy queue delayed — so the producer's timestamp is what
15
+ * the window applies to.
16
+ *
17
+ * @throws {EnvelopeExpired}
18
+ */
19
+ export declare const assertFresh: (envelope: JobEnvelope, ttl?: number) => void;
20
+ /**
21
+ * Run one queued entrypoint call and describe the outcome.
22
+ *
23
+ * It never throws for a DOMAIN failure — the error is marshalled into the returned reply so the
24
+ * producer can rebuild it as its own class, and so the broker does not count a legitimate refusal
25
+ * as a job to retry. Infrastructure failures are left to propagate: those are what retries are for.
26
+ */
27
+ export declare const handleJob: <C extends Config, T extends Context<C>>(context: T, job: JobContext<JobEnvelope>) => Promise<JobReply>;
28
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAkC,MAAM,sBAAsB,CAAA;AAC3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAMrD,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAGpF;;;GAGG;AACH,eAAO,MAAM,SAAS,aACV,WAAW,QAAQ,MAAM,YAAY,YAAY,CAAC,GAAG,CAAC,KAC/D,eAWD,CAAA;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,aAAc,WAAW,QAAQ,MAAM,KAAG,IAQjE,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GAAU,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,WAC3D,CAAC,OAAO,UAAU,CAAC,WAAW,CAAC,KACvC,OAAO,CAAC,QAAQ,CAyDlB,CAAA"}
@@ -0,0 +1,97 @@
1
+ import { EntrypointOutcome, provideResponse } from '@owlmeans/entrypoint';
2
+ import { AuthorizationError } from '@owlmeans/auth';
3
+ import { attachEntity } from '@owlmeans/auth-common';
4
+ import { ResilientError } from '@owlmeans/error';
5
+ import { EnvelopeExpired, JobNotServed } from './errors.js';
6
+ /**
7
+ * Rebuild the request the producer described. A queued call is still a call: the same guards, the
8
+ * same filter, the same handler run against it — only the wire is different.
9
+ */
10
+ export const requestOf = (envelope, path, context) => ({
11
+ alias: envelope.alias,
12
+ params: (envelope.params ?? {}),
13
+ body: envelope.body,
14
+ headers: (envelope.headers ?? {}),
15
+ query: (envelope.query ?? {}),
16
+ path,
17
+ // Handlers reach their context through `original._ctx`, which the HTTP boundary sets on the raw
18
+ // Fastify request. There is no raw request here, so the shape is supplied deliberately — a
19
+ // handler must not have to know which transport delivered it.
20
+ original: context != null ? { _ctx: context } : undefined,
21
+ });
22
+ /**
23
+ * Freshness is judged from when the job was ENQUEUED, not from now.
24
+ *
25
+ * A signed envelope proves who produced it, and a replay window keeps a captured one from being
26
+ * useful forever. But a job can legitimately sit behind a long backlog, and judging it on pickup
27
+ * would reject exactly the work that a busy queue delayed — so the producer's timestamp is what
28
+ * the window applies to.
29
+ *
30
+ * @throws {EnvelopeExpired}
31
+ */
32
+ export const assertFresh = (envelope, ttl) => {
33
+ if (ttl == null || envelope.enqueuedAt == null) {
34
+ return;
35
+ }
36
+ const age = (Date.now() - new Date(envelope.enqueuedAt).getTime()) / 1000;
37
+ if (age > ttl) {
38
+ throw new EnvelopeExpired(`${envelope.alias}: ${Math.round(age)}s > ${ttl}s`);
39
+ }
40
+ };
41
+ /**
42
+ * Run one queued entrypoint call and describe the outcome.
43
+ *
44
+ * It never throws for a DOMAIN failure — the error is marshalled into the returned reply so the
45
+ * producer can rebuild it as its own class, and so the broker does not count a legitimate refusal
46
+ * as a job to retry. Infrastructure failures are left to propagate: those are what retries are for.
47
+ */
48
+ export const handleJob = async (context, job) => {
49
+ const envelope = job.data;
50
+ try {
51
+ assertFresh(envelope, context.cfg.queue?.envelopeTtl);
52
+ const entrypoint = context.entrypoint(envelope.alias);
53
+ if (entrypoint.handle == null) {
54
+ throw new JobNotServed(envelope.alias);
55
+ }
56
+ const request = requestOf(envelope, entrypoint.path(), context);
57
+ const response = provideResponse();
58
+ const guards = entrypoint.getGuards();
59
+ if (guards.length > 0) {
60
+ let matched;
61
+ for (const alias of guards) {
62
+ const guard = context.service(alias);
63
+ if (await guard.match(request, response)) {
64
+ matched = guard;
65
+ break;
66
+ }
67
+ }
68
+ if (matched == null) {
69
+ throw new AuthorizationError(`queue:${envelope.alias}`);
70
+ }
71
+ const auth = provideResponse();
72
+ if (!await matched.handle(request, auth)) {
73
+ throw new AuthorizationError(`queue:${envelope.alias}:${matched.alias}`);
74
+ }
75
+ request.auth = auth.value;
76
+ const entity = await attachEntity(context, request);
77
+ if (entity != null) {
78
+ request.entity = entity;
79
+ }
80
+ }
81
+ await entrypoint.handle(request, response);
82
+ if (response.error != null) {
83
+ return { error: ResilientError.marshal(response.error).message };
84
+ }
85
+ return { value: response.value, outcome: response.outcome ?? EntrypointOutcome.Ok };
86
+ }
87
+ catch (e) {
88
+ // A refusal the caller must see as its own class — `ContentRefused`, `AgentLocked` — travels
89
+ // in the reply. Only a broker- or connection-level fault should ever reach the retry logic:
90
+ // retrying a refusal just spends the work again to be refused identically.
91
+ if (e instanceof ResilientError) {
92
+ return { error: ResilientError.marshal(e).message };
93
+ }
94
+ throw e;
95
+ }
96
+ };
97
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Config, JobOptions, QueueDeclaration, QueueWorkerOptions } from './types.js';
2
+ /**
3
+ * Add a queue to the shared configuration. Every process in the monorepo loads the same
4
+ * declarations — a producer needs them to know a queue exists and what it accepts, a worker needs
5
+ * them to bind. Declaring twice replaces, so a helper that runs per app is safe to call.
6
+ */
7
+ export declare const declareQueue: <C extends Config>(cfg: C, name: string, jobs: string[], opts?: {
8
+ worker?: QueueWorkerOptions;
9
+ defaults?: JobOptions;
10
+ }) => C;
11
+ /**
12
+ * Name the queues this process consumes. It is what turns an otherwise identical binary into a
13
+ * worker, which is why it lives in the process's own config and never in a declaration.
14
+ */
15
+ export declare const listenQueues: <C extends Config>(cfg: C, ...names: string[]) => C;
16
+ /**
17
+ * @throws {UnknownQueue}
18
+ */
19
+ export declare const queueOf: <C extends Config>(cfg: C, name: string) => QueueDeclaration;
20
+ export declare const queueOfJob: <C extends Config>(cfg: C, job: string) => QueueDeclaration | undefined;
21
+ export declare const isListening: <C extends Config>(cfg: C, name: string) => boolean;
22
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAG1F;;;;GAIG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,OACtC,CAAC,QAAQ,MAAM,QAAQ,MAAM,EAAE,SAAS;IAAE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,UAAU,CAAA;CAAE,KAClG,CAcF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,YAAY,MAAM,EAAE,KAAG,CAM3E,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,QAAQ,MAAM,KAAG,gBAQhE,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,OAAO,MAAM,KAAG,gBAAgB,GAAG,SACpB,CAAA;AAElE,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,OAAO,CAAC,QAAQ,MAAM,KAAG,OACzB,CAAA"}
@@ -0,0 +1,41 @@
1
+ import { UnknownQueue } from './errors.js';
2
+ /**
3
+ * Add a queue to the shared configuration. Every process in the monorepo loads the same
4
+ * declarations — a producer needs them to know a queue exists and what it accepts, a worker needs
5
+ * them to bind. Declaring twice replaces, so a helper that runs per app is safe to call.
6
+ */
7
+ export const declareQueue = (cfg, name, jobs, opts) => {
8
+ const queue = { name, jobs, ...opts };
9
+ const queues = cfg.queue?.queues ?? [];
10
+ const existing = queues.findIndex(declared => declared.name === name);
11
+ if (existing < 0) {
12
+ queues.push(queue);
13
+ }
14
+ else {
15
+ queues[existing] = queue;
16
+ }
17
+ cfg.queue = { ...cfg.queue, queues };
18
+ return cfg;
19
+ };
20
+ /**
21
+ * Name the queues this process consumes. It is what turns an otherwise identical binary into a
22
+ * worker, which is why it lives in the process's own config and never in a declaration.
23
+ */
24
+ export const listenQueues = (cfg, ...names) => {
25
+ const listen = new Set([...(cfg.queue?.listen ?? []), ...names]);
26
+ cfg.queue = { ...cfg.queue, listen: [...listen] };
27
+ return cfg;
28
+ };
29
+ /**
30
+ * @throws {UnknownQueue}
31
+ */
32
+ export const queueOf = (cfg, name) => {
33
+ const queue = cfg.queue?.queues?.find(declared => declared.name === name);
34
+ if (queue == null) {
35
+ throw new UnknownQueue(name);
36
+ }
37
+ return queue;
38
+ };
39
+ export const queueOfJob = (cfg, job) => cfg.queue?.queues?.find(declared => declared.jobs.includes(job));
40
+ export const isListening = (cfg, name) => cfg.queue?.listen?.includes(name) === true;
41
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,GAAM,EAAE,IAAY,EAAE,IAAc,EAAE,IAA6D,EAChG,EAAE;IACL,MAAM,KAAK,GAAqB,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,CAAA;IACvD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAA;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAErE,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC;SAAM,CAAC;QACN,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAA;IAC1B,CAAC;IAED,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAA;IAEpC,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAmB,GAAM,EAAE,GAAG,KAAe,EAAK,EAAE;IAC9E,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,CAAA;IAEhE,GAAG,CAAC,KAAK,GAAG,EAAE,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,CAAA;IAEjD,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAmB,GAAM,EAAE,IAAY,EAAoB,EAAE;IAClF,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;IAEzE,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,YAAY,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,CAAmB,GAAM,EAAE,GAAW,EAAgC,EAAE,CAChG,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;AAElE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAmB,GAAM,EAAE,IAAY,EAAW,EAAE,CAC7E,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,CAAA"}
@@ -0,0 +1,33 @@
1
+ export declare const DEFAULT_ALIAS = "queue";
2
+ /**
3
+ * The state a job is in, as the broker reports it. `Waiting` covers both a job queued normally
4
+ * and one held by an unfinished parent — the difference matters to the broker, never to a caller
5
+ * asking whether an answer has arrived.
6
+ */
7
+ export declare enum JobState {
8
+ Waiting = "waiting",
9
+ Delayed = "delayed",
10
+ Active = "active",
11
+ Completed = "completed",
12
+ Failed = "failed",
13
+ Unknown = "unknown"
14
+ }
15
+ /** A job has reached a state it will not leave on its own. */
16
+ export declare const isSettled: (state?: JobState) => boolean;
17
+ export declare enum JobEventType {
18
+ Completed = "completed",
19
+ Failed = "failed",
20
+ Progress = "progress"
21
+ }
22
+ /**
23
+ * How long a caller waits for a reply when neither the route nor the request says. Long enough
24
+ * for work that talks to a model, short enough that a lost job surfaces as an error rather than a
25
+ * hung request.
26
+ */
27
+ export declare const DEFAULT_JOB_TIMEOUT = 60000;
28
+ /**
29
+ * Retries default to one attempt: the jobs here are mostly LLM work, and a blind retry re-spends
30
+ * the tokens that just failed. A queue whose jobs are cheap and idempotent raises it per queue.
31
+ */
32
+ export declare const DEFAULT_ATTEMPTS = 1;
33
+ //# sourceMappingURL=consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,UAAU,CAAA;AAEpC;;;;GAIG;AACH,oBAAY,QAAQ;IAClB,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,OAAO,YAAY;CACpB;AAED,8DAA8D;AAC9D,eAAO,MAAM,SAAS,WAAY,QAAQ,KAAG,OACc,CAAA;AAE3D,oBAAY,YAAY;IACtB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,QAAQ,aAAa;CACtB;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,QAAS,CAAA;AAEzC;;;GAGG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAA"}
@@ -0,0 +1,35 @@
1
+ export const DEFAULT_ALIAS = 'queue';
2
+ /**
3
+ * The state a job is in, as the broker reports it. `Waiting` covers both a job queued normally
4
+ * and one held by an unfinished parent — the difference matters to the broker, never to a caller
5
+ * asking whether an answer has arrived.
6
+ */
7
+ export var JobState;
8
+ (function (JobState) {
9
+ JobState["Waiting"] = "waiting";
10
+ JobState["Delayed"] = "delayed";
11
+ JobState["Active"] = "active";
12
+ JobState["Completed"] = "completed";
13
+ JobState["Failed"] = "failed";
14
+ JobState["Unknown"] = "unknown";
15
+ })(JobState || (JobState = {}));
16
+ /** A job has reached a state it will not leave on its own. */
17
+ export const isSettled = (state) => state === JobState.Completed || state === JobState.Failed;
18
+ export var JobEventType;
19
+ (function (JobEventType) {
20
+ JobEventType["Completed"] = "completed";
21
+ JobEventType["Failed"] = "failed";
22
+ JobEventType["Progress"] = "progress";
23
+ })(JobEventType || (JobEventType = {}));
24
+ /**
25
+ * How long a caller waits for a reply when neither the route nor the request says. Long enough
26
+ * for work that talks to a model, short enough that a lost job surfaces as an error rather than a
27
+ * hung request.
28
+ */
29
+ export const DEFAULT_JOB_TIMEOUT = 60_000;
30
+ /**
31
+ * Retries default to one attempt: the jobs here are mostly LLM work, and a blind retry re-spends
32
+ * the tokens that just failed. A queue whose jobs are cheap and idempotent raises it per queue.
33
+ */
34
+ export const DEFAULT_ATTEMPTS = 1;
35
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG,OAAO,CAAA;AAEpC;;;;GAIG;AACH,MAAM,CAAN,IAAY,QAOX;AAPD,WAAY,QAAQ;IAClB,+BAAmB,CAAA;IACnB,+BAAmB,CAAA;IACnB,6BAAiB,CAAA;IACjB,mCAAuB,CAAA;IACvB,6BAAiB,CAAA;IACjB,+BAAmB,CAAA;AACrB,CAAC,EAPW,QAAQ,KAAR,QAAQ,QAOnB;AAED,8DAA8D;AAC9D,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAgB,EAAW,EAAE,CACrD,KAAK,KAAK,QAAQ,CAAC,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC,MAAM,CAAA;AAE3D,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,uCAAuB,CAAA;IACvB,iCAAiB,CAAA;IACjB,qCAAqB,CAAA;AACvB,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAA;AAEzC;;;GAGG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA"}