@lowdefy/api 4.5.2 → 4.7.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.
Files changed (67) hide show
  1. package/dist/context/createApiContext.js +1 -1
  2. package/dist/context/createAuthorize.js +8 -4
  3. package/dist/context/createEvaluateOperators.js +16 -2
  4. package/dist/context/createReadConfigFile.js +3 -3
  5. package/dist/index.js +4 -3
  6. package/dist/routes/auth/callbacks/addUserFieldsToSession.js +1 -1
  7. package/dist/routes/auth/callbacks/addUserFieldsToToken.js +1 -1
  8. package/dist/routes/auth/callbacks/createCallbackPlugins.js +1 -1
  9. package/dist/routes/auth/callbacks/createCallbacks.js +1 -1
  10. package/dist/routes/auth/callbacks/createJWTCallback.js +1 -1
  11. package/dist/routes/auth/callbacks/createRedirectCallback.js +1 -1
  12. package/dist/routes/auth/callbacks/createSessionCallback.js +1 -1
  13. package/dist/routes/auth/callbacks/createSignInCallback.js +1 -1
  14. package/dist/routes/auth/createAdapter.js +1 -1
  15. package/dist/routes/auth/createLogger.js +21 -17
  16. package/dist/routes/auth/createProviders.js +1 -1
  17. package/dist/routes/auth/events/createCreateUserEvent.js +1 -1
  18. package/dist/routes/auth/events/createEventPlugins.js +1 -1
  19. package/dist/routes/auth/events/createEvents.js +1 -1
  20. package/dist/routes/auth/events/createLinkAccountEvent.js +1 -1
  21. package/dist/routes/auth/events/createSessionEvent.js +1 -1
  22. package/dist/routes/auth/events/createSignInEvent.js +1 -1
  23. package/dist/routes/auth/events/createSignOutEvent.js +1 -1
  24. package/dist/routes/auth/events/createUpdateUserEvent.js +1 -1
  25. package/dist/routes/auth/getNextAuthConfig.js +2 -2
  26. package/dist/routes/endpoints/addStepResult.js +1 -1
  27. package/dist/routes/endpoints/authorizeApiEndpoint.js +3 -3
  28. package/dist/routes/endpoints/callEndpoint.js +1 -1
  29. package/dist/routes/endpoints/control/controlFor.js +11 -7
  30. package/dist/routes/endpoints/control/controlIf.js +4 -4
  31. package/dist/routes/endpoints/control/controlLog.js +11 -6
  32. package/dist/routes/endpoints/control/controlParallel.js +1 -1
  33. package/dist/routes/endpoints/control/controlParallelFor.js +11 -7
  34. package/dist/routes/endpoints/control/controlReject.js +4 -3
  35. package/dist/routes/endpoints/control/controlReturn.js +2 -2
  36. package/dist/routes/endpoints/control/controlSetState.js +1 -1
  37. package/dist/routes/endpoints/control/controlSwitch.js +4 -4
  38. package/dist/routes/endpoints/control/controlThrow.js +4 -3
  39. package/dist/routes/endpoints/control/controlTry.js +1 -1
  40. package/dist/routes/endpoints/control/handleControl.js +1 -1
  41. package/dist/routes/endpoints/getEndpointConfig.js +3 -3
  42. package/dist/routes/endpoints/handleRequest.js +1 -1
  43. package/dist/routes/endpoints/runRoutine.js +2 -2
  44. package/dist/routes/log/formatValidationError.js +31 -0
  45. package/dist/routes/log/logClientError.js +109 -0
  46. package/dist/routes/log/validatePluginSchema.js +25 -0
  47. package/dist/routes/page/getPageConfig.js +6 -5
  48. package/dist/routes/request/authorizeRequest.js +5 -3
  49. package/dist/routes/request/callRequest.js +1 -1
  50. package/dist/routes/request/callRequestResolver.js +43 -6
  51. package/dist/routes/request/checkConnectionRead.js +8 -4
  52. package/dist/routes/request/checkConnectionWrite.js +8 -4
  53. package/dist/routes/request/evaluateOperators.js +1 -1
  54. package/dist/routes/request/getConnection.js +5 -3
  55. package/dist/routes/request/getConnectionConfig.js +8 -4
  56. package/dist/routes/request/getRequestConfig.js +3 -3
  57. package/dist/routes/request/getRequestResolver.js +5 -3
  58. package/dist/routes/request/validateSchemas.js +39 -20
  59. package/dist/routes/rootConfig/getHomeAndMenus.js +1 -1
  60. package/dist/routes/rootConfig/getLowdefyGlobal.js +1 -1
  61. package/dist/routes/rootConfig/getRootConfig.js +1 -1
  62. package/dist/routes/rootConfig/menus/filterMenuList.js +1 -1
  63. package/dist/routes/rootConfig/menus/filterMenus.js +1 -1
  64. package/dist/routes/rootConfig/menus/getMenus.js +1 -1
  65. package/dist/test/testContext.js +4 -1
  66. package/package.json +8 -7
  67. package/dist/context/errors.js +0 -33
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,13 +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
- */ import { ServerError } from './errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function createAuthorize({ session }) {
17
17
  // Next-auth getSession provides a session object if the user is authenticated
