@1agh/maude 0.17.1 → 0.18.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.
Files changed (27) hide show
  1. package/cli/cli-wrapper.cjs +0 -0
  2. package/cli/commands/design.mjs +248 -43
  3. package/package.json +8 -8
  4. package/plugins/design/dev-server/bin/screenshot.sh +12 -0
  5. package/plugins/design/dev-server/boot-self-heal.ts +90 -0
  6. package/plugins/design/dev-server/build.ts +136 -8
  7. package/plugins/design/dev-server/canvas-pipeline.ts +5 -0
  8. package/plugins/design/dev-server/config.schema.json +12 -0
  9. package/plugins/design/dev-server/dist/client.bundle.js +3 -3
  10. package/plugins/design/dev-server/runtime-bundle.ts +29 -1
  11. package/plugins/design/dev-server/server.ts +6 -0
  12. package/plugins/design/dev-server/test/boot-self-heal.test.ts +112 -0
  13. package/plugins/design/dev-server/test/compile-entry.test.ts +134 -0
  14. package/plugins/design/dev-server/test/runtime-bundle-error-mapping.test.ts +43 -0
  15. package/plugins/design/templates/canvas.tsx.template +7 -7
  16. package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +1 -1
  17. package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +11 -7
  18. package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +4 -3
  19. package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +61 -57
  20. package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +2 -0
  21. package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +4 -4
  22. package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +1 -1
  23. package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +3 -0
  24. package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +1 -1
  25. package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +6 -6
  26. package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +3 -2
  27. package/plugins/design/templates/design-system-inspiration/universal/logo.html +2 -1
@@ -15,8 +15,8 @@
15
15
  //
16
16
  // Per DDR-009 (Bun runtime authoritative) + DDR-012 (React 19 unified) + DDR-014 (Lightning CSS).
17
17
 
18
- import { existsSync, mkdirSync } from 'node:fs';
19
- import { dirname, join, resolve } from 'node:path';
18
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
19
+ import { dirname, join, relative, resolve } from 'node:path';
20
20
  import { fileURLToPath } from 'node:url';
21
21
 
22
22
  import { browserslistToTargets, bundle as lcssBundle } from 'lightningcss';
@@ -65,13 +65,29 @@ function ensureDist() {
65
65
  if (!existsSync(DIST)) mkdirSync(DIST, { recursive: true });
66
66
  }
67
67
 
68
+ async function readPluginVersion(): Promise<{ version: string }> {
69
+ // plugins/design/dev-server/ → plugins/design/.claude-plugin/plugin.json
70
+ const manifest = join(ROOT, '..', '.claude-plugin', 'plugin.json');
71
+ try {
72
+ const parsed = JSON.parse(await Bun.file(manifest).text()) as { version?: unknown };
73
+ if (typeof parsed.version === 'string') return { version: parsed.version };
74
+ } catch {
75
+ /* fall through to dev default */
76
+ }
77
+ return { version: 'dev' };
78
+ }
79
+
68
80
  // ---------- (a) Client JSX bundle ----------
69
81
 
