@fugood/bricks-project 2.24.0-beta.3 → 2.24.0-beta.30

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