@impetik/xeer-mcp 0.2.21 → 0.2.22
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 +3 -2
- package/dist/dev-session.d.ts +10 -4
- package/dist/dev-session.js +5 -3
- package/dist/server.js +11 -5
- package/package.json +2 -2
- package/vendor/spec/actions.d.ts +26 -4
- package/vendor/spec/actions.js +40 -11
- package/vendor/spec/diagnostics.d.ts +1 -1
- package/vendor/spec/diagnostics.js +43 -4
- package/vendor/spec/framework-config.d.ts +18 -0
- package/vendor/spec/framework-config.js +22 -0
- package/vendor/spec/index.d.ts +2 -0
- package/vendor/spec/index.js +2 -0
- package/vendor/spec/public-assets.d.ts +49 -4
- package/vendor/spec/public-assets.js +98 -6
- package/vendor/spec/scaffold-names.d.ts +25 -0
- package/vendor/spec/scaffold-names.js +23 -0
- package/vendor/spec/schema.d.ts +2 -2
- package/vendor/spec/template-distribution.d.ts +30 -0
- package/vendor/spec/template-distribution.js +38 -0
- package/vendor/spec/types.d.ts +26 -0
- package/vendor/spec/types.js +26 -0
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ The generated registry is the complete MCP tool and exclusion surface:
|
|
|
63
63
|
| `xeer_check` | `check` | `author` / `operator` | Validate a project and report structured diagnostics. | `directory?`: `string` | `read-source`<br>`write-generated` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
|
|
64
64
|
| `xeer_build` | `build` | `author` / `operator` | Build and verify a content-addressed application artifact. | `directory?`: `string` | `read-source`<br>`write-generated`<br>`run-local` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
|
|
65
65
|
| `xeer_test` | `test` | `author` / `operator` | Run application tests against fresh isolated local state. | `directory?`: `string`<br>`timeoutMilliseconds?`: `integer` [1000..1800000] | `read-source`<br>`write-generated`<br>`run-local`<br>`write-state` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.dev.v0` | none |
|
|
66
|
-
| `xeer_new` | `new` | `author` / `operator` | Create a new project from a supported scaffold. | `directory`: `string`<br>`template?`: `
|
|
66
|
+
| `xeer_new` | `new` | `author` / `operator` | Create a new project from a supported scaffold. | `directory`: `string`<br>`template?`: `string`<br>`framework?`: `sveltekit`<br>`ui?`: `preact` / `react` | `write-source`<br>`network-read` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
|
|
67
67
|
| `xeer_agent_context` | `agent.context` | `author` / `operator` | Read normalized project facts, operations, diagnostics, tests, and safe next actions. | `directory?`: `string` | `read-source` | read-only; idempotent; reversible; non-destructive | `project-relative` | `xeer.agent-context.v0` | none |
|
|
68
68
|
| `xeer_docs_search` | `docs.search` | `author` / `operator` | Search the installed-version Xeer documentation index. | `query`: `string`<br>`limit?`: `integer` [1..20]; default `5` | none | read-only; idempotent; reversible; non-destructive | `none` | `xeer.docs-search.v0` | none |
|
|
69
69
|
| `xeer_doctor` | `doctor` | `author` / `operator` | Diagnose the toolchain and generated project state. | `directory?`: `string` | `read-source`<br>`write-generated` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | none |
|
|
@@ -76,10 +76,11 @@ The generated registry is the complete MCP tool and exclusion surface:
|
|
|
76
76
|
| `xeer_dev_stop` | `dev.stop` | `author` / `operator` | Stop a local development session and release its lease. | `sessionId?`: `string`<br>`cursor?`: `integer` [0..9007199254740991]; default `0` | `run-local` | writes; idempotent; reversible; non-destructive | `none` | `xeer.dev.v0` | none |
|
|
77
77
|
| `xeer_diagnostics` | `diagnostics` | `author` / `operator` | Explain one emitted diagnostic code. | `code`: `string` | none | read-only; idempotent; reversible; non-destructive | `none` | `xeer.command.v0` | none |
|
|
78
78
|
|
|
79
|
-
**CLI actions intentionally excluded from MCP (
|
|
79
|
+
**CLI actions intentionally excluded from MCP (36).**
|
|
80
80
|
|
|
81
81
|
| CLI action | Action | Summary | Effects | Safety | Path policy | Output | Why no MCP tool |
|
|
82
82
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
83
|
+
| `xeer init` | `init` | Initialize Xeer in an existing SvelteKit project. | `read-source`<br>`write-source`<br>`run-local`<br>`network-read` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
|
|
83
84
|
| `xeer agent setup` | `agent.setup` | Install or verify project-confined agent adapters. | `read-source`<br>`write-source` | writes; idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | Not exposed through MCP v0; use the CLI deliberately. |
|
|
84
85
|
| `xeer deploy` | `deploy` | Build and deploy an application artifact. | `read-source`<br>`write-generated`<br>`run-local`<br>`network-read`<br>`network-write`<br>`production-change` | writes; non-idempotent; reversible; non-destructive | `project-relative` | `xeer.command.v0` | MCP exposes a separate preview-only deploy action; direct production deploy stays CLI-only. |
|
|
85
86
|
| `xeer promote` | `promote` | Promote a preview artifact to production. | `network-read`<br>`network-write`<br>`production-change` | writes; non-idempotent; reversible; non-destructive | `project-or-url` | `xeer.command.v0` | MCP promotion is a separate action available only when the server starts in operator profile. |
|
package/dist/dev-session.d.ts
CHANGED
|
@@ -17,12 +17,18 @@ import type { Diagnostic, DevEvent } from '../vendor/spec/index.js';
|
|
|
17
17
|
* inspector, and the rebuild/rollback behaviour that only `dev` has.
|
|
18
18
|
*/
|
|
19
19
|
export type DevSessionStatus = 'starting' | 'ready' | 'compile_failed' | 'stopped' | 'crashed';
|
|
20
|
+
/**
|
|
21
|
+
* Only `url` is universal. The inspector, health, debug, and logs routes are served by the Xeer
|
|
22
|
+
* runtime; a framework project's dev server is Vite and has none of them, so its `preview.ready`
|
|
23
|
+
* carries the URL alone. Modelling them as optional keeps that honest instead of inventing routes
|
|
24
|
+
* that would 404.
|
|
25
|
+
*/
|
|
20
26
|
export interface PreviewUrls {
|
|
21
27
|
url: string;
|
|
22
|
-
healthUrl
|
|
23
|
-
inspectorUrl
|
|
24
|
-
debugUrl
|
|
25
|
-
logsUrl
|
|
28
|
+
healthUrl?: string;
|
|
29
|
+
inspectorUrl?: string;
|
|
30
|
+
debugUrl?: string;
|
|
31
|
+
logsUrl?: string;
|
|
26
32
|
}
|
|
27
33
|
export interface DevSessionSummary {
|
|
28
34
|
sessionId: string;
|
package/dist/dev-session.js
CHANGED
|
@@ -4,8 +4,9 @@ function hasPreviewUrls(value) {
|
|
|
4
4
|
if (typeof value !== 'object' || value === null)
|
|
5
5
|
return false;
|
|
6
6
|
const candidate = value;
|
|
7
|
-
return ['url'
|
|
8
|
-
|
|
7
|
+
return typeof candidate['url'] === 'string'
|
|
8
|
+
&& ['healthUrl', 'inspectorUrl', 'debugUrl', 'logsUrl']
|
|
9
|
+
.every((key) => candidate[key] === undefined || typeof candidate[key] === 'string');
|
|
9
10
|
}
|
|
10
11
|
const MAX_BUFFERED_EVENTS = 2_000;
|
|
11
12
|
const MAX_STDERR_CHARACTERS = 4_096;
|
|
@@ -143,7 +144,8 @@ export class DevSession {
|
|
|
143
144
|
this.#compiling = false;
|
|
144
145
|
break;
|
|
145
146
|
case 'preview.ready':
|
|
146
|
-
|
|
147
|
+
if (hasPreviewUrls(event.data))
|
|
148
|
+
this.#preview = event.data;
|
|
147
149
|
if (!this.#expectsTunnel)
|
|
148
150
|
this.#status = 'ready';
|
|
149
151
|
this.#compiling = false;
|
package/dist/server.js
CHANGED
|
@@ -369,8 +369,12 @@ export function createXeerMcpServer(options = {}) {
|
|
|
369
369
|
server.registerTool('xeer_new', {
|
|
370
370
|
...toolPolicy('new'),
|
|
371
371
|
outputSchema: commandOutput,
|
|
372
|
-
}, async ({ directory, template, ui }) => withDirectory(directory, 'new', (resolved) =>
|
|
372
|
+
}, async ({ directory, template, framework, ui }) => withDirectory(directory, 'new', (resolved) =>
|
|
373
|
+
// Every combination rule stays in the CLI: this forwards what was asked and lets the same
|
|
374
|
+
// XE30xx refusals come back, rather than growing a second copy of them at the tool boundary.
|
|
375
|
+
commandTool('new', ['new', resolved,
|
|
373
376
|
...(template === undefined ? [] : ['--template', template]),
|
|
377
|
+
...(framework === undefined ? [] : ['--framework', framework]),
|
|
374
378
|
...(ui === undefined ? [] : ['--ui', ui]),
|
|
375
379
|
], projectRoot())));
|
|
376
380
|
server.registerTool('xeer_doctor', {
|
|
@@ -429,12 +433,14 @@ export function createXeerMcpServer(options = {}) {
|
|
|
429
433
|
cursor: z.number(),
|
|
430
434
|
generation: z.number().optional(),
|
|
431
435
|
artifactId: z.string().optional(),
|
|
436
|
+
// Only the URL is universal: a framework dev server is Vite and serves none of the Xeer
|
|
437
|
+
// runtime's inspection routes, so it reports the preview URL alone.
|
|
432
438
|
preview: z.object({
|
|
433
439
|
url: z.string(),
|
|
434
|
-
healthUrl: z.string(),
|
|
435
|
-
inspectorUrl: z.string(),
|
|
436
|
-
debugUrl: z.string(),
|
|
437
|
-
logsUrl: z.string(),
|
|
440
|
+
healthUrl: z.string().optional(),
|
|
441
|
+
inspectorUrl: z.string().optional(),
|
|
442
|
+
debugUrl: z.string().optional(),
|
|
443
|
+
logsUrl: z.string().optional(),
|
|
438
444
|
}).loose().optional(),
|
|
439
445
|
tunnel: z.object({
|
|
440
446
|
url: z.string(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@impetik/xeer-mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Model Context Protocol server for Xeer: the scaffold, check, dev, build, and deploy loop as agent tools.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
49
49
|
"zod": "^4.0.10",
|
|
50
|
-
"@impetik/xeer": "0.2.
|
|
50
|
+
"@impetik/xeer": "0.2.22"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@types/node": "^24.1.0"
|
package/vendor/spec/actions.d.ts
CHANGED
|
@@ -194,10 +194,10 @@ declare const actions: readonly [{
|
|
|
194
194
|
readonly command: readonly ["new"];
|
|
195
195
|
readonly summary: "Create a new project from a supported scaffold.";
|
|
196
196
|
readonly description: string;
|
|
197
|
-
readonly usage: readonly ["new
|
|
197
|
+
readonly usage: readonly ["new [directory] [--template <id>] [--framework sveltekit] [--ui preact|react] [--json]"];
|
|
198
198
|
readonly helpOrder: 30;
|
|
199
199
|
readonly outputProtocol: "xeer.command.v0";
|
|
200
|
-
readonly effects: readonly ["write-source"];
|
|
200
|
+
readonly effects: readonly ["write-source", "network-read"];
|
|
201
201
|
readonly idempotent: false;
|
|
202
202
|
readonly reversible: true;
|
|
203
203
|
readonly destructive: false;
|
|
@@ -212,9 +212,13 @@ declare const actions: readonly [{
|
|
|
212
212
|
readonly description: "Target directory, relative to the server root. Must be empty or absent.";
|
|
213
213
|
};
|
|
214
214
|
readonly template: {
|
|
215
|
-
readonly description:
|
|
215
|
+
readonly description: string;
|
|
216
|
+
readonly type: "string";
|
|
217
|
+
};
|
|
218
|
+
readonly framework: {
|
|
219
|
+
readonly description: string;
|
|
216
220
|
readonly type: "string";
|
|
217
|
-
readonly enum: readonly ["
|
|
221
|
+
readonly enum: readonly ["sveltekit"];
|
|
218
222
|
};
|
|
219
223
|
readonly ui: {
|
|
220
224
|
readonly description: string;
|
|
@@ -230,6 +234,24 @@ declare const actions: readonly [{
|
|
|
230
234
|
readonly mcpTool: "xeer_new";
|
|
231
235
|
readonly mcpProfiles: readonly ["author", "operator"];
|
|
232
236
|
};
|
|
237
|
+
}, {
|
|
238
|
+
readonly id: "init";
|
|
239
|
+
readonly command: readonly ["init"];
|
|
240
|
+
readonly summary: "Initialize Xeer in an existing SvelteKit project.";
|
|
241
|
+
readonly description: string;
|
|
242
|
+
readonly usage: readonly ["init [directory] [--dry-run] [--json]"];
|
|
243
|
+
readonly helpOrder: 32;
|
|
244
|
+
readonly outputProtocol: "xeer.command.v0";
|
|
245
|
+
readonly effects: readonly ["read-source", "write-source", "run-local", "network-read"];
|
|
246
|
+
readonly idempotent: true;
|
|
247
|
+
readonly reversible: true;
|
|
248
|
+
readonly destructive: false;
|
|
249
|
+
readonly humanPrerequisites: readonly [];
|
|
250
|
+
readonly pathPolicy: "project-relative";
|
|
251
|
+
readonly surfaces: {
|
|
252
|
+
readonly cli: true;
|
|
253
|
+
readonly mcpExclusion: "Not exposed through MCP v0; use the CLI deliberately.";
|
|
254
|
+
};
|
|
233
255
|
}, {
|
|
234
256
|
readonly id: "agent.setup";
|
|
235
257
|
readonly command: readonly ["agent", "setup"];
|
package/vendor/spec/actions.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Stable protocol for the action catalogue consumed by CLI, MCP, and agent context surfaces. */
|
|
2
2
|
import { REVIEW_RECEIPT_ID_SOURCE } from './review.js';
|
|
3
|
+
import { FRAMEWORK_NAMES } from './scaffold-names.js';
|
|
3
4
|
export const ACTION_REGISTRY_PROTOCOL = 'xeer.actions.v0';
|
|
4
5
|
export const XEER_MCP_PROFILES = ['author', 'operator'];
|
|
5
6
|
export const XEER_ARTIFACT_ID_PATTERN = '^sha256:[a-f0-9]{64}$';
|
|
@@ -93,8 +94,13 @@ const MCP_PRESENTATION = {
|
|
|
93
94
|
+ 'is public to read and private to write, and "personal-site" has no database at all. None of '
|
|
94
95
|
+ 'them scaffolds sign-in UI. "ui" selects the UI provider independently of the template — '
|
|
95
96
|
+ '"preact" (the default) or "react" — and changes the manifest rather than any source file. '
|
|
96
|
-
+ '
|
|
97
|
-
+ '
|
|
97
|
+
+ '"framework" is the other axis and is exclusive with "template": "sveltekit" writes a '
|
|
98
|
+
+ 'SvelteKit project with xeer.config.json instead of xeer.app.json, so it has no UI provider '
|
|
99
|
+
+ 'and no declared database, and its commands delegate to the package scripts after an '
|
|
100
|
+
+ 'install. result is { directory, name, files } plus "template" and "ui" for an application '
|
|
101
|
+
+ 'scaffold or "framework" for a framework one; an unknown template is XE3002, an unknown ui '
|
|
102
|
+
+ 'is XE3003, "ui" with "framework" is XE3004, "template" with "framework" is XE3005, an '
|
|
103
|
+
+ 'unknown framework is XE3006, and none of them writes anything.',
|
|
98
104
|
},
|
|
99
105
|
doctor: {
|
|
100
106
|
mcpTitle: 'Diagnose the Xeer toolchain',
|
|
@@ -204,12 +210,16 @@ const actions = [
|
|
|
204
210
|
id: 'new', command: ['new'], summary: 'Create a new project from a supported scaffold.',
|
|
205
211
|
description: 'Creates a complete project that checks, tests, and builds clean. Templates do not '
|
|
206
212
|
+ 'scaffold sign-in UI: every visitor already has a verified identity, and the generated README '
|
|
207
|
-
+ 'explains how to opt in to a persistent account.
|
|
208
|
-
+ '
|
|
209
|
-
+ '
|
|
210
|
-
+ '
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
+ 'explains how to opt in to a persistent account. The four bundled starter names resolve '
|
|
214
|
+
+ 'offline and accept `--ui`; every other exact template id resolves through the public catalog '
|
|
215
|
+
+ 'and owns its renderer. `--framework sveltekit` writes a framework '
|
|
216
|
+
+ 'project instead: SvelteKit owns the renderer and the routes, Xeer owns `xeer.config.json` '
|
|
217
|
+
+ 'and the deployment, so `result.framework` replaces both fields, `--ui` does not apply and '
|
|
218
|
+
+ 'is refused with `XE3004`, and `--template` alongside it is `XE3005`. To adopt a framework '
|
|
219
|
+
+ 'project that already exists, use `xeer init` rather than this command.',
|
|
220
|
+
usage: ['new [directory] [--template <id>] [--framework sveltekit] [--ui preact|react] [--json]'],
|
|
221
|
+
helpOrder: 30,
|
|
222
|
+
outputProtocol: 'xeer.command.v0', effects: ['write-source', 'network-read'], idempotent: false,
|
|
213
223
|
reversible: true, destructive: false, humanPrerequisites: [], pathPolicy: 'project-relative',
|
|
214
224
|
surfaces: {
|
|
215
225
|
cli: true, mcpTool: 'xeer_new', mcpProfiles: XEER_MCP_PROFILES,
|
|
@@ -221,12 +231,20 @@ const actions = [
|
|
|
221
231
|
type: 'string', description: 'Target directory, relative to the server root. Must be empty or absent.',
|
|
222
232
|
},
|
|
223
233
|
template: {
|
|
224
|
-
description: '
|
|
225
|
-
|
|
234
|
+
description: 'A bundled starter name or exact template catalog id. Defaults to notes. '
|
|
235
|
+
+ 'Bundled starters resolve offline. Exclusive with framework, which is refused with XE3005.',
|
|
236
|
+
type: 'string',
|
|
237
|
+
},
|
|
238
|
+
framework: {
|
|
239
|
+
description: 'Scaffold a fresh project for this framework instead of a Xeer '
|
|
240
|
+
+ 'application: it declares xeer.config.json, owns its own renderer and routes, and '
|
|
241
|
+
+ 'delegates every command to its package scripts. Exclusive with template and ui.',
|
|
242
|
+
type: 'string', enum: FRAMEWORK_NAMES,
|
|
226
243
|
},
|
|
227
244
|
ui: {
|
|
228
245
|
description: 'Which UI provider the written manifest selects. Defaults to preact, and '
|
|
229
|
-
+ 'omitting it writes no client block at all. Orthogonal to template
|
|
246
|
+
+ 'omitting it writes no client block at all. Orthogonal to template, and refused '
|
|
247
|
+
+ 'with XE3004 alongside a framework that owns its own renderer.',
|
|
230
248
|
type: 'string', enum: ['preact', 'react'],
|
|
231
249
|
},
|
|
232
250
|
},
|
|
@@ -234,6 +252,17 @@ const actions = [
|
|
|
234
252
|
},
|
|
235
253
|
},
|
|
236
254
|
},
|
|
255
|
+
{
|
|
256
|
+
id: 'init', command: ['init'], summary: 'Initialize Xeer in an existing SvelteKit project.',
|
|
257
|
+
description: 'Detects an existing SvelteKit project, runs Wrangler setup when Cloudflare is not '
|
|
258
|
+
+ 'configured, preserves the project\'s native dev/check/test/build scripts and application '
|
|
259
|
+
+ 'source, and creates the Xeer framework configuration. Use --dry-run to inspect the '
|
|
260
|
+
+ 'machine-readable Xeer plan without writing.',
|
|
261
|
+
usage: ['init [directory] [--dry-run] [--json]'], helpOrder: 32,
|
|
262
|
+
outputProtocol: 'xeer.command.v0', effects: ['read-source', 'write-source', 'run-local', 'network-read'],
|
|
263
|
+
idempotent: true, reversible: true, destructive: false, humanPrerequisites: [],
|
|
264
|
+
pathPolicy: 'project-relative', surfaces: { cli: true, mcpExclusion: NOT_EXPOSED_V0 },
|
|
265
|
+
},
|
|
237
266
|
{
|
|
238
267
|
id: 'agent.setup', command: ['agent', 'setup'],
|
|
239
268
|
summary: 'Install or verify project-confined agent adapters.',
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* undocumented and a retired code cannot linger here.
|
|
14
14
|
*/
|
|
15
15
|
/** Where an agent observes a code, in the vocabulary of the CLI commands. */
|
|
16
|
-
export type DiagnosticSurface = 'any' | 'check' | 'build' | 'dev' | 'preview' | 'test' | 'new' | 'agent' | 'doctor' | 'inspect' | 'state' | 'auth' | 'deploy' | 'deployments' | 'promote' | 'rollback' | 'disable' | 'enable' | 'delete' | 'link' | 'env' | 'token' | 'domains' | 'export' | 'import' | 'db';
|
|
16
|
+
export type DiagnosticSurface = 'any' | 'check' | 'build' | 'dev' | 'preview' | 'test' | 'new' | 'init' | 'agent' | 'doctor' | 'inspect' | 'state' | 'auth' | 'deploy' | 'deployments' | 'promote' | 'rollback' | 'disable' | 'enable' | 'delete' | 'link' | 'env' | 'token' | 'domains' | 'export' | 'import' | 'db';
|
|
17
17
|
export interface DiagnosticFamily {
|
|
18
18
|
/** Numeric prefix the family owns, as it appears in a code. */
|
|
19
19
|
readonly prefix: string;
|
|
@@ -134,6 +134,11 @@ export const DIAGNOSTIC_FAMILIES = [
|
|
|
134
134
|
title: 'Agent setup',
|
|
135
135
|
summary: 'xeer agent setup found missing, stale, invalid, or user-owned adapter files.',
|
|
136
136
|
},
|
|
137
|
+
{
|
|
138
|
+
prefix: 'XE32',
|
|
139
|
+
title: 'Existing-project initialization',
|
|
140
|
+
summary: 'xeer init could not identify or safely initialize an existing framework project.',
|
|
141
|
+
},
|
|
137
142
|
{
|
|
138
143
|
prefix: 'XE40',
|
|
139
144
|
title: 'Environment',
|
|
@@ -397,15 +402,49 @@ export const DIAGNOSTIC_DEFINITIONS = [
|
|
|
397
402
|
+ 'fix: wait out the advertised retry and read again. '
|
|
398
403
|
+ "Quote `detail.errorId` when correlating with the Worker's console output.", ['inspect', 'state', 'export']),
|
|
399
404
|
define('XE3001', 'xeer new could not scaffold the project — most often a non-empty target directory.', 'Choose an empty or non-existent directory.', ['new']),
|
|
400
|
-
define('XE3002', '`--template` named a
|
|
401
|
-
+ '
|
|
402
|
-
+ 'describes each template.', ['new']),
|
|
405
|
+
define('XE3002', '`--template` named neither a bundled starter nor an exact id in the reachable catalog.', 'Check the exact catalog id, use a bundled starter, or omit --template for the default. A framework '
|
|
406
|
+
+ 'scaffold is selected with `--framework`.', ['new']),
|
|
403
407
|
define('XE3003', '`--ui` named a UI provider that does not exist. Refused before the target '
|
|
404
408
|
+ 'directory is read, so nothing was written.', 'Use preact or react, or omit --ui for the default (preact). The choice is orthogonal to '
|
|
405
|
-
+ '--template
|
|
409
|
+
+ '--template for every Xeer application template: each scaffolds on either provider from the '
|
|
410
|
+
+ 'same client sources.', ['new']),
|
|
411
|
+
define('XE3004', '`--ui` was passed with `--framework`, and a framework renders through its own '
|
|
412
|
+
+ 'toolchain rather than through a Xeer UI provider. Refused before the target directory is '
|
|
413
|
+
+ 'read, so nothing was written.', 'Drop --ui. `--framework sveltekit` owns its renderer: it writes no client.runtime.provider to '
|
|
414
|
+
+ 'select, and no value of --ui would change a file it writes.', ['new']),
|
|
415
|
+
define('XE3005', '`--template` and `--framework` were passed together. They are two exclusive '
|
|
416
|
+
+ 'axes: one writes a Xeer application, the other a project Xeer runs rather than compiles. '
|
|
417
|
+
+ 'Refused before the target directory is read, so nothing was written.', 'Pass exactly one. Keep --template for a bundled Xeer application starter, or keep --framework '
|
|
418
|
+
+ 'to scaffold a fresh framework project.', ['new']),
|
|
419
|
+
define('XE3006', '`--framework` named a framework Xeer cannot scaffold. Refused before the target '
|
|
420
|
+
+ 'directory is read, so nothing was written.', 'Use one of the names the message lists, or omit --framework to scaffold a Xeer application. To '
|
|
421
|
+
+ 'adopt a framework project that already exists, run `xeer init` in it instead.', ['new']),
|
|
422
|
+
define('XE3007', 'xeer new could not reach or read the template catalog.', 'Check the network connection and repeat the command.', ['new']),
|
|
423
|
+
define('XE3008', 'The reachable template catalog is invalid for installation.', 'Wait for the catalog publisher to be repaired, then repeat the command.', ['new']),
|
|
424
|
+
define('XE3009', 'xeer new could not download the selected template archive.', 'Check the network connection and repeat the command.', ['new']),
|
|
425
|
+
define('XE3010', 'The downloaded template archive does not match the catalog SHA-256.', 'Do not use the bytes. Repeat later or report the template artifact.', ['new']),
|
|
426
|
+
define('XE3011', 'The downloaded template archive contains unsafe or unsupported content.', 'Report the template artifact and choose another template.', ['new']),
|
|
427
|
+
define('XE3012', 'The extracted project root does not match the project metadata declared by the catalog.', 'Report the template artifact and choose another template.', ['new']),
|
|
428
|
+
define('XE3013', '`--ui` was passed with a catalog template, which owns its project kind and renderer.', 'Drop --ui and install the catalog template unchanged.', ['new']),
|
|
406
429
|
define('XE3101', 'Agent setup check found one or more generated files missing or stale. No files were written.', 'Run `xeer agent setup`, then repeat `xeer agent setup --check --json`.', ['agent']),
|
|
407
430
|
define('XE3102', 'Agent setup found a path owned by the user or another tool. The entire write was refused.', 'Move, rename, or deliberately remove the conflicting path; setup never overwrites an unowned file.', ['agent']),
|
|
408
431
|
define('XE3103', '`--target` named an agent adapter that Xeer does not support.', 'Use auto, agents, claude, codex, cursor, vscode, or mcp.', ['agent']),
|
|
432
|
+
define('XE3201', 'xeer init did not find an existing SvelteKit package at the selected directory.', 'Run xeer init from the SvelteKit package directory, or pass that directory explicitly.', ['init']),
|
|
433
|
+
define('XE3202', 'xeer init found an existing xeer.config.json that it does not own.', 'Keep and configure the existing file deliberately; xeer init never overwrites an unowned file.', ['init']),
|
|
434
|
+
define('XE3203', 'xeer init could not read the existing package.json.', 'Repair package.json, then run xeer init again. No files were written.', ['init']),
|
|
435
|
+
define('XE3204', 'A framework package script required by Xeer is missing, unavailable, or failed. '
|
|
436
|
+
+ 'A failing check reports one diagnostic per file it could locate, with `file` and `span`.', 'Run the command named in the diagnostic, repair its output, and repeat the Xeer command.', ['check', 'dev', 'build', 'test', 'deploy']),
|
|
437
|
+
define('XE3205', 'xeer.config.json or its adjacent package.json is invalid.', 'Repair the file named by the diagnostic, then repeat the Xeer command.', ['check', 'dev', 'build', 'test', 'deploy', 'doctor']),
|
|
438
|
+
define('XE3206', 'Wrangler could not configure the existing framework project for Cloudflare Workers.', 'Repair the Wrangler setup failure, or run wrangler setup manually, then repeat xeer init.', ['init']),
|
|
439
|
+
define('XE3207', 'The framework build failed, left its previous output in place, or produced '
|
|
440
|
+
+ 'something the platform will not deploy: no Cloudflare Worker at the path the Wrangler '
|
|
441
|
+
+ 'configuration names, or a module, asset, or total payload over the v0 size limits.', 'Read the message: it names the paths that were checked, or the limit that was exceeded. Repair '
|
|
442
|
+
+ 'the project build, then repeat the Xeer build or deploy.', ['build', 'deploy', 'test']),
|
|
443
|
+
define('XE3208', 'The command reads a Xeer application contract that a framework project does not '
|
|
444
|
+
+ 'have. A project with xeer.config.json owns its own routing, rendering, and datastore, so there '
|
|
445
|
+
+ 'is no Xeer manifest, artifact preview, state, or log ring to read.', 'Use the framework equivalent the hint names — `xeer dev` for a running server, `xeer check` for '
|
|
446
|
+
+ 'the project\'s own diagnostics — or run the command against a Xeer application (xeer.app.json). '
|
|
447
|
+
+ 'Never scaffold a new project over an initialized one.', ['preview', 'inspect', 'state', 'agent']),
|
|
409
448
|
define('XE4001', 'A doctor check failed. `hint` carries the check details as JSON, and result.checks '
|
|
410
449
|
+ 'lists every check with its id and status.', 'Fix the environment problem the failing check names. Doctor never edits the project.', ['doctor']),
|
|
411
450
|
define('XE5000', 'An auth subcommand was invoked with wrong arguments.', 'Use `xeer auth <login|status|logout|as|clear>`; `auth as` takes alice or bob.', ['auth']),
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const FRAMEWORK_CONFIG_SCHEMA_URL: "https://docs.xeer.run/config-v0.schema.json";
|
|
3
|
+
export declare const FRAMEWORK_CONFIG_FORMAT: "xeer.config.v0";
|
|
4
|
+
declare const frameworkConfigSchema: z.ZodObject<{
|
|
5
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
6
|
+
format: z.ZodLiteral<"xeer.config.v0">;
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
framework: z.ZodEnum<{
|
|
9
|
+
sveltekit: "sveltekit";
|
|
10
|
+
}>;
|
|
11
|
+
capabilities: z.ZodObject<{
|
|
12
|
+
auth: z.ZodOptional<z.ZodObject<{}, z.core.$strict>>;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type FrameworkConfigV0 = z.infer<typeof frameworkConfigSchema>;
|
|
16
|
+
export declare const frameworkConfigJsonSchema: Readonly<Record<string, unknown>>;
|
|
17
|
+
export declare function parseFrameworkConfig(value: unknown): FrameworkConfigV0;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { FRAMEWORK_NAMES } from './scaffold-names.js';
|
|
3
|
+
export const FRAMEWORK_CONFIG_SCHEMA_URL = 'https://docs.xeer.run/config-v0.schema.json';
|
|
4
|
+
export const FRAMEWORK_CONFIG_FORMAT = 'xeer.config.v0';
|
|
5
|
+
const frameworkConfigSchema = z.object({
|
|
6
|
+
$schema: z.string().url().optional(),
|
|
7
|
+
format: z.literal(FRAMEWORK_CONFIG_FORMAT),
|
|
8
|
+
name: z.string().regex(/^[a-z][a-z0-9-]{0,61}[a-z0-9]$/u),
|
|
9
|
+
framework: z.enum(FRAMEWORK_NAMES),
|
|
10
|
+
capabilities: z.object({
|
|
11
|
+
auth: z.object({}).strict().optional(),
|
|
12
|
+
}).strict(),
|
|
13
|
+
}).strict();
|
|
14
|
+
export const frameworkConfigJsonSchema = Object.freeze({
|
|
15
|
+
...z.toJSONSchema(frameworkConfigSchema),
|
|
16
|
+
$id: FRAMEWORK_CONFIG_SCHEMA_URL,
|
|
17
|
+
title: 'Xeer framework configuration v0',
|
|
18
|
+
description: 'Xeer capabilities and deployment metadata for an existing framework application.',
|
|
19
|
+
});
|
|
20
|
+
export function parseFrameworkConfig(value) {
|
|
21
|
+
return frameworkConfigSchema.parse(value);
|
|
22
|
+
}
|
package/vendor/spec/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './public-assets.js';
|
|
|
8
8
|
export * from './events.js';
|
|
9
9
|
export * from './value.js';
|
|
10
10
|
export * from './route.js';
|
|
11
|
+
export * from './scaffold-names.js';
|
|
11
12
|
export * from './identity-keys.js';
|
|
12
13
|
export * from './local-identity.js';
|
|
13
14
|
export * from './schema-lifecycle.js';
|
|
@@ -21,6 +22,7 @@ export * from './network-policy.js';
|
|
|
21
22
|
export * from './review.js';
|
|
22
23
|
export * from './template.js';
|
|
23
24
|
export * from './template-distribution.js';
|
|
25
|
+
export * from './framework-config.js';
|
|
24
26
|
export * from './tunnel.js';
|
|
25
27
|
export * from './type-check-profile.js';
|
|
26
28
|
export * from './editor-settings.js';
|
package/vendor/spec/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './public-assets.js';
|
|
|
8
8
|
export * from './events.js';
|
|
9
9
|
export * from './value.js';
|
|
10
10
|
export * from './route.js';
|
|
11
|
+
export * from './scaffold-names.js';
|
|
11
12
|
export * from './identity-keys.js';
|
|
12
13
|
export * from './local-identity.js';
|
|
13
14
|
export * from './schema-lifecycle.js';
|
|
@@ -21,6 +22,7 @@ export * from './network-policy.js';
|
|
|
21
22
|
export * from './review.js';
|
|
22
23
|
export * from './template.js';
|
|
23
24
|
export * from './template-distribution.js';
|
|
25
|
+
export * from './framework-config.js';
|
|
24
26
|
export * from './tunnel.js';
|
|
25
27
|
export * from './type-check-profile.js';
|
|
26
28
|
export * from './editor-settings.js';
|
|
@@ -49,12 +49,24 @@ export declare const IMMUTABLE_ASSET_HASH_LENGTH = 64;
|
|
|
49
49
|
* bound moves only in lockstep with the retention fold in the control plane's deployment service.
|
|
50
50
|
*/
|
|
51
51
|
export declare const MAX_RETAINED_ASSET_GENERATIONS = 2;
|
|
52
|
+
/**
|
|
53
|
+
* Upper bound on {@link PublicAssetsV0.declaredImmutablePrefixes}. Four, because the declaration is
|
|
54
|
+
* a trust statement and a short list is one a reviewer can read; no framework adapter mints more
|
|
55
|
+
* than one such directory.
|
|
56
|
+
*/
|
|
57
|
+
export declare const MAX_DECLARED_IMMUTABLE_PREFIXES = 4;
|
|
52
58
|
/**
|
|
53
59
|
* What a declared entry is an alias *of*. Kept as a string rather than a structured pair because its
|
|
54
60
|
* only consumers are humans reading an artifact and a diagnostic naming the file that produced a
|
|
55
61
|
* collision — nothing branches on it.
|
|
56
62
|
*/
|
|
57
63
|
export type PublicAssetOrigin = `module:${string}` | `asset:${string}` | 'document';
|
|
64
|
+
/**
|
|
65
|
+
* How long an origin may be, prefix included. Published because a producer has to refuse a path it
|
|
66
|
+
* cannot describe rather than truncate one: two long paths shortened to the same origin would name
|
|
67
|
+
* one file as the source of both, which is the opposite of what the field is for.
|
|
68
|
+
*/
|
|
69
|
+
export declare const MAX_PUBLIC_ASSET_ORIGIN_LENGTH = 256;
|
|
58
70
|
export interface ImmutableAssetV0 {
|
|
59
71
|
/**
|
|
60
72
|
* `${immutablePrefix}${hash.slice(7)}/${basename}`, and {@link parsePublicAssets} recomputes it
|
|
@@ -110,6 +122,22 @@ export interface PublicAssetsV0 {
|
|
|
110
122
|
* belongs to the control plane and this field should become a floor rather than the whole rule.
|
|
111
123
|
*/
|
|
112
124
|
retainedGenerations: number;
|
|
125
|
+
/**
|
|
126
|
+
* Additional prefixes under which the *producing build* content-addresses its own output, and
|
|
127
|
+
* which therefore get the immutable cache directive without Xeer having minted the address.
|
|
128
|
+
*
|
|
129
|
+
* Optional, and absent on every artifact the Xeer compiler emits: it hashes its own assets into
|
|
130
|
+
* {@link IMMUTABLE_ASSET_PREFIX}, where the path *is* the digest and {@link parsePublicAssets}
|
|
131
|
+
* proves it. A framework bundle cannot do that — SvelteKit's adapter emits `/_app/immutable/`
|
|
132
|
+
* with Vite's own digest in the filename, in an algorithm and width Xeer does not know — so the
|
|
133
|
+
* choice is between serving content-addressed files with `max-age=0, must-revalidate` and
|
|
134
|
+
* accepting a declaration this format cannot verify.
|
|
135
|
+
*
|
|
136
|
+
* This field is that declaration, named so the weakening is visible: entries stay in
|
|
137
|
+
* {@link PublicAssetsV0.revalidate}, because their bytes are not proved by their path and nothing
|
|
138
|
+
* downstream may treat them as if they were. All it buys is the cache header.
|
|
139
|
+
*/
|
|
140
|
+
declaredImmutablePrefixes?: string[];
|
|
113
141
|
/** Sorted by path. Cacheable forever, because the path is the digest. */
|
|
114
142
|
immutable: ImmutableAssetV0[];
|
|
115
143
|
/**
|
|
@@ -123,6 +151,21 @@ export interface PublicAssetsV0 {
|
|
|
123
151
|
*/
|
|
124
152
|
revalidate: RevalidatedAssetV0[];
|
|
125
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* What {@link ImmutableAssetV0.contentType} and {@link RevalidatedAssetV0.contentType} are filled
|
|
156
|
+
* with, for every producer of a declaration.
|
|
157
|
+
*
|
|
158
|
+
* It lives beside the field it populates because the two producers that had a map each — the Xeer
|
|
159
|
+
* compiler and the SvelteKit framework build — drifted, and the drift was silent: the framework map
|
|
160
|
+
* omitted `wasm`, so a WebAssembly module shipped as `application/octet-stream`, which
|
|
161
|
+
* `WebAssembly.instantiateStreaming` refuses. One map, or the same file works through one build path
|
|
162
|
+
* and breaks through the other.
|
|
163
|
+
*
|
|
164
|
+
* Deliberately not a lookup of a real media-type database: this only has to answer for what a build
|
|
165
|
+
* emits, every value is one a browser treats as the thing it is, and an unknown extension gets the
|
|
166
|
+
* one answer that is never wrong about encoding.
|
|
167
|
+
*/
|
|
168
|
+
export declare function assetContentType(path: string): string;
|
|
126
169
|
/** The URL a blob of bytes is served at, and the only sanctioned way to mint one. */
|
|
127
170
|
export declare function immutableAssetPath(hash: `sha256:${string}`, basename: string): string;
|
|
128
171
|
/**
|
|
@@ -134,11 +177,13 @@ export declare function isImmutableAssetPath(path: string): boolean;
|
|
|
134
177
|
/**
|
|
135
178
|
* The `_headers` document Cloudflare's static asset layer applies to a deployment.
|
|
136
179
|
*
|
|
137
|
-
* One rule
|
|
138
|
-
*
|
|
139
|
-
*
|
|
180
|
+
* One rule per prefix, and nothing else: an `immutable` directive that leaked onto the HTML document
|
|
181
|
+
* would pin stale markup in every browser that saw it for a year, and no server-side action reaches
|
|
182
|
+
* a browser that has already stored one. `declaredPrefixes` adds a rule for each entry of
|
|
183
|
+
* {@link PublicAssetsV0.declaredImmutablePrefixes}, which {@link parsePublicAssets} has already
|
|
184
|
+
* refused from taking a platform namespace or naming a whole site.
|
|
140
185
|
*/
|
|
141
|
-
export declare function immutableAssetHeaders(prefix?: string): string;
|
|
186
|
+
export declare function immutableAssetHeaders(prefix?: string, declaredPrefixes?: readonly string[]): string;
|
|
142
187
|
/**
|
|
143
188
|
* Validates a declaration rather than trusting one. Every rule below has a failure it exists to
|
|
144
189
|
* prevent, and the two that matter most are the last two:
|
|
@@ -51,6 +51,62 @@ export const IMMUTABLE_ASSET_HASH_LENGTH = 64;
|
|
|
51
51
|
export const MAX_RETAINED_ASSET_GENERATIONS = 2;
|
|
52
52
|
const IMMUTABLE_PATH = /^\/_xa\/[0-9a-f]{64}\/[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/u;
|
|
53
53
|
const ASSET_HASH = /^sha256:[0-9a-f]{64}$/u;
|
|
54
|
+
/**
|
|
55
|
+
* Upper bound on {@link PublicAssetsV0.declaredImmutablePrefixes}. Four, because the declaration is
|
|
56
|
+
* a trust statement and a short list is one a reviewer can read; no framework adapter mints more
|
|
57
|
+
* than one such directory.
|
|
58
|
+
*/
|
|
59
|
+
export const MAX_DECLARED_IMMUTABLE_PREFIXES = 4;
|
|
60
|
+
const DECLARED_PREFIX = /^\/(?:[A-Za-z0-9._-]+\/)+$/u;
|
|
61
|
+
/** Namespaces the platform claims before assets are consulted, and which no declaration may take. */
|
|
62
|
+
const RESERVED_PREFIXES = ['/_xa/', '/_xeer/', '/__xeer/'];
|
|
63
|
+
/**
|
|
64
|
+
* How long an origin may be, prefix included. Published because a producer has to refuse a path it
|
|
65
|
+
* cannot describe rather than truncate one: two long paths shortened to the same origin would name
|
|
66
|
+
* one file as the source of both, which is the opposite of what the field is for.
|
|
67
|
+
*/
|
|
68
|
+
export const MAX_PUBLIC_ASSET_ORIGIN_LENGTH = 256;
|
|
69
|
+
/**
|
|
70
|
+
* What {@link ImmutableAssetV0.contentType} and {@link RevalidatedAssetV0.contentType} are filled
|
|
71
|
+
* with, for every producer of a declaration.
|
|
72
|
+
*
|
|
73
|
+
* It lives beside the field it populates because the two producers that had a map each — the Xeer
|
|
74
|
+
* compiler and the SvelteKit framework build — drifted, and the drift was silent: the framework map
|
|
75
|
+
* omitted `wasm`, so a WebAssembly module shipped as `application/octet-stream`, which
|
|
76
|
+
* `WebAssembly.instantiateStreaming` refuses. One map, or the same file works through one build path
|
|
77
|
+
* and breaks through the other.
|
|
78
|
+
*
|
|
79
|
+
* Deliberately not a lookup of a real media-type database: this only has to answer for what a build
|
|
80
|
+
* emits, every value is one a browser treats as the thing it is, and an unknown extension gets the
|
|
81
|
+
* one answer that is never wrong about encoding.
|
|
82
|
+
*/
|
|
83
|
+
export function assetContentType(path) {
|
|
84
|
+
const extension = path.toLowerCase().split('.').at(-1) ?? '';
|
|
85
|
+
const types = {
|
|
86
|
+
avif: 'image/avif',
|
|
87
|
+
css: 'text/css; charset=utf-8',
|
|
88
|
+
eot: 'application/vnd.ms-fontobject',
|
|
89
|
+
gif: 'image/gif',
|
|
90
|
+
html: 'text/html; charset=utf-8',
|
|
91
|
+
ico: 'image/x-icon',
|
|
92
|
+
jpeg: 'image/jpeg',
|
|
93
|
+
jpg: 'image/jpeg',
|
|
94
|
+
js: 'text/javascript; charset=utf-8',
|
|
95
|
+
json: 'application/json; charset=utf-8',
|
|
96
|
+
mjs: 'text/javascript; charset=utf-8',
|
|
97
|
+
otf: 'font/otf',
|
|
98
|
+
png: 'image/png',
|
|
99
|
+
svg: 'image/svg+xml',
|
|
100
|
+
ttf: 'font/ttf',
|
|
101
|
+
txt: 'text/plain; charset=utf-8',
|
|
102
|
+
wasm: 'application/wasm',
|
|
103
|
+
webp: 'image/webp',
|
|
104
|
+
woff: 'font/woff',
|
|
105
|
+
woff2: 'font/woff2',
|
|
106
|
+
xml: 'application/xml; charset=utf-8',
|
|
107
|
+
};
|
|
108
|
+
return types[extension] ?? 'application/octet-stream';
|
|
109
|
+
}
|
|
54
110
|
/** The URL a blob of bytes is served at, and the only sanctioned way to mint one. */
|
|
55
111
|
export function immutableAssetPath(hash, basename) {
|
|
56
112
|
return `${IMMUTABLE_ASSET_PREFIX}${hash.slice('sha256:'.length, 'sha256:'.length + IMMUTABLE_ASSET_HASH_LENGTH)}/${basename}`;
|
|
@@ -66,12 +122,15 @@ export function isImmutableAssetPath(path) {
|
|
|
66
122
|
/**
|
|
67
123
|
* The `_headers` document Cloudflare's static asset layer applies to a deployment.
|
|
68
124
|
*
|
|
69
|
-
* One rule
|
|
70
|
-
*
|
|
71
|
-
*
|
|
125
|
+
* One rule per prefix, and nothing else: an `immutable` directive that leaked onto the HTML document
|
|
126
|
+
* would pin stale markup in every browser that saw it for a year, and no server-side action reaches
|
|
127
|
+
* a browser that has already stored one. `declaredPrefixes` adds a rule for each entry of
|
|
128
|
+
* {@link PublicAssetsV0.declaredImmutablePrefixes}, which {@link parsePublicAssets} has already
|
|
129
|
+
* refused from taking a platform namespace or naming a whole site.
|
|
72
130
|
*/
|
|
73
|
-
export function immutableAssetHeaders(prefix = IMMUTABLE_ASSET_PREFIX) {
|
|
74
|
-
return
|
|
131
|
+
export function immutableAssetHeaders(prefix = IMMUTABLE_ASSET_PREFIX, declaredPrefixes = []) {
|
|
132
|
+
return [prefix, ...declaredPrefixes]
|
|
133
|
+
.map((entry) => `${entry}*\n Cache-Control: ${IMMUTABLE_ASSET_CACHE_CONTROL}\n`).join('');
|
|
75
134
|
}
|
|
76
135
|
function fail(message) {
|
|
77
136
|
throw new TypeError(`publicAssets: ${message}`);
|
|
@@ -84,7 +143,7 @@ function object(value, label) {
|
|
|
84
143
|
function origin(value, label) {
|
|
85
144
|
// The `:` must be followed by something. An empty `module:` names nothing, and `documentReferences`
|
|
86
145
|
// resolves the document's three subresources by matching on this exact string.
|
|
87
|
-
if (typeof value !== 'string' || value.length >
|
|
146
|
+
if (typeof value !== 'string' || value.length > MAX_PUBLIC_ASSET_ORIGIN_LENGTH
|
|
88
147
|
|| !(value === 'document' || /^(?:module|asset):.+$/u.test(value))) {
|
|
89
148
|
fail(`${label}.origin must be "document", "module:<path>" or "asset:<path>".`);
|
|
90
149
|
}
|
|
@@ -155,9 +214,42 @@ export function parsePublicAssets(value) {
|
|
|
155
214
|
});
|
|
156
215
|
sorted(immutable.map((entry) => entry.path), 'immutable');
|
|
157
216
|
sorted(revalidate.map((entry) => entry.path), 'revalidate');
|
|
217
|
+
const declaredImmutablePrefixes = declaredPrefixes(input.declaredImmutablePrefixes);
|
|
158
218
|
return { format: PUBLIC_ASSETS_FORMAT, immutablePrefix: IMMUTABLE_ASSET_PREFIX, retainedGenerations,
|
|
219
|
+
...(declaredImmutablePrefixes ? { declaredImmutablePrefixes } : {}),
|
|
159
220
|
immutable, revalidate };
|
|
160
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* The declared prefixes, or `undefined` for the artifact that declares none — which is every
|
|
224
|
+
* artifact the Xeer compiler emits, and every bundle built before the field existed. Absent is
|
|
225
|
+
* carried through as absent rather than as `[]`, so a re-serialized declaration is byte-identical to
|
|
226
|
+
* the one that was parsed and its `artifactId` does not move.
|
|
227
|
+
*
|
|
228
|
+
* The rules are the ones that keep a year-long cache directive from reaching something mutable: a
|
|
229
|
+
* directory rather than a whole site or a bare file, never one of the platform's own namespaces, and
|
|
230
|
+
* never nested inside another entry, which would attach two rules to one path.
|
|
231
|
+
*/
|
|
232
|
+
function declaredPrefixes(value) {
|
|
233
|
+
if (value === undefined)
|
|
234
|
+
return undefined;
|
|
235
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > MAX_DECLARED_IMMUTABLE_PREFIXES) {
|
|
236
|
+
fail(`declaredImmutablePrefixes must be an array of 1 to ${MAX_DECLARED_IMMUTABLE_PREFIXES} prefixes.`);
|
|
237
|
+
}
|
|
238
|
+
const prefixes = value.map((item, index) => {
|
|
239
|
+
if (typeof item !== 'string' || item.length > 128 || !DECLARED_PREFIX.test(item) || item.includes('..')) {
|
|
240
|
+
fail(`declaredImmutablePrefixes[${index}] must be a "/segment/" path prefix.`);
|
|
241
|
+
}
|
|
242
|
+
if (RESERVED_PREFIXES.some((reserved) => item.startsWith(reserved) || reserved.startsWith(item))) {
|
|
243
|
+
fail(`declaredImmutablePrefixes[${index}] uses a reserved platform namespace.`);
|
|
244
|
+
}
|
|
245
|
+
return item;
|
|
246
|
+
});
|
|
247
|
+
sorted(prefixes, 'declaredImmutablePrefixes');
|
|
248
|
+
const nested = prefixes.find((entry, index) => prefixes.some((other, position) => position !== index && entry.startsWith(other)));
|
|
249
|
+
if (nested !== undefined)
|
|
250
|
+
fail(`declaredImmutablePrefixes contains a prefix of another: ${nested}`);
|
|
251
|
+
return prefixes;
|
|
252
|
+
}
|
|
161
253
|
/**
|
|
162
254
|
* Every path that must answer for this deployment, immutable first. The union a control plane uploads
|
|
163
255
|
* is this set for the current artifact plus the immutable half of the retained generations.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names `xeer new` accepts on its two independent axes, in one place because three surfaces
|
|
3
|
+
* already have to agree on them: the CLI's `--template` and `--framework` parsing, the MCP input
|
|
4
|
+
* enums the action registry declares, and the template catalog's reserved-name check.
|
|
5
|
+
*
|
|
6
|
+
* Here rather than in the CLI because the catalog publisher runs without the CLI installed, and a
|
|
7
|
+
* second copy of either list is a copy that can drift into publishing a template that the offline
|
|
8
|
+
* scaffold would shadow.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The bundled Xeer application starters: a `xeer.app.json` the platform compiles, plus the sources
|
|
12
|
+
* it names. Offline, deterministic, and resolved before any catalog request.
|
|
13
|
+
*/
|
|
14
|
+
export declare const BUNDLED_TEMPLATES: readonly ["notes", "todo", "blog", "personal-site"];
|
|
15
|
+
export type BundledTemplateName = (typeof BUNDLED_TEMPLATES)[number];
|
|
16
|
+
/**
|
|
17
|
+
* The frameworks Xeer runs rather than compiles: a `xeer.config.json` beside a project whose own
|
|
18
|
+
* toolchain owns the renderer, the routes, and the build.
|
|
19
|
+
*
|
|
20
|
+
* A separate axis rather than more template names, because the two write different root contracts
|
|
21
|
+
* and every later command dispatches on which one is present. `--framework` selects one for a fresh
|
|
22
|
+
* project; `xeer init` adopts an existing one; `xeer.config.json` declares which one a directory is.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FRAMEWORK_NAMES: readonly ["sveltekit"];
|
|
25
|
+
export type FrameworkName = (typeof FRAMEWORK_NAMES)[number];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names `xeer new` accepts on its two independent axes, in one place because three surfaces
|
|
3
|
+
* already have to agree on them: the CLI's `--template` and `--framework` parsing, the MCP input
|
|
4
|
+
* enums the action registry declares, and the template catalog's reserved-name check.
|
|
5
|
+
*
|
|
6
|
+
* Here rather than in the CLI because the catalog publisher runs without the CLI installed, and a
|
|
7
|
+
* second copy of either list is a copy that can drift into publishing a template that the offline
|
|
8
|
+
* scaffold would shadow.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* The bundled Xeer application starters: a `xeer.app.json` the platform compiles, plus the sources
|
|
12
|
+
* it names. Offline, deterministic, and resolved before any catalog request.
|
|
13
|
+
*/
|
|
14
|
+
export const BUNDLED_TEMPLATES = Object.freeze(['notes', 'todo', 'blog', 'personal-site']);
|
|
15
|
+
/**
|
|
16
|
+
* The frameworks Xeer runs rather than compiles: a `xeer.config.json` beside a project whose own
|
|
17
|
+
* toolchain owns the renderer, the routes, and the build.
|
|
18
|
+
*
|
|
19
|
+
* A separate axis rather than more template names, because the two write different root contracts
|
|
20
|
+
* and every later command dispatches on which one is present. `--framework` selects one for a fresh
|
|
21
|
+
* project; `xeer init` adopts an existing one; `xeer.config.json` declares which one a directory is.
|
|
22
|
+
*/
|
|
23
|
+
export const FRAMEWORK_NAMES = Object.freeze(['sveltekit']);
|
package/vendor/spec/schema.d.ts
CHANGED
|
@@ -9,9 +9,9 @@ export declare const normalizedDatabaseSchema: z.ZodObject<{
|
|
|
9
9
|
string: "string";
|
|
10
10
|
number: "number";
|
|
11
11
|
boolean: "boolean";
|
|
12
|
+
json: "json";
|
|
12
13
|
datetime: "datetime";
|
|
13
14
|
bytes: "bytes";
|
|
14
|
-
json: "json";
|
|
15
15
|
ref: "ref";
|
|
16
16
|
}>;
|
|
17
17
|
optional: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -91,9 +91,9 @@ export declare const applicationManifestSchema: z.ZodObject<{
|
|
|
91
91
|
string: "string";
|
|
92
92
|
number: "number";
|
|
93
93
|
boolean: "boolean";
|
|
94
|
+
json: "json";
|
|
94
95
|
datetime: "datetime";
|
|
95
96
|
bytes: "bytes";
|
|
96
|
-
json: "json";
|
|
97
97
|
ref: "ref";
|
|
98
98
|
}>;
|
|
99
99
|
optional: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -4,6 +4,18 @@ export declare const TEMPLATE_DISTRIBUTION_CATALOG_SCHEMA_URL: "https://docs.xee
|
|
|
4
4
|
/** Exact semantic version: no range operator, no `v` prefix, no wildcard. */
|
|
5
5
|
export declare const EXACT_VERSION_PATTERN: string;
|
|
6
6
|
export declare const TEMPLATE_ARCHIVE_CHECKSUM_PATTERN: "^sha256:[a-f0-9]{64}$";
|
|
7
|
+
/**
|
|
8
|
+
* The two root contracts a template can declare: a `xeer.app.json` the platform compiles, or a
|
|
9
|
+
* `xeer.config.json` naming a framework Xeer runs. The same distinction every directory-taking
|
|
10
|
+
* command already dispatches on, spelled once here so the catalog does not grow a third vocabulary.
|
|
11
|
+
*
|
|
12
|
+
* These are the words for both, not only for a catalog entry: the CLI's `ProjectKind`
|
|
13
|
+
* (`packages/cli/src/framework-project.ts`) aliases {@link TemplateProjectKind} rather than spelling
|
|
14
|
+
* its filesystem probe's answer a second way, so a directory's kind and a published template's kind
|
|
15
|
+
* are the same value and nothing translates between them.
|
|
16
|
+
*/
|
|
17
|
+
export declare const TEMPLATE_PROJECT_KINDS: readonly ["xeer-application", "framework"];
|
|
18
|
+
export type TemplateProjectKind = (typeof TEMPLATE_PROJECT_KINDS)[number];
|
|
7
19
|
/**
|
|
8
20
|
* One published template. The authored metadata is copied verbatim from `template.json`; everything
|
|
9
21
|
* else is produced by the release job that packed, deployed, and verified this exact artifact.
|
|
@@ -45,6 +57,15 @@ export declare const templateDistributionEntrySchema: z.ZodObject<{
|
|
|
45
57
|
tags: z.ZodArray<z.ZodString>;
|
|
46
58
|
keyHighlights: z.ZodArray<z.ZodString>;
|
|
47
59
|
id: z.ZodString;
|
|
60
|
+
project: z.ZodOptional<z.ZodObject<{
|
|
61
|
+
kind: z.ZodEnum<{
|
|
62
|
+
framework: "framework";
|
|
63
|
+
"xeer-application": "xeer-application";
|
|
64
|
+
}>;
|
|
65
|
+
framework: z.ZodOptional<z.ZodEnum<{
|
|
66
|
+
sveltekit: "sveltekit";
|
|
67
|
+
}>>;
|
|
68
|
+
}, z.core.$strict>>;
|
|
48
69
|
}, z.core.$strict>;
|
|
49
70
|
export type TemplateDistributionEntryV0 = z.infer<typeof templateDistributionEntrySchema>;
|
|
50
71
|
/**
|
|
@@ -88,6 +109,15 @@ export declare const templateDistributionCatalogSchema: z.ZodObject<{
|
|
|
88
109
|
tags: z.ZodArray<z.ZodString>;
|
|
89
110
|
keyHighlights: z.ZodArray<z.ZodString>;
|
|
90
111
|
id: z.ZodString;
|
|
112
|
+
project: z.ZodOptional<z.ZodObject<{
|
|
113
|
+
kind: z.ZodEnum<{
|
|
114
|
+
framework: "framework";
|
|
115
|
+
"xeer-application": "xeer-application";
|
|
116
|
+
}>;
|
|
117
|
+
framework: z.ZodOptional<z.ZodEnum<{
|
|
118
|
+
sveltekit: "sveltekit";
|
|
119
|
+
}>>;
|
|
120
|
+
}, z.core.$strict>>;
|
|
91
121
|
}, z.core.$strict>>;
|
|
92
122
|
}, z.core.$strict>;
|
|
93
123
|
export type TemplateDistributionCatalogV0 = z.infer<typeof templateDistributionCatalogSchema>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { FRAMEWORK_NAMES } from './scaffold-names.js';
|
|
2
3
|
import { checkTemplateMetadataUniqueness, templateAltTextSchema, templateIdentityShape, templateMetadataShape, templateSlugSchema, } from './template.js';
|
|
3
4
|
export const TEMPLATE_DISTRIBUTION_CATALOG_FORMAT = 'xeer.template-distribution-catalog.v0';
|
|
4
5
|
export const TEMPLATE_DISTRIBUTION_CATALOG_SCHEMA_URL = 'https://docs.xeer.run/template-distribution-catalog-v0.schema.json';
|
|
@@ -18,6 +19,38 @@ const sourceArchiveSchema = z.strictObject({
|
|
|
18
19
|
url: httpsUrl,
|
|
19
20
|
sha256: archiveChecksum,
|
|
20
21
|
});
|
|
22
|
+
/**
|
|
23
|
+
* The two root contracts a template can declare: a `xeer.app.json` the platform compiles, or a
|
|
24
|
+
* `xeer.config.json` naming a framework Xeer runs. The same distinction every directory-taking
|
|
25
|
+
* command already dispatches on, spelled once here so the catalog does not grow a third vocabulary.
|
|
26
|
+
*
|
|
27
|
+
* These are the words for both, not only for a catalog entry: the CLI's `ProjectKind`
|
|
28
|
+
* (`packages/cli/src/framework-project.ts`) aliases {@link TemplateProjectKind} rather than spelling
|
|
29
|
+
* its filesystem probe's answer a second way, so a directory's kind and a published template's kind
|
|
30
|
+
* are the same value and nothing translates between them.
|
|
31
|
+
*/
|
|
32
|
+
export const TEMPLATE_PROJECT_KINDS = ['xeer-application', 'framework'];
|
|
33
|
+
/**
|
|
34
|
+
* What the template's own root contract says it is, derived by the publisher from that file and never
|
|
35
|
+
* authored: `template.json` has no such field and could not carry one.
|
|
36
|
+
*
|
|
37
|
+
* It exists so a consumer — a gallery, the CLI's result envelope — reads kind and framework from
|
|
38
|
+
* metadata rather than parsing meaning out of an id. Ids are opaque; this is the only carrier.
|
|
39
|
+
*/
|
|
40
|
+
const templateProjectSchema = z.strictObject({
|
|
41
|
+
kind: z.enum(TEMPLATE_PROJECT_KINDS),
|
|
42
|
+
framework: z.enum(FRAMEWORK_NAMES).optional(),
|
|
43
|
+
}).superRefine((project, ctx) => {
|
|
44
|
+
if ((project.framework !== undefined) === (project.kind === 'framework'))
|
|
45
|
+
return;
|
|
46
|
+
ctx.addIssue({
|
|
47
|
+
code: 'custom',
|
|
48
|
+
path: ['framework'],
|
|
49
|
+
message: project.kind === 'framework'
|
|
50
|
+
? 'a framework project must name the framework it runs'
|
|
51
|
+
: `only a framework project names a framework; a ${project.kind} project does not`,
|
|
52
|
+
});
|
|
53
|
+
});
|
|
21
54
|
/**
|
|
22
55
|
* One published template. The authored metadata is copied verbatim from `template.json`; everything
|
|
23
56
|
* else is produced by the release job that packed, deployed, and verified this exact artifact.
|
|
@@ -30,6 +63,11 @@ const sourceArchiveSchema = z.strictObject({
|
|
|
30
63
|
*/
|
|
31
64
|
export const templateDistributionEntrySchema = z.strictObject({
|
|
32
65
|
id: templateSlugSchema,
|
|
66
|
+
/**
|
|
67
|
+
* Absent on every entry published before the field existed, which is why it is optional: those
|
|
68
|
+
* entries are carried forward verbatim rather than re-derived.
|
|
69
|
+
*/
|
|
70
|
+
project: templateProjectSchema.optional(),
|
|
33
71
|
...templateMetadataShape,
|
|
34
72
|
screenshots: z.array(distributedScreenshotSchema).min(1).max(8),
|
|
35
73
|
...templateIdentityShape,
|
package/vendor/spec/types.d.ts
CHANGED
|
@@ -4,6 +4,32 @@ export declare const SOURCE_FORMAT: "xeer.application-source.v0";
|
|
|
4
4
|
export declare const ARTIFACT_FORMAT: "xeer.application.v0";
|
|
5
5
|
export declare const DEV_PROTOCOL: "xeer.dev.v0";
|
|
6
6
|
export declare const INSPECT_PROTOCOL: "xeer.inspect.v0";
|
|
7
|
+
/**
|
|
8
|
+
* The workerd release every Xeer-uploaded Worker is compiled and booted against.
|
|
9
|
+
*
|
|
10
|
+
* One constant rather than one per producer. The compiler stamps it into the artifact, `xeer dev`
|
|
11
|
+
* boots the local workerd with it, and the framework deployment wrapper uploads it — and all three
|
|
12
|
+
* have to agree, because the platform verifies a bundle on the release named here and a project's own
|
|
13
|
+
* `wrangler.jsonc` may legitimately be newer. It lived as three hand-copied literals with a comment
|
|
14
|
+
* asking each to be kept in lockstep; the import is the lockstep.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WORKER_COMPATIBILITY_DATE: "2026-07-23";
|
|
17
|
+
/**
|
|
18
|
+
* The v0 ceilings on what one deployment may carry, enforced wherever a bundle is assembled.
|
|
19
|
+
*
|
|
20
|
+
* They are the platform's limits rather than the compiler's, which is why they are here: a framework
|
|
21
|
+
* bundle assembled without the Xeer compiler has to refuse the same sizes, locally and before
|
|
22
|
+
* encoding, or the failure arrives as a `RangeError` on the builder's machine or an out-of-memory
|
|
23
|
+
* control-plane isolate.
|
|
24
|
+
*/
|
|
25
|
+
export declare const MAX_MODULE_BYTES: number;
|
|
26
|
+
export declare const MAX_ASSET_BYTES: number;
|
|
27
|
+
/**
|
|
28
|
+
* The whole uploaded payload's ceiling, counted the way the control plane counts it: in base64
|
|
29
|
+
* characters, which is what travels. A producer holding raw bytes compares
|
|
30
|
+
* `Math.ceil(bytes / 3) * 4` against it.
|
|
31
|
+
*/
|
|
32
|
+
export declare const MAX_DEPLOYMENT_BASE64_BYTES: number;
|
|
7
33
|
export type DiagnosticSeverity = 'error' | 'warning' | 'info';
|
|
8
34
|
export interface SourceSpan {
|
|
9
35
|
line: number;
|
package/vendor/spec/types.js
CHANGED
|
@@ -2,6 +2,32 @@ export const SOURCE_FORMAT = 'xeer.application-source.v0';
|
|
|
2
2
|
export const ARTIFACT_FORMAT = 'xeer.application.v0';
|
|
3
3
|
export const DEV_PROTOCOL = 'xeer.dev.v0';
|
|
4
4
|
export const INSPECT_PROTOCOL = 'xeer.inspect.v0';
|
|
5
|
+
/**
|
|
6
|
+
* The workerd release every Xeer-uploaded Worker is compiled and booted against.
|
|
7
|
+
*
|
|
8
|
+
* One constant rather than one per producer. The compiler stamps it into the artifact, `xeer dev`
|
|
9
|
+
* boots the local workerd with it, and the framework deployment wrapper uploads it — and all three
|
|
10
|
+
* have to agree, because the platform verifies a bundle on the release named here and a project's own
|
|
11
|
+
* `wrangler.jsonc` may legitimately be newer. It lived as three hand-copied literals with a comment
|
|
12
|
+
* asking each to be kept in lockstep; the import is the lockstep.
|
|
13
|
+
*/
|
|
14
|
+
export const WORKER_COMPATIBILITY_DATE = '2026-07-23';
|
|
15
|
+
/**
|
|
16
|
+
* The v0 ceilings on what one deployment may carry, enforced wherever a bundle is assembled.
|
|
17
|
+
*
|
|
18
|
+
* They are the platform's limits rather than the compiler's, which is why they are here: a framework
|
|
19
|
+
* bundle assembled without the Xeer compiler has to refuse the same sizes, locally and before
|
|
20
|
+
* encoding, or the failure arrives as a `RangeError` on the builder's machine or an out-of-memory
|
|
21
|
+
* control-plane isolate.
|
|
22
|
+
*/
|
|
23
|
+
export const MAX_MODULE_BYTES = 10 * 1024 * 1024;
|
|
24
|
+
export const MAX_ASSET_BYTES = 25 * 1024 * 1024;
|
|
25
|
+
/**
|
|
26
|
+
* The whole uploaded payload's ceiling, counted the way the control plane counts it: in base64
|
|
27
|
+
* characters, which is what travels. A producer holding raw bytes compares
|
|
28
|
+
* `Math.ceil(bytes / 3) * 4` against it.
|
|
29
|
+
*/
|
|
30
|
+
export const MAX_DEPLOYMENT_BASE64_BYTES = 42 * 1024 * 1024;
|
|
5
31
|
/**
|
|
6
32
|
* Whether a set of diagnostics refuses the command that produced it.
|
|
7
33
|
*
|