@storyblok/schema 0.1.0-alpha.0 → 0.1.0-alpha.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.
Files changed (146) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +29 -0
  2. package/dist/generated/capi/types.gen.d.cts +1 -126
  3. package/dist/generated/capi/types.gen.d.mts +1 -126
  4. package/dist/generated/mapi/_internal.gen.d.cts +1 -171
  5. package/dist/generated/mapi/_internal.gen.d.mts +1 -171
  6. package/dist/generated/overlay/zod.gen.cjs +409 -0
  7. package/dist/generated/overlay/zod.gen.cjs.map +1 -0
  8. package/dist/generated/overlay/zod.gen.mjs +403 -0
  9. package/dist/generated/overlay/zod.gen.mjs.map +1 -0
  10. package/dist/generated/types/block.d.cts +27 -14
  11. package/dist/generated/types/block.d.mts +27 -14
  12. package/dist/generated/types/field.d.cts +28 -13
  13. package/dist/generated/types/field.d.mts +28 -13
  14. package/dist/generated/types/mapi-story.d.cts +5 -9
  15. package/dist/generated/types/mapi-story.d.mts +5 -9
  16. package/dist/helpers/define-block.cjs +10 -28
  17. package/dist/helpers/define-block.cjs.map +1 -1
  18. package/dist/helpers/define-block.d.cts +14 -31
  19. package/dist/helpers/define-block.d.mts +14 -31
  20. package/dist/helpers/define-block.mjs +11 -27
  21. package/dist/helpers/define-block.mjs.map +1 -1
  22. package/dist/helpers/define-datasource.cjs +0 -16
  23. package/dist/helpers/define-datasource.cjs.map +1 -1
  24. package/dist/helpers/define-datasource.d.cts +14 -23
  25. package/dist/helpers/define-datasource.d.mts +14 -23
  26. package/dist/helpers/define-datasource.mjs +1 -15
  27. package/dist/helpers/define-datasource.mjs.map +1 -1
  28. package/dist/helpers/define-field.cjs +11 -11
  29. package/dist/helpers/define-field.cjs.map +1 -1
  30. package/dist/helpers/define-field.d.cts +46 -85
  31. package/dist/helpers/define-field.d.mts +46 -85
  32. package/dist/helpers/define-field.mjs +12 -11
  33. package/dist/helpers/define-field.mjs.map +1 -1
  34. package/dist/helpers/schema-type.d.cts +5 -9
  35. package/dist/helpers/schema-type.d.mts +5 -9
  36. package/dist/index.cjs +6 -47
  37. package/dist/index.d.cts +11 -20
  38. package/dist/index.d.mts +11 -20
  39. package/dist/index.mjs +6 -15
  40. package/dist/utils/is-record.cjs +10 -0
  41. package/dist/utils/is-record.cjs.map +1 -0
  42. package/dist/utils/is-record.mjs +9 -0
  43. package/dist/utils/is-record.mjs.map +1 -0
  44. package/dist/validators/create-story-validator.cjs +37 -0
  45. package/dist/validators/create-story-validator.cjs.map +1 -0
  46. package/dist/validators/create-story-validator.d.cts +18 -0
  47. package/dist/validators/create-story-validator.d.mts +18 -0
  48. package/dist/validators/create-story-validator.mjs +37 -0
  49. package/dist/validators/create-story-validator.mjs.map +1 -0
  50. package/dist/validators/internal-schemas.cjs +1 -0
  51. package/dist/validators/internal-schemas.mjs +3 -0
  52. package/dist/validators/shapes.cjs +12 -0
  53. package/dist/validators/shapes.cjs.map +1 -0
  54. package/dist/validators/shapes.d.cts +62 -0
  55. package/dist/validators/shapes.d.mts +62 -0
  56. package/dist/validators/shapes.mjs +12 -0
  57. package/dist/validators/shapes.mjs.map +1 -0
  58. package/dist/validators/types.d.cts +25 -0
  59. package/dist/validators/types.d.mts +25 -0
  60. package/dist/validators/validate-schema.cjs +125 -0
  61. package/dist/validators/validate-schema.cjs.map +1 -0
  62. package/dist/validators/validate-schema.d.cts +18 -0
  63. package/dist/validators/validate-schema.d.mts +18 -0
  64. package/dist/validators/validate-schema.mjs +125 -0
  65. package/dist/validators/validate-schema.mjs.map +1 -0
  66. package/dist/validators/validate-story.cjs +257 -0
  67. package/dist/validators/validate-story.cjs.map +1 -0
  68. package/dist/validators/validate-story.d.cts +17 -0
  69. package/dist/validators/validate-story.d.mts +17 -0
  70. package/dist/validators/validate-story.mjs +257 -0
  71. package/dist/validators/validate-story.mjs.map +1 -0
  72. package/package.json +5 -1
  73. package/dist/generated/mapi/types.gen.d.cts +0 -1272
  74. package/dist/generated/mapi/types.gen.d.mts +0 -1272
  75. package/dist/helpers/create-story-helpers.cjs +0 -42
  76. package/dist/helpers/create-story-helpers.cjs.map +0 -1
  77. package/dist/helpers/create-story-helpers.d.cts +0 -50
  78. package/dist/helpers/create-story-helpers.d.mts +0 -50
  79. package/dist/helpers/create-story-helpers.mjs +0 -42
  80. package/dist/helpers/create-story-helpers.mjs.map +0 -1
  81. package/dist/helpers/define-asset-folder.cjs +0 -46
  82. package/dist/helpers/define-asset-folder.cjs.map +0 -1
  83. package/dist/helpers/define-asset-folder.d.cts +0 -33
  84. package/dist/helpers/define-asset-folder.d.mts +0 -33
  85. package/dist/helpers/define-asset-folder.mjs +0 -43
  86. package/dist/helpers/define-asset-folder.mjs.map +0 -1
  87. package/dist/helpers/define-asset.cjs +0 -60
  88. package/dist/helpers/define-asset.cjs.map +0 -1
  89. package/dist/helpers/define-asset.d.cts +0 -32
  90. package/dist/helpers/define-asset.d.mts +0 -32
  91. package/dist/helpers/define-asset.mjs +0 -57
  92. package/dist/helpers/define-asset.mjs.map +0 -1
  93. package/dist/helpers/define-block-folder.cjs +0 -44
  94. package/dist/helpers/define-block-folder.cjs.map +0 -1
  95. package/dist/helpers/define-block-folder.d.cts +0 -34
  96. package/dist/helpers/define-block-folder.d.mts +0 -34
  97. package/dist/helpers/define-block-folder.mjs +0 -41
  98. package/dist/helpers/define-block-folder.mjs.map +0 -1
  99. package/dist/helpers/define-datasource-entry.cjs +0 -47
  100. package/dist/helpers/define-datasource-entry.cjs.map +0 -1
  101. package/dist/helpers/define-datasource-entry.d.cts +0 -49
  102. package/dist/helpers/define-datasource-entry.d.mts +0 -49
  103. package/dist/helpers/define-datasource-entry.mjs +0 -43
  104. package/dist/helpers/define-datasource-entry.mjs.map +0 -1
  105. package/dist/helpers/define-internal-tag.cjs +0 -41
  106. package/dist/helpers/define-internal-tag.cjs.map +0 -1
  107. package/dist/helpers/define-internal-tag.d.cts +0 -34
  108. package/dist/helpers/define-internal-tag.d.mts +0 -34
  109. package/dist/helpers/define-internal-tag.mjs +0 -38
  110. package/dist/helpers/define-internal-tag.mjs.map +0 -1
  111. package/dist/helpers/define-link.cjs +0 -23
  112. package/dist/helpers/define-link.cjs.map +0 -1
  113. package/dist/helpers/define-link.d.cts +0 -32
  114. package/dist/helpers/define-link.d.mts +0 -32
  115. package/dist/helpers/define-link.mjs +0 -22
  116. package/dist/helpers/define-link.mjs.map +0 -1
  117. package/dist/helpers/define-preset.cjs +0 -44
  118. package/dist/helpers/define-preset.cjs.map +0 -1
  119. package/dist/helpers/define-preset.d.cts +0 -35
  120. package/dist/helpers/define-preset.d.mts +0 -35
  121. package/dist/helpers/define-preset.mjs +0 -41
  122. package/dist/helpers/define-preset.mjs.map +0 -1
  123. package/dist/helpers/define-space.cjs +0 -47
  124. package/dist/helpers/define-space.cjs.map +0 -1
  125. package/dist/helpers/define-space.d.cts +0 -31
  126. package/dist/helpers/define-space.d.mts +0 -31
  127. package/dist/helpers/define-space.mjs +0 -44
  128. package/dist/helpers/define-space.mjs.map +0 -1
  129. package/dist/helpers/define-story.cjs +0 -122
  130. package/dist/helpers/define-story.cjs.map +0 -1
  131. package/dist/helpers/define-story.d.cts +0 -135
  132. package/dist/helpers/define-story.d.mts +0 -135
  133. package/dist/helpers/define-story.mjs +0 -118
  134. package/dist/helpers/define-story.mjs.map +0 -1
  135. package/dist/helpers/define-tag.cjs +0 -13
  136. package/dist/helpers/define-tag.cjs.map +0 -1
  137. package/dist/helpers/define-tag.d.cts +0 -23
  138. package/dist/helpers/define-tag.d.mts +0 -23
  139. package/dist/helpers/define-tag.mjs +0 -12
  140. package/dist/helpers/define-tag.mjs.map +0 -1
  141. package/dist/helpers/define-user.cjs +0 -50
  142. package/dist/helpers/define-user.cjs.map +0 -1
  143. package/dist/helpers/define-user.d.cts +0 -26
  144. package/dist/helpers/define-user.d.mts +0 -26
  145. package/dist/helpers/define-user.mjs +0 -48
  146. package/dist/helpers/define-user.mjs.map +0 -1
