@micacms/runtime 0.5.0 → 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.js +2 -2
- 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 +3 -2
- package/dist/routes/admin-content.js.map +1 -1
- package/dist/routes/admin-system.js +4 -4
- package/dist/routes/admin-system.js.map +1 -1
- package/dist/schema.d.ts +6 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +19 -1
- 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.map +1 -1
- package/dist/services/content-store.js +9 -10
- package/dist/services/content-store.js.map +1 -1
- package/dist/services/staging.d.ts.map +1 -1
- package/dist/services/staging.js +7 -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/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":"AAAA,mGAAmG;AAGnG,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,cAAc,GAOf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAkH9C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAW,EACX,SAAwB,EACxB,MAAqB,EACrB,MAAqB;IAErB,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG;YAAE,MAAM,UAAU,CAAC,iCAAiC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7E,OAAO;YACL,MAAM,EAAE,MAAM,aAAa,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC;YACxD,KAAK,EAAE,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC;SACrD,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,MAAM,UAAU,CAAC,kCAAkC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;IAEpF,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBAAE,MAAM,UAAU,CAAC,IAAI,IAAI,kBAAkB,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAClF,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACrF,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QAEnD,yEAAyE;QACzE,2BAA2B;QAC3B,OAAO;YACL,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC;YAC9D,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;SACnE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAqB,EAAE,EAAE;QAC/C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC1F,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAW,EACX,MAAqB,EACrB,OAAwB;AACxB,uEAAuE;AACvE,SAAyB;IAEzB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAgD,CAAC;IAEpF,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzE,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACtC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0EAA0E;YAC1E,wBAAwB;YACxB,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI,IAAI,IAAI,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAG,KAAe,CAAC,OAAO,EAAE,CAAC;aAC1D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChD,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACjC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAEnB,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC;YACjD,kDAAkD;YAClD,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxE,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC;oBACtD,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACnD,MAAM,EAAE,MAAM,CAAC,MAAM;iBACtB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC5E,SAAS,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAEzC,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1D,IAAI,IAAI,KAAK,IAAI;oBAAE,SAAS;gBAE5B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC3D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC7E,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAG,KAA2B,CAAC,IAAI,IAAI,QAAQ;oBACnD,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAG,KAAe,CAAC,OAAO,EAAE,CAAC;iBAC1D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAmB,EAAE;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC;YACjD,OAAO;gBACL,GAAG,MAAM;gBACT,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE;aACzG,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,QAAQ,EAAE;KACpD,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,KAAW,EAAE,IAAY;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,UAAU,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;YACxD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;QAClF,CAAC;QAED,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,WAAW,EAAE,CAAC;YACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;YAC1B,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAChE,CAAC;QACH,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpD,IAAI,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACvF,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,YAAY,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;IACtE,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAEtE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,+FAA+F;AAC/F,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAC1B,KAAW,EACX,SAAwB,EACxB,MAAqB,EACrB,OAAwB,EACxB,MAAqB,EACrB,MAAyD;IAEzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmD,CAAC;IACzE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAsC,CAAC;QAC5F,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAyB,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,IAAI,GAAG,CAAC,IAA2B,EAAE,EAAE,CAC3C,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACzF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE3D,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAE,CAAC;QACrC,IAAI,MAA4C,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3D,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAuD,EAAE,CAAC;QAC1E,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzB,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,CAAC,CAAC;gBACnG,IAAI,KAAK,IAAI,iBAAiB;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YACpD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAEnC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,mBAAmB,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACzF,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAE,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,mBAAmB,EAAE,CAAC;gBAC/E,MAAM,CAAC,KAAK,CAAC,GAAG;oBACd,IAAI,EAAE,OAAO;oBACb,UAAU,EAAE,IAAI;oBAChB,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,OAAO;oBACd,WAAW,EAAE,SAAS;iBACvB,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;gBAAE,OAAO;YAChE,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,EAAE;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YACnF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,kBAAkB,CAC/B,SAAwB,EACxB,MAAqB,EACrB,IAAY,EACZ,SAA8B;IAE9B,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,CAAC,GAAkB,EAAW,EAAE;QAC5C,IAAI,CAAC;YACH,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAElE,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAa,EAAE,EAAE,CAC5C,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAChE,CAAC;IACF,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,6FAA6F;AAC7F,SAAS,UAAU,CAAC,GAAe,EAAE,CAA0B,EAAE,CAA0B;IACzF,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,IAAI,GAAG,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS;QACtE,QAAQ,IAAI,CAAC,CAAC;QACd,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,SAAS,MAAM,CACb,GAAe,EACf,UAAkB,EAClB,IAAY,EACZ,IAA6B;IAE7B,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW;QAC5C,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QACtF,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED,iEAAiE;AACjE,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,KAAc,EAAE,EAAE;QAC5D,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACtF,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,GAAe,EAAE,IAAY,EAAE,KAAyB;IAC1E,OAAO,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,aAAa,CAAC,GAAe,EAAE,UAAkB,EAAE,IAAY;IACtE,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/D,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAA0B;IACzD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,OAAO,iEAAiE,MAAM,GAAG,CAAC;AACpF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAA0B;IACxD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAClC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI;QAClE,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC","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":"AAAA,mGAAmG;AAGnG,OAAO,EACL,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EACX,UAAU,EACV,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,cAAc,GAQf,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAkH9C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAW,EACX,SAAwB,EACxB,MAAqB,EACrB,MAAqB;IAErB,sFAAsF;IACtF,MAAM,MAAM,GAAG,CAAI,IAAsB,EAAqB,EAAE,CAC9D,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAElG,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,GAAG;YAAE,MAAM,UAAU,CAAC,iCAAiC,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC;QAC7E,OAAO;YACL,MAAM,EAAE,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACtE,KAAK,EAAE,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC;SACrD,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,MAAM,UAAU,CAAC,kCAAkC,MAAM,CAAC,UAAU,IAAI,CAAC,CAAC;IAEpF,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;gBAAE,MAAM,UAAU,CAAC,IAAI,IAAI,kBAAkB,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QACrF,IAAI,IAAI,KAAK,MAAM,CAAC,IAAI;YAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;QAExD,yEAAyE;QACzE,2BAA2B;QAC3B,OAAO;YACL,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC;YACxD,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC;SACnE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAqB,EAAE,EAAE;QAC/C,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SAC1F,CAAC;IACJ,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC5F,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAW,EACX,MAAqB,EACrB,OAAwB;AACxB,uEAAuE;AACvE,SAAyB;IAEzB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAEzE,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC5C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAgD,CAAC;IAEpF,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAE,CAAC;QACrC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACzE,mBAAmB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACtC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0EAA0E;YAC1E,wBAAwB;YACxB,MAAM,IAAI,GAAI,KAA2B,CAAC,IAAI,IAAI,IAAI,CAAC;YACvD,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI;gBACJ,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAG,KAAe,CAAC,OAAO,EAAE,CAAC;aAC1D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;IACtF,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC3D,MAAM,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9C,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YAChD,IAAI,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YACjC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAEnB,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC;YACjD,kDAAkD;YAClD,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACxE,IAAI,CAAC,IAAI;gBAAE,SAAS;YAEpB,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;YAClG,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC;oBACtD,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACnD,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAC5E,SAAS,CAAC,GAAG,CAAC,aAAa,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAEzC,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC;YAC1C,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBAC1D,IAAI,IAAI,KAAK,IAAI;oBAAE,SAAS;gBAE5B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAChD,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;gBAClG,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;oBAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACrF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAG,KAA2B,CAAC,IAAI,IAAI,QAAQ;oBACnD,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAG,KAAe,CAAC,OAAO,EAAE,CAAC;iBAC1D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAEvG,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAmB,EAAE;QAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC;YACjD,OAAO;gBACL,GAAG,MAAM;gBACT,KAAK,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE;aACzG,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,OAAO,EAAE,SAAS;QAClB,UAAU,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,QAAQ,EAAE;KACpD,CAAC;AACJ,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,KAAW,EAAE,IAAY;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAE/B,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,UAAU,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,KAAK,UAAU,EAAE,CAAC;YACxD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,KAAK,SAAS,EAAE,CAAC;QAClF,CAAC;QAED,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,WAAW,EAAE,CAAC;YACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC;YAC1B,IAAI,QAAQ,CAAC,MAAM,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YAChE,CAAC;QACH,CAAC;aAAM,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACpD,IAAI,WAAW,CAAC,IAAI,CAAC;gBAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACvF,CAAC;IACH,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,YAAY,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,CAAC;IACtE,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,SAAS,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,EAAE,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC;IAEtE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACxC,CAAC;AAED,+FAA+F;AAC/F,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAE9B;;;;;GAKG;AACH,KAAK,UAAU,aAAa,CAC1B,KAAW,EACX,SAAwB,EACxB,MAAqB,EACrB,OAAwB,EACxB,MAAqB,EACrB,MAAyD;IAEzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAmD,CAAC;IACzE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO;QACnC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,EAAsC,CAAC;QAC5F,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAyB,CAAC;QACvE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAE,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,IAAI,GAAG,CAAC,IAA2B,EAAE,EAAE,CAC3C,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;QACzF,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACjE,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAE3D,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,CAAE,CAAC;QACrC,IAAI,MAA4C,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3D,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAuD,EAAE,CAAC;QAC1E,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;YAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzB,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC,CAAC,CAAC;gBACnG,IAAI,KAAK,IAAI,iBAAiB;oBAAE,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACxE,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YACpD,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxB,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAEnC,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5E,MAAM,mBAAmB,GAAG,MAAM,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QACzF,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAE,CAAC;QAEhD,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI,IAAI,mBAAmB,EAAE,CAAC;gBAC/E,MAAM,CAAC,KAAK,CAAC,GAAG;oBACd,IAAI,EAAE,OAAO;oBACb,UAAU,EAAE,IAAI;oBAChB,IAAI,EAAE,OAAO;oBACb,KAAK,EAAE,OAAO;oBACd,WAAW,EAAE,SAAS;iBACvB,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,UAAU,KAAK,IAAI;gBAAE,OAAO;YAChE,MAAM,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,EAAE;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YACnF,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,KAAK,UAAU,kBAAkB,CAC/B,SAAwB,EACxB,MAAqB,EACrB,IAAY,EACZ,SAA8B;IAE9B,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,CAAC,GAAkB,EAAW,EAAE;QAC5C,IAAI,CAAC;YACH,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAElE,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAa,EAAE,EAAE,CAC5C,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAChE,CAAC;IACF,OAAO,SAAS,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,6FAA6F;AAC7F,SAAS,UAAU,CAAC,GAAe,EAAE,CAA0B,EAAE,CAA0B;IACzF,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,IAAI,GAAG,KAAK,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAS;QACtE,QAAQ,IAAI,CAAC,CAAC;QACd,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,QAAQ,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,SAAS,MAAM,CACb,GAAe,EACf,UAAkB,EAClB,IAAY,EACZ,IAA6B;IAE7B,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW;QAC5C,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;QACtF,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC7B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED,iEAAiE;AACjE,SAAS,SAAS,CAAC,KAAc;IAC/B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,KAAc,EAAE,EAAE;QAC5D,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACtF,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,eAAe,CAC5B,KAAW,EACX,MAAqB,EACrB,OAAwB,EACxB,MAAqB,EACrB,mBAAsE,EACtE,MAA2B;IAE3B,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoE,CAAC;IACnG,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO;QACrC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,yEAAyE;YACzE,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC;YAC7C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC1D,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC7D,cAAc,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACrI,OAAO;YACT,CAAC;QACH,CAAC;QACD,uFAAuF;QACvF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IACH,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpE,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,MAAM,IAAI,GAAG,CAAC,MAAc,EAAE,IAAY,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjH,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,MAAgB,EAAE,IAA6B,EAAE,EAAE;QAC9E,KAAK,MAAM,SAAS,IAAI,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;YACxD,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC;YAC5G,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC;gBAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClF,CAAC;IACH,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5D,IAAI,OAAO,GAAG,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC;gBACH,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxG,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,CAAC,4DAA4D;YACxE,CAAC;QACH,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC3G,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACtE,IAAI,IAAI;YAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;IACtC,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,UAAU,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,MAAM,OAAO,GAAG,KAAK,EAAE,SAAS;YAC9B,CAAC,CAAC,yBAAyB,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,SAAS,UAAU,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,KAAK,CAAC,IAAI,wDAAwD;YAC3K,CAAC,CAAC,qBAAqB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,uDAAuD,CAAC;QAChG,QAAQ,CAAC,IAAI,CAAC;YACZ,IAAI,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM;YAC9E,KAAK,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;YAC9E,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,GAAe,EAAE,IAAY,EAAE,KAAyB;IAC1E,OAAO,GAAG,GAAG,CAAC,aAAa,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,IAAI,IAAI,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,aAAa,CAAC,GAAe,EAAE,UAAkB,EAAE,IAAY;IACtE,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/D,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAA0B;IACzD,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,OAAO,iEAAiE,MAAM,GAAG,CAAC;AACpF,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,eAAe,CAAC,QAA0B;IACxD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAClC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI;QAClE,OAAO,EAAE,KAAK,CAAC,OAAO;KACvB,CAAC,CAAC,CACJ,CAAC;AACJ,CAAC","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 +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"}
|
|
@@ -4,7 +4,7 @@
|
|
|
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, readCollection, readSingleton, relativizeMedia, resolveSlugField, serializeEntry, singletonPath, slugify, uniqueSlug, validateFields, withDefaults, } from "@micacms/core";
|
|
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";
|
|
8
8
|
import { ConflictError, FilesError } from "../workspace/files.js";
|
|
9
9
|
export class ContentError extends FilesError {
|
|
10
10
|
name = "ContentError";
|
|
@@ -42,7 +42,7 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
|
|
|
42
42
|
return def;
|
|
43
43
|
};
|
|
44
44
|
const validate = (def, data, what) => {
|
|
45
|
-
const result = validateFields(def.fields, data
|
|
45
|
+
const result = validateFields(def.fields, data);
|
|
46
46
|
if (!result.ok) {
|
|
47
47
|
throw new ValidationError(`${what} cannot be saved until these fields are correct.`, result.issues);
|
|
48
48
|
}
|
|
@@ -279,16 +279,10 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
|
|
|
279
279
|
async findReferences(collectionName, slug) {
|
|
280
280
|
const results = [];
|
|
281
281
|
for (const [name, def] of Object.entries(site.collections)) {
|
|
282
|
-
|
|
283
|
-
.filter(([, field]) => field.kind === "relation" && field.collection === collectionName)
|
|
284
|
-
.map(([key]) => key);
|
|
285
|
-
if (keys.length === 0)
|
|
282
|
+
if (!linksTo(def.fields, collectionName))
|
|
286
283
|
continue;
|
|
287
284
|
for (const entry of await readCollection(workspace, def, name)) {
|
|
288
|
-
const points =
|
|
289
|
-
const value = entry.data[key];
|
|
290
|
-
return Array.isArray(value) ? value.includes(slug) : value === slug;
|
|
291
|
-
});
|
|
285
|
+
const points = collectReferences(def.fields, entry.data).some((reference) => reference.kind === "relation" && reference.collection === collectionName && reference.slug === slug);
|
|
292
286
|
if (points) {
|
|
293
287
|
results.push({ collection: name, slug: entry.slug, title: entryTitle(def, entry.data) });
|
|
294
288
|
}
|
|
@@ -298,6 +292,11 @@ export function createContentStore(site, workspace, readOnly = new Map()) {
|
|
|
298
292
|
},
|
|
299
293
|
};
|
|
300
294
|
}
|
|
295
|
+
/** Whether a field map has a relation to `collection`, nested fields included. */
|
|
296
|
+
function linksTo(fields, collection) {
|
|
297
|
+
return Object.values(fields).some((field) => (field.kind === "relation" && field.collection === collection) ||
|
|
298
|
+
((field.kind === "object" || field.kind === "repeater") && linksTo(field.fields, collection)));
|
|
299
|
+
}
|
|
301
300
|
/**
|
|
302
301
|
* Media paths as the repository stores them. Only a directory entry has
|
|
303
302
|
* colocated media to relativize.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-store.js","sourceRoot":"","sources":["../../src/services/content-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Collections and singletons over the working tree's files: validation, slugs,\n * entry files and `_order.json`. An entry is its slug and path, Git keeps the\n * history, a save is a draft, and only Publish makes a commit.\n */\n\nimport { createHash } from \"node:crypto\";\n\nimport {\n ContentPathError,\n DOCUMENT_EXTENSION,\n INDEX_FILE,\n ORDER_FILE,\n collectionEntryPath,\n entryFiles,\n entryLayout,\n entryTitle,\n isValidSlug,\n readCollection,\n readSingleton,\n relativizeMedia,\n resolveSlugField,\n serializeEntry,\n singletonPath,\n slugify,\n uniqueSlug,\n validateFields,\n withDefaults,\n type Collection,\n type ContentEntry,\n type FieldIssue,\n type FieldMap,\n type Singleton,\n type Site,\n} from \"@micacms/core\";\n\nimport { ConflictError, FilesError, type ContentFiles, type FileChange } from \"../workspace/files.js\";\n\nexport class ContentError extends FilesError {\n override name = \"ContentError\";\n}\n\n/** A write to a collection or singleton that this build of Mica cannot interpret. */\nexport class ReadOnlyError extends ContentError {\n override name = \"ReadOnlyError\";\n}\n\nexport class ValidationError extends ContentError {\n override name = \"ValidationError\";\n constructor(\n message: string,\n readonly issues: FieldIssue[],\n ) {\n super(message);\n }\n}\n\nexport interface SaveEntryInput {\n data: Record<string, unknown>;\n /** The entry being replaced. Absent when creating. */\n slug?: string;\n /** The revision `slug` had when the editor opened it. Absent writes unconditionally. */\n expectedRevision?: string | null;\n}\n\nexport interface SavedEntry {\n slug: string;\n title: string;\n /** The entry's revision after this save, for the next save. A file revision, not a commit. */\n revision: string;\n /** True when the save moved the entry's files. */\n renamed: boolean;\n}\n\n/** `readOnly` comes from `readOnlyParts`: its collections and singletons are read, never written. */\nexport function createContentStore(site: Site, workspace: ContentFiles, readOnly: ReadonlyMap<string, string> = new Map()) {\n const requireCollection = (name: string): Collection => {\n const def = site.collections[name];\n if (!def) throw new ContentError(`Unknown collection \"${name}\".`);\n return def;\n };\n\n const requireSingleton = (name: string): Singleton => {\n const def = site.singletons[name];\n if (!def) throw new ContentError(`Unknown singleton \"${name}\".`);\n return def;\n };\n\n const writable = <T>(def: T, key: string): T => {\n const reason = readOnly.get(key);\n if (reason) throw new ReadOnlyError(reason);\n return def;\n };\n\n const validate = (\n def: Collection | Singleton,\n data: unknown,\n what: string,\n ): Record<string, unknown> => {\n const result = validateFields(def.fields, data, \"storage\");\n if (!result.ok) {\n throw new ValidationError(\n `${what} cannot be saved until these fields are correct.`,\n result.issues,\n );\n }\n return result.data as Record<string, unknown>;\n };\n\n /**\n * Deletes for `.mdoc` files not in `keep`, so a cleared rich-text field does\n * not come back on the next read. Media stays: an unreferenced upload is not\n * garbage.\n */\n const staleDocuments = async (base: string, keep: Set<string>): Promise<FileChange[]> => {\n const changes: FileChange[] = [];\n\n for (const item of await workspace.list(base)) {\n if (item.type !== \"file\") continue;\n if (!item.name.endsWith(DOCUMENT_EXTENSION)) continue;\n if (keep.has(item.name)) continue;\n changes.push({ type: \"delete\", path: `${base}/${item.name}` });\n }\n\n return changes;\n };\n\n /**\n * The document's revision after a write. Throws when the file is absent,\n * because an empty token could never match.\n */\n const revisionAfterWrite = async (dataFile: string, folder: string | null): Promise<string> => {\n const revision = await documentRevision(dataFile, folder);\n if (revision === null) {\n throw new ContentError(`Saved, but \"${dataFile}\" is not in the repository afterwards.`);\n }\n return revision;\n };\n\n /** The folder whose files make up one document, or null for a one-file document. */\n const folderOf = (fields: FieldMap, base: string): string | null =>\n entryLayout(fields) === \"directory\" ? base : null;\n\n /**\n * The revision of a whole document: its data file and, in the folder layout,\n * every file of its folder. A change to any one of them makes the next save a\n * conflict, not an overwrite.\n */\n const documentRevision = async (dataFile: string, folder: string | null): Promise<string | null> => {\n const files = await documentFiles(dataFile, folder);\n if (files === null) return null;\n if (folder === null) return files.get(dataFile)!;\n const parts = [...files].map(([path, revision]) => `${path}\\u0000${revision}`);\n return createHash(\"sha256\").update(parts.sort().join(\"\\n\")).digest(\"hex\").slice(0, 16);\n };\n\n /** Every file of a document with its revision, or null when its data file is absent. */\n const documentFiles = async (dataFile: string, folder: string | null): Promise<Map<string, string> | null> => {\n const data = await workspace.revision(dataFile);\n if (data === null) return null;\n const files = new Map([[dataFile, data]]);\n if (folder === null) return files;\n const walk = async (dir: string): Promise<void> => {\n for (const item of await workspace.list(dir)) {\n const child = `${dir}/${item.name}`;\n if (item.type === \"directory\") await walk(child);\n else files.set(child, (await workspace.revision(child)) ?? \"\");\n }\n };\n await walk(folder);\n return files;\n };\n\n /**\n * Refuses when the document changed since the editor read `expected`, and\n * returns every file's revision for `apply`, which checks them all again\n * under the write lock.\n */\n const expectDocument = async (\n expected: string | null | undefined,\n dataFile: string,\n folder: string | null,\n ): Promise<Record<string, string | null> | undefined> => {\n if (expected === undefined) return undefined;\n const files = await documentFiles(dataFile, folder);\n if ((await documentRevision(dataFile, folder)) !== expected) {\n throw new ConflictError(\"This content changed after you opened it. Reload to see the current version.\", [\n dataFile,\n ]);\n }\n return files === null ? { [dataFile]: null } : Object.fromEntries(files);\n };\n\n /**\n * The order file with a renamed slug in its old place, so the entry does not\n * drop to the end of the list.\n */\n const renameInOrder = async (name: string, from: string, to: string): Promise<FileChange | null> => {\n const path = `${name}/${ORDER_FILE}`;\n const raw = await workspace.read(path);\n if (raw === null) return null;\n\n let listed: unknown;\n try {\n listed = JSON.parse(raw);\n } catch {\n // Reading the collection reports a malformed order file; a save need not.\n return null;\n }\n if (!Array.isArray(listed) || !listed.includes(from)) return null;\n\n const order = listed.map((slug: unknown) => (slug === from ? to : slug));\n return { type: \"write\", path, contents: `${JSON.stringify(order, null, 2)}\\n` };\n };\n\n /** Slugs already taken in a collection, so a new one can be made unique. */\n const takenSlugs = async (def: Collection, name: string, except?: string): Promise<string[]> => {\n const entries = await readCollection(workspace, def, name);\n return entries.map((entry) => entry.slug).filter((slug) => slug !== except);\n };\n\n return {\n async list(collectionName: string): Promise<ContentEntry[]> {\n return readCollection(workspace, requireCollection(collectionName), collectionName);\n },\n\n async get(collectionName: string, slug: string): Promise<ContentEntry | null> {\n const entries = await this.list(collectionName);\n return entries.find((entry) => entry.slug === slug) ?? null;\n },\n\n /** The revision an editor must send back with a save of this entry. */\n async revisionOf(collectionName: string, slug: string): Promise<string | null> {\n const def = requireCollection(collectionName);\n return documentRevision(\n dataPath(def, collectionName, slug),\n folderOf(def.fields, collectionEntryPath(def.fields, collectionName, slug)),\n );\n },\n\n /** The revision an editor must send back with a save of this singleton. */\n async revisionOfSingleton(name: string): Promise<string | null> {\n const def = requireSingleton(name);\n return documentRevision(singletonDataPath(def, name), folderOf(def.fields, singletonPath(def.fields, name)));\n },\n\n async getSingleton(name: string): Promise<Record<string, unknown>> {\n const def = requireSingleton(name);\n const data = await readSingleton(workspace, def, name);\n // A singleton with no file yet opens as an empty form, not a 404.\n return withDefaults(def.fields, data);\n },\n\n async save(collectionName: string, input: SaveEntryInput): Promise<SavedEntry> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n const data = validate(def, input.data, \"This entry\");\n\n const slugKey = resolveSlugField(def)!;\n const requested = pickSlug(def, data, input.slug);\n const slug =\n requested === input.slug\n ? requested\n : uniqueSlug(requested, await takenSlugs(def, collectionName, input.slug));\n\n data[slugKey] = slug;\n\n const base = collectionEntryPath(def.fields, collectionName, slug);\n const previousBase =\n input.slug && input.slug !== slug\n ? collectionEntryPath(def.fields, collectionName, input.slug)\n : null;\n\n // The editor uses root-relative media paths; store them relative to the\n // entry (the old directory on a rename).\n const serialized = serializeEntry(\n def.fields,\n mediaForDisk(def.fields, data, previousBase ?? base),\n );\n const files = entryFiles(def.fields, base, serialized);\n\n const changes: FileChange[] = Object.entries(files).map(([path, contents]) => ({\n type: \"write\",\n path,\n contents,\n }));\n\n if (previousBase) {\n changes.push(\n entryLayout(def.fields) === \"directory\"\n ? { type: \"deleteTree\", path: previousBase }\n : { type: \"delete\", path: previousBase },\n );\n await moveMedia(workspace, previousBase, base, changes);\n const order = await renameInOrder(collectionName, input.slug!, slug);\n if (order) changes.push(order);\n } else if (entryLayout(def.fields) === \"directory\") {\n changes.push(...(await staleDocuments(base, new Set(Object.keys(serialized.documents)))));\n }\n\n const title = entryTitle(def, data);\n await workspace.apply({\n changes,\n expect: await expectDocument(\n input.expectedRevision,\n dataPath(def, collectionName, input.slug ?? slug),\n folderOf(def.fields, previousBase ?? base),\n ),\n });\n\n const revision = await revisionAfterWrite(dataPath(def, collectionName, slug), folderOf(def.fields, base));\n\n return { slug, title, revision, renamed: previousBase !== null };\n },\n\n async remove(\n collectionName: string,\n slug: string,\n options: { expectedRevision?: string | null } = {},\n ): Promise<void> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n // One path segment: an empty slug or `a/b` would delete more than one entry.\n if (slug === \"\" || slug.includes(\"/\") || slug === \".\" || slug === \"..\") {\n throw new ContentPathError(`\"${slug}\" is not an entry of \"${collectionName}\".`);\n }\n const base = collectionEntryPath(def.fields, collectionName, slug);\n\n await workspace.apply({\n changes: [\n entryLayout(def.fields) === \"directory\"\n ? { type: \"deleteTree\", path: base }\n : { type: \"delete\", path: base },\n ],\n expect: await expectDocument(\n options.expectedRevision,\n dataPath(def, collectionName, slug),\n folderOf(def.fields, base),\n ),\n });\n },\n\n async saveSingleton(\n name: string,\n payload: unknown,\n options: { expectedRevision?: string | null } = {},\n ): Promise<{ revision: string }> {\n const def = writable(requireSingleton(name), `singletons.${name}`);\n const data = validate(def, payload, `The \"${def.label}\" settings`);\n\n const base = singletonPath(def.fields, name);\n const serialized = serializeEntry(def.fields, mediaForDisk(def.fields, data, base));\n\n const changes: FileChange[] = Object.entries(entryFiles(def.fields, base, serialized)).map(\n ([path, contents]) => ({ type: \"write\", path, contents }),\n );\n\n if (entryLayout(def.fields) === \"directory\") {\n changes.push(...(await staleDocuments(base, new Set(Object.keys(serialized.documents)))));\n }\n\n await workspace.apply({\n changes,\n expect: await expectDocument(options.expectedRevision, singletonDataPath(def, name), folderOf(def.fields, base)),\n });\n\n return { revision: await revisionAfterWrite(singletonDataPath(def, name), folderOf(def.fields, base)) };\n },\n\n async reorder(\n collectionName: string,\n slugs: string[],\n ): Promise<void> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n if (!def.sortable) {\n throw new ContentError(`The \"${def.label}\" collection is not manually sortable.`);\n }\n\n await workspace.apply({\n changes: [\n {\n type: \"write\",\n path: `${collectionName}/${ORDER_FILE}`,\n contents: `${JSON.stringify(slugs, null, 2)}\\n`,\n },\n ],\n });\n },\n\n requireCollection,\n requireSingleton,\n\n /** Slug the server would assign, so the editor's URL preview matches it. */\n async suggestSlug(collectionName: string, source: string, except?: string): Promise<string> {\n const def = requireCollection(collectionName);\n return uniqueSlug(slugify(source), await takenSlugs(def, collectionName, except));\n },\n\n /** Entries pointing at `slug`, so the editor can warn before a delete. */\n async findReferences(collectionName: string, slug: string) {\n const results: Array<{ collection: string; slug: string; title: string }> = [];\n\n for (const [name, def] of Object.entries(site.collections)) {\n const keys = Object.entries(def.fields)\n .filter(([, field]) => field.kind === \"relation\" && field.collection === collectionName)\n .map(([key]) => key);\n\n if (keys.length === 0) continue;\n\n for (const entry of await readCollection(workspace, def, name)) {\n const points = keys.some((key) => {\n const value = entry.data[key];\n return Array.isArray(value) ? value.includes(slug) : value === slug;\n });\n if (points) {\n results.push({ collection: name, slug: entry.slug, title: entryTitle(def, entry.data) });\n }\n }\n }\n\n return results;\n },\n };\n}\n\nexport type ContentStore = ReturnType<typeof createContentStore>;\n\n/**\n * Media paths as the repository stores them. Only a directory entry has\n * colocated media to relativize.\n */\nfunction mediaForDisk(\n fields: FieldMap,\n data: Record<string, unknown>,\n base: string,\n): Record<string, unknown> {\n return entryLayout(fields) === \"directory\" ? relativizeMedia(fields, data, base) : data;\n}\n\nfunction dataPath(def: Collection, collection: string, slug: string): string {\n const base = collectionEntryPath(def.fields, collection, slug);\n return entryLayout(def.fields) === \"directory\" ? `${base}/${INDEX_FILE}` : base;\n}\n\nfunction singletonDataPath(def: Singleton, name: string): string {\n const base = singletonPath(def.fields, name);\n return entryLayout(def.fields) === \"directory\" ? `${base}/${INDEX_FILE}` : base;\n}\n\n/** The slug from the payload's slug field, or one derived from the title. */\nfunction pickSlug(\n def: Collection,\n data: Record<string, unknown>,\n current: string | undefined,\n): string {\n const slugKey = resolveSlugField(def)!;\n const fromData = data[slugKey];\n\n const candidate =\n (typeof fromData === \"string\" && fromData.trim()) || slugify(entryTitle(def, data));\n\n if (!candidate) {\n throw new ContentError(\"An entry needs a title or a URL before it can be saved.\");\n }\n\n const normalised = isValidSlug(candidate) ? candidate : slugify(candidate);\n if (!normalised) {\n throw new ContentError(`\"${candidate}\" cannot be turned into a URL. Use letters and numbers.`);\n }\n\n return normalised === current ? current : normalised;\n}\n\n/** Copies colocated media to the renamed entry; the caller deletes the old directory. */\nasync function moveMedia(\n files: ContentFiles,\n from: string,\n to: string,\n changes: FileChange[],\n): Promise<void> {\n for (const item of await files.list(`${from}/media`)) {\n if (item.type !== \"file\") continue;\n\n const bytes = await files.readBinary(`${from}/media/${item.name}`);\n if (bytes === null) continue;\n\n changes.push({ type: \"write\", path: `${to}/media/${item.name}`, contents: bytes });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"content-store.js","sourceRoot":"","sources":["../../src/services/content-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,EACV,UAAU,EACV,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,OAAO,EACP,UAAU,EACV,cAAc,EACd,YAAY,GAOb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,aAAa,EAAE,UAAU,EAAsC,MAAM,uBAAuB,CAAC;AAEtG,MAAM,OAAO,YAAa,SAAQ,UAAU;IACjC,IAAI,GAAG,cAAc,CAAC;CAChC;AAED,qFAAqF;AACrF,MAAM,OAAO,aAAc,SAAQ,YAAY;IACpC,IAAI,GAAG,eAAe,CAAC;CACjC;AAED,MAAM,OAAO,eAAgB,SAAQ,YAAY;IAIpC;IAHF,IAAI,GAAG,iBAAiB,CAAC;IAClC,YACE,OAAe,EACN,MAAoB;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,WAAM,GAAN,MAAM,CAAc;IAG/B,CAAC;CACF;AAmBD,qGAAqG;AACrG,MAAM,UAAU,kBAAkB,CAAC,IAAU,EAAE,SAAuB,EAAE,WAAwC,IAAI,GAAG,EAAE;IACvH,MAAM,iBAAiB,GAAG,CAAC,IAAY,EAAc,EAAE;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,YAAY,CAAC,uBAAuB,IAAI,IAAI,CAAC,CAAC;QAClE,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,gBAAgB,GAAG,CAAC,IAAY,EAAa,EAAE;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,YAAY,CAAC,sBAAsB,IAAI,IAAI,CAAC,CAAC;QACjE,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CAAI,GAAM,EAAE,GAAW,EAAK,EAAE;QAC7C,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM;YAAE,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC5C,OAAO,GAAG,CAAC;IACb,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,CACf,GAA2B,EAC3B,IAAa,EACb,IAAY,EACa,EAAE;QAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,IAAI,eAAe,CACvB,GAAG,IAAI,kDAAkD,EACzD,MAAM,CAAC,MAAM,CACd,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC,IAA+B,CAAC;IAChD,CAAC,CAAC;IAEF;;;;OAIG;IACH,MAAM,cAAc,GAAG,KAAK,EAAE,IAAY,EAAE,IAAiB,EAAyB,EAAE;QACtF,MAAM,OAAO,GAAiB,EAAE,CAAC;QAEjC,KAAK,MAAM,IAAI,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;gBAAE,SAAS;YACnC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC;gBAAE,SAAS;YACtD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YAClC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,kBAAkB,GAAG,KAAK,EAAE,QAAgB,EAAE,MAAqB,EAAmB,EAAE;QAC5F,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC1D,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,IAAI,YAAY,CAAC,eAAe,QAAQ,wCAAwC,CAAC,CAAC;QAC1F,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;IAEF,oFAAoF;IACpF,MAAM,QAAQ,GAAG,CAAC,MAAgB,EAAE,IAAY,EAAiB,EAAE,CACjE,WAAW,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAEpD;;;;OAIG;IACH,MAAM,gBAAgB,GAAG,KAAK,EAAE,QAAgB,EAAE,MAAqB,EAA0B,EAAE;QACjG,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAE,CAAC;QACjD,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,SAAS,QAAQ,EAAE,CAAC,CAAC;QAC/E,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzF,CAAC,CAAC;IAEF,wFAAwF;IACxF,MAAM,aAAa,GAAG,KAAK,EAAE,QAAgB,EAAE,MAAqB,EAAuC,EAAE;QAC3G,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;YAChD,KAAK,MAAM,IAAI,IAAI,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7C,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpC,IAAI,IAAI,CAAC,IAAI,KAAK,WAAW;oBAAE,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC;;oBAC5C,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC,CAAC;QACF,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF;;;;OAIG;IACH,MAAM,cAAc,GAAG,KAAK,EAC1B,QAAmC,EACnC,QAAgB,EAChB,MAAqB,EAC+B,EAAE;QACtD,IAAI,QAAQ,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC7C,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,MAAM,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC5D,MAAM,IAAI,aAAa,CAAC,8EAA8E,EAAE;gBACtG,QAAQ;aACT,CAAC,CAAC;QACL,CAAC;QACD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAC3E,CAAC,CAAC;IAEF;;;OAGG;IACH,MAAM,aAAa,GAAG,KAAK,EAAE,IAAY,EAAE,IAAY,EAAE,EAAU,EAA8B,EAAE;QACjG,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAE9B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,0EAA0E;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAElE,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAa,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAClF,CAAC,CAAC;IAEF,4EAA4E;IAC5E,MAAM,UAAU,GAAG,KAAK,EAAE,GAAe,EAAE,IAAY,EAAE,MAAe,EAAqB,EAAE;QAC7F,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;QAC3D,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IAC9E,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,cAAsB;YAC/B,OAAO,cAAc,CAAC,SAAS,EAAE,iBAAiB,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC,CAAC;QACtF,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,cAAsB,EAAE,IAAY;YAC5C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAChD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;QAC9D,CAAC;QAED,uEAAuE;QACvE,KAAK,CAAC,UAAU,CAAC,cAAsB,EAAE,IAAY;YACnD,MAAM,GAAG,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;YAC9C,OAAO,gBAAgB,CACrB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EACnC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC,CAC5E,CAAC;QACJ,CAAC;QAED,2EAA2E;QAC3E,KAAK,CAAC,mBAAmB,CAAC,IAAY;YACpC,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACnC,OAAO,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/G,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,IAAY;YAC7B,MAAM,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;YACnC,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YACvD,kEAAkE;YAClE,OAAO,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACxC,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,cAAsB,EAAE,KAAqB;YACtD,MAAM,GAAG,GAAG,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAAE,eAAe,cAAc,EAAE,CAAC,CAAC;YACzF,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YAErD,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAE,CAAC;YACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAClD,MAAM,IAAI,GACR,SAAS,KAAK,KAAK,CAAC,IAAI;gBACtB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAE/E,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YAErB,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;YACnE,MAAM,YAAY,GAChB,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;gBAC/B,CAAC,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC;gBAC7D,CAAC,CAAC,IAAI,CAAC;YAEX,wEAAwE;YACxE,yCAAyC;YACzC,MAAM,UAAU,GAAG,cAAc,CAC/B,GAAG,CAAC,MAAM,EACV,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,IAAI,IAAI,CAAC,CACrD,CAAC;YACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;YAEvD,MAAM,OAAO,GAAiB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC7E,IAAI,EAAE,OAAO;gBACb,IAAI;gBACJ,QAAQ;aACT,CAAC,CAAC,CAAC;YAEJ,IAAI,YAAY,EAAE,CAAC;gBACjB,OAAO,CAAC,IAAI,CACV,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW;oBACrC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE;oBAC5C,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAC3C,CAAC;gBACF,MAAM,SAAS,CAAC,SAAS,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;gBACxD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,cAAc,EAAE,KAAK,CAAC,IAAK,EAAE,IAAI,CAAC,CAAC;gBACrE,IAAI,KAAK;oBAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,CAAC;iBAAM,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,EAAE,CAAC;gBACnD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YACpC,MAAM,SAAS,CAAC,KAAK,CAAC;gBACpB,OAAO;gBACP,MAAM,EAAE,MAAM,cAAc,CAC1B,KAAK,CAAC,gBAAgB,EACtB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,EACjD,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,IAAI,IAAI,CAAC,CAC3C;aACF,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;YAE3G,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,KAAK,IAAI,EAAE,CAAC;QACnE,CAAC;QAED,KAAK,CAAC,MAAM,CACV,cAAsB,EACtB,IAAY,EACZ,UAAgD,EAAE;YAElD,MAAM,GAAG,GAAG,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAAE,eAAe,cAAc,EAAE,CAAC,CAAC;YACzF,6EAA6E;YAC7E,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBACvE,MAAM,IAAI,gBAAgB,CAAC,IAAI,IAAI,yBAAyB,cAAc,IAAI,CAAC,CAAC;YAClF,CAAC;YACD,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,CAAC,CAAC;YAEnE,MAAM,SAAS,CAAC,KAAK,CAAC;gBACpB,OAAO,EAAE;oBACP,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW;wBACrC,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE;wBACpC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE;iBACnC;gBACD,MAAM,EAAE,MAAM,cAAc,CAC1B,OAAO,CAAC,gBAAgB,EACxB,QAAQ,CAAC,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,EACnC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAC3B;aACF,CAAC,CAAC;QACL,CAAC;QAED,KAAK,CAAC,aAAa,CACjB,IAAY,EACZ,OAAgB,EAChB,UAAgD,EAAE;YAElD,MAAM,GAAG,GAAG,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,EAAE,cAAc,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,GAAG,CAAC,KAAK,YAAY,CAAC,CAAC;YAEnE,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;YAEpF,MAAM,OAAO,GAAiB,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,CACxF,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAC1D,CAAC;YAEF,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,EAAE,CAAC;gBAC5C,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5F,CAAC;YAED,MAAM,SAAS,CAAC,KAAK,CAAC;gBACpB,OAAO;gBACP,MAAM,EAAE,MAAM,cAAc,CAAC,OAAO,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;aACjH,CAAC,CAAC;YAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1G,CAAC;QAED,KAAK,CAAC,OAAO,CACX,cAAsB,EACtB,KAAe;YAEf,MAAM,GAAG,GAAG,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,EAAE,eAAe,cAAc,EAAE,CAAC,CAAC;YACzF,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAClB,MAAM,IAAI,YAAY,CAAC,QAAQ,GAAG,CAAC,KAAK,wCAAwC,CAAC,CAAC;YACpF,CAAC;YAED,MAAM,SAAS,CAAC,KAAK,CAAC;gBACpB,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,OAAO;wBACb,IAAI,EAAE,GAAG,cAAc,IAAI,UAAU,EAAE;wBACvC,QAAQ,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI;qBAChD;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,iBAAiB;QACjB,gBAAgB;QAEhB,4EAA4E;QAC5E,KAAK,CAAC,WAAW,CAAC,cAAsB,EAAE,MAAc,EAAE,MAAe;YACvE,MAAM,GAAG,GAAG,iBAAiB,CAAC,cAAc,CAAC,CAAC;YAC9C,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;QACpF,CAAC;QAED,0EAA0E;QAC1E,KAAK,CAAC,cAAc,CAAC,cAAsB,EAAE,IAAY;YACvD,MAAM,OAAO,GAA+D,EAAE,CAAC;YAE/E,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC3D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC;oBAAE,SAAS;gBACnD,KAAK,MAAM,KAAK,IAAI,MAAM,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC;oBAC/D,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAC3D,CAAC,SAAS,EAAE,EAAE,CACZ,SAAS,CAAC,IAAI,KAAK,UAAU,IAAI,SAAS,CAAC,UAAU,KAAK,cAAc,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,CACtG,CAAC;oBACF,IAAI,MAAM,EAAE,CAAC;wBACX,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBAC3F,CAAC;gBACH,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAID,kFAAkF;AAClF,SAAS,OAAO,CAAC,MAAgB,EAAE,UAAkB;IACnD,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAC/B,CAAC,KAAK,EAAE,EAAE,CACR,CAAC,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,KAAK,CAAC,UAAU,KAAK,UAAU,CAAC;QAC9D,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAChG,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CACnB,MAAgB,EAChB,IAA6B,EAC7B,IAAY;IAEZ,OAAO,WAAW,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1F,CAAC;AAED,SAAS,QAAQ,CAAC,GAAe,EAAE,UAAkB,EAAE,IAAY;IACjE,MAAM,IAAI,GAAG,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC;IAC/D,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClF,CAAC;AAED,SAAS,iBAAiB,CAAC,GAAc,EAAE,IAAY;IACrD,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7C,OAAO,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAClF,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CACf,GAAe,EACf,IAA6B,EAC7B,OAA2B;IAE3B,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IAE/B,MAAM,SAAS,GACb,CAAC,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAEtF,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,YAAY,CAAC,yDAAyD,CAAC,CAAC;IACpF,CAAC;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC3E,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CAAC,IAAI,SAAS,yDAAyD,CAAC,CAAC;IACjG,CAAC;IAED,OAAO,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;AACvD,CAAC;AAED,yFAAyF;AACzF,KAAK,UAAU,SAAS,CACtB,KAAmB,EACnB,IAAY,EACZ,EAAU,EACV,OAAqB;IAErB,KAAK,MAAM,IAAI,IAAI,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC;QACrD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QAEnC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,UAAU,CAAC,GAAG,IAAI,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAE7B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,UAAU,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IACrF,CAAC;AACH,CAAC","sourcesContent":["/**\n * Collections and singletons over the working tree's files: validation, slugs,\n * entry files and `_order.json`. An entry is its slug and path, Git keeps the\n * history, a save is a draft, and only Publish makes a commit.\n */\n\nimport { createHash } from \"node:crypto\";\n\nimport {\n ContentPathError,\n DOCUMENT_EXTENSION,\n INDEX_FILE,\n ORDER_FILE,\n collectionEntryPath,\n entryFiles,\n entryLayout,\n entryTitle,\n isValidSlug,\n collectReferences,\n readCollection,\n readSingleton,\n relativizeMedia,\n resolveSlugField,\n serializeEntry,\n singletonPath,\n slugify,\n uniqueSlug,\n validateFields,\n withDefaults,\n type Collection,\n type ContentEntry,\n type FieldIssue,\n type FieldMap,\n type Singleton,\n type Site,\n} from \"@micacms/core\";\n\nimport { ConflictError, FilesError, type ContentFiles, type FileChange } from \"../workspace/files.js\";\n\nexport class ContentError extends FilesError {\n override name = \"ContentError\";\n}\n\n/** A write to a collection or singleton that this build of Mica cannot interpret. */\nexport class ReadOnlyError extends ContentError {\n override name = \"ReadOnlyError\";\n}\n\nexport class ValidationError extends ContentError {\n override name = \"ValidationError\";\n constructor(\n message: string,\n readonly issues: FieldIssue[],\n ) {\n super(message);\n }\n}\n\nexport interface SaveEntryInput {\n data: Record<string, unknown>;\n /** The entry being replaced. Absent when creating. */\n slug?: string;\n /** The revision `slug` had when the editor opened it. Absent writes unconditionally. */\n expectedRevision?: string | null;\n}\n\nexport interface SavedEntry {\n slug: string;\n title: string;\n /** The entry's revision after this save, for the next save. A file revision, not a commit. */\n revision: string;\n /** True when the save moved the entry's files. */\n renamed: boolean;\n}\n\n/** `readOnly` comes from `readOnlyParts`: its collections and singletons are read, never written. */\nexport function createContentStore(site: Site, workspace: ContentFiles, readOnly: ReadonlyMap<string, string> = new Map()) {\n const requireCollection = (name: string): Collection => {\n const def = site.collections[name];\n if (!def) throw new ContentError(`Unknown collection \"${name}\".`);\n return def;\n };\n\n const requireSingleton = (name: string): Singleton => {\n const def = site.singletons[name];\n if (!def) throw new ContentError(`Unknown singleton \"${name}\".`);\n return def;\n };\n\n const writable = <T>(def: T, key: string): T => {\n const reason = readOnly.get(key);\n if (reason) throw new ReadOnlyError(reason);\n return def;\n };\n\n const validate = (\n def: Collection | Singleton,\n data: unknown,\n what: string,\n ): Record<string, unknown> => {\n const result = validateFields(def.fields, data);\n if (!result.ok) {\n throw new ValidationError(\n `${what} cannot be saved until these fields are correct.`,\n result.issues,\n );\n }\n return result.data as Record<string, unknown>;\n };\n\n /**\n * Deletes for `.mdoc` files not in `keep`, so a cleared rich-text field does\n * not come back on the next read. Media stays: an unreferenced upload is not\n * garbage.\n */\n const staleDocuments = async (base: string, keep: Set<string>): Promise<FileChange[]> => {\n const changes: FileChange[] = [];\n\n for (const item of await workspace.list(base)) {\n if (item.type !== \"file\") continue;\n if (!item.name.endsWith(DOCUMENT_EXTENSION)) continue;\n if (keep.has(item.name)) continue;\n changes.push({ type: \"delete\", path: `${base}/${item.name}` });\n }\n\n return changes;\n };\n\n /**\n * The document's revision after a write. Throws when the file is absent,\n * because an empty token could never match.\n */\n const revisionAfterWrite = async (dataFile: string, folder: string | null): Promise<string> => {\n const revision = await documentRevision(dataFile, folder);\n if (revision === null) {\n throw new ContentError(`Saved, but \"${dataFile}\" is not in the repository afterwards.`);\n }\n return revision;\n };\n\n /** The folder whose files make up one document, or null for a one-file document. */\n const folderOf = (fields: FieldMap, base: string): string | null =>\n entryLayout(fields) === \"directory\" ? base : null;\n\n /**\n * The revision of a whole document: its data file and, in the folder layout,\n * every file of its folder. A change to any one of them makes the next save a\n * conflict, not an overwrite.\n */\n const documentRevision = async (dataFile: string, folder: string | null): Promise<string | null> => {\n const files = await documentFiles(dataFile, folder);\n if (files === null) return null;\n if (folder === null) return files.get(dataFile)!;\n const parts = [...files].map(([path, revision]) => `${path}\\u0000${revision}`);\n return createHash(\"sha256\").update(parts.sort().join(\"\\n\")).digest(\"hex\").slice(0, 16);\n };\n\n /** Every file of a document with its revision, or null when its data file is absent. */\n const documentFiles = async (dataFile: string, folder: string | null): Promise<Map<string, string> | null> => {\n const data = await workspace.revision(dataFile);\n if (data === null) return null;\n const files = new Map([[dataFile, data]]);\n if (folder === null) return files;\n const walk = async (dir: string): Promise<void> => {\n for (const item of await workspace.list(dir)) {\n const child = `${dir}/${item.name}`;\n if (item.type === \"directory\") await walk(child);\n else files.set(child, (await workspace.revision(child)) ?? \"\");\n }\n };\n await walk(folder);\n return files;\n };\n\n /**\n * Refuses when the document changed since the editor read `expected`, and\n * returns every file's revision for `apply`, which checks them all again\n * under the write lock.\n */\n const expectDocument = async (\n expected: string | null | undefined,\n dataFile: string,\n folder: string | null,\n ): Promise<Record<string, string | null> | undefined> => {\n if (expected === undefined) return undefined;\n const files = await documentFiles(dataFile, folder);\n if ((await documentRevision(dataFile, folder)) !== expected) {\n throw new ConflictError(\"This content changed after you opened it. Reload to see the current version.\", [\n dataFile,\n ]);\n }\n return files === null ? { [dataFile]: null } : Object.fromEntries(files);\n };\n\n /**\n * The order file with a renamed slug in its old place, so the entry does not\n * drop to the end of the list.\n */\n const renameInOrder = async (name: string, from: string, to: string): Promise<FileChange | null> => {\n const path = `${name}/${ORDER_FILE}`;\n const raw = await workspace.read(path);\n if (raw === null) return null;\n\n let listed: unknown;\n try {\n listed = JSON.parse(raw);\n } catch {\n // Reading the collection reports a malformed order file; a save need not.\n return null;\n }\n if (!Array.isArray(listed) || !listed.includes(from)) return null;\n\n const order = listed.map((slug: unknown) => (slug === from ? to : slug));\n return { type: \"write\", path, contents: `${JSON.stringify(order, null, 2)}\\n` };\n };\n\n /** Slugs already taken in a collection, so a new one can be made unique. */\n const takenSlugs = async (def: Collection, name: string, except?: string): Promise<string[]> => {\n const entries = await readCollection(workspace, def, name);\n return entries.map((entry) => entry.slug).filter((slug) => slug !== except);\n };\n\n return {\n async list(collectionName: string): Promise<ContentEntry[]> {\n return readCollection(workspace, requireCollection(collectionName), collectionName);\n },\n\n async get(collectionName: string, slug: string): Promise<ContentEntry | null> {\n const entries = await this.list(collectionName);\n return entries.find((entry) => entry.slug === slug) ?? null;\n },\n\n /** The revision an editor must send back with a save of this entry. */\n async revisionOf(collectionName: string, slug: string): Promise<string | null> {\n const def = requireCollection(collectionName);\n return documentRevision(\n dataPath(def, collectionName, slug),\n folderOf(def.fields, collectionEntryPath(def.fields, collectionName, slug)),\n );\n },\n\n /** The revision an editor must send back with a save of this singleton. */\n async revisionOfSingleton(name: string): Promise<string | null> {\n const def = requireSingleton(name);\n return documentRevision(singletonDataPath(def, name), folderOf(def.fields, singletonPath(def.fields, name)));\n },\n\n async getSingleton(name: string): Promise<Record<string, unknown>> {\n const def = requireSingleton(name);\n const data = await readSingleton(workspace, def, name);\n // A singleton with no file yet opens as an empty form, not a 404.\n return withDefaults(def.fields, data);\n },\n\n async save(collectionName: string, input: SaveEntryInput): Promise<SavedEntry> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n const data = validate(def, input.data, \"This entry\");\n\n const slugKey = resolveSlugField(def)!;\n const requested = pickSlug(def, data, input.slug);\n const slug =\n requested === input.slug\n ? requested\n : uniqueSlug(requested, await takenSlugs(def, collectionName, input.slug));\n\n data[slugKey] = slug;\n\n const base = collectionEntryPath(def.fields, collectionName, slug);\n const previousBase =\n input.slug && input.slug !== slug\n ? collectionEntryPath(def.fields, collectionName, input.slug)\n : null;\n\n // The editor uses root-relative media paths; store them relative to the\n // entry (the old directory on a rename).\n const serialized = serializeEntry(\n def.fields,\n mediaForDisk(def.fields, data, previousBase ?? base),\n );\n const files = entryFiles(def.fields, base, serialized);\n\n const changes: FileChange[] = Object.entries(files).map(([path, contents]) => ({\n type: \"write\",\n path,\n contents,\n }));\n\n if (previousBase) {\n changes.push(\n entryLayout(def.fields) === \"directory\"\n ? { type: \"deleteTree\", path: previousBase }\n : { type: \"delete\", path: previousBase },\n );\n await moveMedia(workspace, previousBase, base, changes);\n const order = await renameInOrder(collectionName, input.slug!, slug);\n if (order) changes.push(order);\n } else if (entryLayout(def.fields) === \"directory\") {\n changes.push(...(await staleDocuments(base, new Set(Object.keys(serialized.documents)))));\n }\n\n const title = entryTitle(def, data);\n await workspace.apply({\n changes,\n expect: await expectDocument(\n input.expectedRevision,\n dataPath(def, collectionName, input.slug ?? slug),\n folderOf(def.fields, previousBase ?? base),\n ),\n });\n\n const revision = await revisionAfterWrite(dataPath(def, collectionName, slug), folderOf(def.fields, base));\n\n return { slug, title, revision, renamed: previousBase !== null };\n },\n\n async remove(\n collectionName: string,\n slug: string,\n options: { expectedRevision?: string | null } = {},\n ): Promise<void> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n // One path segment: an empty slug or `a/b` would delete more than one entry.\n if (slug === \"\" || slug.includes(\"/\") || slug === \".\" || slug === \"..\") {\n throw new ContentPathError(`\"${slug}\" is not an entry of \"${collectionName}\".`);\n }\n const base = collectionEntryPath(def.fields, collectionName, slug);\n\n await workspace.apply({\n changes: [\n entryLayout(def.fields) === \"directory\"\n ? { type: \"deleteTree\", path: base }\n : { type: \"delete\", path: base },\n ],\n expect: await expectDocument(\n options.expectedRevision,\n dataPath(def, collectionName, slug),\n folderOf(def.fields, base),\n ),\n });\n },\n\n async saveSingleton(\n name: string,\n payload: unknown,\n options: { expectedRevision?: string | null } = {},\n ): Promise<{ revision: string }> {\n const def = writable(requireSingleton(name), `singletons.${name}`);\n const data = validate(def, payload, `The \"${def.label}\" settings`);\n\n const base = singletonPath(def.fields, name);\n const serialized = serializeEntry(def.fields, mediaForDisk(def.fields, data, base));\n\n const changes: FileChange[] = Object.entries(entryFiles(def.fields, base, serialized)).map(\n ([path, contents]) => ({ type: \"write\", path, contents }),\n );\n\n if (entryLayout(def.fields) === \"directory\") {\n changes.push(...(await staleDocuments(base, new Set(Object.keys(serialized.documents)))));\n }\n\n await workspace.apply({\n changes,\n expect: await expectDocument(options.expectedRevision, singletonDataPath(def, name), folderOf(def.fields, base)),\n });\n\n return { revision: await revisionAfterWrite(singletonDataPath(def, name), folderOf(def.fields, base)) };\n },\n\n async reorder(\n collectionName: string,\n slugs: string[],\n ): Promise<void> {\n const def = writable(requireCollection(collectionName), `collections.${collectionName}`);\n if (!def.sortable) {\n throw new ContentError(`The \"${def.label}\" collection is not manually sortable.`);\n }\n\n await workspace.apply({\n changes: [\n {\n type: \"write\",\n path: `${collectionName}/${ORDER_FILE}`,\n contents: `${JSON.stringify(slugs, null, 2)}\\n`,\n },\n ],\n });\n },\n\n requireCollection,\n requireSingleton,\n\n /** Slug the server would assign, so the editor's URL preview matches it. */\n async suggestSlug(collectionName: string, source: string, except?: string): Promise<string> {\n const def = requireCollection(collectionName);\n return uniqueSlug(slugify(source), await takenSlugs(def, collectionName, except));\n },\n\n /** Entries pointing at `slug`, so the editor can warn before a delete. */\n async findReferences(collectionName: string, slug: string) {\n const results: Array<{ collection: string; slug: string; title: string }> = [];\n\n for (const [name, def] of Object.entries(site.collections)) {\n if (!linksTo(def.fields, collectionName)) continue;\n for (const entry of await readCollection(workspace, def, name)) {\n const points = collectReferences(def.fields, entry.data).some(\n (reference) =>\n reference.kind === \"relation\" && reference.collection === collectionName && reference.slug === slug,\n );\n if (points) {\n results.push({ collection: name, slug: entry.slug, title: entryTitle(def, entry.data) });\n }\n }\n }\n\n return results;\n },\n };\n}\n\nexport type ContentStore = ReturnType<typeof createContentStore>;\n\n/** Whether a field map has a relation to `collection`, nested fields included. */\nfunction linksTo(fields: FieldMap, collection: string): boolean {\n return Object.values(fields).some(\n (field) =>\n (field.kind === \"relation\" && field.collection === collection) ||\n ((field.kind === \"object\" || field.kind === \"repeater\") && linksTo(field.fields, collection)),\n );\n}\n\n/**\n * Media paths as the repository stores them. Only a directory entry has\n * colocated media to relativize.\n */\nfunction mediaForDisk(\n fields: FieldMap,\n data: Record<string, unknown>,\n base: string,\n): Record<string, unknown> {\n return entryLayout(fields) === \"directory\" ? relativizeMedia(fields, data, base) : data;\n}\n\nfunction dataPath(def: Collection, collection: string, slug: string): string {\n const base = collectionEntryPath(def.fields, collection, slug);\n return entryLayout(def.fields) === \"directory\" ? `${base}/${INDEX_FILE}` : base;\n}\n\nfunction singletonDataPath(def: Singleton, name: string): string {\n const base = singletonPath(def.fields, name);\n return entryLayout(def.fields) === \"directory\" ? `${base}/${INDEX_FILE}` : base;\n}\n\n/** The slug from the payload's slug field, or one derived from the title. */\nfunction pickSlug(\n def: Collection,\n data: Record<string, unknown>,\n current: string | undefined,\n): string {\n const slugKey = resolveSlugField(def)!;\n const fromData = data[slugKey];\n\n const candidate =\n (typeof fromData === \"string\" && fromData.trim()) || slugify(entryTitle(def, data));\n\n if (!candidate) {\n throw new ContentError(\"An entry needs a title or a URL before it can be saved.\");\n }\n\n const normalised = isValidSlug(candidate) ? candidate : slugify(candidate);\n if (!normalised) {\n throw new ContentError(`\"${candidate}\" cannot be turned into a URL. Use letters and numbers.`);\n }\n\n return normalised === current ? current : normalised;\n}\n\n/** Copies colocated media to the renamed entry; the caller deletes the old directory. */\nasync function moveMedia(\n files: ContentFiles,\n from: string,\n to: string,\n changes: FileChange[],\n): Promise<void> {\n for (const item of await files.list(`${from}/media`)) {\n if (item.type !== \"file\") continue;\n\n const bytes = await files.readBinary(`${from}/media/${item.name}`);\n if (bytes === null) continue;\n\n changes.push({ type: \"write\", path: `${to}/media/${item.name}`, contents: bytes });\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../src/services/staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"staging.d.ts","sourceRoot":"","sources":["../../src/services/staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAuE,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAG/G,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAUL,KAAK,OAAO,EAEb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAQL,KAAK,qBAAqB,EAK3B,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,IAAI,CAAC;IACZ;;;OAGG;IACH,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uFAAuF;IACvF,QAAQ,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,8DAA8D;IAC9D,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mDAAmD;IACnD,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,sBAAsB,GAC3B,qBAAqB,CAyPvB"}
|