@lowdefy/build 0.0.0-experimental-20260114142524 → 0.0.0-experimental-20260122121633
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.
- package/dist/build/addKeys.js +31 -18
- package/dist/build/buildApi/validateStepReferences.js +3 -4
- package/dist/build/buildAuth/buildAuth.js +2 -1
- package/dist/build/buildAuth/buildAuthPlugins.js +13 -13
- package/dist/build/buildAuth/validateAuthConfig.js +40 -8
- package/dist/build/buildAuth/validateMutualExclusivity.js +7 -7
- package/dist/build/buildConnections.js +20 -39
- package/dist/build/buildMenu.js +9 -14
- package/dist/build/buildPages/buildBlock/buildEvents.js +13 -13
- package/dist/build/buildPages/buildBlock/buildRequests.js +15 -15
- package/dist/build/buildPages/buildBlock/validateBlock.js +13 -13
- package/dist/build/buildPages/buildPage.js +5 -5
- package/dist/build/buildPages/validateLinkReferences.js +8 -9
- package/dist/build/buildPages/validatePayloadReferences.js +3 -4
- package/dist/build/buildPages/validateRequestReferences.js +7 -8
- package/dist/build/buildPages/validateStateReferences.js +3 -4
- package/dist/build/buildRefs/buildRefs.js +8 -0
- package/dist/build/buildRefs/evaluateBuildOperators.js +11 -1
- package/dist/build/buildRefs/evaluateStaticOperators.js +54 -0
- package/dist/build/buildRefs/getConfigFile.js +18 -13
- package/dist/build/buildRefs/getRefContent.js +15 -5
- package/dist/build/buildRefs/makeRefDefinition.js +1 -1
- package/dist/build/buildRefs/parseRefContent.js +32 -2
- package/dist/build/buildRefs/populateRefs.js +43 -5
- package/dist/build/buildRefs/recursiveBuild.js +9 -7
- package/dist/build/buildRefs/runRefResolver.js +13 -2
- package/dist/build/buildTypes.js +9 -0
- package/dist/build/collectDynamicIdentifiers.js +35 -0
- package/dist/{utils/formatConfigError.js → build/collectTypeNames.js} +20 -8
- package/dist/build/formatBuildError.js +1 -1
- package/dist/build/testSchema.js +45 -6
- package/dist/build/validateOperatorsDynamic.js +28 -0
- package/dist/build/writeRequests.js +3 -3
- package/dist/createContext.js +42 -1
- package/dist/defaultTypesMap.js +403 -403
- package/dist/index.js +43 -4
- package/dist/lowdefySchema.js +60 -0
- package/dist/test-utils/parseTestYaml.js +110 -0
- package/dist/test-utils/runBuild.js +270 -0
- package/dist/test-utils/runBuildForSnapshots.js +698 -0
- package/dist/{test → test-utils}/testContext.js +15 -1
- package/dist/utils/collectConfigError.js +6 -6
- package/dist/utils/countOperators.js +5 -3
- package/dist/utils/createCheckDuplicateId.js +1 -1
- package/dist/utils/findConfigKey.js +37 -0
- package/dist/utils/makeId.js +12 -7
- package/dist/utils/tryBuildStep.js +12 -5
- package/package.json +39 -39
- package/dist/utils/formatConfigMessage.js +0 -33
- package/dist/utils/formatConfigWarning.js +0 -24
- package/dist/utils/formatErrorMessage.js +0 -56
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsAsyncFunction.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsErrorResolver.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsNullResolver.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsParsingResolver.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsResolver.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsTransform.js +0 -0
- /package/dist/{test → test-utils}/buildRefs/testBuildRefsTransformIdentity.js +0 -0
package/dist/build/addKeys.js
CHANGED
|
@@ -45,26 +45,39 @@ function recArray({ array, nextKey, key, keyMap, keyMapId }) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
function recAddKeys({ object, key, keyMap, parentKeyMapId }) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
48
|
+
let keyMapId;
|
|
49
|
+
let storedKey = key;
|
|
50
|
+
// Skip objects that already have a ~k (already processed)
|
|
51
|
+
if (object['~k']) {
|
|
52
|
+
keyMapId = object['~k'];
|
|
53
|
+
// Use the stored key from keyMap for correct child paths
|
|
54
|
+
storedKey = keyMap[keyMapId]?.key ?? key;
|
|
55
|
+
} else {
|
|
56
|
+
keyMapId = makeId.next();
|
|
57
|
+
const entry = {
|
|
58
|
+
key,
|
|
59
|
+
'~k_parent': parentKeyMapId
|
|
60
|
+
};
|
|
61
|
+
if (object['~r'] !== undefined) entry['~r'] = object['~r'];
|
|
62
|
+
if (object['~l'] !== undefined) entry['~l'] = object['~l'];
|
|
63
|
+
if (object['~ignoreBuildCheck'] !== undefined) entry['~ignoreBuildCheck'] = object['~ignoreBuildCheck'];
|
|
64
|
+
keyMap[keyMapId] = entry;
|
|
65
|
+
Object.defineProperty(object, '~k', {
|
|
66
|
+
value: keyMapId,
|
|
67
|
+
enumerable: false,
|
|
68
|
+
writable: true,
|
|
69
|
+
configurable: true
|
|
70
|
+
});
|
|
71
|
+
delete object['~r'];
|
|
72
|
+
delete object['~l'];
|
|
73
|
+
delete object['~ignoreBuildCheck'];
|
|
74
|
+
}
|
|
75
|
+
// Always recurse into children (they may be new objects without keys)
|
|
63
76
|
Object.keys(object).forEach((nextKey)=>{
|
|
64
77
|
if (type.isObject(object[nextKey])) {
|
|
65
78
|
recAddKeys({
|
|
66
79
|
object: object[nextKey],
|
|
67
|
-
key: `${
|
|
80
|
+
key: `${storedKey}.${nextKey}`,
|
|
68
81
|
keyMap: keyMap,
|
|
69
82
|
parentKeyMapId: keyMapId
|
|
70
83
|
});
|
|
@@ -73,7 +86,7 @@ function recAddKeys({ object, key, keyMap, parentKeyMapId }) {
|
|
|
73
86
|
recArray({
|
|
74
87
|
array: object[nextKey],
|
|
75
88
|
nextKey,
|
|
76
|
-
key,
|
|
89
|
+
key: storedKey,
|
|
77
90
|
keyMap,
|
|
78
91
|
keyMapId
|
|
79
92
|
});
|
|
@@ -81,7 +94,7 @@ function recAddKeys({ object, key, keyMap, parentKeyMapId }) {
|
|
|
81
94
|
});
|
|
82
95
|
}
|
|
83
96
|
function addKeys({ components, context }) {
|
|
84
|
-
const keyMapId = makeId(
|
|
97
|
+
const keyMapId = makeId.next();
|
|
85
98
|
recAddKeys({
|
|
86
99
|
object: components,
|
|
87
100
|
key: 'root',
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { type } from '@lowdefy/helpers';
|
|
16
16
|
import extractOperatorKey from '../../utils/extractOperatorKey.js';
|
|
17
|
-
import formatConfigWarning from '../../utils/formatConfigWarning.js';
|
|
18
17
|
import traverseConfig from '../../utils/traverseConfig.js';
|
|
19
18
|
// Collect all step IDs from a routine (including nested control structures)
|
|
20
19
|
// Note: After buildRoutine, steps have requestId (original id) and id is modified
|
|
@@ -55,11 +54,11 @@ function validateStepReferences({ endpoint, context }) {
|
|
|
55
54
|
stepRefs.forEach((configKey, stepId)=>{
|
|
56
55
|
if (stepIds.has(stepId)) return;
|
|
57
56
|
const message = `_step references "${stepId}" in endpoint "${endpoint.endpointId}", ` + `but no step with id "${stepId}" exists in the routine. ` + `Step IDs are defined by the "id" property of each step. ` + `Check for typos or add a step with this id.`;
|
|
58
|
-
context.logger.
|
|
57
|
+
context.logger.configWarning({
|
|
59
58
|
message,
|
|
60
59
|
configKey,
|
|
61
|
-
|
|
62
|
-
})
|
|
60
|
+
prodError: true
|
|
61
|
+
});
|
|
63
62
|
});
|
|
64
63
|
}
|
|
65
64
|
export default validateStepReferences;
|
|
@@ -20,7 +20,8 @@ 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({
|
|
@@ -13,31 +13,31 @@
|
|
|
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
|
|
16
|
+
import { ConfigError } from '@lowdefy/node-utils';
|
|
17
17
|
function buildAuthPlugin({ counter, pluginConfig, typeClass, context }) {
|
|
18
18
|
if (type.isArray(pluginConfig)) {
|
|
19
19
|
pluginConfig.forEach((plugin)=>{
|
|
20
20
|
const configKey = plugin['~k'];
|
|
21
21
|
if (type.isUndefined(plugin.id)) {
|
|
22
|
-
throw new
|
|
22
|
+
throw new ConfigError({
|
|
23
23
|
message: `Auth ${typeClass} id missing.`,
|
|
24
24
|
configKey,
|
|
25
25
|
context
|
|
26
|
-
})
|
|
26
|
+
});
|
|
27
27
|
}
|
|
28
28
|
if (!type.isString(plugin.id)) {
|
|
29
|
-
throw new
|
|
29
|
+
throw new ConfigError({
|
|
30
30
|
message: `Auth ${typeClass} id is not a string. Received ${JSON.stringify(plugin.id)}.`,
|
|
31
31
|
configKey,
|
|
32
32
|
context
|
|
33
|
-
})
|
|
33
|
+
});
|
|
34
34
|
}
|
|
35
35
|
if (!type.isString(plugin.type)) {
|
|
36
|
-
throw new
|
|
36
|
+
throw new ConfigError({
|
|
37
37
|
message: `Auth ${typeClass} type is not a string at ${typeClass} "${plugin.id}". Received ${JSON.stringify(plugin.type)}.`,
|
|
38
38
|
configKey,
|
|
39
39
|
context
|
|
40
|
-
})
|
|
40
|
+
});
|
|
41
41
|
}
|
|
42
42
|
counter.increment(plugin.type, plugin['~k']);
|
|
43
43
|
});
|
|
@@ -50,25 +50,25 @@ function buildAdapter({ components, context }) {
|
|
|
50
50
|
}
|
|
51
51
|
const configKey = adapter['~k'];
|
|
52
52
|
if (type.isUndefined(adapter.id)) {
|
|
53
|
-
throw new
|
|
53
|
+
throw new ConfigError({
|
|
54
54
|
message: 'Auth adapter id missing.',
|
|
55
55
|
configKey,
|
|
56
56
|
context
|
|
57
|
-
})
|
|
57
|
+
});
|
|
58
58
|
}
|
|
59
59
|
if (!type.isString(adapter.id)) {
|
|
60
|
-
throw new
|
|
60
|
+
throw new ConfigError({
|
|
61
61
|
message: `Auth adapter id is not a string. Received ${JSON.stringify(adapter.id)}.`,
|
|
62
62
|
configKey,
|
|
63
63
|
context
|
|
64
|
-
})
|
|
64
|
+
});
|
|
65
65
|
}
|
|
66
66
|
if (!type.isString(adapter.type)) {
|
|
67
|
-
throw new
|
|
67
|
+
throw new ConfigError({
|
|
68
68
|
message: `Auth adapter type is not a string at adapter "${adapter.id}". Received ${JSON.stringify(adapter.type)}.`,
|
|
69
69
|
configKey,
|
|
70
70
|
context
|
|
71
|
-
})
|
|
71
|
+
});
|
|
72
72
|
}
|
|
73
73
|
context.typeCounters.auth.adapters.increment(adapter.type, adapter['~k']);
|
|
74
74
|
}
|
|
@@ -14,20 +14,19 @@
|
|
|
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/node-utils';
|
|
17
18
|
import lowdefySchema from '../../lowdefySchema.js';
|
|
18
|
-
import formatConfigError from '../../utils/formatConfigError.js';
|
|
19
19
|
import validateMutualExclusivity from './validateMutualExclusivity.js';
|
|
20
|
-
|
|
20
|
+
function validateAuthConfig({ components, context }) {
|
|
21
21
|
if (type.isNone(components.auth)) {
|
|
22
22
|
components.auth = {};
|
|
23
23
|
}
|
|
24
24
|
if (!type.isObject(components.auth)) {
|
|
25
|
-
|
|
26
|
-
throw new Error(formatConfigError({
|
|
25
|
+
throw new ConfigError({
|
|
27
26
|
message: 'lowdefy.auth is not an object.',
|
|
28
|
-
configKey,
|
|
27
|
+
configKey: components['~k'],
|
|
29
28
|
context
|
|
30
|
-
})
|
|
29
|
+
});
|
|
31
30
|
}
|
|
32
31
|
if (type.isNone(components.auth.api)) {
|
|
33
32
|
components.auth.api = {};
|
|
@@ -59,10 +58,35 @@ async function validateAuthConfig({ components, context }) {
|
|
|
59
58
|
if (type.isNone(components.auth.theme)) {
|
|
60
59
|
components.auth.theme = {};
|
|
61
60
|
}
|
|
62
|
-
validate({
|
|
61
|
+
const { valid, errors } = validate({
|
|
63
62
|
schema: lowdefySchema.definitions.authConfig,
|
|
64
|
-
data: components.auth
|
|
63
|
+
data: components.auth,
|
|
64
|
+
returnErrors: true
|
|
65
65
|
});
|
|
66
|
+
if (!valid) {
|
|
67
|
+
errors.forEach((error)=>{
|
|
68
|
+
// Try to get configKey from the item in the error path
|
|
69
|
+
const instancePath = error.instancePath.split('/').filter(Boolean);
|
|
70
|
+
let configKey = components.auth['~k'];
|
|
71
|
+
let currentData = components.auth;
|
|
72
|
+
for (const part of instancePath){
|
|
73
|
+
if (type.isArray(currentData)) {
|
|
74
|
+
const index = parseInt(part, 10);
|
|
75
|
+
currentData = currentData[index];
|
|
76
|
+
} else {
|
|
77
|
+
currentData = currentData?.[part];
|
|
78
|
+
}
|
|
79
|
+
if (currentData?.['~k']) {
|
|
80
|
+
configKey = currentData['~k'];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw new ConfigError({
|
|
84
|
+
message: `Auth ${error.message}.`,
|
|
85
|
+
configKey,
|
|
86
|
+
context
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
66
90
|
validateMutualExclusivity({
|
|
67
91
|
components,
|
|
68
92
|
context,
|
|
@@ -73,6 +97,14 @@ async function validateAuthConfig({ components, context }) {
|
|
|
73
97
|
context,
|
|
74
98
|
entity: 'pages'
|
|
75
99
|
});
|
|
100
|
+
// Validate NEXTAUTH_SECRET is set when auth providers are configured
|
|
101
|
+
if (components.auth.providers.length > 0 && type.isNone(process.env.NEXTAUTH_SECRET)) {
|
|
102
|
+
throw new ConfigError({
|
|
103
|
+
message: '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`).',
|
|
104
|
+
configKey: components.auth.providers['~k'] ?? components.auth['~k'],
|
|
105
|
+
context
|
|
106
|
+
});
|
|
107
|
+
}
|
|
76
108
|
return components;
|
|
77
109
|
}
|
|
78
110
|
export default validateAuthConfig;
|
|
@@ -13,29 +13,29 @@
|
|
|
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
|
|
16
|
+
import { ConfigError } from '@lowdefy/node-utils';
|
|
17
17
|
function validateMutualExclusivity({ components, context, entity }) {
|
|
18
18
|
const configKey = components.auth[entity]?.['~k'] || components.auth?.['~k'];
|
|
19
19
|
if (components.auth[entity].protected === true && components.auth[entity].public === true || type.isArray(components.auth[entity].protected) && type.isArray(components.auth[entity].public)) {
|
|
20
|
-
throw new
|
|
20
|
+
throw new ConfigError({
|
|
21
21
|
message: `Protected and public ${entity} are mutually exclusive. When protected ${entity} are listed, all unlisted ${entity} are public by default and vice versa.`,
|
|
22
22
|
configKey,
|
|
23
23
|
context
|
|
24
|
-
})
|
|
24
|
+
});
|
|
25
25
|
}
|
|
26
26
|
if (components.auth[entity].protected === false) {
|
|
27
|
-
throw new
|
|
27
|
+
throw new ConfigError({
|
|
28
28
|
message: `Protected ${entity} can not be set to false.`,
|
|
29
29
|
configKey,
|
|
30
30
|
context
|
|
31
|
-
})
|
|
31
|
+
});
|
|
32
32
|
}
|
|
33
33
|
if (components.auth[entity].public === false) {
|
|
34
|
-
throw new
|
|
34
|
+
throw new ConfigError({
|
|
35
35
|
message: `Public ${entity} can not be set to false.`,
|
|
36
36
|
configKey,
|
|
37
37
|
context
|
|
38
|
-
})
|
|
38
|
+
});
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
export default validateMutualExclusivity;
|
|
@@ -12,54 +12,35 @@
|
|
|
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
|
|
16
|
-
import collectConfigError from '../utils/collectConfigError.js';
|
|
17
|
-
import countOperators from '../utils/countOperators.js';
|
|
15
|
+
*/ import countOperators from '../utils/countOperators.js';
|
|
18
16
|
import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
|
|
19
17
|
function buildConnections({ components, context }) {
|
|
20
18
|
// Store connection IDs for validation in buildRequests
|
|
21
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)
|
|
22
22
|
const checkDuplicateConnectionId = createCheckDuplicateId({
|
|
23
23
|
message: 'Duplicate connectionId "{{ id }}".',
|
|
24
24
|
context
|
|
25
25
|
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
configKey,
|
|
33
|
-
context
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
if (!type.isString(connection.id)) {
|
|
37
|
-
collectConfigError({
|
|
38
|
-
message: `Connection id is not a string. Received ${JSON.stringify(connection.id)}.`,
|
|
39
|
-
configKey,
|
|
40
|
-
context
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
checkDuplicateConnectionId({
|
|
44
|
-
id: connection.id,
|
|
45
|
-
configKey
|
|
46
|
-
});
|
|
47
|
-
if (!type.isString(connection.type)) {
|
|
48
|
-
collectConfigError({
|
|
49
|
-
message: `Connection type is not a string at connection "${connection.id}". Received ${JSON.stringify(connection.type)}.`,
|
|
50
|
-
configKey,
|
|
51
|
-
context
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
context.typeCounters.connections.increment(connection.type, connection['~k']);
|
|
55
|
-
connection.connectionId = connection.id;
|
|
56
|
-
context.connectionIds.add(connection.connectionId);
|
|
57
|
-
connection.id = `connection:${connection.id}`;
|
|
58
|
-
countOperators(connection.properties || {}, {
|
|
59
|
-
counter: context.typeCounters.operators.server
|
|
60
|
-
});
|
|
26
|
+
(components.connections ?? []).forEach((connection)=>{
|
|
27
|
+
const configKey = connection['~k'];
|
|
28
|
+
// Check duplicates (schema can't validate this)
|
|
29
|
+
checkDuplicateConnectionId({
|
|
30
|
+
id: connection.id,
|
|
31
|
+
configKey
|
|
61
32
|
});
|
|
62
|
-
|
|
33
|
+
// Track type usage for buildTypes validation
|
|
34
|
+
context.typeCounters.connections.increment(connection.type, configKey);
|
|
35
|
+
// Store connectionId for request validation and rename id
|
|
36
|
+
connection.connectionId = connection.id;
|
|
37
|
+
context.connectionIds.add(connection.connectionId);
|
|
38
|
+
connection.id = `connection:${connection.id}`;
|
|
39
|
+
// Count operators in connection properties
|
|
40
|
+
countOperators(connection.properties ?? {}, {
|
|
41
|
+
counter: context.typeCounters.operators.server
|
|
42
|
+
});
|
|
43
|
+
});
|
|
63
44
|
return components;
|
|
64
45
|
}
|
|
65
46
|
export default buildConnections;
|
package/dist/build/buildMenu.js
CHANGED
|
@@ -12,9 +12,9 @@
|
|
|
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
|
|
15
|
+
*/ import { type } from '@lowdefy/helpers';
|
|
16
|
+
import { ConfigError, resolveConfigLocation } from '@lowdefy/node-utils';
|
|
16
17
|
import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
|
|
17
|
-
import formatConfigError from '../utils/formatConfigError.js';
|
|
18
18
|
function buildDefaultMenu({ components, context }) {
|
|
19
19
|
context.logger.warn('No menus found. Building default menu.');
|
|
20
20
|
const pages = type.isArray(components.pages) ? components.pages : [];
|
|
@@ -96,18 +96,18 @@ function buildMenu({ components, context }) {
|
|
|
96
96
|
components.menus.forEach((menu)=>{
|
|
97
97
|
const configKey = menu['~k'];
|
|
98
98
|
if (type.isUndefined(menu.id)) {
|
|
99
|
-
throw new
|
|
99
|
+
throw new ConfigError({
|
|
100
100
|
message: 'Menu id missing.',
|
|
101
101
|
configKey,
|
|
102
102
|
context
|
|
103
|
-
})
|
|
103
|
+
});
|
|
104
104
|
}
|
|
105
105
|
if (!type.isString(menu.id)) {
|
|
106
|
-
throw new
|
|
106
|
+
throw new ConfigError({
|
|
107
107
|
message: `Menu id is not a string. Received ${JSON.stringify(menu.id)}.`,
|
|
108
108
|
configKey,
|
|
109
109
|
context
|
|
110
|
-
})
|
|
110
|
+
});
|
|
111
111
|
}
|
|
112
112
|
checkDuplicateMenuId({
|
|
113
113
|
id: menu.id,
|
|
@@ -129,16 +129,11 @@ function buildMenu({ components, context }) {
|
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
131
|
missingPageWarnings.forEach((warning)=>{
|
|
132
|
-
|
|
132
|
+
context.logger.configWarning({
|
|
133
|
+
message: `Page "${warning.pageId}" referenced in menu link "${warning.menuItemId}" not found.`,
|
|
133
134
|
configKey: warning.configKey,
|
|
134
|
-
|
|
135
|
-
refMap: context.refMap,
|
|
136
|
-
configDirectory: context.directories.config
|
|
135
|
+
prodError: true
|
|
137
136
|
});
|
|
138
|
-
const source = location?.source ? `${location.source} at ${location.config}` : '';
|
|
139
|
-
const link = location?.link || '';
|
|
140
|
-
const message = `Page "${warning.pageId}" referenced in menu link "${warning.menuItemId}" not found.`;
|
|
141
|
-
context.logger.warn(`[Config Error] ${message}\n ${source}\n ${link}`);
|
|
142
137
|
});
|
|
143
138
|
return components;
|
|
144
139
|
}
|
|
@@ -13,23 +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
|
+
import { ConfigError } from '@lowdefy/node-utils';
|
|
16
17
|
import createCheckDuplicateId from '../../../utils/createCheckDuplicateId.js';
|
|
17
|
-
import formatConfigError from '../../../utils/formatConfigError.js';
|
|
18
18
|
function checkAction(action, { blockId, checkDuplicateActionId, context, eventId, linkActionRefs, pageId, requestActionRefs, typeCounters }) {
|
|
19
19
|
const configKey = action['~k'];
|
|
20
20
|
if (type.isUndefined(action.id)) {
|
|
21
|
-
throw new
|
|
21
|
+
throw new ConfigError({
|
|
22
22
|
message: `Action id missing on event "${eventId}" on block "${blockId}" on page "${pageId}".`,
|
|
23
23
|
configKey,
|
|
24
24
|
context
|
|
25
|
-
})
|
|
25
|
+
});
|
|
26
26
|
}
|
|
27
27
|
if (!type.isString(action.id)) {
|
|
28
|
-
throw new
|
|
28
|
+
throw new ConfigError({
|
|
29
29
|
message: `Action id is not a string on event "${eventId}" on block "${blockId}" on page "${pageId}". Received ${JSON.stringify(action.id)}.`,
|
|
30
30
|
configKey,
|
|
31
31
|
context
|
|
32
|
-
})
|
|
32
|
+
});
|
|
33
33
|
}
|
|
34
34
|
checkDuplicateActionId({
|
|
35
35
|
id: action.id,
|
|
@@ -39,11 +39,11 @@ function checkAction(action, { blockId, checkDuplicateActionId, context, eventId
|
|
|
39
39
|
pageId
|
|
40
40
|
});
|
|
41
41
|
if (!type.isString(action.type)) {
|
|
42
|
-
throw new
|
|
42
|
+
throw new ConfigError({
|
|
43
43
|
message: `Action type is not a string on action "${action.id}" on event "${eventId}" on block "${blockId}" on page "${pageId}". Received ${JSON.stringify(action.type)}.`,
|
|
44
44
|
configKey,
|
|
45
45
|
context
|
|
46
|
-
})
|
|
46
|
+
});
|
|
47
47
|
}
|
|
48
48
|
typeCounters.actions.increment(action.type, configKey);
|
|
49
49
|
// Collect static Request action references for validation
|
|
@@ -98,11 +98,11 @@ function buildEvents(block, pageContext) {
|
|
|
98
98
|
Object.keys(block.events).map((key)=>{
|
|
99
99
|
const eventConfigKey = block.events[key]?.['~k'] || block['~k'];
|
|
100
100
|
if (!type.isArray(block.events[key]) && !type.isObject(block.events[key]) || type.isObject(block.events[key]) && type.isNone(block.events[key].try)) {
|
|
101
|
-
throw new
|
|
101
|
+
throw new ConfigError({
|
|
102
102
|
message: `Actions must be an array at "${block.blockId}" in event "${key}" on page "${pageContext.pageId}". Received ${JSON.stringify(block.events[key]?.try)}`,
|
|
103
103
|
configKey: eventConfigKey,
|
|
104
104
|
context
|
|
105
|
-
})
|
|
105
|
+
});
|
|
106
106
|
}
|
|
107
107
|
if (type.isArray(block.events[key])) {
|
|
108
108
|
block.events[key] = {
|
|
@@ -111,21 +111,21 @@ function buildEvents(block, pageContext) {
|
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
113
|
if (!type.isArray(block.events[key].try)) {
|
|
114
|
-
throw new
|
|
114
|
+
throw new ConfigError({
|
|
115
115
|
message: `Try actions must be an array at "${block.blockId}" in event "${key}.try" on page "${pageContext.pageId}". Received ${JSON.stringify(block.events[key].try)}`,
|
|
116
116
|
configKey: eventConfigKey,
|
|
117
117
|
context
|
|
118
|
-
})
|
|
118
|
+
});
|
|
119
119
|
}
|
|
120
120
|
if (type.isNone(block.events[key].catch)) {
|
|
121
121
|
block.events[key].catch = [];
|
|
122
122
|
}
|
|
123
123
|
if (!type.isArray(block.events[key].catch)) {
|
|
124
|
-
throw new
|
|
124
|
+
throw new ConfigError({
|
|
125
125
|
message: `Catch actions must be an array at "${block.blockId}" in event "${key}.catch" on page "${pageContext.pageId}". Received ${JSON.stringify(block.events[key].catch)}`,
|
|
126
126
|
configKey: eventConfigKey,
|
|
127
127
|
context
|
|
128
|
-
})
|
|
128
|
+
});
|
|
129
129
|
}
|
|
130
130
|
const checkDuplicateActionId = createCheckDuplicateId({
|
|
131
131
|
message: 'Duplicate actionId "{{ id }}" on event "{{ eventId }}" on block "{{ blockId }}" on page "{{ pageId }}".',
|
|
@@ -13,23 +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
|
-
import
|
|
16
|
+
import { ConfigError } from '@lowdefy/node-utils';
|
|
17
17
|
function buildRequest(request, pageContext) {
|
|
18
18
|
const { auth, checkDuplicateRequestId, context, pageId, typeCounters } = pageContext;
|
|
19
19
|
const configKey = request['~k'];
|
|
20
20
|
if (type.isUndefined(request.id)) {
|
|
21
|
-
throw new
|
|
21
|
+
throw new ConfigError({
|
|
22
22
|
message: `Request id missing at page "${pageId}".`,
|
|
23
23
|
configKey,
|
|
24
24
|
context
|
|
25
|
-
})
|
|
25
|
+
});
|
|
26
26
|
}
|
|
27
27
|
if (!type.isString(request.id)) {
|
|
28
|
-
throw new
|
|
28
|
+
throw new ConfigError({
|
|
29
29
|
message: `Request id is not a string at page "${pageId}". Received ${JSON.stringify(request.id)}.`,
|
|
30
30
|
configKey,
|
|
31
31
|
context
|
|
32
|
-
})
|
|
32
|
+
});
|
|
33
33
|
}
|
|
34
34
|
checkDuplicateRequestId({
|
|
35
35
|
id: request.id,
|
|
@@ -37,44 +37,44 @@ function buildRequest(request, pageContext) {
|
|
|
37
37
|
pageId
|
|
38
38
|
});
|
|
39
39
|
if (request.id.includes('.')) {
|
|
40
|
-
throw new
|
|
40
|
+
throw new ConfigError({
|
|
41
41
|
message: `Request id "${request.id}" at page "${pageId}" should not include a period (".").`,
|
|
42
42
|
configKey,
|
|
43
43
|
context
|
|
44
|
-
})
|
|
44
|
+
});
|
|
45
45
|
}
|
|
46
46
|
if (!type.isString(request.type)) {
|
|
47
|
-
throw new
|
|
47
|
+
throw new ConfigError({
|
|
48
48
|
message: `Request type is not a string at request "${request.id}" at page "${pageId}". Received ${JSON.stringify(request.type)}.`,
|
|
49
49
|
configKey,
|
|
50
50
|
context
|
|
51
|
-
})
|
|
51
|
+
});
|
|
52
52
|
}
|
|
53
53
|
typeCounters.requests.increment(request.type, configKey);
|
|
54
54
|
// Validate connectionId references an existing connection
|
|
55
55
|
if (!type.isNone(request.connectionId)) {
|
|
56
56
|
if (!type.isString(request.connectionId)) {
|
|
57
|
-
throw new
|
|
57
|
+
throw new ConfigError({
|
|
58
58
|
message: `Request "${request.id}" at page "${pageId}" connectionId is not a string. Received ${JSON.stringify(request.connectionId)}.`,
|
|
59
59
|
configKey,
|
|
60
60
|
context
|
|
61
|
-
})
|
|
61
|
+
});
|
|
62
62
|
}
|
|
63
63
|
if (!context.connectionIds.has(request.connectionId)) {
|
|
64
|
-
throw new
|
|
64
|
+
throw new ConfigError({
|
|
65
65
|
message: `Request "${request.id}" at page "${pageId}" references non-existent connection "${request.connectionId}".`,
|
|
66
66
|
configKey,
|
|
67
67
|
context
|
|
68
|
-
})
|
|
68
|
+
});
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
if (type.isUndefined(request.payload)) request.payload = {};
|
|
72
72
|
if (!type.isObject(request.payload)) {
|
|
73
|
-
throw new
|
|
73
|
+
throw new ConfigError({
|
|
74
74
|
message: `Request "${request.id}" at page "${pageId}" payload should be an object.`,
|
|
75
75
|
configKey,
|
|
76
76
|
context
|
|
77
|
-
})
|
|
77
|
+
});
|
|
78
78
|
}
|
|
79
79
|
request.auth = auth;
|
|
80
80
|
request.requestId = request.id;
|
|
@@ -13,51 +13,51 @@
|
|
|
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
|
|
16
|
+
import { ConfigError } from '@lowdefy/node-utils';
|
|
17
17
|
function validateBlock(block, { pageId, context }) {
|
|
18
18
|
const configKey = block?.['~k'];
|
|
19
19
|
if (!type.isObject(block)) {
|
|
20
|
-
throw new
|
|
20
|
+
throw new ConfigError({
|
|
21
21
|
message: `Expected block to be an object on page "${pageId}". Received ${JSON.stringify(block)}.`,
|
|
22
22
|
configKey,
|
|
23
23
|
context
|
|
24
|
-
})
|
|
24
|
+
});
|
|
25
25
|
}
|
|
26
26
|
if (type.isUndefined(block.id)) {
|
|
27
|
-
throw new
|
|
27
|
+
throw new ConfigError({
|
|
28
28
|
message: `Block id missing at page "${pageId}".`,
|
|
29
29
|
configKey,
|
|
30
30
|
context
|
|
31
|
-
})
|
|
31
|
+
});
|
|
32
32
|
}
|
|
33
33
|
if (!type.isString(block.id)) {
|
|
34
|
-
throw new
|
|
34
|
+
throw new ConfigError({
|
|
35
35
|
message: `Block id is not a string at page "${pageId}". Received ${JSON.stringify(block.id)}.`,
|
|
36
36
|
configKey,
|
|
37
37
|
context
|
|
38
|
-
})
|
|
38
|
+
});
|
|
39
39
|
}
|
|
40
40
|
if (type.isNone(block.type)) {
|
|
41
|
-
throw new
|
|
41
|
+
throw new ConfigError({
|
|
42
42
|
message: `Block type is not defined at "${block.id}" on page "${pageId}".`,
|
|
43
43
|
configKey,
|
|
44
44
|
context
|
|
45
|
-
})
|
|
45
|
+
});
|
|
46
46
|
}
|
|
47
47
|
if (!type.isString(block.type)) {
|
|
48
|
-
throw new
|
|
48
|
+
throw new ConfigError({
|
|
49
49
|
message: `Block type is not a string at "${block.id}" on page "${pageId}". Received ${JSON.stringify(block.type)}.`,
|
|
50
50
|
configKey,
|
|
51
51
|
context
|
|
52
|
-
})
|
|
52
|
+
});
|
|
53
53
|
}
|
|
54
54
|
if (!type.isNone(block.requests)) {
|
|
55
55
|
if (!type.isArray(block.requests)) {
|
|
56
|
-
throw new
|
|
56
|
+
throw new ConfigError({
|
|
57
57
|
message: `Requests is not an array at "${block.id}" on page "${pageId}". Received ${JSON.stringify(block.requests)}`,
|
|
58
58
|
configKey,
|
|
59
59
|
context
|
|
60
|
-
})
|
|
60
|
+
});
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|