@aotter/mantle 0.1.0-alpha.7 → 0.1.0-alpha.9

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.
Files changed (106) hide show
  1. package/README.md +68 -30
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/admin.js +2 -0
  5. package/dist/admin.js.map +1 -0
  6. package/dist/bun.d.ts +2 -0
  7. package/dist/bun.d.ts.map +1 -0
  8. package/dist/bun.js +2 -0
  9. package/dist/bun.js.map +1 -0
  10. package/dist/cli/create.d.ts +2 -0
  11. package/dist/cli/create.d.ts.map +1 -0
  12. package/dist/cli/create.js +243 -0
  13. package/dist/cli/create.js.map +1 -0
  14. package/dist/cli/generate.d.ts.map +1 -0
  15. package/dist/cli/generate.js +101 -0
  16. package/dist/cli/generate.js.map +1 -0
  17. package/dist/cli/harness.d.ts +3 -0
  18. package/dist/cli/harness.d.ts.map +1 -0
  19. package/dist/{harness-cli.js → cli/harness.js} +13 -8
  20. package/dist/cli/harness.js.map +1 -0
  21. package/dist/cli/main.d.ts +3 -0
  22. package/dist/cli/main.d.ts.map +1 -0
  23. package/dist/{cli.js → cli/main.js} +6 -8
  24. package/dist/cli/main.js.map +1 -0
  25. package/dist/cli/skills.d.ts +3 -0
  26. package/dist/cli/skills.d.ts.map +1 -0
  27. package/dist/{skills.js → cli/skills.js} +51 -6
  28. package/dist/cli/skills.js.map +1 -0
  29. package/dist/cli/update.d.ts.map +1 -0
  30. package/dist/{update.js → cli/update.js} +72 -46
  31. package/dist/cli/update.js.map +1 -0
  32. package/dist/codegen/emitMantleModule.d.ts +16 -0
  33. package/dist/codegen/emitMantleModule.d.ts.map +1 -0
  34. package/dist/codegen/emitMantleModule.js +217 -0
  35. package/dist/codegen/emitMantleModule.js.map +1 -0
  36. package/dist/codegen.d.ts +2 -0
  37. package/dist/codegen.d.ts.map +1 -0
  38. package/dist/codegen.js +2 -0
  39. package/dist/codegen.js.map +1 -0
  40. package/dist/provision/renderProvisionBundle.d.ts +70 -0
  41. package/dist/provision/renderProvisionBundle.d.ts.map +1 -0
  42. package/dist/provision/renderProvisionBundle.js +367 -0
  43. package/dist/provision/renderProvisionBundle.js.map +1 -0
  44. package/dist/provision.d.ts +2 -0
  45. package/dist/provision.d.ts.map +1 -0
  46. package/dist/provision.js +2 -0
  47. package/dist/provision.js.map +1 -0
  48. package/dist/vercel-libsql.d.ts +2 -0
  49. package/dist/vercel-libsql.d.ts.map +1 -0
  50. package/dist/vercel-libsql.js +2 -0
  51. package/dist/vercel-libsql.js.map +1 -0
  52. package/dist/vercel.d.ts +2 -0
  53. package/dist/vercel.d.ts.map +1 -0
  54. package/dist/vercel.js +2 -0
  55. package/dist/vercel.js.map +1 -0
  56. package/dist/web.d.ts +2 -0
  57. package/dist/web.d.ts.map +1 -0
  58. package/dist/web.js +2 -0
  59. package/dist/web.js.map +1 -0
  60. package/docs/adapter-guide.md +88 -45
  61. package/docs/adr/0001-four-atom-manifest-model.md +11 -3
  62. package/docs/adr/0007-ai-as-primary-author.md +3 -3
  63. package/docs/adr/0009-consumer-supplied-manifests.md +9 -4
  64. package/docs/adr/0011-adapter-port-spec.md +18 -4
  65. package/docs/adr/0014-auth-better-auth-and-multi-tenant-mcp.md +111 -6
  66. package/docs/adr/0018-core-starters-repository-boundary.md +22 -1
  67. package/docs/adr/0019-sealed-manifest-runtime-pipeline.md +204 -0
  68. package/docs/adr/README.md +12 -10
  69. package/docs/api-mcp-authorization.md +21 -42
  70. package/docs/assets/mantle-admin-operations.png +0 -0
  71. package/docs/assets/mantle-hero.jpg +0 -0
  72. package/docs/auth-hosting-model.md +5 -5
  73. package/docs/cloudflare-low-level-composition.md +30 -18
  74. package/docs/deferred-lifecycle-queues.md +20 -38
  75. package/docs/design-atoms.md +31 -133
  76. package/docs/labels.md +1 -1
  77. package/docs/media-uploads.md +2 -2
  78. package/docs/migration-0.1.2.md +45 -0
  79. package/docs/performance-harness.md +4 -4
  80. package/docs/release-process.md +72 -22
  81. package/docs/sealed-pipeline-ownership.md +100 -0
  82. package/package.json +84 -14
  83. package/skills/README.md +38 -7
  84. package/skills/develop/SKILL.md +7 -5
  85. package/skills/install/SKILL.md +26 -25
  86. package/skills/media-gc/SKILL.md +6 -0
  87. package/skills/plugin/SKILL.md +1 -0
  88. package/skills/provision/SKILL.md +2 -0
  89. package/skills/theme/SKILL.md +1 -0
  90. package/skills/update/SKILL.md +1 -0
  91. package/dist/cli.d.ts +0 -3
  92. package/dist/cli.d.ts.map +0 -1
  93. package/dist/cli.js.map +0 -1
  94. package/dist/generate.d.ts.map +0 -1
  95. package/dist/generate.js +0 -226
  96. package/dist/generate.js.map +0 -1
  97. package/dist/harness-cli.d.ts +0 -3
  98. package/dist/harness-cli.d.ts.map +0 -1
  99. package/dist/harness-cli.js.map +0 -1
  100. package/dist/skills.d.ts +0 -2
  101. package/dist/skills.d.ts.map +0 -1
  102. package/dist/skills.js.map +0 -1
  103. package/dist/update.d.ts.map +0 -1
  104. package/dist/update.js.map +0 -1
  105. /package/dist/{generate.d.ts → cli/generate.d.ts} +0 -0
  106. /package/dist/{update.d.ts → cli/update.d.ts} +0 -0
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @aotter/mantle
2
2
 
