@jxsuite/schema 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kevin Shenk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/class-schema.json CHANGED
@@ -176,7 +176,17 @@
176
176
  "parse",
177
177
  "serialize",
178
178
  "discover",
179
- "load"
179
+ "load",
180
+ "projectData",
181
+ "resolvePaths",
182
+ "lower",
183
+ "emit",
184
+ "assets",
185
+ "mount",
186
+ "dialect",
187
+ "deploySchema",
188
+ "bindings",
189
+ "testConnection"
180
190
  ],
181
191
  "type": "string"
182
192
  },
@@ -237,6 +247,40 @@
237
247
  ],
238
248
  "type": "object"
239
249
  },
250
+ "ConnectorBlockDef": {
251
+ "description": "Connection-provider admission block (specs/extensions.md §12). A class carrying this block provides database connections of the given provider/kind.",
252
+ "properties": {
253
+ "kind": {
254
+ "description": "SQL dialect family the provider speaks.",
255
+ "enum": [
256
+ "sqlite",
257
+ "postgres"
258
+ ],
259
+ "type": "string"
260
+ },
261
+ "local": {
262
+ "description": "Local-development substitute provider (e.g. sqlite for D1).",
263
+ "type": "string"
264
+ },
265
+ "module": {
266
+ "description": "Module specifier exporting the provider implementation.",
267
+ "type": "string"
268
+ },
269
+ "provider": {
270
+ "description": "Provider identifier (e.g. d1, supabase, sqlite).",
271
+ "type": "string"
272
+ },
273
+ "serve": {
274
+ "description": "Module specifier exporting the serve-time worker binding.",
275
+ "type": "string"
276
+ }
277
+ },
278
+ "required": [
279
+ "provider",
280
+ "kind"
281
+ ],
282
+ "type": "object"
283
+ },
240
284
  "FormatDef": {
241
285
  "description": "Format participation marker. A class carrying this block is auto-discovered from the project imports map and used for file-extension dispatch by the compiler, server, and studio.",
242
286
  "properties": {
@@ -279,6 +323,56 @@
279
323
  ],
280
324
  "type": "object"
281
325
  },
