@hirobius/design-system 0.9.0 → 0.10.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/README.md +0 -1
- package/package.json +2 -11
- package/protocol/envelope.mjs +0 -201
- package/protocol/figma-snapshot.schema.json +0 -190
package/README.md
CHANGED
|
@@ -90,7 +90,6 @@ Typical loop:
|
|
|
90
90
|
## Bundle and release hygiene
|
|
91
91
|
|
|
92
92
|
- `pnpm check:size` builds the library bundle and runs `size-limit`.
|
|
93
|
-
- `pnpm perf:budget` runs Lighthouse CI assertions for page performance budgets.
|
|
94
93
|
- `pnpm check:release` runs the full release gate (accessibility, responsive, collision, visual, and bundle-size checks).
|
|
95
94
|
|
|
96
95
|
Releases are cut with [Changesets](https://github.com/changesets/changesets): a merged changeset opens a "Version Packages" PR, and merging that PR publishes the new version to public npm via `.github/workflows/release.yml`.
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@hirobius/design-system",
|
|
3
3
|
"description": "Hirobius Design Systems Architecture: token-governed React components, documentation surfaces, and distribution tooling.",
|
|
4
4
|
"private": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.10.0",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20",
|
|
8
8
|
"pnpm": ">=8"
|
|
@@ -57,16 +57,10 @@
|
|
|
57
57
|
"import": "./dist/mui.js",
|
|
58
58
|
"default": "./dist/mui.js"
|
|
59
59
|
},
|
|
60
|
-
"./protocol": {
|
|
61
|
-
"import": "./protocol/envelope.mjs",
|
|
62
|
-
"default": "./protocol/envelope.mjs"
|
|
63
|
-
},
|
|
64
60
|
"./package.json": "./package.json"
|
|
65
61
|
},
|
|
66
62
|
"files": [
|
|
67
63
|
"dist",
|
|
68
|
-
"protocol/envelope.mjs",
|
|
69
|
-
"protocol/figma-snapshot.schema.json",
|
|
70
64
|
"hirobius.tokens.json",
|
|
71
65
|
"public/hds-manifest.json"
|
|
72
66
|
],
|
|
@@ -226,7 +220,6 @@
|
|
|
226
220
|
"audit:sbom": "node scripts/audit-sbom.mjs",
|
|
227
221
|
"audit:sidecar": "node scripts/generate-security-posture.mjs",
|
|
228
222
|
"audit:style-dictionary": "bash scripts/poc/style-dictionary-poc/run.sh",
|
|
229
|
-
"bridge": "node --env-file=.env.local scripts/hds-bridge.mjs",
|
|
230
223
|
"build": "pnpm sync:health && node scripts/sync-icons.mjs && vite build --configLoader native",
|
|
231
224
|
"build:ssr": "vite build --ssr src/entry-server.tsx --outDir .ssr-cache --configLoader native",
|
|
232
225
|
"build:prerender": "pnpm build && pnpm build:ssr && node scripts/prerender.mjs",
|
|
@@ -282,7 +275,7 @@
|
|
|
282
275
|
"check:page-shell": "node scripts/check-page-shell.mjs",
|
|
283
276
|
"check:refs": "node scripts/check-ref-forwarding.mjs",
|
|
284
277
|
"check:registry": "node scripts/check-registry.mjs",
|
|
285
|
-
"check:release": "pnpm check:full && node scripts/check-style-discipline.mjs && pnpm build && pnpm
|
|
278
|
+
"check:release": "pnpm check:full && node scripts/check-style-discipline.mjs && pnpm build && pnpm test:a11y && pnpm test:visual && pnpm test:responsive && pnpm test:collision && pnpm check:size",
|
|
286
279
|
"check:routes": "node scripts/check-link-integrity.mjs --route-links-only",
|
|
287
280
|
"check:secrets": "node scripts/check-secrets.mjs",
|
|
288
281
|
"check:security": "node scripts/check-security-baseline.mjs",
|
|
@@ -306,7 +299,6 @@
|
|
|
306
299
|
"docs:changelogs": "node scripts/generate-component-changelogs.mjs",
|
|
307
300
|
"docs:llms": "node scripts/generate-llms-txt.mjs",
|
|
308
301
|
"figma-variables": "node scripts/build-figma-variables.mjs",
|
|
309
|
-
"figma:bridge:smoke": "node scripts/figma-bridge-smoke.mjs",
|
|
310
302
|
"figma:diff": "node scripts/figma-diff.mjs",
|
|
311
303
|
"figma:snapshot": "node scripts/normalize-figma-snapshot.mjs",
|
|
312
304
|
"figma:stream": "node scripts/llm-stream-bridge.mjs",
|
|
@@ -322,7 +314,6 @@
|
|
|
322
314
|
"manifest:generate": "node scripts/generate-manifest.mjs && node scripts/generate-component-api.mjs && node scripts/enrich-manifest.mjs",
|
|
323
315
|
"manifest:project": "node scripts/generate-manifest-projection.mjs",
|
|
324
316
|
"nav:generate": "node scripts/generate-nav-model.mjs",
|
|
325
|
-
"perf:budget": "node -e \"require('fs').readdirSync('.lighthouseci').filter(f=>f.startsWith('lhr-')).forEach(f=>require('fs').unlinkSync('.lighthouseci/'+f))\" 2>/dev/null; lhci collect --config=.lighthouserc.json && lhci assert --config=.lighthouserc.json; find . -maxdepth 1 -type d -name 'C:*' -exec rm -rf {} + 2>/dev/null; true",
|
|
326
317
|
"prebuild": "node scripts/validate-manifest.mjs",
|
|
327
318
|
"prebuild:lib": "node scripts/generate-component-api.mjs",
|
|
328
319
|
"pretest": "node scripts/validate-manifest.mjs && node scripts/check-manifest-drift.mjs && node scripts/check-manifest-schema-semver.mjs && node scripts/audit-component-integrity.mjs --completeness && node scripts/check-source-canon.mjs && node scripts/check-binding-drift.mjs && node scripts/test-figma-masters-snapshot.mjs && node scripts/test-doc-pages-snapshot.mjs && node scripts/check-registry.mjs && node scripts/check-tenant-tokens.mjs && knip --no-exit-code",
|
package/protocol/envelope.mjs
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* protocol/envelope.mjs
|
|
3
|
-
*
|
|
4
|
-
* Message-envelope protocol for bridge↔plugin traffic. Build unit p5-1.
|
|
5
|
-
*
|
|
6
|
-
* Envelope shape:
|
|
7
|
-
* {
|
|
8
|
-
* id: string (UUID v4)
|
|
9
|
-
* type: string (verb, e.g. "selection.update")
|
|
10
|
-
* payload: object
|
|
11
|
-
* timestamp: number (ms since epoch)
|
|
12
|
-
* hmac: string (HMAC-SHA256(secret, stableStringify({id,type,timestamp,payload})))
|
|
13
|
-
* }
|
|
14
|
-
*
|
|
15
|
-
* The Figma plugin sandbox cannot import from this file. The mirror
|
|
16
|
-
* implementation lives at `figma-agent-plugin/protocol-envelope.js` and
|
|
17
|
-
* MUST stay byte-equivalent in HMAC output for the same inputs.
|
|
18
|
-
*
|
|
19
|
-
* Schema decisions (also documented in the unit's commit body):
|
|
20
|
-
* - Clock-skew tolerance: ±60s on either side of now.
|
|
21
|
-
* - Hard expiry: 5 minutes (300_000 ms) past `timestamp`.
|
|
22
|
-
* - Replay-protection set: in-memory Map, FIFO-evicted at 1000 entries.
|
|
23
|
-
*
|
|
24
|
-
* Hard rules (CLAUDE.md / OPERATOR_BRIEF.md):
|
|
25
|
-
* - No new npm dependencies; native Node crypto only.
|
|
26
|
-
* - This file is the protocol library only — wiring the envelope into
|
|
27
|
-
* hds-bridge.mjs / plugin code.js belongs to p5-2 and p5-3.
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
import crypto from 'node:crypto';
|
|
31
|
-
|
|
32
|
-
export const CLOCK_SKEW_MS = 60_000; // ±60s tolerance for future timestamps
|
|
33
|
-
export const MAX_AGE_MS = 5 * 60_000; // 5 min hard expiry
|
|
34
|
-
export const REPLAY_SET_LIMIT = 1000; // FIFO eviction beyond this
|
|
35
|
-
|
|
36
|
-
// ── stable serialization ───────────────────────────────────────────────────
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Deterministic JSON serializer: object keys sorted lexicographically at
|
|
40
|
-
* every level, no whitespace. Used as the HMAC pre-image so the Node-side
|
|
41
|
-
* and plugin-side implementations produce identical signatures.
|
|
42
|
-
*/
|
|
43
|
-
export function stableStringify(value) {
|
|
44
|
-
if (value === null || typeof value !== 'object') {
|
|
45
|
-
return JSON.stringify(value);
|
|
46
|
-
}
|
|
47
|
-
if (Array.isArray(value)) {
|
|
48
|
-
return '[' + value.map((v) => stableStringify(v)).join(',') + ']';
|
|
49
|
-
}
|
|
50
|
-
const keys = Object.keys(value).sort();
|
|
51
|
-
const parts = [];
|
|
52
|
-
for (const k of keys) {
|
|
53
|
-
const v = value[k];
|
|
54
|
-
if (v === undefined) continue; // mirror JSON.stringify behavior
|
|
55
|
-
parts.push(JSON.stringify(k) + ':' + stableStringify(v));
|
|
56
|
-
}
|
|
57
|
-
return '{' + parts.join(',') + '}';
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ── helpers ────────────────────────────────────────────────────────────────
|
|
61
|
-
|
|
62
|
-
function computeHmac(secret, preimage) {
|
|
63
|
-
return crypto.createHmac('sha256', String(secret)).update(preimage).digest('hex');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function preimageOf(envelope) {
|
|
67
|
-
// HMAC binds the four schema fields. Order doesn't matter — stableStringify
|
|
68
|
-
// sorts keys — but be explicit so the contract is readable.
|
|
69
|
-
return stableStringify({
|
|
70
|
-
id: envelope.id,
|
|
71
|
-
payload: envelope.payload,
|
|
72
|
-
timestamp: envelope.timestamp,
|
|
73
|
-
type: envelope.type,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function timingSafeEqualHex(a, b) {
|
|
78
|
-
if (typeof a !== 'string' || typeof b !== 'string') return false;
|
|
79
|
-
if (a.length !== b.length) return false;
|
|
80
|
-
try {
|
|
81
|
-
return crypto.timingSafeEqual(Buffer.from(a, 'hex'), Buffer.from(b, 'hex'));
|
|
82
|
-
} catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ── replay-protection set (in-memory, module-scoped) ──────────────────────
|
|
88
|
-
|
|
89
|
-
const _seenIds = new Map(); // id → timestamp; insertion-ordered for FIFO eviction
|
|
90
|
-
|
|
91
|
-
function recordSeen(id) {
|
|
92
|
-
if (_seenIds.has(id)) {
|
|
93
|
-
// refresh insertion order is not desired — keep original entry
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
_seenIds.set(id, Date.now());
|
|
97
|
-
while (_seenIds.size > REPLAY_SET_LIMIT) {
|
|
98
|
-
const firstKey = _seenIds.keys().next().value;
|
|
99
|
-
_seenIds.delete(firstKey);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Test/diagnostics helper — clears the replay-protection set. */
|
|
104
|
-
export function _resetReplaySet() {
|
|
105
|
-
_seenIds.clear();
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/** Test/diagnostics helper — current replay-set size. */
|
|
109
|
-
export function _replaySetSize() {
|
|
110
|
-
return _seenIds.size;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ── public API ─────────────────────────────────────────────────────────────
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Build a signed envelope.
|
|
117
|
-
*
|
|
118
|
-
* @param {string} type Verb, e.g. "selection.update".
|
|
119
|
-
* @param {object} payload Arbitrary serializable object.
|
|
120
|
-
* @param {string} secret Shared secret (UTF-8 string).
|
|
121
|
-
* @returns {object} Envelope ready for transport.
|
|
122
|
-
*/
|
|
123
|
-
export function createEnvelope(type, payload, secret) {
|
|
124
|
-
if (typeof type !== 'string' || type.length === 0) {
|
|
125
|
-
throw new TypeError('createEnvelope: type must be a non-empty string');
|
|
126
|
-
}
|
|
127
|
-
if (payload === null || typeof payload !== 'object') {
|
|
128
|
-
throw new TypeError('createEnvelope: payload must be an object');
|
|
129
|
-
}
|
|
130
|
-
if (typeof secret !== 'string' || secret.length === 0) {
|
|
131
|
-
throw new TypeError('createEnvelope: secret must be a non-empty string');
|
|
132
|
-
}
|
|
133
|
-
const envelope = {
|
|
134
|
-
id: crypto.randomUUID(),
|
|
135
|
-
type,
|
|
136
|
-
payload,
|
|
137
|
-
timestamp: Date.now(),
|
|
138
|
-
};
|
|
139
|
-
envelope.hmac = computeHmac(secret, preimageOf(envelope));
|
|
140
|
-
return envelope;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Verify a received envelope.
|
|
145
|
-
*
|
|
146
|
-
* @param {object} envelope
|
|
147
|
-
* @param {string} secret
|
|
148
|
-
* @param {{ now?: number }} [options] Optional clock override (testing).
|
|
149
|
-
* @returns {{ ok: true } | { ok: false, code: string, message?: string }}
|
|
150
|
-
*/
|
|
151
|
-
export function verifyEnvelope(envelope, secret, options = {}) {
|
|
152
|
-
if (envelope === null || typeof envelope !== 'object') {
|
|
153
|
-
return { ok: false, code: 'MALFORMED', message: 'envelope is not an object' };
|
|
154
|
-
}
|
|
155
|
-
const { id, type, payload, timestamp, hmac } = envelope;
|
|
156
|
-
if (typeof id !== 'string' || id.length === 0) {
|
|
157
|
-
return { ok: false, code: 'MALFORMED', message: 'missing id' };
|
|
158
|
-
}
|
|
159
|
-
if (typeof type !== 'string' || type.length === 0) {
|
|
160
|
-
return { ok: false, code: 'MALFORMED', message: 'missing type' };
|
|
161
|
-
}
|
|
162
|
-
if (payload === null || typeof payload !== 'object') {
|
|
163
|
-
return { ok: false, code: 'MALFORMED', message: 'missing or invalid payload' };
|
|
164
|
-
}
|
|
165
|
-
if (typeof timestamp !== 'number' || !Number.isFinite(timestamp)) {
|
|
166
|
-
return { ok: false, code: 'MALFORMED', message: 'missing or invalid timestamp' };
|
|
167
|
-
}
|
|
168
|
-
if (typeof hmac !== 'string' || hmac.length === 0) {
|
|
169
|
-
return { ok: false, code: 'MALFORMED', message: 'missing hmac' };
|
|
170
|
-
}
|
|
171
|
-
if (typeof secret !== 'string' || secret.length === 0) {
|
|
172
|
-
return { ok: false, code: 'MALFORMED', message: 'verifier secret must be non-empty string' };
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const now = typeof options.now === 'number' ? options.now : Date.now();
|
|
176
|
-
|
|
177
|
-
// Future-skew check (timestamp claims to be too far in the future)
|
|
178
|
-
if (timestamp - now > CLOCK_SKEW_MS) {
|
|
179
|
-
return { ok: false, code: 'EXPIRED', message: 'timestamp is in the future beyond clock-skew tolerance' };
|
|
180
|
-
}
|
|
181
|
-
// Past expiry check (timestamp older than MAX_AGE_MS)
|
|
182
|
-
if (now - timestamp > MAX_AGE_MS) {
|
|
183
|
-
return { ok: false, code: 'EXPIRED', message: 'envelope older than max-age' };
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// HMAC check
|
|
187
|
-
const expected = computeHmac(secret, preimageOf(envelope));
|
|
188
|
-
if (!timingSafeEqualHex(expected, hmac)) {
|
|
189
|
-
return { ok: false, code: 'HMAC_MISMATCH', message: 'hmac signature does not match' };
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Replay check — must come after HMAC (don't pollute the set with junk)
|
|
193
|
-
if (_seenIds.has(id)) {
|
|
194
|
-
return { ok: false, code: 'REPLAY', message: 'envelope id already seen' };
|
|
195
|
-
}
|
|
196
|
-
recordSeen(id);
|
|
197
|
-
|
|
198
|
-
return { ok: true };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export default { createEnvelope, verifyEnvelope, stableStringify };
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "figma-snapshot.schema.json",
|
|
4
|
-
"title": "FigmaSnapshot",
|
|
5
|
-
"description": "10f-1: Read-only normalized snapshot of a Figma document. Same field shapes as the p6-1 selection serializer, extended to cover the full document: all pages, variable collections/modes, all local components/styles.",
|
|
6
|
-
"type": "object",
|
|
7
|
-
"required": ["snapshotAt", "pages", "variables", "styles"],
|
|
8
|
-
"additionalProperties": false,
|
|
9
|
-
"properties": {
|
|
10
|
-
"snapshotAt": {
|
|
11
|
-
"type": "string",
|
|
12
|
-
"format": "date-time",
|
|
13
|
-
"description": "ISO-8601 timestamp (UTC) when the snapshot was taken by the plugin."
|
|
14
|
-
},
|
|
15
|
-
"documentName": {
|
|
16
|
-
"type": "string",
|
|
17
|
-
"description": "Figma document name (figma.root.name)."
|
|
18
|
-
},
|
|
19
|
-
"pages": {
|
|
20
|
-
"type": "array",
|
|
21
|
-
"description": "One entry per page in the document.",
|
|
22
|
-
"items": { "$ref": "#/definitions/PageSummary" }
|
|
23
|
-
},
|
|
24
|
-
"variables": {
|
|
25
|
-
"type": "object",
|
|
26
|
-
"description": "Variable collections keyed by HDS collection name (HDS Primitive | HDS Semantic | HDS Component). Non-HDS collections are omitted.",
|
|
27
|
-
"additionalProperties": { "$ref": "#/definitions/VariableCollection" }
|
|
28
|
-
},
|
|
29
|
-
"styles": {
|
|
30
|
-
"type": "object",
|
|
31
|
-
"description": "Local Figma styles grouped by kind.",
|
|
32
|
-
"required": ["paint", "text", "effect", "grid"],
|
|
33
|
-
"additionalProperties": false,
|
|
34
|
-
"properties": {
|
|
35
|
-
"paint": { "type": "array", "items": { "$ref": "#/definitions/StyleSummary" } },
|
|
36
|
-
"text": { "type": "array", "items": { "$ref": "#/definitions/StyleSummary" } },
|
|
37
|
-
"effect": { "type": "array", "items": { "$ref": "#/definitions/StyleSummary" } },
|
|
38
|
-
"grid": { "type": "array", "items": { "$ref": "#/definitions/StyleSummary" } }
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
"components": {
|
|
42
|
-
"type": "array",
|
|
43
|
-
"description": "All local COMPONENT and COMPONENT_SET nodes in the document (flattened list, not scoped per-page).",
|
|
44
|
-
"items": { "$ref": "#/definitions/ComponentSummary" }
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"definitions": {
|
|
48
|
-
"PageSummary": {
|
|
49
|
-
"type": "object",
|
|
50
|
-
"required": ["id", "name", "type"],
|
|
51
|
-
"additionalProperties": false,
|
|
52
|
-
"properties": {
|
|
53
|
-
"id": { "type": "string" },
|
|
54
|
-
"name": { "type": "string" },
|
|
55
|
-
"type": { "type": "string", "const": "PAGE" },
|
|
56
|
-
"frames": {
|
|
57
|
-
"type": "array",
|
|
58
|
-
"description": "Top-level FRAME and COMPONENT_SET children of this page, serialized with the p6-1 extractNodeTree shape (max depth 4).",
|
|
59
|
-
"items": { "$ref": "#/definitions/SerializedNode" }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
"VariableCollection": {
|
|
64
|
-
"type": "object",
|
|
65
|
-
"required": ["id", "name", "modes", "variables"],
|
|
66
|
-
"additionalProperties": false,
|
|
67
|
-
"properties": {
|
|
68
|
-
"id": { "type": "string" },
|
|
69
|
-
"name": { "type": "string" },
|
|
70
|
-
"modes": {
|
|
71
|
-
"type": "array",
|
|
72
|
-
"items": {
|
|
73
|
-
"type": "object",
|
|
74
|
-
"required": ["modeId", "name"],
|
|
75
|
-
"additionalProperties": false,
|
|
76
|
-
"properties": {
|
|
77
|
-
"modeId": { "type": "string" },
|
|
78
|
-
"name": { "type": "string" }
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
"variables": {
|
|
83
|
-
"type": "array",
|
|
84
|
-
"items": { "$ref": "#/definitions/VariableSummary" }
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
"VariableSummary": {
|
|
89
|
-
"type": "object",
|
|
90
|
-
"required": ["id", "name", "resolvedType", "tokenPath"],
|
|
91
|
-
"additionalProperties": false,
|
|
92
|
-
"properties": {
|
|
93
|
-
"id": { "type": "string" },
|
|
94
|
-
"name": { "type": "string", "description": "Raw Figma variable name (slash/dash separated)." },
|
|
95
|
-
"tokenPath": { "type": "string", "description": "Dot-notation canonical path, e.g. 'semantic.color.surface.raised'." },
|
|
96
|
-
"resolvedType": { "type": "string", "enum": ["COLOR", "FLOAT", "STRING", "BOOLEAN"] },
|
|
97
|
-
"valuesByMode": {
|
|
98
|
-
"type": "object",
|
|
99
|
-
"description": "Map of modeId → resolved value. COLOR values are serialized as hex strings (#rrggbb or #rrggbbaa). FLOAT/STRING/BOOLEAN are primitives.",
|
|
100
|
-
"additionalProperties": {}
|
|
101
|
-
},
|
|
102
|
-
"scopes": {
|
|
103
|
-
"type": "array",
|
|
104
|
-
"items": { "type": "string" },
|
|
105
|
-
"description": "Figma scopes (e.g. FILL_COLOR, CORNER_RADIUS). Omitted when empty."
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
"StyleSummary": {
|
|
110
|
-
"type": "object",
|
|
111
|
-
"required": ["id", "name", "type"],
|
|
112
|
-
"additionalProperties": false,
|
|
113
|
-
"properties": {
|
|
114
|
-
"id": { "type": "string" },
|
|
115
|
-
"name": { "type": "string" },
|
|
116
|
-
"type": { "type": "string" },
|
|
117
|
-
"description": { "type": "string" }
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
"ComponentSummary": {
|
|
121
|
-
"type": "object",
|
|
122
|
-
"required": ["id", "name", "type"],
|
|
123
|
-
"additionalProperties": false,
|
|
124
|
-
"properties": {
|
|
125
|
-
"id": { "type": "string" },
|
|
126
|
-
"name": { "type": "string" },
|
|
127
|
-
"type": { "type": "string", "enum": ["COMPONENT", "COMPONENT_SET"] },
|
|
128
|
-
"componentName": {
|
|
129
|
-
"type": "string",
|
|
130
|
-
"description": "Resolved manifest spec key (from p6-1 resolveComponentName), if matched."
|
|
131
|
-
},
|
|
132
|
-
"variants": {
|
|
133
|
-
"type": "array",
|
|
134
|
-
"description": "Variant names for COMPONENT_SET nodes.",
|
|
135
|
-
"items": { "type": "string" }
|
|
136
|
-
},
|
|
137
|
-
"properties": {
|
|
138
|
-
"type": "object",
|
|
139
|
-
"description": "Component property descriptors (Figma ComponentPropertyDefinitions).",
|
|
140
|
-
"additionalProperties": {}
|
|
141
|
-
},
|
|
142
|
-
"pageId": { "type": "string", "description": "ID of the page that owns this component." },
|
|
143
|
-
"a11y": { "$ref": "#/definitions/A11yMeta" }
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
"SerializedNode": {
|
|
147
|
-
"description": "p6-1 extractNodeTree shape, extended with slot bindings. Mirrors the selection serializer output exactly so downstream validators receive the same contract whether the input came from a selection or a full-page walk.",
|
|
148
|
-
"type": "object",
|
|
149
|
-
"required": ["id", "name", "type"],
|
|
150
|
-
"additionalProperties": true,
|
|
151
|
-
"properties": {
|
|
152
|
-
"id": { "type": "string" },
|
|
153
|
-
"name": { "type": "string" },
|
|
154
|
-
"type": { "type": "string" },
|
|
155
|
-
"width": { "type": "number" },
|
|
156
|
-
"height": { "type": "number" },
|
|
157
|
-
"x": { "type": "number" },
|
|
158
|
-
"y": { "type": "number" },
|
|
159
|
-
"fills": {},
|
|
160
|
-
"boundVariables": {},
|
|
161
|
-
"componentName": { "type": "string" },
|
|
162
|
-
"tokenPaths": { "type": "object", "additionalProperties": {} },
|
|
163
|
-
"a11y": { "$ref": "#/definitions/A11yMeta" },
|
|
164
|
-
"slotBindings": { "$ref": "#/definitions/SlotBindings" },
|
|
165
|
-
"children": {
|
|
166
|
-
"type": "array",
|
|
167
|
-
"items": { "$ref": "#/definitions/SerializedNode" }
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
"A11yMeta": {
|
|
172
|
-
"type": "object",
|
|
173
|
-
"additionalProperties": false,
|
|
174
|
-
"properties": {
|
|
175
|
-
"name": { "type": "string" },
|
|
176
|
-
"role": { "type": "string" },
|
|
177
|
-
"description": { "type": "string" }
|
|
178
|
-
}
|
|
179
|
-
},
|
|
180
|
-
"SlotBindings": {
|
|
181
|
-
"type": "object",
|
|
182
|
-
"description": "Asset/portfolio slot assignments stored as plugin data on the node (hdsSlot key).",
|
|
183
|
-
"additionalProperties": false,
|
|
184
|
-
"properties": {
|
|
185
|
-
"slot": { "type": "string", "description": "Slot identifier, e.g. 'hero-01'." },
|
|
186
|
-
"assetId": { "type": "string" }
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|