@fugood/bricks-project 2.24.0-beta.4 → 2.24.0-beta.41

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 (126) hide show
  1. package/compile/action-name-map.ts +38 -0
  2. package/compile/index.ts +429 -160
  3. package/compile/util.ts +2 -0
  4. package/package.json +7 -3
  5. package/package.json.bak +27 -0
  6. package/skills/{bricks-project → bricks-ctor}/SKILL.md +2 -2
  7. package/skills/{bricks-project → bricks-ctor}/rules/animation.md +1 -1
  8. package/skills/{bricks-project → bricks-ctor}/rules/architecture-patterns.md +7 -0
  9. package/skills/bricks-ctor/rules/automations.md +221 -0
  10. package/skills/{bricks-project → bricks-ctor}/rules/buttress.md +10 -7
  11. package/skills/{bricks-project → bricks-ctor}/rules/data-calculation.md +1 -1
  12. package/skills/{bricks-project → bricks-ctor}/rules/local-sync.md +2 -2
  13. package/skills/{bricks-project → bricks-ctor}/rules/media-flow.md +3 -3
  14. package/skills/{bricks-project → bricks-ctor}/rules/remote-data-bank.md +6 -6
  15. package/skills/{bricks-project → bricks-ctor}/rules/standby-transition.md +1 -1
  16. package/skills/bricks-design/LICENSE.txt +180 -0
  17. package/skills/bricks-design/SKILL.md +66 -0
  18. package/tools/deploy.ts +66 -12
  19. package/tools/icons/fa6pro-meta.json +3669 -26125
  20. package/tools/mcp-server.ts +11 -878
  21. package/tools/mcp-tools/compile.ts +91 -0
  22. package/tools/mcp-tools/huggingface.ts +762 -0
  23. package/tools/mcp-tools/icons.ts +70 -0
  24. package/tools/mcp-tools/lottie.ts +102 -0
  25. package/tools/mcp-tools/media.ts +110 -0
  26. package/tools/postinstall.ts +137 -40
  27. package/tools/preview-main.mjs +146 -9
  28. package/tools/preview.ts +30 -2
  29. package/tools/pull.ts +37 -19
  30. package/tsconfig.json +16 -0
  31. package/types/animation.ts +4 -0
  32. package/types/automation.ts +4 -1
  33. package/types/brick-base.ts +1 -1
  34. package/types/bricks/Camera.ts +48 -13
  35. package/types/bricks/Chart.ts +10 -4
  36. package/types/bricks/GenerativeMedia.ts +30 -14
  37. package/types/bricks/Icon.ts +9 -5
  38. package/types/bricks/Image.ts +10 -6
  39. package/types/bricks/Items.ts +29 -15
  40. package/types/bricks/Lottie.ts +15 -7
  41. package/types/bricks/Maps.ts +16 -8
  42. package/types/bricks/QrCode.ts +9 -5
  43. package/types/bricks/Rect.ts +45 -6
  44. package/types/bricks/RichText.ts +9 -5
  45. package/types/bricks/Rive.ts +21 -11
  46. package/types/bricks/Slideshow.ts +20 -10
  47. package/types/bricks/Svg.ts +8 -4
  48. package/types/bricks/Text.ts +9 -5
  49. package/types/bricks/TextInput.ts +23 -13
  50. package/types/bricks/Video.ts +11 -7
  51. package/types/bricks/VideoStreaming.ts +8 -4
  52. package/types/bricks/WebRtcStream.ts +7 -3
  53. package/types/bricks/WebView.ts +12 -8
  54. package/types/canvas.ts +4 -2
  55. package/types/common.ts +19 -12
  56. package/types/data-calc-command.ts +2 -0
  57. package/types/data-calc.ts +1 -0
  58. package/types/data.ts +2 -0
  59. package/types/generators/AlarmClock.ts +17 -11
  60. package/types/generators/Assistant.ts +69 -18
  61. package/types/generators/BleCentral.ts +31 -11
  62. package/types/generators/BlePeripheral.ts +11 -7
  63. package/types/generators/CanvasMap.ts +10 -6
  64. package/types/generators/CastlesPay.ts +15 -7
  65. package/types/generators/DataBank.ts +44 -9
  66. package/types/generators/File.ts +109 -30
  67. package/types/generators/GraphQl.ts +12 -6
  68. package/types/generators/Http.ts +33 -10
  69. package/types/generators/HttpServer.ts +23 -15
  70. package/types/generators/Information.ts +9 -5
  71. package/types/generators/Intent.ts +15 -5
  72. package/types/generators/Iterator.ts +15 -11
  73. package/types/generators/Keyboard.ts +27 -13
  74. package/types/generators/LlmAnthropicCompat.ts +33 -11
  75. package/types/generators/LlmAppleBuiltin.ts +25 -10
  76. package/types/generators/LlmGgml.ts +140 -31
  77. package/types/generators/LlmMediaTekNeuroPilot.ts +235 -0
  78. package/types/generators/LlmMlx.ts +227 -0
  79. package/types/generators/LlmOnnx.ts +34 -14
  80. package/types/generators/LlmOpenAiCompat.ts +47 -11
  81. package/types/generators/LlmQualcommAiEngine.ts +45 -13
  82. package/types/generators/Mcp.ts +375 -34
  83. package/types/generators/McpServer.ts +58 -19
  84. package/types/generators/MediaFlow.ts +38 -12
  85. package/types/generators/MqttBroker.ts +29 -11
  86. package/types/generators/MqttClient.ts +19 -9
  87. package/types/generators/Question.ts +13 -9
  88. package/types/generators/RealtimeTranscription.ts +108 -19
  89. package/types/generators/RerankerGgml.ts +43 -12
  90. package/types/generators/SerialPort.ts +18 -10
  91. package/types/generators/SoundPlayer.ts +10 -4
  92. package/types/generators/SoundRecorder.ts +24 -9
  93. package/types/generators/SpeechToTextGgml.ts +52 -18
  94. package/types/generators/SpeechToTextOnnx.ts +18 -11
  95. package/types/generators/SpeechToTextPlatform.ts +15 -7
  96. package/types/generators/SqLite.ts +20 -10
  97. package/types/generators/Step.ts +9 -5
  98. package/types/generators/SttAppleBuiltin.ts +22 -9
  99. package/types/generators/Tcp.ts +13 -9
  100. package/types/generators/TcpServer.ts +20 -14
  101. package/types/generators/TextToSpeechAppleBuiltin.ts +21 -8
  102. package/types/generators/TextToSpeechGgml.ts +29 -11
  103. package/types/generators/TextToSpeechOnnx.ts +19 -12
  104. package/types/generators/TextToSpeechOpenAiLike.ts +14 -8
  105. package/types/generators/ThermalPrinter.ts +13 -9
  106. package/types/generators/Tick.ts +11 -7
  107. package/types/generators/Udp.ts +17 -8
  108. package/types/generators/VadGgml.ts +51 -14
  109. package/types/generators/VadOnnx.ts +42 -12
  110. package/types/generators/VadTraditional.ts +28 -13
  111. package/types/generators/VectorStore.ts +33 -12
  112. package/types/generators/Watchdog.ts +19 -10
  113. package/types/generators/WebCrawler.ts +11 -7
  114. package/types/generators/WebRtc.ts +30 -16
  115. package/types/generators/WebSocket.ts +11 -7
  116. package/types/generators/index.ts +2 -0
  117. package/types/subspace.ts +3 -0
  118. package/types/system.ts +1 -1
  119. package/utils/calc.ts +12 -8
  120. package/utils/event-props.ts +833 -1022
  121. package/utils/id.ts +4 -0
  122. package/api/index.ts +0 -1
  123. package/api/instance.ts +0 -213
  124. package/skills/bricks-project/rules/automations.md +0 -175
  125. package/types/generators/TextToSpeechApple.ts +0 -113
  126. package/types/generators/TtsAppleBuiltin.ts +0 -105
