@aotter/mantle 0.1.0-alpha.9 → 0.1.2-alpha.2
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 +38 -38
- package/dist/cli/generate.d.ts +10 -1
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +49 -4
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/main.js +0 -8
- package/dist/cli/main.js.map +1 -1
- package/dist/codegen/emitMantleModule.js +8 -0
- package/dist/codegen/emitMantleModule.js.map +1 -1
- package/docs/adapter-guide.md +23 -26
- package/docs/adr/0008-structured-diagnostic-shape.md +7 -2
- package/docs/adr/0010-locale-and-translates.md +14 -16
- package/docs/adr/0011-adapter-port-spec.md +2 -1
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +100 -0
- package/docs/adr/0018-core-starters-repository-boundary.md +3 -0
- package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +38 -2
- package/docs/adr/0020-builtin-handler-contracts-and-matched-upsert.md +199 -0
- package/docs/adr/0021-retire-starter-scaffolding.md +30 -0
- package/docs/adr/0022-caller-observed-version-occ.md +212 -0
- package/docs/adr/0023-port-failure-contract.md +69 -0
- package/docs/adr/0024-manifest-native-schema-tables.md +260 -0
- package/docs/adr/README.md +19 -3
- package/docs/adr/adr-lite-803-request-diagnostics.md +38 -0
- package/docs/adr/adr-lite-808-route-readiness.md +47 -0
- package/docs/adr/adr-lite-809-bounded-public-content.md +71 -0
- package/docs/adr/adr-lite-812-native-parity.md +150 -0
- package/docs/adr/adr-lite-823-home-statistics.md +64 -0
- package/docs/adr/adr-lite-842-mcp-authoring.md +30 -0
- package/docs/adr/adr-lite-861-admin-webmcp.md +42 -0
- package/docs/adr/adr-lite-909-admin-ui-kit.md +37 -0
- package/docs/api-mcp-authorization.md +3 -664
- package/docs/auth-hosting-model.md +4 -4
- package/docs/cloudflare-low-level-composition.md +3 -104
- package/docs/deferred-lifecycle-queues.md +2 -243
- package/docs/design-atoms.md +16 -796
- package/docs/direct-authoring.md +5 -0
- package/docs/examples/minimal-worker/README.md +37 -0
- package/docs/examples/minimal-worker/manifests/site.yaml +25 -0
- package/docs/examples/minimal-worker/package.json +26 -0
- package/docs/examples/minimal-worker/smoke.mjs +23 -0
- package/docs/examples/minimal-worker/src/index.ts +4 -0
- package/docs/examples/minimal-worker/tsconfig.json +17 -0
- package/docs/examples/minimal-worker/wrangler.jsonc +22 -0
- package/docs/handbook/cloudflare/authentication.md +167 -0
- package/docs/handbook/cloudflare/bindings.md +236 -0
- package/docs/handbook/cloudflare/conventional-worker.md +162 -0
- package/docs/handbook/cloudflare/deferred-hooks-queues.md +171 -0
- package/docs/handbook/cloudflare/deploy-and-operate.md +105 -0
- package/docs/handbook/cloudflare/low-level-composition.md +169 -0
- package/docs/handbook/cloudflare/media-r2.md +216 -0
- package/docs/handbook/cloudflare/public-web.md +144 -0
- package/docs/handbook/concepts/authorization.md +116 -0
- package/docs/handbook/concepts/four-atoms.md +173 -0
- package/docs/handbook/concepts/lifecycle-and-locales.md +142 -0
- package/docs/handbook/concepts/mcp-and-agents.md +109 -0
- package/docs/handbook/concepts/procedures-and-triggers.md +221 -0
- package/docs/handbook/concepts/runtime-and-adapters.md +177 -0
- package/docs/handbook/concepts/views.md +191 -0
- package/docs/handbook/examples/commerce-transaction.md +813 -0
- package/docs/handbook/examples/guarded-api.md +427 -0
- package/docs/handbook/examples/intake-form.md +319 -0
- package/docs/handbook/examples/legal-documents.md +218 -0
- package/docs/handbook/examples/procurement-approvals.md +240 -0
- package/docs/handbook/examples/publication.md +240 -0
- package/docs/handbook/examples/reservation.md +220 -0
- package/docs/handbook/navigation.json +63 -0
- package/docs/handbook/reference/authorization.md +295 -0
- package/docs/handbook/reference/diagnostics.md +183 -0
- package/docs/handbook/reference/manifest.md +132 -0
- package/docs/handbook/reference/procedure.md +263 -0
- package/docs/handbook/reference/schema.md +250 -0
- package/docs/handbook/reference/site-config.md +233 -0
- package/docs/handbook/reference/surface.md +207 -0
- package/docs/handbook/reference/trigger.md +193 -0
- package/docs/handbook/reference/view.md +256 -0
- package/docs/handbook/start/project-and-cli.md +122 -0
- package/docs/handbook/start/quickstart-worker.md +193 -0
- package/docs/labels.md +3 -1
- package/docs/media-uploads.md +3 -184
- package/docs/migration-0.1.2.md +60 -3
- package/docs/performance-harness.md +127 -10
- package/docs/release-process.md +85 -231
- package/docs/schema-indexes.md +3 -180
- package/docs/sealed-pipeline-ownership.md +6 -5
- package/docs/spec-only-host-adoption.md +158 -0
- package/docs/transaction-patterns.md +5 -0
- package/package.json +21 -25
- package/skills/README.md +4 -4
- package/skills/develop/SKILL.md +23 -28
- package/skills/install/SKILL.md +55 -147
- package/skills/provision/SKILL.md +4 -3
- package/skills/theme/SKILL.md +6 -6
- package/skills/update/SKILL.md +32 -68
- package/dist/cli/create.d.ts +0 -2
- package/dist/cli/create.d.ts.map +0 -1
- package/dist/cli/create.js +0 -243
- package/dist/cli/create.js.map +0 -1
- package/dist/cli/update.d.ts +0 -2
- package/dist/cli/update.d.ts.map +0 -1
- package/dist/cli/update.js +0 -413
- package/dist/cli/update.js.map +0 -1
- package/dist/provision/renderProvisionBundle.d.ts +0 -70
- package/dist/provision/renderProvisionBundle.d.ts.map +0 -1
- package/dist/provision/renderProvisionBundle.js +0 -367
- package/dist/provision/renderProvisionBundle.js.map +0 -1
- package/dist/provision.d.ts +0 -2
- package/dist/provision.d.ts.map +0 -1
- package/dist/provision.js +0 -2
- package/dist/provision.js.map +0 -1
|
@@ -1,106 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Cloudflare low-level composition
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
assembly. This copyable fixture keeps Mantle's standard bindings, Auth,
|
|
5
|
-
Admin/API routes, OAuth/MCP dispatch, cache policy and redacted error boundary,
|
|
6
|
-
while adding one application-owned post-response Queue audit across every route.
|
|
3
|
+
This guide moved into the handbook: [`docs/handbook/cloudflare/low-level-composition.md`](handbook/cloudflare/low-level-composition.md).
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
import { Hono } from "hono";
|
|
10
|
-
import {
|
|
11
|
-
applyCachePolicy,
|
|
12
|
-
conventionalMcpResource,
|
|
13
|
-
createMantleRuntimeRef,
|
|
14
|
-
createConventionalAuth,
|
|
15
|
-
createConventionalBindings,
|
|
16
|
-
createMcpApiHandler,
|
|
17
|
-
mountAuthorize,
|
|
18
|
-
mountAdmin,
|
|
19
|
-
mountRuntimeEndpoints,
|
|
20
|
-
runMantleWorkerRequest,
|
|
21
|
-
setupIncompleteAuthResponse,
|
|
22
|
-
type MantleCloudflareEnv,
|
|
23
|
-
} from "@aotter/mantle/cloudflare";
|
|
24
|
-
import { plan } from "../.mantle/generated/mantle.js";
|
|
25
|
-
|
|
26
|
-
interface Env extends MantleCloudflareEnv {
|
|
27
|
-
readonly AUDIT_QUEUE: Queue<{
|
|
28
|
-
readonly kind: "request-complete";
|
|
29
|
-
readonly path: string;
|
|
30
|
-
readonly status: number;
|
|
31
|
-
}>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let assembled: ReturnType<typeof assemble> | undefined;
|
|
35
|
-
|
|
36
|
-
export default {
|
|
37
|
-
fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
38
|
-
return runMantleWorkerRequest(async () => {
|
|
39
|
-
assembled ??= assemble(env);
|
|
40
|
-
const incomplete = await setupIncompleteAuthResponse(request, assembled.auth);
|
|
41
|
-
const response = incomplete ?? await assembled.fetch(request, env, ctx);
|
|
42
|
-
ctx.waitUntil(env.AUDIT_QUEUE.send({
|
|
43
|
-
kind: "request-complete",
|
|
44
|
-
path: new URL(request.url).pathname,
|
|
45
|
-
status: response.status,
|
|
46
|
-
}));
|
|
47
|
-
return response;
|
|
48
|
-
});
|
|
49
|
-
},
|
|
50
|
-
} satisfies ExportedHandler<Env>;
|
|
51
|
-
|
|
52
|
-
function assemble(env: Env) {
|
|
53
|
-
const bindings = createConventionalBindings(env);
|
|
54
|
-
const auth = createConventionalAuth(env);
|
|
55
|
-
const ref = createMantleRuntimeRef({ plan, bindings, auth });
|
|
56
|
-
const app = new Hono<{ Bindings: Env }>();
|
|
57
|
-
|
|
58
|
-
mountRuntimeEndpoints(app, ref);
|
|
59
|
-
if (bindings.adminAssets) mountAdmin(app, ref, bindings.adminAssets);
|
|
60
|
-
mountAuthorize(app, { auth });
|
|
61
|
-
app.get("/cache-probe", () => new Response("public", {
|
|
62
|
-
headers: { "cache-control": "public, s-maxage=60" },
|
|
63
|
-
}));
|
|
64
|
-
|
|
65
|
-
const resource = conventionalMcpResource(env);
|
|
66
|
-
const mcp = new Map([
|
|
67
|
-
["/mcp/staff", createMcpApiHandler<Env>({ ref, surface: "staff", resource })],
|
|
68
|
-
["/mcp", createMcpApiHandler<Env>({ ref, surface: "public", resource })],
|
|
69
|
-
]);
|
|
70
|
-
return {
|
|
71
|
-
auth,
|
|
72
|
-
async fetch(request: Request, workerEnv: Env, ctx: ExecutionContext) {
|
|
73
|
-
// Low-level owners must prepare the canonical D1 schema before Better
|
|
74
|
-
// Auth handles a token, client, consent, or CIMD request.
|
|
75
|
-
await ref.get();
|
|
76
|
-
const handler = mcp.get(new URL(request.url).pathname);
|
|
77
|
-
const response = handler?.fetch
|
|
78
|
-
? await handler.fetch(request, workerEnv, ctx)
|
|
79
|
-
: await app.fetch(request, workerEnv, ctx);
|
|
80
|
-
return applyCachePolicy(request, response);
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Keep the conventional `DB` binding and `nodejs_compat`. CIMD metadata fetches
|
|
87
|
-
also require `global_fetch_strictly_public`; add the Queue producer in
|
|
88
|
-
`wrangler.jsonc`:
|
|
89
|
-
|
|
90
|
-
```jsonc
|
|
91
|
-
{
|
|
92
|
-
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
|
|
93
|
-
"queues": {
|
|
94
|
-
"producers": [
|
|
95
|
-
{ "binding": "AUDIT_QUEUE", "queue": "my-site-audit" }
|
|
96
|
-
]
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
After copying, the Worker entry, request audit, custom route and Queue contract
|
|
102
|
-
belong to the application. Mantle still owns the imported adapters and
|
|
103
|
-
standard route behavior; update them through the package version. Do not copy
|
|
104
|
-
their source or replace Auth, MCP or cache handling locally. A copied
|
|
105
|
-
composition has no automatic merge path back to `createMantleWorker`; keep it
|
|
106
|
-
only while the custom top-level lifecycle remains necessary.
|
|
5
|
+
The handbook ships inside the `@aotter/mantle` package under `node_modules/@aotter/mantle/docs/handbook/` and describes the installed release.
|
|
@@ -1,246 +1,5 @@
|
|
|
1
1
|
# Deferred lifecycle hooks on Cloudflare Queues
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
useful for notifications and projections that should not add request latency,
|
|
5
|
-
but it is not an exactly-once transaction boundary.
|
|
3
|
+
This guide moved into the handbook: [`docs/handbook/cloudflare/deferred-hooks-queues.md`](handbook/cloudflare/deferred-hooks-queues.md).
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- The entry write commits before Queue delivery. D1 and Queue do not share a
|
|
10
|
-
transaction, so a Worker failure can still occur in that gap.
|
|
11
|
-
- A resolved `Queue.send()` means Cloudflare accepted the message. Consumption
|
|
12
|
-
is at-least-once; a message can run more than once.
|
|
13
|
-
- A rejected or ambiguous send falls back with the same event identity through
|
|
14
|
-
`ctx.waitUntil`, or inline when `waitUntil` is unavailable. `waitUntil` is
|
|
15
|
-
best-effort and does not close the D1-to-Queue gap.
|
|
16
|
-
- A consumer acknowledges a message only after every captured Trigger
|
|
17
|
-
succeeds. It runs all captured Triggers before surfacing failures, so a later
|
|
18
|
-
failure can replay an earlier success.
|
|
19
|
-
- Malformed, unsupported, removed-Trigger, and persistently failing messages
|
|
20
|
-
are retried and then sent to the configured dead-letter queue (DLQ). Without
|
|
21
|
-
a DLQ, Cloudflare discards them after `max_retries`.
|
|
22
|
-
|
|
23
|
-
Deferred handlers must therefore be idempotent. Mantle does not promise
|
|
24
|
-
exactly-once execution.
|
|
25
|
-
|
|
26
|
-
## Version 1 event
|
|
27
|
-
|
|
28
|
-
The strict v1 envelope contains:
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
interface DeferredHookEnvelope {
|
|
32
|
-
version: 1;
|
|
33
|
-
eventId: string;
|
|
34
|
-
triggerNames: readonly string[];
|
|
35
|
-
hook: "after_create" | "after_update" | "after_delete" | "after_publish";
|
|
36
|
-
schema: string;
|
|
37
|
-
entry: EntryRow;
|
|
38
|
-
ctxSnapshot: CtxSnapshot | null;
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
`triggerNames` is captured when the entry mutation runs. A later deployment
|
|
43
|
-
does not add a new Trigger to an old event. Removing or renaming a captured
|
|
44
|
-
Trigger makes that old message fail validation at dispatch and eventually
|
|
45
|
-
reach the DLQ instead of silently changing its meaning.
|
|
46
|
-
|
|
47
|
-
Each handler receives persisted `entry.data` as its input and:
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
ctx.event = {
|
|
51
|
-
id: envelope.eventId,
|
|
52
|
-
trigger: currentTriggerName,
|
|
53
|
-
hook: envelope.hook,
|
|
54
|
-
schema: envelope.schema,
|
|
55
|
-
entry: envelope.entry,
|
|
56
|
-
};
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Use `${ctx.event.id}:${ctx.event.trigger}` as the handler's idempotency key.
|
|
60
|
-
The same key survives enqueue fallback, Queue retries, and replay. Synchronous
|
|
61
|
-
`before_*` hooks may still receive pre-projection request input such as a
|
|
62
|
-
CAPTCHA token; deferred `after_*` envelopes never retain that input. The
|
|
63
|
-
identity snapshot contains normalized actor and credential metadata, never raw
|
|
64
|
-
cookies, tokens, or API keys.
|
|
65
|
-
|
|
66
|
-
Cloudflare's 128 KB limit is decimal and includes platform metadata. The
|
|
67
|
-
adapter therefore rejects non-JSON envelopes and encoded envelopes at or above
|
|
68
|
-
127,000 bytes before calling `Queue.send`, leaving at least 1 KB of headroom.
|
|
69
|
-
Keep entry payloads smaller than that supported limit.
|
|
70
|
-
|
|
71
|
-
## Cloudflare opt-in
|
|
72
|
-
|
|
73
|
-
Create the internal queue and its DLQ:
|
|
74
|
-
|
|
75
|
-
```sh
|
|
76
|
-
pnpm wrangler queues create mantle-internal
|
|
77
|
-
pnpm wrangler queues create mantle-internal-dlq
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Add both producer and consumer bindings. These are adapter settings, not Mantle
|
|
81
|
-
manifest grammar:
|
|
82
|
-
|
|
83
|
-
```toml
|
|
84
|
-
[[queues.producers]]
|
|
85
|
-
binding = "MANTLE_INTERNAL_QUEUE"
|
|
86
|
-
queue = "mantle-internal"
|
|
87
|
-
|
|
88
|
-
[[queues.consumers]]
|
|
89
|
-
queue = "mantle-internal"
|
|
90
|
-
max_batch_size = 10
|
|
91
|
-
max_batch_timeout = 5
|
|
92
|
-
max_retries = 5
|
|
93
|
-
retry_delay = 60
|
|
94
|
-
dead_letter_queue = "mantle-internal-dlq"
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Wire the producer into `MantleCloudflareConfig.bindings` and export the consumer alongside
|
|
98
|
-
the existing HTTP/OAuth handler. The same Worker may be both producer and
|
|
99
|
-
consumer:
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
import type { DeferredHookEnvelope } from "@aotter/mantle/runtime";
|
|
103
|
-
import {
|
|
104
|
-
WorkersQueueHookDispatcher,
|
|
105
|
-
createMantleWorker,
|
|
106
|
-
createQueueHandler,
|
|
107
|
-
type MantleCloudflareEnv,
|
|
108
|
-
} from "@aotter/mantle/cloudflare";
|
|
109
|
-
|
|
110
|
-
interface Env extends MantleCloudflareEnv {
|
|
111
|
-
DB: D1Database;
|
|
112
|
-
MANTLE_INTERNAL_QUEUE: Queue<DeferredHookEnvelope>;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const worker = createMantleWorker<Env>({
|
|
116
|
-
plan,
|
|
117
|
-
handlers,
|
|
118
|
-
bindings: (env, conventional) => ({
|
|
119
|
-
...conventional,
|
|
120
|
-
deferredHookDispatcher: new WorkersQueueHookDispatcher(
|
|
121
|
-
env.MANTLE_INTERNAL_QUEUE,
|
|
122
|
-
),
|
|
123
|
-
}),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
export default {
|
|
127
|
-
fetch: worker.fetch,
|
|
128
|
-
queue(batch, env) {
|
|
129
|
-
return createQueueHandler<Env>({
|
|
130
|
-
get: () => worker.getRuntime(env),
|
|
131
|
-
})(batch, env);
|
|
132
|
-
},
|
|
133
|
-
} satisfies ExportedHandler<Env>;
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
Queue opt-in adds only the dispatcher binding and `queue` export; the facade
|
|
137
|
-
keeps Auth, MCP, cache, and runtime assembly on the standard path.
|
|
138
|
-
|
|
139
|
-
## Idempotent handlers
|
|
140
|
-
|
|
141
|
-
For a D1-owned effect, make the event/Trigger key a unique database key. This
|
|
142
|
-
example records a site-owned notification job exactly once even when the
|
|
143
|
-
lifecycle handler is replayed:
|
|
144
|
-
|
|
145
|
-
```sql
|
|
146
|
-
CREATE TABLE notification_jobs (
|
|
147
|
-
idempotency_key TEXT PRIMARY KEY,
|
|
148
|
-
entry_id TEXT NOT NULL,
|
|
149
|
-
payload TEXT NOT NULL,
|
|
150
|
-
created_at INTEGER NOT NULL
|
|
151
|
-
);
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
```ts
|
|
155
|
-
import type { HandlerContext } from "@aotter/mantle/runtime";
|
|
156
|
-
|
|
157
|
-
async function enqueueNotification(
|
|
158
|
-
input: Record<string, unknown>,
|
|
159
|
-
ctx: HandlerContext,
|
|
160
|
-
) {
|
|
161
|
-
if (!ctx.event?.entry) throw new Error("lifecycle entry event required");
|
|
162
|
-
const key = `${ctx.event.id}:${ctx.event.trigger}`;
|
|
163
|
-
await (ctx.env as Env).DB.prepare(
|
|
164
|
-
`INSERT OR IGNORE INTO notification_jobs
|
|
165
|
-
(idempotency_key, entry_id, payload, created_at)
|
|
166
|
-
VALUES (?, ?, ?, ?)`,
|
|
167
|
-
).bind(key, ctx.event.entry.id, JSON.stringify(input), Date.now()).run();
|
|
168
|
-
return { ok: true };
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
For an upstream API that supports idempotency, send the same stable key and
|
|
173
|
-
throw on failure so Queue retries it:
|
|
174
|
-
|
|
175
|
-
```ts
|
|
176
|
-
const key = `${ctx.event!.id}:${ctx.event!.trigger}`;
|
|
177
|
-
const response = await fetch("https://api.example.com/messages", {
|
|
178
|
-
method: "POST",
|
|
179
|
-
headers: {
|
|
180
|
-
"Content-Type": "application/json",
|
|
181
|
-
"Idempotency-Key": key,
|
|
182
|
-
},
|
|
183
|
-
body: JSON.stringify(input),
|
|
184
|
-
});
|
|
185
|
-
if (!response.ok) throw new Error(`message provider returned ${response.status}`);
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
Core intentionally does not add a business outbox, payment state machine,
|
|
189
|
-
provider retry wrapper, or generic job registry. Those remain site-owned.
|
|
190
|
-
|
|
191
|
-
## Multiplexing site queues
|
|
192
|
-
|
|
193
|
-
One Worker can consume the Mantle queue and site-owned queues without a Core
|
|
194
|
-
registry. Route on `batch.queue` in the Worker entry:
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
const mantleQueue = createQueueHandler<Env>(cms);
|
|
198
|
-
|
|
199
|
-
export default {
|
|
200
|
-
fetch: http.fetch.bind(http),
|
|
201
|
-
queue(batch, env, ctx) {
|
|
202
|
-
switch (batch.queue) {
|
|
203
|
-
case "mantle-internal":
|
|
204
|
-
return mantleQueue(batch, env);
|
|
205
|
-
case "billing-jobs":
|
|
206
|
-
return consumeBilling(batch, env, ctx);
|
|
207
|
-
default:
|
|
208
|
-
batch.retryAll();
|
|
209
|
-
console.error(`No consumer for queue '${batch.queue}'`);
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
} satisfies ExportedHandler<Env>;
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
The Mantle consumer processes at most five messages concurrently within each
|
|
217
|
-
delivered batch even if `max_batch_size` is raised. It calls `retryAll()` when
|
|
218
|
-
runtime boot fails, `retry()` for one failed event, and `ack()` exactly once
|
|
219
|
-
after success.
|
|
220
|
-
|
|
221
|
-
## Verification and operations
|
|
222
|
-
|
|
223
|
-
Run the integrated contract test:
|
|
224
|
-
|
|
225
|
-
```sh
|
|
226
|
-
pnpm --filter @aotter/mantle-cloudflare test -- mantle-internal-queue.test.ts
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
It covers real runtime failure propagation, all-Trigger execution, stable
|
|
230
|
-
replay identity, per-message ack/retry, malformed bodies, size/JSON rejection,
|
|
231
|
-
runtime boot failure, and the five-message concurrency bound.
|
|
232
|
-
|
|
233
|
-
For a site integration, run `wrangler dev`, submit a mutation with an
|
|
234
|
-
`after_*` Trigger, and verify the consumer log. Make one handler fail once and
|
|
235
|
-
confirm its first delivery is retried and the replay sees the same
|
|
236
|
-
`ctx.event.id` and `ctx.event.trigger`. Inspect the DLQ before increasing
|
|
237
|
-
`max_retries`; poison messages otherwise hide as repeated retries.
|
|
238
|
-
|
|
239
|
-
### Upgrading from the unversioned envelope
|
|
240
|
-
|
|
241
|
-
Version 1 intentionally rejects the old unversioned envelope and the old
|
|
242
|
-
`originalInput` field. Before deploying this release to a Worker that already
|
|
243
|
-
uses `mantle-internal`, stop old producers and drain the queue (including any
|
|
244
|
-
in-flight retries), or explicitly move the remaining messages to an operator
|
|
245
|
-
workflow. Deploying v1 with legacy messages still queued will retry those
|
|
246
|
-
messages and eventually place them in the DLQ.
|
|
5
|
+
The handbook ships inside the `@aotter/mantle` package under `node_modules/@aotter/mantle/docs/handbook/` and describes the installed release.
|