pixivflow 3.0.1 → 3.0.2
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/cli/fatalError.d.ts +12 -0
- package/dist/cli/fatalError.js +45 -0
- package/dist/index.js +20 -34
- package/dist/logger.js +18 -4
- package/dist/package.json +1 -1
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles fatal errors, prints user-friendly messages, and exits the process.
|
|
3
|
+
*
|
|
4
|
+
* Failure Contract (Deploy `AGENTS.md` §25): a startup failure must be both
|
|
5
|
+
* human-readable *and* machine-readable. The human text is printed here; the
|
|
6
|
+
* structured log carries `stage` and the **error object** — never
|
|
7
|
+
* `error.message` alone, because that drops the contract fields a
|
|
8
|
+
* `PixivFlowError` subclass owns (`code`, `statusCode`, `cause`), which is the
|
|
9
|
+
* same class of defect as a log line rendered as `"error":{}`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function handleFatalError(error: unknown, exit?: (code: number) => void): void;
|
|
12
|
+
//# sourceMappingURL=fatalError.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleFatalError = handleFatalError;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const errors_1 = require("../utils/errors");
|
|
6
|
+
/**
|
|
7
|
+
* Handles fatal errors, prints user-friendly messages, and exits the process.
|
|
8
|
+
*
|
|
9
|
+
* Failure Contract (Deploy `AGENTS.md` §25): a startup failure must be both
|
|
10
|
+
* human-readable *and* machine-readable. The human text is printed here; the
|
|
11
|
+
* structured log carries `stage` and the **error object** — never
|
|
12
|
+
* `error.message` alone, because that drops the contract fields a
|
|
13
|
+
* `PixivFlowError` subclass owns (`code`, `statusCode`, `cause`), which is the
|
|
14
|
+
* same class of defect as a log line rendered as `"error":{}`.
|
|
15
|
+
*/
|
|
16
|
+
function handleFatalError(error, exit = (code) => process.exit(code)) {
|
|
17
|
+
if (error instanceof errors_1.ConfigError) {
|
|
18
|
+
console.error(`\n❌ Configuration Error: ${error.message}\n`);
|
|
19
|
+
logger_1.logger.error('Configuration error', { stage: 'application.startup', error });
|
|
20
|
+
}
|
|
21
|
+
else if (error instanceof errors_1.AuthenticationError) {
|
|
22
|
+
console.error('\n❌ Authentication Error');
|
|
23
|
+
console.error('════════════════════════════════════════════════════════════════');
|
|
24
|
+
console.error(error.message);
|
|
25
|
+
console.error('');
|
|
26
|
+
console.error('💡 Your refresh token may have expired or is invalid.');
|
|
27
|
+
console.error(' Please login again to get a new refresh token:');
|
|
28
|
+
console.error('');
|
|
29
|
+
console.error(' • Interactive login: pixivflow login');
|
|
30
|
+
console.error(' • Headless login: pixivflow login-headless');
|
|
31
|
+
console.error('════════════════════════════════════════════════════════════════\n');
|
|
32
|
+
logger_1.logger.error('Authentication failed', { stage: 'application.startup', error });
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
logger_1.logger.error('Fatal error during application startup', {
|
|
36
|
+
stage: 'application.startup',
|
|
37
|
+
error: error instanceof Error ? error : String(error),
|
|
38
|
+
});
|
|
39
|
+
// Even an unrecognised failure must say something on the console; the log
|
|
40
|
+
// line alone is invisible to the operator who ran the command.
|
|
41
|
+
console.error(`\n❌ ${error instanceof Error ? error.message : String(error)}\n`);
|
|
42
|
+
}
|
|
43
|
+
exit(1);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=fatalError.js.map
|
package/dist/index.js
CHANGED
|
@@ -7,36 +7,9 @@ const CommandRegistry_1 = require("./commands/CommandRegistry");
|
|
|
7
7
|
const CommandResultRenderer_1 = require("./commands/CommandResultRenderer");
|
|
8
8
|
const commands_1 = require("./commands");
|
|
9
9
|
const ArgumentParser_1 = require("./cli/ArgumentParser");
|
|
10
|
+
const fatalError_1 = require("./cli/fatalError");
|
|
10
11
|
const errors_1 = require("./utils/errors");
|
|
11
12
|
const schedules_1 = require("./scheduler/schedules");
|
|
12
|
-
/**
|
|
13
|
-
* Handles fatal errors, prints user-friendly messages, and exits the process.
|
|
14
|
-
*/
|
|
15
|
-
function handleFatalError(error) {
|
|
16
|
-
if (error instanceof errors_1.ConfigError) {
|
|
17
|
-
console.error(`\n❌ Configuration Error: ${error.message}\n`);
|
|
18
|
-
logger_1.logger.error('Configuration error', { error: error.message });
|
|
19
|
-
}
|
|
20
|
-
else if (error instanceof errors_1.AuthenticationError) {
|
|
21
|
-
console.error('\n❌ Authentication Error');
|
|
22
|
-
console.error('════════════════════════════════════════════════════════════════');
|
|
23
|
-
console.error(error.message);
|
|
24
|
-
console.error('');
|
|
25
|
-
console.error('💡 Your refresh token may have expired or is invalid.');
|
|
26
|
-
console.error(' Please login again to get a new refresh token:');
|
|
27
|
-
console.error('');
|
|
28
|
-
console.error(' • Interactive login: pixivflow login');
|
|
29
|
-
console.error(' • Headless login: pixivflow login-headless');
|
|
30
|
-
console.error('════════════════════════════════════════════════════════════════\n');
|
|
31
|
-
logger_1.logger.error('Authentication failed', { error: error.message });
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
logger_1.logger.error('Fatal error during application startup', {
|
|
35
|
-
error: error instanceof Error ? error.stack ?? error.message : String(error),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
13
|
/**
|
|
41
14
|
* Executes the given command.
|
|
42
15
|
*/
|
|
@@ -106,10 +79,16 @@ async function executeCommand(registry, commandName, context, args) {
|
|
|
106
79
|
}
|
|
107
80
|
}
|
|
108
81
|
catch (error) {
|
|
82
|
+
// Pass the error itself: the logger expands it into
|
|
83
|
+
// `{name, message, stack, cause, …contract fields}`. A pre-formatted
|
|
84
|
+
// `error.stack` string dropped the `code` an operator greps for.
|
|
109
85
|
logger_1.logger.error('Unexpected error during command execution', {
|
|
110
86
|
command: commandName,
|
|
111
|
-
|
|
87
|
+
stage: 'command.execute',
|
|
88
|
+
retryable: false,
|
|
89
|
+
error: error instanceof Error ? error : String(error),
|
|
112
90
|
});
|
|
91
|
+
console.error((0, CommandResultRenderer_1.formatCommandFailure)({ error: error instanceof Error ? error : undefined }, commandName));
|
|
113
92
|
process.exit(1);
|
|
114
93
|
}
|
|
115
94
|
}
|
|
@@ -125,15 +104,22 @@ async function executeDefaultBehavior(context, args) {
|
|
|
125
104
|
try {
|
|
126
105
|
const result = await command.execute(context, args);
|
|
127
106
|
if (!result.success) {
|
|
128
|
-
logger_1.logger.error('Default command execution failed', {
|
|
129
|
-
|
|
107
|
+
logger_1.logger.error('Default command execution failed', {
|
|
108
|
+
command: commandName,
|
|
109
|
+
stage: 'command.execute',
|
|
110
|
+
reason: (0, CommandResultRenderer_1.commandFailureReason)(result, commandName),
|
|
111
|
+
retryable: false,
|
|
112
|
+
error: result.error,
|
|
113
|
+
});
|
|
114
|
+
console.error((0, CommandResultRenderer_1.formatCommandFailure)(result, commandName));
|
|
115
|
+
process.exit(result.exitCode ?? 1);
|
|
130
116
|
}
|
|
131
117
|
if (commandName === 'download') {
|
|
132
118
|
process.exit(0);
|
|
133
119
|
}
|
|
134
120
|
}
|
|
135
121
|
catch (error) {
|
|
136
|
-
handleFatalError(error);
|
|
122
|
+
(0, fatalError_1.handleFatalError)(error);
|
|
137
123
|
}
|
|
138
124
|
}
|
|
139
125
|
/**
|
|
@@ -175,7 +161,7 @@ async function bootstrap() {
|
|
|
175
161
|
config = (0, config_1.loadConfig)(configPath, isLoginCommand || isTokenOptional || !commandName);
|
|
176
162
|
}
|
|
177
163
|
catch (error) {
|
|
178
|
-
return handleFatalError(error);
|
|
164
|
+
return (0, fatalError_1.handleFatalError)(error);
|
|
179
165
|
}
|
|
180
166
|
const context = { config, logger: logger_1.logger, configPath };
|
|
181
167
|
// --- Command execution ---
|
|
@@ -186,5 +172,5 @@ async function bootstrap() {
|
|
|
186
172
|
await executeDefaultBehavior(context, commandArgs);
|
|
187
173
|
}
|
|
188
174
|
}
|
|
189
|
-
bootstrap().catch(handleFatalError);
|
|
175
|
+
bootstrap().catch(fatalError_1.handleFatalError);
|
|
190
176
|
//# sourceMappingURL=index.js.map
|
package/dist/logger.js
CHANGED
|
@@ -13,17 +13,31 @@ const ctxStore = new node_async_hooks_1.AsyncLocalStorage();
|
|
|
13
13
|
* non-enumerable — so a structured line carrying an error used to say
|
|
14
14
|
* `"error":{}` and hide the failing stage (the exact shape that made
|
|
15
15
|
* `pixivflow reconcile` undiagnosable in production). Errors are expanded
|
|
16
|
-
* explicitly, `cause` included
|
|
16
|
+
* explicitly, `cause` included, and every own enumerable field is kept:
|
|
17
|
+
* `PixivFlowError` subclasses carry the machine-readable contract there
|
|
18
|
+
* (`code`, `statusCode`, `url`, `isRateLimit`, `waitTime`, `itemId`,
|
|
19
|
+
* `itemType`), which is what the deploy Failure Contract requires an operator
|
|
20
|
+
* to be able to grep.
|
|
17
21
|
*/
|
|
18
22
|
function serializeLogValue(value) {
|
|
19
23
|
return JSON.stringify(value, (_key, item) => {
|
|
20
24
|
if (item instanceof Error) {
|
|
21
|
-
|
|
25
|
+
const serialized = {
|
|
22
26
|
name: item.name,
|
|
23
27
|
message: item.message,
|
|
24
|
-
...(item.stack ? { stack: item.stack } : {}),
|
|
25
|
-
...(item.cause !== undefined ? { cause: item.cause } : {}),
|
|
26
28
|
};
|
|
29
|
+
for (const key of Object.keys(item)) {
|
|
30
|
+
if (!(key in serialized)) {
|
|
31
|
+
serialized[key] = item[key];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (item.stack) {
|
|
35
|
+
serialized.stack = item.stack;
|
|
36
|
+
}
|
|
37
|
+
if (item.cause !== undefined) {
|
|
38
|
+
serialized.cause = item.cause;
|
|
39
|
+
}
|
|
40
|
+
return serialized;
|
|
27
41
|
}
|
|
28
42
|
return item;
|
|
29
43
|
});
|
package/dist/package.json
CHANGED
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BUILD = void 0;
|
|
4
4
|
// GENERATED by scripts/write-version.js — do not edit manually.
|
|
5
|
-
exports.BUILD = { version: '3.0.
|
|
5
|
+
exports.BUILD = { version: '3.0.2', commit: 'a0e5f0be1522' };
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/dist/webui/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixivflow",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "🎨 Pixiv 下载、筛选与自动收集工具 - 批量下载插画和小说、按标签/热度/日期筛选、定时任务与可靠 HTTP 交付 | Pixiv downloader and automation toolkit with filtering, scheduling and reliable HTTP delivery",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|