every-plugin 3.0.0-rc.15 → 3.0.0-rc.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/artifact-names.cjs +8 -5
- package/dist/build/artifact-names.cjs.map +1 -1
- package/dist/build/artifact-names.d.cts +7 -5
- package/dist/build/artifact-names.d.cts.map +1 -1
- package/dist/build/artifact-names.d.mts +7 -5
- package/dist/build/artifact-names.d.mts.map +1 -1
- package/dist/build/artifact-names.mjs +8 -5
- package/dist/build/artifact-names.mjs.map +1 -1
- package/dist/build/ui/stubs.cjs +2 -7
- package/dist/build/ui/stubs.cjs.map +1 -1
- package/dist/build/ui/stubs.d.cts.map +1 -1
- package/dist/build/ui/stubs.d.mts.map +1 -1
- package/dist/build/ui/stubs.mjs +2 -7
- package/dist/build/ui/stubs.mjs.map +1 -1
- package/dist/client.cjs +15 -63
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +12 -11
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +12 -11
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +16 -64
- package/dist/client.mjs.map +1 -1
- package/dist/errors.cjs +11 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +15 -1
- package/dist/errors.d.cts.map +1 -1
- package/dist/errors.d.mts +15 -1
- package/dist/errors.d.mts.map +1 -1
- package/dist/errors.mjs +11 -2
- package/dist/errors.mjs.map +1 -1
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +2 -2
- package/dist/runtime/mf-config.cjs +1 -1
- package/dist/runtime/mf-config.mjs +1 -1
- package/dist/ui/manifest/construct.cjs +13 -38
- package/dist/ui/manifest/construct.cjs.map +1 -1
- package/dist/ui/manifest/construct.d.cts +4 -13
- package/dist/ui/manifest/construct.d.cts.map +1 -1
- package/dist/ui/manifest/construct.d.mts +4 -13
- package/dist/ui/manifest/construct.d.mts.map +1 -1
- package/dist/ui/manifest/construct.mjs +14 -39
- package/dist/ui/manifest/construct.mjs.map +1 -1
- package/dist/ui/manifest/descriptor.cjs +1 -1
- package/dist/ui/manifest/descriptor.mjs +1 -1
- package/dist/ui/manifest/index.d.cts +3 -3
- package/dist/ui/manifest/index.d.mts +3 -3
- package/dist/ui/manifest/mount-registry.cjs +6 -13
- package/dist/ui/manifest/mount-registry.cjs.map +1 -1
- package/dist/ui/manifest/mount-registry.d.cts +29 -36
- package/dist/ui/manifest/mount-registry.d.cts.map +1 -1
- package/dist/ui/manifest/mount-registry.d.mts +29 -36
- package/dist/ui/manifest/mount-registry.d.mts.map +1 -1
- package/dist/ui/manifest/mount-registry.mjs +6 -13
- package/dist/ui/manifest/mount-registry.mjs.map +1 -1
- package/dist/version-manifest.cjs +70 -5
- package/dist/version-manifest.cjs.map +1 -1
- package/dist/version-manifest.d.cts +41 -1
- package/dist/version-manifest.d.cts.map +1 -1
- package/dist/version-manifest.d.mts +41 -1
- package/dist/version-manifest.d.mts.map +1 -1
- package/dist/version-manifest.mjs +66 -6
- package/dist/version-manifest.mjs.map +1 -1
- package/package.json +1 -1
- package/skills/plugin-client/SKILL.md +3 -3
- package/src/build/artifact-names.ts +8 -5
- package/src/build/ui/stubs.ts +2 -7
- package/src/client.ts +14 -111
- package/src/errors.ts +10 -0
- package/src/index.ts +2 -0
- package/src/ui/manifest/construct.ts +20 -65
- package/src/ui/manifest/index.ts +0 -2
- package/src/ui/manifest/mount-registry.ts +20 -20
- package/src/version-manifest.ts +77 -10
|
@@ -12,10 +12,35 @@ let node_crypto = require("node:crypto");
|
|
|
12
12
|
* stops growing with artifact kinds and old versions stay fully servable.
|
|
13
13
|
*/
|
|
14
14
|
const SRI = zod.z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);
|
|
15
|
+
/** Slot-relative directory holding one deploy's immutable file copies (#826). */
|
|
16
|
+
const VERSION_DIR_PATTERN = /^versions\/[0-9a-f]{16}$/;
|
|
17
|
+
/** The attribution block stored in the manifest's `signature` field. The
|
|
18
|
+
* verifying key rides along so a verifier can distinguish "wrong signature"
|
|
19
|
+
* from "signed by a key the config never registered" — both are
|
|
20
|
+
* `SignatureInvalid`, but the reasons name different compromises. */
|
|
21
|
+
const ManifestSignatureSchema = zod.z.object({
|
|
22
|
+
algorithm: zod.z.literal("ed25519"),
|
|
23
|
+
/** NEAR-format verifying key (`ed25519:<base58>`) — must equal the
|
|
24
|
+
* config's `publish.verification.publicKey`. */
|
|
25
|
+
publicKey: zod.z.string().min(1),
|
|
26
|
+
/** base64 ed25519 signature over the canonical payload bytes. */
|
|
27
|
+
value: zod.z.string().min(1)
|
|
28
|
+
});
|
|
15
29
|
const WorkspaceVersionManifestSchema = zod.z.object({
|
|
16
30
|
version: zod.z.string().regex(/^[0-9a-f]{16}$/),
|
|
17
31
|
builtAt: zod.z.string().optional(),
|
|
18
32
|
gitSha: zod.z.string().optional(),
|
|
33
|
+
/**
|
|
34
|
+
* Slot-relative directory (`versions/<dirId>`) holding this deploy's
|
|
35
|
+
* byte-immutable copies of every dist file (#826). When present, all
|
|
36
|
+
* artifact coordinates below resolve against `<slotBase>/<dir>/` — so a
|
|
37
|
+
* pinned child's entry fetches its expose chunk and chunks from the same
|
|
38
|
+
* frozen directory instead of the mutable slot root. Absent on legacy
|
|
39
|
+
* manifests: coordinates resolve against the slot root exactly as before.
|
|
40
|
+
* Derived from the `files` map (see `versionDirPath`), so it participates
|
|
41
|
+
* in the version hash without circularity.
|
|
42
|
+
*/
|
|
43
|
+
dir: zod.z.string().regex(VERSION_DIR_PATTERN).optional(),
|
|
19
44
|
entry: zod.z.string().min(1),
|
|
20
45
|
entryIntegrity: SRI,
|
|
21
46
|
ssr: zod.z.object({
|
|
@@ -48,8 +73,38 @@ const WorkspaceVersionManifestSchema = zod.z.object({
|
|
|
48
73
|
source: zod.z.object({
|
|
49
74
|
file: zod.z.string().min(1),
|
|
50
75
|
integrity: SRI
|
|
51
|
-
}).optional()
|
|
76
|
+
}).optional(),
|
|
77
|
+
/**
|
|
78
|
+
* Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature
|
|
79
|
+
* over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes
|
|
80
|
+
* are, the signature attests WHO published them, with the verifying key
|
|
81
|
+
* anchored in the on-chain config (`publish.verification`). Absent on
|
|
82
|
+
* rollouts with attribution off; readers that predate the field ignore it
|
|
83
|
+
* (zod strips unknown keys). Excluded from the version-id derivation —
|
|
84
|
+
* like `builtAt`, it is not a coordinate of the build.
|
|
85
|
+
*/
|
|
86
|
+
signature: ManifestSignatureSchema.optional()
|
|
52
87
|
});
|
|
88
|
+
/** Canonical key-sorted JSON serialization — the same form the version-id
|
|
89
|
+
* derivation hashes (undefined-valued entries dropped). Exported so the
|
|
90
|
+
* signature payload and the version id can never disagree about what
|
|
91
|
+
* "canonical" means. */
|
|
92
|
+
function stableStringify(value) {
|
|
93
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
94
|
+
if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
|
|
95
|
+
return JSON.stringify(value) ?? "null";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The bytes a manifest signature attests: the document minus its reserved
|
|
99
|
+
* `signature` field, canonically serialized (key-sorted — the version-id
|
|
100
|
+
* rule). The signer signs this before the field exists; the verifier
|
|
101
|
+
* re-derives it from the parsed document, so the stored document's
|
|
102
|
+
* whitespace never matters.
|
|
103
|
+
*/
|
|
104
|
+
function signaturePayloadOf(manifest) {
|
|
105
|
+
const { signature: _signature, ...payload } = manifest;
|
|
106
|
+
return stableStringify(payload);
|
|
107
|
+
}
|
|
53
108
|
/**
|
|
54
109
|
* Compose the manifest with its `version` derived from the canonical content
|
|
55
110
|
* of the artifact coordinates alone (key-sorted, `builtAt` excluded) —
|
|
@@ -65,13 +120,23 @@ function composeVersionManifest(input) {
|
|
|
65
120
|
version
|
|
66
121
|
});
|
|
67
122
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
123
|
+
/**
|
|
124
|
+
* The content-addressed directory name for one dist's file set (#826): a
|
|
125
|
+
* 16-hex hash over the canonical files map. Identical bytes → identical
|
|
126
|
+
* directory → re-deploying unchanged content is a server-side no-op, and a
|
|
127
|
+
* published pin's directory can never be rewritten by a later deploy
|
|
128
|
+
* (the upload service refuses conflicting writes under `versions/`).
|
|
129
|
+
*/
|
|
130
|
+
function versionDirPath(files) {
|
|
131
|
+
return `versions/${(0, node_crypto.createHash)("sha256").update(stableStringify(files)).digest("hex").slice(0, 16)}`;
|
|
72
132
|
}
|
|
73
133
|
|
|
74
134
|
//#endregion
|
|
135
|
+
exports.ManifestSignatureSchema = ManifestSignatureSchema;
|
|
136
|
+
exports.VERSION_DIR_PATTERN = VERSION_DIR_PATTERN;
|
|
75
137
|
exports.WorkspaceVersionManifestSchema = WorkspaceVersionManifestSchema;
|
|
76
138
|
exports.composeVersionManifest = composeVersionManifest;
|
|
139
|
+
exports.signaturePayloadOf = signaturePayloadOf;
|
|
140
|
+
exports.stableStringify = stableStringify;
|
|
141
|
+
exports.versionDirPath = versionDirPath;
|
|
77
142
|
//# sourceMappingURL=version-manifest.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manifest.cjs","names":["z","createHash"],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\
|
|
1
|
+
{"version":3,"file":"version-manifest.cjs","names":["z","createHash"],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\n/** Slot-relative directory holding one deploy's immutable file copies (#826). */\nexport const VERSION_DIR_PATTERN = /^versions\\/[0-9a-f]{16}$/;\n\n/** The attribution block stored in the manifest's `signature` field. The\n * verifying key rides along so a verifier can distinguish \"wrong signature\"\n * from \"signed by a key the config never registered\" — both are\n * `SignatureInvalid`, but the reasons name different compromises. */\nexport const ManifestSignatureSchema = z.object({\n algorithm: z.literal(\"ed25519\"),\n /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the\n * config's `publish.verification.publicKey`. */\n publicKey: z.string().min(1),\n /** base64 ed25519 signature over the canonical payload bytes. */\n value: z.string().min(1),\n});\nexport type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n /**\n * Slot-relative directory (`versions/<dirId>`) holding this deploy's\n * byte-immutable copies of every dist file (#826). When present, all\n * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a\n * pinned child's entry fetches its expose chunk and chunks from the same\n * frozen directory instead of the mutable slot root. Absent on legacy\n * manifests: coordinates resolve against the slot root exactly as before.\n * Derived from the `files` map (see `versionDirPath`), so it participates\n * in the version hash without circularity.\n */\n dir: z.string().regex(VERSION_DIR_PATTERN).optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /**\n * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature\n * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes\n * are, the signature attests WHO published them, with the verifying key\n * anchored in the on-chain config (`publish.verification`). Absent on\n * rollouts with attribution off; readers that predate the field ignore it\n * (zod strips unknown keys). Excluded from the version-id derivation —\n * like `builtAt`, it is not a coordinate of the build.\n */\n signature: ManifestSignatureSchema.optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** Canonical key-sorted JSON serialization — the same form the version-id\n * derivation hashes (undefined-valued entries dropped). Exported so the\n * signature payload and the version id can never disagree about what\n * \"canonical\" means. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * The bytes a manifest signature attests: the document minus its reserved\n * `signature` field, canonically serialized (key-sorted — the version-id\n * rule). The signer signs this before the field exists; the verifier\n * re-derives it from the parsed document, so the stored document's\n * whitespace never matters.\n */\nexport function signaturePayloadOf(manifest: WorkspaceVersionManifest): string {\n const { signature: _signature, ...payload } = manifest;\n return stableStringify(payload);\n}\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n dir?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\n/**\n * The content-addressed directory name for one dist's file set (#826): a\n * 16-hex hash over the canonical files map. Identical bytes → identical\n * directory → re-deploying unchanged content is a server-side no-op, and a\n * published pin's directory can never be rewritten by a later deploy\n * (the upload service refuses conflicting writes under `versions/`).\n */\nexport function versionDirPath(files: Record<string, string>): string {\n const id = createHash(\"sha256\").update(stableStringify(files)).digest(\"hex\").slice(0, 16);\n return `versions/${id}`;\n}\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,MAAMA,MAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;;AAGvD,MAAa,sBAAsB;;;;;AAMnC,MAAa,0BAA0BA,MAAE,OAAO;CAC9C,WAAWA,MAAE,QAAQ,SAAS;;;CAG9B,WAAWA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;;CAE3B,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAGD,MAAa,iCAAiCA,MAAE,OAAO;CACrD,SAASA,MAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAASA,MAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQA,MAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;;;;;CAW5B,KAAKA,MAAE,OAAO,CAAC,CAAC,MAAM,mBAAmB,CAAC,CAAC,SAAS;CACpD,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAKA,MACF,OAAO;EACN,OAAOA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiBA,MACd,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQA,MAAE,OAAOA,MAAE,OAAO,GAAGA,MAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAOA,MAAE,OAAOA,MAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQA,MACL,OAAO;EACN,MAAMA,MAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;;;;;;;;;CAUZ,WAAW,wBAAwB,SAAS;AAC9C,CAAC;;;;;AAOD,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC;;;;;;;;AASA,SAAgB,mBAAmB,UAA4C;CAC7E,MAAM,EAAE,WAAW,YAAY,GAAG,YAAY;CAC9C,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AA6BA,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,cAAUC,wBAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;;;;;;;;AASA,SAAgB,eAAe,OAAuC;CAEpE,OAAO,gBADIA,wBAAW,QAAQ,CAAC,CAAC,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAClE;AACtB"}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/version-manifest.d.ts
|
|
3
|
+
/** Slot-relative directory holding one deploy's immutable file copies (#826). */
|
|
4
|
+
declare const VERSION_DIR_PATTERN: RegExp;
|
|
5
|
+
/** The attribution block stored in the manifest's `signature` field. The
|
|
6
|
+
* verifying key rides along so a verifier can distinguish "wrong signature"
|
|
7
|
+
* from "signed by a key the config never registered" — both are
|
|
8
|
+
* `SignatureInvalid`, but the reasons name different compromises. */
|
|
9
|
+
declare const ManifestSignatureSchema: z.ZodObject<{
|
|
10
|
+
algorithm: z.ZodLiteral<"ed25519">;
|
|
11
|
+
publicKey: z.ZodString;
|
|
12
|
+
value: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;
|
|
3
15
|
declare const WorkspaceVersionManifestSchema: z.ZodObject<{
|
|
4
16
|
version: z.ZodString;
|
|
5
17
|
builtAt: z.ZodOptional<z.ZodString>;
|
|
6
18
|
gitSha: z.ZodOptional<z.ZodString>;
|
|
19
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
7
20
|
entry: z.ZodString;
|
|
8
21
|
entryIntegrity: z.ZodString;
|
|
9
22
|
ssr: z.ZodOptional<z.ZodObject<{
|
|
@@ -21,8 +34,26 @@ declare const WorkspaceVersionManifestSchema: z.ZodObject<{
|
|
|
21
34
|
file: z.ZodString;
|
|
22
35
|
integrity: z.ZodString;
|
|
23
36
|
}, z.core.$strip>>;
|
|
37
|
+
signature: z.ZodOptional<z.ZodObject<{
|
|
38
|
+
algorithm: z.ZodLiteral<"ed25519">;
|
|
39
|
+
publicKey: z.ZodString;
|
|
40
|
+
value: z.ZodString;
|
|
41
|
+
}, z.core.$strip>>;
|
|
24
42
|
}, z.core.$strip>;
|
|
25
43
|
type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;
|
|
44
|
+
/** Canonical key-sorted JSON serialization — the same form the version-id
|
|
45
|
+
* derivation hashes (undefined-valued entries dropped). Exported so the
|
|
46
|
+
* signature payload and the version id can never disagree about what
|
|
47
|
+
* "canonical" means. */
|
|
48
|
+
declare function stableStringify(value: unknown): string;
|
|
49
|
+
/**
|
|
50
|
+
* The bytes a manifest signature attests: the document minus its reserved
|
|
51
|
+
* `signature` field, canonically serialized (key-sorted — the version-id
|
|
52
|
+
* rule). The signer signs this before the field exists; the verifier
|
|
53
|
+
* re-derives it from the parsed document, so the stored document's
|
|
54
|
+
* whitespace never matters.
|
|
55
|
+
*/
|
|
56
|
+
declare function signaturePayloadOf(manifest: WorkspaceVersionManifest): string;
|
|
26
57
|
/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */
|
|
27
58
|
type SourceRef = NonNullable<WorkspaceVersionManifest["source"]>;
|
|
28
59
|
interface VersionManifestInput {
|
|
@@ -31,6 +62,7 @@ interface VersionManifestInput {
|
|
|
31
62
|
* staged copy and the CDN's immutability for republished versions. */
|
|
32
63
|
builtAt?: string;
|
|
33
64
|
gitSha?: string;
|
|
65
|
+
dir?: string;
|
|
34
66
|
entry: string;
|
|
35
67
|
entryIntegrity: string;
|
|
36
68
|
ssr?: {
|
|
@@ -57,6 +89,14 @@ interface VersionManifestInput {
|
|
|
57
89
|
* coordinate → a new version (and a new file).
|
|
58
90
|
*/
|
|
59
91
|
declare function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest;
|
|
92
|
+
/**
|
|
93
|
+
* The content-addressed directory name for one dist's file set (#826): a
|
|
94
|
+
* 16-hex hash over the canonical files map. Identical bytes → identical
|
|
95
|
+
* directory → re-deploying unchanged content is a server-side no-op, and a
|
|
96
|
+
* published pin's directory can never be rewritten by a later deploy
|
|
97
|
+
* (the upload service refuses conflicting writes under `versions/`).
|
|
98
|
+
*/
|
|
99
|
+
declare function versionDirPath(files: Record<string, string>): string;
|
|
60
100
|
//#endregion
|
|
61
|
-
export { SourceRef, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest };
|
|
101
|
+
export { ManifestSignature, ManifestSignatureSchema, SourceRef, VERSION_DIR_PATTERN, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
|
|
62
102
|
//# sourceMappingURL=version-manifest.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manifest.d.cts","names":[],"sources":["../src/version-manifest.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"version-manifest.d.cts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;;cAea,qBAAmB;;;;;cAMnB,yBAAuB,EAAA;;;;GAOlC,EAAA,KAAA;KACU,oBAAoB,EAAE,aAAa;cAElC,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;;;;iBAMtC,gBAAgB;;;;;;;;iBAmBhB,mBAAmB,UAAU;;KAMjC,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB;;;;;;;;iBAgBrD,eAAe,OAAO"}
|
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
//#region src/version-manifest.d.ts
|
|
3
|
+
/** Slot-relative directory holding one deploy's immutable file copies (#826). */
|
|
4
|
+
declare const VERSION_DIR_PATTERN: RegExp;
|
|
5
|
+
/** The attribution block stored in the manifest's `signature` field. The
|
|
6
|
+
* verifying key rides along so a verifier can distinguish "wrong signature"
|
|
7
|
+
* from "signed by a key the config never registered" — both are
|
|
8
|
+
* `SignatureInvalid`, but the reasons name different compromises. */
|
|
9
|
+
declare const ManifestSignatureSchema: z.ZodObject<{
|
|
10
|
+
algorithm: z.ZodLiteral<"ed25519">;
|
|
11
|
+
publicKey: z.ZodString;
|
|
12
|
+
value: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;
|
|
3
15
|
declare const WorkspaceVersionManifestSchema: z.ZodObject<{
|
|
4
16
|
version: z.ZodString;
|
|
5
17
|
builtAt: z.ZodOptional<z.ZodString>;
|
|
6
18
|
gitSha: z.ZodOptional<z.ZodString>;
|
|
19
|
+
dir: z.ZodOptional<z.ZodString>;
|
|
7
20
|
entry: z.ZodString;
|
|
8
21
|
entryIntegrity: z.ZodString;
|
|
9
22
|
ssr: z.ZodOptional<z.ZodObject<{
|
|
@@ -21,8 +34,26 @@ declare const WorkspaceVersionManifestSchema: z.ZodObject<{
|
|
|
21
34
|
file: z.ZodString;
|
|
22
35
|
integrity: z.ZodString;
|
|
23
36
|
}, z.core.$strip>>;
|
|
37
|
+
signature: z.ZodOptional<z.ZodObject<{
|
|
38
|
+
algorithm: z.ZodLiteral<"ed25519">;
|
|
39
|
+
publicKey: z.ZodString;
|
|
40
|
+
value: z.ZodString;
|
|
41
|
+
}, z.core.$strip>>;
|
|
24
42
|
}, z.core.$strip>;
|
|
25
43
|
type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;
|
|
44
|
+
/** Canonical key-sorted JSON serialization — the same form the version-id
|
|
45
|
+
* derivation hashes (undefined-valued entries dropped). Exported so the
|
|
46
|
+
* signature payload and the version id can never disagree about what
|
|
47
|
+
* "canonical" means. */
|
|
48
|
+
declare function stableStringify(value: unknown): string;
|
|
49
|
+
/**
|
|
50
|
+
* The bytes a manifest signature attests: the document minus its reserved
|
|
51
|
+
* `signature` field, canonically serialized (key-sorted — the version-id
|
|
52
|
+
* rule). The signer signs this before the field exists; the verifier
|
|
53
|
+
* re-derives it from the parsed document, so the stored document's
|
|
54
|
+
* whitespace never matters.
|
|
55
|
+
*/
|
|
56
|
+
declare function signaturePayloadOf(manifest: WorkspaceVersionManifest): string;
|
|
26
57
|
/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */
|
|
27
58
|
type SourceRef = NonNullable<WorkspaceVersionManifest["source"]>;
|
|
28
59
|
interface VersionManifestInput {
|
|
@@ -31,6 +62,7 @@ interface VersionManifestInput {
|
|
|
31
62
|
* staged copy and the CDN's immutability for republished versions. */
|
|
32
63
|
builtAt?: string;
|
|
33
64
|
gitSha?: string;
|
|
65
|
+
dir?: string;
|
|
34
66
|
entry: string;
|
|
35
67
|
entryIntegrity: string;
|
|
36
68
|
ssr?: {
|
|
@@ -57,6 +89,14 @@ interface VersionManifestInput {
|
|
|
57
89
|
* coordinate → a new version (and a new file).
|
|
58
90
|
*/
|
|
59
91
|
declare function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest;
|
|
92
|
+
/**
|
|
93
|
+
* The content-addressed directory name for one dist's file set (#826): a
|
|
94
|
+
* 16-hex hash over the canonical files map. Identical bytes → identical
|
|
95
|
+
* directory → re-deploying unchanged content is a server-side no-op, and a
|
|
96
|
+
* published pin's directory can never be rewritten by a later deploy
|
|
97
|
+
* (the upload service refuses conflicting writes under `versions/`).
|
|
98
|
+
*/
|
|
99
|
+
declare function versionDirPath(files: Record<string, string>): string;
|
|
60
100
|
//#endregion
|
|
61
|
-
export { SourceRef, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest };
|
|
101
|
+
export { ManifestSignature, ManifestSignatureSchema, SourceRef, VERSION_DIR_PATTERN, VersionManifestInput, WorkspaceVersionManifest, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
|
|
62
102
|
//# sourceMappingURL=version-manifest.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manifest.d.mts","names":[],"sources":["../src/version-manifest.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"version-manifest.d.mts","names":[],"sources":["../src/version-manifest.ts"],"mappings":";;;cAea,qBAAmB;;;;;cAMnB,yBAAuB,EAAA;;;;GAOlC,EAAA,KAAA;KACU,oBAAoB,EAAE,aAAa;cAElC,gCAA8B,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEzC,EAAA,KAAA;KACU,2BAA2B,EAAE,aAAa;;;;;iBAMtC,gBAAgB;;;;;;;;iBAmBhB,mBAAmB,UAAU;;KAMjC,YAAY,YAAY;UAEnB;;;;EAIf;EACA;EACA;EACA;EACA;EACA;IAAQ;IAAe;;EACvB;IAAoB;IAAc;;EAClC,SAAS;EACT,SAAS;EACT,QAAQ;EACR;IAAW;IAAc;;;;;;;;;;iBAUX,uBAAuB,OAAO,uBAAuB;;;;;;;;iBAgBrD,eAAe,OAAO"}
|
|
@@ -11,10 +11,35 @@ import { createHash } from "node:crypto";
|
|
|
11
11
|
* stops growing with artifact kinds and old versions stay fully servable.
|
|
12
12
|
*/
|
|
13
13
|
const SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);
|
|
14
|
+
/** Slot-relative directory holding one deploy's immutable file copies (#826). */
|
|
15
|
+
const VERSION_DIR_PATTERN = /^versions\/[0-9a-f]{16}$/;
|
|
16
|
+
/** The attribution block stored in the manifest's `signature` field. The
|
|
17
|
+
* verifying key rides along so a verifier can distinguish "wrong signature"
|
|
18
|
+
* from "signed by a key the config never registered" — both are
|
|
19
|
+
* `SignatureInvalid`, but the reasons name different compromises. */
|
|
20
|
+
const ManifestSignatureSchema = z.object({
|
|
21
|
+
algorithm: z.literal("ed25519"),
|
|
22
|
+
/** NEAR-format verifying key (`ed25519:<base58>`) — must equal the
|
|
23
|
+
* config's `publish.verification.publicKey`. */
|
|
24
|
+
publicKey: z.string().min(1),
|
|
25
|
+
/** base64 ed25519 signature over the canonical payload bytes. */
|
|
26
|
+
value: z.string().min(1)
|
|
27
|
+
});
|
|
14
28
|
const WorkspaceVersionManifestSchema = z.object({
|
|
15
29
|
version: z.string().regex(/^[0-9a-f]{16}$/),
|
|
16
30
|
builtAt: z.string().optional(),
|
|
17
31
|
gitSha: z.string().optional(),
|
|
32
|
+
/**
|
|
33
|
+
* Slot-relative directory (`versions/<dirId>`) holding this deploy's
|
|
34
|
+
* byte-immutable copies of every dist file (#826). When present, all
|
|
35
|
+
* artifact coordinates below resolve against `<slotBase>/<dir>/` — so a
|
|
36
|
+
* pinned child's entry fetches its expose chunk and chunks from the same
|
|
37
|
+
* frozen directory instead of the mutable slot root. Absent on legacy
|
|
38
|
+
* manifests: coordinates resolve against the slot root exactly as before.
|
|
39
|
+
* Derived from the `files` map (see `versionDirPath`), so it participates
|
|
40
|
+
* in the version hash without circularity.
|
|
41
|
+
*/
|
|
42
|
+
dir: z.string().regex(VERSION_DIR_PATTERN).optional(),
|
|
18
43
|
entry: z.string().min(1),
|
|
19
44
|
entryIntegrity: SRI,
|
|
20
45
|
ssr: z.object({
|
|
@@ -47,8 +72,38 @@ const WorkspaceVersionManifestSchema = z.object({
|
|
|
47
72
|
source: z.object({
|
|
48
73
|
file: z.string().min(1),
|
|
49
74
|
integrity: SRI
|
|
50
|
-
}).optional()
|
|
75
|
+
}).optional(),
|
|
76
|
+
/**
|
|
77
|
+
* Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature
|
|
78
|
+
* over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes
|
|
79
|
+
* are, the signature attests WHO published them, with the verifying key
|
|
80
|
+
* anchored in the on-chain config (`publish.verification`). Absent on
|
|
81
|
+
* rollouts with attribution off; readers that predate the field ignore it
|
|
82
|
+
* (zod strips unknown keys). Excluded from the version-id derivation —
|
|
83
|
+
* like `builtAt`, it is not a coordinate of the build.
|
|
84
|
+
*/
|
|
85
|
+
signature: ManifestSignatureSchema.optional()
|
|
51
86
|
});
|
|
87
|
+
/** Canonical key-sorted JSON serialization — the same form the version-id
|
|
88
|
+
* derivation hashes (undefined-valued entries dropped). Exported so the
|
|
89
|
+
* signature payload and the version id can never disagree about what
|
|
90
|
+
* "canonical" means. */
|
|
91
|
+
function stableStringify(value) {
|
|
92
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
93
|
+
if (value && typeof value === "object") return `{${Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`).sort().join(",")}}`;
|
|
94
|
+
return JSON.stringify(value) ?? "null";
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The bytes a manifest signature attests: the document minus its reserved
|
|
98
|
+
* `signature` field, canonically serialized (key-sorted — the version-id
|
|
99
|
+
* rule). The signer signs this before the field exists; the verifier
|
|
100
|
+
* re-derives it from the parsed document, so the stored document's
|
|
101
|
+
* whitespace never matters.
|
|
102
|
+
*/
|
|
103
|
+
function signaturePayloadOf(manifest) {
|
|
104
|
+
const { signature: _signature, ...payload } = manifest;
|
|
105
|
+
return stableStringify(payload);
|
|
106
|
+
}
|
|
52
107
|
/**
|
|
53
108
|
* Compose the manifest with its `version` derived from the canonical content
|
|
54
109
|
* of the artifact coordinates alone (key-sorted, `builtAt` excluded) —
|
|
@@ -64,12 +119,17 @@ function composeVersionManifest(input) {
|
|
|
64
119
|
version
|
|
65
120
|
});
|
|
66
121
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
122
|
+
/**
|
|
123
|
+
* The content-addressed directory name for one dist's file set (#826): a
|
|
124
|
+
* 16-hex hash over the canonical files map. Identical bytes → identical
|
|
125
|
+
* directory → re-deploying unchanged content is a server-side no-op, and a
|
|
126
|
+
* published pin's directory can never be rewritten by a later deploy
|
|
127
|
+
* (the upload service refuses conflicting writes under `versions/`).
|
|
128
|
+
*/
|
|
129
|
+
function versionDirPath(files) {
|
|
130
|
+
return `versions/${createHash("sha256").update(stableStringify(files)).digest("hex").slice(0, 16)}`;
|
|
71
131
|
}
|
|
72
132
|
|
|
73
133
|
//#endregion
|
|
74
|
-
export { WorkspaceVersionManifestSchema, composeVersionManifest };
|
|
134
|
+
export { ManifestSignatureSchema, VERSION_DIR_PATTERN, WorkspaceVersionManifestSchema, composeVersionManifest, signaturePayloadOf, stableStringify, versionDirPath };
|
|
75
135
|
//# sourceMappingURL=version-manifest.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-manifest.mjs","names":[],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\
|
|
1
|
+
{"version":3,"file":"version-manifest.mjs","names":[],"sources":["../src/version-manifest.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\n/**\n * WorkspaceVersionManifest — the immutable per-deploy document at\n * `bundles/<account>/<gateway>/<workspace>/versions/<version>.json`\n * (ADR 0020, as amended; atomic-deploys MAP decision 1). The published\n * config's slot pins this document via its `pin` (`manifest` + the\n * manifest's own SRI); every artifact coordinate lives here, so the config\n * stops growing with artifact kinds and old versions stay fully servable.\n */\n\nconst SRI = z.string().regex(/^sha384-[A-Za-z0-9+/=]+$/);\n\n/** Slot-relative directory holding one deploy's immutable file copies (#826). */\nexport const VERSION_DIR_PATTERN = /^versions\\/[0-9a-f]{16}$/;\n\n/** The attribution block stored in the manifest's `signature` field. The\n * verifying key rides along so a verifier can distinguish \"wrong signature\"\n * from \"signed by a key the config never registered\" — both are\n * `SignatureInvalid`, but the reasons name different compromises. */\nexport const ManifestSignatureSchema = z.object({\n algorithm: z.literal(\"ed25519\"),\n /** NEAR-format verifying key (`ed25519:<base58>`) — must equal the\n * config's `publish.verification.publicKey`. */\n publicKey: z.string().min(1),\n /** base64 ed25519 signature over the canonical payload bytes. */\n value: z.string().min(1),\n});\nexport type ManifestSignature = z.infer<typeof ManifestSignatureSchema>;\n\nexport const WorkspaceVersionManifestSchema = z.object({\n version: z.string().regex(/^[0-9a-f]{16}$/),\n builtAt: z.string().optional(),\n gitSha: z.string().optional(),\n /**\n * Slot-relative directory (`versions/<dirId>`) holding this deploy's\n * byte-immutable copies of every dist file (#826). When present, all\n * artifact coordinates below resolve against `<slotBase>/<dir>/` — so a\n * pinned child's entry fetches its expose chunk and chunks from the same\n * frozen directory instead of the mutable slot root. Absent on legacy\n * manifests: coordinates resolve against the slot root exactly as before.\n * Derived from the `files` map (see `versionDirPath`), so it participates\n * in the version hash without circularity.\n */\n dir: z.string().regex(VERSION_DIR_PATTERN).optional(),\n entry: z.string().min(1),\n entryIntegrity: SRI,\n ssr: z\n .object({\n entry: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n browserManifest: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /** extra assets (css, icons) — logical name → served-object SRI */\n assets: z.record(z.string(), SRI).optional(),\n /** shared-dependency versions this build was built against */\n shared: z.record(z.string(), z.string()).optional(),\n /**\n * Full per-file digest map of the dist (object path → served-object SRI).\n * Written by `every-plugin`-based deploys so the next deploy can diff against\n * it and skip re-uploading unchanged files; readers that predate the field\n * ignore it (zod strips unknown keys). Participates in the version hash.\n */\n files: z.record(z.string(), SRI).optional(),\n /**\n * Source archive of the workspace subtree (object path → served-object SRI)\n * published beside the dists when the runtime opts in via\n * `publish.sourcePublish` — lets `bos import` dereference a published\n * config slot to the exact source bytes that build came from, with no\n * GitHub dependency. Readers that predate the field ignore it (zod strips\n * unknown keys). Participates in the version hash.\n */\n source: z\n .object({\n file: z.string().min(1),\n integrity: SRI,\n })\n .optional(),\n /**\n * Attribution (ADR 0046 ticket 05): the deploy train's ed25519 signature\n * over `signaturePayloadOf(this document)` — SRI attests WHAT the bytes\n * are, the signature attests WHO published them, with the verifying key\n * anchored in the on-chain config (`publish.verification`). Absent on\n * rollouts with attribution off; readers that predate the field ignore it\n * (zod strips unknown keys). Excluded from the version-id derivation —\n * like `builtAt`, it is not a coordinate of the build.\n */\n signature: ManifestSignatureSchema.optional(),\n});\nexport type WorkspaceVersionManifest = z.infer<typeof WorkspaceVersionManifestSchema>;\n\n/** Canonical key-sorted JSON serialization — the same form the version-id\n * derivation hashes (undefined-valued entries dropped). Exported so the\n * signature payload and the version id can never disagree about what\n * \"canonical\" means. */\nexport function stableStringify(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(\",\")}]`;\n if (value && typeof value === \"object\") {\n return `{${Object.entries(value)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${JSON.stringify(k)}:${stableStringify(v)}`)\n .sort()\n .join(\",\")}}`;\n }\n return JSON.stringify(value) ?? \"null\";\n}\n\n/**\n * The bytes a manifest signature attests: the document minus its reserved\n * `signature` field, canonically serialized (key-sorted — the version-id\n * rule). The signer signs this before the field exists; the verifier\n * re-derives it from the parsed document, so the stored document's\n * whitespace never matters.\n */\nexport function signaturePayloadOf(manifest: WorkspaceVersionManifest): string {\n const { signature: _signature, ...payload } = manifest;\n return stableStringify(payload);\n}\n\n/** A published-artifact coordinate: object path within the bundle namespace + its served SRI. */\nexport type SourceRef = NonNullable<WorkspaceVersionManifest[\"source\"]>;\n\nexport interface VersionManifestInput {\n /** Omit: a per-build timestamp makes the same version id map to different\n * manifest bytes per machine, breaking the pin's SRI against the image's\n * staged copy and the CDN's immutability for republished versions. */\n builtAt?: string;\n gitSha?: string;\n dir?: string;\n entry: string;\n entryIntegrity: string;\n ssr?: { entry: string; integrity: string };\n browserManifest?: { file: string; integrity: string };\n assets?: Record<string, string>;\n shared?: Record<string, string>;\n files?: Record<string, string>;\n source?: { file: string; integrity: string };\n}\n\n/**\n * Compose the manifest with its `version` derived from the canonical content\n * of the artifact coordinates alone (key-sorted, `builtAt` excluded) —\n * identical bytes → identical version id regardless of when they were built,\n * so republishing unchanged content is a pointer no-op. Any changed\n * coordinate → a new version (and a new file).\n */\nexport function composeVersionManifest(input: VersionManifestInput): WorkspaceVersionManifest {\n const { builtAt: _builtAt, ...hashedInputs } = input;\n const version = createHash(\"sha256\")\n .update(stableStringify(hashedInputs))\n .digest(\"hex\")\n .slice(0, 16);\n return WorkspaceVersionManifestSchema.parse({ ...input, version });\n}\n\n/**\n * The content-addressed directory name for one dist's file set (#826): a\n * 16-hex hash over the canonical files map. Identical bytes → identical\n * directory → re-deploying unchanged content is a server-side no-op, and a\n * published pin's directory can never be rewritten by a later deploy\n * (the upload service refuses conflicting writes under `versions/`).\n */\nexport function versionDirPath(files: Record<string, string>): string {\n const id = createHash(\"sha256\").update(stableStringify(files)).digest(\"hex\").slice(0, 16);\n return `versions/${id}`;\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,0BAA0B;;AAGvD,MAAa,sBAAsB;;;;;AAMnC,MAAa,0BAA0B,EAAE,OAAO;CAC9C,WAAW,EAAE,QAAQ,SAAS;;;CAG9B,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;;CAE3B,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;AACzB,CAAC;AAGD,MAAa,iCAAiC,EAAE,OAAO;CACrD,SAAS,EAAE,OAAO,CAAC,CAAC,MAAM,gBAAgB;CAC1C,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS;;;;;;;;;;;CAW5B,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,mBAAmB,CAAC,CAAC,SAAS;CACpD,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACvB,gBAAgB;CAChB,KAAK,EACF,OAAO;EACN,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACvB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;CACZ,iBAAiB,EACd,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;CAEZ,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;CAE3C,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS;;;;;;;CAOlD,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,GAAG,CAAC,CAAC,SAAS;;;;;;;;;CAS1C,QAAQ,EACL,OAAO;EACN,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;EACtB,WAAW;CACb,CAAC,CAAC,CACD,SAAS;;;;;;;;;;CAUZ,WAAW,wBAAwB,SAAS;AAC9C,CAAC;;;;;AAOD,SAAgB,gBAAgB,OAAwB;CACtD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,IAAI,MAAM,IAAI,eAAe,CAAC,CAAC,KAAK,GAAG,EAAE;CAC1E,IAAI,SAAS,OAAO,UAAU,UAC5B,OAAO,IAAI,OAAO,QAAQ,KAAK,CAAC,CAC7B,QAAQ,GAAG,OAAO,MAAM,MAAS,CAAC,CAClC,KAAK,CAAC,GAAG,OAAO,GAAG,KAAK,UAAU,CAAC,EAAE,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAC7D,KAAK,CAAC,CACN,KAAK,GAAG,EAAE;CAEf,OAAO,KAAK,UAAU,KAAK,KAAK;AAClC;;;;;;;;AASA,SAAgB,mBAAmB,UAA4C;CAC7E,MAAM,EAAE,WAAW,YAAY,GAAG,YAAY;CAC9C,OAAO,gBAAgB,OAAO;AAChC;;;;;;;;AA6BA,SAAgB,uBAAuB,OAAuD;CAC5F,MAAM,EAAE,SAAS,UAAU,GAAG,iBAAiB;CAC/C,MAAM,UAAU,WAAW,QAAQ,CAAC,CACjC,OAAO,gBAAgB,YAAY,CAAC,CAAC,CACrC,OAAO,KAAK,CAAC,CACb,MAAM,GAAG,EAAE;CACd,OAAO,+BAA+B,MAAM;EAAE,GAAG;EAAO;CAAQ,CAAC;AACnE;;;;;;;;AASA,SAAgB,eAAe,OAAuC;CAEpE,OAAO,YADI,WAAW,QAAQ,CAAC,CAAC,OAAO,gBAAgB,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAClE;AACtB"}
|
package/package.json
CHANGED
|
@@ -416,7 +416,7 @@ A child project inherits the parent's host, API, auth, and plugins via the `exte
|
|
|
416
416
|
|
|
417
417
|
```json
|
|
418
418
|
{
|
|
419
|
-
"extends": "bos://dev.everything.near/
|
|
419
|
+
"extends": "bos://dev.everything.near/everything.dev",
|
|
420
420
|
"account": "myapp.near",
|
|
421
421
|
"domain": "myapp.everything.dev"
|
|
422
422
|
}
|
|
@@ -433,7 +433,7 @@ This inherits:
|
|
|
433
433
|
|
|
434
434
|
```bash
|
|
435
435
|
bos init myapp.everything.dev \
|
|
436
|
-
--extends dev.everything.near/
|
|
436
|
+
--extends dev.everything.near/everything.dev \
|
|
437
437
|
--overrides ui,plugins \
|
|
438
438
|
--plugins apps,template
|
|
439
439
|
```
|
|
@@ -470,6 +470,6 @@ See the `extends-config` skill for deep merge semantics, per-environment extends
|
|
|
470
470
|
| 401 `UNAUTHORIZED` "API key required" with `data: { authType: "apiKey", hint: "Provide a valid API key via x-api-key header" }` | the route is `requireApiKey(...)`-protected and your key was absent — usually `Authorization: Bearer` was used instead | switch to the `x-api-key` header |
|
|
471
471
|
| 403 `FORBIDDEN` "API key lacks permission: <resource>:<action>" with `data: { requiredPermissions, keyPermissions }` | the key's `permissions` map does not cover every required resource/action — partial matches are rejected | re-create (server-side) or update the key with the needed permissions |
|
|
472
472
|
| 403 `FORBIDDEN` "Requires role: ..." / "Requires organization role: ..." | session role gate | sign in with an account holding the role; a key cannot satisfy these |
|
|
473
|
-
| 403 `FORBIDDEN` "Organization requires platform-admin approval" |
|
|
473
|
+
| 403 `FORBIDDEN` "Organization requires platform-admin approval" | a downstream app still runs its own approval middleware (this platform ships none) | app-level policy — surface the org's approval state to the user |
|
|
474
474
|
| `authClient.apiKey.list` / `update` return only `prefix` and metadata | expected — the full secret is returned once at `create` | create a new key; the old secret is unrecoverable |
|
|
475
475
|
| Browser request blocked by CORS despite a valid key | session cookies not sent (`credentials: "include"` missing) or origin not allowed by `CORS_ORIGIN` | add `credentials: "include"` to the fetch wrapper, or fix `CORS_ORIGIN` |
|
|
@@ -34,13 +34,16 @@ export function uiEntryFilename(input: { isBuild: boolean; server?: boolean }):
|
|
|
34
34
|
const HASH_SEGMENT_PATTERN = /\.[a-f0-9]{8,}\./;
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Serving classification for bundle objects: anything
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* Serving classification for bundle objects: anything under a version
|
|
38
|
+
* directory (`versions/<dirId>/…`, #826) or a `versions/<id>.json` manifest
|
|
39
|
+
* is immutable by construction — the directory name is content-addressed and
|
|
40
|
+
* the upload service refuses conflicting writes under `versions/`; likewise
|
|
41
|
+
* any name carrying a content-hash segment (including hashed entrypoints
|
|
42
|
+
* like `remoteEntry.8f3a….js`). Fixed-name entrypoints and non-hashed files
|
|
43
|
+
* a redeploy replaces in place must revalidate.
|
|
42
44
|
*/
|
|
43
45
|
export function isImmutableBundlePath(name: string): boolean {
|
|
46
|
+
if (name === "versions" || name.startsWith("versions/")) return true;
|
|
44
47
|
const base = name.split("/").pop() ?? name;
|
|
45
48
|
return HASH_SEGMENT_PATTERN.test(base);
|
|
46
49
|
}
|
package/src/build/ui/stubs.ts
CHANGED
|
@@ -65,19 +65,14 @@ export type {
|
|
|
65
65
|
ConstructedTree,
|
|
66
66
|
ConstructInput,
|
|
67
67
|
ConstructPluginRef,
|
|
68
|
-
GateUser,
|
|
69
|
-
HostContext,
|
|
70
68
|
NavManifest,
|
|
71
69
|
PluginManifest,
|
|
72
70
|
ResolvedPlugin,
|
|
73
71
|
RouteConfigModule,
|
|
74
72
|
RouteOptionsBundle,
|
|
75
73
|
} from "everything-dev/ui/manifest";
|
|
76
|
-
export {
|
|
77
|
-
|
|
78
|
-
ComposePayloadSchema,
|
|
79
|
-
constructTree,
|
|
80
|
-
} from "everything-dev/ui/manifest";
|
|
74
|
+
export { CORE_UI_PLUGIN_KEY, ComposePayloadSchema } from "everything-dev/ui/manifest";
|
|
75
|
+
export { constructTree } from "everything-dev/ui/compose";
|
|
81
76
|
`,
|
|
82
77
|
"src/globals.gen.ts": `// GENERATED FILE — do not edit. Regenerated from the installed framework version
|
|
83
78
|
// by \`bos dev\` / \`bos build\` / \`bos typecheck\` (gitignored).
|