formhell 0.1.5

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/index.mjs ADDED
@@ -0,0 +1,4408 @@
1
+ // src/index.ts
2
+ import "./styles-UY6N5SPU.css";
3
+
4
+ // src/components/SchemaForm.tsx
5
+ import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
6
+
7
+ // src/utils/defaultData.ts
8
+ function createDefaultValueFromSchema(schema, options, isRequiredField = true) {
9
+ const defaultsMode = options?.defaults ?? "all";
10
+ const shouldUseSchemaDefault = defaultsMode === "all" || isRequiredField;
11
+ if (shouldUseSchemaDefault && schema.default !== void 0) {
12
+ return schema.default;
13
+ }
14
+ const type = getPrimaryType(schema);
15
+ switch (type) {
16
+ case "object": {
17
+ const result = {};
18
+ const required = new Set(schema.required ?? []);
19
+ const properties = schema.properties ?? {};
20
+ for (const [key, childSchema] of Object.entries(properties)) {
21
+ const childIsRequired = required.has(key);
22
+ const shouldIncludeField = childIsRequired || defaultsMode === "all" && childSchema.default !== void 0;
23
+ if (shouldIncludeField) {
24
+ result[key] = createDefaultValueFromSchema(childSchema, options, childIsRequired);
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+ case "array":
30
+ return [];
31
+ case "boolean":
32
+ return false;
33
+ case "number":
34
+ case "integer":
35
+ return void 0;
36
+ case "null":
37
+ return null;
38
+ case "string":
39
+ return schema.enum?.[0] ?? "";
40
+ default:
41
+ return void 0;
42
+ }
43
+ }
44
+ function getPrimaryType(schema) {
45
+ if (Array.isArray(schema.type)) {
46
+ return schema.type[0];
47
+ }
48
+ return schema.type;
49
+ }
50
+
51
+ // src/utils/jsonPointer.ts
52
+ function escapeJsonPointerToken(token) {
53
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
54
+ }
55
+ function unescapeJsonPointerToken(token) {
56
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
57
+ }
58
+ function toPointerTokens(pointer) {
59
+ const normalized = pointer.startsWith("#") ? pointer.slice(1) : pointer;
60
+ if (!normalized) {
61
+ return [];
62
+ }
63
+ if (!normalized.startsWith("/")) {
64
+ throw new Error(`Invalid JSON pointer: ${pointer}`);
65
+ }
66
+ return normalized.split("/").slice(1).map(unescapeJsonPointerToken);
67
+ }
68
+ function joinPointer(basePointer, token) {
69
+ const escaped = escapeJsonPointerToken(token);
70
+ if (!basePointer) {
71
+ return `/${escaped}`;
72
+ }
73
+ return `${basePointer}/${escaped}`;
74
+ }
75
+ function getValueAtPointer(source, pointer) {
76
+ const tokens = toPointerTokens(pointer);
77
+ let current = source;
78
+ for (const token of tokens) {
79
+ if (current === void 0 || current === null) {
80
+ return void 0;
81
+ }
82
+ current = current[token];
83
+ }
84
+ return current;
85
+ }
86
+ function setValueAtPointer(source, pointer, value) {
87
+ const tokens = toPointerTokens(pointer);
88
+ if (tokens.length === 0) {
89
+ return value;
90
+ }
91
+ const root = deepClone(source);
92
+ let current = root;
93
+ for (let index = 0; index < tokens.length - 1; index += 1) {
94
+ const token = tokens[index];
95
+ const nextToken = tokens[index + 1];
96
+ if (current[token] === void 0 || current[token] === null) {
97
+ current[token] = isArrayIndexToken(nextToken) ? [] : {};
98
+ }
99
+ current = current[token];
100
+ }
101
+ const leafToken = tokens[tokens.length - 1];
102
+ current[leafToken] = value;
103
+ return root;
104
+ }
105
+ function isArrayIndexToken(token) {
106
+ return /^\d+$/.test(token);
107
+ }
108
+ function deepClone(value) {
109
+ if (typeof structuredClone === "function") {
110
+ return structuredClone(value);
111
+ }
112
+ return JSON.parse(JSON.stringify(value));
113
+ }
114
+
115
+ // src/utils/refResolver.ts
116
+ import { getByPointer } from "json-pointer-relational";
117
+ import { bundle } from "@hyperjump/json-schema/bundle";
118
+ import { hasSchema, registerSchema, unregisterSchema } from "@hyperjump/json-schema/draft-2020-12";
119
+ var MissingPeerSchemaError = class extends Error {
120
+ constructor(ref) {
121
+ super(`Could not find referenced peer schema for: ${ref}`);
122
+ this.name = "MissingPeerSchemaError";
123
+ this.ref = ref;
124
+ }
125
+ };
126
+ var generatedSchemaCounter = 0;
127
+ var DEFAULT_DIALECT_URI = "https://json-schema.org/draft/2020-12/schema";
128
+ async function resolveSchemaRefs(schema, peerSchemas) {
129
+ const cloned = deepClone2(schema);
130
+ const peerCandidates = toSchemaCandidates(peerSchemas);
131
+ const missingRef = findFirstMissingExternalRef(cloned, peerCandidates);
132
+ if (missingRef) {
133
+ throw new MissingPeerSchemaError(missingRef);
134
+ }
135
+ const bundled = await bundleWithRegisteredSchemas(cloned, peerCandidates);
136
+ const bundledCandidates = collectSchemaCandidates(bundled);
137
+ const candidates = mergeCandidates(peerCandidates, bundledCandidates);
138
+ const visiting = /* @__PURE__ */ new Set();
139
+ return resolveNode(bundled, bundled, candidates, visiting);
140
+ }
141
+ function resolveNode(node, currentSchemaRoot, candidates, visiting) {
142
+ if (Array.isArray(node)) {
143
+ return node.map((entry) => resolveNode(entry, currentSchemaRoot, candidates, visiting));
144
+ }
145
+ if (!isObject(node)) {
146
+ return node;
147
+ }
148
+ if (typeof node.$ref === "string") {
149
+ const resolved = resolveRefNode(node, currentSchemaRoot, candidates, visiting);
150
+ return resolveNode(resolved.value, resolved.currentSchemaRoot, candidates, visiting);
151
+ }
152
+ const result = {};
153
+ for (const [key, value] of Object.entries(node)) {
154
+ result[key] = resolveNode(value, currentSchemaRoot, candidates, visiting);
155
+ }
156
+ return result;
157
+ }
158
+ function resolveRefNode(node, currentSchemaRoot, candidates, visiting) {
159
+ const ref = node.$ref;
160
+ const externalReference = isExternalReference(ref);
161
+ const recursionKey = `${currentSchemaRoot.$id ?? "local"}::${ref}`;
162
+ if (visiting.has(recursionKey)) {
163
+ throw new Error(`Infinite recursion detected while resolving reference: ${ref}`);
164
+ }
165
+ visiting.add(recursionKey);
166
+ let targetSchema = currentSchemaRoot;
167
+ let resultJsonPointer = ref;
168
+ if (externalReference) {
169
+ const matched = findBestCandidate(ref, candidates);
170
+ if (!matched) {
171
+ throw new MissingPeerSchemaError(ref);
172
+ }
173
+ targetSchema = matched.schema;
174
+ resultJsonPointer = ref.slice(matched.identifier.length);
175
+ if (!resultJsonPointer) {
176
+ resultJsonPointer = "#";
177
+ }
178
+ }
179
+ const resolvedValue = getByPointer(resultJsonPointer, targetSchema);
180
+ if (!isObject(resolvedValue)) {
181
+ throw new Error(`Could not resolve JSON pointer: ${resultJsonPointer}`);
182
+ }
183
+ const { $ref: _omitRef, ...rest } = node;
184
+ const merged = {
185
+ ...deepClone2(resolvedValue),
186
+ ...rest
187
+ };
188
+ if (externalReference && resultJsonPointer === "#") {
189
+ delete merged.$id;
190
+ }
191
+ visiting.delete(recursionKey);
192
+ return { value: merged, currentSchemaRoot: targetSchema };
193
+ }
194
+ function isExternalReference(ref) {
195
+ return !(ref.startsWith("#") || ref.startsWith("/"));
196
+ }
197
+ function findFirstMissingExternalRef(rootSchema, candidates) {
198
+ const visited = /* @__PURE__ */ new Set();
199
+ const stack = [rootSchema, ...candidates.map((candidate) => candidate.schema)];
200
+ while (stack.length > 0) {
201
+ const current = stack.pop();
202
+ if (!isObject(current)) {
203
+ continue;
204
+ }
205
+ if (visited.has(current)) {
206
+ continue;
207
+ }
208
+ visited.add(current);
209
+ if (typeof current.$ref === "string" && isExternalReference(current.$ref)) {
210
+ if (!findBestCandidate(current.$ref, candidates)) {
211
+ return current.$ref;
212
+ }
213
+ }
214
+ for (const value of Object.values(current)) {
215
+ if (Array.isArray(value)) {
216
+ for (const entry of value) {
217
+ stack.push(entry);
218
+ }
219
+ } else {
220
+ stack.push(value);
221
+ }
222
+ }
223
+ }
224
+ return null;
225
+ }
226
+ async function bundleWithRegisteredSchemas(schema, candidates) {
227
+ const rootUri = typeof schema.$id === "string" && schema.$id.length > 0 ? schema.$id : `urn:formhell:bundled-schema:${generatedSchemaCounter++}`;
228
+ const registeredUris = /* @__PURE__ */ new Set();
229
+ registerSchemaWithOverwrite(asHyperjumpSchema(schema), rootUri);
230
+ registeredUris.add(rootUri);
231
+ for (const candidate of candidates) {
232
+ const candidateUri = normalizeCandidateRegistrationUri(candidate);
233
+ if (registeredUris.has(candidateUri)) {
234
+ continue;
235
+ }
236
+ registerSchemaWithOverwrite(asHyperjumpSchema(candidate.schema), candidateUri);
237
+ registeredUris.add(candidateUri);
238
+ }
239
+ const bundled = await bundle(rootUri);
240
+ if (!isObject(bundled)) {
241
+ throw new Error("Bundling did not produce a valid schema document.");
242
+ }
243
+ return bundled;
244
+ }
245
+ function normalizeCandidateRegistrationUri(candidate) {
246
+ if (typeof candidate.schema.$id === "string" && candidate.schema.$id.length > 0) {
247
+ return candidate.schema.$id;
248
+ }
249
+ return stripFragment(candidate.identifier);
250
+ }
251
+ function stripFragment(uri) {
252
+ const hashIndex = uri.indexOf("#");
253
+ if (hashIndex === -1) {
254
+ return uri;
255
+ }
256
+ return uri.slice(0, hashIndex);
257
+ }
258
+ function asHyperjumpSchema(schema) {
259
+ return schema;
260
+ }
261
+ function registerSchemaWithOverwrite(schema, uri) {
262
+ if (hasSchema(uri)) {
263
+ unregisterSchema(uri);
264
+ }
265
+ registerSchema(schema, uri, DEFAULT_DIALECT_URI);
266
+ }
267
+ function collectSchemaCandidates(schema) {
268
+ const collected = /* @__PURE__ */ new Map();
269
+ const visited = /* @__PURE__ */ new Set();
270
+ const stack = [schema];
271
+ while (stack.length > 0) {
272
+ const current = stack.pop();
273
+ if (!isObject(current)) {
274
+ continue;
275
+ }
276
+ if (visited.has(current)) {
277
+ continue;
278
+ }
279
+ visited.add(current);
280
+ if (typeof current.$id === "string" && current.$id.length > 0) {
281
+ collected.set(current.$id, {
282
+ identifier: current.$id,
283
+ schema: current
284
+ });
285
+ }
286
+ for (const value of Object.values(current)) {
287
+ if (Array.isArray(value)) {
288
+ for (const entry of value) {
289
+ stack.push(entry);
290
+ }
291
+ } else {
292
+ stack.push(value);
293
+ }
294
+ }
295
+ }
296
+ return Array.from(collected.values());
297
+ }
298
+ function mergeCandidates(...candidateGroups) {
299
+ const merged = /* @__PURE__ */ new Map();
300
+ for (const group of candidateGroups) {
301
+ for (const candidate of group) {
302
+ merged.set(candidate.identifier, candidate);
303
+ }
304
+ }
305
+ return Array.from(merged.values());
306
+ }
307
+ function findBestCandidate(ref, candidates) {
308
+ const matches = candidates.filter((candidate) => ref.startsWith(candidate.identifier));
309
+ matches.sort((a, b) => b.identifier.length - a.identifier.length);
310
+ return matches[0];
311
+ }
312
+ function toSchemaCandidates(peerSchemas) {
313
+ if (!peerSchemas) {
314
+ return [];
315
+ }
316
+ if (Array.isArray(peerSchemas)) {
317
+ return peerSchemas.filter((schema) => typeof schema.$id === "string" && schema.$id.length > 0).map((schema) => ({ identifier: schema.$id, schema }));
318
+ }
319
+ return Object.entries(peerSchemas).map(([identifier, schema]) => ({ identifier, schema }));
320
+ }
321
+ function deepClone2(value) {
322
+ if (typeof structuredClone === "function") {
323
+ return structuredClone(value);
324
+ }
325
+ return JSON.parse(JSON.stringify(value));
326
+ }
327
+ function isObject(value) {
328
+ return typeof value === "object" && value !== null && !Array.isArray(value);
329
+ }
330
+
331
+ // src/utils/schemaValidation.ts
332
+ import Ajv from "ajv";
333
+ import Ajv2019 from "ajv/dist/2019";
334
+ import Ajv2020 from "ajv/dist/2020";
335
+ var AJV_SUPPORTED_FORMATS = [
336
+ "date",
337
+ "time",
338
+ "date-time",
339
+ "duration",
340
+ "uri",
341
+ "uri-reference",
342
+ "uri-template",
343
+ "url",
344
+ "email",
345
+ "hostname",
346
+ "ipv4",
347
+ "ipv6",
348
+ "regex",
349
+ "uuid",
350
+ "json-pointer",
351
+ "json-pointer-uri-fragment",
352
+ "relative-json-pointer"
353
+ ];
354
+ var AJV_OPTIONS = {
355
+ allErrors: true,
356
+ strict: false,
357
+ validateSchema: true
358
+ };
359
+ function createAjvForSchema(schema) {
360
+ const schemaUri = schema.$schema ?? "";
361
+ if (schemaUri.includes("2020-12")) {
362
+ return new Ajv2020(AJV_OPTIONS);
363
+ }
364
+ if (schemaUri.includes("2019-09")) {
365
+ return new Ajv2019(AJV_OPTIONS);
366
+ }
367
+ return new Ajv(AJV_OPTIONS);
368
+ }
369
+ function validateSchemaOrThrow(schema, label) {
370
+ const ajv = createAjvForSchema(schema);
371
+ const valid = ajv.validateSchema(schema);
372
+ if (!valid) {
373
+ const errors = ajv.errorsText(ajv.errors, { separator: "; " });
374
+ throw new Error(`${label} is not a valid JSON Schema. ${errors}`.trim());
375
+ }
376
+ }
377
+ function validatePeerSchemasOrThrow(peerSchemas) {
378
+ if (!peerSchemas) {
379
+ return;
380
+ }
381
+ if (Array.isArray(peerSchemas)) {
382
+ peerSchemas.forEach((schema, index) => {
383
+ validateSchemaOrThrow(schema, `peerSchemas[${index}]`);
384
+ });
385
+ return;
386
+ }
387
+ for (const [key, schema] of Object.entries(peerSchemas)) {
388
+ validateSchemaOrThrow(schema, `peerSchemas.${key}`);
389
+ }
390
+ }
391
+ function validateDataOrThrow(data, schema) {
392
+ const ajv = createAjvForSchema(schema);
393
+ const validate = ajv.compile(schema);
394
+ const valid = validate(data);
395
+ if (!valid) {
396
+ const errors = ajv.errorsText(validate.errors, { separator: "; " });
397
+ throw new Error(`Provided data does not match schema. ${errors}`.trim());
398
+ }
399
+ }
400
+
401
+ // src/components/SchemaFieldRenderer.tsx
402
+ import { useEffect, useState } from "react";
403
+
404
+ // src/components/fields/SchemaFormArray.tsx
405
+ import { useRef } from "react";
406
+
407
+ // src/components/fields/FieldShell.tsx
408
+ import { jsx, jsxs } from "react/jsx-runtime";
409
+ function FieldShell({ label, required, controls, children }) {
410
+ return /* @__PURE__ */ jsxs("div", { className: "raf-field", children: [
411
+ /* @__PURE__ */ jsxs("div", { className: "raf-field-label-row", children: [
412
+ /* @__PURE__ */ jsxs("label", { className: "raf-field-label", children: [
413
+ label,
414
+ required ? /* @__PURE__ */ jsx("span", { className: "raf-field-required", children: "*" }) : null
415
+ ] }),
416
+ !required ? /* @__PURE__ */ jsx("span", { className: "raf-field-optional", children: "Optional" }) : null
417
+ ] }),
418
+ controls ? /* @__PURE__ */ jsx("div", { className: "raf-button-row", children: controls }) : null,
419
+ children
420
+ ] });
421
+ }
422
+
423
+ // src/components/fields/SchemaFormArray.tsx
424
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
425
+ function SchemaFormArray({
426
+ label,
427
+ required,
428
+ pointer,
429
+ schema,
430
+ value,
431
+ disabled,
432
+ controls,
433
+ canAddItem,
434
+ canRemoveItems,
435
+ onChange,
436
+ renderItem,
437
+ createDefaultItem
438
+ }) {
439
+ const items = Array.isArray(value) ? value : [];
440
+ const hasUserModifiedRef = useRef(false);
441
+ const lastSignatureRef = useRef(null);
442
+ const maxItems = typeof schema.maxItems === "number" ? schema.maxItems : void 0;
443
+ const initialItemCount = getInitialItemCount(schema.minItems, maxItems);
444
+ const schemaSignature = `${pointer}|${String(schema.minItems ?? "")}|${String(schema.maxItems ?? "")}|${JSON.stringify(schema.items ?? null)}|${JSON.stringify(schema.prefixItems ?? null)}`;
445
+ if (schemaSignature !== lastSignatureRef.current) {
446
+ hasUserModifiedRef.current = false;
447
+ lastSignatureRef.current = schemaSignature;
448
+ }
449
+ const renderedItems = items.length === 0 && !hasUserModifiedRef.current ? Array.from({ length: initialItemCount }, () => createDefaultItem()) : items;
450
+ const showAddItem = !disabled && canAddItem !== false && renderedItems.length < (maxItems ?? Number.POSITIVE_INFINITY);
451
+ return /* @__PURE__ */ jsx2(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsxs2("div", { children: [
452
+ renderedItems.map((item, index) => {
453
+ const itemPointer = `${pointer}/${index}`;
454
+ return /* @__PURE__ */ jsxs2("div", { className: "raf-array-item", children: [
455
+ renderItem(index, itemPointer, item),
456
+ canRemoveItems === false ? null : /* @__PURE__ */ jsx2("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx2(
457
+ "button",
458
+ {
459
+ className: "raf-button raf-button-danger",
460
+ type: "button",
461
+ disabled,
462
+ onClick: () => {
463
+ hasUserModifiedRef.current = true;
464
+ const next = [...renderedItems];
465
+ next.splice(index, 1);
466
+ onChange(next);
467
+ },
468
+ children: "Remove"
469
+ }
470
+ ) })
471
+ ] }, itemPointer);
472
+ }),
473
+ showAddItem ? /* @__PURE__ */ jsx2(
474
+ "button",
475
+ {
476
+ className: "raf-button raf-button-primary",
477
+ type: "button",
478
+ onClick: () => {
479
+ hasUserModifiedRef.current = true;
480
+ const next = [...renderedItems, createDefaultItem()];
481
+ onChange(next);
482
+ },
483
+ children: "Add Item"
484
+ }
485
+ ) : null
486
+ ] }) });
487
+ }
488
+ function getInitialItemCount(minItems, maxItems) {
489
+ const desiredCount = typeof minItems === "number" && minItems > 1 ? Math.floor(minItems) : 1;
490
+ if (maxItems === void 0) {
491
+ return desiredCount;
492
+ }
493
+ return Math.max(0, Math.min(desiredCount, maxItems));
494
+ }
495
+
496
+ // src/components/fields/SchemaFormBoolean.tsx
497
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
498
+ function SchemaFormBoolean({ label, required, value, disabled, controls, onChange }) {
499
+ return /* @__PURE__ */ jsx3(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsxs3("label", { className: "raf-checkbox-row", children: [
500
+ /* @__PURE__ */ jsx3(
501
+ "input",
502
+ {
503
+ className: "raf-checkbox",
504
+ type: "checkbox",
505
+ checked: Boolean(value),
506
+ disabled,
507
+ onChange: (event) => onChange(event.target.checked)
508
+ }
509
+ ),
510
+ /* @__PURE__ */ jsx3("span", { children: value ? "True" : "False" })
511
+ ] }) });
512
+ }
513
+
514
+ // src/components/fields/SchemaFormInteger.tsx
515
+ import { jsx as jsx4 } from "react/jsx-runtime";
516
+ function SchemaFormInteger({ label, required, value, disabled, controls, onChange }) {
517
+ return /* @__PURE__ */ jsx4(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsx4(
518
+ "input",
519
+ {
520
+ className: "raf-input",
521
+ type: "number",
522
+ step: 1,
523
+ value: value ?? "",
524
+ disabled,
525
+ onChange: (event) => {
526
+ const nextValue = event.target.value;
527
+ onChange(nextValue === "" ? void 0 : Math.trunc(Number(nextValue)));
528
+ }
529
+ }
530
+ ) });
531
+ }
532
+
533
+ // src/components/fields/SchemaFormNull.tsx
534
+ import { jsx as jsx5 } from "react/jsx-runtime";
535
+ function SchemaFormNull({ label, required, controls }) {
536
+ return /* @__PURE__ */ jsx5(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsx5("div", { className: "raf-muted", children: "Value is always null." }) });
537
+ }
538
+
539
+ // src/components/fields/SchemaFormNumber.tsx
540
+ import { jsx as jsx6 } from "react/jsx-runtime";
541
+ function SchemaFormNumber({ label, required, value, disabled, controls, onChange }) {
542
+ return /* @__PURE__ */ jsx6(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsx6(
543
+ "input",
544
+ {
545
+ className: "raf-input",
546
+ type: "number",
547
+ value: value ?? "",
548
+ disabled,
549
+ onChange: (event) => {
550
+ const nextValue = event.target.value;
551
+ onChange(nextValue === "" ? void 0 : Number(nextValue));
552
+ }
553
+ }
554
+ ) });
555
+ }
556
+
557
+ // src/components/fields/SchemaFormObject.tsx
558
+ import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
559
+ function SchemaFormObject({ label, required, disabled, controls, children }) {
560
+ return /* @__PURE__ */ jsxs4("details", { className: "raf-object", open: true, children: [
561
+ /* @__PURE__ */ jsxs4("summary", { className: "raf-object-summary", children: [
562
+ label,
563
+ required ? /* @__PURE__ */ jsx7("span", { className: "raf-field-required", children: "*" }) : null
564
+ ] }),
565
+ /* @__PURE__ */ jsxs4("div", { className: "raf-object-content", "aria-disabled": disabled, children: [
566
+ controls ? /* @__PURE__ */ jsx7("div", { className: "raf-button-row", children: controls }) : null,
567
+ children
568
+ ] })
569
+ ] });
570
+ }
571
+
572
+ // src/components/fields/SchemaFormSelect.tsx
573
+ import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
574
+ function SchemaFormSelect({ label, required, schema, value, disabled, controls, onChange }) {
575
+ const options = Array.isArray(schema.enum) ? schema.enum : [];
576
+ const selectedValue = encodeEnumValue(value);
577
+ return /* @__PURE__ */ jsx8(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsxs5(
578
+ "select",
579
+ {
580
+ className: "raf-select",
581
+ value: selectedValue,
582
+ disabled,
583
+ onChange: (event) => onChange(decodeEnumValue(event.target.value)),
584
+ children: [
585
+ !required ? /* @__PURE__ */ jsx8("option", { value: "", children: "Select..." }) : null,
586
+ options.map((option) => /* @__PURE__ */ jsx8("option", { value: encodeEnumValue(option), children: String(option) }, encodeEnumValue(option)))
587
+ ]
588
+ }
589
+ ) });
590
+ }
591
+ function encodeEnumValue(value) {
592
+ if (value === void 0) {
593
+ return "";
594
+ }
595
+ return JSON.stringify(value);
596
+ }
597
+ function decodeEnumValue(value) {
598
+ if (value === "") {
599
+ return "";
600
+ }
601
+ try {
602
+ return JSON.parse(value);
603
+ } catch {
604
+ return value;
605
+ }
606
+ }
607
+
608
+ // src/components/fields/SchemaFormString.tsx
609
+ import { jsx as jsx9 } from "react/jsx-runtime";
610
+ function SchemaFormString({ label, required, value, disabled, controls, onChange }) {
611
+ return /* @__PURE__ */ jsx9(FieldShell, { label, required, controls, children: /* @__PURE__ */ jsx9(
612
+ "input",
613
+ {
614
+ className: "raf-input",
615
+ type: "text",
616
+ value: value ?? "",
617
+ disabled,
618
+ onChange: (event) => onChange(event.target.value)
619
+ }
620
+ ) });
621
+ }
622
+
623
+ // src/components/SchemaFieldRenderer.tsx
624
+ import { Fragment, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
625
+ function SchemaFieldRenderer(props) {
626
+ const { schema, label, required, pointer, schemaPointer, value, onChange, widgets, controls } = props;
627
+ const hasConstValue = Object.prototype.hasOwnProperty.call(schema, "const");
628
+ const lockedValue = hasConstValue ? schema.const : value;
629
+ const isConstLocked = hasConstValue;
630
+ const schemaTypes = resolveTypes(schema);
631
+ const type = resolveType(schema);
632
+ const hasTypeChoices = schemaTypes.length > 1;
633
+ const hasEnum = Array.isArray(schema.enum) && schema.enum.length > 0;
634
+ const inferredType = inferValueType(lockedValue, schemaTypes);
635
+ const [selectedType, setSelectedType] = useState(() => inferredType ?? schemaTypes[0]);
636
+ const activeType = selectedType && schemaTypes.includes(selectedType) ? selectedType : inferredType ?? schemaTypes[0];
637
+ const tupleItems = activeType === "array" ? Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.items) ? schema.items : void 0 : void 0;
638
+ const singleItemsSchema = activeType === "array" && !Array.isArray(schema.items) && isObject2(schema.items) ? schema.items : void 0;
639
+ const itemSchemas = activeType === "array" ? tupleItems ?? (singleItemsSchema ? [singleItemsSchema] : void 0) : void 0;
640
+ const maxItems = activeType === "array" && typeof schema.maxItems === "number" ? schema.maxItems : void 0;
641
+ useEffect(() => {
642
+ if (selectedType && schemaTypes.includes(selectedType) && !inferredType) {
643
+ return;
644
+ }
645
+ if (inferredType && inferredType !== selectedType) {
646
+ setSelectedType(inferredType);
647
+ return;
648
+ }
649
+ if (selectedType && !schemaTypes.includes(selectedType)) {
650
+ setSelectedType(inferredType ?? schemaTypes[0]);
651
+ }
652
+ }, [inferredType, schemaTypes, selectedType]);
653
+ const typeChooser = hasTypeChoices && !hasEnum ? /* @__PURE__ */ jsxs6("div", { className: "raf-button-row", "aria-label": `${label} type chooser`, children: [
654
+ schemaTypes.filter((choice) => choice !== "null").map((choice) => /* @__PURE__ */ jsx10(
655
+ "button",
656
+ {
657
+ className: "raf-button raf-button-secondary",
658
+ type: "button",
659
+ disabled: isConstLocked,
660
+ "aria-pressed": activeType === choice,
661
+ onClick: () => {
662
+ setSelectedType(choice);
663
+ onChange(pointer, createDefaultValueForType(choice));
664
+ },
665
+ children: choice
666
+ },
667
+ choice
668
+ )),
669
+ schemaTypes.includes("null") ? /* @__PURE__ */ jsx10(
670
+ "button",
671
+ {
672
+ className: "raf-button raf-button-secondary",
673
+ type: "button",
674
+ disabled: isConstLocked,
675
+ "aria-pressed": activeType === "null",
676
+ onClick: () => {
677
+ setSelectedType("null");
678
+ onChange(pointer, null);
679
+ },
680
+ children: "Insert NULL"
681
+ }
682
+ ) : null
683
+ ] }) : null;
684
+ const fieldControls = controls && typeChooser ? /* @__PURE__ */ jsxs6(Fragment, { children: [
685
+ controls,
686
+ typeChooser
687
+ ] }) : controls ?? typeChooser;
688
+ if (activeType === "object") {
689
+ const ObjectWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Object ?? SchemaFormObject;
690
+ const objectValue = isObject2(lockedValue) ? lockedValue : {};
691
+ const requiredKeys = new Set(schema.required ?? []);
692
+ const properties = schema.properties ?? {};
693
+ return /* @__PURE__ */ jsx10(
694
+ ObjectWidget,
695
+ {
696
+ label,
697
+ required,
698
+ pointer,
699
+ schema,
700
+ value: objectValue,
701
+ disabled: isConstLocked,
702
+ controls: fieldControls,
703
+ onChange: (next) => {
704
+ if (isConstLocked) {
705
+ return;
706
+ }
707
+ onChange(pointer, next);
708
+ },
709
+ children: Object.entries(properties).map(([propertyName, propertySchema]) => {
710
+ const childPointer = joinPointer(pointer, propertyName);
711
+ const childSchemaPointer = joinPointer(joinPointer(schemaPointer, "properties"), propertyName);
712
+ const childValue = objectValue[propertyName];
713
+ return /* @__PURE__ */ jsx10(
714
+ SchemaFieldRenderer,
715
+ {
716
+ schema: propertySchema,
717
+ label: propertySchema.title ?? propertyName,
718
+ required: requiredKeys.has(propertyName),
719
+ pointer: childPointer,
720
+ schemaPointer: childSchemaPointer,
721
+ value: childValue,
722
+ onChange,
723
+ widgets
724
+ },
725
+ childPointer
726
+ );
727
+ })
728
+ }
729
+ );
730
+ }
731
+ if (activeType === "array") {
732
+ const ArrayWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Array ?? SchemaFormArray;
733
+ const arrayValue = Array.isArray(lockedValue) ? lockedValue : [];
734
+ const addLimit = maxItems ?? Number.POSITIVE_INFINITY;
735
+ const fixedTupleValue = tupleItems ? tupleItems.map(
736
+ (itemSchema, index) => arrayValue[index] === void 0 ? createDefaultValueFromSchema(itemSchema) : arrayValue[index]
737
+ ) : arrayValue;
738
+ const canAddItem = tupleItems ? false : arrayValue.length < addLimit;
739
+ return /* @__PURE__ */ jsx10(
740
+ ArrayWidget,
741
+ {
742
+ label,
743
+ required,
744
+ pointer,
745
+ schema,
746
+ value: fixedTupleValue,
747
+ disabled: isConstLocked,
748
+ controls: fieldControls,
749
+ onChange: (next) => {
750
+ if (isConstLocked) {
751
+ return;
752
+ }
753
+ onChange(pointer, next);
754
+ },
755
+ itemsSchema: singleItemsSchema,
756
+ itemSchemas,
757
+ createDefaultItem: () => createDefaultValueForArrayItem(itemSchemas, arrayValue.length),
758
+ renderItem: (index, itemPointer, itemValue) => /* @__PURE__ */ jsx10(
759
+ SchemaFieldRenderer,
760
+ {
761
+ schema: tupleItems?.[index] ?? singleItemsSchema ?? { type: "string" },
762
+ label: tupleItems?.[index]?.title?.trim() ? tupleItems[index].title : tupleItems ? `Tuple ${index + 1}` : `Item ${index + 1}`,
763
+ required: true,
764
+ pointer: itemPointer,
765
+ schemaPointer: tupleItems ? joinPointer(joinPointer(schemaPointer, "prefixItems"), String(index)) : joinPointer(schemaPointer, "items"),
766
+ value: itemValue,
767
+ onChange,
768
+ widgets
769
+ }
770
+ ),
771
+ canAddItem,
772
+ canRemoveItems: !tupleItems
773
+ }
774
+ );
775
+ }
776
+ if (activeType === "boolean") {
777
+ const BooleanWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Boolean ?? SchemaFormBoolean;
778
+ return /* @__PURE__ */ jsx10(
779
+ BooleanWidget,
780
+ {
781
+ label,
782
+ required,
783
+ pointer,
784
+ schema,
785
+ value: Boolean(lockedValue),
786
+ disabled: isConstLocked,
787
+ controls: fieldControls,
788
+ onChange: (next) => {
789
+ if (isConstLocked) {
790
+ return;
791
+ }
792
+ onChange(pointer, next);
793
+ }
794
+ }
795
+ );
796
+ }
797
+ if (activeType === "number") {
798
+ const NumberWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Number ?? SchemaFormNumber;
799
+ return /* @__PURE__ */ jsx10(
800
+ NumberWidget,
801
+ {
802
+ label,
803
+ required,
804
+ pointer,
805
+ schema,
806
+ value: typeof lockedValue === "number" ? lockedValue : void 0,
807
+ disabled: isConstLocked,
808
+ controls: fieldControls,
809
+ onChange: (next) => {
810
+ if (isConstLocked) {
811
+ return;
812
+ }
813
+ onChange(pointer, next);
814
+ }
815
+ }
816
+ );
817
+ }
818
+ if (activeType === "integer") {
819
+ const IntegerWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Integer ?? SchemaFormInteger;
820
+ return /* @__PURE__ */ jsx10(
821
+ IntegerWidget,
822
+ {
823
+ label,
824
+ required,
825
+ pointer,
826
+ schema,
827
+ value: typeof lockedValue === "number" ? lockedValue : void 0,
828
+ disabled: isConstLocked,
829
+ controls: fieldControls,
830
+ onChange: (next) => {
831
+ if (isConstLocked) {
832
+ return;
833
+ }
834
+ onChange(pointer, next);
835
+ }
836
+ }
837
+ );
838
+ }
839
+ if (activeType === "null") {
840
+ const NullWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Null ?? SchemaFormNull;
841
+ return /* @__PURE__ */ jsx10(
842
+ NullWidget,
843
+ {
844
+ label,
845
+ required,
846
+ pointer,
847
+ schema,
848
+ value: null,
849
+ disabled: isConstLocked,
850
+ controls: fieldControls,
851
+ onChange: () => {
852
+ if (isConstLocked) {
853
+ return;
854
+ }
855
+ onChange(pointer, null);
856
+ }
857
+ }
858
+ );
859
+ }
860
+ if (hasEnum && (activeType === "string" || hasTypeChoices)) {
861
+ const SelectWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.Select ?? SchemaFormSelect;
862
+ return /* @__PURE__ */ jsx10(
863
+ SelectWidget,
864
+ {
865
+ label,
866
+ required,
867
+ pointer,
868
+ schema,
869
+ value: lockedValue,
870
+ disabled: isConstLocked,
871
+ controls: fieldControls,
872
+ onChange: (next) => {
873
+ if (isConstLocked) {
874
+ return;
875
+ }
876
+ onChange(pointer, next);
877
+ }
878
+ }
879
+ );
880
+ }
881
+ const StringWidget = getSchemaPointerWidget(widgets, schemaPointer) ?? widgets?.String ?? SchemaFormString;
882
+ return /* @__PURE__ */ jsx10(
883
+ StringWidget,
884
+ {
885
+ label,
886
+ required,
887
+ pointer,
888
+ schema,
889
+ value: typeof lockedValue === "string" ? lockedValue : "",
890
+ disabled: isConstLocked,
891
+ controls: fieldControls,
892
+ onChange: (next) => {
893
+ if (isConstLocked) {
894
+ return;
895
+ }
896
+ onChange(pointer, next);
897
+ }
898
+ }
899
+ );
900
+ }
901
+ function getSchemaPointerWidget(widgets, schemaPointer) {
902
+ if (!widgets) {
903
+ return void 0;
904
+ }
905
+ const candidate = widgets[schemaPointer];
906
+ if (!candidate) {
907
+ return void 0;
908
+ }
909
+ return candidate;
910
+ }
911
+ function resolveType(schema) {
912
+ if (Array.isArray(schema.type) && schema.type.length > 0) {
913
+ return schema.type[0];
914
+ }
915
+ if (typeof schema.type === "string") {
916
+ return schema.type;
917
+ }
918
+ if (schema.properties) {
919
+ return "object";
920
+ }
921
+ if (schema.items) {
922
+ return "array";
923
+ }
924
+ return "string";
925
+ }
926
+ function resolveTypes(schema) {
927
+ if (Array.isArray(schema.type) && schema.type.length > 0) {
928
+ return schema.type.filter((type) => typeof type === "string");
929
+ }
930
+ return [resolveType(schema)];
931
+ }
932
+ function inferValueType(value, schemaTypes) {
933
+ if (value === null && schemaTypes.includes("null")) {
934
+ return "null";
935
+ }
936
+ if (Array.isArray(value) && schemaTypes.includes("array")) {
937
+ return "array";
938
+ }
939
+ if (isObject2(value) && schemaTypes.includes("object")) {
940
+ return "object";
941
+ }
942
+ if (typeof value === "boolean" && schemaTypes.includes("boolean")) {
943
+ return "boolean";
944
+ }
945
+ if (typeof value === "number") {
946
+ if (schemaTypes.includes("integer") && Number.isInteger(value)) {
947
+ return "integer";
948
+ }
949
+ if (schemaTypes.includes("number")) {
950
+ return "number";
951
+ }
952
+ }
953
+ if (typeof value === "string" && schemaTypes.includes("string")) {
954
+ return "string";
955
+ }
956
+ return void 0;
957
+ }
958
+ function createDefaultValueForType(type) {
959
+ switch (type) {
960
+ case "string":
961
+ return "";
962
+ case "number":
963
+ case "integer":
964
+ return void 0;
965
+ case "boolean":
966
+ return false;
967
+ case "object":
968
+ return {};
969
+ case "array":
970
+ return [];
971
+ case "null":
972
+ return null;
973
+ default:
974
+ return void 0;
975
+ }
976
+ }
977
+ function isObject2(value) {
978
+ return typeof value === "object" && value !== null && !Array.isArray(value);
979
+ }
980
+ function createDefaultValueForArrayItem(itemSchemas, index) {
981
+ const schema = itemSchemas?.[index] ?? itemSchemas?.[0] ?? itemSchemas?.[itemSchemas.length - 1];
982
+ return createDefaultValueFromSchema(schema ?? {});
983
+ }
984
+
985
+ // src/components/SchemaForm.tsx
986
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
987
+ function SchemaForm({ schema, peerSchemas, getSchema, widgets, options, data, onChange }) {
988
+ const onChangeRef = useRef2(onChange);
989
+ const [resolvedSchema, setResolvedSchema] = useState2(null);
990
+ const [resolutionError, setResolutionError] = useState2(null);
991
+ const [isWaitingForPeerSchemas, setIsWaitingForPeerSchemas] = useState2(false);
992
+ useEffect2(() => {
993
+ onChangeRef.current = onChange;
994
+ }, [onChange]);
995
+ useEffect2(() => {
996
+ let cancelled = false;
997
+ const resolveSchema = async () => {
998
+ try {
999
+ validateSchemaOrThrow(schema, "schema");
1000
+ validatePeerSchemasOrThrow(peerSchemas);
1001
+ try {
1002
+ const resolved = await resolveSchemaRefs(schema, peerSchemas);
1003
+ if (!cancelled) {
1004
+ setResolvedSchema(resolved);
1005
+ setResolutionError(null);
1006
+ setIsWaitingForPeerSchemas(false);
1007
+ }
1008
+ return;
1009
+ } catch (error) {
1010
+ if (!(error instanceof MissingPeerSchemaError) || !getSchema) {
1011
+ throw error;
1012
+ }
1013
+ if (!cancelled) {
1014
+ setResolvedSchema(null);
1015
+ setResolutionError(null);
1016
+ setIsWaitingForPeerSchemas(true);
1017
+ }
1018
+ }
1019
+ const resolvedWithFallback = await resolveSchemaWithFallback(schema, peerSchemas, getSchema);
1020
+ if (!cancelled) {
1021
+ setResolvedSchema(resolvedWithFallback);
1022
+ setResolutionError(null);
1023
+ setIsWaitingForPeerSchemas(false);
1024
+ }
1025
+ } catch (error) {
1026
+ if (!cancelled) {
1027
+ setResolvedSchema(null);
1028
+ setResolutionError(error instanceof Error ? error : new Error("Schema resolution failed."));
1029
+ setIsWaitingForPeerSchemas(false);
1030
+ }
1031
+ }
1032
+ };
1033
+ void resolveSchema();
1034
+ return () => {
1035
+ cancelled = true;
1036
+ };
1037
+ }, [schema, peerSchemas, getSchema]);
1038
+ const initialData = useMemo(() => {
1039
+ if (!resolvedSchema) {
1040
+ return data ?? {};
1041
+ }
1042
+ if (data !== void 0) {
1043
+ return data;
1044
+ }
1045
+ const fallback = createDefaultValueFromSchema(resolvedSchema, {
1046
+ defaults: options?.defaults ?? "all"
1047
+ });
1048
+ return fallback ?? {};
1049
+ }, [data, options?.defaults, resolvedSchema]);
1050
+ const [formData, setFormData] = useState2(initialData);
1051
+ useEffect2(() => {
1052
+ if (!resolvedSchema) {
1053
+ return;
1054
+ }
1055
+ setFormData(initialData);
1056
+ const validationErrors = getDataValidationErrors(initialData, resolvedSchema);
1057
+ onChangeRef.current?.(initialData, validationErrors, "", void 0, initialData);
1058
+ }, [initialData, resolvedSchema]);
1059
+ const handleFieldChange = (pointer, next) => {
1060
+ if (!resolvedSchema) {
1061
+ return;
1062
+ }
1063
+ const previousValue = getValueAtPointer(formData, pointer);
1064
+ const updated = setValueAtPointer(formData, pointer, next);
1065
+ const validationErrors = getDataValidationErrors(updated, resolvedSchema);
1066
+ setFormData(updated);
1067
+ onChangeRef.current?.(updated, validationErrors, pointer, previousValue, next);
1068
+ };
1069
+ if (resolutionError) {
1070
+ throw resolutionError;
1071
+ }
1072
+ if (isWaitingForPeerSchemas) {
1073
+ return /* @__PURE__ */ jsxs7("div", { className: "raf-loading-state", role: "status", "aria-live": "polite", children: [
1074
+ /* @__PURE__ */ jsx11("span", { className: "raf-loading-spinner", "aria-hidden": "true" }),
1075
+ /* @__PURE__ */ jsx11("span", { children: "Waiting for required peer schema(s)" })
1076
+ ] });
1077
+ }
1078
+ if (!resolvedSchema) {
1079
+ return /* @__PURE__ */ jsx11("div", { className: "raf-muted", children: "Resolving schema references..." });
1080
+ }
1081
+ return /* @__PURE__ */ jsx11("div", { className: "raf-schema-form", children: /* @__PURE__ */ jsx11(
1082
+ SchemaFieldRenderer,
1083
+ {
1084
+ schema: resolvedSchema,
1085
+ label: resolvedSchema.title ?? "Schema Form",
1086
+ required: true,
1087
+ pointer: "",
1088
+ schemaPointer: "",
1089
+ value: formData,
1090
+ onChange: handleFieldChange,
1091
+ widgets
1092
+ }
1093
+ ) });
1094
+ }
1095
+ function getDataValidationErrors(data, schema) {
1096
+ try {
1097
+ validateDataOrThrow(data, schema);
1098
+ return [];
1099
+ } catch (error) {
1100
+ return [
1101
+ {
1102
+ message: error instanceof Error ? error.message : "Validation error",
1103
+ source: "data"
1104
+ }
1105
+ ];
1106
+ }
1107
+ }
1108
+ async function resolveSchemaWithFallback(rootSchema, initialPeerSchemas, getSchema) {
1109
+ const attemptedRefs = /* @__PURE__ */ new Set();
1110
+ let currentPeerSchemas = initialPeerSchemas;
1111
+ while (true) {
1112
+ try {
1113
+ return await resolveSchemaRefs(rootSchema, currentPeerSchemas);
1114
+ } catch (error) {
1115
+ if (!(error instanceof MissingPeerSchemaError)) {
1116
+ throw error;
1117
+ }
1118
+ const missingRef = error.ref;
1119
+ if (attemptedRefs.has(missingRef)) {
1120
+ throw new Error(`Could not resolve referenced peer schema for: ${missingRef}`);
1121
+ }
1122
+ attemptedRefs.add(missingRef);
1123
+ let fetchedSchema;
1124
+ try {
1125
+ const maybeSchema = await getSchema(missingRef);
1126
+ if (!maybeSchema) {
1127
+ throw new Error(`getSchema resolved without a schema for: ${missingRef}`);
1128
+ }
1129
+ fetchedSchema = maybeSchema;
1130
+ } catch (fetchError) {
1131
+ const message = fetchError instanceof Error ? fetchError.message : "Unknown schema loading error.";
1132
+ throw new Error(`Failed to load referenced schema for: ${missingRef}. ${message}`);
1133
+ }
1134
+ validateSchemaOrThrow(fetchedSchema, `getSchema(${missingRef})`);
1135
+ currentPeerSchemas = appendPeerSchema(currentPeerSchemas, missingRef, fetchedSchema);
1136
+ }
1137
+ }
1138
+ }
1139
+ function appendPeerSchema(peerSchemas, requestedRef, schema) {
1140
+ const next = {};
1141
+ if (Array.isArray(peerSchemas)) {
1142
+ for (const item of peerSchemas) {
1143
+ if (typeof item.$id === "string" && item.$id.length > 0) {
1144
+ next[item.$id] = item;
1145
+ }
1146
+ }
1147
+ } else if (peerSchemas) {
1148
+ Object.assign(next, peerSchemas);
1149
+ }
1150
+ next[requestedRef] = schema;
1151
+ const requestedRootId = extractReferenceRootId(requestedRef);
1152
+ if (requestedRootId) {
1153
+ next[requestedRootId] = schema;
1154
+ }
1155
+ if (typeof schema.$id === "string" && schema.$id.length > 0) {
1156
+ next[schema.$id] = schema;
1157
+ }
1158
+ return next;
1159
+ }
1160
+ function extractReferenceRootId(ref) {
1161
+ const hashIndex = ref.indexOf("#");
1162
+ if (hashIndex === -1) {
1163
+ return ref;
1164
+ }
1165
+ return ref.slice(0, hashIndex);
1166
+ }
1167
+
1168
+ // src/components/SchemaBuilder.tsx
1169
+ import { createContext, useCallback, useContext, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo2, useRef as useRef3, useState as useState3 } from "react";
1170
+ import parse from "html-react-parser";
1171
+ import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1172
+ var FIELD_TYPES = ["string", "number", "integer", "boolean", "object", "array", "null"];
1173
+ var DEFAULT_SCHEMA_URI = "https://json-schema.org/draft/2020-12/schema";
1174
+ var FieldHelpContext = createContext(null);
1175
+ var FIELD_HELP_BASE_CONTENT = {
1176
+ "$id": {
1177
+ summary: "Sets the base URI that identifies this schema.",
1178
+ details: "Use <strong>$id</strong> to assign a stable identifier for the schema resource. Relative references and nested schema identifiers are resolved against this base URI."
1179
+ },
1180
+ "$schema": {
1181
+ summary: "Declares which JSON Schema dialect this schema uses.",
1182
+ details: "Set <strong>$schema</strong> to the meta-schema URI for the draft you target, such as 2020-12. Validators use it to interpret keyword behavior and vocabulary support."
1183
+ },
1184
+ "$ref": {
1185
+ summary: "References another schema and applies it at this location.",
1186
+ details: "Use <strong>$ref</strong> to point to a schema URI or JSON Pointer. Validation of this node is delegated to the referenced schema, allowing reuse and composition."
1187
+ },
1188
+ type: {
1189
+ summary: "Restricts instance values to one or more JSON types.",
1190
+ details: "Set <strong>type</strong> to a single <strong>type</strong> or a list of allowed types. This controls which instances can match and which <strong>type</strong>-specific assertion keywords are meaningful."
1191
+ },
1192
+ title: {
1193
+ summary: "Provides a short human-readable name for the schema.",
1194
+ details: "Use <strong>title</strong> as annotation text for UI labels, documentation, and schema browsing. It does <strong>not</strong> affect assertion or pass/fail validation outcomes."
1195
+ },
1196
+ description: {
1197
+ summary: "Provides longer human-readable documentation for the schema.",
1198
+ details: "Use <strong>description</strong> to explain intent, constraints, and <strong>examples</strong> in prose. It is an annotation keyword and does <strong>not</strong> directly change validation results."
1199
+ },
1200
+ deprecated: {
1201
+ summary: "Marks values as discouraged for future use.",
1202
+ details: "Set <strong>deprecated</strong> to true to signal that this schema location should be phased out. It is metadata for tooling and consumers rather than an assertion failure."
1203
+ },
1204
+ readonly: {
1205
+ summary: "Marks a value as read-only for producers and clients.",
1206
+ details: "<strong>readOnly</strong> is an annotation commonly used by APIs and forms to prevent user edits on output-only fields. It does <strong>not</strong> invalidate JSON instances by itself."
1207
+ },
1208
+ writeonly: {
1209
+ summary: "Marks a value as write-only and not intended for output.",
1210
+ details: "<strong>writeOnly</strong> is an annotation used for sensitive or input-only fields. Tooling may hide these in responses while still accepting them in requests."
1211
+ },
1212
+ examples: {
1213
+ summary: "Supplies sample instance values for this schema location.",
1214
+ details: "Use <strong>examples</strong> as annotation data to illustrate typical values. These samples support docs and UI guidance but are <strong>not</strong> automatically enforced as constraints."
1215
+ },
1216
+ default: {
1217
+ summary: "Provides a suggested default value for consumers.",
1218
+ details: "<strong>default</strong> is an annotation that tools may use to prefill forms or generated objects. Validators do <strong>not</strong> automatically insert or require this value."
1219
+ },
1220
+ const: {
1221
+ summary: "Requires the instance to equal exactly one value.",
1222
+ details: "Use <strong>const</strong> when a field must always be a specific literal value. The instance must match this value exactly, including <strong>type</strong> and structure."
1223
+ },
1224
+ enum: {
1225
+ summary: "Restricts the instance to one of the listed values.",
1226
+ details: "<strong>enum</strong> defines an allowed set of values and the instance must equal one of them. Values can be strings, numbers, booleans, null, objects, or arrays."
1227
+ },
1228
+ minlength: {
1229
+ summary: "Sets the minimum string length in Unicode code points.",
1230
+ details: "<strong>minLength</strong> requires string instances to have at least this many characters. Use it with <strong>maxLength</strong> to bound accepted string sizes."
1231
+ },
1232
+ maxlength: {
1233
+ summary: "Sets the maximum string length in Unicode code points.",
1234
+ details: "<strong>maxLength</strong> requires string instances to be no longer than this value. It applies only when the instance <strong>type</strong> is string."
1235
+ },
1236
+ pattern: {
1237
+ summary: "Requires strings to match a regular expression. (HINT: Don't provide the leading or trailing slashes.)",
1238
+ details: "<strong>pattern</strong> uses an ECMA-262 compatible regular expression. A string is valid when the regex finds a match within the instance text."
1239
+ },
1240
+ format: {
1241
+ summary: "Annotates or optionally asserts semantic string formats.",
1242
+ details: "<strong>format</strong> communicates semantic expectations such as email, uri, or date-time. Depending on validator configuration, it may be informational or enforced."
1243
+ },
1244
+ minimum: {
1245
+ summary: "Sets the inclusive numeric lower bound.",
1246
+ details: "<strong>minimum</strong> requires numbers to be greater than or equal to this value. Use <strong>exclusiveMinimum</strong> when the lower bound should be strict."
1247
+ },
1248
+ maximum: {
1249
+ summary: "Sets the inclusive numeric upper bound.",
1250
+ details: "<strong>maximum</strong> requires numbers to be less than or equal to this value. Use <strong>exclusiveMaximum</strong> when the upper bound should be strict."
1251
+ },
1252
+ multipleof: {
1253
+ summary: "Requires numbers to be a multiple of the given divisor.",
1254
+ details: "<strong>multipleOf</strong> checks exact divisibility by a positive number. It is useful for increments such as currency steps or fixed precision values."
1255
+ },
1256
+ exclusiveminimum: {
1257
+ summary: "Sets a strict numeric lower bound.",
1258
+ details: "<strong>exclusiveMinimum</strong> requires numbers to be strictly greater than this value. Instances equal to the boundary are invalid."
1259
+ },
1260
+ exclusivemaximum: {
1261
+ summary: "Sets a strict numeric upper bound.",
1262
+ details: "<strong>exclusiveMaximum</strong> requires numbers to be strictly less than this value. Instances equal to the boundary are invalid."
1263
+ },
1264
+ properties: {
1265
+ summary: "Defines schemas for named object properties.",
1266
+ details: "<strong>properties</strong> maps property names to subschemas. When an instance has a matching property, that value is validated against the corresponding subschema."
1267
+ },
1268
+ required: {
1269
+ summary: "Lists object properties that must be present.",
1270
+ details: "<strong>required</strong> is an array of property names. Each listed name must appear on the object instance for validation to succeed."
1271
+ },
1272
+ additionalproperties: {
1273
+ summary: "Controls validation of object properties not listed in properties.",
1274
+ details: "<strong>additionalProperties</strong> applies to remaining object members after <strong>properties</strong> and <strong>patternProperties</strong>. Use false to disallow extras or a schema to validate them."
1275
+ },
1276
+ unevaluatedproperties: {
1277
+ summary: "Applies constraints to object properties not yet evaluated.",
1278
+ details: "<strong>unevaluatedProperties</strong> validates leftover object members after all applicable subschemas are considered. It helps enforce closed shapes with composition."
1279
+ },
1280
+ propertynames: {
1281
+ summary: "Validates each object property name as a string.",
1282
+ details: "<strong>propertyNames</strong> applies its subschema to every key name in the object. This is useful for naming rules such as patterns or length limits on keys."
1283
+ },
1284
+ minproperties: {
1285
+ summary: "Sets the minimum number of object properties.",
1286
+ details: "<strong>minProperties</strong> requires objects to contain at least this many <strong>properties</strong>. It is ignored for non-object instances."
1287
+ },
1288
+ maxproperties: {
1289
+ summary: "Sets the maximum number of object properties.",
1290
+ details: "<strong>maxProperties</strong> requires objects to contain no more than this many <strong>properties</strong>. It is ignored for non-object instances."
1291
+ },
1292
+ dependentrequired: {
1293
+ summary: "Requires additional properties when a property is present.",
1294
+ details: "<strong>dependentRequired</strong> maps a property name to a list of peer <strong>properties</strong> that must also exist whenever that property appears."
1295
+ },
1296
+ dependentschemas: {
1297
+ summary: "Applies additional schema rules when a property is present.",
1298
+ details: "<strong>dependentSchemas</strong> maps a property name to a subschema. <strong>If</strong> that property exists in the instance object, the entire object must satisfy the mapped schema."
1299
+ },
1300
+ patternproperties: {
1301
+ summary: "Applies schemas to object properties that match regex keys.",
1302
+ details: "<strong>patternProperties</strong> uses regular-expression keys to target groups of property names. Matching <strong>properties</strong> are validated by the associated subschema."
1303
+ },
1304
+ items: {
1305
+ summary: "Defines the schema for array elements after tuple positions.",
1306
+ details: "In 2020-12, <strong>items</strong> applies to array elements <strong>not</strong> covered by <strong>prefixItems</strong>. Set a schema to validate trailing elements, or false to disallow them."
1307
+ },
1308
+ prefixitems: {
1309
+ summary: "Defines positional schemas for tuple-style arrays.",
1310
+ details: "<strong>prefixItems</strong> is an ordered list of schemas, each applied to the array element at the same index. It models fixed-position tuple structures."
1311
+ },
1312
+ minitems: {
1313
+ summary: "Sets the minimum number of items in an array.",
1314
+ details: "<strong>minItems</strong> requires arrays to contain at least this many elements. It is ignored for non-array instances."
1315
+ },
1316
+ maxitems: {
1317
+ summary: "Sets the maximum number of items in an array.",
1318
+ details: "<strong>maxItems</strong> requires arrays to contain no more than this many elements. It is ignored for non-array instances."
1319
+ },
1320
+ uniqueitems: {
1321
+ summary: "Requires all array items to be pairwise unique.",
1322
+ details: "When <strong>uniqueItems</strong> is true, no two <strong>items</strong> in the array may be deeply equal. It enforces set-like semantics for arrays."
1323
+ },
1324
+ contains: {
1325
+ summary: "Requires at least one array item to match a subschema.",
1326
+ details: "<strong>contains</strong> checks array elements against a subschema and succeeds when enough matches are found. Combine with <strong>minContains</strong> and <strong>maxContains</strong> for match counts."
1327
+ },
1328
+ mincontains: {
1329
+ summary: "Sets the minimum number of contains matches.",
1330
+ details: "<strong>minContains</strong> works with <strong>contains</strong> and requires at least this many matching elements. It is ignored when <strong>contains</strong> is absent."
1331
+ },
1332
+ maxcontains: {
1333
+ summary: "Sets the maximum number of contains matches.",
1334
+ details: "<strong>maxContains</strong> works with <strong>contains</strong> and requires no more than this many matching elements. It is ignored when <strong>contains</strong> is absent."
1335
+ },
1336
+ unevaluateditems: {
1337
+ summary: "Applies constraints to array items not yet evaluated.",
1338
+ details: "<strong>unevaluatedItems</strong> validates leftover array elements after <strong>prefixItems</strong>, <strong>items</strong>, <strong>contains</strong>, and composed schemas are processed."
1339
+ },
1340
+ allof: {
1341
+ summary: "Requires the instance to satisfy every listed subschema.",
1342
+ details: "<strong>allOf</strong> composes schemas with logical AND behavior. The instance is valid only <strong>if</strong> it passes all subschemas in the array."
1343
+ },
1344
+ anyof: {
1345
+ summary: "Requires the instance to satisfy at least one subschema.",
1346
+ details: "<strong>anyOf</strong> composes schemas with logical OR behavior. The instance is valid when one or more listed subschemas validate."
1347
+ },
1348
+ oneof: {
1349
+ summary: "Requires the instance to satisfy exactly one subschema.",
1350
+ details: "<strong>oneOf</strong> succeeds only when exactly one subschema validates. It is useful for mutually exclusive alternatives."
1351
+ },
1352
+ not: {
1353
+ summary: "Requires the instance to fail a given subschema.",
1354
+ details: "<strong>not</strong> inverts schema logic. The instance is valid only when it does <strong>not</strong> validate against the <strong>not</strong> subschema."
1355
+ },
1356
+ if: {
1357
+ summary: "Defines the condition used by conditional schemas.",
1358
+ details: "<strong>if</strong> applies a subschema test. When it passes, <strong>then</strong> is applied; when it fails, <strong>else</strong> is applied, <strong>if</strong> those branches are present."
1359
+ },
1360
+ then: {
1361
+ summary: "Applies extra constraints when if succeeds.",
1362
+ details: "<strong>then</strong> is evaluated only when the <strong>if</strong> subschema validates. Use it to enforce rules that should hold under a matching condition."
1363
+ },
1364
+ else: {
1365
+ summary: "Applies extra constraints when if fails.",
1366
+ details: "<strong>else</strong> is evaluated only when the <strong>if</strong> subschema does <strong>not</strong> validate. Use it as the alternate branch of conditional validation."
1367
+ }
1368
+ };
1369
+ var FIELD_HELP_LONG_DETAILS = {
1370
+ "$id": "The <strong>$id</strong> keyword establishes the canonical URI for a schema resource, which becomes the base for relative references and nested identifiers. In draft 2020-12, stable and absolute identifiers make schema reuse, bundling, and external referencing significantly more predictable across tools.",
1371
+ "$schema": "The <strong>$schema</strong> keyword declares the dialect and meta-schema that define keyword behavior for this document. Declaring this explicitly helps validators select correct semantics and avoids ambiguity when multiple drafts are supported.",
1372
+ "$ref": "The <strong>$ref</strong> keyword replaces local constraints with the referenced schema target, enabling modular schema design and reuse. In 2020-12, references participate in dynamic resolution rules and should be treated as schema application rather than a textual include.",
1373
+ type: "The <strong>type</strong> keyword constrains instance types and is often the first line of validation structure for a field. In 2020-12, <strong>type</strong> can be a single value or an array of values to express unions, and other assertion keywords should align with the allowed types.",
1374
+ title: "The <strong>title</strong> keyword is an annotation intended for human-facing tools such as documentation and generated forms. While it does <strong>not</strong> influence pass/fail validation, it is important for readability and schema maintainability.",
1375
+ description: "The <strong>description</strong> keyword provides richer human guidance about expected data semantics and usage context. It is annotation-only, so it should be used to improve comprehension without being relied on for enforcement.",
1376
+ deprecated: "The <strong>deprecated</strong> keyword communicates that a field or value path is still accepted but should be phased out. This is particularly useful for compatibility windows and migration planning in APIs and event schemas.",
1377
+ readonly: "The <strong>readOnly</strong> keyword is an annotation that indicates values are intended to be supplied by producers and <strong>not</strong> edited by consumers. It is commonly interpreted by UI and API tooling even though it is <strong>not</strong> a direct validation assertion.",
1378
+ writeonly: "The <strong>writeOnly</strong> keyword is an annotation indicating values are intended for input but should <strong>not</strong> be returned in output contexts. It is often used for secrets, credentials, and transient request-only fields.",
1379
+ examples: "The <strong>examples</strong> keyword provides non-normative sample instances that help humans and tools understand expected values. These <strong>examples</strong> are documentation aids and are <strong>not</strong> <strong>required</strong> to be validated as constraints by implementations.",
1380
+ default: "The <strong>default</strong> keyword suggests a value that may be used when an instance omits the field. Because JSON Schema does <strong>not</strong> mandate <strong>default</strong> assignment behavior, producers should treat it as guidance rather than implicit mutation.",
1381
+ const: "The <strong>const</strong> keyword enforces exact deep-equality with one specific JSON value, including <strong>type</strong> and structure. It is useful when a discriminator or fixed contract token must always remain constant.",
1382
+ enum: "The <strong>enum</strong> keyword constrains the instance to one of a finite set of values compared by JSON deep-equality. This is ideal for controlled vocabularies and closed option sets across both primitive and structured values.",
1383
+ minlength: "The <strong>minLength</strong> keyword applies only to strings and sets a lower bound measured in Unicode code points. It should be paired thoughtfully with <strong>maxLength</strong> when defining bounded text fields.",
1384
+ maxlength: "The <strong>maxLength</strong> keyword applies only to strings and sets an upper bound measured in Unicode code points. This protects payload size and supports UI, storage, and transport constraints.",
1385
+ pattern: "The <strong>pattern</strong> keyword applies an ECMA-262 regular expression to string instances, succeeding when a match is found. Patterns are <strong>not</strong> implicitly anchored, so use explicit anchors when full-string matching is <strong>required</strong>.",
1386
+ format: "The <strong>format</strong> keyword conveys semantic expectations such as email, URI, hostname, or date-time. In 2020-12, <strong>format</strong> behavior depends on implementation configuration and may be annotation-only unless assertions are enabled.",
1387
+ minimum: "The <strong>minimum</strong> keyword sets an inclusive numeric lower bound and applies to number and integer instances. Use this when a boundary value itself is valid and should be accepted.",
1388
+ maximum: "The <strong>maximum</strong> keyword sets an inclusive numeric upper bound and applies to number and integer instances. Use this when the boundary value itself should remain valid.",
1389
+ multipleof: "The <strong>multipleOf</strong> keyword requires numeric values to divide evenly by a positive divisor. This is commonly used to enforce precision steps, measurement increments, and monetary granularity.",
1390
+ exclusiveminimum: "The <strong>exclusiveMinimum</strong> keyword defines a strict numeric boundary where values must be greater than the threshold. It is appropriate when an endpoint must be excluded from valid input.",
1391
+ exclusivemaximum: "The <strong>exclusiveMaximum</strong> keyword defines a strict numeric boundary where values must be less than the threshold. It is useful for open upper intervals and strict cap behavior.",
1392
+ properties: "The <strong>properties</strong> keyword maps specific object member names to subschemas that validate corresponding member values. It only applies when those named members are present and does <strong>not</strong> by itself require their presence.",
1393
+ required: "The <strong>required</strong> keyword lists object property names that must exist on an instance object. Presence is enforced independently from value constraints, which are validated by associated subschemas.",
1394
+ additionalproperties: "The <strong>additionalProperties</strong> keyword controls validation for object members <strong>not</strong> matched by <strong>properties</strong> or <strong>patternProperties</strong>. In strict object designs, setting this to false prevents unrecognized keys from passing.",
1395
+ unevaluatedproperties: "The <strong>unevaluatedProperties</strong> keyword applies after other applicators and targets object members that remain unevaluated. This makes it especially powerful with composition keywords when enforcing closed-world object shapes.",
1396
+ propertynames: "The <strong>propertyNames</strong> keyword validates each object key string against a subschema, independent of corresponding values. It is useful for naming conventions, prefix rules, and machine-generated key constraints.",
1397
+ minproperties: "The <strong>minProperties</strong> keyword sets the <strong>minimum</strong> count of key/value pairs <strong>required</strong> in an object instance. It supports cardinality rules independent from which exact <strong>properties</strong> are <strong>required</strong>.",
1398
+ maxproperties: "The <strong>maxProperties</strong> keyword sets the <strong>maximum</strong> count of key/value pairs permitted in an object instance. This can prevent over-populated objects and constrain dynamic key scenarios.",
1399
+ dependentrequired: "The <strong>dependentRequired</strong> keyword expresses conditional presence dependencies between object <strong>properties</strong>. When one property appears, a configured list of sibling <strong>properties</strong> must also be present.",
1400
+ dependentschemas: "The <strong>dependentSchemas</strong> keyword applies whole-object subschemas when specific trigger <strong>properties</strong> are present. It enables conditional object validation patterns that go beyond simple <strong>required</strong> lists.",
1401
+ patternproperties: "The <strong>patternProperties</strong> keyword assigns subschemas to regex-based key groups, allowing families of similarly named members to share validation rules. Multiple regexes may apply to the same property name.",
1402
+ items: "In draft 2020-12, <strong>items</strong> applies to array positions <strong>not</strong> covered by <strong>prefixItems</strong> and therefore governs trailing elements. This separates tuple-prefix constraints from the schema for remaining array entries.",
1403
+ prefixitems: "The <strong>prefixItems</strong> keyword defines positional schemas for tuple-like arrays where each index has a distinct rule. It is evaluated in order and is foundational for fixed-structure array contracts.",
1404
+ minitems: "The <strong>minItems</strong> keyword sets the <strong>minimum</strong> number of elements <strong>required</strong> in an array instance. It is commonly combined with <strong>contains</strong> or tuple rules to ensure baseline completeness.",
1405
+ maxitems: "The <strong>maxItems</strong> keyword sets the <strong>maximum</strong> number of elements permitted in an array instance. It is useful for preventing oversized arrays and bounding processing cost.",
1406
+ uniqueitems: "When <strong>uniqueItems</strong> is true, every pair of array elements must be unequal under deep JSON comparison. This enforces set-like semantics, including for objects and arrays.",
1407
+ contains: "The <strong>contains</strong> keyword requires array instances to include elements matching a given subschema. In 2020-12, it can be further quantified with <strong>minContains</strong> and <strong>maxContains</strong> to constrain match counts.",
1408
+ mincontains: "The <strong>minContains</strong> keyword defines the <strong>minimum</strong> number of elements that must satisfy the <strong>contains</strong> subschema. It is ignored when <strong>contains</strong> is absent and should be configured alongside <strong>contains</strong>.",
1409
+ maxcontains: "The <strong>maxContains</strong> keyword defines the <strong>maximum</strong> number of elements that may satisfy the <strong>contains</strong> subschema. It is ignored when <strong>contains</strong> is absent and helps bound matching frequency.",
1410
+ unevaluateditems: "The <strong>unevaluatedItems</strong> keyword applies to array elements <strong>not</strong> already evaluated by <strong>prefixItems</strong>, <strong>items</strong>, <strong>contains</strong>, or composed branches. It is valuable for enforcing tight post-composition array contracts.",
1411
+ allof: "The <strong>allOf</strong> keyword requires the instance to satisfy every subschema in the array, equivalent to logical conjunction. It is useful for composing orthogonal constraints into a single effective schema.",
1412
+ anyof: "The <strong>anyOf</strong> keyword requires at least one subschema to validate, equivalent to logical disjunction. It is suitable for permissive alternatives where overlaps are acceptable.",
1413
+ oneof: "The <strong>oneOf</strong> keyword requires exactly one subschema to validate, making it stricter than <strong>anyOf</strong>. It is commonly used for tagged union designs where alternatives should be mutually exclusive.",
1414
+ not: "The <strong>not</strong> keyword inverts validation for its subschema and passes only when the subschema fails. It is useful for exclusion constraints and disallowing problematic shapes.",
1415
+ if: "The <strong>if</strong> keyword defines a predicate subschema used to choose conditional branches. Its result controls whether <strong>then</strong> or <strong>else</strong> is evaluated when those keywords are present.",
1416
+ then: "The <strong>then</strong> keyword is applied only when <strong>if</strong> succeeds, allowing additional constraints in the true branch. It is often paired with discriminators and property dependencies.",
1417
+ else: "The <strong>else</strong> keyword is applied only when <strong>if</strong> fails, providing alternate constraints for the false branch. Together with <strong>if</strong>/<strong>then</strong>, it forms full conditional validation flow in a single schema node."
1418
+ };
1419
+ var FIELD_HELP_LINKS = {
1420
+ "$id": "https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-id-keyword",
1421
+ "$schema": "https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-schema-keyword",
1422
+ "$ref": "https://json-schema.org/draft/2020-12/json-schema-core.html#name-the-ref-keyword",
1423
+ type: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-type",
1424
+ title: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-title",
1425
+ description: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-description",
1426
+ deprecated: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-deprecated",
1427
+ readonly: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-readonly",
1428
+ writeonly: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-writeonly",
1429
+ examples: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-examples",
1430
+ default: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-default",
1431
+ const: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-const",
1432
+ enum: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-enum",
1433
+ minlength: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-minlength",
1434
+ maxlength: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxlength",
1435
+ pattern: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-pattern",
1436
+ format: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-format",
1437
+ minimum: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-minimum",
1438
+ maximum: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maximum",
1439
+ multipleof: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-multipleof",
1440
+ exclusiveminimum: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusiveminimum",
1441
+ exclusivemaximum: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-exclusivemaximum",
1442
+ properties: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-properties",
1443
+ required: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-required",
1444
+ additionalproperties: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-additionalproperties",
1445
+ unevaluatedproperties: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-unevaluatedproperties",
1446
+ propertynames: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-propertynames",
1447
+ minproperties: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-minproperties",
1448
+ maxproperties: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxproperties",
1449
+ dependentrequired: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-dependentrequired",
1450
+ dependentschemas: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-dependentschemas",
1451
+ patternproperties: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-patternproperties",
1452
+ items: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-items",
1453
+ prefixitems: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-prefixitems",
1454
+ minitems: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-minitems",
1455
+ maxitems: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxitems",
1456
+ uniqueitems: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-uniqueitems",
1457
+ contains: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-contains",
1458
+ mincontains: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-mincontains",
1459
+ maxcontains: "https://json-schema.org/draft/2020-12/json-schema-validation.html#name-maxcontains",
1460
+ unevaluateditems: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-unevaluateditems",
1461
+ allof: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-allof",
1462
+ anyof: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-anyof",
1463
+ oneof: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-oneof",
1464
+ not: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-not",
1465
+ if: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-if",
1466
+ then: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-then",
1467
+ else: "https://json-schema.org/draft/2020-12/json-schema-core.html#name-else"
1468
+ };
1469
+ var DEFAULT_HELP_LINK = "https://json-schema.org/draft/2020-12/";
1470
+ var FIELD_HELP_CONTENT = Object.fromEntries(
1471
+ Object.entries(FIELD_HELP_BASE_CONTENT).map(([key, value]) => [
1472
+ key,
1473
+ {
1474
+ ...value,
1475
+ longDetails: FIELD_HELP_LONG_DETAILS[key] ?? "This keyword participates in JSON Schema 2020-12 evaluation and should be configured according to the intended assertion and annotation behavior.",
1476
+ link: FIELD_HELP_LINKS[key] ?? DEFAULT_HELP_LINK
1477
+ }
1478
+ ])
1479
+ );
1480
+ function normalizeHelpKey(value) {
1481
+ return value.trim().toLowerCase().replace(/\(.*?\)/g, "").replace(/\s+/g, "").replace(/[^$a-z0-9]/g, "");
1482
+ }
1483
+ function resolveHelpDefinition(keywordOrLabel) {
1484
+ const normalized = normalizeHelpKey(keywordOrLabel);
1485
+ if (normalized === "types") {
1486
+ return FIELD_HELP_CONTENT.type;
1487
+ }
1488
+ if (normalized === "min") {
1489
+ return FIELD_HELP_CONTENT.minimum;
1490
+ }
1491
+ if (normalized === "max") {
1492
+ return FIELD_HELP_CONTENT.maximum;
1493
+ }
1494
+ const direct = FIELD_HELP_CONTENT[normalized];
1495
+ if (direct) {
1496
+ return direct;
1497
+ }
1498
+ return {
1499
+ summary: "Defines behavior for this schema field.",
1500
+ details: "This control edits a JSON Schema keyword or related configuration for the current node. Set a value here to shape validation and annotations.",
1501
+ longDetails: "For exact semantics, check the draft 2020-12 specification section for this keyword and confirm whether your validator treats it as an assertion, annotation, or applicator.",
1502
+ link: DEFAULT_HELP_LINK
1503
+ };
1504
+ }
1505
+ var KEYWORD_DISPLAY_LABELS = {
1506
+ readonly: "readOnly",
1507
+ writeonly: "writeOnly",
1508
+ minlength: "minLength",
1509
+ maxlength: "maxLength",
1510
+ multipleof: "multipleOf",
1511
+ exclusiveminimum: "exclusiveMinimum",
1512
+ exclusivemaximum: "exclusiveMaximum",
1513
+ additionalproperties: "additionalProperties",
1514
+ unevaluatedproperties: "unevaluatedProperties",
1515
+ propertynames: "propertyNames",
1516
+ minproperties: "minProperties",
1517
+ maxproperties: "maxProperties",
1518
+ dependentrequired: "dependentRequired",
1519
+ dependentschemas: "dependentSchemas",
1520
+ patternproperties: "patternProperties",
1521
+ prefixitems: "prefixItems",
1522
+ minitems: "minItems",
1523
+ maxitems: "maxItems",
1524
+ uniqueitems: "uniqueItems",
1525
+ mincontains: "minContains",
1526
+ maxcontains: "maxContains",
1527
+ unevaluateditems: "unevaluatedItems",
1528
+ allof: "allOf",
1529
+ anyof: "anyOf",
1530
+ oneof: "oneOf"
1531
+ };
1532
+ function stripHtmlMarkup(text) {
1533
+ return text.replace(/<[^>]+>/g, "");
1534
+ }
1535
+ function toKeywordDisplayLabel(keyword) {
1536
+ return KEYWORD_DISPLAY_LABELS[keyword] ?? keyword;
1537
+ }
1538
+ function escapeRegExp(value) {
1539
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1540
+ }
1541
+ function uniqueQueryWords(input) {
1542
+ const words = input.toLowerCase().split(/[^$a-z0-9]+/g).filter((word) => word.length > 0);
1543
+ return Array.from(new Set(words));
1544
+ }
1545
+ function countOccurrences(text, query) {
1546
+ const normalizedText = text.toLowerCase();
1547
+ const normalizedQuery = query.toLowerCase();
1548
+ if (!normalizedQuery) {
1549
+ return 0;
1550
+ }
1551
+ let count = 0;
1552
+ let searchIndex = 0;
1553
+ while (searchIndex < normalizedText.length) {
1554
+ const matchIndex = normalizedText.indexOf(normalizedQuery, searchIndex);
1555
+ if (matchIndex < 0) {
1556
+ break;
1557
+ }
1558
+ count += 1;
1559
+ searchIndex = matchIndex + normalizedQuery.length;
1560
+ }
1561
+ return count;
1562
+ }
1563
+ function buildSnippet(text, words, targetLength = 180) {
1564
+ if (text.length <= targetLength) {
1565
+ return text;
1566
+ }
1567
+ const lowerText = text.toLowerCase();
1568
+ let bestIndex = -1;
1569
+ for (const word of words) {
1570
+ const index = lowerText.indexOf(word);
1571
+ if (index >= 0 && (bestIndex === -1 || index < bestIndex)) {
1572
+ bestIndex = index;
1573
+ }
1574
+ }
1575
+ const anchor = bestIndex >= 0 ? bestIndex : 0;
1576
+ const halfWindow = Math.floor(targetLength / 2);
1577
+ let start = Math.max(0, anchor - halfWindow);
1578
+ let end = Math.min(text.length, start + targetLength);
1579
+ if (end - start < targetLength) {
1580
+ start = Math.max(0, end - targetLength);
1581
+ }
1582
+ const rawSlice = text.slice(start, end).trim();
1583
+ const prefix = start > 0 ? "..." : "";
1584
+ const suffix = end < text.length ? "..." : "";
1585
+ return `${prefix}${rawSlice}${suffix}`;
1586
+ }
1587
+ function renderHighlightedSnippet(snippet, words) {
1588
+ if (words.length === 0) {
1589
+ return [snippet];
1590
+ }
1591
+ const pattern = new RegExp(`(${words.map((word) => escapeRegExp(word)).sort((a, b) => b.length - a.length).join("|")})`, "gi");
1592
+ const parts = snippet.split(pattern);
1593
+ const lowerWords = new Set(words.map((word) => word.toLowerCase()));
1594
+ return parts.map((part, index) => {
1595
+ if (part && lowerWords.has(part.toLowerCase())) {
1596
+ return /* @__PURE__ */ jsx12("strong", { children: part }, `snippet-part-${index}`);
1597
+ }
1598
+ return part;
1599
+ });
1600
+ }
1601
+ function resolveHelperContentEntries(helpContent) {
1602
+ if (!helpContent) {
1603
+ return Object.entries(FIELD_HELP_LONG_DETAILS).map(([keyword, longDetails]) => ({
1604
+ keyword,
1605
+ keywordLabel: toKeywordDisplayLabel(keyword),
1606
+ longDetails
1607
+ }));
1608
+ }
1609
+ const resolved = /* @__PURE__ */ new Map();
1610
+ for (const [rawKey, rawEntry] of Object.entries(helpContent)) {
1611
+ const keyword = normalizeHelpKey(rawKey);
1612
+ if (!keyword) {
1613
+ continue;
1614
+ }
1615
+ const entry = typeof rawEntry === "string" ? { longDetails: rawEntry } : {
1616
+ longDetails: rawEntry.longDetails,
1617
+ label: rawEntry.label
1618
+ };
1619
+ if (!entry.longDetails.trim()) {
1620
+ continue;
1621
+ }
1622
+ const fallbackLabel = keyword in FIELD_HELP_LONG_DETAILS ? toKeywordDisplayLabel(keyword) : rawKey.trim() || keyword;
1623
+ const keywordLabel = entry.label?.trim() || fallbackLabel;
1624
+ resolved.set(keyword, {
1625
+ keyword,
1626
+ keywordLabel,
1627
+ longDetails: entry.longDetails
1628
+ });
1629
+ }
1630
+ return Array.from(resolved.values());
1631
+ }
1632
+ function SchemaBuilderHelper({
1633
+ debounceMs = 280,
1634
+ maxResults = 12,
1635
+ placeholder = "Type to search schema keyword guidance...",
1636
+ initialQuery = "",
1637
+ helpContent
1638
+ }) {
1639
+ const [query, setQuery] = useState3(initialQuery);
1640
+ const [debouncedQuery, setDebouncedQuery] = useState3(initialQuery);
1641
+ const helperEntries = useMemo2(() => resolveHelperContentEntries(helpContent), [helpContent]);
1642
+ useEffect3(() => {
1643
+ const handle = window.setTimeout(() => {
1644
+ setDebouncedQuery(query);
1645
+ }, debounceMs);
1646
+ return () => {
1647
+ window.clearTimeout(handle);
1648
+ };
1649
+ }, [query, debounceMs]);
1650
+ const matches = useMemo2(() => {
1651
+ const words = uniqueQueryWords(debouncedQuery);
1652
+ if (words.length === 0) {
1653
+ return [];
1654
+ }
1655
+ const scored = helperEntries.map((entry) => {
1656
+ const plainText = stripHtmlMarkup(entry.longDetails);
1657
+ const normalized = plainText.toLowerCase();
1658
+ let matchedWords = 0;
1659
+ let weightedHits = 0;
1660
+ let totalHits = 0;
1661
+ for (const word of words) {
1662
+ const hits = countOccurrences(normalized, word);
1663
+ if (hits > 0) {
1664
+ matchedWords += 1;
1665
+ totalHits += hits;
1666
+ weightedHits += hits * Math.max(word.length, 1);
1667
+ }
1668
+ }
1669
+ if (matchedWords === 0) {
1670
+ return null;
1671
+ }
1672
+ const score = matchedWords * 100 + weightedHits * 10 + totalHits;
1673
+ return {
1674
+ keyword: entry.keyword,
1675
+ keywordLabel: entry.keywordLabel,
1676
+ snippet: buildSnippet(plainText, words),
1677
+ score
1678
+ };
1679
+ }).filter((entry) => entry !== null).sort((a, b) => b.score - a.score || a.keywordLabel.localeCompare(b.keywordLabel));
1680
+ return scored.slice(0, maxResults);
1681
+ }, [debouncedQuery, helperEntries, maxResults]);
1682
+ const highlightWords = useMemo2(() => uniqueQueryWords(debouncedQuery), [debouncedQuery]);
1683
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-helper-panel", children: [
1684
+ /* @__PURE__ */ jsx12("h3", { className: "raf-helper-title", children: "Keyword Assistant" }),
1685
+ /* @__PURE__ */ jsx12("div", { className: "raf-helper-subtitle", children: "Searches keyword guidance in long-form JSON Schema notes." }),
1686
+ /* @__PURE__ */ jsx12(
1687
+ "input",
1688
+ {
1689
+ className: "raf-input raf-builder-control",
1690
+ type: "text",
1691
+ value: query,
1692
+ onChange: (event) => setQuery(event.target.value),
1693
+ placeholder,
1694
+ "aria-label": "Search SchemaBuilder keyword help"
1695
+ }
1696
+ ),
1697
+ /* @__PURE__ */ jsx12("div", { className: "raf-helper-results", role: "region", "aria-label": "SchemaBuilder helper results", children: highlightWords.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "raf-helper-empty", children: "Start typing to see matching keyword snippets." }) : matches.length === 0 ? /* @__PURE__ */ jsx12("div", { className: "raf-helper-empty", children: "No matching keyword guidance found." }) : matches.map((match) => /* @__PURE__ */ jsxs8("article", { className: "raf-helper-snippet", children: [
1698
+ /* @__PURE__ */ jsx12("header", { className: "raf-helper-snippet-keyword", children: match.keywordLabel }),
1699
+ /* @__PURE__ */ jsx12("div", { className: "raf-helper-snippet-body", children: renderHighlightedSnippet(match.snippet, highlightWords) })
1700
+ ] }, match.keyword)) })
1701
+ ] });
1702
+ }
1703
+ function SchemaBuilder({ schema, domain, onChange }) {
1704
+ const [currentSchema, setCurrentSchema] = useState3(() => schema ?? createDefaultRootSchema());
1705
+ const [rawJson, setRawJson] = useState3(() => JSON.stringify(schema ?? createDefaultRootSchema(), null, 2));
1706
+ const [rawJsonError, setRawJsonError] = useState3(null);
1707
+ const [activeHelp, setActiveHelp] = useState3(null);
1708
+ const onChangeRef = useRef3(onChange);
1709
+ const publishedSchema = useMemo2(() => {
1710
+ const sanitized = sanitizeSchemaForOutput(currentSchema);
1711
+ return applyDomainToRootId(sanitized, domain);
1712
+ }, [currentSchema, domain]);
1713
+ useEffect3(() => {
1714
+ onChangeRef.current = onChange;
1715
+ }, [onChange]);
1716
+ useEffect3(() => {
1717
+ if (schema) {
1718
+ setCurrentSchema(cloneSchema(schema));
1719
+ }
1720
+ }, [schema]);
1721
+ useEffect3(() => {
1722
+ setRawJson(JSON.stringify(currentSchema, null, 2));
1723
+ const validationErrors = validateSchemaDefinition(publishedSchema);
1724
+ onChangeRef.current?.(publishedSchema, validationErrors);
1725
+ }, [currentSchema, publishedSchema]);
1726
+ const handleRootChange = (nextSchema) => {
1727
+ setCurrentSchema(nextSchema);
1728
+ };
1729
+ const openHelp = useCallback((key, label, anchor) => {
1730
+ setActiveHelp({ key, label, anchor });
1731
+ }, []);
1732
+ const closeHelp = useCallback(() => {
1733
+ setActiveHelp(null);
1734
+ }, []);
1735
+ const prettySchema = useMemo2(() => JSON.stringify(publishedSchema, null, 2), [publishedSchema]);
1736
+ return /* @__PURE__ */ jsxs8(FieldHelpContext.Provider, { value: { openHelp }, children: [
1737
+ /* @__PURE__ */ jsxs8("div", { className: "raf-schema-builder", children: [
1738
+ /* @__PURE__ */ jsx12(
1739
+ SchemaNodeEditor,
1740
+ {
1741
+ schema: currentSchema,
1742
+ label: "Root Schema",
1743
+ isRoot: true,
1744
+ domain,
1745
+ onChange: handleRootChange
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxs8("details", { className: "raf-object", children: [
1749
+ /* @__PURE__ */ jsx12("summary", { className: "raf-object-summary", children: "Advanced: Edit Full Schema JSON" }),
1750
+ /* @__PURE__ */ jsxs8("div", { className: "raf-object-content", children: [
1751
+ /* @__PURE__ */ jsx12(
1752
+ "textarea",
1753
+ {
1754
+ className: "raf-textarea",
1755
+ value: rawJson,
1756
+ onChange: (event) => {
1757
+ const nextText = event.target.value;
1758
+ setRawJson(nextText);
1759
+ try {
1760
+ const parsed = JSON.parse(nextText);
1761
+ if (!isObject3(parsed)) {
1762
+ const message = "Schema JSON must be an object.";
1763
+ setRawJsonError(message);
1764
+ onChangeRef.current?.(publishedSchema, [
1765
+ {
1766
+ message,
1767
+ source: "json-parse"
1768
+ }
1769
+ ]);
1770
+ return;
1771
+ }
1772
+ setRawJsonError(null);
1773
+ setCurrentSchema(parsed);
1774
+ } catch (error) {
1775
+ const message = error instanceof Error ? error.message : "Invalid JSON.";
1776
+ setRawJsonError(message);
1777
+ onChangeRef.current?.(publishedSchema, [
1778
+ {
1779
+ message,
1780
+ source: "json-parse"
1781
+ }
1782
+ ]);
1783
+ }
1784
+ }
1785
+ }
1786
+ ),
1787
+ rawJsonError ? /* @__PURE__ */ jsx12("div", { className: "raf-error", children: rawJsonError }) : null
1788
+ ] })
1789
+ ] }),
1790
+ /* @__PURE__ */ jsxs8("details", { className: "raf-object", children: [
1791
+ /* @__PURE__ */ jsx12("summary", { className: "raf-object-summary", children: "Preview JSON Schema" }),
1792
+ /* @__PURE__ */ jsx12("div", { className: "raf-object-content", children: /* @__PURE__ */ jsx12("pre", { className: "raf-json-preview", children: prettySchema }) })
1793
+ ] })
1794
+ ] }),
1795
+ /* @__PURE__ */ jsx12(FieldInfoModal, { activeHelp, onClose: closeHelp })
1796
+ ] });
1797
+ }
1798
+ function SchemaNodeEditor({
1799
+ schema,
1800
+ label,
1801
+ onChange,
1802
+ onRemove,
1803
+ isRoot = false,
1804
+ domain,
1805
+ primaryLeadField,
1806
+ primaryFollowField,
1807
+ showPrimaryTitle = isRoot
1808
+ }) {
1809
+ const schemaTypes = getSchemaTypes(schema);
1810
+ const primaryType = schemaTypes.length === 1 ? schemaTypes[0] : void 0;
1811
+ const hasType = (type) => schemaTypes.includes(type);
1812
+ const editableRootId = isRoot ? toLocalId(stringOrEmpty(schema.$id), domain) : stringOrEmpty(schema.$id);
1813
+ const fullRootId = isRoot ? toFullId(editableRootId, domain) : editableRootId;
1814
+ return /* @__PURE__ */ jsxs8("details", { className: "raf-object", open: true, children: [
1815
+ /* @__PURE__ */ jsx12("summary", { className: "raf-object-summary", children: label }),
1816
+ /* @__PURE__ */ jsxs8("div", { className: "raf-object-content", children: [
1817
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
1818
+ primaryLeadField,
1819
+ showPrimaryTitle ? /* @__PURE__ */ jsx12(
1820
+ TextInput,
1821
+ {
1822
+ label: "Title",
1823
+ keyword: "title",
1824
+ value: stringOrEmpty(schema.title),
1825
+ onChange: (value) => onChange(assignOptionalString(schema, "title", value))
1826
+ }
1827
+ ) : null,
1828
+ /* @__PURE__ */ jsx12(
1829
+ TypeListEditor,
1830
+ {
1831
+ value: schemaTypes,
1832
+ onChange: (nextTypes) => {
1833
+ const next = applyTypes(schema, nextTypes);
1834
+ delete next.const;
1835
+ delete next.enum;
1836
+ onChange(next);
1837
+ }
1838
+ }
1839
+ ),
1840
+ primaryFollowField
1841
+ ] }),
1842
+ hasType("object") ? /* @__PURE__ */ jsx12(ObjectSchemaEditor, { schema, onChange, showAdvancedOptions: false }) : null,
1843
+ /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "More Details", children: [
1844
+ /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "Documentation & References", defaultOpen: true, children: [
1845
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
1846
+ !showPrimaryTitle ? /* @__PURE__ */ jsx12(
1847
+ TextInput,
1848
+ {
1849
+ label: "Title",
1850
+ keyword: "title",
1851
+ value: stringOrEmpty(schema.title),
1852
+ onChange: (value) => onChange(assignOptionalString(schema, "title", value))
1853
+ }
1854
+ ) : null,
1855
+ /* @__PURE__ */ jsx12(
1856
+ TextInput,
1857
+ {
1858
+ label: "$ref",
1859
+ keyword: "$ref",
1860
+ value: stringOrEmpty(schema.$ref),
1861
+ onChange: (value) => onChange(assignOptionalString(schema, "$ref", value))
1862
+ }
1863
+ ),
1864
+ /* @__PURE__ */ jsx12(
1865
+ TextInput,
1866
+ {
1867
+ label: "$id",
1868
+ keyword: "$id",
1869
+ value: editableRootId,
1870
+ onChange: (value) => onChange(assignOptionalString(schema, "$id", isRoot ? toLocalId(value, domain) : value)),
1871
+ helperText: isRoot && domain ? `Full $id: ${fullRootId || "(empty)"}` : void 0
1872
+ }
1873
+ ),
1874
+ /* @__PURE__ */ jsx12(
1875
+ TextInput,
1876
+ {
1877
+ label: "$schema",
1878
+ keyword: "$schema",
1879
+ value: stringOrEmpty(schema.$schema),
1880
+ onChange: (value) => onChange(assignOptionalString(schema, "$schema", value)),
1881
+ placeholder: DEFAULT_SCHEMA_URI
1882
+ }
1883
+ )
1884
+ ] }),
1885
+ /* @__PURE__ */ jsx12(
1886
+ TextAreaInput,
1887
+ {
1888
+ label: "Description",
1889
+ keyword: "description",
1890
+ value: stringOrEmpty(schema.description),
1891
+ onChange: (value) => onChange(assignOptionalString(schema, "description", value))
1892
+ }
1893
+ )
1894
+ ] }),
1895
+ /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "Meta-data", defaultOpen: true, children: [
1896
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
1897
+ /* @__PURE__ */ jsx12(
1898
+ KeywordCheckbox,
1899
+ {
1900
+ label: "deprecated",
1901
+ keyword: "deprecated",
1902
+ checked: schema.deprecated === true,
1903
+ onChange: (checked) => {
1904
+ const next = cloneSchema(schema);
1905
+ if (checked) {
1906
+ next.deprecated = true;
1907
+ } else {
1908
+ delete next.deprecated;
1909
+ }
1910
+ onChange(next);
1911
+ }
1912
+ }
1913
+ ),
1914
+ /* @__PURE__ */ jsx12(
1915
+ KeywordCheckbox,
1916
+ {
1917
+ label: "readOnly",
1918
+ keyword: "readOnly",
1919
+ checked: schema.readOnly === true,
1920
+ onChange: (checked) => {
1921
+ const next = cloneSchema(schema);
1922
+ if (checked) {
1923
+ next.readOnly = true;
1924
+ } else {
1925
+ delete next.readOnly;
1926
+ }
1927
+ onChange(next);
1928
+ }
1929
+ }
1930
+ ),
1931
+ /* @__PURE__ */ jsx12(
1932
+ KeywordCheckbox,
1933
+ {
1934
+ label: "writeOnly",
1935
+ keyword: "writeOnly",
1936
+ checked: schema.writeOnly === true,
1937
+ onChange: (checked) => {
1938
+ const next = cloneSchema(schema);
1939
+ if (checked) {
1940
+ next.writeOnly = true;
1941
+ } else {
1942
+ delete next.writeOnly;
1943
+ }
1944
+ onChange(next);
1945
+ }
1946
+ }
1947
+ )
1948
+ ] }),
1949
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
1950
+ /* @__PURE__ */ jsx12(
1951
+ JsonTextInput,
1952
+ {
1953
+ label: "Default (JSON)",
1954
+ keyword: "default",
1955
+ value: schema.default,
1956
+ placeholder: 'e.g. "abc", 42, true, {"k":"v"}',
1957
+ onClear: () => {
1958
+ const next = cloneSchema(schema);
1959
+ delete next.default;
1960
+ onChange(next);
1961
+ },
1962
+ onValidJson: (parsed) => {
1963
+ onChange({ ...schema, default: parsed });
1964
+ }
1965
+ }
1966
+ ),
1967
+ /* @__PURE__ */ jsx12(
1968
+ JsonTextInput,
1969
+ {
1970
+ label: "examples",
1971
+ keyword: "examples",
1972
+ value: schema.examples,
1973
+ placeholder: 'e.g. ["sample", 1, true]',
1974
+ onClear: () => {
1975
+ const next = cloneSchema(schema);
1976
+ delete next.examples;
1977
+ onChange(next);
1978
+ },
1979
+ onValidJson: (parsed) => {
1980
+ if (!Array.isArray(parsed)) {
1981
+ return;
1982
+ }
1983
+ onChange({ ...schema, examples: parsed });
1984
+ }
1985
+ }
1986
+ )
1987
+ ] })
1988
+ ] }),
1989
+ /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "Value Rules", defaultOpen: true, children: [
1990
+ /* @__PURE__ */ jsx12(ConstEditor, { schema, schemaTypes, primaryType, onChange }),
1991
+ /* @__PURE__ */ jsx12(EnumEditor, { schema, schemaTypes, primaryType, onChange })
1992
+ ] }),
1993
+ hasType("string") ? /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "String Constraints", defaultOpen: true, children: [
1994
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
1995
+ /* @__PURE__ */ jsx12(
1996
+ TextInput,
1997
+ {
1998
+ label: "minLength",
1999
+ keyword: "minLength",
2000
+ value: numberOrEmpty(schema.minLength),
2001
+ onChange: (value) => onChange(assignOptionalInteger(schema, "minLength", value)),
2002
+ type: "number",
2003
+ step: "1",
2004
+ placeholder: "e.g. 1"
2005
+ }
2006
+ ),
2007
+ /* @__PURE__ */ jsx12(
2008
+ TextInput,
2009
+ {
2010
+ label: "maxLength",
2011
+ keyword: "maxLength",
2012
+ value: numberOrEmpty(schema.maxLength),
2013
+ onChange: (value) => onChange(assignOptionalInteger(schema, "maxLength", value)),
2014
+ type: "number",
2015
+ step: "1",
2016
+ placeholder: "e.g. 255"
2017
+ }
2018
+ )
2019
+ ] }),
2020
+ /* @__PURE__ */ jsx12(
2021
+ TextInput,
2022
+ {
2023
+ label: "Pattern",
2024
+ keyword: "pattern",
2025
+ value: stringOrEmpty(schema.pattern),
2026
+ onChange: (value) => onChange(assignOptionalString(schema, "pattern", value)),
2027
+ placeholder: "e.g. ^[A-Za-z]+$"
2028
+ }
2029
+ ),
2030
+ /* @__PURE__ */ jsx12(
2031
+ TypeaheadInput,
2032
+ {
2033
+ label: "format",
2034
+ keyword: "format",
2035
+ value: stringOrEmpty(schema.format),
2036
+ onChange: (value) => onChange(assignOptionalString(schema, "format", value)),
2037
+ options: AJV_SUPPORTED_FORMATS,
2038
+ placeholder: "e.g. email"
2039
+ }
2040
+ )
2041
+ ] }) : null,
2042
+ hasType("number") || hasType("integer") ? /* @__PURE__ */ jsxs8(BuilderDisclosureSection, { title: "Number Constraints", defaultOpen: true, children: [
2043
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2044
+ /* @__PURE__ */ jsx12(
2045
+ TextInput,
2046
+ {
2047
+ label: "Min",
2048
+ keyword: "minimum",
2049
+ value: numberOrEmpty(schema.minimum),
2050
+ onChange: (value) => onChange(assignOptionalNumber(schema, "minimum", value)),
2051
+ type: "number",
2052
+ step: "any",
2053
+ placeholder: "e.g. 0"
2054
+ }
2055
+ ),
2056
+ /* @__PURE__ */ jsx12(
2057
+ TextInput,
2058
+ {
2059
+ label: "Max",
2060
+ keyword: "maximum",
2061
+ value: numberOrEmpty(schema.maximum),
2062
+ onChange: (value) => onChange(assignOptionalNumber(schema, "maximum", value)),
2063
+ type: "number",
2064
+ step: "any",
2065
+ placeholder: "e.g. 100"
2066
+ }
2067
+ )
2068
+ ] }),
2069
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2070
+ /* @__PURE__ */ jsx12(
2071
+ TextInput,
2072
+ {
2073
+ label: "multipleOf",
2074
+ keyword: "multipleOf",
2075
+ value: numberOrEmpty(schema.multipleOf),
2076
+ onChange: (value) => onChange(assignOptionalPositiveNumber(schema, "multipleOf", value)),
2077
+ type: "number",
2078
+ step: "any",
2079
+ placeholder: "e.g. 0.5"
2080
+ }
2081
+ ),
2082
+ /* @__PURE__ */ jsx12(
2083
+ TextInput,
2084
+ {
2085
+ label: "exclusiveMinimum",
2086
+ keyword: "exclusiveMinimum",
2087
+ value: numberOrEmpty(schema.exclusiveMinimum),
2088
+ onChange: (value) => onChange(assignOptionalNumber(schema, "exclusiveMinimum", value)),
2089
+ type: "number",
2090
+ step: "any",
2091
+ placeholder: "e.g. 0"
2092
+ }
2093
+ ),
2094
+ /* @__PURE__ */ jsx12(
2095
+ TextInput,
2096
+ {
2097
+ label: "exclusiveMaximum",
2098
+ keyword: "exclusiveMaximum",
2099
+ value: numberOrEmpty(schema.exclusiveMaximum),
2100
+ onChange: (value) => onChange(assignOptionalNumber(schema, "exclusiveMaximum", value)),
2101
+ type: "number",
2102
+ step: "any",
2103
+ placeholder: "e.g. 100"
2104
+ }
2105
+ )
2106
+ ] })
2107
+ ] }) : null,
2108
+ hasType("array") ? /* @__PURE__ */ jsx12(BuilderDisclosureSection, { title: "Array Rules", defaultOpen: true, children: /* @__PURE__ */ jsx12(ArraySchemaEditor, { schema, onChange }) }) : null,
2109
+ hasType("object") ? /* @__PURE__ */ jsx12(BuilderDisclosureSection, { title: "Object Rules", defaultOpen: true, children: /* @__PURE__ */ jsx12(ObjectSchemaAdvancedEditor, { schema, onChange }) }) : null,
2110
+ /* @__PURE__ */ jsx12(BuilderDisclosureSection, { title: "Composition & Conditionals", defaultOpen: true, children: /* @__PURE__ */ jsxs8("div", { className: "raf-builder-section-group", children: [
2111
+ /* @__PURE__ */ jsx12(CombinationEditor, { kind: "allOf", schema, onChange }),
2112
+ /* @__PURE__ */ jsx12(CombinationEditor, { kind: "anyOf", schema, onChange }),
2113
+ /* @__PURE__ */ jsx12(CombinationEditor, { kind: "oneOf", schema, onChange }),
2114
+ /* @__PURE__ */ jsx12(SingleSchemaEditor, { kind: "not", schema, onChange }),
2115
+ /* @__PURE__ */ jsx12(ConditionalSchemaEditor, { kind: "if", schema, onChange }),
2116
+ /* @__PURE__ */ jsx12(ConditionalSchemaEditor, { kind: "then", schema, onChange }),
2117
+ /* @__PURE__ */ jsx12(ConditionalSchemaEditor, { kind: "else", schema, onChange })
2118
+ ] }) })
2119
+ ] }),
2120
+ !isRoot && onRemove ? /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12("button", { className: "raf-button raf-button-danger", type: "button", onClick: onRemove, children: "Remove Field" }) }) : null
2121
+ ] })
2122
+ ] });
2123
+ }
2124
+ function BuilderDisclosureSection({
2125
+ title,
2126
+ children,
2127
+ defaultOpen = false
2128
+ }) {
2129
+ return /* @__PURE__ */ jsxs8("details", { className: "raf-builder-disclosure", open: defaultOpen, children: [
2130
+ /* @__PURE__ */ jsx12("summary", { className: "raf-builder-disclosure-summary", children: title }),
2131
+ /* @__PURE__ */ jsx12("div", { className: "raf-builder-disclosure-content", children })
2132
+ ] });
2133
+ }
2134
+ function ObjectSchemaEditor({
2135
+ schema,
2136
+ onChange,
2137
+ showAdvancedOptions = true
2138
+ }) {
2139
+ const properties = schema.properties ?? {};
2140
+ const requiredSet = new Set(schema.required ?? []);
2141
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2142
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-heading-wrap", children: [
2143
+ /* @__PURE__ */ jsx12("h4", { className: "raf-builder-heading", children: "Properties" }),
2144
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "properties", keyword: "properties" })
2145
+ ] }),
2146
+ Object.entries(properties).map(([propertyName, propertySchema], index) => /* @__PURE__ */ jsx12("div", { className: "raf-builder-property", children: /* @__PURE__ */ jsx12(
2147
+ SchemaNodeEditor,
2148
+ {
2149
+ label: `Property: ${propertyName}`,
2150
+ schema: propertySchema,
2151
+ showPrimaryTitle: false,
2152
+ primaryLeadField: /* @__PURE__ */ jsx12(
2153
+ TextInput,
2154
+ {
2155
+ label: "Property Name",
2156
+ keyword: "properties",
2157
+ value: propertyName,
2158
+ onChange: (nextName) => {
2159
+ const normalized = nextName.trim();
2160
+ if (normalized === propertyName) {
2161
+ return;
2162
+ }
2163
+ if (normalized === "") {
2164
+ const next2 = cloneSchema(schema);
2165
+ const objectProperties2 = { ...next2.properties ?? {} };
2166
+ objectProperties2[""] = objectProperties2[propertyName];
2167
+ if (propertyName !== "") {
2168
+ delete objectProperties2[propertyName];
2169
+ }
2170
+ next2.properties = objectProperties2;
2171
+ next2.required = (next2.required ?? []).filter((entry) => entry !== propertyName);
2172
+ onChange(next2);
2173
+ return;
2174
+ }
2175
+ const next = cloneSchema(schema);
2176
+ const objectProperties = { ...next.properties ?? {} };
2177
+ if (objectProperties[normalized]) {
2178
+ return;
2179
+ }
2180
+ objectProperties[normalized] = objectProperties[propertyName];
2181
+ delete objectProperties[propertyName];
2182
+ next.properties = objectProperties;
2183
+ const required = new Set(next.required ?? []);
2184
+ if (required.delete(propertyName)) {
2185
+ required.add(normalized);
2186
+ next.required = Array.from(required);
2187
+ }
2188
+ onChange(next);
2189
+ }
2190
+ }
2191
+ ),
2192
+ primaryFollowField: /* @__PURE__ */ jsx12(
2193
+ KeywordCheckbox,
2194
+ {
2195
+ label: "Required",
2196
+ keyword: "required",
2197
+ checked: requiredSet.has(propertyName),
2198
+ onChange: (checked) => {
2199
+ const next = cloneSchema(schema);
2200
+ const required = new Set(next.required ?? []);
2201
+ if (checked) {
2202
+ required.add(propertyName);
2203
+ } else {
2204
+ required.delete(propertyName);
2205
+ }
2206
+ next.required = Array.from(required);
2207
+ onChange(next);
2208
+ }
2209
+ }
2210
+ ),
2211
+ onChange: (nextPropertySchema) => {
2212
+ const next = cloneSchema(schema);
2213
+ next.properties = { ...next.properties ?? {}, [propertyName]: nextPropertySchema };
2214
+ onChange(next);
2215
+ },
2216
+ onRemove: () => {
2217
+ const next = cloneSchema(schema);
2218
+ const objectProperties = { ...next.properties ?? {} };
2219
+ delete objectProperties[propertyName];
2220
+ next.properties = objectProperties;
2221
+ next.required = (next.required ?? []).filter((entry) => entry !== propertyName);
2222
+ onChange(next);
2223
+ }
2224
+ }
2225
+ ) }, index)),
2226
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2227
+ "button",
2228
+ {
2229
+ className: "raf-button raf-button-primary",
2230
+ type: "button",
2231
+ onClick: () => {
2232
+ const next = cloneSchema(schema);
2233
+ next.properties = { ...next.properties ?? {} };
2234
+ const newKey = createUniquePropertyName(next.properties, "field");
2235
+ next.properties[newKey] = { type: "string" };
2236
+ onChange(next);
2237
+ },
2238
+ children: "Add Property"
2239
+ }
2240
+ ) }),
2241
+ showAdvancedOptions ? /* @__PURE__ */ jsx12(ObjectSchemaAdvancedEditor, { schema, onChange }) : null
2242
+ ] });
2243
+ }
2244
+ function ObjectSchemaAdvancedEditor({ schema, onChange }) {
2245
+ const dependentRequired = schema.dependentRequired ?? {};
2246
+ const dependentSchemas = schema.dependentSchemas ?? {};
2247
+ const patternProperties = schema.patternProperties ?? {};
2248
+ const additionalPropertiesSchema = isObject3(schema.additionalProperties) ? schema.additionalProperties : void 0;
2249
+ const additionalPropertiesMode = additionalPropertiesSchema ? "subschema" : schema.additionalProperties === false ? "false" : "true";
2250
+ const propertyNamesSchema = isObject3(schema.propertyNames) ? schema.propertyNames : void 0;
2251
+ const unevaluatedPropertiesSchema = isObject3(schema.unevaluatedProperties) ? schema.unevaluatedProperties : void 0;
2252
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
2253
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2254
+ /* @__PURE__ */ jsx12(
2255
+ TextInput,
2256
+ {
2257
+ label: "minProperties",
2258
+ keyword: "minProperties",
2259
+ value: numberOrEmpty(schema.minProperties),
2260
+ onChange: (value) => onChange(assignOptionalInteger(schema, "minProperties", value)),
2261
+ type: "number",
2262
+ step: "1",
2263
+ placeholder: "e.g. 0"
2264
+ }
2265
+ ),
2266
+ /* @__PURE__ */ jsx12(
2267
+ TextInput,
2268
+ {
2269
+ label: "maxProperties",
2270
+ keyword: "maxProperties",
2271
+ value: numberOrEmpty(schema.maxProperties),
2272
+ onChange: (value) => onChange(assignOptionalInteger(schema, "maxProperties", value)),
2273
+ type: "number",
2274
+ step: "1",
2275
+ placeholder: "e.g. 10"
2276
+ }
2277
+ )
2278
+ ] }),
2279
+ /* @__PURE__ */ jsx12(
2280
+ SelectInput,
2281
+ {
2282
+ label: "additionalProperties",
2283
+ keyword: "additionalProperties",
2284
+ value: additionalPropertiesMode,
2285
+ onChange: (value) => {
2286
+ const next = cloneSchema(schema);
2287
+ if (value === "subschema") {
2288
+ next.additionalProperties = isObject3(next.additionalProperties) ? next.additionalProperties : { type: "string" };
2289
+ } else if (value === "false") {
2290
+ next.additionalProperties = false;
2291
+ } else {
2292
+ next.additionalProperties = true;
2293
+ }
2294
+ onChange(next);
2295
+ },
2296
+ options: [
2297
+ { value: "true", label: "True" },
2298
+ { value: "false", label: "False" },
2299
+ { value: "subschema", label: "Sub-schema" }
2300
+ ]
2301
+ }
2302
+ ),
2303
+ additionalPropertiesSchema ? /* @__PURE__ */ jsx12(
2304
+ SchemaNodeEditor,
2305
+ {
2306
+ label: "additionalProperties",
2307
+ schema: additionalPropertiesSchema,
2308
+ onChange: (nextAdditionalPropertiesSchema) => {
2309
+ const next = cloneSchema(schema);
2310
+ next.additionalProperties = nextAdditionalPropertiesSchema;
2311
+ onChange(next);
2312
+ },
2313
+ onRemove: () => {
2314
+ const next = cloneSchema(schema);
2315
+ next.additionalProperties = true;
2316
+ onChange(next);
2317
+ }
2318
+ }
2319
+ ) : null,
2320
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2321
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "unevaluatedProperties", keyword: "unevaluatedProperties", labelType: "heading" }),
2322
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !unevaluatedPropertiesSchema ? /* @__PURE__ */ jsx12(
2323
+ "button",
2324
+ {
2325
+ className: "raf-button raf-button-secondary",
2326
+ type: "button",
2327
+ onClick: () => {
2328
+ const next = cloneSchema(schema);
2329
+ next.unevaluatedProperties = { type: "string" };
2330
+ onChange(next);
2331
+ },
2332
+ children: "Add unevaluatedProperties"
2333
+ }
2334
+ ) : /* @__PURE__ */ jsx12(
2335
+ "button",
2336
+ {
2337
+ className: "raf-button raf-button-danger",
2338
+ type: "button",
2339
+ onClick: () => {
2340
+ const next = cloneSchema(schema);
2341
+ delete next.unevaluatedProperties;
2342
+ onChange(next);
2343
+ },
2344
+ children: "Clear unevaluatedProperties"
2345
+ }
2346
+ ) }),
2347
+ unevaluatedPropertiesSchema ? /* @__PURE__ */ jsx12(
2348
+ SchemaNodeEditor,
2349
+ {
2350
+ label: "unevaluatedProperties",
2351
+ schema: unevaluatedPropertiesSchema,
2352
+ onChange: (nextUnevaluatedPropertiesSchema) => {
2353
+ const next = cloneSchema(schema);
2354
+ next.unevaluatedProperties = nextUnevaluatedPropertiesSchema;
2355
+ onChange(next);
2356
+ },
2357
+ onRemove: () => {
2358
+ const next = cloneSchema(schema);
2359
+ delete next.unevaluatedProperties;
2360
+ onChange(next);
2361
+ }
2362
+ }
2363
+ ) : null
2364
+ ] }),
2365
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2366
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "propertyNames", keyword: "propertyNames", labelType: "heading" }),
2367
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !propertyNamesSchema ? /* @__PURE__ */ jsx12(
2368
+ "button",
2369
+ {
2370
+ className: "raf-button raf-button-secondary",
2371
+ type: "button",
2372
+ onClick: () => {
2373
+ const next = cloneSchema(schema);
2374
+ next.propertyNames = { type: "string" };
2375
+ onChange(next);
2376
+ },
2377
+ children: "Add propertyNames"
2378
+ }
2379
+ ) : /* @__PURE__ */ jsx12(
2380
+ "button",
2381
+ {
2382
+ className: "raf-button raf-button-danger",
2383
+ type: "button",
2384
+ onClick: () => {
2385
+ const next = cloneSchema(schema);
2386
+ delete next.propertyNames;
2387
+ onChange(next);
2388
+ },
2389
+ children: "Clear propertyNames"
2390
+ }
2391
+ ) }),
2392
+ propertyNamesSchema ? /* @__PURE__ */ jsx12(
2393
+ SchemaNodeEditor,
2394
+ {
2395
+ label: "propertyNames",
2396
+ schema: propertyNamesSchema,
2397
+ onChange: (nextPropertyNamesSchema) => {
2398
+ const next = cloneSchema(schema);
2399
+ next.propertyNames = nextPropertyNamesSchema;
2400
+ onChange(next);
2401
+ },
2402
+ onRemove: () => {
2403
+ const next = cloneSchema(schema);
2404
+ delete next.propertyNames;
2405
+ onChange(next);
2406
+ }
2407
+ }
2408
+ ) : null
2409
+ ] }),
2410
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2411
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "dependentRequired", keyword: "dependentRequired", labelType: "heading" }),
2412
+ Object.entries(dependentRequired).map(([propertyName, dependencies], index) => {
2413
+ const serializedDependencies = Array.isArray(dependencies) ? dependencies.join(", ") : "";
2414
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-property", children: [
2415
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2416
+ /* @__PURE__ */ jsx12(
2417
+ TextInput,
2418
+ {
2419
+ label: "Dependent Property",
2420
+ keyword: "dependentRequired",
2421
+ value: propertyName,
2422
+ onChange: (nextName) => {
2423
+ const normalized = nextName.trim();
2424
+ if (normalized === propertyName) {
2425
+ return;
2426
+ }
2427
+ const next = cloneSchema(schema);
2428
+ const nextEntries = { ...next.dependentRequired ?? {} };
2429
+ const existingValue = nextEntries[propertyName] ?? [];
2430
+ if (normalized === "") {
2431
+ delete nextEntries[propertyName];
2432
+ next.dependentRequired = nextEntries;
2433
+ onChange(next);
2434
+ return;
2435
+ }
2436
+ if (Object.prototype.hasOwnProperty.call(nextEntries, normalized)) {
2437
+ return;
2438
+ }
2439
+ nextEntries[normalized] = Array.isArray(existingValue) ? existingValue : [];
2440
+ delete nextEntries[propertyName];
2441
+ next.dependentRequired = nextEntries;
2442
+ onChange(next);
2443
+ }
2444
+ }
2445
+ ),
2446
+ /* @__PURE__ */ jsx12(
2447
+ TextInput,
2448
+ {
2449
+ label: "Required Properties (comma-separated)",
2450
+ keyword: "dependentRequired",
2451
+ value: serializedDependencies,
2452
+ onChange: (nextValue) => {
2453
+ const next = cloneSchema(schema);
2454
+ const nextEntries = { ...next.dependentRequired ?? {} };
2455
+ nextEntries[propertyName] = parseCommaSeparatedStrings(nextValue);
2456
+ next.dependentRequired = nextEntries;
2457
+ onChange(next);
2458
+ }
2459
+ }
2460
+ )
2461
+ ] }),
2462
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2463
+ "button",
2464
+ {
2465
+ className: "raf-button raf-button-danger",
2466
+ type: "button",
2467
+ onClick: () => {
2468
+ const next = cloneSchema(schema);
2469
+ const nextEntries = { ...next.dependentRequired ?? {} };
2470
+ delete nextEntries[propertyName];
2471
+ next.dependentRequired = nextEntries;
2472
+ onChange(next);
2473
+ },
2474
+ children: "Remove dependentRequired Entry"
2475
+ }
2476
+ ) })
2477
+ ] }, `dependent-required-${index}`);
2478
+ }),
2479
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2480
+ "button",
2481
+ {
2482
+ className: "raf-button raf-button-secondary",
2483
+ type: "button",
2484
+ onClick: () => {
2485
+ const next = cloneSchema(schema);
2486
+ const nextEntries = { ...next.dependentRequired ?? {} };
2487
+ const newKey = createUniqueEntryName(nextEntries, "field");
2488
+ nextEntries[newKey] = [];
2489
+ next.dependentRequired = nextEntries;
2490
+ onChange(next);
2491
+ },
2492
+ children: "Add dependentRequired Entry"
2493
+ }
2494
+ ) })
2495
+ ] }),
2496
+ /* @__PURE__ */ jsx12(
2497
+ SchemaMapEditor,
2498
+ {
2499
+ title: "dependentSchemas",
2500
+ addButtonLabel: "Add dependentSchemas Entry",
2501
+ removeButtonLabel: "Remove dependentSchemas Entry",
2502
+ schemaMap: dependentSchemas,
2503
+ defaultSchemaFactory: () => ({ type: "object", properties: {} }),
2504
+ onChange: (nextMap) => {
2505
+ const next = cloneSchema(schema);
2506
+ next.dependentSchemas = nextMap;
2507
+ onChange(next);
2508
+ }
2509
+ }
2510
+ ),
2511
+ /* @__PURE__ */ jsx12(
2512
+ SchemaMapEditor,
2513
+ {
2514
+ title: "patternProperties",
2515
+ addButtonLabel: "Add patternProperties Entry",
2516
+ removeButtonLabel: "Remove patternProperties Entry",
2517
+ schemaMap: patternProperties,
2518
+ defaultSchemaFactory: () => ({ type: "string" }),
2519
+ onChange: (nextMap) => {
2520
+ const next = cloneSchema(schema);
2521
+ next.patternProperties = nextMap;
2522
+ onChange(next);
2523
+ }
2524
+ }
2525
+ )
2526
+ ] });
2527
+ }
2528
+ function ArraySchemaEditor({ schema, onChange }) {
2529
+ const tupleItems = Array.isArray(schema.prefixItems) ? schema.prefixItems : Array.isArray(schema.items) ? schema.items : void 0;
2530
+ const items = !Array.isArray(schema.items) && isObject3(schema.items) ? schema.items : void 0;
2531
+ const hasContains = isObject3(schema.contains);
2532
+ const unevaluatedItemsSchema = isObject3(schema.unevaluatedItems) ? schema.unevaluatedItems : void 0;
2533
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2534
+ tupleItems ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
2535
+ /* @__PURE__ */ jsx12("h4", { className: "raf-builder-heading", children: "Array Items (Tuple)" }),
2536
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "prefixItems", keyword: "prefixItems" }),
2537
+ tupleItems.map((itemSchema, index) => /* @__PURE__ */ jsx12(
2538
+ SchemaNodeEditor,
2539
+ {
2540
+ label: `Tuple Item ${index + 1}`,
2541
+ schema: itemSchema,
2542
+ onChange: (nextItemSchema) => {
2543
+ const next = cloneSchema(schema);
2544
+ const nextItems = Array.isArray(next.prefixItems) ? [...next.prefixItems] : Array.isArray(next.items) ? [...next.items] : [];
2545
+ nextItems[index] = nextItemSchema;
2546
+ next.prefixItems = nextItems;
2547
+ next.items = false;
2548
+ onChange(next);
2549
+ },
2550
+ onRemove: () => {
2551
+ const next = cloneSchema(schema);
2552
+ const nextItems = Array.isArray(next.prefixItems) ? [...next.prefixItems] : Array.isArray(next.items) ? [...next.items] : [];
2553
+ nextItems.splice(index, 1);
2554
+ next.prefixItems = nextItems;
2555
+ next.items = false;
2556
+ onChange(next);
2557
+ }
2558
+ },
2559
+ `tuple-item-${index}`
2560
+ )),
2561
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2562
+ "button",
2563
+ {
2564
+ className: "raf-button raf-button-primary",
2565
+ type: "button",
2566
+ onClick: () => {
2567
+ const next = cloneSchema(schema);
2568
+ const nextItems = Array.isArray(next.prefixItems) ? [...next.prefixItems] : Array.isArray(next.items) ? [...next.items] : [];
2569
+ nextItems.push({ type: "string" });
2570
+ next.prefixItems = nextItems;
2571
+ next.items = false;
2572
+ onChange(next);
2573
+ },
2574
+ children: "Add Tuple Item Schema"
2575
+ }
2576
+ ) }),
2577
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2578
+ "button",
2579
+ {
2580
+ className: "raf-button raf-button-secondary",
2581
+ type: "button",
2582
+ onClick: () => {
2583
+ const next = cloneSchema(schema);
2584
+ next.items = { type: "string" };
2585
+ delete next.prefixItems;
2586
+ onChange(next);
2587
+ },
2588
+ children: "Switch To Single Items Schema"
2589
+ }
2590
+ ) })
2591
+ ] }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
2592
+ /* @__PURE__ */ jsx12("h4", { className: "raf-builder-heading", children: "Array Items" }),
2593
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "items", keyword: "items" }),
2594
+ /* @__PURE__ */ jsx12(
2595
+ SchemaNodeEditor,
2596
+ {
2597
+ label: "Items Schema",
2598
+ schema: isObject3(items) ? items : { type: "string" },
2599
+ onChange: (nextItemsSchema) => {
2600
+ const next = cloneSchema(schema);
2601
+ next.items = nextItemsSchema;
2602
+ delete next.prefixItems;
2603
+ onChange(next);
2604
+ }
2605
+ }
2606
+ ),
2607
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
2608
+ "button",
2609
+ {
2610
+ className: "raf-button raf-button-secondary",
2611
+ type: "button",
2612
+ onClick: () => {
2613
+ const next = cloneSchema(schema);
2614
+ next.prefixItems = [{ type: "string" }];
2615
+ next.items = false;
2616
+ onChange(next);
2617
+ },
2618
+ children: "Switch To Tuple Items"
2619
+ }
2620
+ ) })
2621
+ ] }),
2622
+ /* @__PURE__ */ jsx12("h4", { className: "raf-builder-heading", children: "Array Constraints" }),
2623
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2624
+ /* @__PURE__ */ jsx12(
2625
+ TextInput,
2626
+ {
2627
+ label: "minItems",
2628
+ keyword: "minItems",
2629
+ value: numberOrEmpty(schema.minItems),
2630
+ onChange: (value) => onChange(assignOptionalInteger(schema, "minItems", value)),
2631
+ type: "number",
2632
+ step: "1",
2633
+ placeholder: "e.g. 0"
2634
+ }
2635
+ ),
2636
+ /* @__PURE__ */ jsx12(
2637
+ TextInput,
2638
+ {
2639
+ label: "maxItems",
2640
+ keyword: "maxItems",
2641
+ value: numberOrEmpty(schema.maxItems),
2642
+ onChange: (value) => onChange(assignOptionalInteger(schema, "maxItems", value)),
2643
+ type: "number",
2644
+ step: "1",
2645
+ placeholder: "e.g. 10"
2646
+ }
2647
+ )
2648
+ ] }),
2649
+ /* @__PURE__ */ jsx12(
2650
+ KeywordCheckbox,
2651
+ {
2652
+ label: "uniqueItems",
2653
+ keyword: "uniqueItems",
2654
+ checked: Boolean(schema.uniqueItems),
2655
+ onChange: (checked) => {
2656
+ const next = cloneSchema(schema);
2657
+ if (checked) {
2658
+ next.uniqueItems = true;
2659
+ } else {
2660
+ delete next.uniqueItems;
2661
+ }
2662
+ onChange(next);
2663
+ }
2664
+ }
2665
+ ),
2666
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !hasContains ? /* @__PURE__ */ jsx12(
2667
+ "button",
2668
+ {
2669
+ className: "raf-button raf-button-secondary",
2670
+ type: "button",
2671
+ onClick: () => {
2672
+ const next = cloneSchema(schema);
2673
+ next.contains = { type: "string" };
2674
+ onChange(next);
2675
+ },
2676
+ children: "Add contains"
2677
+ }
2678
+ ) : /* @__PURE__ */ jsx12(
2679
+ "button",
2680
+ {
2681
+ className: "raf-button raf-button-danger",
2682
+ type: "button",
2683
+ onClick: () => {
2684
+ const next = cloneSchema(schema);
2685
+ delete next.contains;
2686
+ delete next.minContains;
2687
+ delete next.maxContains;
2688
+ onChange(next);
2689
+ },
2690
+ children: "Remove contains"
2691
+ }
2692
+ ) }),
2693
+ hasContains ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
2694
+ /* @__PURE__ */ jsx12(
2695
+ SchemaNodeEditor,
2696
+ {
2697
+ label: "contains",
2698
+ schema: schema.contains,
2699
+ onChange: (nextContainsSchema) => {
2700
+ const next = cloneSchema(schema);
2701
+ next.contains = nextContainsSchema;
2702
+ onChange(next);
2703
+ }
2704
+ }
2705
+ ),
2706
+ /* @__PURE__ */ jsxs8("div", { className: "raf-builder-grid", children: [
2707
+ /* @__PURE__ */ jsx12(
2708
+ TextInput,
2709
+ {
2710
+ label: "minContains",
2711
+ keyword: "minContains",
2712
+ value: numberOrEmpty(schema.minContains),
2713
+ onChange: (value) => onChange(assignOptionalInteger(schema, "minContains", value)),
2714
+ type: "number",
2715
+ step: "1",
2716
+ placeholder: "e.g. 1"
2717
+ }
2718
+ ),
2719
+ /* @__PURE__ */ jsx12(
2720
+ TextInput,
2721
+ {
2722
+ label: "maxContains",
2723
+ keyword: "maxContains",
2724
+ value: numberOrEmpty(schema.maxContains),
2725
+ onChange: (value) => onChange(assignOptionalInteger(schema, "maxContains", value)),
2726
+ type: "number",
2727
+ step: "1",
2728
+ placeholder: "e.g. 3"
2729
+ }
2730
+ )
2731
+ ] })
2732
+ ] }) : null,
2733
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !unevaluatedItemsSchema ? /* @__PURE__ */ jsx12(
2734
+ "button",
2735
+ {
2736
+ className: "raf-button raf-button-secondary",
2737
+ type: "button",
2738
+ onClick: () => {
2739
+ const next = cloneSchema(schema);
2740
+ next.unevaluatedItems = { type: "string" };
2741
+ onChange(next);
2742
+ },
2743
+ children: "Add unevaluatedItems"
2744
+ }
2745
+ ) : /* @__PURE__ */ jsx12(
2746
+ "button",
2747
+ {
2748
+ className: "raf-button raf-button-danger",
2749
+ type: "button",
2750
+ onClick: () => {
2751
+ const next = cloneSchema(schema);
2752
+ delete next.unevaluatedItems;
2753
+ onChange(next);
2754
+ },
2755
+ children: "Clear unevaluatedItems"
2756
+ }
2757
+ ) }),
2758
+ unevaluatedItemsSchema ? /* @__PURE__ */ jsx12(
2759
+ SchemaNodeEditor,
2760
+ {
2761
+ label: "unevaluatedItems",
2762
+ schema: unevaluatedItemsSchema,
2763
+ onChange: (nextUnevaluatedItemsSchema) => {
2764
+ const next = cloneSchema(schema);
2765
+ next.unevaluatedItems = nextUnevaluatedItemsSchema;
2766
+ onChange(next);
2767
+ },
2768
+ onRemove: () => {
2769
+ const next = cloneSchema(schema);
2770
+ delete next.unevaluatedItems;
2771
+ onChange(next);
2772
+ }
2773
+ }
2774
+ ) : null
2775
+ ] });
2776
+ }
2777
+ function CombinationEditor({
2778
+ kind,
2779
+ schema,
2780
+ onChange
2781
+ }) {
2782
+ const entries = Array.isArray(schema[kind]) ? schema[kind] : [];
2783
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2784
+ /* @__PURE__ */ jsx12(FieldLabel, { label: kind, keyword: kind, labelType: "heading" }),
2785
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: entries.length > 0 ? /* @__PURE__ */ jsxs8(
2786
+ "button",
2787
+ {
2788
+ className: "raf-button raf-button-secondary",
2789
+ type: "button",
2790
+ onClick: () => {
2791
+ const next = cloneSchema(schema);
2792
+ delete next[kind];
2793
+ onChange(next);
2794
+ },
2795
+ children: [
2796
+ "Clear ",
2797
+ kind
2798
+ ]
2799
+ }
2800
+ ) : null }),
2801
+ entries.map((entrySchema, index) => /* @__PURE__ */ jsx12(
2802
+ SchemaNodeEditor,
2803
+ {
2804
+ label: `${kind}[${index}]`,
2805
+ schema: entrySchema,
2806
+ onChange: (nextEntrySchema) => {
2807
+ const next = cloneSchema(schema);
2808
+ const currentEntries = Array.isArray(next[kind]) ? next[kind] : [];
2809
+ currentEntries[index] = nextEntrySchema;
2810
+ next[kind] = currentEntries;
2811
+ onChange(next);
2812
+ },
2813
+ onRemove: () => {
2814
+ const next = cloneSchema(schema);
2815
+ const currentEntries = Array.isArray(next[kind]) ? next[kind] : [];
2816
+ currentEntries.splice(index, 1);
2817
+ next[kind] = currentEntries;
2818
+ onChange(next);
2819
+ }
2820
+ },
2821
+ `${kind}-${index}`
2822
+ )),
2823
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsxs8(
2824
+ "button",
2825
+ {
2826
+ className: "raf-button raf-button-primary",
2827
+ type: "button",
2828
+ onClick: () => {
2829
+ const next = cloneSchema(schema);
2830
+ const currentEntries = Array.isArray(next[kind]) ? next[kind] : [];
2831
+ next[kind] = [...currentEntries, { type: "string" }];
2832
+ onChange(next);
2833
+ },
2834
+ children: [
2835
+ "Add ",
2836
+ kind,
2837
+ " Entry"
2838
+ ]
2839
+ }
2840
+ ) })
2841
+ ] });
2842
+ }
2843
+ function ConditionalSchemaEditor({
2844
+ kind,
2845
+ schema,
2846
+ onChange
2847
+ }) {
2848
+ const entry = isObject3(schema[kind]) ? schema[kind] : void 0;
2849
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2850
+ /* @__PURE__ */ jsx12(FieldLabel, { label: kind, keyword: kind, labelType: "heading" }),
2851
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !entry ? /* @__PURE__ */ jsxs8(
2852
+ "button",
2853
+ {
2854
+ className: "raf-button raf-button-primary",
2855
+ type: "button",
2856
+ onClick: () => {
2857
+ const next = cloneSchema(schema);
2858
+ next[kind] = { type: "string" };
2859
+ onChange(next);
2860
+ },
2861
+ children: [
2862
+ "Add ",
2863
+ kind
2864
+ ]
2865
+ }
2866
+ ) : /* @__PURE__ */ jsxs8(
2867
+ "button",
2868
+ {
2869
+ className: "raf-button raf-button-secondary",
2870
+ type: "button",
2871
+ onClick: () => {
2872
+ const next = cloneSchema(schema);
2873
+ delete next[kind];
2874
+ onChange(next);
2875
+ },
2876
+ children: [
2877
+ "Clear ",
2878
+ kind
2879
+ ]
2880
+ }
2881
+ ) }),
2882
+ entry ? /* @__PURE__ */ jsx12(
2883
+ SchemaNodeEditor,
2884
+ {
2885
+ label: kind,
2886
+ schema: entry,
2887
+ onChange: (nextEntrySchema) => {
2888
+ const next = cloneSchema(schema);
2889
+ next[kind] = nextEntrySchema;
2890
+ onChange(next);
2891
+ },
2892
+ onRemove: () => {
2893
+ const next = cloneSchema(schema);
2894
+ delete next[kind];
2895
+ onChange(next);
2896
+ }
2897
+ }
2898
+ ) : null
2899
+ ] });
2900
+ }
2901
+ function SingleSchemaEditor({
2902
+ kind,
2903
+ schema,
2904
+ onChange
2905
+ }) {
2906
+ const entry = isObject3(schema[kind]) ? schema[kind] : void 0;
2907
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2908
+ /* @__PURE__ */ jsx12(FieldLabel, { label: kind, keyword: kind, labelType: "heading" }),
2909
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: !entry ? /* @__PURE__ */ jsxs8(
2910
+ "button",
2911
+ {
2912
+ className: "raf-button raf-button-primary",
2913
+ type: "button",
2914
+ onClick: () => {
2915
+ const next = cloneSchema(schema);
2916
+ next[kind] = { type: "string" };
2917
+ onChange(next);
2918
+ },
2919
+ children: [
2920
+ "Add ",
2921
+ kind
2922
+ ]
2923
+ }
2924
+ ) : /* @__PURE__ */ jsxs8(
2925
+ "button",
2926
+ {
2927
+ className: "raf-button raf-button-secondary",
2928
+ type: "button",
2929
+ onClick: () => {
2930
+ const next = cloneSchema(schema);
2931
+ delete next[kind];
2932
+ onChange(next);
2933
+ },
2934
+ children: [
2935
+ "Clear ",
2936
+ kind
2937
+ ]
2938
+ }
2939
+ ) }),
2940
+ entry ? /* @__PURE__ */ jsx12(
2941
+ SchemaNodeEditor,
2942
+ {
2943
+ label: kind,
2944
+ schema: entry,
2945
+ onChange: (nextEntrySchema) => {
2946
+ const next = cloneSchema(schema);
2947
+ next[kind] = nextEntrySchema;
2948
+ onChange(next);
2949
+ },
2950
+ onRemove: () => {
2951
+ const next = cloneSchema(schema);
2952
+ delete next[kind];
2953
+ onChange(next);
2954
+ }
2955
+ }
2956
+ ) : null
2957
+ ] });
2958
+ }
2959
+ function SchemaMapEditor({
2960
+ title,
2961
+ addButtonLabel,
2962
+ removeButtonLabel,
2963
+ schemaMap,
2964
+ defaultSchemaFactory,
2965
+ onChange
2966
+ }) {
2967
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-builder-block", children: [
2968
+ /* @__PURE__ */ jsx12(FieldLabel, { label: title, keyword: title, labelType: "heading" }),
2969
+ Object.entries(schemaMap).map(([entryName, entrySchema], index) => /* @__PURE__ */ jsxs8("div", { className: "raf-builder-property", children: [
2970
+ /* @__PURE__ */ jsx12("div", { className: "raf-builder-grid", children: /* @__PURE__ */ jsx12(
2971
+ TextInput,
2972
+ {
2973
+ label: `${title} Key`,
2974
+ keyword: title,
2975
+ value: entryName,
2976
+ onChange: (nextName) => {
2977
+ const normalized = nextName.trim();
2978
+ if (normalized === entryName || normalized === "") {
2979
+ return;
2980
+ }
2981
+ const nextMap = { ...schemaMap };
2982
+ if (Object.prototype.hasOwnProperty.call(nextMap, normalized)) {
2983
+ return;
2984
+ }
2985
+ nextMap[normalized] = nextMap[entryName];
2986
+ delete nextMap[entryName];
2987
+ onChange(nextMap);
2988
+ }
2989
+ }
2990
+ ) }),
2991
+ /* @__PURE__ */ jsx12(
2992
+ SchemaNodeEditor,
2993
+ {
2994
+ label: `${title}[${entryName}]`,
2995
+ schema: entrySchema,
2996
+ onChange: (nextEntrySchema) => {
2997
+ const nextMap = { ...schemaMap, [entryName]: nextEntrySchema };
2998
+ onChange(nextMap);
2999
+ },
3000
+ onRemove: () => {
3001
+ const nextMap = { ...schemaMap };
3002
+ delete nextMap[entryName];
3003
+ onChange(nextMap);
3004
+ }
3005
+ }
3006
+ ),
3007
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
3008
+ "button",
3009
+ {
3010
+ className: "raf-button raf-button-danger",
3011
+ type: "button",
3012
+ onClick: () => {
3013
+ const nextMap = { ...schemaMap };
3014
+ delete nextMap[entryName];
3015
+ onChange(nextMap);
3016
+ },
3017
+ children: removeButtonLabel
3018
+ }
3019
+ ) })
3020
+ ] }, `${title}-${index}`)),
3021
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row", children: /* @__PURE__ */ jsx12(
3022
+ "button",
3023
+ {
3024
+ className: "raf-button raf-button-secondary",
3025
+ type: "button",
3026
+ onClick: () => {
3027
+ const nextMap = { ...schemaMap };
3028
+ const newKey = createUniqueEntryName(nextMap, "field");
3029
+ nextMap[newKey] = defaultSchemaFactory();
3030
+ onChange(nextMap);
3031
+ },
3032
+ children: addButtonLabel
3033
+ }
3034
+ ) })
3035
+ ] });
3036
+ }
3037
+ function TypeListEditor({
3038
+ value,
3039
+ onChange
3040
+ }) {
3041
+ const availableTypes = FIELD_TYPES.filter((type) => !value.includes(type));
3042
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-field", children: [
3043
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "Types", keyword: "type" }),
3044
+ value.map((type, index) => /* @__PURE__ */ jsxs8("div", { className: "raf-button-row raf-type-list-row", children: [
3045
+ /* @__PURE__ */ jsx12(
3046
+ "select",
3047
+ {
3048
+ className: "raf-select raf-builder-control",
3049
+ "aria-label": `Type ${index + 1}`,
3050
+ value: type,
3051
+ onChange: (event) => {
3052
+ const nextType = event.target.value;
3053
+ if (nextType === type || value.includes(nextType)) {
3054
+ return;
3055
+ }
3056
+ const nextTypes = [...value];
3057
+ nextTypes[index] = nextType;
3058
+ onChange(nextTypes);
3059
+ },
3060
+ children: FIELD_TYPES.map((optionType) => /* @__PURE__ */ jsx12("option", { value: optionType, children: optionType }, optionType))
3061
+ }
3062
+ ),
3063
+ value.length > 1 ? /* @__PURE__ */ jsx12(
3064
+ "button",
3065
+ {
3066
+ className: "raf-button raf-button-danger",
3067
+ type: "button",
3068
+ onClick: () => {
3069
+ const nextTypes = value.filter((_, currentIndex) => currentIndex !== index);
3070
+ onChange(nextTypes);
3071
+ },
3072
+ children: "Remove"
3073
+ }
3074
+ ) : null
3075
+ ] }, `type-${index}`)),
3076
+ /* @__PURE__ */ jsx12("div", { className: "raf-button-row raf-type-list-add-row", children: /* @__PURE__ */ jsx12(
3077
+ "button",
3078
+ {
3079
+ className: "raf-button raf-button-secondary",
3080
+ type: "button",
3081
+ disabled: availableTypes.length === 0,
3082
+ onClick: () => {
3083
+ if (availableTypes.length === 0) {
3084
+ return;
3085
+ }
3086
+ onChange([...value, availableTypes[0]]);
3087
+ },
3088
+ children: "Add Type"
3089
+ }
3090
+ ) })
3091
+ ] });
3092
+ }
3093
+ function useFieldHelp() {
3094
+ return useContext(FieldHelpContext);
3095
+ }
3096
+ function FieldLabel({
3097
+ label,
3098
+ keyword,
3099
+ labelType = "standard"
3100
+ }) {
3101
+ const fieldHelp = useFieldHelp();
3102
+ const helpKey = keyword ?? label;
3103
+ const help = resolveHelpDefinition(helpKey);
3104
+ return /* @__PURE__ */ jsxs8(Fragment2, { children: [
3105
+ /* @__PURE__ */ jsxs8("div", { className: "raf-field-label-row", children: [
3106
+ labelType === "heading" ? /* @__PURE__ */ jsx12("h4", { className: "raf-builder-heading", children: label }) : /* @__PURE__ */ jsx12("span", { className: "raf-field-label", children: label }),
3107
+ /* @__PURE__ */ jsxs8(
3108
+ "button",
3109
+ {
3110
+ className: "raf-info-button",
3111
+ type: "button",
3112
+ "aria-label": `Info about ${label}`,
3113
+ onClick: (event) => {
3114
+ const rect = event.currentTarget.getBoundingClientRect();
3115
+ fieldHelp?.openHelp(helpKey, label, {
3116
+ top: rect.top,
3117
+ left: rect.left,
3118
+ bottom: rect.bottom,
3119
+ right: rect.right
3120
+ });
3121
+ },
3122
+ children: [
3123
+ /* @__PURE__ */ jsx12("span", { className: "raf-info-icon", "aria-hidden": "true", children: "i" }),
3124
+ /* @__PURE__ */ jsx12("span", { className: "raf-sr-only", children: "Info" })
3125
+ ]
3126
+ }
3127
+ )
3128
+ ] }),
3129
+ /* @__PURE__ */ jsx12("div", { className: "raf-field-summary", children: help.summary })
3130
+ ] });
3131
+ }
3132
+ function KeywordCheckbox({
3133
+ label,
3134
+ keyword,
3135
+ checked,
3136
+ onChange
3137
+ }) {
3138
+ return /* @__PURE__ */ jsxs8("div", { className: "raf-field", children: [
3139
+ /* @__PURE__ */ jsx12(FieldLabel, { label, keyword }),
3140
+ /* @__PURE__ */ jsxs8("label", { className: "raf-checkbox-row", children: [
3141
+ /* @__PURE__ */ jsx12(
3142
+ "input",
3143
+ {
3144
+ className: "raf-checkbox",
3145
+ type: "checkbox",
3146
+ checked,
3147
+ onChange: (event) => onChange(event.target.checked)
3148
+ }
3149
+ ),
3150
+ /* @__PURE__ */ jsx12("span", { children: label })
3151
+ ] })
3152
+ ] });
3153
+ }
3154
+ function FieldInfoModal({
3155
+ activeHelp,
3156
+ onClose
3157
+ }) {
3158
+ const modalRef = useRef3(null);
3159
+ const [desktopPosition, setDesktopPosition] = useState3(null);
3160
+ const [isMobile, setIsMobile] = useState3(false);
3161
+ useLayoutEffect(() => {
3162
+ if (!activeHelp) {
3163
+ return;
3164
+ }
3165
+ const updatePosition = () => {
3166
+ const mobile = window.innerWidth <= 760;
3167
+ setIsMobile(mobile);
3168
+ if (mobile) {
3169
+ setDesktopPosition(null);
3170
+ return;
3171
+ }
3172
+ const modalMaxWidth = Math.min(480, window.innerWidth - 24);
3173
+ const modalMaxHeight = Math.floor(window.innerHeight * 0.38);
3174
+ const desiredLeft = activeHelp.anchor.left;
3175
+ const desiredTop = activeHelp.anchor.bottom + 8;
3176
+ const left = Math.max(12, Math.min(desiredLeft, window.innerWidth - modalMaxWidth - 12));
3177
+ const top = Math.max(12, Math.min(desiredTop, window.innerHeight - modalMaxHeight - 12));
3178
+ setDesktopPosition({ top, left });
3179
+ };
3180
+ updatePosition();
3181
+ window.addEventListener("resize", updatePosition);
3182
+ return () => {
3183
+ window.removeEventListener("resize", updatePosition);
3184
+ };
3185
+ }, [activeHelp]);
3186
+ useEffect3(() => {
3187
+ if (!activeHelp) {
3188
+ return;
3189
+ }
3190
+ const closeIfOutside = (event) => {
3191
+ const target = event.target;
3192
+ if (!target || !(target instanceof Node)) {
3193
+ return;
3194
+ }
3195
+ if (modalRef.current?.contains(target)) {
3196
+ return;
3197
+ }
3198
+ onClose();
3199
+ };
3200
+ const handleKeyDown = (event) => {
3201
+ if (event.key === "Escape") {
3202
+ onClose();
3203
+ }
3204
+ };
3205
+ document.addEventListener("mousedown", closeIfOutside, true);
3206
+ document.addEventListener("touchstart", closeIfOutside, true);
3207
+ document.addEventListener("wheel", closeIfOutside, true);
3208
+ document.addEventListener("scroll", closeIfOutside, true);
3209
+ document.addEventListener("keydown", handleKeyDown);
3210
+ return () => {
3211
+ document.removeEventListener("mousedown", closeIfOutside, true);
3212
+ document.removeEventListener("touchstart", closeIfOutside, true);
3213
+ document.removeEventListener("wheel", closeIfOutside, true);
3214
+ document.removeEventListener("scroll", closeIfOutside, true);
3215
+ document.removeEventListener("keydown", handleKeyDown);
3216
+ };
3217
+ }, [activeHelp, onClose]);
3218
+ if (!activeHelp) {
3219
+ return null;
3220
+ }
3221
+ const help = resolveHelpDefinition(activeHelp.key);
3222
+ return /* @__PURE__ */ jsx12("div", { className: "raf-info-layer", role: "presentation", onMouseDown: onClose, children: /* @__PURE__ */ jsxs8(
3223
+ "div",
3224
+ {
3225
+ ref: modalRef,
3226
+ className: `raf-info-modal${isMobile ? " raf-info-modal-mobile" : ""}`,
3227
+ style: !isMobile && desktopPosition ? { top: `${desktopPosition.top}px`, left: `${desktopPosition.left}px` } : void 0,
3228
+ role: "dialog",
3229
+ "aria-modal": "false",
3230
+ "aria-label": `${activeHelp.label} information`,
3231
+ onMouseDown: (event) => event.stopPropagation(),
3232
+ children: [
3233
+ /* @__PURE__ */ jsxs8("div", { className: "raf-info-modal-header", children: [
3234
+ /* @__PURE__ */ jsx12("strong", { children: activeHelp.label }),
3235
+ /* @__PURE__ */ jsx12("button", { className: "raf-button raf-button-secondary", type: "button", onClick: onClose, children: "Close" })
3236
+ ] }),
3237
+ /* @__PURE__ */ jsxs8("div", { className: "raf-info-modal-body", children: [
3238
+ /* @__PURE__ */ jsxs8("a", { className: "raf-info-link", href: help.link, target: "_blank", rel: "noreferrer", children: [
3239
+ "View ",
3240
+ activeHelp.label,
3241
+ " in JSON Schema 2020-12"
3242
+ ] }),
3243
+ /* @__PURE__ */ jsx12("p", { children: parse(help.details) }),
3244
+ /* @__PURE__ */ jsx12("p", { children: parse(help.longDetails) })
3245
+ ] })
3246
+ ]
3247
+ }
3248
+ ) });
3249
+ }
3250
+ function TextInput({
3251
+ label,
3252
+ keyword,
3253
+ value,
3254
+ onChange,
3255
+ placeholder,
3256
+ helperText,
3257
+ type = "text",
3258
+ step
3259
+ }) {
3260
+ return /* @__PURE__ */ jsxs8("label", { className: "raf-field", children: [
3261
+ /* @__PURE__ */ jsx12(FieldLabel, { label, keyword }),
3262
+ /* @__PURE__ */ jsx12(
3263
+ "input",
3264
+ {
3265
+ className: "raf-input raf-builder-control",
3266
+ type,
3267
+ "aria-label": label,
3268
+ value,
3269
+ placeholder,
3270
+ step,
3271
+ onChange: (event) => onChange(event.target.value)
3272
+ }
3273
+ ),
3274
+ helperText ? /* @__PURE__ */ jsx12("div", { className: "raf-muted", children: helperText }) : null
3275
+ ] });
3276
+ }
3277
+ function SelectInput({
3278
+ label,
3279
+ keyword,
3280
+ value,
3281
+ onChange,
3282
+ options
3283
+ }) {
3284
+ return /* @__PURE__ */ jsxs8("label", { className: "raf-field", children: [
3285
+ /* @__PURE__ */ jsx12(FieldLabel, { label, keyword }),
3286
+ /* @__PURE__ */ jsx12("select", { className: "raf-select raf-builder-control", "aria-label": label, value, onChange: (event) => onChange(event.target.value), children: options.map((option) => /* @__PURE__ */ jsx12("option", { value: option.value, children: option.label }, option.value)) })
3287
+ ] });
3288
+ }
3289
+ function TypeaheadInput({
3290
+ label,
3291
+ keyword,
3292
+ value,
3293
+ onChange,
3294
+ options,
3295
+ placeholder
3296
+ }) {
3297
+ const [isOpen, setIsOpen] = useState3(false);
3298
+ const containerRef = useRef3(null);
3299
+ const normalizedValue = value.trim().toLowerCase();
3300
+ const filteredOptions = options.filter(
3301
+ (option) => normalizedValue === "" ? true : option.toLowerCase().startsWith(normalizedValue)
3302
+ );
3303
+ useEffect3(() => {
3304
+ if (!isOpen) {
3305
+ return;
3306
+ }
3307
+ const handlePointerDown = (event) => {
3308
+ if (!containerRef.current?.contains(event.target)) {
3309
+ setIsOpen(false);
3310
+ }
3311
+ };
3312
+ document.addEventListener("mousedown", handlePointerDown);
3313
+ return () => document.removeEventListener("mousedown", handlePointerDown);
3314
+ }, [isOpen]);
3315
+ return /* @__PURE__ */ jsxs8("label", { className: "raf-field raf-typeahead", ref: containerRef, children: [
3316
+ /* @__PURE__ */ jsx12(FieldLabel, { label, keyword }),
3317
+ /* @__PURE__ */ jsx12(
3318
+ "input",
3319
+ {
3320
+ className: "raf-input raf-builder-control",
3321
+ type: "text",
3322
+ "aria-label": label,
3323
+ value,
3324
+ placeholder,
3325
+ onFocus: () => setIsOpen(true),
3326
+ onClick: () => setIsOpen(true),
3327
+ onChange: (event) => {
3328
+ setIsOpen(true);
3329
+ onChange(event.target.value);
3330
+ }
3331
+ }
3332
+ ),
3333
+ isOpen && filteredOptions.length > 0 ? /* @__PURE__ */ jsx12("div", { className: "raf-typeahead-menu", role: "listbox", "aria-label": `${label} options`, children: filteredOptions.map((option) => /* @__PURE__ */ jsx12(
3334
+ "button",
3335
+ {
3336
+ className: "raf-typeahead-option",
3337
+ type: "button",
3338
+ role: "option",
3339
+ "aria-selected": value === option,
3340
+ onMouseDown: (event) => {
3341
+ event.preventDefault();
3342
+ onChange(option);
3343
+ setIsOpen(false);
3344
+ },
3345
+ children: option
3346
+ },
3347
+ option
3348
+ )) }) : null
3349
+ ] });
3350
+ }
3351
+ function TextAreaInput({
3352
+ label,
3353
+ keyword,
3354
+ value,
3355
+ onChange
3356
+ }) {
3357
+ return /* @__PURE__ */ jsxs8("label", { className: "raf-field", children: [
3358
+ /* @__PURE__ */ jsx12(FieldLabel, { label, keyword }),
3359
+ /* @__PURE__ */ jsx12(
3360
+ "textarea",
3361
+ {
3362
+ className: "raf-textarea raf-builder-control",
3363
+ "aria-label": label,
3364
+ value,
3365
+ onChange: (event) => onChange(event.target.value)
3366
+ }
3367
+ )
3368
+ ] });
3369
+ }
3370
+ function JsonTextInput({
3371
+ label,
3372
+ keyword,
3373
+ value,
3374
+ onValidJson,
3375
+ onClear,
3376
+ onInvalidJsonText,
3377
+ stringValueDisplay = "json",
3378
+ placeholder
3379
+ }) {
3380
+ const serializedValue = value === void 0 ? "" : stringValueDisplay === "raw" && typeof value === "string" ? value : toInlineJson(value);
3381
+ const [draftValue, setDraftValue] = useState3(serializedValue);
3382
+ useEffect3(() => {
3383
+ setDraftValue(serializedValue);
3384
+ }, [serializedValue]);
3385
+ return /* @__PURE__ */ jsx12(
3386
+ TextInput,
3387
+ {
3388
+ label,
3389
+ keyword,
3390
+ value: draftValue,
3391
+ onChange: (nextText) => {
3392
+ setDraftValue(nextText);
3393
+ if (nextText.trim() === "") {
3394
+ onClear();
3395
+ return;
3396
+ }
3397
+ try {
3398
+ const parsed = JSON.parse(nextText);
3399
+ onValidJson(parsed);
3400
+ } catch {
3401
+ onInvalidJsonText?.(nextText);
3402
+ }
3403
+ },
3404
+ placeholder
3405
+ }
3406
+ );
3407
+ }
3408
+ function ConstEditor({
3409
+ schema,
3410
+ schemaTypes,
3411
+ primaryType,
3412
+ onChange
3413
+ }) {
3414
+ if (primaryType === "null" && schemaTypes.length === 1) {
3415
+ return null;
3416
+ }
3417
+ const enumValues = Array.isArray(schema.enum) ? schema.enum : void 0;
3418
+ if (enumValues && enumValues.length > 0) {
3419
+ const selectedIndex = enumValues.findIndex((entry) => deepEqual(entry, schema.const));
3420
+ return /* @__PURE__ */ jsxs8("label", { className: "raf-field", children: [
3421
+ /* @__PURE__ */ jsx12(FieldLabel, { label: "Const", keyword: "const" }),
3422
+ /* @__PURE__ */ jsxs8(
3423
+ "select",
3424
+ {
3425
+ className: "raf-select raf-builder-control",
3426
+ "aria-label": "Const",
3427
+ value: selectedIndex >= 0 ? String(selectedIndex) : "",
3428
+ onChange: (event) => {
3429
+ const indexValue = event.target.value;
3430
+ const next = cloneSchema(schema);
3431
+ if (indexValue === "") {
3432
+ delete next.const;
3433
+ onChange(next);
3434
+ return;
3435
+ }
3436
+ const index = Number(indexValue);
3437
+ if (!Number.isInteger(index) || index < 0 || index >= enumValues.length) {
3438
+ return;
3439
+ }
3440
+ next.const = cloneSchema(enumValues[index]);
3441
+ onChange(next);
3442
+ },
3443
+ children: [
3444
+ /* @__PURE__ */ jsx12("option", { value: "", children: "None" }),
3445
+ enumValues.map((option, index) => /* @__PURE__ */ jsx12("option", { value: String(index), children: String(option) }, `${index}-${String(option)}`))
3446
+ ]
3447
+ }
3448
+ )
3449
+ ] });
3450
+ }
3451
+ if (primaryType === "boolean") {
3452
+ return /* @__PURE__ */ jsx12(
3453
+ KeywordCheckbox,
3454
+ {
3455
+ label: "Const",
3456
+ keyword: "const",
3457
+ checked: schema.const === true,
3458
+ onChange: (checked) => {
3459
+ const next = cloneSchema(schema);
3460
+ next.const = checked;
3461
+ onChange(next);
3462
+ }
3463
+ }
3464
+ );
3465
+ }
3466
+ if (primaryType === "number" || primaryType === "integer") {
3467
+ return /* @__PURE__ */ jsx12(
3468
+ TextInput,
3469
+ {
3470
+ label: "Const",
3471
+ keyword: "const",
3472
+ type: "number",
3473
+ step: primaryType === "integer" ? "1" : "any",
3474
+ value: typeof schema.const === "number" ? String(schema.const) : "",
3475
+ placeholder: primaryType === "integer" ? "e.g. 3" : "e.g. 3.14",
3476
+ onChange: (value) => {
3477
+ const next = cloneSchema(schema);
3478
+ if (value.trim() === "") {
3479
+ delete next.const;
3480
+ onChange(next);
3481
+ return;
3482
+ }
3483
+ const parsed = Number(value);
3484
+ if (!Number.isFinite(parsed)) {
3485
+ return;
3486
+ }
3487
+ if (primaryType === "integer" && !Number.isInteger(parsed)) {
3488
+ return;
3489
+ }
3490
+ next.const = parsed;
3491
+ onChange(next);
3492
+ }
3493
+ }
3494
+ );
3495
+ }
3496
+ if (primaryType === "string") {
3497
+ return /* @__PURE__ */ jsx12(
3498
+ TextInput,
3499
+ {
3500
+ label: "Const",
3501
+ keyword: "const",
3502
+ type: "text",
3503
+ value: typeof schema.const === "string" ? schema.const : "",
3504
+ placeholder: "e.g. fixed-value",
3505
+ onChange: (value) => {
3506
+ const next = cloneSchema(schema);
3507
+ if (value === "") {
3508
+ delete next.const;
3509
+ } else {
3510
+ next.const = value;
3511
+ }
3512
+ onChange(next);
3513
+ }
3514
+ }
3515
+ );
3516
+ }
3517
+ if (!primaryType) {
3518
+ return /* @__PURE__ */ jsx12(
3519
+ JsonTextInput,
3520
+ {
3521
+ label: "Const",
3522
+ keyword: "const",
3523
+ value: schema.const,
3524
+ stringValueDisplay: "raw",
3525
+ placeholder: "e.g. A, 2, true, null",
3526
+ onClear: () => {
3527
+ const next = cloneSchema(schema);
3528
+ delete next.const;
3529
+ onChange(next);
3530
+ },
3531
+ onValidJson: (parsed) => {
3532
+ if (!matchesAnySchemaType(parsed, schemaTypes)) {
3533
+ return;
3534
+ }
3535
+ onChange({ ...schema, const: parsed });
3536
+ },
3537
+ onInvalidJsonText: (rawText) => {
3538
+ const parsed = parseLooseScalarByTypes(rawText, schemaTypes);
3539
+ if (parsed === void 0) {
3540
+ return;
3541
+ }
3542
+ onChange({ ...schema, const: parsed });
3543
+ }
3544
+ }
3545
+ );
3546
+ }
3547
+ return /* @__PURE__ */ jsx12(
3548
+ JsonTextInput,
3549
+ {
3550
+ label: "Const (JSON)",
3551
+ keyword: "const",
3552
+ value: schema.const,
3553
+ placeholder: 'e.g. "fixed-value", 3, true, null, {"k":"v"}',
3554
+ onClear: () => {
3555
+ const next = cloneSchema(schema);
3556
+ delete next.const;
3557
+ onChange(next);
3558
+ },
3559
+ onValidJson: (parsed) => {
3560
+ onChange({ ...schema, const: parsed });
3561
+ }
3562
+ }
3563
+ );
3564
+ }
3565
+ function EnumEditor({
3566
+ schema,
3567
+ schemaTypes,
3568
+ primaryType,
3569
+ onChange
3570
+ }) {
3571
+ if (primaryType === "boolean" || primaryType === "null") {
3572
+ return null;
3573
+ }
3574
+ if (primaryType === "string") {
3575
+ return /* @__PURE__ */ jsx12(
3576
+ StringEnumInput,
3577
+ {
3578
+ value: Array.isArray(schema.enum) ? schema.enum : void 0,
3579
+ onChange: (nextEnum) => {
3580
+ const next = cloneSchema(schema);
3581
+ delete next.const;
3582
+ if (!nextEnum || nextEnum.length === 0) {
3583
+ delete next.enum;
3584
+ } else {
3585
+ next.enum = nextEnum;
3586
+ }
3587
+ onChange(next);
3588
+ }
3589
+ }
3590
+ );
3591
+ }
3592
+ if (primaryType === "number" || primaryType === "integer") {
3593
+ return /* @__PURE__ */ jsx12(
3594
+ NumberEnumInput,
3595
+ {
3596
+ value: Array.isArray(schema.enum) ? schema.enum : void 0,
3597
+ integerOnly: primaryType === "integer",
3598
+ onChange: (nextEnum) => {
3599
+ const next = cloneSchema(schema);
3600
+ delete next.const;
3601
+ if (!nextEnum || nextEnum.length === 0) {
3602
+ delete next.enum;
3603
+ } else {
3604
+ next.enum = nextEnum;
3605
+ }
3606
+ onChange(next);
3607
+ }
3608
+ }
3609
+ );
3610
+ }
3611
+ if (!primaryType && schemaTypes.length > 1) {
3612
+ return /* @__PURE__ */ jsx12(
3613
+ StringEnumInput,
3614
+ {
3615
+ value: Array.isArray(schema.enum) ? schema.enum : void 0,
3616
+ parseEntry: (entry) => parseLooseScalarByTypes(entry, schemaTypes),
3617
+ onChange: (nextEnum) => {
3618
+ const next = cloneSchema(schema);
3619
+ delete next.const;
3620
+ if (!nextEnum || nextEnum.length === 0) {
3621
+ delete next.enum;
3622
+ } else {
3623
+ next.enum = nextEnum;
3624
+ }
3625
+ onChange(next);
3626
+ }
3627
+ }
3628
+ );
3629
+ }
3630
+ return null;
3631
+ }
3632
+ function NumberEnumInput({
3633
+ value,
3634
+ integerOnly,
3635
+ onChange
3636
+ }) {
3637
+ const serializedValue = Array.isArray(value) ? JSON.stringify(value) : "";
3638
+ const [draftValue, setDraftValue] = useState3(serializedValue);
3639
+ const lastSubmittedSignatureRef = useRef3(null);
3640
+ useEffect3(() => {
3641
+ const nextSignature = Array.isArray(value) ? JSON.stringify(value) : "";
3642
+ if (lastSubmittedSignatureRef.current === nextSignature) {
3643
+ return;
3644
+ }
3645
+ setDraftValue(serializedValue);
3646
+ }, [serializedValue, value]);
3647
+ return /* @__PURE__ */ jsx12(
3648
+ TextInput,
3649
+ {
3650
+ label: "Enum",
3651
+ keyword: "enum",
3652
+ value: draftValue,
3653
+ placeholder: integerOnly ? "e.g. [1, 2, 3]" : "e.g. [1, 2.5, 3]",
3654
+ onChange: (nextText) => {
3655
+ if (!/^[\[\]\d,\.\-+\seE]*$/.test(nextText)) {
3656
+ return;
3657
+ }
3658
+ setDraftValue(nextText);
3659
+ if (nextText.trim() === "") {
3660
+ lastSubmittedSignatureRef.current = "";
3661
+ onChange(void 0);
3662
+ return;
3663
+ }
3664
+ const parsed = parseNumberEnum(nextText, integerOnly);
3665
+ if (!parsed) {
3666
+ return;
3667
+ }
3668
+ lastSubmittedSignatureRef.current = JSON.stringify(parsed);
3669
+ onChange(parsed);
3670
+ }
3671
+ }
3672
+ );
3673
+ }
3674
+ function parseNumberEnum(input, integerOnly) {
3675
+ const trimmed = input.trim();
3676
+ if (!trimmed) {
3677
+ return [];
3678
+ }
3679
+ const hasWrappedBrackets = trimmed.startsWith("[") && trimmed.endsWith("]");
3680
+ const core = hasWrappedBrackets ? trimmed.slice(1, -1) : trimmed;
3681
+ if (core.trim() === "") {
3682
+ return [];
3683
+ }
3684
+ const segments = core.split(",").map((entry) => entry.trim());
3685
+ if (segments.some((entry) => entry === "")) {
3686
+ return null;
3687
+ }
3688
+ const parsedValues = [];
3689
+ for (const segment of segments) {
3690
+ const parsed = Number(segment);
3691
+ if (!Number.isFinite(parsed)) {
3692
+ return null;
3693
+ }
3694
+ if (integerOnly && !Number.isInteger(parsed)) {
3695
+ return null;
3696
+ }
3697
+ parsedValues.push(parsed);
3698
+ }
3699
+ return parsedValues;
3700
+ }
3701
+ function StringEnumInput({
3702
+ value,
3703
+ parseEntry,
3704
+ onChange
3705
+ }) {
3706
+ const displayValues = Array.isArray(value) ? value.map((entry) => typeof entry === "string" ? entry : String(entry)) : [];
3707
+ const serializedValue = Array.isArray(value) ? serializeStringEnum(displayValues) : "";
3708
+ const [draftValue, setDraftValue] = useState3(serializedValue);
3709
+ const lastSubmittedSignatureRef = useRef3(null);
3710
+ useEffect3(() => {
3711
+ const nextSignature = Array.isArray(value) ? JSON.stringify(displayValues) : "";
3712
+ if (lastSubmittedSignatureRef.current === nextSignature) {
3713
+ return;
3714
+ }
3715
+ setDraftValue(serializedValue);
3716
+ }, [serializedValue, value]);
3717
+ return /* @__PURE__ */ jsx12(
3718
+ TextInput,
3719
+ {
3720
+ label: "Enum",
3721
+ keyword: "enum",
3722
+ type: "text",
3723
+ value: draftValue,
3724
+ placeholder: `e.g. A, B, "C, D", 'E, F'`,
3725
+ onChange: (nextText) => {
3726
+ setDraftValue(nextText);
3727
+ if (nextText.trim() === "") {
3728
+ lastSubmittedSignatureRef.current = "";
3729
+ onChange(void 0);
3730
+ return;
3731
+ }
3732
+ const parsed = parseStringEnum(nextText);
3733
+ if (!parsed.valid || !parsed.values) {
3734
+ return;
3735
+ }
3736
+ const normalizedValues = parsed.values.map((entry) => {
3737
+ if (!parseEntry) {
3738
+ return entry;
3739
+ }
3740
+ return parseEntry(entry);
3741
+ });
3742
+ if (normalizedValues.some((entry) => entry === void 0)) {
3743
+ return;
3744
+ }
3745
+ const typedValues = normalizedValues;
3746
+ const submittedSignature = JSON.stringify(
3747
+ typedValues.map((entry) => typeof entry === "string" ? entry : String(entry))
3748
+ );
3749
+ lastSubmittedSignatureRef.current = submittedSignature;
3750
+ onChange(typedValues);
3751
+ }
3752
+ }
3753
+ );
3754
+ }
3755
+ function parseLooseScalarByTypes(value, schemaTypes) {
3756
+ const trimmed = value.trim();
3757
+ if (trimmed === "") {
3758
+ return void 0;
3759
+ }
3760
+ if (schemaTypes.includes("boolean")) {
3761
+ if (trimmed === "true") {
3762
+ return true;
3763
+ }
3764
+ if (trimmed === "false") {
3765
+ return false;
3766
+ }
3767
+ }
3768
+ if (schemaTypes.includes("null") && trimmed === "null") {
3769
+ return null;
3770
+ }
3771
+ if (schemaTypes.includes("integer")) {
3772
+ const parsedInteger = Number(trimmed);
3773
+ if (Number.isInteger(parsedInteger)) {
3774
+ return parsedInteger;
3775
+ }
3776
+ }
3777
+ if (schemaTypes.includes("number")) {
3778
+ const parsedNumber = Number(trimmed);
3779
+ if (Number.isFinite(parsedNumber)) {
3780
+ return parsedNumber;
3781
+ }
3782
+ }
3783
+ if (schemaTypes.includes("string")) {
3784
+ return value;
3785
+ }
3786
+ return void 0;
3787
+ }
3788
+ function parseStringEnum(input) {
3789
+ const values = [];
3790
+ let token = "";
3791
+ let inDoubleQuote = false;
3792
+ let inSingleQuote = false;
3793
+ let tokenUsedQuotes = false;
3794
+ let tokenClosedQuote = false;
3795
+ const pushToken = () => {
3796
+ const candidate = tokenUsedQuotes ? token : token.trim();
3797
+ if (candidate.length > 0) {
3798
+ values.push(candidate);
3799
+ }
3800
+ token = "";
3801
+ tokenUsedQuotes = false;
3802
+ tokenClosedQuote = false;
3803
+ };
3804
+ for (let index = 0; index < input.length; index += 1) {
3805
+ const char = input[index];
3806
+ if (inDoubleQuote) {
3807
+ if (char === '"') {
3808
+ if (token.endsWith("/")) {
3809
+ token = `${token.slice(0, -1)}"`;
3810
+ continue;
3811
+ }
3812
+ inDoubleQuote = false;
3813
+ tokenClosedQuote = true;
3814
+ continue;
3815
+ }
3816
+ token += char;
3817
+ continue;
3818
+ }
3819
+ if (inSingleQuote) {
3820
+ if (char === "'") {
3821
+ if (token.endsWith("/")) {
3822
+ token = `${token.slice(0, -1)}'`;
3823
+ continue;
3824
+ }
3825
+ inSingleQuote = false;
3826
+ tokenClosedQuote = true;
3827
+ continue;
3828
+ }
3829
+ token += char;
3830
+ continue;
3831
+ }
3832
+ if (char === ",") {
3833
+ pushToken();
3834
+ continue;
3835
+ }
3836
+ if (char === '"') {
3837
+ if (token.trim().length === 0) {
3838
+ token = "";
3839
+ }
3840
+ inDoubleQuote = true;
3841
+ tokenUsedQuotes = true;
3842
+ continue;
3843
+ }
3844
+ if (char === "'") {
3845
+ if (token.trim().length === 0) {
3846
+ token = "";
3847
+ }
3848
+ inSingleQuote = true;
3849
+ tokenUsedQuotes = true;
3850
+ continue;
3851
+ }
3852
+ if (tokenClosedQuote && /\s/.test(char)) {
3853
+ continue;
3854
+ }
3855
+ token += char;
3856
+ }
3857
+ if (inDoubleQuote || inSingleQuote) {
3858
+ return { valid: false, values: null };
3859
+ }
3860
+ pushToken();
3861
+ return { valid: true, values };
3862
+ }
3863
+ function serializeStringEnum(values) {
3864
+ return values.map((value) => {
3865
+ const needsQuotes = value === "" || /[\s,\"']/.test(value);
3866
+ if (!needsQuotes) {
3867
+ return value;
3868
+ }
3869
+ const escaped = value.replace(/\"/g, '/"');
3870
+ return `"${escaped}"`;
3871
+ }).join(", ");
3872
+ }
3873
+ function createDefaultRootSchema() {
3874
+ return {
3875
+ $schema: DEFAULT_SCHEMA_URI,
3876
+ title: "New Schema",
3877
+ type: "object",
3878
+ properties: {},
3879
+ required: []
3880
+ };
3881
+ }
3882
+ function applyTypes(schema, nextTypesInput) {
3883
+ const next = cloneSchema(schema);
3884
+ const previousTypes = getSchemaTypes(next);
3885
+ const nextTypes = normalizeTypes(nextTypesInput);
3886
+ const removedTypes = previousTypes.filter((type) => !nextTypes.includes(type));
3887
+ for (const removedType of removedTypes) {
3888
+ removeTypeSpecificKeywords(next, removedType, nextTypes);
3889
+ }
3890
+ next.type = nextTypes.length === 1 ? nextTypes[0] : nextTypes;
3891
+ if (nextTypes.includes("object")) {
3892
+ next.properties = next.properties ?? {};
3893
+ next.required = Array.isArray(next.required) ? next.required : [];
3894
+ }
3895
+ if (nextTypes.includes("array")) {
3896
+ next.items = next.items ?? { type: "string" };
3897
+ }
3898
+ return next;
3899
+ }
3900
+ function removeTypeSpecificKeywords(schema, type, activeTypes) {
3901
+ if (type === "object") {
3902
+ if (!activeTypes.includes("object")) {
3903
+ delete schema.properties;
3904
+ delete schema.patternProperties;
3905
+ delete schema.required;
3906
+ delete schema.dependentRequired;
3907
+ delete schema.dependentSchemas;
3908
+ delete schema.additionalProperties;
3909
+ delete schema.unevaluatedProperties;
3910
+ delete schema.propertyNames;
3911
+ delete schema.minProperties;
3912
+ delete schema.maxProperties;
3913
+ }
3914
+ return;
3915
+ }
3916
+ if (type === "array") {
3917
+ if (!activeTypes.includes("array")) {
3918
+ delete schema.items;
3919
+ delete schema.contains;
3920
+ delete schema.minItems;
3921
+ delete schema.maxItems;
3922
+ delete schema.uniqueItems;
3923
+ delete schema.minContains;
3924
+ delete schema.maxContains;
3925
+ delete schema.unevaluatedItems;
3926
+ }
3927
+ return;
3928
+ }
3929
+ if (type === "string") {
3930
+ if (!activeTypes.includes("string")) {
3931
+ delete schema.pattern;
3932
+ }
3933
+ return;
3934
+ }
3935
+ if (type === "number" || type === "integer") {
3936
+ if (!(activeTypes.includes("number") || activeTypes.includes("integer"))) {
3937
+ delete schema.minimum;
3938
+ delete schema.maximum;
3939
+ delete schema.multipleOf;
3940
+ delete schema.exclusiveMinimum;
3941
+ delete schema.exclusiveMaximum;
3942
+ }
3943
+ }
3944
+ }
3945
+ function getSchemaTypes(schema) {
3946
+ if (Array.isArray(schema.type)) {
3947
+ return normalizeTypes(schema.type);
3948
+ }
3949
+ if (typeof schema.type === "string") {
3950
+ return normalizeTypes([schema.type]);
3951
+ }
3952
+ return ["object"];
3953
+ }
3954
+ function normalizeTypes(types) {
3955
+ const normalized = [];
3956
+ for (const type of types) {
3957
+ if (!FIELD_TYPES.includes(type)) {
3958
+ continue;
3959
+ }
3960
+ if (!normalized.includes(type)) {
3961
+ normalized.push(type);
3962
+ }
3963
+ }
3964
+ return normalized.length > 0 ? normalized : ["string"];
3965
+ }
3966
+ function assignOptionalString(schema, key, value) {
3967
+ const next = cloneSchema(schema);
3968
+ if (value.trim() === "") {
3969
+ delete next[key];
3970
+ return next;
3971
+ }
3972
+ next[key] = value;
3973
+ return next;
3974
+ }
3975
+ function assignOptionalNumber(schema, key, value) {
3976
+ const next = cloneSchema(schema);
3977
+ if (value.trim() === "") {
3978
+ delete next[key];
3979
+ return next;
3980
+ }
3981
+ const parsed = Number(value);
3982
+ if (Number.isNaN(parsed)) {
3983
+ return next;
3984
+ }
3985
+ next[key] = parsed;
3986
+ return next;
3987
+ }
3988
+ function assignOptionalInteger(schema, key, value) {
3989
+ const next = cloneSchema(schema);
3990
+ if (value.trim() === "") {
3991
+ delete next[key];
3992
+ return next;
3993
+ }
3994
+ const parsed = Number(value);
3995
+ if (!Number.isInteger(parsed) || parsed < 0) {
3996
+ return next;
3997
+ }
3998
+ next[key] = parsed;
3999
+ return next;
4000
+ }
4001
+ function assignOptionalPositiveNumber(schema, key, value) {
4002
+ const next = cloneSchema(schema);
4003
+ if (value.trim() === "") {
4004
+ delete next[key];
4005
+ return next;
4006
+ }
4007
+ const parsed = Number(value);
4008
+ if (!Number.isFinite(parsed) || parsed <= 0) {
4009
+ return next;
4010
+ }
4011
+ next[key] = parsed;
4012
+ return next;
4013
+ }
4014
+ function parseCommaSeparatedStrings(value) {
4015
+ return Array.from(
4016
+ new Set(
4017
+ value.split(",").map((entry) => entry.trim()).filter((entry) => entry !== "")
4018
+ )
4019
+ );
4020
+ }
4021
+ function createUniquePropertyName(properties, baseName) {
4022
+ if (!properties[baseName]) {
4023
+ return baseName;
4024
+ }
4025
+ let index = 1;
4026
+ while (properties[`${baseName}${index}`]) {
4027
+ index += 1;
4028
+ }
4029
+ return `${baseName}${index}`;
4030
+ }
4031
+ function createUniqueEntryName(entries, baseName) {
4032
+ if (!Object.prototype.hasOwnProperty.call(entries, baseName)) {
4033
+ return baseName;
4034
+ }
4035
+ let index = 1;
4036
+ while (Object.prototype.hasOwnProperty.call(entries, `${baseName}${index}`)) {
4037
+ index += 1;
4038
+ }
4039
+ return `${baseName}${index}`;
4040
+ }
4041
+ function cloneSchema(value) {
4042
+ if (typeof structuredClone === "function") {
4043
+ return structuredClone(value);
4044
+ }
4045
+ return JSON.parse(JSON.stringify(value));
4046
+ }
4047
+ function stringOrEmpty(value) {
4048
+ return typeof value === "string" ? value : "";
4049
+ }
4050
+ function sanitizeSchemaForOutput(schema) {
4051
+ const next = cloneSchema(schema);
4052
+ if (next.properties) {
4053
+ const sanitizedProperties = {};
4054
+ for (const [propertyName, propertySchema] of Object.entries(next.properties)) {
4055
+ if (propertyName.trim() === "") {
4056
+ continue;
4057
+ }
4058
+ sanitizedProperties[propertyName] = sanitizeSchemaForOutput(propertySchema);
4059
+ }
4060
+ if (Object.keys(sanitizedProperties).length > 0) {
4061
+ next.properties = sanitizedProperties;
4062
+ } else {
4063
+ delete next.properties;
4064
+ }
4065
+ }
4066
+ if (next.patternProperties) {
4067
+ const sanitizedPatternProperties = {};
4068
+ for (const [patternKey, patternSchema] of Object.entries(next.patternProperties)) {
4069
+ if (patternKey.trim() === "") {
4070
+ continue;
4071
+ }
4072
+ sanitizedPatternProperties[patternKey] = sanitizeSchemaForOutput(patternSchema);
4073
+ }
4074
+ next.patternProperties = sanitizedPatternProperties;
4075
+ }
4076
+ if (next.dependentSchemas) {
4077
+ const sanitizedDependentSchemas = {};
4078
+ for (const [propertyName, dependentSchema] of Object.entries(next.dependentSchemas)) {
4079
+ if (propertyName.trim() === "") {
4080
+ continue;
4081
+ }
4082
+ sanitizedDependentSchemas[propertyName] = sanitizeSchemaForOutput(dependentSchema);
4083
+ }
4084
+ next.dependentSchemas = sanitizedDependentSchemas;
4085
+ }
4086
+ if (next.dependentRequired) {
4087
+ const sanitizedDependentRequired = {};
4088
+ for (const [propertyName, dependencies] of Object.entries(next.dependentRequired)) {
4089
+ if (propertyName.trim() === "") {
4090
+ continue;
4091
+ }
4092
+ if (!Array.isArray(dependencies)) {
4093
+ sanitizedDependentRequired[propertyName] = [];
4094
+ continue;
4095
+ }
4096
+ const cleanedDependencies = Array.from(
4097
+ new Set(
4098
+ dependencies.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry !== "")
4099
+ )
4100
+ );
4101
+ sanitizedDependentRequired[propertyName] = cleanedDependencies;
4102
+ }
4103
+ next.dependentRequired = sanitizedDependentRequired;
4104
+ }
4105
+ if (Array.isArray(next.required)) {
4106
+ next.required = next.required.filter((entry) => entry.trim() !== "");
4107
+ if (next.required.length === 0) {
4108
+ delete next.required;
4109
+ }
4110
+ }
4111
+ if (Array.isArray(next.items)) {
4112
+ next.prefixItems = next.items.map((itemSchema) => sanitizeSchemaForOutput(itemSchema));
4113
+ next.items = false;
4114
+ }
4115
+ if (Array.isArray(next.prefixItems)) {
4116
+ next.prefixItems = next.prefixItems.map((itemSchema) => sanitizeSchemaForOutput(itemSchema));
4117
+ } else if (isObject3(next.items)) {
4118
+ next.items = sanitizeSchemaForOutput(next.items);
4119
+ }
4120
+ if (isObject3(next.contains)) {
4121
+ next.contains = sanitizeSchemaForOutput(next.contains);
4122
+ }
4123
+ if (isObject3(next.unevaluatedItems)) {
4124
+ next.unevaluatedItems = sanitizeSchemaForOutput(next.unevaluatedItems);
4125
+ }
4126
+ if (isObject3(next.not)) {
4127
+ next.not = sanitizeSchemaForOutput(next.not);
4128
+ }
4129
+ if (isObject3(next.additionalProperties)) {
4130
+ next.additionalProperties = sanitizeSchemaForOutput(next.additionalProperties);
4131
+ }
4132
+ if (isObject3(next.unevaluatedProperties)) {
4133
+ next.unevaluatedProperties = sanitizeSchemaForOutput(next.unevaluatedProperties);
4134
+ }
4135
+ if (isObject3(next.propertyNames)) {
4136
+ next.propertyNames = sanitizeSchemaForOutput(next.propertyNames);
4137
+ }
4138
+ for (const key of ["if", "then", "else"]) {
4139
+ if (isObject3(next[key])) {
4140
+ next[key] = sanitizeSchemaForOutput(next[key]);
4141
+ }
4142
+ }
4143
+ for (const key of ["allOf", "anyOf", "oneOf"]) {
4144
+ if (Array.isArray(next[key])) {
4145
+ next[key] = next[key].map((entry) => sanitizeSchemaForOutput(entry));
4146
+ }
4147
+ }
4148
+ return next;
4149
+ }
4150
+ function applyDomainToRootId(schema, domain) {
4151
+ if (!domain) {
4152
+ return schema;
4153
+ }
4154
+ const next = cloneSchema(schema);
4155
+ const localId = stringOrEmpty(next.$id);
4156
+ if (!localId.trim()) {
4157
+ return next;
4158
+ }
4159
+ next.$id = toFullId(localId, domain);
4160
+ return next;
4161
+ }
4162
+ function toLocalId(value, domain) {
4163
+ const trimmed = value.trim();
4164
+ if (!domain || !trimmed) {
4165
+ return trimmed;
4166
+ }
4167
+ const normalizedDomain = normalizeDomain(domain);
4168
+ if (!normalizedDomain) {
4169
+ return trimmed;
4170
+ }
4171
+ if (trimmed === normalizedDomain) {
4172
+ return "";
4173
+ }
4174
+ const domainWithSlash = `${normalizedDomain}/`;
4175
+ if (trimmed.startsWith(domainWithSlash)) {
4176
+ return trimmed.slice(domainWithSlash.length);
4177
+ }
4178
+ return trimmed;
4179
+ }
4180
+ function toFullId(localId, domain) {
4181
+ const trimmedLocal = localId.trim();
4182
+ if (!domain || !trimmedLocal) {
4183
+ return trimmedLocal;
4184
+ }
4185
+ const normalizedDomain = normalizeDomain(domain);
4186
+ if (!normalizedDomain) {
4187
+ return trimmedLocal;
4188
+ }
4189
+ if (trimmedLocal === normalizedDomain || trimmedLocal.startsWith(`${normalizedDomain}/`)) {
4190
+ return trimmedLocal;
4191
+ }
4192
+ return `${normalizedDomain}/${trimmedLocal.replace(/^\/+/, "")}`;
4193
+ }
4194
+ function normalizeDomain(domain) {
4195
+ return domain.trim().replace(/\/+$/, "");
4196
+ }
4197
+ function validateSchemaDefinition(schema) {
4198
+ const consistencyErrors = validateConstAndEnumConsistency(schema);
4199
+ try {
4200
+ const ajv = createAjvForSchema(schema);
4201
+ const valid = ajv.validateSchema(schema);
4202
+ const schemaErrors = valid ? [] : (ajv.errors ?? []).map((error) => ({
4203
+ message: error.message ?? "Schema validation error",
4204
+ keyword: error.keyword,
4205
+ instancePath: error.instancePath,
4206
+ schemaPath: error.schemaPath,
4207
+ source: "schema"
4208
+ }));
4209
+ return [...schemaErrors, ...consistencyErrors];
4210
+ } catch (error) {
4211
+ return [
4212
+ {
4213
+ message: error instanceof Error ? error.message : "Schema validation failed.",
4214
+ source: "schema"
4215
+ },
4216
+ ...consistencyErrors
4217
+ ];
4218
+ }
4219
+ }
4220
+ function validateConstAndEnumConsistency(schema, schemaPointer = "") {
4221
+ const errors = [];
4222
+ const schemaTypes = getSchemaTypes(schema);
4223
+ const hasConst = Object.prototype.hasOwnProperty.call(schema, "const");
4224
+ if (hasConst && !matchesAnySchemaType(schema.const, schemaTypes)) {
4225
+ errors.push({
4226
+ message: "const value does not match the field type.",
4227
+ keyword: "const",
4228
+ instancePath: schemaPointer,
4229
+ schemaPath: `${schemaPointer}/const`,
4230
+ source: "schema"
4231
+ });
4232
+ }
4233
+ if (schema.enum !== void 0) {
4234
+ if (!Array.isArray(schema.enum)) {
4235
+ errors.push({
4236
+ message: "enum must be an array.",
4237
+ keyword: "enum",
4238
+ instancePath: schemaPointer,
4239
+ schemaPath: `${schemaPointer}/enum`,
4240
+ source: "schema"
4241
+ });
4242
+ } else {
4243
+ schema.enum.forEach((enumValue, index) => {
4244
+ if (!matchesAnySchemaType(enumValue, schemaTypes)) {
4245
+ errors.push({
4246
+ message: `enum value at index ${index} does not match the field type.`,
4247
+ keyword: "enum",
4248
+ instancePath: schemaPointer,
4249
+ schemaPath: `${schemaPointer}/enum/${index}`,
4250
+ source: "schema"
4251
+ });
4252
+ }
4253
+ });
4254
+ if (hasConst && !schema.enum.some((entry) => deepEqual(entry, schema.const))) {
4255
+ errors.push({
4256
+ message: "const value must exist in enum when both are provided.",
4257
+ keyword: "const",
4258
+ instancePath: schemaPointer,
4259
+ schemaPath: `${schemaPointer}/const`,
4260
+ source: "schema"
4261
+ });
4262
+ }
4263
+ }
4264
+ }
4265
+ if (schema.properties) {
4266
+ for (const [propertyName, propertySchema] of Object.entries(schema.properties)) {
4267
+ errors.push(
4268
+ ...validateConstAndEnumConsistency(
4269
+ propertySchema,
4270
+ `${schemaPointer}/properties/${escapeJsonPointerToken2(propertyName)}`
4271
+ )
4272
+ );
4273
+ }
4274
+ }
4275
+ if (schema.patternProperties) {
4276
+ for (const [patternKey, patternSchema] of Object.entries(schema.patternProperties)) {
4277
+ errors.push(
4278
+ ...validateConstAndEnumConsistency(
4279
+ patternSchema,
4280
+ `${schemaPointer}/patternProperties/${escapeJsonPointerToken2(patternKey)}`
4281
+ )
4282
+ );
4283
+ }
4284
+ }
4285
+ if (schema.dependentSchemas) {
4286
+ for (const [propertyName, dependentSchema] of Object.entries(schema.dependentSchemas)) {
4287
+ errors.push(
4288
+ ...validateConstAndEnumConsistency(
4289
+ dependentSchema,
4290
+ `${schemaPointer}/dependentSchemas/${escapeJsonPointerToken2(propertyName)}`
4291
+ )
4292
+ );
4293
+ }
4294
+ }
4295
+ if (Array.isArray(schema.items)) {
4296
+ schema.items.forEach((itemSchema, index) => {
4297
+ errors.push(...validateConstAndEnumConsistency(itemSchema, `${schemaPointer}/items/${index}`));
4298
+ });
4299
+ } else if (isObject3(schema.items)) {
4300
+ errors.push(...validateConstAndEnumConsistency(schema.items, `${schemaPointer}/items`));
4301
+ }
4302
+ if (isObject3(schema.contains)) {
4303
+ errors.push(...validateConstAndEnumConsistency(schema.contains, `${schemaPointer}/contains`));
4304
+ }
4305
+ if (isObject3(schema.unevaluatedItems)) {
4306
+ errors.push(...validateConstAndEnumConsistency(schema.unevaluatedItems, `${schemaPointer}/unevaluatedItems`));
4307
+ }
4308
+ if (isObject3(schema.not)) {
4309
+ errors.push(...validateConstAndEnumConsistency(schema.not, `${schemaPointer}/not`));
4310
+ }
4311
+ if (isObject3(schema.additionalProperties)) {
4312
+ errors.push(
4313
+ ...validateConstAndEnumConsistency(schema.additionalProperties, `${schemaPointer}/additionalProperties`)
4314
+ );
4315
+ }
4316
+ if (isObject3(schema.unevaluatedProperties)) {
4317
+ errors.push(
4318
+ ...validateConstAndEnumConsistency(schema.unevaluatedProperties, `${schemaPointer}/unevaluatedProperties`)
4319
+ );
4320
+ }
4321
+ if (isObject3(schema.propertyNames)) {
4322
+ errors.push(...validateConstAndEnumConsistency(schema.propertyNames, `${schemaPointer}/propertyNames`));
4323
+ }
4324
+ for (const key of ["if", "then", "else"]) {
4325
+ if (isObject3(schema[key])) {
4326
+ errors.push(...validateConstAndEnumConsistency(schema[key], `${schemaPointer}/${key}`));
4327
+ }
4328
+ }
4329
+ for (const combinatorKey of ["allOf", "anyOf", "oneOf"]) {
4330
+ const entries = schema[combinatorKey];
4331
+ if (!Array.isArray(entries)) {
4332
+ continue;
4333
+ }
4334
+ entries.forEach((entry, index) => {
4335
+ errors.push(...validateConstAndEnumConsistency(entry, `${schemaPointer}/${combinatorKey}/${index}`));
4336
+ });
4337
+ }
4338
+ return errors;
4339
+ }
4340
+ function matchesAnySchemaType(value, schemaTypes) {
4341
+ return schemaTypes.some((schemaType) => matchesSchemaType(value, schemaType));
4342
+ }
4343
+ function matchesSchemaType(value, schemaType) {
4344
+ if (schemaType === "string") {
4345
+ return typeof value === "string";
4346
+ }
4347
+ if (schemaType === "number") {
4348
+ return typeof value === "number" && Number.isFinite(value);
4349
+ }
4350
+ if (schemaType === "integer") {
4351
+ return typeof value === "number" && Number.isInteger(value);
4352
+ }
4353
+ if (schemaType === "boolean") {
4354
+ return typeof value === "boolean";
4355
+ }
4356
+ if (schemaType === "null") {
4357
+ return value === null;
4358
+ }
4359
+ if (schemaType === "array") {
4360
+ return Array.isArray(value);
4361
+ }
4362
+ return isObject3(value);
4363
+ }
4364
+ function deepEqual(a, b) {
4365
+ if (a === b) {
4366
+ return true;
4367
+ }
4368
+ if (typeof a !== typeof b) {
4369
+ return false;
4370
+ }
4371
+ if (Array.isArray(a) && Array.isArray(b)) {
4372
+ if (a.length !== b.length) {
4373
+ return false;
4374
+ }
4375
+ return a.every((entry, index) => deepEqual(entry, b[index]));
4376
+ }
4377
+ if (isObject3(a) && isObject3(b)) {
4378
+ const keysA = Object.keys(a);
4379
+ const keysB = Object.keys(b);
4380
+ if (keysA.length !== keysB.length) {
4381
+ return false;
4382
+ }
4383
+ return keysA.every((key) => deepEqual(a[key], b[key]));
4384
+ }
4385
+ return false;
4386
+ }
4387
+ function escapeJsonPointerToken2(value) {
4388
+ return value.replace(/~/g, "~0").replace(/\//g, "~1");
4389
+ }
4390
+ function numberOrEmpty(value) {
4391
+ return typeof value === "number" && Number.isFinite(value) ? String(value) : "";
4392
+ }
4393
+ function toInlineJson(value) {
4394
+ try {
4395
+ return JSON.stringify(value);
4396
+ } catch {
4397
+ return "";
4398
+ }
4399
+ }
4400
+ function isObject3(value) {
4401
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4402
+ }
4403
+ export {
4404
+ SchemaBuilder,
4405
+ SchemaBuilderHelper,
4406
+ SchemaForm
4407
+ };
4408
+ //# sourceMappingURL=index.mjs.map