@fugood/bricks-project 2.23.0-beta.9 → 2.23.2

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 (118) hide show
  1. package/api/instance.ts +37 -5
  2. package/compile/action-name-map.ts +107 -0
  3. package/compile/index.ts +172 -66
  4. package/compile/util.ts +13 -4
  5. package/package.json +9 -5
  6. package/skills/bricks-project/SKILL.md +32 -0
  7. package/skills/bricks-project/rules/animation.md +159 -0
  8. package/skills/bricks-project/rules/architecture-patterns.md +62 -0
  9. package/skills/bricks-project/rules/automations.md +221 -0
  10. package/skills/bricks-project/rules/buttress.md +153 -0
  11. package/skills/bricks-project/rules/data-calculation.md +208 -0
  12. package/skills/bricks-project/rules/local-sync.md +129 -0
  13. package/skills/bricks-project/rules/media-flow.md +158 -0
  14. package/skills/bricks-project/rules/remote-data-bank.md +196 -0
  15. package/skills/bricks-project/rules/standby-transition.md +124 -0
  16. package/skills/rive-marketplace/SKILL.md +99 -0
  17. package/tools/deploy.ts +74 -12
  18. package/tools/icons/.gitattributes +1 -0
  19. package/tools/icons/fa6pro-glyphmap.json +4686 -0
  20. package/tools/icons/fa6pro-meta.json +26127 -0
  21. package/tools/mcp-server.ts +818 -9
  22. package/tools/postinstall.ts +75 -13
  23. package/tools/preview-main.mjs +54 -4
  24. package/tools/preview.ts +54 -7
  25. package/tools/pull.ts +37 -16
  26. package/types/automation.ts +232 -0
  27. package/types/brick-base.ts +1 -0
  28. package/types/bricks/Camera.ts +26 -10
  29. package/types/bricks/Chart.ts +1 -0
  30. package/types/bricks/GenerativeMedia.ts +21 -3
  31. package/types/bricks/Icon.ts +1 -0
  32. package/types/bricks/Image.ts +6 -0
  33. package/types/bricks/Items.ts +1 -0
  34. package/types/bricks/Lottie.ts +1 -0
  35. package/types/bricks/Maps.ts +254 -0
  36. package/types/bricks/QrCode.ts +1 -0
  37. package/types/bricks/Rect.ts +1 -0
  38. package/types/bricks/RichText.ts +1 -0
  39. package/types/bricks/Rive.ts +1 -0
  40. package/types/bricks/Slideshow.ts +1 -0
  41. package/types/bricks/Svg.ts +1 -0
  42. package/types/bricks/Text.ts +1 -0
  43. package/types/bricks/TextInput.ts +1 -0
  44. package/types/bricks/Video.ts +1 -0
  45. package/types/bricks/VideoStreaming.ts +1 -0
  46. package/types/bricks/WebRtcStream.ts +1 -0
  47. package/types/bricks/WebView.ts +8 -1
  48. package/types/bricks/index.ts +2 -0
  49. package/types/canvas.ts +1 -0
  50. package/types/common.ts +2 -0
  51. package/types/data-calc-command.ts +7003 -0
  52. package/types/data-calc-script.ts +21 -0
  53. package/types/data-calc.ts +3 -6977
  54. package/types/data.ts +3 -0
  55. package/types/generators/AlarmClock.ts +2 -0
  56. package/types/generators/Assistant.ts +30 -6
  57. package/types/generators/BleCentral.ts +2 -0
  58. package/types/generators/BlePeripheral.ts +2 -0
  59. package/types/generators/CanvasMap.ts +2 -0
  60. package/types/generators/CastlesPay.ts +2 -0
  61. package/types/generators/DataBank.ts +2 -0
  62. package/types/generators/File.ts +2 -0
  63. package/types/generators/GraphQl.ts +2 -0
  64. package/types/generators/Http.ts +84 -2
  65. package/types/generators/HttpServer.ts +5 -1
  66. package/types/generators/Information.ts +2 -0
  67. package/types/generators/Intent.ts +51 -0
  68. package/types/generators/Iterator.ts +11 -2
  69. package/types/generators/Keyboard.ts +2 -0
  70. package/types/generators/LlmAnthropicCompat.ts +2 -0
  71. package/types/generators/LlmAppleBuiltin.ts +144 -0
  72. package/types/generators/LlmGgml.ts +28 -4
  73. package/types/generators/LlmOnnx.ts +2 -0
  74. package/types/generators/LlmOpenAiCompat.ts +2 -0
  75. package/types/generators/LlmQualcommAiEngine.ts +2 -0
  76. package/types/generators/Mcp.ts +6 -4
  77. package/types/generators/McpServer.ts +8 -6
  78. package/types/generators/MediaFlow.ts +2 -0
  79. package/types/generators/MqttBroker.ts +2 -0
  80. package/types/generators/MqttClient.ts +2 -0
  81. package/types/generators/Question.ts +9 -0
  82. package/types/generators/RealtimeTranscription.ts +18 -8
  83. package/types/generators/RerankerGgml.ts +23 -16
  84. package/types/generators/SerialPort.ts +2 -0
  85. package/types/generators/SoundPlayer.ts +2 -0
  86. package/types/generators/SoundRecorder.ts +2 -0
  87. package/types/generators/SpeechToTextGgml.ts +19 -4
  88. package/types/generators/SpeechToTextOnnx.ts +2 -0
  89. package/types/generators/SpeechToTextPlatform.ts +2 -0
  90. package/types/generators/SqLite.ts +32 -1
  91. package/types/generators/Step.ts +2 -0
  92. package/types/generators/SttAppleBuiltin.ts +117 -0
  93. package/types/generators/Tcp.ts +2 -0
  94. package/types/generators/TcpServer.ts +5 -1
  95. package/types/generators/TextToSpeechApple.ts +113 -0
  96. package/types/generators/TextToSpeechAppleBuiltin.ts +114 -0
  97. package/types/generators/TextToSpeechGgml.ts +24 -3
  98. package/types/generators/TextToSpeechOnnx.ts +2 -0
  99. package/types/generators/TextToSpeechOpenAiLike.ts +2 -0
  100. package/types/generators/ThermalPrinter.ts +2 -0
  101. package/types/generators/Tick.ts +5 -1
  102. package/types/generators/TtsAppleBuiltin.ts +105 -0
  103. package/types/generators/Udp.ts +2 -0
  104. package/types/generators/VadGgml.ts +4 -2
  105. package/types/generators/VadOnnx.ts +201 -0
  106. package/types/generators/VadTraditional.ts +123 -0
  107. package/types/generators/VectorStore.ts +15 -2
  108. package/types/generators/Watchdog.ts +2 -0
  109. package/types/generators/WebCrawler.ts +2 -0
  110. package/types/generators/WebRtc.ts +4 -2
  111. package/types/generators/WebSocket.ts +2 -0
  112. package/types/generators/index.ts +5 -0
  113. package/types/index.ts +3 -0
  114. package/types/system.ts +48 -6
  115. package/utils/calc.ts +15 -9
  116. package/utils/data.ts +1 -0
  117. package/utils/event-props.ts +112 -2
  118. package/utils/id.ts +3 -1
