@lowdefy/engine 4.4.0 → 4.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/engine",
3
- "version": "4.4.0",
3
+ "version": "4.5.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -30,15 +30,15 @@
30
30
  "dist/*"
31
31
  ],
32
32
  "dependencies": {
33
- "@lowdefy/helpers": "4.4.0",
34
- "@lowdefy/operators": "4.4.0"
33
+ "@lowdefy/helpers": "4.5.1",
34
+ "@lowdefy/operators": "4.5.1"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@jest/globals": "28.1.3",
38
- "@lowdefy/actions-core": "4.4.0",
39
- "@lowdefy/build": "4.4.0",
40
- "@lowdefy/operators-js": "4.4.0",
41
- "@lowdefy/operators-mql": "4.4.0",
38
+ "@lowdefy/actions-core": "4.5.1",
39
+ "@lowdefy/build": "4.5.1",
40
+ "@lowdefy/operators-js": "4.5.1",
41
+ "@lowdefy/operators-mql": "4.5.1",
42
42
  "@swc/cli": "0.1.63",
43
43
  "@swc/core": "1.3.99",
44
44
  "@swc/jest": "0.2.29",
package/dist/Blocks.js DELETED
@@ -1,581 +0,0 @@
1
- /* eslint-disable no-param-reassign */ /*
2
- Copyright 2020-2024 Lowdefy, Inc
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
- */ import { applyArrayIndices, get, serializer, swap, type } from '@lowdefy/helpers';
16
- import Events from './Events.js';
17
- let Blocks = class Blocks {
18
- loopBlocks(fn) {
19
- if (type.isObject(this.areas)) {
20
- Object.keys(this.areas).forEach((key)=>{
21
- if (type.isArray(this.areas[key].blocks)) {
22
- this.areas[key].blocks.forEach(fn);
23
- }
24
- });
25
- }
26
- }
27
- init(initState) {
28
- this.loopBlocks((block)=>{
29
- block.idPattern = block.id;
30
- block.blockIdPattern = block.blockId;
31
- block.id = applyArrayIndices(this.arrayIndices, block.idPattern);
32
- block.blockId = applyArrayIndices(this.arrayIndices, block.blockIdPattern);
33
- // CAUTION:
34
- // map is not a direct reference to all blocks, blocks with duplicate ids will be overwritten in map
35
- // which can cause issues with ambiguous config during call method since it will call only the method
36
- // of the last initialized block for the referenced id.
37
- this.context._internal.RootBlocks.map[block.blockId] = block;
38
- block.events = type.isNone(block.events) ? {} : block.events;
39
- block.layout = type.isNone(block.layout) ? {} : block.layout;
40
- block.loading = type.isNone(block.loading) ? false : block.loading;
41
- block.properties = type.isNone(block.properties) ? {} : block.properties;
42
- block.required = type.isNone(block.required) ? false : block.required;
43
- block.skeleton = type.isNone(block.skeleton) ? null : block.skeleton;
44
- block.style = type.isNone(block.style) ? {} : block.style;
45
- block.validate = type.isNone(block.validate) ? [] : block.validate;
46
- block.visible = type.isNone(block.visible) ? true : block.visible;
47
- block.areasLayoutEval = {};
48
- block.layoutEval = {};
49
- block.loadingEval = {};
50
- block.propertiesEval = {};
51
- block.requiredEval = {};
52
- block.skeletonEval = {};
53
- block.styleEval = {};
54
- block.validationEval = {};
55
- block.visibleEval = {};
56
- try {
57
- block.meta = this.context._internal.lowdefy._internal.blockComponents[block.type].meta;
58
- } catch (error) {
59
- throw new Error(`Block type ${block.type} not found at ${block.blockId}. Check your plugins to make sure the block is installed. For more info, see https://docs.lowdefy.com/plugins.`);
60
- }
61
- if (block.meta?.category !== 'container' && block.meta?.category !== 'display' && block.meta?.category !== 'input' && block.meta?.category !== 'list') {
62
- throw new Error(`Block type ${block.type}.meta.category must be either "container", "display", "input" or "list".`);
63
- }
64
- if (!type.isNone(block.areas)) {
65
- block.areasLayout = {};
66
- Object.keys(block.areas).forEach((key)=>{
67
- // eslint-disable-next-line no-unused-vars
68
- const { blocks, ...areaLayout } = block.areas[key];
69
- block.areasLayout[key] = {
70
- ...areaLayout
71
- };
72
- });
73
- } else {
74
- block.areasLayout = {};
75
- }
76
- block.methods = {};
77
- block.registerMethod = (methodName, method)=>{
78
- block.methods[methodName] = method;
79
- };
80
- if (block.meta.category === 'list') {
81
- // TODO: to initialize new object in array, the new value should be passed by method to unshiftItem and pushItem
82
- block.unshiftItem = ()=>{
83
- this.subBlocks[block.id].forEach((bl, i)=>{
84
- bl.recUpdateArrayIndices(this.arrayIndices.concat([
85
- i
86
- ]), this.arrayIndices.concat([
87
- i + 1
88
- ]));
89
- });
90
- this.subBlocks[block.id].unshift(this.newBlocks({
91
- arrayIndices: this.arrayIndices.concat([
92
- 0
93
- ]),
94
- block,
95
- initState: {}
96
- }));
97
- this.context._internal.State.set(block.blockId, undefined);
98
- // set block and subBlock values undefined, so as not to pass values to new blocks
99
- this.subBlocks[block.id][0].recSetUndefined();
100
- block.update = true;
101
- this.context._internal.update();
102
- };
103
- block.pushItem = ()=>{
104
- this.subBlocks[block.id].push(this.newBlocks({
105
- arrayIndices: this.arrayIndices.concat([
106
- this.subBlocks[block.id].length
107
- ]),
108
- block,
109
- initState: {}
110
- }));
111
- block.update = true;
112
- this.context._internal.update();
113
- };
114
- block.removeItem = (index)=>{
115
- this.context._internal.State.removeItem(block.blockId, index);
116
- const lastBlock = this.subBlocks[block.id][this.subBlocks[block.id].length - 1];
117
- lastBlock.recRemoveBlocksFromMap();
118
- const largerBlocks = this.subBlocks[block.id].slice(index + 1);
119
- largerBlocks.forEach((bl, i)=>{
120
- bl.recUpdateArrayIndices(this.arrayIndices.concat([
121
- index + i + 1
122
- ]), this.arrayIndices.concat([
123
- index + i
124
- ]));
125
- });
126
- this.subBlocks[block.id].splice(index, 1);
127
- block.update = true;
128
- this.context._internal.update();
129
- };
130
- block.moveItemUp = (index)=>{
131
- if (index === 0) return;
132
- this.context._internal.State.swapItems(block.blockId, index - 1, index);
133
- this.subBlocks[block.id][index - 1].recUpdateArrayIndices(this.arrayIndices.concat([
134
- index - 1
135
- ]), this.arrayIndices.concat([
136
- index
137
- ]));
138
- this.subBlocks[block.id][index].recUpdateArrayIndices(this.arrayIndices.concat([
139
- index
140
- ]), this.arrayIndices.concat([
141
- index - 1
142
- ]));
143
- swap(this.subBlocks[block.id], index - 1, index);
144
- block.update = true;
145
- this.context._internal.update();
146
- };
147
- block.moveItemDown = (index)=>{
148
- if (index === this.subBlocks[block.id].length - 1) return;
149
- this.context._internal.State.swapItems(block.blockId, index, index + 1);
150
- this.subBlocks[block.id][index + 1].recUpdateArrayIndices(this.arrayIndices.concat([
151
- index + 1
152
- ]), this.arrayIndices.concat([
153
- index
154
- ]));
155
- this.subBlocks[block.id][index].recUpdateArrayIndices(this.arrayIndices.concat([
156
- index
157
- ]), this.arrayIndices.concat([
158
- index + 1
159
- ]));
160
- swap(this.subBlocks[block.id], index, index + 1);
161
- block.update = true;
162
- this.context._internal.update();
163
- };
164
- }
165
- if (block.meta.category === 'input') {
166
- block.setValue = (value)=>{
167
- block.value = type.enforceType(block.meta.valueType, value);
168
- this.context._internal.State.set(block.blockId, block.value);
169
- block.update = true;
170
- this.context._internal.update();
171
- };
172
- }
173
- block.Events = new Events({
174
- arrayIndices: this.arrayIndices,
175
- block,
176
- context: this.context
177
- });
178
- block.triggerEvent = block.Events.triggerEvent;
179
- block.registerEvent = block.Events.registerEvent;
180
- });
181
- this.reset(initState); // set initial values to blocks.
182
- }
183
- reset(initWithState) {
184
- const initState = serializer.copy(initWithState || this.context.state);
185
- this.loopBlocks((block)=>{
186
- block.update = true;
187
- block.showValidation = false;
188
- if (block.meta.category === 'input' || block.meta.category === 'list') {
189
- let blockValue = get(initState, block.blockId);
190
- if (type.isUndefined(blockValue)) {
191
- // default null value for block type
192
- blockValue = type.isUndefined(block.meta.initValue) ? type.enforceType(block.meta.valueType, null) : block.meta.initValue;
193
- this.context._internal.State.set(block.blockId, blockValue);
194
- }
195
- if (block.meta.category === 'list') {
196
- // load list value into list blocks
197
- if (!type.isArray(this.subBlocks[block.id])) {
198
- this.subBlocks[block.id] = [];
199
- }
200
- if (type.isArray(blockValue)) {
201
- blockValue.forEach((item, i)=>{
202
- if (!this.subBlocks[block.id][i]) {
203
- this.subBlocks[block.id].push(this.newBlocks({
204
- arrayIndices: this.arrayIndices.concat([
205
- i
206
- ]),
207
- block,
208
- initState
209
- }));
210
- } else {
211
- this.subBlocks[block.id][i].reset(initState);
212
- }
213
- });
214
- this.subBlocks[block.id].splice(blockValue.length);
215
- }
216
- } else {
217
- block.value = blockValue;
218
- }
219
- } else if (block.meta.category === 'container') {
220
- if (!type.isArray(this.subBlocks[block.id])) {
221
- this.subBlocks[block.id] = [];
222
- }
223
- if (!this.subBlocks[block.id][0]) {
224
- this.subBlocks[block.id].push(this.newBlocks({
225
- arrayIndices: this.arrayIndices,
226
- block,
227
- initState
228
- }));
229
- } else {
230
- this.subBlocks[block.id][0].reset(initState);
231
- }
232
- }
233
- });
234
- }
235
- newBlocks({ arrayIndices, block, initState }) {
236
- const SubBlocks = new Blocks({
237
- arrayIndices,
238
- areas: block.areas,
239
- context: this.context
240
- });
241
- SubBlocks.init(initState);
242
- return SubBlocks;
243
- }
244
- // used for update comparison
245
- static blockEvalToString(block) {
246
- return serializer.serializeToString({
247
- areasLayoutEval: block.areasLayoutEval,
248
- layoutEval: block.layoutEval,
249
- loadingEval: block.loadingEval,
250
- propertiesEval: block.propertiesEval,
251
- requiredEval: block.requiredEval,
252
- skeletonEval: block.skeletonEval,
253
- styleEval: block.styleEval,
254
- validationEval: block.validationEval,
255
- value: block.value,
256
- visibleEval: block.visibleEval
257
- });
258
- }
259
- recEval(visibleParent) {
260
- let repeat = false;
261
- this.loopBlocks((block)=>{
262
- if (block.meta.category === 'input') {
263
- const stateValue = get(this.context.state, block.blockId);
264
- // TODO: related to #345
265
- // enforce type here? should we reassign value here??
266
- block.value = type.isUndefined(stateValue) ? block.value : stateValue;
267
- }
268
- const beforeVisible = block.visibleEval ? block.visibleEval.output : true;
269
- if (visibleParent === false) {
270
- block.visibleEval.output = false;
271
- } else {
272
- block.visibleEval = this.context._internal.parser.parse({
273
- input: block.visible,
274
- location: block.blockId,
275
- arrayIndices: this.arrayIndices
276
- }); // run parser on index combinations to get visible value object
277
- }
278
- if (beforeVisible !== block.visibleEval.output) {
279
- repeat = true;
280
- }
281
- // only evaluate visible blocks
282
- if (block.visibleEval.output !== false) {
283
- block.propertiesEval = this.context._internal.parser.parse({
284
- input: block.properties,
285
- location: block.blockId,
286
- arrayIndices: this.arrayIndices
287
- });
288
- block.requiredEval = this.context._internal.parser.parse({
289
- input: block.required,
290
- location: block.blockId,
291
- arrayIndices: this.arrayIndices
292
- });
293
- const requiredValidation = {
294
- pass: {
295
- _not: {
296
- _type: 'none'
297
- }
298
- },
299
- status: 'error',
300
- message: type.isString(block.requiredEval.output) ? block.requiredEval.output : 'This field is required'
301
- };
302
- const validation = block.requiredEval.output === false ? block.validate : [
303
- ...block.validate,
304
- requiredValidation
305
- ];
306
- block.validationEval = {
307
- output: {
308
- status: null,
309
- errors: [],
310
- warnings: []
311
- },
312
- errors: []
313
- };
314
- let validationError = false;
315
- let validationWarning = false;
316
- validation.forEach((test)=>{
317
- const parsed = this.context._internal.parser.parse({
318
- input: test,
319
- location: block.blockId,
320
- arrayIndices: this.arrayIndices
321
- });
322
- // for parser errors
323
- if (parsed.errors.length > 0) {
324
- block.validationEval.output.errors.push(parsed.output.message);
325
- block.validationEval.errors.push(parsed.errors);
326
- validationError = true;
327
- return;
328
- }
329
- // failed validation
330
- if (!parsed.output.pass) {
331
- // no status indication on validation tests defaults to error
332
- if (!test.status || test.status === 'error') {
333
- block.validationEval.output.errors.push(parsed.output.message);
334
- validationError = true;
335
- }
336
- if (test.status === 'warning') {
337
- block.validationEval.output.warnings.push(parsed.output.message);
338
- validationWarning = true;
339
- }
340
- }
341
- });
342
- if (validation.length > 0) {
343
- block.validationEval.output.status = 'success';
344
- }
345
- if (validationWarning) {
346
- block.validationEval.output.status = 'warning';
347
- }
348
- if (validationError && block.showValidation) {
349
- block.validationEval.output.status = 'error';
350
- }
351
- block.styleEval = this.context._internal.parser.parse({
352
- input: block.style,
353
- location: block.blockId,
354
- arrayIndices: this.arrayIndices
355
- });
356
- block.layoutEval = this.context._internal.parser.parse({
357
- input: block.layout,
358
- location: block.blockId,
359
- arrayIndices: this.arrayIndices
360
- });
361
- block.loadingEval = this.context._internal.parser.parse({
362
- input: block.loading,
363
- location: block.blockId,
364
- arrayIndices: this.arrayIndices
365
- });
366
- block.skeletonEval = this.context._internal.parser.parse({
367
- input: block.skeleton,
368
- location: block.blockId,
369
- arrayIndices: this.arrayIndices
370
- });
371
- block.areasLayoutEval = this.context._internal.parser.parse({
372
- input: block.areasLayout,
373
- location: block.blockId,
374
- arrayIndices: this.arrayIndices
375
- });
376
- }
377
- if (block.meta.category === 'container' || block.meta.category === 'list') {
378
- if (this.subBlocks[block.id] && this.subBlocks[block.id].length > 0) {
379
- this.subBlocks[block.id].forEach((blockClass)=>{
380
- repeat = blockClass.recEval(block.visibleEval.output) || repeat;
381
- });
382
- }
383
- }
384
- const after = Blocks.blockEvalToString(block);
385
- if (block.before !== after) {
386
- block.update = true;
387
- block.before = after;
388
- }
389
- });
390
- return repeat;
391
- }
392
- updateState() {
393
- const toSet = new Set();
394
- const toDelete = new Set();
395
- this.loopBlocks((block)=>{
396
- if (block.visibleEval.output !== false) {
397
- if (block.meta.category === 'container' || block.meta.category === 'list') {
398
- if (this.subBlocks[block.id] && this.subBlocks[block.id].length > 0) {
399
- this.subBlocks[block.id].forEach((blockClass)=>{
400
- blockClass.updateState();
401
- });
402
- } else {
403
- toSet.add(block.blockId);
404
- this.context._internal.State.set(block.blockId, type.enforceType(block.meta.valueType, null));
405
- }
406
- } else if (block.meta.category === 'input') {
407
- toSet.add(block.blockId);
408
- this.context._internal.State.set(block.blockId, block.value);
409
- }
410
- } else if (block.meta.category === 'container') {
411
- this.subBlocks[block.id].forEach((blockClass)=>{
412
- blockClass.recContainerDelState(toDelete);
413
- });
414
- } else {
415
- toDelete.add(block.blockId);
416
- }
417
- });
418
- toDelete.forEach((field)=>{
419
- if (!toSet.has(field)) {
420
- this.context._internal.State.del(field);
421
- }
422
- });
423
- }
424
- recContainerDelState(toDelete) {
425
- this.loopBlocks((block)=>{
426
- if (block.meta.category === 'container') {
427
- this.subBlocks[block.id].forEach((blockClass)=>{
428
- blockClass.recContainerDelState(toDelete);
429
- });
430
- } else {
431
- toDelete.add(block.blockId);
432
- }
433
- });
434
- }
435
- updateStateFromRoot() {
436
- const repeat = this.recEval(true);
437
- this.updateState();
438
- if (repeat && this.recCount < 20) {
439
- this.recCount += 1;
440
- this.updateStateFromRoot();
441
- }
442
- this.recCount = 0;
443
- }
444
- recUpdateArrayIndices(oldIndices, newIndices) {
445
- newIndices.forEach((index, i)=>{
446
- this.arrayIndices[i] = newIndices[i];
447
- });
448
- this.loopBlocks((block)=>{
449
- block.blockId = applyArrayIndices(this.arrayIndices, block.blockIdPattern);
450
- this.context._internal.RootBlocks.map[block.blockId] = block;
451
- });
452
- Object.keys(this.subBlocks).forEach((subKey)=>{
453
- this.subBlocks[subKey].forEach((subBlock)=>{
454
- subBlock.recUpdateArrayIndices(oldIndices, newIndices);
455
- });
456
- });
457
- }
458
- getValidateRec(match, result) {
459
- this.loopBlocks((block)=>{
460
- if (match(block.blockId)) {
461
- block.showValidation = true;
462
- block.update = true;
463
- if (block.visibleEval.output !== false && block.validationEval.output && block.validationEval.output.errors.length > 0) {
464
- block.validationEval.output.status = 'error';
465
- result.push({
466
- blockId: block.blockId,
467
- validation: block.validationEval.output
468
- });
469
- }
470
- }
471
- });
472
- Object.keys(this.subBlocks).forEach((subKey)=>{
473
- this.subBlocks[subKey].forEach((subBlock)=>{
474
- subBlock.getValidateRec(match, result);
475
- });
476
- });
477
- return result;
478
- }
479
- recSetUndefined() {
480
- this.loopBlocks((block)=>{
481
- this.context._internal.State.set(block.blockId, undefined);
482
- });
483
- Object.keys(this.subBlocks).forEach((subKey)=>{
484
- this.subBlocks[subKey].forEach((subBlock)=>{
485
- subBlock.recSetUndefined();
486
- });
487
- });
488
- }
489
- recRemoveBlocksFromMap() {
490
- this.loopBlocks((block)=>{
491
- delete this.context._internal.RootBlocks.map[block.blockId];
492
- });
493
- Object.keys(this.subBlocks).forEach((subKey)=>{
494
- this.subBlocks[subKey].forEach((subBlock)=>{
495
- subBlock.recRemoveBlocksFromMap();
496
- });
497
- });
498
- }
499
- validate(match) {
500
- this.updateStateFromRoot(); // update to recalculate validationEval to raise block errors
501
- const validationErrors = this.getValidateRec(match, []); // get all relevant raised block errors and set showValidation
502
- this.setBlocksCache(); // update cache to render
503
- return validationErrors;
504
- }
505
- resetValidationRec(match) {
506
- this.loopBlocks((block)=>{
507
- if (match(block.blockId)) {
508
- block.showValidation = false;
509
- block.update = true;
510
- }
511
- });
512
- Object.keys(this.subBlocks).forEach((subKey)=>{
513
- this.subBlocks[subKey].forEach((subBlock)=>{
514
- subBlock.resetValidationRec(match);
515
- });
516
- });
517
- }
518
- resetValidation(match) {
519
- this.resetValidationRec(match);
520
- this.setBlocksCache();
521
- }
522
- update() {
523
- this.updateStateFromRoot(); // update all the blocks
524
- this.setBlocksCache(); // finally update cache
525
- }
526
- setBlocksCache() {
527
- this.loopBlocks((block)=>{
528
- if (block.update) {
529
- block.update = false;
530
- block.eval = {
531
- areas: block.areasLayoutEval.output,
532
- events: type.isNone(block.Events.events) ? null : block.Events.events,
533
- properties: block.propertiesEval.output,
534
- loading: block.loadingEval.output,
535
- skeleton: block.skeletonEval.output,
536
- required: block.requiredEval.output,
537
- layout: block.layoutEval.output,
538
- style: block.styleEval.output,
539
- validation: {
540
- ...block.validationEval.output || {},
541
- status: block.showValidation || block.validationEval.output?.status === 'warning' ? block.validationEval.output?.status : null
542
- },
543
- value: type.isNone(block.value) ? null : block.value,
544
- visible: block.visibleEval.output
545
- };
546
- this.context._internal.lowdefy._internal.updateBlock(block.id);
547
- }
548
- });
549
- Object.keys(this.subBlocks).forEach((subKey)=>{
550
- this.subBlocks[subKey].forEach((subBlock)=>{
551
- subBlock.setBlocksCache();
552
- });
553
- });
554
- }
555
- constructor({ arrayIndices = [], areas, context }){
556
- this.id = Math.random().toString(36).replace(/[^a-z]+/g, '').substring(0, 5);
557
- this.areas = serializer.copy(areas || []);
558
- this.arrayIndices = arrayIndices;
559
- this.context = context;
560
- this.map = {};
561
- this.recCount = 0;
562
- this.subBlocks = {};
563
- this.getValidateRec = this.getValidateRec.bind(this);
564
- this.init = this.init.bind(this);
565
- this.newBlocks = this.newBlocks.bind(this);
566
- this.recContainerDelState = this.recContainerDelState.bind(this);
567
- this.recEval = this.recEval.bind(this);
568
- this.recRemoveBlocksFromMap = this.recRemoveBlocksFromMap.bind(this);
569
- this.recSetUndefined = this.recSetUndefined.bind(this);
570
- this.recUpdateArrayIndices = this.recUpdateArrayIndices.bind(this);
571
- this.reset = this.reset.bind(this);
572
- this.resetValidation = this.resetValidation.bind(this);
573
- this.resetValidationRec = this.resetValidationRec.bind(this);
574
- this.setBlocksCache = this.setBlocksCache.bind(this);
575
- this.update = this.update.bind(this);
576
- this.updateState = this.updateState.bind(this);
577
- this.updateStateFromRoot = this.updateStateFromRoot.bind(this);
578
- this.validate = this.validate.bind(this);
579
- }
580
- };
581
- export default Blocks;