@lowdefy/build 5.6.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/build/buildAgents.js +61 -16
  2. package/dist/build/buildApi/buildApi.js +4 -1
  3. package/dist/build/buildApi/buildEndpoint.js +8 -2
  4. package/dist/build/buildApi/buildRoutine/countStepTypes.js +1 -1
  5. package/dist/build/buildApi/buildRoutine/setStepId.js +2 -0
  6. package/dist/build/buildApi/buildRoutine/validateStep.js +61 -0
  7. package/dist/build/buildApi/resolveEndpointSchedules.js +37 -0
  8. package/dist/build/buildApi/validateEndpoint.js +96 -1
  9. package/dist/build/buildApp.js +13 -20
  10. package/dist/build/buildAppMeta.js +130 -0
  11. package/dist/build/buildAuth/buildAgentAuth.js +14 -1
  12. package/dist/build/buildAuth/buildAuth.js +8 -3
  13. package/dist/build/buildAuth/buildAuthPlugins.js +6 -0
  14. package/dist/build/buildAuth/buildAuthStrategies.js +117 -0
  15. package/dist/build/buildAuth/buildWebsocketAuth.js +55 -0
  16. package/dist/build/buildAuth/getProtectedWebsockets.js +29 -0
  17. package/dist/build/buildAuth/getWebsocketRoles.js +39 -0
  18. package/dist/build/buildAuth/validateAuthConfig.js +15 -3
  19. package/dist/build/buildImports/buildImportsDev.js +15 -0
  20. package/dist/build/buildImports/buildImportsProd.js +4 -1
  21. package/dist/build/buildJs/generateClientJsModule.js +27 -0
  22. package/dist/build/buildJs/writeJs.js +3 -5
  23. package/dist/build/buildMcp.js +73 -0
  24. package/dist/build/buildModuleDefs.js +90 -48
  25. package/dist/build/buildModules.js +61 -14
  26. package/dist/build/buildNotifications.js +98 -0
  27. package/dist/build/buildPages/buildBlock/buildBlock.js +3 -1
  28. package/dist/build/buildPages/buildBlock/buildDynamicBlock.js +131 -0
  29. package/dist/build/buildPages/buildBlock/buildEvents.js +186 -21
  30. package/dist/build/buildPages/buildBlock/buildRequests.js +9 -0
  31. package/dist/build/buildPages/buildBlock/normalizeClassAndStyles.js +2 -11
  32. package/dist/build/buildPages/buildBlock/setBlockId.js +5 -2
  33. package/dist/build/buildPages/buildBlock/validateBlock.js +7 -0
  34. package/dist/build/buildPages/buildPage.js +27 -3
  35. package/dist/build/buildPages/buildSubscriptions.js +145 -0
  36. package/dist/build/buildPages/validateDynamicBlockRefs.js +30 -0
  37. package/dist/build/buildPages/validateStateReferences.js +4 -2
  38. package/dist/build/buildPages/validateWebsocketRefs.js +30 -0
  39. package/dist/build/buildRefs/buildRefs.js +21 -12
  40. package/dist/build/buildRefs/cloneWithMarkers.js +57 -0
  41. package/dist/build/buildRefs/deferredRegions.js +80 -0
  42. package/dist/build/buildRefs/deferredRegistry.js +292 -0
  43. package/dist/build/buildRefs/getModuleRefContent.js +21 -6
  44. package/dist/build/buildRefs/getUserJavascriptFunction.js +1 -1
  45. package/dist/build/buildRefs/parseRefContent.js +24 -0
  46. package/dist/build/buildRefs/{evaluateStaticOperators.js → precomputeRuntimeOperators.js} +7 -6
  47. package/dist/build/buildRefs/walker.js +471 -207
  48. package/dist/build/buildTypes.js +22 -1
  49. package/dist/build/buildWebsockets.js +99 -0
  50. package/dist/build/collectPageContent.js +7 -1
  51. package/dist/build/copyAgentFileSystems.js +2 -2
  52. package/dist/build/full/buildPages.js +16 -0
  53. package/dist/build/full/updateServerPackageJson.js +21 -0
  54. package/dist/build/jit/buildPageJit.js +46 -6
  55. package/dist/build/jit/collectSkeletonSourceFiles.js +14 -0
  56. package/dist/build/jit/pageContentKeys.js +1 -0
  57. package/dist/build/jit/shallowBuild.js +81 -1
  58. package/dist/build/jit/writePageJit.js +11 -8
  59. package/dist/build/registerModules.js +125 -24
  60. package/dist/build/validateCallAgentSteps.js +58 -0
  61. package/dist/build/validateConfig.js +4 -0
  62. package/dist/build/validateCronConfig.js +110 -0
  63. package/dist/build/validateRenderNotificationSteps.js +50 -0
  64. package/dist/build/writeApi.js +58 -1
  65. package/dist/build/writeMcp.js +21 -0
  66. package/dist/build/writeNotifications.js +30 -0
  67. package/dist/build/writePluginImports/importPluginModule.js +39 -0
  68. package/dist/build/writePluginImports/writeActionSchemaMap.js +6 -7
  69. package/dist/build/writePluginImports/writeAuthImports.js +4 -0
  70. package/dist/build/writePluginImports/writeAvailableTypes.js +99 -0
  71. package/dist/build/writePluginImports/writeBlockSchemaMap.js +10 -9
  72. package/dist/build/writePluginImports/writeConnectionSchemaMap.js +101 -0
  73. package/dist/build/writePluginImports/writeGlobalsCss.js +20 -10
  74. package/dist/build/writePluginImports/writeNotificationImports.js +31 -0
  75. package/dist/build/writePluginImports/writeOperatorSchemaMap.js +6 -7
  76. package/dist/build/writePluginImports/writePluginImports.js +18 -4
  77. package/dist/build/writePluginImports/writeWebsocketImports.js +22 -0
  78. package/dist/build/writeWebsockets.js +30 -0
  79. package/dist/createContext.js +6 -1
  80. package/dist/defaultPackages.js +7 -1
  81. package/dist/defaultTypesMap.js +666 -388
  82. package/dist/dynamic/buildDynamicBlocks.js +120 -0
  83. package/dist/index.js +78 -8
  84. package/dist/indexDev.js +4 -0
  85. package/dist/lowdefySchema.js +861 -6
  86. package/dist/scripts/generateDefaultTypes.js +5 -2
  87. package/dist/test-utils/expectTerminates.js +33 -0
  88. package/dist/test-utils/runBuild.js +12 -1
  89. package/dist/test-utils/runBuildForSnapshots.js +34 -4
  90. package/dist/test-utils/testContext.js +8 -2
  91. package/dist/utils/countOperators.js +5 -17
  92. package/dist/utils/createPluginTypesMap.js +21 -0
  93. package/dist/utils/getCronEnvironmentNames.js +25 -0
  94. package/dist/utils/logCollectedErrors.js +15 -9
  95. package/dist/utils/serializeBuildException.js +29 -0
  96. package/dist/utils/validateCronExpression.js +131 -0
  97. package/dist/utils/writeBuildArtifact.js +5 -2
  98. package/package.json +66 -59
  99. package/dist/build/writePluginImports/writeServerExternalPackages.js +0 -121
  100. /package/dist/build/{resolveModuleOperators.js → buildRefs/scopeMenuItemIds.js} +0 -0