70
82
  async function buildClient(): Promise<{ outBytes: number; outPath: string }> {
71
83
  ensureDist();
72
84
  const outPath = join(DIST, 'client.bundle.js');
73
- // Read package.json version at build time for the wordmark sub-line.
74
- const pkg = JSON.parse(await Bun.file(join(ROOT, '..', '..', '..', 'package.json')).text());
85
+ // Wordmark sub-line version. Read from plugins/design/.claude-plugin/plugin.json —
86
+ // that file ships in both npm installs AND marketplace-cache clones (it's the
87
+ // plugin manifest, always present). The old `../../../package.json` hop
88
+ // resolved to the repo root in dev but ENOENT'd in marketplace caches where
89
+ // the maude bundle isn't installed as a package. DDR-044, Phase 19.
90
+ const pkg = await readPluginVersion();
75
91
  const result = await Bun.build({
76
92
  entrypoints: [join(ROOT, 'client/app.jsx')],
77
93
  outdir: DIST,
@@ -122,14 +138,117 @@ async function buildCss(): Promise<{ outBytes: number; outPath: string }> {
122
138
 
123
139
  // ---------- (c) Server binary (bun build --compile, per-platform) ----------
124
140
 
141
+ // Per-target oxc-parser binding embed. Bun 1.3.4+ regressed `--compile` NAPI
142
+ // native-binding embedding (see DDR-042-oxc-parser-bun-compile-workaround.md).
143
+ // `with { type: 'file' }` requires a literal string path, so we generate one
144
+ // thin entry file per --target whose only purpose is to (1) embed the matching
145
+ // platform binding as an asset, (2) set NAPI_RS_NATIVE_LIBRARY_PATH from the
146
+ // runtime virtual path, then (3) hand off to the real server.ts. ESM
147
+ // evaluation order guarantees the env var is set before any downstream module
148
+ // touches oxc-parser.
149
+
150
+ // oxc-parser's NAPI-RS binding packages use a richer slug than our build
151
+ // target slug — Linux needs the libc kind suffix, Windows needs the toolchain
152
+ // suffix. Mirrored from oxc-parser/src-js/bindings.js loader cases.
153
+ function oxcBindingSlug(slug: string): string {
154
+ if (slug === 'linux-x64') return 'linux-x64-gnu';
155
+ if (slug === 'linux-arm64') return 'linux-arm64-gnu';
156
+ if (slug === 'win32-x64') return 'win32-x64-msvc';
157
+ // darwin-arm64 / darwin-x64 / linux-x64-musl / linux-arm64-musl already match.
158
+ return slug;
159
+ }
160
+
161
+ // npm/bun's installer respects each package's `os`/`cpu` fields and skips
162
+ // platform sub-packages whose filter doesn't match the host (the bindings
163
+ // declare e.g. `"cpu": ["x64"]`). For cross-compile targets — most notably
164
+ // darwin-x64 built on a darwin-arm64 CI runner — this means the matching
165
+ // `@oxc-parser/binding-<oxcSlug>` is NOT in node_modules even though it's
166
+ // listed as a direct devDependency. Fetch + extract the tarball manually so
167
+ // `with { type: 'file' }` resolution succeeds during `bun build --compile`.
168
+ async function ensureBindingForTarget(oxcSlug: string): Promise<void> {
169
+ const bindingDir = join(ROOT, 'node_modules', '@oxc-parser', `binding-${oxcSlug}`);
170
+ const bindingFile = join(bindingDir, `parser.${oxcSlug}.node`);
171
+ if (existsSync(bindingFile)) return; // host install already placed it
172
+
173
+ // Read the installed oxc-parser version so the tarball URL stays in sync.
174
+ // Walk up from build.ts to find the workspace-visible oxc-parser package.json
175
+ // (pnpm hoists it under node_modules/.pnpm, then symlinks at workspace level).
176
+ let oxcVersion = '0.131.0';
177
+ const candidates = [
178
+ join(ROOT, 'node_modules', 'oxc-parser', 'package.json'),
179
+ join(ROOT, '..', '..', '..', 'node_modules', 'oxc-parser', 'package.json'),
180
+ ];
181
+ for (const c of candidates) {
182
+ if (existsSync(c)) {
183
+ oxcVersion = JSON.parse(readFileSync(c, 'utf8')).version;
184
+ break;
185
+ }
186
+ }
187
+
188
+ console.log(`[build] cross-compile: fetching @oxc-parser/binding-${oxcSlug}@${oxcVersion}`);
189
+ const tarballUrl = `https://registry.npmjs.org/@oxc-parser/binding-${oxcSlug}/-/binding-${oxcSlug}-${oxcVersion}.tgz`;
190
+ const response = await fetch(tarballUrl);
191
+ if (!response.ok) {
192
+ throw new Error(`failed to fetch ${tarballUrl}: HTTP ${response.status}`);
193
+ }
194
+ const tmpTgz = join(ROOT, `.binding-${oxcSlug}.tgz`);
195
+ await Bun.write(tmpTgz, await response.arrayBuffer());
196
+ mkdirSync(bindingDir, { recursive: true });
197
+ // npm pack output: `package/parser.<oxcSlug>.node` + `package/package.json`.
198
+ // --strip-components=1 drops the leading `package/` so files land in bindingDir.
199
+ const tarProc = Bun.spawn(['tar', 'xf', tmpTgz, '-C', bindingDir, '--strip-components=1'], {
200
+ stdout: 'inherit',
201
+ stderr: 'inherit',
202
+ });
203
+ const tarCode = await tarProc.exited;
204
+ if (tarCode !== 0)
205
+ throw new Error(`tar extraction failed for binding-${oxcSlug} (exit ${tarCode})`);
206
+ unlinkSync(tmpTgz);
207
+ }
208
+
209
+ export function writeCompileEntry(target: PlatformTarget): string {
210
+ const slug = platformSlug(target);
211
+ const oxcSlug = oxcBindingSlug(slug);
212
+ const entryDir = join(DIST, '.compile-entries');
213
+ mkdirSync(entryDir, { recursive: true });
214
+
215
+ // ESM hoists `import` statements above top-level code, so the env-var
216
+ // assignment MUST live in a separate leaf module that's imported BEFORE
217
+ // server.ts — otherwise server.ts (and transitively oxc-parser) evaluates
218
+ // first and reads NAPI_RS_NATIVE_LIBRARY_PATH before we set it.
219
+ const initPath = join(entryDir, `init-oxc-${slug}.ts`);
220
+ const entryPath = join(entryDir, `server-${slug}.ts`);
221
+ const bindingSpec = `@oxc-parser/binding-${oxcSlug}/parser.${oxcSlug}.node`;
222
+ const initContent = `// AUTO-GENERATED by build.ts — do not edit by hand.
223
+ // Per-target oxc-parser binding embed (Bun 1.3.4+ --compile NAPI regression
224
+ // workaround — see .ai/decisions/DDR-042-oxc-parser-bun-compile-workaround.md).
225
+ // Side-effect module: must be imported BEFORE any oxc-parser usage.
226
+ import bindingPath from ${JSON.stringify(bindingSpec)} with { type: 'file' };
227
+ process.env.NAPI_RS_NATIVE_LIBRARY_PATH = bindingPath;
228
+ `;
229
+ writeFileSync(initPath, initContent);
230
+
231
+ // Forward slashes for the import specifier even on Windows hosts — ESM uses
232
+ // POSIX-style paths, not the host's filesystem separator.
233
+ const realServerRel = relative(entryDir, join(ROOT, 'server.ts')).split('\\').join('/');
234
+ const entryContent = `// AUTO-GENERATED by build.ts — do not edit by hand.
235
+ // Per-target compile entry — see DDR-042-oxc-parser-bun-compile-workaround.md.
236
+ import './init-oxc-${slug}.ts';
237
+ import ${JSON.stringify(realServerRel)};
238
+ `;
239
+ writeFileSync(entryPath, entryContent);
240
+ return entryPath;
241
+ }
242
+
125
243
  async function buildServerBinary(target: PlatformTarget): Promise<{ outPath: string }> {
126
244
  ensureDist();
127
245
  const slug = platformSlug(target);
128
246
  const ext = slug.startsWith('win32') ? '.exe' : '';
129
247
  const outPath = join(DIST, `maude-${slug}${ext}`);
130
- const entry = join(ROOT, 'server.ts');
131
- if (!existsSync(entry)) {
132
- // T7 not landed yet fall back to the .mjs entry so this script remains runnable mid-migration.
248
+ const realEntry = join(ROOT, 'server.ts');
249
+ if (!existsSync(realEntry)) {
250
+ // Legacy fallbackpre-DDR-009 .mjs path. Doesn't use oxc-parser, so the
251
+ // 1.3.4+ regression doesn't apply; entry stub generator is skipped.
133
252
  const legacy = join(ROOT, 'server.mjs');
134
253
  if (!existsSync(legacy)) throw new Error(`Neither server.ts nor server.mjs exists in ${ROOT}`);
135
254
  const proc = Bun.spawn(
@@ -150,6 +269,10 @@ async function buildServerBinary(target: PlatformTarget): Promise<{ outPath: str
150
269
  throw new Error(`bun build --compile (legacy) failed for ${target} (exit ${code})`);
151
270
  return { outPath };
152
271
  }
272
+ // Make sure the target's NAPI binding is on disk even if bun/npm's os/cpu
273
+ // filter skipped it (cross-compile case — see ensureBindingForTarget docs).
274
+ await ensureBindingForTarget(oxcBindingSlug(slug));
275
+ const entry = writeCompileEntry(target);
153
276
  const proc = Bun.spawn(
154
277
  [
155
278
  'bun',
@@ -262,6 +385,11 @@ async function main() {
262
385
  }
263
386
  }
264
387
 
265
- await main();
388
+ // Only run the build when invoked directly — importers (e.g. the test suite,
389
+ // which calls `writeCompileEntry` from build.ts) should not trigger a dev
390
+ // build as a side effect.
391
+ if (import.meta.main) {
392
+ await main();
393
+ }
266
394
 
267
395
  export { buildClient, buildCss, buildServerBinary, PLATFORM_MATRIX, type PlatformTarget };
@@ -16,6 +16,11 @@
16
16
  // Toolchain matches scripts/migrate-canvases.ts (codemod) and canvas-edit.ts
17
17
  // (AST-aware /design:edit element edits) — same oxc-parser + magic-string pair
18
18
  // in three call sites, one mental model.
19
+ //
20
+ // `bun build --compile` requires a per-target entry stub that sets
21
+ // NAPI_RS_NATIVE_LIBRARY_PATH before this file's transitive imports load
22
+ // oxc-parser. The pipeline itself is unaware of the workaround — it's a
23
+ // build-layer concern. See DDR-042-oxc-parser-bun-compile-workaround.md.
19
24
 
20
25
  import MagicString from 'magic-string';
21
26
  import { parseSync } from 'oxc-parser';
@@ -116,6 +116,18 @@
116
116
  },
117
117
  "default": ["accent"]
118
118
  },
119
+ "accentStrategy": {
120
+ "type": "string",
121
+ "description": "How many --accent* families exist. The completeness-critic C7 gates the actual family count against this declaration. Default: 'single' (backwards-compatible). 'chromatic-N' is parameterized — write the literal N (e.g. 'chromatic-5'). Per DDR-043.",
122
+ "pattern": "^(single|paired|chromatic-[1-9][0-9]?)$",
123
+ "default": "single"
124
+ },
125
+ "colorSpace": {
126
+ "type": "string",
127
+ "enum": ["oklch", "hsl", "hex", "lab"],
128
+ "description": "Color space the project uses in tokens CSS. The completeness-critic V2 gates the actual token format against this declaration. Default: 'oklch' (backwards-compatible). Per DDR-043.",
129
+ "default": "oklch"
130
+ },
119
131
  "designSystems": {
120
132
  "type": "array",
121
133
  "description": "Design systems available in this project. Single-DS projects have one entry; multi-DS projects (marketing vs. admin vs. mobile) list each here. Each canvas's .meta.json declares which DS it uses via the 'designSystem' field.",