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.
@@ -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
- error: error instanceof Error ? error.stack ?? error.message : String(error),
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', { command: commandName, error: result.error });
129
- process.exit(1);
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
- return {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "type": "commonjs",
3
3
  "name": "pixivflow",
4
- "version": "3.0.1",
4
+ "version": "3.0.2",
5
5
  "private": true
6
6
  }
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.1', commit: '33362ac35c11' };
5
+ exports.BUILD = { version: '3.0.2', commit: 'a0e5f0be1522' };
6
6
  //# sourceMappingURL=version.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "type": "commonjs",
3
3
  "name": "pixivflow-webui-backend",
4
- "version": "3.0.1",
4
+ "version": "3.0.2",
5
5
  "description": "PixivFlow WebUI Backend - CommonJS module"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixivflow",
3
- "version": "3.0.1",
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",