@takazudo/zdtp 0.4.4 → 0.4.5

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.
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import { realpathSync as H } from "node:fs";
3
- import { createServer as C } from "node:http";
4
- import { resolve as d } from "node:path";
5
- import { l as x, c as N } from "../load-routing-DtNTKoLW.js";
6
- function g(t, e) {
7
- return typeof t != "string" || t.length === 0 || e.length === 0 ? !1 : e.includes(t);
2
+ import { realpathSync as E } from "node:fs";
3
+ import { createServer as H } from "node:http";
4
+ import { resolve as p } from "node:path";
5
+ import { l as $, c as x } from "../load-routing-LJ72261l.js";
6
+ function g(o, t) {
7
+ return typeof o != "string" || o.length === 0 || t.length === 0 ? !1 : t.includes(o);
8
8
  }
9
- function w(t) {
9
+ function w(o) {
10
10
  return {
11
- "Access-Control-Allow-Origin": t,
11
+ "Access-Control-Allow-Origin": o,
12
12
  "Access-Control-Allow-Methods": "POST, OPTIONS",
13
13
  "Access-Control-Allow-Headers": "content-type",
14
14
  "Access-Control-Max-Age": "600"
15
15
  };
16
16
  }
17
- const k = 24681, b = "127.0.0.1", $ = `Usage: zdtp-server [options]
17
+ const k = 24681, b = "127.0.0.1", N = `Usage: zdtp-server [options]
18
18
 
19
19
  Run a small Node http server that wraps the design-token apply handler.
20
20
 
@@ -39,8 +39,8 @@ Examples:
39
39
  --routing ./my.routing.json \\
40
40
  --allow-origin http://localhost:34434
41
41
  `;
42
- function v(t) {
43
- const e = {
42
+ function v(o) {
43
+ const t = {
44
44
  root: null,
45
45
  writeRoot: null,
46
46
  routing: null,
@@ -50,144 +50,148 @@ function v(t) {
50
50
  quiet: !1,
51
51
  help: !1
52
52
  };
53
- let o = 0;
54
- for (; o < t.length; ) {
55
- const n = t[o];
53
+ let e = 0;
54
+ for (; e < o.length; ) {
55
+ const n = o[e];
56
56
  switch (n) {
57
57
  case "--help":
58
58
  case "-h":
59
- e.help = !0, o += 1;
59
+ t.help = !0, e += 1;
60
60
  break;
61
61
  case "--quiet":
62
- e.quiet = !0, o += 1;
62
+ t.quiet = !0, e += 1;
63
63
  break;
64
64
  case "--root":
65
- e.root = c(t, o, "--root"), o += 2;
65
+ t.root = c(o, e, "--root"), e += 2;
66
66
  break;
67
67
  case "--write-root":
68
- e.writeRoot = c(t, o, "--write-root"), o += 2;
68
+ t.writeRoot = c(o, e, "--write-root"), e += 2;
69
69
  break;
70
70
  case "--routing":
71
- e.routing = c(t, o, "--routing"), o += 2;
71
+ t.routing = c(o, e, "--routing"), e += 2;
72
72
  break;
73
73
  case "--host":
74
- e.host = c(t, o, "--host"), o += 2;
74
+ t.host = c(o, e, "--host"), e += 2;
75
75
  break;
76
76
  case "--port": {
77
- const s = c(t, o, "--port"), r = Number(s);
77
+ const s = c(o, e, "--port"), r = Number(s);
78
78
  if (!Number.isInteger(r) || r < 0 || r > 65535)
79
79
  throw new Error(`--port must be an integer in 0..65535 (got ${JSON.stringify(s)})`);
80
- e.port = r, o += 2;
80
+ t.port = r, e += 2;
81
81
  break;
82
82
  }
83
83
  case "--allow-origin": {
84
- const s = c(t, o, "--allow-origin");
85
- e.allowOrigins.push(s), o += 2;
84
+ const s = c(o, e, "--allow-origin");
85
+ t.allowOrigins.push(s), e += 2;
86
86
  break;
87
87
  }
88
88
  default:
89
89
  throw new Error(`Unknown option: ${n}`);
90
90
  }
91
91
  }
92
- if (e.help) return e;
93
- if (!e.writeRoot)
92
+ if (t.help) return t;
93
+ if (!t.writeRoot)
94
94
  throw new Error("--write-root is required (the only directory the bin will write into)");
95
- if (!e.routing)
95
+ if (!t.routing)
96
96
  throw new Error("--routing is required (path to the routing JSON file)");
97
- return e;
97
+ return t;
98
98
  }
99
- function c(t, e, o) {
100
- const n = t[e + 1];
99
+ function c(o, t, e) {
100
+ const n = o[t + 1];
101
101
  if (typeof n != "string" || n.startsWith("--"))
102
- throw new Error(`${o} requires a value`);
102
+ throw new Error(`${e} requires a value`);
103
103
  return n;
104
104
  }
105
105
  const y = "/apply", D = "/healthz";
106
- function L(t) {
107
- const e = d(t.root ?? process.cwd()), o = d(e, t.writeRoot), n = d(e, t.routing);
106
+ function L(o) {
107
+ const t = p(o.root ?? process.cwd()), e = p(t, o.writeRoot), n = p(t, o.routing);
108
108
  return {
109
- rootDir: e,
110
- writeRoot: o,
109
+ rootDir: t,
110
+ writeRoot: e,
111
111
  routingPath: n,
112
- port: t.port,
113
- host: t.host,
114
- allowOrigins: t.allowOrigins,
115
- quiet: t.quiet
112
+ port: o.port,
113
+ host: o.host,
114
+ allowOrigins: o.allowOrigins,
115
+ quiet: o.quiet
116
116
  };
117
117
  }
118
- function a(t, e, o) {
119
- const n = JSON.stringify(o);
120
- t.statusCode = e, t.setHeader("Content-Type", "application/json"), t.setHeader("Content-Length", Buffer.byteLength(n).toString()), t.end(n);
118
+ function a(o, t, e) {
119
+ const n = JSON.stringify(e);
120
+ o.statusCode = t, o.setHeader("Content-Type", "application/json"), o.setHeader("Content-Length", Buffer.byteLength(n).toString()), o.end(n);
121
121
  }
122
122
  const O = 1048576;
123
- function U(t) {
124
- return new Promise((e, o) => {
123
+ function j(o) {
124
+ return new Promise((t, e) => {
125
125
  const n = [];
126
126
  let s = 0;
127
- t.on("data", (r) => {
127
+ o.on("data", (r) => {
128
128
  if (s += r.byteLength, s > O) {
129
- e(413), t.destroy();
129
+ t(413), o.destroy();
130
130
  return;
131
131
  }
132
132
  n.push(r);
133
- }), t.on("end", () => e(Buffer.concat(n).toString("utf-8"))), t.on("error", o);
133
+ }), o.on("end", () => t(Buffer.concat(n).toString("utf-8"))), o.on("error", e);
134
134
  });
135
135
  }
136
- function I(t) {
137
- const e = new Headers();
138
- for (const [o, n] of Object.entries(t.headers))
136
+ function U(o) {
137
+ const t = new Headers();
138
+ for (const [e, n] of Object.entries(o.headers))
139
139
  if (Array.isArray(n))
140
- for (const s of n) e.append(o, s);
141
- else typeof n == "string" && e.set(o, n);
142
- return e;
140
+ for (const s of n) t.append(e, s);
141
+ else typeof n == "string" && t.set(e, n);
142
+ return t;
143
143
  }
144
- async function j(t, e, o) {
145
- t.statusCode = e.status, e.headers.forEach((s, r) => {
146
- t.setHeader(r, s);
144
+ async function I(o, t, e) {
145
+ o.statusCode = t.status, t.headers.forEach((s, r) => {
146
+ o.setHeader(r, s);
147
147
  });
148
- for (const [s, r] of Object.entries(o))
149
- t.setHeader(s, r);
150
- const n = await e.text();
151
- t.end(n);
148
+ for (const [s, r] of Object.entries(e))
149
+ o.setHeader(s, r);
150
+ const n = await t.text();
151
+ o.end(n);
152
152
  }
153
- function q(t, e) {
154
- if (!t)
153
+ function V(o, t) {
154
+ if (!o)
155
155
  try {
156
- const o = JSON.parse(e);
157
- if (!o.ok || !Array.isArray(o.updated)) return;
158
- const n = o.updated.reduce(
156
+ const e = JSON.parse(t);
157
+ if (!e.ok || !Array.isArray(e.updated)) return;
158
+ const n = e.updated.reduce(
159
159
  (i, l) => i + (Array.isArray(l.changed) ? l.changed.length : 0),
160
160
  0
161
- ), s = o.updated.length, r = o.updated.filter((i) => Array.isArray(i.changed) && i.changed.length > 0).map((i) => i.file);
161
+ ), s = e.updated.length, r = e.updated.filter((i) => Array.isArray(i.changed) && i.changed.length > 0).map((i) => i.file);
162
162
  console.log(
163
163
  `[design-token-panel] applied ${n} tokens to ${s} files (changed: ${r.length > 0 ? r.join(", ") : "none"})`
164
+ ), Array.isArray(e.unknownCssVars) && e.unknownCssVars.length > 0 && console.log(
165
+ `[design-token-panel] unknown: ${e.unknownCssVars.length} cssVar(s) not found (${e.unknownCssVars.join(", ")})`
166
+ ), Array.isArray(e.unknownOutsideBlockCssVars) && e.unknownOutsideBlockCssVars.length > 0 && console.log(
167
+ `[design-token-panel] outside scanned block: ${e.unknownOutsideBlockCssVars.length} of those cssVar(s) are declared in the file but outside the scanned :root/@theme block(s), so they were not rewritable (${e.unknownOutsideBlockCssVars.join(", ")})`
164
168
  );
165
169
  } catch {
166
170
  }
167
171
  }
168
- function F(t) {
169
- const { applyHandler: e, config: o } = t;
172
+ function B(o) {
173
+ const { applyHandler: t, config: e } = o;
170
174
  return async function(s, r) {
171
175
  const i = s.url ?? "/", l = (s.method ?? "GET").toUpperCase(), u = typeof s.headers.origin == "string" ? s.headers.origin : null;
172
176
  try {
173
177
  if (i === D && l === "GET") {
174
178
  a(r, 200, {
175
179
  ok: !0,
176
- writeRoot: o.writeRoot,
177
- routing: o.routingPath,
178
- port: o.port
180
+ writeRoot: e.writeRoot,
181
+ routing: e.routingPath,
182
+ port: e.port
179
183
  });
180
184
  return;
181
185
  }
182
186
  if (i === y) {
183
187
  if (l === "OPTIONS") {
184
- if (!g(u, o.allowOrigins)) {
188
+ if (!g(u, e.allowOrigins)) {
185
189
  a(r, 403, { ok: !1, error: "Origin not allowed" });
186
190
  return;
187
191
  }
188
192
  const S = w(u);
189
- for (const [P, E] of Object.entries(S))
190
- r.setHeader(P, E);
193
+ for (const [C, P] of Object.entries(S))
194
+ r.setHeader(C, P);
191
195
  r.statusCode = 204, r.end();
192
196
  return;
193
197
  }
@@ -195,16 +199,16 @@ function F(t) {
195
199
  r.setHeader("Allow", "POST, OPTIONS"), a(r, 405, { ok: !1, error: "Method not allowed" });
196
200
  return;
197
201
  }
198
- const p = s.headers["content-type"];
199
- if (typeof p != "string" || !p.toLowerCase().includes("application/json")) {
202
+ const d = s.headers["content-type"];
203
+ if (typeof d != "string" || !d.toLowerCase().includes("application/json")) {
200
204
  a(r, 415, { ok: !1, error: "Content-Type must be application/json" });
201
205
  return;
202
206
  }
203
- if (!g(u, o.allowOrigins)) {
207
+ if (!g(u, e.allowOrigins)) {
204
208
  a(r, 403, { ok: !1, error: "Origin not allowed" });
205
209
  return;
206
210
  }
207
- const f = await U(s);
211
+ const f = await j(s);
208
212
  if (f === 413) {
209
213
  a(r, 413, {
210
214
  ok: !1,
@@ -213,71 +217,71 @@ function F(t) {
213
217
  });
214
218
  return;
215
219
  }
216
- const m = f, A = new Request(`http://localhost${y}`, {
220
+ const A = f, m = new Request(`http://localhost${y}`, {
217
221
  method: "POST",
218
- headers: I(s),
219
- body: m
220
- }), h = await e(A), R = await h.clone().text(), T = w(u);
221
- await j(r, h, T), q(o.quiet, R);
222
+ headers: U(s),
223
+ body: A
224
+ }), h = await t(m), R = await h.clone().text(), T = w(u);
225
+ await I(r, h, T), V(e.quiet, R);
222
226
  return;
