@vojtaholik/static-kit-core 2.0.0 → 2.1.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vojtaholik/static-kit-core",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -8,7 +8,9 @@
8
8
  "types": "./src/index.ts"
9
9
  }
10
10
  },
11
- "files": ["src"],
11
+ "files": [
12
+ "src"
13
+ ],
12
14
  "repository": {
13
15
  "type": "git",
14
16
  "url": "https://github.com/vojtaholik/module-kit",
package/src/config.ts CHANGED
@@ -16,8 +16,6 @@ export const configSchema = z.object({
16
16
  publicPath: z.string().default("/public"),
17
17
  /** Dev server port */
18
18
  devPort: z.number().default(3000),
19
- /** Path to cms-blocks file (relative to project root) */
20
- cmsBlocksFile: z.string().optional(),
21
19
  });
22
20
 
23
21
  export type StaticKitConfig = z.infer<typeof configSchema>;
@@ -5,14 +5,41 @@ import { blockRegistry, type RenderContext } from "./block-registry.ts";
5
5
  import type { SchemaAddress } from "./schema-address.ts";
6
6
 
7
7
  /**
8
- * Block instance in a page config
8
+ * Augmentable block props map users register their block types via:
9
+ *
10
+ * declare module "@vojtaholik/static-kit-core" {
11
+ * interface BlockPropsMap {
12
+ * hero: HeroProps;
13
+ * // ...
14
+ * }
15
+ * }
9
16
  */
10
- export interface BlockInstance {
17
+ export interface BlockPropsMap {}
18
+
19
+ type TypedBlockInstance = {
20
+ [K in keyof BlockPropsMap & string]: {
21
+ id: string;
22
+ type: K;
23
+ props: BlockPropsMap[K];
24
+ layout?: Partial<LayoutProps>;
25
+ };
26
+ }[keyof BlockPropsMap & string];
27
+
28
+ type UntypedBlockInstance = {
11
29
  id: string;
12
30
  type: string;
13
31
  props: Record<string, unknown>;
14
32
  layout?: Partial<LayoutProps>;
15
- }
33
+ };
34
+
35
+ /**
36
+ * Block instance in a page config.
37
+ * When BlockPropsMap is augmented, known types get typed props + autocomplete.
38
+ * Arbitrary types still accepted via the fallback.
39
+ */
40
+ export type BlockInstance = keyof BlockPropsMap extends never
41
+ ? UntypedBlockInstance
42
+ : TypedBlockInstance | (UntypedBlockInstance & { type: string & {} });
16
43
 
17
44
  /**
18
45
  * Region config - blocks in a named region
package/src/index.ts CHANGED
@@ -22,19 +22,6 @@ export {
22
22
  type SchemaAddress,
23
23
  } from "./schema-address.ts";
24
24
 
25
- // CMS schema utilities
26
- export {
27
- cmsFieldTypeEnum,
28
- cmsFieldSchema,
29
- cmsBlockSchemaMapSchema,
30
- createSchemaFromCmsFields,
31
- createSchemaFromCmsBlocks,
32
- type CmsFieldType,
33
- type CmsField,
34
- type CmsBlockSchema,
35
- type CmsBlockSchemaMap,
36
- } from "./schema.ts";
37
-
38
25
  // Block registry
39
26
  export {
40
27
  defineBlock,
@@ -52,6 +39,7 @@ export {
52
39
  export {
53
40
  renderPage,
54
41
  renderBlock,
42
+ type BlockPropsMap,
55
43
  type BlockInstance,
56
44
  type RegionConfig,
57
45
  type PageConfig,
package/src/schema.ts DELETED
@@ -1,164 +0,0 @@
1
- import { z } from "zod/v4";
2
-
3
- // CMS field type definitions
4
- export const cmsFieldTypeEnum = z.enum([
5
- "text",
6
- "richText",
7
- "image",
8
- "link",
9
- "boolean",
10
- "select",
11
- "number",
12
- "array",
13
- "object",
14
- ]);
15
-
16
- // Single CMS field definition
17
- export const cmsFieldSchema: z.ZodType<CmsField> = z.lazy(() =>
18
- z.object({
19
- type: cmsFieldTypeEnum,
20
- label: z.string(),
21
- required: z.boolean().optional(),
22
- defaultValue: z.unknown().optional(),
23
- // For select fields
24
- options: z.array(z.object({ label: z.string(), value: z.string() })).optional(),
25
- // For array fields
26
- itemSchema: cmsFieldSchema.optional(),
27
- // For object fields
28
- fields: z.record(z.string(), cmsFieldSchema).optional(),
29
- })
30
- );
31
-
32
- export type CmsFieldType = z.infer<typeof cmsFieldTypeEnum>;
33
-
34
- export interface CmsField {
35
- type: CmsFieldType;
36
- label: string;
37
- required?: boolean;
38
- defaultValue?: unknown;
39
- options?: { label: string; value: string }[];
40
- itemSchema?: CmsField;
41
- fields?: Record<string, CmsField>;
42
- }
43
-
44
- // Block schema definition for CMS
45
- export interface CmsBlockSchema {
46
- type: string;
47
- label: string;
48
- fields: Record<string, CmsField>;
49
- }
50
-
51
- // Map of block type -> CMS schema
52
- export const cmsBlockSchemaMapSchema = z.record(
53
- z.string(),
54
- z.object({
55
- type: z.string(),
56
- label: z.string(),
57
- fields: z.record(z.string(), cmsFieldSchema),
58
- })
59
- );
60
-
61
- export type CmsBlockSchemaMap = z.infer<typeof cmsBlockSchemaMapSchema>;
62
-
63
- /**
64
- * Transform a CMS field definition into a runtime Zod schema
65
- */
66
- function fieldToZod(field: CmsField): z.ZodType {
67
- let schema: z.ZodType;
68
-
69
- switch (field.type) {
70
- case "text":
71
- schema = z.string();
72
- break;
73
- case "richText":
74
- schema = z.string(); // HTML string
75
- break;
76
- case "image":
77
- schema = z.object({
78
- src: z.string(),
79
- alt: z.string(),
80
- width: z.number().optional(),
81
- height: z.number().optional(),
82
- });
83
- break;
84
- case "link":
85
- schema = z.object({
86
- href: z.string(),
87
- label: z.string(),
88
- external: z.boolean().optional(),
89
- });
90
- break;
91
- case "boolean":
92
- schema = z.boolean();
93
- break;
94
- case "select":
95
- if (field.options && field.options.length > 0) {
96
- const values = field.options.map((o) => o.value) as [string, ...string[]];
97
- schema = z.enum(values);
98
- } else {
99
- schema = z.string();
100
- }
101
- break;
102
- case "number":
103
- schema = z.number();
104
- break;
105
- case "array":
106
- if (field.itemSchema) {
107
- schema = z.array(fieldToZod(field.itemSchema));
108
- } else {
109
- schema = z.array(z.unknown());
110
- }
111
- break;
112
- case "object":
113
- if (field.fields) {
114
- const shape: Record<string, z.ZodType> = {};
115
- for (const [key, subField] of Object.entries(field.fields)) {
116
- shape[key] = fieldToZod(subField);
117
- }
118
- schema = z.object(shape);
119
- } else {
120
- schema = z.record(z.string(), z.unknown());
121
- }
122
- break;
123
- default:
124
- schema = z.unknown();
125
- }
126
-
127
- // Apply required/optional
128
- if (!field.required) {
129
- schema = schema.optional();
130
- }
131
-
132
- // Apply default if present
133
- if (field.defaultValue !== undefined) {
134
- schema = (schema as z.ZodOptional<z.ZodType>).default(field.defaultValue as never);
135
- }
136
-
137
- return schema;
138
- }
139
-
140
- /**
141
- * Transform CMS block fields into a Zod object schema
142
- */
143
- export function createSchemaFromCmsFields(
144
- fields: Record<string, CmsField>
145
- ): z.ZodObject<Record<string, z.ZodType>> {
146
- const shape: Record<string, z.ZodType> = {};
147
- for (const [key, field] of Object.entries(fields)) {
148
- shape[key] = fieldToZod(field);
149
- }
150
- return z.object(shape);
151
- }
152
-
153
- /**
154
- * Create runtime Zod schemas for all blocks from CMS definitions
155
- */
156
- export function createSchemaFromCmsBlocks(
157
- blockSchemas: CmsBlockSchemaMap
158
- ): Record<string, z.ZodObject<Record<string, z.ZodType>>> {
159
- const result: Record<string, z.ZodObject<Record<string, z.ZodType>>> = {};
160
- for (const [blockType, blockSchema] of Object.entries(blockSchemas)) {
161
- result[blockType] = createSchemaFromCmsFields(blockSchema.fields);
162
- }
163
- return result;
164
- }