3
- Umbrella entry for the Mantle SDK — a manifest-driven CMS for Cloudflare Workers, built around a 4-atom YAML model (Schema / View / Procedure / Trigger) where agents write config and the runtime carries the complexity.
3
+ Umbrella entry for the embeddable Mantle SDK — a manifest-driven application
4
+ engine built around a 4-atom YAML model (Schema / View / Procedure / Trigger)
5
+ where agents write config and the runtime carries the complexity.
4
6
 
5
7
  > Mantle is prerelease software. Use this package's `package.json` as the exact
6
8
  > installed version; APIs may change between prereleases until v0.1.0.
@@ -15,25 +17,39 @@ pnpm add @aotter/mantle@alpha
15
17
 
16
18
  ## What's inside
17
19
 
18
- Adopters install this one package and import from subpaths. Sub-packages remain individually installable for tooling / alt-adapter authors.
20
+ The umbrella provides Spec and Runtime by default. Install an optional package
21
+ before importing its matching Web, Admin, Bun, Vercel, Cloudflare, or Admin UI
22
+ subpath. Every sub-package also remains directly installable.
19
23
 
20
24
  | Subpath | Re-exports |
21
25
  |---|---|
22
26
  | `@aotter/mantle/spec` (or root) | Manifest grammar, validators, JSON-Schema→Zod, diagnostic catalog (no env / no IO) |
23
27
  | `@aotter/mantle/runtime` | Hexagonal runtime: domain ports, use cases, infrastructure helpers (no adapter deps) |
24
28
  | `@aotter/mantle/runtime/testing` | Node-only crowded SQLite planner and HTTP sampling helpers |
25
- | `@aotter/mantle/cloudflare` | Cloudflare Workers adapter D1, Workers Cache, R2, Better Auth, MCP via `@cloudflare/workers-oauth-provider` |
29
+ | `@aotter/mantle/codegen` | Pure linked manifests typed runtime module emitter (no IO) |
30
+ | `@aotter/mantle/web` | Optional HTML, Markdown, `llms.txt`, sitemap, SEO, and preview composition (no routes or platform deps) |
31
+ | `@aotter/mantle/admin` | Optional Admin API, auth routes, and static-asset composition |
32
+ | `@aotter/mantle/bun` | Bun adapter — caller-owned `bun:sqlite` and Web-standard View/Trigger transport |
33
+ | `@aotter/mantle/vercel` | Vercel Functions adapter — injected durable storage and platform `waitUntil` |
34
+ | `@aotter/mantle/vercel/libsql` | Optional application-owned Turso/libSQL driver |
35
+ | `@aotter/mantle/cloudflare` | Cloudflare Workers adapter — D1, Workers Cache, R2, and Better Auth 1.7 MCP/CIMD |
26
36
  | `@aotter/mantle/admin-ui` | Pre-built React 19 admin SPA bundle |