@@ -0,0 +1,120 @@
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
+ import { ConfigError } from '@lowdefy/errors';
17
+ import buildBlock from '../build/buildPages/buildBlock/buildBlock.js';
18
+ import createCheckDuplicateId from '../utils/createCheckDuplicateId.js';
19
+ import createCounter from '../utils/createCounter.js';
20
+ // At build time, type counters feed types.json and import generation. At
21
+ // runtime the client bundle is fixed, so the same counter calls become
22
+ // membership checks against the bundled types instead.
23
+ function createMembershipCounter({ category, allowed, dynamicBlockId, pageId }) {
24
+ return {
25
+ increment: (typeName)=>{
26
+ if (!allowed.has(typeName)) {
27
+ throw new ConfigError(`Dynamic block "${dynamicBlockId}" on page "${pageId}" resolved content uses ${category} type "${typeName}" which is not included in the app's client bundle. Declare it in the Dynamic block's properties.types.`);
28
+ }
29
+ }
30
+ };
31
+ }
32
+ // types.json is fileCache-cached (same object per server lifetime), so the
33
+ // allowed-type sets are derived once per types object, not per Dynamic block.
34
+ const allowedSetsCache = new WeakMap();
35
+ function getAllowedSets(types) {
36
+ let sets = allowedSetsCache.get(types);
37
+ if (!sets) {
38
+ sets = {
39
+ actions: new Set(Object.keys(types.actions ?? {})),
40
+ blocks: new Set(Object.keys(types.blocks ?? {})),
41
+ operators: new Set(Object.keys(types.operators?.client ?? {}))
42
+ };
43
+ allowedSetsCache.set(types, sets);
44
+ }
45
+ return sets;
46
+ }
47
+ // Counter for categories dynamic content cannot reach (requests are forbidden,
48
+ // so request types and server operators never occur in a valid fragment).
49
+ const noopCounter = {
50
+ increment: ()=>{}
51
+ };
52
+ function buildDynamicBlocks({ blocks, pageId, dynamicBlockId, idPrefix, types, blockMetas }) {
53
+ if (!type.isArray(blocks)) {
54
+ throw new ConfigError(`Dynamic block "${dynamicBlockId}" on page "${pageId}" endpoint must return an object with a "blocks" array.`, {
55
+ received: blocks
56
+ });
57
+ }
58
+ const warnings = [];
59
+ const callApiActionRefs = [];
60
+ const requestActionRefs = [];
61
+ const allowed = getAllowedSets(types);
62
+ const pageContext = {
63
+ blockIdCounter: createCounter(),
64
+ // Namespace runtime ids under the resolving Dynamic block's built id —
65
+ // unique by construction, so they can never collide with static block ids.
66
+ blockIdPrefix: idPrefix,
67
+ callApiActionRefs,
68
+ checkDuplicateRequestId: createCheckDuplicateId({
69
+ message: 'Duplicate requestId "{{ id }}" on page "{{ pageId }}".'
70
+ }),
71
+ context: {
72
+ blockMetas,
73
+ handleWarning: (warning)=>{
74
+ warnings.push(warning);
75
+ }
76
+ },
77
+ // Pure sink — buildDynamicBlock pushes nested Dynamic refs here, but at
78
+ // runtime a bad endpointId surfaces through resolution, not ref checking.
79
+ dynamicBlockRefs: [],
80
+ forbidRequests: true,
81
+ linkActionRefs: [],
82
+ pageId,
83
+ requestActionRefs,
84
+ requests: [],
85
+ shortcutRefs: [],
86
+ typeCounters: {
87
+ actions: createMembershipCounter({
88
+ category: 'action',
89
+ allowed: allowed.actions,
90
+ dynamicBlockId,
91
+ pageId
92
+ }),
93
+ blocks: createMembershipCounter({
94
+ category: 'block',
95
+ allowed: allowed.blocks,
96
+ dynamicBlockId,
97
+ pageId
98
+ }),
99
+ operators: {
100
+ client: createMembershipCounter({
101
+ category: 'operator',
102
+ allowed: allowed.operators,
103
+ dynamicBlockId,
104
+ pageId
105
+ }),
106
+ server: noopCounter
107
+ },
108
+ requests: noopCounter
109
+ },
110
+ websocketActionRefs: []
111
+ };
112
+ blocks.forEach((block)=>buildBlock(block, pageContext));
113
+ return {
114
+ blocks,
115
+ callApiActionRefs,
116
+ requestActionRefs,
117
+ warnings
118
+ };
119
+ }
120
+ export default buildDynamicBlocks;
package/dist/index.js CHANGED
@@ -22,17 +22,22 @@ import addDefaultPages from './build/addDefaultPages/addDefaultPages.js';
22
22
  import addKeys from './build/addKeys.js';
