@penpair/core 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters.js +58 -11
- package/dist/approvals.js +143 -29
- package/dist/atomic-output.js +9 -4
- package/dist/audit-rules.js +345 -79
- package/dist/backup-note.js +29 -8
- package/dist/baseline-coverage.js +169 -35
- package/dist/baselines-config.js +119 -29
- package/dist/baselines.js +280 -57
- package/dist/cli-arguments.js +18 -6
- package/dist/cli-main.js +1645 -392
- package/dist/client.d.ts +5 -2
- package/dist/client.js +127 -28
- package/dist/command-result.js +341 -52
- package/dist/coordination.js +91 -29
- package/dist/credentials.js +57 -13
- package/dist/deep-check.js +56 -12
- package/dist/design-spec.js +189 -49
- package/dist/endpoint.js +102 -34
- package/dist/export.js +86 -18
- package/dist/file-archive.js +33 -7
- package/dist/file-guard.js +29 -11
- package/dist/formats.js +215 -14
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/inspection.d.ts +1 -1
- package/dist/inspection.js +378 -65
- package/dist/instance/config-schema.js +400 -41
- package/dist/instance/config.js +101 -30
- package/dist/instance/connect.js +151 -44
- package/dist/instance/connection.d.ts +26 -5
- package/dist/instance/connection.js +194 -21
- package/dist/instance/gitignore.js +5 -2
- package/dist/instance/init.js +32 -7
- package/dist/instance/runner.js +32 -10
- package/dist/instance/setup.js +46 -13
- package/dist/instance/tty.js +26 -8
- package/dist/journal.d.ts +10 -0
- package/dist/journal.js +212 -49
- package/dist/kit-smoke.js +82 -20
- package/dist/kit-source.js +10 -40
- package/dist/kit.js +102 -34
- package/dist/layout-policy.d.ts +2 -0
- package/dist/layout-policy.js +86 -0
- package/dist/layout.d.ts +7 -1
- package/dist/layout.js +27 -70
- package/dist/live-smoke.js +13 -4
- package/dist/local-profile.js +2 -1
- package/dist/mcp-server.js +360 -47
- package/dist/native-api.js +47 -11
- package/dist/onboard.js +33 -7
- package/dist/operation.d.ts +2 -2
- package/dist/operation.js +279 -69
- package/dist/page-session.d.ts +23 -0
- package/dist/page-session.js +142 -0
- package/dist/png.js +18 -3
- package/dist/portable-tokens.js +254 -57
- package/dist/probe.d.ts +1 -1
- package/dist/probe.js +48 -11
- package/dist/project-input.js +24 -10
- package/dist/project-snippets.js +18 -6
- package/dist/project-status.js +133 -24
- package/dist/registry.js +217 -60
- package/dist/run-file.js +129 -30
- package/dist/shots.js +108 -26
- package/dist/skill-check-entry.d.ts +17 -0
- package/dist/skill-check-entry.js +89 -0
- package/dist/skill-check-instructions.d.ts +7 -0
- package/dist/skill-check-instructions.js +39 -0
- package/dist/skill-check.d.ts +15 -0
- package/dist/skill-check.js +36 -0
- package/dist/skill-file.d.ts +2 -0
- package/dist/skill-file.js +31 -0
- package/dist/skill-instructions.d.ts +4 -0
- package/dist/skill-instructions.js +97 -22
- package/dist/skill-metadata.d.ts +8 -0
- package/dist/skill-metadata.js +52 -0
- package/dist/skill-routing.d.ts +2 -0
- package/dist/skill-routing.js +36 -0
- package/dist/skills.js +131 -41
- package/dist/snippet.js +27 -7
- package/dist/snippets/read.js +8 -2
- package/dist/structure-snapshot.d.ts +12 -0
- package/dist/structure-snapshot.js +439 -85
- package/dist/token-edit.js +219 -64
- package/dist/token-projection.d.ts +56 -0
- package/dist/token-projection.js +286 -0
- package/dist/tokens-diff.js +125 -32
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/skills/design-judgment/SKILL.md +1 -1
- package/skills/design-judgment/agents/openai.yaml +6 -0
- package/skills/penpair-design/SKILL.md +14 -1
- package/skills/penpair-design/agents/openai.yaml +6 -0
- package/skills/penpair-design/references/penpot-plugin-gotchas.md +2 -2
- package/skills/penpair-design/references/project-facts.md +2 -0
- package/skills/penpair-handoff/SKILL.md +14 -1
- package/skills/penpair-handoff/agents/openai.yaml +6 -0
- package/skills/penpair-handoff/references/comparison-companion.md +16 -0
- package/skills/penpair-handoff/references/handoff-flow.md +2 -0
- package/skills/penpair-recipes/SKILL.md +13 -1
- package/skills/penpair-recipes/agents/openai.yaml +6 -0
- package/skills/penpair-recipes/assets/visual-checkpoint.md +48 -0
- package/skills/penpair-recipes/references/visual-checkpoint.md +37 -0
- package/skills/penpair-setup/SKILL.md +9 -3
- package/skills/penpair-setup/agents/openai.yaml +6 -0
- package/skills/penpair-verify/SKILL.md +49 -0
- package/skills/penpair-verify/agents/openai.yaml +6 -0
- package/skills/penpair-verify/references/browser-evidence.md +33 -0
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -3
- package/dist/docker/lifecycle.d.ts +0 -96
- package/dist/docker/lifecycle.js +0 -273
- package/dist/docker/secrets.d.ts +0 -11
- package/dist/docker/secrets.js +0 -38
- package/dist/docker/template.d.ts +0 -8
- package/dist/docker/template.js +0 -158
- package/dist/docker-profile.d.ts +0 -114
- package/dist/docker-profile.js +0 -18
- package/dist/instance/lifecycle.d.ts +0 -129
- package/dist/instance/lifecycle.js +0 -336
- package/dist/instance/secrets.d.ts +0 -11
- package/dist/instance/secrets.js +0 -38
- package/dist/instance/template.d.ts +0 -8
- package/dist/instance/template.js +0 -158
package/dist/token-edit.js
CHANGED
|
@@ -1,112 +1,213 @@
|
|
|
1
1
|
import { snippet } from "./snippet.js";
|
|
2
|
-
export const tokenTypes = [
|
|
2
|
+
export const tokenTypes = [
|
|
3
|
+
"borderRadius",
|
|
4
|
+
"shadow",
|
|
5
|
+
"color",
|
|
6
|
+
"dimension",
|
|
7
|
+
"fontFamilies",
|
|
8
|
+
"fontSizes",
|
|
9
|
+
"fontWeights",
|
|
10
|
+
"letterSpacing",
|
|
11
|
+
"number",
|
|
12
|
+
"opacity",
|
|
13
|
+
"rotation",
|
|
14
|
+
"sizing",
|
|
15
|
+
"spacing",
|
|
16
|
+
"borderWidth",
|
|
17
|
+
"textCase",
|
|
18
|
+
"textDecoration",
|
|
19
|
+
"typography",
|
|
20
|
+
];
|
|
3
21
|
export function validateEditableToken(value) {
|
|
4
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
22
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5
23
|
throw new Error("Token must be an object");
|
|
24
|
+
}
|
|
6
25
|
const token = value;
|
|
7
|
-
if (Object.keys(token).some(key => ![
|
|
26
|
+
if (Object.keys(token).some((key) => ![
|
|
27
|
+
"name",
|
|
28
|
+
"type",
|
|
29
|
+
"value",
|
|
30
|
+
"description",
|
|
31
|
+
].includes(key))) {
|
|
8
32
|
throw new Error("Unknown token field");
|
|
9
|
-
|
|
33
|
+
}
|
|
34
|
+
if (typeof token.name !== "string" ||
|
|
35
|
+
!token.name.trim() ||
|
|
36
|
+
token.name.length > 200 ||
|
|
37
|
+
/[{}\x00-\x1f]/.test(token.name)) {
|
|
10
38
|
throw new Error("Token name must be nonempty, at most 200 characters and contain no braces or control characters");
|
|
11
|
-
|
|
39
|
+
}
|
|
40
|
+
if (!tokenTypes.includes(token.type)) {
|
|
12
41
|
throw new Error("Unsupported token type");
|
|
13
|
-
|
|
42
|
+
}
|
|
43
|
+
if (!((typeof token.value === "string" && token.value.length > 0) ||
|
|
44
|
+
(token.value &&
|
|
45
|
+
typeof token.value === "object" &&
|
|
46
|
+
!Array.isArray(token.value) &&
|
|
47
|
+
Object.keys(token.value).length > 0 &&
|
|
48
|
+
Object.values(token.value).every((v) => typeof v === "string")))) {
|
|
14
49
|
throw new Error("Token value must be a nonempty string or composite string-valued object");
|
|
15
|
-
|
|
50
|
+
}
|
|
51
|
+
if (token.description !== undefined && typeof token.description !== "string") {
|
|
16
52
|
throw new Error("Token description must be a string");
|
|
17
|
-
|
|
53
|
+
}
|
|
54
|
+
if (Buffer.byteLength(JSON.stringify(token)) > 16384) {
|
|
18
55
|
throw new Error("Token exceeds 16 KiB");
|
|
19
|
-
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
name: token.name,
|
|
59
|
+
type: token.type,
|
|
60
|
+
value: token.value,
|
|
61
|
+
description: token.description ?? "",
|
|
62
|
+
};
|
|
20
63
|
}
|
|
21
64
|
export function validateTokenEdit(edit) {
|
|
22
|
-
if (![
|
|
65
|
+
if (![
|
|
66
|
+
"add-set",
|
|
67
|
+
"set",
|
|
68
|
+
"remove",
|
|
69
|
+
"promote",
|
|
70
|
+
].includes(edit.kind)) {
|
|
23
71
|
throw new Error("Unknown token edit");
|
|
24
|
-
|
|
25
|
-
|
|
72
|
+
}
|
|
73
|
+
for (const name of [
|
|
74
|
+
edit.set,
|
|
75
|
+
...(edit.kind === "promote"
|
|
76
|
+
? [
|
|
77
|
+
edit.to,
|
|
78
|
+
]
|
|
79
|
+
: []),
|
|
80
|
+
]) {
|
|
81
|
+
if (typeof name !== "string" || !name.trim() || name.length > 200 || /[\x00-\x1f]/.test(name)) {
|
|
26
82
|
throw new Error("Set names must be nonempty and at most 200 characters without control characters");
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (edit.kind === "
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (edit.kind === "set") {
|
|
86
|
+
return {
|
|
87
|
+
...edit,
|
|
88
|
+
token: validateEditableToken(edit.token),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (edit.kind === "remove" && (typeof edit.name !== "string" || !edit.name.trim())) {
|
|
30
92
|
throw new Error("Remove requires a token name");
|
|
31
|
-
|
|
93
|
+
}
|
|
94
|
+
if (edit.kind === "promote" && edit.set === edit.to) {
|
|
32
95
|
throw new Error("Promotion requires distinct source and destination sets");
|
|
96
|
+
}
|
|
33
97
|
return edit;
|
|
34
98
|
}
|
|
35
99
|
/** Pure desired-state planner. Kept self-contained so the delivered mutation rechecks the same facts. */
|
|
36
100
|
export function planTokenEdit(before, edit) {
|
|
37
101
|
const canonical = (value) => {
|
|
38
|
-
if (Array.isArray(value))
|
|
102
|
+
if (Array.isArray(value)) {
|
|
39
103
|
return "[" + value.map(canonical).join(",") + "]";
|
|
40
|
-
|
|
41
|
-
|
|
104
|
+
}
|
|
105
|
+
if (value && typeof value === "object") {
|
|
106
|
+
return ("{" +
|
|
107
|
+
Object.keys(value)
|
|
108
|
+
.sort()
|
|
109
|
+
.map((key) => JSON.stringify(key) + ":" + canonical(value[key]))
|
|
110
|
+
.join(",") +
|
|
111
|
+
"}");
|
|
112
|
+
}
|
|
42
113
|
return JSON.stringify(value);
|
|
43
114
|
};
|
|
44
115
|
const after = JSON.parse(JSON.stringify(before));
|
|
45
|
-
if (!Array.isArray(after.sets) ||
|
|
116
|
+
if (!Array.isArray(after.sets) ||
|
|
117
|
+
!Array.isArray(after.themes) ||
|
|
118
|
+
!Array.isArray(after.bindings)) {
|
|
46
119
|
throw new Error("Missing token observations");
|
|
120
|
+
}
|
|
47
121
|
const names = new Set();
|
|
48
122
|
for (const set of after.sets) {
|
|
49
|
-
if (names.has(set.name))
|
|
123
|
+
if (names.has(set.name)) {
|
|
50
124
|
throw new Error("Ambiguous set: " + set.name);
|
|
125
|
+
}
|
|
51
126
|
names.add(set.name);
|
|
52
127
|
const tokens = new Set();
|
|
53
128
|
for (const token of set.tokens) {
|
|
54
|
-
if (tokens.has(token.name))
|
|
129
|
+
if (tokens.has(token.name)) {
|
|
55
130
|
throw new Error("Ambiguous token: " + token.name);
|
|
131
|
+
}
|
|
56
132
|
tokens.add(token.name);
|
|
57
133
|
}
|
|
58
134
|
}
|
|
59
135
|
const impact = [];
|
|
60
|
-
const set = after.sets.find(s => s.name === edit.set);
|
|
136
|
+
const set = after.sets.find((s) => s.name === edit.set);
|
|
61
137
|
if (edit.kind === "add-set") {
|
|
62
|
-
if (!set)
|
|
63
|
-
after.sets.push({
|
|
138
|
+
if (!set) {
|
|
139
|
+
after.sets.push({
|
|
140
|
+
name: edit.set,
|
|
141
|
+
active: false,
|
|
142
|
+
tokens: [],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
64
145
|
}
|
|
65
146
|
else {
|
|
66
|
-
if (!set)
|
|
147
|
+
if (!set) {
|
|
67
148
|
throw new Error("Missing set: " + edit.set);
|
|
149
|
+
}
|
|
68
150
|
if (edit.kind === "set") {
|
|
69
|
-
const token = set.tokens.find(t => t.name === edit.token.name);
|
|
70
|
-
if (token && token.type !== edit.token.type)
|
|
151
|
+
const token = set.tokens.find((t) => t.name === edit.token.name);
|
|
152
|
+
if (token && token.type !== edit.token.type) {
|
|
71
153
|
throw new Error("Type replacement requires a separate reviewed removal");
|
|
72
|
-
|
|
154
|
+
}
|
|
155
|
+
if (token) {
|
|
73
156
|
Object.assign(token, edit.token);
|
|
74
|
-
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
75
159
|
set.tokens.push(edit.token);
|
|
76
|
-
|
|
77
|
-
|
|
160
|
+
}
|
|
161
|
+
for (const row of before.bindings) {
|
|
162
|
+
if (Object.values(row.tokens).includes(edit.token.name)) {
|
|
78
163
|
impact.push(row.page + "/" + row.id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
79
166
|
}
|
|
80
167
|
else if (edit.kind === "remove") {
|
|
81
168
|
// Conservatively refuse even when another active set might supply the same name.
|
|
82
|
-
if (set.tokens.some(t => t.name === edit.name)) {
|
|
169
|
+
if (set.tokens.some((t) => t.name === edit.name)) {
|
|
83
170
|
const pattern = "{" + edit.name + "}";
|
|
84
|
-
for (const source of after.sets)
|
|
85
|
-
for (const token of source.tokens)
|
|
86
|
-
if (!(source.name === set.name && token.name === edit.name) &&
|
|
171
|
+
for (const source of after.sets) {
|
|
172
|
+
for (const token of source.tokens) {
|
|
173
|
+
if (!(source.name === set.name && token.name === edit.name) &&
|
|
174
|
+
JSON.stringify(token.value).includes(pattern)) {
|
|
87
175
|
throw new Error("Retained reference from " + source.name + "/" + token.name);
|
|
88
|
-
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (before.bindings.some((row) => Object.values(row.tokens).includes(edit.name))) {
|
|
89
180
|
throw new Error("Token is bound to a retained shape");
|
|
90
|
-
|
|
181
|
+
}
|
|
182
|
+
set.tokens = set.tokens.filter((t) => t.name !== edit.name);
|
|
91
183
|
}
|
|
92
184
|
}
|
|
93
185
|
else {
|
|
94
|
-
const target = after.sets.find(s => s.name === edit.to);
|
|
95
|
-
if (!target)
|
|
186
|
+
const target = after.sets.find((s) => s.name === edit.to);
|
|
187
|
+
if (!target) {
|
|
96
188
|
throw new Error("Missing promotion destination: " + edit.to);
|
|
189
|
+
}
|
|
97
190
|
for (const token of set.tokens) {
|
|
98
|
-
const existing = target.tokens.find(t => t.name === token.name);
|
|
99
|
-
if (existing && canonical(existing) !== canonical(token))
|
|
191
|
+
const existing = target.tokens.find((t) => t.name === token.name);
|
|
192
|
+
if (existing && canonical(existing) !== canonical(token)) {
|
|
100
193
|
throw new Error("Promotion conflicts with destination token: " + token.name);
|
|
101
|
-
|
|
194
|
+
}
|
|
195
|
+
if (!existing) {
|
|
102
196
|
target.tokens.push(token);
|
|
197
|
+
}
|
|
103
198
|
}
|
|
104
199
|
impact.push("Copies tokens; source, themes and active selection are preserved");
|
|
105
200
|
}
|
|
106
201
|
}
|
|
107
|
-
for (const set of after.sets)
|
|
108
|
-
set.tokens.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
109
|
-
|
|
202
|
+
for (const set of after.sets) {
|
|
203
|
+
set.tokens.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
before,
|
|
207
|
+
after,
|
|
208
|
+
changed: canonical(before) !== canonical(after),
|
|
209
|
+
impact,
|
|
210
|
+
};
|
|
110
211
|
}
|
|
111
212
|
// Snapshot getter-backed API fields explicitly; refuse partial/oversized observations before any edit.
|
|
112
213
|
const observe = `
|
|
@@ -131,33 +232,75 @@ const observe = () => {
|
|
|
131
232
|
return state;
|
|
132
233
|
};`;
|
|
133
234
|
const canonical = (value) => {
|
|
134
|
-
if (Array.isArray(value))
|
|
235
|
+
if (Array.isArray(value)) {
|
|
135
236
|
return "[" + value.map(canonical).join(",") + "]";
|
|
136
|
-
|
|
137
|
-
|
|
237
|
+
}
|
|
238
|
+
if (value && typeof value === "object") {
|
|
239
|
+
return ("{" +
|
|
240
|
+
Object.keys(value)
|
|
241
|
+
.sort()
|
|
242
|
+
.map((key) => JSON.stringify(key) + ":" + canonical(value[key]))
|
|
243
|
+
.join(",") +
|
|
244
|
+
"}");
|
|
245
|
+
}
|
|
138
246
|
return JSON.stringify(value);
|
|
139
247
|
};
|
|
140
248
|
function comparable(state) {
|
|
141
|
-
return {
|
|
249
|
+
return {
|
|
250
|
+
...state,
|
|
251
|
+
sets: state.sets.map((set) => ({
|
|
252
|
+
...set,
|
|
253
|
+
tokens: set.tokens.map((token) => {
|
|
142
254
|
let value = token.value;
|
|
143
|
-
if (token.type === "fontFamilies")
|
|
144
|
-
value = Array.isArray(value)
|
|
145
|
-
|
|
255
|
+
if (token.type === "fontFamilies") {
|
|
256
|
+
value = Array.isArray(value)
|
|
257
|
+
? value
|
|
258
|
+
: [
|
|
259
|
+
value,
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
if (token.type === "typography" &&
|
|
263
|
+
value &&
|
|
264
|
+
typeof value === "object" &&
|
|
265
|
+
!Array.isArray(value)) {
|
|
146
266
|
const { fontFamilies, fontFamily, ...rest } = value;
|
|
147
267
|
const family = fontFamily ?? fontFamilies;
|
|
148
|
-
value = {
|
|
268
|
+
value = {
|
|
269
|
+
...rest,
|
|
270
|
+
...(family === undefined
|
|
271
|
+
? {}
|
|
272
|
+
: {
|
|
273
|
+
fontFamily: Array.isArray(family)
|
|
274
|
+
? family
|
|
275
|
+
: [
|
|
276
|
+
family,
|
|
277
|
+
],
|
|
278
|
+
}),
|
|
279
|
+
};
|
|
149
280
|
}
|
|
150
|
-
return {
|
|
151
|
-
|
|
281
|
+
return {
|
|
282
|
+
...token,
|
|
283
|
+
value,
|
|
284
|
+
};
|
|
285
|
+
}),
|
|
286
|
+
})),
|
|
287
|
+
};
|
|
152
288
|
}
|
|
153
289
|
const stateFrom = (result) => {
|
|
154
|
-
if (result.ok !== true || !result.state)
|
|
290
|
+
if (result.ok !== true || !result.state) {
|
|
155
291
|
throw new Error("No independent token-state evidence");
|
|
292
|
+
}
|
|
156
293
|
return result.state;
|
|
157
294
|
};
|
|
158
295
|
export const tokenEditOperation = {
|
|
159
|
-
name: "tokens-edit",
|
|
160
|
-
|
|
296
|
+
name: "tokens-edit",
|
|
297
|
+
page: () => undefined,
|
|
298
|
+
inspect(edit) {
|
|
299
|
+
edit = validateTokenEdit(edit);
|
|
300
|
+
return snippet("tokens-edit-inspect", `${observe}\nconst state=observe();const plan=(${planTokenEdit.toString()})(state,CFG.edit);return {ok:true,readOnly:true,state,plan};`, {
|
|
301
|
+
edit,
|
|
302
|
+
});
|
|
303
|
+
},
|
|
161
304
|
apply(edit, inspected) {
|
|
162
305
|
edit = validateTokenEdit(edit);
|
|
163
306
|
return snippet("tokens-edit-apply", `${observe}
|
|
@@ -180,18 +323,30 @@ if(plan.changed){
|
|
|
180
323
|
}
|
|
181
324
|
}
|
|
182
325
|
}
|
|
183
|
-
return {ok:true,changed:plan.changed};`, {
|
|
326
|
+
return {ok:true,changed:plan.changed};`, {
|
|
327
|
+
edit,
|
|
328
|
+
before: inspected?.state ?? null,
|
|
329
|
+
});
|
|
184
330
|
},
|
|
185
331
|
readback: () => snippet("tokens-edit-readback", `${observe}\nreturn {ok:true,readOnly:true,state:observe()};`),
|
|
186
332
|
verify({ inspect, readback }) {
|
|
187
333
|
try {
|
|
188
334
|
const plan = inspect.plan;
|
|
189
|
-
if (!plan?.after)
|
|
190
|
-
return [
|
|
191
|
-
|
|
335
|
+
if (!plan?.after) {
|
|
336
|
+
return [
|
|
337
|
+
"Missing original desired-state plan",
|
|
338
|
+
];
|
|
339
|
+
}
|
|
340
|
+
return canonical(comparable(stateFrom(readback))) === canonical(comparable(plan.after))
|
|
341
|
+
? []
|
|
342
|
+
: [
|
|
343
|
+
"Independent token state differs from the inspected plan",
|
|
344
|
+
];
|
|
192
345
|
}
|
|
193
346
|
catch (error) {
|
|
194
|
-
return [
|
|
347
|
+
return [
|
|
348
|
+
String(error),
|
|
349
|
+
];
|
|
195
350
|
}
|
|
196
351
|
},
|
|
197
352
|
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
export interface DeclaredToken {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
value: unknown;
|
|
5
|
+
}
|
|
6
|
+
export interface ProjectionCatalog {
|
|
7
|
+
sets: Array<{
|
|
8
|
+
id?: string;
|
|
9
|
+
name: string;
|
|
10
|
+
tokens: DeclaredToken[];
|
|
11
|
+
}>;
|
|
12
|
+
themes: Array<{
|
|
13
|
+
id?: string;
|
|
14
|
+
group: string;
|
|
15
|
+
name: string;
|
|
16
|
+
sets: string[];
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedDeclaredToken {
|
|
20
|
+
name: string;
|
|
21
|
+
type: string;
|
|
22
|
+
value?: unknown;
|
|
23
|
+
unknown?: string;
|
|
24
|
+
}
|
|
25
|
+
/** A small arithmetic grammar, never JavaScript evaluation. Unsupported units/functions are explicit unknowns. */
|
|
26
|
+
export declare function numericExpression(source: string): number;
|
|
27
|
+
/** Catalog order is authoritative; theme membership does not reorder sets. No live API is touched. */
|
|
28
|
+
export declare function resolveDeclaredTokens(catalog: Pick<ProjectionCatalog, "sets">, selectedSets: string[]): ResolvedDeclaredToken[];
|
|
29
|
+
export interface ProjectedThemePreview {
|
|
30
|
+
schemaVersion: 1;
|
|
31
|
+
kind: "projected-theme-preview";
|
|
32
|
+
sourceHash: string;
|
|
33
|
+
theme: {
|
|
34
|
+
id: string | null;
|
|
35
|
+
group: string;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
sets: Array<{
|
|
39
|
+
id: string | null;
|
|
40
|
+
name: string;
|
|
41
|
+
order: number;
|
|
42
|
+
}>;
|
|
43
|
+
resolved: Array<{
|
|
44
|
+
name: string;
|
|
45
|
+
type: string;
|
|
46
|
+
value: unknown;
|
|
47
|
+
}>;
|
|
48
|
+
unknowns: Array<{
|
|
49
|
+
name: string;
|
|
50
|
+
type: string;
|
|
51
|
+
reason: string;
|
|
52
|
+
}>;
|
|
53
|
+
complete: boolean;
|
|
54
|
+
nativeBindings: false;
|
|
55
|
+
}
|
|
56
|
+
export declare function projectTheme(catalog: ProjectionCatalog, themeName: string): ProjectedThemePreview;
|