@wix/web50-cli 0.1.0 → 0.1.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.
Files changed (122) hide show
  1. package/dist/cjs/auth/deviceFlow.js +99 -13
  2. package/dist/cjs/auth/deviceFlow.js.map +1 -1
  3. package/dist/cjs/auth/index.js +65 -12
  4. package/dist/cjs/auth/index.js.map +1 -1
  5. package/dist/cjs/auth/secretStore.js.map +1 -1
  6. package/dist/cjs/cli.js +29 -1
  7. package/dist/cjs/cli.js.map +1 -1
  8. package/dist/cjs/commands/bundle.js +95 -0
  9. package/dist/cjs/commands/bundle.js.map +1 -0
  10. package/dist/cjs/commands/conversation.js +50 -0
  11. package/dist/cjs/commands/conversation.js.map +1 -0
  12. package/dist/cjs/commands/conversationWizard.js +526 -0
  13. package/dist/cjs/commands/conversationWizard.js.map +1 -0
  14. package/dist/cjs/commands/deploy.js +192 -0
  15. package/dist/cjs/commands/deploy.js.map +1 -0
  16. package/dist/cjs/commands/embed.js +118 -0
  17. package/dist/cjs/commands/embed.js.map +1 -0
  18. package/dist/cjs/commands/getClientId.js +60 -0
  19. package/dist/cjs/commands/getClientId.js.map +1 -0
  20. package/dist/cjs/commands/init.js +31 -26
  21. package/dist/cjs/commands/init.js.map +1 -1
  22. package/dist/cjs/commands/instructions.js +360 -0
  23. package/dist/cjs/commands/instructions.js.map +1 -0
  24. package/dist/cjs/commands/login.js +59 -4
  25. package/dist/cjs/commands/login.js.map +1 -1
  26. package/dist/cjs/commands/logout.js +16 -0
  27. package/dist/cjs/commands/logout.js.map +1 -0
  28. package/dist/cjs/commands/serve.js +122 -0
  29. package/dist/cjs/commands/serve.js.map +1 -0
  30. package/dist/cjs/commands/storybook.js +102 -0
  31. package/dist/cjs/commands/storybook.js.map +1 -0
  32. package/dist/cjs/commands/validate.js +266 -18
  33. package/dist/cjs/commands/validate.js.map +1 -1
  34. package/dist/cjs/commands/whoami.js +48 -0
  35. package/dist/cjs/commands/whoami.js.map +1 -0
  36. package/dist/cjs/utils/print.js +12 -0
  37. package/dist/cjs/utils/print.js.map +1 -1
  38. package/dist/cjs/utils/project.js +11 -0
  39. package/dist/cjs/utils/project.js.map +1 -1
  40. package/dist/cjs/utils/wixApi.js +55 -0
  41. package/dist/cjs/utils/wixApi.js.map +1 -0
  42. package/dist/esm/auth/deviceFlow.js +106 -13
  43. package/dist/esm/auth/deviceFlow.js.map +1 -1
  44. package/dist/esm/auth/index.js +71 -13
  45. package/dist/esm/auth/index.js.map +1 -1
  46. package/dist/esm/auth/secretStore.js.map +1 -1
  47. package/dist/esm/cli.js +29 -1
  48. package/dist/esm/cli.js.map +1 -1
  49. package/dist/esm/commands/bundle.js +92 -0
  50. package/dist/esm/commands/bundle.js.map +1 -0
  51. package/dist/esm/commands/conversation.js +44 -0
  52. package/dist/esm/commands/conversation.js.map +1 -0
  53. package/dist/esm/commands/conversationWizard.js +527 -0
  54. package/dist/esm/commands/conversationWizard.js.map +1 -0
  55. package/dist/esm/commands/deploy.js +194 -0
  56. package/dist/esm/commands/deploy.js.map +1 -0
  57. package/dist/esm/commands/embed.js +112 -0
  58. package/dist/esm/commands/embed.js.map +1 -0
  59. package/dist/esm/commands/getClientId.js +56 -0
  60. package/dist/esm/commands/getClientId.js.map +1 -0
  61. package/dist/esm/commands/init.js +32 -27
  62. package/dist/esm/commands/init.js.map +1 -1
  63. package/dist/esm/commands/instructions.js +360 -0
  64. package/dist/esm/commands/instructions.js.map +1 -0
  65. package/dist/esm/commands/login.js +62 -6
  66. package/dist/esm/commands/login.js.map +1 -1
  67. package/dist/esm/commands/logout.js +12 -0
  68. package/dist/esm/commands/logout.js.map +1 -0
  69. package/dist/esm/commands/serve.js +117 -0
  70. package/dist/esm/commands/serve.js.map +1 -0
  71. package/dist/esm/commands/storybook.js +97 -0
  72. package/dist/esm/commands/storybook.js.map +1 -0
  73. package/dist/esm/commands/validate.js +269 -19
  74. package/dist/esm/commands/validate.js.map +1 -1
  75. package/dist/esm/commands/whoami.js +44 -0
  76. package/dist/esm/commands/whoami.js.map +1 -0
  77. package/dist/esm/utils/print.js +10 -0
  78. package/dist/esm/utils/print.js.map +1 -1
  79. package/dist/esm/utils/project.js +8 -0
  80. package/dist/esm/utils/project.js.map +1 -1
  81. package/dist/esm/utils/wixApi.js +51 -0
  82. package/dist/esm/utils/wixApi.js.map +1 -0
  83. package/dist/types/auth/deviceFlow.d.ts +3 -1
  84. package/dist/types/auth/deviceFlow.d.ts.map +1 -1
  85. package/dist/types/auth/index.d.ts +5 -1
  86. package/dist/types/auth/index.d.ts.map +1 -1
  87. package/dist/types/auth/secretStore.d.ts +2 -0
  88. package/dist/types/auth/secretStore.d.ts.map +1 -1
  89. package/dist/types/commands/bundle.d.ts +10 -0
  90. package/dist/types/commands/bundle.d.ts.map +1 -0
  91. package/dist/types/commands/conversation.d.ts +3 -0
  92. package/dist/types/commands/conversation.d.ts.map +1 -0
  93. package/dist/types/commands/conversationWizard.d.ts +3 -0
  94. package/dist/types/commands/conversationWizard.d.ts.map +1 -0
  95. package/dist/types/commands/deploy.d.ts +3 -0
  96. package/dist/types/commands/deploy.d.ts.map +1 -0
  97. package/dist/types/commands/embed.d.ts +3 -0
  98. package/dist/types/commands/embed.d.ts.map +1 -0
  99. package/dist/types/commands/getClientId.d.ts +3 -0
  100. package/dist/types/commands/getClientId.d.ts.map +1 -0
  101. package/dist/types/commands/init.d.ts.map +1 -1
  102. package/dist/types/commands/instructions.d.ts +3 -0
  103. package/dist/types/commands/instructions.d.ts.map +1 -0
  104. package/dist/types/commands/login.d.ts.map +1 -1
  105. package/dist/types/commands/logout.d.ts +3 -0
  106. package/dist/types/commands/logout.d.ts.map +1 -0
  107. package/dist/types/commands/serve.d.ts +3 -0
  108. package/dist/types/commands/serve.d.ts.map +1 -0
  109. package/dist/types/commands/storybook.d.ts +3 -0
  110. package/dist/types/commands/storybook.d.ts.map +1 -0
  111. package/dist/types/commands/validate.d.ts +5 -0
  112. package/dist/types/commands/validate.d.ts.map +1 -1
  113. package/dist/types/commands/whoami.d.ts +3 -0
  114. package/dist/types/commands/whoami.d.ts.map +1 -0
  115. package/dist/types/utils/print.d.ts +3 -0
  116. package/dist/types/utils/print.d.ts.map +1 -1
  117. package/dist/types/utils/project.d.ts +10 -0
  118. package/dist/types/utils/project.d.ts.map +1 -1
  119. package/dist/types/utils/wixApi.d.ts +9 -0
  120. package/dist/types/utils/wixApi.d.ts.map +1 -0
  121. package/package.json +5 -5
  122. package/defaults/package.json +0 -42
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Command","logout","error","logoutCommand","description","action","err_","Error","message","String","process","exit"],"sources":["../../../src/commands/logout.ts"],"sourcesContent":["import { Command } from 'commander';\nimport { logout } from '../auth';\nimport { error } from '../utils/print';\n\nexport const logoutCommand = new Command('logout')\n .description('Sign out and remove stored credentials')\n .action(async () => {\n try {\n await logout();\n } catch (err_) {\n error(err_ instanceof Error ? err_.message : String(err_));\n process.exit(1);\n }\n });\n"],"mappings":"AAAA,SAASA,OAAO,QAAQ,WAAW;AACnC,SAASC,MAAM,QAAQ,SAAS;AAChC,SAASC,KAAK,QAAQ,gBAAgB;AAEtC,OAAO,MAAMC,aAAa,GAAG,IAAIH,OAAO,CAAC,QAAQ,CAAC,CAC/CI,WAAW,CAAC,wCAAwC,CAAC,CACrDC,MAAM,CAAC,YAAY;EAClB,IAAI;IACF,MAAMJ,MAAM,CAAC,CAAC;EAChB,CAAC,CAAC,OAAOK,IAAI,EAAE;IACbJ,KAAK,CAACI,IAAI,YAAYC,KAAK,GAAGD,IAAI,CAACE,OAAO,GAAGC,MAAM,CAACH,IAAI,CAAC,CAAC;IAC1DI,OAAO,CAACC,IAAI,CAAC,CAAC,CAAC;EACjB;AACF,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,117 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import * as http from 'http';
5
+ import { spawn } from 'child_process';
6
+ import { success, error, info, isJsonMode } from '../utils/print';
7
+ import { findProjectRoot } from '../utils/project';
8
+ import { runBundle } from './bundle';
9
+ const DEFAULT_PORT = 4001;
10
+ const MIME_TYPES = {
11
+ '.js': 'application/javascript',
12
+ '.css': 'text/css',
13
+ '.json': 'application/json',
14
+ '.map': 'application/json'
15
+ };
16
+ function startServer(distDir, corsOrigin) {
17
+ const server = http.createServer((req, res) => {
18
+ res.setHeader('Access-Control-Allow-Origin', corsOrigin);
19
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
20
+ res.setHeader('Access-Control-Allow-Headers', '*');
21
+ if (req.method === 'OPTIONS') {
22
+ res.writeHead(204);
23
+ res.end();
24
+ return;
25
+ }
26
+ const urlPath = req.url === '/' ? '' : req.url ?? '';
27
+ const filePath = path.join(distDir, urlPath);
28
+
29
+ // Prevent path traversal outside distDir
30
+ if (!filePath.startsWith(distDir)) {
31
+ res.writeHead(403);
32
+ res.end('Forbidden');
33
+ return;
34
+ }
35
+ if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
36
+ res.writeHead(404);
37
+ res.end('Not found');
38
+ return;
39
+ }
40
+ const ext = path.extname(filePath);
41
+ const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';
42
+ res.setHeader('Content-Type', contentType);
43
+ res.writeHead(200);
44
+ fs.createReadStream(filePath).pipe(res);
45
+ });
46
+ return server;
47
+ }
48
+ export const serveCommand = new Command('serve').description('Bundle components and serve them on http://localhost:<port> with CORS enabled').option('--port <n>', 'Port to listen on', String(DEFAULT_PORT)).option('--no-bundle', 'Serve existing dist/cdn/ without rebuilding').option('--watch', 'Re-bundle on source changes (starts vite --watch alongside server)').option('--open', 'Open the served URL in your browser after start').option('--cors-origin <origin>', 'Restrict CORS Allow-Origin header', '*').action(async opts => {
49
+ try {
50
+ const projectRoot = findProjectRoot(process.cwd());
51
+ if (!projectRoot) {
52
+ error('No web5.config.json found. Run this command from within a Web5 project.');
53
+ process.exit(1);
54
+ }
55
+ const port = parseInt(opts.port, 10);
56
+ if (isNaN(port) || port < 1 || port > 65535) {
57
+ error(`Invalid port: ${opts.port}`);
58
+ process.exit(1);
59
+ }
60
+ const distDir = path.join(projectRoot, 'dist', 'cdn');
61
+
62
+ // ── Bundle (unless --no-bundle) ─────────────────────────────────────────
63
+ if (opts.bundle !== false) {
64
+ await runBundle(projectRoot);
65
+ }
66
+ if (!fs.existsSync(distDir)) {
67
+ error(`Bundle output not found at ${distDir}. Run \`web5 bundle\` first or remove --no-bundle.`);
68
+ process.exit(1);
69
+ }
70
+
71
+ // ── Watch mode: spawn vite --watch in background ────────────────────────
72
+ if (opts.watch) {
73
+ const viteBin = path.join(projectRoot, 'node_modules', '.bin', 'vite');
74
+ if (!fs.existsSync(viteBin)) {
75
+ error('vite not found. Run `npm install` in your project first.');
76
+ process.exit(1);
77
+ }
78
+ const watcher = spawn(`"${viteBin}"`, ['build', '--config', 'src/vite.cdn.config.ts', '--watch'], {
79
+ cwd: projectRoot,
80
+ shell: true,
81
+ stdio: 'inherit'
82
+ });
83
+ watcher.on('error', err_ => error(`Watch process error: ${err_.message}`));
84
+ }
85
+
86
+ // ── Start HTTP server ───────────────────────────────────────────────────
87
+ const server = startServer(distDir, opts.corsOrigin);
88
+ const url = `http://localhost:${port}`;
89
+ server.listen(port, async () => {
90
+ const files = fs.readdirSync(distDir);
91
+ if (isJsonMode()) {
92
+ console.log(JSON.stringify({
93
+ url,
94
+ port,
95
+ files: files.map(f => `${url}/${f}`)
96
+ }));
97
+ } else {
98
+ success(`Serving on ${url}`);
99
+ for (const file of files) {
100
+ info(` ${url}/${file}`);
101
+ }
102
+ if (opts.watch) {
103
+ info('Watch mode active — rebuilds automatically on file changes.');
104
+ }
105
+ info('Press Ctrl+C to stop.');
106
+ }
107
+ if (opts.open) {
108
+ const openPkg = (await import('open')).default;
109
+ await openPkg(url);
110
+ }
111
+ });
112
+ } catch (err_) {
113
+ error(err_ instanceof Error ? err_.message : String(err_));
114
+ process.exit(1);
115
+ }
116
+ });
117
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Command","path","fs","http","spawn","success","error","info","isJsonMode","findProjectRoot","runBundle","DEFAULT_PORT","MIME_TYPES","startServer","distDir","corsOrigin","server","createServer","req","res","setHeader","method","writeHead","end","urlPath","url","filePath","join","startsWith","existsSync","statSync","isDirectory","ext","extname","contentType","createReadStream","pipe","serveCommand","description","option","String","action","opts","projectRoot","process","cwd","exit","port","parseInt","isNaN","bundle","watch","viteBin","watcher","shell","stdio","on","err_","message","listen","files","readdirSync","console","log","JSON","stringify","map","f","file","open","openPkg","default","Error"],"sources":["../../../src/commands/serve.ts"],"sourcesContent":["import { Command } from 'commander';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as http from 'http';\nimport { spawn } from 'child_process';\nimport { success, error, info, isJsonMode } from '../utils/print';\nimport { findProjectRoot } from '../utils/project';\nimport { runBundle } from './bundle';\n\nconst DEFAULT_PORT = 4001;\n\nconst MIME_TYPES: Record<string, string> = {\n '.js': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.map': 'application/json',\n};\n\nfunction startServer(\n distDir: string,\n corsOrigin: string,\n): http.Server {\n const server = http.createServer((req, res) => {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin);\n res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', '*');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n const urlPath = req.url === '/' ? '' : req.url ?? '';\n const filePath = path.join(distDir, urlPath);\n\n // Prevent path traversal outside distDir\n if (!filePath.startsWith(distDir)) {\n res.writeHead(403);\n res.end('Forbidden');\n return;\n }\n\n if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {\n res.writeHead(404);\n res.end('Not found');\n return;\n }\n\n const ext = path.extname(filePath);\n const contentType = MIME_TYPES[ext] ?? 'application/octet-stream';\n res.setHeader('Content-Type', contentType);\n res.writeHead(200);\n fs.createReadStream(filePath).pipe(res);\n });\n\n return server;\n}\n\nexport const serveCommand = new Command('serve')\n .description('Bundle components and serve them on http://localhost:<port> with CORS enabled')\n .option('--port <n>', 'Port to listen on', String(DEFAULT_PORT))\n .option('--no-bundle', 'Serve existing dist/cdn/ without rebuilding')\n .option('--watch', 'Re-bundle on source changes (starts vite --watch alongside server)')\n .option('--open', 'Open the served URL in your browser after start')\n .option('--cors-origin <origin>', 'Restrict CORS Allow-Origin header', '*')\n .action(async (opts: {\n port: string;\n bundle: boolean;\n watch?: boolean;\n open?: boolean;\n corsOrigin: string;\n }) => {\n try {\n const projectRoot = findProjectRoot(process.cwd());\n if (!projectRoot) {\n error(\n 'No web5.config.json found. Run this command from within a Web5 project.',\n );\n process.exit(1);\n }\n\n const port = parseInt(opts.port, 10);\n if (isNaN(port) || port < 1 || port > 65535) {\n error(`Invalid port: ${opts.port}`);\n process.exit(1);\n }\n\n const distDir = path.join(projectRoot, 'dist', 'cdn');\n\n // ── Bundle (unless --no-bundle) ─────────────────────────────────────────\n if (opts.bundle !== false) {\n await runBundle(projectRoot);\n }\n\n if (!fs.existsSync(distDir)) {\n error(\n `Bundle output not found at ${distDir}. Run \\`web5 bundle\\` first or remove --no-bundle.`,\n );\n process.exit(1);\n }\n\n // ── Watch mode: spawn vite --watch in background ────────────────────────\n if (opts.watch) {\n const viteBin = path.join(projectRoot, 'node_modules', '.bin', 'vite');\n if (!fs.existsSync(viteBin)) {\n error('vite not found. Run `npm install` in your project first.');\n process.exit(1);\n }\n const watcher = spawn(\n `\"${viteBin}\"`,\n ['build', '--config', 'src/vite.cdn.config.ts', '--watch'],\n { cwd: projectRoot, shell: true, stdio: 'inherit' },\n );\n watcher.on('error', (err_) => error(`Watch process error: ${err_.message}`));\n }\n\n // ── Start HTTP server ───────────────────────────────────────────────────\n const server = startServer(distDir, opts.corsOrigin);\n const url = `http://localhost:${port}`;\n\n server.listen(port, async () => {\n const files = fs.readdirSync(distDir);\n\n if (isJsonMode()) {\n console.log(JSON.stringify({ url, port, files: files.map((f) => `${url}/${f}`) }));\n } else {\n success(`Serving on ${url}`);\n for (const file of files) {\n info(` ${url}/${file}`);\n }\n if (opts.watch) {\n info('Watch mode active — rebuilds automatically on file changes.');\n }\n info('Press Ctrl+C to stop.');\n }\n\n if (opts.open) {\n const openPkg = (await import('open')).default;\n await openPkg(url);\n }\n });\n } catch (err_) {\n error(err_ instanceof Error ? err_.message : String(err_));\n process.exit(1);\n }\n });\n"],"mappings":"AAAA,SAASA,OAAO,QAAQ,WAAW;AACnC,OAAO,KAAKC,IAAI,MAAM,MAAM;AAC5B,OAAO,KAAKC,EAAE,MAAM,IAAI;AACxB,OAAO,KAAKC,IAAI,MAAM,MAAM;AAC5B,SAASC,KAAK,QAAQ,eAAe;AACrC,SAASC,OAAO,EAAEC,KAAK,EAAEC,IAAI,EAAEC,UAAU,QAAQ,gBAAgB;AACjE,SAASC,eAAe,QAAQ,kBAAkB;AAClD,SAASC,SAAS,QAAQ,UAAU;AAEpC,MAAMC,YAAY,GAAG,IAAI;AAEzB,MAAMC,UAAkC,GAAG;EACzC,KAAK,EAAE,wBAAwB;EAC/B,MAAM,EAAE,UAAU;EAClB,OAAO,EAAE,kBAAkB;EAC3B,MAAM,EAAE;AACV,CAAC;AAED,SAASC,WAAWA,CAClBC,OAAe,EACfC,UAAkB,EACL;EACb,MAAMC,MAAM,GAAGb,IAAI,CAACc,YAAY,CAAC,CAACC,GAAG,EAAEC,GAAG,KAAK;IAC7CA,GAAG,CAACC,SAAS,CAAC,6BAA6B,EAAEL,UAAU,CAAC;IACxDI,GAAG,CAACC,SAAS,CAAC,8BAA8B,EAAE,cAAc,CAAC;IAC7DD,GAAG,CAACC,SAAS,CAAC,8BAA8B,EAAE,GAAG,CAAC;IAElD,IAAIF,GAAG,CAACG,MAAM,KAAK,SAAS,EAAE;MAC5BF,GAAG,CAACG,SAAS,CAAC,GAAG,CAAC;MAClBH,GAAG,CAACI,GAAG,CAAC,CAAC;MACT;IACF;IAEA,MAAMC,OAAO,GAAGN,GAAG,CAACO,GAAG,KAAK,GAAG,GAAG,EAAE,GAAGP,GAAG,CAACO,GAAG,IAAI,EAAE;IACpD,MAAMC,QAAQ,GAAGzB,IAAI,CAAC0B,IAAI,CAACb,OAAO,EAAEU,OAAO,CAAC;;IAE5C;IACA,IAAI,CAACE,QAAQ,CAACE,UAAU,CAACd,OAAO,CAAC,EAAE;MACjCK,GAAG,CAACG,SAAS,CAAC,GAAG,CAAC;MAClBH,GAAG,CAACI,GAAG,CAAC,WAAW,CAAC;MACpB;IACF;IAEA,IAAI,CAACrB,EAAE,CAAC2B,UAAU,CAACH,QAAQ,CAAC,IAAIxB,EAAE,CAAC4B,QAAQ,CAACJ,QAAQ,CAAC,CAACK,WAAW,CAAC,CAAC,EAAE;MACnEZ,GAAG,CAACG,SAAS,CAAC,GAAG,CAAC;MAClBH,GAAG,CAACI,GAAG,CAAC,WAAW,CAAC;MACpB;IACF;IAEA,MAAMS,GAAG,GAAG/B,IAAI,CAACgC,OAAO,CAACP,QAAQ,CAAC;IAClC,MAAMQ,WAAW,GAAGtB,UAAU,CAACoB,GAAG,CAAC,IAAI,0BAA0B;IACjEb,GAAG,CAACC,SAAS,CAAC,cAAc,EAAEc,WAAW,CAAC;IAC1Cf,GAAG,CAACG,SAAS,CAAC,GAAG,CAAC;IAClBpB,EAAE,CAACiC,gBAAgB,CAACT,QAAQ,CAAC,CAACU,IAAI,CAACjB,GAAG,CAAC;EACzC,CAAC,CAAC;EAEF,OAAOH,MAAM;AACf;AAEA,OAAO,MAAMqB,YAAY,GAAG,IAAIrC,OAAO,CAAC,OAAO,CAAC,CAC7CsC,WAAW,CAAC,+EAA+E,CAAC,CAC5FC,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAEC,MAAM,CAAC7B,YAAY,CAAC,CAAC,CAC/D4B,MAAM,CAAC,aAAa,EAAE,6CAA6C,CAAC,CACpEA,MAAM,CAAC,SAAS,EAAE,oEAAoE,CAAC,CACvFA,MAAM,CAAC,QAAQ,EAAE,iDAAiD,CAAC,CACnEA,MAAM,CAAC,wBAAwB,EAAE,mCAAmC,EAAE,GAAG,CAAC,CAC1EE,MAAM,CAAC,MAAOC,IAMd,IAAK;EACJ,IAAI;IACF,MAAMC,WAAW,GAAGlC,eAAe,CAACmC,OAAO,CAACC,GAAG,CAAC,CAAC,CAAC;IAClD,IAAI,CAACF,WAAW,EAAE;MAChBrC,KAAK,CACH,yEACF,CAAC;MACDsC,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;IACjB;IAEA,MAAMC,IAAI,GAAGC,QAAQ,CAACN,IAAI,CAACK,IAAI,EAAE,EAAE,CAAC;IACpC,IAAIE,KAAK,CAACF,IAAI,CAAC,IAAIA,IAAI,GAAG,CAAC,IAAIA,IAAI,GAAG,KAAK,EAAE;MAC3CzC,KAAK,CAAC,iBAAiBoC,IAAI,CAACK,IAAI,EAAE,CAAC;MACnCH,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;IACjB;IAEA,MAAMhC,OAAO,GAAGb,IAAI,CAAC0B,IAAI,CAACgB,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC;;IAErD;IACA,IAAID,IAAI,CAACQ,MAAM,KAAK,KAAK,EAAE;MACzB,MAAMxC,SAAS,CAACiC,WAAW,CAAC;IAC9B;IAEA,IAAI,CAACzC,EAAE,CAAC2B,UAAU,CAACf,OAAO,CAAC,EAAE;MAC3BR,KAAK,CACH,8BAA8BQ,OAAO,oDACvC,CAAC;MACD8B,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;IACjB;;IAEA;IACA,IAAIJ,IAAI,CAACS,KAAK,EAAE;MACd,MAAMC,OAAO,GAAGnD,IAAI,CAAC0B,IAAI,CAACgB,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC;MACtE,IAAI,CAACzC,EAAE,CAAC2B,UAAU,CAACuB,OAAO,CAAC,EAAE;QAC3B9C,KAAK,CAAC,0DAA0D,CAAC;QACjEsC,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;MACjB;MACA,MAAMO,OAAO,GAAGjD,KAAK,CACnB,IAAIgD,OAAO,GAAG,EACd,CAAC,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,SAAS,CAAC,EAC1D;QAAEP,GAAG,EAAEF,WAAW;QAAEW,KAAK,EAAE,IAAI;QAAEC,KAAK,EAAE;MAAU,CACpD,CAAC;MACDF,OAAO,CAACG,EAAE,CAAC,OAAO,EAAGC,IAAI,IAAKnD,KAAK,CAAC,wBAAwBmD,IAAI,CAACC,OAAO,EAAE,CAAC,CAAC;IAC9E;;IAEA;IACA,MAAM1C,MAAM,GAAGH,WAAW,CAACC,OAAO,EAAE4B,IAAI,CAAC3B,UAAU,CAAC;IACpD,MAAMU,GAAG,GAAG,oBAAoBsB,IAAI,EAAE;IAEtC/B,MAAM,CAAC2C,MAAM,CAACZ,IAAI,EAAE,YAAY;MAC9B,MAAMa,KAAK,GAAG1D,EAAE,CAAC2D,WAAW,CAAC/C,OAAO,CAAC;MAErC,IAAIN,UAAU,CAAC,CAAC,EAAE;QAChBsD,OAAO,CAACC,GAAG,CAACC,IAAI,CAACC,SAAS,CAAC;UAAExC,GAAG;UAAEsB,IAAI;UAAEa,KAAK,EAAEA,KAAK,CAACM,GAAG,CAAEC,CAAC,IAAK,GAAG1C,GAAG,IAAI0C,CAAC,EAAE;QAAE,CAAC,CAAC,CAAC;MACpF,CAAC,MAAM;QACL9D,OAAO,CAAC,cAAcoB,GAAG,EAAE,CAAC;QAC5B,KAAK,MAAM2C,IAAI,IAAIR,KAAK,EAAE;UACxBrD,IAAI,CAAC,KAAKkB,GAAG,IAAI2C,IAAI,EAAE,CAAC;QAC1B;QACA,IAAI1B,IAAI,CAACS,KAAK,EAAE;UACd5C,IAAI,CAAC,6DAA6D,CAAC;QACrE;QACAA,IAAI,CAAC,uBAAuB,CAAC;MAC/B;MAEA,IAAImC,IAAI,CAAC2B,IAAI,EAAE;QACb,MAAMC,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,EAAEC,OAAO;QAC9C,MAAMD,OAAO,CAAC7C,GAAG,CAAC;MACpB;IACF,CAAC,CAAC;EACJ,CAAC,CAAC,OAAOgC,IAAI,EAAE;IACbnD,KAAK,CAACmD,IAAI,YAAYe,KAAK,GAAGf,IAAI,CAACC,OAAO,GAAGlB,MAAM,CAACiB,IAAI,CAAC,CAAC;IAC1Db,OAAO,CAACE,IAAI,CAAC,CAAC,CAAC;EACjB;AACF,CAAC,CAAC","ignoreList":[]}
@@ -0,0 +1,97 @@
1
+ import { Command } from 'commander';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { cp, readFile, writeFile } from 'fs/promises';
5
+ import { execSync } from 'child_process';
6
+ import { success, error, info, warn } from '../utils/print';
7
+ import { findProjectRoot } from '../utils/project';
8
+ const DEFAULTS_DIR = path.join(__dirname, '../../../defaults');
9
+ const STORYBOOK_DEPS = {
10
+ storybook: '^8.0.0',
11
+ '@storybook/react-vite': '^8.0.0',
12
+ '@storybook/addon-essentials': '^8.0.0'
13
+ };
14
+ const STORYBOOK_SCRIPTS = {
15
+ storybook: 'storybook dev -p 6006',
16
+ 'build-storybook': 'storybook build'
17
+ };
18
+ export const storybookCommand = new Command('storybook').description('Add Storybook configuration and stories to a Web5 project').action(async () => {
19
+ try {
20
+ const projectRoot = findProjectRoot(process.cwd());
21
+ if (!projectRoot) {
22
+ error('No web5.config.json found. Run this command from within a Web5 project.');
23
+ process.exit(1);
24
+ }
25
+ info(`Adding Storybook to ${projectRoot}`);
26
+
27
+ // ── Copy .storybook/ config (overwrites on re-run — intentional) ──────
28
+ const storybookConfigSrc = path.join(DEFAULTS_DIR, '.storybook');
29
+ const storybookConfigDest = path.join(projectRoot, '.storybook');
30
+ await cp(storybookConfigSrc, storybookConfigDest, {
31
+ recursive: true
32
+ });
33
+
34
+ // ── Copy stories folder (overwrites on re-run — intentional) ──────────
35
+ const storiesSrc = path.join(DEFAULTS_DIR, 'src', 'components', 'storybook');
36
+ const storiesDest = path.join(projectRoot, 'src', 'components', 'storybook');
37
+ await cp(storiesSrc, storiesDest, {
38
+ recursive: true
39
+ });
40
+
41
+ // ── Update package.json (idempotent) ───────────────────────────────────
42
+ const pkgPath = path.join(projectRoot, 'package.json');
43
+ if (!fs.existsSync(pkgPath)) {
44
+ error('No package.json found in project root.');
45
+ process.exit(1);
46
+ }
47
+ const pkg = JSON.parse(await readFile(pkgPath, 'utf8'));
48
+ let pkgChanged = false;
49
+
50
+ // Scripts
51
+ pkg.scripts ??= {};
52
+ for (const [name, cmd] of Object.entries(STORYBOOK_SCRIPTS)) {
53
+ if (!pkg.scripts[name]) {
54
+ pkg.scripts[name] = cmd;
55
+ pkgChanged = true;
56
+ } else {
57
+ warn(`Script "${name}" already exists — skipping`);
58
+ }
59
+ }
60
+
61
+ // devDependencies
62
+ pkg.devDependencies ??= {};
63
+ const missingDeps = [];
64
+ for (const [dep, version] of Object.entries(STORYBOOK_DEPS)) {
65
+ if (!pkg.devDependencies[dep]) {
66
+ pkg.devDependencies[dep] = version;
67
+ missingDeps.push(dep);
68
+ pkgChanged = true;
69
+ }
70
+ }
71
+ if (pkgChanged) {
72
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
73
+ info('Updated package.json');
74
+ } else {
75
+ info('package.json already up to date');
76
+ }
77
+
78
+ // ── Install dependencies ───────────────────────────────────────────────
79
+ if (missingDeps.length > 0) {
80
+ info(`Installing: ${missingDeps.join(', ')}`);
81
+ } else {
82
+ info('Running npm install to ensure dependencies are installed...');
83
+ }
84
+ execSync('npm install', {
85
+ cwd: projectRoot,
86
+ stdio: 'inherit'
87
+ });
88
+ success('Storybook is ready.');
89
+ console.log('');
90
+ info('Run it with:');
91
+ info(' npm run storybook');
92
+ } catch (err_) {
93
+ error(err_ instanceof Error ? err_.message : String(err_));
94
+ process.exit(1);
95
+ }
96
+ });
97
+ //# sourceMappingURL=storybook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["Command","path","fs","cp","readFile","writeFile","execSync","success","error","info","warn","findProjectRoot","DEFAULTS_DIR","join","__dirname","STORYBOOK_DEPS","storybook","STORYBOOK_SCRIPTS","storybookCommand","description","action","projectRoot","process","cwd","exit","storybookConfigSrc","storybookConfigDest","recursive","storiesSrc","storiesDest","pkgPath","existsSync","pkg","JSON","parse","pkgChanged","scripts","name","cmd","Object","entries","devDependencies","missingDeps","dep","version","push","stringify","length","stdio","console","log","err_","Error","message","String"],"sources":["../../../src/commands/storybook.ts"],"sourcesContent":["import { Command } from 'commander';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { cp, readFile, writeFile } from 'fs/promises';\nimport { execSync } from 'child_process';\nimport { success, error, info, warn } from '../utils/print';\nimport { findProjectRoot } from '../utils/project';\n\nconst DEFAULTS_DIR = path.join(__dirname, '../../../defaults');\n\nconst STORYBOOK_DEPS = {\n storybook: '^8.0.0',\n '@storybook/react-vite': '^8.0.0',\n '@storybook/addon-essentials': '^8.0.0',\n};\n\nconst STORYBOOK_SCRIPTS = {\n storybook: 'storybook dev -p 6006',\n 'build-storybook': 'storybook build',\n};\n\nexport const storybookCommand = new Command('storybook')\n .description('Add Storybook configuration and stories to a Web5 project')\n .action(async () => {\n try {\n const projectRoot = findProjectRoot(process.cwd());\n if (!projectRoot) {\n error(\n 'No web5.config.json found. Run this command from within a Web5 project.',\n );\n process.exit(1);\n }\n\n info(`Adding Storybook to ${projectRoot}`);\n\n // ── Copy .storybook/ config (overwrites on re-run — intentional) ──────\n const storybookConfigSrc = path.join(DEFAULTS_DIR, '.storybook');\n const storybookConfigDest = path.join(projectRoot, '.storybook');\n await cp(storybookConfigSrc, storybookConfigDest, { recursive: true });\n\n // ── Copy stories folder (overwrites on re-run — intentional) ──────────\n const storiesSrc = path.join(\n DEFAULTS_DIR,\n 'src',\n 'components',\n 'storybook',\n );\n const storiesDest = path.join(\n projectRoot,\n 'src',\n 'components',\n 'storybook',\n );\n await cp(storiesSrc, storiesDest, { recursive: true });\n\n // ── Update package.json (idempotent) ───────────────────────────────────\n const pkgPath = path.join(projectRoot, 'package.json');\n if (!fs.existsSync(pkgPath)) {\n error('No package.json found in project root.');\n process.exit(1);\n }\n\n const pkg = JSON.parse(await readFile(pkgPath, 'utf8')) as {\n scripts?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n\n let pkgChanged = false;\n\n // Scripts\n pkg.scripts ??= {};\n for (const [name, cmd] of Object.entries(STORYBOOK_SCRIPTS)) {\n if (!pkg.scripts[name]) {\n pkg.scripts[name] = cmd;\n pkgChanged = true;\n } else {\n warn(`Script \"${name}\" already exists — skipping`);\n }\n }\n\n // devDependencies\n pkg.devDependencies ??= {};\n const missingDeps: string[] = [];\n for (const [dep, version] of Object.entries(STORYBOOK_DEPS)) {\n if (!pkg.devDependencies[dep]) {\n pkg.devDependencies[dep] = version;\n missingDeps.push(dep);\n pkgChanged = true;\n }\n }\n\n if (pkgChanged) {\n await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\\n', 'utf8');\n info('Updated package.json');\n } else {\n info('package.json already up to date');\n }\n\n // ── Install dependencies ───────────────────────────────────────────────\n if (missingDeps.length > 0) {\n info(`Installing: ${missingDeps.join(', ')}`);\n } else {\n info('Running npm install to ensure dependencies are installed...');\n }\n\n execSync('npm install', { cwd: projectRoot, stdio: 'inherit' });\n\n success('Storybook is ready.');\n console.log('');\n info('Run it with:');\n info(' npm run storybook');\n } catch (err_) {\n error(err_ instanceof Error ? err_.message : String(err_));\n process.exit(1);\n }\n });\n"],"mappings":"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","ignoreList":[]}
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import Ajv from 'ajv/dist/2020';
3
3
  import { load as yamlLoad } from 'js-yaml';
