@jxsuite/schema 0.28.5 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,11 +15,19 @@ export const arrayNamespaceSchema = {
15
15
  } as const;
16
16
 
17
17
  export const childrenValueSchema = {
18
- description: "Static array of child definitions, or an Array namespace for dynamic lists.",
18
+ description:
19
+ "Array of child definitions — elements, text, or Array namespaces (dynamic lists) mixed " +
20
+ "freely. A bare Array namespace (the whole children slot is one dynamic list) is also " +
21
+ "accepted for backward compatibility.",
19
22
  oneOf: [
20
23
  {
21
24
  items: {
22
- oneOf: [{ $ref: "#/$defs/ElementDef" }, { type: "string" }, { type: "number" }],
25
+ oneOf: [
26
+ { $ref: "#/$defs/ElementDef" },
27
+ { $ref: "#/$defs/ArrayNamespace" },
28
+ { type: "string" },
29
+ { type: "number" },
30
+ ],
23
31
  },
24
32
  type: "array",
25
33
  },
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "dependencies": {
3
- "@webref/css": "^8.5.8",
3
+ "@webref/css": "^8.6.0",
4
4
  "@webref/elements": "^2.7.1",
5
- "@webref/idl": "^3.81.0"
5
+ "@webref/idl": "^3.81.1"
6
6
  },
7
7
  "description": "JSON Schema 2020-12 meta-schema generator for Jx documents",
8
8
  "exports": {
@@ -39,7 +39,7 @@
39
39
  "upgrade": "bunx npm-check-updates -u && bun install"
40
40
  },
41
41
  "type": "module",
42
- "version": "0.28.5",
42
+ "version": "0.30.1",
43
43
  "devDependencies": {
44
44
  "json-schema-to-ts": "^3.1.1"
45
45
  }
package/schema.json CHANGED
@@ -4665,7 +4665,7 @@
4665
4665
  "type": "object"
4666
4666
  },
4667
4667
  "ChildrenValue": {
4668
- "description": "Static array of child definitions, or an Array namespace for dynamic lists.",
4668
+ "description": "Array of child definitions — elements, text, or Array namespaces (dynamic lists) mixed freely. A bare Array namespace (the whole children slot is one dynamic list) is also accepted for backward compatibility.",
4669
4669
  "oneOf": [
4670
4670
  {
4671
4671
  "items": {
@@ -4673,6 +4673,9 @@
4673
4673
  {
4674
4674
  "$ref": "#/$defs/ElementDef"
4675
4675
  },
4676
+ {
4677
+ "$ref": "#/$defs/ArrayNamespace"
4678
+ },
4676
4679
  {
4677
4680
  "type": "string"
4678
4681
  },
package/src/guards.ts CHANGED
@@ -126,6 +126,18 @@ export function isMappedArray(value: unknown): value is JxMappedArray {
126
126
  return isJsonObject(value) && value.$prototype === "Array";
127
127
  }
128
128
 
129
+ /**
130
+ * Whether a node's `children` holds at least one mapped array — either as the bare whole-children
131
+ * slot (legacy) or as a member of the children array. Used by renderers and the compiler to decide
132
+ * if a children list needs inline repeater expansion.
133
+ */
134
+ export function childrenContainArray(children?: unknown): boolean {
135
+ if (isMappedArray(children)) {
136
+ return true;
137
+ }
138
+ return Array.isArray(children) && children.some((c) => isMappedArray(c));
139
+ }
140
+
129
141
  /** A .class.json schema-defined class document — `{ $prototype: "Class" }`. */
130
142
  export function isClassDef(value: unknown): value is JxClassDef {
131
143
  return isJsonObject(value) && value.$prototype === "Class";
package/src/schema.ts CHANGED
@@ -96,7 +96,24 @@ import { projectConfigSchema } from "../defs/project-config.schema";
96
96
 
97
97
  // ─── Web standards data loader ────────────────────────────────────────────────
98
98
 
99
- async function loadWebData() {
99
+ let webDataCache: Promise<{
100
+ cssProps: string[];
101
+ eventHandlers: string[];
102
+ tagExamples: string[];
103
+ }> | null = null;
104
+
105
+ /**
106
+ * Web-standards parsing (WebIDL / CSS / HTML elements) is expensive and the data is static for the
107
+ * Process, so memoize it. generateSchema runs several times across a build or test run, and
108
+ * Re-parsing on each call made the validateDocument / CLI tests load-sensitive (occasionally past
109
+ * The 5s test timeout under full-suite CPU contention).
110
+ */
111
+ function loadWebData() {
112
+ webDataCache ??= computeWebData();
113
+ return webDataCache;
114
+ }
115
+
116
+ async function computeWebData() {
100
117
  const [elementsData, cssData, idlData] = await Promise.all([
101
118
  listElements(),
102
119
  css.listAll(),
@@ -487,13 +504,30 @@ export async function generateSchemaString() {
487
504
  return JSON.stringify(await generateSchema(), null, 2);
488
505
  }
489
506
 
507
+ // Minimal structural types for the optional `ajv` / `ajv-formats` peer deps
508
+ // (no direct dependency, and shipping no types usable from here).
509
+ interface AjvValidateFn {
510
+ (doc: unknown): boolean;
511
+ errors?: unknown[] | null;
512
+ }
513
+ interface AjvInstance {
514
+ compile: (schema: unknown) => AjvValidateFn;
515
+ }
516
+ type AjvCtor = new (opts: {
517
+ allErrors: boolean;
518
+ ownProperties: boolean;
519
+ strict: boolean;
520
+ }) => AjvInstance;
521
+ type AddFormatsFn = (ajv: AjvInstance) => void;
522
+
490
523
  export async function validateDocument(doc: Record<string, unknown>) {
491
- let Ajv, addFormats;
524
+ let Ajv: AjvCtor, addFormats: AddFormatsFn;
492
525
  try {
526
+ // The generated schema is JSON Schema 2020-12, so use the matching Ajv build
527
+ // (the default `ajv` export is draft-07 and can't compile a 2020-12 schema).
528
+ ({ default: Ajv } = (await import("ajv/dist/2020")) as { default: AjvCtor });
493
529
  // @ts-expect-error — optional peer dependency
494
- ({ default: Ajv } = await import("ajv"));
495
- // @ts-expect-error — optional peer dependency
496
- ({ default: addFormats } = await import("ajv-formats"));
530
+ ({ default: addFormats } = (await import("ajv-formats")) as { default: AddFormatsFn });
497
531
  } catch {
498
532
  throw new Error("Schema validation requires ajv and ajv-formats: bun add ajv ajv-formats");
499
533
  }
@@ -510,11 +544,11 @@ export async function validateDocument(doc: Record<string, unknown>) {
510
544
 
511
545
  // ─── CLI ──────────────────────────────────────────────────────────────────────
512
546
 
513
- if (process.argv[1] && process.argv[1].endsWith("schema.ts")) {
547
+ async function runSchemaCli() {
514
548
  const { writeFileSync } = await import("node:fs");
515
549
  const { resolve, dirname } = await import("node:path");
516
550
 
517
- const schemaDir = dirname(resolve(process.argv[1], ".."));
551
+ const schemaDir = dirname(resolve(process.argv[1] as string, ".."));
518
552
 
519
553
  const componentSchema = await generateSchema();
520
554
  const projectSchema = generateProjectSchema();
@@ -539,3 +573,9 @@ if (process.argv[1] && process.argv[1].endsWith("schema.ts")) {
539
573
  console.error(" class-schema.json");
540
574
  }
541
575
  }
576
+
577
+ // Runs when invoked as a script or driven by a test that stages argv[1]. The build lives in an async
578
+ // Function rather than a top-level await: Bun's test runtime drops a dynamically-imported module's
579
+ // Top-level-await continuation on Windows. `ready` lets tests await the same sequence.
580
+ // oxlint-disable-next-line unicorn/prefer-top-level-await
581
+ export const ready = process.argv[1]?.endsWith("schema.ts") ? runSchemaCli() : undefined;
package/types.ts CHANGED
@@ -198,6 +198,13 @@ export interface JxElement {
198
198
  tagName?: string;
199
199
  textContent?: string | null | JxRef;
200
200
  innerHTML?: string;
201
+ /**
202
+ * Child nodes. A repeater (`{ $prototype: "Array", … }`) may appear as a member of the array —
203
+ * nestled among siblings or as the sole child — and is structurally a `JxElement` (its
204
+ * `items`/`map`/`filter`/`sort` are absorbed by the open index signature; narrow with
205
+ * `isMappedArray`). The bare `| JxMappedArray` form (whole children slot is one repeater) is
206
+ * retained for backward compatibility with legacy docs.
207
+ */
201
208
  children?: (JxElement | string)[] | JxMappedArray;
202
209
  style?: JxStyle;
203
210
  attributes?: Record<string, JxAttributeValue>;
@@ -471,6 +478,12 @@ export interface JxMutableNode {
471
478
  tagName?: string;
472
479
  textContent?: string | null | JxRef;
473
480
  innerHTML?: string;
481
+ /**
482
+ * Child nodes. The editor models a mapped-array (repeater) member as a `JxMutableNode` carrying
483
+ * `$prototype: "Array"` (plus `items`/`map`/`filter`/`sort` below), so members stay assignable
484
+ * here. The bare `| JxMappedArray` form (whole children slot is one repeater) is retained for
485
+ * backward compatibility.
486
+ */
474
487
  children?: (JxMutableNode | string)[] | JxMappedArray;
475
488
  style?: JxStyle;
476
489
  attributes?: Record<string, JxAttributeValue>;