@jxsuite/starters 1.2.2 → 1.6.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.
Files changed (82) hide show
  1. package/README.md +196 -0
  2. package/package.json +4 -2
  3. package/registry.json +16 -0
  4. package/sites/blog/document.schema.json +633 -115
  5. package/sites/blog/package.json +3 -3
  6. package/sites/blog/project.schema.json +523 -24
  7. package/sites/course/document.schema.json +633 -115
  8. package/sites/course/package.json +3 -3
  9. package/sites/course/project.schema.json +523 -24
  10. package/sites/event/document.schema.json +633 -115
  11. package/sites/event/package.json +3 -3
  12. package/sites/event/project.schema.json +523 -24
  13. package/sites/fitness-studio/document.schema.json +633 -115
  14. package/sites/fitness-studio/package.json +3 -3
  15. package/sites/fitness-studio/project.schema.json +523 -24
  16. package/sites/home-services/document.schema.json +633 -115
  17. package/sites/home-services/package.json +3 -3
  18. package/sites/home-services/project.schema.json +523 -24
  19. package/sites/museum/components/mu-band.json +92 -0
  20. package/sites/museum/components/mu-cta.json +101 -0
  21. package/sites/museum/components/mu-exhibition-card.json +191 -0
  22. package/sites/museum/components/mu-exhibition-row.json +237 -0
  23. package/sites/museum/components/mu-fact.json +64 -0
  24. package/sites/museum/components/mu-footer.json +256 -0
  25. package/sites/museum/components/mu-hero.json +236 -0
  26. package/sites/museum/components/mu-more.json +63 -0
  27. package/sites/museum/components/mu-oculus.json +44 -0
  28. package/sites/museum/components/mu-pagehead.json +105 -0
  29. package/sites/museum/components/mu-quote.json +49 -0
  30. package/sites/museum/content/exhibitions/ar/clay-salt-thread.md +18 -0
  31. package/sites/museum/content/exhibitions/ar/harbour-and-crane.md +18 -0
  32. package/sites/museum/content/exhibitions/ar/the-cartographers-room.md +18 -0
  33. package/sites/museum/content/exhibitions/en/clay-salt-thread.md +19 -0
  34. package/sites/museum/content/exhibitions/en/harbour-and-crane.md +19 -0
  35. package/sites/museum/content/exhibitions/en/the-cartographers-room.md +19 -0
  36. package/sites/museum/content/exhibitions/fr-CA/clay-salt-thread.md +18 -0
  37. package/sites/museum/content/exhibitions/fr-CA/harbour-and-crane.md +18 -0
  38. package/sites/museum/content/exhibitions/fr-CA/the-cartographers-room.md +18 -0
  39. package/sites/museum/document.schema.json +23513 -0
  40. package/sites/museum/layouts/ar.json +29 -0
  41. package/sites/museum/layouts/base.json +329 -0
  42. package/sites/museum/layouts/fr-ca.json +28 -0
  43. package/sites/museum/package.json +18 -0
  44. package/sites/museum/pages/about.json +378 -0
  45. package/sites/museum/pages/ar/about.json +314 -0
  46. package/sites/museum/pages/ar/exhibitions/[slug].json +211 -0
  47. package/sites/museum/pages/ar/exhibitions/index.json +103 -0
  48. package/sites/museum/pages/ar/index.json +246 -0
  49. package/sites/museum/pages/ar/visit.json +327 -0
  50. package/sites/museum/pages/exhibitions/[slug].json +200 -0
  51. package/sites/museum/pages/exhibitions/index.json +93 -0
  52. package/sites/museum/pages/fr-ca/a-propos.json +313 -0
  53. package/sites/museum/pages/fr-ca/expositions/[slug].json +215 -0
  54. package/sites/museum/pages/fr-ca/expositions/index.json +104 -0
  55. package/sites/museum/pages/fr-ca/index.json +246 -0
  56. package/sites/museum/pages/fr-ca/visite.json +328 -0
  57. package/sites/museum/pages/index.json +236 -0
  58. package/sites/museum/pages/visit.json +317 -0
  59. package/sites/museum/project.json +179 -0
  60. package/sites/museum/project.schema.json +996 -0
  61. package/sites/museum/public/favicon.svg +8 -0
  62. package/sites/nonprofit/document.schema.json +633 -115
  63. package/sites/nonprofit/package.json +3 -3
  64. package/sites/nonprofit/project.schema.json +523 -24
  65. package/sites/portfolio/document.schema.json +633 -115
  66. package/sites/portfolio/package.json +3 -3
  67. package/sites/portfolio/project.schema.json +523 -24
  68. package/sites/professional-firm/document.schema.json +633 -115
  69. package/sites/professional-firm/package.json +3 -3
  70. package/sites/professional-firm/project.schema.json +523 -24
  71. package/sites/real-estate/document.schema.json +633 -115
  72. package/sites/real-estate/package.json +3 -3
  73. package/sites/real-estate/project.schema.json +523 -24
  74. package/sites/restaurant/document.schema.json +633 -115
  75. package/sites/restaurant/package.json +3 -3
  76. package/sites/restaurant/project.schema.json +523 -24
  77. package/sites/saas/document.schema.json +633 -115
  78. package/sites/saas/package.json +3 -3
  79. package/sites/saas/project.schema.json +523 -24
  80. package/sites/shop/document.schema.json +633 -115
  81. package/sites/shop/package.json +3 -3
  82. package/sites/shop/project.schema.json +523 -24
