@ember-home/unbound-styleguide 0.0.14 → 0.0.16

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.
@@ -0,0 +1,429 @@
1
+ // src/@styleguide/forms/schema/define-form-schema.ts
2
+ function defineFormSchema() {
3
+ return function define(config) {
4
+ const sortedFields = [...config.fields].sort((a, b) => {
5
+ const orderA = a.ui?.order ?? Number.MAX_SAFE_INTEGER;
6
+ const orderB = b.ui?.order ?? Number.MAX_SAFE_INTEGER;
7
+ return orderA - orderB;
8
+ });
9
+ const sortedSections = config.sections ? [...config.sections].sort((a, b) => {
10
+ const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
11
+ const orderB = b.order ?? Number.MAX_SAFE_INTEGER;
12
+ return orderA - orderB;
13
+ }) : void 0;
14
+ return {
15
+ fields: sortedFields,
16
+ sections: sortedSections,
17
+ crossFieldValidation: config.crossFieldValidation
18
+ };
19
+ };
20
+ }
21
+ function field() {
22
+ return function defineField(name, kind, config) {
23
+ return {
24
+ name,
25
+ kind,
26
+ ...config
27
+ };
28
+ };
29
+ }
30
+ function textField() {
31
+ return function defineTextField(name, config) {
32
+ return { name, kind: "text", ...config };
33
+ };
34
+ }
35
+ function selectField() {
36
+ return function defineSelectField(name, options, config) {
37
+ return {
38
+ name,
39
+ kind: "select",
40
+ ...config,
41
+ ui: { ...config?.ui, options }
42
+ };
43
+ };
44
+ }
45
+ function checkboxField() {
46
+ return function defineCheckboxField(name, config) {
47
+ return { name, kind: "checkbox", ...config };
48
+ };
49
+ }
50
+ function arrayField() {
51
+ return function defineArrayField(name, itemSchema, config) {
52
+ const { config: arrayConfig, ...rest } = config ?? {};
53
+ return {
54
+ name,
55
+ kind: "array",
56
+ arrayItemSchema: itemSchema,
57
+ arrayConfig,
58
+ ...rest
59
+ };
60
+ };
61
+ }
62
+ function getFieldsForSection(schema, sectionId) {
63
+ return schema.fields.filter((field2) => field2.ui?.section === sectionId);
64
+ }
65
+ function getUnsectionedFields(schema) {
66
+ return schema.fields.filter((field2) => !field2.ui?.section);
67
+ }
68
+ function getFieldByName(schema, name) {
69
+ return schema.fields.find((field2) => field2.name === name);
70
+ }
71
+ function getArrayItemFieldByName(schema, name) {
72
+ const normalizedName = name.replace(/\.\d+\./g, ".0.");
73
+ for (const field2 of schema.fields) {
74
+ if (field2.kind === "array" && field2.arrayItemSchema) {
75
+ const match = field2.arrayItemSchema.find(
76
+ (item) => item.name === normalizedName
77
+ );
78
+ if (match) {
79
+ return { ...match, name };
80
+ }
81
+ }
82
+ }
83
+ return void 0;
84
+ }
85
+ function buildDefaultValues(schema) {
86
+ const defaults = {};
87
+ for (const field2 of schema.fields) {
88
+ if (field2.defaultValue !== void 0) {
89
+ setNestedValue(defaults, field2.name, field2.defaultValue);
90
+ }
91
+ }
92
+ return defaults;
93
+ }
94
+ function isArrayIndex(key) {
95
+ return /^\d+$/.test(key);
96
+ }
97
+ function setNestedValue(obj, path, value) {
98
+ const keys = path.split(".");
99
+ let current = obj;
100
+ for (let i = 0; i < keys.length - 1; i++) {
101
+ const key = keys[i];
102
+ const nextKey = keys[i + 1];
103
+ const nextNeedsArray = isArrayIndex(nextKey);
104
+ if (isArrayIndex(key)) {
105
+ const index = parseInt(key, 10);
106
+ const arr = current;
107
+ while (arr.length <= index) {
108
+ arr.push(void 0);
109
+ }
110
+ if (arr[index] === void 0 || typeof arr[index] !== "object") {
111
+ arr[index] = nextNeedsArray ? [] : {};
112
+ }
113
+ current = arr[index];
114
+ } else {
115
+ const record = current;
116
+ if (!(key in record) || typeof record[key] !== "object" || record[key] === null) {
117
+ record[key] = nextNeedsArray ? [] : {};
118
+ }
119
+ current = record[key];
120
+ }
121
+ }
122
+ const finalKey = keys[keys.length - 1];
123
+ if (isArrayIndex(finalKey)) {
124
+ const arr = current;
125
+ const index = parseInt(finalKey, 10);
126
+ while (arr.length <= index) {
127
+ arr.push(void 0);
128
+ }
129
+ arr[index] = value;
130
+ } else {
131
+ current[finalKey] = value;
132
+ }
133
+ }
134
+ function mergeSchemas(base, override) {
135
+ const fieldMap = new Map(base.fields.map((f) => [f.name, f]));
136
+ if (override.fields) {
137
+ for (const field2 of override.fields) {
138
+ fieldMap.set(field2.name, field2);
139
+ }
140
+ }
141
+ const sectionMap = new Map(
142
+ (base.sections ?? []).map((s) => [s.id, s])
143
+ );
144
+ if (override.sections) {
145
+ for (const section of override.sections) {
146
+ sectionMap.set(section.id, section);
147
+ }
148
+ }
149
+ const crossFieldRules = [
150
+ ...base.crossFieldValidation ?? [],
151
+ ...override.crossFieldValidation ?? []
152
+ ];
153
+ return {
154
+ fields: Array.from(fieldMap.values()),
155
+ sections: sectionMap.size > 0 ? Array.from(sectionMap.values()) : void 0,
156
+ crossFieldValidation: crossFieldRules.length > 0 ? crossFieldRules : void 0
157
+ };
158
+ }
159
+
160
+ // src/@styleguide/forms/server/server-schema-validator.ts
161
+ import { z } from "zod";
162
+ var validationRuleWithMessage = z.union([
163
+ z.number(),
164
+ z.object({
165
+ value: z.number(),
166
+ message: z.string()
167
+ })
168
+ ]);
169
+ var patternRule = z.union([
170
+ z.string(),
171
+ z.object({
172
+ value: z.string(),
173
+ message: z.string()
174
+ })
175
+ ]);
176
+ var serverFieldValidationSchema = z.object({
177
+ required: z.union([z.boolean(), z.string()]).optional(),
178
+ min: validationRuleWithMessage.optional(),
179
+ max: validationRuleWithMessage.optional(),
180
+ minLength: validationRuleWithMessage.optional(),
181
+ maxLength: validationRuleWithMessage.optional(),
182
+ pattern: patternRule.optional(),
183
+ asyncValidatorUrl: z.string().optional(),
184
+ asyncDebounceMs: z.number().optional()
185
+ }).optional();
186
+ var fieldOptionSchema = z.object({
187
+ value: z.string(),
188
+ label: z.string(),
189
+ disabled: z.boolean().optional()
190
+ });
191
+ var serverFieldUISchema = z.object({
192
+ label: z.string().optional(),
193
+ placeholder: z.string().optional(),
194
+ description: z.string().optional(),
195
+ tooltip: z.string().optional(),
196
+ order: z.number().optional(),
197
+ section: z.string().optional(),
198
+ width: z.enum(["full", "half", "third", "quarter"]).optional(),
199
+ className: z.string().optional(),
200
+ options: z.array(fieldOptionSchema).optional(),
201
+ allowCreate: z.boolean().optional(),
202
+ searchable: z.boolean().optional(),
203
+ inputType: z.string().optional(),
204
+ autoCapitalize: z.string().optional(),
205
+ autoComplete: z.string().optional(),
206
+ maxRows: z.number().optional(),
207
+ minRows: z.number().optional()
208
+ }).optional();
209
+ var serverFieldVisibilitySchema = z.object({
210
+ dependsOn: z.array(z.string()).optional(),
211
+ showWhen: z.record(z.string(), z.unknown()).optional(),
212
+ hideWhen: z.record(z.string(), z.unknown()).optional(),
213
+ requiredWhen: z.record(z.string(), z.unknown()).optional(),
214
+ disabledWhen: z.record(z.string(), z.unknown()).optional()
215
+ }).optional();
216
+ var arrayConfigSchema = z.object({
217
+ minItems: z.number().optional(),
218
+ maxItems: z.number().optional(),
219
+ defaultItem: z.record(z.string(), z.unknown()).optional(),
220
+ allowReorder: z.boolean().optional(),
221
+ addButtonLabel: z.string().optional(),
222
+ removeButtonLabel: z.string().optional(),
223
+ showItemNumber: z.boolean().optional(),
224
+ itemHeaderTemplate: z.string().optional(),
225
+ removeConfirmMessage: z.string().optional(),
226
+ collapsible: z.boolean().optional(),
227
+ defaultCollapsed: z.boolean().optional()
228
+ }).optional();
229
+ var baseServerFieldSchema = z.object({
230
+ name: z.string().min(1, "Field name is required"),
231
+ kind: z.string().min(1, "Field kind is required"),
232
+ ui: serverFieldUISchema,
233
+ validation: serverFieldValidationSchema,
234
+ visibility: serverFieldVisibilitySchema,
235
+ defaultValue: z.unknown().optional(),
236
+ readOnly: z.boolean().optional(),
237
+ arrayConfig: arrayConfigSchema,
238
+ meta: z.record(z.string(), z.unknown()).optional()
239
+ });
240
+ var serverFieldSchema = baseServerFieldSchema.extend({
241
+ arrayItemSchema: z.lazy(() => z.array(serverFieldSchema)).optional()
242
+ });
243
+ var sectionSchema = z.object({
244
+ id: z.string().min(1, "Section id is required"),
245
+ title: z.string().optional(),
246
+ description: z.string().optional(),
247
+ collapsible: z.boolean().optional(),
248
+ defaultCollapsed: z.boolean().optional(),
249
+ order: z.number().optional()
250
+ });
251
+ var serverFormSchema = z.object({
252
+ fields: z.array(serverFieldSchema).min(1, "At least one field is required"),
253
+ sections: z.array(sectionSchema).optional()
254
+ });
255
+ function validateServerSchema(data) {
256
+ const result = serverFormSchema.safeParse(data);
257
+ if (result.success) {
258
+ return { success: true, data: result.data };
259
+ }
260
+ const issues = result.error.issues.map((issue) => {
261
+ const path = issue.path.join(".");
262
+ return ` - ${path || "root"}: ${issue.message}`;
263
+ });
264
+ const message = `Invalid server schema:
265
+ ${issues.join("\n")}`;
266
+ return {
267
+ success: false,
268
+ error: result.error,
269
+ message
270
+ };
271
+ }
272
+ function assertServerSchema(data) {
273
+ const result = validateServerSchema(data);
274
+ if (!result.success) {
275
+ throw new Error(result.message);
276
+ }
277
+ }
278
+
279
+ // src/@styleguide/forms/server/fetchServerSchema.ts
280
+ function parseServerField(serverField) {
281
+ const field2 = {
282
+ name: serverField.name,
283
+ kind: serverField.kind
284
+ };
285
+ if (serverField.ui) {
286
+ field2.ui = { ...serverField.ui };
287
+ }
288
+ if (serverField.defaultValue !== void 0) {
289
+ field2.defaultValue = serverField.defaultValue;
290
+ }
291
+ if (serverField.readOnly !== void 0) {
292
+ field2.readOnly = serverField.readOnly;
293
+ }
294
+ if (serverField.meta) {
295
+ field2.meta = serverField.meta;
296
+ }
297
+ if (serverField.validation) {
298
+ const validation = serverField.validation;
299
+ field2.validation = {};
300
+ if (validation.required !== void 0) {
301
+ field2.validation.required = validation.required;
302
+ }
303
+ if (validation.min !== void 0) {
304
+ field2.validation.min = validation.min;
305
+ }
306
+ if (validation.max !== void 0) {
307
+ field2.validation.max = validation.max;
308
+ }
309
+ if (validation.minLength !== void 0) {
310
+ field2.validation.minLength = validation.minLength;
311
+ }
312
+ if (validation.maxLength !== void 0) {
313
+ field2.validation.maxLength = validation.maxLength;
314
+ }
315
+ if (validation.pattern !== void 0) {
316
+ try {
317
+ if (typeof validation.pattern === "string") {
318
+ field2.validation.pattern = new RegExp(validation.pattern);
319
+ } else if (typeof validation.pattern === "object") {
320
+ field2.validation.pattern = {
321
+ value: new RegExp(validation.pattern.value),
322
+ message: validation.pattern.message
323
+ };
324
+ }
325
+ } catch (error) {
326
+ console.error(
327
+ `Invalid regex pattern for field "${serverField.name}": ${typeof validation.pattern === "string" ? validation.pattern : validation.pattern.value}`
328
+ );
329
+ }
330
+ }
331
+ if (validation.asyncValidatorUrl) {
332
+ field2.validation.asyncValidatorUrl = validation.asyncValidatorUrl;
333
+ }
334
+ if (validation.asyncDebounceMs !== void 0) {
335
+ field2.validation.asyncDebounceMs = validation.asyncDebounceMs;
336
+ }
337
+ }
338
+ if (serverField.visibility) {
339
+ const visibility = serverField.visibility;
340
+ field2.visibility = {
341
+ dependsOn: visibility.dependsOn,
342
+ showWhen: visibility.showWhen,
343
+ hideWhen: visibility.hideWhen,
344
+ requiredWhen: visibility.requiredWhen,
345
+ disabledWhen: visibility.disabledWhen
346
+ };
347
+ }
348
+ if (serverField.arrayItemSchema) {
349
+ field2.arrayItemSchema = serverField.arrayItemSchema.map(
350
+ (item) => parseServerField(item)
351
+ );
352
+ }
353
+ if (serverField.arrayConfig) {
354
+ field2.arrayConfig = serverField.arrayConfig;
355
+ }
356
+ return field2;
357
+ }
358
+ function parseServerSchema(serverSchema, options) {
359
+ if (options?.strict) {
360
+ const validationResult = validateServerSchema(serverSchema);
361
+ if (!validationResult.success) {
362
+ throw new Error(validationResult.message);
363
+ }
364
+ }
365
+ return {
366
+ fields: serverSchema.fields.map((field2) => parseServerField(field2)),
367
+ sections: serverSchema.sections
368
+ };
369
+ }
370
+ function parseServerSchemaSafe(data) {
371
+ const validationResult = validateServerSchema(data);
372
+ if (!validationResult.success) {
373
+ return {
374
+ success: false,
375
+ error: validationResult
376
+ };
377
+ }
378
+ const schema = parseServerSchema(
379
+ validationResult.data
380
+ );
381
+ return { success: true, schema };
382
+ }
383
+ async function fetchServerSchema(url, options) {
384
+ const response = await fetch(url, {
385
+ ...options?.fetchOptions,
386
+ headers: {
387
+ "Content-Type": "application/json",
388
+ ...options?.fetchOptions?.headers
389
+ }
390
+ });
391
+ if (!response.ok) {
392
+ throw new Error(`Failed to load schema: ${response.status}`);
393
+ }
394
+ let data = await response.json();
395
+ if (options?.transformResponse) {
396
+ data = options.transformResponse(data);
397
+ }
398
+ const serverSchema = parseServerSchema(data);
399
+ return options?.baseSchema ? mergeSchemas(options.baseSchema, serverSchema) : serverSchema;
400
+ }
401
+ function buildSchemaResponse(schema) {
402
+ return {
403
+ fields: schema.fields,
404
+ sections: schema.sections
405
+ };
406
+ }
407
+
408
+ export {
409
+ defineFormSchema,
410
+ field,
411
+ textField,
412
+ selectField,
413
+ checkboxField,
414
+ arrayField,
415
+ getFieldsForSection,
416
+ getUnsectionedFields,
417
+ getFieldByName,
418
+ getArrayItemFieldByName,
419
+ buildDefaultValues,
420
+ mergeSchemas,
421
+ serverFieldSchema,
422
+ serverFormSchema,
423
+ validateServerSchema,
424
+ assertServerSchema,
425
+ parseServerSchema,
426
+ parseServerSchemaSafe,
427
+ fetchServerSchema,
428
+ buildSchemaResponse
429
+ };