@storyblok/schema 0.1.0-alpha.2 → 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.
- package/dist/generated/types/block.d.cts +6 -0
- package/dist/generated/types/block.d.mts +6 -0
- package/dist/generated/types/field.d.cts +22 -3
- package/dist/generated/types/field.d.mts +22 -3
- package/dist/helpers/define-block.cjs +6 -1
- package/dist/helpers/define-block.cjs.map +1 -1
- package/dist/helpers/define-block.d.cts +14 -4
- package/dist/helpers/define-block.d.mts +14 -4
- package/dist/helpers/define-block.mjs +6 -1
- package/dist/helpers/define-block.mjs.map +1 -1
- package/dist/helpers/define-field.cjs +8 -1
- package/dist/helpers/define-field.cjs.map +1 -1
- package/dist/helpers/define-field.d.cts +12 -3
- package/dist/helpers/define-field.d.mts +12 -3
- package/dist/helpers/define-field.mjs +8 -1
- package/dist/helpers/define-field.mjs.map +1 -1
- package/dist/helpers/define-folder.cjs +19 -0
- package/dist/helpers/define-folder.cjs.map +1 -0
- package/dist/helpers/define-folder.d.cts +39 -0
- package/dist/helpers/define-folder.d.mts +39 -0
- package/dist/helpers/define-folder.mjs +18 -0
- package/dist/helpers/define-folder.mjs.map +1 -0
- package/dist/helpers/define-schema.cjs +1 -0
- package/dist/helpers/define-schema.cjs.map +1 -1
- package/dist/helpers/define-schema.d.cts +3 -0
- package/dist/helpers/define-schema.d.mts +3 -0
- package/dist/helpers/define-schema.mjs +1 -0
- package/dist/helpers/define-schema.mjs.map +1 -1
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +2 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/utils/slugify-folder-path.cjs +21 -0
- package/dist/utils/slugify-folder-path.cjs.map +1 -0
- package/dist/utils/slugify-folder-path.mjs +20 -0
- package/dist/utils/slugify-folder-path.mjs.map +1 -0
- package/dist/validators/shapes.cjs.map +1 -1
- package/dist/validators/shapes.d.cts +6 -2
- package/dist/validators/shapes.d.mts +6 -2
- package/dist/validators/shapes.mjs.map +1 -1
- package/dist/validators/validate-story.cjs +60 -24
- package/dist/validators/validate-story.cjs.map +1 -1
- package/dist/validators/validate-story.mjs +60 -24
- package/dist/validators/validate-story.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -34,6 +34,12 @@ type Block<TName extends string = string, TFields extends BlockFields = BlockFie
|
|
|
34
34
|
* directory layout.
|
|
35
35
|
*/
|
|
36
36
|
component_group_uuid?: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Folder membership as a display name path (e.g. `'Layout/Heros'`); `null` =
|
|
39
|
+
* explicitly ungrouped (push clears the group). Absent = unmanaged (push
|
|
40
|
+
* leaves the remote group untouched).
|
|
41
|
+
*/
|
|
42
|
+
folder?: string | null;
|
|
37
43
|
}>;
|
|
38
44
|
/**
|
|
39
45
|
* A root {@link Block} (`is_root: true`). Given a union of blocks, narrows to
|
|
@@ -34,6 +34,12 @@ type Block<TName extends string = string, TFields extends BlockFields = BlockFie
|
|
|
34
34
|
* directory layout.
|
|
35
35
|
*/
|
|
36
36
|
component_group_uuid?: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Folder membership as a display name path (e.g. `'Layout/Heros'`); `null` =
|
|
39
|
+
* explicitly ungrouped (push clears the group). Absent = unmanaged (push
|
|
40
|
+
* leaves the remote group untouched).
|
|
41
|
+
*/
|
|
42
|
+
folder?: string | null;
|
|
37
43
|
}>;
|
|
38
44
|
/**
|
|
39
45
|
* A root {@link Block} (`is_root: true`). Given a union of blocks, narrows to
|
|
@@ -71,11 +71,30 @@ type IsNestable<T> = T extends {
|
|
|
71
71
|
} ? false : T extends {
|
|
72
72
|
is_nestable: true;
|
|
73
73
|
} ? true : true;
|
|
74
|
+
type AllowEntry = string | {
|
|
75
|
+
folder: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Keeps `TBlock` when its `folder` is `TFolder` or any nested subfolder (mirrors
|
|
79
|
+
* the editor's `isAnywhereInFolder`). This is a best-effort compile-time check,
|
|
80
|
+
* compared case-insensitively via `Lowercase` so `folder: 'blog'` and a `Blog`
|
|
81
|
+
* folder ref narrow the same. TypeScript cannot replicate the CLI's full slug at
|
|
82
|
+
* the type level, so separator/symbol drift (`'My Layout'` vs `'my-layout'`) is
|
|
83
|
+
* only reconciled at push/validate time — full folder identity is enforced
|
|
84
|
+
* there, not here. To rely on narrowing, prefer a `defineFolder` ref over a
|
|
85
|
+
* string path on both the block's `folder` and the field's `allow`: a shared ref
|
|
86
|
+
* carries the exact path on both sides, so no drift is possible.
|
|
87
|
+
*/
|
|
88
|
+
type MatchesFolder<TBlock, TFolder extends string> = TBlock extends {
|
|
89
|
+
folder: infer BF extends string;
|
|
90
|
+
} ? Lowercase<BF> extends Lowercase<TFolder> | `${Lowercase<TFolder>}/${string}` ? TBlock : never : never;
|
|
74
91
|
type ApplyAllow<TField, TBlocks> = TField extends {
|
|
75
|
-
allow: ReadonlyArray<infer TAllowed extends
|
|
76
|
-
} ? Extract<TBlocks, {
|
|
92
|
+
allow: ReadonlyArray<infer TAllowed extends AllowEntry>;
|
|
93
|
+
} ? TAllowed extends string ? Extract<TBlocks, {
|
|
77
94
|
name: TAllowed;
|
|
78
|
-
}> :
|
|
95
|
+
}> : TAllowed extends {
|
|
96
|
+
folder: infer F extends string;
|
|
97
|
+
} ? TBlocks extends any ? MatchesFolder<TBlocks, F> : never : never : TBlocks extends any ? IsNestable<TBlocks> extends true ? TBlocks : never : never;
|
|
79
98
|
/**
|
|
80
99
|
* Resolves a `custom` field to its registered plugin value. When the field's
|
|
81
100
|
* `field_type` is a key of `TFieldPlugins`, the validator output is merged with
|
|
@@ -71,11 +71,30 @@ type IsNestable<T> = T extends {
|
|
|
71
71
|
} ? false : T extends {
|
|
72
72
|
is_nestable: true;
|
|
73
73
|
} ? true : true;
|
|
74
|
+
type AllowEntry = string | {
|
|
75
|
+
folder: string;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Keeps `TBlock` when its `folder` is `TFolder` or any nested subfolder (mirrors
|
|
79
|
+
* the editor's `isAnywhereInFolder`). This is a best-effort compile-time check,
|
|
80
|
+
* compared case-insensitively via `Lowercase` so `folder: 'blog'` and a `Blog`
|
|
81
|
+
* folder ref narrow the same. TypeScript cannot replicate the CLI's full slug at
|
|
82
|
+
* the type level, so separator/symbol drift (`'My Layout'` vs `'my-layout'`) is
|
|
83
|
+
* only reconciled at push/validate time — full folder identity is enforced
|
|
84
|
+
* there, not here. To rely on narrowing, prefer a `defineFolder` ref over a
|
|
85
|
+
* string path on both the block's `folder` and the field's `allow`: a shared ref
|
|
86
|
+
* carries the exact path on both sides, so no drift is possible.
|
|
87
|
+
*/
|
|
88
|
+
type MatchesFolder<TBlock, TFolder extends string> = TBlock extends {
|
|
89
|
+
folder: infer BF extends string;
|
|
90
|
+
} ? Lowercase<BF> extends Lowercase<TFolder> | `${Lowercase<TFolder>}/${string}` ? TBlock : never : never;
|
|
74
91
|
type ApplyAllow<TField, TBlocks> = TField extends {
|
|
75
|
-
allow: ReadonlyArray<infer TAllowed extends
|
|
76
|
-
} ? Extract<TBlocks, {
|
|
92
|
+
allow: ReadonlyArray<infer TAllowed extends AllowEntry>;
|
|
93
|
+
} ? TAllowed extends string ? Extract<TBlocks, {
|
|
77
94
|
name: TAllowed;
|
|
78
|
-
}> :
|
|
95
|
+
}> : TAllowed extends {
|
|
96
|
+
folder: infer F extends string;
|
|
97
|
+
} ? TBlocks extends any ? MatchesFolder<TBlocks, F> : never : never : TBlocks extends any ? IsNestable<TBlocks> extends true ? TBlocks : never : never;
|
|
79
98
|
/**
|
|
80
99
|
* Resolves a `custom` field to its registered plugin value. When the field's
|
|
81
100
|
* `field_type` is a key of `TFieldPlugins`, the validator output is merged with
|
|
@@ -21,9 +21,14 @@ function defineBlock(block) {
|
|
|
21
21
|
pos: index
|
|
22
22
|
};
|
|
23
23
|
});
|
|
24
|
+
const { folder, ...restBlock } = block ?? {};
|
|
25
|
+
if (folder !== void 0 && typeof restBlock.component_group_uuid === "string") throw new Error(`defineBlock: block "${block?.name ?? ""}" sets both "folder" and "component_group_uuid"; use one`);
|
|
26
|
+
if (typeof folder === "string" && !folder.split("/").some((segment) => segment.trim() !== "")) throw new Error(`defineBlock: block "${block?.name ?? ""}" has an empty "folder" path`);
|
|
27
|
+
const normalizedFolder = folder === void 0 ? void 0 : folder !== null && typeof folder === "object" ? folder.path : folder;
|
|
24
28
|
return {
|
|
25
29
|
...BLOCK_DEFAULTS,
|
|
26
|
-
...
|
|
30
|
+
...restBlock,
|
|
31
|
+
...folder !== void 0 && { folder: normalizedFolder },
|
|
27
32
|
fields
|
|
28
33
|
};
|
|
29
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-block.cjs","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
|
|
1
|
+
{"version":3,"file":"define-block.cjs","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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Block, BlockFields, NestableBlock, RootBlock } from "../generated/types/block.cjs";
|
|
2
2
|
import { Prettify } from "../utils/prettify.cjs";
|
|
3
|
+
import { BlockFolder } from "./define-folder.cjs";
|
|
3
4
|
|
|
4
5
|
//#region src/helpers/define-block.d.ts
|
|
5
6
|
declare const BLOCK_DEFAULTS: {
|
|
@@ -11,18 +12,27 @@ declare const BLOCK_DEFAULTS: {
|
|
|
11
12
|
};
|
|
12
13
|
/** Fields that have safe defaults and may be omitted from block input. */
|
|
13
14
|
type BlockOptional = keyof typeof BLOCK_DEFAULTS;
|
|
14
|
-
|
|
15
|
+
/** Accepted input for a block's `folder`: a folder ref, a raw path string, or `null` to explicitly ungroup. */
|
|
16
|
+
type FolderInput = BlockFolder | string | null;
|
|
17
|
+
/** Normalizes a folder input type to the display path literal it resolves to. */
|
|
18
|
+
type NormalizeFolder<T> = T extends {
|
|
19
|
+
path: infer P extends string;
|
|
20
|
+
} ? P : T;
|
|
21
|
+
type BlockInput<TName extends string = string, TFields extends BlockFields = BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true, TFolder extends FolderInput | undefined = undefined> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder' | BlockOptional> & {
|
|
15
22
|
name: TName;
|
|
16
23
|
fields: TFields;
|
|
17
24
|
is_root?: TIsRoot;
|
|
18
25
|
is_nestable?: TIsNestable;
|
|
26
|
+
folder?: TFolder;
|
|
19
27
|
} & Partial<Pick<Block, Exclude<BlockOptional, 'is_root' | 'is_nestable'>>>>;
|
|
20
|
-
type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot extends boolean, TIsNestable extends boolean> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable'> & {
|
|
28
|
+
type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot extends boolean, TIsNestable extends boolean, TFolder extends FolderInput | undefined = undefined> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder'> & {
|
|
21
29
|
name: TName;
|
|
22
30
|
fields: TFields;
|
|
23
31
|
is_root: TIsRoot;
|
|
24
32
|
is_nestable: TIsNestable;
|
|
25
|
-
}
|
|
33
|
+
} & (TFolder extends undefined ? unknown : {
|
|
34
|
+
folder: NormalizeFolder<TFolder>;
|
|
35
|
+
})>;
|
|
26
36
|
/**
|
|
27
37
|
* Returns a {@link Block} content-shape definition. The user-facing input is an
|
|
28
38
|
* ordered array of `defineField` calls under `fields`; the array index becomes
|
|
@@ -39,7 +49,7 @@ type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot ext
|
|
|
39
49
|
* ],
|
|
40
50
|
* });
|
|
41
51
|
*/
|
|
42
|
-
declare function defineBlock<TName extends string, const TFields extends BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true>(block: BlockInput<TName, TFields, TIsRoot, TIsNestable>): DefinedBlock<TName, TFields, TIsRoot, TIsNestable>;
|
|
52
|
+
declare function defineBlock<TName extends string, const TFields extends BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true, const TFolder extends FolderInput | undefined = undefined>(block: BlockInput<TName, TFields, TIsRoot, TIsNestable, TFolder>): DefinedBlock<TName, TFields, TIsRoot, TIsNestable, TFolder>;
|
|
43
53
|
//#endregion
|
|
44
54
|
export { defineBlock };
|
|
45
55
|
//# sourceMappingURL=define-block.d.cts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Block, BlockFields, NestableBlock, RootBlock } from "../generated/types/block.mjs";
|
|
2
2
|
import { Prettify } from "../utils/prettify.mjs";
|
|
3
|
+
import { BlockFolder } from "./define-folder.mjs";
|
|
3
4
|
|
|
4
5
|
//#region src/helpers/define-block.d.ts
|
|
5
6
|
declare const BLOCK_DEFAULTS: {
|
|
@@ -11,18 +12,27 @@ declare const BLOCK_DEFAULTS: {
|
|
|
11
12
|
};
|
|
12
13
|
/** Fields that have safe defaults and may be omitted from block input. */
|
|
13
14
|
type BlockOptional = keyof typeof BLOCK_DEFAULTS;
|
|
14
|
-
|
|
15
|
+
/** Accepted input for a block's `folder`: a folder ref, a raw path string, or `null` to explicitly ungroup. */
|
|
16
|
+
type FolderInput = BlockFolder | string | null;
|
|
17
|
+
/** Normalizes a folder input type to the display path literal it resolves to. */
|
|
18
|
+
type NormalizeFolder<T> = T extends {
|
|
19
|
+
path: infer P extends string;
|
|
20
|
+
} ? P : T;
|
|
21
|
+
type BlockInput<TName extends string = string, TFields extends BlockFields = BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true, TFolder extends FolderInput | undefined = undefined> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder' | BlockOptional> & {
|
|
15
22
|
name: TName;
|
|
16
23
|
fields: TFields;
|
|
17
24
|
is_root?: TIsRoot;
|
|
18
25
|
is_nestable?: TIsNestable;
|
|
26
|
+
folder?: TFolder;
|
|
19
27
|
} & Partial<Pick<Block, Exclude<BlockOptional, 'is_root' | 'is_nestable'>>>>;
|
|
20
|
-
type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot extends boolean, TIsNestable extends boolean> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable'> & {
|
|
28
|
+
type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot extends boolean, TIsNestable extends boolean, TFolder extends FolderInput | undefined = undefined> = Prettify<Omit<Block, 'name' | 'fields' | 'is_root' | 'is_nestable' | 'folder'> & {
|
|
21
29
|
name: TName;
|
|
22
30
|
fields: TFields;
|
|
23
31
|
is_root: TIsRoot;
|
|
24
32
|
is_nestable: TIsNestable;
|
|
25
|
-
}
|
|
33
|
+
} & (TFolder extends undefined ? unknown : {
|
|
34
|
+
folder: NormalizeFolder<TFolder>;
|
|
35
|
+
})>;
|
|
26
36
|
/**
|
|
27
37
|
* Returns a {@link Block} content-shape definition. The user-facing input is an
|
|
28
38
|
* ordered array of `defineField` calls under `fields`; the array index becomes
|
|
@@ -39,7 +49,7 @@ type DefinedBlock<TName extends string, TFields extends BlockFields, TIsRoot ext
|
|
|
39
49
|
* ],
|
|
40
50
|
* });
|
|
41
51
|
*/
|
|
42
|
-
declare function defineBlock<TName extends string, const TFields extends BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true>(block: BlockInput<TName, TFields, TIsRoot, TIsNestable>): DefinedBlock<TName, TFields, TIsRoot, TIsNestable>;
|
|
52
|
+
declare function defineBlock<TName extends string, const TFields extends BlockFields, TIsRoot extends boolean = false, TIsNestable extends boolean = true, const TFolder extends FolderInput | undefined = undefined>(block: BlockInput<TName, TFields, TIsRoot, TIsNestable, TFolder>): DefinedBlock<TName, TFields, TIsRoot, TIsNestable, TFolder>;
|
|
43
53
|
//#endregion
|
|
44
54
|
export { defineBlock };
|
|
45
55
|
//# sourceMappingURL=define-block.d.mts.map
|
|
@@ -20,9 +20,14 @@ function defineBlock(block) {
|
|
|
20
20
|
pos: index
|
|
21
21
|
};
|
|
22
22
|
});
|
|
23
|
+
const { folder, ...restBlock } = block ?? {};
|
|
24
|
+
if (folder !== void 0 && typeof restBlock.component_group_uuid === "string") throw new Error(`defineBlock: block "${block?.name ?? ""}" sets both "folder" and "component_group_uuid"; use one`);
|
|
25
|
+
if (typeof folder === "string" && !folder.split("/").some((segment) => segment.trim() !== "")) throw new Error(`defineBlock: block "${block?.name ?? ""}" has an empty "folder" path`);
|
|
26
|
+
const normalizedFolder = folder === void 0 ? void 0 : folder !== null && typeof folder === "object" ? folder.path : folder;
|
|
23
27
|
return {
|
|
24
28
|
...BLOCK_DEFAULTS,
|
|
25
|
-
...
|
|
29
|
+
...restBlock,
|
|
30
|
+
...folder !== void 0 && { folder: normalizedFolder },
|
|
26
31
|
fields
|
|
27
32
|
};
|
|
28
33
|
}
|
|
@@ -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
|
|
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"}
|
|
@@ -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)
|
|
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
|
|
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
|
|
21
|
-
type
|
|
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
|
|
21
|
-
type
|
|
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)
|
|
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
|
|
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"}
|
|
@@ -1 +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 { 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 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 * datasources: { colorsDatasource },\n * fieldPlugins: { colorPicker },\n * });\n */\nexport function defineSchema<const T extends SchemaConfig>(schema: T): T {\n return schema;\n}\n"],"mappings":"
|
|
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"}
|