@storyblok/schema 0.1.0-alpha.1 → 0.1.0-alpha.3

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 (78) hide show
  1. package/dist/field-plugins/index.cjs +4 -0
  2. package/dist/field-plugins/index.d.cts +2 -0
  3. package/dist/field-plugins/index.d.mts +2 -0
  4. package/dist/field-plugins/index.mjs +3 -0
  5. package/dist/field-plugins/storyblok-color-field.cjs +32 -0
  6. package/dist/field-plugins/storyblok-color-field.cjs.map +1 -0
  7. package/dist/field-plugins/storyblok-color-field.d.cts +16 -0
  8. package/dist/field-plugins/storyblok-color-field.d.mts +16 -0
  9. package/dist/field-plugins/storyblok-color-field.mjs +32 -0
  10. package/dist/field-plugins/storyblok-color-field.mjs.map +1 -0
  11. package/dist/generated/overlay/zod.gen.cjs +0 -2
  12. package/dist/generated/overlay/zod.gen.cjs.map +1 -1
  13. package/dist/generated/overlay/zod.gen.mjs +228 -229
  14. package/dist/generated/overlay/zod.gen.mjs.map +1 -1
  15. package/dist/generated/types/block.d.cts +6 -0
  16. package/dist/generated/types/block.d.mts +6 -0
  17. package/dist/generated/types/field.d.cts +53 -18
  18. package/dist/generated/types/field.d.mts +53 -18
  19. package/dist/generated/types/mapi-story.d.cts +4 -4
  20. package/dist/generated/types/mapi-story.d.mts +4 -4
  21. package/dist/generated/types/story.d.cts +3 -3
  22. package/dist/generated/types/story.d.mts +3 -3
  23. package/dist/helpers/define-block.cjs +6 -1
  24. package/dist/helpers/define-block.cjs.map +1 -1
  25. package/dist/helpers/define-block.d.cts +14 -4
  26. package/dist/helpers/define-block.d.mts +14 -4
  27. package/dist/helpers/define-block.mjs +6 -1
  28. package/dist/helpers/define-block.mjs.map +1 -1
  29. package/dist/helpers/define-field-plugin.cjs +21 -0
  30. package/dist/helpers/define-field-plugin.cjs.map +1 -0
  31. package/dist/helpers/define-field-plugin.d.cts +33 -0
  32. package/dist/helpers/define-field-plugin.d.mts +33 -0
  33. package/dist/helpers/define-field-plugin.mjs +20 -0
  34. package/dist/helpers/define-field-plugin.mjs.map +1 -0
  35. package/dist/helpers/define-field.cjs +8 -1
  36. package/dist/helpers/define-field.cjs.map +1 -1
  37. package/dist/helpers/define-field.d.cts +12 -3
  38. package/dist/helpers/define-field.d.mts +12 -3
  39. package/dist/helpers/define-field.mjs +8 -1
  40. package/dist/helpers/define-field.mjs.map +1 -1
  41. package/dist/helpers/define-folder.cjs +19 -0
  42. package/dist/helpers/define-folder.cjs.map +1 -0
  43. package/dist/helpers/define-folder.d.cts +39 -0
  44. package/dist/helpers/define-folder.d.mts +39 -0
  45. package/dist/helpers/define-folder.mjs +18 -0
  46. package/dist/helpers/define-folder.mjs.map +1 -0
  47. package/dist/helpers/define-schema.cjs +23 -0
  48. package/dist/helpers/define-schema.cjs.map +1 -0
  49. package/dist/helpers/define-schema.d.cts +31 -0
  50. package/dist/helpers/define-schema.d.mts +31 -0
  51. package/dist/helpers/define-schema.mjs +22 -0
  52. package/dist/helpers/define-schema.mjs.map +1 -0
  53. package/dist/helpers/schema-type.d.cts +16 -8
  54. package/dist/helpers/schema-type.d.mts +16 -8
  55. package/dist/index.cjs +6 -0
  56. package/dist/index.d.cts +4 -1
  57. package/dist/index.d.mts +4 -1
  58. package/dist/index.mjs +4 -1
  59. package/dist/utils/slugify-folder-path.cjs +21 -0
  60. package/dist/utils/slugify-folder-path.cjs.map +1 -0
  61. package/dist/utils/slugify-folder-path.mjs +20 -0
  62. package/dist/utils/slugify-folder-path.mjs.map +1 -0
  63. package/dist/validators/internal-schemas.mjs +1 -1
  64. package/dist/validators/shapes.cjs.map +1 -1
  65. package/dist/validators/shapes.d.cts +16 -3
  66. package/dist/validators/shapes.d.mts +16 -3
  67. package/dist/validators/shapes.mjs.map +1 -1
  68. package/dist/validators/validate-schema.cjs +21 -1
  69. package/dist/validators/validate-schema.cjs.map +1 -1
  70. package/dist/validators/validate-schema.d.cts +2 -1
  71. package/dist/validators/validate-schema.d.mts +2 -1
  72. package/dist/validators/validate-schema.mjs +21 -1
  73. package/dist/validators/validate-schema.mjs.map +1 -1
  74. package/dist/validators/validate-story.cjs +96 -32
  75. package/dist/validators/validate-story.cjs.map +1 -1
  76. package/dist/validators/validate-story.mjs +96 -33
  77. package/dist/validators/validate-story.mjs.map +1 -1
  78. package/package.json +13 -2