package/api/instance.ts CHANGED
@@ -66,17 +66,31 @@ export const deployApp = async (
66
66
  appId: string,
67
67
  config: Config,
68
68
  lastCommitId?: string,
69
+ changelogs?: string,
70
+ version?: string,
69
71
  ) => {
70
72
  const app = await pullApp(stage, appId)
71
73
  if (app.config?.bricks_project_last_commit_id === lastCommitId)
72
74
  throw new Error('No changes to deploy')
73
75
 
76
+ const versionName = version || app.name || 'Untitled'
77
+ const releaseNote = changelogs
78
+ ? `${changelogs}\n\nRelease by BRICKS Project`
79
+ : 'Release by BRICKS Project'
80
+
74
81
  const { errors } = await doGQL(
75
82
  stage,
76
- `mutation BRICKS_PROJECT_updateApplication($id: ID!, $config: String) {
83
+ `mutation BRICKS_PROJECT_releaseApplication(
84
+ $id: ID!,
85
+ $config: String,
86
+ $releaseCurrentVersion: String,
87
+ $releaseCurrentVersionNote: String
88
+ ) {
77
89
  updateApplication(
78
90
  id: $id,
79
- config: $config
91
+ config: $config,
92
+ releaseCurrentVersion: $releaseCurrentVersion,
93
+ releaseCurrentVersionNote: $releaseCurrentVersionNote
80
94
  ) {
81
95
  _id
82
96
  name
@@ -86,9 +100,11 @@ export const deployApp = async (
86
100
  id: appId,
87
101
  config: JSON.stringify({
88
102
  ...config,
89
- title: `${config.title || app.name || 'Untitled'} (${Date.now()})`,
103
+ title: versionName,
90
104
  bricks_project_last_commit_id: lastCommitId,
91
105
  }),
106
+ releaseCurrentVersion: versionName,
107
+ releaseCurrentVersionNote: releaseNote,
92
108
  },
93
109
  )
94
110
  if (errors) throw new Error(errors[0].message)
@@ -137,18 +153,32 @@ export const deployModule = async (
137
153
  modId: string,
138
154
  config: Config,
139
155
  lastCommitId?: string,
156
+ changelogs?: string,
157
+ version?: string,
140
158
  ) => {
141
159
  const mod = await pullModule(stage, modId)
142
160
  if (mod.config?.bricks_project_last_commit_id === lastCommitId)
143
161
  throw new Error('No changes to deploy')
144
162
 
163
+ const versionName = version || mod.name || 'Untitled'
164
+ const releaseNote = changelogs
165
+ ? `${changelogs}\n\nRelease by BRICKS Project`
166
+ : 'Release by BRICKS Project'
167
+
145
168
  const { errors } = await doGQL(
146
169
  stage,
147
- `mutation BRICKS_PROJECT_updateModule($id: ID!, $config: String) {
170
+ `mutation BRICKS_PROJECT_releaseModule(
171
+ $id: ID!,
172
+ $config: String,
173
+ $releaseCurrentVersion: String,
174
+ $releaseCurrentVersionNote: String
175
+ ) {
148
176
  updateModule(
149
177
  id: $id
150
178
  config: $config
151
179
  validateConfig: true
180
+ releaseCurrentVersion: $releaseCurrentVersion
181
+ releaseCurrentVersionNote: $releaseCurrentVersionNote
152
182
  ) {
153
183
  _id
154
184
  name
@@ -158,9 +188,11 @@ export const deployModule = async (
158
188
  id: modId,
159
189
  config: JSON.stringify({
160
190
  ...config,
161
- title: `${config.title || mod.name || 'Untitled'} (${Date.now()})`,
191
+ title: versionName,
162
192
  bricks_project_last_commit_id: lastCommitId,
163
193
  }),
194
+ releaseCurrentVersion: versionName,
195
+ releaseCurrentVersionNote: releaseNote,
164
196
  },
165
197
  )
166
198
  if (errors) throw new Error(errors[0].message)
@@ -1,3 +1,5 @@
1
+ /* Auto generated by build script */
2
+
1
3
  // NOTE: The action parameter name convertion is not 1:1 mapping case conversion, so we need to define the mapping here
2
4
  // This may be improved in the future
3
5
  export const templateActionNameMap = {
@@ -302,6 +304,28 @@ export const templateActionNameMap = {
302
304
  maxDuration: 'BRICK_CAMERA_MAX_DURATION',
303
305
  maxFileSize: 'BRICK_CAMERA_MAX_FILE_SIZE',
304
306
  },
307
+ BRICK_CAMERA_FOCUS: {
308
+ focusX: 'BRICK_CAMERA_FOCUS_X',
309
+ focusY: 'BRICK_CAMERA_FOCUS_Y',
310
+ },
311
+ },
312
+
313
+ BRICK_MAPS: {
314
+ BRICK_MAPS_PAN: {
315
+ panDirection: 'BRICK_MAPS_PAN_DIRECTION',
316
+ },
317
+ BRICK_MAPS_NAVIGATE_TO: {
318
+ targetLatitude: 'BRICK_MAPS_TARGET_LATITUDE',
319
+ targetLongitude: 'BRICK_MAPS_TARGET_LONGITUDE',
320
+ targetZoom: 'BRICK_MAPS_TARGET_ZOOM',
321
+ },
322
+ BRICK_MAPS_FOCUS_MARKER: {
323
+ markerId: 'BRICK_MAPS_MARKER_ID',
324
+ },
325
+ BRICK_MAPS_FIT_TO_MARKERS: {
326
+ edgePadding: 'BRICK_MAPS_EDGE_PADDING',
327
+ animated: 'BRICK_MAPS_ANIMATED',
328
+ },
305
329
  },
306
330
 
307
331
  GENERATOR_FILE: {
@@ -357,6 +381,23 @@ export const templateActionNameMap = {
357
381
  variables: 'GENERATOR_GRAPHQL_VARIABLES',
358
382
  },
359
383
  },
384
+ GENERATOR_HTTP: {
385
+ GENERATOR_HTTP_RUN_REQUEST: {
386
+ url: 'GENERATOR_HTTP_URL',
387
+ method: 'GENERATOR_HTTP_METHOD',
388
+ headers: 'GENERATOR_HTTP_HEADERS',
389
+ body: 'GENERATOR_HTTP_BODY',
390
+ timeout: 'GENERATOR_HTTP_TIMEOUT',
391
+ mode: 'GENERATOR_HTTP_MODE',
392
+ credentials: 'GENERATOR_HTTP_CREDENTIALS',
393
+ redirect: 'GENERATOR_HTTP_REDIRECT',
394
+ referrer: 'GENERATOR_HTTP_REFERRER',
395
+ resType: 'GENERATOR_HTTP_RES_TYPE',
396
+ resSelector: 'GENERATOR_HTTP_RES_SELECTOR',
397
+ eventStream: 'GENERATOR_HTTP_EVENT_STREAM',
398
+ eventName: 'GENERATOR_HTTP_EVENT_NAME',
399
+ },
400
+ },
360
401
 
361
402
  GENERATOR_WEB_SOCKET: {
362
403
  GENERATOR_WEB_SOCKET_EMIT: {
@@ -484,6 +525,16 @@ export const templateActionNameMap = {
484
525
  packageName: 'GENERATOR_INTENT_PACKAGE_NAME',
485
526
  category: 'GENERATOR_INTENT_CATEGORY',
486
527
  },
528
+ GENERATOR_INTENT_START_SERVICE: {
529
+ action: 'GENERATOR_INTENT_ACTION',
530
+ data: 'GENERATOR_INTENT_DATA',
531
+ type: 'GENERATOR_INTENT_TYPE',
532
+ extra: 'GENERATOR_INTENT_EXTRA',
533
+ className: 'GENERATOR_INTENT_CLASS_NAME',
534
+ packageName: 'GENERATOR_INTENT_PACKAGE_NAME',
535
+ category: 'GENERATOR_INTENT_CATEGORY',
536
+ foreground: 'GENERATOR_INTENT_FOREGROUND',
537
+ },
487
538
  },
488
539
  GENERATOR_CASTLES_PAY: {
489
540
  GENERATOR_CASTLES_PAY_SALE: {
@@ -512,6 +563,13 @@ export const templateActionNameMap = {
512
563
  params: 'GENERATOR_SQLITE_PARAMS',
513
564
  paramsString: 'GENERATOR_SQLITE_PARAMS_STRING',
514
565
  },
566
+ GENERATOR_SQLITE_TRANSACTION: {
567
+ statements: 'GENERATOR_SQLITE_STATEMENTS',
568
+ },
569
+ GENERATOR_SQLITE_BATCH_EXECUTE: {
570
+ sql: 'GENERATOR_SQLITE_SQL',
571
+ batchParams: 'GENERATOR_SQLITE_BATCH_PARAMS',
572
+ },
515
573
  },
516
574
 
517
575
  GENERATOR_MCP: {
@@ -543,6 +601,14 @@ export const templateActionNameMap = {
543
601
  variables: 'GENERATOR_MCP_VARIABLES',
544
602
  },
545
603
  },
604
+ GENERATOR_TRADITIONAL_VAD: {
605
+ GENERATOR_TRADITIONAL_VAD_DETECT_FILE: {
606
+ fileUrl: 'GENERATOR_TRADITIONAL_VAD_FILE_URL',
607
+ },
608
+ GENERATOR_TRADITIONAL_VAD_DETECT_DATA: {
609
+ data: 'GENERATOR_TRADITIONAL_VAD_DATA',
610
+ },
611
+ },
546
612
  GENERATOR_TTS: {
547
613
  GENERATOR_TTS_GENERATE: {
548
614
  text: 'GENERATOR_TTS_TEXT',
@@ -563,6 +629,24 @@ export const templateActionNameMap = {
563
629
  audioUri: 'GENERATOR_ONNX_STT_AUDIO_URI',
564
630
  },
565
631
  },
632
+ GENERATOR_ONNX_VAD: {
633
+ GENERATOR_ONNX_VAD_DETECT_FILE: {
634
+ fileUrl: 'GENERATOR_ONNX_VAD_FILE_URL',
635
+ threshold: 'GENERATOR_ONNX_VAD_THRESHOLD',
636
+ minSpeechDurationMs: 'GENERATOR_ONNX_VAD_MIN_SPEECH_DURATION_MS',
637
+ minSilenceDurationMs: 'GENERATOR_ONNX_VAD_MIN_SILENCE_DURATION_MS',
638
+ maxSpeechDurationS: 'GENERATOR_ONNX_VAD_MAX_SPEECH_DURATION_S',
639
+ speechPadMs: 'GENERATOR_ONNX_VAD_SPEECH_PAD_MS',
640
+ },
641
+ GENERATOR_ONNX_VAD_DETECT_DATA: {
642
+ data: 'GENERATOR_ONNX_VAD_DATA',
643
+ threshold: 'GENERATOR_ONNX_VAD_THRESHOLD',
644
+ minSpeechDurationMs: 'GENERATOR_ONNX_VAD_MIN_SPEECH_DURATION_MS',
645
+ minSilenceDurationMs: 'GENERATOR_ONNX_VAD_MIN_SILENCE_DURATION_MS',
646
+ maxSpeechDurationS: 'GENERATOR_ONNX_VAD_MAX_SPEECH_DURATION_S',
647
+ speechPadMs: 'GENERATOR_ONNX_VAD_SPEECH_PAD_MS',
648
+ },
649
+ },
566
650
  GENERATOR_SPEECH_INFERENCE: {
567
651
  GENERATOR_SPEECH_INFERENCE_TRANSCRIBE_FILE: {
568
652
  fileUrl: 'GENERATOR_SPEECH_INFERENCE_FILE_URL',
@@ -747,6 +831,29 @@ export const templateActionNameMap = {
747
831
  toolChoice: 'GENERATOR_ANTHROPIC_LLM_TOOL_CHOICE',
748
832
  },
749
833
  },
834
+ GENERATOR_APPLE_LLM: {
835
+ GENERATOR_APPLE_LLM_COMPLETION: {
836
+ messages: 'GENERATOR_APPLE_LLM_MESSAGES',
837
+ maxTokens: 'GENERATOR_APPLE_LLM_MAX_TOKENS',
838
+ temperature: 'GENERATOR_APPLE_LLM_TEMPERATURE',
839
+ topP: 'GENERATOR_APPLE_LLM_TOP_P',
840
+ },
841
+ },
842
+ GENERATOR_APPLE_STT: {
843
+ GENERATOR_APPLE_STT_TRANSCRIBE_FILE: {
844
+ fileUrl: 'GENERATOR_APPLE_STT_FILE_URL',
845
+ language: 'GENERATOR_APPLE_STT_LANGUAGE',
846
+ },
847
+ GENERATOR_APPLE_STT_TRANSCRIBE_DATA: {
848
+ data: 'GENERATOR_APPLE_STT_DATA',
849
+ language: 'GENERATOR_APPLE_STT_LANGUAGE',
850
+ },
851
+ },
852
+ GENERATOR_APPLE_TTS: {
853
+ GENERATOR_APPLE_TTS_GENERATE: {
854
+ text: 'GENERATOR_APPLE_TTS_TEXT',
855
+ },
856
+ },
750
857
  GENERATOR_ASSISTANT: {
751
858
  GENERATOR_ASSISTANT_ADD_MESSAGE: {
752
859
  role: 'GENERATOR_ASSISTANT_ROLE',
package/compile/index.ts CHANGED
@@ -1,4 +1,6 @@
1
- import _ from 'lodash'
1
+ /* eslint-disable no-underscore-dangle -- Uses __typename, __actionName, etc. for type system */
2
+ import snakeCase from 'lodash/snakeCase'
3
+ import omit from 'lodash/omit'
2
4
  import { parse as parseAST } from 'acorn'
3
5
  import type { ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
4
6
  import escodegen from 'escodegen'
@@ -12,7 +14,6 @@ import type {
12
14
  Animation,
13
15
  AnimationDef,
14
16
  AnimationComposeDef,
15
- EventAction,
16
17
  ActionWithDataParams,
17
18
  ActionWithParams,
18
19
  BrickItems,
@@ -24,10 +25,13 @@ import type {
24
25
  DataCalculationData,
25
26
  DataCommand,
26
27
  Brick,
27
- Generator,
28
28
  Canvas,
29
29
  Subspace,
30
- EventActionForItem,
30
+ AutomationMap,
31
+ AutomationTestMap,
32
+ AutomationTest,
33
+ TestCase,
34
+ TestVariable,
31
35
  } from '../types'
32
36
 
33
37
  const compileProperty = (property, errorReference: string, result = {}) => {
@@ -58,7 +62,7 @@ const compileEventActionValue = (templateKey, eventKey, value, errorReference) =
58
62
  const props = tmplEventProperties?.[eventKey]
59
63
  if (!props) return compileProperty(value, errorReference)
60
64
  if (props.includes(value)) return value
61
- if (value?.startsWith(templateKey)) {
65
+ if (typeof value === 'string' && value?.startsWith(templateKey)) {
62
66
  console.warn(
63
67
  `[Warning] Value start with template key but there is no event property: ${value} ${errorReference}`,
64
68
  )
@@ -66,25 +70,22 @@ const compileEventActionValue = (templateKey, eventKey, value, errorReference) =
66
70
  return compileProperty(value, errorReference)
67
71
  }
68
72
 
69
- const convertOutletKey = (templateKey: string, key: string) => {
70
- return `${templateKey}_${_.snakeCase(key).toUpperCase()}`
71
- }
73
+ const convertOutletKey = (templateKey: string, key: string) =>
74
+ `${templateKey}_${snakeCase(key).toUpperCase()}`
72
75
 
73
76
  const compileOutlets = (
74
77
  templateKey: string,
75
78
  outlets: { [key: string]: () => Data },
76
79
  errorReference: string,
77
- ) => {
78
- return Object.entries(outlets).reduce((acc, [key, data]) => {
80
+ ) =>
81
+ Object.entries(outlets).reduce((acc, [key, data]) => {
79
82
  if (!data()?.id) throw new Error(`Invalid data reference ${errorReference}`)
80
83
  acc[convertOutletKey(templateKey, key)] = data().id
81
84
  return acc
82
85
  }, {})
83
- }
84
86
 
85
- const convertEventKey = (templateKey: string, key: string) => {
86
- return `${templateKey ? `${templateKey}_` : ''}${_.snakeCase(key).toUpperCase()}`
87
- }
87
+ const convertEventKey = (templateKey: string, key: string) =>
88
+ `${templateKey ? `${templateKey}_` : ''}${snakeCase(key).toUpperCase()}`
88
89
 
89
90
  const basicAnimationEvents = ['show', 'standby', 'breatheStart']
90
91
 
@@ -92,14 +93,13 @@ const compileAnimations = (
92
93
  templateKey: string,
93
94
  animations: { [key: string]: Animation },
94
95
  errorReference: string,
95
- ) => {
96
- return Object.entries(animations).reduce((acc, [key, animation]) => {
96
+ ) =>
97
+ Object.entries(animations).reduce((acc, [key, animation]) => {
97
98
  if (!animation?.id) throw new Error(`Invalid animation reference ${errorReference}`)
98
99
  acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
99
100
  `ANIMATION#${animation.id}`
100
101
  return acc
101
102
  }, {})
102
- }
103
103
 
104
104
  const compileActionParam = (templateKey: string, actionName: string, paramName: string) =>
105
105
  templateActionNameMap[templateKey]?.[actionName]?.[paramName] || paramName
@@ -190,7 +190,7 @@ const compileSwitchConds = (templateKey, conds, errorReference) =>
190
190
  result.key = cond.data().id
191
191
  result.value = cond.value
192
192
  } else if (item.cond.__typename === 'SwitchCondInnerStateOutlet') {
193
- const cond = item.cond
193
+ const { cond } = item
194
194
  result.type = 'inner_state'
195
195
  result.key = convertOutletKey(templateKey, cond.outlet)
196
196
  result.value = cond.value
@@ -205,35 +205,33 @@ const compileSwitchConds = (templateKey, conds, errorReference) =>
205
205
  return result
206
206
  })
207
207
 
208
- const compileApplicationSettings = (settings: Application['settings']) => {
209
- return {
210
- internet_reachability_url: settings?.internetReachabilityUrl,
211
- enable_data_lock: settings?.enableDataLock,
212
- show_deprecated_features: settings?.showDeprecatedFeatures,
213
- enable_unstable_bricks: settings?.enableUnstableFeatures,
214
- runtime_cache_options: settings?.runtimeCacheOptions
215
- ? {
216
- disabled: settings.runtimeCacheOptions.disabled,
217
- behavior: settings.runtimeCacheOptions.behavior,
218
- retry_attempt_for_failure: settings.runtimeCacheOptions.retryAttemptForFailure,
219
- }
220
- : undefined,
221
- tv_options: settings?.tvOptions
222
- ? {
223
- disabled_selectable: settings.tvOptions.disabledSelectable,
224
- selectable_color: settings.tvOptions.selectableColor,
225
- selectable_border: settings.tvOptions.selectableBorder,
226
- }
227
- : undefined,
228
- ai: settings?.ai
229
- ? {
230
- use_anthropic_api_key_system_data: settings.ai.useAnthropicApiKeySystemData,
231
- use_openai_api_key_system_data: settings.ai.useOpenAiApiKeySystemData,
232
- use_gemini_api_key_system_data: settings.ai.useGeminiApiKeySystemData,
233
- }
234
- : undefined,
235
- }
236
- }
208
+ const compileApplicationSettings = (settings: Application['settings']) => ({
209
+ internet_reachability_url: settings?.internetReachabilityUrl,
210
+ enable_data_lock: settings?.enableDataLock,
211
+ show_deprecated_features: settings?.showDeprecatedFeatures,
212
+ enable_unstable_bricks: settings?.enableUnstableFeatures,
213
+ runtime_cache_options: settings?.runtimeCacheOptions
214
+ ? {
215
+ disabled: settings.runtimeCacheOptions.disabled,
216
+ behavior: settings.runtimeCacheOptions.behavior,
217
+ retry_attempt_for_failure: settings.runtimeCacheOptions.retryAttemptForFailure,
218
+ }
219
+ : undefined,
220
+ tv_options: settings?.tvOptions
221
+ ? {
222
+ disabled_selectable: settings.tvOptions.disabledSelectable,
223
+ selectable_color: settings.tvOptions.selectableColor,
224
+ selectable_border: settings.tvOptions.selectableBorder,
225
+ }
226
+ : undefined,
227
+ ai: settings?.ai
228
+ ? {
229
+ use_anthropic_api_key_system_data: settings.ai.useAnthropicApiKeySystemData,
230
+ use_openai_api_key_system_data: settings.ai.useOpenAiApiKeySystemData,
231
+ use_gemini_api_key_system_data: settings.ai.useGeminiApiKeySystemData,
232
+ }
233
+ : undefined,
234
+ })
237
235
 
238
236
  const animationTypeMap = {
239
237
  AnimationTimingConfig: 'timing',
@@ -325,10 +323,108 @@ const compileModule = (subspace: Subspace) => {
325
323
  }
326
324
  }
327
325
 
326
+ /**
327
+ * Compile a run array element - if it's a getter function, call it and extract the id
328
+ * Note: All entity ids already include their prefix (e.g., SUBSPACE_xxx, BRICK_xxx, PROPERTY_BANK_DATA_NODE_xxx)
329
+ */
330
+ function compileRunElement(element: unknown): unknown {
331
+ if (typeof element !== 'function') return element
332
+
333
+ const result = element()
334
+ if (result && typeof result === 'object' && 'id' in result) {
335
+ return (result as { id: string }).id
336
+ }
337
+ return result
338
+ }
339
+
340
+ /**
341
+ * Compile a run array - resolve getter functions and extract ids with prefixes
342
+ */
343
+ function compileRunArray(run: unknown[]): unknown[] {
344
+ return run.map(compileRunElement)
345
+ }
346
+
347
+ /**
348
+ * Compile typed TestCase to raw format (strips __typename)
349
+ */
350
+ const compileTestCase = (testCase: TestCase) => ({
351
+ id: testCase.id,
352
+ name: testCase.name,
353
+ run: compileRunArray(testCase.run),
354
+ exit_on_failed: testCase.exit_on_failed,
355
+ commented: testCase.commented,
356
+ pre_delay: testCase.pre_delay,
357
+ post_delay: testCase.post_delay,
358
+ post_delay_rule: testCase.post_delay_rule,
359
+ jump_cond: testCase.jump_cond.map((cond) => ({
360
+ type: cond.type,
361
+ status: cond.status,
362
+ variable: cond.variable,
363
+ operator: cond.operator,
364
+ value: cond.value,
365
+ jump_to: cond.jump_to,
366
+ })),
367
+ })
368
+
369
+ /**
370
+ * Compile typed TestVariable to raw format (strips __typename)
371
+ */
372
+ const compileTestVariable = (variable: TestVariable) => ({
373
+ id: variable.id,
374
+ name: variable.name,
375
+ type: variable.type,
376
+ value: variable.value,
377
+ })
378
+
379
+ /**
380
+ * Convert an array of items with id property to an id-keyed record
381
+ */
382
+ const arrayToIdMap = <T extends { id: string }, R>(
383
+ items: T[],
384
+ transform: (item: T) => R,
385
+ ): Record<string, R> => Object.fromEntries(items.map((item) => [item.id, transform(item)]))
386
+
387
+ /**
388
+ * Compile typed AutomationTest to raw format
389
+ */
390
+ const compileAutomationTest = (test: AutomationTest) => ({
391
+ id: test.id,
392
+ title: test.title,
393
+ timeout: test.timeout,
394
+ trigger_type: test.trigger_type,
395
+ cron: test.cron,
396
+ skip_trigger_type_check: test.skip_trigger_type_check,
397
+ local_sync: test.local_sync,
398
+ meta: test.meta,
399
+ case_map: arrayToIdMap(test.cases, compileTestCase),
400
+ var_map: arrayToIdMap(test.variables, compileTestVariable),
401
+ })
402
+
403
+ /**
404
+ * Compile typed AutomationTestMap to raw format
405
+ */
406
+ const compileAutomationTestMap = (testMap: AutomationTestMap) => ({
407
+ title: testMap.title,
408
+ createdAt: testMap.createdAt,
409
+ map: arrayToIdMap(testMap.tests, compileAutomationTest),
410
+ })
411
+
412
+ /**
413
+ * Compile typed AutomationMap to raw automation_map format
414
+ */
415
+ const compileAutomation = (automationMap: AutomationMap) =>
416
+ Object.fromEntries(
417
+ Object.entries(automationMap).map(([mapId, testMap]) => [
418
+ mapId,
419
+ compileAutomationTestMap(testMap),
420
+ ]),
421
+ )
422
+
328
423
  export const compile = async (app: Application) => {
329
424
  await new Promise((resolve) => setImmediate(resolve, 0))
425
+ const timestamp = Date.now()
330
426
  const config = {
331
- title: app.name,
427
+ title: `${app.name || 'Unknown'}(${timestamp})`,
332
428
  subspace_map: app.subspaces.reduce((subspaceMap, subspace) => {
333
429
  subspaceMap[subspace.id] = {
334
430
  title: subspace.title,
@@ -338,7 +434,7 @@ export const compile = async (app: Application) => {
338
434
  brick: !subspace.unexpanded.brick,
339
435
  generator: !subspace.unexpanded.generator,
340
436
  canvas: subspace.unexpanded.canvas?.reduce((acc, canvas) => {
341
- acc[canvas.id] = canvas?.id ? false : true
437
+ acc[canvas.id] = !canvas?.id
342
438
  return acc
343
439
  }, {}),
344
440
  property_bank: !subspace.unexpanded.data,
@@ -365,7 +461,7 @@ export const compile = async (app: Application) => {
365
461
  property: animationDef.property,
366
462
  type: animationTypeMap[animationDef.config.__type],
367
463
  config: compileProperty(
368
- _.omit(animationDef.config, '__type'),
464
+ omit(animationDef.config, '__type'),
369
465
  `(animation: ${animation.id}, subspace ${subspace.id})`,
370
466
  ),
371
467
  }
@@ -377,12 +473,12 @@ export const compile = async (app: Application) => {
377
473
  animationRunType: animationDef.runType,
378
474
  compose_type: animationDef.composeType,
379
475
  item_list: animationDef.items.map((item, index) => {
380
- const animation = item()
381
- if (!animation?.id)
476
+ const innerAnimation = item()
477
+ if (!innerAnimation?.id)
382
478
  throw new Error(
383
- `Invalid animation index: ${index} (animation: ${animation.id}, subspace ${subspace.id})`,
479
+ `Invalid animation index: ${index} (animation: ${innerAnimation.id}, subspace ${subspace.id})`,
384
480
  )
385
- return { animation_id: animation.id }
481
+ return { animation_id: innerAnimation.id }
386
482
  }),
387
483
  }
388
484
  }
@@ -461,7 +557,7 @@ export const compile = async (app: Application) => {
461
557
  property.brickList = brickList
462
558
  } else {
463
559
  // Not supported Data for brickList
464
- throw new Error('Not supported Data for brickList directly')
560
+ throw new TypeError('Not supported Data for brickList directly')
465
561
  }
466
562
  if (Array.isArray(brickItems.brickDetails)) {
467
563
  const brickDetails = (brickItems.brickDetails || []).map((item, index) =>
@@ -470,7 +566,7 @@ export const compile = async (app: Application) => {
470
566
  property.brickDetails = brickDetails
471
567
  } else {
472
568
  // Not supported Data for brickList
473
- throw new Error('Not supported Data for brickList directly')
569
+ throw new TypeError('Not supported Data for brickList directly')
474
570
  }
475
571
  }
476
572
  map[brick.id] = {
@@ -677,6 +773,8 @@ export const compile = async (app: Application) => {
677
773
  camelCase: false,
678
774
  errorReference: `(data: ${data.id}, subspace ${subspace.id})`,
679
775
  }),
776
+ hit_equal: data.hit_equal,
777
+ hit_regex: data.hit_regex,
680
778
  }
681
779
  return map
682
780
  }, {}),
@@ -685,6 +783,7 @@ export const compile = async (app: Application) => {
685
783
  title: dataCalc.title,
686
784
  description: dataCalc.description,
687
785
  }
786
+ if (dataCalc.triggerMode) calc.trigger_type = dataCalc.triggerMode
688
787
  if (dataCalc.__typename === 'DataCalculationMap') {
689
788
  calc.type = 'general'
690
789
  const mapCalc = dataCalc as DataCalculationMap
@@ -694,7 +793,7 @@ export const compile = async (app: Application) => {
694
793
  if (!acc[port.key]) acc[port.key] = null
695
794
 
696
795
  let sourceId
697
- let sourceNode = port.source()
796
+ const sourceNode = port.source()
698
797
  if (sourceNode?.__typename === 'DataCalculationData')
699
798
  sourceId = (sourceNode as DataCalculationData).data().id
700
799
  if (sourceNode?.__typename === 'DataCommand') sourceId = sourceNode.id
@@ -715,7 +814,7 @@ export const compile = async (app: Application) => {
715
814
  if (!acc[port.key]) acc[port.key] = null
716
815
 
717
816
  let targetId
718
- let targetNode = port.target()
817
+ const targetNode = port.target()
719
818
  if (targetNode?.__typename === 'DataCalculationData')
720
819
  targetId = (targetNode as DataCalculationData).data().id
721
820
  if (targetNode?.__typename === 'DataCommand') targetId = targetNode.id
@@ -770,9 +869,9 @@ export const compile = async (app: Application) => {
770
869
  type: `command-node-${type}`,
771
870
  properties: {
772
871
  command: commandNode.__commandName,
773
- args: args.reduce((acc, input) => {
774
- acc[input.key] = input.source
775
- return acc
872
+ args: args.reduce((argsAcc, input) => {
873
+ argsAcc[input.key] = input.source
874
+ return argsAcc
776
875
  }, {}),
777
876
  },
778
877
  in: generateInputPorts(inputs),
@@ -784,12 +883,12 @@ export const compile = async (app: Application) => {
784
883
  calc.editor_info = mapCalc.editorInfo.reduce((acc, editorInfo) => {
785
884
  acc[getNodeId(editorInfo.node)] = {
786
885
  position: editorInfo.position,
787
- points: editorInfo.points.reduce((acc, point) => {
886
+ points: editorInfo.points.reduce((pointsAcc, point) => {
788
887
  const sourceId = getNodeId(point.source)
789
888
  const targetId = getNodeId(point.target)
790
889
  const key = `${sourceId}-${point.sourceOutputKey}-${targetId}-${point.targetInputKey}`
791
- acc[key] = point.positions
792
- return acc
890
+ pointsAcc[key] = point.positions
891
+ return pointsAcc
793
892
  }, {}),
794
893
  }
795
894
  return acc
@@ -819,6 +918,7 @@ export const compile = async (app: Application) => {
819
918
  note: scriptCalc.note,
820
919
  code,
821
920
  enable_async: scriptCalc.enableAsync,
921
+ trigger_mode: scriptCalc.triggerMode,
822
922
  inputs: scriptCalc.inputs.reduce((acc, input) => {
823
923
  acc[input.data().id] = input.key
824
924
  return acc
@@ -864,8 +964,14 @@ export const compile = async (app: Application) => {
864
964
  root_subspace_id: app.rootSubspace.id,
865
965
  fonts: app.fonts,
866
966
  ...compileApplicationSettings(app.settings),
867
- test_map: app.metadata?.TEMP_test_map || {},
868
- automation_map: app.metadata?.TEMP_automation_map || {},
967
+ // Use typed automationMap if available, otherwise fall back to TEMP metadata
968
+ test_map: app.automationMap
969
+ ? compileAutomation(app.automationMap)['AUTOMATION_MAP_DEFAULT']?.map || {}
970
+ : app.metadata?.TEMP_test_map || {},
971
+ automation_map: app.automationMap
972
+ ? compileAutomation(app.automationMap)
973
+ : app.metadata?.TEMP_automation_map || {},
974
+ update_timestamp: timestamp,
869
975
  }
870
976
  return config
871
977
  }