@@ -0,0 +1,996 @@
1
+ {
2
+ "$comment": "Generated by `jx schema` from project.json#/extensions — do not edit.",
3
+ "$defs": {
4
+ "Fields": {
5
+ "anyOf": [
6
+ {
7
+ "$ref": "#/$defs/project-core-v2/$defs/JxFieldSchema"
8
+ },
9
+ {
10
+ "$ref": "#/$defs/project-core-v2/$defs/RelationshipRef"
11
+ }
12
+ ]
13
+ },
14
+ "project-core-v2": {
15
+ "$defs": {
16
+ "ImageConfig": {
17
+ "description": "Image optimization configuration for the project.",
18
+ "properties": {
19
+ "formats": {
20
+ "items": {
21
+ "type": "string"
22
+ },
23
+ "type": "array"
24
+ },
25
+ "lazyLoad": {
26
+ "type": "boolean"
27
+ },
28
+ "optimize": {
29
+ "type": "boolean"
30
+ },
31
+ "picture": {
32
+ "description": "Wrap a multi-format image in a <picture> with one <source> per format (default true). With it off the <img> carries a srcset in the preferred format alone, which browsers that cannot decode that format will still select from.",
33
+ "type": "boolean"
34
+ },
35
+ "quality": {
36
+ "properties": {
37
+ "avif": {
38
+ "type": "number"
39
+ },
40
+ "jpeg": {
41
+ "type": "number"
42
+ },
43
+ "png": {
44
+ "type": "number"
45
+ },
46
+ "webp": {
47
+ "type": "number"
48
+ }
49
+ },
50
+ "type": "object"
51
+ },
52
+ "remoteDomains": {
53
+ "description": "Hostnames whose remote (https) images are optimized through /cdn-cgi/image transform URLs (cloudflare service only), e.g. [\"drive.usercontent.google.com\"]. Remote sources from other hosts are left untouched.",
54
+ "items": {
55
+ "type": "string"
56
+ },
57
+ "type": "array"
58
+ },
59
+ "service": {
60
+ "description": "How images are optimized: \"build\" runs Sharp at build time; \"cloudflare\" emits /cdn-cgi/image transform URLs served by Cloudflare Image Transformations (requires the feature to be enabled on the serving zone).",
61
+ "enum": [
62
+ "build",
63
+ "cloudflare"
64
+ ],
65
+ "type": "string"
66
+ },
67
+ "sizes": {
68
+ "type": "string"
69
+ },
70
+ "widths": {
71
+ "items": {
72
+ "type": "number"
73
+ },
74
+ "type": "array"
75
+ }
76
+ },
77
+ "type": "object"
78
+ },
79
+ "JxFieldSchema": {
80
+ "description": "A single field definition inside a section entry schema — a small, recursive subset of JSON Schema. Recursive positions reference the effective field union via the well-known fields resource $id.",
81
+ "properties": {
82
+ "default": {},
83
+ "description": {
84
+ "type": "string"
85
+ },
86
+ "enum": {
87
+ "type": "array"
88
+ },
89
+ "format": {
90
+ "type": "string"
91
+ },
92
+ "items": {
93
+ "$ref": "#/$defs/Fields"
94
+ },
95
+ "properties": {
96
+ "additionalProperties": {
97
+ "$ref": "#/$defs/Fields"
98
+ },
99
+ "type": "object"
100
+ },
101
+ "required": {
102
+ "items": {
103
+ "type": "string"
104
+ },
105
+ "type": "array"
106
+ },
107
+ "type": {
108
+ "enum": [
109
+ "string",
110
+ "number",
111
+ "integer",
112
+ "boolean",
113
+ "array",
114
+ "object"
115
+ ],
116
+ "type": "string"
117
+ }
118
+ },
119
+ "required": [
120
+ "type"
121
+ ],
122
+ "type": "object"
123
+ },
124
+ "RelationshipRef": {
125
+ "description": "Relationship reference field: points at a named entry of a referenceable section via \"#/<sectionKey>/<name>\" (specs/relationships.md). A bare reference is to-one; wrap in an array field's items for to-many.",
126
+ "properties": {
127
+ "$ref": {
128
+ "pattern": "^#/[A-Za-z][A-Za-z0-9_-]*/[A-Za-z0-9._-]+$",
129
+ "type": "string"
130
+ },
131
+ "description": {
132
+ "type": "string"
133
+ }
134
+ },
135
+ "required": [
136
+ "$ref"
137
+ ],
138
+ "type": "object"
139
+ },
140
+ "StyleObject": {
141
+ "additionalProperties": {
142
+ "oneOf": [
143
+ {
144
+ "type": "string"
145
+ },
146
+ {
147
+ "type": "number"
148
+ },
149
+ {
150
+ "$ref": "#/$defs/project-core-v2/$defs/StyleObject"
151
+ }
152
+ ]
153
+ },
154
+ "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.",
155
+ "properties": {},
156
+ "type": "object"
157
+ }
158
+ },
159
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
160
+ "description": "Core fragment of the Jx project.json schema. Open by design: extension fragments contribute their own top-level sections and the generated per-project entry document (project.schema.json) closes the composition. See specs/extensions.md §5.",
161
+ "properties": {
162
+ "$defs": {
163
+ "description": "Global type definitions available to all pages.",
164
+ "type": "object"
165
+ },
166
+ "$elements": {
167
+ "description": "Global custom element dependencies available to all pages. Items are $ref objects or npm package specifier strings.",
168
+ "items": {
169
+ "oneOf": [
170
+ {
171
+ "additionalProperties": false,
172
+ "properties": {
173
+ "$ref": {
174
+ "type": "string"
175
+ }
176
+ },
177
+ "required": [
178
+ "$ref"
179
+ ],
180
+ "type": "object"
181
+ },
182
+ {
183
+ "type": "string"
184
+ }
185
+ ]
186
+ },
187
+ "type": "array"
188
+ },
189
+ "$head": {
190
+ "description": "Global <head> entries applied to all pages. Array of element definitions for meta tags, link tags, script tags, etc.",
191
+ "examples": [
192
+ [
193
+ {
194
+ "attributes": {
195
+ "href": "/favicon.svg",
196
+ "rel": "icon"
197
+ },
198
+ "tagName": "link"
199
+ },
200
+ {
201
+ "attributes": {
202
+ "content": "Jx",
203
+ "name": "generator"
204
+ },
205
+ "tagName": "meta"
206
+ }
207
+ ]
208
+ ],
209
+ "items": {
210
+ "properties": {
211
+ "attributes": {
212
+ "additionalProperties": {
213
+ "type": "string"
214
+ },
215
+ "type": "object"
216
+ },
217
+ "tagName": {
218
+ "type": "string"
219
+ }
220
+ },
221
+ "type": "object"
222
+ },
223
+ "type": "array"
224
+ },
225
+ "$media": {
226
+ "additionalProperties": {
227
+ "type": "string"
228
+ },
229
+ "description": "Named media breakpoints following CSS @custom-media convention. Available in all component style objects. Declaring a pure color-scheme query (e.g. \"--dark\": \"(prefers-color-scheme: dark)\") additionally opts the site into the forced-scheme contract: @--dark style blocks are dual-emitted so a data-color-scheme attribute on <html> overrides the OS preference, color-scheme is declared on :root, and a pre-paint script restores the visitor's persisted choice (spec §9.5).",
230
+ "examples": [
231
+ {
232
+ "--dark": "(prefers-color-scheme: dark)",
233
+ "--lg": "(min-width: 1024px)",
234
+ "--md": "(min-width: 768px)",
235
+ "--sm": "(min-width: 640px)"
236
+ }
237
+ ],
238
+ "type": "object"
239
+ },
240
+ "$schema": {
241
+ "description": "Relative path to the generated per-project schema (conventionally ./project.schema.json, written by `jx schema`).",
242
+ "type": "string"
243
+ },
244
+ "build": {
245
+ "description": "Build configuration.",
246
+ "properties": {
247
+ "adapter": {
248
+ "description": "Platform adapter for deployment-specific output.",
249
+ "enum": [
250
+ "static",
251
+ "cloudflare-pages",
252
+ "cloudflare-workers",
253
+ "node",
254
+ "bun"
255
+ ],
256
+ "type": "string"
257
+ },
258
+ "deploy": {
259
+ "additionalProperties": false,
260
+ "description": "Deployment tracking: the hosting project this repo publishes to. Identifiers only (no secrets) — safe to commit; Studio uses it to tell whether the publish workflow already exists. `adapter` says how the build is packaged; `deploy` says where it ships.",
261
+ "properties": {
262
+ "accountId": {
263
+ "description": "Hosting account id (e.g. the Cloudflare account id).",
264
+ "type": "string"
265
+ },
266
+ "productionUrl": {
267
+ "description": "Production URL of the connected hosting project.",
268
+ "type": "string"
269
+ },
270
+ "projectName": {
271
+ "description": "Hosting project name (e.g. the Cloudflare Pages project).",
272
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
273
+ "type": "string"
274
+ },
275
+ "provider": {
276
+ "description": "Hosting provider the project is connected to.",
277
+ "enum": [
278
+ "cloudflare-pages"
279
+ ],
280
+ "type": "string"
281
+ }
282
+ },
283
+ "required": [
284
+ "provider",
285
+ "accountId",
286
+ "projectName"
287
+ ],
288
+ "type": "object"
289
+ },
290
+ "format": {
291
+ "default": "directory",
292
+ "description": "Reserved; currently unused. Accepted for forward compatibility with single-file output (desktop single-file mode) — the build emits per-route directories regardless.",
293
+ "enum": [
294
+ "directory",
295
+ "single"
296
+ ],
297
+ "type": "string"
298
+ },
299
+ "outDir": {
300
+ "default": "./dist",
301
+ "description": "Output directory for compiled site.",
302
+ "type": "string"
303
+ },
304
+ "headers": {
305
+ "additionalProperties": false,
306
+ "description": "Emit dist/_headers — the response headers only the build can know. Honored by Cloudflare Pages/Workers assets and Netlify; on the node/bun adapters the file documents what a reverse proxy in front of them must send.",
307
+ "properties": {
308
+ "cache": {
309
+ "default": "auto",
310
+ "description": "\"auto\" marks the content-addressed image variants immutable (RFC 8246) and revalidates everything else (RFC 9111). \"off\" emits no Cache-Control at all.",
311
+ "enum": [
312
+ "auto",
313
+ "off"
314
+ ],
315
+ "type": "string"
316
+ },
317
+ "enabled": {
318
+ "default": true,
319
+ "type": "boolean"
320
+ },
321
+ "rules": {
322
+ "additionalProperties": {
323
+ "additionalProperties": {
324
+ "type": "string"
325
+ },
326
+ "type": "object"
327
+ },
328
+ "description": "Verbatim rules, merged after the generated block.",
329
+ "propertyNames": {
330
+ "pattern": "^/"
331
+ },
332
+ "type": "object"
333
+ },
334
+ "security": {
335
+ "additionalProperties": false,
336
+ "properties": {
337
+ "contentTypeOptions": {
338
+ "default": true,
339
+ "type": "boolean"
340
+ },
341
+ "csp": {
342
+ "default": false,
343
+ "description": "CSP Level 3. Off by default: unlike the other security headers this one governs code the build cannot see — a third-party script that loads a second script, a widget that opens a frame — so turning it on is a change to verify in the browser console, not a safe default. `script-src` is derived from the inline scripts and external origins the build emitted; `style-src` keeps 'unsafe-inline', because per-page <style> blocks and per-element style attributes have no site-wide hash.",
344
+ "oneOf": [
345
+ {
346
+ "type": "boolean"
347
+ },
348
+ {
349
+ "const": "report-only"
350
+ },
351
+ {
352
+ "additionalProperties": false,
353
+ "properties": {
354
+ "directives": {
355
+ "additionalProperties": {
356
+ "oneOf": [
357
+ {
358
+ "type": "string"
359
+ },
360
+ {
361
+ "const": false
362
+ }
363
+ ]
364
+ },
365
+ "description": "Replace (string) or remove (false) a computed directive. A replacement is wholesale, so an addition restates the defaults.",
366
+ "type": "object"
367
+ },
368
+ "mode": {
369
+ "default": "enforce",
370
+ "enum": [
371
+ "enforce",
372
+ "report-only"
373
+ ]
374
+ },
375
+ "reportUri": {
376
+ "format": "uri-reference",
377
+ "type": "string"
378
+ }
379
+ },
380
+ "type": "object"
381
+ }
382
+ ]
383
+ },
384
+ "frameOptions": {
385
+ "default": "SAMEORIGIN",
386
+ "oneOf": [
387
+ {
388
+ "enum": [
389
+ "DENY",
390
+ "SAMEORIGIN"
391
+ ],
392
+ "type": "string"
393
+ },
394
+ {
395
+ "const": false
396
+ }
397
+ ]
398
+ },
399
+ "hsts": {
400
+ "default": false,
401
+ "description": "RFC 6797. Off by default: a wrong max-age locks the apex domain to HTTPS for that long, and the mistake is invisible until a certificate lapses. `preload` requires `includeSubDomains` and is a build error without it.",
402
+ "oneOf": [
403
+ {
404
+ "type": "boolean"
405
+ },
406
+ {
407
+ "additionalProperties": false,
408
+ "properties": {
409
+ "includeSubDomains": {
410
+ "default": true,
411
+ "type": "boolean"
412
+ },
413
+ "maxAge": {
414
+ "default": 31536000,
415
+ "minimum": 0,
416
+ "type": "integer"
417
+ },
418
+ "preload": {
419
+ "default": false,
420
+ "type": "boolean"
421
+ }
422
+ },
423
+ "type": "object"
424
+ }
425
+ ]
426
+ },
427
+ "permissionsPolicy": {
428
+ "default": "camera=(), microphone=(), geolocation=()",
429
+ "oneOf": [
430
+ {
431
+ "type": "string"
432
+ },
433
+ {
434
+ "const": false
435
+ }
436
+ ]
437
+ },
438
+ "referrerPolicy": {
439
+ "default": "strict-origin-when-cross-origin",
440
+ "oneOf": [
441
+ {
442
+ "enum": [
443
+ "no-referrer",
444
+ "no-referrer-when-downgrade",
445
+ "origin",
446
+ "origin-when-cross-origin",
447
+ "same-origin",
448
+ "strict-origin",
449
+ "strict-origin-when-cross-origin",
450
+ "unsafe-url"
451
+ ],
452
+ "type": "string"
453
+ },
454
+ {
455
+ "const": false
456
+ }
457
+ ]
458
+ }
459
+ },
460
+ "type": "object"
461
+ }
462
+ },
463
+ "type": "object"
464
+ },
465
+ "sitemap": {
466
+ "default": true,
467
+ "description": "Generate sitemap.xml from the route table (requires `url`). Set false to disable.",
468
+ "type": "boolean"
469
+ },
470
+ "trailingSlash": {
471
+ "default": "always",
472
+ "description": "Trailing slash behavior for generated URLs.",
473
+ "enum": [
474
+ "always",
475
+ "never",
476
+ "ignore"
477
+ ],
478
+ "type": "string"
479
+ }
480
+ },
481
+ "type": "object"
482
+ },
483
+ "copy": {
484
+ "additionalProperties": {
485
+ "type": "string"
486
+ },
487
+ "description": "Declarative file copy map. Keys are source paths (relative to project root), values are destination paths (relative to outDir).",
488
+ "type": "object"
489
+ },
490
+ "defaults": {
491
+ "description": "Default settings applied to all pages unless overridden.",
492
+ "properties": {
493
+ "charset": {
494
+ "default": "utf8",
495
+ "description": "Default charset for the page.",
496
+ "type": "string"
497
+ },
498
+ "dir": {
499
+ "description": "Base direction for the <html> element. Omitted when unset — a page's `$dir` overrides it. Right-to-left content renders left-to-right without this.",
500
+ "enum": [
501
+ "ltr",
502
+ "rtl",
503
+ "auto"
504
+ ],
505
+ "type": "string"
506
+ },
507
+ "lang": {
508
+ "default": "en",
509
+ "description": "Default lang attribute for the <html> element. A page's `$lang` overrides it.",
510
+ "type": "string"
511
+ },
512
+ "layout": {
513
+ "default": null,
514
+ "description": "Default layout file path applied to all pages. Set to null to render pages without a layout.",
515
+ "examples": [
516
+ "./layouts/base.json"
517
+ ],
518
+ "oneOf": [
519
+ {
520
+ "type": "string"
521
+ },
522
+ {
523
+ "type": "null"
524
+ }
525
+ ]
526
+ },
527
+ "shadow": {
528
+ "default": false,
529
+ "description": "Default shadow-DOM mode for every component (spec.md §16.6). Off unless set: a shadow root isolates component styles from the page and replaces the <slot> emulation with real slot distribution. A component's own `$shadow` overrides this in both directions.",
530
+ "oneOf": [
531
+ {
532
+ "enum": [
533
+ "open",
534
+ "closed"
535
+ ],
536
+ "type": "string"
537
+ },
538
+ {
539
+ "const": false,
540
+ "type": "boolean"
541
+ }
542
+ ]
543
+ }
544
+ },
545
+ "type": "object"
546
+ },
547
+ "extensions": {
548
+ "description": "Extension packages: bare package names (resolved project-first through the package exports map) or relative paths. Each must export jx-extension.json.",
549
+ "examples": [
550
+ [
551
+ "@jxsuite/parser"
552
+ ]
553
+ ],
554
+ "items": {
555
+ "type": "string"
556
+ },
557
+ "type": "array"
558
+ },
559
+ "manifest": {
560
+ "additionalProperties": false,
561
+ "description": "W3C Web App Manifest, emitted as manifest.webmanifest with a <link rel=\"manifest\"> on every page. Absent means no manifest: it is a claim that a site is meant to be installed, and most are not.",
562
+ "properties": {
563
+ "backgroundColor": {
564
+ "type": "string"
565
+ },
566
+ "categories": {
567
+ "items": {
568
+ "type": "string"
569
+ },
570
+ "type": "array"
571
+ },
572
+ "description": {
573
+ "type": "string"
574
+ },
575
+ "dir": {
576
+ "enum": [
577
+ "ltr",
578
+ "rtl",
579
+ "auto"
580
+ ],
581
+ "type": "string"
582
+ },
583
+ "display": {
584
+ "default": "standalone",
585
+ "enum": [
586
+ "fullscreen",
587
+ "standalone",
588
+ "minimal-ui",
589
+ "browser"
590
+ ],
591
+ "type": "string"
592
+ },
593
+ "enabled": {
594
+ "default": true,
595
+ "type": "boolean"
596
+ },
597
+ "icons": {
598
+ "description": "Icons at 192px and 512px are what browsers require before offering to install the site; the build warns when either is missing.",
599
+ "items": {
600
+ "additionalProperties": false,
601
+ "properties": {
602
+ "purpose": {
603
+ "type": "string"
604
+ },
605
+ "sizes": {
606
+ "examples": [
607
+ "192x192"
608
+ ],
609
+ "type": "string"
610
+ },
611
+ "src": {
612
+ "type": "string"
613
+ },
614
+ "type": {
615
+ "type": "string"
616
+ }
617
+ },
618
+ "required": [
619
+ "src"
620
+ ],
621
+ "type": "object"
622
+ },
623
+ "type": "array"
624
+ },
625
+ "lang": {
626
+ "type": "string"
627
+ },
628
+ "name": {
629
+ "description": "Defaults to the project name.",
630
+ "type": "string"
631
+ },
632
+ "orientation": {
633
+ "type": "string"
634
+ },
635
+ "scope": {
636
+ "type": "string"
637
+ },
638
+ "shortName": {
639
+ "type": "string"
640
+ },
641
+ "startUrl": {
642
+ "default": "/",
643
+ "type": "string"
644
+ },
645
+ "themeColor": {
646
+ "type": "string"
647
+ }
648
+ },
649
+ "type": "object"
650
+ },
651
+ "serviceWorker": {
652
+ "default": false,
653
+ "description": "W3C Service Worker, emitted as sw.js. Off unless declared — a worker is sticky, so it is the one output that can keep breaking visitors after a deploy tries to fix them. **Turning it off means `false`, not deleting the key**: `false` emits a tombstone at the same URL that unregisters the old worker and clears its caches, and a 404 there is not an instruction to stop. HTML is always network-first; only the content-addressed /images/_optimized/* is served cache-first.",
654
+ "oneOf": [
655
+ {
656
+ "type": "boolean"
657
+ },
658
+ {
659
+ "additionalProperties": false,
660
+ "properties": {
661
+ "enabled": {
662
+ "default": true,
663
+ "type": "boolean"
664
+ },
665
+ "offlineFallback": {
666
+ "description": "Page served for a failed navigation. Added to `precache` if not already there.",
667
+ "type": "string"
668
+ },
669
+ "precache": {
670
+ "description": "Site-absolute URLs fetched at install time. Each must be a file this build produces; one that is not fails the build, because a single unreachable entry stops the worker installing at all.",
671
+ "items": {
672
+ "pattern": "^/",
673
+ "type": "string"
674
+ },
675
+ "type": "array"
676
+ },
677
+ "scope": {
678
+ "default": "/",
679
+ "type": "string"
680
+ }
681
+ },
682
+ "type": "object"
683
+ }
684
+ ]
685
+ },
686
+ "securityTxt": {
687
+ "additionalProperties": false,
688
+ "description": "RFC 9116 security.txt, emitted at .well-known/security.txt. `expires` is required by §2.5.5 and an absent or past value is a build error — an expired file advertises a reporting channel while telling the reporter not to trust it. A hand-placed public/.well-known/security.txt shadows this, which is how a clearsigned file ships.",
689
+ "properties": {
690
+ "acknowledgments": {
691
+ "items": {
692
+ "type": "string"
693
+ },
694
+ "type": "array"
695
+ },
696
+ "canonical": {
697
+ "type": "string"
698
+ },
699
+ "contact": {
700
+ "description": "At least one mailto:, https: or tel: URI (§2.5.3).",
701
+ "items": {
702
+ "type": "string"
703
+ },
704
+ "type": "array"
705
+ },
706
+ "enabled": {
707
+ "default": true,
708
+ "type": "boolean"
709
+ },
710
+ "encryption": {
711
+ "items": {
712
+ "type": "string"
713
+ },
714
+ "type": "array"
715
+ },
716
+ "expires": {
717
+ "format": "date-time",
718
+ "type": "string"
719
+ },
720
+ "hiring": {
721
+ "items": {
722
+ "type": "string"
723
+ },
724
+ "type": "array"
725
+ },
726
+ "policy": {
727
+ "items": {
728
+ "type": "string"
729
+ },
730
+ "type": "array"
731
+ },
732
+ "preferredLanguages": {
733
+ "description": "BCP 47 tags, validated and canonicalized like i18n.locales.",
734
+ "items": {
735
+ "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
736
+ "type": "string"
737
+ },
738
+ "type": "array"
739
+ }
740
+ },
741
+ "type": "object"
742
+ },
743
+ "i18n": {
744
+ "description": "Internationalization configuration.",
745
+ "properties": {
746
+ "defaultLocale": {
747
+ "description": "Default locale, as a BCP 47 language tag. The pattern catches shape errors (`en_US`, `en--US`); the build is the authority on well-formedness.",
748
+ "examples": [
749
+ "en"
750
+ ],
751
+ "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
752
+ "type": "string"
753
+ },
754
+ "locales": {
755
+ "description": "Available locales, as BCP 47 language tags.",
756
+ "examples": [
757
+ [
758
+ "en",
759
+ "fr",
760
+ "de"
761
+ ]
762
+ ],
763
+ "items": {
764
+ "pattern": "^[A-Za-z]{2,8}(-[A-Za-z0-9]{1,8})*$",
765
+ "type": "string"
766
+ },
767
+ "type": "array"
768
+ },
769
+ "routing": {
770
+ "description": "Locale routing strategy.",
771
+ "enum": [
772
+ "prefix-except-default",
773
+ "prefix-always"
774
+ ],
775
+ "type": "string"
776
+ }
777
+ },
778
+ "type": "object"
779
+ },
780
+ "images": {
781
+ "$ref": "#/$defs/project-core-v2/$defs/ImageConfig"
782
+ },
783
+ "imports": {
784
+ "additionalProperties": {
785
+ "type": "string"
786
+ },
787
+ "description": "Global import map: $prototype names to .class.json file paths. Makes external classes available by name in all pages.",
788
+ "examples": [
789
+ {
790
+ "Markdown": "@jxsuite/parser/Markdown.class.json",
791
+ "MarkdownCollection": "@jxsuite/parser/MarkdownCollection.class.json"
792
+ }
793
+ ],
794
+ "type": "object"
795
+ },
796
+ "name": {
797
+ "default": "Jx Site",
798
+ "description": "Human-readable project name.",
799
+ "examples": [
800
+ "My Portfolio",
801
+ "Jx Example Site"
802
+ ],
803
+ "type": "string"
804
+ },
805
+ "redirects": {
806
+ "additionalProperties": {
807
+ "oneOf": [
808
+ {
809
+ "description": "Destination path or URL; implies a 301.",
810
+ "type": "string"
811
+ },
812
+ {
813
+ "additionalProperties": false,
814
+ "description": "A redirect with an explicit RFC 9110 §15.4 status.",
815
+ "properties": {
816
+ "destination": {
817
+ "type": "string"
818
+ },
819
+ "status": {
820
+ "default": 301,
821
+ "description": "RFC 9110 §15.4 redirection status. 307 and 308 preserve the request method; 301 and 302 historically do not.",
822
+ "enum": [
823
+ 301,
824
+ 302,
825
+ 303,
826
+ 307,
827
+ 308
828
+ ],
829
+ "type": "integer"
830
+ }
831
+ },
832
+ "required": [
833
+ "destination"
834
+ ],
835
+ "type": "object"
836
+ },
837
+ {
838
+ "additionalProperties": false,
839
+ "description": "A rewrite (proxy): the destination's content is served AT the source URL, with no redirect. Emitted as status 200 in `_redirects`, which is the host convention — not an HTTP redirection status.",
840
+ "properties": {
841
+ "destination": {
842
+ "type": "string"
843
+ },
844
+ "rewrite": {
845
+ "const": true,
846
+ "type": "boolean"
847
+ }
848
+ },
849
+ "required": [
850
+ "destination",
851
+ "rewrite"
852
+ ],
853
+ "type": "object"
854
+ }
855
+ ]
856
+ },
857
+ "description": "Static redirect and rewrite rules. Keys are source paths in URLPattern pathname syntax.",
858
+ "examples": [
859
+ {
860
+ "/api/*": {
861
+ "destination": "https://api.example.com/*",
862
+ "rewrite": true
863
+ },
864
+ "/legacy/*": {
865
+ "destination": "/archive/*",
866
+ "status": 308
867
+ },
868
+ "/old-about": "/about"
869
+ }
870
+ ],
871
+ "propertyNames": {
872
+ "pattern": "^/"
873
+ },
874
+ "type": "object"
875
+ },
876
+ "state": {
877
+ "description": "Site-wide reactive state available to all pages.",
878
+ "type": "object"
879
+ },
880
+ "style": {
881
+ "additionalProperties": {
882
+ "oneOf": [
883
+ {
884
+ "type": "string"
885
+ },
886
+ {
887
+ "type": "number"
888
+ },
889
+ {
890
+ "$ref": "#/$defs/project-core-v2/$defs/StyleObject"
891
+ }
892
+ ]
893
+ },
894
+ "description": "Global CSS styles applied to the <body> element. Uses the same camelCase CSSOM convention and recursive selector/at-rule nesting as component styles.",
895
+ "type": "object"
896
+ },
897
+ "url": {
898
+ "description": "Production URL of the deployed site.",
899
+ "examples": [
900
+ "https://example.com",
901
+ "https://jxsuite.com"
902
+ ],
903
+ "type": "string"
904
+ }
905
+ },
906
+ "title": "Jx Project Core",
907
+ "type": "object"
908
+ },
909
+ "ext-parser-project-v1": {
910
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
911
+ "title": "Parser project fragment",
912
+ "description": "Project-schema fragment contributed by @jxsuite/parser: the `content` section declaring file-based content collections.",
913
+ "type": "object",
914
+ "properties": {
915
+ "content": {
916
+ "description": "File-based content collections. Each key names a content type; entries are loaded from the named source through the format registry.",
917
+ "type": "object",
918
+ "additionalProperties": {
919
+ "description": "Content type definition. Defines the source directory, frontmatter schema, and element dependencies.",
920
+ "type": "object",
921
+ "properties": {
922
+ "$elements": {
923
+ "description": "Custom elements available in markdown directives for this content type.",
924
+ "type": "array",
925
+ "items": {
926
+ "oneOf": [
927
+ {
928
+ "type": "object",
929
+ "properties": {
930
+ "$ref": {
931
+ "type": "string"
932
+ }
933
+ },
934
+ "required": [
935
+ "$ref"
936
+ ]
937
+ },
938
+ {
939
+ "type": "string"
940
+ }
941
+ ]
942
+ }
943
+ },
944
+ "format": {
945
+ "description": "Name of a format class provided by an enabled extension (e.g. \"Markdown\", \"Csv\"). When omitted, the format is derived from the source file extension via the format registry. \"json\" is the only built-in.",
946
+ "type": "string"
947
+ },
948
+ "schema": {
949
+ "description": "JSON Schema for validating frontmatter of content type entries.",
950
+ "type": "object",
951
+ "properties": {
952
+ "type": {
953
+ "const": "object"
954
+ },
955
+ "properties": {
956
+ "description": "Field definitions; values are field schemas from the effective project field union.",
957
+ "type": "object",
958
+ "additionalProperties": {
959
+ "$ref": "#/$defs/Fields"
960
+ }
961
+ },
962
+ "required": {
963
+ "description": "Frontmatter fields every entry must provide.",
964
+ "type": "array",
965
+ "items": {
966
+ "type": "string"
967
+ }
968
+ }
969
+ }
970
+ },
971
+ "source": {
972
+ "description": "Path to the content directory relative to the project root.",
973
+ "examples": [
974
+ "./content/blog/",
975
+ "./content/docs/"
976
+ ],
977
+ "type": "string"
978
+ }
979
+ }
980
+ }
981
+ }
982
+ }
983
+ }
984
+ },
985
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
986
+ "allOf": [
987
+ {
988
+ "$ref": "#/$defs/project-core-v2"
989
+ },
990
+ {
991
+ "$ref": "#/$defs/ext-parser-project-v1"
992
+ }
993
+ ],
994
+ "type": "object",
995
+ "unevaluatedProperties": false
996
+ }