@@ -1 +1 @@
1
- {"version":3,"file":"define-block.mjs","names":[],"sources":["../../src/helpers/define-block.ts"],"sourcesContent":["import type {\n Block,\n BlockFields,\n NestableBlock,\n RootBlock,\n} from '../generated/types/block';\nimport type { Prettify } from '../utils/prettify';\n\nexport type { Block, BlockFields, NestableBlock, RootBlock };\n\nconst BLOCK_DEFAULTS = {\n id: 1,\n created_at: '',\n updated_at: '',\n is_root: false,\n is_nestable: true,\n};\n\n/** Fields that have safe defaults and may be omitted from block input. */\ntype BlockOptional = keyof typeof BLOCK_DEFAULTS;\n\ntype BlockInput<\n TName extends string = string,\n TFields extends BlockFields = BlockFields,\n TIsRoot extends boolean = false,\n TIsNestable extends boolean = true,\n> = Prettify<\n Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | BlockOptional> & {\n name: TName;\n fields: TFields;\n is_root?: TIsRoot;\n is_nestable?: TIsNestable;\n } & Partial<Pick<Block, Exclude<BlockOptional, 'is_root' | 'is_nestable'>>>\n>;\n\ntype DefinedBlock<\n TName extends string,\n TFields extends BlockFields,\n TIsRoot extends boolean,\n TIsNestable extends boolean,\n> = Prettify<\n Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable'> & {\n name: TName;\n fields: TFields;\n is_root: TIsRoot;\n is_nestable: TIsNestable;\n }\n>;\n\n/**\n * Returns a {@link Block} content-shape definition. The user-facing input is an\n * ordered array of `defineField` calls under `fields`; the array index becomes\n * each field's `pos`. A thin, strongly-typed helper — it does not map to the\n * MAPI wire shape (the CLI owns the DSL→wire mapping). Throws only on duplicate\n * field names (a programming error).\n *\n * @example\n * const pageBlock = defineBlock({\n * name: 'page',\n * is_root: true,\n * fields: [\n * defineField('headline', { type: 'text', required: true }),\n * ],\n * });\n */\nexport function defineBlock<\n TName extends string,\n const TFields extends BlockFields,\n TIsRoot extends boolean = false,\n TIsNestable extends boolean = true,\n>(\n block: BlockInput<TName, TFields, TIsRoot, TIsNestable>,\n): DefinedBlock<TName, TFields, TIsRoot, TIsNestable>;\n\nexport function defineBlock(block: any) {\n const inputFields = Array.isArray(block?.fields) ? block.fields : [];\n const seen = new Set<string>();\n const fields = inputFields.map((field: any, index: number) => {\n const name = field?.name;\n if (typeof name === 'string') {\n if (seen.has(name)) {\n throw new Error(`defineBlock: duplicate field name \"${name}\" in block \"${block?.name ?? ''}\"`);\n }\n seen.add(name);\n }\n return { ...field, pos: index };\n });\n return { ...BLOCK_DEFAULTS, ...block, fields };\n}\n"],"mappings":";AAUA,MAAM,iBAAiB;CACrB,IAAI;CACJ,YAAY;CACZ,YAAY;CACZ,SAAS;CACT,aAAa;CACd;AA0DD,SAAgB,YAAY,OAAY;CACtC,MAAM,cAAc,MAAM,QAAQ,OAAO,OAAO,GAAG,MAAM,SAAS,EAAE;CACpE,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAAS,YAAY,KAAK,OAAY,UAAkB;EAC5D,MAAM,OAAO,OAAO;AACpB,MAAI,OAAO,SAAS,UAAU;AAC5B,OAAI,KAAK,IAAI,KAAK,CAChB,OAAM,IAAI,MAAM,sCAAsC,KAAK,cAAc,OAAO,QAAQ,GAAG,GAAG;AAEhG,QAAK,IAAI,KAAK;;AAEhB,SAAO;GAAE,GAAG;GAAO,KAAK;GAAO;GAC/B;AACF,QAAO;EAAE,GAAG;EAAgB,GAAG;EAAO;EAAQ"}
1
+ {"version":3,"file":"define-block.mjs","names":[],"sources":["../../src/helpers/define-block.ts"],"sourcesContent":["import type {\n Block,\n BlockFields,\n NestableBlock,\n RootBlock,\n} from '../generated/types/block';\nimport type { BlockFolder } from './define-folder';\nimport type { Prettify } from '../utils/prettify';\n\nexport type { Block, BlockFields, NestableBlock, RootBlock };\n\nconst BLOCK_DEFAULTS = {\n id: 1,\n created_at: '',\n updated_at: '',\n is_root: false,\n is_nestable: true,\n};\n\n/** Fields that have safe defaults and may be omitted from block input. */\ntype BlockOptional = keyof typeof BLOCK_DEFAULTS;\n\n/** Accepted input for a block's `folder`: a folder ref, a raw path string, or `null` to explicitly ungroup. */\ntype FolderInput = BlockFolder | string | null;\n\n/** Normalizes a folder input type to the display path literal it resolves to. */\ntype NormalizeFolder<T> = T extends { path: infer P extends string } ? P : T;\n\ntype BlockInput<\n TName extends string = string,\n TFields extends BlockFields = BlockFields,\n TIsRoot extends boolean = false,\n TIsNestable extends boolean = true,\n TFolder extends FolderInput | undefined = undefined,\n> = Prettify<\n Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder' | BlockOptional> & {\n name: TName;\n fields: TFields;\n is_root?: TIsRoot;\n is_nestable?: TIsNestable;\n folder?: TFolder;\n } & Partial<Pick<Block, Exclude<BlockOptional, 'is_root' | 'is_nestable'>>>\n>;\n\ntype DefinedBlock<\n TName extends string,\n TFields extends BlockFields,\n TIsRoot extends boolean,\n TIsNestable extends boolean,\n TFolder extends FolderInput | undefined = undefined,\n> = Prettify<\n Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder'> & {\n name: TName;\n fields: TFields;\n is_root: TIsRoot;\n is_nestable: TIsNestable;\n } & (TFolder extends undefined ? unknown : { folder: NormalizeFolder<TFolder> })\n>;\n\n/**\n * Returns a {@link Block} content-shape definition. The user-facing input is an\n * ordered array of `defineField` calls under `fields`; the array index becomes\n * each field's `pos`. A thin, strongly-typed helper — it does not map to the\n * MAPI wire shape (the CLI owns the DSL→wire mapping). Throws only on duplicate\n * field names (a programming error).\n *\n * @example\n * const pageBlock = defineBlock({\n * name: 'page',\n * is_root: true,\n * fields: [\n * defineField('headline', { type: 'text', required: true }),\n * ],\n * });\n */\nexport function defineBlock<\n TName extends string,\n const TFields extends BlockFields,\n TIsRoot extends boolean = false,\n TIsNestable extends boolean = true,\n const TFolder extends FolderInput | undefined = undefined,\n>(\n block: BlockInput<TName, TFields, TIsRoot, TIsNestable, TFolder>,\n): DefinedBlock<TName, TFields, TIsRoot, TIsNestable, TFolder>;\n\nexport function defineBlock(block: any) {\n const inputFields = Array.isArray(block?.fields) ? block.fields : [];\n const seen = new Set<string>();\n const fields = inputFields.map((field: any, index: number) => {\n const name = field?.name;\n if (typeof name === 'string') {\n if (seen.has(name)) {\n throw new Error(`defineBlock: duplicate field name \"${name}\" in block \"${block?.name ?? ''}\"`);\n }\n seen.add(name);\n }\n return { ...field, pos: index };\n });\n\n const { folder, ...restBlock } = block ?? {};\n if (folder !== undefined && typeof restBlock.component_group_uuid === 'string') {\n throw new Error(`defineBlock: block \"${block?.name ?? ''}\" sets both \"folder\" and \"component_group_uuid\"; use one`);\n }\n if (typeof folder === 'string' && !folder.split('/').some(segment => segment.trim() !== '')) {\n throw new Error(`defineBlock: block \"${block?.name ?? ''}\" has an empty \"folder\" path`);\n }\n const normalizedFolder = folder === undefined\n ? undefined\n : (folder !== null && typeof folder === 'object' ? folder.path : folder);\n\n return {\n ...BLOCK_DEFAULTS,\n ...restBlock,\n ...(folder !== undefined && { folder: normalizedFolder }),\n fields,\n };\n}\n"],"mappings":";AAWA,MAAM,iBAAiB;CACrB,IAAI;CACJ,YAAY;CACZ,YAAY;CACZ,SAAS;CACT,aAAa;CACd;AAoED,SAAgB,YAAY,OAAY;CACtC,MAAM,cAAc,MAAM,QAAQ,OAAO,OAAO,GAAG,MAAM,SAAS,EAAE;CACpE,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAAS,YAAY,KAAK,OAAY,UAAkB;EAC5D,MAAM,OAAO,OAAO;AACpB,MAAI,OAAO,SAAS,UAAU;AAC5B,OAAI,KAAK,IAAI,KAAK,CAChB,OAAM,IAAI,MAAM,sCAAsC,KAAK,cAAc,OAAO,QAAQ,GAAG,GAAG;AAEhG,QAAK,IAAI,KAAK;;AAEhB,SAAO;GAAE,GAAG;GAAO,KAAK;GAAO;GAC/B;CAEF,MAAM,EAAE,QAAQ,GAAG,cAAc,SAAS,EAAE;AAC5C,KAAI,WAAW,UAAa,OAAO,UAAU,yBAAyB,SACpE,OAAM,IAAI,MAAM,uBAAuB,OAAO,QAAQ,GAAG,0DAA0D;AAErH,KAAI,OAAO,WAAW,YAAY,CAAC,OAAO,MAAM,IAAI,CAAC,MAAK,YAAW,QAAQ,MAAM,KAAK,GAAG,CACzF,OAAM,IAAI,MAAM,uBAAuB,OAAO,QAAQ,GAAG,8BAA8B;CAEzF,MAAM,mBAAmB,WAAW,SAChC,SACC,WAAW,QAAQ,OAAO,WAAW,WAAW,OAAO,OAAO;AAEnE,QAAO;EACL,GAAG;EACH,GAAG;EACH,GAAI,WAAW,UAAa,EAAE,QAAQ,kBAAkB;EACxD;EACD"}
@@ -0,0 +1,21 @@
1
+
2
+ //#region src/helpers/define-field-plugin.ts
3
+ /**
4
+ * Declares a custom field plugin: both the `fieldType` literal and the value
5
+ * type infer from the single argument. `value` accepts any Standard Schema
6
+ * validator (Zod, Valibot, ArkType, or hand-written) and is retained for
7
+ * runtime use. A thin, strongly-typed identity helper — it does not validate.
8
+ *
9
+ * @example
10
+ * const colorPicker = defineFieldPlugin({
11
+ * fieldType: 'my-custom-color-picker',
12
+ * value: z.object({ color: z.string(), alpha: z.number() }),
13
+ * });
14
+ */
15
+ function defineFieldPlugin(config) {
16
+ return config;
17
+ }
18
+
19
+ //#endregion
20
+ exports.defineFieldPlugin = defineFieldPlugin;
21
+ //# sourceMappingURL=define-field-plugin.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-field-plugin.cjs","names":[],"sources":["../../src/helpers/define-field-plugin.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * Binds a Storyblok custom field `field_type` to a\n * [Standard Schema](https://standardschema.dev) validator. The validator (`S`,\n * retained on `value`) supplies both the static value type\n * (`StandardSchemaV1.InferOutput<S>`) and a runtime handle, so a single\n * declaration serves types and (future) runtime validation.\n */\nexport interface FieldPlugin<\n F extends string = string,\n S extends StandardSchemaV1 = StandardSchemaV1,\n> {\n fieldType: F;\n value: S;\n}\n\n/**\n * Declares a custom field plugin: both the `fieldType` literal and the value\n * type infer from the single argument. `value` accepts any Standard Schema\n * validator (Zod, Valibot, ArkType, or hand-written) and is retained for\n * runtime use. A thin, strongly-typed identity helper — it does not validate.\n *\n * @example\n * const colorPicker = defineFieldPlugin({\n * fieldType: 'my-custom-color-picker',\n * value: z.object({ color: z.string(), alpha: z.number() }),\n * });\n */\nexport function defineFieldPlugin<\n const F extends string,\n S extends StandardSchemaV1,\n>(config: { fieldType: F; value: S }): FieldPlugin<F, S> {\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;AA6BA,SAAgB,kBAGd,QAAuD;AACvD,QAAO"}
@@ -0,0 +1,33 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+
3
+ //#region src/helpers/define-field-plugin.d.ts
4
+ /**
5
+ * Binds a Storyblok custom field `field_type` to a
6
+ * [Standard Schema](https://standardschema.dev) validator. The validator (`S`,
7
+ * retained on `value`) supplies both the static value type
8
+ * (`StandardSchemaV1.InferOutput<S>`) and a runtime handle, so a single
9
+ * declaration serves types and (future) runtime validation.
10
+ */
11
+ interface FieldPlugin<F extends string = string, S extends StandardSchemaV1 = StandardSchemaV1> {
12
+ fieldType: F;
13
+ value: S;
14
+ }
15
+ /**
16
+ * Declares a custom field plugin: both the `fieldType` literal and the value
17
+ * type infer from the single argument. `value` accepts any Standard Schema
18
+ * validator (Zod, Valibot, ArkType, or hand-written) and is retained for
19
+ * runtime use. A thin, strongly-typed identity helper — it does not validate.
20
+ *
21
+ * @example
22
+ * const colorPicker = defineFieldPlugin({
23
+ * fieldType: 'my-custom-color-picker',
24
+ * value: z.object({ color: z.string(), alpha: z.number() }),
25
+ * });
26
+ */
27
+ declare function defineFieldPlugin<const F extends string, S extends StandardSchemaV1>(config: {
28
+ fieldType: F;
29
+ value: S;
30
+ }): FieldPlugin<F, S>;
31
+ //#endregion
32
+ export { FieldPlugin, defineFieldPlugin };
33
+ //# sourceMappingURL=define-field-plugin.d.cts.map
@@ -0,0 +1,33 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+
3
+ //#region src/helpers/define-field-plugin.d.ts
4
+ /**
5
+ * Binds a Storyblok custom field `field_type` to a
6
+ * [Standard Schema](https://standardschema.dev) validator. The validator (`S`,
7
+ * retained on `value`) supplies both the static value type
8
+ * (`StandardSchemaV1.InferOutput<S>`) and a runtime handle, so a single
9
+ * declaration serves types and (future) runtime validation.
10
+ */
11
+ interface FieldPlugin<F extends string = string, S extends StandardSchemaV1 = StandardSchemaV1> {
12
+ fieldType: F;
13
+ value: S;
14
+ }
15
+ /**
16
+ * Declares a custom field plugin: both the `fieldType` literal and the value
17
+ * type infer from the single argument. `value` accepts any Standard Schema
18
+ * validator (Zod, Valibot, ArkType, or hand-written) and is retained for
19
+ * runtime use. A thin, strongly-typed identity helper — it does not validate.
20
+ *
21
+ * @example
22
+ * const colorPicker = defineFieldPlugin({
23
+ * fieldType: 'my-custom-color-picker',
24
+ * value: z.object({ color: z.string(), alpha: z.number() }),
25
+ * });
26
+ */
27
+ declare function defineFieldPlugin<const F extends string, S extends StandardSchemaV1>(config: {
28
+ fieldType: F;
29
+ value: S;
30
+ }): FieldPlugin<F, S>;
31
+ //#endregion
32
+ export { FieldPlugin, defineFieldPlugin };
33
+ //# sourceMappingURL=define-field-plugin.d.mts.map
@@ -0,0 +1,20 @@
1
+ //#region src/helpers/define-field-plugin.ts
2
+ /**
3
+ * Declares a custom field plugin: both the `fieldType` literal and the value
4
+ * type infer from the single argument. `value` accepts any Standard Schema
5
+ * validator (Zod, Valibot, ArkType, or hand-written) and is retained for
6
+ * runtime use. A thin, strongly-typed identity helper — it does not validate.
7
+ *
8
+ * @example
9
+ * const colorPicker = defineFieldPlugin({
10
+ * fieldType: 'my-custom-color-picker',
11
+ * value: z.object({ color: z.string(), alpha: z.number() }),
12
+ * });
13
+ */
14
+ function defineFieldPlugin(config) {
15
+ return config;
16
+ }
17
+
18
+ //#endregion
19
+ export { defineFieldPlugin };
20
+ //# sourceMappingURL=define-field-plugin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-field-plugin.mjs","names":[],"sources":["../../src/helpers/define-field-plugin.ts"],"sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n/**\n * Binds a Storyblok custom field `field_type` to a\n * [Standard Schema](https://standardschema.dev) validator. The validator (`S`,\n * retained on `value`) supplies both the static value type\n * (`StandardSchemaV1.InferOutput<S>`) and a runtime handle, so a single\n * declaration serves types and (future) runtime validation.\n */\nexport interface FieldPlugin<\n F extends string = string,\n S extends StandardSchemaV1 = StandardSchemaV1,\n> {\n fieldType: F;\n value: S;\n}\n\n/**\n * Declares a custom field plugin: both the `fieldType` literal and the value\n * type infer from the single argument. `value` accepts any Standard Schema\n * validator (Zod, Valibot, ArkType, or hand-written) and is retained for\n * runtime use. A thin, strongly-typed identity helper — it does not validate.\n *\n * @example\n * const colorPicker = defineFieldPlugin({\n * fieldType: 'my-custom-color-picker',\n * value: z.object({ color: z.string(), alpha: z.number() }),\n * });\n */\nexport function defineFieldPlugin<\n const F extends string,\n S extends StandardSchemaV1,\n>(config: { fieldType: F; value: S }): FieldPlugin<F, S> {\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,SAAgB,kBAGd,QAAuD;AACvD,QAAO"}
@@ -1,13 +1,20 @@
1
1
  const require_is_record = require('../utils/is-record.cjs');