27
37
 
28
38
  ```ts
29
- import { parseManifestsOrThrow } from "@aotter/mantle/spec";
30
- import { createCmsRuntime } from "@aotter/mantle/runtime";
31
- import { mountServerEndpoints } from "@aotter/mantle/cloudflare";
39
+ import { linkManifestSet, parseManifestSources } from "@aotter/mantle/spec";
40
+ import { bootMantleRuntime, compileRuntimePlan } from "@aotter/mantle/runtime";
41
+ import { createMantleWeb } from "@aotter/mantle/web";
42
+ import { mountRuntimeEndpoints } from "@aotter/mantle/cloudflare";
32
43
  ```
33
44
 
45
+ The umbrella installs only Spec and Runtime. Web, Admin, Admin UI, Bun,
46
+ Vercel, and Cloudflare are optional peers; install only the subpaths selected
47
+ by the application.
48
+
34
49
  The package also installs `mantle` and `mantle-harness`:
35
50
 
36
51
  ```bash
52
+ npx -y @aotter/mantle@alpha create <type> <directory>
37
53
  pnpm exec mantle generate
38
54
  pnpm exec mantle generate --check
39
55
  pnpm exec mantle skills
@@ -43,38 +59,56 @@ pnpm exec mantle-harness indexes --require-public
43
59
  pnpm exec mantle-harness http --base-url http://127.0.0.1:8787 --route page=/en/example
44
60
  ```
45
61
 
46
- `mantle generate` validates `./manifests/site.yaml`, writes the
47
- parsed manifest module and handler declarations to `.mantle/generated/`, and
48
- copies the version-matched Admin SPA to `public/_mantle/admin/` for the
49
- platform's static asset service. `--check` verifies both outputs.
50
- It does not sync skills, update packages, style, provision, or deploy.
51
- When manifests declare Views or Procedures, the generated `site.ts` also exports
52
- `bindMantleSite(runtime)`: its `views` and `procedures` keys, inputs, and outputs
53
- come from those manifests, while diagnostics still come from the runtime use
54
- cases.
55
- Dynamic `runtime.viewsByName` access remains available for low-level code.
62
+ `mantle generate` validates and compiles `./manifests/`, then writes one typed
63
+ `.mantle/generated/mantle.ts` module. It performs no Admin asset installation,
64
+ skill sync, package update, styling, provisioning, or deployment.
65
+ The same pure emitter is available from `@aotter/mantle/codegen` when a host
66
+ wants to own parsing and filesystem IO.
56
67
 
57
68
  ```ts
58
- const site = bindMantleSite(runtime);
59
- const notes = await site.views["published-notes"]();
60
- const result = await site.procedures["expire-order"](
69
+ import { createMantle } from "../.mantle/generated/mantle.js";
70
+
71
+ const mantle = await createMantle({ storage, handlers, ports });
72
+ const notes = await mantle.views.publishedNotes();
73
+ const result = await mantle.procedures.expireOrder(
61
74
  { orderId },
62
75
  { user: null, staff: null, env },
63
76
  );
77
+ await mantle.entries.orders.createDraft({ data, authorId: user.id });
78
+
79
+ // The typed projection never hides the underlying Core runtime.
80
+ await mantle.runtime.archive.execute({ id: noteId, ctx });
64
81
  ```
65
82
 
66
- `mantle skills` explicitly copies the installed package's `develop`, `plugin`,
67
- `theme`, and `update` skills to matching `.agent/skills/mantle-*` and
68
- `.claude/skills/mantle-*` paths. Both tool layouts receive identical bytes;
69
- `--check` detects drift without writing. Manifest generation never rewrites
70
- agent instructions.
83
+ Generated property names are deterministic lower-camel identifiers; calls keep
84
+ the authored wire names internally. `createMantle()` eagerly prepares once and
85
+ does not cache or retry. Dynamic and platform hosts can keep their own lifecycle,
86
+ use generated `bindMantle(runtime)`, or skip code generation and call
87
+ `runtime.executeView({ view: "published-notes" })` directly.
88
+
89
+ `mantle create` materializes one version-matched starter bundle into a new
90
+ directory and stops: it installs nothing, initializes no repository, configures
91
+ no auth, and deploys nothing. It resolves only the official immutable starter
92
+ tag for the CLI's own version, and refuses to write into an existing path.
93
+
94
+ `mantle skills` copies every skill the installed package marks
95
+ `projection: project` in its front matter into matching
96
+ `.agents/skills/mantle-*` and `.claude/skills/mantle-*` paths — no list of
97
+ skill names is maintained anywhere else. Skills that act destructively or
98
+ target one platform stay out of that set and are opt-in. Both tool layouts
99
+ receive identical bytes; `--check` detects drift without writing. An older
100
+ project may also carry `.agent/skills/`, which is left untouched. Manifest
101
+ generation never rewrites agent instructions.
71
102
 
