@micacms/runtime 0.6.0-rc.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/app.d.ts.map +1 -1
  2. package/dist/app.js +5 -0
  3. package/dist/app.js.map +1 -1
  4. package/dist/context.d.ts +17 -2
  5. package/dist/context.d.ts.map +1 -1
  6. package/dist/context.js +62 -18
  7. package/dist/context.js.map +1 -1
  8. package/dist/http.d.ts +2 -0
  9. package/dist/http.d.ts.map +1 -1
  10. package/dist/http.js +5 -0
  11. package/dist/http.js.map +1 -1
  12. package/dist/lib/file-type.d.ts +2 -0
  13. package/dist/lib/file-type.d.ts.map +1 -1
  14. package/dist/lib/file-type.js +3 -1
  15. package/dist/lib/file-type.js.map +1 -1
  16. package/dist/routes/admin-content.d.ts +1 -1
  17. package/dist/routes/admin-content.d.ts.map +1 -1
  18. package/dist/routes/admin-content.js +15 -0
  19. package/dist/routes/admin-content.js.map +1 -1
  20. package/dist/routes/admin-media.d.ts +5 -3
  21. package/dist/routes/admin-media.d.ts.map +1 -1
  22. package/dist/routes/admin-media.js +72 -2
  23. package/dist/routes/admin-media.js.map +1 -1
  24. package/dist/routes/admin-staging.d.ts.map +1 -1
  25. package/dist/routes/admin-staging.js +18 -7
  26. package/dist/routes/admin-staging.js.map +1 -1
  27. package/dist/routes/admin-system.d.ts.map +1 -1
  28. package/dist/routes/admin-system.js +4 -0
  29. package/dist/routes/admin-system.js.map +1 -1
  30. package/dist/services/changes.d.ts +19 -4
  31. package/dist/services/changes.d.ts.map +1 -1
  32. package/dist/services/changes.js +60 -1
  33. package/dist/services/changes.js.map +1 -1
  34. package/dist/services/content-store.d.ts +27 -6
  35. package/dist/services/content-store.d.ts.map +1 -1
  36. package/dist/services/content-store.js +231 -18
  37. package/dist/services/content-store.js.map +1 -1
  38. package/dist/services/media.d.ts.map +1 -1
  39. package/dist/services/media.js +4 -1
  40. package/dist/services/media.js.map +1 -1
  41. package/dist/services/staging.d.ts.map +1 -1
  42. package/dist/services/staging.js +26 -14
  43. package/dist/services/staging.js.map +1 -1
  44. package/dist/workspace/developer.d.ts +2 -1
  45. package/dist/workspace/developer.d.ts.map +1 -1
  46. package/dist/workspace/developer.js +19 -4
  47. package/dist/workspace/developer.js.map +1 -1
  48. package/dist/workspace/policy.d.ts +1 -1
  49. package/dist/workspace/policy.d.ts.map +1 -1
  50. package/dist/workspace/policy.js.map +1 -1
  51. package/dist/workspace/tree.d.ts +15 -2
  52. package/dist/workspace/tree.d.ts.map +1 -1
  53. package/dist/workspace/tree.js +29 -6
  54. package/dist/workspace/tree.js.map +1 -1
  55. package/package.json +2 -2
  56. package/src/app.ts +5 -0
  57. package/src/context.ts +69 -20
  58. package/src/http.ts +6 -0
  59. package/src/lib/file-type.ts +4 -1
  60. package/src/routes/admin-content.ts +16 -2
  61. package/src/routes/admin-media.ts +74 -3
  62. package/src/routes/admin-staging.ts +22 -7
  63. package/src/routes/admin-system.ts +4 -0
  64. package/src/services/changes.ts +84 -5
  65. package/src/services/content-store.ts +243 -25
  66. package/src/services/media.ts +7 -1
  67. package/src/services/staging.ts +30 -14
  68. package/src/workspace/developer.ts +22 -5
  69. package/src/workspace/policy.ts +1 -1
  70. package/src/workspace/tree.ts +35 -6
