@drawnagency/primitives 0.1.115 → 0.1.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/chunk-26V4YNOR.js +108 -0
  2. package/dist/chunk-DKZCHCV3.js +64 -0
  3. package/dist/chunk-FGQXHUAE.js +39 -0
  4. package/dist/{chunk-JXATNKLV.js → chunk-HUOW3ZV2.js} +5 -5
  5. package/dist/{chunk-FHTKSELX.js → chunk-LNQUUC2S.js} +5 -5
  6. package/dist/{chunk-T26HAKW3.js → chunk-MSBHMY3C.js} +34 -34
  7. package/dist/components/editor/index.js +8 -8
  8. package/dist/components/primitives/index.js +7 -7
  9. package/dist/components/sections/brand-guide.js +5 -5
  10. package/dist/components/sections/register-schemas.js +12 -12
  11. package/dist/components/sections/register.js +12 -12
  12. package/dist/deck-builder/index.js +8 -8
  13. package/dist/index.js +12 -12
  14. package/dist/lib/document-refs.d.ts +8 -0
  15. package/dist/lib/document-refs.d.ts.map +1 -1
  16. package/dist/lib/document-refs.js +14 -0
  17. package/dist/lib/index-ops.js +3 -3
  18. package/dist/lib/index.js +12 -12
  19. package/dist/lib/reference-validation.js +4 -33
  20. package/dist/lib/section-check.d.ts +49 -0
  21. package/dist/lib/section-check.d.ts.map +1 -0
  22. package/dist/lib/section-check.js +14 -0
  23. package/dist/lib/site-validation.d.ts +123 -0
  24. package/dist/lib/site-validation.d.ts.map +1 -0
  25. package/dist/lib/site-validation.js +230 -0
  26. package/package.json +14 -1
  27. package/src/lib/document-refs.ts +30 -0
  28. package/src/lib/section-check.ts +166 -0
  29. package/src/lib/site-validation.ts +269 -0
  30. package/dist/{chunk-NX7TFHA4.js → chunk-CEFFP3CH.js} +7 -7
  31. package/dist/{chunk-YV3YSZEB.js → chunk-L7WTERU5.js} +3 -3
  32. package/dist/{chunk-WYOFQHKI.js → chunk-W72BPGR3.js} +3 -3
@@ -3,6 +3,9 @@ import {
3
3
  deletePage,
4
4
  uniquePageSlug
5
5
  } from "../chunk-VY3IFGE3.js";
6
+ import {
7
+ slugifyPageSlug
8
+ } from "../chunk-S7QRHUWO.js";
6
9
  import {
7
10
  liftSidecars
8
11
  } from "../chunk-DJABADOO.js";
@@ -18,9 +21,6 @@ import {
18
21
  } from "../chunk-DJOGPSRU.js";
19
22
  import "../chunk-4WQ36ZP7.js";
20
23
  import "../chunk-GNF6KFQ7.js";
