@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
|
@@ -0,0 +1,51 @@
|
|
|
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 fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
function getInstalledPackages(directories) {
|
|
18
|
+
if (!directories.server) return null;
|
|
19
|
+
const pkgPath = path.join(directories.server, 'package.json');
|
|
20
|
+
if (!fs.existsSync(pkgPath)) return null;
|
|
21
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
22
|
+
return new Set(Object.keys(pkg.dependencies ?? {}));
|
|
23
|
+
}
|
|
24
|
+
// In dev mode, page content is built JIT so page-level types (actions, blocks,
|
|
25
|
+
// operators) aren't counted during skeleton build. Include all types from
|
|
26
|
+
// installed packages so they're available for client-side use. Dev server
|
|
27
|
+
// pre-installs default packages so bundle size is not a concern — only
|
|
28
|
+
// production builds tree-shake by counting exact type usage.
|
|
29
|
+
function addInstalledTypes({ components, context }) {
|
|
30
|
+
const installedPackages = getInstalledPackages(context.directories);
|
|
31
|
+
if (!installedPackages) return;
|
|
32
|
+
const addTypes = (store, definitions)=>{
|
|
33
|
+
for (const [typeName, def] of Object.entries(definitions)){
|
|
34
|
+
if (!store[typeName] && installedPackages.has(def.package)) {
|
|
35
|
+
store[typeName] = {
|
|
36
|
+
originalTypeName: def.originalTypeName,
|
|
37
|
+
package: def.package,
|
|
38
|
+
version: def.version,
|
|
39
|
+
count: 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
addTypes(components.types.actions, context.typesMap.actions);
|
|
45
|
+
addTypes(components.types.blocks, context.typesMap.blocks);
|
|
46
|
+
addTypes(components.types.operators.client, context.typesMap.operators.client);
|
|
47
|
+
addTypes(components.types.operators.server, context.typesMap.operators.server);
|
|
48
|
+
// Expose installedPackages on context for later use (e.g., writing build artifact)
|
|
49
|
+
context.installedPackages = installedPackages;
|
|
50
|
+
}
|
|
51
|
+
export default addInstalledTypes;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import jsMapParser from '../buildJs/jsMapParser.js';
|
|
16
|
+
function buildJsShallow({ components, context }) {
|
|
17
|
+
// Extract JS from api/connections (page JS is built JIT)
|
|
18
|
+
if (components.api) {
|
|
19
|
+
components.api = jsMapParser({
|
|
20
|
+
input: components.api,
|
|
21
|
+
jsMap: context.jsMap,
|
|
22
|
+
env: 'server'
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (components.connections) {
|
|
26
|
+
components.connections = jsMapParser({
|
|
27
|
+
input: components.connections,
|
|
28
|
+
jsMap: context.jsMap,
|
|
29
|
+
env: 'server'
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
// Ensure both client and server jsMap keys exist.
|
|
33
|
+
// Page JS extraction is deferred to JIT build.
|
|
34
|
+
if (!context.jsMap.client) {
|
|
35
|
+
context.jsMap.client = {};
|
|
36
|
+
}
|
|
37
|
+
if (!context.jsMap.server) {
|
|
38
|
+
context.jsMap.server = {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export default buildJsShallow;
|
|
@@ -0,0 +1,252 @@
|
|
|
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 fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { serializer, type } from '@lowdefy/helpers';
|
|
18
|
+
import { ConfigError, LowdefyInternalError } from '@lowdefy/errors';
|
|
19
|
+
import addKeys from '../addKeys.js';
|
|
20
|
+
import buildPage from '../buildPages/buildPage.js';
|
|
21
|
+
import validateLinkReferences from '../buildPages/validateLinkReferences.js';
|
|
22
|
+
import validatePayloadReferences from '../buildPages/validatePayloadReferences.js';
|
|
23
|
+
import validateServerStateReferences from '../buildPages/validateServerStateReferences.js';
|
|
24
|
+
import validateStateReferences from '../buildPages/validateStateReferences.js';
|
|
25
|
+
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
26
|
+
import createContext from '../../createContext.js';
|
|
27
|
+
import createRefReviver from '../buildRefs/createRefReviver.js';
|
|
28
|
+
import evaluateBuildOperators from '../buildRefs/evaluateBuildOperators.js';
|
|
29
|
+
import evaluateStaticOperators from '../buildRefs/evaluateStaticOperators.js';
|
|
30
|
+
import jsMapParser from '../buildJs/jsMapParser.js';
|
|
31
|
+
import makeRefDefinition from '../buildRefs/makeRefDefinition.js';
|
|
32
|
+
import recursiveBuild from '../buildRefs/recursiveBuild.js';
|
|
33
|
+
import detectMissingPluginPackages from './detectMissingPluginPackages.js';
|
|
34
|
+
import updateServerPackageJsonJit from './updateServerPackageJsonJit.js';
|
|
35
|
+
import validatePageTypes from './validatePageTypes.js';
|
|
36
|
+
import writePageJit from './writePageJit.js';
|
|
37
|
+
async function buildPageJit({ pageId, pageRegistry, context, directories, logger }) {
|
|
38
|
+
// Use provided context or create a minimal one for JIT builds
|
|
39
|
+
const buildContext = context ?? createContext({
|
|
40
|
+
directories,
|
|
41
|
+
logger: logger ?? console,
|
|
42
|
+
stage: 'dev'
|
|
43
|
+
});
|
|
44
|
+
const pageEntry = type.isFunction(pageRegistry.get) ? pageRegistry.get(pageId) : pageRegistry[pageId];
|
|
45
|
+
if (!pageEntry) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
// Reset errors for this build. Keep a local reference so that concurrent
|
|
49
|
+
// JIT builds (different pages sharing buildContext) cannot corrupt our
|
|
50
|
+
// error list by reassigning buildContext.errors during an await.
|
|
51
|
+
const buildErrors = [];
|
|
52
|
+
buildContext.errors = buildErrors;
|
|
53
|
+
try {
|
|
54
|
+
// Pages without a source file (e.g., default 404) can only be served from
|
|
55
|
+
// their pre-built artifact — they have no YAML to re-resolve from.
|
|
56
|
+
// All user pages (with refId) always JIT-resolve from source YAML so that
|
|
57
|
+
// page-only edits are picked up without a skeleton rebuild.
|
|
58
|
+
if (!pageEntry.refId) {
|
|
59
|
+
const pagePath = path.join(buildContext.directories.build, 'pages', pageId, `${pageId}.json`);
|
|
60
|
+
try {
|
|
61
|
+
const content = await fs.promises.readFile(pagePath, 'utf8');
|
|
62
|
+
return serializer.deserialize(JSON.parse(content));
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (err.code !== 'ENOENT') throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Resolve the page file from scratch using the source file path determined
|
|
68
|
+
// by createPageRegistry's parent chain walk.
|
|
69
|
+
if (!pageEntry.refPath && !pageEntry.resolverOriginal) {
|
|
70
|
+
throw new ConfigError(`Page "${pageId}" has no source file reference. Cannot resolve page content.`);
|
|
71
|
+
}
|
|
72
|
+
// Resolve unresolved vars (which may contain inner _ref objects) fresh from disk.
|
|
73
|
+
// For resolver pages, unresolved vars live in resolverOriginal.vars (single source).
|
|
74
|
+
// For file-backed pages, they're stored separately in unresolvedVars.
|
|
75
|
+
const unresolvedVars = pageEntry.unresolvedVars ?? pageEntry.resolverOriginal?.vars;
|
|
76
|
+
let resolvedVars = null;
|
|
77
|
+
if (unresolvedVars) {
|
|
78
|
+
const varRefDef = makeRefDefinition({}, null, buildContext.refMap);
|
|
79
|
+
resolvedVars = await recursiveBuild({
|
|
80
|
+
context: buildContext,
|
|
81
|
+
refDef: varRefDef,
|
|
82
|
+
count: 0,
|
|
83
|
+
content: unresolvedVars,
|
|
84
|
+
referencedFrom: pageEntry.refPath ?? pageEntry.resolverOriginal?.resolver
|
|
85
|
+
});
|
|
86
|
+
resolvedVars = await evaluateBuildOperators({
|
|
87
|
+
context: buildContext,
|
|
88
|
+
input: resolvedVars,
|
|
89
|
+
refDef: varRefDef
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
let refDef;
|
|
93
|
+
if (pageEntry.resolverOriginal) {
|
|
94
|
+
const resolverDefinition = resolvedVars ? {
|
|
95
|
+
...pageEntry.resolverOriginal,
|
|
96
|
+
vars: resolvedVars
|
|
97
|
+
} : pageEntry.resolverOriginal;
|
|
98
|
+
refDef = makeRefDefinition(resolverDefinition, null, buildContext.refMap);
|
|
99
|
+
buildContext.refMap[refDef.id].path = null;
|
|
100
|
+
} else {
|
|
101
|
+
const refDefinition = resolvedVars ? {
|
|
102
|
+
path: pageEntry.refPath,
|
|
103
|
+
vars: resolvedVars
|
|
104
|
+
} : pageEntry.refPath;
|
|
105
|
+
refDef = makeRefDefinition(refDefinition, null, buildContext.refMap);
|
|
106
|
+
buildContext.refMap[refDef.id].path = refDef.path;
|
|
107
|
+
}
|
|
108
|
+
let processed = await recursiveBuild({
|
|
109
|
+
context: buildContext,
|
|
110
|
+
refDef,
|
|
111
|
+
count: 0
|
|
112
|
+
});
|
|
113
|
+
// Top-level operator evaluation (same as buildRefs does after recursiveBuild)
|
|
114
|
+
processed = await evaluateBuildOperators({
|
|
115
|
+
context: buildContext,
|
|
116
|
+
input: processed,
|
|
117
|
+
refDef
|
|
118
|
+
});
|
|
119
|
+
processed = evaluateStaticOperators({
|
|
120
|
+
context: buildContext,
|
|
121
|
+
input: processed,
|
|
122
|
+
refDef
|
|
123
|
+
});
|
|
124
|
+
// When resolving from a collection file (with vars), the result is an array of pages.
|
|
125
|
+
// Find the specific page by ID.
|
|
126
|
+
if (type.isArray(processed)) {
|
|
127
|
+
processed = processed.find((p)=>type.isObject(p) && p.id === pageId);
|
|
128
|
+
if (!processed) {
|
|
129
|
+
throw new ConfigError(`Page "${pageId}" not found in resolved page source file.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Stamp root-level content with ~r for correct error file tracing.
|
|
133
|
+
// recursiveBuild stamps child _ref content via createRefReviver, but the
|
|
134
|
+
// root file's own objects have no parent to do this. Without ~r, addKeys
|
|
135
|
+
// can't link objects to their source file and errors fall back to lowdefy.yaml.
|
|
136
|
+
const reviver = createRefReviver(refDef.id);
|
|
137
|
+
processed = serializer.copy(processed, {
|
|
138
|
+
reviver
|
|
139
|
+
});
|
|
140
|
+
// Apply skeleton-computed auth (buildAuth ran during skeleton build)
|
|
141
|
+
processed.auth = pageEntry.auth;
|
|
142
|
+
// Add keys to the resolved page
|
|
143
|
+
addKeys({
|
|
144
|
+
components: processed,
|
|
145
|
+
context: buildContext
|
|
146
|
+
});
|
|
147
|
+
// Initialize linkActionRefs for buildPage (normally done by buildPages)
|
|
148
|
+
if (!buildContext.linkActionRefs) {
|
|
149
|
+
buildContext.linkActionRefs = [];
|
|
150
|
+
}
|
|
151
|
+
// Build the page (validation, block processing)
|
|
152
|
+
const checkDuplicatePageId = createCheckDuplicateId({
|
|
153
|
+
message: 'Duplicate pageId "{{ id }}".'
|
|
154
|
+
});
|
|
155
|
+
buildPage({
|
|
156
|
+
page: processed,
|
|
157
|
+
index: 0,
|
|
158
|
+
context: buildContext,
|
|
159
|
+
checkDuplicatePageId
|
|
160
|
+
});
|
|
161
|
+
// Validate that all page-level types (blocks, actions, operators) exist
|
|
162
|
+
validatePageTypes({
|
|
163
|
+
context: buildContext
|
|
164
|
+
});
|
|
165
|
+
// Detect plugin packages that are in typesMap but not installed in server
|
|
166
|
+
const missingPackages = detectMissingPluginPackages({
|
|
167
|
+
context: buildContext,
|
|
168
|
+
installedPluginPackages: buildContext.installedPluginPackages
|
|
169
|
+
});
|
|
170
|
+
if (missingPackages.size > 0) {
|
|
171
|
+
if (buildContext.directories.server) {
|
|
172
|
+
await updateServerPackageJsonJit({
|
|
173
|
+
directories: buildContext.directories,
|
|
174
|
+
missingPackages
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
installing: true,
|
|
179
|
+
packages: [
|
|
180
|
+
...missingPackages.keys()
|
|
181
|
+
]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Validate link, state, payload, and server-state references
|
|
185
|
+
const pageIds = Object.keys(pageRegistry);
|
|
186
|
+
validateLinkReferences({
|
|
187
|
+
linkActionRefs: buildContext.linkActionRefs,
|
|
188
|
+
pageIds,
|
|
189
|
+
context: buildContext
|
|
190
|
+
});
|
|
191
|
+
validateStateReferences({
|
|
192
|
+
page: processed,
|
|
193
|
+
context: buildContext
|
|
194
|
+
});
|
|
195
|
+
validatePayloadReferences({
|
|
196
|
+
page: processed,
|
|
197
|
+
context: buildContext
|
|
198
|
+
});
|
|
199
|
+
validateServerStateReferences({
|
|
200
|
+
page: processed,
|
|
201
|
+
context: buildContext
|
|
202
|
+
});
|
|
203
|
+
// Extract JS functions from the page
|
|
204
|
+
const pageRequests = [
|
|
205
|
+
...processed.requests ?? []
|
|
206
|
+
];
|
|
207
|
+
delete processed.requests;
|
|
208
|
+
const cleanPage = jsMapParser({
|
|
209
|
+
input: processed,
|
|
210
|
+
jsMap: buildContext.jsMap,
|
|
211
|
+
env: 'client'
|
|
212
|
+
});
|
|
213
|
+
const cleanRequests = jsMapParser({
|
|
214
|
+
input: pageRequests,
|
|
215
|
+
jsMap: buildContext.jsMap,
|
|
216
|
+
env: 'server'
|
|
217
|
+
});
|
|
218
|
+
const finalPage = {
|
|
219
|
+
...cleanPage,
|
|
220
|
+
requests: cleanRequests
|
|
221
|
+
};
|
|
222
|
+
// Check for collected errors from validation steps
|
|
223
|
+
if (buildErrors.length > 0) {
|
|
224
|
+
const error = new ConfigError(`Page "${pageId}" build failed with ${buildErrors.length} error(s).`);
|
|
225
|
+
error.buildErrors = buildErrors;
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
// Write page artifacts
|
|
229
|
+
await writePageJit({
|
|
230
|
+
page: finalPage,
|
|
231
|
+
context: buildContext
|
|
232
|
+
});
|
|
233
|
+
return finalPage;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
// Attach any collected errors to the thrown error
|
|
236
|
+
if (buildErrors.length > 0 && !err.buildErrors) {
|
|
237
|
+
err.buildErrors = [
|
|
238
|
+
err,
|
|
239
|
+
...buildErrors
|
|
240
|
+
];
|
|
241
|
+
}
|
|
242
|
+
if (err.isLowdefyError) {
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
const lowdefyErr = new LowdefyInternalError(err.message, {
|
|
246
|
+
cause: err
|
|
247
|
+
});
|
|
248
|
+
lowdefyErr.buildErrors = err.buildErrors;
|
|
249
|
+
throw lowdefyErr;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
export default buildPageJit;
|
|
@@ -0,0 +1,90 @@
|
|
|
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 { serializer } from '@lowdefy/helpers';
|
|
16
|
+
import buildPage from '../buildPages/buildPage.js';
|
|
17
|
+
import jsMapParser from '../buildJs/jsMapParser.js';
|
|
18
|
+
import createCheckDuplicateId from '../../utils/createCheckDuplicateId.js';
|
|
19
|
+
import createPageRegistry from './createPageRegistry.js';
|
|
20
|
+
import PAGE_CONTENT_KEYS from './pageContentKeys.js';
|
|
21
|
+
function buildShallowPages({ components, context }) {
|
|
22
|
+
// Set pageId on all pages (normally done by buildPage in buildPages).
|
|
23
|
+
// Must run before createPageRegistry which uses page.id as map key.
|
|
24
|
+
for (const page of components.pages ?? []){
|
|
25
|
+
if (page.id && !page.pageId) {
|
|
26
|
+
page.pageId = page.id;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const pageRegistry = createPageRegistry({
|
|
30
|
+
components,
|
|
31
|
+
context
|
|
32
|
+
});
|
|
33
|
+
const checkDuplicatePageId = createCheckDuplicateId({
|
|
34
|
+
message: 'Duplicate pageId "{{ id }}".'
|
|
35
|
+
});
|
|
36
|
+
for (const page of components.pages ?? []){
|
|
37
|
+
checkDuplicatePageId({
|
|
38
|
+
id: page.id,
|
|
39
|
+
configKey: page['~k']
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
// Build sourceless pages (e.g., default 404) — no YAML to JIT-resolve from.
|
|
43
|
+
context.linkActionRefs = [];
|
|
44
|
+
const sourcelessPageArtifacts = [];
|
|
45
|
+
(components.pages ?? []).forEach((page, index)=>{
|
|
46
|
+
const entry = pageRegistry.get(page.id);
|
|
47
|
+
// Skip pages with a source file (JIT-resolved) and resolver pages (JIT re-run)
|
|
48
|
+
if (!entry || entry.refPath !== null || entry.resolverOriginal) return;
|
|
49
|
+
buildPage({
|
|
50
|
+
page,
|
|
51
|
+
index,
|
|
52
|
+
context
|
|
53
|
+
});
|
|
54
|
+
const pageRequests = [
|
|
55
|
+
...page.requests ?? []
|
|
56
|
+
];
|
|
57
|
+
delete page.requests;
|
|
58
|
+
const cleanPage = jsMapParser({
|
|
59
|
+
input: page,
|
|
60
|
+
jsMap: context.jsMap,
|
|
61
|
+
env: 'client'
|
|
62
|
+
});
|
|
63
|
+
const cleanRequests = jsMapParser({
|
|
64
|
+
input: pageRequests,
|
|
65
|
+
jsMap: context.jsMap,
|
|
66
|
+
env: 'server'
|
|
67
|
+
});
|
|
68
|
+
const builtPage = {
|
|
69
|
+
...cleanPage,
|
|
70
|
+
requests: cleanRequests
|
|
71
|
+
};
|
|
72
|
+
sourcelessPageArtifacts.push({
|
|
73
|
+
pageId: builtPage.pageId,
|
|
74
|
+
pageJson: serializer.serializeToString(builtPage),
|
|
75
|
+
requests: (builtPage.requests ?? []).map((req)=>({
|
|
76
|
+
requestId: req.requestId,
|
|
77
|
+
requestJson: serializer.serializeToString(req)
|
|
78
|
+
}))
|
|
79
|
+
});
|
|
80
|
+
// Strip content for subsequent skeleton steps
|
|
81
|
+
for (const key of PAGE_CONTENT_KEYS){
|
|
82
|
+
delete page[key];
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
pageRegistry,
|
|
87
|
+
sourcelessPageArtifacts
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export default buildShallowPages;
|
|
@@ -0,0 +1,80 @@
|
|
|
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
|
+
// Walk up from ~r to find the page's source file for JIT re-resolution.
|
|
17
|
+
// Templates receive vars (they need id, title, etc. from the page file),
|
|
18
|
+
// while page files are self-contained or receive vars from a collection file.
|
|
19
|
+
// Stop at the first ref called WITHOUT vars — that's the page file.
|
|
20
|
+
// When ALL refs have vars (module pages), fall back to the first child of root.
|
|
21
|
+
// When the child of root has no path (resolver ref), go shallower — capture
|
|
22
|
+
// the resolver's info so JIT can re-run it with freshly resolved vars.
|
|
23
|
+
function findPageSourceRef(refId, refMap, unresolvedRefVars) {
|
|
24
|
+
let current = refId;
|
|
25
|
+
let firstChildOfRoot = null;
|
|
26
|
+
while(!type.isNone(current)){
|
|
27
|
+
const entry = refMap[current];
|
|
28
|
+
if (!entry) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const hasVars = !type.isNone(unresolvedRefVars[current]);
|
|
32
|
+
// Track the first child of root as fallback
|
|
33
|
+
const parentEntry = !type.isNone(entry.parent) ? refMap[entry.parent] : null;
|
|
34
|
+
if (parentEntry && type.isNone(parentEntry.parent) && !firstChildOfRoot) {
|
|
35
|
+
// Resolver ref: capture the resolver's original definition and vars
|
|
36
|
+
// so JIT can re-run the resolver (instead of falling through to the
|
|
37
|
+
// template file below it, which would discard the resolver's vars).
|
|
38
|
+
firstChildOfRoot = entry.path ? {
|
|
39
|
+
path: entry.path,
|
|
40
|
+
unresolvedVars: unresolvedRefVars[current] ?? null
|
|
41
|
+
} : {
|
|
42
|
+
path: null,
|
|
43
|
+
original: refMap[current].original
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
// First ref without vars = self-contained page file
|
|
47
|
+
if (!hasVars) {
|
|
48
|
+
if (!type.isNone(entry.parent)) {
|
|
49
|
+
return {
|
|
50
|
+
path: entry.path,
|
|
51
|
+
unresolvedVars: null
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Reached root — use first child of root
|
|
55
|
+
return firstChildOfRoot;
|
|
56
|
+
}
|
|
57
|
+
current = entry.parent;
|
|
58
|
+
}
|
|
59
|
+
return firstChildOfRoot;
|
|
60
|
+
}
|
|
61
|
+
function createPageRegistry({ components, context }) {
|
|
62
|
+
const registry = new Map();
|
|
63
|
+
const unresolvedRefVars = context.unresolvedRefVars ?? {};
|
|
64
|
+
(components.pages ?? []).forEach((page)=>{
|
|
65
|
+
// Read ~r from keyMap — addKeys moves ~r there and deletes it from objects.
|
|
66
|
+
const keyMapEntry = context.keyMap[page['~k']];
|
|
67
|
+
const refId = keyMapEntry?.['~r'] ?? null;
|
|
68
|
+
const sourceRef = !type.isNone(refId) ? findPageSourceRef(refId, context.refMap, unresolvedRefVars) : null;
|
|
69
|
+
registry.set(page.id, {
|
|
70
|
+
pageId: page.id,
|
|
71
|
+
auth: page.auth,
|
|
72
|
+
refId,
|
|
73
|
+
refPath: sourceRef?.path ?? null,
|
|
74
|
+
unresolvedVars: sourceRef?.unresolvedVars ?? null,
|
|
75
|
+
resolverOriginal: sourceRef?.original ?? null
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
return registry;
|
|
79
|
+
}
|
|
80
|
+
export default createPageRegistry;
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
*/ function collectMissing({ counter, definitions, installedPluginPackages, missingPackages }) {
|
|
16
|
+
const counts = counter.getCounts();
|
|
17
|
+
for (const typeName of Object.keys(counts)){
|
|
18
|
+
const def = definitions[typeName];
|
|
19
|
+
if (!def) continue;
|
|
20
|
+
if (installedPluginPackages.has(def.package)) continue;
|
|
21
|
+
if (!missingPackages.has(def.package)) {
|
|
22
|
+
missingPackages.set(def.package, {
|
|
23
|
+
version: def.version,
|
|
24
|
+
types: []
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
missingPackages.get(def.package).types.push(typeName);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function detectMissingPluginPackages({ context, installedPluginPackages }) {
|
|
31
|
+
const missingPackages = new Map();
|
|
32
|
+
if (!installedPluginPackages) {
|
|
33
|
+
return missingPackages;
|
|
34
|
+
}
|
|
35
|
+
const { typeCounters, typesMap } = context;
|
|
36
|
+
collectMissing({
|
|
37
|
+
counter: typeCounters.blocks,
|
|
38
|
+
definitions: typesMap.blocks,
|
|
39
|
+
installedPluginPackages,
|
|
40
|
+
missingPackages
|
|
41
|
+
});
|
|
42
|
+
collectMissing({
|
|
43
|
+
counter: typeCounters.actions,
|
|
44
|
+
definitions: typesMap.actions,
|
|
45
|
+
installedPluginPackages,
|
|
46
|
+
missingPackages
|
|
47
|
+
});
|
|
48
|
+
collectMissing({
|
|
49
|
+
counter: typeCounters.operators.client,
|
|
50
|
+
definitions: typesMap.operators.client,
|
|
51
|
+
installedPluginPackages,
|
|
52
|
+
missingPackages
|
|
53
|
+
});
|
|
54
|
+
collectMissing({
|
|
55
|
+
counter: typeCounters.operators.server,
|
|
56
|
+
definitions: typesMap.operators.server,
|
|
57
|
+
installedPluginPackages,
|
|
58
|
+
missingPackages
|
|
59
|
+
});
|
|
60
|
+
return missingPackages;
|
|
61
|
+
}
|
|
62
|
+
export default detectMissingPluginPackages;
|
|
@@ -0,0 +1,38 @@
|
|
|
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 getRefPositions(content, basePath) {
|
|
17
|
+
const positions = new Map();
|
|
18
|
+
walk(content, basePath, positions);
|
|
19
|
+
return positions;
|
|
20
|
+
}
|
|
21
|
+
function walk(node, path, positions) {
|
|
22
|
+
if (type.isObject(node)) {
|
|
23
|
+
if (node._ref && node._ref.id !== undefined) {
|
|
24
|
+
positions.set(node._ref.id, path);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const key of Object.keys(node)){
|
|
28
|
+
const childPath = path ? `${path}.${key}` : key;
|
|
29
|
+
walk(node[key], childPath, positions);
|
|
30
|
+
}
|
|
31
|
+
} else if (type.isArray(node)) {
|
|
32
|
+
for(let i = 0; i < node.length; i++){
|
|
33
|
+
const childPath = path ? `${path}.${i}` : `${i}`;
|
|
34
|
+
walk(node[i], childPath, positions);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export default getRefPositions;
|
|
@@ -0,0 +1,24 @@
|
|
|
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 PAGE_CONTENT_KEYS from './pageContentKeys.js';
|
|
16
|
+
function isPageContentPath(jsonPath) {
|
|
17
|
+
if (!jsonPath.startsWith('pages.')) return false;
|
|
18
|
+
const segments = jsonPath.split('.');
|
|
19
|
+
for(let i = 1; i < segments.length; i++){
|
|
20
|
+
if (PAGE_CONTENT_KEYS.includes(segments[i])) return true;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
export default isPageContentPath;
|
|
@@ -0,0 +1,26 @@
|
|
|
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
|
+
*/ // Keys stripped from pages after createPageRegistry captures the page file ref.
|
|
16
|
+
// Only `id`, `type` (and skeleton-computed `pageId`, `auth`, `~k`, `~r`) survive.
|
|
17
|
+
// `type` is always a resolved string (never a ref target) and must stay on stubs
|
|
18
|
+
// for schema validation (block schema requires both `id` and `type`).
|
|
19
|
+
const PAGE_CONTENT_KEYS = [
|
|
20
|
+
'blocks',
|
|
21
|
+
'areas',
|
|
22
|
+
'events',
|
|
23
|
+
'requests',
|
|
24
|
+
'layout'
|
|
25
|
+
];
|
|
26
|
+
export default PAGE_CONTENT_KEYS;
|