@lowdefy/errors 5.6.0 → 6.1.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.
@@ -0,0 +1,28 @@
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
+ */ // Thrown when a request to a protected endpoint carries no successful
16
+ // authentication - the caller should fix its credentials (401). The server
17
+ // error handlers branch on the name before the structured-log and Sentry
18
+ // pipeline, so probing traffic produces a one-line warning, not error logs.
19
+ let AuthenticationError = class AuthenticationError extends Error {
20
+ constructor(message = 'Authentication required.', { cause } = {}){
21
+ super(message, {
22
+ cause
23
+ });
24
+ this.name = 'AuthenticationError';
25
+ this.isLowdefyError = true;
26
+ }
27
+ };
28
+ export default AuthenticationError;
@@ -12,7 +12,8 @@
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
- */ /**
15
+ */ import readErrorCodes from './readErrorCodes.js';
16
+ /**
16
17
  * Base error class for plugin failures (operators, actions, blocks, requests).
17
18
  *
18
19
  * Plugins throw plain Error objects with simple messages.
@@ -59,6 +60,12 @@
59
60
  this.received = received !== undefined ? received : cause?.received;
60
61
  this.location = location;
61
62
  this.configKey = configKey ?? cause?.configKey ?? null;
63
+ // Config branches on `code`/`statusCode` of the top-level error, which is
64
+ // this wrapper, so lift them from the plugin's error. One level only: each
65
+ // deeper cause is a node of its own and keeps its own codes.
66
+ const { code, statusCode } = readErrorCodes(cause);
67
+ if (code !== undefined) this.code = code;
68
+ if (statusCode !== undefined) this.statusCode = statusCode;
62
69
  // Location outputs (set by server-side resolution)
63
70
  this.source = null;
64
71
  this.config = null;
package/dist/index.js CHANGED
@@ -47,20 +47,34 @@
47
47
  * - Caught: Caller of the action/resolver; signals "the called routine deliberately failed" as distinct from a system fault
48
48
  * - Format: [User Error] message
49
49
  *
50
+ * 7. AuthenticationError - Unauthenticated request to a protected endpoint
51
+ * - Thrown: API/request authorization when no caller resolved
52
+ * - Caught: Server error handlers, before structured logging and Sentry (401)
53
+ * - Format: [AuthenticationError] message
54
+ *
50
55
  * Location Resolution Utilities:
51
56
  * resolveConfigLocation - Sync: configKey → {source, config} via keyMap/refMap
52
57
  * resolveErrorLocation - Sync: unified resolver (configKey or filePath/lineNumber)
53
58
  * loadAndResolveErrorLocation - Async: reads keyMap/refMap files at runtime
54
59
  * shouldSuppressBuildCheck - Check ~ignoreBuildChecks in parent chain
60
+ *
61
+ * Error Field Utilities:
62
+ * readErrorCodes - {code, statusCode} from one error node's own fields
63
+ * lowdefyErrorNames - Set of Lowdefy error class names
64
+ * lowdefyErrorTypes - Lowdefy error class by name, for reviving and reshaping errors
55
65
  */ import ActionError from './ActionError.js';
66
+ import AuthenticationError from './AuthenticationError.js';
56
67
  import BlockError from './BlockError.js';
57
68
  import BuildError from './BuildError.js';
58
69
  import ConfigError from './ConfigError.js';
59
70
  import ConfigWarning from './ConfigWarning.js';
60
71
  import errorToDisplayString from './errorToDisplayString.js';
61
72
  import LowdefyInternalError from './LowdefyInternalError.js';
73
+ import lowdefyErrorNames from './lowdefyErrorNames.js';
74
+ import lowdefyErrorTypes from './lowdefyErrorTypes.js';
62
75
  import OperatorError from './OperatorError.js';
63
76
  import PluginError from './PluginError.js';
77
+ import readErrorCodes from './readErrorCodes.js';
64
78
  import RequestError from './RequestError.js';
65
79
  import resolveConfigLocation from './resolveConfigLocation.js';
66
80
  import loadAndResolveErrorLocation from './loadAndResolveErrorLocation.js';
@@ -68,4 +82,4 @@ import resolveErrorLocation from './resolveErrorLocation.js';
68
82
  import ServiceError from './ServiceError.js';
69
83
  import shouldSuppressBuildCheck, { VALID_CHECK_SLUGS } from './shouldSuppressBuildCheck.js';
70
84
  import UserError from './UserError.js';