package/compile/index.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  /* eslint-disable no-underscore-dangle -- Uses __typename, __actionName, etc. for type system */
2
- import _ from 'lodash'
2
+ import camelCase from 'lodash/camelCase'
3
+ import upperFirst from 'lodash/upperFirst'
4
+ import snakeCase from 'lodash/snakeCase'
5
+ import omit from 'lodash/omit'
3
6
  import { parse as parseAST } from 'acorn'
4
7
  import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
5
8
  import escodegen from 'escodegen'
@@ -33,6 +36,40 @@ import type {
33
36
  TestVariable,
34
37
  } from '../types'
35
38
 
39
+ 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}'
40
+
41
+ const entryIdPatterns = {
42
+ SUBSPACE: new RegExp(`^SUBSPACE_${uuidPattern}$`),
43
+ CANVAS: new RegExp(`^CANVAS_${uuidPattern}$`),
44
+ BRICK: new RegExp(`^BRICK_${uuidPattern}$`),
45
+ GENERATOR: new RegExp(`^(GENERATOR|AUTO_GENERATOR)_${uuidPattern}$`),
46
+ ANIMATION: new RegExp(`^ANIMATION_${uuidPattern}$`),
47
+ PROPERTY_BANK_DATA_NODE: new RegExp(`^PROPERTY_BANK_DATA_NODE_${uuidPattern}$`),
48
+ PROPERTY_BANK_COMMAND_NODE: new RegExp(`^PROPERTY_BANK_COMMAND_NODE_${uuidPattern}$`),
49
+ PROPERTY_BANK_COMMAND_MAP: new RegExp(`^PROPERTY_BANK_COMMAND_MAP_${uuidPattern}$`),
50
+ BRICK_STATE_GROUP: new RegExp(`^BRICK_STATE_GROUP_${uuidPattern}$`),
51
+ TEST: new RegExp(`^TEST_${uuidPattern}$`),
52
+ TEST_CASE: new RegExp(`^TEST_CASE_${uuidPattern}$`),
53
+ TEST_VAR: new RegExp(`^TEST_VAR_${uuidPattern}$`),
54
+ AUTOMATION_MAP: /^AUTOMATION_MAP_.*/,
55
+ } as const
56
+
57
+ type EntryIdPatternKey = keyof typeof entryIdPatterns
58
+
59
+ const assertEntryId = (
60
+ id: unknown,
61
+ patternKey: EntryIdPatternKey,
62
+ errorReference: string = '',
63
+ ): string => {
64
+ const pattern = entryIdPatterns[patternKey]
65
+ if (typeof id !== 'string' || !pattern.test(id)) {
66
+ throw new Error(
67
+ `Invalid ${patternKey} id${errorReference ? ` ${errorReference}` : ''}: ${String(id)}`,
68
+ )
69
+ }
70
+ return id
71
+ }
72
+
36
73
  const compileProperty = (property, errorReference: string, result = {}) => {
37
74
  if (Array.isArray(property)) {
38
75
  return property.map((p) => compileProperty(p, errorReference))
@@ -56,21 +93,46 @@ const compileProperty = (property, errorReference: string, result = {}) => {
56
93
  return property
57
94
  }
58
95
 
96
+ const compileScriptCalculationCode = (code = '') => {
97
+ try {
98
+ const program = parseAST(code, { sourceType: 'module', ecmaVersion: 2020 })
99
+ // export function main() { ... }
100
+ const declarationBody = (
101
+ (program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration
102
+ )?.body
103
+ return escodegen.generate(declarationBody, {
104
+ format: {
105
+ indent: { style: ' ' },
106
+ semicolons: false,
107
+ },
108
+ comment: true,
109
+ })
110
+ } catch {
111
+ return code || ''
112
+ }
113
+ }
114
+
115
+ const getTemplateName = (key: string) =>
116
+ upperFirst(camelCase(key.replace(/^(BRICK|GENERATOR)_/, '')))
117
+
59
118
  const compileEventActionValue = (templateKey, eventKey, value, errorReference) => {
60
- const tmplEventProperties = templateEventPropsMap[templateKey]
119
+ const tmplEventProperties = templateEventPropsMap[getTemplateName(templateKey)]
61
120
  const props = tmplEventProperties?.[eventKey]
62
121
  if (!props) return compileProperty(value, errorReference)
63
- if (props.includes(value)) return value
64
- if (typeof value === 'string' && value?.startsWith(templateKey)) {
122
+ if (typeof value === 'string' && value in props) {
123
+ const expectedType = props[value]
124
+ if (expectedType) return value
125
+ }
126
+ if (typeof value === 'string' && /^(BRICK|GENERATOR)_/.test(value)) {
65
127
  console.warn(
66
- `[Warning] Value start with template key but there is no event property: ${value} ${errorReference}`,
128
+ `[Warning] Event property "${value}" is not compatible with event "${eventKey}" of ${templateKey} ${errorReference}`,
67
129
  )
68
130
  }
69
131
  return compileProperty(value, errorReference)
70
132
  }
71
133
 
72
134
  const convertOutletKey = (templateKey: string, key: string) =>
73
- `${templateKey}_${_.snakeCase(key).toUpperCase()}`
135
+ `${templateKey}_${snakeCase(key).toUpperCase()}`
74
136
 
75
137
  const compileOutlets = (
76
138
  templateKey: string,
@@ -78,13 +140,14 @@ const compileOutlets = (
78
140
  errorReference: string,
79
141
  ) =>
80
142
  Object.entries(outlets).reduce((acc, [key, data]) => {
81
- if (!data()?.id) throw new Error(`Invalid data reference ${errorReference}`)
82
- acc[convertOutletKey(templateKey, key)] = data().id
143
+ const dataInstance = data?.()
144
+ const dataId = assertEntryId(dataInstance?.id, 'PROPERTY_BANK_DATA_NODE', errorReference)
145
+ acc[convertOutletKey(templateKey, key)] = dataId
83
146
  return acc
84
147
  }, {})
85
148
 
86
149
  const convertEventKey = (templateKey: string, key: string) =>
87
- `${templateKey ? `${templateKey}_` : ''}${_.snakeCase(key).toUpperCase()}`
150
+ `${templateKey ? `${templateKey}_` : ''}${snakeCase(key).toUpperCase()}`
88
151
 
89
152
  const basicAnimationEvents = ['show', 'standby', 'breatheStart']
90
153
 
@@ -94,9 +157,9 @@ const compileAnimations = (
94
157
  errorReference: string,
95
158
  ) =>
96
159
  Object.entries(animations).reduce((acc, [key, animation]) => {
97
- if (!animation?.id) throw new Error(`Invalid animation reference ${errorReference}`)
160
+ const animationId = assertEntryId(animation?.id, 'ANIMATION', errorReference)
98
161
  acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
99
- `ANIMATION#${animation.id}`
162
+ `ANIMATION#${animationId}`
100
163
  return acc
101
164
  }, {})
102
165
 
@@ -133,7 +196,7 @@ const compileEvents = (
133
196
  if (!handlerKey) throw new Error(`Invalid handler: ${handler} ${errorReference}`)
134
197
 
135
198
  const parameterList: Array<object> = []
136
- if (Object.hasOwn(action, 'params')) {
199
+ if (Object.prototype.hasOwnProperty.call(action, 'params')) {
137
200
  const actionDef = action as ActionWithParams
138
201
  ;(actionDef.params || []).forEach(({ input, value, mapping }) => {
139
202
  const param = {
@@ -146,7 +209,7 @@ const compileEvents = (
146
209
  if (mapping) param[camelCase ? 'resultDataMapping' : 'result_data_mapping'] = true
147
210
  parameterList.push(param)
148
211
  })
149
- } else if (Object.hasOwn(action, 'dataParams')) {
212
+ } else if (Object.prototype.hasOwnProperty.call(action, 'dataParams')) {
150
213
  const actionDef = action as ActionWithDataParams
151
214
  ;(actionDef.dataParams || []).forEach(({ input, value, mapping }) => {
152
215
  if (!input) return
@@ -230,6 +293,7 @@ const compileApplicationSettings = (settings: Application['settings']) => ({
230
293
  use_gemini_api_key_system_data: settings.ai.useGeminiApiKeySystemData,
231
294
  }
232
295
  : undefined,
296
+ hide_short_refs: settings?.hideShortRefs,
233
297
  })
234
298
 
235
299
  const animationTypeMap = {
@@ -349,20 +413,28 @@ function compileRunArray(run: unknown[]): unknown[] {
349
413
  const compileTestCase = (testCase: TestCase) => ({
350
414
  id: testCase.id,
351
415
  name: testCase.name,
416
+ hide_short_ref: testCase.hideShortRef,
352
417
  run: compileRunArray(testCase.run),
353
418
  exit_on_failed: testCase.exit_on_failed,
354
419
  commented: testCase.commented,
355
420
  pre_delay: testCase.pre_delay,
356
421
  post_delay: testCase.post_delay,
357
422
  post_delay_rule: testCase.post_delay_rule,
358
- jump_cond: testCase.jump_cond.map((cond) => ({
359
- type: cond.type,
360
- status: cond.status,
361
- variable: cond.variable,
362
- operator: cond.operator,
363
- value: cond.value,
364
- jump_to: cond.jump_to,
365
- })),
423
+ jump_cond: testCase.jump_cond.map((cond) => {
424
+ if (cond.jump_to == null) {
425
+ console.warn(
426
+ `[Warning] jump_cond is missing jump_to in test case "${testCase.name}" (${testCase.id})`,
427
+ )
428
+ }
429
+ return {
430
+ type: cond.type,
431
+ status: cond.status,
432
+ variable: cond.variable,
433
+ operator: cond.operator,
434
+ value: cond.value,
435
+ jump_to: cond.jump_to,
436
+ }
437
+ }),
366
438
  })
367
439
 
368
440
  /**
@@ -380,43 +452,89 @@ const compileTestVariable = (variable: TestVariable) => ({
380
452
  */
381
453
  const arrayToIdMap = <T extends { id: string }, R>(
382
454
  items: T[],
383
- transform: (item: T) => R,
384
- ): Record<string, R> => Object.fromEntries(items.map((item) => [item.id, transform(item)]))
455
+ transform: (item: T, index: number) => R,
456
+ options: {
457
+ patternKey: EntryIdPatternKey
458
+ getErrorReference: (item: T, index: number) => string
459
+ },
460
+ ): Record<string, R> =>
461
+ Object.fromEntries(
462
+ items.map((item, index) => {
463
+ const itemId = assertEntryId(
464
+ item.id,
465
+ options.patternKey,
466
+ options.getErrorReference(item, index),
467
+ )
468
+ return [itemId, transform(item, index)]
469
+ }),
470
+ )
385
471
 
386
472
  /**
387
473
  * Compile typed AutomationTest to raw format
388
474
  */
389
- const compileAutomationTest = (test: AutomationTest) => ({
390
- id: test.id,
391
- title: test.title,
392
- timeout: test.timeout,
393
- trigger_type: test.trigger_type,
394
- cron: test.cron,
395
- skip_trigger_type_check: test.skip_trigger_type_check,
396
- local_sync: test.local_sync,
397
- meta: test.meta,
398
- case_map: arrayToIdMap(test.cases, compileTestCase),
399
- var_map: arrayToIdMap(test.variables, compileTestVariable),
400
- })
475
+ const compileAutomationTest = (
476
+ test: AutomationTest,
477
+ options: { mapId: string; testIndex: number },
478
+ ) => {
479
+ const testId = assertEntryId(
480
+ test.id,
481
+ 'TEST',
482
+ `(automation map: ${options.mapId}, test index: ${options.testIndex})`,
483
+ )
484
+
485
+ return {
486
+ id: testId,
487
+ title: test.title,
488
+ hide_short_ref: test.hideShortRef,
489
+ timeout: test.timeout,
490
+ trigger_type: test.trigger_type,
491
+ cron: test.cron,
492
+ skip_trigger_type_check: test.skip_trigger_type_check,
493
+ local_sync: test.local_sync,
494
+ meta: test.meta,
495
+ case_map: arrayToIdMap(test.cases, compileTestCase, {
496
+ patternKey: 'TEST_CASE',
497
+ getErrorReference: (_, index) =>
498
+ `(automation map: ${options.mapId}, test: ${testId}, case index: ${index})`,
499
+ }),
500
+ var_map: arrayToIdMap(test.variables, compileTestVariable, {
501
+ patternKey: 'TEST_VAR',
502
+ getErrorReference: (_, index) =>
503
+ `(automation map: ${options.mapId}, test: ${testId}, variable index: ${index})`,
504
+ }),
505
+ }
506
+ }
401
507
 
402
508
  /**
403
509
  * Compile typed AutomationTestMap to raw format
404
510
  */
405
- const compileAutomationTestMap = (testMap: AutomationTestMap) => ({
406
- title: testMap.title,
407
- createdAt: testMap.createdAt,
408
- map: arrayToIdMap(testMap.tests, compileAutomationTest),
409
- })
511
+ const compileAutomationTestMap = (testMap: AutomationTestMap, mapId: string) => {
512
+ assertEntryId(testMap.id, 'AUTOMATION_MAP', `(automation map id field: ${mapId})`)
513
+
514
+ return {
515
+ title: testMap.title,
516
+ hide_short_ref: testMap.hideShortRef,
517
+ createdAt: testMap.createdAt,
518
+ map: arrayToIdMap(
519
+ testMap.tests,
520
+ (test, index) => compileAutomationTest(test, { mapId, testIndex: index }),
521
+ {
522
+ patternKey: 'TEST',
523
+ getErrorReference: (_, index) => `(automation map: ${mapId}, test index: ${index})`,
524
+ },
525
+ ),
526
+ }
527
+ }
410
528
 
411
529
  /**
412
530
  * Compile typed AutomationMap to raw automation_map format
413
531
  */
414
532
  const compileAutomation = (automationMap: AutomationMap) =>
415
533
  Object.fromEntries(
416
- Object.entries(automationMap).map(([mapId, testMap]) => [
417
- mapId,
418
- compileAutomationTestMap(testMap),
419
- ]),
534
+ Object.entries(automationMap).map(([mapId, testMap]) => {
535
+ const automationMapId = assertEntryId(mapId, 'AUTOMATION_MAP', '(automation map key)')
536
+ return [automationMapId, compileAutomationTestMap(testMap, automationMapId)]
537
+ }),
420
538
  )
421
539
 
422
540
  export const compile = async (app: Application) => {
@@ -424,16 +542,35 @@ export const compile = async (app: Application) => {
424
542
  const timestamp = Date.now()
425
543
  const config = {
426
544
  title: `${app.name || 'Unknown'}(${timestamp})`,
427
- subspace_map: app.subspaces.reduce((subspaceMap, subspace) => {
428
- subspaceMap[subspace.id] = {
545
+ subspace_map: app.subspaces.reduce((subspaceMap, subspace, subspaceIndex) => {
546
+ const subspaceId = assertEntryId(
547
+ subspace.id,
548
+ 'SUBSPACE',
549
+ `(subspace index: ${subspaceIndex})`,
550
+ )
551
+ const rootCanvasId = assertEntryId(
552
+ subspace.rootCanvas?.id,
553
+ 'CANVAS',
554
+ `(subspace: ${subspaceId}, root canvas)`,
555
+ )
556
+
557
+ subspaceMap[subspaceId] = {
429
558
  title: subspace.title,
430
559
  description: subspace.description,
560
+ hide_short_ref: subspace.hideShortRef,
561
+ unused: subspace.unused,
562
+ portal: subspace.portal,
431
563
  _expanded: subspace.unexpanded
432
564
  ? {
433
565
  brick: !subspace.unexpanded.brick,
434
566
  generator: !subspace.unexpanded.generator,
435
- canvas: subspace.unexpanded.canvas?.reduce((acc, canvas) => {
436
- acc[canvas.id] = !canvas?.id
567
+ canvas: subspace.unexpanded.canvas?.reduce((acc, canvas, canvasIndex) => {
568
+ const unexpandedCanvasId = assertEntryId(
569
+ canvas?.id,
570
+ 'CANVAS',
571
+ `(subspace: ${subspaceId}, unexpanded canvas index: ${canvasIndex})`,
572
+ )
573
+ acc[unexpandedCanvasId] = !canvas?.id
437
574
  return acc
438
575
  }, {}),
439
576
  property_bank: !subspace.unexpanded.data,
@@ -450,32 +587,42 @@ export const compile = async (app: Application) => {
450
587
  change_canvas: subspace.localSyncChangeCanvas,
451
588
  }
452
589
  : undefined,
453
- animation_map: subspace.animations.reduce((map, animation) => {
590
+ animation_map: subspace.animations.reduce((map, animation, animationIndex) => {
591
+ const animationId = assertEntryId(
592
+ animation?.id,
593
+ 'ANIMATION',
594
+ `(animation index: ${animationIndex}, subspace: ${subspaceId})`,
595
+ )
596
+
454
597
  if (animation.__typename === 'Animation') {
455
598
  const animationDef = animation as AnimationDef
456
- map[animationDef.id] = {
599
+ map[animationId] = {
600
+ alias: animationDef.alias,
457
601
  title: animationDef.title,
458
602
  description: animationDef.description,
603
+ hide_short_ref: animationDef.hideShortRef,
459
604
  animationRunType: animationDef.runType,
460
605
  property: animationDef.property,
461
606
  type: animationTypeMap[animationDef.config.__type],
462
607
  config: compileProperty(
463
- _.omit(animationDef.config, '__type'),
464
- `(animation: ${animation.id}, subspace ${subspace.id})`,
608
+ omit(animationDef.config, '__type'),
609
+ `(animation: ${animationId}, subspace ${subspaceId})`,
465
610
  ),
466
611
  }
467
612
  } else if (animation.__typename === 'AnimationCompose') {
468
613
  const animationDef = animation as AnimationComposeDef
469
- map[animationDef.id] = {
614
+ map[animationId] = {
615
+ alias: animationDef.alias,
470
616
  title: animationDef.title,
471
617
  description: animationDef.description,
618
+ hide_short_ref: animationDef.hideShortRef,
472
619
  animationRunType: animationDef.runType,
473
620
  compose_type: animationDef.composeType,
474
621
  item_list: animationDef.items.map((item, index) => {
475
622
  const innerAnimation = item()
476
623
  if (!innerAnimation?.id)
477
624
  throw new Error(
478
- `Invalid animation index: ${index} (animation: ${innerAnimation.id}, subspace ${subspace.id})`,
625
+ `Invalid animation index: ${index} (animation: ${innerAnimation.id}, subspace ${subspaceId})`,
479
626
  )
480
627
  return { animation_id: innerAnimation.id }
481
628
  }),
@@ -483,10 +630,15 @@ export const compile = async (app: Application) => {
483
630
  }
484
631
  return map
485
632
  }, {}),
486
- brick_map: subspace.bricks.reduce((map, brick) => {
633
+ brick_map: subspace.bricks.reduce((map, brick, brickIndex) => {
634
+ const brickId = assertEntryId(
635
+ brick.id,
636
+ 'BRICK',
637
+ `(brick index: ${brickIndex}, subspace: ${subspaceId})`,
638
+ )
487
639
  const property = compileProperty(
488
640
  brick.property || {},
489
- `(brick: ${brick.id}, subspace ${subspace.id})`,
641
+ `(brick: ${brickId}, subspace ${subspaceId})`,
490
642
  )
491
643
  if (brick.templateKey === 'BRICK_ITEMS') {
492
644
  const brickItems = brick as BrickItems
@@ -498,25 +650,31 @@ export const compile = async (app: Application) => {
498
650
  frame: itemBrick.frame,
499
651
  property: compileProperty(
500
652
  itemBrick.property,
501
- `(brick: ${brick.id}, ${key}: ${index}, subspace ${subspace.id})`,
653
+ `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
502
654
  ),
503
655
  propertyMapping: itemBrick.propertyMapping,
504
656
  animation: compileAnimations(
505
657
  itemBrick.templateKey,
506
658
  itemBrick.animation || {},
507
- `(brick: ${brick.id}, ${key}: ${index}, subspace ${subspace.id})`,
659
+ `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
508
660
  ),
509
661
  outlet: compileOutlets(
510
662
  itemBrick.templateKey,
511
663
  itemBrick.outlets || {},
512
- `(brick: ${brick.id}, ${key}: ${index}, subspace ${subspace.id})`,
664
+ `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
513
665
  ),
514
666
  eventMap: compileEvents(itemBrick.templateKey, itemBrick.eventMap || {}, {
515
667
  camelCase: true,
516
- errorReference: `(brick: ${brick.id}, ${key}: ${index}, subspace ${subspace.id})`,
668
+ errorReference: `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
517
669
  }),
518
- stateGroup: itemBrick.stateGroup.reduce((acc, stateGroup) => {
519
- acc[stateGroup.id] = {
670
+ stateGroup: itemBrick.stateGroup.reduce((acc, stateGroup, stateGroupIndex) => {
671
+ const stateGroupId = assertEntryId(
672
+ stateGroup.id,
673
+ 'BRICK_STATE_GROUP',
674
+ `(brick: ${brickId}, ${key}: ${index}, switch index: ${stateGroupIndex}, subspace ${subspaceId})`,
675
+ )
676
+
677
+ acc[stateGroupId] = {
520
678
  title: stateGroup.title,
521
679
  description: stateGroup.description,
522
680
  override: stateGroup.override,
@@ -525,35 +683,40 @@ export const compile = async (app: Application) => {
525
683
  conds: compileSwitchConds(
526
684
  itemBrick.templateKey,
527
685
  stateGroup.conds || [],
528
- `(brick: ${brick.id}, ${key}: ${index}, switch: ${stateGroup.id}, subspace ${subspace.id})`,
686
+ `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
529
687
  ),
530
688
  property: compileProperty(
531
689
  stateGroup.property,
532
- `(brick: ${brick.id}, ${key}: ${index}, switch: ${stateGroup.id}, subspace ${subspace.id})`,
690
+ `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
533
691
  ),
534
692
  animation: compileAnimations(
535
693
  itemBrick.templateKey,
536
694
  stateGroup.animation || {},
537
- `(brick: ${brick.id}, ${key}: ${index}, switch: ${stateGroup.id}, subspace ${subspace.id})`,
695
+ `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
538
696
  ),
539
697
  outlet: compileOutlets(
540
698
  itemBrick.templateKey,
541
699
  stateGroup.outlets || {},
542
- `(brick: ${brick.id}, ${key}: ${index}, switch: ${stateGroup.id}, subspace ${subspace.id})`,
700
+ `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
543
701
  ),
544
702
  eventMap: compileEvents(itemBrick.templateKey, stateGroup.eventMap || {}, {
545
703
  camelCase: true,
546
- errorReference: `(brick: ${brick.id}, ${key}: ${index}, switch: ${stateGroup.id}, subspace ${subspace.id})`,
704
+ errorReference: `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
547
705
  }),
548
706
  }
549
707
  return acc
550
708
  }, {}),
709
+ show: itemBrick.show,
710
+ pressToOpenDetail: itemBrick.pressToOpenDetail,
711
+ pressToBackList: itemBrick.pressToBackList,
551
712
  })
552
713
  if (Array.isArray(brickItems.brickList)) {
553
714
  const brickList = (brickItems.brickList || []).map((item, index) =>
554
715
  buildList(item, index, 'brickList'),
555
716
  )
556
717
  property.brickList = brickList
718
+ } else if (!brickItems.brickList) {
719
+ property.brickList = []
557
720
  } else {
558
721
  // Not supported Data for brickList
559
722
  throw new TypeError('Not supported Data for brickList directly')
@@ -563,32 +726,42 @@ export const compile = async (app: Application) => {
563
726
  buildList(item, index, 'brickDetails'),
564
727
  )
565
728
  property.brickDetails = brickDetails
729
+ } else if (!brickItems.brickDetails) {
730
+ property.brickDetails = []
566
731
  } else {
567
732
  // Not supported Data for brickList
568
733
  throw new TypeError('Not supported Data for brickList directly')
569
734
  }
570
735
  }
571
- map[brick.id] = {
736
+ map[brickId] = {
572
737
  template_key: brick.templateKey,
738
+ alias: brick.alias,
573
739
  title: brick.title,
574
740
  description: brick.description,
741
+ hide_short_ref: brick.hideShortRef,
575
742
  property,
576
743
  animation: compileAnimations(
577
744
  brick.templateKey,
578
745
  brick.animation || {},
579
- `(brick: ${brick.id}, subspace ${subspace.id})`,
746
+ `(brick: ${brickId}, subspace ${subspaceId})`,
580
747
  ),
581
748
  event_map: compileEvents(brick.templateKey, brick.events || {}, {
582
749
  camelCase: false,
583
- errorReference: `(brick: ${brick.id}, subspace ${subspace.id})`,
750
+ errorReference: `(brick: ${brickId}, subspace ${subspaceId})`,
584
751
  }),
585
752
  outlet: compileOutlets(
586
753
  brick.templateKey,
587
754
  brick.outlets || {},
588
- `(brick: ${brick.id}, subspace ${subspace.id})`,
755
+ `(brick: ${brickId}, subspace ${subspaceId})`,
589
756
  ),
590
- state_group: brick.switches?.reduce((acc, switchCase) => {
591
- acc[switchCase.id] = {
757
+ state_group: brick.switches?.reduce((acc, switchCase, switchIndex) => {
758
+ const switchId = assertEntryId(
759
+ switchCase.id,
760
+ 'BRICK_STATE_GROUP',
761
+ `(brick: ${brickId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
762
+ )
763
+
764
+ acc[switchId] = {
592
765
  title: switchCase.title,
593
766
  description: switchCase.description,
594
767
  break: switchCase.break,
@@ -597,25 +770,25 @@ export const compile = async (app: Application) => {
597
770
  conds: compileSwitchConds(
598
771
  brick.templateKey,
599
772
  switchCase.conds || [],
600
- `(brick: ${brick.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
773
+ `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
601
774
  ),
602
775
  property: compileProperty(
603
776
  switchCase.property,
604
- `(brick: ${brick.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
777
+ `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
605
778
  ),
606
779
  outlet: compileOutlets(
607
780
  brick.templateKey,
608
781
  switchCase.outlets || {},
609
- `(brick: ${brick.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
782
+ `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
610
783
  ),
611
784
  event_map: compileEvents(brick.templateKey, switchCase.events || {}, {
612
785
  camelCase: false,
613
- errorReference: `(brick: ${brick.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
786
+ errorReference: `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
614
787
  }),
615
788
  animation: compileAnimations(
616
789
  brick.templateKey,
617
790
  switchCase.animation || {},
618
- `(brick: ${brick.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
791
+ `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
619
792
  ),
620
793
  }
621
794
  return acc
@@ -623,21 +796,35 @@ export const compile = async (app: Application) => {
623
796
  }
624
797
  return map
625
798
  }, {}),
626
- root_canvas_id: subspace.rootCanvas.id,
627
- canvas_map: subspace.canvases.reduce((map, canvas) => {
628
- map[canvas.id] = {
799
+ root_canvas_id: rootCanvasId,
800
+ canvas_map: subspace.canvases.reduce((map, canvas, canvasIndex) => {
801
+ const canvasId = assertEntryId(
802
+ canvas.id,
803
+ 'CANVAS',
804
+ `(canvas index: ${canvasIndex}, subspace: ${subspaceId})`,
805
+ )
806
+
807
+ map[canvasId] = {
808
+ alias: canvas.alias,
629
809
  title: canvas.title,
630
810
  description: canvas.description,
811
+ hide_short_ref: canvas.hideShortRef,
631
812
  property: compileProperty(
632
813
  canvas.property,
633
- `(canvas: ${canvas.id}, subspace ${subspace.id})`,
814
+ `(canvas: ${canvasId}, subspace ${subspaceId})`,
634
815
  ),
635
816
  event_map: compileEvents('CANVAS', canvas.events || {}, {
636
817
  camelCase: false,
637
- errorReference: `(canvas: ${canvas.id}, subspace ${subspace.id})`,
818
+ errorReference: `(canvas: ${canvasId}, subspace ${subspaceId})`,
638
819
  }),
639
- state_group: canvas.switches?.reduce((acc, switchCase) => {
640
- acc[switchCase.id] = {
820
+ state_group: canvas.switches?.reduce((acc, switchCase, switchIndex) => {
821
+ const switchId = assertEntryId(
822
+ switchCase.id,
823
+ 'BRICK_STATE_GROUP',
824
+ `(canvas: ${canvasId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
825
+ )
826
+
827
+ acc[switchId] = {
641
828
  title: switchCase.title,
642
829
  description: switchCase.description,
643
830
  break: switchCase.break,
@@ -646,20 +833,20 @@ export const compile = async (app: Application) => {
646
833
  conds: compileSwitchConds(
647
834
  'CANVAS',
648
835
  switchCase.conds || [],
649
- `(canvas: ${canvas.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
836
+ `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
650
837
  ),
651
838
  property: compileProperty(
652
839
  switchCase.property,
653
- `(canvas: ${canvas.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
840
+ `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
654
841
  ),
655
842
  event_map: compileEvents('CANVAS', switchCase.events || {}, {
656
843
  camelCase: false,
657
- errorReference: `(canvas: ${canvas.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
844
+ errorReference: `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
658
845
  }),
659
846
  animation: compileAnimations(
660
847
  'CANVAS',
661
848
  switchCase.animation || {},
662
- `(canvas: ${canvas.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
849
+ `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
663
850
  ),
664
851
  }
665
852
  return acc
@@ -670,16 +857,16 @@ export const compile = async (app: Application) => {
670
857
  const brick = (item.item as () => Brick)()
671
858
  if (!brick?.id)
672
859
  throw new Error(
673
- `Invalid canvas item index: ${index}, brick not found (canvas: ${canvas.id}, subspace ${subspace.id})`,
860
+ `Invalid canvas item index: ${index}, brick not found (canvas: ${canvasId}, subspace ${subspaceId})`,
674
861
  )
675
862
  itemPayload = { brick_id: brick.id }
676
863
  } else {
677
- const subspaceId = item.item
678
- if (!app.subspaces.some((s) => s.id === subspaceId))
864
+ const targetSubspaceId = item.item
865
+ if (!app.subspaces.some((s) => s.id === targetSubspaceId))
679
866
  throw new Error(
680
- `Invalid canvas item index: ${index}, subspace not found (canvas: ${canvas.id}, subspace ${subspace.id})`,
867
+ `Invalid canvas item index: ${index}, subspace not found (canvas: ${canvasId}, subspace ${subspaceId})`,
681
868
  )
682
- itemPayload = { subspace_id: subspaceId }
869
+ itemPayload = { subspace_id: targetSubspaceId }
683
870
  }
684
871
  return {
685
872
  type: typeof item.item === 'function' ? 'brick' : 'subspace',
@@ -691,11 +878,19 @@ export const compile = async (app: Application) => {
691
878
  }
692
879
  return map
693
880
  }, {}),
694
- generator_map: subspace.generators.reduce((map, generator) => {
695
- map[generator.id] = {
881
+ generator_map: subspace.generators.reduce((map, generator, generatorIndex) => {
882
+ const generatorId = assertEntryId(
883
+ generator.id,
884
+ 'GENERATOR',
885
+ `(generator index: ${generatorIndex}, subspace: ${subspaceId})`,
886
+ )
887
+
888
+ map[generatorId] = {
696
889
  template_key: generator.templateKey,
890
+ alias: generator.alias,
697
891
  title: generator.title,
698
892
  description: generator.description,
893
+ hide_short_ref: generator.hideShortRef,
699
894
  local_sync: generator.localSyncRunMode
700
895
  ? {
701
896
  run_mode: generator.localSyncRunMode,
@@ -703,19 +898,25 @@ export const compile = async (app: Application) => {
703
898
  : undefined,
704
899
  property: compileProperty(
705
900
  generator.property || {},
706
- `(generator: ${generator.id}, subspace ${subspace.id})`,
901
+ `(generator: ${generatorId}, subspace ${subspaceId})`,
707
902
  ),
708
903
  event_map: compileEvents(generator.templateKey, generator.events || {}, {
709
904
  camelCase: false,
710
- errorReference: `(generator: ${generator.id}, subspace ${subspace.id})`,
905
+ errorReference: `(generator: ${generatorId}, subspace ${subspaceId})`,
711
906
  }),
712
907
  outlet: compileOutlets(
713
908
  generator.templateKey,
714
909
  generator.outlets || {},
715
- `(generator: ${generator.id}, subspace ${subspace.id})`,
910
+ `(generator: ${generatorId}, subspace ${subspaceId})`,
716
911
  ),
717
- state_group: generator.switches?.reduce((acc, switchCase) => {
718
- acc[switchCase.id] = {
912
+ state_group: generator.switches?.reduce((acc, switchCase, switchIndex) => {
913
+ const switchId = assertEntryId(
914
+ switchCase.id,
915
+ 'BRICK_STATE_GROUP',
916
+ `(generator: ${generatorId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
917
+ )
918
+
919
+ acc[switchId] = {
719
920
  title: switchCase.title,
720
921
  description: switchCase.description,
721
922
  break: switchCase.break,
@@ -724,25 +925,25 @@ export const compile = async (app: Application) => {
724
925
  conds: compileSwitchConds(
725
926
  generator.templateKey,
726
927
  switchCase.conds || [],
727
- `(generator: ${generator.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
928
+ `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
728
929
  ),
729
930
  property: compileProperty(
730
931
  switchCase.property,
731
- `(generator: ${generator.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
932
+ `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
732
933
  ),
733
934
  outlet: compileOutlets(
734
935
  generator.templateKey,
735
936
  switchCase.outlets || {},
736
- `(generator: ${generator.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
937
+ `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
737
938
  ),
738
939
  event_map: compileEvents(generator.templateKey, switchCase.events || {}, {
739
940
  camelCase: false,
740
- errorReference: `(generator: ${generator.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
941
+ errorReference: `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
741
942
  }),
742
943
  animation: compileAnimations(
743
944
  generator.templateKey,
744
945
  switchCase.animation || {},
745
- `(generator: ${generator.id}, switch: ${switchCase.id}, subspace ${subspace.id})`,
946
+ `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
746
947
  ),
747
948
  }
748
949
  return acc
@@ -750,10 +951,18 @@ export const compile = async (app: Application) => {
750
951
  }
751
952
  return map
752
953
  }, {}),
753
- property_bank_map: subspace.data.reduce((map, data) => {
754
- map[data.id] = {
954
+ property_bank_map: subspace.data.reduce((map, data, dataIndex) => {
955
+ const dataId = assertEntryId(
956
+ data.id,
957
+ 'PROPERTY_BANK_DATA_NODE',
958
+ `(data index: ${dataIndex}, subspace: ${subspaceId})`,
959
+ )
960
+
961
+ map[dataId] = {
962
+ alias: data.alias,
755
963
  title: data.title,
756
964
  description: data.description,
965
+ hide_short_ref: data.hideShortRef,
757
966
  linked: data.metadata?.linked,
758
967
  linkedFrom: data.metadata?.linkedFrom,
759
968
  local_sync: data.localSyncUpdateMode
@@ -767,35 +976,72 @@ export const compile = async (app: Application) => {
767
976
  schema: data.schema,
768
977
  type: data.type,
769
978
  ...compileKind(data.kind),
770
- value: compileProperty(data.value, `(data: ${data.id}, subspace ${subspace.id})`),
979
+ value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
771
980
  event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
772
981
  camelCase: false,
773
- errorReference: `(data: ${data.id}, subspace ${subspace.id})`,
982
+ errorReference: `(data: ${dataId}, subspace ${subspaceId})`,
774
983
  }),
775
984
  hit_equal: data.hit_equal,
776
985
  hit_regex: data.hit_regex,
777
986
  }
778
987
  return map
779
988
  }, {}),
780
- property_bank_calc_map: subspace.dataCalculation.reduce((map, dataCalc) => {
989
+ property_bank_calc_map: subspace.dataCalculation.reduce((map, dataCalc, dataCalcIndex) => {
990
+ const dataCalcId = assertEntryId(
991
+ dataCalc.id,
992
+ 'PROPERTY_BANK_COMMAND_MAP',
993
+ `(data calc index: ${dataCalcIndex}, subspace: ${subspaceId})`,
994
+ )
995
+
781
996
  const calc: any = {
782
997
  title: dataCalc.title,
783
998
  description: dataCalc.description,
999
+ hide_short_ref: dataCalc.hideShortRef,
784
1000
  }
785
1001
  if (dataCalc.triggerMode) calc.trigger_type = dataCalc.triggerMode
786
1002
  if (dataCalc.__typename === 'DataCalculationMap') {
787
1003
  calc.type = 'general'
788
1004
  const mapCalc = dataCalc as DataCalculationMap
789
1005
 
1006
+ const getNodeId = (
1007
+ node: DataCalculationData | DataCommand,
1008
+ reference = '',
1009
+ nodeIndex?: number,
1010
+ ) => {
1011
+ const nodeReference = [
1012
+ `data calc: ${dataCalcId}`,
1013
+ `subspace: ${subspaceId}`,
1014
+ typeof nodeIndex === 'number' ? `node index: ${nodeIndex}` : undefined,
1015
+ reference || undefined,
1016
+ ]
1017
+ .filter(Boolean)
1018
+ .join(', ')
1019
+
1020
+ if (node.__typename === 'DataCalculationData') {
1021
+ return assertEntryId(
1022
+ node.data()?.id,
1023
+ 'PROPERTY_BANK_DATA_NODE',
1024
+ `(${nodeReference})`,
1025
+ )
1026
+ }
1027
+ if (node.__typename === 'DataCommand') {
1028
+ return assertEntryId(node.id, 'PROPERTY_BANK_COMMAND_NODE', `(${nodeReference})`)
1029
+ }
1030
+ throw new Error(`Invalid node: ${JSON.stringify(node)}`)
1031
+ }
1032
+
790
1033
  const generateInputPorts = (inputs) =>
791
- inputs.reduce((acc, port) => {
1034
+ inputs.reduce((acc, port, portIndex) => {
792
1035
  if (!acc[port.key]) acc[port.key] = null
793
1036
 
794
1037
  let sourceId
795
1038
  const sourceNode = port.source()
796
- if (sourceNode?.__typename === 'DataCalculationData')
797
- sourceId = (sourceNode as DataCalculationData).data().id
798
- if (sourceNode?.__typename === 'DataCommand') sourceId = sourceNode.id
1039
+ if (
1040
+ sourceNode?.__typename === 'DataCalculationData' ||
1041
+ sourceNode?.__typename === 'DataCommand'
1042
+ ) {
1043
+ sourceId = getNodeId(sourceNode, `input port index: ${portIndex}`)
1044
+ }
799
1045
 
800
1046
  if (!sourceId) return acc
801
1047
  if (!acc[port.key]) acc[port.key] = []
@@ -809,14 +1055,17 @@ export const compile = async (app: Application) => {
809
1055
  }, {})
810
1056
 
811
1057
  const generateOutputPorts = (outputs) =>
812
- outputs.reduce((acc, port) => {
1058
+ outputs.reduce((acc, port, portIndex) => {
813
1059
  if (!acc[port.key]) acc[port.key] = null
814
1060
 
815
1061
  let targetId
816
1062
  const targetNode = port.target()
817
- if (targetNode?.__typename === 'DataCalculationData')
818
- targetId = (targetNode as DataCalculationData).data().id
819
- if (targetNode?.__typename === 'DataCommand') targetId = targetNode.id
1063
+ if (
1064
+ targetNode?.__typename === 'DataCalculationData' ||
1065
+ targetNode?.__typename === 'DataCommand'
1066
+ ) {
1067
+ targetId = getNodeId(targetNode, `output port index: ${portIndex}`)
1068
+ }
820
1069
 
821
1070
  if (!targetId) return acc
822
1071
  if (!acc[port.key]) acc[port.key] = []
@@ -828,18 +1077,13 @@ export const compile = async (app: Application) => {
828
1077
  return acc
829
1078
  }, {})
830
1079
 
831
- const getNodeId = (node) => {
832
- if (node.__typename === 'DataCalculationData') return node.data().id
833
- if (node.__typename === 'DataCommand') return node.id
834
- throw new Error(`Invalid node: ${JSON.stringify(node)}`)
835
- }
836
-
837
- calc.map = mapCalc.nodes.reduce((acc, node) => {
1080
+ calc.map = mapCalc.nodes.reduce((acc, node, nodeIndex) => {
838
1081
  if (node.__typename === 'DataCalculationData') {
839
1082
  const dataNode = node as DataCalculationData
840
- acc[getNodeId(dataNode)] = {
1083
+ acc[getNodeId(dataNode, 'data node', nodeIndex)] = {
841
1084
  title: dataNode.title,
842
1085
  description: dataNode.description,
1086
+ hide_short_ref: dataNode.hideShortRef,
843
1087
  type: 'data-node',
844
1088
  properties: {},
845
1089
  in: generateInputPorts(dataNode.inputs),
@@ -862,9 +1106,10 @@ export const compile = async (app: Application) => {
862
1106
  (input.source()?.__typename === 'DataCalculationData' ||
863
1107
  input.source()?.__typename === 'DataCommand'),
864
1108
  )
865
- acc[getNodeId(commandNode)] = {
1109
+ acc[getNodeId(commandNode, 'command node', nodeIndex)] = {
866
1110
  title: commandNode.title,
867
1111
  description: commandNode.description,
1112
+ hide_short_ref: commandNode.hideShortRef,
868
1113
  type: `command-node-${type}`,
869
1114
  properties: {
870
1115
  command: commandNode.__commandName,
@@ -880,11 +1125,11 @@ export const compile = async (app: Application) => {
880
1125
  return acc
881
1126
  }, {})
882
1127
  calc.editor_info = mapCalc.editorInfo.reduce((acc, editorInfo) => {
883
- acc[getNodeId(editorInfo.node)] = {
1128
+ acc[getNodeId(editorInfo.node, 'editor info node')] = {
884
1129
  position: editorInfo.position,
885
1130
  points: editorInfo.points.reduce((pointsAcc, point) => {
886
- const sourceId = getNodeId(point.source)
887
- const targetId = getNodeId(point.target)
1131
+ const sourceId = getNodeId(point.source, 'editor info point source')
1132
+ const targetId = getNodeId(point.target, 'editor info point target')
888
1133
  const key = `${sourceId}-${point.sourceOutputKey}-${targetId}-${point.targetInputKey}`
889
1134
  pointsAcc[key] = point.positions
890
1135
  return pointsAcc
@@ -896,43 +1141,55 @@ export const compile = async (app: Application) => {
896
1141
  const scriptCalc = dataCalc as DataCalculationScript
897
1142
  calc.type = 'script'
898
1143
 
899
- let code: string
900
- try {
901
- const program = parseAST(scriptCalc.code, { sourceType: 'module', ecmaVersion: 2020 })
902
- // export function main() { ... }
903
- const declarationBody = (
904
- (program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration
905
- )?.body
906
- code = escodegen.generate(declarationBody, {
907
- format: {
908
- indent: { style: ' ' },
909
- semicolons: false,
910
- },
911
- comment: true,
912
- })
913
- } catch {
914
- code = scriptCalc.code || ''
915
- }
1144
+ const code = compileScriptCalculationCode(scriptCalc.code)
916
1145
  calc.script_config = {
917
- note: scriptCalc.note,
1146
+ title: scriptCalc.title ?? '',
1147
+ note: scriptCalc.note ?? '',
918
1148
  code,
919
1149
  enable_async: scriptCalc.enableAsync,
920
1150
  trigger_mode: scriptCalc.triggerMode,
921
1151
  inputs: scriptCalc.inputs.reduce((acc, input) => {
922
- acc[input.data().id] = input.key
1152
+ const inputId = assertEntryId(
1153
+ input.data()?.id,
1154
+ 'PROPERTY_BANK_DATA_NODE',
1155
+ `(data calc: ${dataCalcId}, script input: ${input.key}, subspace: ${subspaceId})`,
1156
+ )
1157
+ acc[inputId] = input.key
923
1158
  return acc
924
1159
  }, {}),
925
1160
  disabled_triggers: scriptCalc.inputs.reduce((acc, input) => {
926
- acc[input.data().id] = !input.trigger
1161
+ const inputId = assertEntryId(
1162
+ input.data()?.id,
1163
+ 'PROPERTY_BANK_DATA_NODE',
1164
+ `(data calc: ${dataCalcId}, script trigger input: ${input.key}, subspace: ${subspaceId})`,
1165
+ )
1166
+ acc[inputId] = !input.trigger
927
1167
  return acc
928
1168
  }, {}),
929
- output: scriptCalc.output?.().id,
1169
+ output: scriptCalc.output
1170
+ ? assertEntryId(
1171
+ scriptCalc.output()?.id,
1172
+ 'PROPERTY_BANK_DATA_NODE',
1173
+ `(data calc: ${dataCalcId}, script output, subspace: ${subspaceId})`,
1174
+ )
1175
+ : null,
930
1176
  outputs: scriptCalc.outputs.reduce((acc, output) => {
931
1177
  if (!acc[output.key]) acc[output.key] = []
932
- acc[output.key].push(output.data().id)
1178
+ const outputId = assertEntryId(
1179
+ output.data()?.id,
1180
+ 'PROPERTY_BANK_DATA_NODE',
1181
+ `(data calc: ${dataCalcId}, script outputs key: ${output.key}, subspace: ${subspaceId})`,
1182
+ )
1183
+ acc[output.key].push(outputId)
933
1184
  return acc
934
1185
  }, {}),
935
- error: scriptCalc.error?.().id,
1186
+ error: scriptCalc.error
1187
+ ? assertEntryId(
1188
+ scriptCalc.error()?.id,
1189
+ 'PROPERTY_BANK_DATA_NODE',
1190
+ `(data calc: ${dataCalcId}, script error output, subspace: ${subspaceId})`,
1191
+ )
1192
+ : null,
936
1193
  }
937
1194
 
938
1195
  Object.assign(
@@ -942,25 +1199,32 @@ export const compile = async (app: Application) => {
942
1199
  }),
943
1200
  )
944
1201
  }
945
- map[dataCalc.id] = calc
1202
+ map[dataCalcId] = calc
946
1203
  return map
947
1204
  }, {}),
948
1205
  action_map: subspace.actions || undefined,
949
1206
  event_map: compileEvents('', subspace.events || {}, {
950
1207
  camelCase: false,
951
- errorReference: `(subspace ${subspace.id})`,
1208
+ errorReference: `(subspace ${subspaceId})`,
952
1209
  }),
953
1210
  routing: subspace.dataRouting.reduce((acc, data, index) => {
954
- if (!data?.id)
955
- throw new Error(`Invalid data routing index: ${index} (subspace ${subspace.id})`)
956
- acc[data.id] = { enabled_routing: true }
1211
+ const dataId = assertEntryId(
1212
+ data?.id,
1213
+ 'PROPERTY_BANK_DATA_NODE',
1214
+ `(data routing index: ${index}, subspace ${subspaceId})`,
1215
+ )
1216
+ acc[dataId] = { enabled_routing: true }
957
1217
  return acc
958
1218
  }, {}),
959
1219
  ...compileModule(subspace),
960
1220
  }
961
1221
  return subspaceMap
962
1222
  }, {}),
963
- root_subspace_id: app.rootSubspace.id,
1223
+ root_subspace_id: assertEntryId(
1224
+ app.rootSubspace?.id,
1225
+ 'SUBSPACE',
1226
+ '(application root subspace)',
1227
+ ),
964
1228
  fonts: app.fonts,
965
1229
  ...compileApplicationSettings(app.settings),
966
1230
  // Use typed automationMap if available, otherwise fall back to TEMP metadata
@@ -974,3 +1238,8 @@ export const compile = async (app: Application) => {
974
1238
  }
975
1239
  return config
976
1240
  }
1241
+
1242
+ export const checkConfig = async (configPath: string) => {
1243
+ const { $ } = await import('bun')
1244
+ await $`bricks app check-config ${configPath}`
1245
+ }