@btravstack/amqp-worker 0.8.0 → 0.9.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.
- package/README.md +29 -31
- package/dist/index.cjs +19 -13
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +19 -13
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -50,37 +50,35 @@ import { ErrAsync, OkAsync, P } from "unthrown";
|
|
|
50
50
|
// built by di from the use cases it lists — no injected context — on the
|
|
51
51
|
// starter's own handlers port, typed by the contract (a consumer serves one
|
|
52
52
|
// handlers record, so there is nothing to name).
|
|
53
|
-
const orderHandlers = AmqpHandlers(orderContract)(
|
|
54
|
-
{ placeOrder: PlaceOrder },
|
|
55
|
-
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
(error) => new NonRetryableError(error._tag, error),
|
|
72
|
-
),
|
|
73
|
-
)
|
|
74
|
-
// Infrastructure failing is not: recover the defect into a retry.
|
|
75
|
-
.recoverDefect((cause) =>
|
|
76
|
-
ErrAsync(new RetryableError("placing the order failed", cause)),
|
|
53
|
+
const orderHandlers = AmqpHandlers(orderContract)({
|
|
54
|
+
inject: { placeOrder: PlaceOrder },
|
|
55
|
+
sync: ({ placeOrder }) => ({
|
|
56
|
+
orderNotifications: (message) =>
|
|
57
|
+
placeOrder
|
|
58
|
+
.execute(
|
|
59
|
+
TenantId(message.payload.tenantId),
|
|
60
|
+
message.payload.id,
|
|
61
|
+
message.payload.payload?.quantity ?? 0,
|
|
62
|
+
)
|
|
63
|
+
.map(() => undefined)
|
|
64
|
+
// A modeled domain error is permanent: dead-letter it, no retry.
|
|
65
|
+
.mapErrCases((matcher) =>
|
|
66
|
+
matcher.with(
|
|
67
|
+
P.tag("InvalidQuantity"),
|
|
68
|
+
P.tag("InvalidOrderId"),
|
|
69
|
+
P.tag("DuplicateOrder"),
|
|
70
|
+
(error) => new NonRetryableError(error._tag, error),
|
|
77
71
|
),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
)
|
|
73
|
+
// Infrastructure failing is not: recover the defect into a retry.
|
|
74
|
+
.recoverDefect((cause) =>
|
|
75
|
+
ErrAsync(new RetryableError("placing the order failed", cause)),
|
|
76
|
+
),
|
|
77
|
+
// Every consumer the contract declares must be covered — an uncovered
|
|
78
|
+
// key is refused at this call, not on the first delivery.
|
|
79
|
+
orderAudit: () => OkAsync(undefined),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
84
82
|
|
|
85
83
|
const Worker = AmqpModule("Worker")({
|
|
86
84
|
needs: [Env],
|
|
@@ -99,7 +97,7 @@ call, not on the first delivery. `runtimeInfo()` reads `{ queues }` back once
|
|
|
99
97
|
consuming.
|
|
100
98
|
|
|
101
99
|
A worker with several consumers can be several slices instead of one record:
|
|
102
|
-
`AmqpHandler(contract, key)({ name: Dep }, arm)` mints a provider for ONE consumer or
|
|
100
|
+
`AmqpHandler(contract, key)({ inject: { name: Dep }, ...arm })` mints a provider for ONE consumer or
|
|
103
101
|
rpc, typed by the key alone, and `AmqpHandlers(contract)([...])` composes an
|
|
104
102
|
array of them into the same handlers provider `AmqpModule` takes — the array
|
|
105
103
|
must cover every key the contract declares, and each piece's own port must
|
package/dist/index.cjs
CHANGED
|
@@ -116,26 +116,29 @@ const amqp = (options) => {
|
|
|
116
116
|
return (0, _btravstack_di.Module)("Amqp")({
|
|
117
117
|
needs: [_btravstack_config.Env, AmqpHandlersPort],
|
|
118
118
|
provides: [config, (0, _btravstack_di.Provider)(AmqpRuntime)({
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
119
|
+
inject: {
|
|
120
|
+
config: AmqpConfig,
|
|
121
|
+
handlers: AmqpHandlersPort
|
|
122
|
+
},
|
|
123
|
+
sync: ({ config: bound, handlers }) => ({
|
|
124
|
+
name: "amqp",
|
|
125
|
+
resolves: [],
|
|
126
|
+
start: (host) => createWorker(host, bound, options, handlers)
|
|
127
|
+
})
|
|
128
|
+
})],
|
|
126
129
|
exports: [AmqpRuntime, AmqpConfig]
|
|
127
130
|
});
|
|
128
131
|
};
|
|
129
132
|
/**
|
|
130
|
-
* The handlers as a provider, from the contract.
|
|
133
|
+
* The handlers as a provider, from the contract. Two call forms, one port.
|
|
131
134
|
*
|
|
132
135
|
* ```ts
|
|
133
|
-
* AmqpHandlers(orderContract)({ logger: Logger },
|
|
136
|
+
* AmqpHandlers(orderContract)({ inject: { logger: Logger }, sync: ({ logger }) => ({ orderNotifications: (m) => … }) })
|
|
134
137
|
* AmqpHandlers(orderContract)([orderNotifications, orderAudit])
|
|
135
138
|
* ```
|
|
136
139
|
*
|
|
137
|
-
* The first
|
|
138
|
-
* typed for the contract. The
|
|
140
|
+
* The first is di's own `Provider(port)` on the starter's handlers port
|
|
141
|
+
* typed for the contract. The second takes the pieces `AmqpHandler(contract,
|
|
139
142
|
* key)` builds: they are the provider's deps, keyed by the contract key each
|
|
140
143
|
* piece's port id carries, so the services record IS the handlers record. Every
|
|
141
144
|
* declared key must be covered, and two slices claiming one key are di's
|
|
@@ -143,8 +146,11 @@ const amqp = (options) => {
|
|
|
143
146
|
*/
|
|
144
147
|
const AmqpHandlers = (contract) => {
|
|
145
148
|
const build = (0, _btravstack_di.Provider)(AmqpHandlersPort);
|
|
146
|
-
const compose = (pieces) => build(
|
|
147
|
-
|
|
149
|
+
const compose = (pieces) => build({
|
|
150
|
+
inject: Object.fromEntries(pieces.map((piece) => [piece.port.portId.slice(12), piece.port])),
|
|
151
|
+
sync: (services) => services
|
|
152
|
+
});
|
|
153
|
+
return ((first) => Array.isArray(first) ? compose(first) : build(first));
|
|
148
154
|
};
|
|
149
155
|
const startFailed = (cause) => new _btravstack_core.RuntimeStartFailed({
|
|
150
156
|
runtime: "amqp",
|
package/dist/index.d.cts
CHANGED
|
@@ -123,15 +123,15 @@ type Compose<C extends AnyAmqpContract> = <const T extends readonly PieceOf<C>[]
|
|
|
123
123
|
readonly port: HandlersPortOf<C>;
|
|
124
124
|
};
|
|
125
125
|
/**
|
|
126
|
-
* The handlers as a provider, from the contract.
|
|
126
|
+
* The handlers as a provider, from the contract. Two call forms, one port.
|
|
127
127
|
*
|
|
128
128
|
* ```ts
|
|
129
|
-
* AmqpHandlers(orderContract)({ logger: Logger },
|
|
129
|
+
* AmqpHandlers(orderContract)({ inject: { logger: Logger }, sync: ({ logger }) => ({ orderNotifications: (m) => … }) })
|
|
130
130
|
* AmqpHandlers(orderContract)([orderNotifications, orderAudit])
|
|
131
131
|
* ```
|
|
132
132
|
*
|
|
133
|
-
* The first
|
|
134
|
-
* typed for the contract. The
|
|
133
|
+
* The first is di's own `Provider(port)` on the starter's handlers port
|
|
134
|
+
* typed for the contract. The second takes the pieces `AmqpHandler(contract,
|
|
135
135
|
* key)` builds: they are the provider's deps, keyed by the contract key each
|
|
136
136
|
* piece's port id carries, so the services record IS the handlers record. Every
|
|
137
137
|
* declared key must be covered, and two slices claiming one key are di's
|
package/dist/index.d.mts
CHANGED
|
@@ -123,15 +123,15 @@ type Compose<C extends AnyAmqpContract> = <const T extends readonly PieceOf<C>[]
|
|
|
123
123
|
readonly port: HandlersPortOf<C>;
|
|
124
124
|
};
|
|
125
125
|
/**
|
|
126
|
-
* The handlers as a provider, from the contract.
|
|
126
|
+
* The handlers as a provider, from the contract. Two call forms, one port.
|
|
127
127
|
*
|
|
128
128
|
* ```ts
|
|
129
|
-
* AmqpHandlers(orderContract)({ logger: Logger },
|
|
129
|
+
* AmqpHandlers(orderContract)({ inject: { logger: Logger }, sync: ({ logger }) => ({ orderNotifications: (m) => … }) })
|
|
130
130
|
* AmqpHandlers(orderContract)([orderNotifications, orderAudit])
|
|
131
131
|
* ```
|
|
132
132
|
*
|
|
133
|
-
* The first
|
|
134
|
-
* typed for the contract. The
|
|
133
|
+
* The first is di's own `Provider(port)` on the starter's handlers port
|
|
134
|
+
* typed for the contract. The second takes the pieces `AmqpHandler(contract,
|
|
135
135
|
* key)` builds: they are the provider's deps, keyed by the contract key each
|
|
136
136
|
* piece's port id carries, so the services record IS the handlers record. Every
|
|
137
137
|
* declared key must be covered, and two slices claiming one key are di's
|
package/dist/index.mjs
CHANGED
|
@@ -115,26 +115,29 @@ const amqp = (options) => {
|
|
|
115
115
|
return Module("Amqp")({
|
|
116
116
|
needs: [Env, AmqpHandlersPort],
|
|
117
117
|
provides: [config, Provider(AmqpRuntime)({
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
inject: {
|
|
119
|
+
config: AmqpConfig,
|
|
120
|
+
handlers: AmqpHandlersPort
|
|
121
|
+
},
|
|
122
|
+
sync: ({ config: bound, handlers }) => ({
|
|
123
|
+
name: "amqp",
|
|
124
|
+
resolves: [],
|
|
125
|
+
start: (host) => createWorker(host, bound, options, handlers)
|
|
126
|
+
})
|
|
127
|
+
})],
|
|
125
128
|
exports: [AmqpRuntime, AmqpConfig]
|
|
126
129
|
});
|
|
127
130
|
};
|
|
128
131
|
/**
|
|
129
|
-
* The handlers as a provider, from the contract.
|
|
132
|
+
* The handlers as a provider, from the contract. Two call forms, one port.
|
|
130
133
|
*
|
|
131
134
|
* ```ts
|
|
132
|
-
* AmqpHandlers(orderContract)({ logger: Logger },
|
|
135
|
+
* AmqpHandlers(orderContract)({ inject: { logger: Logger }, sync: ({ logger }) => ({ orderNotifications: (m) => … }) })
|
|
133
136
|
* AmqpHandlers(orderContract)([orderNotifications, orderAudit])
|
|
134
137
|
* ```
|
|
135
138
|
*
|
|
136
|
-
* The first
|
|
137
|
-
* typed for the contract. The
|
|
139
|
+
* The first is di's own `Provider(port)` on the starter's handlers port
|
|
140
|
+
* typed for the contract. The second takes the pieces `AmqpHandler(contract,
|
|
138
141
|
* key)` builds: they are the provider's deps, keyed by the contract key each
|
|
139
142
|
* piece's port id carries, so the services record IS the handlers record. Every
|
|
140
143
|
* declared key must be covered, and two slices claiming one key are di's
|
|
@@ -142,8 +145,11 @@ const amqp = (options) => {
|
|
|
142
145
|
*/
|
|
143
146
|
const AmqpHandlers = (contract) => {
|
|
144
147
|
const build = Provider(AmqpHandlersPort);
|
|
145
|
-
const compose = (pieces) => build(
|
|
146
|
-
|
|
148
|
+
const compose = (pieces) => build({
|
|
149
|
+
inject: Object.fromEntries(pieces.map((piece) => [piece.port.portId.slice(12), piece.port])),
|
|
150
|
+
sync: (services) => services
|
|
151
|
+
});
|
|
152
|
+
return ((first) => Array.isArray(first) ? compose(first) : build(first));
|
|
147
153
|
};
|
|
148
154
|
const startFailed = (cause) => new RuntimeStartFailed({
|
|
149
155
|
runtime: "amqp",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@btravstack/amqp-worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The AMQP starter for @btravstack/core: the handlers as a port, one unit per delivery, and a drain the kernel's deadline releases",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"amqp",
|
|
@@ -57,19 +57,19 @@
|
|
|
57
57
|
"unthrown": "5.7.0",
|
|
58
58
|
"vitest": "4.1.11",
|
|
59
59
|
"zod": "4.5.1",
|
|
60
|
-
"@btravstack/config": "0.
|
|
61
|
-
"@btravstack/core": "0.
|
|
62
|
-
"@btravstack/di": "0.
|
|
60
|
+
"@btravstack/config": "0.9.0",
|
|
61
|
+
"@btravstack/core": "0.9.0",
|
|
62
|
+
"@btravstack/di": "0.9.0",
|
|
63
63
|
"@btravstack/internal-test-infra": "0.0.0",
|
|
64
|
-
"@btravstack/testing": "0.
|
|
64
|
+
"@btravstack/testing": "0.9.0"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"@amqp-contract/worker": "^3.0.0-beta.6",
|
|
68
68
|
"@opentelemetry/api": "^1",
|
|
69
69
|
"unthrown": "^5.0.0",
|
|
70
|
-
"@btravstack/config": "^0.
|
|
71
|
-
"@btravstack/core": "^0.
|
|
72
|
-
"@btravstack/di": "^0.
|
|
70
|
+
"@btravstack/config": "^0.9.0",
|
|
71
|
+
"@btravstack/core": "^0.9.0",
|
|
72
|
+
"@btravstack/di": "^0.9.0"
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=22"
|