@@ -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 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"]}
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\";\nimport type { UnpushedCommits } from \"../workspace/tree.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 /** When the file was last saved, for \"changed 2 hours ago\". Null for a deletion. */\n savedAt?: string | null;\n /**\n * New pending files that this document uses (a linked entry, an image), so\n * that it cannot be published without them. On the document's data file.\n */\n needs?: string[];\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 /**\n * Commits that no remote has, in the developer's checkout: Mica's waits for a\n * retry, another one blocks Publish. Null for a clone that Mica keeps, or\n * without a remote.\n */\n unpushed: UnpushedCommits | 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 /** Who published, for the log. */\n editor?: string;\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 /** `editor` names who discarded, for the log. */\n discard(paths: string[], editor?: 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\", editor?: string): 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 created = new Set(changes.filter((change) => change.kind === \"create\").map((change) => change.path));\n const needsByPath = new Map<string, string[]>();\n /** How to read each changed document as the website has it, by its data file. */\n const publishedOf = new Map<string, () => Promise<Record<string, unknown> | null>>();\n const addNeeds = (dataPath: string, fields: Collection[\"fields\"], data: Record<string, unknown>): void => {\n const needed = new Set<string>();\n for (const reference of collectReferences(fields, data)) {\n const def = reference.kind === \"relation\" ? model.collections[reference.collection] : undefined;\n const path =\n reference.kind === \"media\" ? reference.path : def ? entryDataPath(def, reference.collection, reference.slug) : null;\n if (path && path !== dataPath && created.has(path)) needed.add(path);\n }\n if (needed.size > 0) needsByPath.set(dataPath, [...needed]);\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 addNeeds(entryDataPath(def, owner.collection, owner.slug), def.fields, data);\n if (published) {\n const { collection, slug } = owner;\n publishedOf.set(entryDataPath(def, collection, slug), () => readCollectionEntry(published, def, collection, slug).catch(() => null));\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 // The data file, not the folder: a folder-layout singleton's change is `<name>/index.json`.\n const dataFile = entryLayout(def.fields) === \"directory\" ? `${dataPath}/index.json` : dataPath;\n addNeeds(dataFile, def.fields, data);\n if (published) {\n const name = owner.name;\n publishedOf.set(dataFile, () => readSingleton(published, def, name).catch(() => null));\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 // A URL change and the links that follow it are published together, both ways. A\n // document follows when the website's version of it links to the old URL; a new\n // draft that links to the new URL only needs the change, and does not hold it back.\n const renamed = new Map(\n owners.flatMap((owner) =>\n owner.kind === \"entry\" && owner.renamedFrom\n ? [[entryDataPath(model.collections[owner.collection]!, owner.collection, owner.slug), owner] as const]\n : [],\n ),\n );\n for (const [document, needed] of [...needsByPath]) {\n for (const path of needed) {\n const target = renamed.get(path);\n if (!target || target.kind !== \"entry\") continue;\n const before = await publishedOf.get(document)?.();\n const fields = before && fieldsOfDocument(model, document);\n const linkedBefore =\n before &&\n fields &&\n collectReferences(fields, before).some(\n (reference) => reference.kind === \"relation\" && reference.collection === target.collection && reference.slug === target.renamedFrom,\n );\n if (linkedBefore) needsByPath.set(path, [...new Set([...(needsByPath.get(path) ?? []), document])]);\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 const needs = needsByPath.get(change.path);\n const extra = needs ? { needs } : {};\n if (owner.kind === \"entry\") {\n const def = model.collections[owner.collection]!;\n return {\n ...change,\n ...extra,\n owner: { ...owner, label: labelEntry(def, owner.slug, titles.get(`${owner.collection}/${owner.slug}`)) },\n };\n }\n return { ...change, ...extra, owner };\n });\n\n return {\n changes: annotated,\n validation: { ok: problems.length === 0, problems },\n };\n}\n\n/** The fields of the entry or singleton whose data file is `path`. */\nfunction fieldsOfDocument(model: Site, path: string): Collection[\"fields\"] | null {\n const owner = classifyPath(model, path);\n if (owner.kind === \"entry\") return model.collections[owner.collection]?.fields ?? null;\n if (owner.kind === \"singleton\") return model.singletons[owner.name]?.fields ?? null;\n return null;\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"]}
@@ -23,7 +23,22 @@ export interface SaveEntryInput {
23
23
  slug?: string;
24
24
  /** The revision `slug` had when the editor opened it. Absent writes unconditionally. */
25
25
  expectedRevision?: string | null;
26
+ /** On a URL change: point the relations in other entries and settings at the new URL, in the same write. */
27
+ updateLinks?: boolean;
26
28
  }
