i18ntk 2.4.0 → 2.5.0

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.
@@ -1,41 +1,44 @@
1
- const { getGlobalReadline, closeGlobalReadline, ask } = require('./cli');
2
-
3
- function isInteractive(opts={}) {
4
- const envSilent = process.env.CI === 'true' || process.env.I18NTK_SILENT === '1' || (process.env.npm_config_loglevel||'').toLowerCase()==='silent';
5
- if (opts.noPrompt) return false;
6
- if (envSilent) return false;
7
- return process.stdin.isTTY === true;
8
- }
9
-
10
- class NoopPrompt {
11
- question(_q) { return Promise.resolve(''); }
12
- close() {}
13
- async pressEnterToContinue() { return; }
14
- }
15
-
16
- class RLPrompt {
17
- constructor() {
18
- this.rl = getGlobalReadline();
19
- this.closed = false;
20
- }
21
- question(q) {
22
- if (this.closed) return Promise.resolve('');
23
- return new Promise(res => this.rl.question(q, ans => res(ans)));
24
- }
25
- close() {
26
- if (!this.closed) {
27
- closeGlobalReadline();
28
- this.closed = true;
29
- }
30
- }
31
- async pressEnterToContinue(msg='\nPress Enter to continue...') {
32
- if (this.closed) return;
33
- await ask(msg);
34
- }
35
- }
36
-
37
- function createPrompt(opts={}) {
38
- return isInteractive(opts) ? new RLPrompt() : new NoopPrompt();
39
- }
40
-
41
- module.exports = { createPrompt, isInteractive };
1
+ const { getGlobalReadline, closeGlobalReadline, ask } = require('./cli');
2
+ const { envManager } = require('./env-manager');
3
+
4
+ function isInteractive(opts={}) {
5
+ const envSilent = envManager.getBoolean('CI') ||
6
+ envManager.getBoolean('I18NTK_SILENT') ||
7
+ envManager.get('npm_config_loglevel') === 'silent';
8
+ if (opts.noPrompt) return false;
9
+ if (envSilent) return false;
10
+ return process.stdin.isTTY === true;
11
+ }
12
+
13
+ class NoopPrompt {
14
+ question(_q) { return Promise.resolve(''); }
15
+ close() {}
16
+ async pressEnterToContinue() { return; }
17
+ }
18
+
19
+ class RLPrompt {
20
+ constructor() {
21
+ this.rl = getGlobalReadline();
22
+ this.closed = false;
23
+ }
24
+ question(q) {
25
+ if (this.closed) return Promise.resolve('');
26
+ return new Promise(res => this.rl.question(q, ans => res(ans)));
27
+ }
28
+ close() {
29
+ if (!this.closed) {
30
+ closeGlobalReadline();
31
+ this.closed = true;
32
+ }
33
+ }
34
+ async pressEnterToContinue(msg='\nPress Enter to continue...') {
35
+ if (this.closed) return;
36
+ await ask(msg);
37
+ }
38
+ }
39
+
40
+ function createPrompt(opts={}) {
41
+ return isInteractive(opts) ? new RLPrompt() : new NoopPrompt();
42
+ }
43
+
44
+ module.exports = { createPrompt, isInteractive };
@@ -1,154 +1,156 @@
1
- // Secure error handling utilities
2
- const crypto = require('crypto');
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- class SecureError extends Error {
7
- constructor(message, code = 'SECURE_ERROR', details = {}) {
8
- super(message);
9
- this.name = 'SecureError';
10
- this.code = code;
11
- this.details = details;
12
- this.timestamp = new Date().toISOString();
13
- this.errorId = crypto.randomBytes(8).toString('hex');
14
-
15
- // Capture stack trace, excluding constructor call from it
16
- Error.captureStackTrace(this, this.constructor);
17
- }
18
-
19
- toJSON() {
20
- return {
21
- error: this.name,
22
- message: this.message,
23
- code: this.code,
24
- errorId: this.errorId,
25
- timestamp: this.timestamp
26
- };
27
- }
28
-
29
- static sanitizeError(error) {
30
- if (error instanceof SecureError) {
31
- return error.toJSON();
32
- }
33
-
34
- // For non-SecureError instances, return a sanitized version
35
- return {
36
- error: 'InternalError',
37
- message: 'An internal error occurred',
38
- code: 'INTERNAL_ERROR',
39
- errorId: crypto.randomBytes(8).toString('hex'),
40
- timestamp: new Date().toISOString()
41
- };
42
- }
43
- }
44
-
45
- // Common error types
46
- class ValidationError extends SecureError {
47
- constructor(message = 'Validation failed', details = {}) {
48
- super(message, 'VALIDATION_ERROR', details);
49
- this.name = 'ValidationError';
50
- }
51
- }
52
-
53
- class SecurityError extends SecureError {
54
- constructor(message = 'Security violation detected', details = {}) {
55
- super(message, 'SECURITY_VIOLATION', details);
56
- this.name = 'SecurityError';
57
- }
58
- }
59
-
60
- class EncryptionError extends SecureError {
61
- constructor(message = 'Encryption/decryption failed', details = {}) {
62
- super(message, 'ENCRYPTION_ERROR', details);
63
- this.name = 'EncryptionError';
64
- }
65
- }
66
-
67
- // Secure error handler middleware
68
- function secureErrorHandler(options = {}) {
69
- const defaults = {
70
- logErrors: process.env.NODE_ENV !== 'production',
71
- logFunction: console.error,
72
- logFilePath: null,
73
- sanitizeStack: true
74
- };
75
-
76
- const config = { ...defaults, ...options };
77
-
78
- // Ensure log directory exists
79
- if (config.logFilePath && !SecurityUtils.safeExistsSync(path.dirname(config.logFilePath))) {
80
- try {
81
- fs.mkdirSync(path.dirname(config.logFilePath), { recursive: true });
82
- } catch (e) {
83
- console.error('Failed to create log directory:', e);
84
- }
85
- }
86
-
87
- return function(error, req, res, next) {
88
- // Handle specific error types
89
- let statusCode = 500;
90
- let response = {};
91
-
92
- if (error instanceof ValidationError) {
93
- statusCode = 400;
94
- response = error.toJSON();
95
- } else if (error instanceof SecurityError) {
96
- statusCode = 403;
97
- response = error.toJSON();
98
- } else if (error instanceof EncryptionError) {
99
- statusCode = 400;
100
- response = error.toJSON();
101
- } else {
102
- // Generic error handling
103
- response = SecureError.sanitizeError(error);
104
- }
105
-
106
- // Log the error if enabled
107
- if (config.logErrors) {
108
- const logEntry = {
109
- timestamp: new Date().toISOString(),
110
- error: {
111
- name: error.name,
112
- message: error.message,
113
- stack: config.sanitizeStack
114
- ? error.stack.split('\n').slice(0, 3).join('\n') + '\n ...'
115
- : error.stack,
116
- ...(error.details && { details: error.details }),
117
- errorId: response.errorId
118
- },
119
- request: req ? {
120
- method: req.method,
121
- url: req.originalUrl,
122
- ip: req.ip,
123
- userAgent: req.get('user-agent')
124
- } : undefined
125
- };
126
-
127
- // Log to console
128
- if (typeof config.logFunction === 'function') {
129
- SecurityUtils.safeWriteFileSync(config.logFilePath, JSON.stringify(logEntry, null, 2));
130
- }
131
-
132
- // Log to file if configured
133
- if (config.logFilePath) {
134
- SecurityUtils.safeWriteFileSync(
135
- config.logFilePath,
136
- JSON.stringify(logEntry) + '\n',
137
- 'utf8'
138
- );
139
- }
140
- }
141
-
142
- // Send response
143
- res.status(statusCode).json(response);
144
- };
145
- }
146
-
147
- module.exports = {
148
- SecureError,
149
- ValidationError,
150
- SecurityError,
151
- EncryptionError,
152
- secureErrorHandler,
153
- createError: (message, code, details) => new SecureError(message, code, details)
154
- };
1
+ // Secure error handling utilities
2
+ const crypto = require('crypto');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const SecurityUtils = require('./security');
6
+ const { envManager } = require('./env-manager');
7
+
8
+ class SecureError extends Error {
9
+ constructor(message, code = 'SECURE_ERROR', details = {}) {
10
+ super(message);
11
+ this.name = 'SecureError';
12
+ this.code = code;
13
+ this.details = details;
14
+ this.timestamp = new Date().toISOString();
15
+ this.errorId = crypto.randomBytes(8).toString('hex');
16
+
17
+ // Capture stack trace, excluding constructor call from it
18
+ Error.captureStackTrace(this, this.constructor);
19
+ }
20
+
21
+ toJSON() {
22
+ return {
23
+ error: this.name,
24
+ message: this.message,
25
+ code: this.code,
26
+ errorId: this.errorId,
27
+ timestamp: this.timestamp
28
+ };
29
+ }
30
+
31
+ static sanitizeError(error) {
32
+ if (error instanceof SecureError) {
33
+ return error.toJSON();
34
+ }
35
+
36
+ // For non-SecureError instances, return a sanitized version
37
+ return {
38
+ error: 'InternalError',
39
+ message: 'An internal error occurred',
40
+ code: 'INTERNAL_ERROR',
41
+ errorId: crypto.randomBytes(8).toString('hex'),
42
+ timestamp: new Date().toISOString()
43
+ };
44
+ }
45
+ }
46
+
47
+ // Common error types
48
+ class ValidationError extends SecureError {
49
+ constructor(message = 'Validation failed', details = {}) {
50
+ super(message, 'VALIDATION_ERROR', details);
51
+ this.name = 'ValidationError';
52
+ }
53
+ }
54
+
55
+ class SecurityError extends SecureError {
56
+ constructor(message = 'Security violation detected', details = {}) {
57
+ super(message, 'SECURITY_VIOLATION', details);
58
+ this.name = 'SecurityError';
59
+ }
60
+ }
61
+
62
+ class EncryptionError extends SecureError {
63
+ constructor(message = 'Encryption/decryption failed', details = {}) {
64
+ super(message, 'ENCRYPTION_ERROR', details);
65
+ this.name = 'EncryptionError';
66
+ }
67
+ }
68
+
69
+ // Secure error handler middleware
70
+ function secureErrorHandler(options = {}) {
71
+ const defaults = {
72
+ logErrors: envManager.get('NODE_ENV') !== 'production',
73
+ logFunction: console.error,
74
+ logFilePath: null,
75
+ sanitizeStack: true
76
+ };
77
+
78
+ const config = { ...defaults, ...options };
79
+
80
+ // Ensure log directory exists
81
+ if (config.logFilePath && !SecurityUtils.safeExistsSync(path.dirname(config.logFilePath))) {
82
+ try {
83
+ fs.mkdirSync(path.dirname(config.logFilePath), { recursive: true });
84
+ } catch (e) {
85
+ console.error('Failed to create log directory:', e);
86
+ }
87
+ }
88
+
89
+ return function(error, req, res, next) {
90
+ // Handle specific error types
91
+ let statusCode = 500;
92
+ let response = {};
93
+
94
+ if (error instanceof ValidationError) {
95
+ statusCode = 400;
96
+ response = error.toJSON();
97
+ } else if (error instanceof SecurityError) {
98
+ statusCode = 403;
99
+ response = error.toJSON();
100
+ } else if (error instanceof EncryptionError) {
101
+ statusCode = 400;
102
+ response = error.toJSON();
103
+ } else {
104
+ // Generic error handling
105
+ response = SecureError.sanitizeError(error);
106
+ }
107
+
108
+ // Log the error if enabled
109
+ if (config.logErrors) {
110
+ const logEntry = {
111
+ timestamp: new Date().toISOString(),
112
+ error: {
113
+ name: error.name,
114
+ message: error.message,
115
+ stack: config.sanitizeStack
116
+ ? error.stack.split('\n').slice(0, 3).join('\n') + '\n ...'
117
+ : error.stack,
118
+ ...(error.details && { details: error.details }),
119
+ errorId: response.errorId
120
+ },
121
+ request: req ? {
122
+ method: req.method,
123
+ url: req.originalUrl,
124
+ ip: req.ip,
125
+ userAgent: req.get('user-agent')
126
+ } : undefined
127
+ };
128
+
129
+ // Log to console
130
+ if (typeof config.logFunction === 'function') {
131
+ SecurityUtils.safeWriteFileSync(config.logFilePath, JSON.stringify(logEntry, null, 2));
132
+ }
133
+
134
+ // Log to file if configured
135
+ if (config.logFilePath) {
136
+ SecurityUtils.safeWriteFileSync(
137
+ config.logFilePath,
138
+ JSON.stringify(logEntry) + '\n',
139
+ 'utf8'
140
+ );
141
+ }
142
+ }
143
+
144
+ // Send response
145
+ res.status(statusCode).json(response);
146
+ };
147
+ }
148
+
149
+ module.exports = {
150
+ SecureError,
151
+ ValidationError,
152
+ SecurityError,
153
+ EncryptionError,
154
+ secureErrorHandler,
155
+ createError: (message, code, details) => new SecureError(message, code, details)
156
+ };