@fugood/bricks-ctor 2.25.0-beta.60 → 2.25.0-beta.62
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.
- package/package.json +4 -28
- package/tools/__tests__/legacy-forwarder.test.js +91 -0
- package/tools/_forward.ts +26 -0
- package/tools/deploy.ts +3 -175
- package/tools/mcp-server.ts +3 -35
- package/tools/postinstall.ts +3 -291
- package/tools/pull.ts +3 -198
- package/tools/push-config.ts +3 -113
- package/tools/simulator.ts +3 -149
- package/compile/__tests__/config-diff.test.js +0 -100
- package/compile/__tests__/index.test.js +0 -461
- package/compile/__tests__/util.test.js +0 -450
- package/compile/action-name-map.ts +0 -1079
- package/compile/config-diff.ts +0 -155
- package/compile/index.ts +0 -1594
- package/compile/util.ts +0 -482
- package/index.ts +0 -6
- package/skills/bricks-ctor/SKILL.md +0 -38
- package/skills/bricks-ctor/references/animation.md +0 -160
- package/skills/bricks-ctor/references/architecture-patterns.md +0 -88
- package/skills/bricks-ctor/references/automations.md +0 -232
- package/skills/bricks-ctor/references/buttress.md +0 -245
- package/skills/bricks-ctor/references/data-calculation.md +0 -252
- package/skills/bricks-ctor/references/local-sync.md +0 -129
- package/skills/bricks-ctor/references/media-flow.md +0 -165
- package/skills/bricks-ctor/references/remote-data-bank.md +0 -196
- package/skills/bricks-ctor/references/simulator.md +0 -132
- package/skills/bricks-ctor/references/source-editing-tools.md +0 -81
- package/skills/bricks-ctor/references/standby-transition.md +0 -124
- package/skills/bricks-ctor/references/verification-toolchain.md +0 -200
- package/skills/bricks-design/SKILL.md +0 -171
- package/skills/bricks-design/references/architecture-truths.md +0 -132
- package/skills/bricks-design/references/avoiding-complexity.md +0 -91
- package/skills/bricks-design/references/design-critique.md +0 -195
- package/skills/bricks-design/references/design-languages.md +0 -265
- package/skills/bricks-design/references/performance.md +0 -116
- package/skills/bricks-design/references/presentation-and-slideshow.md +0 -137
- package/skills/bricks-design/references/translating-inputs.md +0 -152
- package/skills/bricks-design/references/variations-and-tweaks.md +0 -124
- package/skills/bricks-design/references/when-the-brief-is-branded.md +0 -284
- package/skills/bricks-design/references/when-the-brief-is-vague.md +0 -85
- package/skills/bricks-design/references/workflow.md +0 -134
- package/skills/bricks-ux/SKILL.md +0 -114
- package/skills/bricks-ux/references/accessibility.md +0 -162
- package/skills/bricks-ux/references/flow-states.md +0 -175
- package/skills/bricks-ux/references/interaction-archetypes.md +0 -189
- package/skills/bricks-ux/references/monitoring-screens.md +0 -153
- package/skills/bricks-ux/references/pressable-composition.md +0 -126
- package/skills/bricks-ux/references/user-journey.md +0 -168
- package/skills/bricks-ux/references/ux-critique.md +0 -256
- package/skills/rive-marketplace/SKILL.md +0 -99
- package/tools/__tests__/_cli-error.test.ts +0 -35
- package/tools/__tests__/_mcp-config.test.ts +0 -67
- package/tools/__tests__/pull.test.ts +0 -108
- package/tools/_cli-error.ts +0 -17
- package/tools/_edits-log.ts +0 -41
- package/tools/_git-author.ts +0 -37
- package/tools/_last-pushed-commit.ts +0 -28
- package/tools/_mcp-config.ts +0 -42
- package/tools/_shell.ts +0 -180
- package/tools/icons/.gitattributes +0 -1
- package/tools/icons/fa6pro-glyphmap.json +0 -4686
- package/tools/icons/fa6pro-meta.json +0 -1
- package/tools/mcp-env.ts +0 -13
- package/tools/mcp-tools/__tests__/data-calc-editing.test.js +0 -516
- package/tools/mcp-tools/__tests__/entry-editing.test.js +0 -866
- package/tools/mcp-tools/__tests__/huggingface.test.ts +0 -49
- package/tools/mcp-tools/__tests__/icons.test.ts +0 -21
- package/tools/mcp-tools/__tests__/mcp-env.test.js +0 -19
- package/tools/mcp-tools/_editing-helpers.ts +0 -98
- package/tools/mcp-tools/_verify.ts +0 -50
- package/tools/mcp-tools/compile.ts +0 -104
- package/tools/mcp-tools/data-calc-editing.ts +0 -1311
- package/tools/mcp-tools/entry-editing.ts +0 -2297
- package/tools/mcp-tools/huggingface.ts +0 -772
- package/tools/mcp-tools/icons.ts +0 -97
- package/tools/mcp-tools/lottie.ts +0 -102
- package/tools/mcp-tools/media.ts +0 -113
- package/tools/simulator-main.mjs +0 -488
- package/tools/simulator-preload.cjs +0 -16
- package/types/animation.d.ts +0 -116
- package/types/automation.d.ts +0 -231
- package/types/brick-base.d.ts +0 -80
- package/types/bricks/Camera.d.ts +0 -246
- package/types/bricks/Chart.d.ts +0 -372
- package/types/bricks/GenerativeMedia.d.ts +0 -290
- package/types/bricks/Icon.d.ts +0 -98
- package/types/bricks/Image.d.ts +0 -126
- package/types/bricks/Items.d.ts +0 -480
- package/types/bricks/Lottie.d.ts +0 -168
- package/types/bricks/Maps.d.ts +0 -262
- package/types/bricks/QrCode.d.ts +0 -117
- package/types/bricks/Rect.d.ts +0 -150
- package/types/bricks/RichText.d.ts +0 -131
- package/types/bricks/Rive.d.ts +0 -220
- package/types/bricks/Scene3D.d.ts +0 -676
- package/types/bricks/Sketch.d.ts +0 -256
- package/types/bricks/Slideshow.d.ts +0 -201
- package/types/bricks/Svg.d.ts +0 -99
- package/types/bricks/Text.d.ts +0 -148
- package/types/bricks/TextInput.d.ts +0 -242
- package/types/bricks/Video.d.ts +0 -242
- package/types/bricks/VideoStreaming.d.ts +0 -112
- package/types/bricks/WebRtcStream.d.ts +0 -65
- package/types/bricks/WebView.d.ts +0 -168
- package/types/bricks/index.d.ts +0 -23
- package/types/canvas.d.ts +0 -82
- package/types/common.d.ts +0 -141
- package/types/data-calc-command/base.d.ts +0 -57
- package/types/data-calc-command/collection.d.ts +0 -418
- package/types/data-calc-command/color.d.ts +0 -432
- package/types/data-calc-command/constant.d.ts +0 -50
- package/types/data-calc-command/datetime.d.ts +0 -147
- package/types/data-calc-command/file.d.ts +0 -129
- package/types/data-calc-command/index.d.ts +0 -13
- package/types/data-calc-command/iteratee.d.ts +0 -23
- package/types/data-calc-command/logictype.d.ts +0 -190
- package/types/data-calc-command/math.d.ts +0 -275
- package/types/data-calc-command/object.d.ts +0 -119
- package/types/data-calc-command/sandbox.d.ts +0 -66
- package/types/data-calc-command/string.d.ts +0 -407
- package/types/data-calc-script.d.ts +0 -21
- package/types/data-calc.d.ts +0 -12
- package/types/data.d.ts +0 -97
- package/types/generators/AlarmClock.d.ts +0 -110
- package/types/generators/Assistant.d.ts +0 -640
- package/types/generators/BleCentral.d.ts +0 -247
- package/types/generators/BlePeripheral.d.ts +0 -208
- package/types/generators/CanvasMap.d.ts +0 -74
- package/types/generators/CastlesPay.d.ts +0 -87
- package/types/generators/DataBank.d.ts +0 -160
- package/types/generators/File.d.ts +0 -432
- package/types/generators/GraphQl.d.ts +0 -132
- package/types/generators/Http.d.ts +0 -222
- package/types/generators/HttpServer.d.ts +0 -230
- package/types/generators/Information.d.ts +0 -103
- package/types/generators/Intent.d.ts +0 -168
- package/types/generators/Iterator.d.ts +0 -108
- package/types/generators/Keyboard.d.ts +0 -105
- package/types/generators/LlmAnthropicCompat.d.ts +0 -212
- package/types/generators/LlmAppleBuiltin.d.ts +0 -159
- package/types/generators/LlmGgml.d.ts +0 -903
- package/types/generators/LlmMediaTekNeuroPilot.d.ts +0 -235
- package/types/generators/LlmMlx.d.ts +0 -228
- package/types/generators/LlmOnnx.d.ts +0 -213
- package/types/generators/LlmOpenAiCompat.d.ts +0 -312
- package/types/generators/LlmQualcommAiEngine.d.ts +0 -247
- package/types/generators/Mcp.d.ts +0 -637
- package/types/generators/McpServer.d.ts +0 -289
- package/types/generators/MediaFlow.d.ts +0 -170
- package/types/generators/MqttBroker.d.ts +0 -141
- package/types/generators/MqttClient.d.ts +0 -141
- package/types/generators/Question.d.ts +0 -408
- package/types/generators/RealtimeTranscription.d.ts +0 -287
- package/types/generators/RerankerGgml.d.ts +0 -195
- package/types/generators/SerialPort.d.ts +0 -151
- package/types/generators/SoundPlayer.d.ts +0 -94
- package/types/generators/SoundRecorder.d.ts +0 -139
- package/types/generators/SpeechToTextGgml.d.ts +0 -424
- package/types/generators/SpeechToTextOnnx.d.ts +0 -236
- package/types/generators/SpeechToTextPlatform.d.ts +0 -85
- package/types/generators/SqLite.d.ts +0 -159
- package/types/generators/Step.d.ts +0 -107
- package/types/generators/SttAppleBuiltin.d.ts +0 -153
- package/types/generators/Tcp.d.ts +0 -126
- package/types/generators/TcpServer.d.ts +0 -147
- package/types/generators/TextToSpeechAppleBuiltin.d.ts +0 -127
- package/types/generators/TextToSpeechGgml.d.ts +0 -221
- package/types/generators/TextToSpeechOnnx.d.ts +0 -178
- package/types/generators/TextToSpeechOpenAiLike.d.ts +0 -121
- package/types/generators/ThermalPrinter.d.ts +0 -193
- package/types/generators/Tick.d.ts +0 -83
- package/types/generators/Udp.d.ts +0 -120
- package/types/generators/VadGgml.d.ts +0 -260
- package/types/generators/VadOnnx.d.ts +0 -231
- package/types/generators/VadTraditional.d.ts +0 -138
- package/types/generators/VectorStore.d.ts +0 -257
- package/types/generators/Watchdog.d.ts +0 -107
- package/types/generators/WebCrawler.d.ts +0 -103
- package/types/generators/WebRtc.d.ts +0 -181
- package/types/generators/WebSocket.d.ts +0 -148
- package/types/generators/index.d.ts +0 -57
- package/types/index.d.ts +0 -13
- package/types/subspace.d.ts +0 -60
- package/types/switch.d.ts +0 -51
- package/types/system.d.ts +0 -707
- package/utils/__tests__/calc.test.js +0 -25
- package/utils/__tests__/id.test.js +0 -154
- package/utils/calc.ts +0 -130
- package/utils/data.ts +0 -495
- package/utils/event-props.ts +0 -912
- package/utils/id.ts +0 -133
package/compile/index.ts
DELETED
|
@@ -1,1594 +0,0 @@
|
|
|
1
|
-
/* eslint-disable no-underscore-dangle -- Uses __typename, __actionName, etc. for type system */
|
|
2
|
-
import camelCase from 'lodash/camelCase'
|
|
3
|
-
import upperFirst from 'lodash/upperFirst'
|
|
4
|
-
import snakeCase from 'lodash/snakeCase'
|
|
5
|
-
import omit from 'lodash/omit'
|
|
6
|
-
import { parse as parseAST } from 'acorn'
|
|
7
|
-
import type { BlockStatement, ExportNamedDeclaration, FunctionDeclaration } from 'acorn'
|
|
8
|
-
import escodegen from 'escodegen'
|
|
9
|
-
import { makeSeededId } from '../utils/id'
|
|
10
|
-
import { generateCalulationMap } from './util'
|
|
11
|
-
import { templateActionNameMap } from './action-name-map'
|
|
12
|
-
import { templateEventPropsMap } from '../utils/event-props'
|
|
13
|
-
import { sh } from '../tools/_shell'
|
|
14
|
-
import { computeConfigChange, readBuildConfig } from './config-diff'
|
|
15
|
-
import { appendEditRecord, editProvenance } from '../tools/_edits-log'
|
|
16
|
-
import { isTruthyEnv } from '../tools/mcp-env'
|
|
17
|
-
import type {
|
|
18
|
-
Application,
|
|
19
|
-
Data,
|
|
20
|
-
DataAssetKind,
|
|
21
|
-
Animation,
|
|
22
|
-
AnimationDef,
|
|
23
|
-
AnimationComposeDef,
|
|
24
|
-
ActionWithDataParams,
|
|
25
|
-
ActionWithParams,
|
|
26
|
-
BrickItems,
|
|
27
|
-
SwitchCondData,
|
|
28
|
-
SwitchCondInnerStateCurrentCanvas,
|
|
29
|
-
SwitchCondPropertyBankByItemKey,
|
|
30
|
-
DataCalculationMap,
|
|
31
|
-
DataCalculationScript,
|
|
32
|
-
DataCalculationData,
|
|
33
|
-
DataCommand,
|
|
34
|
-
Brick,
|
|
35
|
-
Canvas,
|
|
36
|
-
Subspace,
|
|
37
|
-
AutomationMap,
|
|
38
|
-
AutomationTestMap,
|
|
39
|
-
AutomationTest,
|
|
40
|
-
TestCase,
|
|
41
|
-
TestVariable,
|
|
42
|
-
} from '../types'
|
|
43
|
-
|
|
44
|
-
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}'
|
|
45
|
-
|
|
46
|
-
const entryIdPatterns = {
|
|
47
|
-
SUBSPACE: new RegExp(`^SUBSPACE_${uuidPattern}$`),
|
|
48
|
-
CANVAS: new RegExp(`^CANVAS_${uuidPattern}$`),
|
|
49
|
-
BRICK: new RegExp(`^BRICK_${uuidPattern}$`),
|
|
50
|
-
GENERATOR: new RegExp(`^(GENERATOR|AUTO_GENERATOR)_${uuidPattern}$`),
|
|
51
|
-
ANIMATION: new RegExp(`^ANIMATION_${uuidPattern}$`),
|
|
52
|
-
PROPERTY_BANK_DATA_NODE: new RegExp(`^PROPERTY_BANK_DATA_NODE_${uuidPattern}$`),
|
|
53
|
-
PROPERTY_BANK_COMMAND_NODE: new RegExp(`^PROPERTY_BANK_COMMAND_NODE_${uuidPattern}$`),
|
|
54
|
-
PROPERTY_BANK_COMMAND_MAP: new RegExp(`^PROPERTY_BANK_COMMAND_MAP_${uuidPattern}$`),
|
|
55
|
-
BRICK_STATE_GROUP: new RegExp(`^BRICK_STATE_GROUP_${uuidPattern}$`),
|
|
56
|
-
TEST: new RegExp(`^TEST_${uuidPattern}$`),
|
|
57
|
-
TEST_CASE: new RegExp(`^TEST_CASE_${uuidPattern}$`),
|
|
58
|
-
TEST_VAR: new RegExp(`^TEST_VAR_${uuidPattern}$`),
|
|
59
|
-
AUTOMATION_MAP: /^AUTOMATION_MAP_.*/,
|
|
60
|
-
} as const
|
|
61
|
-
|
|
62
|
-
type EntryIdPatternKey = keyof typeof entryIdPatterns
|
|
63
|
-
|
|
64
|
-
const assertEntryId = (
|
|
65
|
-
id: unknown,
|
|
66
|
-
patternKey: EntryIdPatternKey,
|
|
67
|
-
errorReference: string = '',
|
|
68
|
-
): string => {
|
|
69
|
-
const pattern = entryIdPatterns[patternKey]
|
|
70
|
-
if (typeof id !== 'string' || !pattern.test(id)) {
|
|
71
|
-
throw new Error(
|
|
72
|
-
`Invalid ${patternKey} id${errorReference ? ` ${errorReference}` : ''}: ${String(id)}`,
|
|
73
|
-
)
|
|
74
|
-
}
|
|
75
|
-
return id
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Per-compile error collection. Instead of throwing on the first bad entity, record the
|
|
79
|
-
// message and skip that entity, so a single compile surfaces every entity's first error
|
|
80
|
-
// (e.g. an invalid brick id, generator id and data-calc id all in one run). `errors` is
|
|
81
|
-
// local to each compile() call — no shared state — and compile throws an aggregated error
|
|
82
|
-
// at the end when any were collected, so a failed compile still rejects and never returns
|
|
83
|
-
// or writes a partial config.
|
|
84
|
-
const collect = <T>(errors: string[], fn: () => T, fallback: T): T => {
|
|
85
|
-
try {
|
|
86
|
-
return fn()
|
|
87
|
-
} catch (error) {
|
|
88
|
-
errors.push(error instanceof Error ? error.message : String(error))
|
|
89
|
-
return fallback
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// reduce() into an id-keyed map, recording a per-item compile error and skipping that item
|
|
94
|
-
// instead of aborting the whole compile. The callback is the last argument (and the init is
|
|
95
|
-
// always {}) so the formatter keeps its body inline rather than re-indenting it.
|
|
96
|
-
const collectReduce = <T>(
|
|
97
|
-
errors: string[],
|
|
98
|
-
items: T[],
|
|
99
|
-
fn: (acc: Record<string, unknown>, item: T, index: number) => Record<string, unknown>,
|
|
100
|
-
): Record<string, unknown> =>
|
|
101
|
-
items.reduce((acc, item, index) => collect(errors, () => fn(acc, item, index), acc), {})
|
|
102
|
-
|
|
103
|
-
const compileProperty = (property, errorReference: string, result = {}) => {
|
|
104
|
-
if (Array.isArray(property)) {
|
|
105
|
-
return property.map((p) => compileProperty(p, errorReference))
|
|
106
|
-
}
|
|
107
|
-
if (property?.__typename === 'DataLink') {
|
|
108
|
-
const data = property.data?.()
|
|
109
|
-
if (!data?.id) throw new Error(`Invalid DataLink reference ${errorReference}`)
|
|
110
|
-
return `PROPERTY_BANK#${data.id}`
|
|
111
|
-
}
|
|
112
|
-
if (typeof property === 'function') {
|
|
113
|
-
const instance = property()
|
|
114
|
-
if (!instance?.id) throw new Error(`Invalid ID reference ${errorReference}`)
|
|
115
|
-
return instance?.id // defined type instance getter
|
|
116
|
-
}
|
|
117
|
-
if (property && typeof property === 'object') {
|
|
118
|
-
return Object.entries(property).reduce((acc, [key, value]) => {
|
|
119
|
-
acc[key] = compileProperty(value, errorReference)
|
|
120
|
-
return acc
|
|
121
|
-
}, result)
|
|
122
|
-
}
|
|
123
|
-
return property
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const compileScriptCalculationCode = (code = '') => {
|
|
127
|
-
try {
|
|
128
|
-
const program = parseAST(code, { sourceType: 'module', ecmaVersion: 2020 })
|
|
129
|
-
// The stored config holds the bare function body, which codegen re-wraps as
|
|
130
|
-
// `export function main() { <code> }`. Unwrap it back to that body here.
|
|
131
|
-
let block = ((program.body[0] as ExportNamedDeclaration).declaration as FunctionDeclaration)
|
|
132
|
-
?.body as BlockStatement | undefined
|
|
133
|
-
if (!block) return code || ''
|
|
134
|
-
// Earlier versions emitted the whole BlockStatement (braces included), so every
|
|
135
|
-
// compile -> codegen round-trip nested the body in one more `{ }`. Emit the inner
|
|
136
|
-
// statements instead, collapsing any wrapper blocks previous round-trips added so
|
|
137
|
-
// existing over-wrapped sandboxes heal on the next compile.
|
|
138
|
-
while (block.body.length === 1 && block.body[0].type === 'BlockStatement') {
|
|
139
|
-
block = block.body[0] as BlockStatement
|
|
140
|
-
}
|
|
141
|
-
return escodegen.generate(
|
|
142
|
-
{ type: 'Program', body: block.body },
|
|
143
|
-
{
|
|
144
|
-
format: {
|
|
145
|
-
indent: { style: ' ' },
|
|
146
|
-
semicolons: false,
|
|
147
|
-
},
|
|
148
|
-
comment: true,
|
|
149
|
-
},
|
|
150
|
-
)
|
|
151
|
-
} catch {
|
|
152
|
-
return code || ''
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const getTemplateName = (key: string) =>
|
|
157
|
-
upperFirst(camelCase(key.replace(/^(BRICK|GENERATOR)_/, '')))
|
|
158
|
-
|
|
159
|
-
const compileEventActionValue = (templateKey, eventKey, value, errorReference) => {
|
|
160
|
-
const tmplEventProperties = templateEventPropsMap[getTemplateName(templateKey)]
|
|
161
|
-
const props = tmplEventProperties?.[eventKey]
|
|
162
|
-
if (!props) return compileProperty(value, errorReference)
|
|
163
|
-
if (typeof value === 'string' && value in props) {
|
|
164
|
-
const expectedType = props[value]
|
|
165
|
-
if (expectedType) return value
|
|
166
|
-
}
|
|
167
|
-
if (typeof value === 'string' && /^(BRICK|GENERATOR)_/.test(value)) {
|
|
168
|
-
console.warn(
|
|
169
|
-
`[Warning] Event property "${value}" is not compatible with event "${eventKey}" of ${templateKey} ${errorReference}`,
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
return compileProperty(value, errorReference)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
const convertOutletKey = (templateKey: string, key: string) =>
|
|
176
|
-
`${templateKey}_${snakeCase(key).toUpperCase()}`
|
|
177
|
-
|
|
178
|
-
const compileOutlets = (
|
|
179
|
-
templateKey: string,
|
|
180
|
-
outlets: { [key: string]: () => Data },
|
|
181
|
-
errorReference: string,
|
|
182
|
-
) =>
|
|
183
|
-
Object.entries(outlets).reduce((acc, [key, data]) => {
|
|
184
|
-
const dataInstance = data?.()
|
|
185
|
-
const dataId = assertEntryId(dataInstance?.id, 'PROPERTY_BANK_DATA_NODE', errorReference)
|
|
186
|
-
acc[convertOutletKey(templateKey, key)] = dataId
|
|
187
|
-
return acc
|
|
188
|
-
}, {})
|
|
189
|
-
|
|
190
|
-
const convertEventKey = (templateKey: string, key: string) =>
|
|
191
|
-
`${templateKey ? `${templateKey}_` : ''}${snakeCase(key).toUpperCase()}`
|
|
192
|
-
|
|
193
|
-
const basicAnimationEvents = ['show', 'standby', 'breatheStart']
|
|
194
|
-
|
|
195
|
-
const compileAnimations = (
|
|
196
|
-
templateKey: string,
|
|
197
|
-
animations: { [key: string]: Animation | (() => Animation) },
|
|
198
|
-
errorReference: string,
|
|
199
|
-
) =>
|
|
200
|
-
Object.entries(animations).reduce((acc, [key, animation]) => {
|
|
201
|
-
// Animation events accept either a direct Animation or a getter; unwrap.
|
|
202
|
-
const resolved = typeof animation === 'function' ? animation() : animation
|
|
203
|
-
const animationId = assertEntryId(resolved?.id, 'ANIMATION', errorReference)
|
|
204
|
-
acc[convertEventKey(basicAnimationEvents.includes(key) ? 'BRICK' : templateKey, key)] =
|
|
205
|
-
`ANIMATION#${animationId}`
|
|
206
|
-
return acc
|
|
207
|
-
}, {})
|
|
208
|
-
|
|
209
|
-
const compileActionParam = (templateKey: string, actionName: string, paramName: string) =>
|
|
210
|
-
templateActionNameMap[templateKey]?.[actionName]?.[paramName] || paramName
|
|
211
|
-
|
|
212
|
-
const compileEvents = (
|
|
213
|
-
templateKey: string,
|
|
214
|
-
eventMap: { [key: string]: Array<any> },
|
|
215
|
-
options = { camelCase: false, errorReference: '' },
|
|
216
|
-
) => {
|
|
217
|
-
const { camelCase, errorReference } = options
|
|
218
|
-
return Object.entries(eventMap).reduce((acc, [key, events]) => {
|
|
219
|
-
acc[convertEventKey(templateKey, key)] = events.map((event) => {
|
|
220
|
-
const { handler, action } = event
|
|
221
|
-
|
|
222
|
-
let handlerKey
|
|
223
|
-
let handlerTemplateKey
|
|
224
|
-
if (typeof handler === 'string') {
|
|
225
|
-
// Only the literal 'system' handler is normalized to the SYSTEM template key.
|
|
226
|
-
// SubspaceID (SUBSPACE_*) and ItemBrickID handlers are kept verbatim: the runtime
|
|
227
|
-
// resolves them case-sensitively (see mapEventMapHandlersWithNewId), so uppercasing
|
|
228
|
-
// a mixed-case ItemBrickID would break handler-to-item event wiring.
|
|
229
|
-
if (handler === 'system') {
|
|
230
|
-
handlerKey = 'SYSTEM'
|
|
231
|
-
handlerTemplateKey = 'SYSTEM'
|
|
232
|
-
} else {
|
|
233
|
-
handlerKey = handler
|
|
234
|
-
}
|
|
235
|
-
} else if (typeof handler === 'function') {
|
|
236
|
-
let instance = handler()
|
|
237
|
-
if (instance?.id) {
|
|
238
|
-
instance = instance as { id: string }
|
|
239
|
-
handlerKey = instance?.id
|
|
240
|
-
} else if (instance?.brickId) {
|
|
241
|
-
instance = instance as { brickId: string; templateKey: string }
|
|
242
|
-
handlerKey = instance.brickId
|
|
243
|
-
}
|
|
244
|
-
handlerTemplateKey = instance?.templateKey
|
|
245
|
-
}
|
|
246
|
-
if (!handlerKey) throw new Error(`Invalid handler: ${handler} ${errorReference}`)
|
|
247
|
-
|
|
248
|
-
const parameterList: Array<object> = []
|
|
249
|
-
if (Object.prototype.hasOwnProperty.call(action, 'params')) {
|
|
250
|
-
const actionDef = action as ActionWithParams
|
|
251
|
-
;(actionDef.params || []).forEach(({ input, value, mapping }) => {
|
|
252
|
-
const param = {
|
|
253
|
-
[camelCase ? 'inputToReceiver' : 'input_to_receiver']: handlerTemplateKey
|
|
254
|
-
? compileActionParam(handlerTemplateKey, action.__actionName, input)
|
|
255
|
-
: input,
|
|
256
|
-
[camelCase ? 'resultFromSender' : 'result_from_sender']:
|
|
257
|
-
mapping || compileEventActionValue(handlerTemplateKey, key, value, errorReference),
|
|
258
|
-
}
|
|
259
|
-
if (mapping) param[camelCase ? 'resultDataMapping' : 'result_data_mapping'] = true
|
|
260
|
-
parameterList.push(param)
|
|
261
|
-
})
|
|
262
|
-
} else if (Object.prototype.hasOwnProperty.call(action, 'dataParams')) {
|
|
263
|
-
const actionDef = action as ActionWithDataParams
|
|
264
|
-
;(actionDef.dataParams || []).forEach(({ input, value, mapping }) => {
|
|
265
|
-
if (!input) return
|
|
266
|
-
const param = {
|
|
267
|
-
[camelCase ? 'inputToReceiver' : 'input_to_receiver']: input().id,
|
|
268
|
-
[camelCase ? 'resultFromSender' : 'result_from_sender']:
|
|
269
|
-
mapping || compileEventActionValue(handlerTemplateKey, key, value, errorReference),
|
|
270
|
-
}
|
|
271
|
-
if (mapping) param[camelCase ? 'resultDataMapping' : 'result_data_mapping'] = true
|
|
272
|
-
parameterList.push(param)
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
return {
|
|
276
|
-
handler: handlerKey,
|
|
277
|
-
action: action.__actionName,
|
|
278
|
-
[camelCase ? 'parameterList' : 'parameter_list']: parameterList,
|
|
279
|
-
[camelCase ? 'waitAsync' : 'wait_async']: event.waitAsync,
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
return acc
|
|
283
|
-
}, {})
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
const compileSwitchConds = (templateKey, conds, errorReference) =>
|
|
287
|
-
(conds || []).map((item: any, index) => {
|
|
288
|
-
const result: any = { method: item.method }
|
|
289
|
-
if (!item.cond) return result
|
|
290
|
-
if (item.cond.__typename === 'SwitchCondData') {
|
|
291
|
-
const cond = item.cond as SwitchCondData
|
|
292
|
-
result.type = 'property_bank'
|
|
293
|
-
if (!cond.data().id)
|
|
294
|
-
throw new Error(`Invalid data reference in conds index: ${index} ${errorReference}`)
|
|
295
|
-
result.key = cond.data().id
|
|
296
|
-
result.value = cond.value
|
|
297
|
-
} else if (item.cond.__typename === 'SwitchCondPropertyBankByItemKey') {
|
|
298
|
-
const cond = item.cond as SwitchCondPropertyBankByItemKey
|
|
299
|
-
result.type = 'property_bank_by_item_key'
|
|
300
|
-
if (!cond.data().id)
|
|
301
|
-
throw new Error(`Invalid data reference in conds index: ${index} ${errorReference}`)
|
|
302
|
-
result.key = cond.data().id
|
|
303
|
-
result.value = cond.value
|
|
304
|
-
} else if (item.cond.__typename === 'SwitchCondInnerStateOutlet') {
|
|
305
|
-
const { cond } = item
|
|
306
|
-
result.type = 'inner_state'
|
|
307
|
-
result.key = convertOutletKey(templateKey, cond.outlet)
|
|
308
|
-
result.value = cond.value
|
|
309
|
-
} else if (item.cond.__typename === 'SwitchCondInnerStateCurrentCanvas') {
|
|
310
|
-
const cond = item.cond as SwitchCondInnerStateCurrentCanvas
|
|
311
|
-
result.type = 'inner_state'
|
|
312
|
-
result.key = 'current_canvas'
|
|
313
|
-
if (!cond.value().id)
|
|
314
|
-
throw new Error(`Invalid canvas reference in conds index: ${index} ${errorReference}`)
|
|
315
|
-
result.value = cond.value().id
|
|
316
|
-
}
|
|
317
|
-
return result
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
const compileApplicationSettings = (settings: Application['settings']) => ({
|
|
321
|
-
internet_reachability_url: settings?.internetReachabilityUrl,
|
|
322
|
-
enable_data_lock: settings?.enableDataLock,
|
|
323
|
-
show_deprecated_features: settings?.showDeprecatedFeatures,
|
|
324
|
-
enable_unstable_bricks: settings?.enableUnstableFeatures,
|
|
325
|
-
runtime_cache_options: settings?.runtimeCacheOptions
|
|
326
|
-
? {
|
|
327
|
-
disabled: settings.runtimeCacheOptions.disabled,
|
|
328
|
-
behavior: settings.runtimeCacheOptions.behavior,
|
|
329
|
-
retry_attempt_for_failure: settings.runtimeCacheOptions.retryAttemptForFailure,
|
|
330
|
-
}
|
|
331
|
-
: undefined,
|
|
332
|
-
tv_options: settings?.tvOptions
|
|
333
|
-
? {
|
|
334
|
-
disabled_selectable: settings.tvOptions.disabledSelectable,
|
|
335
|
-
selectable_color: settings.tvOptions.selectableColor,
|
|
336
|
-
selectable_border: settings.tvOptions.selectableBorder,
|
|
337
|
-
}
|
|
338
|
-
: undefined,
|
|
339
|
-
ai: settings?.ai
|
|
340
|
-
? {
|
|
341
|
-
use_anthropic_api_key_system_data: settings.ai.useAnthropicApiKeySystemData,
|
|
342
|
-
use_openai_api_key_system_data: settings.ai.useOpenAiApiKeySystemData,
|
|
343
|
-
use_gemini_api_key_system_data: settings.ai.useGeminiApiKeySystemData,
|
|
344
|
-
}
|
|
345
|
-
: undefined,
|
|
346
|
-
hide_short_refs: settings?.hideShortRefs,
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
const animationTypeMap = {
|
|
350
|
-
AnimationTimingConfig: 'timing',
|
|
351
|
-
AnimationSpringConfig: 'spring',
|
|
352
|
-
AnimationDecayConfig: 'decay',
|
|
353
|
-
} as const
|
|
354
|
-
|
|
355
|
-
type CompiledAnimationType = (typeof animationTypeMap)[keyof typeof animationTypeMap]
|
|
356
|
-
type WarningMetadata = Record<string, unknown>
|
|
357
|
-
|
|
358
|
-
const animationProperties = new Set([
|
|
359
|
-
'transform.translateX',
|
|
360
|
-
'transform.translateY',
|
|
361
|
-
'transform.scale',
|
|
362
|
-
'transform.scaleX',
|
|
363
|
-
'transform.scaleY',
|
|
364
|
-
'transform.rotate',
|
|
365
|
-
'transform.rotateX',
|
|
366
|
-
'transform.rotateY',
|
|
367
|
-
'opacity',
|
|
368
|
-
])
|
|
369
|
-
|
|
370
|
-
const animationComposeTypes = new Set(['parallel', 'sequence'])
|
|
371
|
-
const springConfigFamilies = [
|
|
372
|
-
['stiffness', 'damping', 'mass'],
|
|
373
|
-
['tension', 'friction'],
|
|
374
|
-
['bounciness', 'speed'],
|
|
375
|
-
]
|
|
376
|
-
const springConfigFamilyKeys = new Set(springConfigFamilies.flat())
|
|
377
|
-
|
|
378
|
-
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
379
|
-
Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
380
|
-
|
|
381
|
-
const hasDefinedConfigValue = (config: Record<string, unknown>, key: string) =>
|
|
382
|
-
config[key] !== undefined
|
|
383
|
-
|
|
384
|
-
const assertConfigValue = (
|
|
385
|
-
config: Record<string, unknown>,
|
|
386
|
-
key: string,
|
|
387
|
-
errorReference: string,
|
|
388
|
-
) => {
|
|
389
|
-
if (!hasDefinedConfigValue(config, key)) {
|
|
390
|
-
throw new Error(`Invalid animation config ${errorReference}: missing "${key}"`)
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const assertAnimationProperty = (property: unknown, errorReference: string) => {
|
|
395
|
-
if (typeof property !== 'string' || !animationProperties.has(property)) {
|
|
396
|
-
throw new Error(
|
|
397
|
-
`Invalid animation property${errorReference ? ` ${errorReference}` : ''}: ${String(
|
|
398
|
-
property,
|
|
399
|
-
)}`,
|
|
400
|
-
)
|
|
401
|
-
}
|
|
402
|
-
return property
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const getAnimationType = (config: unknown, errorReference: string): CompiledAnimationType => {
|
|
406
|
-
if (!isRecord(config)) {
|
|
407
|
-
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
const animationType = animationTypeMap[config.__type as keyof typeof animationTypeMap]
|
|
411
|
-
if (!animationType) {
|
|
412
|
-
throw new Error(`Invalid animation config type ${errorReference}: ${String(config.__type)}`)
|
|
413
|
-
}
|
|
414
|
-
return animationType
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const assertAnimationComposeType = (composeType: unknown, errorReference: string) => {
|
|
418
|
-
if (typeof composeType !== 'string' || !animationComposeTypes.has(composeType)) {
|
|
419
|
-
throw new Error(`Invalid animation compose type ${errorReference}: ${String(composeType)}`)
|
|
420
|
-
}
|
|
421
|
-
return composeType
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const pickDefinedConfigValues = (config: Record<string, unknown>, keys: string[]) =>
|
|
425
|
-
keys.reduce((acc, key) => {
|
|
426
|
-
if (hasDefinedConfigValue(config, key)) acc[key] = config[key]
|
|
427
|
-
return acc
|
|
428
|
-
}, {})
|
|
429
|
-
|
|
430
|
-
const getDefinedConfigKeys = (config: Record<string, unknown>, keys: string[]) =>
|
|
431
|
-
keys.filter((key) => hasDefinedConfigValue(config, key))
|
|
432
|
-
|
|
433
|
-
const formatWarningMetadata = (metadata: WarningMetadata = {}) =>
|
|
434
|
-
Object.entries(metadata)
|
|
435
|
-
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
|
436
|
-
.map(([key, value]) => `${key}: ${String(value)}`)
|
|
437
|
-
.join(', ')
|
|
438
|
-
|
|
439
|
-
const formatWarningReference = (metadata?: WarningMetadata) => {
|
|
440
|
-
const metadataText = formatWarningMetadata(metadata)
|
|
441
|
-
return metadataText ? ` [${metadataText}]` : ''
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const normalizeSpringConfig = (
|
|
445
|
-
config: Record<string, unknown>,
|
|
446
|
-
errorReference: string,
|
|
447
|
-
warningMetadata?: WarningMetadata,
|
|
448
|
-
): Record<string, unknown> => {
|
|
449
|
-
assertConfigValue(config, 'toValue', errorReference)
|
|
450
|
-
|
|
451
|
-
const usedFamilies = springConfigFamilies.filter((keys) =>
|
|
452
|
-
keys.some((key) => hasDefinedConfigValue(config, key)),
|
|
453
|
-
)
|
|
454
|
-
if (usedFamilies.length <= 1) return config
|
|
455
|
-
|
|
456
|
-
const configWithoutSpringFamily = Object.entries(config).reduce((acc, [key, value]) => {
|
|
457
|
-
if (!springConfigFamilyKeys.has(key)) acc[key] = value
|
|
458
|
-
return acc
|
|
459
|
-
}, {})
|
|
460
|
-
|
|
461
|
-
// Match runtime normalization: physical spring values are most explicit,
|
|
462
|
-
// otherwise preserve BRICKS' historical tension/friction controls.
|
|
463
|
-
const resolvedFamily =
|
|
464
|
-
usedFamilies.find((keys) => keys.includes('stiffness')) ||
|
|
465
|
-
usedFamilies.find((keys) => keys.includes('tension')) ||
|
|
466
|
-
usedFamilies[0]
|
|
467
|
-
const resolvedFamilyKeys = getDefinedConfigKeys(config, resolvedFamily)
|
|
468
|
-
const droppedFamilyKeys = usedFamilies
|
|
469
|
-
.filter((keys) => keys !== resolvedFamily)
|
|
470
|
-
.flatMap((keys) => getDefinedConfigKeys(config, keys))
|
|
471
|
-
|
|
472
|
-
console.warn(
|
|
473
|
-
`[Warning] Resolved animation spring config${formatWarningReference(
|
|
474
|
-
warningMetadata,
|
|
475
|
-
)}: using ${resolvedFamilyKeys.join('/')}, dropping ${droppedFamilyKeys.join('/')}`,
|
|
476
|
-
)
|
|
477
|
-
|
|
478
|
-
return {
|
|
479
|
-
...configWithoutSpringFamily,
|
|
480
|
-
...pickDefinedConfigValues(config, resolvedFamily),
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const compileAnimationConfig = (
|
|
485
|
-
animationType: CompiledAnimationType,
|
|
486
|
-
config: unknown,
|
|
487
|
-
errorReference: string,
|
|
488
|
-
warningMetadata?: WarningMetadata,
|
|
489
|
-
) => {
|
|
490
|
-
if (!isRecord(config)) {
|
|
491
|
-
throw new Error(`Invalid animation config ${errorReference}: config must be an object`)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const compiledConfig = compileProperty(omit(config, '__type'), errorReference)
|
|
495
|
-
|
|
496
|
-
if (!isRecord(compiledConfig)) {
|
|
497
|
-
throw new Error(`Invalid animation config ${errorReference}: config must compile to an object`)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
if (animationType === 'timing') {
|
|
501
|
-
assertConfigValue(compiledConfig, 'toValue', errorReference)
|
|
502
|
-
} else if (animationType === 'spring') {
|
|
503
|
-
return normalizeSpringConfig(compiledConfig, errorReference, warningMetadata)
|
|
504
|
-
} else if (animationType === 'decay') {
|
|
505
|
-
assertConfigValue(compiledConfig, 'velocity', errorReference)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
return compiledConfig
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
const compileFrame = (frame: Canvas['items'][number]['frame']) => ({
|
|
512
|
-
x: frame.x,
|
|
513
|
-
y: frame.y,
|
|
514
|
-
width: frame.width,
|
|
515
|
-
height: frame.height,
|
|
516
|
-
standby_mode: frame.standbyMode,
|
|
517
|
-
standby_frame: frame.standbyFrame,
|
|
518
|
-
standby_opacity: frame.standbyOpacity,
|
|
519
|
-
standby_delay: frame.standbyDelay,
|
|
520
|
-
standby_delay_random: frame.standbyDelayRandom,
|
|
521
|
-
standby_easing: frame.standbyEasing,
|
|
522
|
-
showing_delay: frame.showingDelay,
|
|
523
|
-
render_out_of_viewport: frame.renderOutOfViewport,
|
|
524
|
-
})
|
|
525
|
-
|
|
526
|
-
const preloadTypes = [
|
|
527
|
-
'media-resource-image',
|
|
528
|
-
'media-resource-video',
|
|
529
|
-
'media-resource-audio',
|
|
530
|
-
'media-resource-file',
|
|
531
|
-
'lottie-file-uri',
|
|
532
|
-
'rive-file-uri',
|
|
533
|
-
'ggml-model-asset',
|
|
534
|
-
'gguf-model-asset',
|
|
535
|
-
'binary-asset',
|
|
536
|
-
'mlx-model-asset',
|
|
537
|
-
'scene3d-objects',
|
|
538
|
-
]
|
|
539
|
-
|
|
540
|
-
const compileKind = (kind: Data['kind']) => {
|
|
541
|
-
const { type, ...rest } = kind || {}
|
|
542
|
-
if (!type) return {}
|
|
543
|
-
if (preloadTypes.includes(type)) {
|
|
544
|
-
const { preload, metadata } = kind as DataAssetKind
|
|
545
|
-
const result: any = { kind: type, ...rest }
|
|
546
|
-
if (preload) {
|
|
547
|
-
result.preload = {
|
|
548
|
-
type: preload.type,
|
|
549
|
-
hashType: preload.hashType,
|
|
550
|
-
}
|
|
551
|
-
if (preload.hashType) result.preload[preload.hashType] = preload.hash
|
|
552
|
-
}
|
|
553
|
-
// Handle metadata
|
|
554
|
-
if (type === 'gguf-model-asset') {
|
|
555
|
-
result._hfRepoInfo = metadata?.hfRepoInfo
|
|
556
|
-
}
|
|
557
|
-
return result
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return { kind: type, ...rest }
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const compileRemoteUpdate = (remoteUpdate: Data['remoteUpdate']) => {
|
|
564
|
-
if (!remoteUpdate) return { bank_type: 'none' }
|
|
565
|
-
if (remoteUpdate.type === 'auto') return { bank_type: 'create', enable_remote_update: true }
|
|
566
|
-
return {
|
|
567
|
-
bank_type: remoteUpdate.type === 'device-specific' ? 'create-device-specific' : 'global',
|
|
568
|
-
enable_remote_update: true,
|
|
569
|
-
...(remoteUpdate.type === 'device-specific' ? { use_remote_id_prefix: true } : {}),
|
|
570
|
-
...(remoteUpdate.type === 'global-data' ? { global_remote_update_prop: remoteUpdate.id } : {}),
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const compileModule = (subspace: Subspace) => {
|
|
575
|
-
if (!subspace.module?.id) return {}
|
|
576
|
-
return {
|
|
577
|
-
module: {
|
|
578
|
-
link: subspace.module.link ?? false,
|
|
579
|
-
id: subspace.module.id,
|
|
580
|
-
version: subspace.module.version,
|
|
581
|
-
},
|
|
582
|
-
selected_requirements: subspace.module.selectedRequirements?.reduce((acc, requirement) => {
|
|
583
|
-
acc[requirement.subspace] = {
|
|
584
|
-
id: requirement.id,
|
|
585
|
-
version: requirement.version,
|
|
586
|
-
}
|
|
587
|
-
return acc
|
|
588
|
-
}, {}),
|
|
589
|
-
requirements: subspace.module.requirements?.reduce((acc, requirement) => {
|
|
590
|
-
acc[requirement.subspace] = {
|
|
591
|
-
id: requirement.id,
|
|
592
|
-
version: requirement.version,
|
|
593
|
-
}
|
|
594
|
-
return acc
|
|
595
|
-
}, {}),
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Compile a run array element - if it's a getter function, call it and extract the id
|
|
601
|
-
* Note: All entity ids already include their prefix (e.g., SUBSPACE_xxx, BRICK_xxx, PROPERTY_BANK_DATA_NODE_xxx)
|
|
602
|
-
*/
|
|
603
|
-
function compileRunElement(element: unknown): unknown {
|
|
604
|
-
if (typeof element !== 'function') return element
|
|
605
|
-
|
|
606
|
-
const result = element()
|
|
607
|
-
if (result && typeof result === 'object' && 'id' in result) {
|
|
608
|
-
return (result as { id: string }).id
|
|
609
|
-
}
|
|
610
|
-
return result
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* Compile a run array - resolve getter functions and extract ids with prefixes
|
|
615
|
-
*/
|
|
616
|
-
function compileRunArray(run: unknown[]): unknown[] {
|
|
617
|
-
return run.map(compileRunElement)
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Compile typed TestCase to raw format (strips __typename)
|
|
622
|
-
*/
|
|
623
|
-
export const compileTestCase = (testCase: TestCase) => ({
|
|
624
|
-
id: testCase.id,
|
|
625
|
-
name: testCase.name,
|
|
626
|
-
hide_short_ref: testCase.hideShortRef,
|
|
627
|
-
run: compileRunArray(testCase.run),
|
|
628
|
-
exit_on_failed: testCase.exit_on_failed,
|
|
629
|
-
commented: testCase.commented,
|
|
630
|
-
pre_delay: testCase.pre_delay,
|
|
631
|
-
post_delay: testCase.post_delay,
|
|
632
|
-
post_delay_rule: testCase.post_delay_rule,
|
|
633
|
-
jump_cond: testCase.jump_cond.map((cond) => {
|
|
634
|
-
if (cond.jump_to == null) {
|
|
635
|
-
console.warn(
|
|
636
|
-
`[Warning] jump_cond is missing jump_to in test case "${testCase.name}" (${testCase.id})`,
|
|
637
|
-
)
|
|
638
|
-
}
|
|
639
|
-
return {
|
|
640
|
-
type: cond.type,
|
|
641
|
-
status: cond.status,
|
|
642
|
-
variable: cond.variable,
|
|
643
|
-
operator: cond.operator,
|
|
644
|
-
value: cond.value,
|
|
645
|
-
// `jump_to` may be a getter (() => TestCase) for dynamic case ids (the project generator
|
|
646
|
-
// emits this form). Resolve it to its id like the `run` array does — otherwise the function
|
|
647
|
-
// is JSON-serialized to nothing and the conditional jump silently vanishes from the config.
|
|
648
|
-
jump_to: compileRunElement(cond.jump_to),
|
|
649
|
-
}
|
|
650
|
-
}),
|
|
651
|
-
})
|
|
652
|
-
|
|
653
|
-
/**
|
|
654
|
-
* Compile typed TestVariable to raw format (strips __typename)
|
|
655
|
-
*/
|
|
656
|
-
const compileTestVariable = (variable: TestVariable) => ({
|
|
657
|
-
id: variable.id,
|
|
658
|
-
name: variable.name,
|
|
659
|
-
type: variable.type,
|
|
660
|
-
value: variable.value,
|
|
661
|
-
})
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Convert an array of items with id property to an id-keyed record
|
|
665
|
-
*/
|
|
666
|
-
const arrayToIdMap = <T extends { id: string }, R>(
|
|
667
|
-
items: T[],
|
|
668
|
-
transform: (item: T, index: number) => R,
|
|
669
|
-
options: {
|
|
670
|
-
patternKey: EntryIdPatternKey
|
|
671
|
-
getErrorReference: (item: T, index: number) => string
|
|
672
|
-
},
|
|
673
|
-
): Record<string, R> =>
|
|
674
|
-
Object.fromEntries(
|
|
675
|
-
items.map((item, index) => {
|
|
676
|
-
const itemId = assertEntryId(
|
|
677
|
-
item.id,
|
|
678
|
-
options.patternKey,
|
|
679
|
-
options.getErrorReference(item, index),
|
|
680
|
-
)
|
|
681
|
-
return [itemId, transform(item, index)]
|
|
682
|
-
}),
|
|
683
|
-
)
|
|
684
|
-
|
|
685
|
-
/**
|
|
686
|
-
* Compile typed AutomationTest to raw format
|
|
687
|
-
*/
|
|
688
|
-
const compileAutomationTest = (
|
|
689
|
-
test: AutomationTest,
|
|
690
|
-
options: { mapId: string; testIndex: number },
|
|
691
|
-
) => {
|
|
692
|
-
const testId = assertEntryId(
|
|
693
|
-
test.id,
|
|
694
|
-
'TEST',
|
|
695
|
-
`(automation map: ${options.mapId}, test index: ${options.testIndex})`,
|
|
696
|
-
)
|
|
697
|
-
|
|
698
|
-
return {
|
|
699
|
-
id: testId,
|
|
700
|
-
alias: test.alias,
|
|
701
|
-
title: test.title,
|
|
702
|
-
hide_short_ref: test.hideShortRef,
|
|
703
|
-
timeout: test.timeout,
|
|
704
|
-
trigger_type: test.trigger_type,
|
|
705
|
-
cron: test.cron,
|
|
706
|
-
skip_trigger_type_check: test.skip_trigger_type_check,
|
|
707
|
-
local_sync: test.local_sync,
|
|
708
|
-
meta: test.meta,
|
|
709
|
-
case_map: arrayToIdMap(test.cases, compileTestCase, {
|
|
710
|
-
patternKey: 'TEST_CASE',
|
|
711
|
-
getErrorReference: (_, index) =>
|
|
712
|
-
`(automation map: ${options.mapId}, test: ${testId}, case index: ${index})`,
|
|
713
|
-
}),
|
|
714
|
-
var_map: arrayToIdMap(test.variables, compileTestVariable, {
|
|
715
|
-
patternKey: 'TEST_VAR',
|
|
716
|
-
getErrorReference: (_, index) =>
|
|
717
|
-
`(automation map: ${options.mapId}, test: ${testId}, variable index: ${index})`,
|
|
718
|
-
}),
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
/**
|
|
723
|
-
* Compile typed AutomationTestMap to raw format
|
|
724
|
-
*/
|
|
725
|
-
const compileAutomationTestMap = (testMap: AutomationTestMap, mapId: string) => {
|
|
726
|
-
assertEntryId(testMap.id, 'AUTOMATION_MAP', `(automation map id field: ${mapId})`)
|
|
727
|
-
|
|
728
|
-
return {
|
|
729
|
-
title: testMap.title,
|
|
730
|
-
hide_short_ref: testMap.hideShortRef,
|
|
731
|
-
createdAt: testMap.createdAt,
|
|
732
|
-
map: arrayToIdMap(
|
|
733
|
-
testMap.tests,
|
|
734
|
-
(test, index) => compileAutomationTest(test, { mapId, testIndex: index }),
|
|
735
|
-
{
|
|
736
|
-
patternKey: 'TEST',
|
|
737
|
-
getErrorReference: (_, index) => `(automation map: ${mapId}, test index: ${index})`,
|
|
738
|
-
},
|
|
739
|
-
),
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
/**
|
|
744
|
-
* Compile typed AutomationMap to raw automation_map format
|
|
745
|
-
*/
|
|
746
|
-
const compileAutomation = (automationMap: AutomationMap) =>
|
|
747
|
-
Object.fromEntries(
|
|
748
|
-
Object.entries(automationMap).map(([mapId, testMap]) => {
|
|
749
|
-
const automationMapId = assertEntryId(mapId, 'AUTOMATION_MAP', '(automation map key)')
|
|
750
|
-
return [automationMapId, compileAutomationTestMap(testMap, automationMapId)]
|
|
751
|
-
}),
|
|
752
|
-
)
|
|
753
|
-
|
|
754
|
-
const buildDefaultExpandedState = (subspace: Subspace) => ({
|
|
755
|
-
brick: false,
|
|
756
|
-
generator: true,
|
|
757
|
-
canvas: (subspace.canvases || []).reduce((acc, canvas) => {
|
|
758
|
-
if (canvas?.id) acc[canvas.id] = false
|
|
759
|
-
return acc
|
|
760
|
-
}, {}),
|
|
761
|
-
property_bank: false,
|
|
762
|
-
property_bank_calc: true,
|
|
763
|
-
})
|
|
764
|
-
|
|
765
|
-
// Record the minimal compiled-config delta this compile produced to the shared audit
|
|
766
|
-
// log (`.bricks/edits.jsonl`), so editing files directly and running `bun compile`
|
|
767
|
-
// leaves the same trail as the MCP source-editing tools. Maintained only in the
|
|
768
|
-
// editing-tools context (`BRICKS_CTOR_ENABLE_EDITING_TOOLS`); the source-editing tools
|
|
769
|
-
// turn it off for their verify compiles (see _verify.ts) so a tool edit records one
|
|
770
|
-
// richer entry instead of an extra generic compile entry. Also silent when there is no
|
|
771
|
-
// prior build to diff against (fresh projects, package tests, tooling outside a project).
|
|
772
|
-
const recordConfigChange = async (previousConfig: unknown, config: unknown) => {
|
|
773
|
-
if (previousConfig == null) return
|
|
774
|
-
if (!isTruthyEnv(process.env.BRICKS_CTOR_ENABLE_EDITING_TOOLS)) return
|
|
775
|
-
// The baseline was parsed from JSON; `computeConfigChange` applies the same
|
|
776
|
-
// JSON-omitted-field rules lazily so compile avoids cloning the full config.
|
|
777
|
-
const change = computeConfigChange(previousConfig, config)
|
|
778
|
-
if (change.status !== 'ok') return
|
|
779
|
-
await appendEditRecord(process.cwd(), {
|
|
780
|
-
ts: new Date().toISOString(),
|
|
781
|
-
tool: 'compile',
|
|
782
|
-
provenance: editProvenance(),
|
|
783
|
-
outcome: 'ok',
|
|
784
|
-
summary:
|
|
785
|
-
change.opCount === 0
|
|
786
|
-
? 'compile: no config change'
|
|
787
|
-
: `compile: ${change.opCount} config op(s)`,
|
|
788
|
-
configChange: change,
|
|
789
|
-
}).catch(() => undefined)
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
export const compile = async (app: Application) => {
|
|
793
|
-
await new Promise((resolve) => setImmediate(resolve, 0))
|
|
794
|
-
// Collected entity-level compile errors (see collect/collectReduce). Aggregated and
|
|
795
|
-
// thrown at the end so one compile reports every entity's first error.
|
|
796
|
-
const errors: string[] = []
|
|
797
|
-
// Snapshot the prior build artifact before the caller's compile.ts overwrites it, so
|
|
798
|
-
// the config change introduced by this compile can be recorded on return.
|
|
799
|
-
const previousConfig = await readBuildConfig(process.cwd())
|
|
800
|
-
const timestamp = Date.now()
|
|
801
|
-
// Pre-index subspace ids so the canvas-item validation below stays O(1).
|
|
802
|
-
const subspaceIdSet = new Set(app.subspaces.map((s) => s.id))
|
|
803
|
-
let compiledAutomationMap: ReturnType<typeof compileAutomation> | null = null
|
|
804
|
-
if (app.automationMap) {
|
|
805
|
-
const { automationMap } = app
|
|
806
|
-
compiledAutomationMap = collect(errors, () => compileAutomation(automationMap), null)
|
|
807
|
-
}
|
|
808
|
-
const config = {
|
|
809
|
-
title: `${app.name || 'Unknown'}(${timestamp})`,
|
|
810
|
-
subspace_map: app.subspaces.reduce((subspaceMap, subspace, subspaceIndex) => {
|
|
811
|
-
const subspaceId = assertEntryId(
|
|
812
|
-
subspace.id,
|
|
813
|
-
'SUBSPACE',
|
|
814
|
-
`(subspace index: ${subspaceIndex})`,
|
|
815
|
-
)
|
|
816
|
-
|
|
817
|
-
// Linked module subspaces reference external content — only include
|
|
818
|
-
// module metadata, not local canvas/brick/data compilation.
|
|
819
|
-
// Include a placeholder root canvas so the config passes schema
|
|
820
|
-
// validation (root_canvas_id is required before the conditional
|
|
821
|
-
// schema fix is published).
|
|
822
|
-
if (subspace.module?.link) {
|
|
823
|
-
// Seed the placeholder id from the (stable) subspace id. `makeId('canvas')` would take
|
|
824
|
-
// the count-fallback branch (a process-global counter that is never reset), so the
|
|
825
|
-
// placeholder id depended on how many prior count-fallback ids had been minted — making
|
|
826
|
-
// it differ between recompiles and breaking compile's byte-stable-output contract
|
|
827
|
-
// (phantom config-change ops). `makeSeededId` keeps no global state, so identical source
|
|
828
|
-
// recompiles to an identical id. (`makeId('canvas', alias)` would instead throw
|
|
829
|
-
// "Duplicate makeId alias" on the second compile in a long-lived process.)
|
|
830
|
-
const placeholderCanvasId = makeSeededId('canvas', `${subspaceId}:module-placeholder`)
|
|
831
|
-
subspaceMap[subspaceId] = {
|
|
832
|
-
title: subspace.title,
|
|
833
|
-
description: subspace.description,
|
|
834
|
-
unused: subspace.unused,
|
|
835
|
-
portal: subspace.portal,
|
|
836
|
-
layout: {
|
|
837
|
-
width: subspace.layout?.width,
|
|
838
|
-
height: subspace.layout?.height,
|
|
839
|
-
resize_mode: subspace.layout?.resizeMode,
|
|
840
|
-
},
|
|
841
|
-
root_canvas_id: placeholderCanvasId,
|
|
842
|
-
property_bank_map: {},
|
|
843
|
-
brick_map: {},
|
|
844
|
-
generator_map: {},
|
|
845
|
-
animation_map: {},
|
|
846
|
-
canvas_map: {
|
|
847
|
-
[placeholderCanvasId]: { item_list: [] },
|
|
848
|
-
},
|
|
849
|
-
...compileModule(subspace),
|
|
850
|
-
}
|
|
851
|
-
return subspaceMap
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
const rootCanvasId = assertEntryId(
|
|
855
|
-
subspace.rootCanvas?.id,
|
|
856
|
-
'CANVAS',
|
|
857
|
-
`(subspace: ${subspaceId}, root canvas)`,
|
|
858
|
-
)
|
|
859
|
-
|
|
860
|
-
subspaceMap[subspaceId] = {
|
|
861
|
-
title: subspace.title,
|
|
862
|
-
description: subspace.description,
|
|
863
|
-
hide_short_ref: subspace.hideShortRef,
|
|
864
|
-
unused: subspace.unused,
|
|
865
|
-
portal: subspace.portal,
|
|
866
|
-
_expanded: subspace.unexpanded
|
|
867
|
-
? {
|
|
868
|
-
brick: !subspace.unexpanded.brick,
|
|
869
|
-
generator: !subspace.unexpanded.generator,
|
|
870
|
-
canvas: subspace.unexpanded.canvas?.reduce((acc, canvas, canvasIndex) => {
|
|
871
|
-
const unexpandedCanvasId = assertEntryId(
|
|
872
|
-
canvas?.id,
|
|
873
|
-
'CANVAS',
|
|
874
|
-
`(subspace: ${subspaceId}, unexpanded canvas index: ${canvasIndex})`,
|
|
875
|
-
)
|
|
876
|
-
acc[unexpandedCanvasId] = !canvas?.id
|
|
877
|
-
return acc
|
|
878
|
-
}, {}),
|
|
879
|
-
property_bank: !subspace.unexpanded.data,
|
|
880
|
-
property_bank_calc: !subspace.unexpanded.dataCalculation,
|
|
881
|
-
}
|
|
882
|
-
: buildDefaultExpandedState(subspace),
|
|
883
|
-
layout: {
|
|
884
|
-
width: subspace.layout?.width,
|
|
885
|
-
height: subspace.layout?.height,
|
|
886
|
-
resize_mode: subspace.layout?.resizeMode,
|
|
887
|
-
},
|
|
888
|
-
local_sync: subspace.localSyncChangeCanvas
|
|
889
|
-
? {
|
|
890
|
-
change_canvas: subspace.localSyncChangeCanvas,
|
|
891
|
-
}
|
|
892
|
-
: undefined,
|
|
893
|
-
animation_map: collectReduce(
|
|
894
|
-
errors,
|
|
895
|
-
subspace.animations,
|
|
896
|
-
(map, animation, animationIndex) => {
|
|
897
|
-
const animationId = assertEntryId(
|
|
898
|
-
animation?.id,
|
|
899
|
-
'ANIMATION',
|
|
900
|
-
`(animation index: ${animationIndex}, subspace: ${subspaceId})`,
|
|
901
|
-
)
|
|
902
|
-
|
|
903
|
-
const animationTypename = animation.__typename
|
|
904
|
-
if (animationTypename === 'Animation') {
|
|
905
|
-
const animationDef = animation as AnimationDef
|
|
906
|
-
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
907
|
-
const animationWarningMetadata = {
|
|
908
|
-
animationIndex,
|
|
909
|
-
animationTitle: animationDef.title,
|
|
910
|
-
animationAlias: animationDef.alias,
|
|
911
|
-
animationProperty: animationDef.property,
|
|
912
|
-
subspaceIndex,
|
|
913
|
-
subspaceTitle: subspace.title,
|
|
914
|
-
}
|
|
915
|
-
const animationType = getAnimationType(animationDef.config, animationErrorReference)
|
|
916
|
-
map[animationId] = {
|
|
917
|
-
alias: animationDef.alias,
|
|
918
|
-
title: animationDef.title,
|
|
919
|
-
description: animationDef.description,
|
|
920
|
-
hide_short_ref: animationDef.hideShortRef,
|
|
921
|
-
animationRunType: animationDef.runType,
|
|
922
|
-
property: assertAnimationProperty(animationDef.property, animationErrorReference),
|
|
923
|
-
type: animationType,
|
|
924
|
-
config: compileAnimationConfig(
|
|
925
|
-
animationType,
|
|
926
|
-
animationDef.config,
|
|
927
|
-
animationErrorReference,
|
|
928
|
-
animationWarningMetadata,
|
|
929
|
-
),
|
|
930
|
-
}
|
|
931
|
-
} else if (animationTypename === 'AnimationCompose') {
|
|
932
|
-
const animationDef = animation as AnimationComposeDef
|
|
933
|
-
const animationErrorReference = `(animation: ${animationId}, subspace ${subspaceId})`
|
|
934
|
-
map[animationId] = {
|
|
935
|
-
alias: animationDef.alias,
|
|
936
|
-
title: animationDef.title,
|
|
937
|
-
description: animationDef.description,
|
|
938
|
-
hide_short_ref: animationDef.hideShortRef,
|
|
939
|
-
animationRunType: animationDef.runType,
|
|
940
|
-
compose_type: assertAnimationComposeType(
|
|
941
|
-
animationDef.composeType,
|
|
942
|
-
animationErrorReference,
|
|
943
|
-
),
|
|
944
|
-
item_list: animationDef.items.map((item, index) => {
|
|
945
|
-
const innerAnimation = item()
|
|
946
|
-
const innerAnimationId = assertEntryId(
|
|
947
|
-
innerAnimation?.id,
|
|
948
|
-
'ANIMATION',
|
|
949
|
-
`(animation item index: ${index}, animation: ${animationId}, subspace ${subspaceId})`,
|
|
950
|
-
)
|
|
951
|
-
return { animation_id: innerAnimationId }
|
|
952
|
-
}),
|
|
953
|
-
}
|
|
954
|
-
} else {
|
|
955
|
-
throw new Error(
|
|
956
|
-
`Invalid animation typename (animation: ${animationId}, subspace ${subspaceId}): ${String(
|
|
957
|
-
animationTypename,
|
|
958
|
-
)}`,
|
|
959
|
-
)
|
|
960
|
-
}
|
|
961
|
-
return map
|
|
962
|
-
},
|
|
963
|
-
),
|
|
964
|
-
brick_map: collectReduce(errors, subspace.bricks, (map, brick, brickIndex) => {
|
|
965
|
-
const brickId = assertEntryId(
|
|
966
|
-
brick.id,
|
|
967
|
-
'BRICK',
|
|
968
|
-
`(brick index: ${brickIndex}, subspace: ${subspaceId})`,
|
|
969
|
-
)
|
|
970
|
-
const property = compileProperty(
|
|
971
|
-
brick.property || {},
|
|
972
|
-
`(brick: ${brickId}, subspace ${subspaceId})`,
|
|
973
|
-
)
|
|
974
|
-
if (brick.templateKey === 'BRICK_ITEMS') {
|
|
975
|
-
const brickItems = brick as BrickItems
|
|
976
|
-
const buildList = (itemBrick, index, key) => ({
|
|
977
|
-
title: itemBrick.title,
|
|
978
|
-
brickId: itemBrick.brickId,
|
|
979
|
-
brickIdPrefix: itemBrick.brickIdPrefix,
|
|
980
|
-
templateKey: itemBrick.templateKey,
|
|
981
|
-
frame: itemBrick.frame,
|
|
982
|
-
property: compileProperty(
|
|
983
|
-
itemBrick.property,
|
|
984
|
-
`(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
|
|
985
|
-
),
|
|
986
|
-
propertyMapping: itemBrick.propertyMapping,
|
|
987
|
-
animation: compileAnimations(
|
|
988
|
-
itemBrick.templateKey,
|
|
989
|
-
itemBrick.animation || {},
|
|
990
|
-
`(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
|
|
991
|
-
),
|
|
992
|
-
outlet: compileOutlets(
|
|
993
|
-
itemBrick.templateKey,
|
|
994
|
-
itemBrick.outlets || {},
|
|
995
|
-
`(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
|
|
996
|
-
),
|
|
997
|
-
eventMap: compileEvents(itemBrick.templateKey, itemBrick.eventMap || {}, {
|
|
998
|
-
camelCase: true,
|
|
999
|
-
errorReference: `(brick: ${brickId}, ${key}: ${index}, subspace ${subspaceId})`,
|
|
1000
|
-
}),
|
|
1001
|
-
stateGroup: itemBrick.stateGroup.reduce((acc, stateGroup, stateGroupIndex) => {
|
|
1002
|
-
const stateGroupId = assertEntryId(
|
|
1003
|
-
stateGroup.id,
|
|
1004
|
-
'BRICK_STATE_GROUP',
|
|
1005
|
-
`(brick: ${brickId}, ${key}: ${index}, switch index: ${stateGroupIndex}, subspace ${subspaceId})`,
|
|
1006
|
-
)
|
|
1007
|
-
|
|
1008
|
-
acc[stateGroupId] = {
|
|
1009
|
-
title: stateGroup.title,
|
|
1010
|
-
description: stateGroup.description,
|
|
1011
|
-
override: stateGroup.override,
|
|
1012
|
-
break: stateGroup.break,
|
|
1013
|
-
commented: stateGroup.disabled,
|
|
1014
|
-
conds: compileSwitchConds(
|
|
1015
|
-
itemBrick.templateKey,
|
|
1016
|
-
stateGroup.conds || [],
|
|
1017
|
-
`(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
|
|
1018
|
-
),
|
|
1019
|
-
property: compileProperty(
|
|
1020
|
-
stateGroup.property,
|
|
1021
|
-
`(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
|
|
1022
|
-
),
|
|
1023
|
-
animation: compileAnimations(
|
|
1024
|
-
itemBrick.templateKey,
|
|
1025
|
-
stateGroup.animation || {},
|
|
1026
|
-
`(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
|
|
1027
|
-
),
|
|
1028
|
-
outlet: compileOutlets(
|
|
1029
|
-
itemBrick.templateKey,
|
|
1030
|
-
stateGroup.outlets || {},
|
|
1031
|
-
`(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
|
|
1032
|
-
),
|
|
1033
|
-
eventMap: compileEvents(itemBrick.templateKey, stateGroup.eventMap || {}, {
|
|
1034
|
-
camelCase: true,
|
|
1035
|
-
errorReference: `(brick: ${brickId}, ${key}: ${index}, switch: ${stateGroupId}, subspace ${subspaceId})`,
|
|
1036
|
-
}),
|
|
1037
|
-
}
|
|
1038
|
-
return acc
|
|
1039
|
-
}, {}),
|
|
1040
|
-
show: itemBrick.show,
|
|
1041
|
-
pressToOpenDetail: itemBrick.pressToOpenDetail,
|
|
1042
|
-
pressToBackList: itemBrick.pressToBackList,
|
|
1043
|
-
})
|
|
1044
|
-
if (Array.isArray(brickItems.brickList)) {
|
|
1045
|
-
const brickList = (brickItems.brickList || []).map((item, index) =>
|
|
1046
|
-
buildList(item, index, 'brickList'),
|
|
1047
|
-
)
|
|
1048
|
-
property.brickList = brickList
|
|
1049
|
-
} else if (!brickItems.brickList) {
|
|
1050
|
-
property.brickList = []
|
|
1051
|
-
} else {
|
|
1052
|
-
// Not supported Data for brickList
|
|
1053
|
-
throw new TypeError('Not supported Data for brickList directly')
|
|
1054
|
-
}
|
|
1055
|
-
if (Array.isArray(brickItems.brickDetails)) {
|
|
1056
|
-
const brickDetails = (brickItems.brickDetails || []).map((item, index) =>
|
|
1057
|
-
buildList(item, index, 'brickDetails'),
|
|
1058
|
-
)
|
|
1059
|
-
property.brickDetails = brickDetails
|
|
1060
|
-
} else if (!brickItems.brickDetails) {
|
|
1061
|
-
property.brickDetails = []
|
|
1062
|
-
} else {
|
|
1063
|
-
// Not supported Data for brickList
|
|
1064
|
-
throw new TypeError('Not supported Data for brickList directly')
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
map[brickId] = {
|
|
1068
|
-
template_key: brick.templateKey,
|
|
1069
|
-
alias: brick.alias,
|
|
1070
|
-
title: brick.title,
|
|
1071
|
-
description: brick.description,
|
|
1072
|
-
hide_short_ref: brick.hideShortRef,
|
|
1073
|
-
property,
|
|
1074
|
-
animation: compileAnimations(
|
|
1075
|
-
brick.templateKey,
|
|
1076
|
-
brick.animation || {},
|
|
1077
|
-
`(brick: ${brickId}, subspace ${subspaceId})`,
|
|
1078
|
-
),
|
|
1079
|
-
event_map: compileEvents(brick.templateKey, brick.events || {}, {
|
|
1080
|
-
camelCase: false,
|
|
1081
|
-
errorReference: `(brick: ${brickId}, subspace ${subspaceId})`,
|
|
1082
|
-
}),
|
|
1083
|
-
outlet: compileOutlets(
|
|
1084
|
-
brick.templateKey,
|
|
1085
|
-
brick.outlets || {},
|
|
1086
|
-
`(brick: ${brickId}, subspace ${subspaceId})`,
|
|
1087
|
-
),
|
|
1088
|
-
state_group: brick.switches?.reduce((acc, switchCase, switchIndex) => {
|
|
1089
|
-
const switchId = assertEntryId(
|
|
1090
|
-
switchCase.id,
|
|
1091
|
-
'BRICK_STATE_GROUP',
|
|
1092
|
-
`(brick: ${brickId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
|
|
1093
|
-
)
|
|
1094
|
-
|
|
1095
|
-
acc[switchId] = {
|
|
1096
|
-
title: switchCase.title,
|
|
1097
|
-
description: switchCase.description,
|
|
1098
|
-
break: switchCase.break,
|
|
1099
|
-
override: switchCase.override,
|
|
1100
|
-
commented: switchCase.disabled,
|
|
1101
|
-
conds: compileSwitchConds(
|
|
1102
|
-
brick.templateKey,
|
|
1103
|
-
switchCase.conds || [],
|
|
1104
|
-
`(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1105
|
-
),
|
|
1106
|
-
property: compileProperty(
|
|
1107
|
-
switchCase.property,
|
|
1108
|
-
`(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1109
|
-
),
|
|
1110
|
-
outlet: compileOutlets(
|
|
1111
|
-
brick.templateKey,
|
|
1112
|
-
switchCase.outlets || {},
|
|
1113
|
-
`(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1114
|
-
),
|
|
1115
|
-
event_map: compileEvents(brick.templateKey, switchCase.events || {}, {
|
|
1116
|
-
camelCase: false,
|
|
1117
|
-
errorReference: `(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1118
|
-
}),
|
|
1119
|
-
animation: compileAnimations(
|
|
1120
|
-
brick.templateKey,
|
|
1121
|
-
switchCase.animation || {},
|
|
1122
|
-
`(brick: ${brickId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1123
|
-
),
|
|
1124
|
-
}
|
|
1125
|
-
return acc
|
|
1126
|
-
}, {}),
|
|
1127
|
-
}
|
|
1128
|
-
return map
|
|
1129
|
-
}),
|
|
1130
|
-
root_canvas_id: rootCanvasId,
|
|
1131
|
-
canvas_map: collectReduce(errors, subspace.canvases, (map, canvas, canvasIndex) => {
|
|
1132
|
-
const canvasId = assertEntryId(
|
|
1133
|
-
canvas.id,
|
|
1134
|
-
'CANVAS',
|
|
1135
|
-
`(canvas index: ${canvasIndex}, subspace: ${subspaceId})`,
|
|
1136
|
-
)
|
|
1137
|
-
|
|
1138
|
-
map[canvasId] = {
|
|
1139
|
-
alias: canvas.alias,
|
|
1140
|
-
title: canvas.title,
|
|
1141
|
-
description: canvas.description,
|
|
1142
|
-
hide_short_ref: canvas.hideShortRef,
|
|
1143
|
-
property: compileProperty(
|
|
1144
|
-
canvas.property,
|
|
1145
|
-
`(canvas: ${canvasId}, subspace ${subspaceId})`,
|
|
1146
|
-
),
|
|
1147
|
-
event_map: compileEvents('CANVAS', canvas.events || {}, {
|
|
1148
|
-
camelCase: false,
|
|
1149
|
-
errorReference: `(canvas: ${canvasId}, subspace ${subspaceId})`,
|
|
1150
|
-
}),
|
|
1151
|
-
state_group: canvas.switches?.reduce((acc, switchCase, switchIndex) => {
|
|
1152
|
-
const switchId = assertEntryId(
|
|
1153
|
-
switchCase.id,
|
|
1154
|
-
'BRICK_STATE_GROUP',
|
|
1155
|
-
`(canvas: ${canvasId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
|
|
1156
|
-
)
|
|
1157
|
-
|
|
1158
|
-
acc[switchId] = {
|
|
1159
|
-
title: switchCase.title,
|
|
1160
|
-
description: switchCase.description,
|
|
1161
|
-
break: switchCase.break,
|
|
1162
|
-
override: switchCase.override,
|
|
1163
|
-
commented: switchCase.disabled,
|
|
1164
|
-
conds: compileSwitchConds(
|
|
1165
|
-
'CANVAS',
|
|
1166
|
-
switchCase.conds || [],
|
|
1167
|
-
`(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1168
|
-
),
|
|
1169
|
-
property: compileProperty(
|
|
1170
|
-
switchCase.property,
|
|
1171
|
-
`(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1172
|
-
),
|
|
1173
|
-
event_map: compileEvents('CANVAS', switchCase.events || {}, {
|
|
1174
|
-
camelCase: false,
|
|
1175
|
-
errorReference: `(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1176
|
-
}),
|
|
1177
|
-
animation: compileAnimations(
|
|
1178
|
-
'CANVAS',
|
|
1179
|
-
switchCase.animation || {},
|
|
1180
|
-
`(canvas: ${canvasId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1181
|
-
),
|
|
1182
|
-
}
|
|
1183
|
-
return acc
|
|
1184
|
-
}, {}),
|
|
1185
|
-
item_list: canvas.items.map((item, index) => {
|
|
1186
|
-
let itemPayload = {}
|
|
1187
|
-
if (typeof item.item === 'function') {
|
|
1188
|
-
const brick = (item.item as () => Brick)()
|
|
1189
|
-
if (!brick?.id)
|
|
1190
|
-
throw new Error(
|
|
1191
|
-
`Invalid canvas item index: ${index}, brick not found (canvas: ${canvasId}, subspace ${subspaceId})`,
|
|
1192
|
-
)
|
|
1193
|
-
itemPayload = { brick_id: brick.id }
|
|
1194
|
-
} else {
|
|
1195
|
-
const targetSubspaceId = item.item
|
|
1196
|
-
if (!subspaceIdSet.has(targetSubspaceId))
|
|
1197
|
-
throw new Error(
|
|
1198
|
-
`Invalid canvas item index: ${index}, subspace not found (canvas: ${canvasId}, subspace ${subspaceId})`,
|
|
1199
|
-
)
|
|
1200
|
-
itemPayload = { subspace_id: targetSubspaceId }
|
|
1201
|
-
}
|
|
1202
|
-
return {
|
|
1203
|
-
type: typeof item.item === 'function' ? 'brick' : 'subspace',
|
|
1204
|
-
...itemPayload,
|
|
1205
|
-
frame: compileFrame(item.frame),
|
|
1206
|
-
hidden: item.hidden,
|
|
1207
|
-
}
|
|
1208
|
-
}),
|
|
1209
|
-
}
|
|
1210
|
-
return map
|
|
1211
|
-
}),
|
|
1212
|
-
generator_map: collectReduce(
|
|
1213
|
-
errors,
|
|
1214
|
-
subspace.generators,
|
|
1215
|
-
(map, generator, generatorIndex) => {
|
|
1216
|
-
const generatorId = assertEntryId(
|
|
1217
|
-
generator.id,
|
|
1218
|
-
'GENERATOR',
|
|
1219
|
-
`(generator index: ${generatorIndex}, subspace: ${subspaceId})`,
|
|
1220
|
-
)
|
|
1221
|
-
|
|
1222
|
-
map[generatorId] = {
|
|
1223
|
-
template_key: generator.templateKey,
|
|
1224
|
-
alias: generator.alias,
|
|
1225
|
-
title: generator.title,
|
|
1226
|
-
description: generator.description,
|
|
1227
|
-
hide_short_ref: generator.hideShortRef,
|
|
1228
|
-
local_sync: generator.localSyncRunMode
|
|
1229
|
-
? {
|
|
1230
|
-
run_mode: generator.localSyncRunMode,
|
|
1231
|
-
}
|
|
1232
|
-
: undefined,
|
|
1233
|
-
property: compileProperty(
|
|
1234
|
-
generator.property || {},
|
|
1235
|
-
`(generator: ${generatorId}, subspace ${subspaceId})`,
|
|
1236
|
-
),
|
|
1237
|
-
event_map: compileEvents(generator.templateKey, generator.events || {}, {
|
|
1238
|
-
camelCase: false,
|
|
1239
|
-
errorReference: `(generator: ${generatorId}, subspace ${subspaceId})`,
|
|
1240
|
-
}),
|
|
1241
|
-
outlet: compileOutlets(
|
|
1242
|
-
generator.templateKey,
|
|
1243
|
-
generator.outlets || {},
|
|
1244
|
-
`(generator: ${generatorId}, subspace ${subspaceId})`,
|
|
1245
|
-
),
|
|
1246
|
-
state_group: generator.switches?.reduce((acc, switchCase, switchIndex) => {
|
|
1247
|
-
const switchId = assertEntryId(
|
|
1248
|
-
switchCase.id,
|
|
1249
|
-
'BRICK_STATE_GROUP',
|
|
1250
|
-
`(generator: ${generatorId}, switch index: ${switchIndex}, subspace ${subspaceId})`,
|
|
1251
|
-
)
|
|
1252
|
-
|
|
1253
|
-
acc[switchId] = {
|
|
1254
|
-
title: switchCase.title,
|
|
1255
|
-
description: switchCase.description,
|
|
1256
|
-
break: switchCase.break,
|
|
1257
|
-
override: switchCase.override,
|
|
1258
|
-
commented: switchCase.disabled,
|
|
1259
|
-
conds: compileSwitchConds(
|
|
1260
|
-
generator.templateKey,
|
|
1261
|
-
switchCase.conds || [],
|
|
1262
|
-
`(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1263
|
-
),
|
|
1264
|
-
property: compileProperty(
|
|
1265
|
-
switchCase.property,
|
|
1266
|
-
`(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1267
|
-
),
|
|
1268
|
-
outlet: compileOutlets(
|
|
1269
|
-
generator.templateKey,
|
|
1270
|
-
switchCase.outlets || {},
|
|
1271
|
-
`(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1272
|
-
),
|
|
1273
|
-
event_map: compileEvents(generator.templateKey, switchCase.events || {}, {
|
|
1274
|
-
camelCase: false,
|
|
1275
|
-
errorReference: `(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1276
|
-
}),
|
|
1277
|
-
animation: compileAnimations(
|
|
1278
|
-
generator.templateKey,
|
|
1279
|
-
switchCase.animation || {},
|
|
1280
|
-
`(generator: ${generatorId}, switch: ${switchId}, subspace ${subspaceId})`,
|
|
1281
|
-
),
|
|
1282
|
-
}
|
|
1283
|
-
return acc
|
|
1284
|
-
}, {}),
|
|
1285
|
-
}
|
|
1286
|
-
return map
|
|
1287
|
-
},
|
|
1288
|
-
),
|
|
1289
|
-
property_bank_map: collectReduce(errors, subspace.data, (map, data, dataIndex) => {
|
|
1290
|
-
const dataId = assertEntryId(
|
|
1291
|
-
data.id,
|
|
1292
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1293
|
-
`(data index: ${dataIndex}, subspace: ${subspaceId})`,
|
|
1294
|
-
)
|
|
1295
|
-
|
|
1296
|
-
map[dataId] = {
|
|
1297
|
-
alias: data.alias,
|
|
1298
|
-
title: data.title,
|
|
1299
|
-
description: data.description,
|
|
1300
|
-
hide_short_ref: data.hideShortRef,
|
|
1301
|
-
linked: data.metadata?.linked,
|
|
1302
|
-
linkedFrom: data.metadata?.linkedFrom,
|
|
1303
|
-
local_sync: data.localSyncUpdateMode
|
|
1304
|
-
? {
|
|
1305
|
-
update_mode: data.localSyncUpdateMode,
|
|
1306
|
-
}
|
|
1307
|
-
: undefined,
|
|
1308
|
-
persist_data: data.persistData,
|
|
1309
|
-
...compileRemoteUpdate(data.remoteUpdate),
|
|
1310
|
-
routing: data.routing,
|
|
1311
|
-
schema: data.schema,
|
|
1312
|
-
type: data.type === 'boolean' ? 'bool' : data.type,
|
|
1313
|
-
...compileKind(data.kind),
|
|
1314
|
-
value: compileProperty(data.value, `(data: ${dataId}, subspace ${subspaceId})`),
|
|
1315
|
-
event_map: compileEvents('PROPERTY_BANK', data.events || {}, {
|
|
1316
|
-
camelCase: false,
|
|
1317
|
-
errorReference: `(data: ${dataId}, subspace ${subspaceId})`,
|
|
1318
|
-
}),
|
|
1319
|
-
hit_equal: data.hit_equal,
|
|
1320
|
-
hit_regex: data.hit_regex,
|
|
1321
|
-
}
|
|
1322
|
-
return map
|
|
1323
|
-
}),
|
|
1324
|
-
property_bank_calc_map: collectReduce(
|
|
1325
|
-
errors,
|
|
1326
|
-
subspace.dataCalculation,
|
|
1327
|
-
(map, dataCalc, dataCalcIndex) => {
|
|
1328
|
-
const dataCalcId = assertEntryId(
|
|
1329
|
-
dataCalc.id,
|
|
1330
|
-
'PROPERTY_BANK_COMMAND_MAP',
|
|
1331
|
-
`(data calc index: ${dataCalcIndex}, subspace: ${subspaceId})`,
|
|
1332
|
-
)
|
|
1333
|
-
|
|
1334
|
-
const calc: any = {
|
|
1335
|
-
alias: dataCalc.alias,
|
|
1336
|
-
title: dataCalc.title,
|
|
1337
|
-
description: dataCalc.description,
|
|
1338
|
-
hide_short_ref: dataCalc.hideShortRef,
|
|
1339
|
-
}
|
|
1340
|
-
if (dataCalc.triggerMode) calc.trigger_type = dataCalc.triggerMode
|
|
1341
|
-
if (dataCalc.__typename === 'DataCalculationMap') {
|
|
1342
|
-
calc.type = 'general'
|
|
1343
|
-
const mapCalc = dataCalc as DataCalculationMap
|
|
1344
|
-
|
|
1345
|
-
const getNodeId = (
|
|
1346
|
-
node: DataCalculationData | DataCommand,
|
|
1347
|
-
reference = '',
|
|
1348
|
-
nodeIndex?: number,
|
|
1349
|
-
) => {
|
|
1350
|
-
const nodeReference = [
|
|
1351
|
-
`data calc: ${dataCalcId}`,
|
|
1352
|
-
`subspace: ${subspaceId}`,
|
|
1353
|
-
typeof nodeIndex === 'number' ? `node index: ${nodeIndex}` : undefined,
|
|
1354
|
-
reference || undefined,
|
|
1355
|
-
]
|
|
1356
|
-
.filter(Boolean)
|
|
1357
|
-
.join(', ')
|
|
1358
|
-
|
|
1359
|
-
if (node.__typename === 'DataCalculationData') {
|
|
1360
|
-
return assertEntryId(
|
|
1361
|
-
node.data()?.id,
|
|
1362
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1363
|
-
`(${nodeReference})`,
|
|
1364
|
-
)
|
|
1365
|
-
}
|
|
1366
|
-
if (node.__typename === 'DataCommand') {
|
|
1367
|
-
return assertEntryId(node.id, 'PROPERTY_BANK_COMMAND_NODE', `(${nodeReference})`)
|
|
1368
|
-
}
|
|
1369
|
-
throw new Error(`Invalid node: ${JSON.stringify(node)}`)
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
const generateInputPorts = (inputs) =>
|
|
1373
|
-
inputs.reduce((acc, port, portIndex) => {
|
|
1374
|
-
if (!acc[port.key]) acc[port.key] = null
|
|
1375
|
-
|
|
1376
|
-
let sourceId
|
|
1377
|
-
const sourceNode = port.source()
|
|
1378
|
-
if (
|
|
1379
|
-
sourceNode?.__typename === 'DataCalculationData' ||
|
|
1380
|
-
sourceNode?.__typename === 'DataCommand'
|
|
1381
|
-
) {
|
|
1382
|
-
sourceId = getNodeId(sourceNode, `input port index: ${portIndex}`)
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1385
|
-
if (!sourceId) return acc
|
|
1386
|
-
if (!acc[port.key]) acc[port.key] = []
|
|
1387
|
-
|
|
1388
|
-
acc[port.key].push({
|
|
1389
|
-
id: sourceId,
|
|
1390
|
-
port: port.sourceKey,
|
|
1391
|
-
disable_trigger_command: !port.trigger ? true : undefined,
|
|
1392
|
-
})
|
|
1393
|
-
return acc
|
|
1394
|
-
}, {})
|
|
1395
|
-
|
|
1396
|
-
const generateOutputPorts = (outputs) =>
|
|
1397
|
-
outputs.reduce((acc, port, portIndex) => {
|
|
1398
|
-
if (!acc[port.key]) acc[port.key] = null
|
|
1399
|
-
|
|
1400
|
-
let targetId
|
|
1401
|
-
const targetNode = port.target()
|
|
1402
|
-
if (
|
|
1403
|
-
targetNode?.__typename === 'DataCalculationData' ||
|
|
1404
|
-
targetNode?.__typename === 'DataCommand'
|
|
1405
|
-
) {
|
|
1406
|
-
targetId = getNodeId(targetNode, `output port index: ${portIndex}`)
|
|
1407
|
-
}
|
|
1408
|
-
|
|
1409
|
-
if (!targetId) return acc
|
|
1410
|
-
if (!acc[port.key]) acc[port.key] = []
|
|
1411
|
-
|
|
1412
|
-
acc[port.key].push({
|
|
1413
|
-
id: targetId,
|
|
1414
|
-
port: port.targetKey,
|
|
1415
|
-
})
|
|
1416
|
-
return acc
|
|
1417
|
-
}, {})
|
|
1418
|
-
|
|
1419
|
-
calc.map = mapCalc.nodes.reduce((acc, node, nodeIndex) => {
|
|
1420
|
-
if (node.__typename === 'DataCalculationData') {
|
|
1421
|
-
const dataNode = node as DataCalculationData
|
|
1422
|
-
acc[getNodeId(dataNode, 'data node', nodeIndex)] = {
|
|
1423
|
-
title: dataNode.title,
|
|
1424
|
-
description: dataNode.description,
|
|
1425
|
-
hide_short_ref: dataNode.hideShortRef,
|
|
1426
|
-
type: 'data-node',
|
|
1427
|
-
properties: {},
|
|
1428
|
-
in: generateInputPorts(dataNode.inputs),
|
|
1429
|
-
out: generateOutputPorts(dataNode.outputs),
|
|
1430
|
-
}
|
|
1431
|
-
} else if (node.__typename === 'DataCommand') {
|
|
1432
|
-
const commandNode = node as DataCommand
|
|
1433
|
-
const commandName = commandNode.__commandName
|
|
1434
|
-
const type = commandName.split('_')[0].toLowerCase()
|
|
1435
|
-
|
|
1436
|
-
const args = commandNode.inputs.filter(
|
|
1437
|
-
(input) =>
|
|
1438
|
-
typeof input.source !== 'function' ||
|
|
1439
|
-
(input.source()?.__typename !== 'DataCalculationData' &&
|
|
1440
|
-
input.source()?.__typename !== 'DataCommand'),
|
|
1441
|
-
)
|
|
1442
|
-
const inputs = commandNode.inputs.filter(
|
|
1443
|
-
(input) =>
|
|
1444
|
-
typeof input.source === 'function' &&
|
|
1445
|
-
(input.source()?.__typename === 'DataCalculationData' ||
|
|
1446
|
-
input.source()?.__typename === 'DataCommand'),
|
|
1447
|
-
)
|
|
1448
|
-
acc[getNodeId(commandNode, 'command node', nodeIndex)] = {
|
|
1449
|
-
title: commandNode.title,
|
|
1450
|
-
description: commandNode.description,
|
|
1451
|
-
hide_short_ref: commandNode.hideShortRef,
|
|
1452
|
-
type: `command-node-${type}`,
|
|
1453
|
-
properties: {
|
|
1454
|
-
command: commandNode.__commandName,
|
|
1455
|
-
args: args.reduce((argsAcc, input) => {
|
|
1456
|
-
argsAcc[input.key] = input.source
|
|
1457
|
-
return argsAcc
|
|
1458
|
-
}, {}),
|
|
1459
|
-
},
|
|
1460
|
-
in: generateInputPorts(inputs),
|
|
1461
|
-
out: generateOutputPorts(commandNode.outputs),
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
return acc
|
|
1465
|
-
}, {})
|
|
1466
|
-
calc.editor_info = mapCalc.editorInfo.reduce((acc, editorInfo) => {
|
|
1467
|
-
acc[getNodeId(editorInfo.node, 'editor info node')] = {
|
|
1468
|
-
position: editorInfo.position,
|
|
1469
|
-
points: editorInfo.points.reduce((pointsAcc, point) => {
|
|
1470
|
-
const sourceId = getNodeId(point.source, 'editor info point source')
|
|
1471
|
-
const targetId = getNodeId(point.target, 'editor info point target')
|
|
1472
|
-
const key = `${sourceId}-${point.sourceOutputKey}-${targetId}-${point.targetInputKey}`
|
|
1473
|
-
pointsAcc[key] = point.positions
|
|
1474
|
-
return pointsAcc
|
|
1475
|
-
}, {}),
|
|
1476
|
-
}
|
|
1477
|
-
return acc
|
|
1478
|
-
}, {})
|
|
1479
|
-
} else if (dataCalc.__typename === 'DataCalculationScript') {
|
|
1480
|
-
const scriptCalc = dataCalc as DataCalculationScript
|
|
1481
|
-
calc.type = 'script'
|
|
1482
|
-
|
|
1483
|
-
const code = compileScriptCalculationCode(scriptCalc.code)
|
|
1484
|
-
calc.script_config = {
|
|
1485
|
-
title: scriptCalc.title ?? '',
|
|
1486
|
-
note: scriptCalc.note ?? '',
|
|
1487
|
-
code,
|
|
1488
|
-
enable_async: scriptCalc.enableAsync,
|
|
1489
|
-
trigger_mode: scriptCalc.triggerMode,
|
|
1490
|
-
inputs: scriptCalc.inputs.reduce((acc, input) => {
|
|
1491
|
-
const inputId = assertEntryId(
|
|
1492
|
-
input.data()?.id,
|
|
1493
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1494
|
-
`(data calc: ${dataCalcId}, script input: ${input.key}, subspace: ${subspaceId})`,
|
|
1495
|
-
)
|
|
1496
|
-
acc[inputId] = input.key
|
|
1497
|
-
return acc
|
|
1498
|
-
}, {}),
|
|
1499
|
-
disabled_triggers: scriptCalc.inputs.reduce((acc, input) => {
|
|
1500
|
-
const inputId = assertEntryId(
|
|
1501
|
-
input.data()?.id,
|
|
1502
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1503
|
-
`(data calc: ${dataCalcId}, script trigger input: ${input.key}, subspace: ${subspaceId})`,
|
|
1504
|
-
)
|
|
1505
|
-
acc[inputId] = !input.trigger
|
|
1506
|
-
return acc
|
|
1507
|
-
}, {}),
|
|
1508
|
-
output: scriptCalc.output
|
|
1509
|
-
? assertEntryId(
|
|
1510
|
-
scriptCalc.output()?.id,
|
|
1511
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1512
|
-
`(data calc: ${dataCalcId}, script output, subspace: ${subspaceId})`,
|
|
1513
|
-
)
|
|
1514
|
-
: null,
|
|
1515
|
-
outputs: scriptCalc.outputs.reduce((acc, output) => {
|
|
1516
|
-
if (!acc[output.key]) acc[output.key] = []
|
|
1517
|
-
const outputId = assertEntryId(
|
|
1518
|
-
output.data()?.id,
|
|
1519
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1520
|
-
`(data calc: ${dataCalcId}, script outputs key: ${output.key}, subspace: ${subspaceId})`,
|
|
1521
|
-
)
|
|
1522
|
-
acc[output.key].push(outputId)
|
|
1523
|
-
return acc
|
|
1524
|
-
}, {}),
|
|
1525
|
-
error: scriptCalc.error
|
|
1526
|
-
? assertEntryId(
|
|
1527
|
-
scriptCalc.error()?.id,
|
|
1528
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1529
|
-
`(data calc: ${dataCalcId}, script error output, subspace: ${subspaceId})`,
|
|
1530
|
-
)
|
|
1531
|
-
: null,
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
Object.assign(calc, generateCalulationMap(calc.script_config, dataCalcId))
|
|
1535
|
-
}
|
|
1536
|
-
map[dataCalcId] = calc
|
|
1537
|
-
return map
|
|
1538
|
-
},
|
|
1539
|
-
),
|
|
1540
|
-
action_map: subspace.actions || undefined,
|
|
1541
|
-
event_map: compileEvents('', subspace.events || {}, {
|
|
1542
|
-
camelCase: false,
|
|
1543
|
-
errorReference: `(subspace ${subspaceId})`,
|
|
1544
|
-
}),
|
|
1545
|
-
routing: collectReduce(errors, subspace.dataRouting, (acc, data, index) => {
|
|
1546
|
-
const dataId = assertEntryId(
|
|
1547
|
-
data?.id,
|
|
1548
|
-
'PROPERTY_BANK_DATA_NODE',
|
|
1549
|
-
`(data routing index: ${index}, subspace ${subspaceId})`,
|
|
1550
|
-
)
|
|
1551
|
-
acc[dataId] = { enabled_routing: true }
|
|
1552
|
-
return acc
|
|
1553
|
-
}),
|
|
1554
|
-
...compileModule(subspace),
|
|
1555
|
-
}
|
|
1556
|
-
return subspaceMap
|
|
1557
|
-
}, {}),
|
|
1558
|
-
root_subspace_id: collect(
|
|
1559
|
-
errors,
|
|
1560
|
-
() => assertEntryId(app.rootSubspace?.id, 'SUBSPACE', '(application root subspace)'),
|
|
1561
|
-
'',
|
|
1562
|
-
),
|
|
1563
|
-
fonts: app.fonts,
|
|
1564
|
-
...compileApplicationSettings(app.settings),
|
|
1565
|
-
// Use typed automationMap if available, otherwise fall back to TEMP metadata
|
|
1566
|
-
test_map: compiledAutomationMap
|
|
1567
|
-
? compiledAutomationMap['AUTOMATION_MAP_DEFAULT']?.map || {}
|
|
1568
|
-
: app.metadata?.TEMP_test_map || {},
|
|
1569
|
-
automation_map: compiledAutomationMap || app.metadata?.TEMP_automation_map || {},
|
|
1570
|
-
update_timestamp: timestamp,
|
|
1571
|
-
}
|
|
1572
|
-
if (errors.length > 0) {
|
|
1573
|
-
throw new Error(
|
|
1574
|
-
`Compile failed with ${errors.length} error(s):\n` +
|
|
1575
|
-
errors.map((message, index) => ` ${index + 1}. ${message}`).join('\n'),
|
|
1576
|
-
)
|
|
1577
|
-
}
|
|
1578
|
-
await recordConfigChange(previousConfig, config)
|
|
1579
|
-
return config
|
|
1580
|
-
}
|
|
1581
|
-
|
|
1582
|
-
export const checkConfig = async (configPath: string) => {
|
|
1583
|
-
// --validate-automation surfaces broken automation_map / test_map refs early,
|
|
1584
|
-
// which catches agent-authored automations that reference deleted bricks.
|
|
1585
|
-
await sh`bricks app check-config --validate-automation ${configPath}`
|
|
1586
|
-
// Doctor adds semantic lint checks after structural validation. Warnings are
|
|
1587
|
-
// surfaced in the compile log, but only errors fail by default. Older published
|
|
1588
|
-
// bricks-cli builds lack `app doctor` — skip rather than fail the compile.
|
|
1589
|
-
const doctor = await sh`bricks app doctor --validate-automation ${configPath}`.nothrow()
|
|
1590
|
-
if (doctor.exitCode !== 0) {
|
|
1591
|
-
if (/unknown command/i.test(doctor.stderr?.toString() ?? '')) return
|
|
1592
|
-
throw new Error(`bricks app doctor failed with exit ${doctor.exitCode}`)
|
|
1593
|
-
}
|
|
1594
|
-
}
|