handoff-app 1.2.0-0 → 1.2.0-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 (50) hide show
  1. package/dist/app-builder/build.js +24 -10
  2. package/dist/app-builder/config-diff/index.d.ts +1 -1
  3. package/dist/app-builder/config-diff/registry.d.ts +2 -2
  4. package/dist/app-builder/config-diff/registry.js +2 -2
  5. package/dist/app-builder/config-diff/strategies/pattern.js +14 -1
  6. package/dist/app-builder/config-diff/types.d.ts +15 -1
  7. package/dist/app-builder/watchers.js +35 -3
  8. package/dist/app-builder/websocket.js +10 -4
  9. package/dist/cli.js +1 -1
  10. package/dist/config/normalizers/pattern.d.ts +0 -1
  11. package/dist/config/normalizers/pattern.js +0 -3
  12. package/dist/config/runtime.js +10 -4
  13. package/dist/pipeline/patterns.d.ts +3 -1
  14. package/dist/pipeline/patterns.js +2 -2
  15. package/dist/transformers/preview/component/builder.js +52 -5
  16. package/dist/transformers/preview/component/css.js +6 -4
  17. package/dist/transformers/preview/component/javascript.d.ts +0 -1
  18. package/dist/transformers/preview/component/javascript.js +25 -14
  19. package/dist/transformers/preview/pattern/api.d.ts +4 -0
  20. package/dist/transformers/preview/pattern/api.js +19 -1
  21. package/dist/transformers/preview/pattern/builder.d.ts +9 -1
  22. package/dist/transformers/preview/pattern/builder.js +123 -42
  23. package/dist/transformers/utils/vite-logger.js +4 -4
  24. package/dist/transformers/vite-config.js +2 -0
  25. package/dist/utils/duration.d.ts +2 -0
  26. package/dist/utils/duration.js +6 -0
  27. package/dist/utils/logger.d.ts +42 -5
  28. package/dist/utils/logger.js +135 -15
  29. package/package.json +1 -1
  30. package/src/app/pages/system/pattern/[pattern]/index.tsx +178 -55
  31. package/src/app-builder/build.ts +26 -10
  32. package/src/app-builder/config-diff/index.ts +1 -1
  33. package/src/app-builder/config-diff/registry.ts +3 -3
  34. package/src/app-builder/config-diff/strategies/pattern.ts +17 -2
  35. package/src/app-builder/config-diff/types.ts +16 -1
  36. package/src/app-builder/watchers.ts +15 -3
  37. package/src/app-builder/websocket.ts +16 -5
  38. package/src/cli.ts +1 -1
  39. package/src/config/normalizers/pattern.ts +0 -7
  40. package/src/config/runtime.ts +11 -4
  41. package/src/pipeline/patterns.ts +5 -3
  42. package/src/transformers/preview/component/builder.ts +61 -5
  43. package/src/transformers/preview/component/css.ts +6 -5
  44. package/src/transformers/preview/component/javascript.ts +29 -19
  45. package/src/transformers/preview/pattern/api.ts +17 -0
  46. package/src/transformers/preview/pattern/builder.ts +156 -58
  47. package/src/transformers/utils/vite-logger.ts +4 -4
  48. package/src/transformers/vite-config.ts +2 -0
  49. package/src/utils/duration.ts +3 -0
  50. package/src/utils/logger.ts +143 -16
@@ -12,75 +12,156 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.getPatternIdsReferencingComponents = void 0;
15
16
  exports.processPatterns = processPatterns;
16
17
  const fs_extra_1 = __importDefault(require("fs-extra"));
17
18
  const path_1 = __importDefault(require("path"));
18
19
  const logger_1 = require("../../../utils/logger");
19
20
  const api_1 = require("./api");
20
21
  const html_1 = require("./html");