2
2
 
3
3
  //#region src/helpers/define-field.ts
4
+ /** Type guard for a defined folder ref: has `path`, and never `fields` (defined block) or `slug` (datasource). */
5
+ const isFolderRef = (ref) => require_is_record.isRecord(ref) && typeof ref.path === "string" && !Array.isArray(ref.fields) && !("slug" in ref);
4
6
  function defineField(name, field) {
5
7
  const { allow, datasource, ...rest } = field;
6
8
  const normalized = {
7
9
  ...rest,
8
10
  name
9
11
  };
10
- if (allow !== void 0) normalized.allow = (Array.isArray(allow) ? allow : [allow]).map((ref) => typeof ref === "string" ? ref : require_is_record.isRecord(ref) ? ref.name : void 0);
12
+ if (allow !== void 0) {
13
+ const refs = Array.isArray(allow) ? allow : [allow];
14
+ const folderRefs = refs.filter(isFolderRef);
15
+ if (folderRefs.length > 0 && folderRefs.length < refs.length) throw new Error(`defineField: "allow" on field "${name}" mixes block and folder references; the editor restricts by either blocks or folders, not both`);
16
+ normalized.allow = refs.map((ref) => isFolderRef(ref) ? { folder: ref.path } : typeof ref === "string" ? ref : require_is_record.isRecord(ref) ? ref.name : void 0);
17
+ }
11
18
  if (datasource !== void 0) normalized.datasource = typeof datasource === "string" ? datasource : require_is_record.isRecord(datasource) ? datasource.slug : void 0;
12
19
  return normalized;
13
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"define-field.cjs","names":["isRecord"],"sources":["../../src/helpers/define-field.ts"],"sourcesContent":["import type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n} from '../generated/types/field';\nimport type { Prettify } from '../utils/prettify';\nimport { isRecord } from '../utils/is-record';\n\nexport type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n};\n\n/** A block reference for `allow`: a defined block object or its name. */\ntype AllowRef = string | { name: string };\n/** A datasource reference for `datasource`: a defined datasource object or its slug. */\ntype DatasourceRef = string | { slug: string };\n\ntype NameOf<T> = T extends string ? T : T extends { name: infer N extends string } ? N : never;\ntype SlugOf<T> = T extends string ? T : T extends { slug: infer S extends string } ? S : never;\n\n/** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of block-name strings. */\ntype NormalizeAllow<T> = T extends readonly any[]\n ? { [I in keyof T]: NameOf<T[I]> }\n : readonly [NameOf<T>];\n\n/**\n * Field config accepted by {@link defineField}: the content-shape field plus the\n * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`\n * holds the datasource ref/slug (the wire `source` selector still passes through).\n */\nexport type FieldInput = Field & {\n allow?: AllowRef | readonly AllowRef[];\n datasource?: DatasourceRef;\n required?: boolean;\n};\n\n/** Result of {@link defineField}: the field stamped with `name`, with refs normalized to strings. */\nexport type DefinedField<TName extends string, TField extends FieldInput> = Prettify<\n Omit<TField, 'allow' | 'datasource' | 'name'>\n & { name: TName }\n & (TField extends { allow: infer A } ? { allow: NormalizeAllow<A> } : unknown)\n & (TField extends { datasource: infer D } ? { datasource: SlugOf<D> } : unknown)\n>;\n\n/**\n * Returns a {@link Field} stamped with the given `name`, normalizing reference\n * keys to strings so everything downstream sees plain names/slugs. A thin,\n * strongly-typed identity helper — it does not validate or throw.\n *\n * Use inside a {@link defineBlock} `fields` array — `pos` is injected from the\n * array index by `defineBlock`.\n *\n * @example\n * defineField('headline', { type: 'text', max_length: 100, required: true });\n * defineField('body', { type: 'bloks', allow: [heroBlock, 'teaser'] });\n * defineField('theme', { type: 'option', source: 'internal', datasource: colors });\n */\nexport function defineField<\n const TName extends string,\n const TField extends FieldInput,\n>(name: TName, field: TField): DefinedField<TName, TField>;\nexport function defineField(name: string, field: Record<string, unknown>): Record<string, unknown> {\n const { allow, datasource, ...rest } = field;\n const normalized: Record<string, unknown> = { ...rest, name };\n if (allow !== undefined) {\n const refs = Array.isArray(allow) ? allow : [allow];\n normalized.allow = refs.map(ref => (typeof ref === 'string' ? ref : isRecord(ref) ? ref.name : undefined));\n }\n if (datasource !== undefined) {\n normalized.datasource = typeof datasource === 'string' ? datasource : isRecord(datasource) ? datasource.slug : undefined;\n }\n return normalized;\n}\n"],"mappings":";;;AAiFA,SAAgB,YAAY,MAAc,OAAyD;CACjG,MAAM,EAAE,OAAO,YAAY,GAAG,SAAS;CACvC,MAAM,aAAsC;EAAE,GAAG;EAAM;EAAM;AAC7D,KAAI,UAAU,OAEZ,YAAW,SADE,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,EAC3B,KAAI,QAAQ,OAAO,QAAQ,WAAW,MAAMA,2BAAS,IAAI,GAAG,IAAI,OAAO,OAAW;AAE5G,KAAI,eAAe,OACjB,YAAW,aAAa,OAAO,eAAe,WAAW,aAAaA,2BAAS,WAAW,GAAG,WAAW,OAAO;AAEjH,QAAO"}
1
+ {"version":3,"file":"define-field.cjs","names":["isRecord"],"sources":["../../src/helpers/define-field.ts"],"sourcesContent":["import type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n} from '../generated/types/field';\nimport type { BlockFolder } from './define-folder';\nimport type { Prettify } from '../utils/prettify';\nimport { isRecord } from '../utils/is-record';\n\nexport type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n};\n\n/** A block reference for `allow`: a defined block object or its name. */\ntype AllowRef = string | { name: string };\n/** A folder reference for `allow`: a defined folder object (no string shorthand — bare strings are block names). */\ntype FolderAllowRef = BlockFolder;\n/** A datasource reference for `datasource`: a defined datasource object or its slug. */\ntype DatasourceRef = string | { slug: string };\n\ntype NameOf<T> = T extends string ? T : T extends { name: infer N extends string } ? N : never;\ntype SlugOf<T> = T extends string ? T : T extends { slug: infer S extends string } ? S : never;\n\n/** Normalizes a single `allow` entry: folder refs to `{ folder: path }`, everything else to a name string. */\ntype NormalizeAllowEntry<T> = T extends { path: infer P extends string }\n ? { folder: P }\n : NameOf<T>;\n/** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of normalized entries. */\ntype NormalizeAllow<T> = T extends readonly any[]\n ? { [I in keyof T]: NormalizeAllowEntry<T[I]> }\n : readonly [NormalizeAllowEntry<T>];\n\n/** Type guard for a defined folder ref: has `path`, and never `fields` (defined block) or `slug` (datasource). */\nconst isFolderRef = (ref: unknown): ref is BlockFolder =>\n isRecord(ref) && typeof ref.path === 'string' && !Array.isArray(ref.fields) && !('slug' in ref);\n\n/**\n * Field config accepted by {@link defineField}: the content-shape field plus the\n * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`\n * holds the datasource ref/slug (the wire `source` selector still passes through).\n */\nexport type FieldInput = Field & {\n allow?: AllowRef | FolderAllowRef | readonly (AllowRef | FolderAllowRef)[];\n datasource?: DatasourceRef;\n required?: boolean;\n};\n\n/** Result of {@link defineField}: the field stamped with `name`, with refs normalized to strings. */\nexport type DefinedField<TName extends string, TField extends FieldInput> = Prettify<\n Omit<TField, 'allow' | 'datasource' | 'name'>\n & { name: TName }\n & (TField extends { allow: infer A } ? { allow: NormalizeAllow<A> } : unknown)\n & (TField extends { datasource: infer D } ? { datasource: SlugOf<D> } : unknown)\n>;\n\n/**\n * Returns a {@link Field} stamped with the given `name`, normalizing reference\n * keys to strings so everything downstream sees plain names/slugs. A thin,\n * strongly-typed identity helper — it does not validate or throw.\n *\n * Use inside a {@link defineBlock} `fields` array — `pos` is injected from the\n * array index by `defineBlock`.\n *\n * @example\n * defineField('headline', { type: 'text', max_length: 100, required: true });\n * defineField('body', { type: 'bloks', allow: [heroBlock, 'teaser'] });\n * defineField('theme', { type: 'option', source: 'internal', datasource: colors });\n */\nexport function defineField<\n const TName extends string,\n const TField extends FieldInput,\n>(name: TName, field: TField): DefinedField<TName, TField>;\nexport function defineField(name: string, field: Record<string, unknown>): Record<string, unknown> {\n const { allow, datasource, ...rest } = field;\n const normalized: Record<string, unknown> = { ...rest, name };\n if (allow !== undefined) {\n const refs = Array.isArray(allow) ? allow : [allow];\n const folderRefs = refs.filter(isFolderRef);\n if (folderRefs.length > 0 && folderRefs.length < refs.length) {\n throw new Error(`defineField: \"allow\" on field \"${name}\" mixes block and folder references; the editor restricts by either blocks or folders, not both`);\n }\n normalized.allow = refs.map(ref =>\n isFolderRef(ref)\n ? { folder: ref.path }\n : (typeof ref === 'string' ? ref : isRecord(ref) ? ref.name : undefined),\n );\n }\n if (datasource !== undefined) {\n normalized.datasource = typeof datasource === 'string' ? datasource : isRecord(datasource) ? datasource.slug : undefined;\n }\n return normalized;\n}\n"],"mappings":";;;;AAqDA,MAAM,eAAe,QACnBA,2BAAS,IAAI,IAAI,OAAO,IAAI,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,OAAO,IAAI,EAAE,UAAU;AAsC7F,SAAgB,YAAY,MAAc,OAAyD;CACjG,MAAM,EAAE,OAAO,YAAY,GAAG,SAAS;CACvC,MAAM,aAAsC;EAAE,GAAG;EAAM;EAAM;AAC7D,KAAI,UAAU,QAAW;EACvB,MAAM,OAAO,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;EACnD,MAAM,aAAa,KAAK,OAAO,YAAY;AAC3C,MAAI,WAAW,SAAS,KAAK,WAAW,SAAS,KAAK,OACpD,OAAM,IAAI,MAAM,kCAAkC,KAAK,iGAAiG;AAE1J,aAAW,QAAQ,KAAK,KAAI,QAC1B,YAAY,IAAI,GACZ,EAAE,QAAQ,IAAI,MAAM,GACnB,OAAO,QAAQ,WAAW,MAAMA,2BAAS,IAAI,GAAG,IAAI,OAAO,OACjE;;AAEH,KAAI,eAAe,OACjB,YAAW,aAAa,OAAO,eAAe,WAAW,aAAaA,2BAAS,WAAW,GAAG,WAAW,OAAO;AAEjH,QAAO"}
@@ -1,12 +1,15 @@
1
1
  import { AssetFieldValue, Field, MultilinkFieldValue, PluginFieldValue, RichtextFieldValue, TableFieldValue } from "../generated/overlay/_internal.gen.cjs";
