@flexkit/schema 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Flexkit.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,35 @@
1
- # Temporary Holding Version
1
+ # @flexkit/schema
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Types and helpers for `flexkit.config.ts`:
4
+
5
+ - `defineConfig` and `defineEntity` to author a project configuration with full typing.
6
+ - The schema types (`Entity`, `Attribute`, `ProjectConfig`, …) and their deployed counterparts (`UiEntity`, `UiAttribute`, `UiProjectSchema`).
7
+ - `serializeConfig`, used by `flexkit deploy`, which turns the config into pure data: Zod `validation` functions are compiled to JSON Schema (draft-07), and anything that cannot be expressed that way fails with a `SchemaSerializationError` naming the entity and attribute.
8
+
9
+ ```ts
10
+ import { defineConfig, defineEntity } from '@flexkit/schema';
11
+
12
+ export default defineConfig({
13
+ projectId: 'abcdefghij',
14
+ title: 'Shop',
15
+ menuGroups: [{ name: 'catalog', title: 'Catalog' }],
16
+ scopes: [{ name: 'default', label: 'Default', isDefault: true }],
17
+ schema: [
18
+ defineEntity({
19
+ name: 'product',
20
+ plural: 'products',
21
+ menu: { group: 'catalog', icon: 'Tag' },
22
+ attributes: [
23
+ {
24
+ name: 'name',
25
+ label: 'Name',
26
+ dataType: 'string',
27
+ inputType: 'text',
28
+ scope: 'global',
29
+ validation: (z) => z.string().min(1),
30
+ },
31
+ ],
32
+ }),
33
+ ],
34
+ });
35
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SUPPORTED_VALIDATION_FORMATS: () => SUPPORTED_VALIDATION_FORMATS,
24
+ SUPPORTED_VALIDATION_KEYWORDS: () => SUPPORTED_VALIDATION_KEYWORDS,
25
+ SchemaSerializationError: () => SchemaSerializationError,
26
+ defineConfig: () => defineConfig,
27
+ defineEntity: () => defineEntity,
28
+ normalizeEntity: () => normalizeEntity,
29
+ serializeAttribute: () => serializeAttribute,
30
+ serializeConfig: () => serializeConfig,
31
+ serializeEntity: () => serializeEntity,
32
+ serializeProjectConfig: () => serializeProjectConfig,
33
+ serializeValidation: () => serializeValidation,
34
+ stripJsonSchemaMeta: () => stripJsonSchemaMeta
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+
38
+ // src/normalize-entity.ts
39
+ function assertAssetScope(attribute, entityName) {
40
+ if (attribute.dataType !== "asset") {
41
+ return;
42
+ }
43
+ if (attribute.scope !== void 0 && attribute.scope !== "global") {
44
+ throw new Error(
45
+ `Attribute "${attribute.name}" in entity "${entityName}" uses dataType "asset" but scope "${String(attribute.scope)}". Asset attributes must use scope "global" or omit scope.`
46
+ );
47
+ }
48
+ }
49
+ function normalizeAttributeScope(attribute) {
50
+ if (attribute.dataType !== "asset") {
51
+ return attribute;
52
+ }
53
+ return {
54
+ ...attribute,
55
+ scope: "global"
56
+ };
57
+ }
58
+ function assertDisplayAttribute(entity) {
59
+ const displayAttribute = entity.display ? entity.attributes.find((attribute) => attribute.name === entity.display) : entity.attributes[0];
60
+ if (entity.display && !displayAttribute) {
61
+ throw new Error(`Entity "${entity.name}" sets display to "${entity.display}", but no attribute has that name.`);
62
+ }
63
+ if (!displayAttribute || (displayAttribute.spaces ?? []).length === 0) {
64
+ return;
65
+ }
66
+ throw new Error(
67
+ `Entity "${entity.name}" cannot use space-bound attribute "${displayAttribute.name}" as its display field.`
68
+ );
69
+ }
70
+ function normalizeEntity(entity) {
71
+ entity.attributes.forEach((attribute) => {
72
+ assertAssetScope(attribute, entity.name);
73
+ });
74
+ assertDisplayAttribute(entity);
75
+ return {
76
+ ...entity,
77
+ attributes: entity.attributes.map(normalizeAttributeScope)
78
+ };
79
+ }
80
+
81
+ // src/define.ts
82
+ function defineConfig(config) {
83
+ return config;
84
+ }
85
+ function defineEntity(entity) {
86
+ return normalizeEntity(entity);
87
+ }
88
+
89
+ // src/json-schema.ts
90
+ function stripJsonSchemaMeta(value) {
91
+ if (Array.isArray(value)) {
92
+ return value.map((entry) => stripJsonSchemaMeta(entry));
93
+ }
94
+ if (!value || typeof value !== "object") {
95
+ return value;
96
+ }
97
+ const result = {};
98
+ for (const [key, nested] of Object.entries(value)) {
99
+ if (key === "$schema") {
100
+ continue;
101
+ }
102
+ result[key] = stripJsonSchemaMeta(nested);
103
+ }
104
+ return result;
105
+ }
106
+
107
+ // src/serialize.ts
108
+ var import_zod = require("zod");
109
+ var SUPPORTED_VALIDATION_KEYWORDS = /* @__PURE__ */ new Set([
110
+ "type",
111
+ "minLength",
112
+ "maxLength",
113
+ "pattern",
114
+ "format",
115
+ "minimum",
116
+ "maximum",
117
+ "exclusiveMinimum",
118
+ "exclusiveMaximum",
119
+ "multipleOf",
120
+ "enum",
121
+ "const",
122
+ "properties",
123
+ "required",
124
+ "additionalProperties",
125
+ "items",
126
+ "minItems",
127
+ "maxItems",
128
+ "anyOf",
129
+ "oneOf",
130
+ "not",
131
+ "description",
132
+ "title"
133
+ ]);
134
+ var SUPPORTED_VALIDATION_FORMATS = /* @__PURE__ */ new Set([
135
+ "email",
136
+ "uri",
137
+ "uuid",
138
+ "date-time",
139
+ "date",
140
+ "time"
141
+ ]);
142
+ var SchemaSerializationError = class extends Error {
143
+ entity;
144
+ attribute;
145
+ constructor(reason, location = {}, options) {
146
+ const where = location.entity ? location.attribute ? `Attribute "${location.attribute}" of entity "${location.entity}": ` : `Entity "${location.entity}": ` : "";
147
+ super(`${where}${reason}`, options);
148
+ this.name = "SchemaSerializationError";
149
+ this.entity = location.entity;
150
+ this.attribute = location.attribute;
151
+ }
152
+ };
153
+ function getDef(schema) {
154
+ return schema._zod.def;
155
+ }
156
+ function findCustomCheck(schema, seen = /* @__PURE__ */ new Set()) {
157
+ if (seen.has(schema)) {
158
+ return false;
159
+ }
160
+ seen.add(schema);
161
+ const def = getDef(schema);
162
+ if (def.checks?.some((check) => check._zod.def.check === "custom")) {
163
+ return true;
164
+ }
165
+ const children = [
166
+ def.innerType,
167
+ def.in,
168
+ def.out,
169
+ def.element,
170
+ def.left,
171
+ def.right,
172
+ def.keyType,
173
+ def.valueType,
174
+ ...def.options ?? [],
175
+ ...Object.values(def.shape ?? {}),
176
+ ...def.type === "lazy" && def.getter ? [def.getter()] : []
177
+ ];
178
+ return children.some((child) => child !== void 0 && findCustomCheck(child, seen));
179
+ }
180
+ function assertSupportedJsonSchema(fragment, location, path = []) {
181
+ if (Array.isArray(fragment)) {
182
+ fragment.forEach((entry, index) => {
183
+ assertSupportedJsonSchema(entry, location, [...path, String(index)]);
184
+ });
185
+ return;
186
+ }
187
+ if (!fragment || typeof fragment !== "object") {
188
+ return;
189
+ }
190
+ for (const [key, value] of Object.entries(fragment)) {
191
+ const insideMap = path.length > 0 && path[path.length - 1] === "properties";
192
+ if (!insideMap && !SUPPORTED_VALIDATION_KEYWORDS.has(key)) {
193
+ throw new SchemaSerializationError(
194
+ `validation uses the JSON Schema keyword "${[...path, key].join(".")}", which the Studio form cannot enforce. Supported keywords: ${[...SUPPORTED_VALIDATION_KEYWORDS].join(", ")}.`,
195
+ location
196
+ );
197
+ }
198
+ if (!insideMap && key === "format" && typeof value === "string" && !SUPPORTED_VALIDATION_FORMATS.has(value)) {
199
+ throw new SchemaSerializationError(
200
+ `validation uses the format "${value}", which the Studio form cannot enforce. Supported formats: ${[...SUPPORTED_VALIDATION_FORMATS].join(", ")}.`,
201
+ location
202
+ );
203
+ }
204
+ if (key === "properties" || key === "items" || key === "anyOf" || key === "oneOf" || key === "not") {
205
+ assertSupportedJsonSchema(value, location, [...path, key]);
206
+ } else if (insideMap) {
207
+ assertSupportedJsonSchema(value, location, [...path, key]);
208
+ }
209
+ }
210
+ }
211
+ function serializeValidation(validation, location = {}) {
212
+ let schema;
213
+ try {
214
+ schema = validation(import_zod.z);
215
+ } catch (error) {
216
+ throw new SchemaSerializationError("validation() threw while building the Zod schema.", location, { cause: error });
217
+ }
218
+ if (findCustomCheck(schema)) {
219
+ throw new SchemaSerializationError(
220
+ "validation uses refine(), superRefine() or check(), which cannot be expressed as JSON Schema. Use built-in Zod checks such as min(), max(), length(), regex() or email().",
221
+ location
222
+ );
223
+ }
224
+ let fragment;
225
+ try {
226
+ fragment = import_zod.z.toJSONSchema(schema, { target: "draft-07", unrepresentable: "throw" });
227
+ } catch (error) {
228
+ const reason = error instanceof Error ? error.message : String(error);
229
+ throw new SchemaSerializationError(`validation cannot be expressed as JSON Schema: ${reason}`, location, {
230
+ cause: error
231
+ });
232
+ }
233
+ const stripped = stripJsonSchemaMeta(fragment);
234
+ assertSupportedJsonSchema(stripped, location);
235
+ return stripped;
236
+ }
237
+ function assertBooleanFlag(value, name, location) {
238
+ if (value !== void 0 && typeof value !== "boolean") {
239
+ throw new SchemaSerializationError(
240
+ `"${name}" must be a boolean. Conditional callbacks are no longer supported; move the rule into the schema or drop it.`,
241
+ location
242
+ );
243
+ }
244
+ }
245
+ function assertIconName(value, where, location) {
246
+ if (value !== void 0 && typeof value !== "string") {
247
+ throw new SchemaSerializationError(
248
+ `${where} must be a lucide icon name such as 'Tag'. React elements are no longer supported in flexkit.config.ts.`,
249
+ location
250
+ );
251
+ }
252
+ }
253
+ function serializeAttribute(attribute, entityName) {
254
+ const location = { entity: entityName, attribute: attribute.name };
255
+ assertBooleanFlag(attribute.hidden, "hidden", location);
256
+ assertBooleanFlag(attribute.readOnly, "readOnly", location);
257
+ const { validation, ...rest } = attribute;
258
+ if (validation === void 0) {
259
+ return rest;
260
+ }
261
+ if (typeof validation !== "function") {
262
+ throw new SchemaSerializationError("validation must be a function receiving the Zod namespace.", location);
263
+ }
264
+ return { ...rest, validation: serializeValidation(validation, location) };
265
+ }
266
+ function serializeEntity(entity) {
267
+ const location = { entity: entity.name };
268
+ assertIconName(entity.menu?.icon, "menu.icon", location);
269
+ entity.groups?.forEach((group) => {
270
+ assertIconName(group.icon, `groups["${group.name}"].icon`, location);
271
+ assertBooleanFlag(group.hidden, `groups["${group.name}"].hidden`, location);
272
+ });
273
+ const serialized = {
274
+ name: entity.name,
275
+ plural: entity.plural,
276
+ attributes: entity.attributes.map((attribute) => serializeAttribute(attribute, entity.name))
277
+ };
278
+ if (entity.display !== void 0) serialized.display = entity.display;
279
+ if (entity.menu !== void 0) serialized.menu = entity.menu;
280
+ if (entity.spaces !== void 0) serialized.spaces = entity.spaces;
281
+ if (entity.groups !== void 0) serialized.groups = entity.groups;
282
+ return serialized;
283
+ }
284
+ function serializeProjectConfig(project) {
285
+ if (!project || typeof project !== "object" || typeof project.projectId !== "string") {
286
+ throw new SchemaSerializationError('each project needs a string "projectId".');
287
+ }
288
+ if (!Array.isArray(project.schema)) {
289
+ throw new SchemaSerializationError('"schema" must be an array of entities.', {});
290
+ }
291
+ const serialized = {
292
+ projectId: project.projectId,
293
+ schema: project.schema.map(serializeEntity)
294
+ };
295
+ if (project.title !== void 0) serialized.title = project.title;
296
+ if (project.menuGroups !== void 0) serialized.menuGroups = project.menuGroups;
297
+ if (project.scopes !== void 0) serialized.scopes = project.scopes;
298
+ if (project.spaces !== void 0) serialized.spaces = project.spaces;
299
+ return serialized;
300
+ }
301
+ function serializeConfig(config) {
302
+ return (Array.isArray(config) ? config : [config]).map(serializeProjectConfig);
303
+ }
304
+ // Annotate the CommonJS export names for ESM import in node:
305
+ 0 && (module.exports = {
306
+ SUPPORTED_VALIDATION_FORMATS,
307
+ SUPPORTED_VALIDATION_KEYWORDS,
308
+ SchemaSerializationError,
309
+ defineConfig,
310
+ defineEntity,
311
+ normalizeEntity,
312
+ serializeAttribute,
313
+ serializeConfig,
314
+ serializeEntity,
315
+ serializeProjectConfig,
316
+ serializeValidation,
317
+ stripJsonSchemaMeta
318
+ });
319
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/normalize-entity.ts","../src/define.ts","../src/json-schema.ts","../src/serialize.ts"],"sourcesContent":["export { defineConfig, defineEntity } from './define';\nexport { normalizeEntity } from './normalize-entity';\nexport { stripJsonSchemaMeta } from './json-schema';\nexport {\n SUPPORTED_VALIDATION_FORMATS,\n SUPPORTED_VALIDATION_KEYWORDS,\n SchemaSerializationError,\n serializeAttribute,\n serializeConfig,\n serializeEntity,\n serializeProjectConfig,\n serializeValidation,\n} from './serialize';\nexport type {\n AssetOptions,\n Attribute,\n AttributeOf,\n AttributeOptions,\n CommonOptions,\n Config,\n DataType,\n DateTimeOptions,\n DefaultValue,\n DefaultValueByDataType,\n Entity,\n EntityMenu,\n EntityOf,\n FieldGroup,\n GroupedSelectList,\n InputType,\n JsonSchemaObject,\n MenuGroup,\n NumberOptions,\n PreviewType,\n ProjectConfig,\n ProjectSchemaResponse,\n RelationshipOptions,\n Schema,\n Scope,\n ScopeType,\n Scopes,\n SelectList,\n SelectOptions,\n Space,\n Spaces,\n UiAttribute,\n UiEntity,\n UiProjectSchema,\n UiSchema,\n ZodValidation,\n} from './types';\n","import type { Attribute, Entity } from './types';\n\nfunction assertAssetScope(attribute: Attribute, entityName: string): void {\n if (attribute.dataType !== 'asset') {\n return;\n }\n\n if (attribute.scope !== undefined && attribute.scope !== 'global') {\n throw new Error(\n `Attribute \"${attribute.name}\" in entity \"${entityName}\" uses dataType \"asset\" but scope \"${String(attribute.scope)}\". Asset attributes must use scope \"global\" or omit scope.`\n );\n }\n}\n\nfunction normalizeAttributeScope(attribute: Attribute): Attribute {\n if (attribute.dataType !== 'asset') {\n return attribute;\n }\n\n return {\n ...attribute,\n scope: 'global',\n };\n}\n\nfunction assertDisplayAttribute(entity: Entity): void {\n const displayAttribute = entity.display\n ? entity.attributes.find((attribute) => attribute.name === entity.display)\n : entity.attributes[0];\n\n if (entity.display && !displayAttribute) {\n throw new Error(`Entity \"${entity.name}\" sets display to \"${entity.display}\", but no attribute has that name.`);\n }\n\n if (!displayAttribute || (displayAttribute.spaces ?? []).length === 0) {\n return;\n }\n\n throw new Error(\n `Entity \"${entity.name}\" cannot use space-bound attribute \"${displayAttribute.name}\" as its display field.`\n );\n}\n\nexport function normalizeEntity(entity: Entity): Entity {\n entity.attributes.forEach((attribute) => {\n assertAssetScope(attribute, entity.name);\n });\n\n assertDisplayAttribute(entity);\n\n return {\n ...entity,\n attributes: entity.attributes.map(normalizeAttributeScope),\n };\n}\n","import { normalizeEntity } from './normalize-entity';\nimport type { Config, Entity } from './types';\n\n/** Types and returns a `flexkit.config.ts` export unchanged. */\nexport function defineConfig<T extends Config>(config: T): T {\n return config;\n}\n\n/**\n * Types an entity and normalizes it: asset attributes get `scope: 'global'`,\n * and invalid asset scopes or display attributes throw at config load time.\n */\nexport function defineEntity<T extends Entity>(entity: T): Entity {\n return normalizeEntity(entity);\n}\n","/** Removes `$schema` at every level of a JSON Schema document. */\nexport function stripJsonSchemaMeta(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((entry) => stripJsonSchemaMeta(entry));\n }\n\n if (!value || typeof value !== 'object') {\n return value;\n }\n\n const result: { [key: string]: unknown } = {};\n\n for (const [key, nested] of Object.entries(value as { [key: string]: unknown })) {\n if (key === '$schema') {\n continue;\n }\n\n result[key] = stripJsonSchemaMeta(nested);\n }\n\n return result;\n}\n","import { z } from 'zod';\nimport { stripJsonSchemaMeta } from './json-schema';\nimport type {\n Attribute,\n Config,\n Entity,\n JsonSchemaObject,\n ProjectConfig,\n UiAttribute,\n UiEntity,\n UiProjectSchema,\n ZodValidation,\n} from './types';\n\n/**\n * JSON Schema keywords the Studio form knows how to validate. Anything else\n * fails the deploy so a rule never silently disappears between the config and\n * the form.\n */\nexport const SUPPORTED_VALIDATION_KEYWORDS: ReadonlySet<string> = new Set([\n 'type',\n 'minLength',\n 'maxLength',\n 'pattern',\n 'format',\n 'minimum',\n 'maximum',\n 'exclusiveMinimum',\n 'exclusiveMaximum',\n 'multipleOf',\n 'enum',\n 'const',\n 'properties',\n 'required',\n 'additionalProperties',\n 'items',\n 'minItems',\n 'maxItems',\n 'anyOf',\n 'oneOf',\n 'not',\n 'description',\n 'title',\n]);\n\nexport const SUPPORTED_VALIDATION_FORMATS: ReadonlySet<string> = new Set([\n 'email',\n 'uri',\n 'uuid',\n 'date-time',\n 'date',\n 'time',\n]);\n\ntype SerializationLocation = { entity?: string; attribute?: string };\n\n/** Raised when a config cannot be turned into a deployable JSON schema. */\nexport class SchemaSerializationError extends Error {\n readonly entity: string | undefined;\n readonly attribute: string | undefined;\n\n constructor(reason: string, location: SerializationLocation = {}, options?: { cause?: unknown }) {\n const where = location.entity\n ? location.attribute\n ? `Attribute \"${location.attribute}\" of entity \"${location.entity}\": `\n : `Entity \"${location.entity}\": `\n : '';\n\n super(`${where}${reason}`, options);\n this.name = 'SchemaSerializationError';\n this.entity = location.entity;\n this.attribute = location.attribute;\n }\n}\n\ntype ZodDef = {\n type?: string;\n checks?: { _zod: { def: { check?: string } } }[];\n innerType?: z.ZodType;\n in?: z.ZodType;\n out?: z.ZodType;\n element?: z.ZodType;\n options?: z.ZodType[];\n left?: z.ZodType;\n right?: z.ZodType;\n keyType?: z.ZodType;\n valueType?: z.ZodType;\n shape?: { [key: string]: z.ZodType };\n getter?: () => z.ZodType;\n};\n\nfunction getDef(schema: z.ZodType): ZodDef {\n return (schema as unknown as { _zod: { def: ZodDef } })._zod.def;\n}\n\n/**\n * `z.toJSONSchema` drops `.refine()` / `.superRefine()` / `.check()` without\n * complaint. Walk the schema and reject them explicitly: a rule that only ran\n * in the config author's head is worse than a deploy error.\n */\nfunction findCustomCheck(schema: z.ZodType, seen = new Set<z.ZodType>()): boolean {\n if (seen.has(schema)) {\n return false;\n }\n\n seen.add(schema);\n const def = getDef(schema);\n\n if (def.checks?.some((check) => check._zod.def.check === 'custom')) {\n return true;\n }\n\n const children: (z.ZodType | undefined)[] = [\n def.innerType,\n def.in,\n def.out,\n def.element,\n def.left,\n def.right,\n def.keyType,\n def.valueType,\n ...(def.options ?? []),\n ...Object.values(def.shape ?? {}),\n ...(def.type === 'lazy' && def.getter ? [def.getter()] : []),\n ];\n\n return children.some((child) => child !== undefined && findCustomCheck(child, seen));\n}\n\nfunction assertSupportedJsonSchema(fragment: unknown, location: SerializationLocation, path: string[] = []): void {\n if (Array.isArray(fragment)) {\n fragment.forEach((entry, index) => {\n assertSupportedJsonSchema(entry, location, [...path, String(index)]);\n });\n\n return;\n }\n\n if (!fragment || typeof fragment !== 'object') {\n return;\n }\n\n for (const [key, value] of Object.entries(fragment as { [key: string]: unknown })) {\n const insideMap = path.length > 0 && path[path.length - 1] === 'properties';\n\n if (!insideMap && !SUPPORTED_VALIDATION_KEYWORDS.has(key)) {\n throw new SchemaSerializationError(\n `validation uses the JSON Schema keyword \"${[...path, key].join('.')}\", which the Studio form cannot enforce. Supported keywords: ${[...SUPPORTED_VALIDATION_KEYWORDS].join(', ')}.`,\n location\n );\n }\n\n if (!insideMap && key === 'format' && typeof value === 'string' && !SUPPORTED_VALIDATION_FORMATS.has(value)) {\n throw new SchemaSerializationError(\n `validation uses the format \"${value}\", which the Studio form cannot enforce. Supported formats: ${[...SUPPORTED_VALIDATION_FORMATS].join(', ')}.`,\n location\n );\n }\n\n if (key === 'properties' || key === 'items' || key === 'anyOf' || key === 'oneOf' || key === 'not') {\n assertSupportedJsonSchema(value, location, [...path, key]);\n } else if (insideMap) {\n assertSupportedJsonSchema(value, location, [...path, key]);\n }\n }\n}\n\n/**\n * Compiles an attribute's Zod validation to a JSON Schema (draft-07) fragment.\n * Throws `SchemaSerializationError` for anything the Studio cannot enforce.\n */\nexport function serializeValidation(validation: ZodValidation, location: SerializationLocation = {}): JsonSchemaObject {\n let schema: z.ZodType;\n\n try {\n schema = validation(z);\n } catch (error) {\n throw new SchemaSerializationError('validation() threw while building the Zod schema.', location, { cause: error });\n }\n\n if (findCustomCheck(schema)) {\n throw new SchemaSerializationError(\n 'validation uses refine(), superRefine() or check(), which cannot be expressed as JSON Schema. Use built-in Zod checks such as min(), max(), length(), regex() or email().',\n location\n );\n }\n\n let fragment: unknown;\n\n try {\n fragment = z.toJSONSchema(schema, { target: 'draft-07', unrepresentable: 'throw' });\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n\n throw new SchemaSerializationError(`validation cannot be expressed as JSON Schema: ${reason}`, location, {\n cause: error,\n });\n }\n\n const stripped = stripJsonSchemaMeta(fragment) as JsonSchemaObject;\n assertSupportedJsonSchema(stripped, location);\n\n return stripped;\n}\n\nfunction assertBooleanFlag(value: unknown, name: string, location: SerializationLocation): void {\n if (value !== undefined && typeof value !== 'boolean') {\n throw new SchemaSerializationError(\n `\"${name}\" must be a boolean. Conditional callbacks are no longer supported; move the rule into the schema or drop it.`,\n location\n );\n }\n}\n\nfunction assertIconName(value: unknown, where: string, location: SerializationLocation): void {\n if (value !== undefined && typeof value !== 'string') {\n throw new SchemaSerializationError(\n `${where} must be a lucide icon name such as 'Tag'. React elements are no longer supported in flexkit.config.ts.`,\n location\n );\n }\n}\n\nexport function serializeAttribute(attribute: Attribute, entityName: string): UiAttribute {\n const location = { entity: entityName, attribute: attribute.name };\n\n assertBooleanFlag(attribute.hidden, 'hidden', location);\n assertBooleanFlag(attribute.readOnly, 'readOnly', location);\n\n const { validation, ...rest } = attribute;\n\n if (validation === undefined) {\n return rest as UiAttribute;\n }\n\n if (typeof validation !== 'function') {\n throw new SchemaSerializationError('validation must be a function receiving the Zod namespace.', location);\n }\n\n return { ...rest, validation: serializeValidation(validation, location) } as UiAttribute;\n}\n\nexport function serializeEntity(entity: Entity): UiEntity {\n const location = { entity: entity.name };\n\n assertIconName(entity.menu?.icon, 'menu.icon', location);\n entity.groups?.forEach((group) => {\n assertIconName(group.icon, `groups[\"${group.name}\"].icon`, location);\n assertBooleanFlag(group.hidden, `groups[\"${group.name}\"].hidden`, location);\n });\n\n const serialized: UiEntity = {\n name: entity.name,\n plural: entity.plural,\n attributes: entity.attributes.map((attribute) => serializeAttribute(attribute, entity.name)),\n };\n\n if (entity.display !== undefined) serialized.display = entity.display;\n if (entity.menu !== undefined) serialized.menu = entity.menu;\n if (entity.spaces !== undefined) serialized.spaces = entity.spaces;\n if (entity.groups !== undefined) serialized.groups = entity.groups;\n\n return serialized;\n}\n\n/**\n * Turns one project of a `flexkit.config.ts` into the payload `flexkit deploy`\n * sends. Only data survives: title, projectId, menuGroups, scopes, spaces and\n * the schema with validation compiled to JSON Schema.\n */\nexport function serializeProjectConfig(project: ProjectConfig): UiProjectSchema {\n if (!project || typeof project !== 'object' || typeof project.projectId !== 'string') {\n throw new SchemaSerializationError('each project needs a string \"projectId\".');\n }\n\n if (!Array.isArray(project.schema)) {\n throw new SchemaSerializationError('\"schema\" must be an array of entities.', {});\n }\n\n const serialized: UiProjectSchema = {\n projectId: project.projectId,\n schema: project.schema.map(serializeEntity),\n };\n\n if (project.title !== undefined) serialized.title = project.title;\n if (project.menuGroups !== undefined) serialized.menuGroups = project.menuGroups;\n if (project.scopes !== undefined) serialized.scopes = project.scopes;\n if (project.spaces !== undefined) serialized.spaces = project.spaces;\n\n return serialized;\n}\n\n/** Serializes a whole `flexkit.config.ts` export (one project or a list). */\nexport function serializeConfig(config: Config): UiProjectSchema[] {\n return (Array.isArray(config) ? config : [config]).map(serializeProjectConfig);\n}\n"],"mappings":"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","names":[]}