223
227
  }
224
228
  a(r, 404, { ok: !1, error: "Not found" });
225
- } catch (p) {
226
- console.error("[design-token-panel] Unhandled error:", p), r.headersSent ? r.end() : a(r, 500, { ok: !1, error: "Internal server error" });
229
+ } catch (d) {
230
+ console.error("[design-token-panel] Unhandled error:", d), r.headersSent ? r.end() : a(r, 500, { ok: !1, error: "Internal server error" });
227
231
  }
228
232
  };
229
233
  }
230
- function _(t, e) {
231
- t.on("error", (n) => {
232
- n.code === "EADDRINUSE" && (console.error(`[design-token-panel] port ${e} already in use`), process.exit(1)), console.error("[design-token-panel] Server error:", n), process.exit(1);
234
+ function q(o, t) {
235
+ o.on("error", (n) => {
236
+ n.code === "EADDRINUSE" && (console.error(`[design-token-panel] port ${t} already in use`), process.exit(1)), console.error("[design-token-panel] Server error:", n), process.exit(1);
233
237
  });
234
- const o = (n) => {
235
- t.close(() => {
238
+ const e = (n) => {
239
+ o.close(() => {
236
240
  process.exit(0);
237
241
  }), setTimeout(() => {
238
242
  console.error(`[design-token-panel] force-exiting after ${n}`), process.exit(0);
239
243
  }, 5e3).unref();
240
244
  };
241
- process.on("SIGINT", o), process.on("SIGTERM", o);
245
+ process.on("SIGINT", e), process.on("SIGTERM", e);
242
246
  }
243
- function B() {
244
- let t;
247
+ function F() {
248
+ let o;
245
249
  try {
246
- t = v(process.argv.slice(2));
250
+ o = v(process.argv.slice(2));
247
251
  } catch (r) {
248
252
  console.error(`[design-token-panel] ${r.message}`), console.error(""), console.error("Run --help for usage."), process.exit(1);
249
253
  }
250
- t.help && (process.stdout.write($), process.exit(0));
251
- const e = L(t);
252
- let o;
254
+ o.help && (process.stdout.write(N), process.exit(0));
255
+ const t = L(o);
256
+ let e;
253
257
  try {
254
- o = x(e.routingPath);
258
+ e = $(t.routingPath);
255
259
  } catch (r) {
256
260
  console.error(`[design-token-panel] Failed to load routing: ${r.message}`), process.exit(1);
257
261
  }
258
- const n = N({
259
- rootDir: e.rootDir,
260
- writeRoot: e.writeRoot,
261
- routing: o
262
- }), s = C(F({ applyHandler: n, config: e }));
263
- _(s, e.port), s.listen(e.port, e.host, () => {
264
- if (!e.quiet) {
265
- const r = s.address(), i = typeof r == "object" && r ? r.port : e.port;
262
+ const n = x({
263
+ rootDir: t.rootDir,
264
+ writeRoot: t.writeRoot,
265
+ routing: e
266
+ }), s = H(B({ applyHandler: n, config: t }));
267
+ q(s, t.port), s.listen(t.port, t.host, () => {
268
+ if (!t.quiet) {
269
+ const r = s.address(), i = typeof r == "object" && r ? r.port : t.port;
266
270
  console.log(
267
- `[design-token-panel] listening on http://${e.host}:${i} (writeRoot: ${e.writeRoot})`
268
- ), e.allowOrigins.length === 0 && console.log(
271
+ `[design-token-panel] listening on http://${t.host}:${i} (writeRoot: ${t.writeRoot})`
272
+ ), t.allowOrigins.length === 0 && console.log(
269
273
  "[design-token-panel] WARNING: no --allow-origin set; browser POST /apply will be rejected."
270
274
  );
271
275
  }
272
276
  });
273
277
  }
274
- const G = (() => {
278
+ const _ = (() => {
275
279
  if (typeof process.argv[1] != "string") return !1;
276
280
  try {
277
- const t = H(process.argv[1]), e = new URL(`file://${d(t)}`).href, o = import.meta.url;
278
- return e === o;
281
+ const o = E(process.argv[1]), t = new URL(`file://${p(o)}`).href, e = import.meta.url;
282
+ return t === e;
279
283
  } catch {
280
284
  return !1;
281
285
  }
282
286
  })();
283
- G && B();
287
+ _ && F();
@@ -86,7 +86,12 @@ export interface PanelConfig {
86
86
  consoleNamespace: string;
87
87
  /** BEM-style prefix used by every modal in the panel (export / import / apply). */
88
88
  modalClassPrefix: string;
89
- /** `$schema` value emitted into export JSON and required on import. */
89
+ /**
90
+ * Schema label surfaced in the Import modal's hint/placeholder/error text
91
+ * and the Export modal's copy. NOT enforced: `serialize()`/`deserialize()`
92
+ * always use the fixed `SCHEMA_V1`/`SCHEMA_V2`/`SCHEMA_V3` constants
93
+ * regardless of this value (#498).
94
+ */
90
95
  schemaId: string;
91
96
  /** Default filename base — exports save as `${exportFilenameBase}.json`. */
92
97
  exportFilenameBase: string;
@@ -364,6 +369,24 @@ export declare function getPanelConfigByPrefix(prefix: string): PanelConfig | nu
364
369
  * instance, parked presets, and parked pre-configure hooks.
365
370
  */
366
371
  export declare function __resetPanelConfigForTests(): void;
372
+ /**
373
+ * #501 — record that the panel instance keyed by `prefix` wrote `color-scheme`
374
+ * to its apply target. Called from the apply path (`applyColorState`) wherever
375
+ * a per-mode literal forces `color-scheme: light dark`.
376
+ */
377
+ export declare function markColorSchemeWritten(prefix: string): void;
378
+ /**
379
+ * #501 — drop the color-scheme ownership claim for `prefix`. Called from the
380
+ * clear paths after a panel-written `color-scheme` is removed (or found to have
381
+ * been overwritten by the host — the stale-ownership case).
382
+ */
383
+ export declare function clearColorSchemeOwnership(prefix: string): void;
384
+ /**
385
+ * #501 — true when the panel instance keyed by `prefix` is the recorded writer
386
+ * of the current inline `color-scheme`. Gates the clear paths so the panel
387
+ * never removes a `color-scheme` it did not write.
388
+ */
389
+ export declare function ownsColorScheme(prefix: string): boolean;
367
390
  export { resolvePrimaryColorCluster, resolveSecondaryColorClusterFromTabs, } from './cluster-config';
368
391
  import type { ColorClusterDataConfig } from './cluster-config';
369
392
  /**
@@ -391,6 +414,18 @@ export declare function storageKey_stateV2(cfg: PanelConfig): string;
391
414
  * this key, then deletes the v2 key.
392
415
  */
393
416
  export declare function storageKey_stateV3(cfg: PanelConfig): string;
417
+ /**
418
+ * Storage key for the v4 unified envelope. Upgrades the "global tweak model" to
419
+ * per-scheme color persistence (#500/#509): the `color` and `secondary` slices
420
+ * become identity-keyed sub-objects (one slot per resolved scheme/mode), so a
421
+ * per-scheme color tweak survives a scheme toggle round-trip instead of being
422
+ * clobbered by the next unrelated edit. Non-color slices stay global. Kept a
423
+ * SINGLE storage key (not one key per scheme) because the Astro bootstrap must
424
+ * existence-check persistence before cluster config loads — a per-scheme key
425
+ * would hit a chicken-and-egg there. v1/v2/v3 migrate into this key on first
426
+ * load; v4 takes precedence on every subsequent load.
427
+ */
428
+ export declare function storageKey_stateV4(cfg: PanelConfig): string;
394
429
  /** Legacy v1 key (Color-only flat state). Migrated into v2 on first load, then deleted. */
395
430
  export declare function storageKey_stateV1(cfg: PanelConfig): string;
396
431
  /** Mirror of the panel's `open` boolean (synchronous mount-time read). */
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * Export modal — renders the current `TweakState` as a design-tokens JSON
3
- * document (default schema: `zudo-design-tokens/v1`; configurable via
4
- * `panelConfig.schemaId`) with a diff-only toggle and a copy-to-clipboard
5
- * button.
3
+ * document (`serialize()` emits `SCHEMA_V2` or opportunistically `SCHEMA_V3`
4
+ * — see `./utils/design-token-serde`; `panelConfig.schemaId` is not read
5
+ * here or anywhere else in this package — it is a UI-label-only field on
6
+ * `PanelConfig` that hosts may read via `getDesignTokenSchema()`, #498) with
7
+ * a diff-only toggle and a copy-to-clipboard button.
6
8
  *
7
9
  * Ported verbatim from zudo-doc's
8
10
  * `src/components/design-token-tweak/export-modal.tsx`. Intentional deltas:
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Import modal — accepts a pasted design-tokens JSON document (default schema:
3
- * `zudo-design-tokens/v1`; configurable via `panelConfig.schemaId`) and lifts
4
- * it into a `TweakState` via `deserialize()`. Validation and unknown-token
5
- * reporting are delegated to serde; this component only renders status
6
- * feedback and routes the parsed state up to the shell.
2
+ * Import modal — accepts a pasted design-tokens JSON document and lifts it
3
+ * into a `TweakState` via `deserialize()`. `deserialize()` validates the
4
+ * `$schema` key against the fixed `SCHEMA_V1`/`SCHEMA_V2`/`SCHEMA_V3`
5
+ * constants exported by the serde — `panelConfig.schemaId` plays no part in
6
+ * that validation; it is a UI-label-only field (#498). This component does
7
+ * not read `schemaId` at all; the hint/placeholder/error copy below names
8
+ * the actual accepted constants directly.
7
9
  *
8
10
  * Ported verbatim from zudo-doc's
9
11
  * `src/components/design-token-tweak/import-modal.tsx`. Intentional deltas:
10
- * - `deserialize` / `DesignTokenSchemaError` / `getDesignTokenSchema` come
11
- * from this package's serde (`./utils/design-token-serde`); the schema
12
- * id is read at render time from `panelConfig.schemaId` instead of
13
- * being a module-level constant.
12
+ * - `deserialize` / `DesignTokenSchemaError` come from this package's serde
13
+ * (`./utils/design-token-serde`).
14
14
  * - State types import from this package's state envelope.
15
15
  * - Modal class names are derived from `panelConfig.modalClassPrefix` via
16
16
  * `modalClass(...)` so a single config swap re-themes every dialog.
package/dist/index.d.ts CHANGED
@@ -60,6 +60,7 @@ export type { TierValueKind, PillSpec, TierItem, TierConfig, TabConfig, ColorClu
60
60
  export { isLengthKind, isNumberKind, isSelectKind, isTextKind, isColorKind, isCursorKind, isContentKind, isMaskImageKind, } from './tokens/tier-model';
61
61
  export type { TweakState } from './state/tweak-state';
62
62
  export { emptyOverrides } from './state/tweak-state';
63
+ export { SCHEMA_V1, SCHEMA_V2, SCHEMA_V3, } from './utils/design-token-serde';
63
64
  export { getClusterDefaultMode, resolvePerModeLiteral, resolveSemanticPreviewColor, } from './state/tweak-state';
64
65
  export declare function showDesignTokenPanel(): void;
65
66
  export declare function hideDesignTokenPanel(): void;