@texuli/spec 0.1.0-next.0 → 0.1.0-next.3

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/README.md CHANGED
@@ -1,98 +1,168 @@
1
1
  # @texuli/spec
2
2
 
3
- The schema-first blueprint contract (spec/Compose design, plan B1): the
4
- platform-neutral source of truth for blueprint structure, the catalog
5
- vocabulary, capability tiers and diagnostics. TypeScript and Kotlin are
6
- deterministic projections generated from it, and a conformance harness keeps
7
- everything from drifting.
8
-
9
- **Status: in development, not published.** Layer 0, no dependencies,
10
- framework-free. Kotlin is generated as committed text by this package's
11
- TypeScript generator; no Kotlin toolchain runs here, and nothing is compiled
12
- or published to Maven.
13
-
14
- ## Layout
15
-
16
- | Path | What it holds |
17
- | --- | --- |
18
- | `schemas/` | The normative JSON schemas: `blueprint` (envelope v1 and the node tree), `catalog`, `diagnostics`, `manifest` |
19
- | `catalog/components.json` | The closed vocabulary, projected from the built catalog: id, label, summary, group, placement (`app`/`content`), emit tiers, capability and props |
20
- | `catalog/capabilities.json` | Reviewed capability claims. Empty: no id is audited portable yet, so every capability is `unknown` (ADR-003) |
21
- | `fixtures/golden/` | Blueprints that must validate: a report, a dashboard and a proactive deck (fictional data) |
22
- | `fixtures/invalid/` | Blueprints that must fail with exact diagnostic codes, one or more per code |
23
- | `manifest.json` | The spec version, the dialect and a SHA-256 per normative file |
24
- | `generated/typescript/`, `generated/kotlin/` | The projections. Never edit them |
25
- | `generated-manifest.json` | A SHA-256 per generated file |
26
-
27
- ## The dialect
28
-
29
- Schemas use a closed subset of JSON Schema 2020-12 (`DIALECT_KEYWORDS`):
30
- `type`, `properties`, `required`, `additionalProperties`, `items`, `enum`,
31
- `const`, string, number and array bounds, `pattern`, and local `$ref` /
32
- `$defs`. Objects must be closed (`additionalProperties: false`) or explicit
33
- maps. Anything else fails `checkSchemaDocument`, and generation refuses it.
34
-
35
- ## Exports
36
-
37
- - `@texuli/spec`, the runtime half:
38
- - the generated types (`BlueprintEnvelope`, `SpecCatalog`, `SpecValidation` …);
39
- - `SPEC_COMPONENT_IDS` and `SpecComponentProps`;
40
- - `validateInstance` (the dialect validator);
41
- - `validateBlueprint` (the target-neutral blueprint check: envelope
42
- schema, closed ids and props, prop kinds and enums, required props, no
43
- platform-extension props from data, the portable profile failing closed,
44
- and an optional catalog binding);
45
- - `canonicalJson` and a synchronous `sha256`.
46
- - `@texuli/spec/codegen`, build time: `buildSpec`, `projectCatalog`,
47
- `checkConformance`, `formatJson`.
48
-
49
- URL policy, tree limits, grammar and rendering stay in the runtime validator,
50
- `@texuli/catalog/validate`.
51
-
52
- ## Projections
53
-
54
- - **Web extensions.** Props of kind `slot`, `event` or `unknown`, and the
55
- `class`, `className`, `style` and chart `renderer` props, are marked
56
- `extension: "web"`. They are left out of `SpecComponentProps`, and data
57
- that sets them is refused.
58
- - **Kotlin** (`com.texuli.spec`, Kotlin 1.9+ for `entries` and
59
- `data object`): data classes, closed enums and static decoders over a
60
- generated `JsonValue` model, with no library dependency. Decoders check
61
- JSON types, required and unknown fields, enums and constants. Patterns
62
- and limits are the shared validator's job.
63
- - **Stamp.** Every generated file carries the spec version and a digest of
64
- the normative files. There are no timestamps, and output is
65
- byte-identical for the same inputs in any order.
66
-
67
- ## Regenerate
3
+ The blueprint contract of [Texuli](https://texuli.com), an MIT-licensed
4
+ Svelte 5 component library by Dailybot, Inc. (*Small pieces. Infinite
5
+ possibilities.*). A **blueprint** is a JSON document, usually written by an
6
+ AI agent, that describes an interface as a tree of components from Texuli's
7
+ closed catalog. `@texuli/spec` is the platform-neutral source of truth for
8
+ that format: the generated TypeScript types, the closed component
9
+ vocabulary, a target-neutral blueprint validator and deterministic hashing.
10
+
11
+ Use it when you generate, store, diff or check blueprints, or build a
12
+ renderer for them. The same contract also generates the Kotlin models used
13
+ by Texuli's Kotlin Multiplatform twin. The package is framework-free: no
14
+ DOM, no Svelte, no Node APIs and no runtime dependencies.
15
+
16
+ > **Status: preview.** Texuli is in development. The packages are published
17
+ > to npm as prereleases under the `next` dist-tag, and there is no stable
18
+ > release yet, so install with `@next`. APIs may still change between
19
+ > previews.
20
+
21
+ ## Install
68
22
 
69
23
  ```bash
70
- pnpm exec tsx packages/spec/scripts/bootstrap-catalog.ts # refresh catalog/components.json from the built catalog
71
- pnpm exec tsx packages/spec/scripts/generate.ts # write manifests and projections
72
- pnpm exec tsx packages/spec/scripts/generate.ts --check # fail on drift
24
+ npm install @texuli/spec@next
73
25
  ```
74
26
 
75
- ## Conformance
76
-
77
- `pnpm vitest run packages/spec` runs the harness. Drift is a failed gate:
78
- - every schema stays in the dialect;
79
- - the manifest digests match the normative files;
80
- - regenerating gives byte-identical files, none missing or extra;
81
- - the golden fixtures validate and the invalid fixtures fail with exactly
82
- their codes, covering every diagnostic code;
83
- - `components.json` equals the projection of the live catalog, and the
84
- blueprint schema matches the envelope `@texuli/catalog` reads;
85
- - generation is deterministic and refuses schemas outside the dialect;
86
- - the Kotlin text declares its package, balances its brackets, defines
87
- every type it references and decodes closed.
88
-
89
- When the catalog changes, refresh `components.json`, regenerate and review
90
- the diff.
91
-
92
- ## Not yet
93
-
94
- - Behavior contracts, token roles and the token-to-Compose generator (B1
95
- steps 2 and 5).
96
- - Making the spec authoritative for the web packages (B1 step 3 cutover);
97
- today the catalog manifests are the bootstrap input.
98
- - Compiling the Kotlin, and the native runtime (B2).
27
+ ```bash
28
+ pnpm add @texuli/spec@next
29
+ # or
30
+ yarn add @texuli/spec@next
31
+ ```
32
+
33
+ No peer dependencies and no CSS. The package is ESM-only and ships
34
+ TypeScript declarations. The normative JSON documents (the blueprint
35
+ schema, the component catalog and the fixtures) live in the
36
+ [repository](https://github.com/DailybotHQ/texuli/tree/main/packages/spec);
37
+ they are not part of the npm package.
38
+
39
+ ## Quick start
40
+
41
+ ```ts
42
+ import {
43
+ canonicalJson,
44
+ type Schema,
45
+ SPEC_COMPONENT_IDS,
46
+ SPEC_VERSION,
47
+ sha256,
48
+ validateInstance,
49
+ } from '@texuli/spec';
50
+
51
+ SPEC_VERSION; // the spec version, e.g. '0.2.0'
52
+ SPEC_COMPONENT_IDS.includes('alert'); // true: the closed component vocabulary
53
+
54
+ // The dialect validator: a closed subset of JSON Schema 2020-12.
55
+ const schema: Schema = {
56
+ type: 'object',
57
+ properties: { title: { type: 'string', maxLength: 80 } },
58
+ required: ['title'],
59
+ additionalProperties: false,
60
+ };
61
+ validateInstance(schema, { title: 'Weekly report' }); // []
62
+ validateInstance(schema, { title: 42, extra: true });
63
+ // [{ code: 'type', path: '$.title', … }, { code: 'additional-property', path: '$.extra', … }]
64
+
65
+ // Deterministic serialization and hashing, identical in every runtime.
66
+ const digest = sha256(canonicalJson({ b: 1, a: [true, null] }));
67
+ ```
68
+
69
+ To check a whole blueprint, pass the blueprint schema and catalog documents
70
+ from the repository's `packages/spec/schemas/blueprint.schema.json` and
71
+ `packages/spec/catalog/components.json`:
72
+
73
+ ```ts
74
+ import { type Schema, type SpecCatalog, validateBlueprint } from '@texuli/spec';
75
+
76
+ const result = validateBlueprint(blueprint, {
77
+ blueprintSchema: blueprintSchemaJson as Schema,
78
+ catalog: componentsJson as SpecCatalog,
79
+ });
80
+ if (!result.ok) {
81
+ for (const d of result.diagnostics) console.warn(d.code, d.path, d.message);
82
+ }
83
+ ```
84
+
85
+ A node with an id outside the catalog fails with `unknown-id`; other codes
86
+ include `schema`, `unknown-prop`, `prop-type`, `enum-value`,
87
+ `missing-required`, `extension-prop`, `catalog-mismatch`,
88
+ `unsupported-profile` and `unknown-slot`.
89
+
90
+ ## What's inside
91
+
92
+ | Subpath | Purpose | Headline exports |
93
+ | --- | --- | --- |
94
+ | `@texuli/spec` | Runtime: types, vocabulary, validation, hashing | `BlueprintEnvelope`, `SpecCatalog`, `SpecValidation` and the other generated types; `SPEC_COMPONENT_IDS`, `SpecComponentProps`, `SPEC_CATALOG_HASH`, `SPEC_VERSION`; `validateBlueprint`, `validateTree`, `validateInstance`, `checkSchemaDocument`; `canonicalJson`, `sha256` |
95
+ | `@texuli/spec/codegen` | Build time: generation and conformance | `buildSpec`, `projectCatalog`, `checkConformance`, `formatJson`, `nextSpecVersion`, `checkVersionHistory` |
96
+
97
+ ## Key guarantees
98
+
99
+ - **Closed vocabulary.** Component ids, prop names, prop kinds and enum
100
+ values are closed; anything outside the catalog is refused.
101
+ - **No platform props from data.** Web-only props (slots, events, `class`,
102
+ `className`, `style` and the chart `renderer`) are marked as web
103
+ extensions, left out of `SpecComponentProps`, and refused when a
104
+ blueprint sets them.
105
+ - **Fails closed.** The portable profile is refused until a capability is
106
+ audited portable, and a blueprint bound to another catalog hash is
107
+ refused when you pass `catalogHash`.
108
+ - **Deterministic.** Canonical JSON sorts keys at every depth, and every
109
+ generated file carries the spec version and digest with no timestamps.
110
+ - **Structure only.** This validator checks structure. URL policy, tree
111
+ limits and rendering live in the runtime validator of
112
+ [`@texuli/catalog`](https://www.npmjs.com/package/@texuli/catalog).
113
+
114
+ ## Versioning
115
+
116
+ The spec has its own semantic version (`SPEC_VERSION`), separate from the
117
+ npm package version. The **spec digest** is the SHA-256 of the manifest
118
+ that pins every normative file (schemas, catalog and fixtures); any
119
+ normative change moves the digest and requires a new spec version.
120
+
121
+ | Change class | Examples | Before 1.0.0 | From 1.0.0 |
122
+ | --- | --- | --- | --- |
123
+ | patch | Descriptions, diagnostic wording, a fixture for behavior that already holds | patch | patch |
124
+ | minor | Optional fields, new catalog ids, new optional props, new diagnostic codes for data that was already refused | minor | minor |
125
+ | major | Removing or renaming ids, props or fields; changing a prop type; making something required | minor | major |
126
+
127
+ For TypeScript consumers, a patch leaves the generated types and
128
+ `SPEC_COMPONENT_IDS` unchanged; code that compiled against a minor release
129
+ keeps compiling, though new optional fields and ids can appear in data it
130
+ reads. Native (Kotlin) builds compile the generated models from the same
131
+ spec, and their decoders are closed: a blueprint that uses a field from a
132
+ newer spec fails to decode on an older native build with a `schema`
133
+ diagnostic instead of passing silently. A blueprint binds to an exact
134
+ catalog hash today; an envelope field for a compatible spec range, and
135
+ migrations between versions, are planned and not built.
136
+
137
+ The full rules (the digest, `versions.json` pins, regeneration and the
138
+ conformance harness) are in
139
+ [contract.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/contract.md).
140
+
141
+ ## Documentation
142
+
143
+ - Website: [texuli.com](https://texuli.com), the
144
+ [agents overview](https://texuli.com/agents-page/) and
145
+ [web and native](https://texuli.com/web-and-native/)
146
+ - Schemas, catalog and fixtures:
147
+ [packages/spec](https://github.com/DailybotHQ/texuli/tree/main/packages/spec)
148
+ - The contract in depth (the JSON Schema dialect, projections, conformance
149
+ and versioning): [contract.md](https://github.com/DailybotHQ/texuli/blob/main/packages/spec/docs/contract.md)
150
+ - The Kotlin Multiplatform twin that consumes the generated models:
151
+ [native/README.md](https://github.com/DailybotHQ/texuli/blob/main/native/README.md)
152
+ - Architecture: [ARCHITECTURE.md](https://github.com/DailybotHQ/texuli/blob/main/docs/ARCHITECTURE.md)
153
+
154
+ ## Related packages
155
+
156
+ - [`@texuli/catalog`](https://www.npmjs.com/package/@texuli/catalog): the agent catalog and the renderer with no HTML sinks.
157
+ - [`@texuli/player`](https://www.npmjs.com/package/@texuli/player): the Svelte paint runtime for validated blueprints.
158
+ - [`texuli`](https://www.npmjs.com/package/texuli): the styled Svelte 5 components blueprints are built from.
159
+
160
+ ## Contributing
161
+
162
+ Source, issues and contribution guidelines live at
163
+ [github.com/DailybotHQ/texuli](https://github.com/DailybotHQ/texuli). Report
164
+ bugs at [github.com/DailybotHQ/texuli/issues](https://github.com/DailybotHQ/texuli/issues).
165
+
166
+ ## License
167
+
168
+ MIT © Dailybot, Inc.
package/dist/catalog.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * projection from a built catalog. The projection is the bootstrap step of
4
4
  * spec/Compose design §2.3; the parity check reruns it to catch drift.
5
5
  */
6
- import type { SpecCatalog, SpecComponent, SpecProp } from '../generated/typescript/catalog-schema.js';
6
+ import type { SpecCatalog, SpecComponent, SpecProp } from './generated/typescript/catalog-schema.js';
7
7
  export type { SpecCatalog, SpecComponent, SpecProp };
8
8
  export declare const CAPABILITIES: readonly ["portable-core", "web-only", "native-only", "unknown"];
9
9
  export type Capability = SpecComponent['capability'];
@@ -9,5 +9,6 @@ export { prettyJson } from '../canonical.js';
9
9
  export { type BuiltCatalogLike, projectCatalog } from '../catalog.js';
10
10
  export { type ConformanceIssue, checkConformance } from '../conformance.js';
11
11
  export { formatJson, LINE_WIDTH } from '../format.js';
12
+ export { checkVersionHistory, nextSpecVersion, type SpecChangeClass, type SpecVersionHistory, type SpecVersionPin, type VersionIssue, } from '../versioning.js';
12
13
  export { KOTLIN_PACKAGE, kotlinString } from './kotlin.js';
13
14
  export type { SchemaUnit, Stamp } from './shared.js';
@@ -9,4 +9,5 @@ export { prettyJson } from "../canonical.js";
9
9
  export { projectCatalog } from "../catalog.js";
10
10
  export { checkConformance } from "../conformance.js";
11
11
  export { formatJson, LINE_WIDTH } from "../format.js";
12
+ export { checkVersionHistory, nextSpecVersion, } from "../versioning.js";
12
13
  export { KOTLIN_PACKAGE, kotlinString } from "./kotlin.js";
@@ -47,13 +47,55 @@ export interface BlueprintEditOp {
47
47
  tree?: BlueprintNode;
48
48
  }
49
49
  export type BlueprintNodePath = readonly number[];
50
- /** One catalog node: id, props and children only. */
50
+ /** One catalog node: id, props, children, and an optional named slot and condition. */
51
51
  export interface BlueprintNode {
52
52
  /** A catalog component id. */
53
53
  id: string;
54
54
  /** Props checked against the component's catalog entry. */
55
55
  props?: Readonly<Record<string, unknown>>;
56
56
  children?: readonly BlueprintNode[];
57
+ /**
58
+ * The parent's named slot this node fills (audit G04): a slot prop of the parent's catalog entry.
59
+ * Omitted or "children" is the default slot.
60
+ */
61
+ slot?: string;
62
+ when?: BlueprintCondition;
63
+ }
64
+ /**
65
+ * Shows the node when true (audit G07): the closed Condition AST of the blocks formula grammar.
66
+ * False or unknown hides the node. Structural shape only; which operands each op needs, and the
67
+ * size limits, are checked by the runtime formula parser.
68
+ */
69
+ export interface BlueprintCondition {
70
+ op: 'lt' | 'lte' | 'gt' | 'gte' | 'eq' | 'neq' | 'and' | 'or' | 'not';
71
+ left?: BlueprintNumberFormula;
72
+ right?: BlueprintNumberFormula;
73
+ /** The conditions of and / or. */
74
+ args?: readonly BlueprintCondition[];
75
+ value?: BlueprintCondition;
76
+ }
77
+ /**
78
+ * A number expression of the blocks formula grammar. Structural shape only; the runtime formula
79
+ * parser checks which operands each op needs.
80
+ */
81
+ export interface BlueprintNumberFormula {
82
+ op: 'num' | 'var' | 'add' | 'sub' | 'mul' | 'div' | 'pow' | 'min' | 'max' | 'round' | 'clamp' | 'if';
83
+ /** A number for num; a number formula for round and clamp. */
84
+ value?: unknown;
85
+ /** The input a var reads. */
86
+ name?: string;
87
+ left?: BlueprintNumberFormula;
88
+ right?: BlueprintNumberFormula;
89
+ base?: BlueprintNumberFormula;
90
+ exponent?: BlueprintNumberFormula;
91
+ /** The operands of min / max. */
92
+ args?: readonly BlueprintNumberFormula[];
93
+ digits?: number;
94
+ min?: BlueprintNumberFormula;
95
+ max?: BlueprintNumberFormula;
96
+ test?: BlueprintCondition;
97
+ ifTrue?: BlueprintNumberFormula;
98
+ ifFalse?: BlueprintNumberFormula;
57
99
  }
58
100
  /**
59
101
  * Envelope version 1 around one catalog tree (ADR-003, audit G13). Mirrors what
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/blueprint.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.1.0, digest 47afaa4f056320fc4111239e03afd97e8a073bd8dbd6d2ef0dec2d171acb2f8e
2
+ // Spec 0.2.0, digest d9eccaba1826e7d87648a4a33787d873245592dda843607a10b2c68a77f1806f
3
3
  export {};
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/catalog.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.1.0, digest 47afaa4f056320fc4111239e03afd97e8a073bd8dbd6d2ef0dec2d171acb2f8e
2
+ // Spec 0.2.0, digest d9eccaba1826e7d87648a4a33787d873245592dda843607a10b2c68a77f1806f
3
3
  export {};
@@ -4,7 +4,7 @@ export interface SpecDiagnostic {
4
4
  message: string;
5
5
  id?: string;
6
6
  }
7
- export type SpecDiagnosticCode = 'schema' | 'unknown-id' | 'unknown-prop' | 'prop-type' | 'enum-value' | 'missing-required' | 'extension-prop' | 'catalog-mismatch' | 'unsupported-profile';
7
+ export type SpecDiagnosticCode = 'schema' | 'unknown-id' | 'unknown-prop' | 'prop-type' | 'enum-value' | 'missing-required' | 'extension-prop' | 'catalog-mismatch' | 'unsupported-profile' | 'unknown-slot';
8
8
  /** The stable result shape of validating a blueprint against the spec. */
9
9
  export interface SpecValidation {
10
10
  ok: boolean;
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/diagnostics.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.1.0, digest 47afaa4f056320fc4111239e03afd97e8a073bd8dbd6d2ef0dec2d171acb2f8e
2
+ // Spec 0.2.0, digest d9eccaba1826e7d87648a4a33787d873245592dda843607a10b2c68a77f1806f
3
3
  export {};
@@ -1,3 +1,3 @@
1
1
  // Generated by @texuli/spec from schemas/manifest.schema.json. Do not edit: run the spec generator.
2
- // Spec 0.1.0, digest 47afaa4f056320fc4111239e03afd97e8a073bd8dbd6d2ef0dec2d171acb2f8e
2
+ // Spec 0.2.0, digest d9eccaba1826e7d87648a4a33787d873245592dda843607a10b2c68a77f1806f
3
3
  export {};
@@ -1,7 +1,7 @@
1
1
  /** The built catalog this vocabulary was projected from. */
2
- export declare const SPEC_CATALOG_HASH = "2516510c15ec62a630d56770349e47cad35f6d0e1654e8116ab1ef2607572617";
2
+ export declare const SPEC_CATALOG_HASH = "b073e30b4c8b1a30b3ef3ccb372e552d2d88e584616b74591c6c32923b471376";
3
3
  /** Every closed component id, sorted. */
4
- export declare const SPEC_COMPONENT_IDS: readonly ["alert", "alert-dialog", "analytics-grid", "app-nav", "badge", "banner", "blocks-code", "blocks-data", "blocks-diagram", "blocks-embed", "blocks-interactive", "blocks-map", "blocks-media", "blocks-structure", "blocks-text", "breadcrumb", "button", "button-group", "callout", "card", "charts-area", "charts-bar", "charts-bubble", "charts-bullet", "charts-canvas-renderer", "charts-cartesian", "charts-combo", "charts-donut", "charts-funnel", "charts-gauge", "charts-geo-map", "charts-geo-point-map", "charts-heatmap", "charts-line", "charts-network", "charts-pie", "charts-progress-ring", "charts-sankey", "charts-scatter", "charts-sparkline", "charts-stacked-bar", "charts-sunburst", "charts-treemap", "checkbox", "chip", "code", "combobox", "command-palette", "content-layout", "context-menu", "context-menu-trigger", "description", "detail-panel", "dialog", "drawer", "dropdown-menu", "dropdown-menu-content", "dropdown-menu-item", "dropdown-menu-trigger", "empty-state", "error-state", "field", "field-error", "form", "hover-card", "hover-card-content", "icon-button", "input", "input-group", "kbd", "kpi", "label", "layout-auth-split", "layout-blog", "layout-card", "layout-chrome", "layout-dashboard-grid", "layout-deck", "layout-doc", "layout-docs", "layout-drawer", "layout-dual-sidebar", "layout-editorial", "layout-grid", "layout-host", "layout-landing", "layout-marketing", "layout-page", "layout-pricing", "layout-settings", "layout-sidebar", "layout-top-nav", "layout-wizard", "media-composition-canvas", "media-composition-svg", "media-image", "menubar", "menubar-trigger", "multi-select", "nav-icon", "navigation-menu", "pagination", "popover", "popover-content", "radio", "radio-group", "responsive-modal", "screen-outlet", "select", "select-content", "select-group", "select-item", "select-label", "select-portal", "select-separator", "select-trigger", "select-value", "select-viewport", "sheet", "sidebar", "split-view", "stat", "stepper", "switch", "table", "tag", "text-counter", "textarea", "timeline", "toaster", "tooltip"];
4
+ export declare const SPEC_COMPONENT_IDS: readonly ["alert", "alert-dialog", "analytics-grid", "app-nav", "badge", "banner", "blocks-code", "blocks-data", "blocks-diagram", "blocks-embed", "blocks-interactive", "blocks-map", "blocks-media", "blocks-structure", "blocks-text", "breadcrumb", "button", "button-group", "callout", "card", "charts-area", "charts-bar", "charts-bubble", "charts-bullet", "charts-canvas-renderer", "charts-cartesian", "charts-combo", "charts-donut", "charts-funnel", "charts-gauge", "charts-geo-map", "charts-geo-point-map", "charts-heatmap", "charts-line", "charts-network", "charts-pie", "charts-progress-ring", "charts-sankey", "charts-scatter", "charts-sparkline", "charts-stacked-bar", "charts-sunburst", "charts-treemap", "checkbox", "chip", "code", "combobox", "command-palette", "content-layout", "context-menu", "context-menu-trigger", "deck-slide", "description", "detail-panel", "dialog", "drawer", "dropdown-menu", "dropdown-menu-content", "dropdown-menu-item", "dropdown-menu-trigger", "empty-state", "error-state", "field", "field-error", "form", "hover-card", "hover-card-content", "icon-button", "inline-text", "input", "input-group", "kbd", "kpi", "label", "layout-auth-split", "layout-blog", "layout-card", "layout-chrome", "layout-dashboard-grid", "layout-deck", "layout-doc", "layout-docs", "layout-drawer", "layout-dual-sidebar", "layout-editorial", "layout-grid", "layout-host", "layout-landing", "layout-marketing", "layout-page", "layout-pricing", "layout-settings", "layout-sidebar", "layout-top-nav", "layout-wizard", "media-composition-canvas", "media-composition-svg", "media-image", "menubar", "menubar-trigger", "multi-select", "nav-icon", "navigation-menu", "pagination", "popover", "popover-content", "radio", "radio-group", "responsive-modal", "screen-outlet", "select", "select-content", "select-group", "select-item", "select-label", "select-portal", "select-separator", "select-trigger", "select-value", "select-viewport", "sheet", "sidebar", "split-view", "stat", "stepper", "switch", "table", "tag", "text-counter", "textarea", "timeline", "toaster", "tooltip"];
5
5
  export type SpecComponentId = (typeof SPEC_COMPONENT_IDS)[number];
6
6
  /** The props a blueprint may set on each component (platform extensions left out). */
7
7
  export interface SpecComponentProps {
@@ -44,6 +44,7 @@ export interface SpecComponentProps {
44
44
  variant?: 'default' | 'inset';
45
45
  };
46
46
  badge: {
47
+ label?: string;
47
48
  variant?: 'default' | 'secondary' | 'outline' | 'success' | 'warning' | 'destructive' | 'info' | 'neutral';
48
49
  };
49
50
  banner: {
@@ -112,6 +113,7 @@ export interface SpecComponentProps {
112
113
  disabled?: boolean;
113
114
  href?: string;
114
115
  hrefTrust?: 'developer' | 'untrusted';
116
+ label?: string;
115
117
  loading?: boolean;
116
118
  loadingLabel?: string;
117
119
  size?: 'sm' | 'md' | 'lg' | 'icon';
@@ -147,6 +149,7 @@ export interface SpecComponentProps {
147
149
  mode?: 'light' | 'dark';
148
150
  series: readonly unknown[];
149
151
  title: string;
152
+ visibleDomain?: string;
150
153
  };
151
154
  'charts-bar': {
152
155
  annotations?: string;
@@ -156,6 +159,7 @@ export interface SpecComponentProps {
156
159
  mode?: 'light' | 'dark';
157
160
  series: readonly unknown[];
158
161
  title: string;
162
+ visibleDomain?: string;
159
163
  };
160
164
  'charts-bubble': {
161
165
  annotations?: string;
@@ -170,6 +174,7 @@ export interface SpecComponentProps {
170
174
  };
171
175
  'charts-bullet': {
172
176
  description?: string;
177
+ format?: Readonly<Record<string, unknown>>;
173
178
  label: string;
174
179
  labels?: Readonly<Record<string, unknown>>;
175
180
  max?: number;
@@ -192,6 +197,7 @@ export interface SpecComponentProps {
192
197
  mode?: 'light' | 'dark';
193
198
  series: readonly unknown[];
194
199
  title: string;
200
+ visibleDomain?: string;
195
201
  };
196
202
  'charts-combo': {
197
203
  annotations?: string;
@@ -201,6 +207,7 @@ export interface SpecComponentProps {
201
207
  mode?: 'light' | 'dark';
202
208
  series: readonly unknown[];
203
209
  title: string;
210
+ visibleDomain?: string;
204
211
  };
205
212
  'charts-donut': {
206
213
  data: readonly unknown[];
@@ -223,6 +230,7 @@ export interface SpecComponentProps {
223
230
  'charts-gauge': {
224
231
  bandLabels?: readonly unknown[];
225
232
  description?: string;
233
+ format?: Readonly<Record<string, unknown>>;
226
234
  label: string;
227
235
  labels?: Readonly<Record<string, unknown>>;
228
236
  max?: number;
@@ -268,6 +276,7 @@ export interface SpecComponentProps {
268
276
  mode?: 'light' | 'dark';
269
277
  series: readonly unknown[];
270
278
  title: string;
279
+ visibleDomain?: string;
271
280
  };
272
281
  'charts-network': {
273
282
  description?: string;
@@ -289,6 +298,7 @@ export interface SpecComponentProps {
289
298
  };
290
299
  'charts-progress-ring': {
291
300
  description?: string;
301
+ format?: Readonly<Record<string, unknown>>;
292
302
  label: string;
293
303
  labels?: Readonly<Record<string, unknown>>;
294
304
  max?: number;
@@ -318,6 +328,7 @@ export interface SpecComponentProps {
318
328
  };
319
329
  'charts-sparkline': {
320
330
  description?: string;
331
+ format?: Readonly<Record<string, unknown>>;
321
332
  height?: number;
322
333
  label: string;
323
334
  labels?: Readonly<Record<string, unknown>>;
@@ -334,6 +345,7 @@ export interface SpecComponentProps {
334
345
  mode?: 'light' | 'dark';
335
346
  series: readonly unknown[];
336
347
  title: string;
348
+ visibleDomain?: string;
337
349
  };
338
350
  'charts-sunburst': {
339
351
  description: string;
@@ -422,6 +434,13 @@ export interface SpecComponentProps {
422
434
  'context-menu-trigger': {
423
435
  disabled?: boolean;
424
436
  };
437
+ 'deck-slide': {
438
+ master?: 'title' | 'section' | 'content' | 'two-column' | 'media-left' | 'media-right' | 'quote' | 'closing';
439
+ notes?: string;
440
+ slideId: string;
441
+ title?: string;
442
+ transition?: 'none' | 'fade' | 'fade-up';
443
+ };
425
444
  description: Record<string, never>;
426
445
  'detail-panel': {
427
446
  closeOnEscape?: boolean;
@@ -508,6 +527,9 @@ export interface SpecComponentProps {
508
527
  loading?: boolean;
509
528
  variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive' | 'link';
510
529
  };
530
+ 'inline-text': {
531
+ text: string;
532
+ };
511
533
  input: {
512
534
  clearLabel?: string;
513
535
  clearable?: boolean;
@@ -525,7 +547,9 @@ export interface SpecComponentProps {
525
547
  readonly?: boolean;
526
548
  value?: string;
527
549
  };
528
- kbd: Record<string, never>;
550
+ kbd: {
551
+ label?: string;
552
+ };
529
553
  kpi: {
530
554
  comparisonLabel?: string;
531
555
  currency?: string;
@@ -576,6 +600,11 @@ export interface SpecComponentProps {
576
600
  storageKey?: string;
577
601
  };
578
602
  'layout-deck': {
603
+ aspect?: '16:9' | '4:3' | '1:1';
604
+ description?: string;
605
+ locale?: string;
606
+ masters?: readonly unknown[];
607
+ settings?: Readonly<Record<string, unknown>>;
579
608
  slides?: readonly unknown[];
580
609
  title?: string;
581
610
  };
@@ -1,7 +1,7 @@
1
1
  // Generated by @texuli/spec from catalog/components.json. Do not edit: run the spec generator.
2
- // Spec 0.1.0, digest 47afaa4f056320fc4111239e03afd97e8a073bd8dbd6d2ef0dec2d171acb2f8e
2
+ // Spec 0.2.0, digest d9eccaba1826e7d87648a4a33787d873245592dda843607a10b2c68a77f1806f
3
3
  /** The built catalog this vocabulary was projected from. */
4
- export const SPEC_CATALOG_HASH = '2516510c15ec62a630d56770349e47cad35f6d0e1654e8116ab1ef2607572617';
4
+ export const SPEC_CATALOG_HASH = 'b073e30b4c8b1a30b3ef3ccb372e552d2d88e584616b74591c6c32923b471376';
5
5
  /** Every closed component id, sorted. */
6
6
  export const SPEC_COMPONENT_IDS = [
7
7
  'alert',
@@ -55,6 +55,7 @@ export const SPEC_COMPONENT_IDS = [
55
55
  'content-layout',
56
56
  'context-menu',
57
57
  'context-menu-trigger',
58
+ 'deck-slide',
58
59
  'description',
59
60
  'detail-panel',
60
61
  'dialog',
@@ -71,6 +72,7 @@ export const SPEC_COMPONENT_IDS = [
71
72
  'hover-card',
72
73
  'hover-card-content',
73
74
  'icon-button',
75
+ 'inline-text',
74
76
  'input',
75
77
  'input-group',
76
78
  'kbd',
package/dist/index.d.ts CHANGED
@@ -5,11 +5,11 @@
5
5
  * hashing. Build-time generation and the conformance harness live in
6
6
  * `@texuli/spec/codegen`. Framework-free: no DOM, no Svelte and no Node APIs.
7
7
  */
8
- export type * from '../generated/typescript/blueprint.js';
9
- export type * from '../generated/typescript/catalog-schema.js';
10
- export type * from '../generated/typescript/diagnostics.js';
11
- export type * from '../generated/typescript/manifest.js';
12
- export { SPEC_CATALOG_HASH, SPEC_COMPONENT_IDS, type SpecComponentId, type SpecComponentProps, } from '../generated/typescript/vocabulary.js';
8
+ export type * from './generated/typescript/blueprint.js';
9
+ export type * from './generated/typescript/catalog-schema.js';
10
+ export type * from './generated/typescript/diagnostics.js';
11
+ export type * from './generated/typescript/manifest.js';
12
+ export { SPEC_CATALOG_HASH, SPEC_COMPONENT_IDS, type SpecComponentId, type SpecComponentProps, } from './generated/typescript/vocabulary.js';
13
13
  export { canonicalJson, sha256 } from './canonical.js';
14
14
  export { CAPABILITIES, type Capability, type SpecPropKind, WEB_ONLY_KINDS, WEB_ONLY_PROP_NAMES, } from './catalog.js';
15
15
  export { checkSchemaDocument, DIALECT_KEYWORDS, DIALECT_TYPES, DIALECT_URI, type DialectType, type Schema, type SchemaIssue, validateInstance, } from './dialect.js';
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * hashing. Build-time generation and the conformance harness live in
6
6
  * `@texuli/spec/codegen`. Framework-free: no DOM, no Svelte and no Node APIs.
7
7
  */
8
- export { SPEC_CATALOG_HASH, SPEC_COMPONENT_IDS, } from "../generated/typescript/vocabulary.js";
8
+ export { SPEC_CATALOG_HASH, SPEC_COMPONENT_IDS, } from "./generated/typescript/vocabulary.js";
9
9
  export { canonicalJson, sha256 } from "./canonical.js";
10
10
  export { CAPABILITIES, WEB_ONLY_KINDS, WEB_ONLY_PROP_NAMES, } from "./catalog.js";
11
11
  export { checkSchemaDocument, DIALECT_KEYWORDS, DIALECT_TYPES, DIALECT_URI, validateInstance, } from "./dialect.js";
@@ -1,4 +1,4 @@
1
- import type { SpecDiagnostic, SpecDiagnosticCode, SpecValidation } from '../generated/typescript/diagnostics.js';
1
+ import type { SpecDiagnostic, SpecDiagnosticCode, SpecValidation } from './generated/typescript/diagnostics.js';
2
2
  import type { SpecCatalog } from './catalog.js';
3
3
  import { type Schema } from './dialect.js';
4
4
  export type { SpecDiagnostic, SpecDiagnosticCode, SpecValidation };
@@ -10,7 +10,9 @@ export interface SpecDocuments {
10
10
  * Validate a blueprint envelope against the spec alone: the envelope schema,
11
11
  * then every node against the catalog vocabulary (closed ids, closed prop
12
12
  * names, prop kinds and enum values, required props, no platform-extension
13
- * props from data) and the declared profile. With `catalogHash`, an envelope
13
+ * props from data, a `slot` that names a slot prop of the parent) and the
14
+ * declared profile. A `when` condition is checked for shape only; the
15
+ * runtime formula parser checks its operands. With `catalogHash`, an envelope
14
16
  * bound to another catalog is refused. This is the shared, target-neutral
15
17
  * contract; URL policy, limits, grammar and rendering stay in the runtime
16
18
  * validators (`@texuli/catalog/validate`).
package/dist/validate.js CHANGED
@@ -4,7 +4,9 @@ const MAX_TREE_DEPTH = 64;
4
4
  * Validate a blueprint envelope against the spec alone: the envelope schema,
5
5
  * then every node against the catalog vocabulary (closed ids, closed prop
6
6
  * names, prop kinds and enum values, required props, no platform-extension
7
- * props from data) and the declared profile. With `catalogHash`, an envelope
7
+ * props from data, a `slot` that names a slot prop of the parent) and the
8
+ * declared profile. A `when` condition is checked for shape only; the
9
+ * runtime formula parser checks its operands. With `catalogHash`, an envelope
8
10
  * bound to another catalog is refused. This is the shared, target-neutral
9
11
  * contract; URL policy, limits, grammar and rendering stay in the runtime
10
12
  * validators (`@texuli/catalog/validate`).
@@ -30,9 +32,10 @@ export function validateBlueprint(input, documents, options = {}) {
30
32
  export function validateTree(tree, catalog, options = {}) {
31
33
  const byId = new Map(catalog.components.map((c) => [c.id, c]));
32
34
  const out = [];
33
- const visit = (node, path, depth) => {
35
+ const visit = (node, path, depth, parent) => {
34
36
  if (!isRecord(node) || typeof node.id !== 'string' || depth > MAX_TREE_DEPTH)
35
37
  return;
38
+ checkSlot(node.slot, parent, node.id, path, out);
36
39
  const component = byId.get(node.id);
37
40
  if (!component) {
38
41
  out.push({
@@ -55,13 +58,35 @@ export function validateTree(tree, catalog, options = {}) {
55
58
  }
56
59
  if (Array.isArray(node.children)) {
57
60
  node.children.forEach((child, index) => {
58
- visit(child, `${path}.children[${index}]`, depth + 1);
61
+ visit(child, `${path}.children[${index}]`, depth + 1, component);
59
62
  });
60
63
  }
61
64
  };
62
- visit(tree, options.path ?? '$', 0);
65
+ visit(tree, options.path ?? '$', 0, null);
63
66
  return out;
64
67
  }
68
+ /**
69
+ * A node's `slot` (audit G04) must name a slot prop of its parent; omitted
70
+ * or `children` is the default slot. A root fills no slot. An unknown
71
+ * parent was already reported, so its children's slots are not judged.
72
+ */
73
+ function checkSlot(slot, parent, id, path, out) {
74
+ if (slot === undefined || slot === 'children' || parent === undefined)
75
+ return;
76
+ const named = parent !== null &&
77
+ typeof slot === 'string' &&
78
+ parent.props.some((prop) => prop.kind === 'slot' && prop.name === slot);
79
+ if (named)
80
+ return;
81
+ out.push({
82
+ code: 'unknown-slot',
83
+ path: `${path}.slot`,
84
+ id,
85
+ message: parent === null
86
+ ? 'A root node fills no slot.'
87
+ : `"${String(slot)}" is not a slot of "${parent.id}".`,
88
+ });
89
+ }
65
90
  function checkProps(props, component, path, out) {
66
91
  const given = isRecord(props) ? props : {};
67
92
  const id = component.id;
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The semantic version of the spec documents in this package. */
2
- export declare const SPEC_VERSION = "0.1.0";
2
+ export declare const SPEC_VERSION = "0.2.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** The semantic version of the spec documents in this package. */
2
- export const SPEC_VERSION = '0.1.0';
2
+ export const SPEC_VERSION = '0.2.0';
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The spec's version history (`versions.json`): every released spec version
3
+ * pinned to the digest of its normative files, so the digest and the semantic
4
+ * version cannot drift apart. A normative change (schema, catalog projection,
5
+ * capability claim or fixture) changes the digest; it needs a new version
6
+ * and a new pin, with the change class the policy assigns to it.
7
+ */
8
+ /** What a release changes, by the policy in the package README. */
9
+ export type SpecChangeClass = 'patch' | 'minor' | 'major';
10
+ export interface SpecVersionPin {
11
+ specVersion: string;
12
+ /** The spec digest of that release, or `null` for a release pinned retroactively without one. */
13
+ specDigest: string | null;
14
+ change: SpecChangeClass | 'initial';
15
+ summary: string;
16
+ }
17
+ export interface SpecVersionHistory {
18
+ releases: readonly SpecVersionPin[];
19
+ }
20
+ export interface VersionIssue {
21
+ version: string;
22
+ message: string;
23
+ }
24
+ /**
25
+ * The version that must follow `previous` for a change of `change` class.
26
+ * Before 1.0.0 any change to accepted data (minor or major class) bumps the
27
+ * minor number and a patch-class change bumps the patch; from 1.0.0 each
28
+ * class bumps its own number.
29
+ */
30
+ export declare function nextSpecVersion(previous: string, change: SpecChangeClass): string;
31
+ /**
32
+ * Check the history and that the newest release is the spec as built now:
33
+ * the same version and the same digest. Returns every problem; empty means
34
+ * the version and the digest agree.
35
+ */
36
+ export declare function checkVersionHistory(history: SpecVersionHistory, current: {
37
+ specVersion: string;
38
+ specDigest: string;
39
+ }): VersionIssue[];
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The spec's version history (`versions.json`): every released spec version
3
+ * pinned to the digest of its normative files, so the digest and the semantic
4
+ * version cannot drift apart. A normative change (schema, catalog projection,
5
+ * capability claim or fixture) changes the digest; it needs a new version
6
+ * and a new pin, with the change class the policy assigns to it.
7
+ */
8
+ const SEMVER = /^(\d+)\.(\d+)\.(\d+)$/;
9
+ const DIGEST = /^[0-9a-f]{64}$/;
10
+ function parts(version) {
11
+ const match = SEMVER.exec(version);
12
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
13
+ }
14
+ /**
15
+ * The version that must follow `previous` for a change of `change` class.
16
+ * Before 1.0.0 any change to accepted data (minor or major class) bumps the
17
+ * minor number and a patch-class change bumps the patch; from 1.0.0 each
18
+ * class bumps its own number.
19
+ */
20
+ export function nextSpecVersion(previous, change) {
21
+ const p = parts(previous);
22
+ if (!p)
23
+ throw new Error(`${previous}: not a semantic version`);
24
+ const [major, minor, patch] = p;
25
+ if (change === 'patch')
26
+ return `${major}.${minor}.${patch + 1}`;
27
+ if (major === 0 || change === 'minor')
28
+ return `${major}.${minor + 1}.0`;
29
+ return `${major + 1}.0.0`;
30
+ }
31
+ /**
32
+ * Check the history and that the newest release is the spec as built now:
33
+ * the same version and the same digest. Returns every problem; empty means
34
+ * the version and the digest agree.
35
+ */
36
+ export function checkVersionHistory(history, current) {
37
+ const issues = [];
38
+ const releases = history.releases;
39
+ if (releases.length === 0) {
40
+ return [{ version: current.specVersion, message: 'versions.json lists no release.' }];
41
+ }
42
+ releases.forEach((release, index) => {
43
+ const { specVersion: version } = release;
44
+ if (!parts(version))
45
+ issues.push({ version, message: 'not a semantic version' });
46
+ if (release.specDigest !== null && !DIGEST.test(release.specDigest)) {
47
+ issues.push({ version, message: 'the digest is not a SHA-256 hex string' });
48
+ }
49
+ if (index === 0) {
50
+ if (release.change !== 'initial') {
51
+ issues.push({ version, message: 'the first release must have change "initial"' });
52
+ }
53
+ return;
54
+ }
55
+ const previous = releases[index - 1];
56
+ if (release.change === 'initial') {
57
+ issues.push({ version, message: 'only the first release is "initial"' });
58
+ return;
59
+ }
60
+ if (release.specDigest === null) {
61
+ issues.push({ version, message: 'only the first release may lack a digest' });
62
+ }
63
+ if (parts(previous.specVersion)) {
64
+ const expected = nextSpecVersion(previous.specVersion, release.change);
65
+ if (version !== expected) {
66
+ issues.push({
67
+ version,
68
+ message: `a ${release.change} change after ${previous.specVersion} is ${expected}`,
69
+ });
70
+ }
71
+ }
72
+ });
73
+ const newest = releases[releases.length - 1];
74
+ if (newest.specVersion !== current.specVersion) {
75
+ issues.push({
76
+ version: current.specVersion,
77
+ message: `the newest release in versions.json is ${newest.specVersion}, the spec is ${current.specVersion}`,
78
+ });
79
+ }
80
+ else if (newest.specDigest !== current.specDigest) {
81
+ issues.push({
82
+ version: current.specVersion,
83
+ message: 'the normative files changed without a new version: classify the change, bump SPEC_VERSION, regenerate, and append a release pinning the new digest',
84
+ });
85
+ }
86
+ return issues;
87
+ }
package/package.json CHANGED
@@ -1,8 +1,23 @@
1
1
  {
2
2
  "name": "@texuli/spec",
3
- "version": "0.1.0-next.0",
3
+ "version": "0.1.0-next.3",
4
4
  "description": "The schema-first blueprint contract: JSON schemas, the catalog vocabulary, deterministic TypeScript and Kotlin projections, and the conformance harness.",
5
+ "keywords": [
6
+ "svelte",
7
+ "svelte-5",
8
+ "components",
9
+ "design-system",
10
+ "texuli"
11
+ ],
12
+ "homepage": "https://github.com/DailybotHQ/texuli#readme",
13
+ "bugs": "https://github.com/DailybotHQ/texuli/issues",
5
14
  "license": "MIT",
15
+ "author": "Dailybot, Inc.",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/DailybotHQ/texuli.git",
19
+ "directory": "packages/spec"
20
+ },
6
21
  "type": "module",
7
22
  "sideEffects": [
8
23
  "**/*.css"
@@ -18,11 +33,6 @@
18
33
  }
19
34
  },
20
35
  "files": [
21
- "dist",
22
- "generated/typescript"
23
- ],
24
- "publishConfig": {
25
- "access": "public",
26
- "tag": "next"
27
- }
36
+ "dist"
37
+ ]
28
38
  }