23
23
  import buildAgents from './build/buildAgents.js';
24
24
  import buildApp from './build/buildApp.js';
25
+ import buildAppMeta from './build/buildAppMeta.js';
25
26
  import buildAuth from './build/buildAuth/buildAuth.js';
26
27
  import buildConnections from './build/buildConnections.js';
27
28
  import buildApi from './build/buildApi/buildApi.js';
28
29
  import buildImports from './build/buildImports/buildImports.js';
29
30
  import buildJs from './build/full/buildJs.js';
30
31
  import buildLogger from './build/buildLogger.js';
32
+ import buildMcp from './build/buildMcp.js';
31
33
  import buildMenu from './build/buildMenu.js';
32
34
  import buildModuleDefs from './build/buildModuleDefs.js';
33
35
  import buildModules from './build/buildModules.js';
36
+ import buildNotifications from './build/buildNotifications.js';
37
+ import precomputeRuntimeOperators from './build/buildRefs/precomputeRuntimeOperators.js';
34
38
  import buildPages from './build/full/buildPages.js';
35
39
  import buildRefs from './build/buildRefs/buildRefs.js';
40
+ import buildWebsockets from './build/buildWebsockets.js';
36
41
  import collectPageContent from './build/collectPageContent.js';
37
42
  import buildTypes from './build/buildTypes.js';
