@aotter/mantle 0.1.0-alpha.15 → 0.1.0-alpha.17
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 +8 -1
- 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/docs/adapter-guide.md +4 -0
- package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +78 -0
- package/docs/adr-lite-803-request-diagnostics.md +38 -0
- package/docs/adr-lite-808-route-readiness.md +47 -0
- package/docs/adr-lite-809-bounded-public-content.md +68 -0
- package/docs/adr-lite-812-native-parity.md +149 -0
- package/docs/adr-lite-823-home-statistics.md +63 -0
- package/docs/api-mcp-authorization.md +7 -0
- package/docs/cloudflare-low-level-composition.md +14 -8
- package/docs/labels.md +2 -0
- package/docs/media-uploads.md +25 -0
- package/docs/performance-harness.md +124 -7
- package/docs/release-process.md +14 -3
- package/docs/spec-only-host-adoption.md +184 -0
- package/package.json +17 -17
package/README.md
CHANGED
|
@@ -60,7 +60,9 @@ pnpm exec mantle-harness http --base-url http://127.0.0.1:8787 --route page=/en/
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
`mantle generate` validates and compiles `./manifests/`, then writes one typed
|
|
63
|
-
`.mantle/generated/mantle.ts` module.
|
|
63
|
+
`.mantle/generated/mantle.ts` module. When `@aotter/mantle-admin-ui` is
|
|
64
|
+
installed, it also syncs the Admin SPA to `public/_mantle/admin/` (excluding
|
|
65
|
+
`server.*` package exports). Core-only installs skip that copy. It performs no
|
|
64
66
|
skill sync, package update, styling, provisioning, or deployment.
|
|
65
67
|
The same pure emitter is available from `@aotter/mantle/codegen` when a host
|
|
66
68
|
wants to own parsing and filesystem IO.
|
|
@@ -279,3 +281,8 @@ adapter is a port-implementation exercise, not a runtime refactor.
|
|
|
279
281
|
## License
|
|
280
282
|
|
|
281
283
|
Apache-2.0
|
|
284
|
+
|
|
285
|
+
Pure extension routes do not await content preparation. Before an extension
|
|
286
|
+
uses Mantle data or database-backed Auth, await its supplied `getRuntime()`
|
|
287
|
+
(or `ref.get()`). Standard protected routes establish this readiness themselves;
|
|
288
|
+
queue/scheduled handlers continue to use `worker.getRuntime(env)`.
|
package/dist/cli/generate.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/** Test seam for Core-only vs Admin-present installs. */
|
|
2
|
+
export interface GenerateDeps {
|
|
3
|
+
readonly resolveAdminUiIndexHtml?: () => string | null;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Locate the optional Admin SPA. A Core-only install does not have
|
|
7
|
+
* `@aotter/mantle-admin-ui`, so resolution failure is not an error.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveAdminUiIndexHtml(): string | null;
|
|
10
|
+
export declare function runGenerate(rawArgs: readonly string[], deps?: GenerateDeps): Promise<number>;
|
|
2
11
|
//# sourceMappingURL=generate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAiBA,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;CACxD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,GAAG,IAAI,CAOvD;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,MAAM,CAAC,CA+CjB"}
|
package/dist/cli/generate.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
2
3
|
import { dirname, join, resolve } from "node:path";
|
|
3
4
|
import { cwd, stderr, stdout } from "node:process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
4
6
|
import { parseArgs } from "node:util";
|
|
5
7
|
import { ValidateManifestsUseCase } from "@aotter/mantle-spec";
|
|
6
8
|
import { loadManifestsFromRoot } from "@aotter/mantle-spec/cli";
|
|
7
9
|
import { assertMantleNamespace, emitMantleModule } from "../codegen/emitMantleModule.js";
|
|
8
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Locate the optional Admin SPA. A Core-only install does not have
|
|
12
|
+
* `@aotter/mantle-admin-ui`, so resolution failure is not an error.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveAdminUiIndexHtml() {
|
|
15
|
+
try {
|
|
16
|
+
const path = fileURLToPath(import.meta.resolve("@aotter/mantle-admin-ui/index.html"));
|
|
17
|
+
return existsSync(path) ? path : null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function runGenerate(rawArgs, deps = {}) {
|
|
9
24
|
let options;
|
|
10
25
|
try {
|
|
11
26
|
const parsed = parseGenerateArgs(rawArgs);
|
|
@@ -38,7 +53,13 @@ export async function runGenerate(rawArgs) {
|
|
|
38
53
|
return 1;
|
|
39
54
|
}
|
|
40
55
|
const output = resolve(cwd(), options.output);
|
|
41
|
-
|
|
56
|
+
let stale = !(await syncText(join(output, "mantle.ts"), emitted.source, options.check));
|
|
57
|
+
const adminIndex = (deps.resolveAdminUiIndexHtml ?? resolveAdminUiIndexHtml)();
|
|
58
|
+
if (adminIndex !== null) {
|
|
59
|
+
const adminSource = dirname(adminIndex);
|
|
60
|
+
const adminTarget = resolve(cwd(), "public/_mantle/admin");
|
|
61
|
+
stale = !(await syncAdminAssets(adminSource, adminTarget, options.check)) || stale;
|
|
62
|
+
}
|
|
42
63
|
if (stale && options.check) {
|
|
43
64
|
stderr.write("Mantle generated files are stale; run `mantle generate`.\n");
|
|
44
65
|
return 1;
|
|
@@ -76,7 +97,7 @@ Options:
|
|
|
76
97
|
--manifests <dir> Manifest directory (default: ./manifests)
|
|
77
98
|
-o, --output <dir> Generated root (default: .mantle/generated)
|
|
78
99
|
--namespace <name> Generated type namespace (default: Mantle)
|
|
79
|
-
--check Fail without writing when generated code
|
|
100
|
+
--check Fail without writing when generated code or Admin assets are stale
|
|
80
101
|
-h, --help This help
|
|
81
102
|
`);
|
|
82
103
|
}
|
|
@@ -95,6 +116,30 @@ async function syncText(path, expected, check) {
|
|
|
95
116
|
await writeFile(path, expected, "utf8");
|
|
96
117
|
return true;
|
|
97
118
|
}
|
|
119
|
+
async function syncAdminAssets(source, target, check) {
|
|
120
|
+
const sourceFiles = (await listFiles(source)).filter((path) => !path.startsWith("server."));
|
|
121
|
+
const targetFiles = await listFiles(target).catch(() => []);
|
|
122
|
+
const current = sourceFiles.length === targetFiles.length
|
|
123
|
+
&& sourceFiles.every((path, index) => path === targetFiles[index])
|
|
124
|
+
&& (await Promise.all(sourceFiles.map(async (path) => (await readFile(join(source, path))).equals(await readFile(join(target, path)))))).every(Boolean);
|
|
125
|
+
if (current || check)
|
|
126
|
+
return current;
|
|
127
|
+
await rm(target, { recursive: true, force: true });
|
|
128
|
+
for (const path of sourceFiles) {
|
|
129
|
+
const destination = join(target, path);
|
|
130
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
131
|
+
await writeFile(destination, await readFile(join(source, path)));
|
|
132
|
+
}
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
async function listFiles(root, prefix = "") {
|
|
136
|
+
const entries = await readdir(join(root, prefix), { withFileTypes: true });
|
|
137
|
+
const files = await Promise.all(entries.map((entry) => {
|
|
138
|
+
const path = join(prefix, entry.name);
|
|
139
|
+
return entry.isDirectory() ? listFiles(root, path) : [path];
|
|
140
|
+
}));
|
|
141
|
+
return files.flat().sort();
|
|
142
|
+
}
|
|
98
143
|
function message(error) {
|
|
99
144
|
return error instanceof Error ? error.message : String(error);
|
|
100
145
|
}
|
package/dist/cli/generate.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,wBAAwB,EAAmB,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAczF;;;GAGG;AACH,MAAM,UAAU,uBAAuB;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,CAAC;QACtF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAA0B,EAC1B,OAAqB,EAAE;IAEvB,IAAI,OAAwB,CAAC;IAC7B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC;QACX,CAAC;QACD,OAAO,GAAG,MAAM,CAAC;IACnB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM;QAC9B,CAAC,CAAC,wBAAwB,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;QACzD,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IACxD,MAAM,gBAAgB,GAAG,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC;SACxE,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IAC3D,IAAI,gBAAgB,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACtD,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;QACnC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC;QAC/B,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,SAAS,EAAE,OAAO,CAAC,SAAS;KAC7B,CAAC,CAAC;IACH,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;QAChB,gBAAgB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACtC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACxF,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,uBAAuB,IAAI,uBAAuB,CAAC,EAAE,CAAC;IAC/E,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,CAAC;QAC3D,KAAK,GAAG,CAAC,CAAC,MAAM,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC;IACrF,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QAC3B,MAAM,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC3E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,iBAAiB,CAAC,OAA0B;IACnD,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QAC3B,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC;QAClB,OAAO,EAAE;YACP,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE;YACtC,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC7B,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;SACtC;KACF,CAAC,CAAC;IACH,IAAI,MAAM,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,QAAQ,CAAC;IAC/C,qBAAqB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAChD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,aAAa;QAC5C,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,mBAAmB;QAC5C,SAAS;QACT,KAAK,EAAE,MAAM,CAAC,KAAK,KAAK,IAAI;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,SAAS;IAChB,MAAM,CAAC,KAAK,CAAC;;;;;;;;;;CAUd,CAAC,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,WAAkC;IAC1D,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC;IACjF,CAAC;AACH,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,IAAY,EAAE,QAAgB,EAAE,KAAc;IACpE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC/D,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,MAAc,EAAE,KAAc;IAC3E,MAAM,WAAW,GAAG,CAAC,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;IAC5F,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM;WACpD,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,WAAW,CAAC,KAAK,CAAC,CAAC;WAC/D,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CACnD,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAChF,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACrB,IAAI,OAAO,IAAI,KAAK;QAAE,OAAO,OAAO,CAAC;IAErC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACvC,MAAM,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,MAAM,SAAS,CAAC,WAAW,EAAE,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,MAAM,GAAG,EAAE;IAChD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACpD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACtC,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC,CAAC;IACJ,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;AAC7B,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
|
package/docs/adapter-guide.md
CHANGED
|
@@ -204,6 +204,10 @@ storage preparation and binding do not accept or require a static asset port.
|
|
|
204
204
|
|
|
205
205
|
## Implementation checklist
|
|
206
206
|
|
|
207
|
+
- [ ] Run `runStorageConformance` from `@aotter/mantle-runtime/testing/storage`
|
|
208
|
+
against a disposable prepared-storage factory. See the
|
|
209
|
+
[Runtime conformance guide](../packages/mantle-runtime/README.md#storage-adapter-conformance)
|
|
210
|
+
for coverage, cleanup, locale setup, and remaining adapter-specific tests.
|
|
207
211
|
- [ ] Implement `MantleStorageAdapter` returning existing semantic ports, or reuse `SqliteMantleStorageAdapter` with an already-owned handle.
|
|
208
212
|
- [ ] Call `bootMantleRuntime()` once per semantic revision, or explicitly prepare before binding.
|
|
209
213
|
- [ ] Mount HTTP Trigger and View REST surfaces.
|
|
@@ -625,3 +625,81 @@ OAuth store and is not renamed by this decision.
|
|
|
625
625
|
This amendment adopts the 2026-07-28 CIMD authorization profile only. Updating
|
|
626
626
|
Mantle's JSON-RPC dispatcher to the complete MCP 2026-07-28 transport revision
|
|
627
627
|
is a separate decision.
|
|
628
|
+
|
|
629
|
+
## 2026-09-07 amendment — shared OAuth surfaces and revocation
|
|
630
|
+
|
|
631
|
+
OAuth product UI is not owned by a runtime adapter. `@aotter/mantle-admin`
|
|
632
|
+
owns `MantleOAuthAuth`, the consent/connected-app view models, and
|
|
633
|
+
`handleMantleOAuth(Request) -> Response | null`; `mountMantleOAuth` is a thin
|
|
634
|
+
Hono bridge. The Cloudflare adapter implements protocol actions using the
|
|
635
|
+
same Better Auth instance and D1. Future adapters reuse this contract, not
|
|
636
|
+
Cloudflare-specific UI glue. The old `mountAuthorize` export remains an alias.
|
|
637
|
+
|
|
638
|
+
`@aotter/mantle-admin-ui` owns the React/shadcn sign-in, consent and connected
|
|
639
|
+
apps surfaces, including i18n, theme and submit state. Auth pages initially
|
|
640
|
+
follow the system theme; the light/dark toggle persists an explicit override.
|
|
641
|
+
Connected apps has an Admin page, but managing one's own grants requires only
|
|
642
|
+
a session, never a staff role. The no-assets HTML fallback uses native forms
|
|
643
|
+
without JavaScript or a second implementation of the Admin design system.
|
|
644
|
+
Both mounts retain same-origin mutation checks and private/no-store responses.
|
|
645
|
+
The consent document's CSP permits only the provider-validated callback
|
|
646
|
+
origin for the browser's form redirect; it never trusts an unsigned query.
|
|
647
|
+
|
|
648
|
+
MCP authorization remains session-bound: the JWT's original Better Auth
|
|
649
|
+
session must still exist and be unexpired. Admin sign-out/session expiration
|
|
650
|
+
therefore also ends that session's MCP access. A refresh token is not an
|
|
651
|
+
independent authorization to bypass this check. Staff roles are still read
|
|
652
|
+
fresh on every protected request.
|
|
653
|
+
|
|
654
|
+
Disconnect is scoped to the authenticated user and the selected client. It
|
|
655
|
+
revokes refresh/opaque access tokens, removes pending authorization codes and
|
|
656
|
+
consent rows, and prevents existing JWTs from becoming valid when the client
|
|
657
|
+
is connected again. The verification-create hook captures the consent row ID
|
|
658
|
+
in Better Auth's existing authorization `referenceId`; Better Auth carries it
|
|
659
|
+
through the authorization code and every refresh rotation. MCP JWTs copy that
|
|
660
|
+
reference into `mantle_consent_id`, which must match the active consent row.
|
|
661
|
+
Never look up a new consent at token mint time: doing so could revive a refresh
|
|
662
|
+
lineage whose insertion raced the revoke batch. Same-second reconnect and a
|
|
663
|
+
delayed old refresh row are required regression cases, not clock delays.
|
|
664
|
+
|
|
665
|
+
MCP mode reserves authorization `referenceId` for this grant identity; future
|
|
666
|
+
curated configuration must not also expose Better Auth's `postLogin` reference
|
|
667
|
+
hooks. MCP access requires a persisted user consent, so `skipConsent` and
|
|
668
|
+
`cachedTrustedClients` must not bypass consent for MCP clients.
|
|
669
|
+
|
|
670
|
+
This alpha hotfix requires existing MCP clients to reconnect once: pre-hotfix
|
|
671
|
+
JWTs without the grant claim are rejected immediately. No account/session reset
|
|
672
|
+
is required. The unshipped watermark migration 0008 is removed; its unused
|
|
673
|
+
table in the phsu development database is harmless and is not queried or
|
|
674
|
+
deleted during deployment.
|
|
675
|
+
|
|
676
|
+
## 2026-09-08 amendment — request security boundaries
|
|
677
|
+
|
|
678
|
+
All Admin session mutations use the existing same-origin guard, including
|
|
679
|
+
same-site sibling origins. Admin HTML forbids framing and is private/no-store.
|
|
680
|
+
The SPA also refuses to render in frames, covering direct static-asset URLs
|
|
681
|
+
that bypass the server mount.
|
|
682
|
+
The Admin/auth mounts cap request bodies at 1 MiB using Hono's body limiter;
|
|
683
|
+
HTTP trigger and MCP dispatchers count streamed JSON bytes before parsing and
|
|
684
|
+
return 413 above the same limit. Media bytes continue through direct uploads.
|
|
685
|
+
This intentionally rejects previously accepted larger control-plane payloads.
|
|
686
|
+
|
|
687
|
+
Cloudflare `createAuth` explicitly enables Better Auth rate limits regardless
|
|
688
|
+
of `NODE_ENV`, including the OAuth provider's anonymous registration limit of
|
|
689
|
+
five requests per minute. Only `CF-Connecting-IP` supplies the client key.
|
|
690
|
+
The upstream memory store limits each isolate; deployments needing a shared
|
|
691
|
+
abuse quota must additionally enforce it at ingress. No D1 migration or new
|
|
692
|
+
runtime platform dependency is introduced.
|
|
693
|
+
|
|
694
|
+
Workers must retain initialization work through `ExecutionContext.waitUntil`
|
|
695
|
+
even when the initial challenge finishes or its client disconnects. Schema
|
|
696
|
+
boot precedes OAuth handling. The adapter's static AsyncLocalStorage seeding
|
|
697
|
+
is a version-pinned Better Auth 1.7.2 integration, with accessor-identity
|
|
698
|
+
regression coverage; it does not replace Better Auth's request context.
|
|
699
|
+
Failed Auth initialization evicts only the failed Worker assembly so a later
|
|
700
|
+
request can retry. Non-HTTP callers await Auth initialization with runtime boot;
|
|
701
|
+
HTTP requests anchor it without making public responses depend on Auth health.
|
|
702
|
+
|
|
703
|
+
The conventional `/favicon.ico` reflects the configured site icon, but is a
|
|
704
|
+
fallback after consumer routes, not a newly reserved namespace. Existing
|
|
705
|
+
consumer icon routes must continue to work after a package update.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# ADR-lite: request-scoped Cloudflare diagnostics
|
|
2
|
+
|
|
3
|
+
Status: implemented diagnostic record for #803; real-auth controls and measured
|
|
4
|
+
comparison acceptance belong to the #812 harness. This does not close the
|
|
5
|
+
separate starter provisioning/Smart Placement portion of #803.
|
|
6
|
+
|
|
7
|
+
Use native AsyncLocalStorage and a small versioned testing entry point instead of
|
|
8
|
+
a global current-request counter or a configurable production telemetry system.
|
|
9
|
+
Production mounts only check for an existing test context. With none, there are
|
|
10
|
+
no diagnostic clock reads, binding wrappers, records, observers or response headers.
|
|
11
|
+
Runtime, auth, role, catalog, dispatcher-build and dispatch retain their ownership
|
|
12
|
+
and authorization order. The ordinary runtime-ready promise keeps its identity.
|
|
13
|
+
|
|
14
|
+
Native D1 instrumentation sits beneath both Auth and Runtime and is idempotent.
|
|
15
|
+
It preserves receivers, bind chains, first-column behavior and native objects in
|
|
16
|
+
batches; no second driver observer is added for counting. Provider failures and
|
|
17
|
+
unknown metadata remain visible. Request context is captured when an operation
|
|
18
|
+
starts, so shared work belongs to its initiator. Catalog waiters inherit source
|
|
19
|
+
classification and wait time without inheriting the owner's binding counts.
|
|
20
|
+
|
|
21
|
+
The record freezes at response creation. Inclusive spans can overlap, and partial
|
|
22
|
+
metadata or deferred work must not be presented as a full total. A test-only cloned
|
|
23
|
+
MCP response supplies the JSON-RPC outcome without exposing its content; that
|
|
24
|
+
inspection is outside totalMs but remains instrumentation overhead for HTTP timing.
|
|
25
|
+
Observer errors never replace application results. D1/KV/R2 operations emit only
|
|
26
|
+
counts, sizes with their source/coverage, and durations; object keys, SQL, caller
|
|
27
|
+
identities and credentials never enter the record.
|
|
28
|
+
|
|
29
|
+
R2 GET bodies stay native. Successful direct stream-to-PUT completion confirms
|
|
30
|
+
payload bytes; canceled, incomplete and unconsumed reads remain unknown. This
|
|
31
|
+
avoids losing R2's known-length stream property or hiding buffering overhead.
|
|
32
|
+
|
|
33
|
+
Checks deliberately overlap MCP requests and share KV hit/failure loads, then
|
|
34
|
+
retry after failure. Native I/O is counted once per owner, and denied requests
|
|
35
|
+
leave unreached phases null. Additional checks preserve native D1 private receiver
|
|
36
|
+
and batch semantics, sync/async observer failure, immutable deferred snapshots,
|
|
37
|
+
R2 stream identity and partial-transfer uncertainty. The collector fixture labels
|
|
38
|
+
its deterministic auth explicitly; it is not evidence of native-auth parity.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Route-owned Cloudflare readiness (#808)
|
|
2
|
+
|
|
3
|
+
Status: accepted for the unreleased #806–812 implementation. Follows ADR-0019
|
|
4
|
+
(preparation owns migrations) and amends ADR-0014's unconditional HTTP boot rule.
|
|
5
|
+
|
|
6
|
+
The facade assembles immutable route projections once, then prepares only for
|
|
7
|
+
routes that consume canonical storage. It retains one retryable preparation
|
|
8
|
+
promise, the sealed plan and Better Auth's existing global AsyncLocalStorage,
|
|
9
|
+
`ready` observation, failed-assembly eviction and HTTP `waitUntil` ownership.
|
|
10
|
+
|
|
11
|
+
| Surface | Readiness, including an empty database |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| Consumer constant health, `/api/views` catalog | No content preparation. |
|
|
14
|
+
| Static Admin shell and assets | No content preparation; incomplete Auth still fails closed as before. |
|
|
15
|
+
| Manifest View/Trigger | Prepare before credential resolution, then use the same runtime invocation. |
|
|
16
|
+
| Admin API, configured Auth base path, OAuth UI/discovery | Prepare before session/provider work. Auth tables share canonical migrations. |
|
|
17
|
+
| MCP, including missing-token challenge | Prepare before the selected Auth verifier. A custom Auth implementation may require D1 even for a denied request; the facade cannot assume otherwise. |
|
|
18
|
+
| Public page/list/discovery and favicon fallback | Their existing `ref.get()` starts preparation when content is needed. |
|
|
19
|
+
| Consumer extension handlers | Call the supplied `getRuntime()` or `ref.get()` before using Mantle content or database-backed Auth. Pure handlers need neither. |
|
|
20
|
+
| Queue/scheduled `getRuntime(env)` | Await both runtime preparation and Auth initialization, unchanged. |
|
|
21
|
+
|
|
22
|
+
No deployment-time migration flag or KV readiness authority is introduced. A
|
|
23
|
+
current database's first protected route needs one fingerprint SELECT; an empty
|
|
24
|
+
or changed database performs canonical setup there. Concurrent first uses share
|
|
25
|
+
that work. A failed preparation returns the facade's redacted 500, and later
|
|
26
|
+
requests retry. Static availability is not a promise that data is ready.
|
|
27
|
+
|
|
28
|
+
The pre-change full-facade fixture recorded 106 SQL operations for its empty-DB
|
|
29
|
+
health request (its four Schemas include indexes), then 1 fingerprint operation
|
|
30
|
+
for each new-state health/catalog/shell/challenge. These are statement counts,
|
|
31
|
+
not network round trips. Its dry-run bundle was 3,921.40 KiB / 690.43 KiB gzip
|
|
32
|
+
with Wrangler 4.124.0 and compatibility date 2026-07-08. Esbuild attributed
|
|
33
|
+
1,342,428 emitted bytes to Better Auth, 76,078 to Admin, 24,624 to Web and
|
|
34
|
+
203,130 to Runtime; the rest includes transitive libraries and adapter code.
|
|
35
|
+
Byte attribution does not measure startup CPU. We retain static imports and
|
|
36
|
+
immutable mount-time projections: deferring those modules has not yet been
|
|
37
|
+
justified by a startup profile. The final #812 report owns the separate startup,
|
|
38
|
+
preparation-wall and warm-dispatch measurements.
|
|
39
|
+
|
|
40
|
+
Post-change full-facade workerd checks pass all 13 operation gates: empty-DB
|
|
41
|
+
health and fresh-state health/catalog/shell perform zero **content** queries;
|
|
42
|
+
the challenge retains its one fingerprint query. The fixture Auth is a stated
|
|
43
|
+
stub. A separate real Better Auth regression records one eager oauthResource
|
|
44
|
+
lookup while `ready` initializes; Better Auth 1.7.2 defers missing-table resource
|
|
45
|
+
seeding to first access. Subsequent static requests perform no additional Auth
|
|
46
|
+
queries. This background initialization is retained, not counted as eliminated
|
|
47
|
+
content preparation or hidden behind a zero-total-SQL claim.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# ADR-lite: bounded public content and complete discovery
|
|
2
|
+
|
|
3
|
+
Status: implemented for #809; combined deployment measurements tracked in #812.
|
|
4
|
+
Context: ADR-0010 translation joins, ADR-0019 sealed semantic storage, #792 Web
|
|
5
|
+
transport/cache ownership. No manifest keys or remote Web SDK are introduced.
|
|
6
|
+
|
|
7
|
+
## Decision
|
|
8
|
+
|
|
9
|
+
Add the semantic `EntryReader.readPublishedPage` operation instead of silently
|
|
10
|
+
capping generic `readPublished`. Forward pages sort by updatedAt and id descending,
|
|
11
|
+
with default 50 / maximum 2,000 rows and a 1-MiB data-JSON budget. A single larger
|
|
12
|
+
entry remains readable and advances the cursor. This budget covers the selected
|
|
13
|
+
canonical data, excluding envelope bytes, joined parents, media and consumer HTML.
|
|
14
|
+
SQLite uses indexed, bounded candidates, a running byte sum and lookahead before
|
|
15
|
+
transfer to Worker memory. Exact locale plus shared entries are merged in SQL.
|
|
16
|
+
Selected data fields retain JSON types and use one bound JSON field list, including
|
|
17
|
+
for projections larger than D1's ordinary bind or SQL-function argument limits.
|
|
18
|
+
|
|
19
|
+
The optional Web use cases return an explicit page object. Cloudflare mounts
|
|
20
|
+
publish its continuation in a body link and `Link: rel="next"`. HTML lists include
|
|
21
|
+
accessible navigation. An empty intermediate/final llms page remains traversable.
|
|
22
|
+
Root llms reads each canonical page once and expands shared entries per locale
|
|
23
|
+
without repeated canonical reads. Unknown/unmapped/no-content rows consume their
|
|
24
|
+
place in the page; they do not prevent reaching later eligible rows.
|
|
25
|
+
|
|
26
|
+
Sitemap parts project path metadata and default to 2,000 entries, expanding shared
|
|
27
|
+
routes per locale. The index walks the same metadata-page boundaries as the parts,
|
|
28
|
+
so a byte-limited part cannot cause skipped URLs. Small sites keep a single urlset.
|
|
29
|
+
Additional routes appear only in the first part. A custom path resolver must declare
|
|
30
|
+
its required data fields to obtain projection; otherwise full bounded data remains
|
|
31
|
+
available. Protocol limits fail explicitly instead of silently truncating URLs.
|
|
32
|
+
|
|
33
|
+
Translation lists request only the newest published parent per join value. Storage
|
|
34
|
+
ranks matching IDs before loading their bodies, preventing duplicate historical
|
|
35
|
+
parents from multiplying transferred rows. Missing/draft parents leave the child
|
|
36
|
+
unchanged. Child data still wins and media resolution remains one batch per page.
|
|
37
|
+
|
|
38
|
+
## Limits and alternatives
|
|
39
|
+
|
|
40
|
+
Sitemap index generation is O(N) metadata work on an origin MISS. It retains one
|
|
41
|
+
page and the index's URL list rather than all entry bodies; the protocol permits
|
|
42
|
+
at most 50,000 parts. Persisted generation/part boundaries would require an explicit
|
|
43
|
+
publication/invalidation owner and are deferred until index traffic justifies it.
|
|
44
|
+
A hard `LIMIT 500` would lose discovery URLs and is rejected. Offset pages would
|
|
45
|
+
make deep traversal grow with depth and are rejected. Entry caching would leave
|
|
46
|
+
the cold materialization spike and stale-state questions unresolved.
|
|
47
|
+
|
|
48
|
+
Pages reflect live canonical state, not a cross-request snapshot: publishing or
|
|
49
|
+
changing sort keys during a crawl can move entries between pages. Browser-local
|
|
50
|
+
IndexedDB shares the semantic cursor and output budget but still scans a local
|
|
51
|
+
collection. Parent/media data and consumer template expansion have separate costs;
|
|
52
|
+
there is no promise that arbitrary consumer rendering fits 1 MiB.
|
|
53
|
+
|
|
54
|
+
## Verification
|
|
55
|
+
|
|
56
|
+
Real SQLite tests walk the 18 combinations of 100/10,000/50,000 rows, 64 B/4 KiB
|
|
57
|
+
body and 1/3/10 locales, comparing complete llms/sitemap URL sets. List transfer is
|
|
58
|
+
21 rows at limit 20 in every case; 50,000 mixed rows and 10 locales retain all
|
|
59
|
+
275,000 URLs. Tests cover tied sort keys, >1-MiB entries, JSON projection types,
|
|
60
|
+
missing projected keys, parent duplicates/missing/drafts, child overrides, media
|
|
61
|
+
batching and persistence-field privacy. HTTP tests walk visible continuations,
|
|
62
|
+
empty final pages, sitemap indexes and staff-only preview behavior. The portable
|
|
63
|
+
storage conformance suite checks new semantics across adapters.
|
|
64
|
+
|
|
65
|
+
Wrangler-local records native D1 query/row work for list, llms and sitemap alongside
|
|
66
|
+
existing readiness, View, Procedure and Admin gates. Node traversal CPU/RSS includes
|
|
67
|
+
fixture/assertion costs and is not reported as Worker CPU. Combined #812 evidence
|
|
68
|
+
will supply Worker CPU, heap, cache HIT/MISS, remote controls and placement results.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# ADR-lite: native and full-facade performance evidence (#812)
|
|
2
|
+
|
|
3
|
+
Status: implemented; local gates passed. The deployment report and per-block
|
|
4
|
+
budget verdicts are maintained in [PR #821](https://github.com/aotter/mantle/pull/821).
|
|
5
|
+
|
|
6
|
+
The existing HTTP harness now records TTFB and full-body timing separately,
|
|
7
|
+
response bytes, and bounded concurrent arrivals. `pnpm bench:wrangler` keeps the
|
|
8
|
+
old row/query regressions and adds a native smoke matrix. `pnpm bench:parity`
|
|
9
|
+
runs the larger matrix, real Better Auth PKCE/OTP/consent/JWT/DPoP issuance,
|
|
10
|
+
revocation/role/replay denials, prepared D1 across fresh workerd processes, and
|
|
11
|
+
R2 transfers. No token, cookie, proof, user ID, SQL, argument or object key enters
|
|
12
|
+
the diagnostic record. Only the secret-protected synthetic fixture has control
|
|
13
|
+
endpoints; never bind it to a consumer database or bucket.
|
|
14
|
+
|
|
15
|
+
## Controls and boundaries
|
|
16
|
+
|
|
17
|
+
| Layer | Work |
|
|
18
|
+
|---|---|
|
|
19
|
+
| F0 | Fixed fetch response; standalone `floor-worker.ts` measures module/bundle floor. R2 F0 performs HEAD only. |
|
|
20
|
+
| F1 | Hono + native SQL and the same public payload; no authentication. |
|
|
21
|
+
| F2 | Native Worker, shared real Auth/session/consent/fresh-role/DPoP checks, SQL compiler and validators, response envelope. MCP shares protocol dispatch and View execution; unmeasured mutations fail closed. |
|
|
22
|
+
| M | Full `createMantleWorker`, same sealed plan, bindings, data and security policy. R2 uses the production storage commit adapter directly. |
|
|
23
|
+
|
|
24
|
+
Protected parity is F2/M. The View and catalog response bodies must match, drafts
|
|
25
|
+
stay private, and auth/protocol errors must fail correctly. Procedure input errors
|
|
26
|
+
compare status/code, not complete diagnostic wording. Admin/Web are facade coverage,
|
|
27
|
+
not a matched-native claim. R2 measures native stream metadata commits and failure/
|
|
28
|
+
retry; D1 MediaAsset publication is covered by the separate #810 integration test.
|
|
29
|
+
The shared comparison bundle intentionally keeps dependencies/configuration equal;
|
|
30
|
+
it cannot measure the difference between standalone application bundle sizes.
|
|
31
|
+
|
|
32
|
+
The planner runs outside workerd. Route/Schema/View axes each use 1/10/100/1,000;
|
|
33
|
+
extra Schemas are unindexed to keep the index-count axis fixed. D1 has a 100-column
|
|
34
|
+
limit, including generated index columns, so this is not a claim that 1,000 indexed
|
|
35
|
+
Schemas fit one entries table. Body/row axes use 64 B/4 KiB and 100/10,000/50,000;
|
|
36
|
+
locales use 1/3/10, MCP client concurrency uses 1/4/8, R2 uses 1/3/12 variants at
|
|
37
|
+
1/64/256 KiB. Actual simultaneous arrivals can be below client concurrency. The remote fleet
|
|
38
|
+
can add isolates during a batch. A request is repeat-in-isolate only after that
|
|
39
|
+
exact workload previously completed there; overlapping first arrivals stay in
|
|
40
|
+
the first-for-workload cohort. Two global warmups alone cannot prove a warm fleet.
|
|
41
|
+
First requests retain their raw records and a bounded extra-six-statement setup
|
|
42
|
+
allowance; repeat MCP requests must meet the exact two/three/four-statement gate.
|
|
43
|
+
Parity summaries use repeat cohorts; first-for-workload does not necessarily mean
|
|
44
|
+
cold module startup (another workload may already have used that isolate).
|
|
45
|
+
|
|
46
|
+
## Stable gates and measured signals
|
|
47
|
+
|
|
48
|
+
- Indexed public View: one statement and at most 100 available rows read at every
|
|
49
|
+
tested data size. MCP catalog: two statements (grant + fresh role), one KV GET;
|
|
50
|
+
View: three statements with Bearer, four with native DPoP replay reservation.
|
|
51
|
+
- Selected Trigger: four segment lookups at every route count in the portable
|
|
52
|
+
regression; native last-route requests retain one statement at 1,000 routes.
|
|
53
|
+
- Public HTML/llms: two warm statements and less than 1 MiB selected JSON. Sitemap
|
|
54
|
+
index remains explicit O(N) metadata work to enumerate complete part links.
|
|
55
|
+
- R2: exactly N GET + N PUT, known full-body bytes, at most three transfers in
|
|
56
|
+
flight; a first-batch failure starts no later batch and a retry succeeds.
|
|
57
|
+
- Fixture HTTP bodies at most 2 MiB; inspector heap after each batch at most
|
|
58
|
+
96 MiB. This is an observed JS heap ceiling, not instantaneous peak memory or
|
|
59
|
+
retained-after-GC heap. Custom application templates retain their own limits.
|
|
60
|
+
|
|
61
|
+
The default fixture bundle gate is 4,500 KiB raw / 800 KiB gzip (measured
|
|
62
|
+
3,699.93 / 647.98 KiB). The standalone F0 is 0.52 / 0.34 KiB; a 1,000-route
|
|
63
|
+
fixture is 4,051.18 / 668.60 KiB. Larger manifest-axis builds are reported
|
|
64
|
+
separately. Module startup profiles are a local diagnostic; actual deployed
|
|
65
|
+
startup must remain below the provider limit and is recorded during acceptance.
|
|
66
|
+
|
|
67
|
+
No cross-machine latency gate is used. CDP batch samples estimate local active JS
|
|
68
|
+
(including warmup); they are not per-request or billing CPU. Native Tail CPU/wall
|
|
69
|
+
are correlated by random request ID only in remote runs; unavailable timing stays
|
|
70
|
+
null. D1 metadata sums cover only `metadataStatements`; native Auth `.first()` does
|
|
71
|
+
not return rows/duration, and zero cannot substitute for that missing coverage.
|
|
72
|
+
|
|
73
|
+
The 2026-09-08 full local run contained 2,697 measured requests / 144 cases, no
|
|
74
|
+
unexpected HTTP status, max response 84,185 B, and max observed heap 67,103,480 B.
|
|
75
|
+
The observed F2/M p50 full-body delta for the 50k-row/4KiB View was -0.347 ms
|
|
76
|
+
(95% within-run bootstrap interval [-0.727, 0.128]); MCP catalog +0.903 ms
|
|
77
|
+
[0.470, 1.070]; Bearer MCP View -0.578 ms [-1.235, 0.329]. These are localhost
|
|
78
|
+
measurements, not a remote parity conclusion. Ten orthogonal route/Schema/View
|
|
79
|
+
runs and both extra locale runs also passed. Self-review corrected a fixture that
|
|
80
|
+
accidentally scaled indexes with Schemas, an English-only page assertion in the
|
|
81
|
+
multilingual fixture, and missing D1 metadata incorrectly summarized as zero.
|
|
82
|
+
|
|
83
|
+
## Remote difference budget (calibrated 2026-09-08)
|
|
84
|
+
|
|
85
|
+
The first complete off-a block (05:44:20–05:57:16 UTC, SDK 1798e78) contains
|
|
86
|
+
1,385 requests with 100% native CPU coverage, 46 first-for-workload arrivals and
|
|
87
|
+
no unexpected status failures. Public cache returned MISS then HIT; the HIT
|
|
88
|
+
had no invocation record. Warm F2/M CPU medians match at 0 ms health, 2 ms View,
|
|
89
|
+
1 ms Procedure, 2 ms MCP catalog and 3 ms Bearer MCP View. DPoP medians differ
|
|
90
|
+
by 0–1 ms; the largest within-run CPU delta upper interval is 2 ms. One hundred
|
|
91
|
+
requests per layer/diagnostics mode measure roughly +1 ms median CPU for the
|
|
92
|
+
collector itself on both layers. This is native millisecond-resolution telemetry.
|
|
93
|
+
|
|
94
|
+
Freeze this initial budget before evaluating the on-a/off-b/on-b blocks:
|
|
95
|
+
|
|
96
|
+
- At least 20 repeat-in-isolate samples per layer, equivalent successful payloads
|
|
97
|
+
and current authorization checks; expected denial cases are functional gates.
|
|
98
|
+
- Upper 95% bootstrap interval for M minus F2 median platform CPU: at most 2 ms.
|
|
99
|
+
- Upper 95% bootstrap interval for M minus F2 median full-body latency: at most
|
|
100
|
+
max(15 ms, 5% of the paired F2 median). Compare consistent placement contexts;
|
|
101
|
+
report changes in ingress/execution placement separately.
|
|
102
|
+
- Exact warm statement/binding budgets and zero unexpected outcomes still apply.
|
|
103
|
+
Latency/CPU differences are a matched deployment acceptance budget, not an
|
|
104
|
+
absolute cross-machine CI timer gate. First-for-workload records stay visible;
|
|
105
|
+
the budget makes no blanket claim about cold setup or all native workloads.
|
|
106
|
+
|
|
107
|
+
## Reproduction and remote acceptance
|
|
108
|
+
|
|
109
|
+
Run from the repository root with built workspace dependencies:
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
pnpm bench:wrangler
|
|
113
|
+
pnpm bench:parity
|
|
114
|
+
BENCH_ROUTES=1000 BENCH_CASES=scaling pnpm bench:parity
|
|
115
|
+
BENCH_SCHEMAS=1000 BENCH_CASES=scaling pnpm bench:parity
|
|
116
|
+
BENCH_VIEWS=1000 BENCH_CASES=scaling pnpm bench:parity
|
|
117
|
+
BENCH_LOCALES=en,fr,de BENCH_QUICK=1 pnpm bench:parity
|
|
118
|
+
node scripts/summarize-wrangler-parity.mjs /path/to/report.json
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
For remote acceptance, provision dedicated synthetic D1/KV (R2 when enabled),
|
|
122
|
+
put a random 32+ character BENCHMARK_KEY secret, and deploy the fixture with
|
|
123
|
+
BENCH_REMOTE_RECORDS=1. Run with BENCH_ORIGIN, BENCHMARK_KEY, BENCH_ACCOUNT_ID,
|
|
124
|
+
BENCH_PROFILE_NAME, BENCH_BLOCK and BENCH_PLACEMENT in the process environment.
|
|
125
|
+
The runner uses Wrangler's existing named-profile authentication to open the
|
|
126
|
+
same native trace-v1 API used by `wrangler tail`. It retains only the nonce-
|
|
127
|
+
correlated diagnostic fields and native CPU/wall timing in memory, never request
|
|
128
|
+
headers/body/URL, credentials or unrelated logs. The tail session is deleted at
|
|
129
|
+
completion. No paid Tail Worker, sink database writes or elevated log API token
|
|
130
|
+
is needed. Off-mode records also correlate platform CPU to measure overhead.
|
|
131
|
+
|
|
132
|
+
PHSU has no R2 subscription; its remote run uses BENCH_SKIP_R2=1. R2 coverage
|
|
133
|
+
comes from native workerd, with no remote R2 latency claim. A paid Tail Worker
|
|
134
|
+
attempt was rejected by the provider, then replaced by the verified real-time API.
|
|
135
|
+
|
|
136
|
+
Alternate off/on/off/on deployment blocks and BENCH_ORDER=reverse for the second
|
|
137
|
+
pair. Record actual placement status, request `cf-placement` when supplied,
|
|
138
|
+
ingress colo, deployment version, SDK/Wrangler/compatibility date, sampling window
|
|
139
|
+
and errors. Origin timings force private/no-store; a separate probe requires a
|
|
140
|
+
real public MISS followed by HIT with no invocation record. Local runs never
|
|
141
|
+
invent HITs. A deployed Worker with existing D1 is labeled deployment-first.
|
|
142
|
+
|
|
143
|
+
Use `wrangler deploy --dry-run --outfile <bundle>` followed by
|
|
144
|
+
`wrangler check startup --worker <bundle> --outfile <profile>` for module startup.
|
|
145
|
+
Record standalone F0, the full fixture and the actual packed consumer separately.
|
|
146
|
+
Set the remote F2/M CPU/latency difference budget after collecting the baseline,
|
|
147
|
+
then evaluate repeated blocks with bootstrap intervals, explaining shared-host
|
|
148
|
+
and cross-request correlation. Smart Placement provisioning belongs to #803;
|
|
149
|
+
INSUFFICIENT_INVOCATIONS is not evidence of a placement latency improvement.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# ADR-lite: collection creation statistics on Admin home
|
|
2
|
+
|
|
3
|
+
Status: proposed with #823; implementation stays in an unmerged demo PR.
|
|
4
|
+
Context: ADR-0019 semantic storage and optional Admin composition.
|
|
5
|
+
|
|
6
|
+
Add one optional `EntryReader.readCreationStatistics` read. SQLite/D1 implements
|
|
7
|
+
it; custom adapters without it return an explicit unavailable state in Admin.
|
|
8
|
+
The staff guard runs before storage access, and successful responses are private,
|
|
9
|
+
no-store. The endpoint accepts only primary, visible collections and four range
|
|
10
|
+
presets. No manifest keys, generic aggregation language or new cache port.
|
|
11
|
+
|
|
12
|
+
Each card independently persists its range and interval/cumulative mode in
|
|
13
|
+
`cms.preference.collection-statistics.<collection>`. Only preferences enter
|
|
14
|
+
localStorage. The chart and CSV describe current retained rows by native
|
|
15
|
+
`createdAt`, all statuses, with current `uiSchema.list.filterField` classification.
|
|
16
|
+
Unknown values share an Other series. Deleted rows are absent: these are creation
|
|
17
|
+
cohorts of retained rows, not an event log or historical inventory. Cumulative
|
|
18
|
+
means a prefix sum inside the selected range, starting from zero.
|
|
19
|
+
|
|
20
|
+
Ranges are 1h/5m, 24h/1h, 7d/6h and 20d/1d. Every interval is half-open; the last
|
|
21
|
+
ends at the observation time. CSV uses ISO UTC bounds while chart labels use the
|
|
22
|
+
browser's timezone. SVG steps preserve exact bucket extents without interpolation.
|
|
23
|
+
The toolbar download exports the current card's mode, current total and series;
|
|
24
|
+
CSV cells are quoted and formula-shaped headers are neutralized.
|
|
25
|
+
|
|
26
|
+
## Aggregation and freshness
|
|
27
|
+
|
|
28
|
+
A single SQL statement returns a total and sparse grouped counts from one SQLite
|
|
29
|
+
snapshot. `entries(collection, created_at)` is prepared by canonical migration
|
|
30
|
+
0008. The range branch searches the index, and the total branch counts index
|
|
31
|
+
entries. Payload JSON stays in the database; only count rows reach the Worker.
|
|
32
|
+
|
|
33
|
+
The existing Cloudflare KV decorator caches MCP site configuration, not entry
|
|
34
|
+
queries. Reusing that key/projection would mix unrelated data and mutation
|
|
35
|
+
ownership. Direct aggregation is sufficient for this demo: Wrangler-local with
|
|
36
|
+
10,000 rows returned 897 bytes with one statement, 30,002 engine rows read and
|
|
37
|
+
p50/p95 of 4.26/5.80 ms (10 samples). A real SQLite test also aggregates 50,000
|
|
38
|
+
4-KiB rows into at most 40 count rows / 4 KiB and verifies immediate updates and
|
|
39
|
+
deletes. These are local measurements, not remote latency guarantees.
|
|
40
|
+
|
|
41
|
+
There is no server statistics cache to evict or repopulate after a racing write.
|
|
42
|
+
Every Admin/MCP/Procedure committed write is visible on the next read, including
|
|
43
|
+
subtype updates and deletes. React Query data stays in memory, becomes stale
|
|
44
|
+
immediately, and refetches on mount/focus and every 60 seconds while the home is
|
|
45
|
+
active. Successful UI mutations invalidate its common statistics query prefix.
|
|
46
|
+
Changing only cumulative mode reuses the same count data without another query.
|
|
47
|
+
|
|
48
|
+
`ponytail:` exact totals still take O(N) index work and filtered time ranges may
|
|
49
|
+
read JSON for subtype grouping; returned bytes are bounded by bucket/enum count,
|
|
50
|
+
not row count. If measured production read volume makes this expensive, add a
|
|
51
|
+
transactionally maintained collection revision/projection and revision-keyed KV
|
|
52
|
+
snapshots. Plain KV delete-after-write is insufficient because eventual
|
|
53
|
+
consistency and racing miss fills can serve old aggregates. No cron, event store,
|
|
54
|
+
generic cache framework or eager write-through aggregate is justified here.
|
|
55
|
+
|
|
56
|
+
## Verification
|
|
57
|
+
|
|
58
|
+
Runtime SQLite tests cover lower/upper edges, unknown subtype, collection
|
|
59
|
+
isolation, zero rows, edits/deletes, invalid windows, range index use and 50k rows.
|
|
60
|
+
Admin tests cover 401/403 before storage, invalid collection/range (including
|
|
61
|
+
prototype names), all presets, no-store, and unsupported adapters. UI tests cover
|
|
62
|
+
all preference combinations, zero filling, stacking, prefix sums and CSV safety.
|
|
63
|
+
Browser QA covers independent per-card preferences, reload, both themes and CSV.
|
|
@@ -664,3 +664,10 @@ pnpm --filter @aotter/mantle-cloudflare exec vitest run \
|
|
|
664
664
|
contains all four scenarios and the exact public API names used by the fixture.
|
|
665
665
|
The package typecheck catches changes to those APIs; the integration test
|
|
666
666
|
catches changes to REST/MCP enforcement and mutable guard behavior.
|
|
667
|
+
|
|
668
|
+
The canonical MCP grant check joins the JWT's exact consent and original
|
|
669
|
+
session in one indexed D1 statement. Both identities, their user/client
|
|
670
|
+
bindings, session expiration, resource and the complete token scope set must
|
|
671
|
+
still match. The adapter then reads the user's role on every protected request;
|
|
672
|
+
no grant or role result is cached. With warm JWKS, this is one grant binding
|
|
673
|
+
call plus one role binding call, excluding DPoP replay, catalog and tool work.
|
|
@@ -14,13 +14,13 @@ import {
|
|
|
14
14
|
createConventionalAuth,
|
|
15
15
|
createConventionalBindings,
|
|
16
16
|
createMcpApiHandler,
|
|
17
|
-
mountAuthorize,
|
|
18
17
|
mountAdmin,
|
|
19
18
|
mountRuntimeEndpoints,
|
|
20
19
|
runMantleWorkerRequest,
|
|
21
20
|
setupIncompleteAuthResponse,
|
|
22
21
|
type MantleCloudflareEnv,
|
|
23
22
|
} from "@aotter/mantle/cloudflare";
|
|
23
|
+
import { mountMantleOAuth } from "@aotter/mantle/admin";
|
|
24
24
|
import { plan } from "../.mantle/generated/mantle.js";
|
|
25
25
|
|
|
26
26
|
interface Env extends MantleCloudflareEnv {
|
|
@@ -36,9 +36,13 @@ let assembled: ReturnType<typeof assemble> | undefined;
|
|
|
36
36
|
export default {
|
|
37
37
|
fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
|
38
38
|
return runMantleWorkerRequest(async () => {
|
|
39
|
-
assembled ??= assemble(env);
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const worker = assembled ??= assemble(env);
|
|
40
|
+
if (worker.auth.ready) ctx.waitUntil(worker.auth.ready.catch((error) => {
|
|
41
|
+
if (assembled === worker) assembled = undefined;
|
|
42
|
+
throw error;
|
|
43
|
+
}));
|
|
44
|
+
const incomplete = await setupIncompleteAuthResponse(request, worker.auth);
|
|
45
|
+
const response = incomplete ?? await worker.fetch(request, env, ctx);
|
|
42
46
|
ctx.waitUntil(env.AUDIT_QUEUE.send({
|
|
43
47
|
kind: "request-complete",
|
|
44
48
|
path: new URL(request.url).pathname,
|
|
@@ -57,7 +61,7 @@ function assemble(env: Env) {
|
|
|
57
61
|
|
|
58
62
|
mountRuntimeEndpoints(app, ref);
|
|
59
63
|
if (bindings.adminAssets) mountAdmin(app, ref, bindings.adminAssets);
|
|
60
|
-
|
|
64
|
+
mountMantleOAuth(app, { auth, assets: bindings.adminAssets });
|
|
61
65
|
app.get("/cache-probe", () => new Response("public", {
|
|
62
66
|
headers: { "cache-control": "public, s-maxage=60" },
|
|
63
67
|
}));
|
|
@@ -83,9 +87,11 @@ function assemble(env: Env) {
|
|
|
83
87
|
}
|
|
84
88
|
```
|
|
85
89
|
|
|
86
|
-
Keep the conventional `DB` binding and `nodejs_compat`.
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
Keep the conventional `DB` binding and `nodejs_compat`. An optional
|
|
91
|
+
deployment-owned `MANTLE_KV` binding lets `createConventionalBindings` maintain
|
|
92
|
+
the MCP catalog's site-settings projection at write time while D1 remains
|
|
93
|
+
canonical. CIMD metadata fetches also require `global_fetch_strictly_public`;
|
|
94
|
+
add the Queue producer in `wrangler.jsonc`:
|
|
89
95
|
|
|
90
96
|
```jsonc
|
|
91
97
|
{
|
package/docs/labels.md
CHANGED
|
@@ -34,6 +34,7 @@ For package README or package-local docs changes, prefer the package area label
|
|
|
34
34
|
| `area:admin-ui` | `packages/mantle-admin-ui` React admin SPA. |
|
|
35
35
|
| `area:docs` | Repo-wide human docs, governance docs, ADR text, release docs, root README content, and cross-cutting documentation work. |
|
|
36
36
|
| `area:adapter` | Adapter boundary work spanning Cloudflare or future adapters. |
|
|
37
|
+
| `area:ci` | GitHub Actions, dependency automation, and repository checks. |
|
|
37
38
|
|
|
38
39
|
## Release and review gates
|
|
39
40
|
|
|
@@ -60,6 +61,7 @@ gh label create "area:skills" --description "Agent Skills and install/extend/pro
|
|
|
60
61
|
gh label create "area:admin-ui" --description "React admin UI" --color "1d76db"
|
|
61
62
|
gh label create "area:docs" --description "Documentation and governance" --color "1d76db"
|
|
62
63
|
gh label create "area:adapter" --description "Adapter boundary and future adapter work" --color "1d76db"
|
|
64
|
+
gh label create "area:ci" --description "CI, dependency automation, and repository checks" --color "1d76db"
|
|
63
65
|
gh label create "breaking-change" --description "Semver-relevant breaking change" --color "b60205"
|
|
64
66
|
gh label create "skip-release-notes" --description "Release bookkeeping only; omit from generated GitHub notes" --color "ededed"
|
|
65
67
|
gh label create "needs-adr" --description "Requires an ADR or ADR-lite decision before merge" --color "d93f0b"
|
package/docs/media-uploads.md
CHANGED
|
@@ -184,3 +184,28 @@ Run the version-matched `media-gc` skill when an upload reached R2 but was
|
|
|
184
184
|
never committed. It audits first and removes only stale objects without
|
|
185
185
|
`committedAt` metadata after explicit operator confirmation. Do not use an R2
|
|
186
186
|
lifecycle rule: committed and uncommitted media share the same purpose prefix.
|
|
187
|
+
|
|
188
|
+
### R2 commit cost and recovery
|
|
189
|
+
|
|
190
|
+
R2 has no metadata-only patch. Mantle retains the existing `committedAt`,
|
|
191
|
+
`role`, `uploadGroupId` and filename markers by streaming each uploaded object
|
|
192
|
+
through GET → PUT. Commits validate the bundle shape before I/O and process
|
|
193
|
+
batches of at most three variants. A batch settles before another starts or an
|
|
194
|
+
error returns. MIME/size failures cancel the unused GET stream; failed PUTs also
|
|
195
|
+
attempt cancellation while preserving the original error.
|
|
196
|
+
|
|
197
|
+
A successful N-variant commit still uses N GETs, N PUTs and rewrites the sum of
|
|
198
|
+
variant sizes. Parallelism reduces serial waiting, not operation count or
|
|
199
|
+
bytes. Streaming-fake checks cover 1 × 1 KiB, 3 × 64 KiB and 12 × 256 KiB, with
|
|
200
|
+
maximum in-flight variants 1/3/3 and exact rewrite budgets; these are not remote
|
|
201
|
+
R2 latency measurements. Native R2 measurements and the complete media use case's separate D1 costs
|
|
202
|
+
are tracked by the shared #812 performance harness.
|
|
203
|
+
|
|
204
|
+
The asset row is saved only after every variant succeeds. Partial R2 failure
|
|
205
|
+
keeps the pending D1 record for retry before expiry. Already stamped objects
|
|
206
|
+
remain stamped and old/new committed media remains protected by the existing
|
|
207
|
+
GC rule. After pending expiry, partially stamped orphan objects require an
|
|
208
|
+
operator audit against D1 references; the ordinary GC must not remove their
|
|
209
|
+
markers or assume that a missing pending record means an object is unused.
|
|
210
|
+
This retains the existing conservative recovery contract without moving commit
|
|
211
|
+
or GC authority into KV.
|
|
@@ -75,12 +75,14 @@ Timing always reports p50/p95/max. A test-only Worker wrapper may also return
|
|
|
75
75
|
`x-mantle-query-count` and `x-mantle-rows-read`; those become distributions in
|
|
76
76
|
the same report. Do not expose these diagnostic headers in production.
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
routing, View execution, and origin
|
|
80
|
-
row fixtures and gates row-read
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
The path-scoped Cloudflare benchmark workflow runs `pnpm bench:wrangler`
|
|
79
|
+
against real Wrangler-local D1, Worker HTTP routing, View execution, and origin
|
|
80
|
+
page rendering. It compares 100 and 10,000 row fixtures and gates row-read
|
|
81
|
+
scaling plus endpoint query budgets, not absolute milliseconds. It is separate
|
|
82
|
+
from the required repository checks so an unrelated dependency or docs PR does
|
|
83
|
+
not fail on the platform harness. Wrangler-local does not emulate the new
|
|
84
|
+
entrypoint Workers Cache, so cache hits are a deployment-level smoke check
|
|
85
|
+
rather than a fabricated local metric.
|
|
84
86
|
|
|
85
87
|
## Seven findings: measured disposition
|
|
86
88
|
|
|
@@ -91,7 +93,7 @@ diagnostic, while query/row counts are the stable assertions.
|
|
|
91
93
|
|---|---|
|
|
92
94
|
| Public cache hits read D1 first | Removed from Worker code. Cloudflare's entrypoint Workers Cache runs before the Worker; Core has no inner render cache. |
|
|
93
95
|
| Slug/locale reads bypass generated indexes | Fixed by the shared schema-aware entry-read boundary. A 10,000-row page MISS measured 2 queries / 5 rows read. |
|
|
94
|
-
| OFFSET pagination |
|
|
96
|
+
| OFFSET pagination | Retained only where the View/Admin contract explicitly uses it, with a 500-row response cap. Public content lists and discovery now use forward keyset pages; they are not covered by the old 500-row claim. |
|
|
95
97
|
| Admin substring search scans | Accepted only for the authenticated Admin collection browser, with a 500-row response cap. Large/search-heavy sites should add a purpose-shaped indexed View or dedicated search service; do not expose this scan publicly. |
|
|
96
98
|
| Published list/sitemap/llms paths lack system indexes | Fixed with measured partial indexes for published global, locale, collection, and collection+locale ordering. The 100-row and 10,000-row API runs both measured 1 query / 20 rows read. |
|
|
97
99
|
| Page MISS waits for cache write-back | Removed. Origin rendering returns directly; Workers Cache owns response storage outside the Worker. |
|
|
@@ -102,3 +104,118 @@ not patterns for new public APIs. Re-measure before widening either scope.
|
|
|
102
104
|
|
|
103
105
|
See also [Schema indexes](./schema-indexes.md) and the official Cloudflare
|
|
104
106
|
[D1 index guidance](https://developers.cloudflare.com/d1/best-practices/use-indexes/).
|
|
107
|
+
|
|
108
|
+
### Prepared database, new Worker state
|
|
109
|
+
|
|
110
|
+
The Wrangler fixture resets its in-isolate runtime after seeding while retaining
|
|
111
|
+
D1. The first public page has a four-statement budget (fingerprint, lazy locale,
|
|
112
|
+
site settings, entry); subsequent origin pages have a two-statement budget.
|
|
113
|
+
This is a new application state in the same workerd isolate, not a measurement
|
|
114
|
+
of module startup CPU or an entrypoint cache HIT. Locale caching is enabled by
|
|
115
|
+
a successful preparation fingerprint; editable settings and media policy still
|
|
116
|
+
read the canonical database on every call.
|
|
117
|
+
|
|
118
|
+
### HTTP Trigger routing
|
|
119
|
+
|
|
120
|
+
The portable request handler indexes Trigger paths by method and segments once.
|
|
121
|
+
Literal and wildcard branches retain sealed-plan order, including encoded literal
|
|
122
|
+
collisions that an outer router may select differently. Each request decodes its
|
|
123
|
+
segments once and invokes the original Trigger identity through the same runtime.
|
|
124
|
+
The 1/10/100/1,000-route regression uses four segment lookups at every size;
|
|
125
|
+
overlapping wildcard shapes can visit multiple branches, pruned by route rank.
|
|
126
|
+
The HTTP microbench and workerd harness include the same route-count axis.
|
|
127
|
+
Workerd wall times include I/O and are not CPU measurements or a fixed-ms CI gate.
|
|
128
|
+
|
|
129
|
+
### Public content pages and discovery (#809)
|
|
130
|
+
|
|
131
|
+
| Surface | Canonical read / continuation |
|
|
132
|
+
|---|---|
|
|
133
|
+
| Collection HTML and collection Markdown | 50 entries by default, forward `cursor`; visible Next link plus HTTP `Link: rel="next"`. |
|
|
134
|
+
| Locale and root llms.txt | One canonical page, default 50 entries; root expands that page across configured locales in memory. Shared entries are not reread once per locale. Follow the body/HTTP continuation link. |
|
|
135
|
+
| Sitemap part | Up to 2,000 entries, with only declared path fields (built-in resolver: `slug`). A small site returns a urlset directly; a larger site returns a sitemap index linking every part. |
|
|
136
|
+
| Sitemap index | Walks metadata pages to derive exact part cursors; O(N) metadata work on an index MISS, with one page resident at a time. It is not a constant-work list endpoint. |
|
|
137
|
+
|
|
138
|
+
`EntryReader.readPublishedPage` caps returned data JSON at 1 MiB and 2,000 rows.
|
|
139
|
+
One oversized entry is returned alone to make progress. SQLite applies the byte
|
|
140
|
+
budget before transferring/parsing JSON in the Worker; one extra candidate
|
|
141
|
+
identifies continuation. A localized + shared page merges two indexed ranges
|
|
142
|
+
inside the same statement. The original `readPublished` remains an explicit
|
|
143
|
+
unbounded read unless its caller supplies a limit.
|
|
144
|
+
|
|
145
|
+
Translation lists resolve at most one newest published parent per join value,
|
|
146
|
+
then resolve media once for the bounded list. Parent payloads and media metadata
|
|
147
|
+
are additional input; the 1 MiB budget describes the canonical child page, not
|
|
148
|
+
arbitrary template output or total Worker heap. Custom renderers own their output
|
|
149
|
+
size. IndexedDB keeps identical page semantics but currently scans its local
|
|
150
|
+
collection; this is not a claim of bounded IndexedDB storage I/O.
|
|
151
|
+
|
|
152
|
+
The real SQLite fixture matrix covers 100/10,000/50,000 published rows, 64 B/4 KiB
|
|
153
|
+
bodies, and 1/3/10 locales. At limit 20, every case transfers 21 candidate rows;
|
|
154
|
+
4 KiB body data occupies 87,003–87,129 bytes, independent of collection size.
|
|
155
|
+
Complete llms traversal uses 2/200/1,000 statements at the default 50-row page
|
|
156
|
+
size, independent of locale count. Sitemap and llms URL sets match, including
|
|
157
|
+
275,000 URLs for 50,000 mixed localized/shared rows across 10 locales.
|
|
158
|
+
|
|
159
|
+
Run `pnpm --filter @aotter/mantle-cloudflare exec vitest run
|
|
160
|
+
test/public-content-scaling.test.ts` to emit JSON transfer sizes and traversal
|
|
161
|
+
CPU/wall/RSS diagnostics. These are Node + SQLite + assertions, including the
|
|
162
|
+
fixture database and URL-validation set; RSS is a process high-water mark, not
|
|
163
|
+
per-request Worker peak memory. Worker CPU, true cache HIT/MISS and placement
|
|
164
|
+
measurements belong to the matched native/full-stack harness (#812).
|
|
165
|
+
|
|
166
|
+
The workerd smoke also measures public list, llms and sitemap. The first two
|
|
167
|
+
use two warm statements (settings + page) and bounded D1 work. Sitemap index
|
|
168
|
+
queries and rows-read scale with the number of metadata parts; this explicit
|
|
169
|
+
cost preserves complete discovery instead of silently dropping URLs.
|
|
170
|
+
|
|
171
|
+
### Request diagnostics (version 1, test/performance only)
|
|
172
|
+
|
|
173
|
+
Import `runWithRequestDiagnostics`, `instrumentD1`, `instrumentKv` and
|
|
174
|
+
`instrumentR2` from `@aotter/mantle-cloudflare/testing`. Wrap native bindings once
|
|
175
|
+
before handing the same D1 object to Auth and Runtime. Open the request context
|
|
176
|
+
outside the complete facade fetch, and pass binding-presence flags matching the
|
|
177
|
+
instrumented fixture. The observer receives one response-time record; it never
|
|
178
|
+
receives request headers, tokens, proofs, user IDs, SQL, parameters, tool arguments,
|
|
179
|
+
object keys or response content. Sync/async observer failure cannot change the
|
|
180
|
+
original response or exception. The library emits no diagnostic headers or logs.
|
|
181
|
+
|
|
182
|
+
Records distinguish HTTP outcome from JSON-RPC result/error/tool-error. They
|
|
183
|
+
include actual in-isolate arrivals, inclusive OAuth/DPoP, role, runtime, catalog,
|
|
184
|
+
dispatcher construction and dispatch wall spans. Unreached phases are null.
|
|
185
|
+
Shared KV loads charge native I/O once to the initiating request; waiters record
|
|
186
|
+
wait duration and the same hit/miss/repair/error source. Boot publication has a
|
|
187
|
+
separate counter. The original rejected shared load remains retryable.
|
|
188
|
+
|
|
189
|
+
D1 statements and binding calls are separate: a batch is one call and N attempted
|
|
190
|
+
statements. Failed attempts count. `exec` uses the provider's count; unavailable
|
|
191
|
+
counts stay in `unknownStatementCalls`, never a semicolon parser. `first(column)`
|
|
192
|
+
and `raw` preserve native behavior and do not silently execute `all` to manufacture
|
|
193
|
+
metadata. Their absent metadata is null. Rows/duration are sums of available
|
|
194
|
+
metadata, and `metadataStatements` identifies coverage; incomplete coverage is
|
|
195
|
+
not a full-workload total. Serialized binding results are measured bytes, not a
|
|
196
|
+
claim about bytes on the provider's wire. Existing D1DatabaseDriver observers can
|
|
197
|
+
request metadata for first-row reads when a fixture explicitly chooses that mode.
|
|
198
|
+
|
|
199
|
+
KV bytes identify UTF-8, buffer or reserialized JSON sources. R2 payload bytes
|
|
200
|
+
remain unknown for an unconsumed/partly consumed GET. A successful PUT of that exact
|
|
201
|
+
native GET stream confirms the transferred body size on both operations. Streams
|
|
202
|
+
are never wrapped or buffered for diagnostics, preserving R2's native known-length
|
|
203
|
+
contract. `byteSamples` distinguishes known payload samples from the operation
|
|
204
|
+
count. Metadata/list response serialization is labeled separately from object
|
|
205
|
+
payload. R2 coverage is head/get/put/delete/list, not multipart-upload instrumentation.
|
|
206
|
+
|
|
207
|
+
Snapshots freeze at response creation; outstanding/deferred operations remain
|
|
208
|
+
visible through `inFlight` and metadata coverage and cannot rewrite a published
|
|
209
|
+
record. `totalMs` excludes the subsequent test-only JSON-RPC response inspection,
|
|
210
|
+
delivery and deferred work. CPU, TTFB, full-body duration and heap must be measured
|
|
211
|
+
separately. Worker wall clocks advance on I/O and are not a CPU timer; use the
|
|
212
|
+
[official CPU profiler](https://developers.cloudflare.com/workers/observability/dev-tools/cpu-usage/).
|
|
213
|
+
Measure diagnostics off/on overhead with the same workload before interpreting
|
|
214
|
+
small latency differences.
|
|
215
|
+
|
|
216
|
+
### Matched native facade controls (#812)
|
|
217
|
+
|
|
218
|
+
`pnpm bench:wrangler` now also runs the native parity smoke. The full
|
|
219
|
+
`pnpm bench:parity` matrix adds real Auth/MCP, cold workerd processes, R2,
|
|
220
|
+
TTFB/full-body timing and CPU/heap evidence. See
|
|
221
|
+
[the controls, gates and reproducible commands](./adr-lite-812-native-parity.md).
|
package/docs/release-process.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Release process
|
|
2
2
|
|
|
3
|
-
Mantle remains prerelease software until the stable v0.1.
|
|
3
|
+
Mantle remains prerelease software until the first stable v0.1.2 gate closes.
|
|
4
4
|
Published package versions, Git tags, GitHub releases, and Starter tags are
|
|
5
5
|
immutable: repair a bad release with the next version, never by replacing
|
|
6
6
|
public state.
|
|
@@ -87,8 +87,8 @@ rollback or unpublish behavior, or deploy Landing unless
|
|
|
87
87
|
- npm dist-tags follow the suffix: `alpha`, `beta`, `rc`, or `latest` for
|
|
88
88
|
stable versions.
|
|
89
89
|
- During the legacy `0.0.x-alpha` cadence, `latest` follows the current alpha.
|
|
90
|
-
The final `0.1.0-alpha.N` candidates advance only `alpha`; `latest`
|
|
91
|
-
|
|
90
|
+
The final `0.1.0-alpha.N` candidates advance only `alpha`; `latest` advances to the first stable `0.1.2` after its gate passes.
|
|
91
|
+
Do not roll back an existing legacy `latest` value.
|
|
92
92
|
|
|
93
93
|
## Release PR
|
|
94
94
|
|
|
@@ -252,3 +252,14 @@ deployment was started.
|
|
|
252
252
|
- A cross-cutting rename must include an explicit infrastructure-config diff
|
|
253
253
|
and live smoke test. CI success does not prove renamed Worker, D1, KV, route,
|
|
254
254
|
or secret bindings are correct.
|
|
255
|
+
|
|
256
|
+
## Final legacy release and next stable target
|
|
257
|
+
|
|
258
|
+
Owner decision (2026-09-08): `0.1.0-alpha.17` closes the legacy Landing/Starter
|
|
259
|
+
product line. Pin the existing Landing packages, Core deployment SHA and Starter
|
|
260
|
+
refs to that release, and retain immutable Starter tags for existing consumers.
|
|
261
|
+
No stable `0.1.0` release is planned. Issue #621 is superseded, not a claim that
|
|
262
|
+
its former production soak passed. First stable targets milestone `0.1.2`, with
|
|
263
|
+
new acceptance covering identity isolation, safe updates and retained production
|
|
264
|
+
stability after the breaking architecture is settled. Retiring Starter launch
|
|
265
|
+
is tracked by #786; the separate landing-next repository is out of this release.
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Spec-only adoption in an existing host
|
|
2
|
+
|
|
3
|
+
An existing application can reuse Mantle's Schema grammar and validation by
|
|
4
|
+
consuming the published `@aotter/mantle-spec` package, without running Mantle
|
|
5
|
+
Runtime. This Spec-only path is allowed by
|
|
6
|
+
[ADR-0019](adr/0019-sealed-manifest-runtime-pipeline.md), not a new adapter,
|
|
7
|
+
manifest grammar, or fork of Core.
|
|
8
|
+
|
|
9
|
+
This recipe targets `0.1.0-alpha.16`. Its public APIs and peer requirements are
|
|
10
|
+
prerelease contracts: pin the package, record the tested version, and rerun
|
|
11
|
+
compatibility checks when upgrading.
|
|
12
|
+
|
|
13
|
+
## What stays with the host
|
|
14
|
+
|
|
15
|
+
| Concern | Spec reuse | Host responsibility |
|
|
16
|
+
|---|---|---|
|
|
17
|
+
| Model definitions | Schema grammar, parse/link diagnostics | Project existing definitions into supported JSON Schema |
|
|
18
|
+
| Input | `EntryDataValidator` and structured errors | Authentication, authorization, normalization, write orchestration |
|
|
19
|
+
| Model browser | Parsed/linked Schema metadata | UI, metadata visibility, field display, graph layout |
|
|
20
|
+
| Relationships | Translation declaration/link validation | Existing slug joins, record integrity, locale policy |
|
|
21
|
+
| Storage and publishing | None in this recipe | Files, database, transactions, revisions, release/recovery |
|
|
22
|
+
| Operations and tools | None in this recipe | No automatic View, Procedure, Trigger, REST or MCP execution |
|
|
23
|
+
|
|
24
|
+
Mantle's Web, Admin UI and platform adapters remain optional. A Vue host does
|
|
25
|
+
not have to embed the React Admin SPA to reuse Spec. Avoid importing SDK
|
|
26
|
+
internals or maintaining a second manifest interpreter.
|
|
27
|
+
|
|
28
|
+
## One definition, several projections
|
|
29
|
+
|
|
30
|
+
The motivating Aotter official-website implementation keeps its existing
|
|
31
|
+
Nuxt/Nitro host and Git/D1 storage. Shared content definitions drive the site's
|
|
32
|
+
content layer, admin write validation, a responsive model/ER browser, and
|
|
33
|
+
Schema export. That website implementation was validated locally; this SDK
|
|
34
|
+
contribution does not deploy it or claim a production rollout.
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
Host-owned content definitions
|
|
38
|
+
├─ existing CMS/content queries
|
|
39
|
+
└─ JSON Schema in Mantle Schema manifests
|
|
40
|
+
├─ parse + link → model browser / metadata export
|
|
41
|
+
└─ EntryDataValidator → host-authorized write path
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Use the existing definitions as the source of truth. If the host starts with
|
|
45
|
+
Zod or another schema language, conversion is a host concern: verify that the
|
|
46
|
+
result uses supported JSON Schema keywords and preserves the intended
|
|
47
|
+
validation semantics. Do not hand-maintain a second field list for the graph.
|
|
48
|
+
|
|
49
|
+
## Minimal public-API recipe
|
|
50
|
+
|
|
51
|
+
Install the exact Spec package and its supported peer, without Runtime:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
npm install --save-exact @aotter/mantle-spec@0.1.0-alpha.16 zod@4.5.4
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The [synthetic fixture](../packages/mantle-spec/test/fixtures/spec-only-host.yaml)
|
|
58
|
+
contains categories, articles and article translations. It contains no real
|
|
59
|
+
website records, account configuration or credentials. Given that fixture as
|
|
60
|
+
`manifestYaml`, the application can prepare its validated model once:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import {
|
|
64
|
+
EntryDataValidator,
|
|
65
|
+
parseManifestSources,
|
|
66
|
+
ValidateManifestsUseCase,
|
|
67
|
+
} from "@aotter/mantle-spec";
|
|
68
|
+
|
|
69
|
+
const parsed = parseManifestSources({
|
|
70
|
+
sources: [{ sourceId: "host:content-model", text: manifestYaml }],
|
|
71
|
+
});
|
|
72
|
+
if (!parsed.ok) throw new Error(JSON.stringify(parsed.diagnostics));
|
|
73
|
+
|
|
74
|
+
const checked = ValidateManifestsUseCase.run({
|
|
75
|
+
parsed: parsed.value,
|
|
76
|
+
siteLocales: ["zh-TW", "en-US"],
|
|
77
|
+
});
|
|
78
|
+
if (checked.errorCount || !checked.linked) {
|
|
79
|
+
throw new Error(JSON.stringify(checked.diagnostics));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const article = checked.linked.schemas.find(
|
|
83
|
+
({ manifest }) => manifest.metadata.name === "articles",
|
|
84
|
+
)!.manifest;
|
|
85
|
+
const validator = new EntryDataValidator();
|
|
86
|
+
const diagnostics = validator.validate(article, { category: 42 }, { partial: true });
|
|
87
|
+
// INPUT_VALIDATION_FAILED at /category. The host must reject the write.
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The non-null assertion is specific to this known fixture. A dynamic host must
|
|
91
|
+
handle an unknown collection explicitly and fail closed. Do not fabricate
|
|
92
|
+
sealed parser/linker values or re-parse the same revision in each request.
|
|
93
|
+
|
|
94
|
+
`EntryDataValidator` returns diagnostics, not a sanitized payload. It does not
|
|
95
|
+
persist, authorize, coerce the original object, or enforce storage integrity.
|
|
96
|
+
Normalize explicitly in the host before validation and write only after
|
|
97
|
+
successful validation and authorization. An `additionalProperties: true`
|
|
98
|
+
compatibility policy accepts legacy fields; it is not a reason to expose those
|
|
99
|
+
fields publicly.
|
|
100
|
+
|
|
101
|
+
`partial: true` relaxes top-level required fields for drafts, while still
|
|
102
|
+
checking supplied types and nested required fields. Validate without `partial`
|
|
103
|
+
when the host needs a complete record, alongside its own publication rules.
|
|
104
|
+
Create a fresh validator for each model revision: its compiled cache is keyed
|
|
105
|
+
by manifest name (and partial/full mode), not by a changing schema body.
|
|
106
|
+
|
|
107
|
+
## Honest ER diagrams and metadata export
|
|
108
|
+
|
|
109
|
+
- Derive model nodes and field details from Schema properties. Use a small
|
|
110
|
+
host-owned relationship registry for relations not represented by Mantle
|
|
111
|
+
grammar; show logical joins distinctly from actual database foreign keys.
|
|
112
|
+
- The fixture's `x-example-source` and `x-example-relations` live inside JSON
|
|
113
|
+
Schema. They are illustrative vendor annotations, **not new Mantle keys or
|
|
114
|
+
executable relationship definitions**. Core may preserve them without
|
|
115
|
+
interpreting them. The host must validate their shape and endpoint fields.
|
|
116
|
+
- Do not label a filename/slug join as `x-mantle-ref`: that keyword represents
|
|
117
|
+
Mantle entry-ID references. A category slug matching a key inside a YAML
|
|
118
|
+
array remains a host-specific relation, not a generated SQL foreign key.
|
|
119
|
+
- `translates` validates the parent and join-field declarations, not whether
|
|
120
|
+
actual translation records exist. A Spec-only host still owns allowed
|
|
121
|
+
locales, uniqueness, fallback, and orphan handling. See
|
|
122
|
+
[ADR-0010](adr/0010-locale-and-translates.md); Runtime locale/storage gates
|
|
123
|
+
are not installed by this recipe.
|
|
124
|
+
- A read-only inventory can add derived fields such as filename `slug` or
|
|
125
|
+
directory `locale` without changing stored files. Document that projection;
|
|
126
|
+
it is not evidence that existing data is ready for Runtime import.
|
|
127
|
+
- The fixture uses JSON Schema `readOnly` as an inventory annotation. It is
|
|
128
|
+
not a security boundary: entry validation does not enforce host write
|
|
129
|
+
permissions. Export only after applying the host's metadata access policy.
|
|
130
|
+
- Exclude credential/account models and private data. Export schemas, not
|
|
131
|
+
records. Where appropriate, require an administrator and use
|
|
132
|
+
`Cache-Control: private, no-store` for model and export endpoints.
|
|
133
|
+
- Keep the graph usable on narrow screens: searchable model selection,
|
|
134
|
+
focused relationships, zoom/scroll, keyboard controls and a field-table
|
|
135
|
+
alternative. A graph must not be the only accessible representation.
|
|
136
|
+
|
|
137
|
+
## Reproducible evidence
|
|
138
|
+
|
|
139
|
+
From this SDK checkout:
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
pnpm --filter @aotter/mantle-spec test -- test/spec-only-host.test.ts
|
|
143
|
+
pnpm --filter @aotter/mantle-spec typecheck
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The [regression test](../packages/mantle-spec/test/spec-only-host.test.ts)
|
|
147
|
+
exercises the public export surface, manifest round trips, translation linking,
|
|
148
|
+
and strict/partial entry validation without Runtime. It is an SDK regression
|
|
149
|
+
fixture, not a new Starter workspace or a certification test suite.
|
|
150
|
+
|
|
151
|
+
An adopting host must also test its own installed package, real content
|
|
152
|
+
compatibility, HTTP authorization/invalid-input paths, metadata exclusions and
|
|
153
|
+
responsive UI. Existing data need not be published or uploaded to prove this.
|
|
154
|
+
Keep sensitive evidence internal and expose only an agreed verification report.
|
|
155
|
+
|
|
156
|
+
## Maintainer decision requested: ecosystem identity and recognition
|
|
157
|
+
|
|
158
|
+
The Aotter website is offered as a first **candidate** for a maintainer-designed
|
|
159
|
+
recognition process. This section asks for that design; it does not establish
|
|
160
|
+
an official certification, new compatibility tier or permission to use a badge.
|
|
161
|
+
|
|
162
|
+
Decisions requested from Mantle maintainers:
|
|
163
|
+
|
|
164
|
+
1. **Presentation:** how should a host show its relationship to Mantle in its
|
|
165
|
+
admin/about UI? Define approved terminology, visual mark and link target,
|
|
166
|
+
including how Spec-only adoption differs from Runtime/module adoption.
|
|
167
|
+
2. **Criteria and evidence:** which capabilities are being attested, against
|
|
168
|
+
which exact SDK version, and which checks must be reproducible? Distinguish
|
|
169
|
+
schema validation from runtime interoperability and security review.
|
|
170
|
+
3. **Authority and lifecycle:** who can grant recognition, where can a badge
|
|
171
|
+
be independently verified, and when must it be renewed or withdrawn after
|
|
172
|
+
package or application changes? Prevent self-issued marks from implying
|
|
173
|
+
maintainer approval.
|
|
174
|
+
4. **Pilot:** what additional evidence should this existing-host candidate
|
|
175
|
+
provide before receiving a mark, without exposing content or credentials?
|
|
176
|
+
|
|
177
|
+
Until that decision exists, an accurate technical status is **"Spec validation
|
|
178
|
+
passed against <exact version>"**, with a tested-capabilities list. It must not
|
|
179
|
+
be presented as **"Mantle certified"** or as a guarantee of data migration,
|
|
180
|
+
release reliability, security, shared UI components or full Runtime support.
|
|
181
|
+
|
|
182
|
+
This proposal uses the optional composition in ADR-0019 and respects
|
|
183
|
+
[ADR-0018](adr/0018-core-starters-repository-boundary.md): the website remains
|
|
184
|
+
an external consumer; the SDK receives only documentation and synthetic tests.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aotter/mantle",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.17",
|
|
4
4
|
"description": "Embeddable Mantle Core umbrella with Spec and Runtime; Web, Admin, Bun, Vercel, Cloudflare, and Admin UI are optional peer packages.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://mantle.tools/",
|
|
@@ -83,21 +83,21 @@
|
|
|
83
83
|
"README.md"
|
|
84
84
|
],
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@aotter/mantle-
|
|
87
|
-
"@aotter/mantle-
|
|
86
|
+
"@aotter/mantle-spec": "0.1.0-alpha.17",
|
|
87
|
+
"@aotter/mantle-runtime": "0.1.0-alpha.17"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
90
|
"aws4fetch": "^1.0.20",
|
|
91
|
-
"better-auth": "
|
|
91
|
+
"better-auth": "1.7.2",
|
|
92
92
|
"hono": "^4.12.0",
|
|
93
93
|
"@libsql/client": "^0.17.4",
|
|
94
94
|
"zod": "^4.5.0",
|
|
95
|
-
"@aotter/mantle-
|
|
96
|
-
"@aotter/mantle-admin-ui": "0.1.0-alpha.
|
|
97
|
-
"@aotter/mantle-
|
|
98
|
-
"@aotter/mantle-
|
|
99
|
-
"@aotter/mantle-
|
|
100
|
-
"@aotter/mantle-
|
|
95
|
+
"@aotter/mantle-admin": "0.1.0-alpha.17",
|
|
96
|
+
"@aotter/mantle-admin-ui": "0.1.0-alpha.17",
|
|
97
|
+
"@aotter/mantle-bun": "0.1.0-alpha.17",
|
|
98
|
+
"@aotter/mantle-vercel": "0.1.0-alpha.17",
|
|
99
|
+
"@aotter/mantle-cloudflare": "0.1.0-alpha.17",
|
|
100
|
+
"@aotter/mantle-web": "0.1.0-alpha.17"
|
|
101
101
|
},
|
|
102
102
|
"peerDependenciesMeta": {
|
|
103
103
|
"@aotter/mantle-admin": {
|
|
@@ -134,18 +134,18 @@
|
|
|
134
134
|
"devDependencies": {
|
|
135
135
|
"@types/node": "^26",
|
|
136
136
|
"aws4fetch": "^1.0.20",
|
|
137
|
-
"better-auth": "
|
|
137
|
+
"better-auth": "1.7.2",
|
|
138
138
|
"hono": "^4.13.3",
|
|
139
139
|
"@libsql/client": "^0.17.4",
|
|
140
140
|
"typescript": "^6.0.3",
|
|
141
141
|
"vitest": "^4.1.11",
|
|
142
142
|
"zod": "^4.5.4",
|
|
143
|
-
"@aotter/mantle-admin": "0.1.0-alpha.
|
|
144
|
-
"@aotter/mantle-admin
|
|
145
|
-
"@aotter/mantle-
|
|
146
|
-
"@aotter/mantle-
|
|
147
|
-
"@aotter/mantle-vercel": "0.1.0-alpha.
|
|
148
|
-
"@aotter/mantle-web": "0.1.0-alpha.
|
|
143
|
+
"@aotter/mantle-admin-ui": "0.1.0-alpha.17",
|
|
144
|
+
"@aotter/mantle-admin": "0.1.0-alpha.17",
|
|
145
|
+
"@aotter/mantle-bun": "0.1.0-alpha.17",
|
|
146
|
+
"@aotter/mantle-cloudflare": "0.1.0-alpha.17",
|
|
147
|
+
"@aotter/mantle-vercel": "0.1.0-alpha.17",
|
|
148
|
+
"@aotter/mantle-web": "0.1.0-alpha.17"
|
|
149
149
|
},
|
|
150
150
|
"engines": {
|
|
151
151
|
"node": ">=22"
|