@nordcraft/runtime 1.0.94 → 1.0.96

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 (75) hide show
  1. package/dist/api/createAPI.js +29 -11
  2. package/dist/api/createAPI.js.map +1 -1
  3. package/dist/api/createAPIv2.js +49 -11
  4. package/dist/api/createAPIv2.js.map +1 -1
  5. package/dist/components/createComponent.js +14 -5
  6. package/dist/components/createComponent.js.map +1 -1
  7. package/dist/components/createElement.js +48 -16
  8. package/dist/components/createElement.js.map +1 -1
  9. package/dist/components/createNode.js +23 -10
  10. package/dist/components/createNode.js.map +1 -1
  11. package/dist/components/createNode.test.js +3 -3
  12. package/dist/components/createNode.test.js.map +1 -1
  13. package/dist/components/createSlot.js +2 -1
  14. package/dist/components/createSlot.js.map +1 -1
  15. package/dist/components/createText.js +7 -2
  16. package/dist/components/createText.js.map +1 -1
  17. package/dist/components/renderComponent.d.ts +4 -1
  18. package/dist/components/renderComponent.js +4 -2
  19. package/dist/components/renderComponent.js.map +1 -1
  20. package/dist/context/subscribeToContext.js +12 -2
  21. package/dist/context/subscribeToContext.js.map +1 -1
  22. package/dist/custom-element/ToddleComponent.js +12 -4
  23. package/dist/custom-element/ToddleComponent.js.map +1 -1
  24. package/dist/custom-element.main.esm.js +29 -29
  25. package/dist/custom-element.main.esm.js.map +4 -4
  26. package/dist/editor/editorUtils.d.ts +1 -0
  27. package/dist/editor/editorUtils.js +14 -0
  28. package/dist/editor/editorUtils.js.map +1 -0
  29. package/dist/editor/types.d.ts +4 -0
  30. package/dist/editor/types.js.map +1 -1
  31. package/dist/editor-preview.main.js +71 -18
  32. package/dist/editor-preview.main.js.map +1 -1
  33. package/dist/events/handleAction.js +62 -31
  34. package/dist/events/handleAction.js.map +1 -1
  35. package/dist/page.main.esm.js +3 -3
  36. package/dist/page.main.esm.js.map +4 -4
  37. package/dist/page.main.js +39 -8
  38. package/dist/page.main.js.map +1 -1
  39. package/dist/utils/createFormulaCache.js +1 -1
  40. package/dist/utils/createFormulaCache.js.map +1 -1
  41. package/dist/utils/nodes.d.ts +1 -0
  42. package/dist/utils/nodes.js +9 -0
  43. package/dist/utils/nodes.js.map +1 -1
  44. package/dist/utils/nodes.test.d.ts +1 -0
  45. package/dist/utils/nodes.test.js +192 -0
  46. package/dist/utils/nodes.test.js.map +1 -0
  47. package/dist/utils/subscribeCustomProperty.d.ts +1 -1
  48. package/dist/utils/subscribeCustomProperty.js +8 -4
  49. package/dist/utils/subscribeCustomProperty.js.map +1 -1
  50. package/dist/utils/subscribeCustomProperty.test.d.ts +1 -0
  51. package/dist/utils/subscribeCustomProperty.test.js +63 -0
  52. package/dist/utils/subscribeCustomProperty.test.js.map +1 -0
  53. package/package.json +3 -3
  54. package/src/api/createAPI.ts +90 -46
  55. package/src/api/createAPIv2.ts +67 -13
  56. package/src/components/createComponent.ts +85 -67
  57. package/src/components/createElement.ts +63 -27
  58. package/src/components/createNode.test.ts +3 -3
  59. package/src/components/createNode.ts +47 -22
  60. package/src/components/createSlot.ts +2 -1
  61. package/src/components/createText.ts +34 -18
  62. package/src/components/renderComponent.ts +8 -1
  63. package/src/context/subscribeToContext.ts +12 -2
  64. package/src/custom-element/ToddleComponent.ts +37 -22
  65. package/src/editor/editorUtils.ts +13 -0
  66. package/src/editor/types.ts +5 -0
  67. package/src/editor-preview.main.ts +125 -46
  68. package/src/events/handleAction.ts +173 -96
  69. package/src/page.main.ts +64 -26
  70. package/src/types.d.ts +3 -0
  71. package/src/utils/createFormulaCache.ts +3 -3
  72. package/src/utils/nodes.test.ts +246 -0
  73. package/src/utils/nodes.ts +11 -0
  74. package/src/utils/subscribeCustomProperty.test.ts +78 -0
  75. package/src/utils/subscribeCustomProperty.ts +21 -22
