moonscratch 0.1.0 → 0.1.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 (150) hide show
  1. package/dist/chunk-DQk6qfdC.mjs +18 -0
  2. package/dist/index.d.mts +1173 -0
  3. package/dist/index.mjs +27135 -0
  4. package/package.json +6 -1
  5. package/.agents/skills/moonbit-agent-guide/LICENSE +0 -202
  6. package/.agents/skills/moonbit-agent-guide/SKILL.mbt.md +0 -1126
  7. package/.agents/skills/moonbit-agent-guide/SKILL.md +0 -1126
  8. package/.agents/skills/moonbit-agent-guide/ide.md +0 -116
  9. package/.agents/skills/moonbit-agent-guide/references/advanced-moonbit-build.md +0 -106
  10. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.mbt.md +0 -422
  11. package/.agents/skills/moonbit-agent-guide/references/moonbit-language-fundamentals.md +0 -422
  12. package/.agents/skills/moonbit-practice/SKILL.md +0 -258
  13. package/.agents/skills/moonbit-practice/assets/ci.yaml +0 -25
  14. package/.agents/skills/moonbit-practice/reference/agents.md +0 -1469
  15. package/.agents/skills/moonbit-practice/reference/configuration.md +0 -228
  16. package/.agents/skills/moonbit-practice/reference/ffi.md +0 -229
  17. package/.agents/skills/moonbit-practice/reference/ide.md +0 -189
  18. package/.agents/skills/moonbit-practice/reference/performance.md +0 -217
  19. package/.agents/skills/moonbit-practice/reference/refactor.md +0 -154
  20. package/.agents/skills/moonbit-practice/reference/stdlib.md +0 -351
  21. package/.agents/skills/moonbit-practice/reference/testing.md +0 -228
  22. package/.agents/skills/moonbit-refactoring/LICENSE +0 -21
  23. package/.agents/skills/moonbit-refactoring/SKILL.md +0 -323
  24. package/.githooks/README.md +0 -23
  25. package/.githooks/pre-commit +0 -3
  26. package/.github/workflows/copilot-setup-steps.yml +0 -40
  27. package/.turbo/turbo-typecheck.log +0 -2
  28. package/AGENTS.md +0 -91
  29. package/PLAN.md +0 -64
  30. package/TODO.md +0 -120
  31. package/benchmarks/calc.bench.ts +0 -144
  32. package/benchmarks/draw.bench.ts +0 -215
  33. package/benchmarks/load.bench.ts +0 -28
  34. package/benchmarks/render.bench.ts +0 -53
  35. package/benchmarks/run.bench.ts +0 -8
  36. package/benchmarks/types.d.ts +0 -15
  37. package/docs/scratch-vm-specs/eventloop.md +0 -103
  38. package/docs/scratch-vm-specs/moonscratch-time-separation.md +0 -50
  39. package/index.html +0 -91
  40. package/js/AGENTS.md +0 -5
  41. package/js/a.ts +0 -52
  42. package/js/assets/AGENTS.md +0 -5
  43. package/js/assets/base64.test.ts +0 -14
  44. package/js/assets/base64.ts +0 -21
  45. package/js/assets/build-asset.test.ts +0 -26
  46. package/js/assets/build-asset.ts +0 -28
  47. package/js/assets/create.test.ts +0 -142
  48. package/js/assets/create.ts +0 -122
  49. package/js/assets/index.test.ts +0 -15
  50. package/js/assets/index.ts +0 -2
  51. package/js/assets/types.ts +0 -26
  52. package/js/assets/validation.test.ts +0 -34
  53. package/js/assets/validation.ts +0 -25
  54. package/js/assets.test.ts +0 -14
  55. package/js/assets.ts +0 -1
  56. package/js/index.test.ts +0 -26
  57. package/js/index.ts +0 -3
  58. package/js/render/index.test.ts +0 -65
  59. package/js/render/index.ts +0 -13
  60. package/js/render/sharp.ts +0 -87
  61. package/js/render/svg.ts +0 -68
  62. package/js/render/types.ts +0 -35
  63. package/js/render/utils.ts +0 -108
  64. package/js/render/webgl.ts +0 -274
  65. package/js/sharp-optional.d.ts +0 -16
  66. package/js/test/helpers.ts +0 -116
  67. package/js/test/hikkaku-sample.test.ts +0 -37
  68. package/js/test/rubik-components.input-motion.test.ts +0 -60
  69. package/js/test/rubik-components.lists.test.ts +0 -49
  70. package/js/test/rubik-components.operators.test.ts +0 -104
  71. package/js/test/rubik-components.pen.test.ts +0 -112
  72. package/js/test/rubik-components.procedures-loops.test.ts +0 -72
  73. package/js/test/rubik-components.variables-branches.test.ts +0 -57
  74. package/js/test/rubik-components.visibility-entry.test.ts +0 -31
  75. package/js/test/test-projects.ts +0 -598
  76. package/js/test/variable.ts +0 -200
  77. package/js/test/warp.test.ts +0 -59
  78. package/js/vm/AGENTS.md +0 -6
  79. package/js/vm/README.md +0 -183
  80. package/js/vm/bindings.test.ts +0 -13
  81. package/js/vm/bindings.ts +0 -5
  82. package/js/vm/compare-operators.test.ts +0 -145
  83. package/js/vm/constants.test.ts +0 -11
  84. package/js/vm/constants.ts +0 -4
  85. package/js/vm/effect-guards.test.ts +0 -68
  86. package/js/vm/effect-guards.ts +0 -44
  87. package/js/vm/factory.test.ts +0 -486
  88. package/js/vm/factory.ts +0 -615
  89. package/js/vm/headless-vm.test.ts +0 -131
  90. package/js/vm/headless-vm.ts +0 -342
  91. package/js/vm/index.test.ts +0 -28
  92. package/js/vm/index.ts +0 -5
  93. package/js/vm/internal-types.ts +0 -32
  94. package/js/vm/json.test.ts +0 -40
  95. package/js/vm/json.ts +0 -273
  96. package/js/vm/normalize.test.ts +0 -48
  97. package/js/vm/normalize.ts +0 -65
  98. package/js/vm/options.test.ts +0 -30
  99. package/js/vm/options.ts +0 -55
  100. package/js/vm/pen-transparency.test.ts +0 -115
  101. package/js/vm/program-wasm.ts +0 -322
  102. package/js/vm/scheduler-render.test.ts +0 -401
  103. package/js/vm/scratch-assets.test.ts +0 -136
  104. package/js/vm/scratch-assets.ts +0 -202
  105. package/js/vm/types.ts +0 -358
  106. package/js/vm/value-guards.test.ts +0 -25
  107. package/js/vm/value-guards.ts +0 -18
  108. package/moon.mod.json +0 -10
  109. package/scripts/preinstall.ts +0 -4
  110. package/src/AGENTS.md +0 -6
  111. package/src/api.mbt +0 -161
  112. package/src/api_aot_commands.mbt +0 -184
  113. package/src/api_effects_json.mbt +0 -72
  114. package/src/api_options.mbt +0 -60
  115. package/src/api_program_wasm.mbt +0 -1647
  116. package/src/api_program_wat.mbt +0 -2206
  117. package/src/api_snapshot_json.mbt +0 -44
  118. package/src/cmd/AGENTS.md +0 -5
  119. package/src/cmd/main/AGENTS.md +0 -5
  120. package/src/cmd/main/main.mbt +0 -29
  121. package/src/cmd/main/moon.pkg +0 -7
  122. package/src/cmd/main/pkg.generated.mbti +0 -13
  123. package/src/json_helpers.mbt +0 -176
  124. package/src/moon.pkg +0 -65
  125. package/src/moonscratch.mbt +0 -3
  126. package/src/moonscratch_wbtest.mbt +0 -40
  127. package/src/parser_sb3.mbt +0 -890
  128. package/src/pkg.generated.mbti +0 -479
  129. package/src/runtime_eval.mbt +0 -2844
  130. package/src/runtime_exec.mbt +0 -3850
  131. package/src/runtime_render.mbt +0 -2550
  132. package/src/runtime_state.mbt +0 -870
  133. package/src/test/AGENTS.md +0 -3
  134. package/src/test/projects/AGENTS.md +0 -6
  135. package/src/test/projects/moon.pkg +0 -4
  136. package/src/test/projects/moonscratch_compat_test.mbt +0 -642
  137. package/src/test/projects/moonscratch_core_test.mbt +0 -1332
  138. package/src/test/projects/moonscratch_runtime_test.mbt +0 -1087
  139. package/src/test/projects/pkg.generated.mbti +0 -13
  140. package/src/test/projects/test_support.mbt +0 -35
  141. package/src/types_effects.mbt +0 -20
  142. package/src/types_error.mbt +0 -4
  143. package/src/types_options.mbt +0 -31
  144. package/src/types_runtime_structs.mbt +0 -254
  145. package/src/types_vm.mbt +0 -109
  146. package/tsconfig.json +0 -29
  147. package/viewer/index.ts +0 -399
  148. package/viewer/vite.d.ts +0 -1
  149. package/viewer/worker.ts +0 -161
  150. package/vite.config.ts +0 -11
