@forumone/throughline-design-contract 0.0.1 → 0.2.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +135 -28
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/lint.d.ts +38 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +110 -0
- package/dist/lint.js.map +1 -0
- package/dist/loader.d.ts +42 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +79 -0
- package/dist/loader.js.map +1 -0
- package/dist/manifest.d.ts +449 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +29 -0
- package/dist/manifest.js.map +1 -0
- package/dist/schema.d.ts +276 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +110 -0
- package/dist/schema.js.map +1 -0
- package/package.json +58 -7
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @forumone/throughline-design-contract
|
|
2
|
+
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#9](https://github.com/forumone/throughline/pull/9) [`337f2ca`](https://github.com/forumone/throughline/commit/337f2ca779a30d2f135845259bbae8e961a625ed) Thanks [@briangraves](https://github.com/briangraves)! - Initial release. Defines `ComponentContractSchema`, `ManifestSchema`, `loadManifest`, `loadManifestFromUrl`, and `lintManifest`. Every design system that satisfies this contract is a valid input to the framework's Component Server.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Forum One Communications Corporation
|
|
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/README.md
CHANGED
|
@@ -1,45 +1,152 @@
|
|
|
1
1
|
# @forumone/throughline-design-contract
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The contract every AI-ready design system satisfies to be consumable by Throughline.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## What this package provides
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **`ComponentContractSchema`** — the Zod schema for per-component metadata (intent, composition rules, content fields, tokens, accessibility, examples).
|
|
8
|
+
- **`ManifestSchema`** — the aggregated JSON format a design system publishes, versioned via `contractVersion`.
|
|
9
|
+
- **`loadManifest` / `loadManifestFromUrl`** — runtime loaders with strict validation and a readable error format.
|
|
10
|
+
- **`lintManifest`** (via the `/lint` subpath) — CI-runnable consistency checks for design system repos.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
Zero runtime dependencies beyond Zod. Safe to import into client apps, design systems, and server packages alike.
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
1. Configure OIDC trusted publishing for the package name `@forumone/throughline-design-contract`
|
|
13
|
-
2. Enable secure, token-less publishing from CI/CD workflows
|
|
14
|
-
3. Establish provenance for packages published under this name
|
|
14
|
+
## Installation
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @forumone/throughline-design-contract
|
|
18
|
+
```
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
## Authoring contracts in a design system
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
Each component has a co-located contract file:
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
```
|
|
25
|
+
src/components/Hero/
|
|
26
|
+
├── Hero.tsx
|
|
27
|
+
├── Hero.stories.tsx
|
|
28
|
+
├── Hero.contract.ts
|
|
29
|
+
└── index.ts
|
|
30
|
+
```
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
2. Configure the trusted publisher (e.g., GitHub Actions)
|
|
26
|
-
3. Specify the repository and workflow that should be allowed to publish
|
|
27
|
-
4. Use the configured workflow to publish your actual package
|
|
32
|
+
`Hero.contract.ts` exports a single object that satisfies `ComponentContract`:
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
```typescript
|
|
35
|
+
import type { ComponentContract } from '@forumone/throughline-design-contract'
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
export const contract: ComponentContract = {
|
|
38
|
+
name: 'Hero',
|
|
39
|
+
category: 'hero',
|
|
40
|
+
description: 'A page opener with a headline and optional call-to-action.',
|
|
41
|
+
intent:
|
|
42
|
+
'Used to establish what a page is about within the first viewport. Appropriate for top-level pages that need editorial framing.',
|
|
43
|
+
composition: {
|
|
44
|
+
placement: ['page'],
|
|
45
|
+
maxPerPage: 1,
|
|
46
|
+
requiredSiblings: [],
|
|
47
|
+
forbiddenAdjacent: ['Hero'],
|
|
48
|
+
},
|
|
49
|
+
content: {
|
|
50
|
+
fields: [{ name: 'headline', type: 'text', required: true, maxLength: 80 }],
|
|
51
|
+
},
|
|
52
|
+
tokens: { consumes: ['color.brand.primary'] },
|
|
53
|
+
accessibility: {
|
|
54
|
+
keyboardSupport: [],
|
|
55
|
+
screenReaderBehavior: 'Headline is announced as h1 by default.',
|
|
56
|
+
contentWarnings: [],
|
|
57
|
+
},
|
|
58
|
+
examples: [{ label: 'Default', intent: 'Standard page opener', storyId: 'hero--default' }],
|
|
59
|
+
antiExamples: [
|
|
60
|
+
{ label: 'Stacked heroes', why: 'Breaks the visual rhythm.', useInstead: 'Section' },
|
|
61
|
+
],
|
|
62
|
+
}
|
|
63
|
+
```
|
|
36
64
|
|
|
37
|
-
|
|
65
|
+
Your design system's build tooling aggregates these into a `Manifest`. See the reference design system for a canonical example.
|
|
38
66
|
|
|
39
|
-
|
|
40
|
-
- [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
|
|
41
|
-
- [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
|
|
67
|
+
## Loading a manifest at runtime
|
|
42
68
|
|
|
43
|
-
|
|
69
|
+
```typescript
|
|
70
|
+
import { loadManifest } from '@forumone/throughline-design-contract'
|
|
71
|
+
import manifest from '@my-company/design-system/manifest.json'
|
|
44
72
|
|
|
45
|
-
|
|
73
|
+
const loaded = loadManifest(manifest)
|
|
74
|
+
|
|
75
|
+
const hero = loaded.requireComponent('Hero')
|
|
76
|
+
console.log(hero.intent)
|
|
77
|
+
|
|
78
|
+
for (const name of loaded.listByCategory('card')) {
|
|
79
|
+
console.log(name)
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Invalid manifests throw immediately with a path-qualified error message. The loader never partially loads invalid data.
|
|
84
|
+
|
|
85
|
+
### Loading over HTTP
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { loadManifestFromUrl } from '@forumone/throughline-design-contract'
|
|
89
|
+
|
|
90
|
+
const loaded = await loadManifestFromUrl('https://ds.example.com/manifest.json')
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Linting in CI
|
|
94
|
+
|
|
95
|
+
Every design system should lint its own manifest before publishing. Import the lint helpers from the `/lint` subpath:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { lintManifest, formatLintIssues } from '@forumone/throughline-design-contract/lint'
|
|
99
|
+
import manifest from './dist/manifest.json'
|
|
100
|
+
|
|
101
|
+
const issues = lintManifest(manifest, {
|
|
102
|
+
availableStoryIds: new Set(/* collected from storybook-static */),
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
if (issues.some((i) => i.severity === 'error')) {
|
|
106
|
+
console.error(formatLintIssues(issues))
|
|
107
|
+
process.exit(1)
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`lintManifest` checks:
|
|
112
|
+
|
|
113
|
+
- Every `requiredSiblings` and `forbiddenAdjacent` entry references a real component in the manifest.
|
|
114
|
+
- Every token in `tokens.consumes` exists in the manifest's token table (or in `availableTokens` if you pass one).
|
|
115
|
+
- Every example's `storyId` exists in `availableStoryIds` (skipped when the option is omitted).
|
|
116
|
+
- Warnings: components with no anti-examples; intent statements shorter than 50 characters.
|
|
117
|
+
|
|
118
|
+
`assertManifestClean(manifest, options)` is the CI-friendly form: throws on any error, silent on warnings-only.
|
|
119
|
+
|
|
120
|
+
## Versioning
|
|
121
|
+
|
|
122
|
+
The current contract version is `1.0.0`, exported as `CONTRACT_VERSION`. Manifests declare it via the `contractVersion` field; the loader rejects mismatches so Claude never recommends a component from a schema it does not understand.
|
|
123
|
+
|
|
124
|
+
When the contract evolves, this package ships a new major version with migration guidance in the changelog.
|
|
125
|
+
|
|
126
|
+
## Relationship to Storybook AI manifests
|
|
127
|
+
|
|
128
|
+
Storybook ships its own [AI manifest format](https://storybook.js.org/docs/ai/manifests) (`/manifests/components.json`). It is a static-analysis artifact that describes what *exists* in a Storybook — component ids, paths, props with types and JSDoc, story ids, import statements. Storybook marks it as preview / unstable.
|
|
129
|
+
|
|
130
|
+
This package's contract describes what's *appropriate* — intent, composition rules, accessibility expectations, anti-examples, token consumption. Hand-authored, stable, versioned.
|
|
131
|
+
|
|
132
|
+
They complement rather than overlap. Typical integration: during a design system's CI, parse Storybook's `components.json`, collect every `stories[].id`, and pass the set to `lintManifest` as `availableStoryIds`. That gives you the "does this `storyId` resolve to a real story?" check for free:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import storybookManifest from './storybook-static/manifests/components.json'
|
|
136
|
+
import contractManifest from './dist/manifest.json'
|
|
137
|
+
import { lintManifest } from '@forumone/throughline-design-contract/lint'
|
|
138
|
+
|
|
139
|
+
const availableStoryIds = new Set(
|
|
140
|
+
Object.values(storybookManifest.components).flatMap((c: { stories: Array<{ id: string }> }) =>
|
|
141
|
+
c.stories.map((s) => s.id),
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
const issues = lintManifest(contractManifest, { availableStoryIds })
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Related packages
|
|
149
|
+
|
|
150
|
+
- `@forumone/throughline-reference-ds` — a reference design system that satisfies this contract (C3).
|
|
151
|
+
- `@forumone/throughline-components` — the Component Server MCP that consumes manifests (C5).
|
|
152
|
+
- `@forumone/throughline-plugin-contract` — the plugin contract for packages that extend Payload.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAA;AAC3B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA"}
|
package/dist/lint.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Manifest } from './manifest.js';
|
|
2
|
+
export interface LintIssue {
|
|
3
|
+
severity: 'error' | 'warning';
|
|
4
|
+
component: string;
|
|
5
|
+
rule: string;
|
|
6
|
+
message: string;
|
|
7
|
+
}
|
|
8
|
+
export interface LintOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Source of truth for Storybook story IDs. If provided, lint verifies
|
|
11
|
+
* every example's `storyId` exists in this set. Omit to skip the check
|
|
12
|
+
* (e.g. in environments that do not have Storybook output available).
|
|
13
|
+
*/
|
|
14
|
+
availableStoryIds?: Set<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Source of truth for token names. If provided, lint verifies every
|
|
17
|
+
* token referenced by a component's `tokens.consumes` exists in this set.
|
|
18
|
+
* Defaults to the manifest's own token table when omitted.
|
|
19
|
+
*/
|
|
20
|
+
availableTokens?: Set<string>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Lints a manifest against the contract's internal consistency rules.
|
|
24
|
+
* Returns an array of issues; an empty array means the manifest is clean.
|
|
25
|
+
*
|
|
26
|
+
* Errors describe unambiguous contract violations (unknown component
|
|
27
|
+
* references, unknown tokens). Warnings surface weaker signals that may
|
|
28
|
+
* still be worth addressing.
|
|
29
|
+
*/
|
|
30
|
+
export declare function lintManifest(manifest: Manifest, options?: LintOptions): LintIssue[];
|
|
31
|
+
/** Formats lint issues as a multi-line string suitable for CI output. */
|
|
32
|
+
export declare function formatLintIssues(issues: LintIssue[]): string;
|
|
33
|
+
/**
|
|
34
|
+
* Throws when the manifest has any lint errors. Warnings do not throw.
|
|
35
|
+
* Use in CI to fail the build on contract violations.
|
|
36
|
+
*/
|
|
37
|
+
export declare function assertManifestClean(manifest: Manifest, options?: LintOptions): void;
|
|
38
|
+
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC/B;;;;OAIG;IACH,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,GAAE,WAAgB,GAAG,SAAS,EAAE,CAyEvF;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAwB5D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAMnF"}
|
package/dist/lint.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lints a manifest against the contract's internal consistency rules.
|
|
3
|
+
* Returns an array of issues; an empty array means the manifest is clean.
|
|
4
|
+
*
|
|
5
|
+
* Errors describe unambiguous contract violations (unknown component
|
|
6
|
+
* references, unknown tokens). Warnings surface weaker signals that may
|
|
7
|
+
* still be worth addressing.
|
|
8
|
+
*/
|
|
9
|
+
export function lintManifest(manifest, options = {}) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
const componentNames = new Set(Object.keys(manifest.components));
|
|
12
|
+
const tokenNames = options.availableTokens ?? new Set(manifest.tokens.map((t) => t.name));
|
|
13
|
+
const storyIds = options.availableStoryIds;
|
|
14
|
+
for (const [name, component] of Object.entries(manifest.components)) {
|
|
15
|
+
for (const sibling of component.composition.requiredSiblings) {
|
|
16
|
+
if (!componentNames.has(sibling)) {
|
|
17
|
+
issues.push({
|
|
18
|
+
severity: 'error',
|
|
19
|
+
component: name,
|
|
20
|
+
rule: 'composition.requiredSiblings',
|
|
21
|
+
message: `References unknown component "${sibling}"`,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
for (const adjacent of component.composition.forbiddenAdjacent) {
|
|
26
|
+
if (!componentNames.has(adjacent)) {
|
|
27
|
+
issues.push({
|
|
28
|
+
severity: 'error',
|
|
29
|
+
component: name,
|
|
30
|
+
rule: 'composition.forbiddenAdjacent',
|
|
31
|
+
message: `References unknown component "${adjacent}"`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
for (const token of component.tokens.consumes) {
|
|
36
|
+
if (!tokenNames.has(token)) {
|
|
37
|
+
issues.push({
|
|
38
|
+
severity: 'error',
|
|
39
|
+
component: name,
|
|
40
|
+
rule: 'tokens.consumes',
|
|
41
|
+
message: `References unknown token "${token}"`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (storyIds) {
|
|
46
|
+
for (const example of component.examples) {
|
|
47
|
+
if (!storyIds.has(example.storyId)) {
|
|
48
|
+
issues.push({
|
|
49
|
+
severity: 'error',
|
|
50
|
+
component: name,
|
|
51
|
+
rule: 'examples.storyId',
|
|
52
|
+
message: `Example "${example.label}" references unknown story "${example.storyId}"`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (component.antiExamples.length === 0) {
|
|
58
|
+
issues.push({
|
|
59
|
+
severity: 'warning',
|
|
60
|
+
component: name,
|
|
61
|
+
rule: 'antiExamples.empty',
|
|
62
|
+
message: 'Component has no anti-examples; consider adding at least one',
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (component.intent.length < 50) {
|
|
66
|
+
issues.push({
|
|
67
|
+
severity: 'warning',
|
|
68
|
+
component: name,
|
|
69
|
+
rule: 'intent.brevity',
|
|
70
|
+
message: 'Intent statement is quite short; consider a more specific description',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return issues;
|
|
75
|
+
}
|
|
76
|
+
/** Formats lint issues as a multi-line string suitable for CI output. */
|
|
77
|
+
export function formatLintIssues(issues) {
|
|
78
|
+
if (issues.length === 0)
|
|
79
|
+
return 'No issues found.';
|
|
80
|
+
const errors = issues.filter((i) => i.severity === 'error');
|
|
81
|
+
const warnings = issues.filter((i) => i.severity === 'warning');
|
|
82
|
+
const lines = [];
|
|
83
|
+
if (errors.length > 0) {
|
|
84
|
+
lines.push(`Errors (${errors.length}):`);
|
|
85
|
+
for (const issue of errors) {
|
|
86
|
+
lines.push(` [${issue.component}] ${issue.rule}: ${issue.message}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (warnings.length > 0) {
|
|
90
|
+
if (lines.length > 0)
|
|
91
|
+
lines.push('');
|
|
92
|
+
lines.push(`Warnings (${warnings.length}):`);
|
|
93
|
+
for (const issue of warnings) {
|
|
94
|
+
lines.push(` [${issue.component}] ${issue.rule}: ${issue.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return lines.join('\n');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Throws when the manifest has any lint errors. Warnings do not throw.
|
|
101
|
+
* Use in CI to fail the build on contract violations.
|
|
102
|
+
*/
|
|
103
|
+
export function assertManifestClean(manifest, options) {
|
|
104
|
+
const issues = lintManifest(manifest, options);
|
|
105
|
+
const errors = issues.filter((i) => i.severity === 'error');
|
|
106
|
+
if (errors.length > 0) {
|
|
107
|
+
throw new Error(`Manifest has errors:\n${formatLintIssues(errors)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=lint.js.map
|
package/dist/lint.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"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"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type Manifest } from './manifest.js';
|
|
2
|
+
import type { ComponentContract, ComponentCategory } from './schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* A validated, queryable manifest. Returned by {@link loadManifest} and
|
|
5
|
+
* {@link loadManifestFromUrl}. Consumers use the helper methods rather than
|
|
6
|
+
* poking at {@link raw} directly, which lets the implementation evolve.
|
|
7
|
+
*/
|
|
8
|
+
export declare class LoadedManifest {
|
|
9
|
+
readonly raw: Manifest;
|
|
10
|
+
constructor(raw: Manifest);
|
|
11
|
+
/** The design system's metadata. */
|
|
12
|
+
get designSystem(): Manifest['designSystem'];
|
|
13
|
+
/** The contract version this manifest satisfies. */
|
|
14
|
+
get contractVersion(): Manifest['contractVersion'];
|
|
15
|
+
/** Full contract for a component by name, or `undefined` if absent. */
|
|
16
|
+
getComponent(name: string): ComponentContract | undefined;
|
|
17
|
+
/** Like {@link getComponent} but throws when the component is missing. */
|
|
18
|
+
requireComponent(name: string): ComponentContract;
|
|
19
|
+
/** Every component name in declaration order. */
|
|
20
|
+
listComponents(): string[];
|
|
21
|
+
/** Every component whose category matches. */
|
|
22
|
+
listByCategory(category: ComponentCategory | string): ComponentContract[];
|
|
23
|
+
/** Every distinct category present, sorted alphabetically. */
|
|
24
|
+
listCategories(): string[];
|
|
25
|
+
/** Token definition by name, or `undefined` if absent. */
|
|
26
|
+
getToken(name: string): {
|
|
27
|
+
value: string;
|
|
28
|
+
name: string;
|
|
29
|
+
category: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loads a manifest from a plain object. Validates against {@link ManifestSchema}
|
|
34
|
+
* and throws on any validation error with a readable, line-by-line message.
|
|
35
|
+
*/
|
|
36
|
+
export declare function loadManifest(input: unknown): LoadedManifest;
|
|
37
|
+
/**
|
|
38
|
+
* Fetches a manifest from a URL and loads it. Useful when a design system
|
|
39
|
+
* serves its manifest via HTTP rather than bundling into the consumer.
|
|
40
|
+
*/
|
|
41
|
+
export declare function loadManifestFromUrl(url: string, init?: RequestInit): Promise<LoadedManifest>;
|
|
42
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAkB,MAAM,eAAe,CAAA;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEvE;;;;GAIG;AACH,qBAAa,cAAc;aACG,GAAG,EAAE,QAAQ;gBAAb,GAAG,EAAE,QAAQ;IAEzC,oCAAoC;IACpC,IAAI,YAAY,IAAI,QAAQ,CAAC,cAAc,CAAC,CAE3C;IAED,oDAAoD;IACpD,IAAI,eAAe,IAAI,QAAQ,CAAC,iBAAiB,CAAC,CAEjD;IAED,uEAAuE;IACvE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIzD,0EAA0E;IAC1E,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB;IAQjD,iDAAiD;IACjD,cAAc,IAAI,MAAM,EAAE;IAI1B,8CAA8C;IAC9C,cAAc,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,GAAG,iBAAiB,EAAE;IAIzE,8DAA8D;IAC9D,cAAc,IAAI,MAAM,EAAE;IAQ1B,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM;;;;;CAGtB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAS3D;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,WAAW,GACjB,OAAO,CAAC,cAAc,CAAC,CAOzB"}
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ManifestSchema } from './manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* A validated, queryable manifest. Returned by {@link loadManifest} and
|
|
4
|
+
* {@link loadManifestFromUrl}. Consumers use the helper methods rather than
|
|
5
|
+
* poking at {@link raw} directly, which lets the implementation evolve.
|
|
6
|
+
*/
|
|
7
|
+
export class LoadedManifest {
|
|
8
|
+
raw;
|
|
9
|
+
constructor(raw) {
|
|
10
|
+
this.raw = raw;
|
|
11
|
+
}
|
|
12
|
+
/** The design system's metadata. */
|
|
13
|
+
get designSystem() {
|
|
14
|
+
return this.raw.designSystem;
|
|
15
|
+
}
|
|
16
|
+
/** The contract version this manifest satisfies. */
|
|
17
|
+
get contractVersion() {
|
|
18
|
+
return this.raw.contractVersion;
|
|
19
|
+
}
|
|
20
|
+
/** Full contract for a component by name, or `undefined` if absent. */
|
|
21
|
+
getComponent(name) {
|
|
22
|
+
return this.raw.components[name];
|
|
23
|
+
}
|
|
24
|
+
/** Like {@link getComponent} but throws when the component is missing. */
|
|
25
|
+
requireComponent(name) {
|
|
26
|
+
const component = this.getComponent(name);
|
|
27
|
+
if (!component) {
|
|
28
|
+
throw new Error(`Component "${name}" not found in manifest`);
|
|
29
|
+
}
|
|
30
|
+
return component;
|
|
31
|
+
}
|
|
32
|
+
/** Every component name in declaration order. */
|
|
33
|
+
listComponents() {
|
|
34
|
+
return Object.keys(this.raw.components);
|
|
35
|
+
}
|
|
36
|
+
/** Every component whose category matches. */
|
|
37
|
+
listByCategory(category) {
|
|
38
|
+
return Object.values(this.raw.components).filter((c) => c.category === category);
|
|
39
|
+
}
|
|
40
|
+
/** Every distinct category present, sorted alphabetically. */
|
|
41
|
+
listCategories() {
|
|
42
|
+
const categories = new Set();
|
|
43
|
+
for (const component of Object.values(this.raw.components)) {
|
|
44
|
+
categories.add(component.category);
|
|
45
|
+
}
|
|
46
|
+
return Array.from(categories).sort();
|
|
47
|
+
}
|
|
48
|
+
/** Token definition by name, or `undefined` if absent. */
|
|
49
|
+
getToken(name) {
|
|
50
|
+
return this.raw.tokens.find((t) => t.name === name);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Loads a manifest from a plain object. Validates against {@link ManifestSchema}
|
|
55
|
+
* and throws on any validation error with a readable, line-by-line message.
|
|
56
|
+
*/
|
|
57
|
+
export function loadManifest(input) {
|
|
58
|
+
const result = ManifestSchema.safeParse(input);
|
|
59
|
+
if (!result.success) {
|
|
60
|
+
const issues = result.error.issues
|
|
61
|
+
.map((i) => ` - ${i.path.join('.') || '(root)'}: ${i.message}`)
|
|
62
|
+
.join('\n');
|
|
63
|
+
throw new Error(`Invalid manifest:\n${issues}`);
|
|
64
|
+
}
|
|
65
|
+
return new LoadedManifest(result.data);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Fetches a manifest from a URL and loads it. Useful when a design system
|
|
69
|
+
* serves its manifest via HTTP rather than bundling into the consumer.
|
|
70
|
+
*/
|
|
71
|
+
export async function loadManifestFromUrl(url, init) {
|
|
72
|
+
const response = await fetch(url, init);
|
|
73
|
+
if (!response.ok) {
|
|
74
|
+
throw new Error(`Failed to fetch manifest from ${url}: HTTP ${response.status}`);
|
|
75
|
+
}
|
|
76
|
+
const json = (await response.json());
|
|
77
|
+
return loadManifest(json);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|