71
- export { ActionError, BlockError, BuildError, ConfigError, ConfigWarning, errorToDisplayString, LowdefyInternalError, OperatorError, PluginError, RequestError, resolveConfigLocation, loadAndResolveErrorLocation, resolveErrorLocation, ServiceError, shouldSuppressBuildCheck, UserError, VALID_CHECK_SLUGS };
85
+ export { ActionError, AuthenticationError, BlockError, BuildError, ConfigError, ConfigWarning, errorToDisplayString, LowdefyInternalError, lowdefyErrorNames, lowdefyErrorTypes, OperatorError, PluginError, readErrorCodes, RequestError, resolveConfigLocation, loadAndResolveErrorLocation, resolveErrorLocation, ServiceError, shouldSuppressBuildCheck, UserError, VALID_CHECK_SLUGS };
@@ -0,0 +1,33 @@
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
+ */ // Keyed on the class name rather than `isLowdefyError`, because an error rebuilt
16
+ // from a caught value can drop `isLowdefyError` yet must keep its class name.
17
+ const lowdefyErrorNames = new Set([
18
+ 'ActionError',
19
+ 'AuthenticationError',
20
+ 'AuthorizationError',
21
+ 'BlockError',
22
+ 'BuildError',
23
+ 'ConfigError',
24
+ 'ConfigWarning',
25
+ 'LowdefyInternalError',
26
+ 'OperatorError',
27
+ 'PluginError',
28
+ 'RequestError',
29
+ 'ServiceError',
30
+ 'TwoFactorEnrolmentRequiredError',
31
+ 'UserError'
32
+ ]);
33
+ export default lowdefyErrorNames;
@@ -0,0 +1,41 @@
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 ActionError from './ActionError.js';
16
+ import BlockError from './BlockError.js';
17
+ import BuildError from './BuildError.js';
18
+ import ConfigError from './ConfigError.js';
19
+ import ConfigWarning from './ConfigWarning.js';
20
+ import LowdefyInternalError from './LowdefyInternalError.js';
21
+ import OperatorError from './OperatorError.js';
22
+ import PluginError from './PluginError.js';
23
+ import RequestError from './RequestError.js';
24
+ import ServiceError from './ServiceError.js';
25
+ import UserError from './UserError.js';
26
+ // Name to class, for rebuilding an error whose class identity is gone but whose
27
+ // name survived - a serializer round trip, or a caught error being reshaped.
28
+ const lowdefyErrorTypes = {
29
+ ActionError,
30
+ BlockError,
31
+ BuildError,
32
+ ConfigError,
33
+ ConfigWarning,
34
+ LowdefyInternalError,
35
+ OperatorError,
36
+ PluginError,
37
+ RequestError,
38
+ ServiceError,
39
+ UserError
40
+ };
41
+ export default lowdefyErrorTypes;
@@ -0,0 +1,37 @@
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
+ */ // One rule over the conventions libraries already follow, instead of a
16
+ // per-driver table that goes stale: Node system errors, Postgres and Mongo set
17
+ // `code`; axios and most HTTP SDKs set `statusCode`, `status` or
18
+ // `response.status`. Non-number statuses are skipped so config branching on
19
+ // `statusCode` only ever compares against a number.
20
+ function readErrorCodes(node) {
21
+ if (node === null || typeof node !== 'object') {
22
+ return {
23
+ code: undefined,
24
+ statusCode: undefined
25
+ };
26
+ }
27
+ const statusCode = [
28
+ node.statusCode,
29
+ node.status,
30
+ node.response?.status
31
+ ].find((value)=>typeof value === 'number');
32
+ return {
33
+ code: node.code,
34
+ statusCode
35
+ };
36
+ }
37
+ export default readErrorCodes;
@@ -34,15 +34,34 @@
34
34
  * // source: '/Users/dev/myapp/pages/home.yaml:5',
35
35
  * // config: 'root.pages[0:home].blocks[0:header]',
36
36
  * // }
37
- */ function resolveConfigLocation({ configKey, keyMap, refMap, configDirectory }) {
37
+ */ // Not every ref is a file — a module invocation's ref has no path of its own
38
+ // (its content came from the invoking file's vars, and ~l line numbers point
39
+ // into that file). Walk the refMap parent chain to the nearest ref that is a
40
+ // real file; the root ref with no parent is lowdefy.yaml itself.
41
+ function resolveRefPath({ refId, refMap }) {
42
+ let currentId = refId;
43
+ const seen = new Set();
44
+ while(currentId && refMap?.[currentId] && !seen.has(currentId)){
45
+ seen.add(currentId);
46
+ const refEntry = refMap[currentId];
47
+ if (refEntry.path) {
48
+ return refEntry.path;
49
+ }
50
+ currentId = refEntry.parent;
51
+ }
52
+ return 'lowdefy.yaml';
53
+ }
54
+ function resolveConfigLocation({ configKey, keyMap, refMap, configDirectory }) {
38
55
  if (!configKey || !keyMap || !keyMap[configKey]) {
39
56
  return null;
40
57
  }
41
58
  const keyEntry = keyMap[configKey];
42
59
  const refId = keyEntry['~r'];
43
60
  const lineNumber = keyEntry['~l'];
44
- const refEntry = refMap?.[refId];
45
- const filePath = refEntry?.path || 'lowdefy.yaml';
61
+ const filePath = resolveRefPath({
62
+ refId,
63
+ refMap
64
+ });
46
65
  // config: the config path (e.g., "root.pages[0:home].blocks[0:header]")
47
66
  const config = keyEntry.key;
48
67
  // Use absolute path when configDirectory is available for clickable terminal links
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/errors",
3
- "version": "5.6.0",
3
+ "version": "6.1.0",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Lowdefy error classes for consistent error handling across build, server, and client",
6
6
  "homepage": "https://lowdefy.com",
@@ -35,8 +35,8 @@
35
35
  ],
36
36
  "devDependencies": {
37
37
  "@jest/globals": "28.1.3",
38
- "@swc/cli": "0.8.0",
39
- "@swc/core": "1.15.18",
38
+ "@swc/cli": "0.8.1",
39
+ "@swc/core": "1.15.32",
40
40
  "@swc/jest": "0.2.39",
41
41
  "jest": "28.1.3"
42
42
  },