@@ -0,0 +1,125 @@
1
+ const require_is_record = require('../utils/is-record.cjs');
2
+ const require_shapes = require('./shapes.cjs');
3
+
4
+ //#region src/validators/validate-schema.ts
5
+ /**
6
+ * Validates a schema definition without throwing. Checks structural identity
7
+ * (duplicate block names, field names, and datasource slugs) and cross-references
8
+ * (every `allow` entry resolves to a defined block; every field `datasource`
9
+ * resolves to a defined datasource).
10
+ *
11
+ * @example
12
+ * const result = validateSchema({ blocks: { hero }, datasources: { colors } });
13
+ * if (!result.ok) console.error(result.issues);
14
+ */
15
+ function validateSchema(schema) {
16
+ const issues = [];
17
+ const blocks = require_shapes.toValues(schema.blocks);
18
+ const datasources = require_shapes.toValues(schema.datasources);
19
+ const datasourceSlugs = /* @__PURE__ */ new Set();
20
+ for (const datasource of datasources) {
21
+ const slug = datasource?.slug;
22
+ if (typeof slug !== "string") continue;
23
+ if (datasourceSlugs.has(slug)) issues.push({
24
+ severity: "error",
25
+ code: "duplicate_datasource_slug",
26
+ path: ["datasources", slug],
27
+ entity: `datasource:${slug}`,
28
+ message: `Duplicate datasource slug "${slug}".`
29
+ });
30
+ datasourceSlugs.add(slug);
31
+ }
32
+ const blockNames = /* @__PURE__ */ new Set();
33
+ for (const block of blocks) {
34
+ const name = block?.name;
35
+ if (typeof name !== "string") continue;
36
+ if (blockNames.has(name)) issues.push({
37
+ severity: "error",
38
+ code: "duplicate_block_name",
39
+ path: ["blocks", name],
40
+ entity: `block:${name}`,
41
+ message: `Duplicate block name "${name}".`
42
+ });
43
+ blockNames.add(name);
44
+ }
45
+ for (const block of blocks) {
46
+ const blockName = typeof block?.name === "string" ? block.name : "";
47
+ const fieldNames = /* @__PURE__ */ new Set();
48
+ const fields = block?.fields ?? [];
49
+ for (let index = 0; index < fields.length; index++) {
50
+ const field = fields[index];
51
+ if (!require_is_record.isRecord(field)) {
52
+ issues.push({
53
+ severity: "error",
54
+ code: "invalid_field",
55
+ path: [
56
+ "blocks",
57
+ blockName,
58
+ index
59
+ ],
60
+ entity: `block:${blockName}`,
61
+ message: `Field at index ${index} in block "${blockName}" is not an object.`
62
+ });
63
+ continue;
64
+ }
65
+ const fieldName = field.name;
66
+ if (typeof fieldName === "string") {
67
+ if (fieldNames.has(fieldName)) issues.push({
68
+ severity: "error",
69
+ code: "duplicate_field_name",
70
+ path: [
71
+ "blocks",
72
+ blockName,
73
+ fieldName
74
+ ],
75
+ entity: `block:${blockName}`,
76
+ message: `Duplicate field name "${fieldName}" in block "${blockName}".`
77
+ });
78
+ fieldNames.add(fieldName);
79
+ } else issues.push({
80
+ severity: "error",
81
+ code: "missing_field_name",
82
+ path: [
83
+ "blocks",
84
+ blockName,
85
+ index
86
+ ],
87
+ entity: `block:${blockName}`,
88
+ message: `Field at index ${index} in block "${blockName}" is missing a string "name".`
89
+ });
90
+ for (const allowed of field.allow ?? []) if (typeof allowed === "string" && !blockNames.has(allowed)) issues.push({
91
+ severity: "error",
92
+ code: "unresolved_allow",
93
+ path: [
94
+ "blocks",
95
+ blockName,
96
+ fieldName ?? "",
97
+ "allow"
98
+ ],
99
+ entity: `block:${blockName}`,
100
+ message: `Field "${fieldName}" allows unknown block "${allowed}".`
101
+ });
102
+ const datasource = field.datasource;
103
+ if (typeof datasource === "string" && !datasourceSlugs.has(datasource)) issues.push({
104
+ severity: "error",
105
+ code: "unresolved_datasource",
106
+ path: [
107
+ "blocks",
108
+ blockName,
109
+ fieldName ?? "",
110
+ "datasource"
111
+ ],
112
+ entity: `block:${blockName}`,
113
+ message: `Field "${fieldName}" references unknown datasource "${datasource}".`
114
+ });
115
+ }
116
+ }
117
+ return {
118
+ ok: issues.every((issue) => issue.severity !== "error"),
119
+ issues
120
+ };
121
+ }
122
+
123
+ //#endregion
124
+ exports.validateSchema = validateSchema;
125
+ //# sourceMappingURL=validate-schema.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-schema.cjs","names":["toValues","isRecord"],"sources":["../../src/validators/validate-schema.ts"],"sourcesContent":["import type { SchemaLike } from './shapes';\nimport type { ValidationIssue, ValidationResult } from './types';\nimport { isRecord, toValues } from './shapes';\n\n/**\n * Validates a schema definition without throwing. Checks structural identity\n * (duplicate block names, field names, and datasource slugs) and cross-references\n * (every `allow` entry resolves to a defined block; every field `datasource`\n * resolves to a defined datasource).\n *\n * @example\n * const result = validateSchema({ blocks: { hero }, datasources: { colors } });\n * if (!result.ok) console.error(result.issues);\n */\nexport function validateSchema(schema: SchemaLike): ValidationResult {\n const issues: ValidationIssue[] = [];\n const blocks = toValues(schema.blocks);\n const datasources = toValues(schema.datasources);\n\n const datasourceSlugs = new Set<string>();\n for (const datasource of datasources) {\n const slug = datasource?.slug;\n if (typeof slug !== 'string') {\n continue;\n }\n if (datasourceSlugs.has(slug)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_datasource_slug',\n path: ['datasources', slug],\n entity: `datasource:${slug}`,\n message: `Duplicate datasource slug \"${slug}\".`,\n });\n }\n datasourceSlugs.add(slug);\n }\n\n const blockNames = new Set<string>();\n for (const block of blocks) {\n const name = block?.name;\n if (typeof name !== 'string') {\n continue;\n }\n if (blockNames.has(name)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_block_name',\n path: ['blocks', name],\n entity: `block:${name}`,\n message: `Duplicate block name \"${name}\".`,\n });\n }\n blockNames.add(name);\n }\n\n // Field-level checks run after the name/slug sets are fully populated so that\n // forward and circular references (resolved by name) validate correctly.\n for (const block of blocks) {\n const blockName = typeof block?.name === 'string' ? block.name : '';\n const fieldNames = new Set<string>();\n const fields = block?.fields ?? [];\n for (let index = 0; index < fields.length; index++) {\n const field = fields[index];\n // Flag malformed fields the wire mapper would otherwise silently drop:\n // a non-object entry, or one without a string `name` (its mapping key).\n if (!isRecord(field)) {\n issues.push({\n severity: 'error',\n code: 'invalid_field',\n path: ['blocks', blockName, index],\n entity: `block:${blockName}`,\n message: `Field at index ${index} in block \"${blockName}\" is not an object.`,\n });\n continue;\n }\n const fieldName = field.name;\n if (typeof fieldName === 'string') {\n if (fieldNames.has(fieldName)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_field_name',\n path: ['blocks', blockName, fieldName],\n entity: `block:${blockName}`,\n message: `Duplicate field name \"${fieldName}\" in block \"${blockName}\".`,\n });\n }\n fieldNames.add(fieldName);\n }\n else {\n issues.push({\n severity: 'error',\n code: 'missing_field_name',\n path: ['blocks', blockName, index],\n entity: `block:${blockName}`,\n message: `Field at index ${index} in block \"${blockName}\" is missing a string \"name\".`,\n });\n }\n\n for (const allowed of field.allow ?? []) {\n if (typeof allowed === 'string' && !blockNames.has(allowed)) {\n issues.push({\n severity: 'error',\n code: 'unresolved_allow',\n path: ['blocks', blockName, fieldName ?? '', 'allow'],\n entity: `block:${blockName}`,\n message: `Field \"${fieldName}\" allows unknown block \"${allowed}\".`,\n });\n }\n }\n\n const datasource = field.datasource;\n if (typeof datasource === 'string' && !datasourceSlugs.has(datasource)) {\n issues.push({\n severity: 'error',\n code: 'unresolved_datasource',\n path: ['blocks', blockName, fieldName ?? '', 'datasource'],\n entity: `block:${blockName}`,\n message: `Field \"${fieldName}\" references unknown datasource \"${datasource}\".`,\n });\n }\n }\n }\n\n return { ok: issues.every(issue => issue.severity !== 'error'), issues };\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,eAAe,QAAsC;CACnE,MAAM,SAA4B,EAAE;CACpC,MAAM,SAASA,wBAAS,OAAO,OAAO;CACtC,MAAM,cAAcA,wBAAS,OAAO,YAAY;CAEhD,MAAM,kCAAkB,IAAI,KAAa;AACzC,MAAK,MAAM,cAAc,aAAa;EACpC,MAAM,OAAO,YAAY;AACzB,MAAI,OAAO,SAAS,SAClB;AAEF,MAAI,gBAAgB,IAAI,KAAK,CAC3B,QAAO,KAAK;GACV,UAAU;GACV,MAAM;GACN,MAAM,CAAC,eAAe,KAAK;GAC3B,QAAQ,cAAc;GACtB,SAAS,8BAA8B,KAAK;GAC7C,CAAC;AAEJ,kBAAgB,IAAI,KAAK;;CAG3B,MAAM,6BAAa,IAAI,KAAa;AACpC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,OAAO;AACpB,MAAI,OAAO,SAAS,SAClB;AAEF,MAAI,WAAW,IAAI,KAAK,CACtB,QAAO,KAAK;GACV,UAAU;GACV,MAAM;GACN,MAAM,CAAC,UAAU,KAAK;GACtB,QAAQ,SAAS;GACjB,SAAS,yBAAyB,KAAK;GACxC,CAAC;AAEJ,aAAW,IAAI,KAAK;;AAKtB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,YAAY,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO;EACjE,MAAM,6BAAa,IAAI,KAAa;EACpC,MAAM,SAAS,OAAO,UAAU,EAAE;AAClC,OAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;GAClD,MAAM,QAAQ,OAAO;AAGrB,OAAI,CAACC,2BAAS,MAAM,EAAE;AACpB,WAAO,KAAK;KACV,UAAU;KACV,MAAM;KACN,MAAM;MAAC;MAAU;MAAW;MAAM;KAClC,QAAQ,SAAS;KACjB,SAAS,kBAAkB,MAAM,aAAa,UAAU;KACzD,CAAC;AACF;;GAEF,MAAM,YAAY,MAAM;AACxB,OAAI,OAAO,cAAc,UAAU;AACjC,QAAI,WAAW,IAAI,UAAU,CAC3B,QAAO,KAAK;KACV,UAAU;KACV,MAAM;KACN,MAAM;MAAC;MAAU;MAAW;MAAU;KACtC,QAAQ,SAAS;KACjB,SAAS,yBAAyB,UAAU,cAAc,UAAU;KACrE,CAAC;AAEJ,eAAW,IAAI,UAAU;SAGzB,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW;KAAM;IAClC,QAAQ,SAAS;IACjB,SAAS,kBAAkB,MAAM,aAAa,UAAU;IACzD,CAAC;AAGJ,QAAK,MAAM,WAAW,MAAM,SAAS,EAAE,CACrC,KAAI,OAAO,YAAY,YAAY,CAAC,WAAW,IAAI,QAAQ,CACzD,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW,aAAa;KAAI;KAAQ;IACrD,QAAQ,SAAS;IACjB,SAAS,UAAU,UAAU,0BAA0B,QAAQ;IAChE,CAAC;GAIN,MAAM,aAAa,MAAM;AACzB,OAAI,OAAO,eAAe,YAAY,CAAC,gBAAgB,IAAI,WAAW,CACpE,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW,aAAa;KAAI;KAAa;IAC1D,QAAQ,SAAS;IACjB,SAAS,UAAU,UAAU,mCAAmC,WAAW;IAC5E,CAAC;;;AAKR,QAAO;EAAE,IAAI,OAAO,OAAM,UAAS,MAAM,aAAa,QAAQ;EAAE;EAAQ"}
@@ -0,0 +1,18 @@
1
+ import { SchemaLike } from "./shapes.cjs";
2
+ import { ValidationResult } from "./types.cjs";
3
+
4
+ //#region src/validators/validate-schema.d.ts
5
+ /**
6
+ * Validates a schema definition without throwing. Checks structural identity
7
+ * (duplicate block names, field names, and datasource slugs) and cross-references
8
+ * (every `allow` entry resolves to a defined block; every field `datasource`
9
+ * resolves to a defined datasource).
10
+ *
11
+ * @example
12
+ * const result = validateSchema({ blocks: { hero }, datasources: { colors } });
13
+ * if (!result.ok) console.error(result.issues);
14
+ */
15
+ declare function validateSchema(schema: SchemaLike): ValidationResult;
16
+ //#endregion
17
+ export { validateSchema };
18
+ //# sourceMappingURL=validate-schema.d.cts.map
@@ -0,0 +1,18 @@
1
+ import { SchemaLike } from "./shapes.mjs";
2
+ import { ValidationResult } from "./types.mjs";
3
+
4
+ //#region src/validators/validate-schema.d.ts
5
+ /**
6
+ * Validates a schema definition without throwing. Checks structural identity
7
+ * (duplicate block names, field names, and datasource slugs) and cross-references
8
+ * (every `allow` entry resolves to a defined block; every field `datasource`
9
+ * resolves to a defined datasource).
10
+ *
11
+ * @example
12
+ * const result = validateSchema({ blocks: { hero }, datasources: { colors } });
13
+ * if (!result.ok) console.error(result.issues);
14
+ */
15
+ declare function validateSchema(schema: SchemaLike): ValidationResult;
16
+ //#endregion
17
+ export { validateSchema };
18
+ //# sourceMappingURL=validate-schema.d.mts.map
@@ -0,0 +1,125 @@
1
+ import { isRecord } from "../utils/is-record.mjs";
2
+ import { toValues } from "./shapes.mjs";
3
+
4
+ //#region src/validators/validate-schema.ts
5
+ /**
6
+ * Validates a schema definition without throwing. Checks structural identity
7
+ * (duplicate block names, field names, and datasource slugs) and cross-references
8
+ * (every `allow` entry resolves to a defined block; every field `datasource`
9
+ * resolves to a defined datasource).
10
+ *
11
+ * @example
12
+ * const result = validateSchema({ blocks: { hero }, datasources: { colors } });
13
+ * if (!result.ok) console.error(result.issues);
14
+ */
15
+ function validateSchema(schema) {
16
+ const issues = [];
17
+ const blocks = toValues(schema.blocks);
18
+ const datasources = toValues(schema.datasources);
19
+ const datasourceSlugs = /* @__PURE__ */ new Set();
20
+ for (const datasource of datasources) {
21
+ const slug = datasource?.slug;
22
+ if (typeof slug !== "string") continue;
23
+ if (datasourceSlugs.has(slug)) issues.push({
24
+ severity: "error",
25
+ code: "duplicate_datasource_slug",
26
+ path: ["datasources", slug],
27
+ entity: `datasource:${slug}`,
28
+ message: `Duplicate datasource slug "${slug}".`
29
+ });
30
+ datasourceSlugs.add(slug);
31
+ }
32
+ const blockNames = /* @__PURE__ */ new Set();
33
+ for (const block of blocks) {
34
+ const name = block?.name;
35
+ if (typeof name !== "string") continue;
36
+ if (blockNames.has(name)) issues.push({
37
+ severity: "error",
38
+ code: "duplicate_block_name",
39
+ path: ["blocks", name],
40
+ entity: `block:${name}`,
41
+ message: `Duplicate block name "${name}".`
42
+ });
43
+ blockNames.add(name);
44
+ }
45
+ for (const block of blocks) {
46
+ const blockName = typeof block?.name === "string" ? block.name : "";
47
+ const fieldNames = /* @__PURE__ */ new Set();
48
+ const fields = block?.fields ?? [];
49
+ for (let index = 0; index < fields.length; index++) {
50
+ const field = fields[index];
51
+ if (!isRecord(field)) {
52
+ issues.push({
53
+ severity: "error",
54
+ code: "invalid_field",
55
+ path: [
56
+ "blocks",
57
+ blockName,
58
+ index
59
+ ],
60
+ entity: `block:${blockName}`,
61
+ message: `Field at index ${index} in block "${blockName}" is not an object.`
62
+ });
63
+ continue;
64
+ }
65
+ const fieldName = field.name;
66
+ if (typeof fieldName === "string") {
67
+ if (fieldNames.has(fieldName)) issues.push({
68
+ severity: "error",
69
+ code: "duplicate_field_name",
70
+ path: [
71
+ "blocks",
72
+ blockName,
73
+ fieldName
74
+ ],
75
+ entity: `block:${blockName}`,
76
+ message: `Duplicate field name "${fieldName}" in block "${blockName}".`
77
+ });
78
+ fieldNames.add(fieldName);
79
+ } else issues.push({
80
+ severity: "error",
81
+ code: "missing_field_name",
82
+ path: [
83
+ "blocks",
84
+ blockName,
85
+ index
86
+ ],
87
+ entity: `block:${blockName}`,
88
+ message: `Field at index ${index} in block "${blockName}" is missing a string "name".`
89
+ });
90
+ for (const allowed of field.allow ?? []) if (typeof allowed === "string" && !blockNames.has(allowed)) issues.push({
91
+ severity: "error",
92
+ code: "unresolved_allow",
93
+ path: [
94
+ "blocks",
95
+ blockName,
96
+ fieldName ?? "",
97
+ "allow"
98
+ ],
99
+ entity: `block:${blockName}`,
100
+ message: `Field "${fieldName}" allows unknown block "${allowed}".`
101
+ });
102
+ const datasource = field.datasource;
103
+ if (typeof datasource === "string" && !datasourceSlugs.has(datasource)) issues.push({
104
+ severity: "error",
105
+ code: "unresolved_datasource",
106
+ path: [
107
+ "blocks",
108
+ blockName,
109
+ fieldName ?? "",
110
+ "datasource"
111
+ ],
112
+ entity: `block:${blockName}`,
113
+ message: `Field "${fieldName}" references unknown datasource "${datasource}".`
114
+ });
115
+ }
116
+ }
117
+ return {
118
+ ok: issues.every((issue) => issue.severity !== "error"),
119
+ issues
120
+ };
121
+ }
122
+
123
+ //#endregion
124
+ export { validateSchema };
125
+ //# sourceMappingURL=validate-schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-schema.mjs","names":[],"sources":["../../src/validators/validate-schema.ts"],"sourcesContent":["import type { SchemaLike } from './shapes';\nimport type { ValidationIssue, ValidationResult } from './types';\nimport { isRecord, toValues } from './shapes';\n\n/**\n * Validates a schema definition without throwing. Checks structural identity\n * (duplicate block names, field names, and datasource slugs) and cross-references\n * (every `allow` entry resolves to a defined block; every field `datasource`\n * resolves to a defined datasource).\n *\n * @example\n * const result = validateSchema({ blocks: { hero }, datasources: { colors } });\n * if (!result.ok) console.error(result.issues);\n */\nexport function validateSchema(schema: SchemaLike): ValidationResult {\n const issues: ValidationIssue[] = [];\n const blocks = toValues(schema.blocks);\n const datasources = toValues(schema.datasources);\n\n const datasourceSlugs = new Set<string>();\n for (const datasource of datasources) {\n const slug = datasource?.slug;\n if (typeof slug !== 'string') {\n continue;\n }\n if (datasourceSlugs.has(slug)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_datasource_slug',\n path: ['datasources', slug],\n entity: `datasource:${slug}`,\n message: `Duplicate datasource slug \"${slug}\".`,\n });\n }\n datasourceSlugs.add(slug);\n }\n\n const blockNames = new Set<string>();\n for (const block of blocks) {\n const name = block?.name;\n if (typeof name !== 'string') {\n continue;\n }\n if (blockNames.has(name)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_block_name',\n path: ['blocks', name],\n entity: `block:${name}`,\n message: `Duplicate block name \"${name}\".`,\n });\n }\n blockNames.add(name);\n }\n\n // Field-level checks run after the name/slug sets are fully populated so that\n // forward and circular references (resolved by name) validate correctly.\n for (const block of blocks) {\n const blockName = typeof block?.name === 'string' ? block.name : '';\n const fieldNames = new Set<string>();\n const fields = block?.fields ?? [];\n for (let index = 0; index < fields.length; index++) {\n const field = fields[index];\n // Flag malformed fields the wire mapper would otherwise silently drop:\n // a non-object entry, or one without a string `name` (its mapping key).\n if (!isRecord(field)) {\n issues.push({\n severity: 'error',\n code: 'invalid_field',\n path: ['blocks', blockName, index],\n entity: `block:${blockName}`,\n message: `Field at index ${index} in block \"${blockName}\" is not an object.`,\n });\n continue;\n }\n const fieldName = field.name;\n if (typeof fieldName === 'string') {\n if (fieldNames.has(fieldName)) {\n issues.push({\n severity: 'error',\n code: 'duplicate_field_name',\n path: ['blocks', blockName, fieldName],\n entity: `block:${blockName}`,\n message: `Duplicate field name \"${fieldName}\" in block \"${blockName}\".`,\n });\n }\n fieldNames.add(fieldName);\n }\n else {\n issues.push({\n severity: 'error',\n code: 'missing_field_name',\n path: ['blocks', blockName, index],\n entity: `block:${blockName}`,\n message: `Field at index ${index} in block \"${blockName}\" is missing a string \"name\".`,\n });\n }\n\n for (const allowed of field.allow ?? []) {\n if (typeof allowed === 'string' && !blockNames.has(allowed)) {\n issues.push({\n severity: 'error',\n code: 'unresolved_allow',\n path: ['blocks', blockName, fieldName ?? '', 'allow'],\n entity: `block:${blockName}`,\n message: `Field \"${fieldName}\" allows unknown block \"${allowed}\".`,\n });\n }\n }\n\n const datasource = field.datasource;\n if (typeof datasource === 'string' && !datasourceSlugs.has(datasource)) {\n issues.push({\n severity: 'error',\n code: 'unresolved_datasource',\n path: ['blocks', blockName, fieldName ?? '', 'datasource'],\n entity: `block:${blockName}`,\n message: `Field \"${fieldName}\" references unknown datasource \"${datasource}\".`,\n });\n }\n }\n }\n\n return { ok: issues.every(issue => issue.severity !== 'error'), issues };\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,eAAe,QAAsC;CACnE,MAAM,SAA4B,EAAE;CACpC,MAAM,SAAS,SAAS,OAAO,OAAO;CACtC,MAAM,cAAc,SAAS,OAAO,YAAY;CAEhD,MAAM,kCAAkB,IAAI,KAAa;AACzC,MAAK,MAAM,cAAc,aAAa;EACpC,MAAM,OAAO,YAAY;AACzB,MAAI,OAAO,SAAS,SAClB;AAEF,MAAI,gBAAgB,IAAI,KAAK,CAC3B,QAAO,KAAK;GACV,UAAU;GACV,MAAM;GACN,MAAM,CAAC,eAAe,KAAK;GAC3B,QAAQ,cAAc;GACtB,SAAS,8BAA8B,KAAK;GAC7C,CAAC;AAEJ,kBAAgB,IAAI,KAAK;;CAG3B,MAAM,6BAAa,IAAI,KAAa;AACpC,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,OAAO;AACpB,MAAI,OAAO,SAAS,SAClB;AAEF,MAAI,WAAW,IAAI,KAAK,CACtB,QAAO,KAAK;GACV,UAAU;GACV,MAAM;GACN,MAAM,CAAC,UAAU,KAAK;GACtB,QAAQ,SAAS;GACjB,SAAS,yBAAyB,KAAK;GACxC,CAAC;AAEJ,aAAW,IAAI,KAAK;;AAKtB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,YAAY,OAAO,OAAO,SAAS,WAAW,MAAM,OAAO;EACjE,MAAM,6BAAa,IAAI,KAAa;EACpC,MAAM,SAAS,OAAO,UAAU,EAAE;AAClC,OAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;GAClD,MAAM,QAAQ,OAAO;AAGrB,OAAI,CAAC,SAAS,MAAM,EAAE;AACpB,WAAO,KAAK;KACV,UAAU;KACV,MAAM;KACN,MAAM;MAAC;MAAU;MAAW;MAAM;KAClC,QAAQ,SAAS;KACjB,SAAS,kBAAkB,MAAM,aAAa,UAAU;KACzD,CAAC;AACF;;GAEF,MAAM,YAAY,MAAM;AACxB,OAAI,OAAO,cAAc,UAAU;AACjC,QAAI,WAAW,IAAI,UAAU,CAC3B,QAAO,KAAK;KACV,UAAU;KACV,MAAM;KACN,MAAM;MAAC;MAAU;MAAW;MAAU;KACtC,QAAQ,SAAS;KACjB,SAAS,yBAAyB,UAAU,cAAc,UAAU;KACrE,CAAC;AAEJ,eAAW,IAAI,UAAU;SAGzB,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW;KAAM;IAClC,QAAQ,SAAS;IACjB,SAAS,kBAAkB,MAAM,aAAa,UAAU;IACzD,CAAC;AAGJ,QAAK,MAAM,WAAW,MAAM,SAAS,EAAE,CACrC,KAAI,OAAO,YAAY,YAAY,CAAC,WAAW,IAAI,QAAQ,CACzD,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW,aAAa;KAAI;KAAQ;IACrD,QAAQ,SAAS;IACjB,SAAS,UAAU,UAAU,0BAA0B,QAAQ;IAChE,CAAC;GAIN,MAAM,aAAa,MAAM;AACzB,OAAI,OAAO,eAAe,YAAY,CAAC,gBAAgB,IAAI,WAAW,CACpE,QAAO,KAAK;IACV,UAAU;IACV,MAAM;IACN,MAAM;KAAC;KAAU;KAAW,aAAa;KAAI;KAAa;IAC1D,QAAQ,SAAS;IACjB,SAAS,UAAU,UAAU,mCAAmC,WAAW;IAC5E,CAAC;;;AAKR,QAAO;EAAE,IAAI,OAAO,OAAM,UAAS,MAAM,aAAa,QAAQ;EAAE;EAAQ"}
@@ -0,0 +1,257 @@
1
+ const require_is_record = require('../utils/is-record.cjs');
2
+ const require_shapes = require('./shapes.cjs');
3
+ const require_zod_gen = require('../generated/overlay/zod.gen.cjs');
4
+ require('./internal-schemas.cjs');
5
+
6
+ //#region src/validators/validate-story.ts
7
+ /** Field-content keys that are not user-defined fields. */
8
+ const RESERVED_KEYS = new Set([
9
+ "_uid",
10
+ "component",
11
+ "_editable"
12
+ ]);
13
+ /** Maps a Standard Schema validator to a {@link ValidationIssue} reporter at `path`. */
14
+ function checkValue(schema, value, path, entity, issues) {
15
+ const result = schema["~standard"].validate(value);
16
+ if (result instanceof Promise) return;
17
+ if (result.issues) for (const issue of result.issues) {
18
+ const issuePath = (issue.path ?? []).map((segment) => typeof segment === "object" && segment !== null ? String(segment.key) : segment);
19
+ issues.push({
20
+ severity: "error",
21
+ code: "invalid_value",
22
+ path: [...path, ...issuePath],
23
+ entity,
24
+ message: issue.message
25
+ });
26
+ }
27
+ }
28
+ function validateFieldValue(field, value, blocksByName, path, entity, issues) {
29
+ switch (field.type) {
30
+ case "asset":
31
+ checkValue(require_zod_gen.zAssetFieldValue, value, path, entity, issues);
32
+ break;
33
+ case "multiasset":
34
+ if (!Array.isArray(value)) {
35
+ pushTypeIssue(value, "array", path, entity, issues);
36
+ break;
37
+ }
38
+ value.forEach((item, index) => checkValue(require_zod_gen.zAssetFieldValue, item, [...path, index], entity, issues));
39
+ checkCount(value.length, field.minimum_entries, field.maximum_entries, "asset(s)", path, entity, issues);
40
+ break;
41
+ case "multilink":
42
+ checkValue(require_zod_gen.zMultilinkFieldValue, value, path, entity, issues);
43
+ break;
44
+ case "table":
45
+ checkValue(require_zod_gen.zTableFieldValue, value, path, entity, issues);
46
+ break;
47
+ case "richtext":
48
+ checkValue(require_zod_gen.zRichtextFieldValue, value, path, entity, issues);
49
+ validateRichtextBloks(value, blocksByName, path, issues);
50
+ break;
51
+ case "custom":
52
+ checkValue(require_zod_gen.zPluginFieldValue, value, path, entity, issues);
53
+ break;
54
+ case "bloks":
55
+ if (!Array.isArray(value)) {
56
+ pushTypeIssue(value, "array", path, entity, issues);
57
+ break;
58
+ }
59
+ checkCount(value.length, field.minimum, field.maximum, "block(s)", path, entity, issues);
60
+ value.forEach((item, index) => {
61
+ if (field.allow && field.allow.length > 0 && require_is_record.isRecord(item) && typeof item.component === "string" && !field.allow.includes(item.component)) issues.push({
62
+ severity: "error",
63
+ code: "disallowed_component",
64
+ path: [
65
+ ...path,
66
+ index,
67
+ "component"
68
+ ],
69
+ entity,
70
+ message: `Component "${item.component}" is not allowed in field "${field.name}"; allowed: ${field.allow.join(", ")}.`
71
+ });
72
+ validateBlokContent(item, blocksByName, [...path, index], issues);
73
+ });
74
+ break;
75
+ case "text":
76
+ case "textarea":
77
+ case "markdown":
78
+ if (typeof value !== "string") {
79
+ pushTypeIssue(value, "string", path, entity, issues);
80
+ break;
81
+ }
82
+ checkStringLength(field, value, path, entity, issues);
83
+ break;
84
+ case "option":
85
+ case "datetime":
86
+ if (typeof value !== "string") pushTypeIssue(value, "string", path, entity, issues);
87
+ break;
88
+ case "number":
89
+ if (typeof value !== "number") {
90
+ pushTypeIssue(value, "number", path, entity, issues);
91
+ break;
92
+ }
93
+ if (field.min_value != null && value < field.min_value) pushConstraint(`Value ${value} is below the minimum of ${field.min_value}.`, path, entity, issues);
94
+ if (field.max_value != null && value > field.max_value) pushConstraint(`Value ${value} exceeds the maximum of ${field.max_value}.`, path, entity, issues);
95
+ if (field.decimals != null && decimalPlaces(value) > field.decimals) pushConstraint(`Value ${value} has more than ${field.decimals} decimal place(s).`, path, entity, issues);
96
+ if (field.steps != null && field.steps > 0 && !isMultipleOf(value, field.steps, field.min_value ?? 0)) {
97
+ const base = field.min_value ?? 0;
98
+ pushConstraint(`Value ${value} is not a multiple of the step ${field.steps}${base ? ` (offset from ${base})` : ""}.`, path, entity, issues);
99
+ }
100
+ break;
101
+ case "boolean":
102
+ if (typeof value !== "boolean") pushTypeIssue(value, "boolean", path, entity, issues);
103
+ break;
104
+ case "options":
105
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
106
+ pushTypeIssue(value, "string[]", path, entity, issues);
107
+ break;
108
+ }
109
+ checkCount(value.length, toCount(field.min_options), toCount(field.max_options), "option(s)", path, entity, issues);
110
+ break;
111
+ case "section":
112
+ case "tab": break;
113
+ default:
114
+ field.type;
115
+ break;
116
+ }
117
+ }
118
+ /** Reports a constraint (bound/length/count) violation as an error issue. */
119
+ function pushConstraint(message, path, entity, issues) {
120
+ issues.push({
121
+ severity: "error",
122
+ code: "constraint_violation",
123
+ path,
124
+ entity,
125
+ message
126
+ });
127
+ }
128
+ /** Checks an array length against optional inclusive `min`/`max` bounds. */
129
+ function checkCount(length, min, max, noun, path, entity, issues) {
130
+ if (min != null && length < min) pushConstraint(`Expected at least ${min} ${noun}, received ${length}.`, path, entity, issues);
131
+ if (max != null && length > max) pushConstraint(`Expected at most ${max} ${noun}, received ${length}.`, path, entity, issues);
132
+ }
133
+ /** Checks a string against optional `max_length`/`maxlength` and `minlength` bounds. */
134
+ function checkStringLength(field, value, path, entity, issues) {
135
+ const max = field.max_length ?? field.maxlength;
136
+ if (max != null && value.length > max) pushConstraint(`Text length ${value.length} exceeds the maximum of ${max}.`, path, entity, issues);
137
+ if (field.minlength != null && value.length < field.minlength) pushConstraint(`Text length ${value.length} is below the minimum of ${field.minlength}.`, path, entity, issues);
138
+ }
139
+ /** Counts a number's fractional digits, handling exponential notation (e.g. `1e-7` → 7). */
140
+ function decimalPlaces(value) {
141
+ if (!Number.isFinite(value)) return 0;
142
+ const [mantissa, exponent] = String(value).toLowerCase().split("e");
143
+ const fractionDigits = mantissa.includes(".") ? mantissa.split(".")[1].length : 0;
144
+ return exponent ? Math.max(0, fractionDigits - Number(exponent)) : fractionDigits;
145
+ }
146
+ /** Whether `value` lands on a `step` increment offset from `base`, with float tolerance. */
147
+ function isMultipleOf(value, step, base) {
148
+ const ratio = (value - base) / step;
149
+ const tolerance = 1e-9 * Math.max(1, Math.abs(ratio));
150
+ return Math.abs(ratio - Math.round(ratio)) <= tolerance;
151
+ }
152
+ /** Parses a numeric constraint stored as a string (e.g. `min_options`). Empty/non-numeric → undefined. */
153
+ function toCount(value) {
154
+ if (value == null || value === "") return;
155
+ const parsed = Number(value);
156
+ return Number.isFinite(parsed) ? parsed : void 0;
157
+ }
158
+ function pushTypeIssue(value, expected, path, entity, issues) {
159
+ issues.push({
160
+ severity: "error",
161
+ code: "invalid_value",
162
+ path,
163
+ entity,
164
+ message: `Expected ${expected}, received ${value === null ? "null" : typeof value}.`
165
+ });
166
+ }
167
+ /** Walks richtext `content` nodes and validates embedded bloks (`type: 'blok'`). */
168
+ function validateRichtextBloks(value, blocksByName, path, issues) {
169
+ if (!require_is_record.isRecord(value) || !Array.isArray(value.content)) return;
170
+ value.content.forEach((node, index) => {
171
+ if (!require_is_record.isRecord(node)) return;
172
+ if (node.type === "blok" && require_is_record.isRecord(node.attrs) && Array.isArray(node.attrs.body)) node.attrs.body.forEach((blok, blokIndex) => validateBlokContent(blok, blocksByName, [
173
+ ...path,
174
+ "content",
175
+ index,
176
+ "attrs",
177
+ "body",
178
+ blokIndex
179
+ ], issues));
180
+ else if (Array.isArray(node.content)) validateRichtextBloks(node, blocksByName, [
181
+ ...path,
182
+ "content",
183
+ index
184
+ ], issues);
185
+ });
186
+ }
187
+ /** Validates a single blok content object against its component definition. */
188
+ function validateBlokContent(content, blocksByName, path, issues) {
189
+ if (!require_is_record.isRecord(content)) {
190
+ issues.push({
191
+ severity: "error",
192
+ code: "invalid_content",
193
+ path,
194
+ entity: "story",
195
+ message: "Expected a block content object."
196
+ });
197
+ return;
198
+ }
199
+ const component = content.component;
200
+ const block = typeof component === "string" ? blocksByName.get(component) : void 0;
201
+ if (!block) {
202
+ issues.push({
203
+ severity: "error",
204
+ code: "unknown_component",
205
+ path: [...path, "component"],
206
+ entity: "story",
207
+ message: `Unknown component "${String(component)}".`
208
+ });
209
+ return;
210
+ }
211
+ const entity = `block:${block.name}`;
212
+ const fields = block.fields ?? [];
213
+ const fieldsByName = new Map(fields.map((field) => [field.name, field]));
214
+ for (const key of Object.keys(content)) if (!RESERVED_KEYS.has(key) && !fieldsByName.has(key)) issues.push({
215
+ severity: "warning",
216
+ code: "unknown_field",
217
+ path: [...path, key],
218
+ entity,
219
+ message: `Unknown field "${key}" on component "${block.name}".`
220
+ });
221
+ for (const field of fields) {
222
+ const value = content[field.name];
223
+ if (value === void 0 || value === null) {
224
+ if (field.required) issues.push({
225
+ severity: "error",
226
+ code: "missing_required_field",
227
+ path: [...path, field.name],
228
+ entity,
229
+ message: `Missing required field "${field.name}" on component "${block.name}".`
230
+ });
231
+ continue;
232
+ }
233
+ validateFieldValue(field, value, blocksByName, [...path, field.name], entity, issues);
234
+ }
235
+ }
236
+ /**
237
+ * Validates a story's content against a schema without throwing. Reports unknown
238
+ * components (error), unknown fields (warning), missing required fields (error),
239
+ * and invalid field-value shapes (error), recursing into nested `bloks` and
240
+ * richtext-embedded bloks.
241
+ *
242
+ * @example
243
+ * const result = validateStory(story, { blocks: { page, hero } });
244
+ */
245
+ function validateStory(story, schema) {
246
+ const issues = [];
247
+ const blocksByName = new Map(require_shapes.toValues(schema.blocks).map((block) => [block.name, block]));
248
+ validateBlokContent(require_is_record.isRecord(story) ? story.content : void 0, blocksByName, ["content"], issues);
249
+ return {
250
+ ok: issues.every((issue) => issue.severity !== "error"),
251
+ issues
252
+ };
253
+ }
254
+
255
+ //#endregion
256
+ exports.validateStory = validateStory;
257
+ //# sourceMappingURL=validate-story.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-story.cjs","names":["zAssetFieldValue","zMultilinkFieldValue","zTableFieldValue","zRichtextFieldValue","zPluginFieldValue","isRecord","toValues"],"sources":["../../src/validators/validate-story.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\nimport type { SchemaBlockLike, SchemaFieldLike, SchemaLike } from './shapes';\nimport type { ValidationIssue, ValidationResult } from './types';\nimport {\n zAssetFieldValue,\n zMultilinkFieldValue,\n zPluginFieldValue,\n zRichtextFieldValue,\n zTableFieldValue,\n} from './internal-schemas';\nimport { isRecord, toValues } from './shapes';\n\n/** Field-content keys that are not user-defined fields. */\nconst RESERVED_KEYS = new Set(['_uid', 'component', '_editable']);\n\n/** Maps a Standard Schema validator to a {@link ValidationIssue} reporter at `path`. */\nfunction checkValue(\n schema: StandardSchemaV1,\n value: unknown,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n const result = schema['~standard'].validate(value);\n // The Zod schemas are synchronous; a thenable result would indicate misuse.\n if (result instanceof Promise) {\n return;\n }\n if (result.issues) {\n for (const issue of result.issues) {\n const issuePath = (issue.path ?? []).map(segment =>\n (typeof segment === 'object' && segment !== null ? String(segment.key) : (segment as string | number)),\n );\n issues.push({\n severity: 'error',\n code: 'invalid_value',\n path: [...path, ...issuePath],\n entity,\n message: issue.message,\n });\n }\n }\n}\n\nfunction validateFieldValue(\n field: SchemaFieldLike,\n value: unknown,\n blocksByName: Map<string, SchemaBlockLike>,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n switch (field.type) {\n case 'asset':\n checkValue(zAssetFieldValue, value, path, entity, issues);\n break;\n case 'multiasset':\n if (!Array.isArray(value)) {\n pushTypeIssue(value, 'array', path, entity, issues);\n break;\n }\n value.forEach((item, index) => checkValue(zAssetFieldValue, item, [...path, index], entity, issues));\n checkCount(value.length, field.minimum_entries, field.maximum_entries, 'asset(s)', path, entity, issues);\n break;\n case 'multilink':\n checkValue(zMultilinkFieldValue, value, path, entity, issues);\n break;\n case 'table':\n checkValue(zTableFieldValue, value, path, entity, issues);\n break;\n case 'richtext':\n checkValue(zRichtextFieldValue, value, path, entity, issues);\n validateRichtextBloks(value, blocksByName, path, issues);\n break;\n case 'custom':\n checkValue(zPluginFieldValue, value, path, entity, issues);\n break;\n case 'bloks':\n if (!Array.isArray(value)) {\n pushTypeIssue(value, 'array', path, entity, issues);\n break;\n }\n checkCount(value.length, field.minimum, field.maximum, 'block(s)', path, entity, issues);\n value.forEach((item, index) => {\n if (\n field.allow && field.allow.length > 0\n && isRecord(item) && typeof item.component === 'string'\n && !field.allow.includes(item.component)\n ) {\n issues.push({\n severity: 'error',\n code: 'disallowed_component',\n path: [...path, index, 'component'],\n entity,\n message: `Component \"${item.component}\" is not allowed in field \"${field.name}\"; allowed: ${field.allow.join(', ')}.`,\n });\n }\n validateBlokContent(item, blocksByName, [...path, index], issues);\n });\n break;\n case 'text':\n case 'textarea':\n case 'markdown':\n if (typeof value !== 'string') {\n pushTypeIssue(value, 'string', path, entity, issues);\n break;\n }\n checkStringLength(field, value, path, entity, issues);\n break;\n case 'option':\n case 'datetime':\n if (typeof value !== 'string') {\n pushTypeIssue(value, 'string', path, entity, issues);\n }\n break;\n case 'number':\n if (typeof value !== 'number') {\n pushTypeIssue(value, 'number', path, entity, issues);\n break;\n }\n if (field.min_value != null && value < field.min_value) {\n pushConstraint(`Value ${value} is below the minimum of ${field.min_value}.`, path, entity, issues);\n }\n if (field.max_value != null && value > field.max_value) {\n pushConstraint(`Value ${value} exceeds the maximum of ${field.max_value}.`, path, entity, issues);\n }\n if (field.decimals != null && decimalPlaces(value) > field.decimals) {\n pushConstraint(`Value ${value} has more than ${field.decimals} decimal place(s).`, path, entity, issues);\n }\n if (field.steps != null && field.steps > 0 && !isMultipleOf(value, field.steps, field.min_value ?? 0)) {\n const base = field.min_value ?? 0;\n pushConstraint(\n `Value ${value} is not a multiple of the step ${field.steps}${base ? ` (offset from ${base})` : ''}.`,\n path,\n entity,\n issues,\n );\n }\n break;\n case 'boolean':\n if (typeof value !== 'boolean') {\n pushTypeIssue(value, 'boolean', path, entity, issues);\n }\n break;\n case 'options':\n if (!Array.isArray(value) || value.some(item => typeof item !== 'string')) {\n pushTypeIssue(value, 'string[]', path, entity, issues);\n break;\n }\n checkCount(value.length, toCount(field.min_options), toCount(field.max_options), 'option(s)', path, entity, issues);\n break;\n case 'section':\n case 'tab':\n // Layout-only field types carry no content value.\n break;\n default:\n // Exhaustiveness guard: when a new `FieldType` is added, this fails to\n // compile until the field type is handled (or explicitly skipped) above.\n field.type satisfies never;\n break;\n }\n}\n\n/** Reports a constraint (bound/length/count) violation as an error issue. */\nfunction pushConstraint(\n message: string,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n issues.push({ severity: 'error', code: 'constraint_violation', path, entity, message });\n}\n\n/** Checks an array length against optional inclusive `min`/`max` bounds. */\nfunction checkCount(\n length: number,\n min: number | undefined,\n max: number | undefined,\n noun: string,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n if (min != null && length < min) {\n pushConstraint(`Expected at least ${min} ${noun}, received ${length}.`, path, entity, issues);\n }\n if (max != null && length > max) {\n pushConstraint(`Expected at most ${max} ${noun}, received ${length}.`, path, entity, issues);\n }\n}\n\n/** Checks a string against optional `max_length`/`maxlength` and `minlength` bounds. */\nfunction checkStringLength(\n field: SchemaFieldLike,\n value: string,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n const max = field.max_length ?? field.maxlength;\n if (max != null && value.length > max) {\n pushConstraint(`Text length ${value.length} exceeds the maximum of ${max}.`, path, entity, issues);\n }\n if (field.minlength != null && value.length < field.minlength) {\n pushConstraint(`Text length ${value.length} is below the minimum of ${field.minlength}.`, path, entity, issues);\n }\n}\n\n/** Counts a number's fractional digits, handling exponential notation (e.g. `1e-7` → 7). */\nfunction decimalPlaces(value: number): number {\n if (!Number.isFinite(value)) {\n return 0;\n }\n const text = String(value).toLowerCase();\n const [mantissa, exponent] = text.split('e');\n const fractionDigits = mantissa.includes('.') ? mantissa.split('.')[1].length : 0;\n // A negative exponent (e.g. `1e-7`) adds that many fractional digits.\n return exponent ? Math.max(0, fractionDigits - Number(exponent)) : fractionDigits;\n}\n\n/** Whether `value` lands on a `step` increment offset from `base`, with float tolerance. */\nfunction isMultipleOf(value: number, step: number, base: number): boolean {\n const ratio = (value - base) / step;\n const tolerance = 1e-9 * Math.max(1, Math.abs(ratio));\n return Math.abs(ratio - Math.round(ratio)) <= tolerance;\n}\n\n/** Parses a numeric constraint stored as a string (e.g. `min_options`). Empty/non-numeric → undefined. */\nfunction toCount(value: string | undefined): number | undefined {\n if (value == null || value === '') {\n return undefined;\n }\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction pushTypeIssue(\n value: unknown,\n expected: string,\n path: (string | number)[],\n entity: string,\n issues: ValidationIssue[],\n): void {\n issues.push({\n severity: 'error',\n code: 'invalid_value',\n path,\n entity,\n message: `Expected ${expected}, received ${value === null ? 'null' : typeof value}.`,\n });\n}\n\n/** Walks richtext `content` nodes and validates embedded bloks (`type: 'blok'`). */\nfunction validateRichtextBloks(\n value: unknown,\n blocksByName: Map<string, SchemaBlockLike>,\n path: (string | number)[],\n issues: ValidationIssue[],\n): void {\n if (!isRecord(value) || !Array.isArray(value.content)) {\n return;\n }\n value.content.forEach((node, index) => {\n if (!isRecord(node)) {\n return;\n }\n if (node.type === 'blok' && isRecord(node.attrs) && Array.isArray(node.attrs.body)) {\n node.attrs.body.forEach((blok, blokIndex) =>\n validateBlokContent(blok, blocksByName, [...path, 'content', index, 'attrs', 'body', blokIndex], issues),\n );\n }\n else if (Array.isArray(node.content)) {\n // Recurse into nested marks/nodes that may themselves embed bloks.\n validateRichtextBloks(node, blocksByName, [...path, 'content', index], issues);\n }\n });\n}\n\n/** Validates a single blok content object against its component definition. */\nfunction validateBlokContent(\n content: unknown,\n blocksByName: Map<string, SchemaBlockLike>,\n path: (string | number)[],\n issues: ValidationIssue[],\n): void {\n if (!isRecord(content)) {\n issues.push({\n severity: 'error',\n code: 'invalid_content',\n path,\n entity: 'story',\n message: 'Expected a block content object.',\n });\n return;\n }\n\n const component = content.component;\n const block = typeof component === 'string' ? blocksByName.get(component) : undefined;\n if (!block) {\n issues.push({\n severity: 'error',\n code: 'unknown_component',\n path: [...path, 'component'],\n entity: 'story',\n message: `Unknown component \"${String(component)}\".`,\n });\n return;\n }\n\n const entity = `block:${block.name}`;\n const fields = block.fields ?? [];\n const fieldsByName = new Map(fields.map(field => [field.name, field]));\n\n for (const key of Object.keys(content)) {\n if (!RESERVED_KEYS.has(key) && !fieldsByName.has(key)) {\n issues.push({\n severity: 'warning',\n code: 'unknown_field',\n path: [...path, key],\n entity,\n message: `Unknown field \"${key}\" on component \"${block.name}\".`,\n });\n }\n }\n\n for (const field of fields) {\n const value = content[field.name];\n if (value === undefined || value === null) {\n if (field.required) {\n issues.push({\n severity: 'error',\n code: 'missing_required_field',\n path: [...path, field.name],\n entity,\n message: `Missing required field \"${field.name}\" on component \"${block.name}\".`,\n });\n }\n continue;\n }\n validateFieldValue(field, value, blocksByName, [...path, field.name], entity, issues);\n }\n}\n\n/**\n * Validates a story's content against a schema without throwing. Reports unknown\n * components (error), unknown fields (warning), missing required fields (error),\n * and invalid field-value shapes (error), recursing into nested `bloks` and\n * richtext-embedded bloks.\n *\n * @example\n * const result = validateStory(story, { blocks: { page, hero } });\n */\nexport function validateStory(story: unknown, schema: SchemaLike): ValidationResult {\n const issues: ValidationIssue[] = [];\n const blocksByName = new Map(toValues(schema.blocks).map(block => [block.name, block]));\n const content = isRecord(story) ? story.content : undefined;\n validateBlokContent(content, blocksByName, ['content'], issues);\n return { ok: issues.every(issue => issue.severity !== 'error'), issues };\n}\n"],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { SchemaLike } from "./shapes.cjs";
2
+ import { ValidationResult } from "./types.cjs";
3
+
4
+ //#region src/validators/validate-story.d.ts
5
+ /**
6
+ * Validates a story's content against a schema without throwing. Reports unknown
7
+ * components (error), unknown fields (warning), missing required fields (error),
8
+ * and invalid field-value shapes (error), recursing into nested `bloks` and
9
+ * richtext-embedded bloks.
10
+ *
11
+ * @example
12
+ * const result = validateStory(story, { blocks: { page, hero } });
13
+ */
14
+ declare function validateStory(story: unknown, schema: SchemaLike): ValidationResult;
15
+ //#endregion
16
+ export { validateStory };
17
+ //# sourceMappingURL=validate-story.d.cts.map
@@ -0,0 +1,17 @@
1
+ import { SchemaLike } from "./shapes.mjs";
2
+ import { ValidationResult } from "./types.mjs";
3
+
4
+ //#region src/validators/validate-story.d.ts
5
+ /**
6
+ * Validates a story's content against a schema without throwing. Reports unknown
7
+ * components (error), unknown fields (warning), missing required fields (error),
8
+ * and invalid field-value shapes (error), recursing into nested `bloks` and
9
+ * richtext-embedded bloks.
10
+ *
11
+ * @example
12
+ * const result = validateStory(story, { blocks: { page, hero } });
13
+ */
14
+ declare function validateStory(story: unknown, schema: SchemaLike): ValidationResult;
15
+ //#endregion
16
+ export { validateStory };
17
+ //# sourceMappingURL=validate-story.d.mts.map