@aotter/mantle-cloudflare 0.0.11-alpha.59 → 0.0.11-alpha.61

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 CHANGED
@@ -72,3 +72,18 @@ of the Core SDK skill contract or Day 1 landing path. Use the Cloudflare recipe
72
72
  only when a site actually needs staff-managed images or files:
73
73
 
74
74
  <https://raw.githubusercontent.com/aotter/mantle/develop/docs/media-uploads.md>
75
+
76
+ ## Optional deferred lifecycle hooks
77
+
78
+ `WorkersQueueHookDispatcher` and `createQueueHandler` opt `after_*` lifecycle
79
+ Triggers into Cloudflare Queues. Delivery is at-least-once, not exactly-once;
80
+ the D1 write and Queue send are not atomic, and fallback through `waitUntil` is
81
+ best-effort. Handlers receive a stable `ctx.event.id` plus
82
+ `ctx.event.trigger` for idempotency. The adapter validates strict v1 JSON
83
+ envelopes, reserves metadata headroom under Cloudflare's 128 KB message limit,
84
+ and maps failures to per-message retry/DLQ behavior.
85
+
86
+ Producer/consumer bindings, Worker export, idempotent D1/upstream examples,
87
+ site-queue multiplexing, verification, and the legacy-envelope drain step are
88
+ in the shipped
89
+ [deferred lifecycle Queue guide](../../../docs/deferred-lifecycle-queues.md).
@@ -106,7 +106,21 @@ class D1Migrations {
106
106
  ops.push(this.db
107
107
  .prepare(`INSERT INTO _migrations (id, applied_at) VALUES (?, ?)`)
108
108
  .bind(m.id, Date.now()));
109
- await this.db.batch(ops);
109
+ try {
110
+ await this.db.batch(ops);
111
+ }
112
+ catch (error) {
113
+ // Two cold starts can read the same stale `seen` snapshot. The
114
+ // losing batch rolls back atomically; accept it only when the
115
+ // winner has recorded this exact migration in the meantime.
116
+ const winner = await this.db
117
+ .prepare(`SELECT id FROM _migrations WHERE id = ?`)
118
+ .bind(m.id)
119
+ .first();
120
+ if (winner?.id !== m.id)
121
+ throw error;
122
+ }
123
+ seen.add(m.id);
110
124
  }
111
125
  }
112
126
  }
@@ -1 +1 @@
1
- {"version":3,"file":"D1DatabaseDriver.js","sourceRoot":"","sources":["../../src/bindings/D1DatabaseDriver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"D1DatabaseDriver.js","sourceRoot":"","sources":["../../src/bindings/D1DatabaseDriver.ts"],"names":[],"mappings":"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"}
@@ -2,10 +2,9 @@ import type { CmsRuntime, DeferredHookDispatcher, DeferredHookEnvelope } from "@
2
2
  /**
3
3
  * `DeferredHookDispatcher` impl backed by a Cloudflare Workers Queue
4
4
  * binding. Producer side: serialises each `DeferredHookEnvelope` and
5
- * sends it via `queue.send`. The runtime decorator handles fallback
6
- * on rejection (its three-rung ladder downgrades to `ctx.waitUntil`
7
- * inline-await), so this dispatcher does not retry — it lets the
8
- * caller decide.
5
+ * sends it explicitly as JSON. The runtime decorator handles a
6
+ * rejection with its best-effort fallback. Queue acceptance is not
7
+ * atomic with the preceding D1 mutation.
9
8
  *
10
9
  * Per ADR-0011 only this adapter package may import the CF binding
11
10
  * type. The runtime sees only the `DeferredHookDispatcher` port.
@@ -17,7 +16,7 @@ export declare class WorkersQueueHookDispatcher implements DeferredHookDispatche
17
16
  }
18
17
  /**
19
18
  * Build the `queue(batch, env, ctx)` Workers handler that consumes
20
- * `mantle_internal` messages and re-fires each `after_*` hook through
19
+ * `mantle-internal` messages and re-fires each `after_*` hook through
21
20
  * the runtime. Consumers wire this alongside `fetch` in their default
22
21
  * Worker export:
23
22
  *
@@ -28,11 +27,11 @@ export declare class WorkersQueueHookDispatcher implements DeferredHookDispatche
28
27
  * } satisfies ExportedHandler<Env>;
29
28
  * ```
30
29
  *
31
- * Per-message ack / retry: a thrown handler error retries the
32
- * message; success acks. CF Queues then handles backoff and the
33
- * dead-letter queue per the binding's wrangler config.
30
+ * Per-message ack / retry: any validation or handler failure retries
31
+ * the message; success acks. CF Queues then applies the consumer's
32
+ * retry delay, max_retries, and DLQ configuration.
34
33
  */
35
34
  export declare function createQueueHandler<Env>(cmsRef: {
36
35
  get(): Promise<CmsRuntime>;
37
- }): (batch: MessageBatch<DeferredHookEnvelope>, env: Env) => Promise<void>;
36
+ }): (batch: MessageBatch<unknown>, env: Env) => Promise<void>;
38
37
  //# sourceMappingURL=WorkersQueueHookDispatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkersQueueHookDispatcher.d.ts","sourceRoot":"","sources":["../../src/bindings/WorkersQueueHookDispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;GAUG;AACH,qBAAa,0BAA2B,YAAW,sBAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC;IAEzD,OAAO,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CAG7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EACpC,MAAM,EAAE;IAAE,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,GACrC,CAAC,KAAK,EAAE,YAAY,CAAC,oBAAoB,CAAC,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAgCxE"}