38
43
  import cleanBuildDirectory from './build/cleanBuildDirectory.js';
@@ -40,7 +45,9 @@ import copyAgentFileSystems from './build/copyAgentFileSystems.js';
40
45
  import copyPublicFolder from './build/copyPublicFolder.js';
41
46
  import testSchema from './build/testSchema.js';
42
47
  import updateServerPackageJson from './build/full/updateServerPackageJson.js';
48
+ import validateCallAgentSteps from './build/validateCallAgentSteps.js';
43
49
  import validateConfig from './build/validateConfig.js';
50
+ import validateRenderNotificationSteps from './build/validateRenderNotificationSteps.js';
44
51
  import writeAgents from './build/writeAgents.js';
45
52
  import writeApp from './build/writeApp.js';
46
53
  import writeAppMeta from './build/writeAppMeta.js';
@@ -49,6 +56,8 @@ import writeConfig from './build/writeConfig.js';
49
56
  import writeConnections from './build/writeConnections.js';
50
57
  import writeApi from './build/writeApi.js';
51
58
  import writeGlobal from './build/writeGlobal.js';
59
+ import writeMcp from './build/writeMcp.js';
60
+ import writeWebsockets from './build/writeWebsockets.js';
52
61
  import codegenI18nLocales from './build/codegenI18nLocales.js';
53
62
  import writeI18n from './build/writeI18n.js';
54
63
  import writeTheme from './build/writeTheme.js';
@@ -56,6 +65,7 @@ import writeJs from './build/buildJs/writeJs.js';
56
65
  import writeLogger from './build/writeLogger.js';
57
66
  import writeMaps from './build/writeMaps.js';
58
67
  import writeMenus from './build/writeMenus.js';
68
+ import writeNotifications from './build/writeNotifications.js';
59
69
  import writePages from './build/full/writePages.js';
60
70
  import writePluginImports from './build/writePluginImports/writePluginImports.js';
61
71
  import writeRequests from './build/full/writeRequests.js';
