@lowdefy/build 4.5.1 → 4.6.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 (165) hide show
  1. package/dist/build/addDefaultPages/404.js +1 -1
  2. package/dist/build/addDefaultPages/addDefaultPages.js +10 -4
  3. package/dist/build/addKeys.js +85 -29
  4. package/dist/build/buildApi/buildApi.js +27 -8
  5. package/dist/build/buildApi/buildEndpoint.js +7 -1
  6. package/dist/build/buildApi/buildRoutine/buildControl.js +1 -1
  7. package/dist/build/buildApi/buildRoutine/buildRoutine.js +1 -1
  8. package/dist/build/buildApi/buildRoutine/buildStep.js +1 -1
  9. package/dist/build/buildApi/buildRoutine/controlTypes.js +34 -11
  10. package/dist/build/buildApi/buildRoutine/countControl.js +1 -1
  11. package/dist/build/buildApi/buildRoutine/countStepTypes.js +2 -2
  12. package/dist/build/buildApi/buildRoutine/setStepId.js +1 -1
  13. package/dist/build/buildApi/buildRoutine/validateStep.js +30 -9
  14. package/dist/build/buildApi/validateEndpoint.js +36 -7
  15. package/dist/build/buildApi/validateStepReferences.js +65 -0
  16. package/dist/build/buildApp.js +1 -1
  17. package/dist/build/buildAuth/buildApiAuth.js +7 -3
  18. package/dist/build/buildAuth/buildAuth.js +7 -4
  19. package/dist/build/buildAuth/buildAuthPlugins.js +42 -13
  20. package/dist/build/buildAuth/buildPageAuth.js +14 -3
  21. package/dist/build/buildAuth/getApiRoles.js +1 -1
  22. package/dist/build/buildAuth/getPageRoles.js +1 -1
  23. package/dist/build/buildAuth/getProtectedApi.js +1 -1
  24. package/dist/build/buildAuth/getProtectedPages.js +1 -1
  25. package/dist/build/buildAuth/validateAuthConfig.js +39 -5
  26. package/dist/build/buildAuth/validateMutualExclusivity.js +13 -5
  27. package/dist/build/buildConnections.js +23 -24
  28. package/dist/build/buildImports/buildIconImports.js +1 -1
  29. package/dist/build/buildImports/buildImports.js +1 -1
  30. package/dist/build/buildImports/buildImportsDev.js +1 -1
  31. package/dist/build/buildImports/buildImportsProd.js +1 -1
  32. package/dist/build/buildImports/buildStyleImports.js +1 -1
  33. package/dist/build/buildImports/defaultIconsDev.js +1 -1
  34. package/dist/build/buildImports/defaultIconsProd.js +1 -1
  35. package/dist/build/buildJs/generateJsFile.js +1 -1
  36. package/dist/build/buildJs/jsMapParser.js +1 -1
  37. package/dist/build/buildJs/writeJs.js +1 -1
  38. package/dist/build/buildLogger.js +41 -0
  39. package/dist/build/buildMenu.js +36 -12
  40. package/dist/build/buildPages/buildBlock/buildBlock.js +1 -1
  41. package/dist/build/buildPages/buildBlock/buildEvents.js +79 -9
  42. package/dist/build/buildPages/buildBlock/buildRequests.js +38 -8
  43. package/dist/build/buildPages/buildBlock/buildSubBlocks.js +6 -2
  44. package/dist/build/buildPages/buildBlock/countBlockOperators.js +1 -1
  45. package/dist/build/buildPages/buildBlock/countBlockTypes.js +2 -2
  46. package/dist/build/buildPages/buildBlock/moveSkeletonBlocksToArea.js +6 -2
  47. package/dist/build/buildPages/buildBlock/moveSubBlocksToArea.js +6 -2
  48. package/dist/build/buildPages/buildBlock/setBlockId.js +1 -1
  49. package/dist/build/buildPages/buildBlock/validateBlock.js +25 -7
  50. package/dist/build/buildPages/buildPage.js +35 -6
  51. package/dist/build/buildPages/validateLinkReferences.js +33 -0
  52. package/dist/build/buildPages/validatePayloadReferences.js +59 -0
  53. package/dist/build/buildPages/validateRequestReferences.js +33 -0
  54. package/dist/build/buildPages/validateServerStateReferences.js +41 -0
  55. package/dist/build/buildPages/validateStateReferences.js +79 -0
  56. package/dist/build/buildRefs/buildRefs.js +20 -3
  57. package/dist/build/buildRefs/createRefReviver.js +28 -0
  58. package/dist/build/buildRefs/evaluateBuildOperators.js +26 -7
  59. package/dist/build/buildRefs/evaluateStaticOperators.js +56 -0
  60. package/dist/build/buildRefs/getConfigFile.js +25 -6
  61. package/dist/build/buildRefs/getKey.js +1 -1
  62. package/dist/build/buildRefs/getRefContent.js +1 -1
  63. package/dist/build/buildRefs/getRefPath.js +1 -1
  64. package/dist/build/buildRefs/getRefsFromFile.js +9 -4
  65. package/dist/build/buildRefs/getUserJavascriptFunction.js +18 -4
  66. package/dist/build/buildRefs/makeRefDefinition.js +10 -5
  67. package/dist/build/buildRefs/parseNunjucks.js +1 -1
  68. package/dist/build/buildRefs/parseRefContent.js +100 -6
  69. package/dist/build/buildRefs/populateRefs.js +75 -13
  70. package/dist/build/buildRefs/recursiveBuild.js +66 -18
  71. package/dist/build/buildRefs/runRefResolver.js +11 -3
  72. package/dist/build/buildRefs/runTransformer.js +7 -3
  73. package/dist/build/buildTypes.js +22 -7
  74. package/dist/build/cleanBuildDirectory.js +1 -1
  75. package/dist/build/collectDynamicIdentifiers.js +35 -0
  76. package/dist/build/collectTypeNames.js +36 -0
  77. package/dist/build/copyPublicFolder.js +1 -1
  78. package/dist/build/{buildJs → full}/buildJs.js +3 -3
  79. package/dist/build/full/buildPages.js +87 -0
  80. package/dist/build/{buildPages → full}/buildTestPage.js +15 -3
  81. package/dist/build/{updateServerPackageJson.js → full/updateServerPackageJson.js} +1 -1
  82. package/dist/build/{writePages.js → full/writePages.js} +1 -1
  83. package/dist/build/{writeRequests.js → full/writeRequests.js} +11 -3
  84. package/dist/build/{writeTypes.js → full/writeTypes.js} +1 -1
  85. package/dist/build/jit/addInstalledTypes.js +51 -0
  86. package/dist/build/jit/buildJsShallow.js +41 -0
  87. package/dist/build/jit/buildPageJit.js +252 -0
  88. package/dist/build/jit/buildShallowPages.js +90 -0
  89. package/dist/build/jit/createPageRegistry.js +80 -0
  90. package/dist/build/jit/detectMissingPluginPackages.js +62 -0
  91. package/dist/build/jit/getRefPositions.js +38 -0
  92. package/dist/build/jit/isPageContentPath.js +24 -0
  93. package/dist/build/jit/pageContentKeys.js +26 -0
  94. package/dist/build/jit/shallowBuild.js +245 -0
  95. package/dist/build/jit/stripPageContent.js +23 -0
  96. package/dist/build/jit/updateServerPackageJsonJit.js +33 -0
  97. package/dist/build/jit/validatePageTypes.js +73 -0
  98. package/dist/build/jit/writePageJit.js +44 -0
  99. package/dist/build/jit/writePageRegistry.js +23 -0
  100. package/dist/build/jit/writeSourcelessPages.js +23 -0
  101. package/dist/build/testSchema.js +45 -7
  102. package/dist/build/validateConfig.js +2 -2
  103. package/dist/build/validateOperatorsDynamic.js +28 -0
  104. package/dist/build/writeApi.js +1 -1
  105. package/dist/build/writeApp.js +1 -1
  106. package/dist/build/writeAuth.js +1 -1
  107. package/dist/build/writeConfig.js +1 -1
  108. package/dist/build/writeConnections.js +1 -1
  109. package/dist/build/writeGlobal.js +1 -1
  110. package/dist/build/writeLogger.js +19 -0
  111. package/dist/build/writeMaps.js +1 -1
  112. package/dist/build/writeMenus.js +1 -1
  113. package/dist/build/writePluginImports/generateImportFile.js +1 -1
  114. package/dist/build/writePluginImports/writeActionImports.js +1 -1
  115. package/dist/build/writePluginImports/writeActionSchemaMap.js +42 -0
  116. package/dist/build/writePluginImports/writeAuthImports.js +1 -1
  117. package/dist/build/writePluginImports/writeBlockImports.js +1 -1
  118. package/dist/build/writePluginImports/writeBlockSchemaMap.js +42 -0
  119. package/dist/build/writePluginImports/writeConnectionImports.js +1 -1
  120. package/dist/build/writePluginImports/writeIconImports.js +1 -1
  121. package/dist/build/writePluginImports/writeOperatorImports.js +1 -1
  122. package/dist/build/writePluginImports/writeOperatorSchemaMap.js +49 -0
  123. package/dist/build/writePluginImports/writePluginImports.js +16 -1
  124. package/dist/build/writePluginImports/writeStyleImports.js +1 -1
  125. package/dist/createContext.js +16 -4
  126. package/dist/defaultTypesMap.js +477 -455
  127. package/dist/index.js +188 -127
  128. package/dist/indexDev.js +18 -0
  129. package/dist/lowdefySchema.js +589 -0
  130. package/dist/scripts/generateDefaultTypes.js +2 -1
  131. package/dist/scripts/run.js +1 -1
  132. package/dist/{test → test-utils}/buildRefs/testBuildRefsAsyncFunction.js +1 -1
  133. package/dist/{test → test-utils}/buildRefs/testBuildRefsErrorResolver.js +1 -1
  134. package/dist/{test → test-utils}/buildRefs/testBuildRefsNullResolver.js +1 -1
  135. package/dist/{test → test-utils}/buildRefs/testBuildRefsParsingResolver.js +1 -1
  136. package/dist/{test → test-utils}/buildRefs/testBuildRefsResolver.js +1 -1
  137. package/dist/{test → test-utils}/buildRefs/testBuildRefsTransform.js +1 -1
  138. package/dist/{test → test-utils}/buildRefs/testBuildRefsTransformIdentity.js +1 -1
  139. package/dist/test-utils/buildRefs/testJitPageResolver.js +24 -0
  140. package/dist/test-utils/createTestLogger.js +36 -0
  141. package/dist/test-utils/parseTestYaml.js +126 -0
  142. package/dist/test-utils/runBuild.js +228 -0
  143. package/dist/test-utils/runBuildForSnapshots.js +704 -0
  144. package/dist/{test → test-utils}/testContext.js +12 -2
  145. package/dist/utils/collectExceptions.js +34 -0
  146. package/dist/utils/countOperators.js +31 -12
  147. package/dist/utils/createBuildHandleError.js +38 -0
  148. package/dist/utils/createCheckDuplicateId.js +15 -9
  149. package/dist/utils/createCounter.js +16 -3
  150. package/dist/utils/createHandleWarning.js +41 -0
  151. package/dist/utils/createPluginTypesMap.js +1 -1
  152. package/dist/utils/extractOperatorKey.js +36 -0
  153. package/dist/utils/findConfigKey.js +37 -0
  154. package/dist/utils/findSimilarString.js +53 -0
  155. package/dist/utils/logCollectedErrors.js +30 -0
  156. package/dist/utils/makeId.js +13 -8
  157. package/dist/utils/preserveMetaProperties.js +27 -0
  158. package/dist/utils/readConfigFile.js +1 -1
  159. package/dist/utils/setNonEnumerableProperty.js +23 -0
  160. package/dist/utils/traverseConfig.js +43 -0
  161. package/dist/utils/tryBuildStep.js +46 -0
  162. package/dist/utils/writeBuildArtifact.js +1 -1
  163. package/package.json +46 -41
  164. package/dist/build/buildPages/buildPages.js +0 -31
  165. package/dist/utils/formatErrorMessage.js +0 -56
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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,9 +13,10 @@
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
  import getApiRoles from './getApiRoles.js';