18
18
  // else session will be null
19
19
  const authenticated = !!session;
20
20
  const roles = session?.user?.roles ?? [];
21
- function authorize({ auth }) {
21
+ function authorize(config) {
22
+ const { auth } = config;
22
23
  if (auth.public === true) return true;
23
24
  if (auth.public === false) {
24
25
  if (auth.roles) {
@@ -26,7 +27,10 @@ function createAuthorize({ session }) {
26
27
  }
27
28
  return authenticated;
28
29
  }
29
- throw new ServerError('Invalid auth configuration');
30
+ throw new ConfigError('auth.public must be true or false.', {
31
+ received: auth.public,
32
+ configKey: config['~k']
33
+ });
30
34
  }
31
35
  return authorize;
32
36
  }
@@ -1,4 +1,18 @@
1
- import { ServerParser } from '@lowdefy/operators';
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 { ServerParser } from '@lowdefy/operators';
2
16
  function createEvaluateOperators(context) {
3
17
  const { jsMap, operators, payload, secrets, state, steps, user } = context;
4
18
  const operatorsParser = new ServerParser({
@@ -17,7 +31,7 @@ function createEvaluateOperators(context) {
17
31
  location
18
32
  });
19
33
  if (errors.length > 0) {
20
- throw new Error(errors[0]);
34
+ throw errors[0];
21
35
  }
22
36
  return output;
23
37
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -13,13 +13,13 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import path from 'path';
16
- import { cachedPromises } from '@lowdefy/helpers';
16
+ import { cachedPromises, serializer } from '@lowdefy/helpers';
17
17
  import { getFileExtension, readFile } from '@lowdefy/node-utils';
18
18
  function createReadConfigFile({ buildDirectory, fileCache }) {
19
19
  async function readConfigFile(filePath) {
20
20
  const fileContent = await readFile(path.resolve(buildDirectory, filePath));
21
21
  if (getFileExtension(filePath) === 'json') {
22
- return JSON.parse(fileContent);
22
+ return serializer.deserializeFromString(fileContent);
23
23
  }
24
24
  return fileContent;
25
25
  }
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -15,9 +15,10 @@
15
15
  */ import callEndpoint from './routes/endpoints/callEndpoint.js';
16
16
  import callRequest from './routes/request/callRequest.js';
17
17
  import createApiContext from './context/createApiContext.js';
18
+ import createSessionCallback from './routes/auth/callbacks/createSessionCallback.js';
18
19
  import getHomeAndMenus from './routes/rootConfig/getHomeAndMenus.js';
19
20
  import getNextAuthConfig from './routes/auth/getNextAuthConfig.js';
20
21
  import getPageConfig from './routes/page/getPageConfig.js';
21
22
  import getRootConfig from './routes/rootConfig/getRootConfig.js';
22
- import { ConfigurationError, RequestError, ServerError } from './context/errors.js';
23
- export { callEndpoint, callRequest, ConfigurationError, createApiContext, getHomeAndMenus, getNextAuthConfig, getPageConfig, getRootConfig, RequestError, ServerError };
23
+ import logClientError from './routes/log/logClientError.js';
24
+ export { callEndpoint, callRequest, createApiContext, createSessionCallback, getHomeAndMenus, getNextAuthConfig, getPageConfig, getRootConfig, logClientError };
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,23 +12,27 @@
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
- */ function createLogger({ logger }) {
15
+ */ const authWarnings = {
16
+ NEXTAUTH_URL: 'NEXTAUTH_URL - Environment variable NEXTAUTH_URL is missing. Set it in your .env file. On Vercel, VERCEL_URL is used automatically.',
17
+ NO_SECRET: 'NO_SECRET - No `secret` configured. In development a temporary secret is generated, but production requires an explicit secret.',
18
+ DEBUG_ENABLED: 'DEBUG_ENABLED - NextAuth debug mode is enabled. This is meant for development only — remove it before deploying to production.',
19
+ EXPERIMENTAL_API: 'EXPERIMENTAL_API - An experimental NextAuth API is in use. It may change or be removed in a future version.'
20
+ };
21
+ function createLogger({ logger }) {
16
22
  return {
17
- error: (code, metadata)=>logger.error({
18
- code,
19
- metadata,
20
- event: 'auth_error'
21
- }),
22
- warn: (code, metadata)=>logger.warn({
23
- code,
24
- metadata,
25
- event: 'auth_warning'
26
- }),
27
- debug: (code, metadata)=>logger.debug({
28
- code,
29
- metadata,
30
- event: 'auth_debug'
31
- })
23
+ error: (code, metadata)=>{
24
+ const error = metadata instanceof Error ? metadata : metadata?.error;
25
+ if (error) {
26
+ error.code = code;
27
+ }
28
+ logger.error(error);
29
+ },
30
+ warn: (code)=>{
31
+ logger.warn(authWarnings[code] ?? code);
32
+ },
33
+ debug: (code, metadata)=>{
34
+ logger.debug(metadata, code);
35
+ }
32
36
  };
33
37
  }
34
38
  export default createLogger;
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -36,7 +36,7 @@ function getNextAuthConfig({ authJson, logger, plugins, secrets }) {
36
36
  location: 'auth'
37
37
  });
38
38
  if (operatorErrors.length > 0) {
39
- throw new Error(operatorErrors[0]);
39
+ throw operatorErrors[0];
40
40
  }
41
41
  nextAuthConfig.adapter = createAdapter({
42
42
  authConfig,
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
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 { ConfigurationError } from '../../context/errors.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
16
  function authorizeApiEndpoint({ authorize, logger }, { endpointConfig }) {
17
17
  if (!authorize(endpointConfig)) {
18
18
  logger.debug({
@@ -20,7 +20,7 @@ function authorizeApiEndpoint({ authorize, logger }, { endpointConfig }) {
20
20
  authorized: false,
21
21
  auth_config: endpointConfig.auth
22
22
  });
23
- throw new ConfigurationError(`API Endpoint "${endpointConfig.endpointId}" does not exist.`);
23
+ throw new ConfigError(`API Endpoint "${endpointConfig.endpointId}" does not exist.`);
24
24
  }
25
25
  logger.debug({
26
26
  event: 'debug_api_authorize',
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,18 +12,19 @@
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 runRoutine from '../runRoutine.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
+ import runRoutine from '../runRoutine.js';
16
17
  async function controlFor(context, routineContext, { control }) {
17
- const { logger, evaluateOperators } = context;
18
+ const { endpointId, logger, evaluateOperators } = context;
18
19
  const { items } = routineContext;
19
20
  const itemName = control[':for'];
20
21
  if (!itemName) {
21
- throw new Error('Invalid :for - missing variable name in :for.');
22
+ throw new Error(`Invalid :for in endpoint "${endpointId}" - missing variable name in :for.`);
22
23
  }
23
24
  const array = evaluateOperators({
24
25
  input: control[':in'],
25
26
  items,
26
- location: 'controlFor'
27
+ location: control['~k'] ?? ':for'
27
28
  });
28
29
  logger.debug({
29
30
  event: 'debug_control_for',
@@ -31,10 +32,13 @@ async function controlFor(context, routineContext, { control }) {
31
32
  itemName
32
33
  });
33
34
  if (!Array.isArray(array)) {
34
- throw new Error('Invalid :for - evaluated :in to non-array.');
35
+ throw new ConfigError(`Invalid :for in endpoint "${endpointId}" - :in must evaluate to an array.`, {
36
+ received: array,
37
+ configKey: control['~k']
38
+ });
35
39
  }
36
40
  if (!control[':do']) {
37
- throw new Error('Invalid :for - missing :do.');
41
+ throw new Error(`Invalid :for in endpoint "${endpointId}" - missing :do.`);
38
42
  }
39
43
  for (const [index, item] of array.entries()){
40
44
  const updatedItems = {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -14,12 +14,12 @@
14
14
  limitations under the License.
15
15
  */ import runRoutine from '../runRoutine.js';
16
16
  async function controlIf(context, routineContext, { control }) {
17
- const { logger, evaluateOperators } = context;
17
+ const { endpointId, logger, evaluateOperators } = context;
18
18
  const { items } = routineContext;
19
19
  const evaluatedIf = evaluateOperators({
20
20
  input: control[':if'],
21
21
  items,
22
- location: 'TODO:'
22
+ location: control['~k'] ?? ':if'
23
23
  });
24
24
  logger.debug({
25
25
  event: 'debug_control_if',
@@ -33,7 +33,7 @@ async function controlIf(context, routineContext, { control }) {
33
33
  event: 'debug_control_if_run_then'
34
34
  });
35
35
  if (!control[':then']) {
36
- throw new Error('Invalid :if - missing :then.');
36
+ throw new Error(`Invalid :if in endpoint "${endpointId}" - missing :then.`);
37
37
  }
38
38
  return runRoutine(context, routineContext, {
39
39
  routine: control[':then']
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -13,27 +13,32 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */ import { type } from '@lowdefy/helpers';
16
+ import { ConfigError } from '@lowdefy/errors';
16
17
  async function controlLog(context, routineContext, { control }) {
17
- const { logger, evaluateOperators } = context;
18
+ const { endpointId, logger, evaluateOperators } = context;
18
19
  const { items } = routineContext;
20
+ const location = control['~k'] ?? ':log';
19
21
  logger.debug({
20
22
  event: 'debug_control_log'
21
23
  });
22
24
  const log = evaluateOperators({
23
25
  input: control[':log'],
24
26
  items,
25
- location: 'TODO:'
27
+ location
26
28
  });
27
29
  const logLevel = evaluateOperators({
28
30
  input: control[':level'],
29
31
  items,
30
- location: 'TODO:'
32
+ location
31
33
  }) ?? 'info';
32
34
  if (!type.isString(logLevel)) {
33
- throw new Error(`Unrecognised type for :level. Received ${logLevel}.`);
35
+ throw new ConfigError(`Invalid :log in endpoint "${endpointId}" - :level must be a string.`, {
36
+ received: logLevel,
37
+ configKey: control['~k']
38
+ });
34
39
  }
35
40
  if (!logger[logLevel]) {
36
- throw new Error(`Invalid log level for :log. Received ${logLevel}.`);
41
+ throw new Error(`Invalid :log in endpoint "${endpointId}" - unrecognised log level. Received "${logLevel}".`);
37
42
  }
38
43
  logger[logLevel](log);
39
44
  return {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -12,18 +12,19 @@
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 runRoutine from '../runRoutine.js';
15
+ */ import { ConfigError } from '@lowdefy/errors';
16
+ import runRoutine from '../runRoutine.js';
16
17
  async function controlParallelFor(context, routineContext, { control }) {
17
- const { logger, evaluateOperators } = context;
18
+ const { endpointId, logger, evaluateOperators } = context;
18
19
  const { items } = routineContext;
19
20
  const itemName = control[':parallel_for'];
20
21
  if (!itemName) {
21
- throw new Error('Invalid :parallel_for - missing variable name in :parallel_for.');
22
+ throw new Error(`Invalid :parallel_for in endpoint "${endpointId}" - missing variable name in :parallel_for.`);
22
23
  }
23
24
  const array = evaluateOperators({
24
25
  input: control[':in'],
25
26
  items,
26
- location: 'controlParallelFor'
27
+ location: control['~k'] ?? ':parallel_for'
27
28
  });
28
29
  logger.debug({
29
30
  event: 'debug_control_parallel',
@@ -31,10 +32,13 @@ async function controlParallelFor(context, routineContext, { control }) {
31
32
  itemName
32
33
  });
33
34
  if (!Array.isArray(array)) {
34
- throw new Error('Invalid :parallel_for - evaluated :in to non-array.');
35
+ throw new ConfigError(`Invalid :parallel_for in endpoint "${endpointId}" - :in must evaluate to an array.`, {
36
+ received: array,
37
+ configKey: control['~k']
38
+ });
35
39
  }
36
40
  if (!control[':do']) {
37
- throw new Error('Invalid :parallel_for - missing :do.');
41
+ throw new Error(`Invalid :parallel_for in endpoint "${endpointId}" - missing :do.`);
38
42
  }
39
43
  const promises = array.map((item, index)=>{
40
44
  const updatedItems = {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -15,15 +15,16 @@
15
15
  */ async function controlReject(context, routineContext, { control }) {
16
16
  const { evaluateOperators } = context;
17
17
  const { items } = routineContext;
18
+ const location = control['~k'] ?? ':reject';
18
19
  const message = evaluateOperators({
19
20
  input: control[':reject'],
20
21
  items,
21
- location: 'TODO'
22
+ location
22
23
  });
23
24
  const cause = evaluateOperators({
24
25
  input: control[':cause'],
25
26
  items,
26
- location: 'TODO'
27
+ location
27
28
  });
28
29
  const error = new Error(message, {
29
30
  cause
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
18
18
  const response = evaluateOperators({
19
19
  input: control[':return'],
20
20
  items,
21
- location: 'TODO'
21
+ location: control['~k'] ?? ':return'
22
22
  });
23
23
  context.logger.debug({
24
24
  event: 'debug_control_return',
@@ -5,7 +5,7 @@ function controlSetState(context, routineContext, { control }) {
5
5
  const evaluatedSetState = evaluateOperators({
6
6
  input: control[':set_state'],
7
7
  items,
8
- location: 'TODO:'
8
+ location: control['~k'] ?? ':set_state'
9
9
  });
10
10
  logger.debug({
11
11
  event: 'debug_control_set_state',
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2020-2024 Lowdefy, Inc
2
+ Copyright 2020-2026 Lowdefy, Inc
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
14
14
  limitations under the License.
15
15
  */ import runRoutine from '../runRoutine.js';
16
16
  async function controlSwitch(context, routineContext, { control }) {
17
- const { logger, evaluateOperators } = context;
17
+ const { endpointId, logger, evaluateOperators } = context;
18
18
  const { items } = routineContext;
19
19
  const cases = control[':switch'];
20
20
  logger.debug({
@@ -24,7 +24,7 @@ async function controlSwitch(context, routineContext, { control }) {
24
24
  const evaluatedCase = evaluateOperators({
25
25
  input: caseObj[':case'],
26
26
  items,
27
- location: 'TODO'
27
+ location: caseObj['~k'] ?? control['~k'] ?? ':switch'
28
28
  });
29
29
  logger.debug({
30
30
  event: 'debug_control_switch_case',
@@ -38,7 +38,7 @@ async function controlSwitch(context, routineContext, { control }) {
38
38
  event: 'debug_control_switch_run_then'
39
39
  });
40
40
  if (!caseObj[':then']) {
41
- throw new Error('Invalid switch :case - missing :then');
41
+ throw new Error(`Invalid :switch :case in endpoint "${endpointId}" - missing :then.`);
42
42
  }
43
43
  return runRoutine(context, routineContext, {
44
44
  routine: caseObj[':then']