@nordcraft/core 1.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 (119) hide show
  1. package/README.md +5 -0
  2. package/dist/api/LegacyToddleApi.d.ts +34 -0
  3. package/dist/api/LegacyToddleApi.js +178 -0
  4. package/dist/api/LegacyToddleApi.js.map +1 -0
  5. package/dist/api/ToddleApiV2.d.ts +77 -0
  6. package/dist/api/ToddleApiV2.js +271 -0
  7. package/dist/api/ToddleApiV2.js.map +1 -0
  8. package/dist/api/api.d.ts +49 -0
  9. package/dist/api/api.js +276 -0
  10. package/dist/api/api.js.map +1 -0
  11. package/dist/api/apiTypes.d.ts +125 -0
  12. package/dist/api/apiTypes.js +11 -0
  13. package/dist/api/apiTypes.js.map +1 -0
  14. package/dist/api/headers.d.ts +10 -0
  15. package/dist/api/headers.js +36 -0
  16. package/dist/api/headers.js.map +1 -0
  17. package/dist/api/template.d.ts +5 -0
  18. package/dist/api/template.js +7 -0
  19. package/dist/api/template.js.map +1 -0
  20. package/dist/component/ToddleComponent.d.ts +45 -0
  21. package/dist/component/ToddleComponent.js +373 -0
  22. package/dist/component/ToddleComponent.js.map +1 -0
  23. package/dist/component/actionUtils.d.ts +2 -0
  24. package/dist/component/actionUtils.js +56 -0
  25. package/dist/component/actionUtils.js.map +1 -0
  26. package/dist/component/component.types.d.ts +313 -0
  27. package/dist/component/component.types.js +9 -0
  28. package/dist/component/component.types.js.map +1 -0
  29. package/dist/component/isPageComponent.d.ts +2 -0
  30. package/dist/component/isPageComponent.js +3 -0
  31. package/dist/component/isPageComponent.js.map +1 -0
  32. package/dist/formula/ToddleFormula.d.ts +15 -0
  33. package/dist/formula/ToddleFormula.js +20 -0
  34. package/dist/formula/ToddleFormula.js.map +1 -0
  35. package/dist/formula/formula.d.ts +103 -0
  36. package/dist/formula/formula.js +186 -0
  37. package/dist/formula/formula.js.map +1 -0
  38. package/dist/formula/formulaTypes.d.ts +30 -0
  39. package/dist/formula/formulaTypes.js +2 -0
  40. package/dist/formula/formulaTypes.js.map +1 -0
  41. package/dist/formula/formulaUtils.d.ts +18 -0
  42. package/dist/formula/formulaUtils.js +240 -0
  43. package/dist/formula/formulaUtils.js.map +1 -0
  44. package/dist/styling/className.d.ts +2 -0
  45. package/dist/styling/className.js +52 -0
  46. package/dist/styling/className.js.map +1 -0
  47. package/dist/styling/style.css.d.ts +5 -0
  48. package/dist/styling/style.css.js +242 -0
  49. package/dist/styling/style.css.js.map +1 -0
  50. package/dist/styling/theme.const.d.ts +3 -0
  51. package/dist/styling/theme.const.js +381 -0
  52. package/dist/styling/theme.const.js.map +1 -0
  53. package/dist/styling/theme.d.ts +72 -0
  54. package/dist/styling/theme.js +200 -0
  55. package/dist/styling/theme.js.map +1 -0
  56. package/dist/styling/variantSelector.d.ts +123 -0
  57. package/dist/styling/variantSelector.js +25 -0
  58. package/dist/styling/variantSelector.js.map +1 -0
  59. package/dist/types.d.ts +97 -0
  60. package/dist/types.js +2 -0
  61. package/dist/types.js.map +1 -0
  62. package/dist/utils/collections.d.ts +21 -0
  63. package/dist/utils/collections.js +75 -0
  64. package/dist/utils/collections.js.map +1 -0
  65. package/dist/utils/customElements.d.ts +6 -0
  66. package/dist/utils/customElements.js +15 -0
  67. package/dist/utils/customElements.js.map +1 -0
  68. package/dist/utils/hash.d.ts +1 -0
  69. package/dist/utils/hash.js +17 -0
  70. package/dist/utils/hash.js.map +1 -0
  71. package/dist/utils/json.d.ts +5 -0
  72. package/dist/utils/json.js +16 -0
  73. package/dist/utils/json.js.map +1 -0
  74. package/dist/utils/sha1.d.ts +2 -0
  75. package/dist/utils/sha1.js +13 -0
  76. package/dist/utils/sha1.js.map +1 -0
  77. package/dist/utils/url.d.ts +5 -0
  78. package/dist/utils/url.js +27 -0
  79. package/dist/utils/url.js.map +1 -0
  80. package/dist/utils/util.d.ts +3 -0
  81. package/dist/utils/util.js +4 -0
  82. package/dist/utils/util.js.map +1 -0
  83. package/package.json +18 -0
  84. package/src/api/LegacyToddleApi.ts +205 -0
  85. package/src/api/ToddleApiV2.ts +331 -0
  86. package/src/api/api.test.ts +319 -0
  87. package/src/api/api.ts +414 -0
  88. package/src/api/apiTypes.ts +145 -0
  89. package/src/api/headers.ts +41 -0
  90. package/src/api/template.ts +10 -0
  91. package/src/component/ToddleComponent.actionReferences.test.ts +75 -0
  92. package/src/component/ToddleComponent.formulasInComponent.test.ts +234 -0
  93. package/src/component/ToddleComponent.ts +470 -0
  94. package/src/component/actionUtils.ts +61 -0
  95. package/src/component/component.types.ts +362 -0
  96. package/src/component/isPageComponent.ts +6 -0
  97. package/src/formula/ToddleFormula.ts +30 -0
  98. package/src/formula/formula.ts +355 -0
  99. package/src/formula/formulaTypes.ts +45 -0
  100. package/src/formula/formulaUtils.ts +287 -0
  101. package/src/styling/className.test.ts +19 -0
  102. package/src/styling/className.ts +73 -0
  103. package/src/styling/style.css.ts +309 -0
  104. package/src/styling/theme.const.ts +390 -0
  105. package/src/styling/theme.ts +339 -0
  106. package/src/styling/variantSelector.ts +168 -0
  107. package/src/types.ts +158 -0
  108. package/src/utils/collections.test.ts +57 -0
  109. package/src/utils/collections.ts +122 -0
  110. package/src/utils/customElements.test.ts +40 -0
  111. package/src/utils/customElements.ts +16 -0
  112. package/src/utils/hash.test.ts +32 -0
  113. package/src/utils/hash.ts +18 -0
  114. package/src/utils/json.ts +18 -0
  115. package/src/utils/sha1.test.ts +50 -0
  116. package/src/utils/sha1.ts +17 -0
  117. package/src/utils/url.test.ts +17 -0
  118. package/src/utils/url.ts +33 -0
  119. package/src/utils/util.ts +8 -0
