@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,339 @@
1
+ import { RESET_STYLES } from './theme.const'
2
+
3
+ export interface ThemeOptions {
4
+ includeResetStyle: boolean
5
+ createFontFaces: boolean
6
+ }
7
+
8
+ export type StyleToken = {
9
+ name: string
10
+ type: 'value' | 'variable'
11
+ value: string
12
+ }
13
+
14
+ export type StyleTokenGroup = {
15
+ name: string
16
+ tokens: StyleToken[]
17
+ }
18
+
19
+ export type StyleTokenCategory =
20
+ | 'spacing'
21
+ | 'color'
22
+ | 'font-size'
23
+ | 'font-weight'
24
+ | 'z-index'
25
+ | 'border-radius'
26
+ | 'shadow'
27
+
28
+ export type FontFamily = {
29
+ name: string
30
+ family: string
31
+ provider: 'google' | 'upload'
32
+ type: 'serif' | 'sans-serif' | 'monospace' | 'cursive'
33
+ variants: Array<{
34
+ name: string
35
+ weight:
36
+ | '100'
37
+ | '200'
38
+ | '300'
39
+ | '400'
40
+ | '500'
41
+ | '600'
42
+ | '700'
43
+ | '800'
44
+ | '900'
45
+
46
+ italic: boolean
47
+ url: string
48
+ }>
49
+ }
50
+
51
+ export type OldTheme = {
52
+ spacing: number
53
+ colors: Record<
54
+ string,
55
+ {
56
+ order: number
57
+ variants: Record<string, { value: string; order: number }>
58
+ }
59
+ >
60
+ fontFamily: Record<
61
+ string,
62
+ { value: string[]; order: number; default?: boolean }
63
+ >
64
+ fontWeight: Record<
65
+ string,
66
+ { value: string; order: number; default?: boolean }
67
+ >
68
+ fontSize: Record<string, { value: string; order: number; default?: boolean }>
69
+ shadow: Record<string, { value: string; order: number }>
70
+ breakpoints: Record<string, { value: number; order: number }>
71
+ }
72
+ export type Theme = {
73
+ scheme?: 'dark' | 'light'
74
+ color: StyleTokenGroup[]
75
+ fonts: FontFamily[]
76
+ 'font-size': StyleTokenGroup[]
77
+ 'font-weight': StyleTokenGroup[]
78
+ spacing: StyleTokenGroup[]
79
+ 'border-radius': StyleTokenGroup[]
80
+ shadow: StyleTokenGroup[]
81
+ 'z-index': StyleTokenGroup[]
82
+ }
83
+
84
+ export const getThemeCss = (theme: Theme | OldTheme, options: ThemeOptions) => {
85
+ if ('breakpoints' in theme) {
86
+ return getOldThemeCss(theme)
87
+ }
88
+ return `${options.includeResetStyle ? RESET_STYLES : ''}
89
+ @layer base {
90
+ ${
91
+ options.createFontFaces
92
+ ? theme.fonts
93
+ .map(
94
+ (font) => `
95
+ ${font.variants
96
+ .map(
97
+ (variant) => `
98
+ @font-face {
99
+ font-family: "${font.family}";
100
+ font-style: ${variant.italic ? 'italic' : 'normal'};
101
+ font-weight: ${variant.weight};
102
+ font-display: auto;
103
+ src: local("${variant.url.substring(
104
+ variant.url.lastIndexOf('/') + 1,
105
+ )}"), url("${variant.url.replace(
106
+ 'https://fonts.gstatic.com',
107
+ '/.toddle/fonts/font',
108
+ )}") format("woff2");
109
+ }
110
+ `,
111
+ )
112
+ .join('\n')}
113
+ `,
114
+ )
115
+ .join('\n')
116
+ : ''
117
+ }
118
+ body, :host {
119
+ /* Color */
120
+ ${theme.color
121
+ .flatMap((group) =>
122
+ group.tokens.map((color) => `--${color.name}: ${color.value};`),
123
+ )
124
+ .join('\n')}
125
+ /* Fonts */
126
+ ${theme.fonts
127
+ .map((font) => `--font-${font.name}: '${font.family}',${font.type};`)
128
+ .join('\n')}
129
+
130
+ /* Font size */
131
+ ${theme['font-size']
132
+ .flatMap((group) =>
133
+ group.tokens.map(
134
+ (variable) =>
135
+ `--${variable.name}: ${
136
+ variable.type === 'variable'
137
+ ? `var(--${variable.value})`
138
+ : variable.value
139
+ };`,
140
+ ),
141
+ )
142
+ .join('\n')}
143
+ /* Font weight */
144
+ ${theme['font-weight']
145
+ .flatMap((group) => {
146
+ return group.tokens.map(
147
+ (variable) =>
148
+ `--${variable.name}: ${
149
+ variable.type === 'variable'
150
+ ? `var(--${variable.value})`
151
+ : variable.value
152
+ };`,
153
+ )
154
+ })
155
+ .join('\n')}
156
+ /* Shadows */
157
+ ${theme.shadow
158
+ .flatMap((group) => {
159
+ return group.tokens.map(
160
+ (variable) =>
161
+ `--${variable.name}: ${
162
+ variable.type === 'variable'
163
+ ? `var(--${variable.value})`
164
+ : variable.value
165
+ };`,
166
+ )
167
+ })
168
+ .join('\n')}
169
+ /* Border radius */
170
+ ${theme['border-radius']
171
+ .flatMap((group) => {
172
+ return group.tokens.map(
173
+ (token) =>
174
+ `--${token.name}: ${
175
+ token.type === 'variable' ? `var(--${token.value})` : token.value
176
+ };`,
177
+ )
178
+ })
179
+ .join('\n')}
180
+ /* Spacing */
181
+ ${theme.spacing
182
+ .map((group) => {
183
+ return group.tokens
184
+ .map(
185
+ (token) =>
186
+ `--${token.name}: ${
187
+ token.type === 'variable'
188
+ ? `var(--${token.value})`
189
+ : token.value
190
+ };`,
191
+ )
192
+ .join('\n')
193
+ })
194
+ .join('\n')}
195
+ /* Z-index */
196
+ ${theme['z-index']
197
+ .map((group) => {
198
+ return group.tokens
199
+ .map(
200
+ (token) =>
201
+ `--${token.name}: ${
202
+ token.type === 'variable'
203
+ ? `var(--${token.value})`
204
+ : token.value
205
+ };`,
206
+ )
207
+ .join('\n')
208
+ })
209
+ .join('\n')}
210
+ }
211
+ @keyframes animation-spin {
212
+ from {
213
+ transform: rotate(0deg);
214
+ }
215
+ to {
216
+ transform: rotate(360deg);
217
+ }
218
+ }
219
+ @keyframes animation-fade-in {
220
+ from {
221
+ opacity:0;
222
+ }
223
+ to {
224
+ opacity:1;
225
+ }
226
+ }
227
+ @keyframes animation-fade-out {
228
+ from {
229
+ opacity:1;
230
+ }
231
+ to {
232
+ opacity:0;
233
+ }
234
+ }
235
+ }
236
+ `
237
+ }
238
+
239
+ export const getOldThemeCss = (theme: OldTheme) => {
240
+ const colorVars = Object.entries(theme.colors).flatMap(
241
+ ([color, { variants }]) =>
242
+ Object.entries(variants).map(
243
+ ([variant, { value }]) => `--${color}-${variant}:${value}`,
244
+ ),
245
+ )
246
+ return `
247
+
248
+
249
+
250
+
251
+ body, :host {
252
+ ${Object.entries(theme.fontFamily)
253
+ .map(
254
+ ([
255
+ name,
256
+ {
257
+ value: [family, ...fallback],
258
+ },
259
+ ]) => `--font-${name}: '${family}',${fallback.join(',')};`,
260
+ )
261
+ .join('\n')}
262
+
263
+ ${Object.entries(theme.fontWeight)
264
+ .map(([name, { value }]) => `--font-weight-${name}: ${value};`)
265
+ .join('\n')}
266
+
267
+ ${Object.entries(theme.fontSize)
268
+ .map(([name, { value }]) => `--font-size-${name}: ${value};`)
269
+ .join('\n')}
270
+
271
+ --spacing:${theme.spacing}rem;
272
+ ${colorVars.join(';\n')};
273
+
274
+
275
+ --text-xxs:0.625rem;
276
+ --line-height-xxs:0.9rem;
277
+
278
+ --text-xs:0.75rem;
279
+ --line-height-xs:1rem;
280
+
281
+ --text-sm:0.875rem;
282
+ --line-height-sm:1.25rem;
283
+
284
+ --text-base:1rem;
285
+ --line-height-base:1.5rem;
286
+
287
+ --text-lg:1.125rem;
288
+ --line-height-lg:1.75rem;
289
+
290
+ --text-xl:1.25rem;
291
+ --line-height-xl:1.75rem;
292
+
293
+ --text-2xl:1.5rem;
294
+ --line-height-2xl:2rem;
295
+
296
+ --text-3xl:1.875rem;
297
+ --line-height-3xl:2.25rem;
298
+
299
+ --text-4xl:2.25rem;
300
+ --line-height-4xl:2.5rem;
301
+
302
+ --text-5xl:3rem;
303
+ --line-height-5xl:3rem;
304
+
305
+ ${Object.entries(theme.shadow)
306
+ .map(([name, { value }]) => `--shadow-${name}:${value};`)
307
+ .join('\n')}
308
+ }
309
+
310
+ ${RESET_STYLES}
311
+
312
+ @keyframes animation-spin {
313
+ from {
314
+ transform: rotate(0deg);
315
+ }
316
+ to {
317
+ transform: rotate(360deg);
318
+ }
319
+ }
320
+
321
+ @keyframes animation-fade-in {
322
+ from {
323
+ opacity:0;
324
+ }
325
+ to {
326
+ opacity:1;
327
+ }
328
+ }
329
+
330
+
331
+ @keyframes animation-fade-out {
332
+ from {
333
+ opacity:1;
334
+ }
335
+ to {
336
+ opacity:0;
337
+ }
338
+ }`
339
+ }
@@ -0,0 +1,168 @@
1
+ import type { CSSProperties } from 'react'
2
+ import type {
3
+ AnimationKeyframe,
4
+ EventModel,
5
+ } from '../component/component.types'
6
+ import type { Formula } from '../formula/formula'
7
+
8
+ export type Shadow = {
9
+ x: number
10
+ y: number
11
+ blur: number
12
+ spread: number
13
+ color: string
14
+ inset: boolean
15
+ }
16
+
17
+ export type Filter =
18
+ | {
19
+ name: 'Blur'
20
+ radius: number
21
+ }
22
+ | {
23
+ name: 'Opacity'
24
+ percent: number
25
+ }
26
+
27
+ export interface StyleDeclarationBlock extends CSSProperties {
28
+ filters?: Filter[]
29
+ shadows?: Shadow[]
30
+ }
31
+
32
+ type MediaQuery = {
33
+ 'min-width'?: string
34
+ 'max-width'?: string
35
+ 'min-height'?: string
36
+ 'max-height'?: string
37
+ }
38
+
39
+ export interface StyleVariant {
40
+ autofill?: boolean
41
+ 'even-child'?: boolean
42
+ 'first-child'?: boolean
43
+ 'first-of-type'?: boolean
44
+ 'focus-visible'?: boolean
45
+ 'focus-within'?: boolean
46
+ 'last-child'?: boolean
47
+ 'last-of-type'?: boolean
48
+ 'popover-open'?: boolean
49
+ active?: boolean
50
+ breakpoint: 'small' | 'medium' | 'large'
51
+ checked?: boolean
52
+ class?: string
53
+ className?: string
54
+ disabled?: boolean
55
+ empty?: boolean
56
+ evenChild?: boolean
57
+ firstChild?: boolean
58
+ focus?: boolean
59
+ focusWithin?: boolean
60
+ hover?: boolean
61
+ id?: string
62
+ invalid?: boolean
63
+ lastChild?: boolean
64
+ link?: boolean
65
+ mediaQuery?: MediaQuery
66
+ pseudoElement?: string
67
+ startingStyle?: boolean
68
+ style: StyleDeclarationBlock
69
+ visited?: boolean
70
+ }
71
+
72
+ export interface NodeStyleModel extends StyleDeclarationBlock {
73
+ variants?: StyleVariant[]
74
+ breakpoints?: {
75
+ small?: NodeStyleModel
76
+ medium?: NodeStyleModel
77
+ large?: NodeStyleModel
78
+ }
79
+ mediaQuery?: MediaQuery
80
+ }
81
+
82
+ export type NodeClass = {
83
+ name: string
84
+ formula?: Formula
85
+ }
86
+
87
+ export type NodeModel =
88
+ | ElementNodeModel
89
+ | TextNodeModel
90
+ | ComponentNodeModel
91
+ | SlotNodeModel
92
+
93
+ export type ElementNodeModel = {
94
+ id: string
95
+ type: 'element'
96
+ condition?: Formula
97
+ repeat?: Formula
98
+ repeatKey?: Formula
99
+ tag: string
100
+ classList: NodeClass[]
101
+ attrs: Record<string, Formula>
102
+ style: NodeStyleModel
103
+ variants?: StyleVariant[]
104
+ animations?: Record<string, Record<string, AnimationKeyframe>>
105
+ children: NodeModel[]
106
+ events: EventModel[]
107
+ }
108
+
109
+ export type ComponentNodeModel = {
110
+ id: string
111
+ type: 'component'
112
+ path?: string
113
+ name: string
114
+ condition?: Formula
115
+ repeat?: Formula
116
+ repeatKey?: Formula
117
+ style?: NodeStyleModel
118
+ variants?: StyleVariant[]
119
+ animations?: Record<string, Record<string, AnimationKeyframe>>
120
+ attrs: Record<string, Formula>
121
+ children: NodeModel[]
122
+ events: EventModel[]
123
+ }
124
+
125
+ export type TextNodeModel = {
126
+ id: string
127
+ type: 'text'
128
+ condition?: Formula
129
+ repeat?: Formula
130
+ repeatKey?: Formula
131
+ value: Formula
132
+ children?: undefined
133
+ }
134
+
135
+ export type SlotNodeModel = {
136
+ id: string
137
+ type: 'slot'
138
+ condition?: Formula
139
+ repeat?: undefined
140
+ repeatKey?: Formula
141
+ children: NodeModel[]
142
+ }
143
+
144
+ export const variantSelector = (variant: StyleVariant) =>
145
+ [
146
+ (variant.className ?? variant['class']) && `.${variant.className}`,
147
+ (variant.evenChild ?? variant['even-child']) && ':nth-child(even)',
148
+ (variant.firstChild ?? variant['first-child']) && ':first-child',
149
+ (variant.focusWithin ?? variant['focus-within']) && ':focus-within',
150
+ (variant.lastChild ?? variant['last-child']) && ':last-child',
151
+ variant.active && ':active',
152
+ variant.autofill && ':is(:-webkit-autofill, :autofill)',
153
+ variant.checked && ':checked',
154
+ variant.disabled && ':disabled',
155
+ variant.empty && ':empty',
156
+ variant.focus && ':focus',
157
+ variant.hover && ':hover',
158
+ variant.invalid && ':invalid',
159
+ variant.link && ':link',
160
+ variant.visited && ':visited',
161
+ variant['first-of-type'] && ':first-of-type',
162
+ variant['focus-visible'] && ':focus-visible',
163
+ variant['last-of-type'] && ':last-of-type',
164
+ variant['popover-open'] && ':popover-open',
165
+ variant.pseudoElement && `::${variant.pseudoElement}`,
166
+ ]
167
+ .filter(Boolean)
168
+ .join('')
package/src/types.ts ADDED
@@ -0,0 +1,158 @@
1
+ import type { Component, ComponentData } from './component/component.types'
2
+ import type { Formula, ToddleEnv } from './formula/formula'
3
+ import type { PluginFormula } from './formula/formulaTypes'
4
+
5
+ export type FormulaHandlerV2<R = unknown> = (
6
+ // For v2 of formulas
7
+ args: Record<string, unknown>,
8
+ ctx: {
9
+ root: Document | ShadowRoot
10
+ env: ToddleEnv
11
+ },
12
+ ) => R | null
13
+
14
+ export type ActionHandlerV2 = (
15
+ // For v2 of actions
16
+ args: Record<string, unknown>,
17
+ ctx: {
18
+ triggerActionEvent: (trigger: string, data: any, event?: Event) => void
19
+ root: Document | ShadowRoot
20
+ },
21
+ event?: Event,
22
+ // If the action returns a function, that function will be called
23
+ // from our abort signal (for cleanup)
24
+ ) => void | (() => void) | Promise<void> | Promise<() => void>
25
+
26
+ export type ActionHandler<Args = unknown[]> = (
27
+ args: Args,
28
+ ctx: {
29
+ triggerActionEvent: (trigger: string, data: any, event?: Event) => void
30
+ env: ToddleEnv
31
+ abortSignal: AbortSignal
32
+ },
33
+ event?: Event,
34
+ ) => void
35
+
36
+ export type FormulaHandler<T = void> = (
37
+ args: unknown[],
38
+ ctx: {
39
+ component: Component
40
+ data: ComponentData
41
+ root: Document | ShadowRoot
42
+ env: ToddleEnv
43
+ },
44
+ ) => T | null
45
+
46
+ interface PluginActionBase {
47
+ name: string
48
+ description?: string
49
+ arguments: Array<{
50
+ name: string
51
+ formula: Formula
52
+ }>
53
+ variableArguments: boolean | null
54
+ }
55
+
56
+ export interface PluginActionV2 extends PluginActionBase {
57
+ handler: ActionHandlerV2
58
+ version: 2
59
+ }
60
+
61
+ export type ArgumentInputDataFunction = (
62
+ items: unknown[],
63
+ index: number,
64
+ input: any,
65
+ ) => ComponentData
66
+
67
+ export type CustomFormulaHandler = (
68
+ name: string,
69
+ packageName: string | undefined,
70
+ ) => PluginFormula<FormulaHandlerV2> | undefined
71
+
72
+ export type FormulaLookup = (name: string) => FormulaHandler | undefined
73
+
74
+ export interface Toddle<LocationSignal, ShowSignal> {
75
+ project: string
76
+ branch: string
77
+ commit: string
78
+ errors: Error[]
79
+ formulas: Record<string, Record<string, PluginFormula<FormulaHandlerV2>>>
80
+ actions: Record<string, Record<string, PluginActionV2>>
81
+ isEqual: (a: any, b: any) => boolean
82
+ registerAction: (name: string, handler: ActionHandler) => void
83
+ registerFormula: (
84
+ name: string,
85
+ handler: FormulaHandler,
86
+ getArgumentInputData?: ArgumentInputDataFunction,
87
+ ) => void
88
+ getAction: (name: string) => ActionHandler | undefined
89
+ getFormula: FormulaLookup
90
+ getCustomFormula: CustomFormulaHandler
91
+ getCustomAction: (
92
+ name: string,
93
+ packageName: string | undefined,
94
+ ) => PluginActionV2 | undefined
95
+ getArgumentInputData: (
96
+ name: string,
97
+ items: unknown[],
98
+ index: number,
99
+ input: any,
100
+ ) => ComponentData
101
+ data: Record<string, unknown>
102
+ components: Component[]
103
+ locationSignal: LocationSignal
104
+ eventLog: Array<{
105
+ component: string
106
+ node: string
107
+ nodeId: string
108
+ event: string
109
+ time: number
110
+ data: any
111
+ }>
112
+ pageState: ComponentData
113
+ _preview?: {
114
+ showSignal: ShowSignal
115
+ }
116
+ // We temporarily expose the env here until we add a new version of
117
+ // the APPLY_FORMULA function that can handle the env as an argument
118
+ env: ToddleEnv
119
+ }
120
+
121
+ export interface ToddleInternals {
122
+ project: string
123
+ branch: string
124
+ commit: string
125
+ pageState: ComponentData
126
+ component: Component
127
+ components: Component[]
128
+ // Flag that indicates if the page is done with the createRoot function.
129
+ isPageLoaded: boolean
130
+ cookies: string[]
131
+ }
132
+
133
+ export interface Comment {
134
+ text: string
135
+ }
136
+
137
+ export interface ToddleMetadata {
138
+ '@toddle/metadata'?: {
139
+ comments: Record<string, Comment & { index: number }> | null
140
+ } | null
141
+ }
142
+
143
+ export type RequireFields<T, K extends keyof T> = Omit<T, K> & {
144
+ [P in K]-?: NonNullable<T[P]>
145
+ }
146
+
147
+ export type NestedOmit<
148
+ Schema,
149
+ Path extends string,
150
+ > = Path extends `${infer Head}.${infer Tail}`
151
+ ? Head extends keyof Schema
152
+ ? {
153
+ [K in keyof Schema]: K extends Head
154
+ ? NestedOmit<Schema[K], Tail>
155
+ : Schema[K]
156
+ }
157
+ : Schema
158
+ : Omit<Schema, Path>
@@ -0,0 +1,57 @@
1
+ import { omitPaths, sortObjectEntries } from './collections'
2
+
3
+ describe('sortObjectEntries()', () => {
4
+ test('it sorts entries in an object based on the callback function', () => {
5
+ expect(
6
+ sortObjectEntries(
7
+ { c: 'hello', a: 'value', b: 'otherValue' },
8
+ ([key]) => key,
9
+ ),
10
+ ).toEqual([
11
+ ['a', 'value'],
12
+ ['b', 'otherValue'],
13
+ ['c', 'hello'],
14
+ ])
15
+ expect(
16
+ sortObjectEntries(
17
+ { c: 'hello', a: 'value', b: 'otherValue' },
18
+ ([_, value]) => value,
19
+ ),
20
+ ).toEqual([
21
+ ['c', 'hello'],
22
+ ['b', 'otherValue'],
23
+ ['a', 'value'],
24
+ ])
25
+ })
26
+ })
27
+
28
+ describe('omitPaths()', () => {
29
+ test('it filters out paths from an object', () => {
30
+ expect(
31
+ omitPaths(
32
+ {
33
+ a: 'value',
34
+ b: {
35
+ c: 'hello',
36
+ d: 'world',
37
+ },
38
+ e: {
39
+ f: {
40
+ g: 'foo',
41
+ },
42
+ h: 'bar',
43
+ },
44
+ },
45
+ [['a'], ['b', 'c'], ['e', 'f', 'g']],
46
+ ),
47
+ ).toEqual({
48
+ b: {
49
+ d: 'world',
50
+ },
51
+ e: {
52
+ f: {},
53
+ h: 'bar',
54
+ },
55
+ })
56
+ })
57
+ })