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/viewer/index.ts DELETED
@@ -1,399 +0,0 @@
1
- /// <reference types="vite-plus/client" />
2
-
3
- import type { RenderFrame, VMInputEvent } from '../js/index.ts'
4
- import VMWorker from './worker.ts?worker'
5
-
6
- type ExampleProject = {
7
- id: string
8
- label: string
9
- projectJson: string
10
- }
11
-
12
- type ProjectModule = {
13
- default?: unknown
14
- }
15
-
16
- type ViewerWorkerRequest =
17
- | {
18
- type: 'load'
19
- projectJson: string
20
- }
21
- | {
22
- type: 'input'
23
- input: VMInputEvent
24
- }
25
-
26
- type ViewerWorkerResponse =
27
- | {
28
- type: 'frame'
29
- frame: RenderFrame
30
- workerFps: number
31
- workerOpsPerSecond: number
32
- }
33
- | {
34
- type: 'finished'
35
- }
36
- | {
37
- type: 'warp-exit'
38
- isInWarp: boolean
39
- }
40
- | {
41
- type: 'error'
42
- message: string
43
- }
44
-
45
- const formatProjectId = (path: string): string => {
46
- const match = path.match(/([^/]+)\/dist\/project\.json$/)
47
- return match?.[1] ?? path
48
- }
49
-
50
- const toProjectJson = (_path: string, raw: unknown): string | null => {
51
- const rawModule = raw as ProjectModule | string
52
- const extracted =
53
- typeof rawModule === 'object' &&
54
- rawModule !== null &&
55
- 'default' in rawModule
56
- ? rawModule.default
57
- : rawModule
58
- if (typeof extracted === 'string') {
59
- return extracted
60
- }
61
- if (extracted === undefined || extracted === null) {
62
- return null
63
- }
64
- try {
65
- return JSON.stringify(extracted)
66
- } catch {
67
- return null
68
- }
69
- }
70
-
71
- const loadedModules = import.meta.glob(
72
- '../../../examples/*/dist/project.json',
73
- {
74
- eager: true,
75
- import: 'default',
76
- },
77
- )
78
-
79
- const projects = Object.entries(loadedModules)
80
- .map(([path, raw]) => {
81
- const projectJson = toProjectJson(path, raw)
82
- if (!projectJson) {
83
- return null
84
- }
85
-
86
- const id = formatProjectId(path)
87
- return {
88
- id,
89
- label: id,
90
- projectJson,
91
- }
92
- })
93
- .filter(
94
- (entry): entry is ExampleProject =>
95
- entry !== null && entry.id.trim().length > 0,
96
- )
97
- .sort((left, right) => left.label.localeCompare(right.label))
98
-
99
- const appElement = document.querySelector<HTMLElement>('#app') ?? document.body
100
-
101
- const controlPanel = document.createElement('div')
102
- controlPanel.className = 'controls'
103
-
104
- const selectLabel = document.createElement('label')
105
- selectLabel.textContent = 'Project'
106
- selectLabel.htmlFor = 'example-project-select'
107
-
108
- const projectSelect = document.createElement('select')
109
- projectSelect.id = 'example-project-select'
110
- projectSelect.setAttribute('aria-label', 'Select a project')
111
-
112
- const status = document.createElement('p')
113
- status.className = 'status'
114
-
115
- const fpsLabel = document.createElement('small')
116
- fpsLabel.style.color = '#facc15'
117
-
118
- const canvas = document.createElement('canvas')
119
- canvas.width = 480
120
- canvas.height = 360
121
- canvas.tabIndex = 0
122
-
123
- const statusContainer = document.createElement('div')
124
- statusContainer.append(status, fpsLabel)
125
-
126
- if (projects.length === 0) {
127
- const heading = document.createElement('h1')
128
- heading.textContent = 'MoonScratch Viewer'
129
- const errorMessage = document.createElement('p')
130
- errorMessage.textContent =
131
- 'examples/*/dist/project.json を検出できませんでした。'
132
- appElement.append(heading, errorMessage)
133
- } else {
134
- projects.forEach((project) => {
135
- const option = document.createElement('option')
136
- option.value = project.id
137
- option.textContent = project.label
138
- projectSelect.append(option)
139
- })
140
-
141
- const heading = document.createElement('h1')
142
- heading.textContent = 'MoonScratch Viewer'
143
- controlPanel.append(selectLabel, projectSelect)
144
- appElement.append(heading, controlPanel, statusContainer, canvas)
145
- }
146
-
147
- document.body.append(appElement)
148
-
149
- const context = canvas.getContext('2d')
150
- if (!context) {
151
- throw new Error('canvas 2D context is unavailable')
152
- }
153
-
154
- let worker: Worker | null = null
155
- let fpsFrames = 0
156
- let fpsStartedAt = 0
157
- let playbackToken = 0
158
- let isPointerDown = false
159
- const keysDown = new Set<string>()
160
- let workerFps = 0
161
- let workerOpsPerSecond = 0
162
-
163
- const postInput = (input: VMInputEvent): void => {
164
- if (!worker) {
165
- return
166
- }
167
- const message: ViewerWorkerRequest = {
168
- type: 'input',
169
- input,
170
- }
171
- worker.postMessage(message)
172
- }
173
-
174
- const normalizeDomKey = (key: string): string => {
175
- if (key === ' ') {
176
- return 'space'
177
- }
178
- return key.toLowerCase()
179
- }
180
-
181
- const syncKeysDown = (): void => {
182
- postInput({
183
- type: 'keys_down',
184
- keys: Array.from(keysDown),
185
- })
186
- }
187
-
188
- const toStageMousePoint = (
189
- event: Pick<MouseEvent, 'clientX' | 'clientY'>,
190
- ): { x: number; y: number } => {
191
- const rect = canvas.getBoundingClientRect()
192
- if (rect.width <= 0 || rect.height <= 0) {
193
- return { x: 0, y: 0 }
194
- }
195
- const offsetX = (event.clientX - rect.left) / rect.width
196
- const offsetY = (event.clientY - rect.top) / rect.height
197
- const rawX = offsetX * canvas.width - canvas.width / 2
198
- const rawY = canvas.height / 2 - offsetY * canvas.height
199
- const x = Math.max(-canvas.width / 2, Math.min(canvas.width / 2, rawX))
200
- const y = Math.max(-canvas.height / 2, Math.min(canvas.height / 2, rawY))
201
- return {
202
- x,
203
- y,
204
- }
205
- }
206
-
207
- const postMouseState = (
208
- event: Pick<MouseEvent, 'clientX' | 'clientY'>,
209
- isDown: boolean,
210
- ): void => {
211
- const point = toStageMousePoint(event)
212
- postInput({
213
- type: 'mouse',
214
- x: point.x,
215
- y: point.y,
216
- isDown,
217
- })
218
- }
219
-
220
- const postMouseTargets = (): void => {
221
- postInput({
222
- type: 'mouse_targets',
223
- stage: true,
224
- targets: [],
225
- })
226
- }
227
-
228
- const updateFps = (now = performance.now()): void => {
229
- if (fpsStartedAt === 0) {
230
- fpsStartedAt = now
231
- }
232
- fpsFrames += 1
233
- const elapsedMs = now - fpsStartedAt
234
- if (elapsedMs >= 500) {
235
- const renderFps = (fpsFrames * 1000) / elapsedMs
236
- fpsLabel.textContent = `${renderFps.toFixed(1)} FPS, worker ${workerFps.toFixed(1)} FPS, ${workerOpsPerSecond.toFixed(0)} ops/s`
237
- fpsFrames = 0
238
- fpsStartedAt = now
239
- }
240
- }
241
-
242
- const renderFrameToCanvas = (frame: RenderFrame): void => {
243
- const { width, height, pixels } = frame
244
- if (width <= 0 || height <= 0) {
245
- status.textContent = '空のフレームです'
246
- return
247
- }
248
-
249
- if (canvas.width !== width || canvas.height !== height) {
250
- canvas.width = width
251
- canvas.height = height
252
- }
253
- const clamped = Uint8ClampedArray.from(pixels)
254
- const imageData = new ImageData(clamped, width, height)
255
- context.putImageData(imageData, 0, 0)
256
- updateFps()
257
- }
258
-
259
- const stopPlayback = (): void => {
260
- if (worker) {
261
- worker.terminate()
262
- worker = null
263
- }
264
- keysDown.clear()
265
- isPointerDown = false
266
- fpsFrames = 0
267
- fpsStartedAt = 0
268
- workerFps = 0
269
- workerOpsPerSecond = 0
270
- }
271
-
272
- const startPlayback = async (projectId: string) => {
273
- const selected = projects.find((project) => project.id === projectId)
274
- if (!selected) {
275
- return
276
- }
277
-
278
- stopPlayback()
279
-
280
- status.textContent = `${selected.label} を起動中...`
281
- fpsLabel.textContent = ''
282
- try {
283
- const token = playbackToken + 1
284
- playbackToken = token
285
- status.textContent = `${selected.label} を再生中`
286
- worker = new VMWorker()
287
- const startMessage: ViewerWorkerRequest = {
288
- type: 'load',
289
- projectJson: selected.projectJson,
290
- }
291
- worker.postMessage(startMessage)
292
- worker.onmessage = (event) => {
293
- if (token !== playbackToken) {
294
- return
295
- }
296
- const payload = event.data as ViewerWorkerResponse
297
- if (payload.type === 'frame') {
298
- workerFps = payload.workerFps
299
- workerOpsPerSecond = payload.workerOpsPerSecond
300
- renderFrameToCanvas(payload.frame)
301
- return
302
- }
303
- if (payload.type === 'finished') {
304
- status.textContent = `${selected.label} の再生が完了`
305
- return
306
- }
307
- if (payload.type === 'warp-exit') {
308
- status.textContent = `${selected.label}: warp-exit (isInWarp=${payload.isInWarp})`
309
- return
310
- }
311
- if (payload.type === 'error') {
312
- status.textContent = `読み込みに失敗しました: ${payload.message}`
313
- stopPlayback()
314
- }
315
- }
316
- worker.onerror = (event) => {
317
- if (token !== playbackToken) {
318
- return
319
- }
320
- status.textContent = `読み込みに失敗しました: ${event.message}`
321
- stopPlayback()
322
- }
323
- } catch (error) {
324
- status.textContent = `読み込みに失敗しました: ${
325
- error instanceof Error ? error.message : String(error)
326
- }`
327
- stopPlayback()
328
- }
329
- }
330
-
331
- projectSelect.addEventListener('change', () => {
332
- startPlayback(projectSelect.value)
333
- })
334
-
335
- canvas.addEventListener('pointerdown', (event) => {
336
- if (!worker) {
337
- return
338
- }
339
- isPointerDown = true
340
- postMouseState(event, true)
341
- postMouseTargets()
342
- canvas.focus()
343
- })
344
-
345
- canvas.addEventListener('pointermove', (event) => {
346
- if (!worker) {
347
- return
348
- }
349
- postMouseState(event, isPointerDown || event.buttons > 0)
350
- postMouseTargets()
351
- })
352
-
353
- canvas.addEventListener('pointerup', (event) => {
354
- if (!worker) {
355
- return
356
- }
357
- isPointerDown = false
358
- postMouseState(event, false)
359
- postMouseTargets()
360
- })
361
-
362
- canvas.addEventListener('pointercancel', () => {
363
- isPointerDown = false
364
- })
365
-
366
- canvas.addEventListener('keydown', (event) => {
367
- if (!worker) {
368
- return
369
- }
370
- const key = normalizeDomKey(event.key)
371
- if (event.repeat || keysDown.has(key)) {
372
- return
373
- }
374
- keysDown.add(key)
375
- syncKeysDown()
376
- })
377
-
378
- canvas.addEventListener('keyup', (event) => {
379
- if (!worker) {
380
- return
381
- }
382
- const key = normalizeDomKey(event.key)
383
- if (!keysDown.delete(key)) {
384
- return
385
- }
386
- syncKeysDown()
387
- })
388
-
389
- canvas.addEventListener('blur', () => {
390
- if (!worker || keysDown.size === 0) {
391
- return
392
- }
393
- keysDown.clear()
394
- syncKeysDown()
395
- })
396
-
397
- if (projects.length > 0) {
398
- startPlayback('rubiks-cube')
399
- }
package/viewer/vite.d.ts DELETED
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />
package/viewer/worker.ts DELETED
@@ -1,161 +0,0 @@
1
- import {
2
- createHeadlessVM,
3
- createProgramModuleFromProject,
4
- type VMInputEvent,
5
- } from '../js'
6
-
7
- const FRAME_FORCE_TIMEOUT_OUT_OF_WARP = 1000 / 30 // 30 FPS
8
- const FRAME_FORCE_TIMEOUT_IN_WARP = 1000 / 5 // 5 FPS
9
- const TICKS_TIMEOUT = 1
10
- const WORKER_METRICS_WINDOW_MS = 1000
11
-
12
- type ViewerWorkerRequest =
13
- | {
14
- type: 'load'
15
- projectJson: string
16
- }
17
- | {
18
- type: 'input'
19
- input: VMInputEvent
20
- }
21
-
22
- let vm: ReturnType<typeof createHeadlessVM> | null = null
23
- let runToken = 0
24
- const pendingInputs: VMInputEvent[] = []
25
-
26
- const flushPendingInputs = (): void => {
27
- if (!vm || pendingInputs.length === 0) {
28
- return
29
- }
30
- for (const input of pendingInputs) {
31
- vm.dispatchInputEvent(input)
32
- }
33
- pendingInputs.length = 0
34
- }
35
-
36
- const waitForNextFrame = async (): Promise<void> => {
37
- await new Promise<void>((resolve) => {
38
- if (typeof requestAnimationFrame === 'function') {
39
- requestAnimationFrame(() => resolve())
40
- return
41
- }
42
- setTimeout(() => resolve(), FRAME_FORCE_TIMEOUT_OUT_OF_WARP)
43
- })
44
- }
45
-
46
- const playbackLoop = async (token: number): Promise<void> => {
47
- let workerFpsFrames = 0
48
- let workerOps = 0
49
- let metricsStartedAt = 0
50
- let currentWorkerFps = 0
51
- let currentWorkerOpsPerSecond = 0
52
- while (true) {
53
- if (!vm || token !== runToken) {
54
- return
55
- }
56
- flushPendingInputs()
57
-
58
- const frameStart = performance.now()
59
- vm.setTime(frameStart)
60
- let frameOps = 0
61
- let shouldRender = false
62
- let isFinished = false
63
- while (true) {
64
- const frameInfo = vm.stepFrame()
65
- frameOps += frameInfo.ops
66
- if (frameInfo.stopReason === 'finished') {
67
- shouldRender = frameInfo.shouldRender
68
- isFinished = true
69
- break
70
- }
71
- if (frameInfo.shouldRender) {
72
- shouldRender = true
73
- break
74
- }
75
-
76
- const frameBudget = frameInfo.isInWarp
77
- ? FRAME_FORCE_TIMEOUT_IN_WARP
78
- : FRAME_FORCE_TIMEOUT_OUT_OF_WARP
79
- if (performance.now() - frameStart > frameBudget) {
80
- break
81
- }
82
- }
83
-
84
- workerFpsFrames += 1
85
- workerOps += frameOps
86
- const now = performance.now()
87
- if (metricsStartedAt === 0) {
88
- metricsStartedAt = now
89
- } else {
90
- const elapsedMs = now - metricsStartedAt
91
- if (elapsedMs >= WORKER_METRICS_WINDOW_MS) {
92
- currentWorkerFps = (workerFpsFrames * 1000) / elapsedMs
93
- currentWorkerOpsPerSecond = (workerOps * 1000) / elapsedMs
94
- workerFpsFrames = 0
95
- workerOps = 0
96
- metricsStartedAt = now
97
- }
98
- }
99
-
100
- if (shouldRender) {
101
- const frame = vm.renderFrame()
102
- postMessage({
103
- type: 'frame',
104
- frame,
105
- workerFps: currentWorkerFps,
106
- workerOpsPerSecond: currentWorkerOpsPerSecond,
107
- })
108
- }
109
-
110
- if (isFinished) {
111
- postMessage({ type: 'finished' })
112
- return
113
- }
114
-
115
- await waitForNextFrame()
116
- }
117
- }
118
-
119
- const toErrorMessage = (error: unknown): string =>
120
- error instanceof Error ? error.message : String(error)
121
-
122
- globalThis.onmessage = (event) => {
123
- const data = event.data as ViewerWorkerRequest
124
- if (!data || typeof data !== 'object') {
125
- return
126
- }
127
- if (data.type === 'input') {
128
- if (!vm) {
129
- return
130
- }
131
- pendingInputs.push(data.input)
132
- return
133
- }
134
- if (data.type !== 'load') {
135
- return
136
- }
137
-
138
- pendingInputs.length = 0
139
- runToken += 1
140
- const token = runToken
141
- try {
142
- const program = createProgramModuleFromProject({
143
- projectJson: data.projectJson,
144
- })
145
- vm = createHeadlessVM({
146
- program,
147
- options: {
148
- stepTimeoutTicks: TICKS_TIMEOUT,
149
- },
150
- })
151
- vm.start()
152
- vm.greenFlag()
153
- void playbackLoop(token)
154
- } catch (error) {
155
- vm = null
156
- postMessage({
157
- type: 'error',
158
- message: toErrorMessage(error),
159
- })
160
- }
161
- }
package/vite.config.ts DELETED
@@ -1,11 +0,0 @@
1
- import { defineConfig } from 'vite-plus'
2
-
3
- export default defineConfig({
4
- pack: {
5
- entry: 'js/index.ts',
6
- dts: true,
7
- },
8
- test: {
9
- include: ['./js/**/*.test.ts'],
10
- },
11
- })