@@ -0,0 +1,234 @@
1
+ import { functionFormula, valueFormula } from '../formula/formulaUtils'
2
+ import { ToddleComponent } from './ToddleComponent'
3
+
4
+ describe('ToddleComponent.formulasInComponent', () => {
5
+ test('it return formulas used in parameters of TriggerWorkflow actions', () => {
6
+ const demo = new ToddleComponent({
7
+ component: {
8
+ name: 'demo',
9
+ apis: {},
10
+ attributes: {},
11
+ nodes: {},
12
+ variables: {},
13
+ workflows: {
14
+ '7XLoA3': {
15
+ name: 'my-workflow',
16
+ actions: [
17
+ {
18
+ type: 'TriggerWorkflow',
19
+ workflow: 'jm3yUN',
20
+ parameters: {
21
+ 'param-1': {
22
+ formula: {
23
+ name: 'scrollPosition',
24
+ type: 'function',
25
+ arguments: [],
26
+ },
27
+ },
28
+ },
29
+ },
30
+ ],
31
+ parameters: [],
32
+ },
33
+ },
34
+ },
35
+ getComponent: () => undefined,
36
+ packageName: 'demo',
37
+ globalFormulas: { formulas: {}, packages: {} },
38
+ })
39
+ const formulas = Array.from(demo.formulasInComponent()).map(
40
+ ([, formula]) => formula,
41
+ )
42
+ expect(formulas).toContainEqual({
43
+ name: 'scrollPosition',
44
+ type: 'function',
45
+ arguments: [],
46
+ })
47
+ })
48
+ test('it returns formulas used in APIs', () => {
49
+ const demo = new ToddleComponent({
50
+ component: {
51
+ name: 'demo',
52
+ apis: {
53
+ legacyApi: {
54
+ name: 'legacyAPI',
55
+ type: 'REST',
56
+ url: valueFormula('https://api.example.com'),
57
+ autoFetch: {
58
+ type: 'function',
59
+ name: 'customFunction',
60
+ arguments: [],
61
+ },
62
+ onCompleted: null,
63
+ onFailed: null,
64
+ },
65
+ v2Api: {
66
+ name: 'v2API',
67
+ type: 'http',
68
+ version: 2,
69
+ url: valueFormula('https://api.example.com'),
70
+ autoFetch: {
71
+ type: 'function',
72
+ name: 'otherFunction',
73
+ arguments: [],
74
+ },
75
+ client: {
76
+ parserMode: 'auto',
77
+ onCompleted: null,
78
+ onFailed: null,
79
+ },
80
+ inputs: {},
81
+ },
82
+ },
83
+ attributes: {},
84
+ nodes: {},
85
+ variables: {},
86
+ workflows: {},
87
+ },
88
+ getComponent: () => undefined,
89
+ packageName: undefined,
90
+ globalFormulas: { formulas: {}, packages: {} },
91
+ })
92
+ const formulas = Array.from(demo.formulasInComponent()).map(
93
+ ([, formula]) => formula,
94
+ )
95
+ expect(formulas).toContainEqual({
96
+ name: 'customFunction',
97
+ type: 'function',
98
+ arguments: [],
99
+ })
100
+ expect(formulas).toContainEqual({
101
+ name: 'otherFunction',
102
+ type: 'function',
103
+ arguments: [],
104
+ })
105
+ })
106
+ test('it returns global formulas', () => {
107
+ const demo = new ToddleComponent({
108
+ component: {
109
+ name: 'demo',
110
+ apis: {},
111
+ attributes: {},
112
+ nodes: {},
113
+ variables: {
114
+ x: {
115
+ initialValue: functionFormula('globalFunction'),
116
+ },
117
+ },
118
+ workflows: {},
119
+ },
120
+ getComponent: () => undefined,
121
+ packageName: undefined,
122
+ globalFormulas: {
123
+ formulas: {
124
+ globalFunction: {
125
+ name: 'globalFunction',
126
+ arguments: [],
127
+ formula: valueFormula(4),
128
+ },
129
+ },
130
+ packages: {},
131
+ },
132
+ })
133
+ const formulas = Array.from(demo.formulasInComponent()).map(
134
+ ([, formula]) => formula,
135
+ )
136
+ expect(formulas).toContainEqual({
137
+ name: 'globalFunction',
138
+ type: 'function',
139
+ arguments: [],
140
+ })
141
+ })
142
+ test('it returns global formulas that are referenced through global formulas', () => {
143
+ const demo = new ToddleComponent({
144
+ component: {
145
+ name: 'demo',
146
+ apis: {},
147
+ attributes: {},
148
+ nodes: {},
149
+ variables: {
150
+ x: {
151
+ initialValue: functionFormula('globalFunction'),
152
+ },
153
+ },
154
+ workflows: {},
155
+ },
156
+ getComponent: () => undefined,
157
+ packageName: undefined,
158
+ globalFormulas: {
159
+ formulas: {
160
+ globalFunction: {
161
+ name: 'globalFunction',
162
+ arguments: [],
163
+ formula: functionFormula('otherGlobalFunction'),
164
+ },
165
+ otherGlobalFunction: {
166
+ name: 'otherGlobalFunction',
167
+ arguments: [],
168
+ formula: valueFormula(4),
169
+ },
170
+ },
171
+ packages: {},
172
+ },
173
+ })
174
+ const formulas = Array.from(demo.formulasInComponent()).map(
175
+ ([, formula]) => formula,
176
+ )
177
+ expect(formulas).toContainEqual({
178
+ name: 'globalFunction',
179
+ type: 'function',
180
+ arguments: [],
181
+ })
182
+ expect(formulas).toContainEqual({
183
+ name: 'otherGlobalFunction',
184
+ type: 'function',
185
+ arguments: [],
186
+ })
187
+ })
188
+ test("it stops following function refs after they've been visited once (no infinite loops)", () => {
189
+ const demo = new ToddleComponent({
190
+ component: {
191
+ name: 'demo',
192
+ apis: {},
193
+ attributes: {},
194
+ nodes: {},
195
+ variables: {
196
+ x: {
197
+ initialValue: functionFormula('globalFunction'),
198
+ },
199
+ },
200
+ workflows: {},
201
+ },
202
+ getComponent: () => undefined,
203
+ packageName: undefined,
204
+ globalFormulas: {
205
+ formulas: {
206
+ globalFunction: {
207
+ name: 'globalFunction',
208
+ arguments: [],
209
+ formula: functionFormula('otherGlobalFunction'),
210
+ },
211
+ otherGlobalFunction: {
212
+ name: 'otherGlobalFunction',
213
+ arguments: [],
214
+ formula: functionFormula('globalFunction'),
215
+ },
216
+ },
217
+ packages: {},
218
+ },
219
+ })
220
+ const formulas = Array.from(demo.formulasInComponent()).map(
221
+ ([, formula]) => formula,
222
+ )
223
+ expect(formulas).toContainEqual({
224
+ name: 'globalFunction',
225
+ type: 'function',
226
+ arguments: [],
227
+ })
228
+ expect(formulas).toContainEqual({
229
+ name: 'otherGlobalFunction',
230
+ type: 'function',
231
+ arguments: [],
232
+ })
233
+ })
234
+ })
@@ -0,0 +1,470 @@
1
+ import { isLegacyApi } from '../api/api'
2
+ import { LegacyToddleApi } from '../api/LegacyToddleApi'
3
+ import { ToddleApiV2 } from '../api/ToddleApiV2'
4
+ import type { Formula, FunctionOperation } from '../formula/formula'
5
+ import type { GlobalFormulas } from '../formula/formulaTypes'
6
+ import {
7
+ getFormulasInAction,
8
+ getFormulasInFormula,
9
+ } from '../formula/formulaUtils'
10
+ import { isDefined } from '../utils/util'
11
+ import { getActionsInAction } from './actionUtils'
12
+ import type {
13
+ ActionModel,
14
+ Component,
15
+ CustomActionModel,
16
+ NodeModel,
17
+ } from './component.types'
18
+ import { isPageComponent } from './isPageComponent'
19
+
20
+ export class ToddleComponent<Handler> {
21
+ private component: Component
22
+ private globalFormulas: GlobalFormulas<Handler>
23
+ private getComponent: (
24
+ name: string,
25
+ packageName?: string,
26
+ ) => Component | undefined
27
+ public packageName?: string
28
+
29
+ constructor({
30
+ component,
31
+ getComponent,
32
+ packageName,
33
+ globalFormulas,
34
+ }: {
35
+ component: Component
36
+ getComponent: (name: string, packageName?: string) => Component | undefined
37
+ packageName: string | undefined
38
+ globalFormulas: GlobalFormulas<Handler>
39
+ }) {
40
+ this.component = component
41
+ this.getComponent = getComponent
42
+ this.packageName = packageName
43
+ this.globalFormulas = globalFormulas
44
+ }
45
+
46
+ get uniqueSubComponents(): ToddleComponent<Handler>[] {
47
+ const components = new Map<string, ToddleComponent<Handler>>()
48
+
49
+ const visitNode = (packageName?: string) => (node: NodeModel) => {
50
+ if (node.type !== 'component') {
51
+ return
52
+ }
53
+ if (components.has(node.name)) {
54
+ return
55
+ }
56
+ const component = this.getComponent(
57
+ node.name,
58
+ node.package ?? packageName,
59
+ )
60
+ if (!component) {
61
+ return
62
+ }
63
+ components.set(
64
+ component.name,
65
+ new ToddleComponent({
66
+ component,
67
+ getComponent: this.getComponent,
68
+ packageName: node.package ?? packageName,
69
+ globalFormulas: this.globalFormulas,
70
+ }),
71
+ )
72
+ Object.values(component.nodes).forEach(
73
+ visitNode(node.package ?? packageName),
74
+ )
75
+ }
76
+ Object.values(this.nodes).forEach(visitNode())
77
+ return [...components.values()]
78
+ }
79
+
80
+ get formulaReferences() {
81
+ return new Set(
82
+ Array.from(this.formulasInComponent())
83
+ .filter(
84
+ (entry): entry is [(string | number)[], FunctionOperation] =>
85
+ entry[1].type === 'function',
86
+ )
87
+ .map(([, f]) => [f.package, f.name].filter(isDefined).join('/')),
88
+ )
89
+ }
90
+
91
+ get actionReferences(): Set<string> {
92
+ return new Set(
93
+ Array.from(this.actionModelsInComponent())
94
+ .filter(
95
+ (entry): entry is [(string | number)[], CustomActionModel] =>
96
+ entry[1].type === 'Custom' || entry[1].type === undefined,
97
+ )
98
+ .map(([, a]) => [a.package, a.name].filter(isDefined).join('/')),
99
+ )
100
+ }
101
+
102
+ /**
103
+ * Traverse all formulas in the component.
104
+ * @returns An iterable that yields the path and formula.
105
+ */
106
+ *formulasInComponent(): Generator<[(string | number)[], Formula]> {
107
+ const globalFormulas = this.globalFormulas
108
+ function* visitNode(
109
+ node: NodeModel,
110
+ path: (string | number)[] = [],
111
+ ): Generator<[(string | number)[], Formula]> {
112
+ switch (node.type) {
113
+ case 'text':
114
+ yield* getFormulasInFormula({
115
+ formula: node.condition,
116
+ globalFormulas,
117
+ path: [...path, 'condition'],
118
+ })
119
+ yield* getFormulasInFormula({
120
+ formula: node.repeat,
121
+ globalFormulas,
122
+ path: [...path, 'repeat'],
123
+ })
124
+ yield* getFormulasInFormula({
125
+ formula: node.repeatKey,
126
+ globalFormulas,
127
+ path: [...path, 'repeatKey'],
128
+ })
129
+ yield* getFormulasInFormula({
130
+ formula: node.value,
131
+ globalFormulas,
132
+ path: [...path, 'value'],
133
+ })
134
+ break
135
+ case 'slot':
136
+ yield* getFormulasInFormula({
137
+ formula: node.condition,
138
+ globalFormulas,
139
+ path: [...path, 'condition'],
140
+ })
141
+ break
142
+ case 'component':
143
+ yield* getFormulasInFormula({
144
+ formula: node.condition,
145
+ globalFormulas,
146
+ path: [...path, 'condition'],
147
+ })
148
+ yield* getFormulasInFormula({
149
+ formula: node.repeat,
150
+ globalFormulas,
151
+ path: [...path, 'repeat'],
152
+ })
153
+ yield* getFormulasInFormula({
154
+ formula: node.repeatKey,
155
+ globalFormulas,
156
+ path: [...path, 'repeatKey'],
157
+ })
158
+ for (const [attrKey, attr] of Object.entries(node.attrs ?? {})) {
159
+ yield* getFormulasInFormula({
160
+ formula: attr,
161
+ globalFormulas,
162
+ path: [...path, 'attrs', attrKey],
163
+ })
164
+ }
165
+ for (const [eventKey, event] of Object.entries(node.events ?? {})) {
166
+ for (const [actionKey, action] of Object.entries(
167
+ event?.actions ?? {},
168
+ )) {
169
+ yield* getFormulasInAction({
170
+ action,
171
+ globalFormulas,
172
+ path: [...path, 'events', eventKey, 'actions', actionKey],
173
+ })
174
+ }
175
+ }
176
+ break
177
+ case 'element':
178
+ yield* getFormulasInFormula({
179
+ formula: node.condition,
180
+ globalFormulas,
181
+ path: [...path, 'condition'],
182
+ })
183
+ yield* getFormulasInFormula({
184
+ formula: node.repeat,
185
+ globalFormulas,
186
+ path: [...path, 'repeat'],
187
+ })
188
+ yield* getFormulasInFormula({
189
+ formula: node.repeatKey,
190
+ globalFormulas,
191
+ path: [...path, 'repeatKey'],
192
+ })
193
+ for (const [attrKey, attr] of Object.entries(node.attrs ?? {})) {
194
+ yield* getFormulasInFormula({
195
+ formula: attr,
196
+ globalFormulas,
197
+ path: [...path, 'attrs', attrKey],
198
+ })
199
+ }
200
+ for (const [eventKey, event] of Object.entries(node.events ?? {})) {
201
+ for (const [actionKey, a] of Object.entries(event?.actions ?? {})) {
202
+ yield* getFormulasInAction({
203
+ action: a,
204
+ globalFormulas,
205
+ path: [...path, 'events', eventKey, 'actions', actionKey],
206
+ })
207
+ }
208
+ }
209
+ for (const [classKey, c] of Object.entries(node.classes ?? {})) {
210
+ yield* getFormulasInFormula({
211
+ formula: c.formula,
212
+ globalFormulas,
213
+ path: [...path, 'classes', classKey, 'formula'],
214
+ })
215
+ }
216
+
217
+ for (const [styleVariableKey, styleVariable] of Object.entries(
218
+ node['style-variables'] ?? {},
219
+ )) {
220
+ yield* getFormulasInFormula({
221
+ formula: styleVariable.formula,
222
+ globalFormulas,
223
+ path: [...path, 'style-variables', styleVariableKey, 'formula'],
224
+ })
225
+ }
226
+ break
227
+ }
228
+ }
229
+
230
+ yield* getFormulasInFormula({
231
+ formula: this.route?.info?.language?.formula,
232
+ globalFormulas,
233
+ path: ['route', 'info', 'language', 'formula'],
234
+ })
235
+ yield* getFormulasInFormula({
236
+ formula: this.route?.info?.title?.formula,
237
+ globalFormulas,
238
+ path: ['route', 'info', 'title', 'formula'],
239
+ })
240
+ yield* getFormulasInFormula({
241
+ formula: this.route?.info?.description?.formula,
242
+ globalFormulas,
243
+ path: ['route', 'info', 'description', 'formula'],
244
+ })
245
+ yield* getFormulasInFormula({
246
+ formula: this.route?.info?.icon?.formula,
247
+ globalFormulas,
248
+ path: ['route', 'info', 'icon', 'formula'],
249
+ })
250
+ yield* getFormulasInFormula({
251
+ formula: this.route?.info?.charset?.formula,
252
+ globalFormulas,
253
+ path: ['route', 'info', 'charset', 'formula'],
254
+ })
255
+ for (const [metaKey, meta] of Object.entries(
256
+ this.route?.info?.meta ?? {},
257
+ )) {
258
+ yield* getFormulasInFormula({
259
+ formula: meta.content,
260
+ globalFormulas,
261
+ path: ['route', 'info', 'meta', metaKey, 'content'],
262
+ })
263
+ for (const [attrKey, a] of Object.entries(meta.attrs)) {
264
+ yield* getFormulasInFormula({
265
+ formula: a,
266
+ globalFormulas,
267
+ path: ['route', 'info', 'meta', metaKey, 'attrs', attrKey],
268
+ })
269
+ }
270
+ }
271
+ for (const [formulaKey, formula] of Object.entries(this.formulas ?? {})) {
272
+ yield* getFormulasInFormula({
273
+ formula: formula.formula,
274
+ globalFormulas,
275
+ path: ['formulas', formulaKey, 'formula'],
276
+ })
277
+ }
278
+ for (const [variableKey, variable] of Object.entries(
279
+ this.variables ?? {},
280
+ )) {
281
+ yield* getFormulasInFormula({
282
+ formula: variable.initialValue,
283
+ globalFormulas,
284
+ path: ['variables', variableKey, 'initialValue'],
285
+ })
286
+ }
287
+ for (const [workflowKey, workflow] of Object.entries(
288
+ this.workflows ?? {},
289
+ )) {
290
+ for (const [actionKey, action] of workflow.actions.entries()) {
291
+ yield* getFormulasInAction({
292
+ action,
293
+ globalFormulas,
294
+ path: ['workflows', workflowKey, 'actions', actionKey],
295
+ })
296
+ }
297
+ }
298
+ for (const [, api] of Object.entries(this.apis)) {
299
+ yield* api.formulasInApi()
300
+ }
301
+ for (const [actionKey, action] of Object.entries(
302
+ this.component.onLoad?.actions ?? {},
303
+ )) {
304
+ yield* getFormulasInAction({
305
+ action,
306
+ globalFormulas,
307
+ path: ['onLoad', 'actions', actionKey],
308
+ })
309
+ }
310
+ for (const [actionKey, action] of Object.entries(
311
+ this.component.onAttributeChange?.actions ?? {},
312
+ )) {
313
+ yield* getFormulasInAction({
314
+ action,
315
+ globalFormulas,
316
+ path: ['onAttributeChange', 'actions', actionKey],
317
+ })
318
+ }
319
+ for (const [nodeKey, node] of Object.entries(this.nodes ?? {})) {
320
+ yield* visitNode(node, ['nodes', nodeKey])
321
+ }
322
+ }
323
+
324
+ /**
325
+ * Traverse all actions in the component.
326
+ * @returns An iterable that yields the path and action.
327
+ */
328
+ *actionModelsInComponent(): Generator<[(string | number)[], ActionModel]> {
329
+ function* visitNode(
330
+ node: NodeModel,
331
+ path: (string | number)[] = [],
332
+ ): Generator<[(string | number)[], ActionModel]> {
333
+ switch (node.type) {
334
+ case 'text':
335
+ case 'slot':
336
+ break
337
+ case 'component':
338
+ case 'element':
339
+ for (const [eventKey, event] of Object.entries(node.events ?? {})) {
340
+ for (const [actionKey, a] of Object.entries(event?.actions ?? {})) {
341
+ yield* getActionsInAction(a, [
342
+ ...path,
343
+ 'events',
344
+ eventKey,
345
+ 'actions',
346
+ actionKey,
347
+ ])
348
+ }
349
+ }
350
+ break
351
+ }
352
+ }
353
+
354
+ for (const [workflowKey, workflow] of Object.entries(
355
+ this.workflows ?? {},
356
+ )) {
357
+ for (const [key, a] of Object.entries(workflow?.actions ?? {})) {
358
+ yield* getActionsInAction(a, ['workflows', workflowKey, 'actions', key])
359
+ }
360
+ }
361
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
362
+ for (const [apiKey, api] of Object.entries(this.apis ?? {})) {
363
+ if (!isLegacyApi(api)) {
364
+ yield* api.actionModelsInApi()
365
+ continue
366
+ }
367
+
368
+ // Legacy API
369
+ for (const [actionKey, a] of Object.entries(
370
+ api.onCompleted?.actions ?? {},
371
+ )) {
372
+ yield* getActionsInAction(a, [
373
+ 'apis',
374
+ apiKey,
375
+ 'onCompleted',
376
+ 'actions',
377
+ actionKey,
378
+ ])
379
+ }
380
+ for (const [actionKey, a] of Object.entries(
381
+ api.onFailed?.actions ?? {},
382
+ )) {
383
+ yield* getActionsInAction(a, [
384
+ 'apis',
385
+ apiKey,
386
+ 'onFailed',
387
+ 'actions',
388
+ actionKey,
389
+ ])
390
+ }
391
+ }
392
+ for (const [actionKey, action] of Object.entries(
393
+ this.component.onLoad?.actions ?? {},
394
+ )) {
395
+ yield* getActionsInAction(action, ['onLoad', 'actions', actionKey])
396
+ }
397
+ for (const [actionKey, action] of Object.entries(
398
+ this.component.onAttributeChange?.actions ?? {},
399
+ )) {
400
+ yield* getActionsInAction(action, [
401
+ 'onAttributeChange',
402
+ 'actions',
403
+ actionKey,
404
+ ])
405
+ }
406
+ for (const [nodeKey, node] of Object.entries(this.nodes ?? {})) {
407
+ yield* visitNode(node, ['nodes', nodeKey])
408
+ }
409
+ }
410
+
411
+ get formulas() {
412
+ return this.component.formulas
413
+ }
414
+
415
+ get name() {
416
+ return this.component.name
417
+ }
418
+
419
+ get route() {
420
+ return this.component.route
421
+ }
422
+
423
+ get attributes() {
424
+ return this.component.attributes
425
+ }
426
+
427
+ get variables() {
428
+ return this.component.variables
429
+ }
430
+
431
+ get workflows() {
432
+ return this.component.workflows
433
+ }
434
+
435
+ get apis() {
436
+ return Object.fromEntries(
437
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
438
+ Object.entries(this.component.apis ?? {}).map(([key, api]) => [
439
+ key,
440
+ isLegacyApi(api)
441
+ ? new LegacyToddleApi(api, key, this.globalFormulas)
442
+ : new ToddleApiV2(api, key, this.globalFormulas),
443
+ ]),
444
+ )
445
+ }
446
+
447
+ get nodes() {
448
+ return this.component.nodes
449
+ }
450
+
451
+ get events() {
452
+ return this.component.events
453
+ }
454
+
455
+ get onLoad() {
456
+ return this.component.onLoad
457
+ }
458
+
459
+ get onAttributeChange() {
460
+ return this.component.onAttributeChange
461
+ }
462
+
463
+ get isPage() {
464
+ return isPageComponent(this.component)
465
+ }
466
+
467
+ get contexts() {
468
+ return this.component.contexts
469
+ }
470
+ }