17
18
  import getProtectedApi from './getProtectedApi.js';
18
- function buildApiAuth({ components }) {
19
+ function buildApiAuth({ components, context }) {
19
20
  const protectedApiEndpoints = getProtectedApi({
20
21
  components
21
22
  });
@@ -29,7 +30,10 @@ function buildApiAuth({ components }) {
29
30
  (components.api || []).forEach((endpoint)=>{
30
31
  if (apiRoles[endpoint.id]) {
31
32
  if (configPublicApi.includes(endpoint.id)) {
32
- throw new Error(`Page "${endpoint.id}" is both protected by roles ${JSON.stringify(apiRoles[endpoint.id])} and public.`);
33
+ throw new ConfigError(`Endpoint "${endpoint.id}" is both protected by roles and public.`, {
34
+ received: apiRoles[endpoint.id],
35
+ configKey: endpoint['~k']
36
+ });
33
37
  }
34
38
  endpoint.auth = {
35
39
  public: false,
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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.
@@ -20,14 +20,17 @@ import validateAuthConfig from './validateAuthConfig.js';
20
20
  function buildAuth({ components, context }) {
21
21
  const configured = !type.isNone(components.auth);
22
22
  validateAuthConfig({
23
- components
23
+ components,
24
+ context
24
25
  });
25
26
  components.auth.configured = configured;
26
27
  buildApiAuth({
27
- components
28
+ components,
29
+ context
28
30
  });
29
31
  buildPageAuth({
30
- components
32
+ components,
33
+ context
31
34
  });
32
35
  buildAuthPlugins({
33
36
  components,
@@ -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,19 +13,33 @@
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
- function buildAuthPlugin({ counter, pluginConfig, typeClass }) {
16
+ import { ConfigError } from '@lowdefy/errors';
17
+ import collectExceptions from '../../utils/collectExceptions.js';
18
+ function buildAuthPlugin({ counter, pluginConfig, typeClass, context }) {
17
19
  if (type.isArray(pluginConfig)) {
18
20
  pluginConfig.forEach((plugin)=>{
21
+ const configKey = plugin['~k'];
19
22
  if (type.isUndefined(plugin.id)) {
20
- throw new Error(`Auth ${typeClass} id missing.`);
23
+ collectExceptions(context, new ConfigError(`Auth ${typeClass} id missing.`, {
24
+ configKey
25
+ }));
26
+ return;
21
27
  }
22
28
  if (!type.isString(plugin.id)) {
23
- throw new Error(`Auth ${typeClass} id is not a string. Received ${JSON.stringify(plugin.id)}.`);
29
+ collectExceptions(context, new ConfigError(`Auth ${typeClass} id is not a string.`, {
30
+ received: plugin.id,
31
+ configKey
32
+ }));
33
+ return;
24
34
  }
25
35
  if (!type.isString(plugin.type)) {
26
- throw new Error(`Auth ${typeClass} type is not a string at ${typeClass} "${plugin.id}". Received ${JSON.stringify(plugin.type)}.`);
36
+ collectExceptions(context, new ConfigError(`Auth ${typeClass} type is not a string at ${typeClass} "${plugin.id}".`, {
37
+ received: plugin.type,
38
+ configKey
39
+ }));
40
+ return;
27
41
  }
28
- counter.increment(plugin.type);
42
+ counter.increment(plugin.type, plugin['~k']);
29
43
  });
30
44
  }
31
45
  }
@@ -34,16 +48,28 @@ function buildAdapter({ components, context }) {
34
48
  if (type.isNone(adapter)) {
35
49
  return;
36
50
  }
51
+ const configKey = adapter['~k'];
37
52
  if (type.isUndefined(adapter.id)) {
38
- throw new Error(`Auth adapter id missing.`);
53
+ collectExceptions(context, new ConfigError('Auth adapter id missing.', {
54
+ configKey
55
+ }));
56
+ return;
39
57
  }
40
58
  if (!type.isString(adapter.id)) {
41
- throw new Error(`Auth adapter id is not a string. Received ${JSON.stringify(adapter.id)}.`);
59
+ collectExceptions(context, new ConfigError('Auth adapter id is not a string.', {
60
+ received: adapter.id,
61
+ configKey
62
+ }));
63
+ return;
42
64
  }
43
65
  if (!type.isString(adapter.type)) {
44
- throw new Error(`Auth adapter type is not a string at adapter "${adapter.id}". Received ${JSON.stringify(adapter.type)}.`);
66
+ collectExceptions(context, new ConfigError(`Auth adapter type is not a string at adapter "${adapter.id}".`, {
67
+ received: adapter.type,
68
+ configKey
69
+ }));
70
+ return;
45
71
  }
46
- context.typeCounters.auth.adapters.increment(adapter.type);
72
+ context.typeCounters.auth.adapters.increment(adapter.type, adapter['~k']);
47
73
  }
48
74
  function buildAuthPlugins({ components, context }) {
49
75
  const counters = context.typeCounters.auth;
@@ -55,17 +81,20 @@ function buildAuthPlugins({ components, context }) {
55
81
  buildAuthPlugin({
56
82
  counter: counters.callbacks,
57
83
  pluginConfig: authConfig.callbacks,
58
- typeClass: 'callback'
84
+ typeClass: 'callback',
85
+ context
59
86
  });
60
87
  buildAuthPlugin({
61
88
  counter: counters.events,
62
89
  pluginConfig: authConfig.events,
63
- typeClass: 'event'
90
+ typeClass: 'event',
91
+ context
64
92
  });
65
93
  buildAuthPlugin({
66
94
  counter: counters.providers,
67
95
  pluginConfig: authConfig.providers,
68
- typeClass: 'provider'
96
+ typeClass: 'provider',
97
+ context
69
98
  });
70
99
  }
71
100
  export default buildAuthPlugins;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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,9 +13,10 @@
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
  import getPageRoles from './getPageRoles.js';
17
18
  import getProtectedPages from './getProtectedPages.js';
18
- function buildPageAuth({ components }) {
19
+ function buildPageAuth({ components, context }) {
19
20
  const protectedPages = getProtectedPages({
20
21
  components
21
22
  });
@@ -27,9 +28,19 @@ function buildPageAuth({ components }) {
27
28
  configPublicPages = components.auth.pages.public;
28
29
  }
29
30
  (components.pages || []).forEach((page)=>{
31
+ // The 404 page must always be public so unauthenticated users can see it.
32
+ if (page.id === '404') {
33
+ page.auth = {
34
+ public: true
35
+ };
36
+ return;
37
+ }
30
38
  if (pageRoles[page.id]) {
31
39
  if (configPublicPages.includes(page.id)) {
32
- throw new Error(`Page "${page.id}" is both protected by roles ${JSON.stringify(pageRoles[page.id])} and public.`);
40
+ throw new ConfigError(`Page "${page.id}" is both protected by roles and public.`, {
41
+ received: pageRoles[page.id],
42
+ configKey: page['~k']
43
+ });
33
44
  }
34
45
  page.auth = {
35
46
  public: false,
@@ -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
  /* eslint-disable no-param-reassign */ /*
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,14 +14,17 @@
14
14
  limitations under the License.
15
15
  */ import { type } from '@lowdefy/helpers';
16
16
  import { validate } from '@lowdefy/ajv';
17
+ import { ConfigError } from '@lowdefy/errors';
17
18
  import lowdefySchema from '../../lowdefySchema.js';
18
19
  import validateMutualExclusivity from './validateMutualExclusivity.js';
19
- async function validateAuthConfig({ components }) {
20
+ function validateAuthConfig({ components, context }) {
20
21
  if (type.isNone(components.auth)) {
21
22
  components.auth = {};
22
23
  }
23
24
  if (!type.isObject(components.auth)) {
24
- throw new Error('lowdefy.auth is not an object.');
25
+ throw new ConfigError('lowdefy.auth is not an object.', {
26
+ configKey: components['~k']
27
+ });
25
28
  }
26
29
  if (type.isNone(components.auth.api)) {
27
30
  components.auth.api = {};
@@ -53,18 +56,49 @@ async function validateAuthConfig({ components }) {
53
56
  if (type.isNone(components.auth.theme)) {
54
57
  components.auth.theme = {};
55
58
  }
56
- validate({
59
+ const { valid, errors } = validate({
57
60
  schema: lowdefySchema.definitions.authConfig,
58
- data: components.auth
61
+ data: components.auth,
62
+ returnErrors: true
59
63
  });
64
+ if (!valid) {
65
+ errors.forEach((error)=>{
66
+ // Try to get configKey from the item in the error path
67
+ const instancePath = error.instancePath.split('/').filter(Boolean);
68
+ let configKey = components.auth['~k'];
69
+ let currentData = components.auth;
70
+ for (const part of instancePath){
71
+ if (type.isArray(currentData)) {
72
+ const index = parseInt(part, 10);
73
+ currentData = currentData[index];
74
+ } else {
75
+ currentData = currentData?.[part];
76
+ }
77
+ if (currentData?.['~k']) {
78
+ configKey = currentData['~k'];
79
+ }
80
+ }
81
+ throw new ConfigError(`Auth ${error.message}.`, {
82
+ configKey
83
+ });
84
+ });
85
+ }
60
86
  validateMutualExclusivity({
61
87
  components,
88
+ context,
62
89
  entity: 'api'
63
90
  });
64
91
  validateMutualExclusivity({
65
92
  components,
93
+ context,
66
94
  entity: 'pages'
67
95
  });
96
+ // Validate NEXTAUTH_SECRET is set when auth providers are configured
97
+ if (components.auth.providers.length > 0 && type.isNone(process.env.NEXTAUTH_SECRET)) {
98
+ throw new ConfigError('Auth providers are configured but NEXTAUTH_SECRET environment variable is not set. ' + 'Set NEXTAUTH_SECRET to a secure random string (e.g., generate with `openssl rand -base64 32`).', {
99
+ configKey: components.auth.providers['~k'] ?? components.auth['~k']
100
+ });
101
+ }
68
102
  return components;
69
103
  }
70
104
  export default validateAuthConfig;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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,15 +13,23 @@
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
- function validateMutualExclusivity({ components, entity }) {
16
+ import { ConfigError } from '@lowdefy/errors';
17
+ function validateMutualExclusivity({ components, context, entity }) {
18
+ const configKey = components.auth[entity]?.['~k'] || components.auth?.['~k'];
17
19
  if (components.auth[entity].protected === true && components.auth[entity].public === true || type.isArray(components.auth[entity].protected) && type.isArray(components.auth[entity].public)) {
18
- throw new Error(`Protected and public ${entity} are mutually exclusive. When protected ${entity} are listed, all unlisted ${entity} are public by default and vice versa.`);
20
+ throw new ConfigError(`Protected and public ${entity} are mutually exclusive. When protected ${entity} are listed, all unlisted ${entity} are public by default and vice versa.`, {
21
+ configKey
22
+ });
19
23
  }
20
24
  if (components.auth[entity].protected === false) {
21
- throw new Error(`Protected ${entity} can not be set to false.`);
25
+ throw new ConfigError(`Protected ${entity} can not be set to false.`, {
26
+ configKey
27
+ });
22
28
  }
23
29
  if (components.auth[entity].public === false) {
24
- throw new Error(`Public ${entity} can not be set to false.`);
30
+ throw new ConfigError(`Public ${entity} can not be set to false.`, {
31
+ configKey
32
+ });
25
33
  }
26
34
  }
27
35
  export default validateMutualExclusivity;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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,35 +12,34 @@
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 { type } from '@lowdefy/helpers';
16
- import countOperators from '../utils/countOperators.js';
15
+ */ import countOperators from '../utils/countOperators.js';
17
16
  import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
18
17
  function buildConnections({ components, context }) {
18
+ // Store connection IDs for validation in buildRequests
19
+ context.connectionIds = new Set();
20
+ // Schema validates: id required, id is string, type is string
21
+ // Only check for duplicates here (schema can't do that)
19
22
  const checkDuplicateConnectionId = createCheckDuplicateId({
20
23
  message: 'Duplicate connectionId "{{ id }}".'
21
24
  });
22
- if (type.isArray(components.connections)) {
23
- components.connections.forEach((connection)=>{
24
- if (type.isUndefined(connection.id)) {
25
- throw new Error(`Connection id missing.`);
26
- }
27
- if (!type.isString(connection.id)) {
28
- throw new Error(`Connection id is not a string. Received ${JSON.stringify(connection.id)}.`);
29
- }
30
- checkDuplicateConnectionId({
31
- id: connection.id
32
- });
33
- if (!type.isString(connection.type)) {
34
- throw new Error(`Connection type is not a string at connection "${connection.id}". Received ${JSON.stringify(connection.type)}.`);
35
- }
36
- context.typeCounters.connections.increment(connection.type);
37
- connection.connectionId = connection.id;
38
- connection.id = `connection:${connection.id}`;
39
- countOperators(connection.properties || {}, {
40
- counter: context.typeCounters.operators.server
41
- });
25
+ (components.connections ?? []).forEach((connection)=>{
26
+ const configKey = connection['~k'];
27
+ // Check duplicates (schema can't validate this)
28
+ checkDuplicateConnectionId({
29
+ id: connection.id,
30
+ configKey
42
31
  });
43
- }
32
+ // Track type usage for buildTypes validation
33
+ context.typeCounters.connections.increment(connection.type, configKey);
34
+ // Store connectionId for request validation and rename id
35
+ connection.connectionId = connection.id;
36
+ context.connectionIds.add(connection.connectionId);
37
+ connection.id = `connection:${connection.id}`;
38
+ // Count operators in connection properties
39
+ countOperators(connection.properties ?? {}, {
40
+ counter: context.typeCounters.operators.server
41
+ });
42
+ });
44
43
  return components;
45
44
  }
46
45
  export default buildConnections;
@@ -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.
@@ -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 { type } from '@lowdefy/helpers';
16
+ const sentryDefaults = {
17
+ client: true,
18
+ server: true,
19
+ tracesSampleRate: 0.1,
20
+ replaysSessionSampleRate: 0,
21
+ replaysOnErrorSampleRate: 0.1,
22
+ feedback: false,
23
+ userFields: [
24
+ 'id',
25
+ '_id'
26
+ ]
27
+ };
28
+ function buildLogger({ components }) {
29
+ if (type.isNone(components.logger)) {
30
+ components.logger = {};
31
+ }
32
+ // Only apply defaults if sentry is explicitly configured
33
+ if (!type.isNone(components.logger.sentry)) {
34
+ components.logger.sentry = {
35
+ ...sentryDefaults,
36
+ ...components.logger.sentry
37
+ };
38
+ }
39
+ return components;
40
+ }
41
+ export default buildLogger;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable no-param-reassign */ /*
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,6 +13,8 @@
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, ConfigWarning } from '@lowdefy/errors';
17
+ import collectExceptions from '../utils/collectExceptions.js';
16
18
  import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
17
19
  function buildDefaultMenu({ components, context }) {
18
20
  context.logger.warn('No menus found. Building default menu.');
@@ -30,16 +32,18 @@ function buildDefaultMenu({ components, context }) {
30
32
  ];
31
33
  return menus;
32
34
  }
33
- function loopItems({ parent, menuId, pages, missingPageWarnings, checkDuplicateMenuItemId }) {
35
+ function loopItems({ parent, menuId, pages, missingPageWarnings, checkDuplicateMenuItemId, context }) {
34
36
  if (type.isArray(parent.links)) {
35
37
  parent.links.forEach((menuItem)=>{
38
+ const configKey = menuItem['~k'];
36
39
  if (menuItem.type === 'MenuLink') {
37
40
  if (type.isString(menuItem.pageId)) {
38
41
  const page = pages.find((pg)=>pg.pageId === menuItem.pageId);
39
42
  if (!page) {
40
43
  missingPageWarnings.push({
41
44
  menuItemId: menuItem.id,
42
- pageId: menuItem.pageId
45
+ pageId: menuItem.pageId,
46
+ configKey
43
47
  });
44
48
  // remove menuItem from menu
45
49
  menuItem.remove = true;
@@ -60,7 +64,8 @@ function loopItems({ parent, menuId, pages, missingPageWarnings, checkDuplicateM
60
64
  }
61
65
  checkDuplicateMenuItemId({
62
66
  id: menuItem.id,
63
- menuId
67
+ menuId,
68
+ configKey
64
69
  });
65
70
  menuItem.menuItemId = menuItem.id;
66
71
  menuItem.id = `menuitem:${menuId}:${menuItem.id}`;
@@ -69,7 +74,8 @@ function loopItems({ parent, menuId, pages, missingPageWarnings, checkDuplicateM
69
74
  menuId,
70
75
  pages,
71
76
  missingPageWarnings,
72
- checkDuplicateMenuItemId
77
+ checkDuplicateMenuItemId,
78
+ context
73
79
  });
74
80
  });
75
81
  parent.links = parent.links.filter((item)=>item.remove !== true);
@@ -87,15 +93,28 @@ function buildMenu({ components, context }) {
87
93
  const checkDuplicateMenuId = createCheckDuplicateId({
88
94
  message: 'Duplicate menuId "{{ id }}".'
89
95
  });
90
- components.menus.forEach((menu)=>{
96
+ // Track which menus failed validation so we skip processing them
97
+ const failedMenuIndices = new Set();
98
+ components.menus.forEach((menu, menuIndex)=>{
99
+ const configKey = menu['~k'];
91
100
  if (type.isUndefined(menu.id)) {
92
- throw new Error(`Menu id missing.`);
101
+ collectExceptions(context, new ConfigError('Menu id missing.', {
102
+ configKey
103
+ }));
104
+ failedMenuIndices.add(menuIndex);
105
+ return;
93
106
  }
94
107
  if (!type.isString(menu.id)) {
95
- throw new Error(`Menu id is not a string. Received ${JSON.stringify(menu.id)}.`);
108
+ collectExceptions(context, new ConfigError('Menu id is not a string.', {
109
+ received: menu.id,
110
+ configKey
111
+ }));
112
+ failedMenuIndices.add(menuIndex);
113
+ return;
96
114
  }
97
115
  checkDuplicateMenuId({
98
- id: menu.id
116
+ id: menu.id,
117
+ configKey
99
118
  });
100
119
  menu.menuId = menu.id;
101
120
  menu.id = `menu:${menu.id}`;
@@ -107,11 +126,16 @@ function buildMenu({ components, context }) {
107
126
  menuId: menu.menuId,
108
127
  pages,
109
128
  missingPageWarnings,
110
- checkDuplicateMenuItemId
129
+ checkDuplicateMenuItemId,
130
+ context
111
131
  });
112
132
  });
113
- missingPageWarnings.map(async (warning)=>{
114
- context.logger.warn(`Page "${warning.pageId}" referenced in menu link "${warning.menuItemId}" not found.`);
133
+ missingPageWarnings.forEach((warning)=>{
134
+ context.handleWarning(new ConfigWarning(`Page "${warning.pageId}" referenced in menu link "${warning.menuItemId}" not found.`, {
135
+ configKey: warning.configKey,
136
+ prodError: true,
137
+ checkSlug: 'link-refs'
138
+ }));
115
139
  });
116
140
  return components;
117
141
  }
@@ -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.