29
+ /** An entry or settings page that links to an entry. `readOnly`: this build cannot change its links. */
30
+ export type Referrer = {
31
+ kind: "entry";
32
+ collection: string;
33
+ slug: string;
34
+ title: string;
35
+ readOnly?: true;
36
+ } | {
37
+ kind: "singleton";
38
+ name: string;
39
+ title: string;
40
+ readOnly?: true;
41
+ };
27
42
  export interface SavedEntry {
28
43
  slug: string;
29
44
  title: string;
@@ -31,6 +46,8 @@ export interface SavedEntry {
31
46
  revision: string;
32
47
  /** True when the save moved the entry's files. */
33
48
  renamed: boolean;
49
+ /** How many other entries and settings pages now link to the new URL. */
50
+ relinked: number;
34
51
  }
35
52
  /** `readOnly` comes from `readOnlyParts`: its collections and singletons are read, never written. */
36
53
  export declare function createContentStore(site: Site, workspace: ContentFiles, readOnly?: ReadonlyMap<string, string>): {
@@ -55,12 +72,16 @@ export declare function createContentStore(site: Site, workspace: ContentFiles,
55
72
  requireSingleton: (name: string) => Singleton;
56
73
  /** Slug the server would assign, so the editor's URL preview matches it. */
57
74
  suggestSlug(collectionName: string, source: string, except?: string): Promise<string>;
58
- /** Entries pointing at `slug`, so the editor can warn before a delete. */
59
- findReferences(collectionName: string, slug: string): Promise<{
60
- collection: string;
61
- slug: string;
62
- title: string;
63
- }[]>;
75
+ /**
76
+ * Where each file is used: in a media field, or as an image or a link in rich
77
+ * text. A file that something uses is not deleted.
78
+ */
79
+ mediaUses(paths: string[]): Promise<{
80
+ uses: Record<string, Referrer[]>;
81
+ unreadable: string[];
82
+ }>;
83
+ /** Entries and settings pages that link to `slug`: the warning before a delete or a URL change. */
84
+ findReferences(collectionName: string, slug: string): Promise<Referrer[]>;
64
85
  };
65
86
  export type ContentStore = ReturnType<typeof createContentStore>;
66
87
  //# sourceMappingURL=content-store.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"content-store.d.ts","sourceRoot":"","sources":["../../src/services/content-store.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,8 @@
4
4
  * history, a save is a draft, and only Publish makes a commit.
5
5
  */
6
6
  import { createHash } from "node:crypto";
7
- import { ContentPathError, DOCUMENT_EXTENSION, INDEX_FILE, ORDER_FILE, collectionEntryPath, entryFiles, entryLayout, entryTitle, isValidSlug, collectReferences, readCollection, readSingleton, relativizeMedia, resolveSlugField, serializeEntry, singletonPath, slugify, uniqueSlug, validateFields, withDefaults, } from "@micacms/core";
7
+ import { posix } from "node:path";
8
+ import { ContentPathError, DOCUMENT_EXTENSION, INDEX_FILE, ORDER_FILE, collectionEntryPath, entryFiles, entryLayout, entryTitle, isValidSlug, collectReferences, readCollection, mapMedia, readCollectionEntry, readSingleton, relativizeMedia, relinkReferences, resolveSlugField, resolveTitleField, serializeEntry, singletonPath, slugify, uniqueSlug, validateFields, withDefaults, } from "@micacms/core";
8
9
  import { ConflictError, FilesError } from "../workspace/files.js";
9
10
  export class ContentError extends FilesError {
10
11
  name = "ContentError";
@@ -42,7 +43,8 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
42
43
  return def;
43
44
  };
44
45
  const validate = (def, data, what) => {
45
- const result = validateFields(def.fields, data);
46
+ // Save keeps a half-finished entry; Publish checks `required` (decision "Publish a selection").
47
+ const result = validateFields(def.fields, data, { enforceRequired: false });
46
48
  if (!result.ok) {
47
49
  throw new ValidationError(`${what} cannot be saved until these fields are correct.`, result.issues);
48
50
  }
@@ -151,6 +153,80 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
151
153
  const order = listed.map((slug) => (slug === from ? to : slug));
152
154
  return { type: "write", path, contents: `${JSON.stringify(order, null, 2)}\n` };
153
155
  };
156
+ /** Entries and settings pages whose relations point at `collection`/`slug`, with what a relink needs. */
157
+ const referrers = async (collection, slug) => {
158
+ const found = [];
159
+ const readOnlyMark = (key) => (readOnly.has(key) ? { readOnly: true } : {});
160
+ const points = (fields, data) => collectReferences(fields, data).some((reference) => reference.kind === "relation" && reference.collection === collection && reference.slug === slug);
161
+ for (const [name, def] of Object.entries(site.collections)) {
162
+ if (!linksTo(def.fields, collection))
163
+ continue;
164
+ for (const entry of await readCollection(workspace, def, name)) {
165
+ if (!points(def.fields, entry.data))
166
+ continue;
167
+ found.push({
168
+ referrer: {
169
+ kind: "entry",
170
+ collection: name,
171
+ slug: entry.slug,
172
+ title: entryTitle(def, entry.data),
173
+ ...readOnlyMark(`collections.${name}`),
174
+ },
175
+ fields: def.fields,
176
+ base: collectionEntryPath(def.fields, name, entry.slug),
177
+ dataFile: dataPath(def, name, entry.slug),
178
+ read: () => readCollectionEntry(workspace, def, name, entry.slug),
179
+ });
180
+ }
181
+ }
182
+ for (const [name, def] of Object.entries(site.singletons)) {
183
+ if (!linksTo(def.fields, collection))
184
+ continue;
185
+ const data = await readSingleton(workspace, def, name);
186
+ if (!data || !points(def.fields, data))
187
+ continue;
188
+ found.push({
189
+ referrer: { kind: "singleton", name, title: def.label, ...readOnlyMark(`singletons.${name}`) },
190
+ fields: def.fields,
191
+ base: singletonPath(def.fields, name),
192
+ dataFile: singletonDataPath(def, name),
193
+ read: () => readSingleton(workspace, def, name),
194
+ });
195
+ }
196
+ return found;
197
+ };
198
+ /**
199
+ * Files that a new entry uses from `<collection>/<other slug>/media/`:
200
+ * uploads made before the URL was final. Each gets a free name in the
201
+ * entry's own `media/` folder. From a folder where no entry lives they move;
202
+ * from another entry's folder (a colleague saved that URL first) they are
203
+ * copied, because that entry may use them too.
204
+ */
205
+ const stagedUploads = async (def, collection, data, base) => {
206
+ const moves = [];
207
+ const taken = new Set((await workspace.list(`${base}/media`)).map((item) => item.name));
208
+ for (const reference of collectReferences(def.fields, data)) {
209
+ if (reference.kind !== "media")
210
+ continue;
211
+ const match = /^([^/]+)\/([^/]+)\/media\/([^/]+)$/.exec(reference.path);
212
+ if (!match || match[1] !== collection || `${collection}/${match[2]}` === base)
213
+ continue;
214
+ if (moves.some((move) => move.from === reference.path))
215
+ continue;
216
+ const keep = (await workspace.read(dataPath(def, collection, match[2]))) !== null;
217
+ const bytes = await workspace.readBinary(reference.path);
218
+ if (bytes === null)
219
+ continue;
220
+ const extension = match[3].includes(".") ? match[3].slice(match[3].lastIndexOf(".")) : "";
221
+ const stem = match[3].slice(0, match[3].length - extension.length);
222
+ let name = match[3];
223
+ for (let n = 2; taken.has(name); n += 1)
224
+ name = `${stem}-${n}${extension}`;
225
+ taken.add(name);
226
+ moves.push({ from: reference.path, to: `${base}/media/${name}`, bytes, keep });
227
+ }
228
+ return moves;
229
+ };
154
230
  /** Slugs already taken in a collection, so a new one can be made unique. */
155
231
  const takenSlugs = async (def, name, except) => {
156
232
  const entries = await readCollection(workspace, def, name);
@@ -190,6 +266,16 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
190
266
  : uniqueSlug(requested, await takenSlugs(def, collectionName, input.slug));
191
267
  data[slugKey] = slug;
192
268
  const base = collectionEntryPath(def.fields, collectionName, slug);
269
+ // A new entry's uploads went into the folder of the URL it had then.
270
+ const staged = input.slug === undefined ? await stagedUploads(def, collectionName, data, base) : [];
271
+ if (staged.length > 0) {
272
+ const moved = new Map(staged.map((item) => [item.from, item.to]));
273
+ Object.assign(data, mapMedia(def.fields, data, (value) => (typeof value === "string" ? (moved.get(value) ?? value) : value)));
274
+ }
275
+ // An entry that links to itself follows its own new URL.
276
+ const own = previousLinks(input, slug) ? relinkReferences(def.fields, data, collectionName, input.slug, slug) : null;
277
+ if (own)
278
+ Object.assign(data, own);
193
279
  const previousBase = input.slug && input.slug !== slug
194
280
  ? collectionEntryPath(def.fields, collectionName, input.slug)
195
281
  : null;
@@ -202,6 +288,11 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
202
288
  path,
203
289
  contents,
204
290
  }));
