@aotter/mantle 0.2.0-alpha.10 → 0.2.0-alpha.11
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/dist/auth/adapter.js +1 -28
- package/dist/auth/admin.js +2 -5
- package/dist/auth/caller.js +0 -27
- package/dist/auth/consent.js +100 -0
- package/dist/auth/hardening.js +0 -7
- package/dist/auth/identity.js +0 -11
- package/dist/auth/oauth.js +0 -32
- package/dist/auth/resource-seeds.js +0 -2
- package/dist/auth/route-http.js +33 -0
- package/dist/auth/routes.js +5 -151
- package/dist/build/compiler.d.ts +12 -611
- package/dist/build/compiler.js +3 -7029
- package/dist/bun/assets.js +0 -2
- package/dist/domain/adminOrder.js +0 -2
- package/dist/domain/columns.js +0 -15
- package/dist/domain/describe/chunk-SGMRVDGE.js +1 -0
- package/dist/domain/describe/index.js +1 -115
- package/dist/domain/describe/target-hash.js +1 -12
- package/dist/domain/entry.js +0 -14
- package/dist/domain/generated-tools.js +0 -1
- package/dist/domain/input.js +0 -2
- package/dist/domain/paging.js +0 -17
- package/dist/domain/recipes.js +0 -2
- package/dist/domain/userReferences.js +0 -1
- package/dist/domain/wireScalars.js +0 -6
- package/dist/infrastructure/adminEntries.js +9 -6
- package/dist/infrastructure/adminPaging.js +0 -2
- package/dist/infrastructure/artifacts.js +0 -4
- package/dist/infrastructure/authSchema.js +0 -9
- package/dist/infrastructure/bundle.js +0 -29
- package/dist/infrastructure/cli.js +0 -7
- package/dist/infrastructure/generate.js +0 -14
- package/dist/infrastructure/generateAuth.js +1 -9
- package/dist/infrastructure/help.js +0 -5
- package/dist/infrastructure/init.js +0 -4
- package/dist/infrastructure/inputs.js +0 -9
- package/dist/infrastructure/mounts.js +0 -3
- package/dist/infrastructure/wrangler.js +0 -6
- package/dist/kernel/authorization.js +0 -1
- package/dist/kernel/binds.js +1 -11
- package/dist/kernel/caller-identity.d.ts +0 -3
- package/dist/kernel/caller-identity.js +0 -15
- package/dist/kernel/caller-mode.d.ts +4 -0
- package/dist/kernel/caller-mode.js +14 -0
- package/dist/kernel/contracts.d.ts +1 -9
- package/dist/kernel/contracts.js +1 -42
- package/dist/kernel/cron.js +0 -4
- package/dist/kernel/dialect/common.js +0 -3
- package/dist/kernel/dialect/postgres.js +0 -2
- package/dist/kernel/dialect/sqlite.js +1 -6
- package/dist/kernel/drain.js +2 -8
- package/dist/kernel/driver/bun-sqlite.js +0 -4
- package/dist/kernel/driver/d1.js +0 -1
- package/dist/kernel/driver/postgres.js +0 -15
- package/dist/kernel/driver/transaction-context.js +0 -1
- package/dist/kernel/email.js +0 -4
- package/dist/kernel/host-access.js +2 -3
- package/dist/kernel/host-caller.d.ts +1 -1
- package/dist/kernel/host-caller.js +3 -6
- package/dist/kernel/json-body.js +0 -2
- package/dist/kernel/mcp-apps.js +0 -5
- package/dist/kernel/mcp-call.js +0 -1
- package/dist/kernel/mcp-handler.js +2 -26
- package/dist/kernel/mcp.js +0 -5
- package/dist/kernel/media.js +0 -18
- package/dist/kernel/paging.js +1 -9
- package/dist/kernel/publishing.js +1 -2
- package/dist/kernel/r2-media.js +0 -7
- package/dist/kernel/rest-dispatch.js +0 -1
- package/dist/kernel/rest-router.js +2 -3
- package/dist/kernel/rest.js +0 -3
- package/dist/kernel/scalars.js +1 -9
- package/dist/kernel/search.js +0 -1
- package/dist/kernel/site.js +0 -2
- package/dist/kernel/store.js +1 -26
- package/dist/kernel/surface-failure.d.ts +10 -0
- package/dist/kernel/surface-failure.js +29 -0
- package/dist/kernel/surface.js +0 -3
- package/dist/kernel/sweep.js +0 -1
- package/dist/kernel/translations.js +0 -1
- package/dist/kernel/user-caller.js +0 -6
- package/dist/kernel/user-fields.d.ts +1 -1
- package/dist/kernel/user-fields.js +1 -11
- package/dist/kernel/validation.js +0 -8
- package/dist/kernel/web-gateway.js +0 -2
- package/dist/kernel/web-head.js +0 -1
- package/dist/kernel/web-index.js +0 -1
- package/dist/kernel/web-response.js +0 -1
- package/dist/kernel/web-serve.js +97 -0
- package/dist/kernel/web.d.ts +1 -1
- package/dist/kernel/web.js +10 -91
- package/dist/kernel/where.js +0 -6
- package/dist/usecase/adminBindings.js +28 -21
- package/dist/usecase/adminClient.js +0 -21
- package/dist/usecase/adminDescriptors.js +2 -9
- package/dist/usecase/generate.js +0 -6
- package/dist/usecase/handlers.js +0 -3
- package/dist/usecase/mcp-media.js +0 -1
- package/dist/usecase/mcp.js +0 -4
- package/dist/usecase/paging.js +0 -2
- package/dist/usecase/publishing.js +0 -4
- package/dist/usecase/rest.js +0 -1
- package/dist/usecase/site.js +0 -1
- package/dist/usecase/stores.js +1 -13
- package/dist/usecase/tool-version.js +0 -3
- package/dist/usecase/translations.js +0 -2
- package/dist/usecase/validation.js +0 -20
- package/docs/examples/README.md +1 -1
- package/docs/examples/app-shell/package.json +3 -3
- package/docs/examples/app-shell/src/routes.ts +3 -1
- package/docs/examples/app-shell/src/service.ts +2 -1
- package/docs/handbook/cloudflare/service-entry.md +3 -0
- package/docs/handbook/examples/hub.md +1 -1
- package/docs/handbook/guides/agent-setup.md +4 -4
- package/docs/handbook/guides/public-pages.md +6 -0
- package/docs/handbook/reference/admin-descriptors.md +4 -1
- package/docs/handbook/reference/schema.md +1 -1
- package/docs/handbook/releases/index.md +3 -3
- package/package.json +2 -2
- package/skills/README.md +1 -1
- package/dist/domain/describe/chunk-ZVXBGP7Y.js +0 -424
package/dist/usecase/rest.js
CHANGED
|
@@ -14,7 +14,6 @@ export function emitRest(plan, lowered, target, description) {
|
|
|
14
14
|
const views = [], procedures = [];
|
|
15
15
|
let hasWire = false;
|
|
16
16
|
const convert = (code) => code ? `, convert: ${code}` : "";
|
|
17
|
-
// Each segment matches in its encoded form; `{name}` segments capture.
|
|
18
17
|
const route = (method, path) => {
|
|
19
18
|
const keys = [];
|
|
20
19
|
const source = path.split("/").map(segment => segment.startsWith("{") && segment.endsWith("}") ? (keys.push(segment.slice(1, -1)), "([^/]+)") : encodeURIComponent(segment).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("/");
|
package/dist/usecase/site.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Generate the optional site/media module and deployment DDL, never runtime preparation. */
|
|
2
1
|
import { assertSiteDefaultsCanonical, canonicalizeLocaleList, expandPolicyRequired } from "../build/compiler.js";
|
|
3
2
|
import { driverFor, isSqlite, q } from "../domain/entry.js";
|
|
4
3
|
export function emitSite(target, defaults, refuse, schemaNames) {
|
package/dist/usecase/stores.js
CHANGED
|
@@ -1,24 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One typed collection per Schema, over the generic `createStore`. Core's row rules (`rowPolicy`, `writePolicy`) are evaluated
|
|
3
|
-
* here, at build time, ONCE for a placeholder scope and TTL into the shared `Rules`; a Schema adds only a `Fact` (scope, TTL,
|
|
4
|
-
* publishing, its date-time and date columns). Generation is refused if a Schema's expanded rules differ from Core's own for it
|
|
5
|
-
* (caller and trusted rows, insert fills, update sets and lock, delete lock).
|
|
6
|
-
* Column names, value types, writable columns and `orderBy` are TypeScript types only: nothing at runtime knows them.
|
|
7
|
-
*/
|
|
8
1
|
import { NATIVE_NOT_NULL, notNullColumns, rowPolicy, writePolicy } from "../build/compiler.js";
|
|
9
2
|
import { declaredType, inputType, nativeType } from "../domain/columns.js";
|
|
10
3
|
import { q } from "../domain/entry.js";
|
|
11
4
|
import { expand, SCOPE, TTL } from "../kernel/store.js";
|
|
12
5
|
import { refChecks, userScopeTerm } from '../build/compiler.js';
|
|
13
|
-
// Types the typed Store compares and writes as plain bind values. Fixed numerics are scaled integers on SQLite; geo is two columns.
|
|
14
6
|
const SCALAR = new Set(["text", "real", "float8", "integer", "int4", "int8", "bool", "timestamptz", "date"]);
|
|
15
|
-
/** Core's own rules for a Schema, in the shape of `Rules` without the id expression (a delete's lock is compared separately). */
|
|
16
7
|
const core = (s, schemas) => {
|
|
17
8
|
const update = writePolicy(s, "update", { lock: true });
|
|
18
9
|
return { caller: rowPolicy(s, "caller", schemas), trusted: rowPolicy(s, "trusted", schemas), fills: writePolicy(s, "insert").fills, sets: update.sets, lock: update.lock };
|
|
19
10
|
};
|
|
20
11
|
const probe = { scope: SCOPE, ttl: TTL, ttlSeconds: 0, publishing: true };
|
|
21
|
-
/** @internal */
|
|
22
12
|
export function emitStores(schemas, isSqlite, newId, refuse, required = false) {
|
|
23
13
|
const all = Object.values(schemas).sort((a, b) => a.name < b.name ? -1 : 1);
|
|
24
14
|
if (!all.length && !required)
|
|
@@ -28,7 +18,7 @@ export function emitStores(schemas, isSqlite, newId, refuse, required = false) {
|
|
|
28
18
|
for (const s of all) {
|
|
29
19
|
const type = (column) => declaredType(s.fields, column) ?? (column === "version" ? "integer" : column === "published_at" ? "timestamptz" : "text");
|
|
30
20
|
const declared = Object.keys(s.fields).filter(f => f !== s.scope && s.fields[f] !== "geo");
|
|
31
|
-
const geo = Object.keys(s.fields).filter(f => s.fields[f] === "geo").flatMap(f => [`${f}_lat`, `${f}_lng`]);
|
|
21
|
+
const geo = Object.keys(s.fields).filter(f => s.fields[f] === "geo").flatMap(f => [`${f}_lat`, `${f}_lng`]);
|
|
32
22
|
const names = [...["id", "version", "created_at", "updated_at", "author_id", ...(s.publishing ? ["status", "published_at"] : [])], ...declared];
|
|
33
23
|
const required = new Set(s.schema.required ?? []), notNull = notNullColumns(s);
|
|
34
24
|
const nonNull = (column) => NATIVE_NOT_NULL.has(column) || notNull.has(column);
|
|
@@ -45,11 +35,9 @@ export function emitStores(schemas, isSqlite, newId, refuse, required = false) {
|
|
|
45
35
|
...(ref.length ? { ref } : {}),
|
|
46
36
|
...(parent ? { parent: { on: s.translates.on.toLowerCase(), ...(parent.scope ? { scope: [parent.scope, s.scope] } : {}), table: s.translates.parent.toLowerCase(), visible: rowPolicy(s, "caller", schemas).find(t => t[1] === "parent"), caller: rowPolicy(parent, "caller", schemas), trusted: rowPolicy(parent, "trusted", schemas), fields: inherited, ts: inherited.filter(c => parent.fields[c] === "timestamptz"), d: inherited.filter(c => parent.fields[c] === "date") } } : {}),
|
|
47
37
|
};
|
|
48
|
-
// the shared rules, with this Schema's scope and TTL put in, are Core's rules for it: or the runtime would enforce something else
|
|
49
38
|
const expected = q([core(s, schemas), writePolicy(s, "delete", { lock: true }).lock]), got = q([expand(rules, fact), rules.lock]);
|
|
50
39
|
if (expected !== got)
|
|
51
40
|
refuse(`schemas/${s.name}`, `Core's row rules for this Schema differ from the shared rules (${expected} vs ${got}); the generic Store cannot serve it.`);
|
|
52
|
-
// ORDER BY is allowed where the caller's scoped read can walk an index (its first column, or the one after the scope column) and the column is NOT NULL.
|
|
53
41
|
const indexed = new Set([...(s.publishing ? [[...(s.scope ? [s.scope] : []), "published_at", "id"]] : []), ...(s.indexes ?? []), ...(s.unique ?? [])].map(i => i[0] === s.scope ? i[1] : i[0]).filter((c) => !!c));
|
|
54
42
|
const key = (c) => /^[A-Za-z_]\w*$/.test(c) ? c : q(c);
|
|
55
43
|
const of = (columns, value) => `{ ${columns.map(c => `${key(c)}: ${value(c)};`).join(" ")} }`;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/** Generate-time tool versions: a stale host copy shows the model an older version than results carry. */
|
|
2
1
|
import { canonical, fnv } from "../build/compiler.js";
|
|
3
|
-
/** A hash of what a catalogue lists, so an unchanged definition keeps its version across deploys. */
|
|
4
2
|
export const versionOf = (value) => fnv(canonical(value));
|
|
5
|
-
/** A listed tool: its version, and its description (each locale's) ending with that version. */
|
|
6
3
|
export function versioned(tool) {
|
|
7
4
|
const v = versionOf(tool), mark = (text) => `${text} (tool version ${v})`;
|
|
8
5
|
return { ...tool, description: typeof tool.description === "string" ? mark(tool.description) : Object.fromEntries(Object.entries(tool.description).map(([locale, text]) => [locale, mark(text)])), version: v };
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { userReferences } from "../domain/userReferences.js";
|
|
2
|
-
/** Translation helpers use generated SQL policy, never a request-time manifest. */
|
|
3
2
|
import { rowPolicy } from "../build/compiler.js";
|
|
4
3
|
import { renderTerm } from "../kernel/store.js";
|
|
5
4
|
import { dialect as SQLITE } from "../kernel/dialect/sqlite.js";
|
|
6
5
|
import { dialect as POSTGRES } from "../kernel/dialect/postgres.js";
|
|
7
6
|
import { declaredType } from "../domain/columns.js";
|
|
8
7
|
import { driverFor, isSqlite, q } from "../domain/entry.js";
|
|
9
|
-
/** The parent is checked by the same atomic UPDATE that publishes the child. */
|
|
10
8
|
export function publishedParent(plan, child, d, recipes, trusted) {
|
|
11
9
|
const tr = child.translates, parent = plan.schemas[tr.parent.toLowerCase()], on = tr.on.toLowerCase();
|
|
12
10
|
const conditions = [`p.${d.ident(on)} = ${d.ident(child.name)}.${d.ident(on)}`];
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Compile boundary checks into each operation; no schema enters the request bundle. */
|
|
2
1
|
import { INPUT_RULE_KEYWORDS, lookupLocalRef as lookup, rootRefChain } from "../build/compiler.js";
|
|
3
2
|
import { literal, q } from "../domain/entry.js";
|
|
4
3
|
import { publishingInput } from "./publishing.js";
|
|
@@ -6,14 +5,9 @@ import { equal } from "../kernel/validation.js";
|
|
|
6
5
|
const pointer = (key) => key.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
7
6
|
const supported = new Set([..."$defs $ref $schema $id type nullable properties required additionalProperties items enum const format pattern minLength maxLength minimum maximum exclusiveMinimum exclusiveMaximum multipleOf minItems maxItems title description default readOnly writeOnly examples deprecated x-mantle-ref x-mcp-hint".split(" "), ...INPUT_RULE_KEYWORDS]);
|
|
8
7
|
const shapes = "see docs/handbook/reference/surface.md";
|
|
9
|
-
// YAML .inf and .nan parse to numbers that JSON writes as null.
|
|
10
8
|
const finite = (value) => typeof value === "number" ? Number.isFinite(value) : !!value && typeof value === "object" ? Object.values(value).every(finite) : true;
|
|
11
9
|
const has = (key) => `Object.hasOwn(object,${q(key)})`;
|
|
12
10
|
const exactly = (value, keys) => !!value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).sort().join(" ") === keys;
|
|
13
|
-
/**
|
|
14
|
-
* An object schema's rules, each failing `when` (JavaScript on `object`) with its sentence at the missing field `key`, or the
|
|
15
|
-
* reason a rule has an unsupported shape.
|
|
16
|
-
*/
|
|
17
11
|
function rules(schema) {
|
|
18
12
|
const declared = (names) => Array.isArray(names) && names.length > 0 && names.every(name => typeof name === "string" && Object.hasOwn(schema.properties ?? {}, name));
|
|
19
13
|
const list = (names) => names.map(name => `'${name}'`).join(" and ");
|
|
@@ -34,7 +28,6 @@ function rules(schema) {
|
|
|
34
28
|
if (condition !== undefined || then !== undefined || otherwise !== undefined) {
|
|
35
29
|
const [key] = exactly(condition, "properties required") ? Object.keys(condition.properties) : [];
|
|
36
30
|
const value = key === undefined ? undefined : condition.properties[key];
|
|
37
|
-
// Without required: [key], an absent key satisfies `if`.
|
|
38
31
|
if (key === undefined || !exactly(condition.properties, key) || !exactly(value, "const") || !(["string", "boolean"].includes(typeof value.const) || Number.isFinite(value.const) || value.const === null) || q(condition.required) !== q([key]) || !declared([key]))
|
|
39
32
|
return `if is supported only as exactly { properties: { <field>: { const: <string, finite number, boolean or null> } }, required: [<field>] }, so an absent field does not satisfy it; ${shapes}.`;
|
|
40
33
|
if (then === undefined && otherwise === undefined || [then, otherwise].some(branch => branch !== undefined && !(exactly(branch, "required") && declared(branch.required))))
|
|
@@ -47,19 +40,12 @@ function rules(schema) {
|
|
|
47
40
|
}
|
|
48
41
|
return found;
|
|
49
42
|
}
|
|
50
|
-
/** Whether an input schema accepts `{}`: a list View is read with no input, so web indexes need this. */
|
|
51
43
|
export function acceptsEmpty(input) {
|
|
52
44
|
if (input === undefined)
|
|
53
45
|
return true;
|
|
54
|
-
// Validation applies a root `$ref`'s target too, so each schema along the chain must accept `{}`; an unresolved or cyclic chain does not.
|
|
55
|
-
// dependentRequired needs a present key; an if needs its field, so `{}` meets only an else. An enum or const must admit `{}`.
|
|
56
46
|
return rootRefChain(input)?.every(schema => !schema.required?.length && schema.anyOf === undefined && !schema.else?.required?.length
|
|
57
47
|
&& (schema.enum === undefined || schema.enum.some(option => equal({}, option))) && (schema.const === undefined || equal({}, schema.const)) && (schema.type === undefined || [schema.type].flat().includes("object"))) ?? false;
|
|
58
48
|
}
|
|
59
|
-
/**
|
|
60
|
-
* An input schema as an AI tool catalogue advertises it: some model APIs refuse rules (a root `anyOf`, `if`), so each rule leaves
|
|
61
|
-
* the schema and becomes a sentence of the description.
|
|
62
|
-
*/
|
|
63
49
|
export function advertised(input, description) {
|
|
64
50
|
if (!input)
|
|
65
51
|
return { inputSchema: input, description };
|
|
@@ -68,7 +54,6 @@ export function advertised(input, description) {
|
|
|
68
54
|
if (!node || typeof node !== "object")
|
|
69
55
|
return;
|
|
70
56
|
const schema = node;
|
|
71
|
-
// An MCP View tool keeps each rule set of its root `$ref` chain as an `allOf` branch over the merged properties.
|
|
72
57
|
const branches = Array.isArray(schema.allOf) ? schema.allOf : [];
|
|
73
58
|
for (const branch of [schema, ...branches.map(branch => ({ ...branch, properties: schema.properties }))]) {
|
|
74
59
|
const found = rules(branch);
|
|
@@ -111,9 +96,7 @@ export function emitValidation(plan, refuse = (path, message) => { throw Error(`
|
|
|
111
96
|
files[file] = `import { transition } from "@aotter/mantle/validation";\nexport const validate = (raw: unknown) => transition(${q(base)}, raw);\n`;
|
|
112
97
|
continue;
|
|
113
98
|
}
|
|
114
|
-
// Only a local `$ref`, or more than 100 nested objects and arrays, can reach the nesting limit: only then is `depth` passed.
|
|
115
99
|
const recursive = json.includes('"$ref"') || json.split(/[[{]/).length > 101, depth = recursive ? ",depth+1" : "";
|
|
116
|
-
// Identical bodies share one function.
|
|
117
100
|
const nodes = new Map(), bodies = new Map(), functions = [], formats = new Set(), patterns = new Map();
|
|
118
101
|
let equality = false;
|
|
119
102
|
const resolve = (ref) => {
|
|
@@ -142,7 +125,6 @@ export function emitValidation(plan, refuse = (path, message) => { throw Error(`
|
|
|
142
125
|
refuse(base, `Unsupported input type '${type}'.`);
|
|
143
126
|
if (types.length)
|
|
144
127
|
lines.push(typeCheck(types));
|
|
145
|
-
// After a single type check `fail` (never) has narrowed `value`: that type's guard is implied.
|
|
146
128
|
const only = types.length === 1 ? types[0] : undefined;
|
|
147
129
|
const guard = (type, test = `(${condition(type)})`) => only === type || type === "number" && only === "integer" ? "" : test + " && ";
|
|
148
130
|
if (schema.enum) {
|
|
@@ -197,7 +179,6 @@ export function emitValidation(plan, refuse = (path, message) => { throw Error(`
|
|
|
197
179
|
}
|
|
198
180
|
if (schema.items)
|
|
199
181
|
lines.push(`${only === "array" ? "" : "if (Array.isArray(value)) "}for (let i=0;i<value.length;i++) ${emit(schema.items)}(value[i],path+"/"+i${depth});`);
|
|
200
|
-
// Several keys that need no JSON-pointer escaping are checked by one loop, in the same order.
|
|
201
182
|
const required = schema.required ?? [], checks = Object.entries(schema.properties ?? {}).map(([key, child]) => [key, emit(child)]);
|
|
202
183
|
const loop = (keys) => keys.length > 1 && keys.every(key => pointer(key) === key);
|
|
203
184
|
const object = loop(required) ? [`for (const key of ${q(required)}) if (!Object.hasOwn(object,key)) fail(path+"/"+key,"Required field is missing.");`] : required.map(key => `if (!Object.hasOwn(object,${q(key)})) fail(path+${q("/" + pointer(key))},"Required field is missing.");`);
|
|
@@ -217,7 +198,6 @@ export function emitValidation(plan, refuse = (path, message) => { throw Error(`
|
|
|
217
198
|
if (object.length)
|
|
218
199
|
lines.push(`${only === "object" ? "" : `if (${condition("object")}) `}{ const object = value as Record<string,unknown>;\n${object.join("\n")}\n}`);
|
|
219
200
|
const body = lines.join("\n");
|
|
220
|
-
// Without recursion nothing calls a check before its body is complete, so an identical body's function serves it.
|
|
221
201
|
const fn = recursive ? named : bodies.get(body) ?? named;
|
|
222
202
|
if (fn === named) {
|
|
223
203
|
bodies.set(body, fn);
|
package/docs/examples/README.md
CHANGED
|
@@ -39,5 +39,5 @@ shell's allow-all `src/signup.ts`, unless sign-up should be invite-only. Purchas
|
|
|
39
39
|
|
|
40
40
|
Every example uses two roles: `admin` (the admin layer) and `user`.
|
|
41
41
|
|
|
42
|
-
[Reference service](https://github.com/aotter/mantle/tree/v0.2.0-alpha.
|
|
42
|
+
[Reference service](https://github.com/aotter/mantle/tree/v0.2.0-alpha.11/docs/examples/reference-service) is the release smoke test for packed packages, not a starting point. Its source lives in the tagged repository; it is not included in the npm docs.
|
|
43
43
|
Projects on 0.1.x move with [the upgrade guide](../upgrade-0.1-to-0.2.md).
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"accept": "node accept.mjs"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@aotter/mantle": "0.2.0-alpha.
|
|
19
|
-
"@aotter/mantle-ui": "0.2.0-alpha.
|
|
18
|
+
"@aotter/mantle": "0.2.0-alpha.11",
|
|
19
|
+
"@aotter/mantle-ui": "0.2.0-alpha.11",
|
|
20
20
|
"@better-auth/cimd": "1.7.7",
|
|
21
21
|
"@better-auth/core": "1.7.7",
|
|
22
22
|
"@better-auth/mcp": "1.7.7",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@modelcontextprotocol/ext-apps": "2.0.0",
|
|
26
26
|
"@modelcontextprotocol/server": "2.2.0",
|
|
27
27
|
"better-auth": "1.7.7",
|
|
28
|
-
"@aotter/mantle-admin": "0.2.0-alpha.
|
|
28
|
+
"@aotter/mantle-admin": "0.2.0-alpha.11",
|
|
29
29
|
"react": "19.3.0",
|
|
30
30
|
"react-dom": "19.3.0"
|
|
31
31
|
},
|
|
@@ -2,7 +2,9 @@ import type { Caller } from "@aotter/mantle/contracts";
|
|
|
2
2
|
import { signInPage } from "./sign-in.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* App-owned routes, tried before
|
|
5
|
+
* App-owned routes, tried before REST. Return undefined to fall through. Paths under /admin and /mcp never reach here, and
|
|
6
|
+
* neither do Better Auth's (/api/auth/*, /oauth/consent*, /.well-known/oauth-*). A root catch-all page (/:slug) must
|
|
7
|
+
* still return undefined for the paths of the app's own HTTP Triggers (REST), or it shadows them.
|
|
6
8
|
* GET /sign-in: member sign-in (src/sign-in.ts); a member page sends anonymous visitors there with signInRedirect.
|
|
7
9
|
* AGENT: add visitor pages and callbacks/webhooks here. Visitor pages carry everything in `mantle help public-pages`
|
|
8
10
|
* (canonical, Open Graph, JSON-LD, answer block, .md alternates, sitemap.xml, robots.txt, llms.txt; recipes example).
|
|
@@ -43,7 +43,8 @@ export async function fetchService(request: Request, env: Env, context: Executio
|
|
|
43
43
|
const ownAuth = await withAuthTasks(context.waitUntil.bind(context), () => authRoutes(auth!, { methods: [{ kind: "email-otp" }], resolver: caller })(request));
|
|
44
44
|
if (ownAuth) return respond(ownAuth);
|
|
45
45
|
const url = new URL(request.url);
|
|
46
|
-
|
|
46
|
+
// /admin and /mcp belong to Admin and MCP: a root catch-all page in src/routes.ts (/:slug) never shadows them.
|
|
47
|
+
const own = /^\/(?:admin|mcp)(?:\/|$)/.test(url.pathname) ? undefined : await appRoutes(request, env, { caller, waitUntil: context.waitUntil.bind(context) });
|
|
47
48
|
if (own) return respond(own);
|
|
48
49
|
let response: Response | undefined;
|
|
49
50
|
if (url.pathname === "/admin/api" || url.pathname.startsWith("/admin/api/")) {
|
|
@@ -81,6 +81,9 @@ export default {
|
|
|
81
81
|
if (shell) return shell;
|
|
82
82
|
// Your public frontend/web.ts routes can use an anonymous Caller without
|
|
83
83
|
// loading auth; their generated SQL still controls publication visibility.
|
|
84
|
+
// Mounted here, after every Mantle surface, a root catch-all page (/:slug)
|
|
85
|
+
// cannot shadow /admin, /mcp or REST. Mount them earlier only behind a
|
|
86
|
+
// guard that skips those paths, as the app shell does.
|
|
84
87
|
return new Response('Not found', { status: 404, headers: { 'cache-control': 'no-store' } });
|
|
85
88
|
} catch (error) { return surfaceFailure(error); }
|
|
86
89
|
});
|
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
|
|
3
3
|
Use the [Worker quickstart](../start/quickstart-worker.md) for a complete native manifest and entry, [typed queries](../guides/typed-queries.md) for embedding, and [service composition](../cloudflare/service-entry.md) for optional surfaces.
|
|
4
4
|
|
|
5
|
-
The runnable [reference service](https://github.com/aotter/mantle/tree/v0.2.0-alpha.
|
|
5
|
+
The runnable [reference service](https://github.com/aotter/mantle/tree/v0.2.0-alpha.11/docs/examples/reference-service) exercises the native package integration. See the [generated public website](../../examples/generated-web/README.md) for a native Worker example.
|
|
@@ -13,21 +13,21 @@ carries.
|
|
|
13
13
|
The Mantle plugin carries one OSS authoring skill, `mantle`, and local
|
|
14
14
|
acceptance references. It does not configure a hosted-service connection.
|
|
15
15
|
Install from the Git tag matching the exact SDK version you selected (currently
|
|
16
|
-
`v0.2.0-alpha.
|
|
16
|
+
`v0.2.0-alpha.11`). Alpha releases integrate on `develop`; an unqualified
|
|
17
17
|
`aotter/mantle` source follows `main` and can install older, incompatible
|
|
18
18
|
instructions. A skill install does not upgrade the application's SDK.
|
|
19
19
|
|
|
20
20
|
For Codex, pin the marketplace checkout:
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
|
-
codex plugin marketplace add aotter/mantle --ref v0.2.0-alpha.
|
|
23
|
+
codex plugin marketplace add aotter/mantle --ref v0.2.0-alpha.11
|
|
24
24
|
codex plugin add mantle@mantle
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
For Claude Code, first clone the release into a directory you will keep:
|
|
28
28
|
|
|
29
29
|
```sh
|
|
30
|
-
git clone --depth 1 --branch v0.2.0-alpha.
|
|
30
|
+
git clone --depth 1 --branch v0.2.0-alpha.11 https://github.com/aotter/mantle.git mantle-plugin
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Then, in Claude Code, use that checkout's absolute path:
|
|
@@ -41,7 +41,7 @@ Cursor and GitHub Copilot use their plugin manifests from the same tagged
|
|
|
41
41
|
checkout. Without a plugin host, install only the consumer skill by name:
|
|
42
42
|
|
|
43
43
|
```sh
|
|
44
|
-
npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.
|
|
44
|
+
npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.11 --skill mantle
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
When upgrading the SDK, update the marketplace ref or local tagged checkout and
|
|
@@ -11,6 +11,12 @@ here instead of repeating it.
|
|
|
11
11
|
- **App-owned pages** in `src/routes.ts`, when the page needs its own layout, JSON-LD or languages. The reference
|
|
12
12
|
implementation is `node_modules/@aotter/mantle/docs/examples/recipes` (`src/pages.ts`, `src/routes.ts`; `smoke.mjs` checks every item below).
|
|
13
13
|
|
|
14
|
+
## URLs
|
|
15
|
+
|
|
16
|
+
Mantle owns `/admin`, `/mcp`, `/api/auth/`, `/oauth/consent` and `/.well-known/oauth-*`, and the app's HTTP Triggers own
|
|
17
|
+
their declared paths. Give visitor pages their own prefix (`/{locale}/…`, `/teas/{slug}`), or make a root page (`/{slug}`)
|
|
18
|
+
return nothing for those paths; the app shell already keeps `/admin` and `/mcp` away from `src/routes.ts`.
|
|
19
|
+
|
|
14
20
|
## Every public page
|
|
15
21
|
|
|
16
22
|
- Server-rendered HTML per URL with the content in the body. A client-only SPA shell or metadata set by script is not
|
|
@@ -37,7 +37,10 @@ execution plan or SQL AST, storage access or execution observer is included.
|
|
|
37
37
|
admin tool names. A conflicting declared admin tool is refused.
|
|
38
38
|
- `webmcp` carries static tool declarations and Admin page routes. `bootstrap`
|
|
39
39
|
combines `collections`, `operations`, `views` and `webmcp`; the handler adds
|
|
40
|
-
request-owned `me`, `site` and any selected entries page.
|
|
40
|
+
request-owned `me`, `site` and any selected entries page. Its
|
|
41
|
+
`developerConsole` is a loader for the same snapshot, generated again in
|
|
42
|
+
`admin-developer-console.ts`, so Admin imports it on a developer-console
|
|
43
|
+
request instead of at Worker startup.
|
|
41
44
|
|
|
42
45
|
Generation reuses the 0.2 projection and SQL-logic helpers. View columns come
|
|
43
46
|
from the compiled plan (Core's `viewOutputs` provenance, including aliases,
|
|
@@ -94,7 +94,7 @@ For inserts, one allowed partition autofills a missing value; multiple values re
|
|
|
94
94
|
|
|
95
95
|
#### References to a scoped Schema
|
|
96
96
|
|
|
97
|
-
A non-null `x-mantle-ref` value that points to a scoped Schema (field or owner scope) must name a row of that Schema the caller can read, matched on `id` or on the object form's `field`, and that row must have the same value as the child for every scope field the two share (`ADR-0034`: a reference between scoped Schemas stays inside one owner or partition). A Store insert or update,
|
|
97
|
+
A non-null `x-mantle-ref` value that points to a scoped Schema (field or owner scope) must name a row of that Schema the caller can read, matched on `id` or on the object form's `field`, and that row must have the same value as the child for every scope field the two share (`ADR-0034`: a reference between scoped Schemas stays inside one owner or partition). A Store insert or update, a Procedure `INSERT … VALUES`, `UPDATE … SET` or `DO UPDATE SET`, and native Admin's generic create and patch check it in the same statement; a missing, unreadable or other-partition parent behaves like a missing row: a single-row statement returns HTTP 409 (`CONFLICT`), and an `ON CONFLICT` insert or a set update writes nothing for that row. A caller holding several values or `{ all: true }` can still not point a child in one partition at a parent in another. `INSERT … SELECT` that writes such a reference runs only for system callers. A caller's `UPDATE … SET` or `DO UPDATE SET` of such a reference takes no subquery, which could read the parent's columns as its own. Trusted writes (system callers, `runtime.store`) are not checked, like the other row rules. NULL is always allowed; deleting a parent does not touch its children.
|
|
98
98
|
|
|
99
99
|
## Reserved entry columns
|
|
100
100
|
|
|
@@ -17,10 +17,10 @@ Pin every package you use to the same exact version and upgrade them together.
|
|
|
17
17
|
|
|
18
18
|
## 0.2.0 — in preparation
|
|
19
19
|
|
|
20
|
-
### Upgrading to 0.2.0-alpha.
|
|
20
|
+
### Upgrading to 0.2.0-alpha.11
|
|
21
21
|
|
|
22
22
|
Pin every selected `@aotter/mantle`, `@aotter/mantle-ui` and
|
|
23
|
-
`@aotter/mantle-admin` dependency to `0.2.0-alpha.
|
|
23
|
+
`@aotter/mantle-admin` dependency to `0.2.0-alpha.11`, refresh the lockfile,
|
|
24
24
|
then regenerate and check the application's owned artifacts. Keep existing
|
|
25
25
|
production migrations application-owned; generated initial DDL is for an empty
|
|
26
26
|
database. This alpha includes breaking Admin authoring and paging changes.
|
|
@@ -112,7 +112,7 @@ Existing alpha.6 applications require a source migration:
|
|
|
112
112
|
application code. Review [authorization and Caller wiring](../concepts/runtime-and-adapters.md)
|
|
113
113
|
and regenerate all owned artifacts before running `generate --check`.
|
|
114
114
|
|
|
115
|
-
The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0-alpha.
|
|
115
|
+
The [reference Worker](https://github.com/aotter/mantle/tree/v0.2.0-alpha.11/docs/examples/reference-service) and
|
|
116
116
|
[app shell](../../examples/app-shell/) demonstrate the current composition.
|
|
117
117
|
The following earlier-alpha notes are historical; the current handbook and
|
|
118
118
|
the native migration above supersede their runtime and CLI instructions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aotter/mantle",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"engines": {
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"registry": "https://registry.npmjs.org"
|
|
141
141
|
},
|
|
142
142
|
"scripts": {
|
|
143
|
-
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.lib.json && node scripts/bundle-compiler.mjs && node scripts/prune-declarations.mjs",
|
|
143
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.lib.json && node ../../scripts/strip-comments.mjs tsconfig.lib.json && node scripts/bundle-compiler.mjs && node scripts/prune-declarations.mjs",
|
|
144
144
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
145
145
|
"test": "bun test tests/*.test.ts && bun run test:compiler",
|
|
146
146
|
"generate": "bun src/infrastructure/cli.ts",
|
package/skills/README.md
CHANGED
|
@@ -27,7 +27,7 @@ release tag as the application's SDK; alpha releases on `develop` can be newer
|
|
|
27
27
|
than the default `main` branch. For a skill-only installation:
|
|
28
28
|
|
|
29
29
|
```sh
|
|
30
|
-
npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.
|
|
30
|
+
npx skills add https://github.com/aotter/mantle/tree/v0.2.0-alpha.11 --skill mantle
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
The explicit skill name excludes repository maintainer skills. The manifests
|