72
103
  `mantle update` compares the recorded source bundle, a target bundle, and the
73
104
  local project, then writes `.mantle/update-report.json`. It never applies the
74
105
  diff. Configure `.mantle/features.json` `registry.bundleBaseUrl` with a URL
75
106
  containing `{ref}`, or pass `--bundle-base-url` for the alpha.63 bridge. The
76
107
  report carries the only supported metadata migration; review and apply it
77
- after porting selected upstream changes.
108
+ after porting selected upstream changes. Format-versioned bundles use the same
109
+ strict renderer as `mantle create`; an invalid current launch state fails
110
+ instead of producing a guessed diff. Older bundles without `formatVersion`
111
+ retain a read-only compatibility renderer.
78
112
 
79
113
  ## Conventional Cloudflare Worker
80
114
 
@@ -82,9 +116,9 @@ The normal Worker entry delegates Core-owned assembly to the SDK:
82
116
 
83
117
  ```ts
84
118
  import { createMantleWorker } from "@aotter/mantle/cloudflare";
85
- import { manifest } from "../.mantle/generated/site.js";
119
+ import { plan } from "../.mantle/generated/mantle.js";
86
120
 
87
- export default createMantleWorker({ manifest });
121
+ export default createMantleWorker({ plan });
88
122
  ```
89
123
 
90
124
  `createMantleWorker` owns conventional D1/assets bindings, Auth, Admin,
@@ -120,8 +154,8 @@ the consumer build. Computed paths cannot be proven statically, so the facade
120
154
  checks Hono's assembled route table and fails closed before serving requests.
121
155
  There is no standard-route override option.
122
156
 
123
- Cloudflare projects expose that generated Admin bundle and the site's own
124
- frontend assets through one native binding:
157
+ Cloudflare projects may expose an independently installed Admin bundle and the
158
+ application's own frontend assets through one native binding:
125
159
 