291
+ for (const item of staged) {
292
+ changes.push({ type: "write", path: item.to, contents: item.bytes });
293
+ if (!item.keep)
294
+ changes.push({ type: "delete", path: item.from });
295
+ }
205
296
  if (previousBase) {
206
297
  changes.push(entryLayout(def.fields) === "directory"
207
298
  ? { type: "deleteTree", path: previousBase }
@@ -214,13 +305,44 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
214
305
  else if (entryLayout(def.fields) === "directory") {
215
306
  changes.push(...(await staleDocuments(base, new Set(Object.keys(serialized.documents)))));
216
307
  }
308
+ // Only the data file of each referrer changes; its rich text and media stay as they are.
309
+ // A read-only part is left alone: this build may not write it.
310
+ let relinked = 0;
311
+ const referrerRevisions = {};
312
+ if (previousLinks(input, slug)) {
313
+ for (const found of await referrers(collectionName, input.slug)) {
314
+ if (found.referrer.readOnly)
315
+ continue;
316
+ if (found.referrer.kind === "entry" && found.referrer.collection === collectionName && found.referrer.slug === input.slug)
317
+ continue;
318
+ // The revision before the read: a save in between makes the write refuse, not overwrite it.
319
+ const revision = await workspace.revision(found.dataFile);
320
+ const current = await found.read();
321
+ const next = current && relinkReferences(found.fields, current, collectionName, input.slug, slug);
322
+ if (!next)
323
+ continue;
324
+ const written = entryFiles(found.fields, found.base, serializeEntry(found.fields, mediaForDisk(found.fields, next, found.base)));
325
+ changes.push({ type: "write", path: found.dataFile, contents: written[found.dataFile] });
326
+ referrerRevisions[found.dataFile] = revision;
327
+ relinked += 1;
328
+ }
329
+ }
217
330
  const title = entryTitle(def, data);
218
- await workspace.apply({
219
- changes,
220
- expect: await expectDocument(input.expectedRevision, dataPath(def, collectionName, input.slug ?? slug), folderOf(def.fields, previousBase ?? base)),
221
- });
331
+ const expected = await expectDocument(input.expectedRevision, dataPath(def, collectionName, input.slug ?? slug), folderOf(def.fields, previousBase ?? base));
332
+ try {
333
+ await workspace.apply({
334
+ changes,
335
+ expect: relinked > 0 ? { ...expected, ...referrerRevisions } : expected,
336
+ });
337
+ }
338
+ catch (error) {
339
+ if (error instanceof ConflictError && error.paths.some((path) => path in referrerRevisions)) {
340
+ throw new ConflictError("An entry or page that links here changed while you saved. Save again.", error.paths);
341
+ }
342
+ throw error;
343
+ }
222
344
  const revision = await revisionAfterWrite(dataPath(def, collectionName, slug), folderOf(def.fields, base));
223
- return { slug, title, revision, renamed: previousBase !== null };
345
+ return { slug, title, revision, renamed: previousBase !== null, relinked };
224
346
  },
225
347
  async remove(collectionName, slug, options = {}) {
226
348
  const def = writable(requireCollection(collectionName), `collections.${collectionName}`);
@@ -275,22 +397,110 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
275
397
  const def = requireCollection(collectionName);
276
398
  return uniqueSlug(slugify(source), await takenSlugs(def, collectionName, except));
277
399
  },
278
- /** Entries pointing at `slug`, so the editor can warn before a delete. */
279
- async findReferences(collectionName, slug) {
280
- const results = [];
400
+ /**
401
+ * Where each file is used: in a media field, or as an image or a link in rich
402
+ * text. A file that something uses is not deleted.
403
+ */
404
+ async mediaUses(paths) {
405
+ const wanted = new Set(paths);
406
+ /** Collections and settings that could not be read: a use there cannot be ruled out. */
407
+ const unreadable = [];
408
+ const uses = Object.fromEntries(paths.map((path) => [path, []]));
409
+ const check = (referrer, fields, data, folder) => {
410
+ const found = new Set();
411
+ for (const reference of collectReferences(fields, data)) {
412
+ if (reference.kind === "media" && wanted.has(reference.path))
413
+ found.add(reference.path);
414
+ }
415
+ for (const text of richTextValues(fields, data)) {
416
+ for (const target of markdocTargets(text)) {
417
+ const path = resolveTarget(target, folder);
418
+ if (path && wanted.has(path))
419
+ found.add(path);
420
+ }
421
+ }
422
+ for (const path of found)
423
+ uses[path].push(referrer);
424
+ };
281
425
  for (const [name, def] of Object.entries(site.collections)) {
282
- if (!linksTo(def.fields, collectionName))
426
+ const entries = await readCollection(workspace, def, name).catch(() => null);
427
+ if (!entries) {
428
+ unreadable.push(def.label);
283
429
  continue;
284
- for (const entry of await readCollection(workspace, def, name)) {
285
- const points = collectReferences(def.fields, entry.data).some((reference) => reference.kind === "relation" && reference.collection === collectionName && reference.slug === slug);
286
- if (points) {
287
- results.push({ collection: name, slug: entry.slug, title: entryTitle(def, entry.data) });
288
- }
289
430
  }
431
+ for (const entry of entries) {
432
+ const base = collectionEntryPath(def.fields, name, entry.slug);
433
+ const folder = entryLayout(def.fields) === "directory" ? base : name;
434
+ check({ kind: "entry", collection: name, slug: entry.slug, title: entryTitle(def, entry.data) }, def.fields, entry.data, folder);
435
+ }
436
+ }
437
+ for (const [name, def] of Object.entries(site.singletons)) {
438
+ const data = await readSingleton(workspace, def, name).catch(() => undefined);
439
+ if (data === undefined)
440
+ unreadable.push(def.label);
441
+ if (!data)
442
+ continue;
443
+ const folder = entryLayout(def.fields) === "directory" ? singletonPath(def.fields, name) : "";
444
+ check({ kind: "singleton", name, title: def.label }, def.fields, data, folder);
290
445
  }
291
- return results;
446
+ return { uses, unreadable };
292
447
  },
448
+ /** Entries and settings pages that link to `slug`: the warning before a delete or a URL change. */
449
+ async findReferences(collectionName, slug) {
450
+ requireCollection(collectionName);
451
+ return (await referrers(collectionName, slug)).map((found) => found.referrer);
452
+ },
453
+ };
454
+ }
455
+ /** Whether a save is a URL change whose links should follow. */
456
+ function previousLinks(input, slug) {
457
+ return input.updateLinks === true && input.slug !== undefined && input.slug !== slug;
458
+ }
459
+ /** Every rich-text value in a payload, nested fields included. */
460
+ function richTextValues(fields, data) {
461
+ const found = [];
462
+ const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
463
+ const visit = (map, record) => {
464
+ for (const [key, field] of Object.entries(map)) {
465
+ const value = record[key];
466
+ if (field.kind === "richText" && typeof value === "string")
467
+ found.push(value);
468
+ else if (field.kind === "object" && isRecord(value))
469
+ visit(field.fields, value);
470
+ else if (field.kind === "repeater" && Array.isArray(value))
471
+ for (const row of value)
472
+ if (isRecord(row))
473
+ visit(field.fields, row);
474
+ }
293
475
  };
476
+ visit(fields, data);
477
+ return found;
478
+ }
479
+ /**
480
+ * The targets of the images and links in Markdoc: `![alt](media/x.jpg)`,
481
+ * `[Price list](/media/prices.pdf)`, a reference `[x]: media/x.jpg`, and a tag
482
+ * attribute such as `{% figure src="media/x.jpg" %}`.
483
+ */
484
+ function markdocTargets(text) {
485
+ const inline = [...text.matchAll(/\]\(\s*<?([^)\s>]+)/g)].map((match) => match[1]);
486
+ const references = [...text.matchAll(/^\s{0,3}\[[^\]]+\]:\s*<?([^\s>]+)/gm)].map((match) => match[1]);
487
+ const attributes = [...text.matchAll(/\{%[^%]*%\}/g)].flatMap((tag) => [...tag[0].matchAll(/\b[\w-]+\s*=\s*"([^"]+)"/g)].map((match) => match[1]));
488
+ return [...inline, ...references, ...attributes];
489
+ }
490
+ /** A rich-text target as a content path: from the content root with `/`, else from the document's folder. */
491
+ function resolveTarget(target, folder) {
492
+ if (/^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith("#"))
493
+ return null;
494
+ let decoded;
495
+ try {
496
+ decoded = decodeURI(target.split(/[?#]/)[0]);
497
+ }
498
+ catch {
499
+ return null;
500
+ }
501
+ const joined = decoded.startsWith("/") ? decoded.slice(1) : folder === "" ? decoded : `${folder}/${decoded}`;
502
+ const normal = posix.normalize(joined);
503
+ return normal.startsWith("..") ? null : normal;
294
504
  }
295
505
  /** Whether a field map has a relation to `collection`, nested fields included. */
296
506
  function linksTo(fields, collection) {
@@ -316,7 +526,10 @@ function singletonDataPath(def, name) {
316
526
  function pickSlug(def, data, current) {
317
527
  const slugKey = resolveSlugField(def);
318
528
  const fromData = data[slugKey];
319
- const candidate = (typeof fromData === "string" && fromData.trim()) || slugify(entryTitle(def, data));
529
+ // Not `entryTitle`, whose "Untitled" would give every entry without a title the same URL.
530
+ const titleKey = resolveTitleField(def);
531
+ const title = titleKey ? data[titleKey] : undefined;
532
+ const candidate = (typeof fromData === "string" && fromData.trim()) || (typeof title === "string" ? slugify(title.trim()) : "");
320
533
  if (!candidate) {
321
534
  throw new ContentError("An entry needs a title or a URL before it can be saved.");
322
535
  }