@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.
- package/dist/build/addDefaultPages/404.js +1 -1
- package/dist/build/addDefaultPages/addDefaultPages.js +10 -4
- package/dist/build/addKeys.js +85 -29
- package/dist/build/buildApi/buildApi.js +27 -8
- package/dist/build/buildApi/buildEndpoint.js +7 -1
- package/dist/build/buildApi/buildRoutine/buildControl.js +1 -1
- package/dist/build/buildApi/buildRoutine/buildRoutine.js +1 -1
- package/dist/build/buildApi/buildRoutine/buildStep.js +1 -1
- package/dist/build/buildApi/buildRoutine/controlTypes.js +34 -11
- package/dist/build/buildApi/buildRoutine/countControl.js +1 -1
- package/dist/build/buildApi/buildRoutine/countStepTypes.js +2 -2
- package/dist/build/buildApi/buildRoutine/setStepId.js +1 -1
- package/dist/build/buildApi/buildRoutine/validateStep.js +30 -9
- package/dist/build/buildApi/validateEndpoint.js +36 -7
- package/dist/build/buildApi/validateStepReferences.js +65 -0
- package/dist/build/buildApp.js +1 -1
- package/dist/build/buildAuth/buildApiAuth.js +7 -3
- package/dist/build/buildAuth/buildAuth.js +7 -4
- package/dist/build/buildAuth/buildAuthPlugins.js +42 -13
- package/dist/build/buildAuth/buildPageAuth.js +14 -3
- package/dist/build/buildAuth/getApiRoles.js +1 -1
- package/dist/build/buildAuth/getPageRoles.js +1 -1
- package/dist/build/buildAuth/getProtectedApi.js +1 -1
- package/dist/build/buildAuth/getProtectedPages.js +1 -1
- package/dist/build/buildAuth/validateAuthConfig.js +39 -5
- package/dist/build/buildAuth/validateMutualExclusivity.js +13 -5
- package/dist/build/buildConnections.js +23 -24
- package/dist/build/buildImports/buildIconImports.js +1 -1
- package/dist/build/buildImports/buildImports.js +1 -1
- package/dist/build/buildImports/buildImportsDev.js +1 -1
- package/dist/build/buildImports/buildImportsProd.js +1 -1
- package/dist/build/buildImports/buildStyleImports.js +1 -1
- package/dist/build/buildImports/defaultIconsDev.js +1 -1
- package/dist/build/buildImports/defaultIconsProd.js +1 -1
- package/dist/build/buildJs/generateJsFile.js +1 -1
- package/dist/build/buildJs/jsMapParser.js +1 -1
- package/dist/build/buildJs/writeJs.js +1 -1
- package/dist/build/buildLogger.js +41 -0
- package/dist/build/buildMenu.js +36 -12
- package/dist/build/buildPages/buildBlock/buildBlock.js +1 -1
- package/dist/build/buildPages/buildBlock/buildEvents.js +79 -9
- package/dist/build/buildPages/buildBlock/buildRequests.js +38 -8
- package/dist/build/buildPages/buildBlock/buildSubBlocks.js +6 -2
- package/dist/build/buildPages/buildBlock/countBlockOperators.js +1 -1
- package/dist/build/buildPages/buildBlock/countBlockTypes.js +2 -2
- package/dist/build/buildPages/buildBlock/moveSkeletonBlocksToArea.js +6 -2
- package/dist/build/buildPages/buildBlock/moveSubBlocksToArea.js +6 -2
- package/dist/build/buildPages/buildBlock/setBlockId.js +1 -1
- package/dist/build/buildPages/buildBlock/validateBlock.js +25 -7
- package/dist/build/buildPages/buildPage.js +35 -6
- package/dist/build/buildPages/validateLinkReferences.js +33 -0
- package/dist/build/buildPages/validatePayloadReferences.js +59 -0
- package/dist/build/buildPages/validateRequestReferences.js +33 -0
- package/dist/build/buildPages/validateServerStateReferences.js +41 -0
- package/dist/build/buildPages/validateStateReferences.js +79 -0
- package/dist/build/buildRefs/buildRefs.js +20 -3
- package/dist/build/buildRefs/createRefReviver.js +28 -0
- package/dist/build/buildRefs/evaluateBuildOperators.js +26 -7
- package/dist/build/buildRefs/evaluateStaticOperators.js +56 -0
- package/dist/build/buildRefs/getConfigFile.js +25 -6
- package/dist/build/buildRefs/getKey.js +1 -1
- package/dist/build/buildRefs/getRefContent.js +1 -1
- package/dist/build/buildRefs/getRefPath.js +1 -1
- package/dist/build/buildRefs/getRefsFromFile.js +9 -4
- package/dist/build/buildRefs/getUserJavascriptFunction.js +18 -4
- package/dist/build/buildRefs/makeRefDefinition.js +10 -5
- package/dist/build/buildRefs/parseNunjucks.js +1 -1
- package/dist/build/buildRefs/parseRefContent.js +100 -6
- package/dist/build/buildRefs/populateRefs.js +75 -13
- package/dist/build/buildRefs/recursiveBuild.js +66 -18
- package/dist/build/buildRefs/runRefResolver.js +11 -3
- package/dist/build/buildRefs/runTransformer.js +7 -3
- package/dist/build/buildTypes.js +22 -7
- package/dist/build/cleanBuildDirectory.js +1 -1
- package/dist/build/collectDynamicIdentifiers.js +35 -0
- package/dist/build/collectTypeNames.js +36 -0
- package/dist/build/copyPublicFolder.js +1 -1
- package/dist/build/{buildJs → full}/buildJs.js +3 -3
- package/dist/build/full/buildPages.js +87 -0
- package/dist/build/{buildPages → full}/buildTestPage.js +15 -3
- package/dist/build/{updateServerPackageJson.js → full/updateServerPackageJson.js} +1 -1
- package/dist/build/{writePages.js → full/writePages.js} +1 -1
- package/dist/build/{writeRequests.js → full/writeRequests.js} +11 -3
- package/dist/build/{writeTypes.js → full/writeTypes.js} +1 -1
- package/dist/build/jit/addInstalledTypes.js +51 -0
- package/dist/build/jit/buildJsShallow.js +41 -0
- package/dist/build/jit/buildPageJit.js +252 -0
- package/dist/build/jit/buildShallowPages.js +90 -0
- package/dist/build/jit/createPageRegistry.js +80 -0
- package/dist/build/jit/detectMissingPluginPackages.js +62 -0
- package/dist/build/jit/getRefPositions.js +38 -0
- package/dist/build/jit/isPageContentPath.js +24 -0
- package/dist/build/jit/pageContentKeys.js +26 -0
- package/dist/build/jit/shallowBuild.js +245 -0
- package/dist/build/jit/stripPageContent.js +23 -0
- package/dist/build/jit/updateServerPackageJsonJit.js +33 -0
- package/dist/build/jit/validatePageTypes.js +73 -0
- package/dist/build/jit/writePageJit.js +44 -0
- package/dist/build/jit/writePageRegistry.js +23 -0
- package/dist/build/jit/writeSourcelessPages.js +23 -0
- package/dist/build/testSchema.js +45 -7
- package/dist/build/validateConfig.js +2 -2
- package/dist/build/validateOperatorsDynamic.js +28 -0
- package/dist/build/writeApi.js +1 -1
- package/dist/build/writeApp.js +1 -1
- package/dist/build/writeAuth.js +1 -1
- package/dist/build/writeConfig.js +1 -1
- package/dist/build/writeConnections.js +1 -1
- package/dist/build/writeGlobal.js +1 -1
- package/dist/build/writeLogger.js +19 -0
- package/dist/build/writeMaps.js +1 -1
- package/dist/build/writeMenus.js +1 -1
- package/dist/build/writePluginImports/generateImportFile.js +1 -1
- package/dist/build/writePluginImports/writeActionImports.js +1 -1
- package/dist/build/writePluginImports/writeActionSchemaMap.js +42 -0
- package/dist/build/writePluginImports/writeAuthImports.js +1 -1
- package/dist/build/writePluginImports/writeBlockImports.js +1 -1
- package/dist/build/writePluginImports/writeBlockSchemaMap.js +42 -0
- package/dist/build/writePluginImports/writeConnectionImports.js +1 -1
- package/dist/build/writePluginImports/writeIconImports.js +1 -1
- package/dist/build/writePluginImports/writeOperatorImports.js +1 -1
- package/dist/build/writePluginImports/writeOperatorSchemaMap.js +49 -0
- package/dist/build/writePluginImports/writePluginImports.js +16 -1
- package/dist/build/writePluginImports/writeStyleImports.js +1 -1
- package/dist/createContext.js +16 -4
- package/dist/defaultTypesMap.js +477 -455
- package/dist/index.js +188 -127
- package/dist/indexDev.js +18 -0
- package/dist/lowdefySchema.js +589 -0
- package/dist/scripts/generateDefaultTypes.js +2 -1
- package/dist/scripts/run.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsAsyncFunction.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsErrorResolver.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsNullResolver.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsParsingResolver.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsResolver.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsTransform.js +1 -1
- package/dist/{test → test-utils}/buildRefs/testBuildRefsTransformIdentity.js +1 -1
- package/dist/test-utils/buildRefs/testJitPageResolver.js +24 -0
- package/dist/test-utils/createTestLogger.js +36 -0
- package/dist/test-utils/parseTestYaml.js +126 -0
- package/dist/test-utils/runBuild.js +228 -0
- package/dist/test-utils/runBuildForSnapshots.js +704 -0
- package/dist/{test → test-utils}/testContext.js +12 -2
- package/dist/utils/collectExceptions.js +34 -0
- package/dist/utils/countOperators.js +31 -12
- package/dist/utils/createBuildHandleError.js +38 -0
- package/dist/utils/createCheckDuplicateId.js +15 -9
- package/dist/utils/createCounter.js +16 -3
- package/dist/utils/createHandleWarning.js +41 -0
- package/dist/utils/createPluginTypesMap.js +1 -1
- package/dist/utils/extractOperatorKey.js +36 -0
- package/dist/utils/findConfigKey.js +37 -0
- package/dist/utils/findSimilarString.js +53 -0
- package/dist/utils/logCollectedErrors.js +30 -0
- package/dist/utils/makeId.js +13 -8
- package/dist/utils/preserveMetaProperties.js +27 -0
- package/dist/utils/readConfigFile.js +1 -1
- package/dist/utils/setNonEnumerableProperty.js +23 -0
- package/dist/utils/traverseConfig.js +43 -0
- package/dist/utils/tryBuildStep.js +46 -0
- package/dist/utils/writeBuildArtifact.js +1 -1
- package/package.json +46 -41
- package/dist/build/buildPages/buildPages.js +0 -31
- package/dist/utils/formatErrorMessage.js +0 -56
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,32 +12,94 @@
|
|
|
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 { get, type } from '@lowdefy/helpers';
|
|
15
|
+
*/ import { get, serializer, type } from '@lowdefy/helpers';
|
|
16
|
+
/**
|
|
17
|
+
* Copies a _var value while preserving source location markers.
|
|
18
|
+
*
|
|
19
|
+
* When _var copies a value from vars into a template, we need to preserve
|
|
20
|
+
* the ~r (ref ID) and ~l (line number) from WHERE THE VALUE IS DEFINED
|
|
21
|
+
* (the source file), not where _var is used (the template).
|
|
22
|
+
*
|
|
23
|
+
* The issue is that serializer.copy loses non-enumerable properties, and
|
|
24
|
+
* recursiveBuild.js then sets ~r to the template file for objects without ~r.
|
|
25
|
+
*
|
|
26
|
+
* This function preserves the source location by making ~r enumerable on
|
|
27
|
+
* the copied value, so it survives through subsequent serializer.copy calls.
|
|
28
|
+
*
|
|
29
|
+
* @param {*} value - The value to copy from vars
|
|
30
|
+
* @param {string} sourceRefId - The ref ID of the source file where the var is defined
|
|
31
|
+
* @returns {*} The copied value with preserved source location markers
|
|
32
|
+
*/ function copyVarValue(value, sourceRefId) {
|
|
33
|
+
if (!type.isObject(value) && !type.isArray(value)) {
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
// Copy the value, preserving ~l and setting ~r to the source file
|
|
37
|
+
return serializer.copy(value, {
|
|
38
|
+
reviver: (_, v)=>{
|
|
39
|
+
if (type.isObject(v) || type.isArray(v)) {
|
|
40
|
+
// Preserve the source file's ref ID by setting it explicitly
|
|
41
|
+
// This prevents recursiveBuild from overwriting it with the template's ref ID
|
|
42
|
+
if (sourceRefId && v['~r'] === undefined) {
|
|
43
|
+
v['~r'] = sourceRefId;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return v;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
16
50
|
function refReviver(key, value) {
|
|
17
51
|
if (type.isObject(value)) {
|
|
18
52
|
if (!type.isUndefined(value._ref)) {
|
|
19
|
-
|
|
53
|
+
const result = this.parsedFiles[value._ref.id];
|
|
54
|
+
if (value._ref.ignoreBuildChecks !== undefined) {
|
|
55
|
+
if (type.isObject(result)) {
|
|
56
|
+
result['~ignoreBuildChecks'] = value._ref.ignoreBuildChecks;
|
|
57
|
+
} else if (type.isArray(result)) {
|
|
58
|
+
result.forEach((item)=>{
|
|
59
|
+
if (type.isObject(item)) {
|
|
60
|
+
item['~ignoreBuildChecks'] = value._ref.ignoreBuildChecks;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
20
66
|
}
|
|
21
67
|
if (value._var) {
|
|
22
68
|
if (type.isString(value._var)) {
|
|
23
|
-
|
|
69
|
+
const varValue = get(this.vars, value._var, {
|
|
24
70
|
default: null
|
|
25
|
-
})
|
|
71
|
+
});
|
|
72
|
+
return copyVarValue(varValue, this.sourceRefId);
|
|
26
73
|
}
|
|
27
74
|
if (type.isObject(value._var) && type.isString(value._var.key)) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
75
|
+
const varKey = value._var.key;
|
|
76
|
+
const varFromParent = get(this.vars, varKey);
|
|
77
|
+
// Check if var was explicitly provided (even if null) vs not provided at all (undefined)
|
|
78
|
+
// - Var provided (including null): use parent's sourceRefId for location
|
|
79
|
+
// - Var not provided (undefined): use default, preserve template's location
|
|
80
|
+
if (!type.isUndefined(varFromParent)) {
|
|
81
|
+
// Var was explicitly provided from parent file - use parent's location
|
|
82
|
+
return copyVarValue(varFromParent, this.sourceRefId);
|
|
83
|
+
}
|
|
84
|
+
// Using default value defined in template - preserve template's location markers
|
|
85
|
+
// Pass null for sourceRefId so we don't override the template file's ~r
|
|
86
|
+
const defaultValue = type.isNone(value._var.default) ? null : value._var.default;
|
|
87
|
+
return copyVarValue(defaultValue, null);
|
|
31
88
|
}
|
|
32
|
-
throw new Error(
|
|
89
|
+
throw new Error('_var operator takes a string or object with "key" field as arguments.');
|
|
33
90
|
}
|
|
34
91
|
}
|
|
35
92
|
return value;
|
|
36
93
|
}
|
|
37
94
|
function populateRefs({ parsedFiles, refDef, toPopulate }) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
95
|
+
// Use serializer.copy to preserve non-enumerable properties like ~r, ~k, ~l
|
|
96
|
+
// sourceRefId is the PARENT file's ref ID where vars are defined (not the template's ID)
|
|
97
|
+
return serializer.copy(toPopulate, {
|
|
98
|
+
reviver: refReviver.bind({
|
|
99
|
+
parsedFiles,
|
|
100
|
+
vars: refDef.vars,
|
|
101
|
+
sourceRefId: refDef.parent
|
|
102
|
+
})
|
|
103
|
+
});
|
|
42
104
|
}
|
|
43
105
|
export default populateRefs;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,30 +12,65 @@
|
|
|
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';
|
|
15
|
+
*/ import { serializer, type } from '@lowdefy/helpers';
|
|
16
|
+
import { ConfigError } from '@lowdefy/errors';
|
|
17
|
+
import createRefReviver from './createRefReviver.js';
|
|
16
18
|
import evaluateBuildOperators from './evaluateBuildOperators.js';
|
|
17
19
|
import getKey from './getKey.js';
|
|
18
20
|
import getRefContent from './getRefContent.js';
|
|
21
|
+
import getRefPositions from '../jit/getRefPositions.js';
|
|
19
22
|
import getRefsFromFile from './getRefsFromFile.js';
|
|
20
23
|
import populateRefs from './populateRefs.js';
|
|
21
24
|
import runTransformer from './runTransformer.js';
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
import isPageContentPath from '../jit/isPageContentPath.js';
|
|
26
|
+
async function recursiveBuild({ context, refDef, count, content, referencedFrom, refChainSet = new Set(), refChainList = [], shallowOptions, jsonPath = '' }) {
|
|
27
|
+
// Detect circular references by tracking the chain of files being resolved
|
|
28
|
+
// Skip circular reference checking for refs without paths (e.g., resolver refs)
|
|
29
|
+
const currentPath = refDef.path;
|
|
30
|
+
if (currentPath) {
|
|
31
|
+
if (refChainSet.has(currentPath)) {
|
|
32
|
+
const chainDisplay = [
|
|
33
|
+
...refChainList,
|
|
34
|
+
currentPath
|
|
35
|
+
].join('\n -> ');
|
|
36
|
+
throw new ConfigError(`Circular reference detected. File "${currentPath}" references itself through:\n -> ${chainDisplay}`, {
|
|
37
|
+
filePath: referencedFrom ?? null,
|
|
38
|
+
lineNumber: referencedFrom ? refDef.lineNumber : null
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
refChainSet.add(currentPath);
|
|
42
|
+
refChainList.push(currentPath);
|
|
43
|
+
}
|
|
44
|
+
// Keep count as a fallback safety limit
|
|
24
45
|
if (count > 10000) {
|
|
25
|
-
throw new
|
|
46
|
+
throw new ConfigError('Maximum recursion depth of references exceeded (10000 levels). This likely indicates a circular reference.');
|
|
26
47
|
}
|
|
27
|
-
let fileContent = await getRefContent({
|
|
48
|
+
let fileContent = content ?? await getRefContent({
|
|
28
49
|
context,
|
|
29
50
|
refDef,
|
|
30
51
|
referencedFrom
|
|
31
52
|
});
|
|
32
53
|
const { foundRefs, fileContentBuiltRefs } = getRefsFromFile(fileContent, refDef.id, context.refMap);
|
|
33
54
|
const parsedFiles = {};
|
|
55
|
+
// Compute the JSON path position of each ref within the current file's content.
|
|
56
|
+
// This lets us check if a ref should be skipped during shallow builds.
|
|
57
|
+
const refPositions = shallowOptions ? getRefPositions(fileContentBuiltRefs, jsonPath) : null;
|
|
34
58
|
// Since we can have references in the variables of a reference, we need to first parse
|
|
35
59
|
// the deeper nodes, so we can use those parsed files in references higher in the tree.
|
|
36
60
|
// To do this, since foundRefs is an array of ref definitions that are in order of the
|
|
37
61
|
// deepest nodes first we for loop over over foundRefs one by one, awaiting each result.
|
|
38
62
|
for (const newRefDef of foundRefs.values()){
|
|
63
|
+
// Check if this ref should be skipped (shallow build)
|
|
64
|
+
const refJsonPath = refPositions?.get(newRefDef.id) ?? '';
|
|
65
|
+
if (shallowOptions && isPageContentPath(refJsonPath)) {
|
|
66
|
+
// Store shallow marker instead of resolving
|
|
67
|
+
parsedFiles[newRefDef.id] = {
|
|
68
|
+
'~shallow': true,
|
|
69
|
+
_ref: newRefDef.original,
|
|
70
|
+
_refId: newRefDef.id
|
|
71
|
+
};
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
39
74
|
// Parse vars and path before passing down to parse new file
|
|
40
75
|
const parsedRefDef = populateRefs({
|
|
41
76
|
toPopulate: newRefDef,
|
|
@@ -43,11 +78,22 @@ async function recursiveBuild({ context, refDef, count, referencedFrom }) {
|
|
|
43
78
|
refDef
|
|
44
79
|
});
|
|
45
80
|
context.refMap[parsedRefDef.id].path = parsedRefDef.path;
|
|
81
|
+
// Store original definition for resolver refs so JIT can re-run them
|
|
82
|
+
if (!parsedRefDef.path) {
|
|
83
|
+
context.refMap[parsedRefDef.id].original = newRefDef.original;
|
|
84
|
+
}
|
|
85
|
+
if (Object.keys(newRefDef.vars).length > 0) {
|
|
86
|
+
context.unresolvedRefVars[newRefDef.id] = newRefDef.vars;
|
|
87
|
+
}
|
|
46
88
|
const parsedFile = await recursiveBuild({
|
|
47
89
|
context,
|
|
48
90
|
refDef: parsedRefDef,
|
|
49
91
|
count: count + 1,
|
|
50
|
-
referencedFrom: refDef.path
|
|
92
|
+
referencedFrom: refDef.path ?? referencedFrom,
|
|
93
|
+
refChainSet,
|
|
94
|
+
refChainList,
|
|
95
|
+
shallowOptions,
|
|
96
|
+
jsonPath: refJsonPath
|
|
51
97
|
});
|
|
52
98
|
const transformedFile = await runTransformer({
|
|
53
99
|
context,
|
|
@@ -64,17 +110,19 @@ async function recursiveBuild({ context, refDef, count, referencedFrom }) {
|
|
|
64
110
|
input: evaluatedOperators,
|
|
65
111
|
refDef: parsedRefDef
|
|
66
112
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
113
|
+
// Use serializer.copy to preserve non-enumerable properties like ~l.
|
|
114
|
+
// Only set ~r if not already present to preserve original file references from nested imports.
|
|
115
|
+
// Use child file's ref ID (parsedRefDef.id) not parent's (refDef.id) for correct error tracing.
|
|
116
|
+
const reviver = createRefReviver(parsedRefDef.id);
|
|
117
|
+
parsedFiles[newRefDef.id] = serializer.copy(withRefKey, {
|
|
118
|
+
reviver
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Backtrack: remove current file from chain so sibling refs can use it
|
|
122
|
+
// Only remove if it was added (i.e., if currentPath exists)
|
|
123
|
+
if (currentPath) {
|
|
124
|
+
refChainSet.delete(currentPath);
|
|
125
|
+
refChainList.pop();
|
|
78
126
|
}
|
|
79
127
|
return populateRefs({
|
|
80
128
|
toPopulate: fileContentBuiltRefs,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,7 @@
|
|
|
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 getUserJavascriptFunction from './getUserJavascriptFunction.js';
|
|
17
18
|
async function runRefResolver({ context, refDef, referencedFrom }) {
|
|
18
19
|
const resolverFn = await getUserJavascriptFunction({
|
|
@@ -23,10 +24,17 @@ async function runRefResolver({ context, refDef, referencedFrom }) {
|
|
|
23
24
|
try {
|
|
24
25
|
content = await resolverFn(refDef.path, refDef.vars, context);
|
|
25
26
|
} catch (error) {
|
|
26
|
-
throw new
|
|
27
|
+
throw new ConfigError(`Error calling resolver "${refDef.resolver}".`, {
|
|
28
|
+
cause: error,
|
|
29
|
+
filePath: referencedFrom,
|
|
30
|
+
lineNumber: refDef.lineNumber
|
|
31
|
+
});
|
|
27
32
|
}
|
|
28
33
|
if (type.isNone(content)) {
|
|
29
|
-
throw new
|
|
34
|
+
throw new ConfigError(`Resolver "${refDef.resolver}" returned "${content}".`, {
|
|
35
|
+
filePath: referencedFrom,
|
|
36
|
+
lineNumber: refDef.lineNumber
|
|
37
|
+
});
|
|
30
38
|
}
|
|
31
39
|
return content;
|
|
32
40
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
3
|
|
|
4
4
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
5
|
you may not use this file except in compliance with the License.
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import
|
|
15
|
+
*/ import { ConfigError } from '@lowdefy/errors';
|
|
16
|
+
import getUserJavascriptFunction from './getUserJavascriptFunction.js';
|
|
16
17
|
async function runTransformer({ context, input, refDef }) {
|
|
17
18
|
if (refDef.transformer) {
|
|
18
19
|
const transformerFn = await getUserJavascriptFunction({
|
|
@@ -22,7 +23,10 @@ async function runTransformer({ context, input, refDef }) {
|
|
|
22
23
|
try {
|
|
23
24
|
return transformerFn(input, refDef.vars);
|
|
24
25
|
} catch (error) {
|
|
25
|
-
throw
|
|
26
|
+
throw new ConfigError(`Error calling transformer "${refDef.transformer}" from "${refDef.path}".`, {
|
|
27
|
+
cause: error,
|
|
28
|
+
filePath: refDef.transformer
|
|
29
|
+
});
|
|
26
30
|
}
|
|
27
31
|
}
|
|
28
32
|
return input;
|
package/dist/build/buildTypes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,21 +14,36 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/ import basicTypes from '@lowdefy/blocks-basic/types';
|
|
16
16
|
import loaderTypes from '@lowdefy/blocks-loaders/types';
|
|
17
|
+
import { ConfigError, ConfigWarning } from '@lowdefy/errors';
|
|
18
|
+
import findSimilarString from '../utils/findSimilarString.js';
|
|
17
19
|
function buildTypeClass(context, { counter, definitions, store, typeClass, warnIfMissing = false }) {
|
|
18
20
|
const counts = counter.getCounts();
|
|
21
|
+
const definedTypes = Object.keys(definitions);
|
|
19
22
|
Object.keys(counts).forEach((typeName)=>{
|
|
20
23
|
if (!definitions[typeName]) {
|
|
24
|
+
const configKey = counter.getLocation(typeName);
|
|
25
|
+
let message = `${typeClass} type "${typeName}" was used but is not defined.`;
|
|
26
|
+
const suggestion = findSimilarString({
|
|
27
|
+
input: typeName,
|
|
28
|
+
candidates: definedTypes
|
|
29
|
+
});
|
|
30
|
+
if (suggestion) {
|
|
31
|
+
message += ` Did you mean "${suggestion}"?`;
|
|
32
|
+
}
|
|
21
33
|
if (warnIfMissing) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
34
|
+
context.handleWarning(new ConfigWarning(message, {
|
|
35
|
+
configKey,
|
|
36
|
+
checkSlug: 'types'
|
|
37
|
+
}));
|
|
26
38
|
return;
|
|
27
39
|
}
|
|
28
|
-
throw new
|
|
40
|
+
throw new ConfigError(message, {
|
|
41
|
+
configKey,
|
|
42
|
+
checkSlug: 'types'
|
|
43
|
+
});
|
|
29
44
|
}
|
|
30
45
|
store[typeName] = {
|
|
31
|
-
originalTypeName: definitions[typeName].originalTypeName,
|
|
46
|
+
originalTypeName: definitions[typeName].originalTypeName ?? typeName,
|
|
32
47
|
package: definitions[typeName].package,
|
|
33
48
|
version: definitions[typeName].version,
|
|
34
49
|
count: counts[typeName]
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
function collectDynamicIdentifiers({ operators }) {
|
|
17
|
+
const dynamicIdentifiers = new Set();
|
|
18
|
+
Object.entries(operators).forEach(([operatorName, operatorFn])=>{
|
|
19
|
+
if (!type.isFunction(operatorFn)) return;
|
|
20
|
+
if (operatorFn.dynamic === true) {
|
|
21
|
+
dynamicIdentifiers.add(operatorName);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
// Check for method-level dynamic in meta
|
|
25
|
+
if (type.isObject(operatorFn.meta)) {
|
|
26
|
+
Object.entries(operatorFn.meta).forEach(([methodName, methodMeta])=>{
|
|
27
|
+
if (type.isObject(methodMeta) && methodMeta.dynamic === true) {
|
|
28
|
+
dynamicIdentifiers.add(`${operatorName}.${methodName}`);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
return dynamicIdentifiers;
|
|
34
|
+
}
|
|
35
|
+
export default collectDynamicIdentifiers;
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
function collectTypeNames({ typesMap }) {
|
|
17
|
+
const typeNames = new Set();
|
|
18
|
+
if (!type.isObject(typesMap)) {
|
|
19
|
+
return typeNames;
|
|
20
|
+
}
|
|
21
|
+
[
|
|
22
|
+
'blocks',
|
|
23
|
+
'requests',
|
|
24
|
+
'connections',
|
|
25
|
+
'actions',
|
|
26
|
+
'controls'
|
|
27
|
+
].forEach((category)=>{
|
|
28
|
+
if (type.isObject(typesMap[category])) {
|
|
29
|
+
Object.keys(typesMap[category]).forEach((typeName)=>{
|
|
30
|
+
typeNames.add(typeName);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
return typeNames;
|
|
35
|
+
}
|
|
36
|
+
export default collectTypeNames;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,11 +12,11 @@
|
|
|
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 jsMapParser from '
|
|
15
|
+
*/ import jsMapParser from '../buildJs/jsMapParser.js';
|
|
16
16
|
function buildJs({ components, context }) {
|
|
17
17
|
components.pages = components.pages.map((page)=>{
|
|
18
18
|
const pageRequests = [
|
|
19
|
-
...page.requests
|
|
19
|
+
...page.requests ?? []
|
|
20
20
|
];
|
|
21
21
|
delete page.requests;
|
|
22
22
|
const cleanPage = jsMapParser({
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */ /*
|
|
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
|
+
import { ConfigError, shouldSuppressBuildCheck } from '@lowdefy/errors';
|
|
17
|
+
import buildPage from '../buildPages/buildPage.js';
|
|
18
|
+
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
19
|
+
import validateLinkReferences from '../buildPages/validateLinkReferences.js';
|
|
20
|
+
import validatePayloadReferences from '../buildPages/validatePayloadReferences.js';
|
|
21
|
+
import validateServerStateReferences from '../buildPages/validateServerStateReferences.js';
|
|
22
|
+
import validateStateReferences from '../buildPages/validateStateReferences.js';
|
|
23
|
+
function buildPages({ components, context }) {
|
|
24
|
+
const pages = type.isArray(components.pages) ? components.pages : [];
|
|
25
|
+
const checkDuplicatePageId = createCheckDuplicateId({
|
|
26
|
+
message: 'Duplicate pageId "{{ id }}".'
|
|
27
|
+
});
|
|
28
|
+
// Initialize linkActionRefs to collect Link action references across all pages
|
|
29
|
+
context.linkActionRefs = [];
|
|
30
|
+
// Track which pages failed to build so we skip them in validation
|
|
31
|
+
const failedPageIndices = new Set();
|
|
32
|
+
// Wrap each page build to collect errors instead of stopping on first error
|
|
33
|
+
pages.forEach((page, index)=>{
|
|
34
|
+
try {
|
|
35
|
+
const result = buildPage({
|
|
36
|
+
page,
|
|
37
|
+
index,
|
|
38
|
+
context,
|
|
39
|
+
checkDuplicatePageId
|
|
40
|
+
});
|
|
41
|
+
// buildPage returns { failed: true } when validation fails
|
|
42
|
+
if (result?.failed) {
|
|
43
|
+
failedPageIndices.add(index);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
// Skip suppressed ConfigErrors (via ~ignoreBuildChecks)
|
|
47
|
+
if (error instanceof ConfigError && shouldSuppressBuildCheck(error, context.keyMap)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
// Collect error object if context.errors exists, otherwise throw (for backward compat with tests)
|
|
51
|
+
if (context?.errors) {
|
|
52
|
+
context.errors.push(error);
|
|
53
|
+
failedPageIndices.add(index);
|
|
54
|
+
} else {
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
// Validate that all Link actions reference existing pages
|
|
60
|
+
// Include all pages — a link to a broken page is valid; the page error is already reported
|
|
61
|
+
const pageIds = pages.map((page)=>page.pageId);
|
|
62
|
+
validateLinkReferences({
|
|
63
|
+
linkActionRefs: context.linkActionRefs,
|
|
64
|
+
pageIds,
|
|
65
|
+
context
|
|
66
|
+
});
|
|
67
|
+
// Validate that _state references use defined block IDs
|
|
68
|
+
// and _payload references use defined payload keys
|
|
69
|
+
// Skip pages that failed to build
|
|
70
|
+
pages.forEach((page, index)=>{
|
|
71
|
+
if (failedPageIndices.has(index)) return;
|
|
72
|
+
validateStateReferences({
|
|
73
|
+
page,
|
|
74
|
+
context
|
|
75
|
+
});
|
|
76
|
+
validatePayloadReferences({
|
|
77
|
+
page,
|
|
78
|
+
context
|
|
79
|
+
});
|
|
80
|
+
validateServerStateReferences({
|
|
81
|
+
page,
|
|
82
|
+
context
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
return components;
|
|
86
|
+
}
|
|
87
|
+
export default buildPages;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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.
|
|
@@ -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: {},
|
|
@@ -24,10 +24,22 @@ function buildTestPage({ pageConfig }) {
|
|
|
24
24
|
debug: ()=>{},
|
|
25
25
|
log: ()=>{},
|
|
26
26
|
warn: ()=>{},
|
|
27
|
-
error: ()=>{}
|
|
27
|
+
error: ()=>{},
|
|
28
|
+
ui: {
|
|
29
|
+
warn: ()=>{},
|
|
30
|
+
error: ()=>{}
|
|
31
|
+
}
|
|
28
32
|
},
|
|
29
33
|
stage: 'test'
|
|
30
34
|
});
|
|
35
|
+
// Add any connectionIds from test config to allow validation to pass
|
|
36
|
+
connectionIds.forEach((id)=>context.connectionIds.add(id));
|
|
37
|
+
// Also extract connectionIds from requests in the pageConfig
|
|
38
|
+
(pageConfig.requests || []).forEach((request)=>{
|
|
39
|
+
if (request.connectionId) {
|
|
40
|
+
context.connectionIds.add(request.connectionId);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
31
43
|
const components = {
|
|
32
44
|
pages: [
|
|
33
45
|
pageConfig
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Copyright 2020-
|
|
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,9 +12,17 @@
|
|
|
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 { serializer } from '@lowdefy/helpers';
|
|
15
|
+
*/ import { serializer, type } from '@lowdefy/helpers';
|
|
16
|
+
import { ConfigError } from '@lowdefy/errors';
|
|
16
17
|
async function writeRequestsOnPage({ page, context }) {
|
|
17
|
-
|
|
18
|
+
const requests = page.requests ?? [];
|
|
19
|
+
if (!type.isArray(requests)) {
|
|
20
|
+
throw new ConfigError('Page requests must be an array.', {
|
|
21
|
+
received: requests,
|
|
22
|
+
configKey: page['~k']
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return Promise.all(requests.map(async (request)=>{
|
|
18
26
|
await context.writeBuildArtifact(`pages/${page.pageId}/requests/${request.requestId}.json`, serializer.serializeToString(request ?? {}));
|
|
19
27
|
delete request.properties;
|
|
20
28
|
delete request.type;
|