22
+ /**
23
+ * Returns pattern ids whose declarations reference any of the given component ids.
24
+ */
25
+ const getPatternIdsReferencingComponents = (handoff, componentIds) => {
26
+ var _a, _b, _c, _d;
27
+ if (componentIds.length === 0) {
28
+ return [];
29
+ }
30
+ const idSet = new Set(componentIds);
31
+ const runtimePatterns = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) !== null && _c !== void 0 ? _c : {};
32
+ const result = [];
33
+ for (const patternId of Object.keys(runtimePatterns)) {
34
+ const pattern = runtimePatterns[patternId];
35
+ if ((_d = pattern.components) === null || _d === void 0 ? void 0 : _d.some((ref) => idSet.has(ref.id))) {
36
+ result.push(patternId);
37
+ }
38
+ }
39
+ return result;
40
+ };
41
+ exports.getPatternIdsReferencingComponents = getPatternIdsReferencingComponents;
42
+ function buildPattern(handoff, patternId, pattern, componentOutputDir, basePath) {
43
+ return __awaiter(this, void 0, void 0, function* () {
44
+ const fragments = [];
45
+ let hasErrors = false;
46
+ for (let i = 0; i < pattern.components.length; i++) {
47
+ const ref = pattern.components[i];
48
+ if (ref.resolved === false && !ref.resolvedPreview) {
49
+ hasErrors = true;
50
+ continue;
51
+ }
52
+ const previewKey = ref.resolvedPreview || ref.preview;
53
+ if (!previewKey) {
54
+ const error = `Pattern "${patternId}" component[${i}] ("${ref.id}") has no resolved preview key. Skipping.`;
55
+ logger_1.Logger.warn(error);
56
+ ref.resolved = false;
57
+ hasErrors = true;
58
+ continue;
59
+ }
60
+ const htmlFileName = `${ref.id}-${previewKey}.html`;
61
+ const htmlFilePath = path_1.default.resolve(componentOutputDir, htmlFileName);
62
+ if (!fs_extra_1.default.existsSync(htmlFilePath)) {
63
+ const error = `Pattern "${patternId}" component[${i}] ("${ref.id}") preview file not found: ${htmlFileName}. ` +
64
+ `Ensure the component has a preview named "${previewKey}".`;
65
+ logger_1.Logger.warn(error);
66
+ ref.resolved = false;
67
+ hasErrors = true;
68
+ continue;
69
+ }
70
+ ref.resolved = true;
71
+ const html = yield fs_extra_1.default.readFile(htmlFilePath, 'utf8');
72
+ fragments.push({ componentId: ref.id, html });
73
+ }
74
+ if (fragments.length === 0) {
75
+ logger_1.Logger.warn(`Pattern "${patternId}" produced no fragments. Skipping.`);
76
+ return null;
77
+ }
78
+ if (hasErrors) {
79
+ logger_1.Logger.warn(`Pattern "${patternId}" has missing components but will be composed from available fragments.`);
80
+ }
81
+ const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath);
82
+ const patternUrl = `${patternId}.html`;
83
+ const patternData = Object.assign(Object.assign({}, pattern), { id: patternId, url: patternUrl, path: `${basePath}/api/pattern/${patternId}.json` });
84
+ yield (0, api_1.writePatternHtml)(patternId, composedHtml, handoff);
85
+ yield (0, api_1.writePatternApi)(patternId, patternData, handoff);
86
+ logger_1.Logger.debug(`Built pattern: ${patternId} (${fragments.length} component(s))`);
87
+ return patternData;
88
+ });
89
+ }
21
90
  /**
22
91
  * Process all pattern declarations and compose their HTML from pre-built
23
92
  * component preview files. This function must run AFTER buildComponents
24
93
  * so that all component preview HTML files already exist on disk.
25
94
  */