1
+ {"version":3,"file":"WorkersQueueHookDispatcher.d.ts","sourceRoot":"","sources":["../../src/bindings/WorkersQueueHookDispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,sBAAsB,EACtB,oBAAoB,EACrB,MAAM,wBAAwB,CAAC;AAUhC;;;;;;;;;GASG;AACH,qBAAa,0BAA2B,YAAW,sBAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC;IAEzD,OAAO,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CAiC7D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EACpC,MAAM,EAAE;IAAE,GAAG,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;CAAE,GACrC,CAAC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,CAkC3D"}
@@ -1,10 +1,16 @@
1
+ import { z } from "zod";
2
+ // Cloudflare's 128 KB limit is decimal and includes roughly 100 bytes
3
+ // of platform metadata. Reserve a full 1 KB so accepted envelopes stay
4
+ // below the provider limit rather than failing only at queue.send.
5
+ const MAX_ENVELOPE_BYTES = 127_000;
6
+ const MAX_CONCURRENCY = 5;
7
+ const jsonValue = z.json();
1
8
  /**
2
9
  * `DeferredHookDispatcher` impl backed by a Cloudflare Workers Queue
3
10
  * binding. Producer side: serialises each `DeferredHookEnvelope` and
4
- * sends it via `queue.send`. The runtime decorator handles fallback
5
- * on rejection (its three-rung ladder downgrades to `ctx.waitUntil`
6
- * inline-await), so this dispatcher does not retry — it lets the
7
- * caller decide.
11
+ * sends it explicitly as JSON. The runtime decorator handles a
12
+ * rejection with its best-effort fallback. Queue acceptance is not
13
+ * atomic with the preceding D1 mutation.
8
14
  *
9
15
  * Per ADR-0011 only this adapter package may import the CF binding
10
16
  * type. The runtime sees only the `DeferredHookDispatcher` port.
@@ -15,12 +21,36 @@ export class WorkersQueueHookDispatcher {
15
21
  this.queue = queue;
16
22
  }
17
23
  async enqueue(envelope) {
18
- await this.queue.send(envelope);
24
+ // `EntryRow.locale?` may be present with value `undefined` in a
25
+ // repository object. JSON transport omits that optional field;
26
+ // normalize only this declared optional before the strict
27
+ // JSON-safe check so undefined values inside entry.data still
28
+ // fail instead of disappearing silently.
29
+ const { locale, ...entryWithoutLocale } = envelope.entry;
30
+ const wireEnvelope = locale === undefined
31
+ ? { ...envelope, entry: entryWithoutLocale }
32
+ : envelope;
33
+ let json;
34
+ try {
35
+ json = jsonValue.safeParse(wireEnvelope);
36
+ }
37
+ catch (cause) {
38
+ throw new Error(`Deferred lifecycle envelope '${envelope.eventId}' is not JSON-safe.`, { cause });
39
+ }
40
+ if (!json.success) {
41
+ const issue = json.error.issues[0];
42
+ throw new Error(`Deferred lifecycle envelope '${envelope.eventId}' is not JSON-safe at '${issue?.path.join("/") ?? "unknown"}'.`);
43
+ }
44
+ const bytes = new TextEncoder().encode(JSON.stringify(json.data)).byteLength;
45
+ if (bytes >= MAX_ENVELOPE_BYTES) {
46
+ throw new Error(`Deferred lifecycle envelope '${envelope.eventId}' is ${bytes} bytes; this adapter requires less than ${MAX_ENVELOPE_BYTES} to leave room under Cloudflare's 128 KB message limit.`);
47
+ }
48
+ await this.queue.send(wireEnvelope, { contentType: "json" });
19
49
  }
20
50
  }
21
51
  /**
22
52
  * Build the `queue(batch, env, ctx)` Workers handler that consumes
23
- * `mantle_internal` messages and re-fires each `after_*` hook through
53
+ * `mantle-internal` messages and re-fires each `after_*` hook through
24
54
  * the runtime. Consumers wire this alongside `fetch` in their default
25
55
  * Worker export:
26
56
  *
@@ -31,9 +61,9 @@ export class WorkersQueueHookDispatcher {
31
61
  * } satisfies ExportedHandler<Env>;
32
62
  * ```
33
63
  *
34
- * Per-message ack / retry: a thrown handler error retries the
35
- * message; success acks. CF Queues then handles backoff and the
36
- * dead-letter queue per the binding's wrangler config.
64
+ * Per-message ack / retry: any validation or handler failure retries
65
+ * the message; success acks. CF Queues then applies the consumer's
66
+ * retry delay, max_retries, and DLQ configuration.
37
67
  */
38
68
  export function createQueueHandler(cmsRef) {
39
69
  return async (batch, env) => {
@@ -46,23 +76,28 @@ export function createQueueHandler(cmsRef) {
46
76
  // batch.retryAll so each message's per-message attempt counter
47
77
  // increments — letting wrangler's max_retries / DLQ rules
48
78
  // engage instead of looping forever at the batch level.
49
- console.error("[mantle-internal] runtime boot failed; retrying batch", err);
50
79
  batch.retryAll();
80
+ console.error("[mantle-internal] runtime boot failed; retrying batch", err);
51
81
  return;
52
82
  }
53
- // Drain in parallel after-hooks are independent and CF Queues
54
- // ack/retry is per-message, so serial processing only burns the
55
- // 30s consumer wall clock on larger batches.
56
- await Promise.allSettled(batch.messages.map(async (message) => {
57
- try {
58
- await cms.runDeferredHook.execute({ envelope: message.body, env });
59
- message.ack();
60
- }
61
- catch (err) {
62
- console.error(`[mantle-internal] consume failed for ${message.body.hook} on ${message.body.schema}/${message.body.entry.id}`, err);
63
- message.retry();
64
- }
65
- }));
83
+ // Bound fan-out even when a consumer raises max_batch_size.
84
+ for (let offset = 0; offset < batch.messages.length; offset += MAX_CONCURRENCY) {
85
+ const chunk = batch.messages.slice(offset, offset + MAX_CONCURRENCY);
86
+ await Promise.all(chunk.map(async (message) => {
87
+ try {
88
+ await cms.runDeferredHook.execute({ envelope: message.body, env });
89
+ message.ack();
90
+ }
91
+ catch (err) {
92
+ message.retry();
93
+ console.error("[mantle-internal] deferred lifecycle consume failed; retrying", {
94
+ messageId: message.id,
95
+ attempts: message.attempts,
96
+ error: err,
97
+ });
98
+ }
99
+ }));
100
+ }
66
101
  };
67
102
  }
68
103
  //# sourceMappingURL=WorkersQueueHookDispatcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkersQueueHookDispatcher.js","sourceRoot":"","sources":["../../src/bindings/WorkersQueueHookDispatcher.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,0BAA0B;IACR;IAA7B,YAA6B,KAAkC;QAAlC,UAAK,GAAL,KAAK,CAA6B;IAAG,CAAC;IAEnE,KAAK,CAAC,OAAO,CAAC,QAA8B;QAC1C,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAsC;IAEtC,OAAO,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC1B,IAAI,GAAe,CAAC;QACpB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,yDAAyD;YACzD,+DAA+D;YAC/D,0DAA0D;YAC1D,wDAAwD;YACxD,OAAO,CAAC,KAAK,CAAC,uDAAuD,EAAE,GAAG,CAAC,CAAC;YAC5E,KAAK,CAAC,QAAQ,EAAE,CAAC;YACjB,OAAO;QACT,CAAC;QACD,gEAAgE;QAChE,gEAAgE;QAChE,6CAA6C;QAC7C,MAAM,OAAO,CAAC,UAAU,CACtB,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,eAAe,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;gBACnE,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CACX,wCAAwC,OAAO,CAAC,IAAI,CAAC,IAAI,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,EAC9G,GAAG,CACJ,CAAC;gBACF,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,CAAC;QACH,CAAC,CAAC,CACH,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"WorkersQueueHookDispatcher.js","sourceRoot":"","sources":["../../src/bindings/WorkersQueueHookDispatcher.ts"],"names":[],"mappings":"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"}
@@ -18,10 +18,11 @@ export interface CmsConfig {
18
18
  * `/admin/api/media/*` endpoints are registered. Forwarded to the
19
19
  * runtime as `mediaStorage`. */
20
20
  readonly mediaStorage?: CreateCmsRuntimeArgs["mediaStorage"];
21
- /** Optional deferred-delivery dispatcher. When set, `after_*`
22
- * lifecycle hooks fire through this instead of riding
23
- * `ctx.waitUntil`. The Cloudflare adapter expects a
24
- * `WorkersQueueHookDispatcher` bound to the `mantle_internal` queue
21
+ /** Optional at-least-once dispatcher. When set, `after_*`
22
+ * lifecycle hooks enqueue after the entry write; a rejected send
23
+ * falls back to best-effort `ctx.waitUntil`/inline execution. The
24
+ * Cloudflare adapter expects a
25
+ * `WorkersQueueHookDispatcher` bound to the `mantle-internal` queue
25
26
  * here. */
26
27
  readonly deferredHookDispatcher?: CreateCmsRuntimeArgs["deferredHookDispatcher"];
27
28
  };
@@ -1 +1 @@
1
- {"version":3,"file":"cmsConfig.d.ts","sourceRoot":"","sources":["../../src/mount/cmsConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACrC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC,GAAG;QACtE;;yCAEiC;QACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAC;QAC7D;;;;oBAIY;QACZ,QAAQ,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,CAAC,wBAAwB,CAAC,CAAC;KAClF,CAAC;IACF,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB;;yBAEqB;IACrB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IACzD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,CAAC;CACH"}
1
+ {"version":3,"file":"cmsConfig.d.ts","sourceRoot":"","sources":["../../src/mount/cmsConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACrC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC,oBAAoB,EAAE,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC,GAAG;QACtE;;yCAEiC;QACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAC;QAC7D;;;;;oBAKY;QACZ,QAAQ,CAAC,sBAAsB,CAAC,EAAE,oBAAoB,CAAC,wBAAwB,CAAC,CAAC;KAClF,CAAC;IACF,gEAAgE;IAChE,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB;;yBAEqB;IACrB,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;IACzD,sEAAsE;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE;QACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,CAAC;CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aotter/mantle-cloudflare",
3
- "version": "0.0.11-alpha.59",
3
+ "version": "0.0.11-alpha.61",
4
4
  "description": "Cloudflare Workers adapter for mantle. Hono-based mountServerEndpoints / createMcpApiHandler / mountAuthorize / mountPublicRoutes. Better Auth for staff sign-in; @cloudflare/workers-oauth-provider for the MCP OAuth AS surface.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://mantle.tools/",
@@ -30,9 +30,9 @@
30
30
  ],
31
31
  "dependencies": {
32
32
  "@better-auth/oauth-provider": "^1.6.23",
33
- "@aotter/mantle-admin-ui": "0.0.11-alpha.59",
34
- "@aotter/mantle-runtime": "0.0.11-alpha.59",
35
- "@aotter/mantle-spec": "0.0.11-alpha.59"
33
+ "@aotter/mantle-admin-ui": "0.0.11-alpha.61",
34
+ "@aotter/mantle-runtime": "0.0.11-alpha.61",
35
+ "@aotter/mantle-spec": "0.0.11-alpha.61"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@cloudflare/workers-oauth-provider": "^0.8.0",