21
- import {
22
- slugifyPageSlug
23
- } from "../chunk-S7QRHUWO.js";
24
24
  import {
25
25
  IndexSchema,
26
26
  PageStatusSchema,
package/dist/lib/index.js CHANGED
@@ -19,32 +19,33 @@ import {
19
19
  import {
20
20
  formatTimestamp
21
21
  } from "../chunk-MM7KZS5C.js";
22
- import {
23
- generateNavLinks,
24
- resolveReference,
25
- toSectionId
26
- } from "../chunk-WYOFQHKI.js";
27
- import {
28
- ensureSanitizer,
29
- sanitizeHtml
30
- } from "../chunk-QQ3S3P5P.js";
31
22
  import {
32
23
  curatedIcons,
33
24
  getIcon
34
25
  } from "../chunk-WDSVHKAH.js";
35
- import "../chunk-L45X7DKX.js";
36
26
  import {
37
27
  createEvent,
38
28
  darkModeEvent,
39
29
  editModeEvent
40
30
  } from "../chunk-XDEPXTBY.js";
41
- import "../chunk-FRVO75DK.js";
31
+ import {
32
+ generateNavLinks,
33
+ resolveReference,
34
+ toSectionId
35
+ } from "../chunk-W72BPGR3.js";
36
+ import {
37
+ ensureSanitizer,
38
+ sanitizeHtml
39
+ } from "../chunk-QQ3S3P5P.js";
42
40
  import {
43
41
  cn
44
42
  } from "../chunk-7EOSDFS3.js";
45
43
  import {
46
44
  audiencePasses
47
45
  } from "../chunk-VY3IFGE3.js";
46
+ import "../chunk-S7QRHUWO.js";
47
+ import "../chunk-FRVO75DK.js";
48
+ import "../chunk-L45X7DKX.js";
48
49
  import {
49
50
  NAV,
50
51
  SIDECARS,
@@ -67,7 +68,6 @@ import {
67
68
  tagDisplayLabel
68
69
  } from "../chunk-4WQ36ZP7.js";
69
70
  import "../chunk-GNF6KFQ7.js";
70
- import "../chunk-S7QRHUWO.js";
71
71
  import {
72
72
  REFERENCE_TYPE
73
73
  } from "../chunk-5GFACGSN.js";
@@ -1,38 +1,9 @@
1
1
  import {
2
- REFERENCE_TYPE
3
- } from "../chunk-5GFACGSN.js";
2
+ describeReferenceProblem,
3
+ referenceProblems
4
+ } from "../chunk-FGQXHUAE.js";
5
+ import "../chunk-5GFACGSN.js";
4
6
  import "../chunk-JSBRDJBE.js";
5
-
6
- // src/lib/reference-validation.ts
7
- var strings = (v) => Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
8
- function referenceProblems(sections, index, tagIds) {
9
- const onPage = new Set(index.pages.flatMap((p) => p.order));
10
- const problems = [];
11
- for (const { id, content } of sections) {
12
- const body = content;
13
- if (body?.type !== REFERENCE_TYPE && index.sections[id]?.type !== REFERENCE_TYPE) continue;
14
- const picks = strings(body?.content?.sectionIds).filter((pick) => pick !== id);
15
- const problem = {
16
- referenceId: id,
17
- unknownTagIds: tagIds ? strings(body?.content?.tagIds).filter((tag) => !tagIds.has(tag)) : [],
18
- unplacedSectionIds: picks.filter((pick) => !onPage.has(pick)),
19
- referencePickIds: picks.filter((pick) => onPage.has(pick) && index.sections[pick]?.type === REFERENCE_TYPE),
20
- picksItself: strings(body?.content?.sectionIds).includes(id)
21
- };
22
- if (problem.unknownTagIds.length || problem.unplacedSectionIds.length || problem.referencePickIds.length || problem.picksItself) problems.push(problem);
23
- }
24
- return problems;
25
- }
26
- function describeReferenceProblem(problem) {
27
- const list = (ids) => ids.map((id) => `"${id}"`).join(", ");
28
- const issues = [
29
- ...problem.unknownTagIds.length ? [`tagIds ${list(problem.unknownTagIds)} are not tags on this site`] : [],
30
- ...problem.unplacedSectionIds.length ? [`sectionIds ${list(problem.unplacedSectionIds)} are not top-level sections on any page (a container's child cannot be picked \u2014 pick the container)`] : [],
31
- ...problem.referencePickIds.length ? [`sectionIds ${list(problem.referencePickIds)} are References, which a Reference never shows`] : [],
32
- ...problem.picksItself ? ["sectionIds includes the Reference itself"] : []
33
- ];
34
- return `Reference "${problem.referenceId}": ${issues.join("; ")}.`;
35
- }
36
7
  export {
37
8
  describeReferenceProblem,
38
9
  referenceProblems
@@ -0,0 +1,49 @@
1
+ import type { ZodError } from "zod";
2
+ /**
3
+ * The first thing wrong with one section body: schema, then layout envelope,
4
+ * then nesting depth, then a Reference nested in a container. Only ever one — a
5
+ * body failing its schema is not also judged on its layout — so the write
6
+ * path's `errors[0]` stays the first failure it has always reported.
7
+ */
8
+ export type SectionCheckError = {
9
+ code: "schema";
10
+ zodError: ZodError;
11
+ } | {
12
+ code: "layout";
13
+ zodError: ZodError;
14
+ } | {
15
+ code: "depth";
16
+ } | {
17
+ code: "reference-in-container";
18
+ };
19
+ /** HTML entities found in one plain-text field, by path relative to the section. */
20
+ export interface EntityFinding {
21
+ path: string;
22
+ entities: string[];
23
+ }
24
+ export interface SectionCheck {
25
+ /**
26
+ * No schema is registered for the type, so only the nesting bound was
27
+ * checked. Whether that is acceptable (a site-local type) or an unknown type
28
+ * is the caller's call.
29
+ */
30
+ unregistered: boolean;
31
+ error: SectionCheckError | null;
32
+ /** Empty whenever `error` is set. */
33
+ entities: EntityFinding[];
34
+ }
35
+ /**
36
+ * Judge one section body against `type`'s registered schema — the write path's
37
+ * (`validateContent`) per-section rules, kept here so the site validators can
38
+ * apply the same ones.
39
+ * Reads the schema registry only: callers register the schemas first.
40
+ */
41
+ export declare function checkSection(type: string, body: unknown): SectionCheck;
42
+ /**
43
+ * The write path's sentence for a section check's error, shared with
44
+ * validate_site so a dry run words a failure exactly as the save it predicts.
45
+ */
46
+ export declare function sectionCheckMessage(sectionId: string, type: string, code: SectionCheckError["code"]): string;
47
+ /** The write path's warning for one entity finding, shared with validate_site. */
48
+ export declare function entityWarning(sectionId: string, { path, entities }: EntityFinding): string;
49
+ //# sourceMappingURL=section-check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"section-check.d.ts","sourceRoot":"","sources":["../../src/lib/section-check.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAKpC;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,wBAAwB,CAAA;CAAE,CAAC;AAEvC,oFAAoF;AACpF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAChC,qCAAqC;IACrC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,YAAY,CAgCtE;AAsED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,MAAM,CAW5G;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,aAAa,GAAG,MAAM,CAG1F"}
@@ -0,0 +1,14 @@
1
+ import {
2
+ checkSection,
3
+ entityWarning,
4
+ sectionCheckMessage
5
+ } from "../chunk-26V4YNOR.js";
6
+ import "../chunk-5GFACGSN.js";
7
+ import "../chunk-FTPETWXE.js";
8
+ import "../chunk-NSCT3AMV.js";
9
+ import "../chunk-JSBRDJBE.js";
10
+ export {
11
+ checkSection,
12
+ entityWarning,
13
+ sectionCheckMessage
14
+ };
@@ -0,0 +1,123 @@
1
+ import { type SidecarDiagnostic } from "./sidecars";
2
+ import { type ReferenceProblem } from "./reference-validation";
3
+ /** What the caller can vouch for. Never how severe anything is — that is SEVERITY. */
4
+ export interface SiteValidationPolicy {
5
+ /**
6
+ * A section type with no registered schema: `trust` reads it as a real type
7
+ * this caller cannot check (the connector, whose branch index is the proof; a
8
+ * CLI whose site sections module failed to load), `reject` as an unknown type.
9
+ */
10
+ unregisteredTypes: "trust" | "reject";
11
+ }
12
+ export interface SchemaIssue {
13
+ path: readonly PropertyKey[];
14
+ message: string;
15
+ }
16
+ type ProblemBody = {
17
+ code: "index-missing";
18
+ } | {
19
+ code: "index-invalid";
20
+ detail: string;
21
+ } | {
22
+ code: "site-id-empty";
23
+ } | {
24
+ code: "site-config-missing";
25
+ } | {
26
+ code: "site-config-invalid";
27
+ detail: string;
28
+ } | {
29
+ code: "sidecar-degraded";
30
+ sidecar: "nav" | "tags";
31
+ } | {
32
+ code: "sidecar-repaired";
33
+ sidecar: "nav" | "tags";
34
+ diagnostic: SidecarDiagnostic;
35
+ } | {
36
+ code: "image-manifest-invalid";
37
+ detail: string;
38
+ } | {
39
+ code: "document-manifest-invalid";
40
+ detail: string;
41
+ } | {
42
+ code: "section-missing";
43
+ sectionId: string;
44
+ } | {
45
+ code: "section-unparseable";
46
+ sectionId: string;
47
+ detail: string;
48
+ } | {
49
+ code: "section-id-mismatch";
50
+ sectionId: string;
51
+ fileId: unknown;
52
+ } | {
53
+ code: "section-type-mismatch";
54
+ sectionId: string;
55
+ fileType: unknown;
56
+ indexType: string;
57
+ } | {
58
+ code: "unknown-type";
59
+ sectionId: string;
60
+ type: string;
61
+ } | {
62
+ code: "unvalidated-type";
63
+ sectionId: string;
64
+ type: string;
65
+ } | {
66
+ code: "section-invalid";
67
+ sectionId: string;
68
+ type: string;
69
+ issues: SchemaIssue[];
70
+ } | {
71
+ code: "layout-invalid";
72
+ sectionId: string;
73
+ issues: SchemaIssue[];
74
+ } | {
75
+ code: "too-deep";
76
+ sectionId: string;
77
+ } | {
78
+ code: "reference-in-container";
79
+ sectionId: string;
80
+ } | {
81
+ code: "html-entities";
82
+ sectionId: string;
83
+ path: string;
84
+ entities: string[];
85
+ } | {
86
+ code: "dangling-image";
87
+ sectionId: string;
88
+ imageId: string;
89
+ } | {
90
+ code: "dangling-document";
91
+ sectionId: string;
92
+ documentId: string;
93
+ } | {
94
+ code: "reference-problem";
95
+ problem: ReferenceProblem;
96
+ };
97
+ export type SiteProblemCode = ProblemBody["code"];
98
+ export type Severity = "error" | "warning";
99
+ export type SiteProblem = ProblemBody & {
100
+ severity: Severity;
101
+ };
102
+ /**
103
+ * The one severity table every caller shares. An ERROR is something the site
104
+ * cannot load, or authored content it silently loses or rejects. A WARNING is
105
+ * something the site renders through — dropping or repairing it itself — and
106
+ * that is fixable in-product, so it must not fail a build.
107
+ */
108
+ export declare const SEVERITY: Readonly<Record<SiteProblemCode, Severity>>;
109
+ export interface SiteValidation {
110
+ problems: SiteProblem[];
111
+ /** The section ids the index places on pages; null when the index could not be read. */
112
+ placedIds: string[] | null;
113
+ }
114
+ /**
115
+ * Validate a site's committed content. `files` maps repo paths under
116
+ * `src/content/` to their text; an absent path is an absent file. Pure: it reads
117
+ * the schema registry, so callers register built-in (and any site-local)
118
+ * schemas first. Only sections the index places on a page are judged — orphan
119
+ * files are the caller's concern.
120
+ */
121
+ export declare function validateSite(files: Readonly<Record<string, string>>, policy: SiteValidationPolicy): SiteValidation;
122
+ export {};
123
+ //# sourceMappingURL=site-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-validation.d.ts","sourceRoot":"","sources":["../../src/lib/site-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,230 @@
1
+ import {
2
+ collectDocumentIds
3
+ } from "../chunk-DKZCHCV3.js";
4
+ import {
5
+ referenceProblems
6
+ } from "../chunk-FGQXHUAE.js";
7
+ import {
8
+ checkSection
9
+ } from "../chunk-26V4YNOR.js";
10
+ import {
11
+ upgradeLegacySection
12
+ } from "../chunk-FRVO75DK.js";
13
+ import "../chunk-L45X7DKX.js";
14
+ import {
15
+ NAV,
16
+ TAGS,
17
+ readSidecarText
18
+ } from "../chunk-DJABADOO.js";
19
+ import "../chunk-DJOGPSRU.js";
20
+ import "../chunk-4WQ36ZP7.js";
21
+ import "../chunk-GNF6KFQ7.js";
22
+ import "../chunk-5GFACGSN.js";
23
+ import {
24
+ IndexSchema,
25
+ SiteConfigSchema
26
+ } from "../chunk-67P34WNW.js";
27
+ import "../chunk-4TZJZU2T.js";
28
+ import "../chunk-4Y2JQQZL.js";
29
+ import {
30
+ ImageManifestSchema
31
+ } from "../chunk-APIJPTFG.js";
32
+ import "../chunk-FTPETWXE.js";
33
+ import "../chunk-AASEGXNT.js";
34
+ import "../chunk-NSCT3AMV.js";
35
+ import {
36
+ DOCUMENT_MANIFEST_PATH,
37
+ DocumentManifestSchema
38
+ } from "../chunk-BS5AQHZG.js";
39
+ import "../chunk-JSBRDJBE.js";
40
+
41
+ // src/lib/image-refs.ts
42
+ function tallyImageIds(value, counts = /* @__PURE__ */ new Map()) {
43
+ if (Array.isArray(value)) {
44
+ for (const item of value) tallyImageIds(item, counts);
45
+ } else if (value !== null && typeof value === "object") {
46
+ for (const [key, v] of Object.entries(value)) {
47
+ if (key === "imageId" && typeof v === "string" && v !== "") {
48
+ counts.set(v, (counts.get(v) ?? 0) + 1);
49
+ } else {
50
+ tallyImageIds(v, counts);
51
+ }
52
+ }
53
+ }
54
+ return counts;
55
+ }
56
+ function collectImageIds(value) {
57
+ return new Set(tallyImageIds(value).keys());
58
+ }
59
+
60
+ // src/lib/site-validation.ts
61
+ var INDEX_PATH = "src/content/index.json";
62
+ var SITE_CONFIG_PATH = "src/content/site-config.json";
63
+ var IMAGE_MANIFEST_PATH = "src/content/image-manifest.json";
64
+ var sectionPath = (id) => `src/content/sections/${id}.json`;
65
+ var SEVERITY = {
66
+ "index-missing": "error",
67
+ "index-invalid": "error",
68
+ "site-id-empty": "error",
69
+ "site-config-missing": "error",
70
+ "site-config-invalid": "error",
71
+ // The loader drops what it cannot read, so authored grouping or tags vanish.
72
+ "sidecar-degraded": "error",
73
+ // What reconcile repairs on every load: the runtime has already fixed it.
74
+ "sidecar-repaired": "warning",
75
+ "image-manifest-invalid": "error",
76
+ "document-manifest-invalid": "error",
77
+ "section-missing": "error",
78
+ "section-unparseable": "error",
79
+ "section-id-mismatch": "error",
80
+ "section-type-mismatch": "error",
81
+ "unknown-type": "error",
82
+ "unvalidated-type": "warning",
83
+ "section-invalid": "error",
84
+ "layout-invalid": "error",
85
+ "too-deep": "error",
86
+ "reference-in-container": "error",
87
+ "html-entities": "warning",
88
+ "dangling-image": "warning",
89
+ "dangling-document": "warning",
90
+ "reference-problem": "warning"
91
+ };
92
+ function parseJson(text) {
93
+ try {
94
+ return { ok: true, value: JSON.parse(text) };
95
+ } catch (e) {
96
+ return { ok: false, detail: e instanceof Error ? e.message : String(e) };
97
+ }
98
+ }
99
+ function parseFile(text, schema) {
100
+ const json = parseJson(text);
101
+ if (!json.ok) return json;
102
+ const parsed = schema.safeParse(json.value);
103
+ return parsed.success ? { ok: true, data: parsed.data } : { ok: false, detail: parsed.error.message };
104
+ }
105
+ function validateSite(files, policy) {
106
+ const problems = [];
107
+ const report = (body) => problems.push({ ...body, severity: SEVERITY[body.code] });
108
+ const indexText = files[INDEX_PATH];
109
+ if (indexText === void 0) {
110
+ report({ code: "index-missing" });
111
+ return { problems, placedIds: null };
112
+ }
113
+ const indexRead = parseFile(indexText, IndexSchema);
114
+ if (!indexRead.ok) {
115
+ report({ code: "index-invalid", detail: indexRead.detail });
116
+ return { problems, placedIds: null };
117
+ }
118
+ const index = indexRead.data;
119
+ const placedIds = [...new Set(index.pages.flatMap((p) => p.order))];
120
+ if (!index.siteId) report({ code: "site-id-empty" });
121
+ const configText = files[SITE_CONFIG_PATH];
122
+ if (configText === void 0) {
123
+ report({ code: "site-config-missing" });
124
+ } else {
125
+ const configRead = parseFile(configText, SiteConfigSchema);
126
+ if (!configRead.ok) report({ code: "site-config-invalid", detail: configRead.detail });
127
+ }
128
+ const checkSidecar = (def) => {
129
+ const text = files[def.path];
130
+ if (text === void 0) return [];
131
+ const read = readSidecarText(def, text);
132
+ if (read.degraded) {
133
+ report({ code: "sidecar-degraded", sidecar: def.name });
134
+ return null;
135
+ }
136
+ for (const diagnostic of def.reconcile(read.entries, index).diagnostics) {
137
+ report({ code: "sidecar-repaired", sidecar: def.name, diagnostic });
138
+ }
139
+ return read.entries;
140
+ };
141
+ checkSidecar(NAV);
142
+ const tags = checkSidecar(TAGS);
143
+ const tagIds = tags === null ? null : new Set(tags.map((t) => t.id));
144
+ let imageIds = /* @__PURE__ */ new Set();
145
+ const imageText = files[IMAGE_MANIFEST_PATH];
146
+ if (imageText !== void 0) {
147
+ const read = parseFile(imageText, ImageManifestSchema);
148
+ if (read.ok) imageIds = new Set(Object.keys(read.data.images));
149
+ else {
150
+ report({ code: "image-manifest-invalid", detail: read.detail });
151
+ imageIds = null;
152
+ }
153
+ }
154
+ let documentIds = /* @__PURE__ */ new Set();
155
+ const documentText = files[DOCUMENT_MANIFEST_PATH];
156
+ if (documentText !== void 0) {
157
+ const read = parseFile(documentText, DocumentManifestSchema);
158
+ if (read.ok) documentIds = new Set(Object.keys(read.data));
159
+ else {
160
+ report({ code: "document-manifest-invalid", detail: read.detail });
161
+ documentIds = null;
162
+ }
163
+ }
164
+ const bodies = [];
165
+ for (const id of placedIds) {
166
+ const text = files[sectionPath(id)];
167
+ if (text === void 0) {
168
+ report({ code: "section-missing", sectionId: id });
169
+ continue;
170
+ }
171
+ const json = parseJson(text);
172
+ if (!json.ok) {
173
+ report({ code: "section-unparseable", sectionId: id, detail: json.detail });
174
+ continue;
175
+ }
176
+ const onDisk = typeof json.value === "object" && json.value !== null ? json.value : {};
177
+ if (onDisk.id !== id) report({ code: "section-id-mismatch", sectionId: id, fileId: onDisk.id });
178
+ const body = upgradeLegacySection(json.value);
179
+ bodies.push({ id, content: body });
180
+ const upgradedType = typeof body === "object" && body !== null ? body.type : void 0;
181
+ const type = typeof upgradedType === "string" ? upgradedType : "";
182
+ const indexType = index.sections[id].type;
183
+ if (indexType !== onDisk.type && indexType !== upgradedType) {
184
+ report({ code: "section-type-mismatch", sectionId: id, fileType: onDisk.type, indexType });
185
+ }
186
+ if (type === "") continue;
187
+ const check = checkSection(type, body);
188
+ if (check.unregistered) {
189
+ if (policy.unregisteredTypes === "reject") {
190
+ report({ code: "unknown-type", sectionId: id, type });
191
+ } else {
192
+ report({ code: "unvalidated-type", sectionId: id, type });
193
+ if (check.error?.code === "depth") report({ code: "too-deep", sectionId: id });
194
+ }
195
+ } else if (check.error) {
196
+ const error = check.error;
197
+ switch (error.code) {
198
+ case "schema":
199
+ report({ code: "section-invalid", sectionId: id, type, issues: error.zodError.issues });
200
+ break;
201
+ case "layout":
202
+ report({ code: "layout-invalid", sectionId: id, issues: error.zodError.issues });
203
+ break;
204
+ case "depth":
205
+ report({ code: "too-deep", sectionId: id });
206
+ break;
207
+ case "reference-in-container":
208
+ report({ code: "reference-in-container", sectionId: id });
209
+ break;
210
+ }
211
+ }
212
+ for (const { path, entities } of check.entities) report({ code: "html-entities", sectionId: id, path, entities });
213
+ if (imageIds) {
214
+ for (const imageId of collectImageIds(body)) {
215
+ if (!imageIds.has(imageId)) report({ code: "dangling-image", sectionId: id, imageId });
216
+ }
217
+ }
218
+ if (documentIds) {
219
+ for (const documentId of collectDocumentIds(body)) {
220
+ if (!documentIds.has(documentId)) report({ code: "dangling-document", sectionId: id, documentId });
221
+ }
222
+ }
223
+ }
224
+ for (const problem of referenceProblems(bodies, index, tagIds)) report({ code: "reference-problem", problem });
225
+ return { problems, placedIds };
226
+ }
227
+ export {
228
+ SEVERITY,
229
+ validateSite
230
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.115",
3
+ "version": "0.1.116",
4
4
  "description": "Browser-safe foundation of the Brand Portal framework: Zod schemas, section components, editor shell, hooks, pure libs, collab contract, document engine.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -105,6 +105,18 @@
105
105
  "types": "./dist/lib/reference-validation.d.ts",
106
106
  "default": "./dist/lib/reference-validation.js"
107
107
  },
108
+ "./lib/section-check": {
109
+ "types": "./dist/lib/section-check.d.ts",
110
+ "default": "./dist/lib/section-check.js"
111
+ },
112
+ "./lib/site-validation": {
113
+ "types": "./dist/lib/site-validation.d.ts",
114
+ "default": "./dist/lib/site-validation.js"
115
+ },
116
+ "./lib/document-refs": {
117
+ "types": "./dist/lib/document-refs.d.ts",
118
+ "default": "./dist/lib/document-refs.js"
119
+ },
108
120
  "./lib/deck-access": {
109
121
  "types": "./dist/lib/deck-access.d.ts",
110
122
  "default": "./dist/lib/deck-access.js"
@@ -213,6 +225,7 @@
213
225
  "clsx": "^2.1.1",
214
226
  "dexie": "^4.4.2",
215
227
  "dompurify": "^3.2.6",
228
+ "entities": "^6.0.1",
216
229
  "jose": "^6.2.2",
217
230
  "lucide-react": "^1.7.0",
218
231
  "marked": "^18.0.5",
@@ -81,3 +81,33 @@ export function clearDocumentRefs(
81
81
  if (!changed) return { changed: false, section };
82
82
  return { changed: true, section: { ...section, content: { ...section.content, children: next } } };
83
83
  }
84
+
85
+ /**
86
+ * The non-empty `documentId`s referenced by unvalidated section JSON, including
87
+ * ones nested in a container's children — `tallyDocumentIds` for a body that
88
+ * has not been through a schema, so every step is shape-checked. Walks by node
89
+ * type like the rest of this module, so an unrelated field called `doc` is
90
+ * never mistaken for a reference.
91
+ */
92
+ export function collectDocumentIds(value: unknown, out: Set<string> = new Set()): Set<string> {
93
+ if (Array.isArray(value)) {
94
+ for (const item of value) collectDocumentIds(item, out);
95
+ return out;
96
+ }
97
+ if (typeof value !== "object" || value === null) return out;
98
+ const node = value as { type?: unknown; content?: unknown };
99
+ const content = node.content;
100
+ if (typeof content !== "object" || content === null || Array.isArray(content)) return out;
101
+ const record = content as Record<string, unknown>;
102
+
103
+ if (node.type === "document") {
104
+ const doc = record.doc;
105
+ const documentId =
106
+ typeof doc === "object" && doc !== null ? (doc as Record<string, unknown>).documentId : undefined;
107
+ // Empty string is the routine unconfigured slot (the schema defaults to
108
+ // it), not a broken reference.
109
+ if (typeof documentId === "string" && documentId !== "") out.add(documentId);
110
+ }
111
+ if (Array.isArray(record.children)) collectDocumentIds(record.children, out);
112
+ return out;
113
+ }