@jxsuite/schema 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/class-schema.json +290 -0
- package/package.json +25 -0
- package/project-schema.json +264 -0
- package/schema.json +18891 -0
- package/src/schema.js +1514 -0
package/src/schema.js
ADDED
|
@@ -0,0 +1,1514 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* jx-schema.js — Jx JSON Schema 2020-12 meta-schema generator
|
|
3
|
+
* @version 1.0.0
|
|
4
|
+
* @license MIT
|
|
5
|
+
*
|
|
6
|
+
* Generates a comprehensive JSON Schema 2020-12 document that validates Jx
|
|
7
|
+
* source files. All HTML element names, CSS property names, and DOM event
|
|
8
|
+
* handler names are derived at generation time from upstream web standards via:
|
|
9
|
+
*
|
|
10
|
+
* webref/elements — HTML element tag names
|
|
11
|
+
* webref/css — CSS property names (camelCase CSSOM)
|
|
12
|
+
* webref/idl — DOM EventHandler attribute names
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* import { generateSchema } from './schema.js';
|
|
16
|
+
* const schema = await generateSchema();
|
|
17
|
+
* fs.writeFileSync('schema.json', JSON.stringify(schema, null, 2));
|
|
18
|
+
*
|
|
19
|
+
* CLI:
|
|
20
|
+
* bun run schema.js [output-path]
|
|
21
|
+
*
|
|
22
|
+
* @module jx-schema
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { listAll as listElements } from "@webref/elements";
|
|
26
|
+
import css from "@webref/css";
|
|
27
|
+
import idl from "@webref/idl";
|
|
28
|
+
|
|
29
|
+
// ─── Built-in $prototype values (Jx-specific, not from web standards) ─────
|
|
30
|
+
|
|
31
|
+
const BUILT_IN_PROTOTYPES = [
|
|
32
|
+
"Function",
|
|
33
|
+
"Request",
|
|
34
|
+
"URLSearchParams",
|
|
35
|
+
"FormData",
|
|
36
|
+
"LocalStorage",
|
|
37
|
+
"SessionStorage",
|
|
38
|
+
"Cookie",
|
|
39
|
+
"IndexedDB",
|
|
40
|
+
"Array",
|
|
41
|
+
"Set",
|
|
42
|
+
"Map",
|
|
43
|
+
"Blob",
|
|
44
|
+
"ReadableStream",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// ─── Web standards data loader ────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Fetch and normalise the three webref datasets in parallel.
|
|
51
|
+
*
|
|
52
|
+
* @returns {Promise<{ tagExamples: string[]; cssProps: string[]; eventHandlers: string[] }>}
|
|
53
|
+
*/
|
|
54
|
+
async function loadWebData() {
|
|
55
|
+
const [elementsData, cssData, idlData] = await Promise.all([
|
|
56
|
+
listElements(),
|
|
57
|
+
css.listAll(),
|
|
58
|
+
idl.parseAll(),
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
// ── Tag names ──────────────────────────────────────────────────────────────
|
|
62
|
+
const tagSet = new Set();
|
|
63
|
+
for (const { elements } of Object.values(elementsData)) {
|
|
64
|
+
for (const el of elements) {
|
|
65
|
+
if (!el.obsolete) tagSet.add(el.name);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const tagExamples = [...tagSet].sort();
|
|
69
|
+
|
|
70
|
+
// ── CSS camelCase property names (CSSOM styleDeclaration) ─────────────────
|
|
71
|
+
const cssSet = new Set();
|
|
72
|
+
for (const prop of cssData.properties) {
|
|
73
|
+
for (const decl of prop.styleDeclaration ?? []) {
|
|
74
|
+
cssSet.add(decl);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const cssProps = [...cssSet].sort();
|
|
78
|
+
|
|
79
|
+
// ── EventHandler attribute names from IDL ─────────────────────────────────
|
|
80
|
+
const handlerSet = new Set();
|
|
81
|
+
for (const ast of Object.values(idlData)) {
|
|
82
|
+
for (const def of ast) {
|
|
83
|
+
if (def.type !== "interface" && def.type !== "interface mixin") continue;
|
|
84
|
+
for (const member of def.members) {
|
|
85
|
+
if (
|
|
86
|
+
member.type === "attribute" &&
|
|
87
|
+
member.name?.startsWith("on") &&
|
|
88
|
+
typeof member.idlType?.idlType === "string" &&
|
|
89
|
+
member.idlType.idlType === "EventHandler"
|
|
90
|
+
) {
|
|
91
|
+
handlerSet.add(member.name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const eventHandlers = [...handlerSet].sort();
|
|
97
|
+
|
|
98
|
+
return { tagExamples, cssProps, eventHandlers };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─── Generator ────────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Generate the full Jx meta-schema as a plain JavaScript object. Derives HTML elements, CSS
|
|
105
|
+
* properties, and event handlers from upstream web standards data at generation time.
|
|
106
|
+
*
|
|
107
|
+
* @returns {Promise<object>} JSON Schema 2020-12 document
|
|
108
|
+
*/
|
|
109
|
+
export async function generateSchema() {
|
|
110
|
+
const { tagExamples, cssProps, eventHandlers } = await loadWebData();
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
114
|
+
$id: "https://jxsuite.com/schema/v1",
|
|
115
|
+
title: "Jx Document",
|
|
116
|
+
description:
|
|
117
|
+
"Schema for Jx component files. " +
|
|
118
|
+
"A Jx document is a JSON object that declaratively describes a reactive " +
|
|
119
|
+
"web component: its structure (DOM tree), styling, type definitions ($defs), " +
|
|
120
|
+
"runtime state, and inline or external functions. Reactivity is powered by @vue/reactivity.",
|
|
121
|
+
type: "object",
|
|
122
|
+
|
|
123
|
+
// ── Top-level properties ────────────────────────────────────────────────
|
|
124
|
+
properties: {
|
|
125
|
+
$schema: {
|
|
126
|
+
description: "URI identifying the Jx dialect version. Enables schema-aware IDE tooling.",
|
|
127
|
+
type: "string",
|
|
128
|
+
examples: ["https://jxsuite.com/schema/v1"],
|
|
129
|
+
},
|
|
130
|
+
$id: {
|
|
131
|
+
description: "Component identifier string. Used by tooling and the builder.",
|
|
132
|
+
type: "string",
|
|
133
|
+
examples: ["Counter", "TodoApp", "UserCard"],
|
|
134
|
+
},
|
|
135
|
+
$defs: {
|
|
136
|
+
description:
|
|
137
|
+
"Pure JSON Schema type definitions for this component. " +
|
|
138
|
+
"All entries are reusable type schemas — no runtime artifacts are produced. " +
|
|
139
|
+
"Referenced from state entries via $ref. Naming convention: PascalCase.",
|
|
140
|
+
$ref: "#/$defs/DefsMap",
|
|
141
|
+
},
|
|
142
|
+
state: {
|
|
143
|
+
description:
|
|
144
|
+
"Runtime variables for this component. All entries are reactive by default. " +
|
|
145
|
+
"Entry shape is determined by value type: " +
|
|
146
|
+
"scalar/array → reactive property, string with ${} → computed, " +
|
|
147
|
+
"object with $prototype → function or data source, " +
|
|
148
|
+
"object with type and default → typed reactive property.",
|
|
149
|
+
$ref: "#/$defs/StateMap",
|
|
150
|
+
},
|
|
151
|
+
$media: {
|
|
152
|
+
description:
|
|
153
|
+
"Named media breakpoints following CSS @custom-media convention. " +
|
|
154
|
+
"Keys use the CSS custom property -- prefix.",
|
|
155
|
+
type: "object",
|
|
156
|
+
additionalProperties: { type: "string" },
|
|
157
|
+
examples: [
|
|
158
|
+
{
|
|
159
|
+
"--sm": "(min-width: 640px)",
|
|
160
|
+
"--md": "(min-width: 768px)",
|
|
161
|
+
"--dark": "(prefers-color-scheme: dark)",
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
$elements: {
|
|
166
|
+
description:
|
|
167
|
+
"Custom element dependencies. Items are either $ref objects pointing to JX " +
|
|
168
|
+
"element definitions, or bare npm package name strings for web component libraries.",
|
|
169
|
+
type: "array",
|
|
170
|
+
items: {
|
|
171
|
+
oneOf: [
|
|
172
|
+
{
|
|
173
|
+
type: "object",
|
|
174
|
+
required: ["$ref"],
|
|
175
|
+
properties: { $ref: { type: "string" } },
|
|
176
|
+
additionalProperties: false,
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: "string",
|
|
180
|
+
description: "npm package specifier (must declare customElements in package.json)",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
$head: {
|
|
186
|
+
description:
|
|
187
|
+
"Page-level <head> entries. Array of element definitions for meta tags, " +
|
|
188
|
+
"link tags, script tags, etc. Merged with layout and site-level $head entries.",
|
|
189
|
+
type: "array",
|
|
190
|
+
items: { $ref: "#/$defs/ElementDef" },
|
|
191
|
+
},
|
|
192
|
+
$layout: {
|
|
193
|
+
description:
|
|
194
|
+
"Layout reference for pages. String path to a layout JSON file, " +
|
|
195
|
+
"or false to opt out of the default layout.",
|
|
196
|
+
oneOf: [{ type: "string" }, { type: "boolean", const: false }],
|
|
197
|
+
examples: ["./layouts/base.json"],
|
|
198
|
+
},
|
|
199
|
+
$paths: {
|
|
200
|
+
description:
|
|
201
|
+
"Dynamic route parameters. Maps parameter names to data sources " +
|
|
202
|
+
"for generating one page per entry at build time.",
|
|
203
|
+
type: "object",
|
|
204
|
+
},
|
|
205
|
+
title: {
|
|
206
|
+
description:
|
|
207
|
+
"Page title. Can be a static string or a template string with ${} expressions.",
|
|
208
|
+
$ref: "#/$defs/StringOrRef",
|
|
209
|
+
},
|
|
210
|
+
imports: {
|
|
211
|
+
description:
|
|
212
|
+
"Import map: $prototype names to .class.json file paths. " +
|
|
213
|
+
"Allows state entries to reference external classes by name without $src.",
|
|
214
|
+
type: "object",
|
|
215
|
+
additionalProperties: { type: "string" },
|
|
216
|
+
},
|
|
217
|
+
observedAttributes: {
|
|
218
|
+
description:
|
|
219
|
+
"HTML attributes the custom element watches for changes. " +
|
|
220
|
+
"Follows the Web Components observedAttributes convention.",
|
|
221
|
+
type: "array",
|
|
222
|
+
items: { type: "string" },
|
|
223
|
+
},
|
|
224
|
+
cases: {
|
|
225
|
+
description:
|
|
226
|
+
"Switch cases object. Maps case values to element definitions or external " +
|
|
227
|
+
"component refs. Used alongside $switch for dynamic component rendering.",
|
|
228
|
+
type: "object",
|
|
229
|
+
additionalProperties: {
|
|
230
|
+
oneOf: [{ $ref: "#/$defs/ElementDef" }, { $ref: "#/$defs/ExternalComponentRef" }],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
tagName: { $ref: "#/$defs/TagName" },
|
|
234
|
+
children: { $ref: "#/$defs/ChildrenValue" },
|
|
235
|
+
style: { $ref: "#/$defs/StyleObject" },
|
|
236
|
+
attributes: { $ref: "#/$defs/AttributesObject" },
|
|
237
|
+
},
|
|
238
|
+
additionalProperties: { $ref: "#/$defs/ElementPropertyValue" },
|
|
239
|
+
|
|
240
|
+
// ── Reusable sub-schemas ────────────────────────────────────────────────
|
|
241
|
+
$defs: {
|
|
242
|
+
// ── $defs map (type definitions only) ────────────────────────────────
|
|
243
|
+
DefsMap: {
|
|
244
|
+
description:
|
|
245
|
+
"Map of reusable JSON Schema type definitions. " +
|
|
246
|
+
"Keys are PascalCase type names. No runtime artifacts are produced.",
|
|
247
|
+
type: "object",
|
|
248
|
+
additionalProperties: { $ref: "#/$defs/TypeDefEntry" },
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
TypeDefEntry: {
|
|
252
|
+
description:
|
|
253
|
+
"A $defs type definition entry. Must be a pure JSON Schema type " +
|
|
254
|
+
"definition or a class definition (.class.json format).",
|
|
255
|
+
oneOf: [{ $ref: "#/$defs/PureTypeDef" }, { $ref: "#/$defs/ClassDef" }],
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// ── state map (runtime variables) ───────────────────────────────────
|
|
259
|
+
StateMap: {
|
|
260
|
+
description:
|
|
261
|
+
"Map of runtime variables. Keys are camelCase (public) or #-prefixed (private). " +
|
|
262
|
+
"All entries are reactive by default.",
|
|
263
|
+
type: "object",
|
|
264
|
+
additionalProperties: { $ref: "#/$defs/StateEntry" },
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
StateEntry: {
|
|
268
|
+
description:
|
|
269
|
+
"A single state entry. Shape is determined by value type: " +
|
|
270
|
+
"scalar/array → naked reactive property, string with ${} → computed, " +
|
|
271
|
+
'object with $prototype: "Function" → function, ' +
|
|
272
|
+
"object with $prototype: <other> → data source, " +
|
|
273
|
+
"object with type and default → typed reactive property, " +
|
|
274
|
+
"plain object → naked object reactive property.",
|
|
275
|
+
oneOf: [
|
|
276
|
+
// Shape 1: Naked value (scalar)
|
|
277
|
+
{ type: "number" },
|
|
278
|
+
{ type: "boolean" },
|
|
279
|
+
{ type: "null" },
|
|
280
|
+
{ type: "string" },
|
|
281
|
+
{ type: "array" },
|
|
282
|
+
// Shape 2: Typed value (object with type and default, no $prototype)
|
|
283
|
+
{ $ref: "#/$defs/TypedStateDef" },
|
|
284
|
+
// Shape 3: Function ($prototype: "Function")
|
|
285
|
+
{ $ref: "#/$defs/FunctionDef" },
|
|
286
|
+
// Shape 4: External class / data source ($prototype: <other>)
|
|
287
|
+
{ $ref: "#/$defs/ExternalClassDef" },
|
|
288
|
+
// Shape 1: Naked object (plain object, no reserved keys)
|
|
289
|
+
{
|
|
290
|
+
type: "object",
|
|
291
|
+
not: {
|
|
292
|
+
anyOf: [
|
|
293
|
+
{ required: ["$prototype"] },
|
|
294
|
+
{ required: ["default"] },
|
|
295
|
+
{ required: ["type"] },
|
|
296
|
+
],
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
// ── Shape 2: Typed State Variable ──────────────────────────────────
|
|
303
|
+
TypedStateDef: {
|
|
304
|
+
description:
|
|
305
|
+
"A typed reactive state variable with explicit type and default value. " +
|
|
306
|
+
"The type property is a JSON Schema or $ref to a $defs type definition. " +
|
|
307
|
+
"The default property is the initial runtime value.",
|
|
308
|
+
type: "object",
|
|
309
|
+
required: ["default"],
|
|
310
|
+
properties: {
|
|
311
|
+
default: { description: "Initial state value." },
|
|
312
|
+
type: {
|
|
313
|
+
description:
|
|
314
|
+
"JSON Schema type definition, $ref to a $defs type, or JSON Schema type string.",
|
|
315
|
+
oneOf: [{ type: "string" }, { type: "object" }],
|
|
316
|
+
},
|
|
317
|
+
description: { type: "string" },
|
|
318
|
+
attribute: {
|
|
319
|
+
description: "Linked HTML attribute name for CEM extraction.",
|
|
320
|
+
type: "string",
|
|
321
|
+
},
|
|
322
|
+
reflects: {
|
|
323
|
+
description: "Whether property changes reflect back to the HTML attribute.",
|
|
324
|
+
type: "boolean",
|
|
325
|
+
},
|
|
326
|
+
deprecated: {
|
|
327
|
+
description: "Deprecation notice for CEM extraction.",
|
|
328
|
+
oneOf: [{ type: "boolean" }, { type: "string" }],
|
|
329
|
+
},
|
|
330
|
+
examples: { type: "array" },
|
|
331
|
+
$ref: { description: "Reference to a shared type definition.", type: "string" },
|
|
332
|
+
},
|
|
333
|
+
not: { required: ["$prototype"] },
|
|
334
|
+
},
|
|
335
|
+
|
|
336
|
+
// ── Shape 2b: Pure Type Definition ───────────────────────────────────
|
|
337
|
+
PureTypeDef: {
|
|
338
|
+
description:
|
|
339
|
+
"A reusable JSON Schema type definition for tooling only. " +
|
|
340
|
+
"No function, no runtime artifact. " +
|
|
341
|
+
"Referenced from state entries via $ref. " +
|
|
342
|
+
"Naming convention: PascalCase.",
|
|
343
|
+
type: "object",
|
|
344
|
+
required: ["type"],
|
|
345
|
+
properties: {
|
|
346
|
+
type: { $ref: "#/$defs/JsonSchemaType" },
|
|
347
|
+
description: { type: "string" },
|
|
348
|
+
enum: { type: "array" },
|
|
349
|
+
minimum: { type: "number" },
|
|
350
|
+
maximum: { type: "number" },
|
|
351
|
+
minLength: { type: "integer", minimum: 0 },
|
|
352
|
+
maxLength: { type: "integer", minimum: 0 },
|
|
353
|
+
pattern: { type: "string" },
|
|
354
|
+
items: {},
|
|
355
|
+
properties: { type: "object" },
|
|
356
|
+
required: { type: "array", items: { type: "string" } },
|
|
357
|
+
examples: { type: "array" },
|
|
358
|
+
},
|
|
359
|
+
not: {
|
|
360
|
+
anyOf: [{ required: ["default"] }, { required: ["$prototype"] }],
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
// ── Shape 4: Function ────────────────────────────────────────────────
|
|
365
|
+
FunctionDef: {
|
|
366
|
+
description:
|
|
367
|
+
'A function declaration. $prototype must be "Function". ' +
|
|
368
|
+
"body (inline) and $src (external) are mutually exclusive. " +
|
|
369
|
+
"First parameter is always state (the reactive scope).",
|
|
370
|
+
type: "object",
|
|
371
|
+
required: ["$prototype"],
|
|
372
|
+
properties: {
|
|
373
|
+
$prototype: { type: "string", const: "Function" },
|
|
374
|
+
body: {
|
|
375
|
+
description: "Inline function body string. First implicit parameter is state.",
|
|
376
|
+
type: "string",
|
|
377
|
+
examples: [
|
|
378
|
+
"state.count++",
|
|
379
|
+
'state.items.push({ id: Date.now(), text: "", done: false })',
|
|
380
|
+
'return state.score >= 90 ? "gold" : "silver"',
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
parameters: {
|
|
384
|
+
description:
|
|
385
|
+
"Function parameters (after the implicit state parameter). " +
|
|
386
|
+
"Accepts CEM-compatible parameter objects or bare string names for backward compatibility.",
|
|
387
|
+
type: "array",
|
|
388
|
+
items: {
|
|
389
|
+
oneOf: [{ type: "string" }, { $ref: "#/$defs/CemParameter" }],
|
|
390
|
+
},
|
|
391
|
+
examples: [
|
|
392
|
+
["event"],
|
|
393
|
+
[{ name: "event", type: { text: "Event" } }],
|
|
394
|
+
[{ name: "id", type: { text: "number" }, description: "Item identifier" }],
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
name: {
|
|
398
|
+
description: "Explicit function name. Defaults to the state key name.",
|
|
399
|
+
type: "string",
|
|
400
|
+
},
|
|
401
|
+
$src: {
|
|
402
|
+
description: "External module specifier. Mutually exclusive with body.",
|
|
403
|
+
type: "string",
|
|
404
|
+
examples: ["./counter.js", "npm:@myorg/validators"],
|
|
405
|
+
},
|
|
406
|
+
$export: {
|
|
407
|
+
description: "Named export in $src module. Defaults to the state key name.",
|
|
408
|
+
type: "string",
|
|
409
|
+
},
|
|
410
|
+
type: {
|
|
411
|
+
description: "Return type for tooling (JSON Schema or CEM { text } format).",
|
|
412
|
+
},
|
|
413
|
+
emits: {
|
|
414
|
+
description:
|
|
415
|
+
"Array of CEM-compatible Event objects this function dispatches. " +
|
|
416
|
+
"Used for CEM extraction and studio event discovery.",
|
|
417
|
+
type: "array",
|
|
418
|
+
items: { $ref: "#/$defs/CemEvent" },
|
|
419
|
+
},
|
|
420
|
+
description: { type: "string" },
|
|
421
|
+
},
|
|
422
|
+
additionalProperties: false,
|
|
423
|
+
},
|
|
424
|
+
|
|
425
|
+
// ── Shape 6: Class Definition ($prototype: "Class") ────────────────
|
|
426
|
+
ClassDef: {
|
|
427
|
+
description:
|
|
428
|
+
'A .class.json schema-defined class. $prototype must be "Class". ' +
|
|
429
|
+
"Defines fields, constructor, methods, and type parameters via $defs. " +
|
|
430
|
+
"Optionally points to a JS module via $implementation for hybrid execution.",
|
|
431
|
+
type: "object",
|
|
432
|
+
required: ["$prototype", "title"],
|
|
433
|
+
properties: {
|
|
434
|
+
$schema: { type: "string" },
|
|
435
|
+
$id: { type: "string" },
|
|
436
|
+
$prototype: { type: "string", const: "Class" },
|
|
437
|
+
title: {
|
|
438
|
+
description: "PascalCase class name, used as the export name.",
|
|
439
|
+
type: "string",
|
|
440
|
+
},
|
|
441
|
+
description: { type: "string" },
|
|
442
|
+
extends: {
|
|
443
|
+
description: "Base class — string name or $ref to another .class.json.",
|
|
444
|
+
oneOf: [
|
|
445
|
+
{ type: "string" },
|
|
446
|
+
{ type: "object", required: ["$ref"], properties: { $ref: { type: "string" } } },
|
|
447
|
+
],
|
|
448
|
+
},
|
|
449
|
+
$implementation: {
|
|
450
|
+
description: "Relative path to a JS module containing the actual class implementation.",
|
|
451
|
+
type: "string",
|
|
452
|
+
},
|
|
453
|
+
$defs: {
|
|
454
|
+
description: "Class members: parameters, returnTypes, fields, constructor, methods.",
|
|
455
|
+
type: "object",
|
|
456
|
+
properties: {
|
|
457
|
+
parameters: {
|
|
458
|
+
description: "Reusable typed parameter schemas, keyed by name.",
|
|
459
|
+
type: "object",
|
|
460
|
+
additionalProperties: { $ref: "#/$defs/ClassParameterDef" },
|
|
461
|
+
},
|
|
462
|
+
returnTypes: {
|
|
463
|
+
description: "Output type schemas, keyed by name.",
|
|
464
|
+
type: "object",
|
|
465
|
+
additionalProperties: { type: "object" },
|
|
466
|
+
},
|
|
467
|
+
fields: {
|
|
468
|
+
description: "Class fields with role, access, scope, and type information.",
|
|
469
|
+
type: "object",
|
|
470
|
+
additionalProperties: { $ref: "#/$defs/ClassFieldDef" },
|
|
471
|
+
},
|
|
472
|
+
constructor: { $ref: "#/$defs/ClassConstructorDef" },
|
|
473
|
+
methods: {
|
|
474
|
+
description: "Class methods and accessors.",
|
|
475
|
+
type: "object",
|
|
476
|
+
additionalProperties: { $ref: "#/$defs/ClassMethodDef" },
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
},
|
|
481
|
+
additionalProperties: false,
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
ClassParameterDef: {
|
|
485
|
+
description: "A typed parameter definition for a class.",
|
|
486
|
+
type: "object",
|
|
487
|
+
required: ["identifier"],
|
|
488
|
+
properties: {
|
|
489
|
+
identifier: { type: "string" },
|
|
490
|
+
type: {},
|
|
491
|
+
format: {
|
|
492
|
+
description: 'When "json-schema", this parameter\'s value is itself a JSON Schema.',
|
|
493
|
+
type: "string",
|
|
494
|
+
},
|
|
495
|
+
description: { type: "string" },
|
|
496
|
+
default: {},
|
|
497
|
+
examples: { type: "array" },
|
|
498
|
+
},
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
ClassFieldDef: {
|
|
502
|
+
description: "A class field definition with access control and scope.",
|
|
503
|
+
type: "object",
|
|
504
|
+
properties: {
|
|
505
|
+
role: { type: "string", const: "field" },
|
|
506
|
+
access: { type: "string", enum: ["public", "private", "protected"] },
|
|
507
|
+
scope: { type: "string", enum: ["instance", "static"] },
|
|
508
|
+
identifier: { type: "string" },
|
|
509
|
+
type: {},
|
|
510
|
+
$prototype: {
|
|
511
|
+
description: 'Data source prototype for this field (e.g., "Request").',
|
|
512
|
+
type: "string",
|
|
513
|
+
},
|
|
514
|
+
initializer: {},
|
|
515
|
+
default: {},
|
|
516
|
+
description: { type: "string" },
|
|
517
|
+
examples: { type: "array" },
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
|
|
521
|
+
ClassConstructorDef: {
|
|
522
|
+
description: "Class constructor definition.",
|
|
523
|
+
type: "object",
|
|
524
|
+
properties: {
|
|
525
|
+
role: { type: "string", const: "constructor" },
|
|
526
|
+
$prototype: { type: "string", const: "Function" },
|
|
527
|
+
parameters: {
|
|
528
|
+
type: "array",
|
|
529
|
+
items: {
|
|
530
|
+
oneOf: [
|
|
531
|
+
{
|
|
532
|
+
type: "object",
|
|
533
|
+
required: ["$ref"],
|
|
534
|
+
properties: { $ref: { type: "string" } },
|
|
535
|
+
additionalProperties: false,
|
|
536
|
+
},
|
|
537
|
+
{ $ref: "#/$defs/ClassParameterDef" },
|
|
538
|
+
],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
superCall: {
|
|
542
|
+
type: "object",
|
|
543
|
+
properties: {
|
|
544
|
+
arguments: { type: "array", items: { type: "string" } },
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
body: {
|
|
548
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
ClassMethodDef: {
|
|
554
|
+
description: "A class method or accessor definition.",
|
|
555
|
+
type: "object",
|
|
556
|
+
properties: {
|
|
557
|
+
role: { type: "string", enum: ["method", "accessor"] },
|
|
558
|
+
$prototype: { type: "string", const: "Function" },
|
|
559
|
+
access: { type: "string", enum: ["public", "private", "protected"] },
|
|
560
|
+
scope: { type: "string", enum: ["instance", "static"] },
|
|
561
|
+
identifier: { type: "string" },
|
|
562
|
+
parameters: {
|
|
563
|
+
type: "array",
|
|
564
|
+
items: {
|
|
565
|
+
oneOf: [
|
|
566
|
+
{
|
|
567
|
+
type: "object",
|
|
568
|
+
required: ["$ref"],
|
|
569
|
+
properties: { $ref: { type: "string" } },
|
|
570
|
+
additionalProperties: false,
|
|
571
|
+
},
|
|
572
|
+
{ $ref: "#/$defs/ClassParameterDef" },
|
|
573
|
+
],
|
|
574
|
+
},
|
|
575
|
+
},
|
|
576
|
+
returnType: {},
|
|
577
|
+
body: {
|
|
578
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
579
|
+
},
|
|
580
|
+
getter: {
|
|
581
|
+
type: "object",
|
|
582
|
+
properties: { body: { type: "string" } },
|
|
583
|
+
},
|
|
584
|
+
setter: {
|
|
585
|
+
type: "object",
|
|
586
|
+
properties: {
|
|
587
|
+
parameters: { type: "array" },
|
|
588
|
+
body: { type: "string" },
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
description: { type: "string" },
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
|
|
595
|
+
// ── Shape 5: External Class ──────────────────────────────────────────
|
|
596
|
+
ExternalClassDef: {
|
|
597
|
+
description:
|
|
598
|
+
'An external class / data source. $prototype is a constructor name (not "Function"). ' +
|
|
599
|
+
"When $prototype is not in the built-in registry, $src is required. " +
|
|
600
|
+
"All state entries are reactive by default.",
|
|
601
|
+
type: "object",
|
|
602
|
+
required: ["$prototype"],
|
|
603
|
+
properties: {
|
|
604
|
+
$prototype: {
|
|
605
|
+
description: "Constructor name — built-in Web API class or external class name.",
|
|
606
|
+
type: "string",
|
|
607
|
+
not: { const: "Function" },
|
|
608
|
+
examples: [
|
|
609
|
+
...BUILT_IN_PROTOTYPES.filter((p) => p !== "Function"),
|
|
610
|
+
"MarkdownCollection",
|
|
611
|
+
"MyParser",
|
|
612
|
+
],
|
|
613
|
+
},
|
|
614
|
+
$src: {
|
|
615
|
+
description: "External module specifier. Required when $prototype is not a built-in.",
|
|
616
|
+
type: "string",
|
|
617
|
+
examples: ["@jxplatform/md", "./lib/my-parser.js", "npm:@myorg/data"],
|
|
618
|
+
},
|
|
619
|
+
$export: {
|
|
620
|
+
description: "Named export in $src module. Defaults to the $prototype value.",
|
|
621
|
+
type: "string",
|
|
622
|
+
},
|
|
623
|
+
timing: { type: "string", enum: ["compiler", "server", "client"] },
|
|
624
|
+
manual: { type: "boolean" },
|
|
625
|
+
debounce: { type: "integer", minimum: 0 },
|
|
626
|
+
url: { $ref: "#/$defs/StringOrRef" },
|
|
627
|
+
method: {
|
|
628
|
+
type: "string",
|
|
629
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"],
|
|
630
|
+
},
|
|
631
|
+
headers: { type: "object", additionalProperties: { type: "string" } },
|
|
632
|
+
body: {},
|
|
633
|
+
responseType: {
|
|
634
|
+
type: "string",
|
|
635
|
+
enum: ["json", "text", "blob", "arraybuffer", "document", ""],
|
|
636
|
+
},
|
|
637
|
+
key: { type: "string" },
|
|
638
|
+
name: { type: "string" },
|
|
639
|
+
maxAge: { type: "integer" },
|
|
640
|
+
expires: { type: "string" },
|
|
641
|
+
path: { type: "string" },
|
|
642
|
+
domain: { type: "string" },
|
|
643
|
+
secure: { type: "boolean" },
|
|
644
|
+
sameSite: { type: "string", enum: ["strict", "lax", "none"] },
|
|
645
|
+
database: { type: "string" },
|
|
646
|
+
store: { type: "string" },
|
|
647
|
+
version: { type: "integer", minimum: 1 },
|
|
648
|
+
keyPath: { type: "string" },
|
|
649
|
+
autoIncrement: { type: "boolean" },
|
|
650
|
+
indexes: {
|
|
651
|
+
type: "array",
|
|
652
|
+
items: {
|
|
653
|
+
type: "object",
|
|
654
|
+
required: ["name", "keyPath"],
|
|
655
|
+
properties: {
|
|
656
|
+
name: { type: "string" },
|
|
657
|
+
keyPath: {
|
|
658
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
659
|
+
},
|
|
660
|
+
unique: { type: "boolean" },
|
|
661
|
+
},
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
default: {},
|
|
665
|
+
description: { type: "string" },
|
|
666
|
+
items: {},
|
|
667
|
+
map: { $ref: "#/$defs/ElementDef" },
|
|
668
|
+
filter: { $ref: "#/$defs/RefObject" },
|
|
669
|
+
sort: { $ref: "#/$defs/RefObject" },
|
|
670
|
+
src: {
|
|
671
|
+
description: "Configuration property passed to external class constructor.",
|
|
672
|
+
type: "string",
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
},
|
|
676
|
+
|
|
677
|
+
// ── Element definition ────────────────────────────────────────────────
|
|
678
|
+
ElementDef: {
|
|
679
|
+
description: "A Jx element definition. Maps directly to a DOM element.",
|
|
680
|
+
type: "object",
|
|
681
|
+
required: ["tagName"],
|
|
682
|
+
properties: {
|
|
683
|
+
tagName: { $ref: "#/$defs/TagName" },
|
|
684
|
+
id: { type: "string" },
|
|
685
|
+
className: { $ref: "#/$defs/StringOrRef" },
|
|
686
|
+
textContent: { $ref: "#/$defs/StringOrRef" },
|
|
687
|
+
innerHTML: { $ref: "#/$defs/StringOrRef" },
|
|
688
|
+
innerText: { $ref: "#/$defs/StringOrRef" },
|
|
689
|
+
hidden: { $ref: "#/$defs/BoolOrRef" },
|
|
690
|
+
tabIndex: { $ref: "#/$defs/NumberOrRef" },
|
|
691
|
+
title: { $ref: "#/$defs/StringOrRef" },
|
|
692
|
+
lang: { $ref: "#/$defs/StringOrRef" },
|
|
693
|
+
dir: { type: "string", enum: ["ltr", "rtl", "auto"] },
|
|
694
|
+
value: { $ref: "#/$defs/StringOrRef" },
|
|
695
|
+
checked: { $ref: "#/$defs/BoolOrRef" },
|
|
696
|
+
disabled: { $ref: "#/$defs/BoolOrRef" },
|
|
697
|
+
selected: { $ref: "#/$defs/BoolOrRef" },
|
|
698
|
+
src: { $ref: "#/$defs/StringOrRef" },
|
|
699
|
+
href: { $ref: "#/$defs/StringOrRef" },
|
|
700
|
+
alt: { $ref: "#/$defs/StringOrRef" },
|
|
701
|
+
type: { $ref: "#/$defs/StringOrRef" },
|
|
702
|
+
name: { $ref: "#/$defs/StringOrRef" },
|
|
703
|
+
placeholder: { $ref: "#/$defs/StringOrRef" },
|
|
704
|
+
children: { $ref: "#/$defs/ChildrenValue" },
|
|
705
|
+
style: { $ref: "#/$defs/StyleObject" },
|
|
706
|
+
attributes: { $ref: "#/$defs/AttributesObject" },
|
|
707
|
+
$switch: { $ref: "#/$defs/SwitchDef" },
|
|
708
|
+
$ref: { $ref: "#/$defs/ExternalRef" },
|
|
709
|
+
$props: { $ref: "#/$defs/PropsObject" },
|
|
710
|
+
"$map/item": { $ref: "#/$defs/RefObject" },
|
|
711
|
+
"$map/index": { $ref: "#/$defs/RefObject" },
|
|
712
|
+
// Event handlers (derived from @webref/idl at generation time)
|
|
713
|
+
...buildEventHandlerProperties(eventHandlers),
|
|
714
|
+
},
|
|
715
|
+
additionalProperties: { $ref: "#/$defs/ElementPropertyValue" },
|
|
716
|
+
},
|
|
717
|
+
|
|
718
|
+
// ── Children ─────────────────────────────────────────────────────────
|
|
719
|
+
ChildrenValue: {
|
|
720
|
+
description: "Static array of child definitions, or an Array namespace for dynamic lists.",
|
|
721
|
+
oneOf: [
|
|
722
|
+
{
|
|
723
|
+
type: "array",
|
|
724
|
+
items: {
|
|
725
|
+
oneOf: [{ $ref: "#/$defs/ElementDef" }, { type: "string" }, { type: "number" }],
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
{ $ref: "#/$defs/ArrayNamespace" },
|
|
729
|
+
],
|
|
730
|
+
},
|
|
731
|
+
|
|
732
|
+
ArrayNamespace: {
|
|
733
|
+
description: "Dynamic mapped list. Re-renders when the items state entry changes.",
|
|
734
|
+
type: "object",
|
|
735
|
+
required: ["$prototype", "items", "map"],
|
|
736
|
+
properties: {
|
|
737
|
+
$prototype: { type: "string", const: "Array" },
|
|
738
|
+
items: {
|
|
739
|
+
oneOf: [{ $ref: "#/$defs/RefObject" }, { type: "array" }],
|
|
740
|
+
},
|
|
741
|
+
map: { $ref: "#/$defs/ElementDef" },
|
|
742
|
+
filter: { $ref: "#/$defs/RefObject" },
|
|
743
|
+
sort: { $ref: "#/$defs/RefObject" },
|
|
744
|
+
},
|
|
745
|
+
additionalProperties: false,
|
|
746
|
+
},
|
|
747
|
+
|
|
748
|
+
// ── $switch ───────────────────────────────────────────────────────────
|
|
749
|
+
SwitchDef: {
|
|
750
|
+
description: "Reactive $ref that drives which case to render.",
|
|
751
|
+
type: "object",
|
|
752
|
+
required: ["$ref"],
|
|
753
|
+
properties: { $ref: { $ref: "#/$defs/InternalRef" } },
|
|
754
|
+
additionalProperties: false,
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
SwitchNode: {
|
|
758
|
+
type: "object",
|
|
759
|
+
required: ["$switch", "cases"],
|
|
760
|
+
properties: {
|
|
761
|
+
tagName: { $ref: "#/$defs/TagName" },
|
|
762
|
+
$switch: { $ref: "#/$defs/SwitchDef" },
|
|
763
|
+
cases: {
|
|
764
|
+
type: "object",
|
|
765
|
+
additionalProperties: {
|
|
766
|
+
oneOf: [{ $ref: "#/$defs/ElementDef" }, { $ref: "#/$defs/ExternalComponentRef" }],
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
},
|
|
771
|
+
|
|
772
|
+
// ── Style (CSS properties derived from @webref/css) ───────────────────
|
|
773
|
+
StyleObject: {
|
|
774
|
+
description:
|
|
775
|
+
"CSS style definition. camelCase property names follow CSSOM convention. " +
|
|
776
|
+
"Keys starting with :, ., &, or [ are treated as nested CSS selectors. " +
|
|
777
|
+
"Keys matching $media breakpoint names are treated as responsive rules.",
|
|
778
|
+
type: "object",
|
|
779
|
+
// Known camelCase CSS properties give IDE autocompletion
|
|
780
|
+
properties: buildCssProperties(cssProps),
|
|
781
|
+
// Nested selectors, media breakpoints, and custom / unknown properties
|
|
782
|
+
additionalProperties: {
|
|
783
|
+
oneOf: [
|
|
784
|
+
{ type: "string" },
|
|
785
|
+
{ type: "number" },
|
|
786
|
+
{
|
|
787
|
+
description: "Nested CSS selector or media breakpoint rules.",
|
|
788
|
+
type: "object",
|
|
789
|
+
additionalProperties: { oneOf: [{ type: "string" }, { type: "number" }] },
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
|
|
795
|
+
AttributesObject: {
|
|
796
|
+
description: "HTML attributes and ARIA attributes set via element.setAttribute().",
|
|
797
|
+
type: "object",
|
|
798
|
+
additionalProperties: {
|
|
799
|
+
oneOf: [
|
|
800
|
+
{ type: "string" },
|
|
801
|
+
{ type: "number" },
|
|
802
|
+
{ type: "boolean" },
|
|
803
|
+
{ $ref: "#/$defs/RefObject" },
|
|
804
|
+
],
|
|
805
|
+
},
|
|
806
|
+
},
|
|
807
|
+
|
|
808
|
+
PropsObject: {
|
|
809
|
+
description: "Explicit prop passing at a component boundary.",
|
|
810
|
+
type: "object",
|
|
811
|
+
additionalProperties: {
|
|
812
|
+
oneOf: [
|
|
813
|
+
{ type: "string" },
|
|
814
|
+
{ type: "number" },
|
|
815
|
+
{ type: "boolean" },
|
|
816
|
+
{ type: "array" },
|
|
817
|
+
{ type: "object" },
|
|
818
|
+
{ $ref: "#/$defs/RefObject" },
|
|
819
|
+
],
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
|
|
823
|
+
// ── $ref types ────────────────────────────────────────────────────────
|
|
824
|
+
RefObject: {
|
|
825
|
+
description:
|
|
826
|
+
"A $ref binding. Resolves to a state entry (reactive) or plain value (static).",
|
|
827
|
+
type: "object",
|
|
828
|
+
required: ["$ref"],
|
|
829
|
+
properties: { $ref: { $ref: "#/$defs/AnyRef" } },
|
|
830
|
+
additionalProperties: false,
|
|
831
|
+
},
|
|
832
|
+
|
|
833
|
+
AnyRef: {
|
|
834
|
+
type: "string",
|
|
835
|
+
oneOf: [
|
|
836
|
+
{ $ref: "#/$defs/InternalRef" },
|
|
837
|
+
{ $ref: "#/$defs/StateRef" },
|
|
838
|
+
{ $ref: "#/$defs/ExternalRef" },
|
|
839
|
+
{ $ref: "#/$defs/GlobalRef" },
|
|
840
|
+
{ $ref: "#/$defs/ParentRef" },
|
|
841
|
+
{ $ref: "#/$defs/MapRef" },
|
|
842
|
+
],
|
|
843
|
+
},
|
|
844
|
+
|
|
845
|
+
InternalRef: {
|
|
846
|
+
description: "Reference to a $defs type definition in the current component.",
|
|
847
|
+
type: "string",
|
|
848
|
+
pattern: "^#/\\$defs/",
|
|
849
|
+
examples: ["#/$defs/Count", "#/$defs/TodoItem", "#/$defs/Status"],
|
|
850
|
+
},
|
|
851
|
+
|
|
852
|
+
StateRef: {
|
|
853
|
+
description: "Reference to a state entry (runtime variable) in the current component.",
|
|
854
|
+
type: "string",
|
|
855
|
+
pattern: "^#/state/",
|
|
856
|
+
examples: ["#/state/count", "#/state/addTask", "#/state/items"],
|
|
857
|
+
},
|
|
858
|
+
|
|
859
|
+
ExternalRef: {
|
|
860
|
+
description: "Reference to an external Jx component file.",
|
|
861
|
+
type: "string",
|
|
862
|
+
pattern: "^(\\./|\\.\\./).*\\.json$|^https?://",
|
|
863
|
+
examples: ["./card.json", "https://cdn.example.com/button.json"],
|
|
864
|
+
},
|
|
865
|
+
|
|
866
|
+
ExternalComponentRef: {
|
|
867
|
+
type: "object",
|
|
868
|
+
required: ["$ref"],
|
|
869
|
+
properties: {
|
|
870
|
+
$ref: { $ref: "#/$defs/ExternalRef" },
|
|
871
|
+
$props: { $ref: "#/$defs/PropsObject" },
|
|
872
|
+
},
|
|
873
|
+
},
|
|
874
|
+
|
|
875
|
+
GlobalRef: {
|
|
876
|
+
description: "Reference to a window or document global.",
|
|
877
|
+
type: "string",
|
|
878
|
+
pattern: "^(window|document)#/",
|
|
879
|
+
examples: ["window#/currentUser", "document#/appConfig"],
|
|
880
|
+
},
|
|
881
|
+
|
|
882
|
+
ParentRef: {
|
|
883
|
+
description: "Reference to a named state entry passed via $props from a parent component.",
|
|
884
|
+
type: "string",
|
|
885
|
+
pattern: "^parent#/",
|
|
886
|
+
examples: ["parent#/sharedState", "parent#/theme"],
|
|
887
|
+
},
|
|
888
|
+
|
|
889
|
+
MapRef: {
|
|
890
|
+
description: "Reference to the current Array map iteration context.",
|
|
891
|
+
type: "string",
|
|
892
|
+
pattern: "^\\$map/(item|index)(/.*)?$",
|
|
893
|
+
examples: ["$map/item", "$map/index", "$map/item/text", "$map/item/done"],
|
|
894
|
+
},
|
|
895
|
+
|
|
896
|
+
// ── Property value types ──────────────────────────────────────────────
|
|
897
|
+
ElementPropertyValue: {
|
|
898
|
+
oneOf: [
|
|
899
|
+
{ type: "string" },
|
|
900
|
+
{ type: "number" },
|
|
901
|
+
{ type: "boolean" },
|
|
902
|
+
{ type: "null" },
|
|
903
|
+
{ $ref: "#/$defs/RefObject" },
|
|
904
|
+
],
|
|
905
|
+
},
|
|
906
|
+
|
|
907
|
+
StringOrRef: {
|
|
908
|
+
oneOf: [{ type: "string" }, { $ref: "#/$defs/RefObject" }],
|
|
909
|
+
},
|
|
910
|
+
|
|
911
|
+
BoolOrRef: {
|
|
912
|
+
oneOf: [{ type: "boolean" }, { $ref: "#/$defs/RefObject" }],
|
|
913
|
+
},
|
|
914
|
+
|
|
915
|
+
NumberOrRef: {
|
|
916
|
+
oneOf: [{ type: "number" }, { $ref: "#/$defs/RefObject" }],
|
|
917
|
+
},
|
|
918
|
+
|
|
919
|
+
// ── CEM-compatible definitions ────────────────────────────────────────
|
|
920
|
+
CemParameter: {
|
|
921
|
+
description:
|
|
922
|
+
"A CEM-compatible parameter definition for a function. " +
|
|
923
|
+
"Follows the Custom Elements Manifest Parameter shape.",
|
|
924
|
+
type: "object",
|
|
925
|
+
required: ["name"],
|
|
926
|
+
properties: {
|
|
927
|
+
name: { description: "Parameter name.", type: "string" },
|
|
928
|
+
type: { description: "Parameter type (JSON Schema or CEM { text } format)." },
|
|
929
|
+
description: { description: "Parameter documentation.", type: "string" },
|
|
930
|
+
optional: { description: "Whether the parameter is optional.", type: "boolean" },
|
|
931
|
+
default: { description: "Default value for the parameter." },
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
|
|
935
|
+
CemEvent: {
|
|
936
|
+
description:
|
|
937
|
+
"A CEM-compatible event definition. " +
|
|
938
|
+
"Describes a CustomEvent the function dispatches.",
|
|
939
|
+
type: "object",
|
|
940
|
+
required: ["name"],
|
|
941
|
+
properties: {
|
|
942
|
+
name: { description: "Event name (e.g. 'task-toggled').", type: "string" },
|
|
943
|
+
type: { description: "Event type (e.g. { text: 'CustomEvent' })." },
|
|
944
|
+
description: { description: "Event documentation.", type: "string" },
|
|
945
|
+
deprecated: {
|
|
946
|
+
description: "Deprecation notice.",
|
|
947
|
+
oneOf: [{ type: "boolean" }, { type: "string" }],
|
|
948
|
+
},
|
|
949
|
+
},
|
|
950
|
+
},
|
|
951
|
+
|
|
952
|
+
// ── Primitives ────────────────────────────────────────────────────────
|
|
953
|
+
TagName: {
|
|
954
|
+
description:
|
|
955
|
+
"HTML element tag name or custom element name (must contain a hyphen per Web Components spec).",
|
|
956
|
+
type: "string",
|
|
957
|
+
minLength: 1,
|
|
958
|
+
// Examples derived from @webref/elements at generation time
|
|
959
|
+
examples: [...tagExamples, "my-counter", "todo-app", "user-card"],
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
JsonSchemaType: {
|
|
963
|
+
type: "string",
|
|
964
|
+
enum: ["string", "number", "integer", "boolean", "array", "object", "null"],
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
// ─── Schema building helpers ──────────────────────────────────────────────────
|
|
971
|
+
|
|
972
|
+
/**
|
|
973
|
+
* Build the event handler `properties` fragment for ElementDef. Each key maps to a RefObject
|
|
974
|
+
* pointing at a declared handler function. Derived from @webref/idl EventHandler attributes at
|
|
975
|
+
* generation time.
|
|
976
|
+
*
|
|
977
|
+
* @param {string[]} eventHandlers
|
|
978
|
+
* @returns {object}
|
|
979
|
+
*/
|
|
980
|
+
function buildEventHandlerProperties(eventHandlers) {
|
|
981
|
+
/** @type {Record<string, any>} */
|
|
982
|
+
const properties = {};
|
|
983
|
+
for (const name of eventHandlers) {
|
|
984
|
+
properties[name] = {
|
|
985
|
+
description: `Event handler for the "${name.slice(2)}" event.`,
|
|
986
|
+
$ref: "#/$defs/RefObject",
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
return properties;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* Build the explicit CSS `properties` fragment for StyleObject. Each key is a camelCase CSSOM
|
|
994
|
+
* property name; the value schema accepts strings and numbers (CSS values are always coerced to
|
|
995
|
+
* strings at runtime). Derived from @webref/css styleDeclaration names at generation time.
|
|
996
|
+
*
|
|
997
|
+
* @param {string[]} cssProps
|
|
998
|
+
* @returns {object}
|
|
999
|
+
*/
|
|
1000
|
+
function buildCssProperties(cssProps) {
|
|
1001
|
+
/** @type {Record<string, any>} */
|
|
1002
|
+
const properties = {};
|
|
1003
|
+
for (const name of cssProps) {
|
|
1004
|
+
properties[name] = { oneOf: [{ type: "string" }, { type: "number" }] };
|
|
1005
|
+
}
|
|
1006
|
+
return properties;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
// ─── Project Schema Generator ────────────────────────────────────────────────
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Generate the Jx project.json schema as a plain JavaScript object. This schema validates project
|
|
1013
|
+
* configuration files. No webref data needed.
|
|
1014
|
+
*
|
|
1015
|
+
* @returns {object} JSON Schema 2020-12 document
|
|
1016
|
+
*/
|
|
1017
|
+
export function generateProjectSchema() {
|
|
1018
|
+
return {
|
|
1019
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1020
|
+
$id: "https://jxsuite.com/schema/project/v1",
|
|
1021
|
+
title: "Jx Project",
|
|
1022
|
+
description:
|
|
1023
|
+
"Schema for Jx project.json files. " +
|
|
1024
|
+
"A project.json file is the root anchor file for a Jx project, " +
|
|
1025
|
+
"declaring site metadata, default settings, global styles, content collections, " +
|
|
1026
|
+
"and build configuration.",
|
|
1027
|
+
type: "object",
|
|
1028
|
+
|
|
1029
|
+
properties: {
|
|
1030
|
+
name: {
|
|
1031
|
+
description: "Human-readable project name.",
|
|
1032
|
+
type: "string",
|
|
1033
|
+
default: "Jx Site",
|
|
1034
|
+
examples: ["My Portfolio", "Jx Example Site"],
|
|
1035
|
+
},
|
|
1036
|
+
url: {
|
|
1037
|
+
description: "Production URL of the deployed site.",
|
|
1038
|
+
type: "string",
|
|
1039
|
+
examples: ["https://example.com", "https://jxsuite.com"],
|
|
1040
|
+
},
|
|
1041
|
+
defaults: {
|
|
1042
|
+
description: "Default settings applied to all pages unless overridden.",
|
|
1043
|
+
type: "object",
|
|
1044
|
+
properties: {
|
|
1045
|
+
layout: {
|
|
1046
|
+
description:
|
|
1047
|
+
"Default layout file path applied to all pages. " +
|
|
1048
|
+
"Set to null to render pages without a layout.",
|
|
1049
|
+
oneOf: [{ type: "string" }, { type: "null" }],
|
|
1050
|
+
default: null,
|
|
1051
|
+
examples: ["./layouts/base.json"],
|
|
1052
|
+
},
|
|
1053
|
+
lang: {
|
|
1054
|
+
description: "Default lang attribute for the <html> element.",
|
|
1055
|
+
type: "string",
|
|
1056
|
+
default: "en",
|
|
1057
|
+
},
|
|
1058
|
+
charset: {
|
|
1059
|
+
description: "Default charset for the page.",
|
|
1060
|
+
type: "string",
|
|
1061
|
+
default: "utf-8",
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
},
|
|
1065
|
+
$head: {
|
|
1066
|
+
description:
|
|
1067
|
+
"Global <head> entries applied to all pages. " +
|
|
1068
|
+
"Array of element definitions for meta tags, link tags, script tags, etc.",
|
|
1069
|
+
type: "array",
|
|
1070
|
+
items: {
|
|
1071
|
+
type: "object",
|
|
1072
|
+
properties: {
|
|
1073
|
+
tagName: { type: "string" },
|
|
1074
|
+
attributes: {
|
|
1075
|
+
type: "object",
|
|
1076
|
+
additionalProperties: { type: "string" },
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
},
|
|
1080
|
+
examples: [
|
|
1081
|
+
[
|
|
1082
|
+
{ tagName: "link", attributes: { rel: "icon", href: "/favicon.svg" } },
|
|
1083
|
+
{ tagName: "meta", attributes: { name: "generator", content: "Jx" } },
|
|
1084
|
+
],
|
|
1085
|
+
],
|
|
1086
|
+
},
|
|
1087
|
+
$elements: {
|
|
1088
|
+
description:
|
|
1089
|
+
"Global custom element dependencies available to all pages. " +
|
|
1090
|
+
"Items are $ref objects or npm package specifier strings.",
|
|
1091
|
+
type: "array",
|
|
1092
|
+
items: {
|
|
1093
|
+
oneOf: [
|
|
1094
|
+
{
|
|
1095
|
+
type: "object",
|
|
1096
|
+
required: ["$ref"],
|
|
1097
|
+
properties: { $ref: { type: "string" } },
|
|
1098
|
+
additionalProperties: false,
|
|
1099
|
+
},
|
|
1100
|
+
{ type: "string" },
|
|
1101
|
+
],
|
|
1102
|
+
},
|
|
1103
|
+
},
|
|
1104
|
+
imports: {
|
|
1105
|
+
description:
|
|
1106
|
+
"Global import map: $prototype names to .class.json file paths. " +
|
|
1107
|
+
"Makes external classes available by name in all pages.",
|
|
1108
|
+
type: "object",
|
|
1109
|
+
additionalProperties: { type: "string" },
|
|
1110
|
+
examples: [
|
|
1111
|
+
{
|
|
1112
|
+
MarkdownFile: "@jxplatform/parser/MarkdownFile.class.json",
|
|
1113
|
+
MarkdownCollection: "@jxplatform/parser/MarkdownCollection.class.json",
|
|
1114
|
+
},
|
|
1115
|
+
],
|
|
1116
|
+
},
|
|
1117
|
+
$media: {
|
|
1118
|
+
description:
|
|
1119
|
+
"Named media breakpoints following CSS @custom-media convention. " +
|
|
1120
|
+
"Available in all component style objects.",
|
|
1121
|
+
type: "object",
|
|
1122
|
+
additionalProperties: { type: "string" },
|
|
1123
|
+
examples: [
|
|
1124
|
+
{
|
|
1125
|
+
"--sm": "(min-width: 640px)",
|
|
1126
|
+
"--md": "(min-width: 768px)",
|
|
1127
|
+
"--lg": "(min-width: 1024px)",
|
|
1128
|
+
},
|
|
1129
|
+
],
|
|
1130
|
+
},
|
|
1131
|
+
style: {
|
|
1132
|
+
description:
|
|
1133
|
+
"Global CSS styles applied to the <body> element. " +
|
|
1134
|
+
"Uses the same camelCase CSSOM convention as component styles.",
|
|
1135
|
+
type: "object",
|
|
1136
|
+
additionalProperties: {
|
|
1137
|
+
oneOf: [{ type: "string" }, { type: "number" }, { type: "object" }],
|
|
1138
|
+
},
|
|
1139
|
+
},
|
|
1140
|
+
state: {
|
|
1141
|
+
description: "Site-wide reactive state available to all pages.",
|
|
1142
|
+
type: "object",
|
|
1143
|
+
},
|
|
1144
|
+
collections: {
|
|
1145
|
+
description:
|
|
1146
|
+
"Content collection definitions. Each key is a collection name; " +
|
|
1147
|
+
"the value defines the source glob, frontmatter schema, and element dependencies.",
|
|
1148
|
+
type: "object",
|
|
1149
|
+
additionalProperties: {
|
|
1150
|
+
type: "object",
|
|
1151
|
+
properties: {
|
|
1152
|
+
source: {
|
|
1153
|
+
description: "Glob pattern for content files relative to the content directory.",
|
|
1154
|
+
type: "string",
|
|
1155
|
+
examples: ["./blog/**/*.md", "./docs/**/*.md"],
|
|
1156
|
+
},
|
|
1157
|
+
schema: {
|
|
1158
|
+
description: "JSON Schema for validating frontmatter of collection entries.",
|
|
1159
|
+
type: "object",
|
|
1160
|
+
},
|
|
1161
|
+
$elements: {
|
|
1162
|
+
description: "Custom elements available in markdown directives for this collection.",
|
|
1163
|
+
type: "array",
|
|
1164
|
+
items: {
|
|
1165
|
+
oneOf: [
|
|
1166
|
+
{
|
|
1167
|
+
type: "object",
|
|
1168
|
+
required: ["$ref"],
|
|
1169
|
+
properties: { $ref: { type: "string" } },
|
|
1170
|
+
},
|
|
1171
|
+
{ type: "string" },
|
|
1172
|
+
],
|
|
1173
|
+
},
|
|
1174
|
+
},
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
},
|
|
1178
|
+
redirects: {
|
|
1179
|
+
description: "Static redirect rules. Maps source paths to destination paths.",
|
|
1180
|
+
type: "object",
|
|
1181
|
+
additionalProperties: { type: "string" },
|
|
1182
|
+
examples: [{ "/old-about": "/about" }],
|
|
1183
|
+
},
|
|
1184
|
+
copy: {
|
|
1185
|
+
description:
|
|
1186
|
+
"Declarative file copy map. Keys are source paths (relative to project root), values are destination paths (relative to outDir).",
|
|
1187
|
+
type: "object",
|
|
1188
|
+
additionalProperties: { type: "string" },
|
|
1189
|
+
},
|
|
1190
|
+
build: {
|
|
1191
|
+
description: "Build configuration.",
|
|
1192
|
+
type: "object",
|
|
1193
|
+
properties: {
|
|
1194
|
+
outDir: {
|
|
1195
|
+
description: "Output directory for compiled site.",
|
|
1196
|
+
type: "string",
|
|
1197
|
+
default: "./dist",
|
|
1198
|
+
},
|
|
1199
|
+
format: {
|
|
1200
|
+
description: "Output format.",
|
|
1201
|
+
type: "string",
|
|
1202
|
+
enum: ["directory", "single"],
|
|
1203
|
+
default: "directory",
|
|
1204
|
+
},
|
|
1205
|
+
trailingSlash: {
|
|
1206
|
+
description: "Trailing slash behavior for generated URLs.",
|
|
1207
|
+
type: "string",
|
|
1208
|
+
enum: ["always", "never", "ignore"],
|
|
1209
|
+
default: "always",
|
|
1210
|
+
},
|
|
1211
|
+
adapter: {
|
|
1212
|
+
description: "Platform adapter for deployment-specific output.",
|
|
1213
|
+
type: "string",
|
|
1214
|
+
enum: ["netlify", "vercel", "cloudflare"],
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
},
|
|
1218
|
+
i18n: {
|
|
1219
|
+
description: "Internationalization configuration.",
|
|
1220
|
+
type: "object",
|
|
1221
|
+
properties: {
|
|
1222
|
+
defaultLocale: {
|
|
1223
|
+
description: "Default locale code.",
|
|
1224
|
+
type: "string",
|
|
1225
|
+
examples: ["en"],
|
|
1226
|
+
},
|
|
1227
|
+
locales: {
|
|
1228
|
+
description: "Available locale codes.",
|
|
1229
|
+
type: "array",
|
|
1230
|
+
items: { type: "string" },
|
|
1231
|
+
examples: [["en", "fr", "de"]],
|
|
1232
|
+
},
|
|
1233
|
+
routing: {
|
|
1234
|
+
description: "Locale routing strategy.",
|
|
1235
|
+
type: "string",
|
|
1236
|
+
enum: ["prefix-except-default", "prefix-always"],
|
|
1237
|
+
},
|
|
1238
|
+
},
|
|
1239
|
+
},
|
|
1240
|
+
},
|
|
1241
|
+
|
|
1242
|
+
additionalProperties: false,
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
// ─── Class Schema Generator ─────────────────────────────────────────────────
|
|
1247
|
+
|
|
1248
|
+
/**
|
|
1249
|
+
* Generate the standalone .class.json schema as a plain JavaScript object. This schema validates Jx
|
|
1250
|
+
* class definition files.
|
|
1251
|
+
*
|
|
1252
|
+
* @returns {object} JSON Schema 2020-12 document
|
|
1253
|
+
*/
|
|
1254
|
+
export function generateClassSchema() {
|
|
1255
|
+
return {
|
|
1256
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
1257
|
+
$id: "https://jxsuite.com/schema/class/v1",
|
|
1258
|
+
title: "Jx Class Definition",
|
|
1259
|
+
description:
|
|
1260
|
+
"Schema for Jx .class.json files. A class definition describes a schema-defined " +
|
|
1261
|
+
"class with fields, constructor, methods, and type parameters. Optionally points " +
|
|
1262
|
+
"to a JS module via $implementation for hybrid execution.",
|
|
1263
|
+
type: "object",
|
|
1264
|
+
required: ["$prototype", "title"],
|
|
1265
|
+
|
|
1266
|
+
properties: {
|
|
1267
|
+
$schema: { type: "string" },
|
|
1268
|
+
$id: { type: "string" },
|
|
1269
|
+
$prototype: {
|
|
1270
|
+
description: 'Must be "Class" for class definition files.',
|
|
1271
|
+
type: "string",
|
|
1272
|
+
const: "Class",
|
|
1273
|
+
},
|
|
1274
|
+
title: {
|
|
1275
|
+
description: "PascalCase class name, used as the export name.",
|
|
1276
|
+
type: "string",
|
|
1277
|
+
examples: ["MarkdownFile", "DataSource", "Calculator"],
|
|
1278
|
+
},
|
|
1279
|
+
description: { type: "string" },
|
|
1280
|
+
extends: {
|
|
1281
|
+
description: "Base class — string name or $ref to another .class.json.",
|
|
1282
|
+
oneOf: [
|
|
1283
|
+
{ type: "string" },
|
|
1284
|
+
{
|
|
1285
|
+
type: "object",
|
|
1286
|
+
required: ["$ref"],
|
|
1287
|
+
properties: { $ref: { type: "string" } },
|
|
1288
|
+
additionalProperties: false,
|
|
1289
|
+
},
|
|
1290
|
+
],
|
|
1291
|
+
},
|
|
1292
|
+
$implementation: {
|
|
1293
|
+
description: "Relative path to a JS module containing the actual class implementation.",
|
|
1294
|
+
type: "string",
|
|
1295
|
+
examples: ["./md.js", "./lib/calculator.js"],
|
|
1296
|
+
},
|
|
1297
|
+
$defs: {
|
|
1298
|
+
description: "Class members: parameters, returnTypes, fields, constructor, methods.",
|
|
1299
|
+
type: "object",
|
|
1300
|
+
properties: {
|
|
1301
|
+
parameters: {
|
|
1302
|
+
description: "Reusable typed parameter schemas, keyed by name.",
|
|
1303
|
+
type: "object",
|
|
1304
|
+
additionalProperties: { $ref: "#/$defs/ClassParameterDef" },
|
|
1305
|
+
},
|
|
1306
|
+
returnTypes: {
|
|
1307
|
+
description: "Output type schemas, keyed by name.",
|
|
1308
|
+
type: "object",
|
|
1309
|
+
additionalProperties: { type: "object" },
|
|
1310
|
+
},
|
|
1311
|
+
fields: {
|
|
1312
|
+
description: "Class fields with role, access, scope, and type information.",
|
|
1313
|
+
type: "object",
|
|
1314
|
+
additionalProperties: { $ref: "#/$defs/ClassFieldDef" },
|
|
1315
|
+
},
|
|
1316
|
+
constructor: { $ref: "#/$defs/ClassConstructorDef" },
|
|
1317
|
+
methods: {
|
|
1318
|
+
description: "Class methods and accessors.",
|
|
1319
|
+
type: "object",
|
|
1320
|
+
additionalProperties: { $ref: "#/$defs/ClassMethodDef" },
|
|
1321
|
+
},
|
|
1322
|
+
},
|
|
1323
|
+
},
|
|
1324
|
+
},
|
|
1325
|
+
additionalProperties: false,
|
|
1326
|
+
|
|
1327
|
+
$defs: {
|
|
1328
|
+
ClassParameterDef: {
|
|
1329
|
+
description: "A typed parameter definition for a class.",
|
|
1330
|
+
type: "object",
|
|
1331
|
+
required: ["identifier"],
|
|
1332
|
+
properties: {
|
|
1333
|
+
identifier: { type: "string" },
|
|
1334
|
+
type: {},
|
|
1335
|
+
format: {
|
|
1336
|
+
description: 'When "json-schema", this parameter\'s value is itself a JSON Schema.',
|
|
1337
|
+
type: "string",
|
|
1338
|
+
},
|
|
1339
|
+
description: { type: "string" },
|
|
1340
|
+
default: {},
|
|
1341
|
+
examples: { type: "array" },
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
|
|
1345
|
+
ClassFieldDef: {
|
|
1346
|
+
description: "A class field definition with access control and scope.",
|
|
1347
|
+
type: "object",
|
|
1348
|
+
properties: {
|
|
1349
|
+
role: { type: "string", const: "field" },
|
|
1350
|
+
access: { type: "string", enum: ["public", "private", "protected"] },
|
|
1351
|
+
scope: { type: "string", enum: ["instance", "static"] },
|
|
1352
|
+
identifier: { type: "string" },
|
|
1353
|
+
type: {},
|
|
1354
|
+
$prototype: {
|
|
1355
|
+
description: 'Data source prototype for this field (e.g., "Request").',
|
|
1356
|
+
type: "string",
|
|
1357
|
+
},
|
|
1358
|
+
initializer: {},
|
|
1359
|
+
default: {},
|
|
1360
|
+
description: { type: "string" },
|
|
1361
|
+
examples: { type: "array" },
|
|
1362
|
+
},
|
|
1363
|
+
},
|
|
1364
|
+
|
|
1365
|
+
ClassConstructorDef: {
|
|
1366
|
+
description: "Class constructor definition.",
|
|
1367
|
+
type: "object",
|
|
1368
|
+
properties: {
|
|
1369
|
+
role: { type: "string", const: "constructor" },
|
|
1370
|
+
$prototype: { type: "string", const: "Function" },
|
|
1371
|
+
parameters: {
|
|
1372
|
+
type: "array",
|
|
1373
|
+
items: {
|
|
1374
|
+
oneOf: [
|
|
1375
|
+
{
|
|
1376
|
+
type: "object",
|
|
1377
|
+
required: ["$ref"],
|
|
1378
|
+
properties: { $ref: { type: "string" } },
|
|
1379
|
+
additionalProperties: false,
|
|
1380
|
+
},
|
|
1381
|
+
{ $ref: "#/$defs/ClassParameterDef" },
|
|
1382
|
+
],
|
|
1383
|
+
},
|
|
1384
|
+
},
|
|
1385
|
+
superCall: {
|
|
1386
|
+
type: "object",
|
|
1387
|
+
properties: {
|
|
1388
|
+
arguments: { type: "array", items: { type: "string" } },
|
|
1389
|
+
},
|
|
1390
|
+
},
|
|
1391
|
+
body: {
|
|
1392
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
1393
|
+
},
|
|
1394
|
+
description: { type: "string" },
|
|
1395
|
+
},
|
|
1396
|
+
},
|
|
1397
|
+
|
|
1398
|
+
ClassMethodDef: {
|
|
1399
|
+
description: "A class method or accessor definition.",
|
|
1400
|
+
type: "object",
|
|
1401
|
+
properties: {
|
|
1402
|
+
role: { type: "string", enum: ["method", "accessor"] },
|
|
1403
|
+
$prototype: { type: "string", const: "Function" },
|
|
1404
|
+
access: { type: "string", enum: ["public", "private", "protected"] },
|
|
1405
|
+
scope: { type: "string", enum: ["instance", "static"] },
|
|
1406
|
+
identifier: { type: "string" },
|
|
1407
|
+
parameters: {
|
|
1408
|
+
type: "array",
|
|
1409
|
+
items: {
|
|
1410
|
+
oneOf: [
|
|
1411
|
+
{
|
|
1412
|
+
type: "object",
|
|
1413
|
+
required: ["$ref"],
|
|
1414
|
+
properties: { $ref: { type: "string" } },
|
|
1415
|
+
additionalProperties: false,
|
|
1416
|
+
},
|
|
1417
|
+
{ $ref: "#/$defs/ClassParameterDef" },
|
|
1418
|
+
],
|
|
1419
|
+
},
|
|
1420
|
+
},
|
|
1421
|
+
returnType: {},
|
|
1422
|
+
body: {
|
|
1423
|
+
oneOf: [{ type: "string" }, { type: "array", items: { type: "string" } }],
|
|
1424
|
+
},
|
|
1425
|
+
getter: {
|
|
1426
|
+
type: "object",
|
|
1427
|
+
properties: { body: { type: "string" } },
|
|
1428
|
+
},
|
|
1429
|
+
setter: {
|
|
1430
|
+
type: "object",
|
|
1431
|
+
properties: {
|
|
1432
|
+
parameters: { type: "array" },
|
|
1433
|
+
body: { type: "string" },
|
|
1434
|
+
},
|
|
1435
|
+
},
|
|
1436
|
+
description: { type: "string" },
|
|
1437
|
+
},
|
|
1438
|
+
},
|
|
1439
|
+
},
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
1444
|
+
|
|
1445
|
+
/**
|
|
1446
|
+
* Return the meta-schema as a formatted JSON string.
|
|
1447
|
+
*
|
|
1448
|
+
* @returns {Promise<string>}
|
|
1449
|
+
*/
|
|
1450
|
+
export async function generateSchemaString() {
|
|
1451
|
+
return JSON.stringify(await generateSchema(), null, 2);
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* Validate a Jx document against the generated schema using Ajv.
|
|
1456
|
+
*
|
|
1457
|
+
* @param {object} doc
|
|
1458
|
+
* @returns {Promise<{ valid: boolean; errors: object[] | null }>}
|
|
1459
|
+
*/
|
|
1460
|
+
export async function validateDocument(doc) {
|
|
1461
|
+
let Ajv, addFormats;
|
|
1462
|
+
try {
|
|
1463
|
+
// @ts-ignore — optional peer dependency
|
|
1464
|
+
({ default: Ajv } = await import("ajv"));
|
|
1465
|
+
// @ts-ignore — optional peer dependency
|
|
1466
|
+
({ default: addFormats } = await import("ajv-formats"));
|
|
1467
|
+
} catch {
|
|
1468
|
+
throw new Error("Schema validation requires ajv and ajv-formats: bun add ajv ajv-formats");
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
const ajv = new Ajv({ allErrors: true, strict: false, ownProperties: true });
|
|
1472
|
+
addFormats(ajv);
|
|
1473
|
+
|
|
1474
|
+
const schema = await generateSchema();
|
|
1475
|
+
const validate = ajv.compile(schema);
|
|
1476
|
+
const valid = validate(doc);
|
|
1477
|
+
|
|
1478
|
+
return { valid, errors: validate.errors ?? null };
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
1482
|
+
|
|
1483
|
+
if (process.argv[1] && process.argv[1].endsWith("schema.js")) {
|
|
1484
|
+
const { writeFileSync } = await import("node:fs");
|
|
1485
|
+
const { resolve, dirname } = await import("node:path");
|
|
1486
|
+
|
|
1487
|
+
const schemaDir = dirname(resolve(process.argv[1], ".."));
|
|
1488
|
+
|
|
1489
|
+
// Generate all three schemas
|
|
1490
|
+
const componentSchema = await generateSchema();
|
|
1491
|
+
const projectSchema = generateProjectSchema();
|
|
1492
|
+
const classSchema = generateClassSchema();
|
|
1493
|
+
|
|
1494
|
+
const componentStr = JSON.stringify(componentSchema, null, 2);
|
|
1495
|
+
const projectStr = JSON.stringify(projectSchema, null, 2);
|
|
1496
|
+
const classStr = JSON.stringify(classSchema, null, 2);
|
|
1497
|
+
|
|
1498
|
+
const [, , out] = process.argv;
|
|
1499
|
+
|
|
1500
|
+
if (out) {
|
|
1501
|
+
// Legacy single-file mode
|
|
1502
|
+
writeFileSync(out, componentStr, "utf8");
|
|
1503
|
+
console.error(`Jx component schema written to ${out}`);
|
|
1504
|
+
} else {
|
|
1505
|
+
// Default: write all three to packages/schema/
|
|
1506
|
+
writeFileSync(resolve(schemaDir, "schema.json"), componentStr, "utf8");
|
|
1507
|
+
writeFileSync(resolve(schemaDir, "project-schema.json"), projectStr, "utf8");
|
|
1508
|
+
writeFileSync(resolve(schemaDir, "class-schema.json"), classStr, "utf8");
|
|
1509
|
+
console.error("Generated:");
|
|
1510
|
+
console.error(" schema.json (component)");
|
|
1511
|
+
console.error(" project-schema.json");
|
|
1512
|
+
console.error(" class-schema.json");
|
|
1513
|
+
}
|
|
1514
|
+
}
|