26
- function processPatterns(handoff) {
95
+ function processPatterns(handoff, options) {
27
96
  return __awaiter(this, void 0, void 0, function* () {
28
97
  var _a, _b, _c, _d;
29
98
  const runtimePatterns = (_c = (_b = (_a = handoff.runtimeConfig) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.patterns) !== null && _c !== void 0 ? _c : {};
30
99
  const patternIds = Object.keys(runtimePatterns);
31
- const result = [];
100
+ const onlyPatternIds = options === null || options === void 0 ? void 0 : options.onlyPatternIds;
101
+ const isPartial = onlyPatternIds !== undefined;
32
102
  if (patternIds.length === 0) {
33
103
  yield (0, api_1.syncPatternArtifacts)(handoff, []);
34
- yield (0, api_1.writePatternSummaryApi)(handoff, result);
35
- return result;
104
+ yield (0, api_1.writePatternSummaryApi)(handoff, []);
105
+ return [];
36
106
  }
37
- logger_1.Logger.info(`Building ${patternIds.length} pattern(s)...`);
38
107
  const componentOutputDir = path_1.default.resolve(handoff.workingPath, 'public/api/component');
39
108
  const basePath = (_d = process.env.HANDOFF_APP_BASE_PATH) !== null && _d !== void 0 ? _d : '';
109
+ if (!isPartial) {
110
+ logger_1.Logger.info(`Building ${patternIds.length} pattern(s)...`);
111
+ const result = [];
112
+ for (const patternId of patternIds) {
113
+ const pattern = runtimePatterns[patternId];
114
+ const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
115
+ if (built) {
116
+ result.push(built);
117
+ }
118
+ }
119
+ yield (0, api_1.syncPatternArtifacts)(handoff, result.map((p) => p.id));
120
+ yield (0, api_1.writePatternSummaryApi)(handoff, result);
121
+ logger_1.Logger.info(`Finished building ${result.length} pattern(s).`);
122
+ return result;
123
+ }
124
+ if (onlyPatternIds.size === 0) {
125
+ const existing = yield (0, api_1.readPatternSummaryApi)(handoff);
126
+ return existing !== null && existing !== void 0 ? existing : [];
127
+ }
128
+ const existingList = yield (0, api_1.readPatternSummaryApi)(handoff);
129
+ if (!existingList) {
130
+ logger_1.Logger.warn('Partial pattern rebuild requested but patterns.json is missing; rebuilding all patterns.');
131
+ return processPatterns(handoff);
132
+ }
133
+ const existingById = new Map(existingList.map((p) => [p.id, p]));
134
+ logger_1.Logger.info(`Rebuilding ${onlyPatternIds.size} of ${patternIds.length} pattern(s) (incremental)...`);
40
135
  for (const patternId of patternIds) {
41
- const pattern = runtimePatterns[patternId];
42
- const fragments = [];
43
- let hasErrors = false;
44
- for (let i = 0; i < pattern.components.length; i++) {
45
- const ref = pattern.components[i];
46
- if (ref.resolved === false && !ref.resolvedPreview) {
47
- hasErrors = true;
48
- continue;
136
+ if (onlyPatternIds.has(patternId)) {
137
+ const pattern = runtimePatterns[patternId];
138
+ const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
139
+ if (built) {
140
+ existingById.set(patternId, built);
49
141
  }
50
- const previewKey = ref.resolvedPreview || ref.preview || 'default';
51
- const htmlFileName = `${ref.id}-${previewKey}.html`;
52
- const htmlFilePath = path_1.default.resolve(componentOutputDir, htmlFileName);
53
- if (!fs_extra_1.default.existsSync(htmlFilePath)) {
54
- const error = `Pattern "${patternId}" component[${i}] ("${ref.id}") preview file not found: ${htmlFileName}. ` +
55
- `Ensure the component has a preview named "${previewKey}".`;
56
- logger_1.Logger.warn(error);
57
- ref.resolved = false;
58
- hasErrors = true;
59
- continue;
142
+ else {
143
+ existingById.delete(patternId);
60
144
  }
61
- ref.resolved = true;
62
- const html = yield fs_extra_1.default.readFile(htmlFilePath, 'utf8');
63
- fragments.push({ componentId: ref.id, html });
64
145
  }
65
- if (fragments.length === 0) {
66
- logger_1.Logger.warn(`Pattern "${patternId}" produced no fragments. Skipping.`);
67
- continue;
146
+ else if (!existingById.has(patternId)) {
147
+ const pattern = runtimePatterns[patternId];
148
+ const built = yield buildPattern(handoff, patternId, pattern, componentOutputDir, basePath);
149
+ if (built) {
150
+ existingById.set(patternId, built);
151
+ }
68
152
  }
69
- if (hasErrors) {
70
- logger_1.Logger.warn(`Pattern "${patternId}" has missing components but will be composed from available fragments.`);
153
+ }
154
+ const merged = [];
155
+ for (const patternId of patternIds) {
156
+ const entry = existingById.get(patternId);
157
+ if (entry) {
158
+ merged.push(entry);
71
159
  }
72
- const composedHtml = (0, html_1.composePatternHtml)(patternId, pattern.title, fragments, basePath);
73
- const patternUrl = `${patternId}.html`;
74
- const patternData = Object.assign(Object.assign({}, pattern), { url: patternUrl, path: `${basePath}/api/pattern/${patternId}.json` });
75
- yield (0, api_1.writePatternHtml)(patternId, composedHtml, handoff);
76
- yield (0, api_1.writePatternApi)(patternId, patternData, handoff);
77
- result.push(patternData);
78
- logger_1.Logger.debug(`Built pattern: ${patternId} (${fragments.length} component(s))`);
79
160
  }
80
- yield (0, api_1.syncPatternArtifacts)(handoff, result.map((pattern) => pattern.id));
81
- yield (0, api_1.writePatternSummaryApi)(handoff, result);
82
- logger_1.Logger.info(`Finished building ${result.length} pattern(s).`);
83
- return result;
161
+ yield (0, api_1.syncPatternArtifacts)(handoff, merged.map((p) => p.id));
162
+ yield (0, api_1.writePatternSummaryApi)(handoff, merged);
163
+ logger_1.Logger.info(`Finished incremental pattern build (${onlyPatternIds.size} recomposed).`);
164
+ return merged;
84
165
  });
85
166
  }
86
167
  exports.default = processPatterns;
@@ -7,21 +7,21 @@ const createViteLogger = () => {
7
7
  return {
8
8
  hasWarned: false,
9
9
  info(msg, options) {
10
- logger_1.Logger.info(msg);
10
+ logger_1.Logger.info(msg, 'vite');
11
11
  },
12
12
  warn(msg, options) {
13
13
  this.hasWarned = true;
14
- logger_1.Logger.warn(msg);
14
+ logger_1.Logger.warn(msg, 'vite');
15
15
  },
16
16
  warnOnce(msg, options) {
17
17
  if (warnedMessages.has(msg))
18
18
  return;
19
19
  warnedMessages.add(msg);
20
20
  this.hasWarned = true;
21
- logger_1.Logger.warn(msg);
21
+ logger_1.Logger.warn(msg, 'vite');
22
22
  },
23
23
  error(msg, options) {
24
- logger_1.Logger.error(msg);
24
+ logger_1.Logger.error(msg, undefined, 'vite');
25
25
  },
26
26
  clearScreen(type) {
27
27
  // No-op to preserve terminal history
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  // vite.config.ts
4
4
  const vite_1 = require("vite");
5
+ const vite_logger_1 = require("./utils/vite-logger");
5
6
  exports.default = (0, vite_1.defineConfig)({
7
+ customLogger: (0, vite_logger_1.createViteLogger)(),
6
8
  publicDir: false,
7
9
  build: {
8
10
  lib: {
@@ -0,0 +1,2 @@
1
+ /** Human-readable duration for CLI build logs (ms under 1s, seconds with 2 decimals otherwise). */
2
+ export declare const formatDurationMs: (elapsedMs: number) => string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDurationMs = void 0;
4
+ /** Human-readable duration for CLI build logs (ms under 1s, seconds with 2 decimals otherwise). */
5
+ const formatDurationMs = (elapsedMs) => elapsedMs < 1000 ? `${Math.round(elapsedMs)}ms` : `${(elapsedMs / 1000).toFixed(2)}s`;
6
+ exports.formatDurationMs = formatDurationMs;
@@ -1,13 +1,50 @@
1
+ /** Logical source of a log line — used for a fixed-width–style colored tag after the timestamp. */
2
+ export type LogScope = 'handoff' | 'vite' | 'next';
3
+ /**
4
+ * Env (e.g. project `.env` via `dotenv/config`):
5
+ * - `HANDOFF_LOG_LEVEL` — `debug` | `info` | `warn` | `error` | `silent` (default: `info`).
6
+ * Controls minimum severity: at `warn`, info/success/debug lines are hidden; at `error`, only errors.
7
+ * - `HANDOFF_LOG_SCOPES` — comma-separated allowlist: `handoff`, `vite`, `next`. Defaults to all three if unset or empty.
8
+ *
9
+ * Programmatic `Logger.init({ debug: true })` (Handoff `debug` flag) forces debug-level verbosity unless overridden.
10
+ */
1
11
  export declare class Logger {
12
+ /** From Handoff constructor; enables debug lines together with env. */
2
13
  private static debugMode;
14
+ /** Minimum message severity to print: 0=debug, 1=info, 2=warn, 3=error. `silent` uses 99. */
15
+ private static severityThreshold;
16
+ /** Enabled scopes; defaults to all of `handoff`, `vite`, `next`. */
17
+ private static scopesFilter;
3
18
  static init(options?: {
4
19
  debug?: boolean;
5
20
  }): void;
21
+ /** Re-read `process.env` (useful if env is loaded after first `init`). */
22
+ static applyEnvConfig(): void;
23
+ private static scopeEnabled;
24
+ /** Message severities: debug=0, info-tier=1, warn=2, error=3 */
25
+ private static shouldEmit;
6
26
  private static getTimestamp;
7
- static log(message: string): void;
8
- static info(message: string): void;
9
- static success(message: string): void;
10
- static warn(message: string): void;
11
- static error(message: string, error?: any): void;
27
+ /** Short label shown after the timestamp so you can scan for handoff vs vite vs next. */
28
+ private static scopeTag;
29
+ private static basePrefix;
30
+ static log(message: string, scope?: LogScope): void;
31
+ /**
32
+ * Vite often passes strings that already contain ANSI; avoid wrapping those in chalk.cyan so colors stay correct.
33
+ */
34
+ static info(message: string, scope?: LogScope): void;
35
+ static success(message: string, scope?: LogScope): void;
36
+ static warn(message: string, scope?: LogScope): void;
37
+ static error(message: string, error?: any, scope?: LogScope): void;
12
38
  static debug(message: string, data?: any): void;
39
+ /**
40
+ * Log one line from a child process with timestamp + scope tag. Child ANSI is preserved (no extra chalk on the line).
41
+ */
42
+ static childProcessLine(line: string, scope?: LogScope): void;
43
+ /**
44
+ * Pipe stdout/stderr from a spawned child through {@link childProcessLine}. Use with
45
+ * `stdio: ['inherit' | 'ignore', 'pipe', 'pipe']`.
46
+ */
47
+ static pipeChildStreams(stdout: NodeJS.ReadableStream | null, stderr: NodeJS.ReadableStream | null, scope?: LogScope): void;
48
+ /** Forward a completed child-process buffer (e.g. from `spawn.sync` with stdio pipe) line-by-line. */
49
+ static childProcessBuffer(buffer: Buffer | null | undefined, scope?: LogScope): void;
13
50
  }
@@ -4,43 +4,163 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Logger = void 0;
7
+ const readline_1 = __importDefault(require("readline"));
7
8
  const chalk_1 = __importDefault(require("chalk"));
9
+ /**
10
+ * Env (e.g. project `.env` via `dotenv/config`):
11
+ * - `HANDOFF_LOG_LEVEL` — `debug` | `info` | `warn` | `error` | `silent` (default: `info`).
12
+ * Controls minimum severity: at `warn`, info/success/debug lines are hidden; at `error`, only errors.
13
+ * - `HANDOFF_LOG_SCOPES` — comma-separated allowlist: `handoff`, `vite`, `next`. Defaults to all three if unset or empty.
14
+ *
15
+ * Programmatic `Logger.init({ debug: true })` (Handoff `debug` flag) forces debug-level verbosity unless overridden.
16
+ */
8
17
  class Logger {
9
18
  static init(options) {
19
+ this.applyEnvConfig();
10
20
  if ((options === null || options === void 0 ? void 0 : options.debug) !== undefined) {
11
21
  this.debugMode = options.debug;
12
22
  }
23
+ if ((options === null || options === void 0 ? void 0 : options.debug) === true) {
24
+ this.severityThreshold = 0;
25
+ }
26
+ }
27
+ /** Re-read `process.env` (useful if env is loaded after first `init`). */
28
+ static applyEnvConfig() {
29
+ var _a, _b;
30
+ this.severityThreshold = 1;
31
+ const allScopes = ['handoff', 'vite', 'next'];
32
+ this.scopesFilter = new Set(allScopes);
33
+ const levelRaw = (_a = process.env.HANDOFF_LOG_LEVEL) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase();
34
+ const levelMap = {
35
+ debug: 0,
36
+ info: 1,
37
+ warn: 2,
38
+ error: 3,
39
+ silent: 99,
40
+ };
41
+ if (levelRaw && levelRaw in levelMap) {
42
+ this.severityThreshold = levelMap[levelRaw];
43
+ }
44
+ const scopesRaw = (_b = process.env.HANDOFF_LOG_SCOPES) === null || _b === void 0 ? void 0 : _b.trim();
45
+ if (scopesRaw) {
46
+ const set = new Set();
47
+ for (const part of scopesRaw.split(',')) {
48
+ const s = part.trim().toLowerCase();
49
+ if (s === 'handoff' || s === 'vite' || s === 'next') {
50
+ set.add(s);
51
+ }
52
+ }
53
+ if (set.size > 0) {
54
+ this.scopesFilter = set;
55
+ }
56
+ }
57
+ }
58
+ static scopeEnabled(scope) {
59
+ return this.scopesFilter.has(scope);
60
+ }
61
+ /** Message severities: debug=0, info-tier=1, warn=2, error=3 */
62
+ static shouldEmit(severity) {
63
+ return severity >= this.severityThreshold;
13
64
  }
14
65
  static getTimestamp() {
15
66
  const now = new Date();
16
67
  return chalk_1.default.gray(`[${now.toISOString()}]`);
17
68
  }
18
- static log(message) {
19
- console.log(`${this.getTimestamp()} ${message}`);
69
+ /** Short label shown after the timestamp so you can scan for handoff vs vite vs next. */
70
+ static scopeTag(scope) {
71
+ const tags = {
72
+ handoff: chalk_1.default.blue.bold('[handoff]'),
73
+ vite: chalk_1.default.magenta.bold('[vite]'),
74
+ next: chalk_1.default.green.bold('[next]'),
75
+ };
76
+ return tags[scope];
20
77
  }
21
- static info(message) {
22
- console.log(`${this.getTimestamp()} ${chalk_1.default.cyan(message)}`);
78
+ static basePrefix(scope) {
79
+ return `${this.getTimestamp()} ${this.scopeTag(scope)}`;
23
80
  }
24
- static success(message) {
25
- console.log(`${this.getTimestamp()} ${chalk_1.default.green(message)}`);
81
+ static log(message, scope = 'handoff') {
82
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(1))
83
+ return;
84
+ console.log(`${this.basePrefix(scope)} ${message}`);
26
85
  }
27
- static warn(message) {
28
- console.warn(`${this.getTimestamp()} ${chalk_1.default.yellow(message)}`);
86
+ /**
87
+ * Vite often passes strings that already contain ANSI; avoid wrapping those in chalk.cyan so colors stay correct.
88
+ */
89
+ static info(message, scope = 'handoff') {
90
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(1))
91
+ return;
92
+ const body = scope === 'vite' ? message : chalk_1.default.cyan(message);
93
+ console.log(`${this.basePrefix(scope)} ${body}`);
29
94
  }
30
- static error(message, error) {
31
- console.error(`${this.getTimestamp()} ${chalk_1.default.red(message)}`);
95
+ static success(message, scope = 'handoff') {
96
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(1))
97
+ return;
98
+ console.log(`${this.basePrefix(scope)} ${chalk_1.default.green(message)}`);
99
+ }
100
+ static warn(message, scope = 'handoff') {
101
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(2))
102
+ return;
103
+ const body = scope === 'vite' ? message : chalk_1.default.yellow(message);
104
+ console.warn(`${this.basePrefix(scope)} ${body}`);
105
+ }
106
+ static error(message, error, scope = 'handoff') {
107
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(3))
108
+ return;
109
+ const body = scope === 'vite' ? message : chalk_1.default.red(message);
110
+ console.error(`${this.basePrefix(scope)} ${body}`);
32
111
  if (error) {
33
112
  console.error(error);
34
113
  }
35
114
  }
36
115
  static debug(message, data) {
37
- if (this.debugMode) {
38
- console.log(`${this.getTimestamp()} ${chalk_1.default.gray(`[DEBUG] ${message}`)}`);
39
- if (data) {
40
- console.log(data);
41
- }
116
+ if (this.severityThreshold >= 99)
117
+ return;
118
+ if (!this.scopeEnabled('handoff'))
119
+ return;
120
+ const allowDebug = this.debugMode || this.severityThreshold === 0;
121
+ if (!allowDebug)
122
+ return;
123
+ console.log(`${this.basePrefix('handoff')} ${chalk_1.default.gray(`[DEBUG] ${message}`)}`);
124
+ if (data) {
125
+ console.log(data);
126
+ }
127
+ }
128
+ /**
129
+ * Log one line from a child process with timestamp + scope tag. Child ANSI is preserved (no extra chalk on the line).
130
+ */
131
+ static childProcessLine(line, scope = 'next') {
132
+ if (!this.scopeEnabled(scope) || !this.shouldEmit(1))
133
+ return;
134
+ console.log(`${this.basePrefix(scope)} ${line}`);
135
+ }
136
+ /**
137
+ * Pipe stdout/stderr from a spawned child through {@link childProcessLine}. Use with
138
+ * `stdio: ['inherit' | 'ignore', 'pipe', 'pipe']`.
139
+ */
140
+ static pipeChildStreams(stdout, stderr, scope = 'next') {
141
+ for (const stream of [stdout, stderr]) {
142
+ if (!stream)
143
+ continue;
144
+ const rl = readline_1.default.createInterface({ input: stream, crlfDelay: Infinity });
145
+ rl.on('line', (line) => {
146
+ this.childProcessLine(line, scope);
147
+ });
148
+ }
149
+ }
150
+ /** Forward a completed child-process buffer (e.g. from `spawn.sync` with stdio pipe) line-by-line. */
151
+ static childProcessBuffer(buffer, scope = 'next') {
152
+ if (!(buffer === null || buffer === void 0 ? void 0 : buffer.length))
153
+ return;
154
+ const text = buffer.toString('utf8');
155
+ for (const line of text.split(/\r?\n/)) {
156
+ this.childProcessLine(line, scope);
42
157
  }
43
158
  }
44
159
  }
45
160
  exports.Logger = Logger;
161
+ /** From Handoff constructor; enables debug lines together with env. */
46
162
  Logger.debugMode = false;
163
+ /** Minimum message severity to print: 0=debug, 1=info, 2=warn, 3=error. `silent` uses 99. */
164
+ Logger.severityThreshold = 1;
165
+ /** Enabled scopes; defaults to all of `handoff`, `vite`, `next`. */
166
+ Logger.scopesFilter = new Set(['handoff', 'vite', 'next']);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-app",
3
- "version": "1.2.0-0",
3
+ "version": "1.2.0-1",
4
4
  "description": "Automated documentation toolchain for building client side documentation from figma",
5
5
  "author ": {
6
6
  "name": "Convertiv",