@fugood/bricks-project 2.24.0-beta.2 → 2.24.0-beta.20

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