@lowdefy/build 0.0.0-experimental-20251203205559 → 0.0.0-experimental-20260113081624
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 +2 -0
- package/dist/build/buildApi/buildEndpoint.js +6 -0
- package/dist/build/buildApi/buildRoutine/countStepTypes.js +1 -1
- package/dist/build/buildApi/validateStepReferences.js +65 -0
- package/dist/build/buildAuth/buildAuthPlugins.js +42 -12
- package/dist/build/buildAuth/validateAuthConfig.js +10 -2
- package/dist/build/buildAuth/validateMutualExclusivity.js +18 -4
- package/dist/build/buildConnections.js +25 -6
- package/dist/build/buildImports/buildIconImports.js +19 -36
- package/dist/build/buildLogger.js +41 -0
- package/dist/build/buildMenu.js +40 -13
- package/dist/build/buildPages/buildBlock/buildEvents.js +90 -9
- package/dist/build/buildPages/buildBlock/buildRequests.js +47 -7
- package/dist/build/buildPages/buildBlock/countBlockTypes.js +1 -1
- package/dist/build/buildPages/buildBlock/validateBlock.js +33 -7
- package/dist/build/buildPages/buildPage.js +28 -4
- package/dist/build/buildPages/buildPages.js +26 -1
- package/dist/build/buildPages/buildTestPage.js +9 -1
- package/dist/build/buildPages/validateLinkReferences.js +33 -0
- package/dist/build/buildPages/validatePayloadReferences.js +52 -0
- package/dist/build/buildPages/validateRequestReferences.js +33 -0
- package/dist/build/buildPages/validateStateReferences.js +52 -0
- package/dist/build/buildRefs/buildRefs.js +11 -33
- package/dist/build/buildRefs/evaluateBuildOperators.js +8 -2
- package/dist/build/buildRefs/getConfigFile.js +2 -1
- package/dist/build/buildRefs/getRefContent.js +17 -31
- package/dist/build/buildRefs/getRefsFromFile.js +8 -3
- package/dist/build/buildRefs/makeRefDefinition.js +9 -8
- package/dist/build/buildRefs/parseRefContent.js +61 -2
- package/dist/build/buildRefs/populateRefs.js +14 -11
- package/dist/build/buildRefs/recursiveBuild.js +62 -71
- package/dist/build/buildTypes.js +19 -2
- package/dist/build/formatBuildError.js +34 -0
- package/dist/build/writeLogger.js +19 -0
- package/dist/createContext.js +3 -19
- package/dist/defaultTypesMap.js +541 -534
- package/dist/index.js +138 -125
- package/dist/lowdefySchema.js +75 -0
- package/dist/test/testContext.js +2 -1
- package/dist/utils/collectConfigError.js +40 -0
- package/dist/utils/countOperators.js +24 -11
- package/dist/utils/createCheckDuplicateId.js +30 -9
- package/dist/utils/createCounter.js +15 -2
- package/dist/utils/extractOperatorKey.js +36 -0
- package/dist/utils/findSimilarString.js +53 -0
- package/dist/utils/formatConfigError.js +24 -0
- package/dist/utils/formatConfigMessage.js +33 -0
- package/dist/utils/formatConfigWarning.js +24 -0
- package/dist/utils/traverseConfig.js +43 -0
- package/dist/utils/tryBuildStep.js +38 -0
- package/package.json +39 -39
- package/dist/utils/createBuildProfiler.js +0 -125
- package/dist/utils/invalidateChangedFiles.js +0 -89
- package/dist/utils/makeRefHash.js +0 -15
|
@@ -14,29 +14,95 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import { type } from '@lowdefy/helpers';
|
|
16
16
|
import createCheckDuplicateId from '../../../utils/createCheckDuplicateId.js';
|
|
17
|
-
|
|
17
|
+
import formatConfigError from '../../../utils/formatConfigError.js';
|
|
18
|
+
function checkAction(action, { blockId, checkDuplicateActionId, context, eventId, linkActionRefs, pageId, requestActionRefs, typeCounters }) {
|
|
19
|
+
const configKey = action['~k'];
|
|
18
20
|
if (type.isUndefined(action.id)) {
|
|
19
|
-
throw new Error(
|
|
21
|
+
throw new Error(formatConfigError({
|
|
22
|
+
message: `Action id missing on event "${eventId}" on block "${blockId}" on page "${pageId}".`,
|
|
23
|
+
configKey,
|
|
24
|
+
context
|
|
25
|
+
}));
|
|
20
26
|
}
|
|
21
27
|
if (!type.isString(action.id)) {
|
|
22
|
-
throw new Error(
|
|
28
|
+
throw new Error(formatConfigError({
|
|
29
|
+
message: `Action id is not a string on event "${eventId}" on block "${blockId}" on page "${pageId}". Received ${JSON.stringify(action.id)}.`,
|
|
30
|
+
configKey,
|
|
31
|
+
context
|
|
32
|
+
}));
|
|
23
33
|
}
|
|
24
34
|
checkDuplicateActionId({
|
|
25
35
|
id: action.id,
|
|
36
|
+
configKey,
|
|
26
37
|
eventId,
|
|
27
38
|
blockId,
|
|
28
39
|
pageId
|
|
29
40
|
});
|
|
30
41
|
if (!type.isString(action.type)) {
|
|
31
|
-
throw new Error(
|
|
42
|
+
throw new Error(formatConfigError({
|
|
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
|
+
configKey,
|
|
45
|
+
context
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
typeCounters.actions.increment(action.type, configKey);
|
|
49
|
+
// Collect static Request action references for validation
|
|
50
|
+
if (action.type === 'Request' && !type.isNone(action.params)) {
|
|
51
|
+
const params = action.params;
|
|
52
|
+
if (type.isString(params)) {
|
|
53
|
+
requestActionRefs.push({
|
|
54
|
+
requestId: params,
|
|
55
|
+
action,
|
|
56
|
+
blockId,
|
|
57
|
+
eventId
|
|
58
|
+
});
|
|
59
|
+
} else if (type.isArray(params)) {
|
|
60
|
+
params.forEach((param)=>{
|
|
61
|
+
if (type.isString(param)) {
|
|
62
|
+
requestActionRefs.push({
|
|
63
|
+
requestId: param,
|
|
64
|
+
action,
|
|
65
|
+
blockId,
|
|
66
|
+
eventId
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Collect static Link action references for validation
|
|
73
|
+
if (action.type === 'Link' && !type.isNone(action.params)) {
|
|
74
|
+
const params = action.params;
|
|
75
|
+
// Link params can be a string (pageId) or object with pageId property
|
|
76
|
+
if (type.isString(params)) {
|
|
77
|
+
linkActionRefs.push({
|
|
78
|
+
pageId: params,
|
|
79
|
+
action,
|
|
80
|
+
blockId,
|
|
81
|
+
eventId,
|
|
82
|
+
sourcePageId: pageId
|
|
83
|
+
});
|
|
84
|
+
} else if (type.isObject(params) && type.isString(params.pageId)) {
|
|
85
|
+
linkActionRefs.push({
|
|
86
|
+
pageId: params.pageId,
|
|
87
|
+
action,
|
|
88
|
+
blockId,
|
|
89
|
+
eventId,
|
|
90
|
+
sourcePageId: pageId
|
|
91
|
+
});
|
|
92
|
+
}
|
|
32
93
|
}
|
|
33
|
-
typeCounters.actions.increment(action.type);
|
|
34
94
|
}
|
|
35
95
|
function buildEvents(block, pageContext) {
|
|
96
|
+
const { context } = pageContext;
|
|
36
97
|
if (block.events) {
|
|
37
98
|
Object.keys(block.events).map((key)=>{
|
|
99
|
+
const eventConfigKey = block.events[key]?.['~k'] || block['~k'];
|
|
38
100
|
if (!type.isArray(block.events[key]) && !type.isObject(block.events[key]) || type.isObject(block.events[key]) && type.isNone(block.events[key].try)) {
|
|
39
|
-
throw new Error(
|
|
101
|
+
throw new Error(formatConfigError({
|
|
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
|
+
configKey: eventConfigKey,
|
|
104
|
+
context
|
|
105
|
+
}));
|
|
40
106
|
}
|
|
41
107
|
if (type.isArray(block.events[key])) {
|
|
42
108
|
block.events[key] = {
|
|
@@ -45,29 +111,44 @@ function buildEvents(block, pageContext) {
|
|
|
45
111
|
};
|
|
46
112
|
}
|
|
47
113
|
if (!type.isArray(block.events[key].try)) {
|
|
48
|
-
throw new Error(
|
|
114
|
+
throw new Error(formatConfigError({
|
|
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
|
+
configKey: eventConfigKey,
|
|
117
|
+
context
|
|
118
|
+
}));
|
|
49
119
|
}
|
|
50
120
|
if (type.isNone(block.events[key].catch)) {
|
|
51
121
|
block.events[key].catch = [];
|
|
52
122
|
}
|
|
53
123
|
if (!type.isArray(block.events[key].catch)) {
|
|
54
|
-
throw new Error(
|
|
124
|
+
throw new Error(formatConfigError({
|
|
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
|
+
configKey: eventConfigKey,
|
|
127
|
+
context
|
|
128
|
+
}));
|
|
55
129
|
}
|
|
56
130
|
const checkDuplicateActionId = createCheckDuplicateId({
|
|
57
|
-
message: 'Duplicate actionId "{{ id }}" on event "{{ eventId }}" on block "{{ blockId }}" on page "{{ pageId }}".'
|
|
131
|
+
message: 'Duplicate actionId "{{ id }}" on event "{{ eventId }}" on block "{{ blockId }}" on page "{{ pageId }}".',
|
|
132
|
+
context
|
|
58
133
|
});
|
|
59
134
|
block.events[key].try.map((action)=>checkAction(action, {
|
|
60
135
|
eventId: key,
|
|
61
136
|
blockId: block.blockId,
|
|
137
|
+
context,
|
|
62
138
|
typeCounters: pageContext.typeCounters,
|
|
63
139
|
pageId: pageContext.pageId,
|
|
140
|
+
linkActionRefs: pageContext.linkActionRefs,
|
|
141
|
+
requestActionRefs: pageContext.requestActionRefs,
|
|
64
142
|
checkDuplicateActionId
|
|
65
143
|
}));
|
|
66
144
|
block.events[key].catch.map((action)=>checkAction(action, {
|
|
67
145
|
eventId: key,
|
|
68
146
|
blockId: block.blockId,
|
|
147
|
+
context,
|
|
69
148
|
typeCounters: pageContext.typeCounters,
|
|
70
149
|
pageId: pageContext.pageId,
|
|
150
|
+
linkActionRefs: pageContext.linkActionRefs,
|
|
151
|
+
requestActionRefs: pageContext.requestActionRefs,
|
|
71
152
|
checkDuplicateActionId
|
|
72
153
|
}));
|
|
73
154
|
});
|
|
@@ -13,28 +13,68 @@
|
|
|
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 formatConfigError from '../../../utils/formatConfigError.js';
|
|
16
17
|
function buildRequest(request, pageContext) {
|
|
17
|
-
const { auth, checkDuplicateRequestId, pageId, typeCounters } = pageContext;
|
|
18
|
+
const { auth, checkDuplicateRequestId, context, pageId, typeCounters } = pageContext;
|
|
19
|
+
const configKey = request['~k'];
|
|
18
20
|
if (type.isUndefined(request.id)) {
|
|
19
|
-
throw new Error(
|
|
21
|
+
throw new Error(formatConfigError({
|
|
22
|
+
message: `Request id missing at page "${pageId}".`,
|
|
23
|
+
configKey,
|
|
24
|
+
context
|
|
25
|
+
}));
|
|
20
26
|
}
|
|
21
27
|
if (!type.isString(request.id)) {
|
|
22
|
-
throw new Error(
|
|
28
|
+
throw new Error(formatConfigError({
|
|
29
|
+
message: `Request id is not a string at page "${pageId}". Received ${JSON.stringify(request.id)}.`,
|
|
30
|
+
configKey,
|
|
31
|
+
context
|
|
32
|
+
}));
|
|
23
33
|
}
|
|
24
34
|
checkDuplicateRequestId({
|
|
25
35
|
id: request.id,
|
|
36
|
+
configKey,
|
|
26
37
|
pageId
|
|
27
38
|
});
|
|
28
39
|
if (request.id.includes('.')) {
|
|
29
|
-
throw new Error(
|
|
40
|
+
throw new Error(formatConfigError({
|
|
41
|
+
message: `Request id "${request.id}" at page "${pageId}" should not include a period (".").`,
|
|
42
|
+
configKey,
|
|
43
|
+
context
|
|
44
|
+
}));
|
|
30
45
|
}
|
|
31
46
|
if (!type.isString(request.type)) {
|
|
32
|
-
throw new Error(
|
|
47
|
+
throw new Error(formatConfigError({
|
|
48
|
+
message: `Request type is not a string at request "${request.id}" at page "${pageId}". Received ${JSON.stringify(request.type)}.`,
|
|
49
|
+
configKey,
|
|
50
|
+
context
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
typeCounters.requests.increment(request.type, configKey);
|
|
54
|
+
// Validate connectionId references an existing connection
|
|
55
|
+
if (!type.isNone(request.connectionId)) {
|
|
56
|
+
if (!type.isString(request.connectionId)) {
|
|
57
|
+
throw new Error(formatConfigError({
|
|
58
|
+
message: `Request "${request.id}" at page "${pageId}" connectionId is not a string. Received ${JSON.stringify(request.connectionId)}.`,
|
|
59
|
+
configKey,
|
|
60
|
+
context
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
if (!context.connectionIds.has(request.connectionId)) {
|
|
64
|
+
throw new Error(formatConfigError({
|
|
65
|
+
message: `Request "${request.id}" at page "${pageId}" references non-existent connection "${request.connectionId}".`,
|
|
66
|
+
configKey,
|
|
67
|
+
context
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
33
70
|
}
|
|
34
|
-
typeCounters.requests.increment(request.type);
|
|
35
71
|
if (type.isUndefined(request.payload)) request.payload = {};
|
|
36
72
|
if (!type.isObject(request.payload)) {
|
|
37
|
-
throw new Error(
|
|
73
|
+
throw new Error(formatConfigError({
|
|
74
|
+
message: `Request "${request.id}" at page "${pageId}" payload should be an object.`,
|
|
75
|
+
configKey,
|
|
76
|
+
context
|
|
77
|
+
}));
|
|
38
78
|
}
|
|
39
79
|
request.auth = auth;
|
|
40
80
|
request.requestId = request.id;
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ function countBlockTypes(block, { typeCounters }) {
|
|
16
|
-
typeCounters.blocks.increment(block.type);
|
|
16
|
+
typeCounters.blocks.increment(block.type, block['~k']);
|
|
17
17
|
}
|
|
18
18
|
export default countBlockTypes;
|
|
@@ -13,25 +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
|
-
|
|
16
|
+
import formatConfigError from '../../../utils/formatConfigError.js';
|
|
17
|
+
function validateBlock(block, { pageId, context }) {
|
|
18
|
+
const configKey = block?.['~k'];
|
|
17
19
|
if (!type.isObject(block)) {
|
|
18
|
-
throw new Error(
|
|
20
|
+
throw new Error(formatConfigError({
|
|
21
|
+
message: `Expected block to be an object on page "${pageId}". Received ${JSON.stringify(block)}.`,
|
|
22
|
+
configKey,
|
|
23
|
+
context
|
|
24
|
+
}));
|
|
19
25
|
}
|
|
20
26
|
if (type.isUndefined(block.id)) {
|
|
21
|
-
throw new Error(
|
|
27
|
+
throw new Error(formatConfigError({
|
|
28
|
+
message: `Block id missing at page "${pageId}".`,
|
|
29
|
+
configKey,
|
|
30
|
+
context
|
|
31
|
+
}));
|
|
22
32
|
}
|
|
23
33
|
if (!type.isString(block.id)) {
|
|
24
|
-
throw new Error(
|
|
34
|
+
throw new Error(formatConfigError({
|
|
35
|
+
message: `Block id is not a string at page "${pageId}". Received ${JSON.stringify(block.id)}.`,
|
|
36
|
+
configKey,
|
|
37
|
+
context
|
|
38
|
+
}));
|
|
25
39
|
}
|
|
26
40
|
if (type.isNone(block.type)) {
|
|
27
|
-
throw new Error(
|
|
41
|
+
throw new Error(formatConfigError({
|
|
42
|
+
message: `Block type is not defined at "${block.id}" on page "${pageId}".`,
|
|
43
|
+
configKey,
|
|
44
|
+
context
|
|
45
|
+
}));
|
|
28
46
|
}
|
|
29
47
|
if (!type.isString(block.type)) {
|
|
30
|
-
throw new Error(
|
|
48
|
+
throw new Error(formatConfigError({
|
|
49
|
+
message: `Block type is not a string at "${block.id}" on page "${pageId}". Received ${JSON.stringify(block.type)}.`,
|
|
50
|
+
configKey,
|
|
51
|
+
context
|
|
52
|
+
}));
|
|
31
53
|
}
|
|
32
54
|
if (!type.isNone(block.requests)) {
|
|
33
55
|
if (!type.isArray(block.requests)) {
|
|
34
|
-
throw new Error(
|
|
56
|
+
throw new Error(formatConfigError({
|
|
57
|
+
message: `Requests is not an array at "${block.id}" on page "${pageId}". Received ${JSON.stringify(block.requests)}`,
|
|
58
|
+
configKey,
|
|
59
|
+
context
|
|
60
|
+
}));
|
|
35
61
|
}
|
|
36
62
|
}
|
|
37
63
|
}
|
|
@@ -16,30 +16,54 @@
|
|
|
16
16
|
import buildBlock from './buildBlock/buildBlock.js';
|
|
17
17
|
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
18
18
|
import createCounter from '../../utils/createCounter.js';
|
|
19
|
+
import formatConfigError from '../../utils/formatConfigError.js';
|
|
20
|
+
import validateRequestReferences from './validateRequestReferences.js';
|
|
19
21
|
function buildPage({ page, index, context, checkDuplicatePageId }) {
|
|
22
|
+
const configKey = page['~k'];
|
|
20
23
|
if (type.isUndefined(page.id)) {
|
|
21
|
-
throw new Error(
|
|
24
|
+
throw new Error(formatConfigError({
|
|
25
|
+
message: `Page id missing at page ${index}.`,
|
|
26
|
+
configKey,
|
|
27
|
+
context
|
|
28
|
+
}));
|
|
22
29
|
}
|
|
23
30
|
if (!type.isString(page.id)) {
|
|
24
|
-
throw new Error(
|
|
31
|
+
throw new Error(formatConfigError({
|
|
32
|
+
message: `Page id is not a string at page ${index}. Received ${JSON.stringify(page.id)}.`,
|
|
33
|
+
configKey,
|
|
34
|
+
context
|
|
35
|
+
}));
|
|
25
36
|
}
|
|
26
37
|
checkDuplicatePageId({
|
|
27
|
-
id: page.id
|
|
38
|
+
id: page.id,
|
|
39
|
+
configKey
|
|
28
40
|
});
|
|
29
41
|
page.pageId = page.id;
|
|
30
42
|
const requests = [];
|
|
43
|
+
const requestActionRefs = [];
|
|
31
44
|
buildBlock(page, {
|
|
32
45
|
auth: page.auth,
|
|
33
46
|
blockIdCounter: createCounter(),
|
|
34
47
|
checkDuplicateRequestId: createCheckDuplicateId({
|
|
35
|
-
message: 'Duplicate requestId "{{ id }}" on page "{{ pageId }}".'
|
|
48
|
+
message: 'Duplicate requestId "{{ id }}" on page "{{ pageId }}".',
|
|
49
|
+
context
|
|
36
50
|
}),
|
|
51
|
+
context,
|
|
37
52
|
pageId: page.pageId,
|
|
38
53
|
requests,
|
|
54
|
+
requestActionRefs,
|
|
55
|
+
linkActionRefs: context.linkActionRefs,
|
|
39
56
|
typeCounters: context.typeCounters
|
|
40
57
|
});
|
|
41
58
|
// set page.id since buildBlock sets id as well.
|
|
42
59
|
page.id = `page:${page.pageId}`;
|
|
60
|
+
// Validate that all Request actions reference defined requests
|
|
61
|
+
validateRequestReferences({
|
|
62
|
+
requestActionRefs,
|
|
63
|
+
requests,
|
|
64
|
+
pageId: page.pageId,
|
|
65
|
+
context
|
|
66
|
+
});
|
|
43
67
|
page.requests = requests;
|
|
44
68
|
}
|
|
45
69
|
export default buildPage;
|
|
@@ -15,17 +15,42 @@
|
|
|
15
15
|
*/ import { type } from '@lowdefy/helpers';
|
|
16
16
|
import buildPage from './buildPage.js';
|
|
17
17
|
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
18
|
+
import validateLinkReferences from './validateLinkReferences.js';
|
|
19
|
+
import validatePayloadReferences from './validatePayloadReferences.js';
|
|
20
|
+
import validateStateReferences from './validateStateReferences.js';
|
|
18
21
|
function buildPages({ components, context }) {
|
|
19
22
|
const pages = type.isArray(components.pages) ? components.pages : [];
|
|
20
23
|
const checkDuplicatePageId = createCheckDuplicateId({
|
|
21
|
-
message: 'Duplicate pageId "{{ id }}".'
|
|
24
|
+
message: 'Duplicate pageId "{{ id }}".',
|
|
25
|
+
context
|
|
22
26
|
});
|
|
27
|
+
// Initialize linkActionRefs to collect Link action references across all pages
|
|
28
|
+
context.linkActionRefs = [];
|
|
23
29
|
pages.map((page, index)=>buildPage({
|
|
24
30
|
page,
|
|
25
31
|
index,
|
|
26
32
|
context,
|
|
27
33
|
checkDuplicatePageId
|
|
28
34
|
}));
|
|
35
|
+
// Validate that all Link actions reference existing pages
|
|
36
|
+
const pageIds = pages.map((page)=>page.pageId);
|
|
37
|
+
validateLinkReferences({
|
|
38
|
+
linkActionRefs: context.linkActionRefs,
|
|
39
|
+
pageIds,
|
|
40
|
+
context
|
|
41
|
+
});
|
|
42
|
+
// Validate that _state references use defined block IDs
|
|
43
|
+
// and _payload references use defined payload keys
|
|
44
|
+
pages.forEach((page)=>{
|
|
45
|
+
validateStateReferences({
|
|
46
|
+
page,
|
|
47
|
+
context
|
|
48
|
+
});
|
|
49
|
+
validatePayloadReferences({
|
|
50
|
+
page,
|
|
51
|
+
context
|
|
52
|
+
});
|
|
53
|
+
});
|
|
29
54
|
return components;
|
|
30
55
|
}
|
|
31
56
|
export default buildPages;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import buildAuth from '../buildAuth/buildAuth.js';
|
|
17
17
|
import buildPages from './buildPages.js';
|
|
18
18
|
import createContext from '../../createContext.js';
|
|
19
|
-
function buildTestPage({ pageConfig }) {
|
|
19
|
+
function buildTestPage({ pageConfig, connectionIds = [] }) {
|
|
20
20
|
const context = createContext({
|
|
21
21
|
customTypesMap: {},
|
|
22
22
|
directories: {},
|
|
@@ -28,6 +28,14 @@ function buildTestPage({ pageConfig }) {
|
|
|
28
28
|
},
|
|
29
29
|
stage: 'test'
|
|
30
30
|
});
|
|
31
|
+
// Add any connectionIds from test config to allow validation to pass
|
|
32
|
+
connectionIds.forEach((id)=>context.connectionIds.add(id));
|
|
33
|
+
// Also extract connectionIds from requests in the pageConfig
|
|
34
|
+
(pageConfig.requests || []).forEach((request)=>{
|
|
35
|
+
if (request.connectionId) {
|
|
36
|
+
context.connectionIds.add(request.connectionId);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
31
39
|
const components = {
|
|
32
40
|
pages: [
|
|
33
41
|
pageConfig
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2024 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import formatConfigError from '../../utils/formatConfigError.js';
|
|
16
|
+
function validateLinkReferences({ linkActionRefs, pageIds, context }) {
|
|
17
|
+
const pageIdSet = new Set(pageIds);
|
|
18
|
+
linkActionRefs.forEach(({ pageId, action, sourcePageId })=>{
|
|
19
|
+
if (!pageIdSet.has(pageId)) {
|
|
20
|
+
const errorMessage = formatConfigError({
|
|
21
|
+
message: `Page "${pageId}" not found. Link on page "${sourcePageId}" references non-existent page.`,
|
|
22
|
+
configKey: action['~k'],
|
|
23
|
+
context
|
|
24
|
+
});
|
|
25
|
+
if (context.stage === 'dev' || context.stage === 'test') {
|
|
26
|
+
context.logger.warn(errorMessage);
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error(errorMessage);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export default validateLinkReferences;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2024 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import extractOperatorKey from '../../utils/extractOperatorKey.js';
|
|
16
|
+
import formatConfigWarning from '../../utils/formatConfigWarning.js';
|
|
17
|
+
import traverseConfig from '../../utils/traverseConfig.js';
|
|
18
|
+
function validatePayloadReferences({ page, context }) {
|
|
19
|
+
const requests = page.requests || [];
|
|
20
|
+
requests.forEach((request)=>{
|
|
21
|
+
// Collect payload keys defined in the request
|
|
22
|
+
const payloadKeys = new Set(Object.keys(request.payload || {}));
|
|
23
|
+
// Skip if no payload defined (nothing to reference)
|
|
24
|
+
if (payloadKeys.size === 0) return;
|
|
25
|
+
// Find _payload references in request.properties
|
|
26
|
+
const payloadRefs = new Map(); // topLevelKey -> configKey (first occurrence)
|
|
27
|
+
traverseConfig({
|
|
28
|
+
config: request.properties,
|
|
29
|
+
visitor: (obj)=>{
|
|
30
|
+
if (obj._payload !== undefined) {
|
|
31
|
+
const topLevelKey = extractOperatorKey({
|
|
32
|
+
operatorValue: obj._payload
|
|
33
|
+
});
|
|
34
|
+
if (topLevelKey && !payloadRefs.has(topLevelKey)) {
|
|
35
|
+
payloadRefs.set(topLevelKey, obj['~k']);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
// Warn for undefined payload references
|
|
41
|
+
payloadRefs.forEach((configKey, topLevelKey)=>{
|
|
42
|
+
if (payloadKeys.has(topLevelKey)) return;
|
|
43
|
+
const message = `_payload references "${topLevelKey}" in request "${request.requestId}" on page "${page.pageId}", ` + `but no key "${topLevelKey}" exists in the request payload definition. ` + `Payload keys are defined in the request's "payload" property. ` + `Check for typos or add the key to the payload definition.`;
|
|
44
|
+
context.logger.warn(formatConfigWarning({
|
|
45
|
+
message,
|
|
46
|
+
configKey,
|
|
47
|
+
context
|
|
48
|
+
}));
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export default validatePayloadReferences;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2024 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import formatConfigError from '../../utils/formatConfigError.js';
|
|
16
|
+
function validateRequestReferences({ requestActionRefs, requests, pageId, context }) {
|
|
17
|
+
const requestIds = new Set(requests.map((req)=>req.requestId));
|
|
18
|
+
requestActionRefs.forEach(({ requestId, action })=>{
|
|
19
|
+
if (!requestIds.has(requestId)) {
|
|
20
|
+
const errorMessage = formatConfigError({
|
|
21
|
+
message: `Request "${requestId}" not defined on page "${pageId}".`,
|
|
22
|
+
configKey: action['~k'],
|
|
23
|
+
context
|
|
24
|
+
});
|
|
25
|
+
if (context.stage === 'dev' || context.stage === 'test') {
|
|
26
|
+
context.logger.warn(errorMessage);
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error(errorMessage);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export default validateRequestReferences;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2024 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import extractOperatorKey from '../../utils/extractOperatorKey.js';
|
|
16
|
+
import formatConfigWarning from '../../utils/formatConfigWarning.js';
|
|
17
|
+
import traverseConfig from '../../utils/traverseConfig.js';
|
|
18
|
+
function validateStateReferences({ page, context }) {
|
|
19
|
+
// Single traversal collects both blockIds and _state references
|
|
20
|
+
// More memory-efficient than stringify+regex for massive pages
|
|
21
|
+
const blockIds = new Set();
|
|
22
|
+
const stateRefs = new Map(); // topLevelKey -> configKey (first occurrence)
|
|
23
|
+
traverseConfig({
|
|
24
|
+
config: page,
|
|
25
|
+
visitor: (obj)=>{
|
|
26
|
+
// Collect blockId if present
|
|
27
|
+
if (obj.blockId) {
|
|
28
|
+
blockIds.add(obj.blockId);
|
|
29
|
+
}
|
|
30
|
+
// Collect _state reference if present
|
|
31
|
+
if (obj._state !== undefined) {
|
|
32
|
+
const topLevelKey = extractOperatorKey({
|
|
33
|
+
operatorValue: obj._state
|
|
34
|
+
});
|
|
35
|
+
if (topLevelKey && !stateRefs.has(topLevelKey)) {
|
|
36
|
+
stateRefs.set(topLevelKey, obj['~k']);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
// Filter to only undefined references and warn
|
|
42
|
+
stateRefs.forEach((configKey, topLevelKey)=>{
|
|
43
|
+
if (blockIds.has(topLevelKey)) return;
|
|
44
|
+
const message = `_state references "${topLevelKey}" on page "${page.pageId}", ` + `but no input block with id "${topLevelKey}" exists on this page. ` + `State keys are created from input block ids. ` + `Check for typos, add an input block with this id, or initialize the state with SetState.`;
|
|
45
|
+
context.logger.warn(formatConfigWarning({
|
|
46
|
+
message,
|
|
47
|
+
configKey,
|
|
48
|
+
context
|
|
49
|
+
}));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
export default validateStateReferences;
|
|
@@ -12,43 +12,21 @@
|
|
|
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 recursiveBuild from './recursiveBuild.js';
|
|
15
|
+
*/ import recursiveBuild from './recursiveBuild.js';
|
|
17
16
|
import makeRefDefinition from './makeRefDefinition.js';
|
|
18
17
|
import evaluateBuildOperators from './evaluateBuildOperators.js';
|
|
19
|
-
import invalidateChangedFiles from '../../utils/invalidateChangedFiles.js';
|
|
20
18
|
async function buildRefs({ context }) {
|
|
21
|
-
const profiler = createBuildProfiler({
|
|
22
|
-
logger: context.logger,
|
|
23
|
-
prefix: 'buildRefs'
|
|
24
|
-
});
|
|
25
|
-
// For incremental builds, invalidate caches for changed files and their dependents
|
|
26
|
-
if (context.changedFiles && context.changedFiles.length > 0) {
|
|
27
|
-
invalidateChangedFiles({
|
|
28
|
-
changedFiles: context.changedFiles,
|
|
29
|
-
dependencyGraph: context.dependencyGraph,
|
|
30
|
-
parsedContentCache: context.parsedContentCache,
|
|
31
|
-
refCache: context.refCache,
|
|
32
|
-
pathToRefHashes: context.pathToRefHashes,
|
|
33
|
-
logger: context.logger
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
19
|
const refDef = makeRefDefinition('lowdefy.yaml', null, context.refMap);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
context,
|
|
48
|
-
input: components,
|
|
49
|
-
refDef
|
|
50
|
-
}));
|
|
51
|
-
profiler.printSummary();
|
|
20
|
+
let components = await recursiveBuild({
|
|
21
|
+
context,
|
|
22
|
+
refDef,
|
|
23
|
+
count: 0
|
|
24
|
+
});
|
|
25
|
+
components = await evaluateBuildOperators({
|
|
26
|
+
context,
|
|
27
|
+
input: components,
|
|
28
|
+
refDef
|
|
29
|
+
});
|
|
52
30
|
return components ?? {};
|
|
53
31
|
}
|
|
54
32
|
export default buildRefs;
|