@@ -5,8 +5,11 @@ import type {
5
5
  SetMultiUrlParameterAction,
6
6
  SetURLParameterAction,
7
7
  } from '@nordcraft/core/dist/component/component.types'
8
- import { applyFormula } from '@nordcraft/core/dist/formula/formula'
9
- import { mapValues, omitKeys } from '@nordcraft/core/dist/utils/collections'
8
+ import {
9
+ applyFormula,
10
+ type FormulaContext,
11
+ } from '@nordcraft/core/dist/formula/formula'
12
+ import { mapObject, omitKeys } from '@nordcraft/core/dist/utils/collections'
10
13
  import { isDefined, toBoolean } from '@nordcraft/core/dist/utils/util'
11
14
  import fastDeepEqual from 'fast-deep-equal'
12
15
  import { isContextApiV2 } from '../api/apiUtils'
@@ -32,15 +35,21 @@ export function handleAction(
32
35
  const actionList =
33
36
  action.cases?.find(({ condition }) =>
34
37
  toBoolean(
35
- applyFormula(condition, {
36
- data,
37
- component: ctx.component,
38
- formulaCache: ctx.formulaCache,
39
- root: ctx.root,
40
- package: ctx.package,
41
- toddle: ctx.toddle,
42
- env: ctx.env,
43
- }),
38
+ applyFormula(
39
+ condition,
40
+ {
41
+ data,
42
+ component: ctx.component,
43
+ formulaCache: ctx.formulaCache,
44
+ root: ctx.root,
45
+ package: ctx.package,
46
+ toddle: ctx.toddle,
47
+ env: ctx.env,
48
+ jsonPath: ctx.jsonPath,
49
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
50
+ },
51
+ ['cases', 'condition'],
52
+ ),
44
53
  ),
45
54
  ) ?? action.default
46
55
  // handle all actions for the case
@@ -57,15 +66,21 @@ export function handleAction(
57
66
  break
58
67
  }
59
68
  case 'SetVariable': {
60
- const value = applyFormula(action.data, {
61
- data,
62
- component: ctx.component,
63
- formulaCache: ctx.formulaCache,
64
- root: ctx.root,
65
- package: ctx.package,
66
- toddle: ctx.toddle,
67
- env: ctx.env,
68
- })
69
+ const value = applyFormula(
70
+ action.data,
71
+ {
72
+ data,
73
+ component: ctx.component,
74
+ formulaCache: ctx.formulaCache,
75
+ root: ctx.root,
76
+ package: ctx.package,
77
+ toddle: ctx.toddle,
78
+ env: ctx.env,
79
+ jsonPath: ctx.jsonPath,
80
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
81
+ },
82
+ ['data'],
83
+ )
69
84
  ctx.dataSignal.update((data) => {
70
85
  return {
71
86
  ...data,
@@ -78,34 +93,28 @@ export function handleAction(
78
93
  break
79
94
  }
80
95
  case 'TriggerEvent': {
81
- const payload = applyFormula(action.data, {
82
- data,
83
- component: ctx.component,
84
- formulaCache: ctx.formulaCache,
85
- root: ctx.root,
86
- package: ctx.package,
87
- toddle: ctx.toddle,
88
- env: ctx.env,
89
- })
96
+ const payload = applyFormula(
97
+ action.data,
98
+ {
99
+ data,
100
+ component: ctx.component,
101
+ formulaCache: ctx.formulaCache,
102
+ root: ctx.root,
103
+ package: ctx.package,
104
+ toddle: ctx.toddle,
105
+ env: ctx.env,
106
+ jsonPath: ctx.jsonPath,
107
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
108
+ },
109
+ ['data'],
110
+ )
90
111
  ctx.triggerEvent(action.event, payload)
91
112
  break
92
113
  }
93
114
  case 'TriggerWorkflowCallback': {
94
- const payload = applyFormula(action.data, {
95
- data,
96
- component: ctx.component,
97
- formulaCache: ctx.formulaCache,
98
- root: ctx.root,
99
- package: ctx.package,
100
- toddle: ctx.toddle,
101
- env: ctx.env,
102
- })
103
- workflowCallback?.(action.event, payload)
104
- break
105
- }
106
- case 'SetURLParameter': {
107
- ctx.toddle.locationSignal.update((current) => {
108
- const value = applyFormula(action.data, {
115
+ const payload = applyFormula(
116
+ action.data,
117
+ {
109
118
  data,
110
119
  component: ctx.component,
111
120
  formulaCache: ctx.formulaCache,
@@ -113,7 +122,31 @@ export function handleAction(
113
122
  package: ctx.package,
114
123
  toddle: ctx.toddle,
115
124
  env: ctx.env,
116
- })
125
+ jsonPath: ctx.jsonPath,
126
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
127
+ },
128
+ ['data'],
129
+ )
130
+ workflowCallback?.(action.event, payload)
131
+ break
132
+ }
133
+ case 'SetURLParameter': {
134
+ ctx.toddle.locationSignal.update((current) => {
135
+ const value = applyFormula(
136
+ action.data,
137
+ {
138
+ data,
139
+ component: ctx.component,
140
+ formulaCache: ctx.formulaCache,
141
+ root: ctx.root,
142
+ package: ctx.package,
143
+ toddle: ctx.toddle,
144
+ env: ctx.env,
145
+ jsonPath: ctx.jsonPath,
146
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
147
+ },
148
+ ['data'],
149
+ )
117
150
  // historyMode was previously not declared explicitly, and we default
118
151
  // to push for state changes and replace for query changes
119
152
  let historyMode: SetURLParameterAction['historyMode'] | undefined
@@ -179,7 +212,7 @@ export function handleAction(
179
212
  let historyMode: SetMultiUrlParameterAction['historyMode'] = 'replace'
180
213
  const queryUpdates: Record<string, string> = {}
181
214
  const pathUpdates: Record<string, string> = {}
182
- const urlParameterCtx = {
215
+ const urlParameterCtx: FormulaContext = {
183
216
  data,
184
217
  component: ctx.component,
185
218
  formulaCache: ctx.formulaCache,
@@ -187,6 +220,8 @@ export function handleAction(
187
220
  package: ctx.package,
188
221
  toddle: ctx.toddle,
189
222
  env: ctx.env,
223
+ jsonPath: ctx.jsonPath,
224
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
190
225
  }
191
226
  // Only match on p.type === 'param'
192
227
  const isValidPathParameter = (param: string) =>
@@ -199,7 +234,11 @@ export function handleAction(
199
234
  )
200
235
 
201
236
  for (const [parameter, formula] of parameters) {
202
- const value = applyFormula(formula, urlParameterCtx) ?? null
237
+ const value =
238
+ applyFormula(formula, urlParameterCtx, [
239
+ 'parameters',
240
+ parameter,
241
+ ]) ?? null
203
242
  if (isValidPathParameter(parameter)) {
204
243
  historyMode = 'push'
205
244
  pathUpdates[parameter] = value as string
@@ -260,16 +299,26 @@ export function handleAction(
260
299
  if (isContextApiV2(api)) {
261
300
  // Evaluate potential inputs here to make sure the api have the right values
262
301
  // This is needed if the inputs are formulas referencing workflow parameters
263
- const actionInputs = mapValues(action.inputs ?? {}, (input) =>
264
- applyFormula(input.formula, {
265
- data,
266
- component: ctx.component,
267
- formulaCache: ctx.formulaCache,
268
- root: ctx.root,
269
- package: ctx.package,
270
- toddle: ctx.toddle,
271
- env: ctx.env,
272
- }),
302
+ const actionInputs = mapObject(
303
+ action.inputs ?? {},
304
+ ([key, input]) => [
305
+ key,
306
+ applyFormula(
307
+ input.formula,
308
+ {
309
+ data,
310
+ component: ctx.component,
311
+ formulaCache: ctx.formulaCache,
312
+ root: ctx.root,
313
+ package: ctx.package,
314
+ toddle: ctx.toddle,
315
+ env: ctx.env,
316
+ jsonPath: ctx.jsonPath,
317
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
318
+ },
319
+ ['inputs', key, 'formula'],
320
+ ),
321
+ ],
273
322
  )
274
323
  const actionModels = {
275
324
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -326,16 +375,26 @@ export function handleAction(
326
375
  break
327
376
  }
328
377
  case 'TriggerWorkflow': {
329
- const parameters = mapValues(action.parameters ?? {}, (parameter) =>
330
- applyFormula(parameter.formula, {
331
- data,
332
- component: ctx.component,
333
- formulaCache: ctx.formulaCache,
334
- root: ctx.root,
335
- package: ctx.package,
336
- toddle: ctx.toddle,
337
- env: ctx.env,
338
- }),
378
+ const parameters = mapObject(
379
+ action.parameters ?? {},
380
+ ([key, parameter]) => [
381
+ key,
382
+ applyFormula(
383
+ parameter.formula,
384
+ {
385
+ data,
386
+ component: ctx.component,
387
+ formulaCache: ctx.formulaCache,
388
+ root: ctx.root,
389
+ package: ctx.package,
390
+ toddle: ctx.toddle,
391
+ env: ctx.env,
392
+ jsonPath: ctx.jsonPath,
393
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
394
+ },
395
+ ['parameters', key, 'formula'],
396
+ ),
397
+ ],
339
398
  )
340
399
  const callbacks = action.callbacks
341
400
  if (action.contextProvider) {
@@ -455,19 +514,25 @@ export function handleAction(
455
514
  const args = (action.arguments ?? []).reduce<
456
515
  Record<string, unknown>
457
516
  >(
458
- (args, arg) =>
517
+ (args, arg, i) =>
459
518
  arg
460
519
  ? {
461
520
  ...args,
462
- [arg.name]: applyFormula(arg.formula, {
463
- data,
464
- component: ctx.component,
465
- formulaCache: ctx.formulaCache,
466
- root: ctx.root,
467
- package: ctx.package,
468
- toddle: ctx.toddle,
469
- env: ctx.env,
470
- }),
521
+ [arg.name]: applyFormula(
522
+ arg.formula,
523
+ {
524
+ data,
525
+ component: ctx.component,
526
+ formulaCache: ctx.formulaCache,
527
+ root: ctx.root,
528
+ package: ctx.package,
529
+ toddle: ctx.toddle,
530
+ env: ctx.env,
531
+ jsonPath: ctx.jsonPath,
532
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
533
+ },
534
+ ['arguments', i, 'formula'],
535
+ ),
471
536
  }
472
537
  : args,
473
538
  {},
@@ -511,26 +576,38 @@ export function handleAction(
511
576
  return
512
577
  }
513
578
  // First evaluate any arguments (input) to the action
514
- const args = action.arguments?.map((arg) =>
515
- applyFormula(arg?.formula, {
516
- data,
517
- component: ctx.component,
518
- formulaCache: ctx.formulaCache,
519
- root: ctx.root,
520
- package: ctx.package,
521
- toddle: ctx.toddle,
522
- env: ctx.env,
523
- }),
579
+ const args = action.arguments?.map((arg, i) =>
580
+ applyFormula(
581
+ arg?.formula,
582
+ {
583
+ data,
584
+ component: ctx.component,
585
+ formulaCache: ctx.formulaCache,
586
+ root: ctx.root,
587
+ package: ctx.package,
588
+ toddle: ctx.toddle,
589
+ env: ctx.env,
590
+ jsonPath: ctx.jsonPath,
591
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
592
+ },
593
+ ['arguments', i, 'formula'],
594
+ ),
524
595
  ) ?? [
525
- applyFormula(action.data, {
526
- data,
527
- component: ctx.component,
528
- formulaCache: ctx.formulaCache,
529
- root: ctx.root,
530
- package: ctx.package,
531
- toddle: ctx.toddle,
532
- env: ctx.env,
533
- }),
596
+ applyFormula(
597
+ action.data,
598
+ {
599
+ data,
600
+ component: ctx.component,
601
+ formulaCache: ctx.formulaCache,
602
+ root: ctx.root,
603
+ package: ctx.package,
604
+ toddle: ctx.toddle,
605
+ env: ctx.env,
606
+ jsonPath: ctx.jsonPath,
607
+ reportFormulaEvaluation: ctx.reportFormulaEvaluation,
608
+ },
609
+ ['arguments', 'data'],
610
+ ),
534
611
  ] // action.data is a fallback to handle an older version of the action spec.
535
612
  return legacyHandler(args, { ...ctx, triggerActionEvent }, event)
536
613
  }
package/src/page.main.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isLegacyApi, sortApiObjects } from '@nordcraft/core/dist/api/api'
2
+ import type { ComponentAPI } from '@nordcraft/core/dist/api/apiTypes'
2
3
  import type {
3
4
  Component,
4
5
  ComponentData,
@@ -168,15 +169,20 @@ export const createRoot = (domNode: HTMLElement) => {
168
169
  // state (e.g. localStorage, sensors etc.)
169
170
  Variables: mapObject(component.variables ?? {}, ([name, variable]) => [
170
171
  name,
171
- applyFormula(variable.initialValue, {
172
- data: window.toddle.pageState,
173
- component,
174
- formulaCache: {},
175
- root: document,
176
- package: undefined,
177
- toddle: window.toddle,
178
- env,
179
- }),
172
+ applyFormula(
173
+ variable.initialValue,
174
+ {
175
+ data: window.toddle.pageState,
176
+ component,
177
+ formulaCache: {},
178
+ root: document,
179
+ package: undefined,
180
+ toddle: window.toddle,
181
+ env,
182
+ jsonPath: [],
183
+ },
184
+ ['variables', name],
185
+ ),
180
186
  ]),
181
187
  })
182
188
 
@@ -217,22 +223,31 @@ export const createRoot = (domNode: HTMLElement) => {
217
223
  console.info('EVENT FIRED', event, data),
218
224
  package: undefined,
219
225
  env,
226
+ jsonPath: [],
220
227
  }
221
228
 
222
229
  // Note: this function must run procedurally to ensure apis (which are in correct order) can reference each other
223
- sortApiObjects(Object.entries(component.apis ?? {})).forEach(
224
- ([name, api]) => {
225
- if (isLegacyApi(api)) {
226
- ctx.apis[name] = createLegacyAPI(api, ctx)
227
- } else {
228
- ctx.apis[name] = createAPI({
229
- apiRequest: api,
230
- ctx,
231
- componentData: dataSignal.get(),
232
- })
233
- }
234
- },
235
- )
230
+ sortApiObjects(
231
+ Object.entries(component.apis ?? {}).filter(
232
+ (entry): entry is [string, ComponentAPI] => isDefined(entry[1]),
233
+ ),
234
+ ).forEach(([name, api]) => {
235
+ if (isLegacyApi(api)) {
236
+ ctx.apis[name] = createLegacyAPI(api, {
237
+ ...ctx,
238
+ jsonPath: ['apis', name],
239
+ })
240
+ } else {
241
+ ctx.apis[name] = createAPI({
242
+ apiRequest: api,
243
+ ctx: {
244
+ ...ctx,
245
+ jsonPath: ['apis', name],
246
+ },
247
+ componentData: dataSignal.get(),
248
+ })
249
+ }
250
+ })
236
251
  // Trigger actions for all APIs after all of them are created.
237
252
  Object.values(ctx.apis)
238
253
  .filter(isContextApiV2)
@@ -375,7 +390,13 @@ const setupMetaUpdates = (
375
390
  if (dynamicLang) {
376
391
  dataSignal
377
392
  .map((data) =>
378
- component ? applyFormula(langFormula, getFormulaContext(data)) : null,
393
+ component
394
+ ? applyFormula(langFormula, getFormulaContext(data), [
395
+ 'route',
396
+ 'info',
397
+ 'language',
398
+ ])
399
+ : null,
379
400
  )
380
401
  .subscribe((newLang) => {
381
402
  if (isDefined(newLang) && document.documentElement.lang !== newLang) {
@@ -390,7 +411,13 @@ const setupMetaUpdates = (
390
411
  if (dynamicTitle) {
391
412
  dataSignal
392
413
  .map((data) =>
393
- component ? applyFormula(titleFormula, getFormulaContext(data)) : null,
414
+ component
415
+ ? applyFormula(titleFormula, getFormulaContext(data), [
416
+ 'route',
417
+ 'info',
418
+ 'title',
419
+ ])
420
+ : null,
394
421
  )
395
422
  .subscribe((newTitle) => {
396
423
  if (isDefined(newTitle) && document.title !== newTitle) {
@@ -455,7 +482,11 @@ const setupMetaUpdates = (
455
482
  dataSignal
456
483
  .map((data) =>
457
484
  component
458
- ? applyFormula(descriptionFormula, getFormulaContext(data))
485
+ ? applyFormula(descriptionFormula, getFormulaContext(data), [
486
+ 'route',
487
+ 'info',
488
+ 'description',
489
+ ])
459
490
  : null,
460
491
  )
461
492
  .subscribe((newDescription) => {
@@ -511,7 +542,14 @@ const setupMetaUpdates = (
511
542
  component
512
543
  ? {
513
544
  ...agg,
514
- [key]: applyFormula(formula, context),
545
+ [key]: applyFormula(formula, context, [
546
+ 'route',
547
+ 'info',
548
+ 'meta',
549
+ id,
550
+ 'attrs',
551
+ key,
552
+ ]),
515
553
  }
516
554
  : agg,
517
555
  {},
package/src/types.d.ts CHANGED
@@ -9,6 +9,7 @@ import type {
9
9
  ToddleEnv,
10
10
  ValueOperationValue,
11
11
  } from '@nordcraft/core/dist/formula/formula'
12
+ import type { FormulaEvaluationReporter } from '@nordcraft/core/dist/formula/formulaTypes'
12
13
  import type {
13
14
  Toddle as NewToddle,
14
15
  Toddle,
@@ -84,6 +85,8 @@ export interface ComponentContext {
84
85
  theme: Signal<string | null>
85
86
  }
86
87
  toddle: Toddle<LocationSignal, PreviewShowSignal>
88
+ jsonPath: Array<string | number> | undefined
89
+ reportFormulaEvaluation?: FormulaEvaluationReporter | undefined
87
90
  env: ToddleEnv
88
91
  }
89
92
 
@@ -48,7 +48,7 @@ export function createFormulaCache(component: Component): FormulaCache {
48
48
  }
49
49
 
50
50
  function getFormulaCacheConfig(formula: Formula, component: Component) {
51
- const paths: string[][] = []
51
+ const paths: Array<string | number>[] = []
52
52
  function visitOperation(op: Formula) {
53
53
  if (!op) {
54
54
  return
@@ -57,7 +57,7 @@ function getFormulaCacheConfig(formula: Formula, component: Component) {
57
57
  paths.push(op.path)
58
58
  }
59
59
  if (Array.isArray((op as any)?.arguments)) {
60
- ;(op as FunctionOperation)?.arguments.forEach((arg) =>
60
+ ;(op as FunctionOperation)?.arguments?.forEach((arg) =>
61
61
  visitOperation(arg.formula),
62
62
  )
63
63
  }
@@ -81,7 +81,7 @@ function getFormulaCacheConfig(formula: Formula, component: Component) {
81
81
  }
82
82
  }
83
83
 
84
- const keys: string[][] = []
84
+ const keys: Array<string | number>[] = []
85
85
  paths
86
86
  .sort((a, b) => a.length - b.length)
87
87
  .forEach((path) => {