@fugood/bricks-ctor 2.25.0-beta.7 → 2.25.0-beta.70

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 (135) hide show
  1. package/package.json +4 -24
  2. package/tools/__tests__/legacy-forwarder.test.js +91 -0
  3. package/tools/_forward.ts +26 -0
  4. package/tools/deploy.ts +3 -164
  5. package/tools/mcp-server.ts +3 -27
  6. package/tools/postinstall.ts +3 -234
  7. package/tools/pull.ts +3 -121
  8. package/tools/push-config.ts +4 -0
  9. package/tools/simulator.ts +4 -0
  10. package/compile/action-name-map.ts +0 -1015
  11. package/compile/index.ts +0 -1278
  12. package/compile/util.ts +0 -358
  13. package/index.ts +0 -6
  14. package/skills/bricks-ctor/SKILL.md +0 -32
  15. package/skills/bricks-ctor/rules/animation.md +0 -159
  16. package/skills/bricks-ctor/rules/architecture-patterns.md +0 -69
  17. package/skills/bricks-ctor/rules/automations.md +0 -221
  18. package/skills/bricks-ctor/rules/buttress.md +0 -156
  19. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  20. package/skills/bricks-ctor/rules/local-sync.md +0 -129
  21. package/skills/bricks-ctor/rules/media-flow.md +0 -158
  22. package/skills/bricks-ctor/rules/remote-data-bank.md +0 -196
  23. package/skills/bricks-ctor/rules/standby-transition.md +0 -124
  24. package/skills/bricks-design/LICENSE.txt +0 -180
  25. package/skills/bricks-design/SKILL.md +0 -66
  26. package/skills/rive-marketplace/SKILL.md +0 -99
  27. package/tools/_git-author.ts +0 -29
  28. package/tools/_shell.ts +0 -173
  29. package/tools/icons/.gitattributes +0 -1
  30. package/tools/icons/fa6pro-glyphmap.json +0 -4686
  31. package/tools/icons/fa6pro-meta.json +0 -1
  32. package/tools/mcp-tools/compile.ts +0 -92
  33. package/tools/mcp-tools/huggingface.ts +0 -762
  34. package/tools/mcp-tools/icons.ts +0 -81
  35. package/tools/mcp-tools/lottie.ts +0 -102
  36. package/tools/mcp-tools/media.ts +0 -110
  37. package/tools/preview-main.mjs +0 -293
  38. package/tools/preview.ts +0 -150
  39. package/types/animation.ts +0 -100
  40. package/types/automation.ts +0 -235
  41. package/types/brick-base.ts +0 -80
  42. package/types/bricks/Camera.ts +0 -246
  43. package/types/bricks/Chart.ts +0 -372
  44. package/types/bricks/GenerativeMedia.ts +0 -290
  45. package/types/bricks/Icon.ts +0 -98
  46. package/types/bricks/Image.ts +0 -126
  47. package/types/bricks/Items.ts +0 -476
  48. package/types/bricks/Lottie.ts +0 -168
  49. package/types/bricks/Maps.ts +0 -262
  50. package/types/bricks/QrCode.ts +0 -117
  51. package/types/bricks/Rect.ts +0 -150
  52. package/types/bricks/RichText.ts +0 -128
  53. package/types/bricks/Rive.ts +0 -220
  54. package/types/bricks/Sketch.ts +0 -254
  55. package/types/bricks/Slideshow.ts +0 -201
  56. package/types/bricks/Svg.ts +0 -99
  57. package/types/bricks/Text.ts +0 -148
  58. package/types/bricks/TextInput.ts +0 -242
  59. package/types/bricks/Video.ts +0 -175
  60. package/types/bricks/VideoStreaming.ts +0 -112
  61. package/types/bricks/WebRtcStream.ts +0 -65
  62. package/types/bricks/WebView.ts +0 -168
  63. package/types/bricks/index.ts +0 -22
  64. package/types/canvas.ts +0 -82
  65. package/types/common.ts +0 -144
  66. package/types/data-calc-command.ts +0 -7005
  67. package/types/data-calc-script.ts +0 -21
  68. package/types/data-calc.ts +0 -11
  69. package/types/data.ts +0 -95
  70. package/types/generators/AlarmClock.ts +0 -110
  71. package/types/generators/Assistant.ts +0 -621
  72. package/types/generators/BleCentral.ts +0 -247
  73. package/types/generators/BlePeripheral.ts +0 -208
  74. package/types/generators/CanvasMap.ts +0 -74
  75. package/types/generators/CastlesPay.ts +0 -87
  76. package/types/generators/DataBank.ts +0 -160
  77. package/types/generators/File.ts +0 -432
  78. package/types/generators/GraphQl.ts +0 -132
  79. package/types/generators/Http.ts +0 -222
  80. package/types/generators/HttpServer.ts +0 -176
  81. package/types/generators/Information.ts +0 -103
  82. package/types/generators/Intent.ts +0 -168
  83. package/types/generators/Iterator.ts +0 -108
  84. package/types/generators/Keyboard.ts +0 -105
  85. package/types/generators/LlmAnthropicCompat.ts +0 -212
  86. package/types/generators/LlmAppleBuiltin.ts +0 -159
  87. package/types/generators/LlmGgml.ts +0 -861
  88. package/types/generators/LlmMediaTekNeuroPilot.ts +0 -235
  89. package/types/generators/LlmMlx.ts +0 -227
  90. package/types/generators/LlmOnnx.ts +0 -213
  91. package/types/generators/LlmOpenAiCompat.ts +0 -312
  92. package/types/generators/LlmQualcommAiEngine.ts +0 -247
  93. package/types/generators/Mcp.ts +0 -637
  94. package/types/generators/McpServer.ts +0 -289
  95. package/types/generators/MediaFlow.ts +0 -170
  96. package/types/generators/MqttBroker.ts +0 -141
  97. package/types/generators/MqttClient.ts +0 -141
  98. package/types/generators/Question.ts +0 -408
  99. package/types/generators/RealtimeTranscription.ts +0 -287
  100. package/types/generators/RerankerGgml.ts +0 -191
  101. package/types/generators/SerialPort.ts +0 -151
  102. package/types/generators/SoundPlayer.ts +0 -94
  103. package/types/generators/SoundRecorder.ts +0 -130
  104. package/types/generators/SpeechToTextGgml.ts +0 -419
  105. package/types/generators/SpeechToTextOnnx.ts +0 -236
  106. package/types/generators/SpeechToTextPlatform.ts +0 -85
  107. package/types/generators/SqLite.ts +0 -159
  108. package/types/generators/Step.ts +0 -107
  109. package/types/generators/SttAppleBuiltin.ts +0 -130
  110. package/types/generators/Tcp.ts +0 -126
  111. package/types/generators/TcpServer.ts +0 -147
  112. package/types/generators/TextToSpeechAppleBuiltin.ts +0 -127
  113. package/types/generators/TextToSpeechGgml.ts +0 -221
  114. package/types/generators/TextToSpeechOnnx.ts +0 -178
  115. package/types/generators/TextToSpeechOpenAiLike.ts +0 -121
  116. package/types/generators/ThermalPrinter.ts +0 -191
  117. package/types/generators/Tick.ts +0 -83
  118. package/types/generators/Udp.ts +0 -120
  119. package/types/generators/VadGgml.ts +0 -250
  120. package/types/generators/VadOnnx.ts +0 -231
  121. package/types/generators/VadTraditional.ts +0 -138
  122. package/types/generators/VectorStore.ts +0 -257
  123. package/types/generators/Watchdog.ts +0 -107
  124. package/types/generators/WebCrawler.ts +0 -103
  125. package/types/generators/WebRtc.ts +0 -181
  126. package/types/generators/WebSocket.ts +0 -148
  127. package/types/generators/index.ts +0 -57
  128. package/types/index.ts +0 -13
  129. package/types/subspace.ts +0 -60
  130. package/types/switch.ts +0 -51
  131. package/types/system.ts +0 -707
  132. package/utils/calc.ts +0 -126
  133. package/utils/data.ts +0 -497
  134. package/utils/event-props.ts +0 -886
  135. package/utils/id.ts +0 -80
