@micacms/runtime 0.5.0-rc.2 → 0.6.0-rc.1
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 -1
- package/dist/app.js +1 -1
- package/dist/app.js.map +1 -1
- package/dist/config.d.ts +13 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -9
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +9 -3
- package/dist/context.js.map +1 -1
- package/dist/http.d.ts +1 -3
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +2 -9
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/lib/errors.d.ts +1 -2
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +0 -2
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/id.d.ts +0 -10
- package/dist/lib/id.d.ts.map +1 -1
- package/dist/lib/id.js +0 -50
- package/dist/lib/id.js.map +1 -1
- package/dist/lib/rate-limit.d.ts +0 -2
- package/dist/lib/rate-limit.d.ts.map +1 -1
- package/dist/lib/rate-limit.js +0 -3
- package/dist/lib/rate-limit.js.map +1 -1
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +2 -2
- package/dist/middleware/index.js.map +1 -1
- package/dist/routes/admin-content.d.ts.map +1 -1
- package/dist/routes/admin-content.js +4 -3
- package/dist/routes/admin-content.js.map +1 -1
- package/dist/routes/admin-media.d.ts.map +1 -1
- package/dist/routes/admin-media.js +6 -1
- package/dist/routes/admin-media.js.map +1 -1
- package/dist/routes/admin-system.d.ts.map +1 -1
- package/dist/routes/admin-system.js +6 -4
- package/dist/routes/admin-system.js.map +1 -1
- package/dist/schema.d.ts +15 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +63 -10
- package/dist/schema.js.map +1 -1
- package/dist/services/changes.d.ts.map +1 -1
- package/dist/services/changes.js +89 -13
- package/dist/services/changes.js.map +1 -1
- package/dist/services/content-store.d.ts +6 -1
- package/dist/services/content-store.d.ts.map +1 -1
- package/dist/services/content-store.js +25 -15
- package/dist/services/content-store.js.map +1 -1
- package/dist/services/staging.d.ts +2 -0
- package/dist/services/staging.d.ts.map +1 -1
- package/dist/services/staging.js +15 -4
- package/dist/services/staging.js.map +1 -1
- package/dist/workspace/tree.d.ts +6 -0
- package/dist/workspace/tree.d.ts.map +1 -1
- package/dist/workspace/tree.js +28 -0
- package/dist/workspace/tree.js.map +1 -1
- package/package.json +12 -4
- package/src/app.ts +127 -0
- package/src/config.ts +113 -0
- package/src/context.ts +167 -0
- package/src/http.ts +46 -0
- package/src/index.ts +141 -0
- package/src/lib/errors.ts +84 -0
- package/src/lib/file-type.ts +108 -0
- package/src/lib/id.ts +6 -0
- package/src/lib/logger.ts +87 -0
- package/src/lib/ports.ts +17 -0
- package/src/lib/rate-limit.ts +72 -0
- package/src/lib/schema-stats.ts +183 -0
- package/src/lib/version.ts +29 -0
- package/src/middleware/index.ts +234 -0
- package/src/routes/admin-content.ts +197 -0
- package/src/routes/admin-media.ts +96 -0
- package/src/routes/admin-spa.ts +82 -0
- package/src/routes/admin-staging.ts +126 -0
- package/src/routes/admin-system.ts +146 -0
- package/src/routes/health.ts +67 -0
- package/src/schema.ts +217 -0
- package/src/server.ts +68 -0
- package/src/services/changes.ts +605 -0
- package/src/services/content-store.ts +492 -0
- package/src/services/media.ts +229 -0
- package/src/services/staging.ts +330 -0
- package/src/workspace/developer.ts +46 -0
- package/src/workspace/files.ts +243 -0
- package/src/workspace/git.ts +108 -0
- package/src/workspace/locks.ts +103 -0
- package/src/workspace/policy.ts +77 -0
- package/src/workspace/tree.ts +314 -0
package/dist/schema.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* The Astro project writes it. The manifest is untrusted input (another core
|
|
4
4
|
* version, hand-edited or truncated), so it is validated again here.
|
|
5
5
|
*/
|
|
6
|
-
import { checkCompatibility, defineSite, isFieldKind, isProtocolManifest, MANIFEST_PATH, PROTOCOL_VERSION, } from "@micacms/core";
|
|
6
|
+
import { checkCompatibility, defineSite, deriveLayout, entryLayout, LayoutError, misplacedFiles, recordLayout, isFieldKind, isProtocolManifest, MANIFEST_PATH, PROTOCOL_VERSION, } from "@micacms/core";
|
|
7
7
|
export class SchemaError extends Error {
|
|
8
8
|
name = "SchemaError";
|
|
9
9
|
}
|
|
@@ -39,6 +39,24 @@ export async function loadSchema(source) {
|
|
|
39
39
|
}
|
|
40
40
|
return checkSchema(raw, origin);
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* One message for each collection or singleton with entries in the other
|
|
44
|
+
* layout. Mica refuses to start on any: editing next to hidden entries would
|
|
45
|
+
* let a save create a second copy of a page.
|
|
46
|
+
*/
|
|
47
|
+
export async function layoutProblems(source, site) {
|
|
48
|
+
const problems = [];
|
|
49
|
+
const parts = [
|
|
50
|
+
...Object.entries(site.collections).map(([name, def]) => [name, def.fields, "collection"]),
|
|
51
|
+
...Object.entries(site.singletons).map(([name, def]) => [name, def.fields, "singleton"]),
|
|
52
|
+
];
|
|
53
|
+
for (const [name, fields, kind] of parts) {
|
|
54
|
+
const paths = await misplacedFiles(source, fields, name, kind);
|
|
55
|
+
if (paths.length > 0)
|
|
56
|
+
problems.push(new LayoutError(paths, name, entryLayout(fields)).message);
|
|
57
|
+
}
|
|
58
|
+
return problems;
|
|
59
|
+
}
|
|
42
60
|
function describeMissing(origin) {
|
|
43
61
|
return (`No content model at ${origin}.\n` +
|
|
44
62
|
` Mica ships without one — the Astro project defines it and writes it here.\n` +
|
|
@@ -46,9 +64,8 @@ function describeMissing(origin) {
|
|
|
46
64
|
` then commit ${MANIFEST_PATH} and push it to the branch Mica is reading.`);
|
|
47
65
|
}
|
|
48
66
|
/**
|
|
49
|
-
* Runs the `defineSite` checks
|
|
50
|
-
*
|
|
51
|
-
* startup error, not a later failure on an editor's screen.
|
|
67
|
+
* Runs the `defineSite` checks on untrusted input. A newer `protocolVersion` is
|
|
68
|
+
* refused; an unknown field kind is not, and `readOnlyParts` names what it locks.
|
|
52
69
|
*/
|
|
53
70
|
export function checkSchema(raw, origin = "the schema") {
|
|
54
71
|
if (raw === null || typeof raw !== "object") {
|
|
@@ -86,7 +103,14 @@ export function checkSchema(raw, origin = "the schema") {
|
|
|
86
103
|
delete def.defaultSort;
|
|
87
104
|
}
|
|
88
105
|
try {
|
|
89
|
-
|
|
106
|
+
const site = defineSite({ collections, singletons });
|
|
107
|
+
// Read and classify entries where the manifest says they are, not where fields this build may not know imply.
|
|
108
|
+
for (const def of [...Object.values(site.collections), ...Object.values(site.singletons)]) {
|
|
109
|
+
const stored = def.layout;
|
|
110
|
+
if (stored === "file" || stored === "directory")
|
|
111
|
+
recordLayout(def.fields, stored);
|
|
112
|
+
}
|
|
113
|
+
return site;
|
|
90
114
|
}
|
|
91
115
|
catch (error) {
|
|
92
116
|
throw new SchemaError(`${origin} is not a valid content model.\n ${error.message}`);
|
|
@@ -101,16 +125,45 @@ function assertFieldMap(fields, at, origin) {
|
|
|
101
125
|
if (field === null || typeof field !== "object") {
|
|
102
126
|
throw new SchemaError(`${origin}: field "${here}" is not an object.`);
|
|
103
127
|
}
|
|
104
|
-
if (!isFieldKind(field.kind)) {
|
|
105
|
-
throw new SchemaError(`${origin}: field "${here}" has unknown kind "${String(field.kind)}".\n` +
|
|
106
|
-
` This build of Mica does not support it — the project may have been built\n` +
|
|
107
|
-
` against a newer @micacms/astro than the server is running.`);
|
|
108
|
-
}
|
|
109
128
|
if (field.kind === "object" || field.kind === "repeater") {
|
|
110
129
|
assertFieldMap(field.fields, here, origin);
|
|
111
130
|
}
|
|
112
131
|
}
|
|
113
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* The collections and singletons this build of Mica must not write, keyed
|
|
135
|
+
* `collections.<name>` or `singletons.<name>`, with the reason for the editor.
|
|
136
|
+
* A field kind from a newer @micacms/astro makes only its own collection or
|
|
137
|
+
* singleton read-only; Mica still starts, and the rest stays editable.
|
|
138
|
+
*/
|
|
139
|
+
export function readOnlyParts(site) {
|
|
140
|
+
const parts = new Map();
|
|
141
|
+
const add = (key, def) => {
|
|
142
|
+
const unknown = unknownKinds(def.fields, []);
|
|
143
|
+
const stored = def.layout;
|
|
144
|
+
if (unknown.length > 0) {
|
|
145
|
+
parts.set(key, `"${def.label}" uses ${unknown.map((field) => `"${field.path}" (${field.kind})`).join(", ")}, ` +
|
|
146
|
+
"a field type that this version of Mica does not know. Upgrade Mica to edit it.");
|
|
147
|
+
}
|
|
148
|
+
else if (stored !== undefined && stored !== deriveLayout(def.fields)) {
|
|
149
|
+
parts.set(key, `"${def.label}" is stored as ${String(stored)}, which this version of Mica does not expect. ` +
|
|
150
|
+
"Upgrade Mica to edit it.");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
for (const [name, def] of Object.entries(site.collections))
|
|
154
|
+
add(`collections.${name}`, def);
|
|
155
|
+
for (const [name, def] of Object.entries(site.singletons))
|
|
156
|
+
add(`singletons.${name}`, def);
|
|
157
|
+
return parts;
|
|
158
|
+
}
|
|
159
|
+
function unknownKinds(fields, at) {
|
|
160
|
+
return Object.entries(fields).flatMap(([key, field]) => {
|
|
161
|
+
const here = [...at, key];
|
|
162
|
+
if (!isFieldKind(field.kind))
|
|
163
|
+
return [{ path: here.join("."), kind: String(field.kind) }];
|
|
164
|
+
return field.kind === "object" || field.kind === "repeater" ? unknownKinds(field.fields, here) : [];
|
|
165
|
+
});
|
|
166
|
+
}
|
|
114
167
|
/**
|
|
115
168
|
* A short digest of a model, to show drift. Field order counts, because it is
|
|
116
169
|
* the order of the admin form.
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reads the content model from `.mica/schema.json` in the content repository.\n * The Astro project writes it. The manifest is untrusted input (another core\n * version, hand-edited or truncated), so it is validated again here.\n */\n\nimport {\n checkCompatibility,\n defineSite,\n isFieldKind,\n isProtocolManifest,\n MANIFEST_PATH,\n PROTOCOL_VERSION,\n type Collection,\n type ContentSource,\n type FieldMap,\n type Singleton,\n type Site,\n} from \"@micacms/core\";\n\nexport class SchemaError extends Error {\n override name = \"SchemaError\";\n}\n\n/** Where a model came from, for logs and the system screen. */\nexport function schemaOrigin(source: ContentSource): string {\n return `${source.origin}/${MANIFEST_PATH}`;\n}\n\n/**\n * Reads the model from the content repository. A missing file gets a long\n * message because a first deployment hits it.\n */\nexport async function loadSchema(source: ContentSource): Promise<Site> {\n const origin = schemaOrigin(source);\n\n let text: string | null;\n try {\n text = await source.read(MANIFEST_PATH);\n } catch (error) {\n // An unreachable repository is not a bad model; the fixes are different.\n throw new SchemaError(\n `Could not read the content model from ${source.origin}.\\n` +\n ` ${(error as Error).message}\\n` +\n ` The repository has to be reachable before Mica can start: the model\\n` +\n ` lives in it, at ${MANIFEST_PATH}.`,\n );\n }\n\n if (text === null) throw new SchemaError(describeMissing(origin));\n\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error) {\n throw new SchemaError(`${origin} is not valid JSON: ${(error as Error).message}`);\n }\n\n return checkSchema(raw, origin);\n}\n\nfunction describeMissing(origin: string): string {\n return (\n `No content model at ${origin}.\\n` +\n ` Mica ships without one — the Astro project defines it and writes it here.\\n` +\n ` In the project: npx astro build (or start the dev server once)\\n` +\n ` then commit ${MANIFEST_PATH} and push it to the branch Mica is reading.`\n );\n}\n\n/**\n * Runs the `defineSite` checks, and also refuses an unknown field `kind`, which\n * `defineSite` does not check. This turns a core version mismatch into a\n * startup error, not a later failure on an editor's screen.\n */\nexport function checkSchema(raw: unknown, origin = \"the schema\"): Site {\n if (raw === null || typeof raw !== \"object\") {\n throw new SchemaError(`${origin} is ${raw === null ? \"null\" : typeof raw}, expected an object.`);\n }\n\n // A bare `{ collections }` without the protocol envelope is also accepted.\n if (isProtocolManifest(raw)) {\n const compatibility = checkCompatibility(raw);\n if (!compatibility.ok && compatibility.reason === \"newer\") {\n throw new SchemaError(\n `${origin} follows protocol version ${compatibility.version}; this build of Mica ` +\n `understands version ${PROTOCOL_VERSION}.\\n` +\n ` The project was built against a newer @micacms/astro. Upgrade Mica.`,\n );\n }\n }\n\n const candidate = raw as { collections?: unknown; singletons?: unknown };\n\n if (candidate.collections === null || typeof candidate.collections !== \"object\") {\n throw new SchemaError(`${origin} has no \\`collections\\`. Expected defineSite({ collections: { ... } }).`);\n }\n\n const rawSingletons = candidate.singletons;\n if (rawSingletons !== undefined && (rawSingletons === null || typeof rawSingletons !== \"object\")) {\n throw new SchemaError(`${origin} has a \\`singletons\\` that is not an object.`);\n }\n\n const collections = candidate.collections as Record<string, Collection>;\n const singletons = (rawSingletons ?? {}) as Record<string, Singleton>;\n\n for (const [name, def] of Object.entries(collections)) {\n assertFieldMap(def?.fields, `collections.${name}`, origin);\n }\n for (const [name, def] of Object.entries(singletons)) {\n assertFieldMap(def?.fields, `singletons.${name}`, origin);\n }\n\n // Drop, do not refuse, a default sort on an undeclared field: only the list order is affected.\n for (const def of Object.values(collections)) {\n const field = def?.defaultSort?.field;\n if (field && field !== \"position\" && !def.fields?.[field]) delete def.defaultSort;\n }\n\n try {\n return defineSite({ collections, singletons });\n } catch (error) {\n throw new SchemaError(`${origin} is not a valid content model.\\n ${(error as Error).message}`);\n }\n}\n\nfunction assertFieldMap(fields: unknown, at: string, origin: string): void {\n if (fields === null || typeof fields !== \"object\") {\n throw new SchemaError(`${origin}: \"${at}\" has no \\`fields\\` object.`);\n }\n\n for (const [key, field] of Object.entries(fields as FieldMap)) {\n const here = `${at}.${key}`;\n\n if (field === null || typeof field !== \"object\") {\n throw new SchemaError(`${origin}: field \"${here}\" is not an object.`);\n }\n if (!isFieldKind(field.kind)) {\n throw new SchemaError(\n `${origin}: field \"${here}\" has unknown kind \"${String(field.kind)}\".\\n` +\n ` This build of Mica does not support it — the project may have been built\\n` +\n ` against a newer @micacms/astro than the server is running.`,\n );\n }\n if (field.kind === \"object\" || field.kind === \"repeater\") {\n assertFieldMap(field.fields, here, origin);\n }\n }\n}\n\n/**\n * A short digest of a model, to show drift. Field order counts, because it is\n * the order of the admin form.\n */\nexport async function schemaFingerprint(site: Site): Promise<string> {\n const { createHash } = await import(\"node:crypto\");\n return createHash(\"sha256\").update(JSON.stringify(site)).digest(\"hex\").slice(0, 12);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Reads the content model from `.mica/schema.json` in the content repository.\n * The Astro project writes it. The manifest is untrusted input (another core\n * version, hand-edited or truncated), so it is validated again here.\n */\n\nimport {\n checkCompatibility,\n defineSite,\n deriveLayout,\n entryLayout,\n LayoutError,\n misplacedFiles,\n recordLayout,\n isFieldKind,\n isProtocolManifest,\n MANIFEST_PATH,\n PROTOCOL_VERSION,\n type Collection,\n type ContentSource,\n type FieldMap,\n type Singleton,\n type Site,\n} from \"@micacms/core\";\n\nexport class SchemaError extends Error {\n override name = \"SchemaError\";\n}\n\n/** Where a model came from, for logs and the system screen. */\nexport function schemaOrigin(source: ContentSource): string {\n return `${source.origin}/${MANIFEST_PATH}`;\n}\n\n/**\n * Reads the model from the content repository. A missing file gets a long\n * message because a first deployment hits it.\n */\nexport async function loadSchema(source: ContentSource): Promise<Site> {\n const origin = schemaOrigin(source);\n\n let text: string | null;\n try {\n text = await source.read(MANIFEST_PATH);\n } catch (error) {\n // An unreachable repository is not a bad model; the fixes are different.\n throw new SchemaError(\n `Could not read the content model from ${source.origin}.\\n` +\n ` ${(error as Error).message}\\n` +\n ` The repository has to be reachable before Mica can start: the model\\n` +\n ` lives in it, at ${MANIFEST_PATH}.`,\n );\n }\n\n if (text === null) throw new SchemaError(describeMissing(origin));\n\n let raw: unknown;\n try {\n raw = JSON.parse(text);\n } catch (error) {\n throw new SchemaError(`${origin} is not valid JSON: ${(error as Error).message}`);\n }\n\n return checkSchema(raw, origin);\n}\n\n/**\n * One message for each collection or singleton with entries in the other\n * layout. Mica refuses to start on any: editing next to hidden entries would\n * let a save create a second copy of a page.\n */\nexport async function layoutProblems(source: ContentSource, site: Site): Promise<string[]> {\n const problems: string[] = [];\n const parts = [\n ...Object.entries(site.collections).map(([name, def]) => [name, def.fields, \"collection\"] as const),\n ...Object.entries(site.singletons).map(([name, def]) => [name, def.fields, \"singleton\"] as const),\n ];\n for (const [name, fields, kind] of parts) {\n const paths = await misplacedFiles(source, fields, name, kind);\n if (paths.length > 0) problems.push(new LayoutError(paths, name, entryLayout(fields)).message);\n }\n return problems;\n}\n\nfunction describeMissing(origin: string): string {\n return (\n `No content model at ${origin}.\\n` +\n ` Mica ships without one — the Astro project defines it and writes it here.\\n` +\n ` In the project: npx astro build (or start the dev server once)\\n` +\n ` then commit ${MANIFEST_PATH} and push it to the branch Mica is reading.`\n );\n}\n\n/**\n * Runs the `defineSite` checks on untrusted input. A newer `protocolVersion` is\n * refused; an unknown field kind is not, and `readOnlyParts` names what it locks.\n */\nexport function checkSchema(raw: unknown, origin = \"the schema\"): Site {\n if (raw === null || typeof raw !== \"object\") {\n throw new SchemaError(`${origin} is ${raw === null ? \"null\" : typeof raw}, expected an object.`);\n }\n\n // A bare `{ collections }` without the protocol envelope is also accepted.\n if (isProtocolManifest(raw)) {\n const compatibility = checkCompatibility(raw);\n if (!compatibility.ok && compatibility.reason === \"newer\") {\n throw new SchemaError(\n `${origin} follows protocol version ${compatibility.version}; this build of Mica ` +\n `understands version ${PROTOCOL_VERSION}.\\n` +\n ` The project was built against a newer @micacms/astro. Upgrade Mica.`,\n );\n }\n }\n\n const candidate = raw as { collections?: unknown; singletons?: unknown };\n\n if (candidate.collections === null || typeof candidate.collections !== \"object\") {\n throw new SchemaError(`${origin} has no \\`collections\\`. Expected defineSite({ collections: { ... } }).`);\n }\n\n const rawSingletons = candidate.singletons;\n if (rawSingletons !== undefined && (rawSingletons === null || typeof rawSingletons !== \"object\")) {\n throw new SchemaError(`${origin} has a \\`singletons\\` that is not an object.`);\n }\n\n const collections = candidate.collections as Record<string, Collection>;\n const singletons = (rawSingletons ?? {}) as Record<string, Singleton>;\n\n for (const [name, def] of Object.entries(collections)) {\n assertFieldMap(def?.fields, `collections.${name}`, origin);\n }\n for (const [name, def] of Object.entries(singletons)) {\n assertFieldMap(def?.fields, `singletons.${name}`, origin);\n }\n\n // Drop, do not refuse, a default sort on an undeclared field: only the list order is affected.\n for (const def of Object.values(collections)) {\n const field = def?.defaultSort?.field;\n if (field && field !== \"position\" && !def.fields?.[field]) delete def.defaultSort;\n }\n\n try {\n const site = defineSite({ collections, singletons });\n // Read and classify entries where the manifest says they are, not where fields this build may not know imply.\n for (const def of [...Object.values(site.collections), ...Object.values(site.singletons)]) {\n const stored = (def as { layout?: unknown }).layout;\n if (stored === \"file\" || stored === \"directory\") recordLayout(def.fields, stored);\n }\n return site;\n } catch (error) {\n throw new SchemaError(`${origin} is not a valid content model.\\n ${(error as Error).message}`);\n }\n}\n\nfunction assertFieldMap(fields: unknown, at: string, origin: string): void {\n if (fields === null || typeof fields !== \"object\") {\n throw new SchemaError(`${origin}: \"${at}\" has no \\`fields\\` object.`);\n }\n\n for (const [key, field] of Object.entries(fields as FieldMap)) {\n const here = `${at}.${key}`;\n\n if (field === null || typeof field !== \"object\") {\n throw new SchemaError(`${origin}: field \"${here}\" is not an object.`);\n }\n if (field.kind === \"object\" || field.kind === \"repeater\") {\n assertFieldMap(field.fields, here, origin);\n }\n }\n}\n\n/**\n * The collections and singletons this build of Mica must not write, keyed\n * `collections.<name>` or `singletons.<name>`, with the reason for the editor.\n * A field kind from a newer @micacms/astro makes only its own collection or\n * singleton read-only; Mica still starts, and the rest stays editable.\n */\nexport function readOnlyParts(site: Site): Map<string, string> {\n const parts = new Map<string, string>();\n const add = (key: string, def: Collection | Singleton) => {\n const unknown = unknownKinds(def.fields, []);\n const stored = (def as { layout?: unknown }).layout;\n if (unknown.length > 0) {\n parts.set(\n key,\n `\"${def.label}\" uses ${unknown.map((field) => `\"${field.path}\" (${field.kind})`).join(\", \")}, ` +\n \"a field type that this version of Mica does not know. Upgrade Mica to edit it.\",\n );\n } else if (stored !== undefined && stored !== deriveLayout(def.fields)) {\n parts.set(\n key,\n `\"${def.label}\" is stored as ${String(stored)}, which this version of Mica does not expect. ` +\n \"Upgrade Mica to edit it.\",\n );\n }\n };\n for (const [name, def] of Object.entries(site.collections)) add(`collections.${name}`, def);\n for (const [name, def] of Object.entries(site.singletons)) add(`singletons.${name}`, def);\n return parts;\n}\n\nfunction unknownKinds(fields: FieldMap, at: string[]): Array<{ path: string; kind: string }> {\n return Object.entries(fields).flatMap(([key, field]) => {\n const here = [...at, key];\n if (!isFieldKind(field.kind)) return [{ path: here.join(\".\"), kind: String(field.kind) }];\n return field.kind === \"object\" || field.kind === \"repeater\" ? unknownKinds(field.fields, here) : [];\n });\n}\n\n/**\n * A short digest of a model, to show drift. Field order counts, because it is\n * the order of the admin form.\n */\nexport async function schemaFingerprint(site: Site): Promise<string> {\n const { createHash } = await import(\"node:crypto\");\n return createHash(\"sha256\").update(JSON.stringify(site)).digest(\"hex\").slice(0, 12);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/services/changes.ts"],"names":[],"mappings":"AAAA,mGAAmG;AAEnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,
|
|
1
|
+
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/services/changes.ts"],"names":[],"mappings":"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"}
|
package/dist/services/changes.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/** The staging API types, the before/after comparison and the analysis of a pending change set. */
|
|
2
|
-
import { ORDER_FILE, collectionEntryPath, entryLayout, entryTitle, isValidSlug, readCollection, readCollectionEntry, readSingleton, relativizeMedia, resolveSlugField, singletonPath, validateFields, } from "@micacms/core";
|
|
2
|
+
import { LayoutError, ORDER_FILE, collectReferences, collectionEntryPath, createReferenceCheck, entryLayout, entryTitle, isValidSlug, readCollection, readCollectionEntry, readSingleton, relativizeMedia, resolveSlugField, singletonPath, validateFields, } from "@micacms/core";
|
|
3
3
|
import { badRequest } from "../lib/errors.js";
|
|
4
4
|
/**
|
|
5
5
|
* Reads one document from `published` and from `staged`. An order target
|
|
6
6
|
* becomes `{ order: [{ slug, title }] }` on each side, in position order.
|
|
7
7
|
*/
|
|
8
8
|
export async function compareSources(model, published, staged, target) {
|
|
9
|
+
// During a layout migration HEAD still has the old layout: then there is no "before".
|
|
10
|
+
const before = (read) => read().catch((error) => (error instanceof LayoutError ? null : Promise.reject(error)));
|
|
9
11
|
if (target.kind === "singleton") {
|
|
10
12
|
const def = model.singletons[target.name];
|
|
11
13
|
if (!def)
|
|
12
14
|
throw badRequest(`There is no singleton called "${target.name}".`);
|
|
13
15
|
return {
|
|
14
|
-
before: await readSingleton(published, def, target.name),
|
|
16
|
+
before: await before(() => readSingleton(published, def, target.name)),
|
|
15
17
|
after: await readSingleton(staged, def, target.name),
|
|
16
18
|
};
|
|
17
19
|
}
|
|
@@ -24,14 +26,14 @@ export async function compareSources(model, published, staged, target) {
|
|
|
24
26
|
if (!isValidSlug(slug))
|
|
25
27
|
throw badRequest(`"${slug}" is not a slug.`);
|
|
26
28
|
}
|
|
27
|
-
const
|
|
29
|
+
const old = await readCollectionEntry(published, def, target.collection, from);
|
|
28
30
|
const after = await readCollectionEntry(staged, def, target.collection, target.slug);
|
|
29
31
|
if (from === target.slug)
|
|
30
|
-
return { before, after };
|
|
32
|
+
return { before: old, after };
|
|
31
33
|
// A rename: compare the on-disk form, where colocated media paths do not
|
|
32
34
|
// name the entry's folder.
|
|
33
35
|
return {
|
|
34
|
-
before:
|
|
36
|
+
before: old && onDisk(def, target.collection, from, old),
|
|
35
37
|
after: after && onDisk(def, target.collection, target.slug, after),
|
|
36
38
|
};
|
|
37
39
|
}
|
|
@@ -43,7 +45,7 @@ export async function compareSources(model, published, staged, target) {
|
|
|
43
45
|
order: entries.map((entry) => ({ slug: entry.slug, title: entryTitle(def, entry.data) })),
|
|
44
46
|
};
|
|
45
47
|
};
|
|
46
|
-
return { before: await ordering(published), after: await ordering(staged) };
|
|
48
|
+
return { before: await before(() => ordering(published)), after: await ordering(staged) };
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
51
|
* Annotates a change set with owners and validates the entries it touches, in
|
|
@@ -86,6 +88,10 @@ published) {
|
|
|
86
88
|
await detectRenames(model, published, source, changes, owners, entriesByCollection);
|
|
87
89
|
}
|
|
88
90
|
const validated = new Set();
|
|
91
|
+
const targets = createReferenceCheck(model, source, (name) => {
|
|
92
|
+
const entries = entriesByCollection.get(name);
|
|
93
|
+
return entries ? new Set(entries.keys()) : undefined;
|
|
94
|
+
});
|
|
89
95
|
for (const owner of owners) {
|
|
90
96
|
if (owner.kind === "entry") {
|
|
91
97
|
const key = `${owner.collection}/${owner.slug}`;
|
|
@@ -97,12 +103,13 @@ published) {
|
|
|
97
103
|
const data = entriesByCollection.get(owner.collection)?.get(owner.slug);
|
|
98
104
|
if (!data)
|
|
99
105
|
continue;
|
|
100
|
-
const result = validateFields(def.fields, data
|
|
101
|
-
|
|
106
|
+
const result = validateFields(def.fields, data);
|
|
107
|
+
const issues = [...(result.ok ? [] : result.issues), ...(await targets.issues(def.fields, data))];
|
|
108
|
+
if (issues.length > 0) {
|
|
102
109
|
problems.push({
|
|
103
110
|
path: entryDataPath(def, owner.collection, owner.slug),
|
|
104
111
|
label: labelEntry(def, owner.slug, titles.get(key)),
|
|
105
|
-
issues
|
|
112
|
+
issues,
|
|
106
113
|
});
|
|
107
114
|
}
|
|
108
115
|
}
|
|
@@ -114,10 +121,10 @@ published) {
|
|
|
114
121
|
const data = await readSingleton(source, def, owner.name);
|
|
115
122
|
if (data === null)
|
|
116
123
|
continue;
|
|
117
|
-
const result = validateFields(def.fields, data
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
const result = validateFields(def.fields, data);
|
|
125
|
+
const issues = [...(result.ok ? [] : result.issues), ...(await targets.issues(def.fields, data))];
|
|
126
|
+
if (issues.length > 0)
|
|
127
|
+
problems.push({ path: dataPath, label: def.label, issues });
|
|
121
128
|
}
|
|
122
129
|
catch (error) {
|
|
123
130
|
problems.push({
|
|
@@ -128,6 +135,7 @@ published) {
|
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
}
|
|
138
|
+
problems.push(...(await brokenByRemoval(model, source, changes, owners, entriesByCollection, titles)));
|
|
131
139
|
const annotated = changes.map((change, index) => {
|
|
132
140
|
const owner = owners[index];
|
|
133
141
|
if (owner.kind === "entry") {
|
|
@@ -317,6 +325,74 @@ function canonical(value) {
|
|
|
317
325
|
return Object.fromEntries(Object.keys(record).sort().map((key) => [key, record[key]]));
|
|
318
326
|
});
|
|
319
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Entries and files that the change set removes while another entry still
|
|
330
|
+
* points at them. Publishing them would break a link or an image on the site.
|
|
331
|
+
*/
|
|
332
|
+
async function brokenByRemoval(model, source, changes, owners, entriesByCollection, titles) {
|
|
333
|
+
const removedEntries = new Map();
|
|
334
|
+
const removedFiles = new Set();
|
|
335
|
+
changes.forEach((change, index) => {
|
|
336
|
+
const owner = owners[index];
|
|
337
|
+
if (change.kind !== "delete")
|
|
338
|
+
return;
|
|
339
|
+
if (owner.kind === "entry") {
|
|
340
|
+
// A URL change removes the old slug: links to it break as with a delete.
|
|
341
|
+
const gone = owner.renamedFrom ?? owner.slug;
|
|
342
|
+
if (!entriesByCollection.get(owner.collection)?.has(gone)) {
|
|
343
|
+
const renamedTo = owner.renamedFrom ? owner.slug : undefined;
|
|
344
|
+
removedEntries.set(`${owner.collection}/${gone}`, { collection: owner.collection, slug: gone, ...(renamedTo ? { renamedTo } : {}) });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Only a media file can be a target; a cleared rich-text file or an order file cannot.
|
|
349
|
+
if (!change.path.endsWith(".json") && !change.path.endsWith(".mdoc"))
|
|
350
|
+
removedFiles.add(change.path);
|
|
351
|
+
});
|
|
352
|
+
if (removedEntries.size === 0 && removedFiles.size === 0)
|
|
353
|
+
return [];
|
|
354
|
+
const linkedFrom = new Map();
|
|
355
|
+
const note = (target, from) => linkedFrom.set(target, [...(linkedFrom.get(target) ?? []), from]);
|
|
356
|
+
const scan = (label, fields, data) => {
|
|
357
|
+
for (const reference of collectReferences(fields, data)) {
|
|
358
|
+
const target = reference.kind === "relation" ? `${reference.collection}/${reference.slug}` : reference.path;
|
|
359
|
+
if (removedEntries.has(target) || removedFiles.has(target))
|
|
360
|
+
note(target, label);
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
for (const [name, def] of Object.entries(model.collections)) {
|
|
364
|
+
let entries = entriesByCollection.get(name);
|
|
365
|
+
if (!entries) {
|
|
366
|
+
try {
|
|
367
|
+
entries = new Map((await readCollection(source, def, name)).map((entry) => [entry.slug, entry.data]));
|
|
368
|
+
}
|
|
369
|
+
catch {
|
|
370
|
+
continue; // An unreadable collection is reported where it is changed.
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const [slug, data] of entries)
|
|
374
|
+
scan(labelEntry(def, slug, entryTitle(def, data)), def.fields, data);
|
|
375
|
+
}
|
|
376
|
+
for (const [name, def] of Object.entries(model.singletons)) {
|
|
377
|
+
const data = await readSingleton(source, def, name).catch(() => null);
|
|
378
|
+
if (data)
|
|
379
|
+
scan(def.label, def.fields, data);
|
|
380
|
+
}
|
|
381
|
+
const problems = [];
|
|
382
|
+
for (const [target, from] of linkedFrom) {
|
|
383
|
+
const entry = removedEntries.get(target);
|
|
384
|
+
const def = entry ? model.collections[entry.collection] : null;
|
|
385
|
+
const message = entry?.renamedTo
|
|
386
|
+
? `The URL changes from "${entry.slug}" to "${entry.renamedTo}", and ${from.join(", ")} still link to "${entry.slug}". Change those links first, or publish them together.`
|
|
387
|
+
: `Still linked from ${from.join(", ")}. Remove those links first, or publish them together.`;
|
|
388
|
+
problems.push({
|
|
389
|
+
path: entry && def ? entryDataPath(def, entry.collection, entry.slug) : target,
|
|
390
|
+
label: entry && def ? labelEntry(def, entry.slug, titles.get(target)) : target,
|
|
391
|
+
issues: [{ path: "", message }],
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
return problems;
|
|
395
|
+
}
|
|
320
396
|
function labelEntry(def, slug, title) {
|
|
321
397
|
return `${def.labelSingular ?? def.label}: ${title ?? slug}`;
|
|
322
398
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/services/changes.ts"],"names":[],"mappings":"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","sourcesContent":["/** The staging API types, the before/after comparison and the analysis of a pending change set. */\n\nimport type { Author } from \"../workspace/files.js\";\nimport {\n ORDER_FILE,\n collectionEntryPath,\n entryLayout,\n entryTitle,\n isValidSlug,\n readCollection,\n readCollectionEntry,\n readSingleton,\n relativizeMedia,\n resolveSlugField,\n singletonPath,\n validateFields,\n type Collection,\n type ContentChange,\n type ContentSource,\n type FieldIssue,\n type PublishResult,\n type Site,\n} from \"@micacms/core\";\n\nimport { badRequest } from \"../lib/errors.js\";\nimport type { SyncStatus } from \"../workspace/policy.js\";\n\n/** Where the change happened, in words an editor recognises. */\nexport type ChangeOwner =\n | {\n kind: \"entry\";\n collection: string;\n slug: string;\n label: string;\n /** The slug before a pending rename. Set on the files of both slugs. */\n renamedFrom?: string;\n }\n | { kind: \"singleton\"; name: string; label: string }\n | { kind: \"order\"; collection: string; label: string }\n | { kind: \"media\"; label: string }\n | { kind: \"other\"; label: string };\n\nexport interface AnnotatedChange extends ContentChange {\n owner: ChangeOwner;\n /**\n * The file's revision, null for a deletion. Publish sends it back, so a file\n * saved after the review is not published unseen.\n */\n revision?: string | null;\n}\n\n/** One thing that would make the staged result unpublishable. */\nexport interface StagingProblem {\n /** The file the problem is about, relative to the content root. */\n path: string;\n label: string;\n issues: FieldIssue[];\n}\n\nexport interface StagingValidation {\n ok: boolean;\n problems: StagingProblem[];\n}\n\nexport interface StagingStatus {\n /** Whose working tree it is. The UI wording changes with it. */\n owner: \"developer\" | \"managed\";\n publish: { enabled: boolean; reason?: string };\n /** The commit the pending changes sit on. */\n head: string | null;\n /** The branch a publication lands on, where the workspace knows it. */\n branch: string | null;\n /** Commits created and not yet pushed. */\n ahead: number | null;\n changes: AnnotatedChange[];\n validation: StagingValidation;\n /** Where a clone that Mica keeps stands against its remote. Null for the developer's checkout. */\n sync: SyncStatus | null;\n}\n\nexport interface PublishInput {\n /** Subject line of the commit. */\n message: string;\n /**\n * The reviewed paths, with the revision each had in the review. Only these\n * are published; a file saved again since, or another file of the same entry,\n * makes Publish refuse.\n */\n paths: Record<string, string | null>;\n author?: Author;\n}\n\n/** What the System page shows about the repository. */\nexport interface RepositoryInfo {\n owner: \"developer\" | \"managed\";\n branch: string | null;\n head: string | null;\n /** The remote, for a clone that Mica keeps. */\n url: string | null;\n cloneBytes: number | null;\n sync: SyncStatus | null;\n}\n\nexport interface ContentStagingService {\n status(): Promise<StagingStatus>;\n publish(input: PublishInput): Promise<PublishResult>;\n discard(paths: string[]): Promise<{ discarded: string[] }>;\n /** Retries the push after `committed-pending-push`. */\n push(): Promise<PublishResult>;\n /**\n * A document before and after a pending change. `origin` compares against the\n * website's version, not the last commit.\n */\n compare(target: CompareTarget, against?: \"head\" | \"origin\"): Promise<Comparison>;\n /** Brings a clone that Mica keeps up to date now. Refused for the developer's checkout. */\n sync(): Promise<void>;\n resolve(group: string, choice: \"mine\" | \"website\"): Promise<void>;\n repository(): Promise<RepositoryInfo>;\n}\n\n/** What to compare on the Publish page. */\nexport type CompareTarget =\n /** `from` names the published slug of a renamed entry. */\n | { kind: \"entry\"; collection: string; slug: string; from?: string }\n | { kind: \"singleton\"; name: string }\n | { kind: \"order\"; collection: string };\n\n/**\n * Raw field data on both sides of a pending change. The editor renders the\n * comparison from the model.\n */\nexport interface Comparison {\n /** As the repository has it — null when the change creates the document. */\n before: Record<string, unknown> | null;\n /** As staged for publication — null when the change deletes it. */\n after: Record<string, unknown> | null;\n}\n\n/**\n * Reads one document from `published` and from `staged`. An order target\n * becomes `{ order: [{ slug, title }] }` on each side, in position order.\n */\nexport async function compareSources(\n model: Site,\n published: ContentSource,\n staged: ContentSource,\n target: CompareTarget,\n): Promise<Comparison> {\n if (target.kind === \"singleton\") {\n const def = model.singletons[target.name];\n if (!def) throw badRequest(`There is no singleton called \"${target.name}\".`);\n return {\n before: await readSingleton(published, def, target.name),\n after: await readSingleton(staged, def, target.name),\n };\n }\n\n const def = model.collections[target.collection];\n if (!def) throw badRequest(`There is no collection called \"${target.collection}\".`);\n\n if (target.kind === \"entry\") {\n const from = target.from ?? target.slug;\n for (const slug of [target.slug, from]) {\n if (!isValidSlug(slug)) throw badRequest(`\"${slug}\" is not a slug.`);\n }\n const before = await readCollectionEntry(published, def, target.collection, from);\n const after = await readCollectionEntry(staged, def, target.collection, target.slug);\n if (from === target.slug) return { before, after };\n\n // A rename: compare the on-disk form, where colocated media paths do not\n // name the entry's folder.\n return {\n before: before && onDisk(def, target.collection, from, before),\n after: after && onDisk(def, target.collection, target.slug, after),\n };\n }\n\n const ordering = async (source: ContentSource) => {\n const entries = await readCollection(source, def, target.collection);\n if (entries.length === 0) return null;\n return {\n order: entries.map((entry) => ({ slug: entry.slug, title: entryTitle(def, entry.data) })),\n };\n };\n\n return { before: await ordering(published), after: await ordering(staged) };\n}\n\n/**\n * Annotates a change set with owners and validates the entries it touches, in\n * one read of the affected collections. Only changed documents are validated: a\n * broken entry committed earlier must not block an unrelated publication.\n */\nexport async function analyseChanges(\n model: Site,\n source: ContentSource,\n changes: ContentChange[],\n /** The repository's side. When given, renamed entries are detected. */\n published?: ContentSource,\n): Promise<{ changes: AnnotatedChange[]; validation: StagingValidation }> {\n const owners = changes.map((change) => classifyPath(model, change.path));\n\n const collectionsToRead = new Set<string>();\n for (const owner of owners) {\n if (owner.kind === \"entry\" || owner.kind === \"order\") collectionsToRead.add(owner.collection);\n }\n\n const problems: StagingProblem[] = [];\n const titles = new Map<string, string>();\n const entriesByCollection = new Map<string, Map<string, Record<string, unknown>>>();\n\n for (const name of collectionsToRead) {\n const def = model.collections[name]!;\n try {\n const entries = await readCollection(source, def, name);\n const bySlug = new Map(entries.map((entry) => [entry.slug, entry.data]));\n entriesByCollection.set(name, bySlug);\n for (const [slug, data] of bySlug) titles.set(`${name}/${slug}`, entryTitle(def, data));\n } catch (error) {\n // An unreadable collection, possibly from an untouched file, is a problem\n // at the path it names.\n const path = (error as { path?: string }).path ?? name;\n problems.push({\n path,\n label: def.label,\n issues: [{ path: \"\", message: (error as Error).message }],\n });\n }\n }\n\n if (published) {\n await detectRenames(model, published, source, changes, owners, entriesByCollection);\n }\n\n const validated = new Set<string>();\n\n for (const owner of owners) {\n if (owner.kind === \"entry\") {\n const key = `${owner.collection}/${owner.slug}`;\n if (validated.has(key)) continue;\n validated.add(key);\n\n const def = model.collections[owner.collection]!;\n // Absent means the change set deletes this entry.\n const data = entriesByCollection.get(owner.collection)?.get(owner.slug);\n if (!data) continue;\n\n const result = validateFields(def.fields, data, \"storage\");\n if (!result.ok) {\n problems.push({\n path: entryDataPath(def, owner.collection, owner.slug),\n label: labelEntry(def, owner.slug, titles.get(key)),\n issues: result.issues,\n });\n }\n }\n\n if (owner.kind === \"singleton\" && !validated.has(`singleton:${owner.name}`)) {\n validated.add(`singleton:${owner.name}`);\n\n const def = model.singletons[owner.name]!;\n const dataPath = singletonPath(def.fields, owner.name);\n try {\n const data = await readSingleton(source, def, owner.name);\n if (data === null) continue;\n\n const result = validateFields(def.fields, data, \"storage\");\n if (!result.ok) {\n problems.push({ path: dataPath, label: def.label, issues: result.issues });\n }\n } catch (error) {\n problems.push({\n path: (error as { path?: string }).path ?? dataPath,\n label: def.label,\n issues: [{ path: \"\", message: (error as Error).message }],\n });\n }\n }\n }\n\n const annotated = changes.map((change, index): AnnotatedChange => {\n const owner = owners[index]!;\n if (owner.kind === \"entry\") {\n const def = model.collections[owner.collection]!;\n return {\n ...change,\n owner: { ...owner, label: labelEntry(def, owner.slug, titles.get(`${owner.collection}/${owner.slug}`)) },\n };\n }\n return { ...change, owner };\n });\n\n return {\n changes: annotated,\n validation: { ok: problems.length === 0, problems },\n };\n}\n\n/** Which piece of content a file belongs to, read from the model's path layout. */\nexport function classifyPath(model: Site, path: string): ChangeOwner {\n const segments = path.split(\"/\");\n const head = segments[0] ?? \"\";\n\n const collection = model.collections[head];\n if (collection && segments.length > 1) {\n if (segments.length === 2 && segments[1] === ORDER_FILE) {\n return { kind: \"order\", collection: head, label: `${collection.label}: order` };\n }\n\n if (entryLayout(collection.fields) === \"directory\") {\n const slug = segments[1]!;\n if (segments.length >= 3 && isValidSlug(slug)) {\n return { kind: \"entry\", collection: head, slug, label: slug };\n }\n } else if (segments.length === 2 && segments[1]!.endsWith(\".json\")) {\n const slug = segments[1]!.slice(0, -\".json\".length);\n if (isValidSlug(slug)) return { kind: \"entry\", collection: head, slug, label: slug };\n }\n }\n\n const singletonDir = model.singletons[head];\n if (singletonDir && segments.length > 1) {\n return { kind: \"singleton\", name: head, label: singletonDir.label };\n }\n if (head.endsWith(\".json\")) {\n const name = head.slice(0, -\".json\".length);\n const singleton = model.singletons[name];\n if (singleton && segments.length === 1) {\n return { kind: \"singleton\", name, label: singleton.label };\n }\n }\n\n if (head === \"media\") return { kind: \"media\", label: \"Shared media\" };\n\n return { kind: \"other\", label: path };\n}\n\n/** Content share that two versions of an entry need to count as one moved entry, as in Git. */\nconst RENAME_SIMILARITY = 0.5;\n\n/**\n * Marks renamed entries in place in `owners`. An entry has no identity but its\n * slug, so deleted and created entries are paired by content, like Git's rename\n * detection. An order file whose only change is the renamed slug joins the\n * entry, so a discard cannot leave the order naming a slug that does not exist.\n */\nasync function detectRenames(\n model: Site,\n published: ContentSource,\n source: ContentSource,\n changes: ContentChange[],\n owners: ChangeOwner[],\n staged: Map<string, Map<string, Record<string, unknown>>>,\n): Promise<void> {\n const kinds = new Map<string, Map<string, Set<ContentChange[\"kind\"]>>>();\n owners.forEach((owner, index) => {\n if (owner.kind !== \"entry\") return;\n const bySlug = kinds.get(owner.collection) ?? new Map<string, Set<ContentChange[\"kind\"]>>();\n kinds.set(owner.collection, bySlug);\n const set = bySlug.get(owner.slug) ?? new Set<ContentChange[\"kind\"]>();\n bySlug.set(owner.slug, set);\n set.add(changes[index]!.kind);\n });\n\n for (const [name, bySlug] of kinds) {\n const after = staged.get(name);\n if (!after) continue;\n\n const only = (kind: ContentChange[\"kind\"]) =>\n [...bySlug].filter(([, set]) => set.size === 1 && set.has(kind)).map(([slug]) => slug);\n const deleted = only(\"delete\").filter((slug) => !after.has(slug));\n const created = only(\"create\").filter((slug) => after.has(slug));\n if (deleted.length === 0 || created.length === 0) continue;\n\n const def = model.collections[name]!;\n let before: Map<string, Record<string, unknown>>;\n try {\n const entries = await readCollection(published, def, name);\n before = new Map(entries.map((entry) => [entry.slug, entry.data]));\n } catch {\n continue;\n }\n\n const candidates: Array<{ from: string; to: string; score: number }> = [];\n for (const from of deleted) {\n const old = before.get(from);\n if (!old) continue;\n for (const to of created) {\n if (before.has(to)) continue;\n const score = similarity(def, onDisk(def, name, from, old), onDisk(def, name, to, after.get(to)!));\n if (score >= RENAME_SIMILARITY) candidates.push({ from, to, score });\n }\n }\n\n const renamedTo = new Map<string, string>();\n const paired = new Set<string>();\n for (const { from, to } of candidates.sort((a, b) => b.score - a.score)) {\n if (renamedTo.has(from) || paired.has(to)) continue;\n renamedTo.set(from, to);\n paired.add(to);\n }\n\n if (renamedTo.size === 0) continue;\n\n const renamedFrom = new Map([...renamedTo].map(([from, to]) => [to, from]));\n const orderMovesOnlySlugs = await onlyRenamedInOrder(published, source, name, renamedTo);\n const [firstFrom, firstTo] = [...renamedTo][0]!;\n\n owners.forEach((owner, index) => {\n if (owner.kind === \"order\" && owner.collection === name && orderMovesOnlySlugs) {\n owners[index] = {\n kind: \"entry\",\n collection: name,\n slug: firstTo,\n label: firstTo,\n renamedFrom: firstFrom,\n };\n }\n if (owner.kind !== \"entry\" || owner.collection !== name) return;\n const to = renamedTo.get(owner.slug);\n if (to) owners[index] = { ...owner, slug: to, label: to, renamedFrom: owner.slug };\n const from = renamedFrom.get(owner.slug);\n if (from) owners[index] = { ...owner, renamedFrom: from };\n });\n }\n}\n\n/** True when a collection's staged order is the published one with the renamed slugs replaced. */\nasync function onlyRenamedInOrder(\n published: ContentSource,\n staged: ContentSource,\n name: string,\n renamedTo: Map<string, string>,\n): Promise<boolean> {\n const path = `${name}/${ORDER_FILE}`;\n const parse = (raw: string | null): unknown => {\n try {\n return raw === null ? null : JSON.parse(raw);\n } catch {\n return null;\n }\n };\n\n const before = parse(await published.read(path));\n const after = parse(await staged.read(path));\n if (!Array.isArray(before) || !Array.isArray(after)) return false;\n\n const expected = before.map((slug: unknown) =>\n typeof slug === \"string\" ? (renamedTo.get(slug) ?? slug) : slug,\n );\n return canonical(expected) === canonical(after);\n}\n\n/** The share of non-empty fields, the slug aside, that two versions of an entry agree on. */\nfunction similarity(def: Collection, a: Record<string, unknown>, b: Record<string, unknown>): number {\n const slugKey = resolveSlugField(def);\n let compared = 0;\n let equal = 0;\n for (const key of Object.keys(def.fields)) {\n if (key === slugKey || (isEmpty(a[key]) && isEmpty(b[key]))) continue;\n compared += 1;\n if (canonical(a[key]) === canonical(b[key])) equal += 1;\n }\n return compared === 0 ? 0 : equal / compared;\n}\n\n/**\n * Entry data with media paths as the files store them: relative to the entry,\n * where it has a folder.\n */\nfunction onDisk(\n def: Collection,\n collection: string,\n slug: string,\n data: Record<string, unknown>,\n): Record<string, unknown> {\n return entryLayout(def.fields) === \"directory\"\n ? relativizeMedia(def.fields, data, collectionEntryPath(def.fields, collection, slug))\n : data;\n}\n\nfunction isEmpty(value: unknown): boolean {\n return value === undefined || value === null || value === \"\" || (Array.isArray(value) && value.length === 0);\n}\n\n/** JSON with sorted keys, so two equal objects compare equal. */\nfunction canonical(value: unknown): string {\n return JSON.stringify(value ?? null, (_key, entry: unknown) => {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) return entry;\n const record = entry as Record<string, unknown>;\n return Object.fromEntries(Object.keys(record).sort().map((key) => [key, record[key]]));\n });\n}\n\nfunction labelEntry(def: Collection, slug: string, title: string | undefined): string {\n return `${def.labelSingular ?? def.label}: ${title ?? slug}`;\n}\n\nfunction entryDataPath(def: Collection, collection: string, slug: string): string {\n const base = collectionEntryPath(def.fields, collection, slug);\n return entryLayout(def.fields) === \"directory\" ? `${base}/index.json` : base;\n}\n\nexport function describeProblems(problems: StagingProblem[]): string {\n const names = problems.map((problem) => problem.label);\n const listed = names.slice(0, 3).join(\", \") + (names.length > 3 ? \", …\" : \"\");\n return `The pending changes cannot be published until they are valid: ${listed}.`;\n}\n\n/** Field issues with the file path folded in, for the flat API envelope. */\nexport function flattenProblems(problems: StagingProblem[]): FieldIssue[] {\n return problems.flatMap((problem) =>\n problem.issues.map((issue) => ({\n path: issue.path ? `${problem.path}: ${issue.path}` : problem.path,\n message: issue.message,\n })),\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/services/changes.ts"],"names":[],"mappings":"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","sourcesContent":["/** The staging API types, the before/after comparison and the analysis of a pending change set. */\n\nimport type { Author } from \"../workspace/files.js\";\nimport {\n LayoutError,\n ORDER_FILE,\n collectReferences,\n collectionEntryPath,\n createReferenceCheck,\n entryLayout,\n entryTitle,\n isValidSlug,\n readCollection,\n readCollectionEntry,\n readSingleton,\n relativizeMedia,\n resolveSlugField,\n singletonPath,\n validateFields,\n type Collection,\n type ContentChange,\n type ContentSource,\n type FieldIssue,\n type FieldMap,\n type PublishResult,\n type Site,\n} from \"@micacms/core\";\n\nimport { badRequest } from \"../lib/errors.js\";\nimport type { SyncStatus } from \"../workspace/policy.js\";\n\n/** Where the change happened, in words an editor recognises. */\nexport type ChangeOwner =\n | {\n kind: \"entry\";\n collection: string;\n slug: string;\n label: string;\n /** The slug before a pending rename. Set on the files of both slugs. */\n renamedFrom?: string;\n }\n | { kind: \"singleton\"; name: string; label: string }\n | { kind: \"order\"; collection: string; label: string }\n | { kind: \"media\"; label: string }\n | { kind: \"other\"; label: string };\n\nexport interface AnnotatedChange extends ContentChange {\n owner: ChangeOwner;\n /**\n * The file's revision, null for a deletion. Publish sends it back, so a file\n * saved after the review is not published unseen.\n */\n revision?: string | null;\n}\n\n/** One thing that would make the staged result unpublishable. */\nexport interface StagingProblem {\n /** The file the problem is about, relative to the content root. */\n path: string;\n label: string;\n issues: FieldIssue[];\n}\n\nexport interface StagingValidation {\n ok: boolean;\n problems: StagingProblem[];\n}\n\nexport interface StagingStatus {\n /** Whose working tree it is. The UI wording changes with it. */\n owner: \"developer\" | \"managed\";\n publish: { enabled: boolean; reason?: string };\n /** The commit the pending changes sit on. */\n head: string | null;\n /** The branch a publication lands on, where the workspace knows it. */\n branch: string | null;\n /** Commits created and not yet pushed. */\n ahead: number | null;\n changes: AnnotatedChange[];\n validation: StagingValidation;\n /** Where a clone that Mica keeps stands against its remote. Null for the developer's checkout. */\n sync: SyncStatus | null;\n}\n\nexport interface PublishInput {\n /** Subject line of the commit. */\n message: string;\n /**\n * The reviewed paths, with the revision each had in the review. Only these\n * are published; a file saved again since, or another file of the same entry,\n * makes Publish refuse.\n */\n paths: Record<string, string | null>;\n author?: Author;\n}\n\n/** What the System page shows about the repository. */\nexport interface RepositoryInfo {\n owner: \"developer\" | \"managed\";\n branch: string | null;\n head: string | null;\n /** The remote, for a clone that Mica keeps. */\n url: string | null;\n cloneBytes: number | null;\n sync: SyncStatus | null;\n}\n\nexport interface ContentStagingService {\n status(): Promise<StagingStatus>;\n publish(input: PublishInput): Promise<PublishResult>;\n discard(paths: string[]): Promise<{ discarded: string[] }>;\n /** Retries the push after `committed-pending-push`. */\n push(): Promise<PublishResult>;\n /**\n * A document before and after a pending change. `origin` compares against the\n * website's version, not the last commit.\n */\n compare(target: CompareTarget, against?: \"head\" | \"origin\"): Promise<Comparison>;\n /** Brings a clone that Mica keeps up to date now. Refused for the developer's checkout. */\n sync(): Promise<void>;\n resolve(group: string, choice: \"mine\" | \"website\"): Promise<void>;\n repository(): Promise<RepositoryInfo>;\n}\n\n/** What to compare on the Publish page. */\nexport type CompareTarget =\n /** `from` names the published slug of a renamed entry. */\n | { kind: \"entry\"; collection: string; slug: string; from?: string }\n | { kind: \"singleton\"; name: string }\n | { kind: \"order\"; collection: string };\n\n/**\n * Raw field data on both sides of a pending change. The editor renders the\n * comparison from the model.\n */\nexport interface Comparison {\n /** As the repository has it — null when the change creates the document. */\n before: Record<string, unknown> | null;\n /** As staged for publication — null when the change deletes it. */\n after: Record<string, unknown> | null;\n}\n\n/**\n * Reads one document from `published` and from `staged`. An order target\n * becomes `{ order: [{ slug, title }] }` on each side, in position order.\n */\nexport async function compareSources(\n model: Site,\n published: ContentSource,\n staged: ContentSource,\n target: CompareTarget,\n): Promise<Comparison> {\n // During a layout migration HEAD still has the old layout: then there is no \"before\".\n const before = <T>(read: () => Promise<T>): Promise<T | null> =>\n read().catch((error: unknown) => (error instanceof LayoutError ? null : Promise.reject(error)));\n\n if (target.kind === \"singleton\") {\n const def = model.singletons[target.name];\n if (!def) throw badRequest(`There is no singleton called \"${target.name}\".`);\n return {\n before: await before(() => readSingleton(published, def, target.name)),\n after: await readSingleton(staged, def, target.name),\n };\n }\n\n const def = model.collections[target.collection];\n if (!def) throw badRequest(`There is no collection called \"${target.collection}\".`);\n\n if (target.kind === \"entry\") {\n const from = target.from ?? target.slug;\n for (const slug of [target.slug, from]) {\n if (!isValidSlug(slug)) throw badRequest(`\"${slug}\" is not a slug.`);\n }\n const old = await readCollectionEntry(published, def, target.collection, from);\n const after = await readCollectionEntry(staged, def, target.collection, target.slug);\n if (from === target.slug) return { before: old, after };\n\n // A rename: compare the on-disk form, where colocated media paths do not\n // name the entry's folder.\n return {\n before: old && onDisk(def, target.collection, from, old),\n after: after && onDisk(def, target.collection, target.slug, after),\n };\n }\n\n const ordering = async (source: ContentSource) => {\n const entries = await readCollection(source, def, target.collection);\n if (entries.length === 0) return null;\n return {\n order: entries.map((entry) => ({ slug: entry.slug, title: entryTitle(def, entry.data) })),\n };\n };\n\n return { before: await before(() => ordering(published)), after: await ordering(staged) };\n}\n\n/**\n * Annotates a change set with owners and validates the entries it touches, in\n * one read of the affected collections. Only changed documents are validated: a\n * broken entry committed earlier must not block an unrelated publication.\n */\nexport async function analyseChanges(\n model: Site,\n source: ContentSource,\n changes: ContentChange[],\n /** The repository's side. When given, renamed entries are detected. */\n published?: ContentSource,\n): Promise<{ changes: AnnotatedChange[]; validation: StagingValidation }> {\n const owners = changes.map((change) => classifyPath(model, change.path));\n\n const collectionsToRead = new Set<string>();\n for (const owner of owners) {\n if (owner.kind === \"entry\" || owner.kind === \"order\") collectionsToRead.add(owner.collection);\n }\n\n const problems: StagingProblem[] = [];\n const titles = new Map<string, string>();\n const entriesByCollection = new Map<string, Map<string, Record<string, unknown>>>();\n\n for (const name of collectionsToRead) {\n const def = model.collections[name]!;\n try {\n const entries = await readCollection(source, def, name);\n const bySlug = new Map(entries.map((entry) => [entry.slug, entry.data]));\n entriesByCollection.set(name, bySlug);\n for (const [slug, data] of bySlug) titles.set(`${name}/${slug}`, entryTitle(def, data));\n } catch (error) {\n // An unreadable collection, possibly from an untouched file, is a problem\n // at the path it names.\n const path = (error as { path?: string }).path ?? name;\n problems.push({\n path,\n label: def.label,\n issues: [{ path: \"\", message: (error as Error).message }],\n });\n }\n }\n\n if (published) {\n await detectRenames(model, published, source, changes, owners, entriesByCollection);\n }\n\n const validated = new Set<string>();\n const targets = createReferenceCheck(model, source, (name) => {\n const entries = entriesByCollection.get(name);\n return entries ? new Set(entries.keys()) : undefined;\n });\n\n for (const owner of owners) {\n if (owner.kind === \"entry\") {\n const key = `${owner.collection}/${owner.slug}`;\n if (validated.has(key)) continue;\n validated.add(key);\n\n const def = model.collections[owner.collection]!;\n // Absent means the change set deletes this entry.\n const data = entriesByCollection.get(owner.collection)?.get(owner.slug);\n if (!data) continue;\n\n const result = validateFields(def.fields, data);\n const issues = [...(result.ok ? [] : result.issues), ...(await targets.issues(def.fields, data))];\n if (issues.length > 0) {\n problems.push({\n path: entryDataPath(def, owner.collection, owner.slug),\n label: labelEntry(def, owner.slug, titles.get(key)),\n issues,\n });\n }\n }\n\n if (owner.kind === \"singleton\" && !validated.has(`singleton:${owner.name}`)) {\n validated.add(`singleton:${owner.name}`);\n\n const def = model.singletons[owner.name]!;\n const dataPath = singletonPath(def.fields, owner.name);\n try {\n const data = await readSingleton(source, def, owner.name);\n if (data === null) continue;\n\n const result = validateFields(def.fields, data);\n const issues = [...(result.ok ? [] : result.issues), ...(await targets.issues(def.fields, data))];\n if (issues.length > 0) problems.push({ path: dataPath, label: def.label, issues });\n } catch (error) {\n problems.push({\n path: (error as { path?: string }).path ?? dataPath,\n label: def.label,\n issues: [{ path: \"\", message: (error as Error).message }],\n });\n }\n }\n }\n\n problems.push(...(await brokenByRemoval(model, source, changes, owners, entriesByCollection, titles)));\n\n const annotated = changes.map((change, index): AnnotatedChange => {\n const owner = owners[index]!;\n if (owner.kind === \"entry\") {\n const def = model.collections[owner.collection]!;\n return {\n ...change,\n owner: { ...owner, label: labelEntry(def, owner.slug, titles.get(`${owner.collection}/${owner.slug}`)) },\n };\n }\n return { ...change, owner };\n });\n\n return {\n changes: annotated,\n validation: { ok: problems.length === 0, problems },\n };\n}\n\n/** Which piece of content a file belongs to, read from the model's path layout. */\nexport function classifyPath(model: Site, path: string): ChangeOwner {\n const segments = path.split(\"/\");\n const head = segments[0] ?? \"\";\n\n const collection = model.collections[head];\n if (collection && segments.length > 1) {\n if (segments.length === 2 && segments[1] === ORDER_FILE) {\n return { kind: \"order\", collection: head, label: `${collection.label}: order` };\n }\n\n if (entryLayout(collection.fields) === \"directory\") {\n const slug = segments[1]!;\n if (segments.length >= 3 && isValidSlug(slug)) {\n return { kind: \"entry\", collection: head, slug, label: slug };\n }\n } else if (segments.length === 2 && segments[1]!.endsWith(\".json\")) {\n const slug = segments[1]!.slice(0, -\".json\".length);\n if (isValidSlug(slug)) return { kind: \"entry\", collection: head, slug, label: slug };\n }\n }\n\n const singletonDir = model.singletons[head];\n if (singletonDir && segments.length > 1) {\n return { kind: \"singleton\", name: head, label: singletonDir.label };\n }\n if (head.endsWith(\".json\")) {\n const name = head.slice(0, -\".json\".length);\n const singleton = model.singletons[name];\n if (singleton && segments.length === 1) {\n return { kind: \"singleton\", name, label: singleton.label };\n }\n }\n\n if (head === \"media\") return { kind: \"media\", label: \"Shared media\" };\n\n return { kind: \"other\", label: path };\n}\n\n/** Content share that two versions of an entry need to count as one moved entry, as in Git. */\nconst RENAME_SIMILARITY = 0.5;\n\n/**\n * Marks renamed entries in place in `owners`. An entry has no identity but its\n * slug, so deleted and created entries are paired by content, like Git's rename\n * detection. An order file whose only change is the renamed slug joins the\n * entry, so a discard cannot leave the order naming a slug that does not exist.\n */\nasync function detectRenames(\n model: Site,\n published: ContentSource,\n source: ContentSource,\n changes: ContentChange[],\n owners: ChangeOwner[],\n staged: Map<string, Map<string, Record<string, unknown>>>,\n): Promise<void> {\n const kinds = new Map<string, Map<string, Set<ContentChange[\"kind\"]>>>();\n owners.forEach((owner, index) => {\n if (owner.kind !== \"entry\") return;\n const bySlug = kinds.get(owner.collection) ?? new Map<string, Set<ContentChange[\"kind\"]>>();\n kinds.set(owner.collection, bySlug);\n const set = bySlug.get(owner.slug) ?? new Set<ContentChange[\"kind\"]>();\n bySlug.set(owner.slug, set);\n set.add(changes[index]!.kind);\n });\n\n for (const [name, bySlug] of kinds) {\n const after = staged.get(name);\n if (!after) continue;\n\n const only = (kind: ContentChange[\"kind\"]) =>\n [...bySlug].filter(([, set]) => set.size === 1 && set.has(kind)).map(([slug]) => slug);\n const deleted = only(\"delete\").filter((slug) => !after.has(slug));\n const created = only(\"create\").filter((slug) => after.has(slug));\n if (deleted.length === 0 || created.length === 0) continue;\n\n const def = model.collections[name]!;\n let before: Map<string, Record<string, unknown>>;\n try {\n const entries = await readCollection(published, def, name);\n before = new Map(entries.map((entry) => [entry.slug, entry.data]));\n } catch {\n continue;\n }\n\n const candidates: Array<{ from: string; to: string; score: number }> = [];\n for (const from of deleted) {\n const old = before.get(from);\n if (!old) continue;\n for (const to of created) {\n if (before.has(to)) continue;\n const score = similarity(def, onDisk(def, name, from, old), onDisk(def, name, to, after.get(to)!));\n if (score >= RENAME_SIMILARITY) candidates.push({ from, to, score });\n }\n }\n\n const renamedTo = new Map<string, string>();\n const paired = new Set<string>();\n for (const { from, to } of candidates.sort((a, b) => b.score - a.score)) {\n if (renamedTo.has(from) || paired.has(to)) continue;\n renamedTo.set(from, to);\n paired.add(to);\n }\n\n if (renamedTo.size === 0) continue;\n\n const renamedFrom = new Map([...renamedTo].map(([from, to]) => [to, from]));\n const orderMovesOnlySlugs = await onlyRenamedInOrder(published, source, name, renamedTo);\n const [firstFrom, firstTo] = [...renamedTo][0]!;\n\n owners.forEach((owner, index) => {\n if (owner.kind === \"order\" && owner.collection === name && orderMovesOnlySlugs) {\n owners[index] = {\n kind: \"entry\",\n collection: name,\n slug: firstTo,\n label: firstTo,\n renamedFrom: firstFrom,\n };\n }\n if (owner.kind !== \"entry\" || owner.collection !== name) return;\n const to = renamedTo.get(owner.slug);\n if (to) owners[index] = { ...owner, slug: to, label: to, renamedFrom: owner.slug };\n const from = renamedFrom.get(owner.slug);\n if (from) owners[index] = { ...owner, renamedFrom: from };\n });\n }\n}\n\n/** True when a collection's staged order is the published one with the renamed slugs replaced. */\nasync function onlyRenamedInOrder(\n published: ContentSource,\n staged: ContentSource,\n name: string,\n renamedTo: Map<string, string>,\n): Promise<boolean> {\n const path = `${name}/${ORDER_FILE}`;\n const parse = (raw: string | null): unknown => {\n try {\n return raw === null ? null : JSON.parse(raw);\n } catch {\n return null;\n }\n };\n\n const before = parse(await published.read(path));\n const after = parse(await staged.read(path));\n if (!Array.isArray(before) || !Array.isArray(after)) return false;\n\n const expected = before.map((slug: unknown) =>\n typeof slug === \"string\" ? (renamedTo.get(slug) ?? slug) : slug,\n );\n return canonical(expected) === canonical(after);\n}\n\n/** The share of non-empty fields, the slug aside, that two versions of an entry agree on. */\nfunction similarity(def: Collection, a: Record<string, unknown>, b: Record<string, unknown>): number {\n const slugKey = resolveSlugField(def);\n let compared = 0;\n let equal = 0;\n for (const key of Object.keys(def.fields)) {\n if (key === slugKey || (isEmpty(a[key]) && isEmpty(b[key]))) continue;\n compared += 1;\n if (canonical(a[key]) === canonical(b[key])) equal += 1;\n }\n return compared === 0 ? 0 : equal / compared;\n}\n\n/**\n * Entry data with media paths as the files store them: relative to the entry,\n * where it has a folder.\n */\nfunction onDisk(\n def: Collection,\n collection: string,\n slug: string,\n data: Record<string, unknown>,\n): Record<string, unknown> {\n return entryLayout(def.fields) === \"directory\"\n ? relativizeMedia(def.fields, data, collectionEntryPath(def.fields, collection, slug))\n : data;\n}\n\nfunction isEmpty(value: unknown): boolean {\n return value === undefined || value === null || value === \"\" || (Array.isArray(value) && value.length === 0);\n}\n\n/** JSON with sorted keys, so two equal objects compare equal. */\nfunction canonical(value: unknown): string {\n return JSON.stringify(value ?? null, (_key, entry: unknown) => {\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) return entry;\n const record = entry as Record<string, unknown>;\n return Object.fromEntries(Object.keys(record).sort().map((key) => [key, record[key]]));\n });\n}\n\n/**\n * Entries and files that the change set removes while another entry still\n * points at them. Publishing them would break a link or an image on the site.\n */\nasync function brokenByRemoval(\n model: Site,\n source: ContentSource,\n changes: ContentChange[],\n owners: ChangeOwner[],\n entriesByCollection: Map<string, Map<string, Record<string, unknown>>>,\n titles: Map<string, string>,\n): Promise<StagingProblem[]> {\n const removedEntries = new Map<string, { collection: string; slug: string; renamedTo?: string }>();\n const removedFiles = new Set<string>();\n changes.forEach((change, index) => {\n const owner = owners[index]!;\n if (change.kind !== \"delete\") return;\n if (owner.kind === \"entry\") {\n // A URL change removes the old slug: links to it break as with a delete.\n const gone = owner.renamedFrom ?? owner.slug;\n if (!entriesByCollection.get(owner.collection)?.has(gone)) {\n const renamedTo = owner.renamedFrom ? owner.slug : undefined;\n removedEntries.set(`${owner.collection}/${gone}`, { collection: owner.collection, slug: gone, ...(renamedTo ? { renamedTo } : {}) });\n return;\n }\n }\n // Only a media file can be a target; a cleared rich-text file or an order file cannot.\n if (!change.path.endsWith(\".json\") && !change.path.endsWith(\".mdoc\")) removedFiles.add(change.path);\n });\n if (removedEntries.size === 0 && removedFiles.size === 0) return [];\n\n const linkedFrom = new Map<string, string[]>();\n const note = (target: string, from: string) => linkedFrom.set(target, [...(linkedFrom.get(target) ?? []), from]);\n\n const scan = (label: string, fields: FieldMap, data: Record<string, unknown>) => {\n for (const reference of collectReferences(fields, data)) {\n const target = reference.kind === \"relation\" ? `${reference.collection}/${reference.slug}` : reference.path;\n if (removedEntries.has(target) || removedFiles.has(target)) note(target, label);\n }\n };\n\n for (const [name, def] of Object.entries(model.collections)) {\n let entries = entriesByCollection.get(name);\n if (!entries) {\n try {\n entries = new Map((await readCollection(source, def, name)).map((entry) => [entry.slug, entry.data]));\n } catch {\n continue; // An unreadable collection is reported where it is changed.\n }\n }\n for (const [slug, data] of entries) scan(labelEntry(def, slug, entryTitle(def, data)), def.fields, data);\n }\n for (const [name, def] of Object.entries(model.singletons)) {\n const data = await readSingleton(source, def, name).catch(() => null);\n if (data) scan(def.label, def.fields, data);\n }\n\n const problems: StagingProblem[] = [];\n for (const [target, from] of linkedFrom) {\n const entry = removedEntries.get(target);\n const def = entry ? model.collections[entry.collection]! : null;\n const message = entry?.renamedTo\n ? `The URL changes from \"${entry.slug}\" to \"${entry.renamedTo}\", and ${from.join(\", \")} still link to \"${entry.slug}\". Change those links first, or publish them together.`\n : `Still linked from ${from.join(\", \")}. Remove those links first, or publish them together.`;\n problems.push({\n path: entry && def ? entryDataPath(def, entry.collection, entry.slug) : target,\n label: entry && def ? labelEntry(def, entry.slug, titles.get(target)) : target,\n issues: [{ path: \"\", message }],\n });\n }\n return problems;\n}\n\nfunction labelEntry(def: Collection, slug: string, title: string | undefined): string {\n return `${def.labelSingular ?? def.label}: ${title ?? slug}`;\n}\n\nfunction entryDataPath(def: Collection, collection: string, slug: string): string {\n const base = collectionEntryPath(def.fields, collection, slug);\n return entryLayout(def.fields) === \"directory\" ? `${base}/index.json` : base;\n}\n\nexport function describeProblems(problems: StagingProblem[]): string {\n const names = problems.map((problem) => problem.label);\n const listed = names.slice(0, 3).join(\", \") + (names.length > 3 ? \", …\" : \"\");\n return `The pending changes cannot be published until they are valid: ${listed}.`;\n}\n\n/** Field issues with the file path folded in, for the flat API envelope. */\nexport function flattenProblems(problems: StagingProblem[]): FieldIssue[] {\n return problems.flatMap((problem) =>\n problem.issues.map((issue) => ({\n path: issue.path ? `${problem.path}: ${issue.path}` : problem.path,\n message: issue.message,\n })),\n );\n}\n"]}
|
|
@@ -8,6 +8,10 @@ import { FilesError, type ContentFiles } from "../workspace/files.js";
|
|
|
8
8
|
export declare class ContentError extends FilesError {
|
|
9
9
|
name: string;
|
|
10
10
|
}
|
|
11
|
+
/** A write to a collection or singleton that this build of Mica cannot interpret. */
|
|
12
|
+
export declare class ReadOnlyError extends ContentError {
|
|
13
|
+
name: string;
|
|
14
|
+
}
|
|
11
15
|
export declare class ValidationError extends ContentError {
|
|
12
16
|
readonly issues: FieldIssue[];
|
|
13
17
|
name: string;
|
|
@@ -28,7 +32,8 @@ export interface SavedEntry {
|
|
|
28
32
|
/** True when the save moved the entry's files. */
|
|
29
33
|
renamed: boolean;
|
|
30
34
|
}
|
|
31
|
-
|
|
35
|
+
/** `readOnly` comes from `readOnlyParts`: its collections and singletons are read, never written. */
|
|
36
|
+
export declare function createContentStore(site: Site, workspace: ContentFiles, readOnly?: ReadonlyMap<string, string>): {
|
|
32
37
|
list(collectionName: string): Promise<ContentEntry[]>;
|
|
33
38
|
get(collectionName: string, slug: string): Promise<ContentEntry | null>;
|
|
34
39
|
/** The revision an editor must send back with a save of this entry. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-store.d.ts","sourceRoot":"","sources":["../../src/services/content-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,
|
|
1
|
+
{"version":3,"file":"content-store.d.ts","sourceRoot":"","sources":["../../src/services/content-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAqBL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,UAAU,EAEf,KAAK,SAAS,EACd,KAAK,IAAI,EACV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAiB,UAAU,EAAE,KAAK,YAAY,EAAmB,MAAM,uBAAuB,CAAC;AAEtG,qBAAa,YAAa,SAAQ,UAAU;IACjC,IAAI,SAAkB;CAChC;AAED,qFAAqF;AACrF,qBAAa,aAAc,SAAQ,YAAY;IACpC,IAAI,SAAmB;CACjC;AAED,qBAAa,eAAgB,SAAQ,YAAY;IAI7C,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE;IAHtB,IAAI,SAAqB;gBAEhC,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,UAAU,EAAE;CAIhC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa;yBAmJ1F,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;wBAIjC,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAK7E,uEAAuE;+BACtC,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ9E,2EAA2E;8BAC3C,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;uBAKtC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;yBAOvC,MAAM,SAAS,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;2BA8D5D,MAAM,QAChB,MAAM,YACH;QAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC;wBAuBR,MAAM,WACH,OAAO,YACP;QAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAC5C,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;4BAwBd,MAAM,SACf,MAAM,EAAE,GACd,OAAO,CAAC,IAAI,CAAC;8BAtSe,MAAM,KAAG,UAAU;6BAMpB,MAAM,KAAG,SAAS;IAoThD,4EAA4E;gCAC1C,MAAM,UAAU,MAAM,WAAW,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK3F,0EAA0E;mCACrC,MAAM,QAAQ,MAAM;oBACpB,MAAM;cAAQ,MAAM;eAAS,MAAM;;EAkB3E;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|