2
2
  import { BlockContent, BlockContentInput, BlocksFieldValue, FieldType, FieldValue, FieldValueInput } from "../generated/types/field.cjs";
3
3
  import { Prettify } from "../utils/prettify.cjs";
4
+ import { BlockFolder } from "./define-folder.cjs";
4
5
 
5
6
  //#region src/helpers/define-field.d.ts
6
7
  /** A block reference for `allow`: a defined block object or its name. */
7
8
  type AllowRef = string | {
8
9
  name: string;
9
10
  };
11
+ /** A folder reference for `allow`: a defined folder object (no string shorthand — bare strings are block names). */
12
+ type FolderAllowRef = BlockFolder;
10
13
  /** A datasource reference for `datasource`: a defined datasource object or its slug. */
11
14
  type DatasourceRef = string | {
12
15
  slug: string;
@@ -17,15 +20,21 @@ type NameOf<T> = T extends string ? T : T extends {
17
20
  type SlugOf<T> = T extends string ? T : T extends {
18
21
  slug: infer S extends string;
19
22
  } ? S : never;
20
- /** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of block-name strings. */
21
- type NormalizeAllow<T> = T extends readonly any[] ? { [I in keyof T]: NameOf<T[I]> } : readonly [NameOf<T>];
23
+ /** Normalizes a single `allow` entry: folder refs to `{ folder: path }`, everything else to a name string. */
24
+ type NormalizeAllowEntry<T> = T extends {
25
+ path: infer P extends string;
26
+ } ? {
27
+ folder: P;
28
+ } : NameOf<T>;
29
+ /** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of normalized entries. */
30
+ type NormalizeAllow<T> = T extends readonly any[] ? { [I in keyof T]: NormalizeAllowEntry<T[I]> } : readonly [NormalizeAllowEntry<T>];
22
31
  /**
23
32
  * Field config accepted by {@link defineField}: the content-shape field plus the
24
33
  * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`
25
34
  * holds the datasource ref/slug (the wire `source` selector still passes through).
26
35
  */
27
36
  type FieldInput = Field & {
28
- allow?: AllowRef | readonly AllowRef[];
37
+ allow?: AllowRef | FolderAllowRef | readonly (AllowRef | FolderAllowRef)[];
29
38
  datasource?: DatasourceRef;
30
39
  required?: boolean;
31
40
  };
@@ -1,12 +1,15 @@
1
1
  import { AssetFieldValue, Field, MultilinkFieldValue, PluginFieldValue, RichtextFieldValue, TableFieldValue } from "../generated/overlay/_internal.gen.mjs";
2
2
  import { BlockContent, BlockContentInput, BlocksFieldValue, FieldType, FieldValue, FieldValueInput } from "../generated/types/field.mjs";
3
3
  import { Prettify } from "../utils/prettify.mjs";
4
+ import { BlockFolder } from "./define-folder.mjs";
4
5
 
5
6
  //#region src/helpers/define-field.d.ts
6
7
  /** A block reference for `allow`: a defined block object or its name. */
7
8
  type AllowRef = string | {
8
9
  name: string;
9
10
  };
11
+ /** A folder reference for `allow`: a defined folder object (no string shorthand — bare strings are block names). */
12
+ type FolderAllowRef = BlockFolder;
10
13
  /** A datasource reference for `datasource`: a defined datasource object or its slug. */
11
14
  type DatasourceRef = string | {
12
15
  slug: string;
@@ -17,15 +20,21 @@ type NameOf<T> = T extends string ? T : T extends {
17
20
  type SlugOf<T> = T extends string ? T : T extends {
18
21
  slug: infer S extends string;
19
22
  } ? S : never;
20
- /** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of block-name strings. */
21
- type NormalizeAllow<T> = T extends readonly any[] ? { [I in keyof T]: NameOf<T[I]> } : readonly [NameOf<T>];
23
+ /** Normalizes a single `allow` entry: folder refs to `{ folder: path }`, everything else to a name string. */
24
+ type NormalizeAllowEntry<T> = T extends {
25
+ path: infer P extends string;
26
+ } ? {
27
+ folder: P;
28
+ } : NameOf<T>;
29
+ /** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of normalized entries. */
30
+ type NormalizeAllow<T> = T extends readonly any[] ? { [I in keyof T]: NormalizeAllowEntry<T[I]> } : readonly [NormalizeAllowEntry<T>];
22
31
  /**
23
32
  * Field config accepted by {@link defineField}: the content-shape field plus the
24
33
  * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`
25
34
  * holds the datasource ref/slug (the wire `source` selector still passes through).
26
35
  */
27
36
  type FieldInput = Field & {
28
- allow?: AllowRef | readonly AllowRef[];
37
+ allow?: AllowRef | FolderAllowRef | readonly (AllowRef | FolderAllowRef)[];
29
38
  datasource?: DatasourceRef;
30
39
  required?: boolean;
31
40
  };
@@ -1,13 +1,20 @@
1
1
  import { isRecord } from "../utils/is-record.mjs";
2
2
 
3
3
  //#region src/helpers/define-field.ts
4
+ /** Type guard for a defined folder ref: has `path`, and never `fields` (defined block) or `slug` (datasource). */
5
+ const isFolderRef = (ref) => isRecord(ref) && typeof ref.path === "string" && !Array.isArray(ref.fields) && !("slug" in ref);
4
6
  function defineField(name, field) {
5
7
  const { allow, datasource, ...rest } = field;
6
8
  const normalized = {
7
9
  ...rest,
8
10
  name
9
11
  };
10
- if (allow !== void 0) normalized.allow = (Array.isArray(allow) ? allow : [allow]).map((ref) => typeof ref === "string" ? ref : isRecord(ref) ? ref.name : void 0);
12
+ if (allow !== void 0) {
13
+ const refs = Array.isArray(allow) ? allow : [allow];
14
+ const folderRefs = refs.filter(isFolderRef);
15
+ if (folderRefs.length > 0 && folderRefs.length < refs.length) throw new Error(`defineField: "allow" on field "${name}" mixes block and folder references; the editor restricts by either blocks or folders, not both`);
16
+ normalized.allow = refs.map((ref) => isFolderRef(ref) ? { folder: ref.path } : typeof ref === "string" ? ref : isRecord(ref) ? ref.name : void 0);
17
+ }
11
18
  if (datasource !== void 0) normalized.datasource = typeof datasource === "string" ? datasource : isRecord(datasource) ? datasource.slug : void 0;
12
19
  return normalized;
13
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"define-field.mjs","names":[],"sources":["../../src/helpers/define-field.ts"],"sourcesContent":["import type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n} from '../generated/types/field';\nimport type { Prettify } from '../utils/prettify';\nimport { isRecord } from '../utils/is-record';\n\nexport type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n};\n\n/** A block reference for `allow`: a defined block object or its name. */\ntype AllowRef = string | { name: string };\n/** A datasource reference for `datasource`: a defined datasource object or its slug. */\ntype DatasourceRef = string | { slug: string };\n\ntype NameOf<T> = T extends string ? T : T extends { name: infer N extends string } ? N : never;\ntype SlugOf<T> = T extends string ? T : T extends { slug: infer S extends string } ? S : never;\n\n/** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of block-name strings. */\ntype NormalizeAllow<T> = T extends readonly any[]\n ? { [I in keyof T]: NameOf<T[I]> }\n : readonly [NameOf<T>];\n\n/**\n * Field config accepted by {@link defineField}: the content-shape field plus the\n * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`\n * holds the datasource ref/slug (the wire `source` selector still passes through).\n */\nexport type FieldInput = Field & {\n allow?: AllowRef | readonly AllowRef[];\n datasource?: DatasourceRef;\n required?: boolean;\n};\n\n/** Result of {@link defineField}: the field stamped with `name`, with refs normalized to strings. */\nexport type DefinedField<TName extends string, TField extends FieldInput> = Prettify<\n Omit<TField, 'allow' | 'datasource' | 'name'>\n & { name: TName }\n & (TField extends { allow: infer A } ? { allow: NormalizeAllow<A> } : unknown)\n & (TField extends { datasource: infer D } ? { datasource: SlugOf<D> } : unknown)\n>;\n\n/**\n * Returns a {@link Field} stamped with the given `name`, normalizing reference\n * keys to strings so everything downstream sees plain names/slugs. A thin,\n * strongly-typed identity helper — it does not validate or throw.\n *\n * Use inside a {@link defineBlock} `fields` array — `pos` is injected from the\n * array index by `defineBlock`.\n *\n * @example\n * defineField('headline', { type: 'text', max_length: 100, required: true });\n * defineField('body', { type: 'bloks', allow: [heroBlock, 'teaser'] });\n * defineField('theme', { type: 'option', source: 'internal', datasource: colors });\n */\nexport function defineField<\n const TName extends string,\n const TField extends FieldInput,\n>(name: TName, field: TField): DefinedField<TName, TField>;\nexport function defineField(name: string, field: Record<string, unknown>): Record<string, unknown> {\n const { allow, datasource, ...rest } = field;\n const normalized: Record<string, unknown> = { ...rest, name };\n if (allow !== undefined) {\n const refs = Array.isArray(allow) ? allow : [allow];\n normalized.allow = refs.map(ref => (typeof ref === 'string' ? ref : isRecord(ref) ? ref.name : undefined));\n }\n if (datasource !== undefined) {\n normalized.datasource = typeof datasource === 'string' ? datasource : isRecord(datasource) ? datasource.slug : undefined;\n }\n return normalized;\n}\n"],"mappings":";;;AAiFA,SAAgB,YAAY,MAAc,OAAyD;CACjG,MAAM,EAAE,OAAO,YAAY,GAAG,SAAS;CACvC,MAAM,aAAsC;EAAE,GAAG;EAAM;EAAM;AAC7D,KAAI,UAAU,OAEZ,YAAW,SADE,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM,EAC3B,KAAI,QAAQ,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI,GAAG,IAAI,OAAO,OAAW;AAE5G,KAAI,eAAe,OACjB,YAAW,aAAa,OAAO,eAAe,WAAW,aAAa,SAAS,WAAW,GAAG,WAAW,OAAO;AAEjH,QAAO"}
1
+ {"version":3,"file":"define-field.mjs","names":[],"sources":["../../src/helpers/define-field.ts"],"sourcesContent":["import type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n} from '../generated/types/field';\nimport type { BlockFolder } from './define-folder';\nimport type { Prettify } from '../utils/prettify';\nimport { isRecord } from '../utils/is-record';\n\nexport type {\n AssetFieldValue,\n BlockContent,\n BlockContentInput,\n BlocksFieldValue,\n Field,\n FieldType,\n FieldValue,\n FieldValueInput,\n MultilinkFieldValue,\n PluginFieldValue,\n RichtextFieldValue,\n TableFieldValue,\n};\n\n/** A block reference for `allow`: a defined block object or its name. */\ntype AllowRef = string | { name: string };\n/** A folder reference for `allow`: a defined folder object (no string shorthand — bare strings are block names). */\ntype FolderAllowRef = BlockFolder;\n/** A datasource reference for `datasource`: a defined datasource object or its slug. */\ntype DatasourceRef = string | { slug: string };\n\ntype NameOf<T> = T extends string ? T : T extends { name: infer N extends string } ? N : never;\ntype SlugOf<T> = T extends string ? T : T extends { slug: infer S extends string } ? S : never;\n\n/** Normalizes a single `allow` entry: folder refs to `{ folder: path }`, everything else to a name string. */\ntype NormalizeAllowEntry<T> = T extends { path: infer P extends string }\n ? { folder: P }\n : NameOf<T>;\n/** Normalizes an `allow` input (ref, name, or array thereof) to a tuple of normalized entries. */\ntype NormalizeAllow<T> = T extends readonly any[]\n ? { [I in keyof T]: NormalizeAllowEntry<T[I]> }\n : readonly [NormalizeAllowEntry<T>];\n\n/** Type guard for a defined folder ref: has `path`, and never `fields` (defined block) or `slug` (datasource). */\nconst isFolderRef = (ref: unknown): ref is BlockFolder =>\n isRecord(ref) && typeof ref.path === 'string' && !Array.isArray(ref.fields) && !('slug' in ref);\n\n/**\n * Field config accepted by {@link defineField}: the content-shape field plus the\n * DSL reference keys. `allow` replaces the wire `component_whitelist`; `datasource`\n * holds the datasource ref/slug (the wire `source` selector still passes through).\n */\nexport type FieldInput = Field & {\n allow?: AllowRef | FolderAllowRef | readonly (AllowRef | FolderAllowRef)[];\n datasource?: DatasourceRef;\n required?: boolean;\n};\n\n/** Result of {@link defineField}: the field stamped with `name`, with refs normalized to strings. */\nexport type DefinedField<TName extends string, TField extends FieldInput> = Prettify<\n Omit<TField, 'allow' | 'datasource' | 'name'>\n & { name: TName }\n & (TField extends { allow: infer A } ? { allow: NormalizeAllow<A> } : unknown)\n & (TField extends { datasource: infer D } ? { datasource: SlugOf<D> } : unknown)\n>;\n\n/**\n * Returns a {@link Field} stamped with the given `name`, normalizing reference\n * keys to strings so everything downstream sees plain names/slugs. A thin,\n * strongly-typed identity helper — it does not validate or throw.\n *\n * Use inside a {@link defineBlock} `fields` array — `pos` is injected from the\n * array index by `defineBlock`.\n *\n * @example\n * defineField('headline', { type: 'text', max_length: 100, required: true });\n * defineField('body', { type: 'bloks', allow: [heroBlock, 'teaser'] });\n * defineField('theme', { type: 'option', source: 'internal', datasource: colors });\n */\nexport function defineField<\n const TName extends string,\n const TField extends FieldInput,\n>(name: TName, field: TField): DefinedField<TName, TField>;\nexport function defineField(name: string, field: Record<string, unknown>): Record<string, unknown> {\n const { allow, datasource, ...rest } = field;\n const normalized: Record<string, unknown> = { ...rest, name };\n if (allow !== undefined) {\n const refs = Array.isArray(allow) ? allow : [allow];\n const folderRefs = refs.filter(isFolderRef);\n if (folderRefs.length > 0 && folderRefs.length < refs.length) {\n throw new Error(`defineField: \"allow\" on field \"${name}\" mixes block and folder references; the editor restricts by either blocks or folders, not both`);\n }\n normalized.allow = refs.map(ref =>\n isFolderRef(ref)\n ? { folder: ref.path }\n : (typeof ref === 'string' ? ref : isRecord(ref) ? ref.name : undefined),\n );\n }\n if (datasource !== undefined) {\n normalized.datasource = typeof datasource === 'string' ? datasource : isRecord(datasource) ? datasource.slug : undefined;\n }\n return normalized;\n}\n"],"mappings":";;;;AAqDA,MAAM,eAAe,QACnB,SAAS,IAAI,IAAI,OAAO,IAAI,SAAS,YAAY,CAAC,MAAM,QAAQ,IAAI,OAAO,IAAI,EAAE,UAAU;AAsC7F,SAAgB,YAAY,MAAc,OAAyD;CACjG,MAAM,EAAE,OAAO,YAAY,GAAG,SAAS;CACvC,MAAM,aAAsC;EAAE,GAAG;EAAM;EAAM;AAC7D,KAAI,UAAU,QAAW;EACvB,MAAM,OAAO,MAAM,QAAQ,MAAM,GAAG,QAAQ,CAAC,MAAM;EACnD,MAAM,aAAa,KAAK,OAAO,YAAY;AAC3C,MAAI,WAAW,SAAS,KAAK,WAAW,SAAS,KAAK,OACpD,OAAM,IAAI,MAAM,kCAAkC,KAAK,iGAAiG;AAE1J,aAAW,QAAQ,KAAK,KAAI,QAC1B,YAAY,IAAI,GACZ,EAAE,QAAQ,IAAI,MAAM,GACnB,OAAO,QAAQ,WAAW,MAAM,SAAS,IAAI,GAAG,IAAI,OAAO,OACjE;;AAEH,KAAI,eAAe,OACjB,YAAW,aAAa,OAAO,eAAe,WAAW,aAAa,SAAS,WAAW,GAAG,WAAW,OAAO;AAEjH,QAAO"}
@@ -0,0 +1,19 @@
1
+
2
+ //#region src/helpers/define-folder.ts
3
+ function defineFolder(input) {
4
+ if (input.name.trim() === "") throw new Error(`defineFolder: folder name must not be empty`);
5
+ if (input.name.includes("/")) throw new Error(`defineFolder: folder name "${input.name}" must not contain "/"`);
6
+ const path = input.parent ? `${input.parent.path}/${input.name}` : input.name;
7
+ return input.parent ? {
8
+ name: input.name,
9
+ parent: input.parent,
10
+ path
11
+ } : {
12
+ name: input.name,
13
+ path
14
+ };
15
+ }
16
+
17
+ //#endregion
18
+ exports.defineFolder = defineFolder;
19
+ //# sourceMappingURL=define-folder.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-folder.cjs","names":[],"sources":["../../src/helpers/define-folder.ts"],"sourcesContent":["import type { Prettify } from '../utils/prettify';\n\n/**\n * A block folder (Storyblok component group) content-shape definition. Identity\n * is the resolved name path — no UUID; the CLI matches/creates groups by path\n * at push time.\n */\nexport interface BlockFolder {\n name: string;\n parent?: BlockFolder;\n path: string;\n}\n\ntype FolderPath<TName extends string, TParent extends BlockFolder | undefined>\n = TParent extends { path: infer P extends string } ? `${P}/${TName}` : TName;\n\ntype DefinedFolder<TName extends string, TParent extends BlockFolder | undefined> = Prettify<\n { name: TName; path: FolderPath<TName, TParent> }\n & (TParent extends BlockFolder ? { parent: TParent } : unknown)\n>;\n\n/**\n * Returns a {@link BlockFolder} definition. `path` is computed eagerly from the\n * `parent` chain (display names joined with `/`). Throws only on `/` in the\n * name (a programming error — it would corrupt path semantics).\n *\n * @example\n * const layout = defineFolder({ name: 'Layout' });\n * const heros = defineFolder({ name: 'Heros', parent: layout });\n * // heros.path === 'Layout/Heros'\n */\nexport function defineFolder<\n const TName extends string,\n const TParent extends BlockFolder | undefined = undefined,\n>(input: { name: TName; parent?: TParent }): DefinedFolder<TName, TParent>;\nexport function defineFolder(input: { name: string; parent?: BlockFolder }): BlockFolder {\n if (input.name.trim() === '') {\n throw new Error(`defineFolder: folder name must not be empty`);\n }\n if (input.name.includes('/')) {\n throw new Error(`defineFolder: folder name \"${input.name}\" must not contain \"/\"`);\n }\n const path = input.parent ? `${input.parent.path}/${input.name}` : input.name;\n return input.parent\n ? { name: input.name, parent: input.parent, path }\n : { name: input.name, path };\n}\n"],"mappings":";;AAmCA,SAAgB,aAAa,OAA4D;AACvF,KAAI,MAAM,KAAK,MAAM,KAAK,GACxB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,KAAI,MAAM,KAAK,SAAS,IAAI,CAC1B,OAAM,IAAI,MAAM,8BAA8B,MAAM,KAAK,wBAAwB;CAEnF,MAAM,OAAO,MAAM,SAAS,GAAG,MAAM,OAAO,KAAK,GAAG,MAAM,SAAS,MAAM;AACzE,QAAO,MAAM,SACT;EAAE,MAAM,MAAM;EAAM,QAAQ,MAAM;EAAQ;EAAM,GAChD;EAAE,MAAM,MAAM;EAAM;EAAM"}
@@ -0,0 +1,39 @@
1
+ import { Prettify } from "../utils/prettify.cjs";
2
+
3
+ //#region src/helpers/define-folder.d.ts
4
+ /**
5
+ * A block folder (Storyblok component group) content-shape definition. Identity
6
+ * is the resolved name path — no UUID; the CLI matches/creates groups by path
7
+ * at push time.
8
+ */
9
+ interface BlockFolder {
10
+ name: string;
11
+ parent?: BlockFolder;
12
+ path: string;
13
+ }
14
+ type FolderPath<TName extends string, TParent extends BlockFolder | undefined> = TParent extends {
15
+ path: infer P extends string;
16
+ } ? `${P}/${TName}` : TName;
17
+ type DefinedFolder<TName extends string, TParent extends BlockFolder | undefined> = Prettify<{
18
+ name: TName;
19
+ path: FolderPath<TName, TParent>;
20
+ } & (TParent extends BlockFolder ? {
21
+ parent: TParent;
22
+ } : unknown)>;
23
+ /**
24
+ * Returns a {@link BlockFolder} definition. `path` is computed eagerly from the
25
+ * `parent` chain (display names joined with `/`). Throws only on `/` in the
26
+ * name (a programming error — it would corrupt path semantics).
27
+ *
28
+ * @example
29
+ * const layout = defineFolder({ name: 'Layout' });
30
+ * const heros = defineFolder({ name: 'Heros', parent: layout });
31
+ * // heros.path === 'Layout/Heros'
32
+ */
33
+ declare function defineFolder<const TName extends string, const TParent extends BlockFolder | undefined = undefined>(input: {
34
+ name: TName;
35
+ parent?: TParent;
36
+ }): DefinedFolder<TName, TParent>;
37
+ //#endregion
38
+ export { BlockFolder, defineFolder };
39
+ //# sourceMappingURL=define-folder.d.cts.map
@@ -0,0 +1,39 @@
1
+ import { Prettify } from "../utils/prettify.mjs";
2
+
3
+ //#region src/helpers/define-folder.d.ts
4
+ /**
5
+ * A block folder (Storyblok component group) content-shape definition. Identity
6
+ * is the resolved name path — no UUID; the CLI matches/creates groups by path
7
+ * at push time.
8
+ */
9
+ interface BlockFolder {
10
+ name: string;
11
+ parent?: BlockFolder;
12
+ path: string;
13
+ }
14
+ type FolderPath<TName extends string, TParent extends BlockFolder | undefined> = TParent extends {
15
+ path: infer P extends string;
16
+ } ? `${P}/${TName}` : TName;
17
+ type DefinedFolder<TName extends string, TParent extends BlockFolder | undefined> = Prettify<{
18
+ name: TName;
19
+ path: FolderPath<TName, TParent>;
20
+ } & (TParent extends BlockFolder ? {
21
+ parent: TParent;
22
+ } : unknown)>;
23
+ /**
24
+ * Returns a {@link BlockFolder} definition. `path` is computed eagerly from the
25
+ * `parent` chain (display names joined with `/`). Throws only on `/` in the
26
+ * name (a programming error — it would corrupt path semantics).
27
+ *
28
+ * @example
29
+ * const layout = defineFolder({ name: 'Layout' });
30
+ * const heros = defineFolder({ name: 'Heros', parent: layout });
31
+ * // heros.path === 'Layout/Heros'
32
+ */
33
+ declare function defineFolder<const TName extends string, const TParent extends BlockFolder | undefined = undefined>(input: {
34
+ name: TName;
35
+ parent?: TParent;
36
+ }): DefinedFolder<TName, TParent>;
37
+ //#endregion
38
+ export { BlockFolder, defineFolder };
39
+ //# sourceMappingURL=define-folder.d.mts.map
@@ -0,0 +1,18 @@
1
+ //#region src/helpers/define-folder.ts
2
+ function defineFolder(input) {
3
+ if (input.name.trim() === "") throw new Error(`defineFolder: folder name must not be empty`);
4
+ if (input.name.includes("/")) throw new Error(`defineFolder: folder name "${input.name}" must not contain "/"`);
5
+ const path = input.parent ? `${input.parent.path}/${input.name}` : input.name;
6
+ return input.parent ? {
7
+ name: input.name,
8
+ parent: input.parent,
9
+ path
10
+ } : {
11
+ name: input.name,
12
+ path
13
+ };
14
+ }
15
+
16
+ //#endregion
17
+ export { defineFolder };
18
+ //# sourceMappingURL=define-folder.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-folder.mjs","names":[],"sources":["../../src/helpers/define-folder.ts"],"sourcesContent":["import type { Prettify } from '../utils/prettify';\n\n/**\n * A block folder (Storyblok component group) content-shape definition. Identity\n * is the resolved name path — no UUID; the CLI matches/creates groups by path\n * at push time.\n */\nexport interface BlockFolder {\n name: string;\n parent?: BlockFolder;\n path: string;\n}\n\ntype FolderPath<TName extends string, TParent extends BlockFolder | undefined>\n = TParent extends { path: infer P extends string } ? `${P}/${TName}` : TName;\n\ntype DefinedFolder<TName extends string, TParent extends BlockFolder | undefined> = Prettify<\n { name: TName; path: FolderPath<TName, TParent> }\n & (TParent extends BlockFolder ? { parent: TParent } : unknown)\n>;\n\n/**\n * Returns a {@link BlockFolder} definition. `path` is computed eagerly from the\n * `parent` chain (display names joined with `/`). Throws only on `/` in the\n * name (a programming error — it would corrupt path semantics).\n *\n * @example\n * const layout = defineFolder({ name: 'Layout' });\n * const heros = defineFolder({ name: 'Heros', parent: layout });\n * // heros.path === 'Layout/Heros'\n */\nexport function defineFolder<\n const TName extends string,\n const TParent extends BlockFolder | undefined = undefined,\n>(input: { name: TName; parent?: TParent }): DefinedFolder<TName, TParent>;\nexport function defineFolder(input: { name: string; parent?: BlockFolder }): BlockFolder {\n if (input.name.trim() === '') {\n throw new Error(`defineFolder: folder name must not be empty`);\n }\n if (input.name.includes('/')) {\n throw new Error(`defineFolder: folder name \"${input.name}\" must not contain \"/\"`);\n }\n const path = input.parent ? `${input.parent.path}/${input.name}` : input.name;\n return input.parent\n ? { name: input.name, parent: input.parent, path }\n : { name: input.name, path };\n}\n"],"mappings":";AAmCA,SAAgB,aAAa,OAA4D;AACvF,KAAI,MAAM,KAAK,MAAM,KAAK,GACxB,OAAM,IAAI,MAAM,8CAA8C;AAEhE,KAAI,MAAM,KAAK,SAAS,IAAI,CAC1B,OAAM,IAAI,MAAM,8BAA8B,MAAM,KAAK,wBAAwB;CAEnF,MAAM,OAAO,MAAM,SAAS,GAAG,MAAM,OAAO,KAAK,GAAG,MAAM,SAAS,MAAM;AACzE,QAAO,MAAM,SACT;EAAE,MAAM,MAAM;EAAM,QAAQ,MAAM;EAAQ;EAAM,GAChD;EAAE,MAAM,MAAM;EAAM;EAAM"}
@@ -0,0 +1,23 @@
1
+
2
+ //#region src/helpers/define-schema.ts
3
+ /**
4
+ * Typed identity wrapper for a schema object, consistent with the other
5
+ * `define*` helpers. Preserves the exact literal shape via `const` inference so
6
+ * {@link Schema} can derive block, datasource, and field-plugin types from
7
+ * `typeof schema`. Does not validate or transform.
8
+ *
9
+ * @example
10
+ * export const schema = defineSchema({
11
+ * blocks: { pageBlock, heroBlock },
12
+ * folders: { layout },
13
+ * datasources: { colorsDatasource },
14
+ * fieldPlugins: { colorPicker },
15
+ * });
16
+ */
17
+ function defineSchema(schema) {
18
+ return schema;
19
+ }
20
+
21
+ //#endregion
22
+ exports.defineSchema = defineSchema;
23
+ //# sourceMappingURL=define-schema.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-schema.cjs","names":[],"sources":["../../src/helpers/define-schema.ts"],"sourcesContent":["import type { Block } from './define-block';\nimport type { Datasource } from './define-datasource';\nimport type { BlockFolder } from './define-folder';\nimport type { FieldPlugin } from './define-field-plugin';\n\n/** Minimal shape accepted by {@link defineSchema}; extra keys pass through unchanged. */\nexport interface SchemaConfig {\n blocks: Record<string, Block>;\n folders?: Record<string, BlockFolder>;\n datasources?: Record<string, Datasource>;\n fieldPlugins?: Record<string, FieldPlugin>;\n}\n\n/**\n * Typed identity wrapper for a schema object, consistent with the other\n * `define*` helpers. Preserves the exact literal shape via `const` inference so\n * {@link Schema} can derive block, datasource, and field-plugin types from\n * `typeof schema`. Does not validate or transform.\n *\n * @example\n * export const schema = defineSchema({\n * blocks: { pageBlock, heroBlock },\n * folders: { layout },\n * datasources: { colorsDatasource },\n * fieldPlugins: { colorPicker },\n * });\n */\nexport function defineSchema<const T extends SchemaConfig>(schema: T): T {\n return schema;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA2BA,SAAgB,aAA2C,QAAc;AACvE,QAAO"}
@@ -0,0 +1,31 @@
1
+ import { Block } from "../generated/types/block.cjs";
2
+ import { BlockFolder } from "./define-folder.cjs";
3
+ import { Datasource } from "./define-datasource.cjs";
4
+ import { FieldPlugin } from "./define-field-plugin.cjs";
5
+
6
+ //#region src/helpers/define-schema.d.ts
7
+ /** Minimal shape accepted by {@link defineSchema}; extra keys pass through unchanged. */
8
+ interface SchemaConfig {
9
+ blocks: Record<string, Block>;
10
+ folders?: Record<string, BlockFolder>;
11
+ datasources?: Record<string, Datasource>;
12
+ fieldPlugins?: Record<string, FieldPlugin>;
13
+ }
14
+ /**
15
+ * Typed identity wrapper for a schema object, consistent with the other
16
+ * `define*` helpers. Preserves the exact literal shape via `const` inference so
17
+ * {@link Schema} can derive block, datasource, and field-plugin types from
18
+ * `typeof schema`. Does not validate or transform.
19
+ *
20
+ * @example
21
+ * export const schema = defineSchema({
22
+ * blocks: { pageBlock, heroBlock },
23
+ * folders: { layout },
24
+ * datasources: { colorsDatasource },
25
+ * fieldPlugins: { colorPicker },
26
+ * });
27
+ */
28
+ declare function defineSchema<const T extends SchemaConfig>(schema: T): T;
29
+ //#endregion
30
+ export { SchemaConfig, defineSchema };
31
+ //# sourceMappingURL=define-schema.d.cts.map
@@ -0,0 +1,31 @@
1
+ import { Block } from "../generated/types/block.mjs";
2
+ import { BlockFolder } from "./define-folder.mjs";
3
+ import { Datasource } from "./define-datasource.mjs";
4
+ import { FieldPlugin } from "./define-field-plugin.mjs";
5
+
6
+ //#region src/helpers/define-schema.d.ts
7
+ /** Minimal shape accepted by {@link defineSchema}; extra keys pass through unchanged. */
8
+ interface SchemaConfig {
9
+ blocks: Record<string, Block>;
10
+ folders?: Record<string, BlockFolder>;
11
+ datasources?: Record<string, Datasource>;
12
+ fieldPlugins?: Record<string, FieldPlugin>;
13
+ }
14
+ /**
15
+ * Typed identity wrapper for a schema object, consistent with the other
16
+ * `define*` helpers. Preserves the exact literal shape via `const` inference so
17
+ * {@link Schema} can derive block, datasource, and field-plugin types from
18
+ * `typeof schema`. Does not validate or transform.
19
+ *
20
+ * @example
21
+ * export const schema = defineSchema({
22
+ * blocks: { pageBlock, heroBlock },
23
+ * folders: { layout },
24
+ * datasources: { colorsDatasource },
25
+ * fieldPlugins: { colorPicker },
26
+ * });
27
+ */
28
+ declare function defineSchema<const T extends SchemaConfig>(schema: T): T;
29
+ //#endregion
30
+ export { SchemaConfig, defineSchema };
31
+ //# sourceMappingURL=define-schema.d.mts.map
@@ -0,0 +1,22 @@
1
+ //#region src/helpers/define-schema.ts
2
+ /**
3
+ * Typed identity wrapper for a schema object, consistent with the other
4
+ * `define*` helpers. Preserves the exact literal shape via `const` inference so
5
+ * {@link Schema} can derive block, datasource, and field-plugin types from
6
+ * `typeof schema`. Does not validate or transform.
7
+ *
8
+ * @example
9
+ * export const schema = defineSchema({
10
+ * blocks: { pageBlock, heroBlock },
11
+ * folders: { layout },
12
+ * datasources: { colorsDatasource },
13
+ * fieldPlugins: { colorPicker },
14
+ * });
15
+ */
16
+ function defineSchema(schema) {
17
+ return schema;
18
+ }
19
+
20
+ //#endregion
21
+ export { defineSchema };
22
+ //# sourceMappingURL=define-schema.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-schema.mjs","names":[],"sources":["../../src/helpers/define-schema.ts"],"sourcesContent":["import type { Block } from './define-block';\nimport type { Datasource } from './define-datasource';\nimport type { BlockFolder } from './define-folder';\nimport type { FieldPlugin } from './define-field-plugin';\n\n/** Minimal shape accepted by {@link defineSchema}; extra keys pass through unchanged. */\nexport interface SchemaConfig {\n blocks: Record<string, Block>;\n folders?: Record<string, BlockFolder>;\n datasources?: Record<string, Datasource>;\n fieldPlugins?: Record<string, FieldPlugin>;\n}\n\n/**\n * Typed identity wrapper for a schema object, consistent with the other\n * `define*` helpers. Preserves the exact literal shape via `const` inference so\n * {@link Schema} can derive block, datasource, and field-plugin types from\n * `typeof schema`. Does not validate or transform.\n *\n * @example\n * export const schema = defineSchema({\n * blocks: { pageBlock, heroBlock },\n * folders: { layout },\n * datasources: { colorsDatasource },\n * fieldPlugins: { colorPicker },\n * });\n */\nexport function defineSchema<const T extends SchemaConfig>(schema: T): T {\n return schema;\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,SAAgB,aAA2C,QAAc;AACvE,QAAO"}