package/compile/index.ts DELETED
@@ -1,1278 +0,0 @@
1
- /* eslint-disable no-underscore-dangle -- Uses __typename, __actionName, etc. for type system */
2
- import camelCase from 'lodash/camelCase'
3
- import upperFirst from 'lodash/upperFirst'
4
- import snakeCase from 'lodash/snakeCase'
5
- import omit from 'lodash/omit'
6
- import { parse as parseAST } from 'acorn'
7
- import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
8
- import escodegen from 'escodegen'
9
- import { makeId } from '../utils/id'
10
- import { generateCalulationMap } from './util'
11
- import { templateActionNameMap } from './action-name-map'
12
- import { templateEventPropsMap } from '../utils/event-props'
13
- import type {
14
- Application,
15
- Data,
16
- DataAssetKind,
17
- Animation,
18
- AnimationDef,
19
- AnimationComposeDef,
20
- ActionWithDataParams,
21
- ActionWithParams,
22
- BrickItems,
23
- SwitchCondData,
24
- SwitchCondInnerStateCurrentCanvas,
25
- SwitchCondPropertyBankByItemKey,
26
- DataCalculationMap,
27
- DataCalculationScript,
28
- DataCalculationData,
29
- DataCommand,
30
- Brick,
31
- Canvas,
32
- Subspace,
33
- AutomationMap,
34
- AutomationTestMap,
35
- AutomationTest,
36
- TestCase,
37
- TestVariable,
38
- } from '../types'
39
-
40
- const uuidPattern = '[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'
41
-
42
- const entryIdPatterns = {
43
- SUBSPACE: new RegExp(`^SUBSPACE_${uuidPattern}$`),
44
- CANVAS: new RegExp(`^CANVAS_${uuidPattern}$`),
45
- BRICK: new RegExp(`^BRICK_${uuidPattern}$`),
46
- GENERATOR: new RegExp(`^(GENERATOR|AUTO_GENERATOR)_${uuidPattern}$`),
47
- ANIMATION: new RegExp(`^ANIMATION_${uuidPattern}$`),
48
- PROPERTY_BANK_DATA_NODE: new RegExp(`^PROPERTY_BANK_DATA_NODE_${uuidPattern}$`),
49
- PROPERTY_BANK_COMMAND_NODE: new RegExp(`^PROPERTY_BANK_COMMAND_NODE_${uuidPattern}$`),
50
- PROPERTY_BANK_COMMAND_MAP: new RegExp(`^PROPERTY_BANK_COMMAND_MAP_${uuidPattern}$`),
51
- BRICK_STATE_GROUP: new RegExp(`^BRICK_STATE_GROUP_${uuidPattern}$`),
52
- TEST: new RegExp(`^TEST_${uuidPattern}$`),
53
- TEST_CASE: new RegExp(`^TEST_CASE_${uuidPattern}$`),
54
- TEST_VAR: new RegExp(`^TEST_VAR_${uuidPattern}$`),
55
- AUTOMATION_MAP: /^AUTOMATION_MAP_.*/,
56
- } as const
57
-
58
- type EntryIdPatternKey = keyof typeof entryIdPatterns
59
-
60
- const assertEntryId = (
61
- id: unknown,
62
- patternKey: EntryIdPatternKey,
63
- errorReference: string = '',
64
- ): string => {
65
- const pattern = entryIdPatterns[patternKey]
66
- if (typeof id !== 'string' || !pattern.test(id)) {
67
- throw new Error(
68
- `Invalid ${patternKey} id${errorReference ? ` ${errorReference}` : ''}: ${String(id)}`,
69
- )
70
- }
71
- return id
72
- }
73
-
74
- const compileProperty = (property, errorReference: string, result = {}) => {
75
- if (Array.isArray(property)) {
76
- return property.map((p) => compileProperty(p, errorReference))
77
- }
78
- if (property?.__typename === 'DataLink') {
79
- const data = property.data?.()
80
- if (!data?.id) throw new Error(`Invalid DataLink reference ${errorReference}`)
81
- return `PROPERTY_BANK#${data.id}`
82
- }
83
- if (typeof property === 'function') {
84
- const instance = property()
85
- if (!instance?.id) throw new Error(`Invalid ID reference ${errorReference}`)
86
- return instance?.id // defined type instance getter
87
- }
88
- if (property && typeof property === 'object') {
89
- return Object.entries(property).reduce((acc, [key, value]) => {
90
- acc[key] = compileProperty(value, errorReference)
91
- return acc
92
- }, result)
93
- }
94
- return property
95
- }
96
-
97
- const compileScriptCalculationCode = (code = '') => {
98
- try {
99
- const program = parseAST(code, { sourceType: 'module', ecmaVersion: 2020 })
100
- // export function main() { ... }
101
- const declarationBody = (
102
- (program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration
103
- )?.body
104
- return escodegen.generate(declarationBody, {
105
- format: {
106
- indent: { style: ' ' },
107
- semicolons: false,
108
- },
109
- comment: true,
110
- })
111
- } catch {
112
- return code || ''
113
- }
114
- }
115
-
116
- const getTemplateName = (key: string) =>
117
- upperFirst(camelCase(key.replace(/^(BRICK|GENERATOR)_/, '')))
118
-
119
- const compileEventActionValue = (templateKey, eventKey, value, errorReference) => {
120
- const tmplEventProperties = templateEventPropsMap[getTemplateName(templateKey)]
121
- const props = tmplEventProperties?.[eventKey]
122
- if (!props) return compileProperty(value, errorReference)
123
- if (typeof value === 'string' && value in props) {
124
- const expectedType = props[value]
125
- if (expectedType) return value
126
- }
127
- if (typeof value === 'string' && /^(BRICK|GENERATOR)_/.test(value)) {
128
- console.warn(
129
- `[Warning] Event property "${value}" is not compatible with event "${eventKey}" of ${templateKey} ${errorReference}`,
130
- )
131
- }
132
- return compileProperty(value, errorReference)
133
- }
134
-
135
- const convertOutletKey = (templateKey: string, key: string) =>
136
- `${templateKey}_${snakeCase(key).toUpperCase()}`
137
-
138
- const compileOutlets = (
139
- templateKey: string,
140
- outlets: { [key: string]: () => Data },
141
- errorReference: string,
142
- ) =>
143
- Object.entries(outlets).reduce((acc, [key, data]) => {
144
- const dataInstance = data?.()
145
- const dataId = assertEntryId(dataInstance?.id, 'PROPERTY_BANK_DATA_NODE', errorReference)
146
- acc[convertOutletKey(templateKey, key)] = dataId
147
- return acc
148
- }, {})
149
-
150
- const convertEventKey = (templateKey: string, key: string) =>
151
- `${templateKey ? `${templateKey}_` : ''}${snakeCase(key).toUpperCase()}`
152
-
153
- const basicAnimationEvents = ['show', 'standby', 'breatheStart']
154
-
155
- const compileAnimations = (
156
- templateKey: string,
157
- animations: { [key: string]: Animation },
158
- errorReference: string,
159
- ) =>
160
- Object.entries(animations).reduce((acc, [key, animation]) => {
161
- const animationId = assertEntryId(animation?.id, 'ANIMATION', errorReference)
162
- acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
163
- `ANIMATION#${animationId}`
164
- return acc
165
- }, {})
166
-
167
- const compileActionParam = (templateKey: string, actionName: string, paramName: string) =>
168
- templateActionNameMap[templateKey]?.[actionName]?.[paramName] || paramName
169
-
170
- const compileEvents = (
171
- templateKey: string,
172
- eventMap: { [key: string]: Array<any> },
173
- options = { camelCase: false, errorReference: '' },
174
- ) => {
175
- const { camelCase, errorReference } = options
176
- return Object.entries(eventMap).reduce((acc, [key, events]) => {
177
- acc[convertEventKey(templateKey, key)] = events.map((event) => {
178
- const { handler, action } = event
179
-
180
- let handlerKey
181
- let handlerTemplateKey
182
- if (handler === 'system' || typeof handler === 'string') {
183
- if (handler.startsWith('SUBSPACE_')) handlerKey = handler
184
- else handlerKey = handler.toUpperCase()
185
- if (handlerKey === 'SYSTEM') handlerTemplateKey = 'SYSTEM'
186
- } else if (typeof handler === 'function') {
187
- let instance = handler()
188
- if (instance?.id) {
189
- instance = instance as { id: string }
190
- handlerKey = instance?.id
191
- } else if (instance?.brickId) {
192
- instance = instance as { brickId: string; templateKey: string }
193
- handlerKey = instance.brickId
194
- }
195
- handlerTemplateKey = instance?.templateKey
196
- }
197
- if (!handlerKey) throw new Error(`Invalid handler: ${handler} ${errorReference}`)
198
-
199
- const parameterList: Array<object> = []
200
- if (Object.prototype.hasOwnProperty.call(action, 'params')) {
201
- const actionDef = action as ActionWithParams
202
- ;(actionDef.params || []).forEach(({ input, value, mapping }) => {
203
- const param = {
204
- [camelCase ? 'inputToReceiver' : 'input_to_receiver']: handlerTemplateKey
205
- ? compileActionParam(handlerTemplateKey, action.__actionName, input)
206
- : input,
207
- [camelCase ? 'resultFromSender' : 'result_from_sender']:
208
- mapping || compileEventActionValue(handlerTemplateKey, key, value, errorReference),
209
- }
210
- if (mapping) param[camelCase ? 'resultDataMapping' : 'result_data_mapping'] = true
211
- parameterList.push(param)
212
- })
213
- } else if (Object.prototype.hasOwnProperty.call(action, 'dataParams')) {
214
- const actionDef = action as ActionWithDataParams
215
- ;(actionDef.dataParams || []).forEach(({ input, value, mapping }) => {
216
- if (!input) return
217
- const param = {
218
- [camelCase ? 'inputToReceiver' : 'input_to_receiver']: input().id,
219
- [camelCase ? 'resultFromSender' : 'result_from_sender']:
220
- mapping || compileEventActionValue(handlerTemplateKey, key, value, errorReference),
221
- }
222
- if (mapping) param[camelCase ? 'resultDataMapping' : 'result_data_mapping'] = true
223
- parameterList.push(param)
224
- })
225
- }
226
- return {
227
- handler: handlerKey,
228
- action: action.__actionName,
229
- [camelCase ? 'parameterList' : 'parameter_list']: parameterList,
230
- [camelCase ? 'waitAsync' : 'wait_async']: event.waitAsync,
231
- }
232
- })
233
- return acc
234
- }, {})
235
- }
236
-
237
- const compileSwitchConds = (templateKey, conds, errorReference) =>
238
- (conds || []).map((item: any, index) => {
239
- const result: any = { method: item.method }
240
- if (!item.cond) return result
241
- if (item.cond.__typename === 'SwitchCondData') {
242
- const cond = item.cond as SwitchCondData
243
- result.type = 'property_bank'
244
- if (!cond.data().id)
245
- throw new Error(`Invalid data reference in conds index: ${index} ${errorReference}`)
246
- result.key = cond.data().id
247
- result.value = cond.value
248
- } else if (item.cond.__typename === 'SwitchCondPropertyBankByItemKey') {
249
- const cond = item.cond as SwitchCondPropertyBankByItemKey
250
- result.type = 'property_bank_by_item_key'
251
- if (!cond.data().id)
252
- throw new Error(`Invalid data reference in conds index: ${index} ${errorReference}`)
253
- result.key = cond.data().id
254
- result.value = cond.value
255
- } else if (item.cond.__typename === 'SwitchCondInnerStateOutlet') {
256
- const { cond } = item
257
- result.type = 'inner_state'
258
- result.key = convertOutletKey(templateKey, cond.outlet)
259
- result.value = cond.value
260
- } else if (item.cond.__typename === 'SwitchCondInnerStateCurrentCanvas') {
261
- const cond = item.cond as SwitchCondInnerStateCurrentCanvas
262
- result.type = 'inner_state'
263
- result.key = 'current_canvas'
264
- if (!cond.value().id)
265
- throw new Error(`Invalid canvas reference in conds index: ${index} ${errorReference}`)
266
- result.value = cond.value().id
267
- }
268
- return result
269
- })
270
-
271
- const compileApplicationSettings = (settings: Application['settings']) => ({
272
- internet_reachability_url: settings?.internetReachabilityUrl,
273
- enable_data_lock: settings?.enableDataLock,
274
- show_deprecated_features: settings?.showDeprecatedFeatures,
275
- enable_unstable_bricks: settings?.enableUnstableFeatures,
276
- runtime_cache_options: settings?.runtimeCacheOptions
277
- ? {
278
- disabled: settings.runtimeCacheOptions.disabled,
279
- behavior: settings.runtimeCacheOptions.behavior,
280
- retry_attempt_for_failure: settings.runtimeCacheOptions.retryAttemptForFailure,
281
- }
282
- : undefined,
283
- tv_options: settings?.tvOptions
284
- ? {
285
- disabled_selectable: settings.tvOptions.disabledSelectable,
286
- selectable_color: settings.tvOptions.selectableColor,
287
- selectable_border: settings.tvOptions.selectableBorder,
288
- }
289
- : undefined,
290
- ai: settings?.ai
291
- ? {
292
- use_anthropic_api_key_system_data: settings.ai.useAnthropicApiKeySystemData,
293
- use_openai_api_key_system_data: settings.ai.useOpenAiApiKeySystemData,
294
- use_gemini_api_key_system_data: settings.ai.useGeminiApiKeySystemData,
295
- }
296
- : undefined,
297
- hide_short_refs: settings?.hideShortRefs,
298
- })
299
-
300
- const animationTypeMap = {
301
- AnimationTimingConfig: 'timing',
302
- AnimationSpringConfig: 'spring',
303
- AnimationDecayConfig: 'decay',
304
- }
305
-
306
- const compileFrame = (frame: Canvas['items'][number]['frame']) => ({
307
- x: frame.x,
308
- y: frame.y,
309
- width: frame.width,
310
- height: frame.height,
311
- standby_mode: frame.standbyMode,
312
- standby_frame: frame.standbyFrame,
313
- standby_opacity: frame.standbyOpacity,
314
- standby_delay: frame.standbyDelay,
315
- standby_delay_random: frame.standbyDelayRandom,
316
- standby_easing: frame.standbyEasing,
317
- showing_delay: frame.showingDelay,
318
- render_out_of_viewport: frame.renderOutOfViewport,
319
- })
320
-
321
- const preloadTypes = [
322
- 'media-resource-image',
323
- 'media-resource-video',
324
- 'media-resource-audio',
325
- 'media-resource-file',
326
- 'lottie-file-uri',
327
- 'ggml-model-asset',
328
- 'gguf-model-asset',
329
- 'binary-asset',
330
- ]
331
-
332
- const compileKind = (kind: Data['kind']) => {
333
- const { type, ...rest } = kind || {}
334
- if (!type) return {}
335
- if (preloadTypes.includes(type)) {
336
- const { preload, metadata } = kind as DataAssetKind
337
- const result: any = { kind: type, ...rest }
338
- if (preload) {
339
- result.preload = {
340
- type: preload.type,
341
- hashType: preload.hashType,
342
- }
343
- if (preload.hashType) result.preload[preload.hashType] = preload.hash
344
- }
345
- // Handle metadata
346
- if (type === 'gguf-model-asset') {
347
- result._hfRepoInfo = metadata?.hfRepoInfo
348
- }
349
- return result
350
- }
351
-
352
- return { kind: type, ...rest }
353
- }
354
-
355
- const compileRemoteUpdate = (remoteUpdate: Data['remoteUpdate']) => {
356
- if (!remoteUpdate) return {}
357
- if (remoteUpdate.type === 'auto') return { enable_remote_update: true }
358
- return {
359
- enable_remote_update: true,
360
- ...(remoteUpdate.type === 'device-specific' ? { use_remote_id_prefix: true } : {}),
361
- ...(remoteUpdate.type === 'global-data' ? { global_remote_update_prop: remoteUpdate.id } : {}),
362
- }
363
- }
364
-
365
- const compileModule = (subspace: Subspace) => {
366
- if (!subspace.module?.id) return {}
367
- return {
368
- module: {
369
- link: subspace.module.link ?? false,
370
- id: subspace.module.id,
371
- version: subspace.module.version,
372
- },
373
- selected_requirements: subspace.module.selectedRequirements?.reduce((acc, requirement) => {
374
- acc[requirement.subspace] = {
375
- id: requirement.id,
376
- version: requirement.version,
377
- }
378
- return acc
379
- }, {}),
380
- requirements: subspace.module.requirements?.reduce((acc, requirement) => {
381
- acc[requirement.subspace] = {
382
- id: requirement.id,
383
- version: requirement.version,
384
- }
385
- return acc
386
- }, {}),
387
- }
388
- }
389
-
390
- /**
391
- * Compile a run array element - if it's a getter function, call it and extract the id
392
- * Note: All entity ids already include their prefix (e.g., SUBSPACE_xxx, BRICK_xxx, PROPERTY_BANK_DATA_NODE_xxx)
393
- */
394
- function compileRunElement(element: unknown): unknown {
395
- if (typeof element !== 'function') return element
396
-
397
- const result = element()
398
- if (result && typeof result === 'object' && 'id' in result) {
399
- return (result as { id: string }).id
400
- }
401
- return result
402
- }
403
-
404
- /**
405
- * Compile a run array - resolve getter functions and extract ids with prefixes
406
- */
407
- function compileRunArray(run: unknown[]): unknown[] {
408
- return run.map(compileRunElement)
409
- }
410
-
411
- /**
412
- * Compile typed TestCase to raw format (strips __typename)
413
- */
414
- const compileTestCase = (testCase: TestCase) => ({
415
- id: testCase.id,
416
- name: testCase.name,
417
- hide_short_ref: testCase.hideShortRef,
418
- run: compileRunArray(testCase.run),
419
- exit_on_failed: testCase.exit_on_failed,
420
- commented: testCase.commented,
421
- pre_delay: testCase.pre_delay,
422
- post_delay: testCase.post_delay,
423
- post_delay_rule: testCase.post_delay_rule,
424
- jump_cond: testCase.jump_cond.map((cond) => {
425
- if (cond.jump_to == null) {
426
- console.warn(
427
- `[Warning] jump_cond is missing jump_to in test case "${testCase.name}" (${testCase.id})`,
428
- )
429
- }
430
- return {
431
- type: cond.type,
432
- status: cond.status,
433
- variable: cond.variable,
434
- operator: cond.operator,
435
- value: cond.value,
436
- jump_to: cond.jump_to,
437
- }
438
- }),
439
- })
440
-
441
- /**
442
- * Compile typed TestVariable to raw format (strips __typename)
443
- */
444
- const compileTestVariable = (variable: TestVariable) => ({
445
- id: variable.id,
446
- name: variable.name,
447
- type: variable.type,
448
- value: variable.value,
449
- })
450
-
451
- /**
452
- * Convert an array of items with id property to an id-keyed record
453
- */
454
- const arrayToIdMap = <T extends { id: string }, R>(
455
- items: T[],
456
- transform: (item: T, index: number) => R,
457
- options: {
458
- patternKey: EntryIdPatternKey
459
- getErrorReference: (item: T, index: number) => string
460
- },
461
- ): Record<string, R> =>
462
- Object.fromEntries(
463
- items.map((item, index) => {
464
- const itemId = assertEntryId(
465
- item.id,
466
- options.patternKey,
467
- options.getErrorReference(item, index),
468
- )
469
- return [itemId, transform(item, index)]
470
- }),
471
- )
472
-
473
- /**
474
- * Compile typed AutomationTest to raw format
475
- */
476
- const compileAutomationTest = (
477
- test: AutomationTest,
478
- options: { mapId: string; testIndex: number },
479
- ) => {
480
- const testId = assertEntryId(
481
- test.id,
482
- 'TEST',
483
- `(automation map: ${options.mapId}, test index: ${options.testIndex})`,
484
- )
485
-
486
- return {
487
- id: testId,
488
- title: test.title,
489
- hide_short_ref: test.hideShortRef,
490
- timeout: test.timeout,
491
- trigger_type: test.trigger_type,
492
- cron: test.cron,
493
- skip_trigger_type_check: test.skip_trigger_type_check,
494
- local_sync: test.local_sync,
495
- meta: test.meta,
496
- case_map: arrayToIdMap(test.cases, compileTestCase, {
497
- patternKey: 'TEST_CASE',
498
- getErrorReference: (_, index) =>
499
- `(automation map: ${options.mapId}, test: ${testId}, case index: ${index})`,
500
- }),
501
- var_map: arrayToIdMap(test.variables, compileTestVariable, {
502
- patternKey: 'TEST_VAR',
503
- getErrorReference: (_, index) =>
504
- `(automation map: ${options.mapId}, test: ${testId}, variable index: ${index})`,
505
- }),
506
- }
507
- }
508
-
509
- /**
510
- * Compile typed AutomationTestMap to raw format
511
- */
512
- const compileAutomationTestMap = (testMap: AutomationTestMap, mapId: string) => {
513
- assertEntryId(testMap.id, 'AUTOMATION_MAP', `(automation map id field: ${mapId})`)
514
-
515
- return {
516
- title: testMap.title,
517
- hide_short_ref: testMap.hideShortRef,
518
- createdAt: testMap.createdAt,
519
- map: arrayToIdMap(
520
- testMap.tests,
521
- (test, index) => compileAutomationTest(test, { mapId, testIndex: index }),
522
- {
523
- patternKey: 'TEST',
524
- getErrorReference: (_, index) => `(automation map: ${mapId}, test index: ${index})`,
525
- },
526
- ),
527
- }
528
- }
529
-
530
- /**
531
- * Compile typed AutomationMap to raw automation_map format
532
- */
533
- const compileAutomation = (automationMap: AutomationMap) =>
534
- Object.fromEntries(
535
- Object.entries(automationMap).map(([mapId, testMap]) => {
536
- const automationMapId = assertEntryId(mapId, 'AUTOMATION_MAP', '(automation map key)')
537
- return [automationMapId, compileAutomationTestMap(testMap, automationMapId)]
538
- }),
539
- )
540
-
541
- export const compile = async (app: Application) => {
542
- await new Promise((resolve) => setImmediate(resolve, 0))
543
- const timestamp = Date.now()
544
- // Pre-index subspace ids so the canvas-item validation below stays O(1).
545
- const subspaceIdSet = new Set(app.subspaces.map((s) => s.id))
546
- const compiledAutomationMap = app.automationMap ? compileAutomation(app.automationMap) : null
547
- const config = {
548
- title: `${app.name || 'Unknown'}(${timestamp})`,
549
- subspace_map: app.subspaces.reduce((subspaceMap, subspace, subspaceIndex) => {
550
- const subspaceId = assertEntryId(
551
- subspace.id,
552
- 'SUBSPACE',
553
- `(subspace index: ${subspaceIndex})`,
554
- )
555
-
556
- // Linked module subspaces reference external content — only include
557
- // module metadata, not local canvas/brick/data compilation.
558
- // Include a placeholder root canvas so the config passes schema
559
- // validation (root_canvas_id is required before the conditional
560
- // schema fix is published).
561
- if (subspace.module?.link) {
562
- const placeholderCanvasId = makeId('canvas')
563
- subspaceMap[subspaceId] = {
564
- title: subspace.title,
565
- description: subspace.description,
566
- unused: subspace.unused,
567
- portal: subspace.portal,
568
- layout: {
569
- width: subspace.layout?.width,
570
- height: subspace.layout?.height,
571
- resize_mode: subspace.layout?.resizeMode,
572
- },
573
- root_canvas_id: placeholderCanvasId,
574
- property_bank_map: {},
575
- brick_map: {},
576
- generator_map: {},
577
- animation_map: {},
578
- canvas_map: {
579
- [placeholderCanvasId]: { item_list: [] },
580
- },
581
- ...compileModule(subspace),
582
- }
583
- return subspaceMap
584
- }
585
-
586
- const rootCanvasId = assertEntryId(
587
- subspace.rootCanvas?.id,
588
- 'CANVAS',
589
- `(subspace: ${subspaceId}, root canvas)`,
590
- )
591
-
592
- subspaceMap[subspaceId] = {
593
- title: subspace.title,
594
- description: subspace.description,
595
- hide_short_ref: subspace.hideShortRef,
596
- unused: subspace.unused,
597
- portal: subspace.portal,
598
- _expanded: subspace.unexpanded
599
- ? {
600
- brick: !subspace.unexpanded.brick,
601
- generator: !subspace.unexpanded.generator,
602
- canvas: subspace.unexpanded.canvas?.reduce((acc, canvas, canvasIndex) => {
603
- const unexpandedCanvasId = assertEntryId(
604
- canvas?.id,
605
- 'CANVAS',
606
- `(subspace: ${subspaceId}, unexpanded canvas index: ${canvasIndex})`,
607
- )
608
- acc[unexpandedCanvasId] = !canvas?.id
609
- return acc
610
- }, {}),
611
- property_bank: !subspace.unexpanded.data,
612
- property_bank_calc: !subspace.unexpanded.dataCalculation,
613
- }
614
- : undefined,
615
- layout: {
616
- width: subspace.layout?.width,
617
- height: subspace.layout?.height,
618
- resize_mode: subspace.layout?.resizeMode,
619
- },
620
- local_sync: subspace.localSyncChangeCanvas
621
- ? {
622
- change_canvas: subspace.localSyncChangeCanvas,
623
- }
624
- : undefined,
625
- animation_map: subspace.animations.reduce((map, animation, animationIndex) => {
626
- const animationId = assertEntryId(
627
- animation?.id,
628
- 'ANIMATION',
629
- `(animation index: ${animationIndex}, subspace: ${subspaceId})`,
630
- )
631
-
632
- if (animation.__typename === 'Animation') {
633
- const animationDef = animation as AnimationDef
634
- map[animationId] = {
635
- alias: animationDef.alias,
636
- title: animationDef.title,
637
- description: animationDef.description,
638
- hide_short_ref: animationDef.hideShortRef,
639
- animationRunType: animationDef.runType,
640
- property: animationDef.property,
641
- type: animationTypeMap[animationDef.config.__type],
642
- config: compileProperty(
643
- omit(animationDef.config, '__type'),
644
- `(animation: ${animationId}, subspace ${subspaceId})`,
645
- ),
646
- }
647
- } else if (animation.__typename === 'AnimationCompose') {
648
- const animationDef = animation as AnimationComposeDef
649
- map[animationId] = {
650
- alias: animationDef.alias,
651
- title: animationDef.title,
652
- description: animationDef.description,
653
- hide_short_ref: animationDef.hideShortRef,
654
- animationRunType: animationDef.runType,
655
- compose_type: animationDef.composeType,
656
- item_list: animationDef.items.map((item, index) => {
657
- const innerAnimation = item()
658
- if (!innerAnimation?.id)
659
- throw new Error(
660
- `Invalid animation index: ${index} (animation: ${innerAnimation.id}, subspace ${subspaceId})`,
661
- )
662
- return { animation_id: innerAnimation.id }
663
- }),
664
- }
665
- }
666
- return map
667
- }, {}),
668
- brick_map: subspace.bricks.reduce((map, brick, brickIndex) => {
669
- const brickId = assertEntryId(
670
- brick.id,
671
- 'BRICK',
672
- `(brick index: ${brickIndex}, subspace: ${subspaceId})`,
673
- )
674
- const property = compileProperty(
675
- brick.property || {},
676
- `(brick: ${brickId}, subspace ${subspaceId})`,
677
- )
678
- if (brick.templateKey === 'BRICK_ITEMS') {
679
- const brickItems = brick as BrickItems
680
- const buildList = (itemBrick, index, key) => ({
681
- title: itemBrick.title,
682
- brickId: itemBrick.brickId,
683
- brickIdPrefix: itemBrick.brickIdPrefix,
684
- templateKey: itemBrick.templateKey,
685
- frame: itemBrick.frame,
686
- property: compileProperty(
687
- itemBrick.property,
688
- `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
689
- ),
690
- propertyMapping: itemBrick.propertyMapping,
691
- animation: compileAnimations(
692
- itemBrick.templateKey,
693
- itemBrick.animation || {},
694
- `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
695
- ),
696
- outlet: compileOutlets(
697
- itemBrick.templateKey,
698
- itemBrick.outlets || {},
699
- `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
700
- ),
701
- eventMap: compileEvents(itemBrick.templateKey, itemBrick.eventMap || {}, {
702
- camelCase: true,
703
- errorReference: `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
704
- }),
705
- stateGroup: itemBrick.stateGroup.reduce((acc, stateGroup, stateGroupIndex) => {
706
- const stateGroupId = assertEntryId(
707
- stateGroup.id,
708
- 'BRICK_STATE_GROUP',
709
- `(brick: ${brickId}, ${key}: ${index}, switch index: ${stateGroupIndex}, subspace ${subspaceId})`,
710
- )
711
-
712
- acc[stateGroupId] = {
713
- title: stateGroup.title,
714
- description: stateGroup.description,
715
- override: stateGroup.override,
716
- break: stateGroup.break,
717
- commented: stateGroup.disabled,
718
- conds: compileSwitchConds(
719
- itemBrick.templateKey,
720
- stateGroup.conds || [],
721
- `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
722
- ),
723
- property: compileProperty(
724
- stateGroup.property,
725
- `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
726
- ),
727
- animation: compileAnimations(
728
- itemBrick.templateKey,
729
- stateGroup.animation || {},
730
- `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
731
- ),
732
- outlet: compileOutlets(
733
- itemBrick.templateKey,
734
- stateGroup.outlets || {},
735
- `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
736
- ),
737
- eventMap: compileEvents(itemBrick.templateKey, stateGroup.eventMap || {}, {
738
- camelCase: true,
739
- errorReference: `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
740
- }),
741
- }
742
- return acc
743
- }, {}),
744
- show: itemBrick.show,
745
- pressToOpenDetail: itemBrick.pressToOpenDetail,
746
- pressToBackList: itemBrick.pressToBackList,
747
- })
748
- if (Array.isArray(brickItems.brickList)) {
749
- const brickList = (brickItems.brickList || []).map((item, index) =>
750
- buildList(item, index, 'brickList'),
751
- )
752
- property.brickList = brickList
753
- } else if (!brickItems.brickList) {
754
- property.brickList = []
755
- } else {
756
- // Not supported Data for brickList
757
- throw new TypeError('Not supported Data for brickList directly')
758
- }
759
- if (Array.isArray(brickItems.brickDetails)) {
760
- const brickDetails = (brickItems.brickDetails || []).map((item, index) =>
761
- buildList(item, index, 'brickDetails'),
762
- )
763
- property.brickDetails = brickDetails
764
- } else if (!brickItems.brickDetails) {
765
- property.brickDetails = []
766
- } else {
767
- // Not supported Data for brickList
768
- throw new TypeError('Not supported Data for brickList directly')
769
- }
770
- }
771
- map[brickId] = {
772
- template_key: brick.templateKey,
773
- alias: brick.alias,
774
- title: brick.title,
775
- description: brick.description,
776
- hide_short_ref: brick.hideShortRef,
777
- property,
778
- animation: compileAnimations(
779
- brick.templateKey,
780
- brick.animation || {},
781
- `(brick: ${brickId}, subspace ${subspaceId})`,
782
- ),
783
- event_map: compileEvents(brick.templateKey, brick.events || {}, {
784
- camelCase: false,
785
- errorReference: `(brick: ${brickId}, subspace ${subspaceId})`,
786
- }),
787
- outlet: compileOutlets(
788
- brick.templateKey,
789
- brick.outlets || {},
790
- `(brick: ${brickId}, subspace ${subspaceId})`,
791
- ),
792
- state_group: brick.switches?.reduce((acc, switchCase, switchIndex) => {
793
- const switchId = assertEntryId(
794
- switchCase.id,
795
- 'BRICK_STATE_GROUP',
796
- `(brick: ${brickId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
797
- )
798
-
799
- acc[switchId] = {
800
- title: switchCase.title,
801
- description: switchCase.description,
802
- break: switchCase.break,
803
- override: switchCase.override,
804
- commented: switchCase.disabled,
805
- conds: compileSwitchConds(
806
- brick.templateKey,
807
- switchCase.conds || [],
808
- `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
809
- ),
810
- property: compileProperty(
811
- switchCase.property,
812
- `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
813
- ),
814
- outlet: compileOutlets(
815
- brick.templateKey,
816
- switchCase.outlets || {},
817
- `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
818
- ),
819
- event_map: compileEvents(brick.templateKey, switchCase.events || {}, {
820
- camelCase: false,
821
- errorReference: `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
822
- }),
823
- animation: compileAnimations(
824
- brick.templateKey,
825
- switchCase.animation || {},
826
- `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
827
- ),
828
- }
829
- return acc
830
- }, {}),
831
- }
832
- return map
833
- }, {}),
834
- root_canvas_id: rootCanvasId,
835
- canvas_map: subspace.canvases.reduce((map, canvas, canvasIndex) => {
836
- const canvasId = assertEntryId(
837
- canvas.id,
838
- 'CANVAS',
839
- `(canvas index: ${canvasIndex}, subspace: ${subspaceId})`,
840
- )
841
-
842
- map[canvasId] = {
843
- alias: canvas.alias,
844
- title: canvas.title,
845
- description: canvas.description,
846
- hide_short_ref: canvas.hideShortRef,
847
- property: compileProperty(
848
- canvas.property,
849
- `(canvas: ${canvasId}, subspace ${subspaceId})`,
850
- ),
851
- event_map: compileEvents('CANVAS', canvas.events || {}, {
852
- camelCase: false,
853
- errorReference: `(canvas: ${canvasId}, subspace ${subspaceId})`,
854
- }),
855
- state_group: canvas.switches?.reduce((acc, switchCase, switchIndex) => {
856
- const switchId = assertEntryId(
857
- switchCase.id,
858
- 'BRICK_STATE_GROUP',
859
- `(canvas: ${canvasId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
860
- )
861
-
862
- acc[switchId] = {
863
- title: switchCase.title,
864
- description: switchCase.description,
865
- break: switchCase.break,
866
- override: switchCase.override,
867
- commented: switchCase.disabled,
868
- conds: compileSwitchConds(
869
- 'CANVAS',
870
- switchCase.conds || [],
871
- `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
872
- ),
873
- property: compileProperty(
874
- switchCase.property,
875
- `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
876
- ),
877
- event_map: compileEvents('CANVAS', switchCase.events || {}, {
878
- camelCase: false,
879
- errorReference: `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
880
- }),
881
- animation: compileAnimations(
882
- 'CANVAS',
883
- switchCase.animation || {},
884
- `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
885
- ),
886
- }
887
- return acc
888
- }, {}),
889
- item_list: canvas.items.map((item, index) => {
890
- let itemPayload = {}
891
- if (typeof item.item === 'function') {
892
- const brick = (item.item as () => Brick)()
893
- if (!brick?.id)
894
- throw new Error(
895
- `Invalid canvas item index: ${index}, brick not found (canvas: ${canvasId}, subspace ${subspaceId})`,
896
- )
897
- itemPayload = { brick_id: brick.id }
898
- } else {
899
- const targetSubspaceId = item.item
900
- if (!subspaceIdSet.has(targetSubspaceId))
901
- throw new Error(
902
- `Invalid canvas item index: ${index}, subspace not found (canvas: ${canvasId}, subspace ${subspaceId})`,
903
- )
904
- itemPayload = { subspace_id: targetSubspaceId }
905
- }
906
- return {
907
- type: typeof item.item === 'function' ? 'brick' : 'subspace',
908
- ...itemPayload,
909
- frame: compileFrame(item.frame),
910
- hidden: item.hidden,
911
- }
912
- }),
913
- }
914
- return map
915
- }, {}),
916
- generator_map: subspace.generators.reduce((map, generator, generatorIndex) => {
917
- const generatorId = assertEntryId(
918
- generator.id,
919
- 'GENERATOR',
920
- `(generator index: ${generatorIndex}, subspace: ${subspaceId})`,
921
- )
922
-
923
- map[generatorId] = {
924
- template_key: generator.templateKey,
925
- alias: generator.alias,
926
- title: generator.title,
927
- description: generator.description,
928
- hide_short_ref: generator.hideShortRef,
929
- local_sync: generator.localSyncRunMode
930
- ? {
931
- run_mode: generator.localSyncRunMode,
932
- }
933
- : undefined,
934
- property: compileProperty(
935
- generator.property || {},
936
- `(generator: ${generatorId}, subspace ${subspaceId})`,
937
- ),
938
- event_map: compileEvents(generator.templateKey, generator.events || {}, {
939
- camelCase: false,
940
- errorReference: `(generator: ${generatorId}, subspace ${subspaceId})`,
941
- }),
942
- outlet: compileOutlets(
943
- generator.templateKey,
944
- generator.outlets || {},
945
- `(generator: ${generatorId}, subspace ${subspaceId})`,
946
- ),
947
- state_group: generator.switches?.reduce((acc, switchCase, switchIndex) => {
948
- const switchId = assertEntryId(
949
- switchCase.id,
950
- 'BRICK_STATE_GROUP',
951
- `(generator: ${generatorId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
952
- )
953
-
954
- acc[switchId] = {
955
- title: switchCase.title,
956
- description: switchCase.description,
957
- break: switchCase.break,
958
- override: switchCase.override,
959
- commented: switchCase.disabled,
960
- conds: compileSwitchConds(
961
- generator.templateKey,
962
- switchCase.conds || [],
963
- `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
964
- ),
965
- property: compileProperty(
966
- switchCase.property,
967
- `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
968
- ),
969
- outlet: compileOutlets(
970
- generator.templateKey,
971
- switchCase.outlets || {},
972
- `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
973
- ),
974
- event_map: compileEvents(generator.templateKey, switchCase.events || {}, {
975
- camelCase: false,
976
- errorReference: `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
977
- }),
978
- animation: compileAnimations(
979
- generator.templateKey,
980
- switchCase.animation || {},
981
- `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
982
- ),
983
- }
984
- return acc
985
- }, {}),
986
- }
987
- return map
988
- }, {}),
989
- property_bank_map: subspace.data.reduce((map, data, dataIndex) => {
990
- const dataId = assertEntryId(
991
- data.id,
992
- 'PROPERTY_BANK_DATA_NODE',
993
- `(data index: ${dataIndex}, subspace: ${subspaceId})`,
994
- )
995
-
996
- map[dataId] = {
997
- alias: data.alias,
998
- title: data.title,
999
- description: data.description,
1000
- hide_short_ref: data.hideShortRef,
1001
- linked: data.metadata?.linked,
1002
- linkedFrom: data.metadata?.linkedFrom,
1003
- local_sync: data.localSyncUpdateMode
1004
- ? {
1005
- update_mode: data.localSyncUpdateMode,
1006
- }
1007
- : undefined,
1008
- persist_data: data.persistData,
1009
- ...compileRemoteUpdate(data.remoteUpdate),
1010
- routing: data.routing,
1011
- schema: data.schema,
1012
- type: data.type,
1013
- ...compileKind(data.kind),
1014
- value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
1015
- event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
1016
- camelCase: false,
1017
- errorReference: `(data: ${dataId}, subspace ${subspaceId})`,
1018
- }),
1019
- hit_equal: data.hit_equal,
1020
- hit_regex: data.hit_regex,
1021
- }
1022
- return map
1023
- }, {}),
1024
- property_bank_calc_map: subspace.dataCalculation.reduce((map, dataCalc, dataCalcIndex) => {
1025
- const dataCalcId = assertEntryId(
1026
- dataCalc.id,
1027
- 'PROPERTY_BANK_COMMAND_MAP',
1028
- `(data calc index: ${dataCalcIndex}, subspace: ${subspaceId})`,
1029
- )
1030
-
1031
- const calc: any = {
1032
- title: dataCalc.title,
1033
- description: dataCalc.description,
1034
- hide_short_ref: dataCalc.hideShortRef,
1035
- }
1036
- if (dataCalc.triggerMode) calc.trigger_type = dataCalc.triggerMode
1037
- if (dataCalc.__typename === 'DataCalculationMap') {
1038
- calc.type = 'general'
1039
- const mapCalc = dataCalc as DataCalculationMap
1040
-
1041
- const getNodeId = (
1042
- node: DataCalculationData | DataCommand,
1043
- reference = '',
1044
- nodeIndex?: number,
1045
- ) => {
1046
- const nodeReference = [
1047
- `data calc: ${dataCalcId}`,
1048
- `subspace: ${subspaceId}`,
1049
- typeof nodeIndex === 'number' ? `node index: ${nodeIndex}` : undefined,
1050
- reference || undefined,
1051
- ]
1052
- .filter(Boolean)
1053
- .join(', ')
1054
-
1055
- if (node.__typename === 'DataCalculationData') {
1056
- return assertEntryId(
1057
- node.data()?.id,
1058
- 'PROPERTY_BANK_DATA_NODE',
1059
- `(${nodeReference})`,
1060
- )
1061
- }
1062
- if (node.__typename === 'DataCommand') {
1063
- return assertEntryId(node.id, 'PROPERTY_BANK_COMMAND_NODE', `(${nodeReference})`)
1064
- }
1065
- throw new Error(`Invalid node: ${JSON.stringify(node)}`)
1066
- }
1067
-
1068
- const generateInputPorts = (inputs) =>
1069
- inputs.reduce((acc, port, portIndex) => {
1070
- if (!acc[port.key]) acc[port.key] = null
1071
-
1072
- let sourceId
1073
- const sourceNode = port.source()
1074
- if (
1075
- sourceNode?.__typename === 'DataCalculationData' ||
1076
- sourceNode?.__typename === 'DataCommand'
1077
- ) {
1078
- sourceId = getNodeId(sourceNode, `input port index: ${portIndex}`)
1079
- }
1080
-
1081
- if (!sourceId) return acc
1082
- if (!acc[port.key]) acc[port.key] = []
1083
-
1084
- acc[port.key].push({
1085
- id: sourceId,
1086
- port: port.sourceKey,
1087
- disable_trigger_command: !port.trigger ? true : undefined,
1088
- })
1089
- return acc
1090
- }, {})
1091
-
1092
- const generateOutputPorts = (outputs) =>
1093
- outputs.reduce((acc, port, portIndex) => {
1094
- if (!acc[port.key]) acc[port.key] = null
1095
-
1096
- let targetId
1097
- const targetNode = port.target()
1098
- if (
1099
- targetNode?.__typename === 'DataCalculationData' ||
1100
- targetNode?.__typename === 'DataCommand'
1101
- ) {
1102
- targetId = getNodeId(targetNode, `output port index: ${portIndex}`)
1103
- }
1104
-
1105
- if (!targetId) return acc
1106
- if (!acc[port.key]) acc[port.key] = []
1107
-
1108
- acc[port.key].push({
1109
- id: targetId,
1110
- port: port.targetKey,
1111
- })
1112
- return acc
1113
- }, {})
1114
-
1115
- calc.map = mapCalc.nodes.reduce((acc, node, nodeIndex) => {
1116
- if (node.__typename === 'DataCalculationData') {
1117
- const dataNode = node as DataCalculationData
1118
- acc[getNodeId(dataNode, 'data node', nodeIndex)] = {
1119
- title: dataNode.title,
1120
- description: dataNode.description,
1121
- hide_short_ref: dataNode.hideShortRef,
1122
- type: 'data-node',
1123
- properties: {},
1124
- in: generateInputPorts(dataNode.inputs),
1125
- out: generateOutputPorts(dataNode.outputs),
1126
- }
1127
- } else if (node.__typename === 'DataCommand') {
1128
- const commandNode = node as DataCommand
1129
- const commandName = commandNode.__commandName
1130
- const type = commandName.split('_')[0].toLowerCase()
1131
-
1132
- const args = commandNode.inputs.filter(
1133
- (input) =>
1134
- typeof input.source !== 'function' ||
1135
- (input.source()?.__typename !== 'DataCalculationData' &&
1136
- input.source()?.__typename !== 'DataCommand'),
1137
- )
1138
- const inputs = commandNode.inputs.filter(
1139
- (input) =>
1140
- typeof input.source === 'function' &&
1141
- (input.source()?.__typename === 'DataCalculationData' ||
1142
- input.source()?.__typename === 'DataCommand'),
1143
- )
1144
- acc[getNodeId(commandNode, 'command node', nodeIndex)] = {
1145
- title: commandNode.title,
1146
- description: commandNode.description,
1147
- hide_short_ref: commandNode.hideShortRef,
1148
- type: `command-node-${type}`,
1149
- properties: {
1150
- command: commandNode.__commandName,
1151
- args: args.reduce((argsAcc, input) => {
1152
- argsAcc[input.key] = input.source
1153
- return argsAcc
1154
- }, {}),
1155
- },
1156
- in: generateInputPorts(inputs),
1157
- out: generateOutputPorts(commandNode.outputs),
1158
- }
1159
- }
1160
- return acc
1161
- }, {})
1162
- calc.editor_info = mapCalc.editorInfo.reduce((acc, editorInfo) => {
1163
- acc[getNodeId(editorInfo.node, 'editor info node')] = {
1164
- position: editorInfo.position,
1165
- points: editorInfo.points.reduce((pointsAcc, point) => {
1166
- const sourceId = getNodeId(point.source, 'editor info point source')
1167
- const targetId = getNodeId(point.target, 'editor info point target')
1168
- const key = `${sourceId}-${point.sourceOutputKey}-${targetId}-${point.targetInputKey}`
1169
- pointsAcc[key] = point.positions
1170
- return pointsAcc
1171
- }, {}),
1172
- }
1173
- return acc
1174
- }, {})
1175
- } else if (dataCalc.__typename === 'DataCalculationScript') {
1176
- const scriptCalc = dataCalc as DataCalculationScript
1177
- calc.type = 'script'
1178
-
1179
- const code = compileScriptCalculationCode(scriptCalc.code)
1180
- calc.script_config = {
1181
- title: scriptCalc.title ?? '',
1182
- note: scriptCalc.note ?? '',
1183
- code,
1184
- enable_async: scriptCalc.enableAsync,
1185
- trigger_mode: scriptCalc.triggerMode,
1186
- inputs: scriptCalc.inputs.reduce((acc, input) => {
1187
- const inputId = assertEntryId(
1188
- input.data()?.id,
1189
- 'PROPERTY_BANK_DATA_NODE',
1190
- `(data calc: ${dataCalcId}, script input: ${input.key}, subspace: ${subspaceId})`,
1191
- )
1192
- acc[inputId] = input.key
1193
- return acc
1194
- }, {}),
1195
- disabled_triggers: scriptCalc.inputs.reduce((acc, input) => {
1196
- const inputId = assertEntryId(
1197
- input.data()?.id,
1198
- 'PROPERTY_BANK_DATA_NODE',
1199
- `(data calc: ${dataCalcId}, script trigger input: ${input.key}, subspace: ${subspaceId})`,
1200
- )
1201
- acc[inputId] = !input.trigger
1202
- return acc
1203
- }, {}),
1204
- output: scriptCalc.output
1205
- ? assertEntryId(
1206
- scriptCalc.output()?.id,
1207
- 'PROPERTY_BANK_DATA_NODE',
1208
- `(data calc: ${dataCalcId}, script output, subspace: ${subspaceId})`,
1209
- )
1210
- : null,
1211
- outputs: scriptCalc.outputs.reduce((acc, output) => {
1212
- if (!acc[output.key]) acc[output.key] = []
1213
- const outputId = assertEntryId(
1214
- output.data()?.id,
1215
- 'PROPERTY_BANK_DATA_NODE',
1216
- `(data calc: ${dataCalcId}, script outputs key: ${output.key}, subspace: ${subspaceId})`,
1217
- )
1218
- acc[output.key].push(outputId)
1219
- return acc
1220
- }, {}),
1221
- error: scriptCalc.error
1222
- ? assertEntryId(
1223
- scriptCalc.error()?.id,
1224
- 'PROPERTY_BANK_DATA_NODE',
1225
- `(data calc: ${dataCalcId}, script error output, subspace: ${subspaceId})`,
1226
- )
1227
- : null,
1228
- }
1229
-
1230
- Object.assign(
1231
- calc,
1232
- generateCalulationMap(calc.script_config, {
1233
- snapshotMode: process.env.BRICKS_SNAPSHOT_MODE === '1',
1234
- }),
1235
- )
1236
- }
1237
- map[dataCalcId] = calc
1238
- return map
1239
- }, {}),
1240
- action_map: subspace.actions || undefined,
1241
- event_map: compileEvents('', subspace.events || {}, {
1242
- camelCase: false,
1243
- errorReference: `(subspace ${subspaceId})`,
1244
- }),
1245
- routing: subspace.dataRouting.reduce((acc, data, index) => {
1246
- const dataId = assertEntryId(
1247
- data?.id,
1248
- 'PROPERTY_BANK_DATA_NODE',
1249
- `(data routing index: ${index}, subspace ${subspaceId})`,
1250
- )
1251
- acc[dataId] = { enabled_routing: true }
1252
- return acc
1253
- }, {}),
1254
- ...compileModule(subspace),
1255
- }
1256
- return subspaceMap
1257
- }, {}),
1258
- root_subspace_id: assertEntryId(
1259
- app.rootSubspace?.id,
1260
- 'SUBSPACE',
1261
- '(application root subspace)',
1262
- ),
1263
- fonts: app.fonts,
1264
- ...compileApplicationSettings(app.settings),
1265
- // Use typed automationMap if available, otherwise fall back to TEMP metadata
1266
- test_map: compiledAutomationMap
1267
- ? compiledAutomationMap['AUTOMATION_MAP_DEFAULT']?.map || {}
1268
- : app.metadata?.TEMP_test_map || {},
1269
- automation_map: compiledAutomationMap || app.metadata?.TEMP_automation_map || {},
1270
- update_timestamp: timestamp,
1271
- }
1272
- return config
1273
- }
1274
-
1275
- export const checkConfig = async (configPath: string) => {
1276
- const { sh } = await import('../tools/_shell')
1277
- await sh`bricks app check-config ${configPath}`
1278
- }