126
160
  ```toml
127
161
  [assets]
@@ -205,9 +239,13 @@ Core-owned `mantle:plugin` skill and records it in `.mantle/plugins.json` plus
205
239
 
206
240
  | Adapter | Status |
207
241
  |---|---|
242
+ | Bun | ✅ shipping |
243
+ | Vercel Functions (Node.js) | ✅ shipping |
208
244
  | Cloudflare Workers | ✅ shipping |
209
245
 
210
- The `mantle-runtime` package never imports Cloudflare-specific types — adapters bind concrete drivers (D1 / R2) to the runtime's `domain/port/*` interfaces, so adding a new adapter is a port-implementation exercise, not a refactor.
246
+ The `mantle-runtime` package never imports platform-specific types — adapters
247
+ bind concrete storage and lifecycle primitives to Core ports, so adding a new
248
+ adapter is a port-implementation exercise, not a runtime refactor.
211
249
 
212
250
  ## Documentation
213
251
 
@@ -0,0 +1,2 @@
1
+ export * from "@aotter/mantle-admin";
2
+ //# sourceMappingURL=admin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
package/dist/admin.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "@aotter/mantle-admin";
2
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../src/admin.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC"}
package/dist/bun.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "@aotter/mantle-bun";
2
+ //# sourceMappingURL=bun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.d.ts","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
package/dist/bun.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from "@aotter/mantle-bun";
2
+ //# sourceMappingURL=bun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.js","sourceRoot":"","sources":["../src/bun.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function runCreate(rawArgs: readonly string[]): Promise<number>;
2
+ //# sourceMappingURL=create.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/cli/create.ts"],"names":[],"mappings":"AAwCA,wBAAsB,SAAS,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAoG3E"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * `mantle create <type> <directory>` — materialize one version-matched starter
3
+ * bundle into a new local directory.
4
+ *
5
+ * It does not install dependencies, initialize git, configure auth, provision
6
+ * providers, or deploy. A coding agent reads the generated project
7
+ * instructions and continues from there.
8
+ *
9
+ * A bundle is remote input that can carry package.json, install scripts,
10
+ * executable source, and agent instructions, so it is treated as code: only
11
+ * the official immutable tag for this exact package version is accepted, the
12
+ * response is bounded, the whole tree is validated and rendered before any
13
+ * write, and the write lands in an exclusively-owned temporary directory that
14
+ * is renamed into place. There is no --force.
15
+ */
16
+ import { mkdir, mkdtemp, rename, rm, stat, writeFile } from "node:fs/promises";
17
+ import { basename, dirname, join, resolve } from "node:path";
18
+ import { cwd, stderr, stdout } from "node:process";
19
+ import { parseArgs } from "node:util";
20
+ import packageJson from "../../package.json" with { type: "json" };
21
+ import { DEFAULT_PROVISION_LIMITS, ProvisionRenderError, renderProvisionBundle, } from "../provision.js";
22
+ const STARTERS_OWNER = "aotter";
23
+ const STARTERS_REPO = "mantle-starters";
24
+ /**
25
+ * Released starter types. Community and Membership are deliberately absent:
26
+ * they are not released yet, and the create catalog must not offer a type the
27
+ * launch flow cannot deliver.
28
+ */
29
+ const CREATE_ARCHETYPES = ["blank", "presence", "intake", "publication", "transaction", "reservation"];
30
+ const MAX_BUNDLE_BYTES = 32 * 1024 * 1024;
31
+ const FILE_MODE = 0o644;
32
+ const DIRECTORY_MODE = 0o755;
33
+ export async function runCreate(rawArgs) {
34
+ let archetype;
35
+ let directory;
36
+ let brand;
37
+ let description;
38
+ let locales;
39
+ try {
40
+ const { values, positionals } = parseArgs({
41
+ args: [...rawArgs],
42
+ allowPositionals: true,
43
+ options: {
44
+ brand: { type: "string" },
45
+ description: { type: "string" },
46
+ locales: { type: "string" },
47
+ help: { type: "boolean", short: "h" },
48
+ },
49
+ });
50
+ if (values.help) {
51
+ printHelp();
52
+ return 0;
53
+ }
54
+ if (positionals.length !== 2) {
55
+ stderr.write("Usage: mantle create <type> <directory>\n");
56
+ return 2;
57
+ }
58
+ [archetype, directory] = positionals;
59
+ if (!CREATE_ARCHETYPES.includes(archetype)) {
60
+ stderr.write(`Unknown type: ${archetype}\nAvailable: ${CREATE_ARCHETYPES.join(", ")}\n`);
61
+ return 2;
62
+ }
63
+ brand = values.brand;
64
+ description = values.description;
65
+ locales = (values.locales ?? "en").split(",").map((locale) => locale.trim()).filter(Boolean);
66
+ }
67
+ catch (error) {
68
+ stderr.write(`${message(error)}\n`);
69
+ return 2;
70
+ }
71
+ const target = resolve(cwd(), directory);
72
+ const projectName = slugify(basename(target));
73
+ if (projectName.length === 0) {
74
+ stderr.write("The target directory name must contain a letter or digit.\n");
75
+ return 2;
76
+ }
77
+ if (await exists(target)) {
78
+ stderr.write(`Refusing to write into an existing path: ${target}\n`);
79
+ return 1;
80
+ }
81
+ // Creating the parent chain would be state a failure could not undo. Ask for
82
+ // an existing parent instead, so an aborted run leaves the disk untouched.
83
+ if (!(await exists(dirname(target)))) {
84
+ stderr.write(`Parent directory does not exist: ${dirname(target)}\n`);
85
+ return 1;
86
+ }
87
+ const version = packageJson.version;
88
+ const starterRef = `v${version}`;
89
+ const resolvedBrand = brand ?? titleCase(projectName);
90
+ const launch = {
91
+ archetype,
92
+ projectName,
93
+ brand: resolvedBrand,
94
+ description: description ?? `A Mantle ${archetype} site.`,
95
+ locales,
96
+ canonicalLocale: locales[0] ?? "en",
97
+ // create stops before auth; self-managed is the free default the
98
+ // generated handoff walks the owner through.
99
+ authMode: "self-managed",
100
+ starterRef,
101
+ installTimestamp: new Date().toISOString(),
102
+ siteUrl: "http://localhost:8787",
103
+ githubOwner: "",
104
+ adminGithubLogin: "",
105
+ afterLaunchSkillUrl: "",
106
+ };
107
+ let rendered;
108
+ try {
109
+ const bundle = await fetchOfficialBundle(starterRef, archetype, version);
110
+ rendered = renderProvisionBundle({ bundle, launch, limits: DEFAULT_PROVISION_LIMITS });
111
+ }
112
+ catch (error) {
113
+ stderr.write(`mantle create: ${error instanceof ProvisionRenderError ? `${error.code}: ${error.message}` : message(error)}\n`);
114
+ return 1;
115
+ }
116
+ try {
117
+ await writeAtomically(target, rendered);
118
+ }
119
+ catch (error) {
120
+ stderr.write(`mantle create: ${message(error)}\n`);
121
+ return 1;
122
+ }
123
+ stdout.write(`Created ${archetype} project in ${target}\n`
124
+ + ` Mantle ${version} - starter ${starterRef}\n\n`
125
+ + "Next: read AGENTS.md in the new directory. It carries the install and\n"
126
+ + "first-run steps for this project; nothing has been installed yet.\n");
127
+ return 0;
128
+ }
129
+ function printHelp() {
130
+ stdout.write(`mantle create - materialize a version-matched starter into a new directory
131
+
132
+ Usage: mantle create <type> <directory> [options]
133
+
134
+ Types:
135
+ ${CREATE_ARCHETYPES.join(", ")}
136
+
137
+ Options:
138
+ --brand <name> Display brand (default: derived from the directory name)
139
+ --description <text> One sentence describing the site
140
+ --locales <list> Comma-separated locales; the first is canonical (default: en)
141
+ -h, --help This help
142
+
143
+ Dependencies are not installed, git is not initialized, and nothing is
144
+ deployed. The generated AGENTS.md carries the next steps.
145
+ `);
146
+ }
147
+ /**
148
+ * Only the official immutable tag for this exact package version. A bundle
149
+ * URL or ref is never taken from user input in this path.
150
+ */
151
+ async function fetchOfficialBundle(starterRef, archetype, version) {
152
+ const url = `https://raw.githubusercontent.com/${STARTERS_OWNER}/${STARTERS_REPO}/${encodeURIComponent(starterRef)}/provision-bundles/${encodeURIComponent(archetype)}.json`;
153
+ const response = await fetch(url, { headers: { accept: "application/json" }, redirect: "error" });
154
+ if (!response.ok) {
155
+ // A 404 on a version-pinned tag has one common cause worth naming: the
156
+ // package was published before its matching starter tag exists.
157
+ const hint = response.status === 404
158
+ ? ` The ${starterRef} starter tag does not exist yet — if this version was just published, wait for its starter release, or pin an earlier version.`
159
+ : "";
160
+ throw new Error(`could not fetch the ${archetype} starter for ${starterRef} (HTTP ${response.status}).${hint}`);
161
+ }
162
+ const declared = Number(response.headers.get("content-length"));
163
+ if (Number.isFinite(declared) && declared > MAX_BUNDLE_BYTES) {
164
+ throw new Error(`starter bundle exceeds ${MAX_BUNDLE_BYTES} bytes.`);
165
+ }
166
+ const text = await readBounded(response, MAX_BUNDLE_BYTES);
167
+ let bundle;
168
+ try {
169
+ bundle = JSON.parse(text);
170
+ }
171
+ catch {
172
+ throw new Error("starter bundle is not valid JSON.");
173
+ }
174
+ // The URL is version-pinned, but a mis-built bundle published under that tag
175
+ // would still render. Refuse a version the tag does not promise.
176
+ if (bundle.version !== version) {
177
+ throw new Error(`starter bundle at ${starterRef} declares version ${String(bundle.version)}, expected ${version}.`);
178
+ }
179
+ return bundle;
180
+ }
181
+ /** Stops reading at the limit; a chunked response never gets to allocate past it. */
182
+ async function readBounded(response, max) {
183
+ const body = response.body;
184
+ if (!body)
185
+ return response.text();
186
+ const reader = body.getReader();
187
+ const chunks = [];
188
+ let total = 0;
189
+ for (;;) {
190
+ const { done, value } = await reader.read();
191
+ if (done)
192
+ break;
193
+ if (value === undefined)
194
+ continue;
195
+ total += value.byteLength;
196
+ if (total > max) {
197
+ await reader.cancel();
198
+ throw new Error(`starter bundle exceeds ${max} bytes.`);
199
+ }
200
+ chunks.push(value);
201
+ }
202
+ return new TextDecoder("utf-8", { fatal: false }).decode(Buffer.concat(chunks));
203
+ }
204
+ /**
205
+ * Writes into an exclusively-created sibling directory, then renames it onto a
206
+ * target that did not previously exist. A failure leaves nothing behind.
207
+ */
208
+ async function writeAtomically(target, rendered) {
209
+ const staging = await mkdtemp(join(dirname(target), ".mantle-create-"));
210
+ try {
211
+ for (const [path, content] of rendered.files) {
212
+ await writeFileAt(staging, path, Buffer.from(content, "utf8"));
213
+ }
214
+ for (const [path, content] of rendered.binaryFiles) {
215
+ await writeFileAt(staging, path, Buffer.from(content, "base64"));
216
+ }
217
+ await rename(staging, target);
218
+ }
219
+ catch (error) {
220
+ await rm(staging, { recursive: true, force: true });
221
+ throw error;
222
+ }
223
+ }
224
+ async function writeFileAt(root, relativePath, data) {
225
+ const destination = join(root, relativePath);
226
+ await mkdir(dirname(destination), { recursive: true, mode: DIRECTORY_MODE });
227
+ // Regular file, fixed mode: a bundle cannot create a symlink or an
228
+ // executable.
229
+ await writeFile(destination, data, { mode: FILE_MODE, flag: "wx" });
230
+ }
231
+ function slugify(value) {
232
+ return value.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63).replace(/-+$/g, "");
233
+ }
234
+ function titleCase(slug) {
235
+ return slug.split("-").filter(Boolean).map((word) => word[0]?.toUpperCase() + word.slice(1)).join(" ");
236
+ }
237
+ async function exists(path) {
238
+ return stat(path).then(() => true, () => false);
239
+ }
240
+ function message(error) {
241
+ return error instanceof Error ? error.message : String(error);
242
+ }
243
+ //# sourceMappingURL=create.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/cli/create.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"AAeA,wBAAsB,WAAW,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyC7E"}
@@ -0,0 +1,101 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { cwd, stderr, stdout } from "node:process";
4
+ import { parseArgs } from "node:util";
5
+ import { ValidateManifestsUseCase } from "@aotter/mantle-spec";
6
+ import { loadManifestsFromRoot } from "@aotter/mantle-spec/cli";
7
+ import { assertMantleNamespace, emitMantleModule } from "../codegen/emitMantleModule.js";
8
+ export async function runGenerate(rawArgs) {
9
+ let options;
10
+ try {
11
+ const parsed = parseGenerateArgs(rawArgs);
12
+ if (parsed === null) {
13
+ printHelp();
14
+ return 0;
15
+ }
16
+ options = parsed;
17
+ }
18
+ catch (error) {
19
+ stderr.write(`${message(error)}\n`);
20
+ return 2;
21
+ }
22
+ const loaded = await loadManifestsFromRoot(options.manifests);
23
+ const validation = loaded.parsed
24
+ ? ValidateManifestsUseCase.run({ parsed: loaded.parsed })
25
+ : { diagnostics: [], errorCount: 0, warningCount: 0 };
26
+ const validationErrors = [...loaded.parseErrors, ...validation.diagnostics]
27
+ .filter((diagnostic) => diagnostic.severity === "error");
28
+ if (validationErrors.length > 0 || !validation.linked) {
29
+ printDiagnostics(validationErrors);
30
+ return 1;
31
+ }
32
+ const emitted = emitMantleModule({
33
+ linked: validation.linked,
34
+ namespace: options.namespace,
35
+ });
36
+ if (!emitted.ok) {
37
+ printDiagnostics(emitted.diagnostics);
38
+ return 1;
39
+ }
40
+ const output = resolve(cwd(), options.output);
41
+ const stale = !(await syncText(join(output, "mantle.ts"), emitted.source, options.check));
42
+ if (stale && options.check) {
43
+ stderr.write("Mantle generated files are stale; run `mantle generate`.\n");
44
+ return 1;
45
+ }
46
+ return 0;
47
+ }
48
+ function parseGenerateArgs(rawArgs) {
49
+ const { values } = parseArgs({
50
+ args: [...rawArgs],
51
+ options: {
52
+ manifests: { type: "string" },
53
+ output: { type: "string", short: "o" },
54
+ namespace: { type: "string" },
55
+ check: { type: "boolean" },
56
+ help: { type: "boolean", short: "h" },
57
+ },
58
+ });
59
+ if (values.help)
60
+ return null;
61
+ const namespace = values.namespace ?? "Mantle";
62
+ assertMantleNamespace(namespace, "--namespace");
63
+ return {
64
+ manifests: values.manifests ?? "./manifests",
65
+ output: values.output ?? ".mantle/generated",
66
+ namespace,
67
+ check: values.check === true,
68
+ };
69
+ }
70
+ function printHelp() {
71
+ stdout.write(`mantle generate — compile manifests into a typed runtime binding
72
+
73
+ Usage: mantle generate [options]
74
+
75
+ Options:
76
+ --manifests <dir> Manifest directory (default: ./manifests)
77
+ -o, --output <dir> Generated root (default: .mantle/generated)
78
+ --namespace <name> Generated type namespace (default: Mantle)
79
+ --check Fail without writing when generated code is stale
80
+ -h, --help This help
81
+ `);
82
+ }
83
+ function printDiagnostics(diagnostics) {
84
+ for (const diagnostic of diagnostics) {
85
+ stderr.write(`${diagnostic.code} ${diagnostic.path}: ${diagnostic.message}\n`);
86
+ }
87
+ }
88
+ async function syncText(path, expected, check) {
89
+ const current = await readFile(path, "utf8").catch(() => null);
90
+ if (current === expected)
91
+ return true;
92
+ if (check)
93
+ return false;
94
+ await mkdir(dirname(path), { recursive: true });
95
+ await writeFile(path, expected, "utf8");
96
+ return true;
97
+ }
98
+ function message(error) {
99
+ return error instanceof Error ? error.message : String(error);
100
+ }
101
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/cli/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../../src/cli/harness.ts"],"names":[],"mappings":""}
@@ -2,6 +2,7 @@
2
2
  import { argv, stderr, stdout } from "node:process";
