@lowdefy/errors 0.0.0-experimental-20260202125814

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,120 @@
1
+ /*
2
+ Copyright 2020-2024 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
88
+ let location = null;
89
+ const configDir = configDirectory ?? context?.directories?.config;
90
+ if (configKey && context?.keyMap) {
91
+ // Mode 1: Use configKey -> keyMap -> refMap path (standard case after addKeys)
92
+ location = ConfigMessage.resolveLocation({
93
+ configKey,
94
+ context
95
+ });
96
+ } else if (operatorLocation && context?.refMap) {
97
+ // Mode 2: Use operatorLocation directly with refMap (early build stages)
98
+ location = ConfigMessage.resolveOperatorLocation({
99
+ operatorLocation,
100
+ context
101
+ });
102
+ } else if (filePath) {
103
+ // Mode 3: Use raw filePath/lineNumber (YAML parse errors, etc.)
104
+ location = ConfigMessage.resolveRawLocation({
105
+ filePath,
106
+ lineNumber: finalLineNumber,
107
+ configDirectory: configDir
108
+ });
109
+ }
110
+ // Set location properties
111
+ this.source = location?.source ?? null;
112
+ this.config = location?.config ?? null;
113
+ this.link = location?.link ?? null;
114
+ // Set message (no prefix - logger uses error.name for display)
115
+ this.message = finalMessage;
116
+ // Mark as resolved
117
+ this.resolved = true;
118
+ }
119
+ };
120
+ export default ConfigError;
@@ -0,0 +1,122 @@
1
+ /*
2
+ Copyright 2020-2024 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 path from 'path';
16
+ import resolveConfigLocation from './resolveConfigLocation.js';
17
+ /**
18
+ * Valid check slugs for ~ignoreBuildChecks.
19
+ * Keys are the slug names, values are descriptions for error messages.
20
+ * These suppress BUILD-TIME validation only - runtime errors still occur.
21
+ */ export const VALID_CHECK_SLUGS = {
22
+ 'state-refs': 'Undefined _state reference warnings',
23
+ 'payload-refs': 'Undefined _payload reference warnings',
24
+ 'step-refs': 'Undefined _step reference warnings',
25
+ 'link-refs': 'Invalid Link action page reference warnings',
26
+ 'request-refs': 'Invalid Request action reference warnings',
27
+ 'connection-refs': 'Nonexistent connection ID references',
28
+ types: 'All type validation (blocks, operators, actions, requests, connections)',
29
+ schema: 'JSON schema validation errors'
30
+ };
31
+ /**
32
+ * Base class for config message utilities.
33
+ * Provides shared utilities for ConfigError and ConfigWarning.
34
+ */ let ConfigMessage = class ConfigMessage {
35
+ /**
36
+ * Checks if a message should be suppressed based on ~ignoreBuildChecks.
37
+ * Walks up the parent chain looking for suppressions that cover this check.
38
+ * This walk happens ONLY when an error/warning is about to be logged.
39
+ *
40
+ * @param {Object} params
41
+ * @param {string} params.configKey - Config key (~k) of the error location
42
+ * @param {Object} params.keyMap - The keyMap from build context
43
+ * @param {string} [params.checkSlug] - The specific check being performed (e.g., 'state-refs')
44
+ * @returns {boolean} True if message should be suppressed
45
+ */ static shouldSuppress({ configKey, keyMap, checkSlug }) {
46
+ if (!configKey || !keyMap) return false;
47
+ let currentKey = configKey;
48
+ let depth = 0;
49
+ const MAX_DEPTH = 100; // Guard against circular parents
50
+ while(currentKey && depth < MAX_DEPTH){
51
+ const entry = keyMap[currentKey];
52
+ if (!entry) break;
53
+ const ignoredChecks = entry['~ignoreBuildChecks'];
54
+ if (ignoredChecks === true) {
55
+ return true;
56
+ }
57
+ if (Array.isArray(ignoredChecks) && checkSlug && ignoredChecks.includes(checkSlug)) {
58
+ return true;
59
+ }
60
+ currentKey = entry['~k_parent'];
61
+ depth++;
62
+ }
63
+ return false;
64
+ }
65
+ /**
66
+ * Resolves location from configKey using keyMap and refMap.
67
+ * @param {Object} params
68
+ * @param {string} params.configKey - Config key (~k) for keyMap lookup
69
+ * @param {Object} params.context - Build context with keyMap, refMap, directories
70
+ * @returns {Object|null} Location object { source, config, link } or null
71
+ */ static resolveLocation({ configKey, context }) {
72
+ if (!configKey || !context?.keyMap) return null;
73
+ return resolveConfigLocation({
74
+ configKey,
75
+ keyMap: context.keyMap,
76
+ refMap: context.refMap,
77
+ configDirectory: context.directories?.config
78
+ });
79
+ }
80
+ /**
81
+ * Resolves location directly from operatorLocation (ref + line) without keyMap.
82
+ * Used during early build stages (buildRefs) when keyMap doesn't exist yet.
83
+ * @param {Object} params
84
+ * @param {Object} params.operatorLocation - { ref, line }
85
+ * @param {Object} params.context - Build context with refMap, directories
86
+ * @returns {Object|null} Location object { source, link } or null
87
+ */ static resolveOperatorLocation({ operatorLocation, context }) {
88
+ if (!operatorLocation) return null;
89
+ const refEntry = context?.refMap?.[operatorLocation.ref];
90
+ const filePath = refEntry?.path ?? 'lowdefy.yaml';
91
+ const lineNumber = operatorLocation.line;
92
+ const source = lineNumber ? `${filePath}:${lineNumber}` : filePath;
93
+ let link = null;
94
+ if (context?.directories?.config) {
95
+ link = path.join(context.directories.config, filePath) + (lineNumber ? `:${lineNumber}` : '');
96
+ }
97
+ return {
98
+ source,
99
+ link
100
+ };
101
+ }
102
+ /**
103
+ * Resolves location from raw filePath and lineNumber.
104
+ * Used for YAML parse errors and other cases without config context.
105
+ * @param {Object} params
106
+ * @param {string} params.filePath - Direct file path
107
+ * @param {number|string} [params.lineNumber] - Direct line number
108
+ * @param {string} [params.configDirectory] - Config directory for link
109
+ * @returns {Object} Location object { source, link }
110
+ */ static resolveRawLocation({ filePath, lineNumber, configDirectory }) {
111
+ const source = lineNumber ? `${filePath}:${lineNumber}` : filePath;
112
+ let link = null;
113
+ if (configDirectory) {
114
+ link = path.join(configDirectory, filePath) + (lineNumber ? `:${lineNumber}` : '');
115
+ }
116
+ return {
117
+ source,
118
+ link
119
+ };
120
+ }
121
+ };
122
+ export default ConfigMessage;
@@ -0,0 +1,104 @@
1
+ /*
2
+ Copyright 2020-2024 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 ConfigError from './ConfigError.js';
16
+ import ConfigMessage from './ConfigMessage.js';
17
+ /**
18
+ * Build-time configuration warning class.
19
+ * All formatting happens in the constructor - properties are ready for the logger.
20
+ *
21
+ * In development: Creates a warning with source and message properties
22
+ * In production with prodError: Throws ConfigError instead
23
+ *
24
+ * @example
25
+ * const warning = new ConfigWarning({ message, configKey, context });
26
+ * if (!warning.suppressed) {
27
+ * logger.warn({ source: warning.source }, warning.message);
28
+ * }
29
+ *
30
+ * @example
31
+ * // Throws ConfigError in prod mode when prodError is true
32
+ * new ConfigWarning({ message, configKey, context, prodError: true });
33
+ */ let ConfigWarning = class ConfigWarning {
34
+ /**
35
+ * @param {Object} params
36
+ * @param {string} params.message - The warning message
37
+ * @param {string} [params.configKey] - Config key (~k) for location resolution
38
+ * @param {Object} [params.operatorLocation] - { ref, line } for direct refMap lookup
39
+ * @param {string} [params.filePath] - Direct file path (for raw mode)
40
+ * @param {number|string} [params.lineNumber] - Direct line number (for raw mode)
41
+ * @param {Object} [params.context] - Build context with keyMap, refMap, directories, stage
42
+ * @param {string} [params.configDirectory] - Config directory (for raw mode without context)
43
+ * @param {string} [params.checkSlug] - The specific check being performed (e.g., 'state-refs')
44
+ * @param {boolean} [params.prodError] - If true, throw ConfigError in prod mode
45
+ * @param {*} [params.received] - The value that caused the warning (for logger to format)
46
+ * @throws {ConfigError} When prodError is true and context.stage is 'prod'
47
+ */ constructor({ message, configKey, operatorLocation, filePath, lineNumber, context, configDirectory, checkSlug, prodError, received }){
48
+ // In prod mode with prodError flag, throw ConfigError instead
49
+ if (prodError && context?.stage === 'prod') {
50
+ throw new ConfigError({
51
+ message,
52
+ configKey,
53
+ operatorLocation,
54
+ context,
55
+ checkSlug,
56
+ received
57
+ });
58
+ }
59
+ // Store all properties for the logger
60
+ this.configKey = configKey ?? null;
61
+ this.checkSlug = checkSlug;
62
+ this.received = received;
63
+ // Check for ~ignoreBuildChecks suppression
64
+ this.suppressed = ConfigMessage.shouldSuppress({
65
+ configKey,
66
+ keyMap: context?.keyMap,
67
+ checkSlug
68
+ });
69
+ if (this.suppressed) {
70
+ this.message = '';
71
+ this.source = null;
72
+ this.config = null;
73
+ this.link = null;
74
+ return;
75
+ }
76
+ // Resolve location based on available info
77
+ let location = null;
78
+ const configDir = configDirectory ?? context?.directories?.config;
79
+ if (configKey && context?.keyMap) {
80
+ location = ConfigMessage.resolveLocation({
81
+ configKey,
82
+ context
83
+ });
84
+ } else if (operatorLocation && context?.refMap) {
85
+ location = ConfigMessage.resolveOperatorLocation({
86
+ operatorLocation,
87
+ context
88
+ });
89
+ } else if (filePath) {
90
+ location = ConfigMessage.resolveRawLocation({
91
+ filePath,
92
+ lineNumber,
93
+ configDirectory: configDir
94
+ });
95
+ }
96
+ // Set location properties
97
+ this.source = location?.source ?? null;
98
+ this.config = location?.config ?? null;
99
+ this.link = location?.link ?? null;
100
+ // Set message (no prefix - logger uses class name for display)
101
+ this.message = message;
102
+ }
103
+ };
104
+ export default ConfigWarning;
@@ -0,0 +1,37 @@
1
+ /*
2
+ Copyright 2020-2024 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
+ */ /**
16
+ * @lowdefy/errors/build - Build-time error classes.
17
+ *
18
+ * Use this entry point for build-time code that needs synchronous location resolution
19
+ * using keyMap and refMap from the build context.
20
+ *
21
+ * @example
22
+ * import { ConfigError, ConfigWarning, ConfigMessage } from '@lowdefy/errors/build';
23
+ *
24
+ * throw new ConfigError({
25
+ * message: 'Connection id missing.',
26
+ * configKey: block['~k'],
27
+ * context,
28
+ * });
29
+ */ import ConfigError from './ConfigError.js';
30
+ import ConfigWarning from './ConfigWarning.js';
31
+ import ConfigMessage, { VALID_CHECK_SLUGS } from './ConfigMessage.js';
32
+ import resolveConfigLocation from './resolveConfigLocation.js';
33
+ import resolveErrorConfigLocation from './resolveErrorConfigLocation.js';
34
+ import LowdefyError from '../LowdefyError.js';
35
+ import PluginError from '../PluginError.js';
36
+ import ServiceError from '../ServiceError.js';
37
+ export { ConfigError, ConfigMessage, ConfigWarning, LowdefyError, PluginError, resolveConfigLocation, resolveErrorConfigLocation, ServiceError, VALID_CHECK_SLUGS };
@@ -0,0 +1,63 @@
1
+ /*
2
+ Copyright 2020-2024 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 path from 'path';
16
+ /**
17
+ * Resolves a config key (~k) to source and config location.
18
+ *
19
+ * @param {Object} params
20
+ * @param {string} params.configKey - The ~k value from the config object
21
+ * @param {Object} params.keyMap - The keyMap from build output
22
+ * @param {Object} params.refMap - The refMap from build output
23
+ * @param {string} [params.configDirectory] - Absolute path to config directory for clickable links
24
+ * @returns {Object|null} Location object with source, config, and link, or null if not resolvable
25
+ *
26
+ * @example
27
+ * const location = resolveConfigLocation({
28
+ * configKey: 'abc123',
29
+ * keyMap: { 'abc123': { key: 'root.pages[0:home].blocks[0:header]', '~r': 'ref1', '~l': 5 } },
30
+ * refMap: { 'ref1': { path: 'pages/home.yaml' } },
31
+ * configDirectory: '/Users/dev/myapp'
32
+ * });
33
+ * // Returns: {
34
+ * // source: 'pages/home.yaml:5',
35
+ * // config: 'root.pages[0:home].blocks[0:header]',
36
+ * // link: '/Users/dev/myapp/pages/home.yaml:5'
37
+ * // }
38
+ */ function resolveConfigLocation({ configKey, keyMap, refMap, configDirectory }) {
39
+ if (!configKey || !keyMap || !keyMap[configKey]) {
40
+ return null;
41
+ }
42
+ const keyEntry = keyMap[configKey];
43
+ const refId = keyEntry['~r'];
44
+ const lineNumber = keyEntry['~l'];
45
+ const refEntry = refMap?.[refId];
46
+ const filePath = refEntry?.path || 'lowdefy.yaml';
47
+ // source: filepath:line (e.g., "lowdefy.yaml:16")
48
+ const source = lineNumber ? `${filePath}:${lineNumber}` : filePath;
49
+ // config: the config path (e.g., "root.pages[0:home].blocks[0:header]")
50
+ const config = keyEntry.key;
51
+ // Absolute path for clickable links in VSCode terminal
52
+ let link = null;
53
+ if (configDirectory) {
54
+ const absolutePath = path.resolve(configDirectory, filePath);
55
+ link = lineNumber ? `${absolutePath}:${lineNumber}` : absolutePath;
56
+ }
57
+ return {
58
+ source,
59
+ config,
60
+ link
61
+ };
62
+ }
63
+ export default resolveConfigLocation;
@@ -0,0 +1,45 @@
1
+ /*
2
+ Copyright 2020-2024 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 resolveConfigLocation from './resolveConfigLocation.js';
16
+ /**
17
+ * Resolves error config location at runtime by reading keyMap and refMap files.
18
+ * Used by server-side error logging to trace errors back to source files.
19
+ *
20
+ * @param {Object} params
21
+ * @param {Object} params.error - Error object with optional configKey property
22
+ * @param {Function} params.readConfigFile - Async function to read config files
23
+ * @param {string} params.configDirectory - Absolute path to config directory
24
+ * @returns {Promise<Object|null>} Location object with source, config, and link, or null
25
+ */ async function resolveErrorConfigLocation({ error, readConfigFile, configDirectory }) {
26
+ if (!error?.configKey) {
27
+ return null;
28
+ }
29
+ try {
30
+ const [keyMap, refMap] = await Promise.all([
31
+ readConfigFile('keyMap.json'),
32
+ readConfigFile('refMap.json')
33
+ ]);
34
+ const location = resolveConfigLocation({
35
+ configKey: error.configKey,
36
+ keyMap,
37
+ refMap,
38
+ configDirectory
39
+ });
40
+ return location || null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+ export default resolveErrorConfigLocation;
@@ -0,0 +1,79 @@
1
+ /*
2
+ Copyright 2020-2024 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
+ /**
17
+ * Client-side ConfigError with async location resolution via API.
18
+ *
19
+ * Extends the base ConfigError to add async resolution that calls
20
+ * the /api/client-error endpoint to resolve configKey to file:line.
21
+ *
22
+ * @example
23
+ * const error = new ConfigError({ message: 'Invalid operator', configKey });
24
+ * await error.resolve(lowdefy);
25
+ * console.error(error.source); // "pages/home.yaml:42"
26
+ * console.error(error.message); // "Invalid operator"
27
+ */ let ConfigError = class ConfigError extends BaseConfigError {
28
+ /**
29
+ * Resolves location from server (async).
30
+ * Updates this.message with the resolved location.
31
+ * @param {Object} lowdefy - Lowdefy context with basePath and pageId
32
+ * @param {Object} [options]
33
+ * @param {number} [options.timeout=1000] - Fetch timeout in ms
34
+ * @returns {Promise<ConfigError>} Returns this for chaining
35
+ */ async resolve(lowdefy, { timeout = 1000 } = {}) {
36
+ // basePath can be empty string "" which is valid (no custom base path)
37
+ if (this.resolved || lowdefy?.basePath === undefined) {
38
+ this.resolved = true;
39
+ return this;
40
+ }
41
+ const controller = new AbortController();
42
+ const timeoutId = setTimeout(()=>controller.abort(), timeout);
43
+ try {
44
+ const response = await fetch(`${lowdefy.basePath}/api/client-error`, {
45
+ method: 'POST',
46
+ headers: {
47
+ 'Content-Type': 'application/json'
48
+ },
49
+ body: JSON.stringify(this.serialize()),
50
+ signal: controller.signal,
51
+ credentials: 'same-origin'
52
+ });
53
+ clearTimeout(timeoutId);
54
+ if (response.ok) {
55
+ const result = await response.json();
56
+ this.source = result.source;
57
+ this.config = result.config;
58
+ this.link = result.link;
59
+ }
60
+ } catch {
61
+ clearTimeout(timeoutId);
62
+ // Resolution failed (timeout or network error) - continue without location
63
+ }
64
+ this.resolved = true;
65
+ return this;
66
+ }
67
+ /**
68
+ * Resolves location (if not already resolved) and logs to console.
69
+ * @param {Object} [lowdefy] - Lowdefy context for resolution
70
+ * @param {Object} [options] - Options for resolution
71
+ * @returns {Promise<void>}
72
+ */ async log(lowdefy, options) {
73
+ if (!this.resolved && lowdefy) {
74
+ await this.resolve(lowdefy, options);
75
+ }
76
+ console.error(this.message);
77
+ }
78
+ };
79
+ export default ConfigError;
@@ -0,0 +1,30 @@
1
+ /*
2
+ Copyright 2020-2024 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
+ */ /**
16
+ * @lowdefy/errors/client - Client-side error classes.
17
+ *
18
+ * Use this entry point for client-side (browser) code that needs async location resolution.
19
+ *
20
+ * @example
21
+ * import { ConfigError, PluginError } from '@lowdefy/errors/client';
22
+ *
23
+ * const error = new ConfigError({ message: 'Invalid operator', configKey });
24
+ * await error.resolve(lowdefy);
25
+ */ import ConfigError from './ConfigError.js';
26
+ import ConfigWarning from '../ConfigWarning.js';
27
+ import LowdefyError from '../LowdefyError.js';
28
+ import PluginError from '../PluginError.js';
29
+ import ServiceError from '../ServiceError.js';
30
+ export { ConfigError, ConfigWarning, LowdefyError, PluginError, ServiceError };
@@ -0,0 +1,36 @@
1
+ /*
2
+ Copyright 2020-2024 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 ConfigError from './ConfigError.js';
16
+ import LowdefyError from './LowdefyError.js';
17
+ import PluginError from './PluginError.js';
18
+ import ServiceError from './ServiceError.js';
19
+ const errorTypes = {
20
+ ConfigError,
21
+ LowdefyError,
22
+ PluginError,
23
+ ServiceError
24
+ };
25
+ /**
26
+ * Deserializes error data back into the appropriate error class.
27
+ * @param {Object} data - Serialized error data with ~err type marker
28
+ * @returns {Error} The deserialized error instance
29
+ */ function deserializeError(data) {
30
+ const ErrorClass = errorTypes[data['~err']];
31
+ if (!ErrorClass) {
32
+ throw new Error(`Unknown error type: ${data['~err']}`);
33
+ }
34
+ return ErrorClass.deserialize(data);
35
+ }
36
+ export default deserializeError;