@al8b/time 0.1.0

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 (68) hide show
  1. package/README.md +23 -0
  2. package/dist/constants.d.mts +6 -0
  3. package/dist/constants.d.ts +6 -0
  4. package/dist/constants.js +34 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/constants.mjs +8 -0
  7. package/dist/constants.mjs.map +1 -0
  8. package/dist/core/index.d.mts +2 -0
  9. package/dist/core/index.d.ts +2 -0
  10. package/dist/core/index.js +522 -0
  11. package/dist/core/index.js.map +1 -0
  12. package/dist/core/index.mjs +497 -0
  13. package/dist/core/index.mjs.map +1 -0
  14. package/dist/core/machine.d.mts +101 -0
  15. package/dist/core/machine.d.ts +101 -0
  16. package/dist/core/machine.js +520 -0
  17. package/dist/core/machine.js.map +1 -0
  18. package/dist/core/machine.mjs +497 -0
  19. package/dist/core/machine.mjs.map +1 -0
  20. package/dist/index.d.mts +4 -0
  21. package/dist/index.d.ts +4 -0
  22. package/dist/index.js +526 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/index.mjs +499 -0
  25. package/dist/index.mjs.map +1 -0
  26. package/dist/playback/index.d.mts +2 -0
  27. package/dist/playback/index.d.ts +2 -0
  28. package/dist/playback/index.js +172 -0
  29. package/dist/playback/index.js.map +1 -0
  30. package/dist/playback/index.mjs +147 -0
  31. package/dist/playback/index.mjs.map +1 -0
  32. package/dist/playback/player.d.mts +41 -0
  33. package/dist/playback/player.d.ts +41 -0
  34. package/dist/playback/player.js +172 -0
  35. package/dist/playback/player.js.map +1 -0
  36. package/dist/playback/player.mjs +147 -0
  37. package/dist/playback/player.mjs.map +1 -0
  38. package/dist/recording/index.d.mts +2 -0
  39. package/dist/recording/index.d.ts +2 -0
  40. package/dist/recording/index.js +149 -0
  41. package/dist/recording/index.js.map +1 -0
  42. package/dist/recording/index.mjs +124 -0
  43. package/dist/recording/index.mjs.map +1 -0
  44. package/dist/recording/recorder.d.mts +41 -0
  45. package/dist/recording/recorder.d.ts +41 -0
  46. package/dist/recording/recorder.js +149 -0
  47. package/dist/recording/recorder.js.map +1 -0
  48. package/dist/recording/recorder.mjs +124 -0
  49. package/dist/recording/recorder.mjs.map +1 -0
  50. package/dist/types/index.d.mts +1 -0
  51. package/dist/types/index.d.ts +1 -0
  52. package/dist/types/index.js +19 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/types/index.mjs +1 -0
  55. package/dist/types/index.mjs.map +1 -0
  56. package/dist/types/state.d.mts +33 -0
  57. package/dist/types/state.d.ts +33 -0
  58. package/dist/types/state.js +19 -0
  59. package/dist/types/state.js.map +1 -0
  60. package/dist/types/state.mjs +1 -0
  61. package/dist/types/state.mjs.map +1 -0
  62. package/dist/utils.d.mts +9 -0
  63. package/dist/utils.d.ts +9 -0
  64. package/dist/utils.js +60 -0
  65. package/dist/utils.js.map +1 -0
  66. package/dist/utils.mjs +37 -0
  67. package/dist/utils.mjs.map +1 -0
  68. package/package.json +37 -0