3
3
  import { parseArgs } from "node:util";
4
4
  import { ValidateManifestsUseCase, } from "@aotter/mantle-spec";
5
+ import { compileRuntimePlan } from "@aotter/mantle-runtime";
5
6
  import { loadManifestsFromRoot } from "@aotter/mantle-spec/cli";
6
7
  import { benchmarkHttpRoutes, inspectIndexCoverage, } from "@aotter/mantle-runtime/testing";
7
8
  async function main() {
@@ -42,26 +43,30 @@ async function runIndexes(rawArgs) {
42
43
  }
43
44
  const format = outputFormat(values.format);
44
45
  const loaded = await loadManifestsFromRoot(values.manifests ?? "./manifests");
45
- const validation = ValidateManifestsUseCase.run({
46
- manifests: loaded.manifests,
47
- filePaths: loaded.filePaths,
48
- });
46
+ const validation = loaded.parsed
47
+ ? ValidateManifestsUseCase.run({ parsed: loaded.parsed })
48
+ : { diagnostics: [], errorCount: 0, warningCount: 0 };
49
49
  const errors = [...loaded.parseErrors, ...validation.diagnostics]
50
50
  .filter((diagnostic) => diagnostic.severity === "error");
51
51
  if (errors.length > 0) {
52
52
  emitErrors(errors, format);
53
53
  return 1;
54
54
  }
55
- if (loaded.manifests.length === 0) {
55
+ if (!validation.linked || validation.linked.schemas.length === 0) {
56
56
  stderr.write(`No manifests found under ${values.manifests ?? "./manifests"}\n`);
57
57
  return 2;
58
58
  }
59
+ const compiled = compileRuntimePlan(validation.linked);
60
+ if (!compiled.ok) {
61
+ emitErrors(compiled.diagnostics, format);
62
+ return 1;
63
+ }
59
64
  const rows = values.rows === undefined ? undefined : Number(values.rows);
60
65
  if (rows !== undefined && (!Number.isFinite(rows) || rows <= 0)) {
61
66
  stderr.write("--rows must be a positive number\n");
62
67
  return 2;
63
68
  }
64
- const report = await inspectIndexCoverage(loaded.manifests, {
69
+ const report = await inspectIndexCoverage(compiled.value, {
65
70
  rowsPerSchema: rows,
66
71
  requirePublic: values["require-public"] === true,
67
72
  requiredViews: values.require ?? [],
@@ -188,7 +193,7 @@ function printHelp() {
188
193
  }
189
194
  function printIndexesHelp() {
190
195
  stdout.write(`Usage: mantle-harness indexes [options]\n\n` +
191
- ` --manifests <dir> Directory containing site.yaml (default: ./manifests)\n` +
196
+ ` --manifests <dir> Directory containing YAML manifests (default: ./manifests)\n` +
192
197
  ` --rows <n> Deterministic rows per Schema (default: 2000)\n` +
193
198
  ` --require <view> Fail when this View lacks its access path; repeatable\n` +
194
199
  ` --require-public Apply the gate to public Views\n` +
@@ -206,4 +211,4 @@ main().then((code) => { process.exitCode = code; }, (error) => {
206
211
  stderr.write(`${message(error)}\n`);
207
212
  process.exitCode = 2;
208
213
  });
209
- //# sourceMappingURL=harness-cli.js.map
214
+ //# sourceMappingURL=harness.js.map