4
- import { readFileSync, existsSync, readdirSync } from 'fs';
4
+ import { readFileSync, existsSync, readdirSync, watch as fsWatch } from 'fs';
5
5
  import * as path from 'path';
6
6
  import chalk from 'chalk';
7
7
  import { error, info } from '../utils/print';
@@ -666,27 +666,21 @@ function buildActionsDomain(root) {
666
666
  };
667
667
  }
668
668
 
669
- // ── Command ───────────────────────────────────────────────────────────────────
669
+ // ── Rendering ─────────────────────────────────────────────────────────────────
670
670
 
671
- export const validateCommand = new Command('validate').description('Validate project config files against their schemas').action(() => {
672
- const root = findProjectRoot(process.cwd());
673
- if (!root) {
674
- error('No web5.config.json found — run this command from inside a Web5 project');
675
- process.exit(1);
676
- }
677
- info(`Project root: ${root}`);
678
- console.log('');
679
- const domains = [buildAiInstructionsDomain(root), buildCmsSchemaDomain(root), buildActionsDomain(root)];
680
- let hasErrors = false;
681
- for (const domain of domains) {
671
+ function printDomain(domain, results, verbose) {
672
+ const failed = results.filter(r => !r.result.ok);
673
+ const domainOk = failed.length === 0;
674
+ if (verbose) {
682
675
  console.log(domain.name);
683
- for (const check of domain.checks) {
684
- const result = check.run();
676
+ for (const {
677
+ label,
678
+ result
679
+ } of results) {
685
680
  if (result.ok) {
686
- console.log(chalk.green(` \u2714 ${check.label}`));
681
+ console.log(chalk.green(` \u2714 ${label}`));
687
682
  } else {
688
- hasErrors = true;
689
- process.stderr.write(chalk.red(` \u2716 ${check.label}\n`));
683
+ process.stderr.write(chalk.red(` \u2716 ${label}\n`));
690
684
  for (const e_ of result.errors) {
691
685
  process.stderr.write(` ${e_.message}\n`);
692
686
  if (e_.fix) {
@@ -696,9 +690,265 @@ export const validateCommand = new Command('validate').description('Validate pro
696
690
  }
697
691
  }
698
692
  console.log('');
693
+ return domainOk;
694
+ }
695
+
696
+ // Slim mode
697
+ if (domainOk) {
698
+ console.log(chalk.green(`\u2714 ${domain.name} — valid`));
699
+ return true;
700
+ }
701
+ console.log(domain.name);
702
+ for (const {
703
+ label,
704
+ result
705
+ } of failed) {
706
+ process.stderr.write(chalk.red(` \u2716 ${label}\n`));
707
+ for (const e_ of result.errors) {
708
+ process.stderr.write(` ${e_.message}\n`);
709
+ if (e_.fix) {
710
+ process.stderr.write(` Fix: ${e_.fix}\n`);
711
+ }
712
+ }
713
+ }
714
+ console.log('');
715
+ return false;
716
+ }
717
+
718
+ // ── JSON output ───────────────────────────────────────────────────────────────
719
+
720
+ function collectDomainJson(domain, results) {
721
+ return {
722
+ name: domain.name,
723
+ ok: results.every(r => r.result.ok),
724
+ checks: results.map(_ref => {
725
+ let {
726
+ label,
727
+ result
728
+ } = _ref;
729
+ return {
730
+ label,
731
+ ok: result.ok,
732
+ errors: result.errors
733
+ };
734
+ })
735
+ };
736
+ }
737
+
738
+ // ── Build scripts domain ──────────────────────────────────────────────────────
739
+
740
+ function buildScriptsDomain(root) {
741
+ const pkgPath = path.join(root, 'package.json');
742
+ let pkg = {};
743
+ let fileExists = false;
744
+ return {
745
+ name: 'Build Scripts',
746
+ checks: [{
747
+ label: 'package.json — file exists',
748
+ run() {
749
+ fileExists = existsSync(pkgPath);
750
+ if (!fileExists) {
751
+ return {
752
+ ok: false,
753
+ errors: [{
754
+ message: `File not found: ${pkgPath}`,
755
+ fix: `Create a 'package.json' at the root of your project.`
756
+ }]
757
+ };
758
+ }
759
+ try {
760
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
761
+ } catch (e) {
762
+ fileExists = false;
763
+ return {
764
+ ok: false,
765
+ errors: [{
766
+ message: `Failed to parse package.json: ${e instanceof Error ? e.message : String(e)}`,
767
+ fix: `Fix the JSON syntax in your package.json.`
768
+ }]
769
+ };
770
+ }
771
+ return {
772
+ ok: true,
773
+ errors: []
774
+ };
775
+ }
776
+ }, {
777
+ label: 'package.json — bundle script defined',
778
+ run() {
779
+ var _pkg$scripts;
780
+ if (!fileExists) {
781
+ return {
782
+ ok: false,
783
+ errors: [{
784
+ message: 'skipped — package.json not readable'
785
+ }]
786
+ };
787
+ }
788
+ if (typeof ((_pkg$scripts = pkg.scripts) == null ? void 0 : _pkg$scripts.bundle) === 'string' && pkg.scripts.bundle.length > 0) {
789
+ return {
790
+ ok: true,
791
+ errors: []
792
+ };
793
+ }
794
+ return {
795
+ ok: false,
796
+ errors: [{
797
+ message: `Missing 'bundle' script in package.json`,
798
+ fix: `Add a 'bundle' script to your package.json scripts, e.g.:\n "bundle": "vite build --config src/vite.cdn.config.ts"`
799
+ }]
800
+ };
801
+ }
802
+ }, {
803
+ label: 'package.json — build script defined',
804
+ run() {
805
+ var _pkg$scripts2;
806
+ if (!fileExists) {
807
+ return {
808
+ ok: false,
809
+ errors: [{
810
+ message: 'skipped — package.json not readable'
811
+ }]
812
+ };
813
+ }
814
+ if (typeof ((_pkg$scripts2 = pkg.scripts) == null ? void 0 : _pkg$scripts2.build) === 'string' && pkg.scripts.build.length > 0) {
815
+ return {
816
+ ok: true,
817
+ errors: []
818
+ };
819
+ }
820
+ return {
821
+ ok: false,
822
+ errors: [{
823
+ message: `Missing 'build' script in package.json`,
824
+ fix: `Add a 'build' script to your package.json scripts, e.g.:\n "build": "tsc"`
825
+ }]
826
+ };
827
+ }
828
+ }]
829
+ };
830
+ }
831
+
832
+ // ── Domain selection ──────────────────────────────────────────────────────────
833
+
834
+ function selectDomains(root, filter) {
835
+ const all = [buildAiInstructionsDomain(root), buildCmsSchemaDomain(root), buildActionsDomain(root), buildScriptsDomain(root)];
836
+ if (!filter) return all;
837
+ const map = {
838
+ ai: 'AI Instructions',
839
+ cms: 'CMS Schema',
840
+ scripts: 'Build Scripts',
841
+ actions: 'Actions'
842
+ };
843
+ return all.filter(d => d.name === map[filter]);
844
+ }
845
+
846
+ // ── Core runner ───────────────────────────────────────────────────────────────
847
+
848
+ function runDomains(domains, verbose, format) {
849
+ let hasErrors = false;
850
+ const jsonResults = [];
851
+ for (const domain of domains) {
852
+ const results = domain.checks.map(check => ({
853
+ label: check.label,
854
+ result: check.run()
855
+ }));
856
+ let domainOk;
857
+ if (format === 'json') {
858
+ const jr = collectDomainJson(domain, results);
859
+ jsonResults.push(jr);
860
+ domainOk = jr.ok;
861
+ } else {
862
+ domainOk = printDomain(domain, results, verbose);
863
+ }
864
+ if (!domainOk) hasErrors = true;
865
+ }
866
+ return {
867
+ ok: !hasErrors,
868
+ jsonResults
869
+ };
870
+ }
871
+
872
+ // ── Programmatic API ─────────────────────────────────────────────────────────
873
+
874
+ /**
875
+ * Runs all validation domains against the given project root.
876
+ * Prints results in slim mode and returns true if everything passes.
877
+ */
878
+ export function runValidation(root) {
879
+ const domains = selectDomains(root);
880
+ const {
881
+ ok
882
+ } = runDomains(domains, false, 'text');
883
+ return ok;
884
+ }
885
+
886
+ // ── Watch mode ────────────────────────────────────────────────────────────────
887
+
888
+ function startWatch(root, run) {
889
+ const watchDirs = [path.join(root, 'src', 'configuration'), path.join(root, 'src', 'actions')].filter(d => existsSync(d));
890
+ if (watchDirs.length === 0) {
891
+ error('No directories to watch found.');
892
+ return;
699
893
  }
700
- if (hasErrors) {
894
+ let debounce = null;
895
+ const trigger = () => {
896
+ if (debounce) clearTimeout(debounce);
897
+ debounce = setTimeout(() => {
898
+ console.clear();
899
+ info(`[${new Date().toLocaleTimeString()}] Re-validating...`);
900
+ console.log('');
901
+ run();
902
+ }, 200);
903
+ };
904
+ for (const dir of watchDirs) {
905
+ fsWatch(dir, {
906
+ recursive: true
907
+ }, trigger);
908
+ }
909
+ info(`Watching for changes in ${watchDirs.map(d => path.relative(root, d)).join(', ')}...`);
910
+ info('Press Ctrl+C to stop.');
911
+ console.log('');
912
+ }
913
+
914
+ // ── Command ───────────────────────────────────────────────────────────────────
915
+
916
+ export const validateCommand = new Command('validate').description('Validate project config files against their schemas').option('--verbose', 'Show all checks, including passing ones').option('--domain <domain>', 'Only validate one domain: ai, cms, actions, or scripts').option('--format <format>', 'Output format: text (default) or json', 'text').option('--watch', 'Re-validate on file changes (TTY only)').option('--strict', 'Exit with error even on warnings').option('--project <path>', 'Explicit project root (overrides cwd auto-detection)').action(opts => {
917
+ const verbose = Boolean(opts.verbose);
918
+ const format = opts.format ?? 'text';
919
+ const domainFilter = opts.domain;
920
+ const root = opts.project ? path.resolve(opts.project) : findProjectRoot(process.cwd());
921
+ if (!root) {
922
+ error('No web5.config.json found — run this command from inside a Web5 project');
701
923
  process.exit(1);
702
924
  }
925
+ if (format === 'text') {
926
+ info(`Project root: ${root}`);
927
+ console.log('');
928
+ }
929
+ const run = () => {
930
+ const domains = selectDomains(root, domainFilter);
931
+ const {
932
+ ok,
933
+ jsonResults
934
+ } = runDomains(domains, verbose, format);
935
+ if (format === 'json') {
936
+ console.log(JSON.stringify({
937
+ ok,
938
+ domains: jsonResults
939
+ }, null, 2));
940
+ }
941
+ if (!ok && !opts.watch) {
942
+ process.exit(1);
943
+ }
944
+ };
945
+ run();
946
+ if (opts.watch) {
947
+ if (!process.stdout.isTTY) {
948
+ error('--watch requires a TTY (interactive terminal).');
949
+ process.exit(1);
950
+ }
951
+ startWatch(root, run);
952
+ }
703
953
  });
704
954
  //# sourceMappingURL=validate.js.map