@lowdefy/errors 0.0.0-experimental-20260220095000 → 0.0.0-experimental-20260220142815

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.
@@ -15,7 +15,10 @@
15
15
  */ /**
16
16
  * Base error class for configuration errors (invalid YAML, schema violations, validation errors).
17
17
  *
18
- * Import from @lowdefy/errors for general use.
18
+ * This is the environment-agnostic base class. For environment-specific behavior:
19
+ * - Build-time: Use @lowdefy/errors/build (sync resolution via keyMap/refMap)
20
+ * - Server-side: Use @lowdefy/errors/server (re-exports base)
21
+ * - Client-side: Use @lowdefy/errors/client (async resolution via API)
19
22
  *
20
23
  * @example
21
24
  * // Simple string (for plugins - configKey added by interface layer)
@@ -23,46 +26,71 @@
23
26
  *
24
27
  * @example
25
28
  * // With options
26
- * throw new ConfigError('Invalid block type', { configKey: block['~k'] });
27
- *
28
- * @example
29
- * // With raw file location (YAML parse errors, pre-addKeys)
30
- * throw new ConfigError('Error parsing file', {
31
- * filePath: 'pages/home.yaml',
32
- * lineNumber: 6,
29
+ * throw new ConfigError({
30
+ * message: 'Invalid block type',
31
+ * configKey: block['~k'],
32
+ * location: { source: 'pages/home.yaml:42', link: '/path/to/pages/home.yaml:42' }
33
33
  * });
34
- *
35
- * @example
36
- * // Wrapping an error
37
- * throw new ConfigError(error.message, { cause: error, filePath });
38
- */ let ConfigError = class ConfigError extends Error {
34
+ */ import formatErrorMessage from './formatErrorMessage.js';
35
+ let ConfigError = class ConfigError extends Error {
36
+ print() {
37
+ return formatErrorMessage(this);
38
+ }
39
39
  /**
40
- * Creates a ConfigError instance with formatted message.
41
- * @param {string} [message] - Error message (falls back to cause.message)
42
- * @param {Object} [options]
43
- * @param {Error} [options.cause] - Original error to wrap
44
- * @param {string} [options.configKey] - Config key (~k) for location resolution
45
- * @param {string} [options.filePath] - Raw file path for pre-addKeys errors
46
- * @param {string|number} [options.lineNumber] - Line number for pre-addKeys errors
47
- * @param {string} [options.checkSlug] - The build check that triggered this error
48
- * @param {*} [options.received] - The input that caused the error
49
- */ constructor(message, { cause, configKey, filePath, lineNumber, checkSlug, received } = {}){
50
- const resolvedMessage = message ?? cause?.message;
51
- super(resolvedMessage, {
52
- cause
40
+ * Serializes the error for transport (e.g., client to server).
41
+ * @returns {Object} Serialized error data with type marker
42
+ */ serialize() {
43
+ return {
44
+ '~err': 'ConfigError',
45
+ message: this.message,
46
+ configKey: this.configKey
47
+ };
48
+ }
49
+ /**
50
+ * Deserializes error data back into a ConfigError.
51
+ * @param {Object} data - Serialized error data
52
+ * @returns {ConfigError}
53
+ */ static deserialize(data) {
54
+ return new ConfigError({
55
+ message: data.message,
56
+ configKey: data.configKey
53
57
  });
58
+ }
59
+ /**
60
+ * Creates a ConfigError instance with formatted message.
61
+ * @param {string|Object} messageOrParams - Error message string, or params object
62
+ * @param {string} [messageOrParams.message] - The error message (if object)
63
+ * @param {Error} [messageOrParams.error] - Original error to wrap (extracts message/configKey/stack)
64
+ * @param {string} [messageOrParams.configKey] - Config key (~k) for location resolution
65
+ * @param {Object} [messageOrParams.location] - Pre-resolved location { source, link, config }
66
+ * @param {string} [messageOrParams.checkSlug] - The build check that triggered this error
67
+ */ constructor(messageOrParams){
68
+ // Support both string and object parameter
69
+ const isString = typeof messageOrParams === 'string';
70
+ const error = isString ? null : messageOrParams.error;
71
+ const message = isString ? messageOrParams : messageOrParams.message ?? error?.message;
72
+ const configKey = isString ? null : messageOrParams.configKey ?? error?.configKey;
73
+ const location = isString ? null : messageOrParams.location;
74
+ const checkSlug = isString ? undefined : messageOrParams.checkSlug;
75
+ const received = isString ? undefined : messageOrParams.received !== undefined ? messageOrParams.received : error?.received;
76
+ const operatorLocation = isString ? null : messageOrParams.operatorLocation;
77
+ // Message without prefix - logger uses error.name for display
78
+ super(message);
54
79
  this.name = 'ConfigError';
55
- this.isLowdefyError = true;
56
- this.configKey = configKey ?? cause?.configKey ?? null;
80
+ this.configKey = configKey ?? null;
57
81
  this.checkSlug = checkSlug;
58
82
  // For logger formatting
59
- this.received = received !== undefined ? received : cause?.received;
60
- // Raw file location for pre-addKeys errors (resolved by handleError/handleWarning)
61
- this.filePath = filePath ?? null;
62
- this.lineNumber = lineNumber ?? null;
63
- // Location outputs (set by handlers via resolveErrorLocation, not at construction)
64
- this.source = null;
65
- this.config = null;
83
+ this.received = received;
84
+ this.operatorLocation = operatorLocation;
85
+ // Location info (can be set via constructor or subclass)
86
+ this.source = location?.source ?? null;
87
+ this.link = location?.link ?? null;
88
+ this.config = location?.config ?? null;
89
+ this.resolved = !!location;
90
+ // Preserve original error's stack if wrapping
91
+ if (error?.stack) {
92
+ this.stack = error.stack;
93
+ }
66
94
  }
67
95
  };
68
96
  export default ConfigError;
@@ -12,19 +12,31 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ import ConfigError from './ConfigError.js';
16
- let ConfigWarning = class ConfigWarning extends ConfigError {
17
- constructor(message, { cause, configKey, checkSlug, filePath, lineNumber, received, prodError } = {}){
18
- super(message, {
19
- cause,
20
- configKey,
21
- checkSlug,
22
- filePath,
23
- lineNumber,
24
- received
25
- });
26
- this.name = 'ConfigWarning';
27
- this.prodError = prodError ?? false;
15
+ */ /**
16
+ * Base configuration warning class.
17
+ *
18
+ * This is the environment-agnostic base class. For environment-specific behavior:
19
+ * - Build-time: Use @lowdefy/errors/build (prodError flag, suppression logic)
20
+ * - Server-side: Use @lowdefy/errors/server (re-exports base)
21
+ * - Client-side: Use @lowdefy/errors/client (re-exports base)
22
+ *
23
+ * @example
24
+ * const warning = new ConfigWarning({ message: 'Deprecated feature used', source: 'config.yaml:10' });
25
+ * console.warn(warning.message);
26
+ */ import formatErrorMessage from './formatErrorMessage.js';
27
+ let ConfigWarning = class ConfigWarning {
28
+ print() {
29
+ return formatErrorMessage(this);
30
+ }
31
+ /**
32
+ * @param {Object} params
33
+ * @param {string} params.message - The warning message
34
+ * @param {string} [params.source] - Source file:line
35
+ */ constructor({ message, source }){
36
+ this.name = 'Config Warning';
37
+ // Message without prefix - logger uses class name for display
38
+ this.message = message;
39
+ this.source = source ?? null;
28
40
  }
29
41
  };
30
42
  export default ConfigWarning;
@@ -20,7 +20,7 @@
20
20
  *
21
21
  * @example
22
22
  * // Create a new error
23
- * throw new LowdefyInternalError('Unexpected condition');
23
+ * throw new LowdefyError('Unexpected condition');
24
24
  *
25
25
  * @example
26
26
  * // At top-level catch, log the error with stack
@@ -28,18 +28,46 @@
28
28
  * console.error(err.message);
29
29
  * console.error(err.stack);
30
30
  * }
31
- */ let LowdefyInternalError = class LowdefyInternalError extends Error {
31
+ */ import formatErrorMessage from './formatErrorMessage.js';
32
+ let LowdefyError = class LowdefyError extends Error {
33
+ print() {
34
+ const message = formatErrorMessage(this);
35
+ if (this.stack) {
36
+ return `${message}\n${this.stack}`;
37
+ }
38
+ return message;
39
+ }
40
+ /**
41
+ * Serializes the error for transport (e.g., client to server).
42
+ * @returns {Object} Serialized error data with type marker
43
+ */ serialize() {
44
+ return {
45
+ '~err': 'LowdefyError',
46
+ message: this.message,
47
+ stack: this.stack
48
+ };
49
+ }
50
+ /**
51
+ * Deserializes error data back into a LowdefyError.
52
+ * @param {Object} data - Serialized error data
53
+ * @returns {LowdefyError}
54
+ */ static deserialize(data) {
55
+ const error = new LowdefyError(data.message);
56
+ if (data.stack) {
57
+ error.stack = data.stack;
58
+ }
59
+ return error;
60
+ }
32
61
  /**
33
- * Creates a LowdefyInternalError instance.
62
+ * Creates a LowdefyError instance.
34
63
  * @param {string} message - The error message
35
64
  * @param {Object} [options]
36
65
  * @param {Error} [options.cause] - The original error that caused this
37
66
  */ constructor(message, options = {}){
38
67
  // Message without prefix - logger uses error.name for display
39
68
  super(message, options);
40
- this.name = 'LowdefyInternalError';
41
- this.isLowdefyError = true;
69
+ this.name = 'LowdefyError';
42
70
  this.configKey = null;
43
71
  }
44
72
  };
45
- export default LowdefyInternalError;
73
+ export default LowdefyError;
@@ -13,11 +13,11 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ /**
16
- * Base error class for plugin failures (operators, actions, blocks, requests).
16
+ * Error class for plugin failures (operators, actions, blocks, connections, requests).
17
17
  *
18
18
  * Plugins throw plain Error objects with simple messages.
19
- * The plugin interface layer catches these and wraps them in a typed subclass
20
- * (OperatorError, ActionError, BlockError, RequestError) with additional context.
19
+ * The plugin interface layer catches these and wraps them in PluginError
20
+ * with additional context (received values, location, plugin type).
21
21
  *
22
22
  * @example
23
23
  * // In operator parser (plugin interface layer):
@@ -25,43 +25,96 @@
25
25
  * return operator({ params });
26
26
  * } catch (error) {
27
27
  * if (error instanceof ConfigError) throw error;
28
- * throw new OperatorError(error.message, {
29
- * cause: error,
30
- * typeName: '_if',
28
+ * throw new PluginError({
29
+ * error,
30
+ * pluginType: 'operator',
31
+ * pluginName: '_if',
31
32
  * received: params,
32
33
  * location: 'blocks.0.properties.visible',
33
34
  * configKey: block['~k'],
34
35
  * });
35
36
  * }
36
- */ let PluginError = class PluginError extends Error {
37
+ * // error.message = "[Plugin Error] _if requires boolean test. Received: {...} at blocks.0.properties.visible."
38
+ */ import formatErrorMessage from './formatErrorMessage.js';
39
+ let PluginError = class PluginError extends Error {
40
+ print() {
41
+ return formatErrorMessage(this);
42
+ }
43
+ /**
44
+ * Serializes the error for transport (e.g., client to server).
45
+ * @returns {Object} Serialized error data with type marker
46
+ */ serialize() {
47
+ return {
48
+ '~err': 'PluginError',
49
+ message: this.message,
50
+ rawMessage: this.rawMessage,
51
+ pluginType: this.pluginType,
52
+ pluginName: this.pluginName,
53
+ location: this.location,
54
+ configKey: this.configKey,
55
+ stack: this.stack
56
+ };
57
+ }
58
+ /**
59
+ * Deserializes error data back into a PluginError.
60
+ * Note: message already contains location/received, so we don't pass them
61
+ * to avoid double-formatting.
62
+ * @param {Object} data - Serialized error data
63
+ * @returns {PluginError}
64
+ */ static deserialize(data) {
65
+ // Use rawMessage if available, fallback to message
66
+ const messageToUse = data.rawMessage || data.message;
67
+ const error = new PluginError({
68
+ message: messageToUse,
69
+ pluginType: data.pluginType,
70
+ pluginName: data.pluginName,
71
+ configKey: data.configKey
72
+ });
73
+ // Set location separately to preserve it without re-formatting message
74
+ error.location = data.location;
75
+ // Preserve the formatted message if different from rawMessage
76
+ if (data.message && data.message !== messageToUse) {
77
+ error.message = data.message;
78
+ }
79
+ if (data.stack) {
80
+ error.stack = data.stack;
81
+ }
82
+ return error;
83
+ }
37
84
  /**
38
85
  * Creates a PluginError instance with formatted message.
39
- * @param {string} [message] - Error message (falls back to cause.message)
40
- * @param {Object} [options]
41
- * @param {Error} [options.cause] - The original error thrown by the plugin
42
- * @param {string} [options.typeName] - The config type name (e.g., '_if', 'SetState', 'MongoDBFind')
43
- * @param {*} [options.received] - The input that caused the error
44
- * @param {string} [options.location] - Where in the config the error occurred
45
- * @param {string} [options.configKey] - Config key (~k) for location resolution
46
- */ constructor(message, { cause, typeName, received, location, configKey } = {}){
47
- const rawMessage = message ?? cause?.message;
86
+ * @param {Object} params
87
+ * @param {Error} params.error - The original error thrown by the plugin
88
+ * @param {string} [params.pluginType] - Type of plugin (operator, action, block, request, connection)
89
+ * @param {string} [params.pluginName] - Name of the plugin (e.g., '_if', 'SetState')
90
+ * @param {*} [params.received] - The input that caused the error
91
+ * @param {string} [params.location] - Where in the config the error occurred
92
+ * @param {string} [params.configKey] - Config key (~k) for location resolution
93
+ */ constructor({ error, message, pluginType, pluginName, received, location, configKey }){
94
+ // Store raw message - logger formats received value
95
+ // Accept either error object or direct message string
96
+ const rawMessage = message ?? error?.message;
48
97
  let formattedMessage = rawMessage;
49
98
  if (location) {
50
- formattedMessage = rawMessage ? `${rawMessage} at ${location}.` : `at ${location}.`;
99
+ formattedMessage = rawMessage != null ? `${rawMessage} at ${location}.` : `at ${location}.`;
51
100
  }
52
101
  super(formattedMessage, {
53
- cause
102
+ cause: error
54
103
  });
55
104
  this.name = 'PluginError';
56
- this.isLowdefyError = true;
57
- this.typeName = typeName;
58
- this._message = rawMessage;
59
- this.received = received !== undefined ? received : cause?.received;
105
+ this.pluginType = pluginType;
106
+ this.pluginName = pluginName;
107
+ this.rawMessage = rawMessage; // Original message without location
108
+ this.received = received !== undefined ? received : error?.received;
60
109
  this.location = location;
61
- this.configKey = configKey ?? cause?.configKey ?? null;
62
- // Location outputs (set by server-side resolution)
110
+ this.configKey = error?.configKey ?? configKey ?? null;
111
+ // Location info (set by server-side resolution)
63
112
  this.source = null;
64
113
  this.config = null;
114
+ this.link = null;
115
+ if (error?.stack) {
116
+ this.stack = error.stack;
117
+ }
65
118
  }
66
119
  };
67
120
  export default PluginError;
@@ -32,8 +32,8 @@
32
32
  * Error class for external service failures (network, timeout, database, 5xx).
33
33
  *
34
34
  * ServiceError represents infrastructure/service issues that are NOT caused by
35
- * invalid configuration. The config location is still resolved to help developers
36
- * identify which request/connection triggered the service failure.
35
+ * invalid configuration. These errors should not include config location info
36
+ * since the config is correct - the external service is the problem.
37
37
  *
38
38
  * The message is formatted in the constructor - no format() method needed.
39
39
  *
@@ -43,12 +43,13 @@
43
43
  * return await fetch(url);
44
44
  * } catch (error) {
45
45
  * if (ServiceError.isServiceError(error)) {
46
- * throw new ServiceError(undefined, { cause: error, service: 'MongoDB' });
46
+ * throw ServiceError.from(error, 'MongoDB');
47
47
  * }
48
- * throw new PluginError(error.message, { cause: error, ... });
48
+ * throw new PluginError({ error, ... });
49
49
  * }
50
- * // error.message = "MongoDB: Connection refused. The service may be down..."
51
- */ let ServiceError = class ServiceError extends Error {
50
+ * // error.message = "[Service Error] MongoDB: Connection refused. The service may be down..."
51
+ */ import formatErrorMessage from './formatErrorMessage.js';
52
+ let ServiceError = class ServiceError extends Error {
52
53
  /**
53
54
  * Checks if an error is a service error (network issues, timeouts, 5xx).
54
55
  * @param {Error} error - The error to check
@@ -79,50 +80,82 @@
79
80
  const code = error.code;
80
81
  const statusCode = error.statusCode ?? error.status ?? error.response?.status;
81
82
  if (code === 'ECONNREFUSED') {
82
- return `Connection refused. The service may be down or the address may be incorrect. ${error.message ?? ''}`;
83
+ return `Connection refused. The service may be down or the address may be incorrect. ${error.message}`;
83
84
  }
84
85
  if (code === 'ENOTFOUND') {
85
- return `DNS lookup failed. The hostname could not be resolved. ${error.message ?? ''}`;
86
+ return `DNS lookup failed. The hostname could not be resolved. ${error.message}`;
86
87
  }
87
88
  if (code === 'ETIMEDOUT') {
88
- return `Connection timed out. The service may be slow or unreachable. ${error.message ?? ''}`;
89
+ return `Connection timed out. The service may be slow or unreachable. ${error.message}`;
89
90
  }
90
91
  if (code === 'ECONNRESET') {
91
- return `Connection reset by the server. ${error.message ?? ''}`;
92
+ return `Connection reset by the server. ${error.message}`;
92
93
  }
93
94
  if (statusCode && statusCode >= 500) {
94
- return `Server returned error ${statusCode}. ${error.message ?? ''}`;
95
+ return `Server returned error ${statusCode}. ${error.message}`;
95
96
  }
96
97
  return error.message;
97
98
  }
99
+ print() {
100
+ return formatErrorMessage(this);
101
+ }
102
+ /**
103
+ * Serializes the error for transport (e.g., client to server).
104
+ * @returns {Object} Serialized error data with type marker
105
+ */ serialize() {
106
+ return {
107
+ '~err': 'ServiceError',
108
+ message: this.message,
109
+ service: this.service,
110
+ code: this.code,
111
+ statusCode: this.statusCode
112
+ };
113
+ }
114
+ /**
115
+ * Deserializes error data back into a ServiceError.
116
+ * Note: message already contains service prefix, so we don't pass service
117
+ * to avoid double-prefixing.
118
+ * @param {Object} data - Serialized error data
119
+ * @returns {ServiceError}
120
+ */ static deserialize(data) {
121
+ const error = new ServiceError({
122
+ message: data.message,
123
+ code: data.code,
124
+ statusCode: data.statusCode
125
+ });
126
+ // Set service separately to preserve it without re-prefixing the message
127
+ error.service = data.service;
128
+ return error;
129
+ }
98
130
  /**
99
131
  * Creates a ServiceError instance with formatted message.
100
- * @param {string} [message] - Error message (falls back to enhanced cause message)
101
- * @param {Object} [options]
102
- * @param {Error} [options.cause] - Original error to wrap (auto-enhances message)
103
- * @param {string} [options.service] - Name of the service that failed
104
- * @param {string} [options.code] - Error code (e.g., 'ECONNREFUSED')
105
- * @param {number} [options.statusCode] - HTTP status code if applicable
106
- * @param {string} [options.configKey] - Config key for location resolution
107
- */ constructor(message, { cause, service, code, statusCode, configKey } = {}){
132
+ * @param {Object} params
133
+ * @param {string} [params.message] - The error message (required if no error)
134
+ * @param {Error} [params.error] - Original error to wrap (auto-enhances message)
135
+ * @param {string} [params.service] - Name of the service that failed
136
+ * @param {string} [params.code] - Error code (e.g., 'ECONNREFUSED')
137
+ * @param {number} [params.statusCode] - HTTP status code if applicable
138
+ * @param {string} [params.configKey] - Config key for location resolution
139
+ */ constructor({ message, error, service, code, statusCode, configKey }){
108
140
  // Extract info from wrapped error if provided
109
- const errorCode = code ?? cause?.code;
110
- const errorStatusCode = statusCode ?? cause?.statusCode ?? cause?.status ?? cause?.response?.status;
141
+ const errorCode = code ?? error?.code;
142
+ const errorStatusCode = statusCode ?? error?.statusCode ?? error?.status ?? error?.response?.status;
111
143
  // Use provided message, or enhance wrapped error's message
112
- const baseMessage = message ?? (cause ? ServiceError.enhanceMessage(cause) : 'Service error');
144
+ const baseMessage = message ?? (error ? ServiceError.enhanceMessage(error) : 'Service error');
113
145
  // Message without prefix - logger uses error.name for display
114
146
  // Include service in message if provided
115
147
  const formattedMessage = service ? `${service}: ${baseMessage}` : baseMessage;
116
148
  super(formattedMessage, {
117
- cause
149
+ cause: error
118
150
  });
119
151
  this.name = 'ServiceError';
120
- this.isLowdefyError = true;
121
- this._message = baseMessage;
122
152
  this.service = service;
123
153
  this.code = errorCode;
124
154
  this.statusCode = errorStatusCode;
125
155
  this.configKey = configKey ?? null;
156
+ if (error?.stack) {
157
+ this.stack = error.stack;
158
+ }
126
159
  }
127
160
  };
128
161
  export default ServiceError;
package/dist/UserError.js CHANGED
@@ -12,11 +12,14 @@
12
12
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
- */ let UserError = class UserError extends Error {
15
+ */ import formatErrorMessage from './formatErrorMessage.js';
16
+ let UserError = class UserError extends Error {
17
+ print() {
18
+ return formatErrorMessage(this);
19
+ }
16
20
  constructor(message, { blockId, metaData, pageId } = {}){
17
21
  super(message);
18
22
  this.name = 'UserError';
19
- this.isLowdefyError = true;
20
23
  this.blockId = blockId;
21
24
  this.metaData = metaData;
22
25
  this.pageId = pageId;
@@ -0,0 +1,119 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */ import BaseConfigError from '../ConfigError.js';
16
+ import ConfigMessage from './ConfigMessage.js';
17
+ /**
18
+ * Build-time configuration error class.
19
+ * Extends base ConfigError with synchronous location resolution using keyMap/refMap.
20
+ * All formatting happens in the constructor - properties are ready for the logger.
21
+ *
22
+ * @example
23
+ * // Standard usage with message and context
24
+ * throw new ConfigError({
25
+ * message: 'Connection id missing.',
26
+ * configKey: block['~k'],
27
+ * context,
28
+ * });
29
+ *
30
+ * @example
31
+ * // YAML parse error - pass error object instead of message
32
+ * throw new ConfigError({
33
+ * error: yamlParseError,
34
+ * filePath: 'lowdefy.yaml',
35
+ * configDirectory,
36
+ * });
37
+ */ let ConfigError = class ConfigError extends BaseConfigError {
38
+ /**
39
+ * Creates a ConfigError instance with build-time formatting.
40
+ * All location resolution happens here - no format() method needed.
41
+ *
42
+ * @param {Object} params
43
+ * @param {string} [params.message] - The error message (or use params.error for YAML errors)
44
+ * @param {Error} [params.error] - Original error (for YAML parse errors - extracts line number)
45
+ * @param {string} [params.configKey] - Config key (~k) for keyMap lookup
46
+ * @param {Object} [params.operatorLocation] - { ref, line } for direct refMap lookup
47
+ * @param {string} [params.filePath] - Direct file path (for raw mode)
48
+ * @param {number|string} [params.lineNumber] - Direct line number (for raw mode)
49
+ * @param {Object} [params.context] - Build context with keyMap, refMap, directories
50
+ * @param {string} [params.configDirectory] - Config directory (for raw mode without context)
51
+ * @param {string} [params.checkSlug] - The specific check being performed
52
+ * @param {*} [params.received] - The value that caused the error (for logger to format)
53
+ */ constructor({ message, error, configKey, operatorLocation, filePath, lineNumber, context, configDirectory, checkSlug, received }){
54
+ // Handle YAML parse errors - extract line number from error message
55
+ let finalMessage = message;
56
+ let finalLineNumber = lineNumber;
57
+ if (error instanceof Error) {
58
+ finalMessage = `Could not parse YAML. ${error.message}`;
59
+ const lineMatch = error.message.match(/at line (\d+)/);
60
+ finalLineNumber = lineMatch ? lineMatch[1] : null;
61
+ }
62
+ // Call base constructor with minimal info first
63
+ super({
64
+ message: finalMessage,
65
+ configKey,
66
+ checkSlug
67
+ });
68
+ // Store all properties for the logger
69
+ this.configKey = configKey ?? null;
70
+ this.checkSlug = checkSlug;
71
+ this.operatorLocation = operatorLocation;
72
+ this.received = received;
73
+ // Check for ~ignoreBuildChecks suppression
74
+ this.suppressed = ConfigMessage.shouldSuppress({
75
+ configKey,
76
+ keyMap: context?.keyMap,
77
+ checkSlug
78
+ });
79
+ if (this.suppressed) {
80
+ this.message = '';
81
+ this.source = null;
82
+ this.config = null;
83
+ this.link = null;
84
+ this.resolved = true;
85
+ return;
86
+ }
87
+ // Resolve location based on available info, falling through if a method returns null
88
+ let location = null;
89
+ const configDir = configDirectory ?? context?.directories?.config;
90
+ if (configKey && context?.keyMap) {
91
+ location = ConfigMessage.resolveLocation({
92
+ configKey,
93
+ context
94
+ });
95
+ }
96
+ if (!location && operatorLocation && context?.refMap) {
97
+ location = ConfigMessage.resolveOperatorLocation({
98
+ operatorLocation,
99
+ context
100
+ });
101
+ }
102
+ if (!location && filePath) {
103
+ location = ConfigMessage.resolveRawLocation({
104
+ filePath,
105
+ lineNumber: finalLineNumber,
106
+ configDirectory: configDir
107
+ });
108
+ }
109
+ // Set location properties
110
+ this.source = location?.source ?? null;
111
+ this.config = location?.config ?? null;
112
+ this.link = location?.link ?? null;
113
+ // Set message (no prefix - logger uses error.name for display)
114
+ this.message = finalMessage;
115
+ // Mark as resolved
116
+ this.resolved = true;
117
+ }
118
+ };
119
+ export default ConfigError;