@fugood/bricks-ctor 2.25.0-beta.6 → 2.25.0-beta.61

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 (133) hide show
  1. package/package.json +4 -24
  2. package/tools/deploy.ts +19 -165
  3. package/tools/mcp-server.ts +17 -26
  4. package/tools/postinstall.ts +21 -235
  5. package/tools/pull.ts +18 -121
  6. package/tools/push-config.ts +19 -0
  7. package/tools/simulator.ts +21 -0
  8. package/compile/action-name-map.ts +0 -1015
  9. package/compile/index.ts +0 -1278
  10. package/compile/util.ts +0 -358
  11. package/index.ts +0 -6
  12. package/skills/bricks-ctor/SKILL.md +0 -32
  13. package/skills/bricks-ctor/rules/animation.md +0 -159
  14. package/skills/bricks-ctor/rules/architecture-patterns.md +0 -69
  15. package/skills/bricks-ctor/rules/automations.md +0 -221
  16. package/skills/bricks-ctor/rules/buttress.md +0 -156
  17. package/skills/bricks-ctor/rules/data-calculation.md +0 -209
  18. package/skills/bricks-ctor/rules/local-sync.md +0 -129
  19. package/skills/bricks-ctor/rules/media-flow.md +0 -158
  20. package/skills/bricks-ctor/rules/remote-data-bank.md +0 -196
  21. package/skills/bricks-ctor/rules/standby-transition.md +0 -124
  22. package/skills/bricks-design/LICENSE.txt +0 -180
  23. package/skills/bricks-design/SKILL.md +0 -66
  24. package/skills/rive-marketplace/SKILL.md +0 -99
  25. package/tools/_git-author.ts +0 -29
  26. package/tools/_shell.ts +0 -173
  27. package/tools/icons/.gitattributes +0 -1
  28. package/tools/icons/fa6pro-glyphmap.json +0 -4686
  29. package/tools/icons/fa6pro-meta.json +0 -1
  30. package/tools/mcp-tools/compile.ts +0 -92
  31. package/tools/mcp-tools/huggingface.ts +0 -762
  32. package/tools/mcp-tools/icons.ts +0 -81
  33. package/tools/mcp-tools/lottie.ts +0 -102
  34. package/tools/mcp-tools/media.ts +0 -110
  35. package/tools/preview-main.mjs +0 -293
  36. package/tools/preview.ts +0 -150
  37. package/types/animation.ts +0 -100
  38. package/types/automation.ts +0 -235
  39. package/types/brick-base.ts +0 -80
  40. package/types/bricks/Camera.ts +0 -246
  41. package/types/bricks/Chart.ts +0 -372
  42. package/types/bricks/GenerativeMedia.ts +0 -290
  43. package/types/bricks/Icon.ts +0 -98
  44. package/types/bricks/Image.ts +0 -114
  45. package/types/bricks/Items.ts +0 -476
  46. package/types/bricks/Lottie.ts +0 -168
  47. package/types/bricks/Maps.ts +0 -262
  48. package/types/bricks/QrCode.ts +0 -117
  49. package/types/bricks/Rect.ts +0 -150
  50. package/types/bricks/RichText.ts +0 -128
  51. package/types/bricks/Rive.ts +0 -220
  52. package/types/bricks/Sketch.ts +0 -254
  53. package/types/bricks/Slideshow.ts +0 -201
  54. package/types/bricks/Svg.ts +0 -99
  55. package/types/bricks/Text.ts +0 -148
  56. package/types/bricks/TextInput.ts +0 -242
  57. package/types/bricks/Video.ts +0 -175
  58. package/types/bricks/VideoStreaming.ts +0 -112
  59. package/types/bricks/WebRtcStream.ts +0 -65
  60. package/types/bricks/WebView.ts +0 -168
  61. package/types/bricks/index.ts +0 -22
  62. package/types/canvas.ts +0 -82
  63. package/types/common.ts +0 -144
  64. package/types/data-calc-command.ts +0 -7005
  65. package/types/data-calc-script.ts +0 -21
  66. package/types/data-calc.ts +0 -11
  67. package/types/data.ts +0 -95
  68. package/types/generators/AlarmClock.ts +0 -110
  69. package/types/generators/Assistant.ts +0 -621
  70. package/types/generators/BleCentral.ts +0 -247
  71. package/types/generators/BlePeripheral.ts +0 -208
  72. package/types/generators/CanvasMap.ts +0 -74
  73. package/types/generators/CastlesPay.ts +0 -87
  74. package/types/generators/DataBank.ts +0 -160
  75. package/types/generators/File.ts +0 -432
  76. package/types/generators/GraphQl.ts +0 -132
  77. package/types/generators/Http.ts +0 -222
  78. package/types/generators/HttpServer.ts +0 -176
  79. package/types/generators/Information.ts +0 -103
  80. package/types/generators/Intent.ts +0 -168
  81. package/types/generators/Iterator.ts +0 -108
  82. package/types/generators/Keyboard.ts +0 -105
  83. package/types/generators/LlmAnthropicCompat.ts +0 -212
  84. package/types/generators/LlmAppleBuiltin.ts +0 -159
  85. package/types/generators/LlmGgml.ts +0 -861
  86. package/types/generators/LlmMediaTekNeuroPilot.ts +0 -235
  87. package/types/generators/LlmMlx.ts +0 -227
  88. package/types/generators/LlmOnnx.ts +0 -213
  89. package/types/generators/LlmOpenAiCompat.ts +0 -312
  90. package/types/generators/LlmQualcommAiEngine.ts +0 -247
  91. package/types/generators/Mcp.ts +0 -637
  92. package/types/generators/McpServer.ts +0 -289
  93. package/types/generators/MediaFlow.ts +0 -170
  94. package/types/generators/MqttBroker.ts +0 -141
  95. package/types/generators/MqttClient.ts +0 -141
  96. package/types/generators/Question.ts +0 -408
  97. package/types/generators/RealtimeTranscription.ts +0 -287
  98. package/types/generators/RerankerGgml.ts +0 -191
  99. package/types/generators/SerialPort.ts +0 -151
  100. package/types/generators/SoundPlayer.ts +0 -94
  101. package/types/generators/SoundRecorder.ts +0 -130
  102. package/types/generators/SpeechToTextGgml.ts +0 -419
  103. package/types/generators/SpeechToTextOnnx.ts +0 -236
  104. package/types/generators/SpeechToTextPlatform.ts +0 -85
  105. package/types/generators/SqLite.ts +0 -159
  106. package/types/generators/Step.ts +0 -107
  107. package/types/generators/SttAppleBuiltin.ts +0 -130
  108. package/types/generators/Tcp.ts +0 -126
  109. package/types/generators/TcpServer.ts +0 -147
  110. package/types/generators/TextToSpeechAppleBuiltin.ts +0 -127
  111. package/types/generators/TextToSpeechGgml.ts +0 -221
  112. package/types/generators/TextToSpeechOnnx.ts +0 -178
  113. package/types/generators/TextToSpeechOpenAiLike.ts +0 -121
  114. package/types/generators/ThermalPrinter.ts +0 -191
  115. package/types/generators/Tick.ts +0 -83
  116. package/types/generators/Udp.ts +0 -120
  117. package/types/generators/VadGgml.ts +0 -250
  118. package/types/generators/VadOnnx.ts +0 -231
  119. package/types/generators/VadTraditional.ts +0 -138
  120. package/types/generators/VectorStore.ts +0 -257
  121. package/types/generators/Watchdog.ts +0 -107
  122. package/types/generators/WebCrawler.ts +0 -103
  123. package/types/generators/WebRtc.ts +0 -181
  124. package/types/generators/WebSocket.ts +0 -148
  125. package/types/generators/index.ts +0 -57
  126. package/types/index.ts +0 -13
  127. package/types/subspace.ts +0 -60
  128. package/types/switch.ts +0 -51
  129. package/types/system.ts +0 -707
  130. package/utils/calc.ts +0 -126
  131. package/utils/data.ts +0 -497
  132. package/utils/event-props.ts +0 -886
  133. 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
- }