@@ -66,6 +76,15 @@ async function build(options) {
66
76
  let context;
67
77
  try {
68
78
  context = createContext(options);
79
+ // Phase 0: Resolve root app metadata into context.appMeta before any
80
+ // operator evaluation. _app / _build.app read this, and buildModuleDefs
81
+ // (next) evaluates operators on root config, consumer vars, and connections.
82
+ await buildAppMeta({
83
+ context
84
+ });
85
+ // Surface bad root metadata (unsupported operators, failed _build.*) now,
86
+ // before module operators evaluate against it — avoids cascade errors.
87
+ logCollectedErrors(context);
69
88
  // Phase 1: Build module definitions
70
89
  // Parses lowdefy.yaml, resolves module refs, populates context.modules
71
90
  await buildModuleDefs({
@@ -92,6 +111,18 @@ async function build(options) {
92
111
  components,
93
112
  context
94
113
  });
114
+ // Phase 3.5: Pre-compute static runtime operators (_sum, _if, _string, etc.)
115
+ // whose arguments are fully static. This single fold covers components after
116
+ // module manifests are merged (replacing the old per-region folds in buildRefs,
117
+ // buildModuleDefs, and registerModules). Must run before addKeys so that ~k
118
+ // markers are added to the already-folded tree.
119
+ // context.rootRefDef is the lowdefy.yaml refDef stashed by buildRefs (Phase 2)
120
+ // so we can resolve error source file paths without allocating a new makeId entry.
121
+ components = precomputeRuntimeOperators({
122
+ context,
123
+ input: components,
124
+ refDef: context.rootRefDef
125
+ });
95
126
  // Build steps - collect all errors before stopping
96
127
  // addKeys runs first so testSchema has ~k markers for error location info
97
128
  tryBuildStep(addKeys, 'addKeys', {
@@ -110,6 +141,9 @@ async function build(options) {
110
141
  components,
111
142
  context
112
143
  });
144
+ // appMeta is computed in Phase 0; attach it here (where buildApp used to
145
+ // create it) so the following addKeys pass keys it identically.
146
+ components.appMeta = context.appMeta;
113
147
  tryBuildStep(buildLogger, 'buildLogger', {
114
148
  components,
115
149
  context
@@ -143,32 +177,39 @@ async function build(options) {
143
177
  components,
144
178
  context
145
179
  });
146
- tryBuildStep(buildPages, 'buildPages', {
180
+ tryBuildStep(buildMcp, 'buildMcp', {
147
181
  components,
148
182
  context
149
183
  });
150
- tryBuildStep(buildMenu, 'buildMenu', {
184
+ tryBuildStep(buildWebsockets, 'buildWebsockets', {
151
185
  components,
152
186
  context
153
187
  });
154
- tryBuildStep(buildJs, 'buildJs', {
188
+ tryBuildStep(buildNotifications, 'buildNotifications', {
155
189
  components,
156
190
  context
157
191
  });
158
- tryBuildStep(buildTypes, 'buildTypes', {
192
+ // Cross-config step validations — need buildApi (stepIds) and the
193
+ // buildAgents/buildNotifications id sets
194
+ tryBuildStep(validateCallAgentSteps, 'validateCallAgentSteps', {
159
195
  components,
160
196
  context
161
197
  });
162
- tryBuildStep(buildImports, 'buildImports', {
198
+ tryBuildStep(validateRenderNotificationSteps, 'validateRenderNotificationSteps', {
163
199
  components,
164
200
  context
165
201
  });
166
- // Final addKeys pass to ensure all objects (including those created by build steps) have ~k
167
- tryBuildStep(addKeys, 'addKeys', {
202
+ tryBuildStep(buildPages, 'buildPages', {
203
+ components,
204
+ context
205
+ });
206
+ tryBuildStep(buildMenu, 'buildMenu', {
168
207
  components,
169
208
  context
170
209
  });
171
- // Collect page content strings for Tailwind to scan
210
+ // Collect page content strings for Tailwind to scan. Must run before
211
+ // buildJs — jsMapParser replaces _js source with hashes, and class
212
+ // candidates used only inside _js source would otherwise never be scanned.
172
213
  context.tailwindContentMap = new Map();
173
214
  for (const page of components.pages ?? []){
174
215
  const content = collectPageContent([
@@ -178,6 +219,23 @@ async function build(options) {
178
219
  context.tailwindContentMap.set(page.pageId, content);
179
220
  }
180
221
  }
222
+ tryBuildStep(buildJs, 'buildJs', {
223
+ components,
224
+ context
225
+ });
226
+ tryBuildStep(buildTypes, 'buildTypes', {
227
+ components,
228
+ context
229
+ });
230
+ tryBuildStep(buildImports, 'buildImports', {
231
+ components,
232
+ context
233
+ });
234
+ // Final addKeys pass to ensure all objects (including those created by build steps) have ~k
235
+ tryBuildStep(addKeys, 'addKeys', {
236
+ components,
237
+ context
238
+ });
181
239
  // Check if there are any collected errors before writing
182
240
  logCollectedErrors(context);
183
241
  // Write steps - only if no errors
@@ -208,6 +266,18 @@ async function build(options) {
208
266
  components,
209
267
  context
210
268
  });
269
+ await writeMcp({
270
+ components,
271
+ context
272
+ });
273
+ await writeWebsockets({
274
+ components,
275
+ context
276
+ });
277
+ await writeNotifications({
278
+ components,
279
+ context
280
+ });
211
281
  await writeRequests({
212
282
  components,
213
283
  context
package/dist/indexDev.js CHANGED
@@ -14,8 +14,12 @@
14
14
  limitations under the License.
15
15
  */ export { default as buildModuleDefs } from './build/buildModuleDefs.js';
16
16
  export { default as buildModules } from './build/buildModules.js';
17
+ export { default as generateClientJsModule } from './build/buildJs/generateClientJsModule.js';
18
+ export { default as iconPackages } from './build/buildImports/iconPackages.js';
17
19
  export { default as shallowBuild } from './build/jit/shallowBuild.js';
18
20
  export { default as buildPageJit } from './build/jit/buildPageJit.js';
19
21
  export { default as createPageRegistry } from './build/jit/createPageRegistry.js';
20
22
  export { default as createContext } from './createContext.js';
21
23
  export { default as makeId } from './utils/makeId.js';
24
+ export { default as serializeBuildException } from './utils/serializeBuildException.js';
25
+ export { hydrateDeferredRecords } from './build/buildRefs/deferredRegistry.js';