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.
- package/dist/app-builder/build.js +24 -10
- package/dist/app-builder/config-diff/index.d.ts +1 -1
- package/dist/app-builder/config-diff/registry.d.ts +2 -2
- package/dist/app-builder/config-diff/registry.js +2 -2
- package/dist/app-builder/config-diff/strategies/pattern.js +14 -1
- package/dist/app-builder/config-diff/types.d.ts +15 -1
- package/dist/app-builder/watchers.js +35 -3
- package/dist/app-builder/websocket.js +10 -4
- package/dist/cli.js +1 -1
- package/dist/config/normalizers/pattern.d.ts +0 -1
- package/dist/config/normalizers/pattern.js +0 -3
- package/dist/config/runtime.js +10 -4
- package/dist/pipeline/patterns.d.ts +3 -1
- package/dist/pipeline/patterns.js +2 -2
- package/dist/transformers/preview/component/builder.js +52 -5
- package/dist/transformers/preview/component/css.js +6 -4
- package/dist/transformers/preview/component/javascript.d.ts +0 -1
- package/dist/transformers/preview/component/javascript.js +25 -14
- package/dist/transformers/preview/pattern/api.d.ts +4 -0
- package/dist/transformers/preview/pattern/api.js +19 -1
- package/dist/transformers/preview/pattern/builder.d.ts +9 -1
- package/dist/transformers/preview/pattern/builder.js +123 -42
- package/dist/transformers/utils/vite-logger.js +4 -4
- package/dist/transformers/vite-config.js +2 -0
- package/dist/utils/duration.d.ts +2 -0
- package/dist/utils/duration.js +6 -0
- package/dist/utils/logger.d.ts +42 -5
- package/dist/utils/logger.js +135 -15
- package/package.json +1 -1
- package/src/app/pages/system/pattern/[pattern]/index.tsx +178 -55
- package/src/app-builder/build.ts +26 -10
- package/src/app-builder/config-diff/index.ts +1 -1
- package/src/app-builder/config-diff/registry.ts +3 -3
- package/src/app-builder/config-diff/strategies/pattern.ts +17 -2
- package/src/app-builder/config-diff/types.ts +16 -1
- package/src/app-builder/watchers.ts +15 -3
- package/src/app-builder/websocket.ts +16 -5
- package/src/cli.ts +1 -1
- package/src/config/normalizers/pattern.ts +0 -7
- package/src/config/runtime.ts +11 -4
- package/src/pipeline/patterns.ts +5 -3
- package/src/transformers/preview/component/builder.ts +61 -5
- package/src/transformers/preview/component/css.ts +6 -5
- package/src/transformers/preview/component/javascript.ts +29 -19
- package/src/transformers/preview/pattern/api.ts +17 -0
- package/src/transformers/preview/pattern/builder.ts +156 -58
- package/src/transformers/utils/vite-logger.ts +4 -4
- package/src/transformers/vite-config.ts +2 -0
- package/src/utils/duration.ts +3 -0
- 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
|
|
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,
|
|
35
|
-
return
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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 (
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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,
|
|
81
|
-
yield (0, api_1.writePatternSummaryApi)(handoff,
|
|
82
|
-
logger_1.Logger.info(`Finished
|
|
83
|
-
return
|
|
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,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;
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -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
|
-
|
|
8
|
-
static
|
|
9
|
-
static
|
|
10
|
-
static
|
|
11
|
-
|
|
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
|
}
|
package/dist/utils/logger.js
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
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
|
|
22
|
-
|
|
78
|
+
static basePrefix(scope) {
|
|
79
|
+
return `${this.getTimestamp()} ${this.scopeTag(scope)}`;
|
|
23
80
|
}
|
|
24
|
-
static
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
31
|
-
|
|
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.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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']);
|