package/js/vm/factory.ts DELETED
@@ -1,615 +0,0 @@
1
- import { moonscratch } from './bindings.ts'
2
- import { HeadlessVM } from './headless-vm.ts'
3
- import type { MoonResult } from './internal-types.ts'
4
- import {
5
- toOptionalJsonString,
6
- toProjectJsonString,
7
- unwrapResult,
8
- } from './json.ts'
9
- import { toOptionsJson } from './options.ts'
10
- import { instantiateProgramModule } from './program-wasm.ts'
11
- import { resolveMissingScratchAssets } from './scratch-assets.ts'
12
- import type {
13
- CompileProjectToWasmOptions,
14
- CompileProjectToWasmResult,
15
- CompileProjectToWatOptions,
16
- CompileProjectToWatResult,
17
- CreateHeadlessVMFromProjectOptions,
18
- CreateHeadlessVMOptions,
19
- CreateHeadlessVMWithScratchAssetsOptions,
20
- CreateProgramModuleFromProjectOptions,
21
- CreateProgramModuleOptions,
22
- JsonValue,
23
- PrecompileProgramForRuntimeOptions,
24
- ProgramManifest,
25
- ProgramModule,
26
- RuntimeHandle,
27
- } from './types.ts'
28
-
29
- type BoundMoonscratchFactory = {
30
- vm_compile_from_json?: (
31
- projectJson: string,
32
- assetsJson?: string,
33
- ) => MoonResult<unknown, unknown>
34
- vm_compile_project_to_wat?: (
35
- projectJson: string,
36
- assetsJson?: string,
37
- ) => MoonResult<string, unknown>
38
- vm_compile_project_to_wasm?: (
39
- projectJson: string,
40
- assetsJson?: string,
41
- ) => MoonResult<string, unknown>
42
- vm_abi_version?: () => number
43
- vm_set_aot_commands_json?: (
44
- vmHandle: unknown,
45
- commandsJson: string,
46
- ) => MoonResult<unknown, unknown>
47
- vm_get_variable_number_by_id?: (
48
- vmHandle: unknown,
49
- targetIndex: number,
50
- variableId: string,
51
- ) => number
52
- vm_set_variable_number_by_id?: (
53
- vmHandle: unknown,
54
- targetIndex: number,
55
- variableId: string,
56
- value: number,
57
- ) => void
58
- vm_set_variable_json_by_id?: (
59
- vmHandle: unknown,
60
- targetIndex: number,
61
- variableId: string,
62
- valueJson: string,
63
- ) => void
64
- vm_exec_script_tail_by_pc?: (
65
- vmHandle: unknown,
66
- targetIndex: number,
67
- startPc: number,
68
- ) => number
69
- vm_exec_opcode_once_by_pc?: (
70
- vmHandle: unknown,
71
- targetIndex: number,
72
- pc: number,
73
- ) => number
74
- vm_exec_draw_opcode?: (
75
- vmHandle: unknown,
76
- targetIndex: number,
77
- opcode: string,
78
- arg0: number,
79
- arg1: number,
80
- extra: number,
81
- ) => number
82
- vm_new_from_compiled?: (
83
- precompiled: unknown,
84
- optionsJson?: string,
85
- ) => MoonResult<unknown, unknown>
86
- }
87
-
88
- const OPCODE_SET_VERSION = 2
89
- const runtimePrecompiledCacheSymbol = Symbol(
90
- 'moonscratch.runtime_precompiled_cache',
91
- )
92
-
93
- type ProgramModuleInternal = ProgramModule & {
94
- [runtimePrecompiledCacheSymbol]?: WeakMap<object, unknown>
95
- }
96
-
97
- const hasAnyAssetEntry = (assets: Record<string, JsonValue>): boolean => {
98
- for (const _key in assets) {
99
- return true
100
- }
101
- return false
102
- }
103
-
104
- const toAssetsJson = (
105
- assets: string | Record<string, JsonValue> | undefined,
106
- ): string | undefined => {
107
- return assets === undefined ||
108
- (typeof assets !== 'string' && !hasAnyAssetEntry(assets))
109
- ? undefined
110
- : toOptionalJsonString(assets, 'assets')
111
- }
112
-
113
- const requireBinding = (): BoundMoonscratchFactory => {
114
- return moonscratch as unknown as BoundMoonscratchFactory
115
- }
116
-
117
- const requireAbiVersion = (binding: BoundMoonscratchFactory): number => {
118
- if (typeof binding.vm_abi_version !== 'function') {
119
- throw new Error(
120
- 'vm_abi_version is unavailable in this build. Please rebuild moonscratch JS bindings.',
121
- )
122
- }
123
- const abiVersion = binding.vm_abi_version()
124
- if (!Number.isInteger(abiVersion) || abiVersion <= 0) {
125
- throw new Error(
126
- `vm_abi_version returned an invalid value: ${String(abiVersion)}`,
127
- )
128
- }
129
- return abiVersion
130
- }
131
-
132
- const encodeUtf8Length = (value: string): number =>
133
- new TextEncoder().encode(value).length
134
-
135
- const fnv1aHex = (input: string): string => {
136
- let hash = 0x811c9dc5
137
- for (let index = 0; index < input.length; index += 1) {
138
- hash ^= input.charCodeAt(index)
139
- hash = Math.imul(hash, 0x01000193)
140
- }
141
- return (hash >>> 0).toString(16).padStart(8, '0')
142
- }
143
-
144
- const createManifest = ({
145
- abiVersion,
146
- projectJson,
147
- assetsJson,
148
- }: {
149
- abiVersion: number
150
- projectJson: string
151
- assetsJson: string
152
- }): ProgramManifest => {
153
- return {
154
- abiVersion,
155
- opcodeSetVersion: OPCODE_SET_VERSION,
156
- projectByteLength: encodeUtf8Length(projectJson),
157
- assetsByteLength: encodeUtf8Length(assetsJson),
158
- buildFingerprint: fnv1aHex(
159
- `abi=${String(abiVersion)}|project=${projectJson}|assets=${assetsJson}`,
160
- ),
161
- }
162
- }
163
-
164
- export const createRuntime = (): RuntimeHandle => {
165
- const binding = requireBinding()
166
- const abiVersion = requireAbiVersion(binding)
167
- return {
168
- raw: binding,
169
- abiVersion,
170
- }
171
- }
172
-
173
- export const compileProjectToWat = ({
174
- projectJson,
175
- assets,
176
- }: CompileProjectToWatOptions): CompileProjectToWatResult => {
177
- const binding = requireBinding()
178
- if (typeof binding.vm_compile_project_to_wat !== 'function') {
179
- throw new Error(
180
- 'vm_compile_project_to_wat is unavailable in this build. Please rebuild moonscratch JS bindings.',
181
- )
182
- }
183
-
184
- const normalizedProjectJson = toProjectJsonString(projectJson)
185
- const normalizedAssetsJson = toAssetsJson(assets)
186
- const abiVersion = requireAbiVersion(binding)
187
- const wat = unwrapResult(
188
- binding.vm_compile_project_to_wat(
189
- normalizedProjectJson,
190
- normalizedAssetsJson,
191
- ),
192
- 'vm_compile_project_to_wat failed',
193
- )
194
- return {
195
- wat,
196
- abiVersion,
197
- manifest: createManifest({
198
- abiVersion,
199
- projectJson: normalizedProjectJson,
200
- assetsJson: normalizedAssetsJson ?? '{}',
201
- }),
202
- }
203
- }
204
-
205
- const toUint8Array = (value: Uint8Array | ArrayBuffer): Uint8Array => {
206
- return value instanceof Uint8Array ? value : new Uint8Array(value)
207
- }
208
-
209
- const decodeBase64ToUint8Array = (base64: string): Uint8Array => {
210
- const maybeBuffer = globalThis as {
211
- Buffer?: { from: (input: string, encoding: string) => Uint8Array }
212
- }
213
- if (typeof maybeBuffer.Buffer?.from === 'function') {
214
- return new Uint8Array(maybeBuffer.Buffer.from(base64, 'base64'))
215
- }
216
- const maybeAtob = (globalThis as { atob?: (input: string) => string }).atob
217
- if (typeof maybeAtob === 'function') {
218
- const binary = maybeAtob(base64)
219
- const out = new Uint8Array(binary.length)
220
- for (let index = 0; index < binary.length; index += 1) {
221
- out[index] = binary.charCodeAt(index) & 0xff
222
- }
223
- return out
224
- }
225
- throw new Error('No base64 decoder found in this runtime')
226
- }
227
-
228
- export const compileProjectToWasm = ({
229
- watToWasm,
230
- ...rest
231
- }: CompileProjectToWasmOptions): CompileProjectToWasmResult => {
232
- const compiled = compileProjectToWat(rest)
233
- let wasm: Uint8Array
234
- if (watToWasm) {
235
- wasm = toUint8Array(watToWasm(compiled.wat))
236
- } else {
237
- const binding = requireBinding()
238
- if (typeof binding.vm_compile_project_to_wasm !== 'function') {
239
- throw new Error(
240
- 'vm_compile_project_to_wasm is unavailable in this build. Please rebuild moonscratch JS bindings.',
241
- )
242
- }
243
- const wasmBase64 = unwrapResult(
244
- binding.vm_compile_project_to_wasm(
245
- toProjectJsonString(rest.projectJson),
246
- toAssetsJson(rest.assets),
247
- ),
248
- 'vm_compile_project_to_wasm failed',
249
- )
250
- wasm = decodeBase64ToUint8Array(wasmBase64)
251
- }
252
- return {
253
- ...compiled,
254
- wasmBytes: wasm,
255
- }
256
- }
257
-
258
- export const createProgramModule = ({
259
- wasmBytes,
260
- manifest,
261
- }: CreateProgramModuleOptions): ProgramModule => {
262
- return instantiateProgramModule(wasmBytes, manifest)
263
- }
264
-
265
- export const createProgramModuleFromProject = (
266
- options: CreateProgramModuleFromProjectOptions,
267
- ): ProgramModule => {
268
- const compiled = compileProjectToWasm(options)
269
- return createProgramModule({
270
- wasmBytes: compiled.wasmBytes,
271
- manifest: compiled.manifest,
272
- })
273
- }
274
-
275
- const requireRuntimeBinding = (
276
- runtime?: RuntimeHandle,
277
- ): BoundMoonscratchFactory => {
278
- return (
279
- (runtime?.raw as BoundMoonscratchFactory | undefined) ?? requireBinding()
280
- )
281
- }
282
-
283
- const runtimePrecompiledCache = (
284
- program: ProgramModule,
285
- ): WeakMap<object, unknown> => {
286
- const internal = program as ProgramModuleInternal
287
- if (!internal[runtimePrecompiledCacheSymbol]) {
288
- internal[runtimePrecompiledCacheSymbol] = new WeakMap<object, unknown>()
289
- }
290
- return internal[runtimePrecompiledCacheSymbol]
291
- }
292
-
293
- const compileProgramForRuntime = (
294
- program: ProgramModule,
295
- binding: BoundMoonscratchFactory,
296
- ): unknown => {
297
- if (typeof binding.vm_compile_from_json !== 'function') {
298
- throw new Error(
299
- 'vm_compile_from_json is unavailable in this build. Please rebuild moonscratch JS bindings.',
300
- )
301
- }
302
-
303
- const runtimeKey = binding as object
304
- const cache = runtimePrecompiledCache(program)
305
- const cached = cache.get(runtimeKey)
306
- if (cached !== undefined) {
307
- return cached
308
- }
309
- const payload = program.readPayload()
310
- const compiled = unwrapResult(
311
- binding.vm_compile_from_json(payload.projectJson, payload.assetsJson),
312
- 'vm_compile_from_json failed',
313
- )
314
- cache.set(runtimeKey, compiled)
315
- return compiled
316
- }
317
-
318
- const withCommandsExecMode = (
319
- commandsJson: string,
320
- execMode: string,
321
- ): string | null => {
322
- try {
323
- const parsed = JSON.parse(commandsJson) as unknown
324
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
325
- return null
326
- }
327
- const next = {
328
- ...(parsed as Record<string, unknown>),
329
- exec_mode: execMode,
330
- }
331
- return JSON.stringify(next)
332
- } catch {
333
- return null
334
- }
335
- }
336
-
337
- const commandContainsHostTail = (entry: unknown, depth = 0): boolean => {
338
- if (depth > 64 || !entry || typeof entry !== 'object') {
339
- return false
340
- }
341
- const command = entry as {
342
- op?: unknown
343
- then?: unknown
344
- else?: unknown
345
- body?: unknown
346
- }
347
- if (command.op === 'host_tail') {
348
- return true
349
- }
350
- if (command.op === 'if' && Array.isArray(command.then)) {
351
- return command.then.some((child) =>
352
- commandContainsHostTail(child, depth + 1),
353
- )
354
- }
355
- if (command.op === 'if_else') {
356
- const thenHasTail =
357
- Array.isArray(command.then) &&
358
- command.then.some((child) => commandContainsHostTail(child, depth + 1))
359
- const elseHasTail =
360
- Array.isArray(command.else) &&
361
- command.else.some((child) => commandContainsHostTail(child, depth + 1))
362
- return thenHasTail || elseHasTail
363
- }
364
- if (
365
- (command.op === 'repeat' ||
366
- command.op === 'repeat_until' ||
367
- command.op === 'while') &&
368
- Array.isArray(command.body)
369
- ) {
370
- return command.body.some((child) =>
371
- commandContainsHostTail(child, depth + 1),
372
- )
373
- }
374
- return false
375
- }
376
-
377
- const commandsIncludeHostTail = (commandsJson: string): boolean => {
378
- try {
379
- const parsed = JSON.parse(commandsJson) as unknown
380
- const exec = Array.isArray(parsed)
381
- ? parsed
382
- : parsed &&
383
- typeof parsed === 'object' &&
384
- Array.isArray((parsed as { exec?: unknown }).exec)
385
- ? (parsed as { exec: unknown[] }).exec
386
- : []
387
- return exec.some((entry) => commandContainsHostTail(entry))
388
- } catch {
389
- return false
390
- }
391
- }
392
-
393
- const hasWasmHostBridgeApi = (binding: BoundMoonscratchFactory): boolean => {
394
- return (
395
- typeof binding.vm_get_variable_number_by_id === 'function' &&
396
- typeof binding.vm_set_variable_number_by_id === 'function' &&
397
- typeof binding.vm_set_variable_json_by_id === 'function' &&
398
- typeof binding.vm_exec_opcode_once_by_pc === 'function' &&
399
- typeof binding.vm_exec_script_tail_by_pc === 'function' &&
400
- typeof binding.vm_exec_draw_opcode === 'function'
401
- )
402
- }
403
-
404
- export const precompileProgramForRuntime = ({
405
- program,
406
- runtime,
407
- }: PrecompileProgramForRuntimeOptions): void => {
408
- const binding = requireRuntimeBinding(runtime)
409
- const runtimeAbi = requireAbiVersion(binding)
410
- if (program.abiVersion !== runtimeAbi) {
411
- throw new Error(
412
- `Program ABI ${String(program.abiVersion)} does not match runtime ABI ${String(runtimeAbi)}`,
413
- )
414
- }
415
- void compileProgramForRuntime(program, binding)
416
- }
417
-
418
- export const createHeadlessVM = ({
419
- runtime,
420
- program,
421
- options,
422
- initialNowMs,
423
- viewerLanguage,
424
- translateCache,
425
- }: CreateHeadlessVMOptions): HeadlessVM => {
426
- const binding = requireRuntimeBinding(runtime)
427
- if (typeof binding.vm_new_from_compiled !== 'function') {
428
- throw new Error(
429
- 'vm_new_from_compiled is unavailable in this build. Please rebuild moonscratch JS bindings.',
430
- )
431
- }
432
-
433
- const runtimeAbi = requireAbiVersion(binding)
434
- if (program.abiVersion !== runtimeAbi) {
435
- throw new Error(
436
- `Program ABI ${String(program.abiVersion)} does not match runtime ABI ${String(runtimeAbi)}`,
437
- )
438
- }
439
-
440
- const precompiled = compileProgramForRuntime(program, binding)
441
- const vm = unwrapResult(
442
- binding.vm_new_from_compiled(precompiled, toOptionsJson(options)),
443
- 'vm_new_from_compiled failed',
444
- )
445
- let afterGreenFlag: (() => void) | undefined
446
- let beforeStepFrame: (() => void) | undefined
447
- const payload = program.readPayload()
448
- if (payload.commandsJson && payload.commandsJson.trim().length > 0) {
449
- if (typeof binding.vm_set_aot_commands_json !== 'function') {
450
- throw new Error(
451
- 'vm_set_aot_commands_json is unavailable in this build. Please rebuild moonscratch JS bindings.',
452
- )
453
- }
454
- const hasHostTail = commandsIncludeHostTail(payload.commandsJson)
455
- let commandsJsonForRuntime = payload.commandsJson
456
- if (program.hasWasmExec() && hasWasmHostBridgeApi(binding)) {
457
- const commandsJsonWasmOnly = withCommandsExecMode(
458
- payload.commandsJson,
459
- 'wasm_only',
460
- )
461
- if (commandsJsonWasmOnly !== null) {
462
- const runner = program.createWasmExecRunner({
463
- getVarNumber: (targetIndex, variableId) => {
464
- return (
465
- binding.vm_get_variable_number_by_id?.(
466
- vm,
467
- targetIndex,
468
- variableId,
469
- ) ?? 0
470
- )
471
- },
472
- setVarNumber: (targetIndex, variableId, value) => {
473
- binding.vm_set_variable_number_by_id?.(
474
- vm,
475
- targetIndex,
476
- variableId,
477
- value,
478
- )
479
- },
480
- setVarJson: (targetIndex, variableId, valueJson) => {
481
- binding.vm_set_variable_json_by_id?.(
482
- vm,
483
- targetIndex,
484
- variableId,
485
- valueJson,
486
- )
487
- },
488
- execHostTail: (targetIndex, startPc) => {
489
- return (
490
- binding.vm_exec_script_tail_by_pc?.(vm, targetIndex, startPc) ?? 0
491
- )
492
- },
493
- execHostOpcode: (targetIndex, pc) => {
494
- return binding.vm_exec_opcode_once_by_pc?.(vm, targetIndex, pc) ?? 0
495
- },
496
- execDrawOpcode: (targetIndex, opcode, arg0, arg1, extra) => {
497
- return (
498
- binding.vm_exec_draw_opcode?.(
499
- vm,
500
- targetIndex,
501
- opcode,
502
- arg0,
503
- arg1,
504
- extra,
505
- ) ?? 0
506
- )
507
- },
508
- })
509
- if (runner) {
510
- commandsJsonForRuntime = commandsJsonWasmOnly
511
- if (hasHostTail) {
512
- let pendingRunner = false
513
- afterGreenFlag = () => {
514
- pendingRunner = true
515
- }
516
- beforeStepFrame = () => {
517
- if (!pendingRunner) {
518
- return
519
- }
520
- pendingRunner = false
521
- void runner()
522
- }
523
- } else {
524
- afterGreenFlag = () => {
525
- void runner()
526
- }
527
- }
528
- }
529
- }
530
- }
531
- unwrapResult(
532
- binding.vm_set_aot_commands_json(vm, commandsJsonForRuntime),
533
- 'vm_set_aot_commands_json failed',
534
- )
535
- }
536
-
537
- const runtimeVm = new HeadlessVM(vm, {
538
- afterGreenFlag,
539
- beforeStepFrame,
540
- })
541
- runtimeVm.setTime(initialNowMs ?? Date.now())
542
- if (viewerLanguage !== undefined) {
543
- runtimeVm.setViewerLanguage(viewerLanguage)
544
- }
545
- if (translateCache !== undefined) {
546
- runtimeVm.setTranslateCache(translateCache)
547
- }
548
- return runtimeVm
549
- }
550
-
551
- export const createVM = createHeadlessVM
552
-
553
- export const createHeadlessVMFromProject = ({
554
- projectJson,
555
- assets,
556
- watToWasm,
557
- runtime,
558
- options,
559
- initialNowMs,
560
- viewerLanguage,
561
- translateCache,
562
- }: CreateHeadlessVMFromProjectOptions): HeadlessVM => {
563
- const program = createProgramModuleFromProject({
564
- projectJson,
565
- assets,
566
- watToWasm,
567
- })
568
- return createHeadlessVM({
569
- runtime,
570
- program,
571
- options,
572
- initialNowMs,
573
- viewerLanguage,
574
- translateCache,
575
- })
576
- }
577
-
578
- export const createVMFromProject = createHeadlessVMFromProject
579
-
580
- export const createHeadlessVMWithScratchAssets = async ({
581
- projectJson,
582
- assets = {},
583
- scratchCdnBaseUrl,
584
- fetchAsset,
585
- decodeImageBytes,
586
- watToWasm,
587
- runtime,
588
- options,
589
- initialNowMs,
590
- viewerLanguage,
591
- translateCache,
592
- }: CreateHeadlessVMWithScratchAssetsOptions): Promise<HeadlessVM> => {
593
- const resolvedAssets = await resolveMissingScratchAssets({
594
- projectJson,
595
- assets,
596
- scratchCdnBaseUrl,
597
- fetchAsset,
598
- decodeImageBytes,
599
- })
600
-
601
- return createHeadlessVMFromProject({
602
- projectJson,
603
- assets: resolvedAssets,
604
- watToWasm,
605
- runtime,
606
- options,
607
- initialNowMs,
608
- viewerLanguage,
609
- translateCache,
610
- })
611
- }
612
-
613
- export const createVMWithScratchAssets = createHeadlessVMWithScratchAssets
614
-
615
- export { moonscratch }