@@ -0,0 +1,101 @@
1
+ import { TimeMachineMessage, TimeMachineStatus } from '../types/state.js';
2
+
3
+ /**
4
+ * TimeMachine - Main time machine controller
5
+ *
6
+ * Responsibilities:
7
+ * - Coordinate recording and playback
8
+ * - Handle commands
9
+ * - Manage state
10
+ */
11
+
12
+ interface TimeMachineRuntime {
13
+ vm?: {
14
+ context?: {
15
+ global?: any;
16
+ };
17
+ };
18
+ updateCall?: () => void;
19
+ drawCall?: () => void;
20
+ }
21
+ declare class TimeMachine {
22
+ private runtime;
23
+ private recorder;
24
+ private player;
25
+ private recording;
26
+ private replayPosition;
27
+ private statusCallback?;
28
+ constructor(runtime: TimeMachineRuntime);
29
+ /**
30
+ * Setup objects to exclude from recording
31
+ */
32
+ private setupExclusions;
33
+ /**
34
+ * Step function - call each frame
35
+ */
36
+ step(): void;
37
+ /**
38
+ * Handle incoming messages
39
+ */
40
+ messageReceived(data: TimeMachineMessage): void;
41
+ /**
42
+ * Start recording
43
+ */
44
+ private startRecording;
45
+ /**
46
+ * Stop recording
47
+ */
48
+ private stopRecording;
49
+ /**
50
+ * Step backward in time
51
+ */
52
+ private stepBackward;
53
+ /**
54
+ * Step forward in time
55
+ */
56
+ private stepForward;
57
+ /**
58
+ * Set replay position
59
+ */
60
+ private setReplayPosition;
61
+ /**
62
+ * Start looping
63
+ */
64
+ private startLooping;
65
+ /**
66
+ * Stop looping
67
+ */
68
+ private stopLooping;
69
+ /**
70
+ * Replay state at current position
71
+ */
72
+ private replay;
73
+ /**
74
+ * Loop callback
75
+ */
76
+ private loopCallback;
77
+ /**
78
+ * Send status update
79
+ */
80
+ private sendStatus;
81
+ /**
82
+ * Advance the loop playback by one tick.
83
+ * Call once per real game frame (from the orchestrator's watch-step hook).
84
+ * No-op when not looping.
85
+ */
86
+ loopStep(): void;
87
+ /**
88
+ * Set status callback
89
+ */
90
+ onStatus(callback: (status: TimeMachineStatus) => void): void;
91
+ /**
92
+ * Check if recording
93
+ */
94
+ isRecording(): boolean;
95
+ /**
96
+ * Check if looping
97
+ */
98
+ isLooping(): boolean;
99
+ }
100
+
101
+ export { TimeMachine, type TimeMachineRuntime };
@@ -0,0 +1,520 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/core/machine.ts
22
+ var machine_exports = {};
23
+ __export(machine_exports, {
24
+ TimeMachine: () => TimeMachine
25
+ });
26
+ module.exports = __toCommonJS(machine_exports);
27
+ var import_diagnostics = require("@al8b/diagnostics");
28
+
29
+ // src/constants.ts
30
+ var DEFAULT_RECORD_BUFFER_FRAMES = 60 * 30;
31
+ var DEFAULT_LOOP_BUFFER_FRAMES = 60 * 4;
32
+
33
+ // src/utils.ts
34
+ function deepCopy(value, excluded) {
35
+ if (value == null) {
36
+ return value;
37
+ }
38
+ if (excluded && excluded.includes(value)) {
39
+ return null;
40
+ }
41
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
42
+ return value;
43
+ }
44
+ if (Array.isArray(value)) {
45
+ const result = [];
46
+ for (let i = 0; i < value.length; i++) {
47
+ result[i] = deepCopy(value[i], excluded);
48
+ }
49
+ return result;
50
+ }
51
+ if (typeof value === "object") {
52
+ const result = {};
53
+ for (const key in value) {
54
+ if (Object.hasOwn(value, key)) {
55
+ result[key] = deepCopy(value[key], excluded);
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ return excluded ? null : value;
61
+ }
62
+ __name(deepCopy, "deepCopy");
63
+
64
+ // src/playback/player.ts
65
+ var StatePlayer = class {
66
+ static {
67
+ __name(this, "StatePlayer");
68
+ }
69
+ looping = false;
70
+ loopStart = 0;
71
+ loopIndex = 0;
72
+ loopLength;
73
+ loopCallback = null;
74
+ constructor(loopLength = DEFAULT_LOOP_BUFFER_FRAMES) {
75
+ this.loopLength = loopLength;
76
+ }
77
+ /**
78
+ * Check if currently looping
79
+ */
80
+ isLooping() {
81
+ return this.looping;
82
+ }
83
+ /**
84
+ * Start loop playback
85
+ */
86
+ startLoop(position, callback) {
87
+ this.looping = true;
88
+ this.loopStart = Math.max(position, 1);
89
+ this.loopIndex = 0;
90
+ this.loopCallback = callback;
91
+ }
92
+ /**
93
+ * Stop loop playback
94
+ */
95
+ stopLoop() {
96
+ this.looping = false;
97
+ this.loopCallback = null;
98
+ return this.loopStart;
99
+ }
100
+ /**
101
+ * Update loop (call each frame)
102
+ * Returns position to replay, or null if not looping
103
+ */
104
+ updateLoop() {
105
+ if (!this.looping) {
106
+ return null;
107
+ }
108
+ if (this.loopIndex === 0) {
109
+ this.loopIndex++;
110
+ return this.loopStart;
111
+ }
112
+ this.loopIndex++;
113
+ if (this.loopIndex > this.loopLength) {
114
+ this.loopIndex = 0;
115
+ }
116
+ return this.loopStart - this.loopIndex;
117
+ }
118
+ /**
119
+ * Execute loop callback
120
+ */
121
+ executeCallback() {
122
+ if (this.loopCallback) {
123
+ this.loopCallback();
124
+ }
125
+ }
126
+ /**
127
+ * Restore state to target object
128
+ */
129
+ restoreState(target, snapshot) {
130
+ if (!snapshot || !target) {
131
+ return;
132
+ }
133
+ for (const key in target) {
134
+ if (Object.hasOwn(target, key) && !this.isProtectedKey(key)) {
135
+ delete target[key];
136
+ }
137
+ }
138
+ for (const key in snapshot) {
139
+ if (Object.hasOwn(snapshot, key)) {
140
+ target[key] = deepCopy(snapshot[key]);
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Check if key should be protected from restoration
146
+ */
147
+ isProtectedKey(key) {
148
+ const protected_keys = [
149
+ "screen",
150
+ "audio",
151
+ "keyboard",
152
+ "mouse",
153
+ "touch",
154
+ "gamepad",
155
+ "system",
156
+ "storage",
157
+ "sprites",
158
+ "maps",
159
+ "sounds",
160
+ "music",
161
+ "assets",
162
+ "host",
163
+ "session",
164
+ "memory"
165
+ ];
166
+ return protected_keys.includes(key);
167
+ }
168
+ };
169
+
170
+ // src/recording/recorder.ts
171
+ var StateRecorder = class {
172
+ static {
173
+ __name(this, "StateRecorder");
174
+ }
175
+ history = [];
176
+ recordIndex = 0;
177
+ recordLength = 0;
178
+ maxLength;
179
+ excluded = [];
180
+ constructor(maxLength = DEFAULT_RECORD_BUFFER_FRAMES) {
181
+ this.maxLength = maxLength;
182
+ }
183
+ /**
184
+ * Set objects to exclude from serialization
185
+ */
186
+ setExcluded(excluded) {
187
+ this.excluded = excluded;
188
+ }
189
+ /**
190
+ * Record a state snapshot
191
+ */
192
+ record(state) {
193
+ const snapshot = this.makeStorableState(state);
194
+ this.history[this.recordIndex++] = snapshot;
195
+ this.recordLength = Math.min(this.recordLength + 1, this.maxLength);
196
+ if (this.recordIndex >= this.maxLength) {
197
+ this.recordIndex = 0;
198
+ }
199
+ }
200
+ /**
201
+ * Get state at specific position (0 = most recent)
202
+ */
203
+ getState(position) {
204
+ if (position >= this.recordLength) {
205
+ return null;
206
+ }
207
+ const index = (this.recordIndex - position - 1 + this.maxLength) % this.maxLength;
208
+ return this.history[index];
209
+ }
210
+ /**
211
+ * Get current record length
212
+ */
213
+ getLength() {
214
+ return this.recordLength;
215
+ }
216
+ /**
217
+ * Get maximum record length
218
+ */
219
+ getMaxLength() {
220
+ return this.maxLength;
221
+ }
222
+ /**
223
+ * Clear all recorded history
224
+ */
225
+ clear() {
226
+ this.history = [];
227
+ this.recordIndex = 0;
228
+ this.recordLength = 0;
229
+ }
230
+ /**
231
+ * Trim history to specific position
232
+ */
233
+ trimTo(position) {
234
+ if (position >= this.recordLength) {
235
+ return;
236
+ }
237
+ const histo = [];
238
+ const start = this.recordLength;
239
+ const end = position + 1;
240
+ for (let i = start; i >= end; i--) {
241
+ const index = (this.recordIndex - i + this.maxLength) % this.maxLength;
242
+ histo.push(this.history[index]);
243
+ }
244
+ this.history = histo;
245
+ this.recordIndex = this.history.length;
246
+ this.recordLength = this.history.length;
247
+ }
248
+ makeStorableState(value) {
249
+ return deepCopy(value, this.excluded);
250
+ }
251
+ };
252
+
253
+ // src/core/machine.ts
254
+ var TimeMachine = class {
255
+ static {
256
+ __name(this, "TimeMachine");
257
+ }
258
+ runtime;
259
+ recorder;
260
+ player;
261
+ recording = false;
262
+ replayPosition = 0;
263
+ statusCallback;
264
+ constructor(runtime) {
265
+ this.runtime = runtime;
266
+ this.recorder = new StateRecorder(DEFAULT_RECORD_BUFFER_FRAMES);
267
+ this.player = new StatePlayer(DEFAULT_LOOP_BUFFER_FRAMES);
268
+ this.setupExclusions();
269
+ }
270
+ /**
271
+ * Setup objects to exclude from recording
272
+ */
273
+ setupExclusions() {
274
+ const excluded = [];
275
+ if (this.runtime.vm?.context?.global) {
276
+ const global = this.runtime.vm.context.global;
277
+ if (global.random) excluded.push(global.random);
278
+ if (global.screen) excluded.push(global.screen);
279
+ if (global.audio) excluded.push(global.audio);
280
+ if (global.keyboard) excluded.push(global.keyboard);
281
+ if (global.mouse) excluded.push(global.mouse);
282
+ if (global.touch) excluded.push(global.touch);
283
+ if (global.gamepad) excluded.push(global.gamepad);
284
+ if (global.system) excluded.push(global.system);
285
+ if (global.storage) excluded.push(global.storage);
286
+ if (global.host) excluded.push(global.host);
287
+ if (global.session) excluded.push(global.session);
288
+ if (global.memory) excluded.push(global.memory);
289
+ }
290
+ this.recorder.setExcluded(excluded);
291
+ }
292
+ /**
293
+ * Step function - call each frame
294
+ */
295
+ step() {
296
+ if (!this.recording) {
297
+ return;
298
+ }
299
+ try {
300
+ if (this.replayPosition !== 0) {
301
+ this.recorder.trimTo(this.replayPosition);
302
+ if (this.player.isLooping()) {
303
+ this.player.startLoop(this.recorder.getLength(), () => this.loopCallback());
304
+ }
305
+ this.replayPosition = 0;
306
+ }
307
+ if (this.runtime.vm?.context?.global) {
308
+ this.recorder.record(this.runtime.vm.context.global);
309
+ }
310
+ this.sendStatus();
311
+ } catch (err) {
312
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7082, {
313
+ error: String(err)
314
+ });
315
+ }
316
+ }
317
+ /**
318
+ * Handle incoming messages
319
+ */
320
+ messageReceived(data) {
321
+ switch (data.command) {
322
+ case "start_recording":
323
+ this.startRecording();
324
+ break;
325
+ case "stop_recording":
326
+ this.stopRecording();
327
+ break;
328
+ case "step_backward":
329
+ this.stepBackward();
330
+ break;
331
+ case "step_forward":
332
+ this.stepForward();
333
+ break;
334
+ case "replay_position":
335
+ if (data.position != null) {
336
+ this.setReplayPosition(data.position);
337
+ }
338
+ break;
339
+ case "start_looping":
340
+ this.startLooping();
341
+ break;
342
+ case "stop_looping":
343
+ this.stopLooping();
344
+ break;
345
+ }
346
+ }
347
+ /**
348
+ * Start recording
349
+ */
350
+ startRecording() {
351
+ if (this.recording) {
352
+ return;
353
+ }
354
+ try {
355
+ this.recording = true;
356
+ this.recorder.clear();
357
+ this.replayPosition = 0;
358
+ this.sendStatus();
359
+ } catch (err) {
360
+ this.recording = false;
361
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7083, {
362
+ error: String(err)
363
+ });
364
+ }
365
+ }
366
+ /**
367
+ * Stop recording
368
+ */
369
+ stopRecording() {
370
+ if (!this.recording) {
371
+ return;
372
+ }
373
+ try {
374
+ this.recording = false;
375
+ this.sendStatus();
376
+ } catch (err) {
377
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7083, {
378
+ error: String(err)
379
+ });
380
+ }
381
+ }
382
+ /**
383
+ * Step backward in time
384
+ */
385
+ stepBackward() {
386
+ if (this.replayPosition + 1 >= this.recorder.getLength()) {
387
+ return;
388
+ }
389
+ this.stopLooping();
390
+ this.replayPosition++;
391
+ this.replay();
392
+ this.sendStatus();
393
+ }
394
+ /**
395
+ * Step forward in time
396
+ */
397
+ stepForward() {
398
+ if (this.replayPosition <= 1) {
399
+ return;
400
+ }
401
+ this.stopLooping();
402
+ this.replayPosition--;
403
+ this.replay();
404
+ this.sendStatus();
405
+ }
406
+ /**
407
+ * Set replay position
408
+ */
409
+ setReplayPosition(position) {
410
+ if (!isFinite(position) || position < 0) {
411
+ (0, import_diagnostics.reportRuntimeError)(this.runtime?.listener, import_diagnostics.APIErrorCode.E7081, {
412
+ value: String(position)
413
+ });
414
+ return;
415
+ }
416
+ const pos = Math.round(position);
417
+ this.replayPosition = Math.max(2, Math.min(this.recorder.getLength() - 1, pos));
418
+ if (this.player.isLooping()) {
419
+ this.player.startLoop(this.replayPosition, () => this.loopCallback());
420
+ }
421
+ this.replay();
422
+ this.sendStatus();
423
+ }
424
+ /**
425
+ * Start looping
426
+ */
427
+ startLooping() {
428
+ if (this.recorder.getLength() === 0) {
429
+ return;
430
+ }
431
+ this.recording = false;
432
+ const position = Math.max(this.replayPosition, 1);
433
+ this.player.startLoop(position, () => this.loopCallback());
434
+ }
435
+ /**
436
+ * Stop looping
437
+ */
438
+ stopLooping() {
439
+ if (!this.player.isLooping()) {
440
+ return;
441
+ }
442
+ this.replayPosition = this.player.stopLoop();
443
+ this.sendStatus();
444
+ }
445
+ /**
446
+ * Replay state at current position
447
+ */
448
+ replay(skipDraw = false) {
449
+ if (this.replayPosition >= this.recorder.getLength()) {
450
+ return;
451
+ }
452
+ const snapshot = this.recorder.getState(this.replayPosition);
453
+ if (snapshot && this.runtime.vm?.context?.global) {
454
+ this.player.restoreState(this.runtime.vm.context.global, snapshot);
455
+ }
456
+ if (!skipDraw) {
457
+ this.runtime.drawCall?.();
458
+ }
459
+ }
460
+ /**
461
+ * Loop callback
462
+ */
463
+ loopCallback() {
464
+ const position = this.player.updateLoop();
465
+ if (position !== null) {
466
+ this.replayPosition = position;
467
+ this.replay(true);
468
+ this.runtime.updateCall?.();
469
+ this.runtime.drawCall?.();
470
+ }
471
+ this.sendStatus();
472
+ }
473
+ /**
474
+ * Send status update
475
+ */
476
+ sendStatus() {
477
+ if (this.statusCallback) {
478
+ this.statusCallback({
479
+ recording: this.recording,
480
+ looping: this.player.isLooping(),
481
+ position: this.replayPosition,
482
+ length: this.recorder.getLength(),
483
+ max: this.recorder.getMaxLength()
484
+ });
485
+ }
486
+ }
487
+ /**
488
+ * Advance the loop playback by one tick.
489
+ * Call once per real game frame (from the orchestrator's watch-step hook).
490
+ * No-op when not looping.
491
+ */
492
+ loopStep() {
493
+ if (this.player.isLooping()) {
494
+ this.player.executeCallback();
495
+ }
496
+ }
497
+ /**
498
+ * Set status callback
499
+ */
500
+ onStatus(callback) {
501
+ this.statusCallback = callback;
502
+ }
503
+ /**
504
+ * Check if recording
505
+ */
506
+ isRecording() {
507
+ return this.recording;
508
+ }
509
+ /**
510
+ * Check if looping
511
+ */
512
+ isLooping() {
513
+ return this.player.isLooping();
514
+ }
515
+ };
516
+ // Annotate the CommonJS export names for ESM import in node:
517
+ 0 && (module.exports = {
518
+ TimeMachine
519
+ });
520
+ //# sourceMappingURL=machine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/machine.ts","../../src/constants.ts","../../src/utils.ts","../../src/playback/player.ts","../../src/recording/recorder.ts"],"sourcesContent":["/**\n * TimeMachine - Main time machine controller\n *\n * Responsibilities:\n * - Coordinate recording and playback\n * - Handle commands\n * - Manage state\n */\n\nimport { APIErrorCode, reportRuntimeError } from \"@al8b/diagnostics\";\nimport { DEFAULT_LOOP_BUFFER_FRAMES, DEFAULT_RECORD_BUFFER_FRAMES } from \"../constants\";\nimport { StatePlayer } from \"../playback\";\nimport { StateRecorder } from \"../recording\";\nimport type { TimeMachineMessage, TimeMachineStatus } from \"../types\";\n\nexport interface TimeMachineRuntime {\n\tvm?: {\n\t\tcontext?: {\n\t\t\tglobal?: any;\n\t\t};\n\t};\n\tupdateCall?: () => void;\n\tdrawCall?: () => void;\n}\n\nexport class TimeMachine {\n\tprivate runtime: TimeMachineRuntime;\n\tprivate recorder: StateRecorder;\n\tprivate player: StatePlayer;\n\tprivate recording = false;\n\tprivate replayPosition = 0;\n\tprivate statusCallback?: (status: TimeMachineStatus) => void;\n\n\tconstructor(runtime: TimeMachineRuntime) {\n\t\tthis.runtime = runtime;\n\t\tthis.recorder = new StateRecorder(DEFAULT_RECORD_BUFFER_FRAMES);\n\t\tthis.player = new StatePlayer(DEFAULT_LOOP_BUFFER_FRAMES);\n\n\t\t// Configure which objects should be excluded from state recording\n\t\tthis.setupExclusions();\n\t}\n\n\t/**\n\t * Setup objects to exclude from recording\n\t */\n\tprivate setupExclusions(): void {\n\t\tconst excluded: any[] = [];\n\n\t\tif (this.runtime.vm?.context?.global) {\n\t\t\tconst global = this.runtime.vm.context.global;\n\n\t\t\t// Exclude system APIs and non-serializable objects from recording\n\t\t\tif (global.random) excluded.push(global.random);\n\t\t\tif (global.screen) excluded.push(global.screen);\n\t\t\tif (global.audio) excluded.push(global.audio);\n\t\t\tif (global.keyboard) excluded.push(global.keyboard);\n\t\t\tif (global.mouse) excluded.push(global.mouse);\n\t\t\tif (global.touch) excluded.push(global.touch);\n\t\t\tif (global.gamepad) excluded.push(global.gamepad);\n\t\t\tif (global.system) excluded.push(global.system);\n\t\t\tif (global.storage) excluded.push(global.storage);\n\t\t\tif (global.host) excluded.push(global.host);\n\t\t\tif (global.session) excluded.push(global.session);\n\t\t\tif (global.memory) excluded.push(global.memory);\n\t\t}\n\n\t\tthis.recorder.setExcluded(excluded);\n\t}\n\n\t/**\n\t * Step function - call each frame\n\t */\n\tstep(): void {\n\t\tif (!this.recording) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\t// Apply replay position changes and update loop if active\n\t\t\tif (this.replayPosition !== 0) {\n\t\t\t\tthis.recorder.trimTo(this.replayPosition);\n\t\t\t\tif (this.player.isLooping()) {\n\t\t\t\t\tthis.player.startLoop(this.recorder.getLength(), () => this.loopCallback());\n\t\t\t\t}\n\t\t\t\tthis.replayPosition = 0;\n\t\t\t}\n\n\t\t\t// Capture current global state snapshot for time travel\n\t\t\tif (this.runtime.vm?.context?.global) {\n\t\t\t\tthis.recorder.record(this.runtime.vm.context.global);\n\t\t\t}\n\n\t\t\tthis.sendStatus();\n\t\t} catch (err) {\n\t\t\treportRuntimeError((this.runtime as any)?.listener, APIErrorCode.E7082, { error: String(err) });\n\t\t}\n\t}\n\n\t/**\n\t * Handle incoming messages\n\t */\n\tmessageReceived(data: TimeMachineMessage): void {\n\t\tswitch (data.command) {\n\t\t\tcase \"start_recording\":\n\t\t\t\tthis.startRecording();\n\t\t\t\tbreak;\n\n\t\t\tcase \"stop_recording\":\n\t\t\t\tthis.stopRecording();\n\t\t\t\tbreak;\n\n\t\t\tcase \"step_backward\":\n\t\t\t\tthis.stepBackward();\n\t\t\t\tbreak;\n\n\t\t\tcase \"step_forward\":\n\t\t\t\tthis.stepForward();\n\t\t\t\tbreak;\n\n\t\t\tcase \"replay_position\":\n\t\t\t\tif (data.position != null) {\n\t\t\t\t\tthis.setReplayPosition(data.position);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"start_looping\":\n\t\t\t\tthis.startLooping();\n\t\t\t\tbreak;\n\n\t\t\tcase \"stop_looping\":\n\t\t\t\tthis.stopLooping();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Start recording\n\t */\n\tprivate startRecording(): void {\n\t\tif (this.recording) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.recording = true;\n\t\t\tthis.recorder.clear();\n\t\t\tthis.replayPosition = 0;\n\t\t\tthis.sendStatus();\n\t\t} catch (err) {\n\t\t\tthis.recording = false;\n\t\t\treportRuntimeError((this.runtime as any)?.listener, APIErrorCode.E7083, { error: String(err) });\n\t\t}\n\t}\n\n\t/**\n\t * Stop recording\n\t */\n\tprivate stopRecording(): void {\n\t\tif (!this.recording) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.recording = false;\n\t\t\tthis.sendStatus();\n\t\t} catch (err) {\n\t\t\treportRuntimeError((this.runtime as any)?.listener, APIErrorCode.E7083, { error: String(err) });\n\t\t}\n\t}\n\n\t/**\n\t * Step backward in time\n\t */\n\tprivate stepBackward(): void {\n\t\tif (this.replayPosition + 1 >= this.recorder.getLength()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.stopLooping();\n\t\tthis.replayPosition++;\n\t\tthis.replay();\n\t\tthis.sendStatus();\n\t}\n\n\t/**\n\t * Step forward in time\n\t */\n\tprivate stepForward(): void {\n\t\tif (this.replayPosition <= 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.stopLooping();\n\t\tthis.replayPosition--;\n\t\tthis.replay();\n\t\tthis.sendStatus();\n\t}\n\n\t/**\n\t * Set replay position\n\t */\n\tprivate setReplayPosition(position: number): void {\n\t\t// Validate time value is finite and non-negative\n\t\tif (!isFinite(position) || position < 0) {\n\t\t\treportRuntimeError((this.runtime as any)?.listener, APIErrorCode.E7081, { value: String(position) });\n\t\t\treturn;\n\t\t}\n\n\t\tconst pos = Math.round(position);\n\t\tthis.replayPosition = Math.max(2, Math.min(this.recorder.getLength() - 1, pos));\n\n\t\tif (this.player.isLooping()) {\n\t\t\tthis.player.startLoop(this.replayPosition, () => this.loopCallback());\n\t\t}\n\n\t\tthis.replay();\n\t\tthis.sendStatus();\n\t}\n\n\t/**\n\t * Start looping\n\t */\n\tprivate startLooping(): void {\n\t\tif (this.recorder.getLength() === 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.recording = false;\n\t\tconst position = Math.max(this.replayPosition, 1);\n\t\tthis.player.startLoop(position, () => this.loopCallback());\n\t}\n\n\t/**\n\t * Stop looping\n\t */\n\tprivate stopLooping(): void {\n\t\tif (!this.player.isLooping()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis.replayPosition = this.player.stopLoop();\n\t\tthis.sendStatus();\n\t}\n\n\t/**\n\t * Replay state at current position\n\t */\n\tprivate replay(skipDraw = false): void {\n\t\tif (this.replayPosition >= this.recorder.getLength()) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst snapshot = this.recorder.getState(this.replayPosition);\n\t\tif (snapshot && this.runtime.vm?.context?.global) {\n\t\t\tthis.player.restoreState(this.runtime.vm.context.global, snapshot);\n\t\t}\n\n\t\tif (!skipDraw) {\n\t\t\tthis.runtime.drawCall?.();\n\t\t}\n\t}\n\n\t/**\n\t * Loop callback\n\t */\n\tprivate loopCallback(): void {\n\t\tconst position = this.player.updateLoop();\n\t\tif (position !== null) {\n\t\t\tthis.replayPosition = position;\n\t\t\tthis.replay(true);\n\t\t\tthis.runtime.updateCall?.();\n\t\t\tthis.runtime.drawCall?.();\n\t\t}\n\t\tthis.sendStatus();\n\t}\n\n\t/**\n\t * Send status update\n\t */\n\tprivate sendStatus(): void {\n\t\tif (this.statusCallback) {\n\t\t\tthis.statusCallback({\n\t\t\t\trecording: this.recording,\n\t\t\t\tlooping: this.player.isLooping(),\n\t\t\t\tposition: this.replayPosition,\n\t\t\t\tlength: this.recorder.getLength(),\n\t\t\t\tmax: this.recorder.getMaxLength(),\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Advance the loop playback by one tick.\n\t * Call once per real game frame (from the orchestrator's watch-step hook).\n\t * No-op when not looping.\n\t */\n\tloopStep(): void {\n\t\tif (this.player.isLooping()) {\n\t\t\tthis.player.executeCallback();\n\t\t}\n\t}\n\n\t/**\n\t * Set status callback\n\t */\n\tonStatus(callback: (status: TimeMachineStatus) => void): void {\n\t\tthis.statusCallback = callback;\n\t}\n\n\t/**\n\t * Check if recording\n\t */\n\tisRecording(): boolean {\n\t\treturn this.recording;\n\t}\n\n\t/**\n\t * Check if looping\n\t */\n\tisLooping(): boolean {\n\t\treturn this.player.isLooping();\n\t}\n}\n","/** Default recording buffer: 30 seconds at 60fps (1800 frames) */\nexport const DEFAULT_RECORD_BUFFER_FRAMES = 60 * 30;\n\n/** Default loop playback buffer: 4 seconds at 60fps (240 frames) */\nexport const DEFAULT_LOOP_BUFFER_FRAMES = 60 * 4;\n","/**\n * Deep copy a value, optionally skipping excluded references.\n *\n * @param value - The value to deep copy\n * @param excluded - Optional array of object references to skip (replaced with null)\n */\nexport function deepCopy(value: any, excluded?: any[]): any {\n\tif (value == null) {\n\t\treturn value;\n\t}\n\n\tif (excluded && excluded.includes(value)) {\n\t\treturn null;\n\t}\n\n\tif (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n\t\treturn value;\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tconst result: any[] = [];\n\t\tfor (let i = 0; i < value.length; i++) {\n\t\t\tresult[i] = deepCopy(value[i], excluded);\n\t\t}\n\t\treturn result;\n\t}\n\n\tif (typeof value === \"object\") {\n\t\tconst result: any = {};\n\t\tfor (const key in value) {\n\t\t\tif (Object.hasOwn(value, key)) {\n\t\t\t\tresult[key] = deepCopy(value[key], excluded);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t// Non-serializable types: return null when filtering, passthrough otherwise\n\treturn excluded ? null : value;\n}\n","/**\n * StatePlayer - Handles replay and loop playback\n *\n * Responsibilities:\n * - Restore state snapshots\n * - Manage loop playback\n * - Control playback position\n */\nimport { DEFAULT_LOOP_BUFFER_FRAMES } from \"../constants\";\nimport { deepCopy } from \"../utils\";\n\nimport type { StateSnapshot } from \"../types\";\n\nexport class StatePlayer {\n\tprivate looping = false;\n\tprivate loopStart = 0;\n\tprivate loopIndex = 0;\n\tprivate loopLength: number;\n\tprivate loopCallback: (() => void) | null = null;\n\n\tconstructor(loopLength = DEFAULT_LOOP_BUFFER_FRAMES) {\n\t\tthis.loopLength = loopLength;\n\t}\n\n\t/**\n\t * Check if currently looping\n\t */\n\tisLooping(): boolean {\n\t\treturn this.looping;\n\t}\n\n\t/**\n\t * Start loop playback\n\t */\n\tstartLoop(position: number, callback: () => void): void {\n\t\tthis.looping = true;\n\t\tthis.loopStart = Math.max(position, 1);\n\t\tthis.loopIndex = 0;\n\t\tthis.loopCallback = callback;\n\t}\n\n\t/**\n\t * Stop loop playback\n\t */\n\tstopLoop(): number {\n\t\tthis.looping = false;\n\t\tthis.loopCallback = null;\n\t\treturn this.loopStart;\n\t}\n\n\t/**\n\t * Update loop (call each frame)\n\t * Returns position to replay, or null if not looping\n\t */\n\tupdateLoop(): number | null {\n\t\tif (!this.looping) {\n\t\t\treturn null;\n\t\t}\n\n\t\tif (this.loopIndex === 0) {\n\t\t\tthis.loopIndex++;\n\t\t\treturn this.loopStart;\n\t\t}\n\n\t\tthis.loopIndex++;\n\t\tif (this.loopIndex > this.loopLength) {\n\t\t\tthis.loopIndex = 0;\n\t\t}\n\n\t\treturn this.loopStart - this.loopIndex;\n\t}\n\n\t/**\n\t * Execute loop callback\n\t */\n\texecuteCallback(): void {\n\t\tif (this.loopCallback) {\n\t\t\tthis.loopCallback();\n\t\t}\n\t}\n\n\t/**\n\t * Restore state to target object\n\t */\n\trestoreState(target: any, snapshot: StateSnapshot): void {\n\t\tif (!snapshot || !target) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Remove all existing properties except protected system APIs\n\t\tfor (const key in target) {\n\t\t\tif (Object.hasOwn(target, key) && !this.isProtectedKey(key)) {\n\t\t\t\tdelete target[key];\n\t\t\t}\n\t\t}\n\n\t\t// Apply snapshot properties to target object via deep copy\n\t\tfor (const key in snapshot) {\n\t\t\tif (Object.hasOwn(snapshot, key)) {\n\t\t\t\ttarget[key] = deepCopy(snapshot[key]);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Check if key should be protected from restoration\n\t */\n\tprivate isProtectedKey(key: string): boolean {\n\t\t// Prevent system APIs and runtime objects from being overwritten during restore\n\t\tconst protected_keys = [\n\t\t\t\"screen\",\n\t\t\t\"audio\",\n\t\t\t\"keyboard\",\n\t\t\t\"mouse\",\n\t\t\t\"touch\",\n\t\t\t\"gamepad\",\n\t\t\t\"system\",\n\t\t\t\"storage\",\n\t\t\t\"sprites\",\n\t\t\t\"maps\",\n\t\t\t\"sounds\",\n\t\t\t\"music\",\n\t\t\t\"assets\",\n\t\t\t\"host\",\n\t\t\t\"session\",\n\t\t\t\"memory\",\n\t\t];\n\t\treturn protected_keys.includes(key);\n\t}\n\n}\n","/**\n * StateRecorder - Records game state history\n *\n * Responsibilities:\n * - Capture state snapshots\n * - Manage circular buffer\n * - Exclude non-serializable objects\n */\nimport { DEFAULT_RECORD_BUFFER_FRAMES } from \"../constants\";\nimport { deepCopy } from \"../utils\";\n\nimport type { StateSnapshot } from \"../types\";\n\nexport class StateRecorder {\n\tprivate history: StateSnapshot[] = [];\n\tprivate recordIndex = 0;\n\tprivate recordLength = 0;\n\tprivate maxLength: number;\n\tprivate excluded: any[] = [];\n\n\tconstructor(maxLength = DEFAULT_RECORD_BUFFER_FRAMES) {\n\t\tthis.maxLength = maxLength;\n\t}\n\n\t/**\n\t * Set objects to exclude from serialization\n\t */\n\tsetExcluded(excluded: any[]): void {\n\t\tthis.excluded = excluded;\n\t}\n\n\t/**\n\t * Record a state snapshot\n\t */\n\trecord(state: any): void {\n\t\tconst snapshot = this.makeStorableState(state);\n\t\tthis.history[this.recordIndex++] = snapshot;\n\t\tthis.recordLength = Math.min(this.recordLength + 1, this.maxLength);\n\n\t\tif (this.recordIndex >= this.maxLength) {\n\t\t\tthis.recordIndex = 0;\n\t\t}\n\t}\n\n\t/**\n\t * Get state at specific position (0 = most recent)\n\t */\n\tgetState(position: number): StateSnapshot | null {\n\t\tif (position >= this.recordLength) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst index = (this.recordIndex - position - 1 + this.maxLength) % this.maxLength;\n\t\treturn this.history[index];\n\t}\n\n\t/**\n\t * Get current record length\n\t */\n\tgetLength(): number {\n\t\treturn this.recordLength;\n\t}\n\n\t/**\n\t * Get maximum record length\n\t */\n\tgetMaxLength(): number {\n\t\treturn this.maxLength;\n\t}\n\n\t/**\n\t * Clear all recorded history\n\t */\n\tclear(): void {\n\t\tthis.history = [];\n\t\tthis.recordIndex = 0;\n\t\tthis.recordLength = 0;\n\t}\n\n\t/**\n\t * Trim history to specific position\n\t */\n\ttrimTo(position: number): void {\n\t\tif (position >= this.recordLength) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst histo: StateSnapshot[] = [];\n\t\tconst start = this.recordLength;\n\t\tconst end = position + 1;\n\n\t\tfor (let i = start; i >= end; i--) {\n\t\t\tconst index = (this.recordIndex - i + this.maxLength) % this.maxLength;\n\t\t\thisto.push(this.history[index]);\n\t\t}\n\n\t\tthis.history = histo;\n\t\tthis.recordIndex = this.history.length;\n\t\tthis.recordLength = this.history.length;\n\t}\n\n\tprivate makeStorableState(value: any): any {\n\t\treturn deepCopy(value, this.excluded);\n\t}\n}\n"],"mappings":"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","names":["DEFAULT_RECORD_BUFFER_FRAMES","DEFAULT_LOOP_BUFFER_FRAMES","deepCopy","value","excluded","includes","Array","isArray","result","i","length","key","Object","hasOwn","StatePlayer","looping","loopStart","loopIndex","loopLength","loopCallback","DEFAULT_LOOP_BUFFER_FRAMES","isLooping","startLoop","position","callback","Math","max","stopLoop","updateLoop","executeCallback","restoreState","target","snapshot","key","Object","hasOwn","isProtectedKey","deepCopy","protected_keys","includes","StateRecorder","history","recordIndex","recordLength","maxLength","excluded","DEFAULT_RECORD_BUFFER_FRAMES","setExcluded","record","state","snapshot","makeStorableState","Math","min","getState","position","index","getLength","getMaxLength","clear","trimTo","histo","start","end","i","push","length","value","deepCopy","TimeMachine","runtime","recorder","player","recording","replayPosition","statusCallback","StateRecorder","DEFAULT_RECORD_BUFFER_FRAMES","StatePlayer","DEFAULT_LOOP_BUFFER_FRAMES","setupExclusions","excluded","vm","context","global","random","push","screen","audio","keyboard","mouse","touch","gamepad","system","storage","host","session","memory","setExcluded","step","trimTo","isLooping","startLoop","getLength","loopCallback","record","sendStatus","err","reportRuntimeError","listener","APIErrorCode","E7082","error","String","messageReceived","data","command","startRecording","stopRecording","stepBackward","stepForward","position","setReplayPosition","startLooping","stopLooping","clear","E7083","replay","isFinite","E7081","value","pos","Math","round","max","min","stopLoop","skipDraw","snapshot","getState","restoreState","drawCall","updateLoop","updateCall","looping","length","getMaxLength","loopStep","executeCallback","onStatus","callback","isRecording"]}