326
+ "ProjectBlockDef": {
327
+ "additionalProperties": false,
328
+ "description": "Project-section admission block (specs/extensions.md §9). A class carrying this block owns the project.json section named by `key`; hosts dispatch that section's data through the class's projectData/resolvePaths capabilities.",
329
+ "properties": {
330
+ "description": {
331
+ "type": "string"
332
+ },
333
+ "key": {
334
+ "description": "The project.json top-level section this class owns (exclusive per project).",
335
+ "type": "string"
336
+ },
337
+ "referenceable": {
338
+ "description": "Whether section entries can be referenced from documents (e.g. content types).",
339
+ "type": "boolean"
340
+ },
341
+ "title": {
342
+ "type": "string"
343
+ }
344
+ },
345
+ "required": [
346
+ "key"
347
+ ],
348
+ "type": "object"
349
+ },
350
+ "ServerBlockDef": {
351
+ "additionalProperties": false,
352
+ "description": "Server-mount admission block (specs/extensions.md §11). A class carrying this block mounts routes under its /_jx/ basePath in the site worker and dev server.",
353
+ "properties": {
354
+ "basePath": {
355
+ "description": "Route subtree this class mounts — must be under /_jx/ (exclusive per project).",
356
+ "examples": [
357
+ "/_jx/data",
358
+ "/_jx/auth"
359
+ ],
360
+ "type": "string"
361
+ },
362
+ "module": {
363
+ "description": "Module specifier exporting the mount handler.",
364
+ "type": "string"
365
+ },
366
+ "order": {
367
+ "description": "Mount order (ascending; default 100).",
368
+ "type": "number"
369
+ }
370
+ },
371
+ "required": [
372
+ "basePath"
373
+ ],
374
+ "type": "object"
375
+ },
282
376
  "StudioHints": {
283
377
  "description": "Studio control-surface hints for a format class: editor modes, document-mode rules, templates, and the element/nesting constraints that gate structural editing.",
284
378
  "properties": {
@@ -442,6 +536,9 @@
442
536
  "$studio": {
443
537
  "$ref": "#/$defs/StudioHints"
444
538
  },
539
+ "connector": {
540
+ "$ref": "#/$defs/ConnectorBlockDef"
541
+ },
445
542
  "description": {
446
543
  "type": "string"
447
544
  },
@@ -468,6 +565,12 @@
468
565
  "format": {
469
566
  "$ref": "#/$defs/FormatDef"
470
567
  },
568
+ "project": {
569
+ "$ref": "#/$defs/ProjectBlockDef"
570
+ },
571
+ "server": {
572
+ "$ref": "#/$defs/ServerBlockDef"
573
+ },
471
574
  "title": {
472
575
  "description": "PascalCase class name, used as the export name.",
473
576
  "examples": [
@@ -18,7 +18,8 @@ export const childrenValueSchema = {
18
18
  description:
19
19
  "Array of child definitions — elements, text, or Array namespaces (dynamic lists) mixed " +
20
20
  "freely. A bare Array namespace (the whole children slot is one dynamic list) is also " +
21
- "accepted for backward compatibility.",
21
+ "accepted for backward compatibility, as is a ${…} template string that resolves at build " +
22
+ "time to an array of child definitions.",
22
23
  oneOf: [
23
24
  {
24
25
  items: {
@@ -32,5 +33,13 @@ export const childrenValueSchema = {
32
33
  type: "array",
33
34
  },
34
35
  { $ref: "#/$defs/ArrayNamespace" },
36
+ {
37
+ description:
38
+ "Computed children — a ${…} template that resolves at build time to an array of child " +
39
+ 'definitions (e.g. "${state.entry.$children}" for parsed content). A bare plain-text ' +
40
+ "string is NOT a valid children value; text children must be array items.",
41
+ pattern: "\\$\\{",
42
+ type: "string",
43
+ },
35
44
  ],
36
45
  } as const;
@@ -1,3 +1,28 @@
1
+ /**
2
+ * Method capability roles: "method"/"accessor" plus every static capability role hosts dispatch on
3
+ * (format-registry EXTENSION_CAPABILITIES — specs/extensions.md §8). Kept as a literal tuple so
4
+ * FromSchema inference stays precise; the drift-guard test in tests/class-schema-drift.test.ts
5
+ * asserts parity with EXTENSION_CAPABILITIES.
6
+ */
7
+ export const CLASS_METHOD_ROLES = [
8
+ "method",
9
+ "accessor",
10
+ "parse",
11
+ "serialize",
12
+ "discover",
13
+ "load",
14
+ "projectData",
15
+ "resolvePaths",
16
+ "lower",
17
+ "emit",
18
+ "assets",
19
+ "mount",
20
+ "dialect",
21
+ "deploySchema",
22
+ "bindings",
23
+ "testConnection",
24
+ ] as const;
25
+
1
26
  export const classParameterDefSchema = {
2
27
  description: "A typed parameter definition for a class.",
3
28
  properties: {
@@ -100,7 +125,7 @@ export const classMethodDefSchema = {
100
125
  },
101
126
  returnType: {},
102
127
  role: {
103
- enum: ["method", "accessor", "parse", "serialize", "discover", "load"],
128
+ enum: CLASS_METHOD_ROLES,
104
129
  type: "string",
105
130
  },
106
131
  scope: { enum: ["instance", "static"], type: "string" },
@@ -157,6 +182,85 @@ export const formatDefSchema = {
157
182
  type: "object",
158
183
  } as const;
159
184
 
185
+ export const projectBlockDefSchema = {
186
+ additionalProperties: false,
187
+ description:
188
+ "Project-section admission block (specs/extensions.md §9). A class carrying this block owns " +
189
+ "the project.json section named by `key`; hosts dispatch that section's data through the " +
190
+ "class's projectData/resolvePaths capabilities.",
191
+ properties: {
192
+ description: { type: "string" },
193
+ key: {
194
+ description: "The project.json top-level section this class owns (exclusive per project).",
195
+ type: "string",
196
+ },
197
+ referenceable: {
198
+ description: "Whether section entries can be referenced from documents (e.g. content types).",
199
+ type: "boolean",
200
+ },
201
+ title: { type: "string" },
202
+ },
203
+ required: ["key"],
204
+ type: "object",
205
+ } as const;
206
+
207
+ export const serverBlockDefSchema = {
208
+ additionalProperties: false,
209
+ description:
210
+ "Server-mount admission block (specs/extensions.md §11). A class carrying this block mounts " +
211
+ "routes under its /_jx/ basePath in the site worker and dev server.",
212
+ properties: {
213
+ basePath: {
214
+ description: "Route subtree this class mounts — must be under /_jx/ (exclusive per project).",
215
+ examples: ["/_jx/data", "/_jx/auth"],
216
+ type: "string",
217
+ },
218
+ module: {
219
+ description: "Module specifier exporting the mount handler.",
220
+ type: "string",
221
+ },
222
+ order: {
223
+ description: "Mount order (ascending; default 100).",
224
+ type: "number",
225
+ },
226
+ },
227
+ required: ["basePath"],
228
+ type: "object",
229
+ } as const;
230
+
231
+ export const connectorBlockDefSchema = {
232
+ // Open by design: the block mirrors format-registry's ConnectorBlock ([key: string]: unknown) —
233
+ // Providers carry extra keys (e.g. D1's `module`), so additionalProperties stays true.
234
+ description:
235
+ "Connection-provider admission block (specs/extensions.md §12). A class carrying this block " +
236
+ "provides database connections of the given provider/kind.",
237
+ properties: {
238
+ kind: {
239
+ description: "SQL dialect family the provider speaks.",
240
+ enum: ["sqlite", "postgres"],
241
+ type: "string",
242
+ },
243
+ local: {
244
+ description: "Local-development substitute provider (e.g. sqlite for D1).",
245
+ type: "string",
246
+ },
247
+ module: {
248
+ description: "Module specifier exporting the provider implementation.",
249
+ type: "string",
250
+ },
251
+ provider: {
252
+ description: "Provider identifier (e.g. d1, supabase, sqlite).",
253
+ type: "string",
254
+ },
255
+ serve: {
256
+ description: "Module specifier exporting the serve-time worker binding.",
257
+ type: "string",
258
+ },
259
+ },
260
+ required: ["provider", "kind"],
261
+ type: "object",
262
+ } as const;
263
+
160
264
  export const studioHintsSchema = {
161
265
  description:
162
266
  "Studio control-surface hints for a format class: editor modes, document-mode rules, " +
@@ -258,6 +362,7 @@ export const classDefSchema = {
258
362
  $prototype: { const: "Class", type: "string" },
259
363
  $schema: { type: "string" },
260
364
  $studio: { $ref: "#/$defs/StudioHints" },
365
+ connector: { $ref: "#/$defs/ConnectorBlockDef" },
261
366
  description: { type: "string" },
262
367
  extends: {
263
368
  description: "Base class — string name or $ref to another .class.json.",
@@ -267,6 +372,8 @@ export const classDefSchema = {
267
372
  ],
268
373
  },
269
374
  format: { $ref: "#/$defs/FormatDef" },
375
+ project: { $ref: "#/$defs/ProjectBlockDef" },
376
+ server: { $ref: "#/$defs/ServerBlockDef" },
270
377
  title: {
271
378
  description: "PascalCase class name, used as the export name.",
272
379
  type: "string",
@@ -12,8 +12,10 @@ export const attributesObjectSchema = {
12
12
  } as const;
13
13
 
14
14
  export const propsObjectSchema = {
15
+ // AnyOf, not oneOf: a RefObject value legitimately matches both the plain-object branch and
16
+ // The RefObject branch, and exclusive matching would reject every $ref-bound prop.
15
17
  additionalProperties: {
16
- oneOf: [
18
+ anyOf: [
17
19
  { type: "string" },
18
20
  { type: "number" },
19
21
  { type: "boolean" },
@@ -39,7 +41,9 @@ export const elementPropertyValueSchema = {
39
41
  export const switchDefSchema = {
40
42
  additionalProperties: false,
41
43
  description: "Reactive $ref that drives which case to render.",
42
- properties: { $ref: { $ref: "#/$defs/InternalRef" } },
44
+ // The discriminant is a reactive state path (e.g. "#/state/currentRoute") resolved by
45
+ // ResolveRef at render time — not a #/$defs/ type pointer.
46
+ properties: { $ref: { $ref: "#/$defs/StateRef" } },
43
47
  required: ["$ref"],
44
48
  type: "object",
45
49
  } as const;
@@ -70,6 +74,15 @@ export const elementDefSchema = {
70
74
  $switch: { $ref: "#/$defs/SwitchDef" },
71
75
  alt: { $ref: "#/$defs/StringOrRef" },
72
76
  attributes: { $ref: "#/$defs/AttributesObject" },
77
+ cases: {
78
+ additionalProperties: {
79
+ oneOf: [{ $ref: "#/$defs/ElementDef" }, { $ref: "#/$defs/ExternalComponentRef" }],
80
+ },
81
+ description:
82
+ "Switch cases for this element. Maps case values to element definitions or external " +
83
+ "component refs, rendered according to the reactive $switch discriminant.",
84
+ type: "object",
85
+ },
73
86
  checked: { $ref: "#/$defs/BoolOrRef" },
74
87
  children: { $ref: "#/$defs/ChildrenValue" },
75
88
  className: { $ref: "#/$defs/StringOrRef" },
@@ -92,7 +92,20 @@ export const externalClassDefSchema = {
92
92
  },
93
93
  sameSite: { enum: ["strict", "lax", "none"], type: "string" },
94
94
  secure: { type: "boolean" },
95
- sort: { $ref: "#/$defs/RefObject" },
95
+ sort: {
96
+ anyOf: [
97
+ { $ref: "#/$defs/RefObject" },
98
+ { description: "A single sort rule, e.g. { field, order }.", type: "object" },
99
+ {
100
+ description: 'Ordered sort rules, e.g. [{ field: "date", order: "desc" }].',
101
+ items: { type: "object" },
102
+ type: "array",
103
+ },
104
+ ],
105
+ description:
106
+ "Sort configuration: a reactive $ref binding, a single { field, order } rule, or an " +
107
+ "ordered array of rules.",
108
+ },
96
109
  src: {
97
110
  description: "Configuration property passed to external class constructor.",
98
111
  type: "string",
@@ -15,6 +15,14 @@ export const functionDefSchema = {
15
15
  examples: ["./counter.js", "npm:@myorg/validators"],
16
16
  type: "string",
17
17
  },
18
+ arguments: {
19
+ description:
20
+ "Legacy alias for parameters: bare string parameter names (state may be listed " +
21
+ "explicitly as the first name). Superseded by parameters.",
22
+ examples: [["state", "event"]],
23
+ items: { type: "string" },
24
+ type: "array",
25
+ },
18
26
  body: {
19
27
  description:
20
28
  "Inline function body: an opaque JS source string (first implicit parameter is state), or a structured statement array (spec §20) — explicit structured function declaration, analyzable by tooling and editable visually. The escalation ladder within the shape is structured statements → JS string.",
package/defs/index.ts CHANGED
@@ -69,12 +69,16 @@ export {
69
69
 
70
70
  // Class definitions
71
71
  export {
72
+ CLASS_METHOD_ROLES,
72
73
  classParameterDefSchema,
73
74
  classFieldDefSchema,
74
75
  classConstructorDefSchema,
75
76
  classMethodDefSchema,
76
77
  classDefSchema,
78
+ connectorBlockDefSchema,
77
79
  formatDefSchema,
80
+ projectBlockDefSchema,
81
+ serverBlockDefSchema,
78
82
  studioHintsSchema,
79
83
  } from "./class-def.schema";
80
84
 
@@ -233,11 +233,12 @@ export const projectConfigSchema = {
233
233
  },
234
234
  style: {
235
235
  additionalProperties: {
236
- oneOf: [{ type: "string" }, { type: "number" }, { type: "object" }],
236
+ oneOf: [{ type: "string" }, { type: "number" }, { $ref: "#/$defs/StyleObject" }],
237
237
  },
238
238
  description:
239
239
  "Global CSS styles applied to the <body> element. " +
240
- "Uses the same camelCase CSSOM convention as component styles.",
240
+ "Uses the same camelCase CSSOM convention and recursive selector/at-rule " +
241
+ "nesting as component styles.",
241
242
  type: "object",
242
243
  },
243
244
  url: {
@@ -1,19 +1,13 @@
1
1
  export const styleObjectSchema = {
2
2
  additionalProperties: {
3
- oneOf: [
4
- { type: "string" },
5
- { type: "number" },
6
- {
7
- additionalProperties: { oneOf: [{ type: "string" }, { type: "number" }] },
8
- description: "Nested CSS selector or media breakpoint rules.",
9
- type: "object",
10
- },
11
- ],
3
+ oneOf: [{ type: "string" }, { type: "number" }, { $ref: "#/$defs/StyleObject" }],
12
4
  },
13
5
  description:
14
6
  "CSS style definition. camelCase property names follow CSSOM convention. " +
15
7
  "Keys starting with :, ., &, or [ are treated as nested CSS selectors. " +
16
- "Keys matching $media breakpoint names are treated as responsive rules.",
8
+ "Keys matching $media breakpoint names (or other @-prefixed at-rules) group nested rules. " +
9
+ "Nesting is recursive: selector and at-rule groups may nest to arbitrary depth " +
10
+ "(e.g. breakpoint → selector → pseudo-class), mirroring the compiler's recursive emission.",
17
11
  properties: {},
18
12
  type: "object",
19
13
  } as const;
package/package.json CHANGED
@@ -50,7 +50,7 @@
50
50
  "upgrade": "bunx npm-check-updates -u && bun install"
51
51
  },
52
52
  "type": "module",
53
- "version": "1.2.0",
53
+ "version": "1.4.0",
54
54
  "devDependencies": {
55
55
  "json-schema-to-ts": "^3.1.1"
56
56
  }
@@ -58,6 +58,24 @@
58
58
  }
59
59
  },
60
60
  "type": "object"
61
+ },
62
+ "StyleObject": {
63
+ "additionalProperties": {
64
+ "oneOf": [
65
+ {
66
+ "type": "string"
67
+ },
68
+ {
69
+ "type": "number"
70
+ },
71
+ {
72
+ "$ref": "#/$defs/StyleObject"
73
+ }
74
+ ]
75
+ },
76
+ "description": "CSS style definition. camelCase property names follow CSSOM convention. Keys starting with :, ., &, or [ are treated as nested CSS selectors. Keys matching $media breakpoint names (or other @-prefixed at-rules) group nested rules. Nesting is recursive: selector and at-rule groups may nest to arbitrary depth (e.g. breakpoint → selector → pseudo-class), mirroring the compiler's recursive emission.",
77
+ "properties": {},
78
+ "type": "object"
61
79
  }
62
80
  },
63
81
  "$id": "https://jxsuite.com/schema/project/v1",
@@ -360,11 +378,11 @@
360
378
  "type": "number"
361
379
  },
362
380
  {
363
- "type": "object"
381
+ "$ref": "#/$defs/StyleObject"
364
382
  }
365
383
  ]
366
384
  },
367
- "description": "Global CSS styles applied to the <body> element. Uses the same camelCase CSSOM convention as component styles.",
385
+ "description": "Global CSS styles applied to the <body> element. Uses the same camelCase CSSOM convention and recursive selector/at-rule nesting as component styles.",
368
386
  "type": "object"
369
387
  },
370
388
  "url": {