optipack 0.0.0-stage → 1.0.1

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 (148) hide show
  1. package/BENCHMARKING.md +127 -0
  2. package/LICENSE +21 -0
  3. package/README.md +136 -2
  4. package/bin/optipack.js +12 -0
  5. package/example/manifest.json +27 -0
  6. package/example/scripts/drops.js +24 -0
  7. package/example/scripts/main.js +67 -0
  8. package/example/scripts/util.js +30 -0
  9. package/package.json +34 -4
  10. package/src/bench.js +231 -0
  11. package/src/cli.js +245 -0
  12. package/src/config.js +141 -0
  13. package/src/diff.js +108 -0
  14. package/src/harness/driver.js +216 -0
  15. package/src/harness/loader-hooks.mjs +63 -0
  16. package/src/harness/mock/@minecraft/common.mjs +17 -0
  17. package/src/harness/mock/@minecraft/debug-utilities.mjs +10 -0
  18. package/src/harness/mock/@minecraft/diagnostics.mjs +14 -0
  19. package/src/harness/mock/@minecraft/server-admin.mjs +14 -0
  20. package/src/harness/mock/@minecraft/server-editor.mjs +39 -0
  21. package/src/harness/mock/@minecraft/server-extras.mjs +538 -0
  22. package/src/harness/mock/@minecraft/server-gametest.mjs +22 -0
  23. package/src/harness/mock/@minecraft/server-graphics.mjs +21 -0
  24. package/src/harness/mock/@minecraft/server-net.mjs +15 -0
  25. package/src/harness/mock/@minecraft/server-ui.mjs +125 -0
  26. package/src/harness/mock/@minecraft/server.mjs +394 -0
  27. package/src/harness/mock/permissive.mjs +71 -0
  28. package/src/hotpath.js +158 -0
  29. package/src/lint.js +150 -0
  30. package/src/pack-walker.js +50 -0
  31. package/src/pass-utils.js +419 -0
  32. package/src/passes/b1-cache-getter-chains.js +406 -0
  33. package/src/passes/b2-closure-lift.js +316 -0
  34. package/src/passes/b3-string-build.js +167 -0
  35. package/src/passes/b4-dynprop-hoist.js +177 -0
  36. package/src/passes/b5-null-chain-flatten.js +137 -0
  37. package/src/passes/index.js +43 -0
  38. package/src/passes/r1-cross-tick-cache.js +192 -0
  39. package/src/passes/r2-json-fold.js +201 -0
  40. package/src/passes/r3-try-catch-strip.js +61 -0
  41. package/src/passes/r4-getter-cache-cross-barrier.js +29 -0
  42. package/src/passes/s1-const-fold.js +130 -0
  43. package/src/passes/s2-dead-code.js +183 -0
  44. package/src/passes/s3-bool-simplify.js +82 -0
  45. package/src/passes/s4-for-of-to-indexed.js +140 -0
  46. package/src/passes/s5-array-methods-to-loops.js +357 -0
  47. package/src/passes/s6-destructure-to-direct.js +186 -0
  48. package/src/passes/s7-hoist-invariant-members.js +327 -0
  49. package/src/pipeline.js +186 -0
  50. package/src/pragma.js +115 -0
  51. package/src/report.js +98 -0
  52. package/test/array-methods-to-loops/expected.js +58 -0
  53. package/test/array-methods-to-loops/expects.json +6 -0
  54. package/test/array-methods-to-loops/input.js +23 -0
  55. package/test/array-methods-to-loops/scenario.json +22 -0
  56. package/test/array-methods-to-loops-bail/expected.js +10 -0
  57. package/test/array-methods-to-loops-bail/input.js +24 -0
  58. package/test/bool-simplify/expected.js +14 -0
  59. package/test/bool-simplify/expects.json +11 -0
  60. package/test/bool-simplify/input.js +18 -0
  61. package/test/cache-getter-chains/expected.js +20 -0
  62. package/test/cache-getter-chains/expects.json +11 -0
  63. package/test/cache-getter-chains/input.js +20 -0
  64. package/test/cache-getter-chains/scenario.json +35 -0
  65. package/test/closure-lift/expected.js +58 -0
  66. package/test/closure-lift/expects.json +1 -0
  67. package/test/closure-lift/input.js +50 -0
  68. package/test/closure-lift/scenario.json +9 -0
  69. package/test/const-fold/expected.js +12 -0
  70. package/test/const-fold/expects.json +1 -0
  71. package/test/const-fold/input.js +12 -0
  72. package/test/const-fold-bail/expected.js +7 -0
  73. package/test/const-fold-bail/expects.json +3 -0
  74. package/test/const-fold-bail/input.js +7 -0
  75. package/test/cross-tick-cache/config.json +1 -0
  76. package/test/cross-tick-cache/expected.js +38 -0
  77. package/test/cross-tick-cache/expects.json +6 -0
  78. package/test/cross-tick-cache/input.js +13 -0
  79. package/test/cross-tick-cache/scenario.json +33 -0
  80. package/test/dead-code/expected.js +14 -0
  81. package/test/dead-code/expects.json +5 -0
  82. package/test/dead-code/input.js +23 -0
  83. package/test/destructure-to-direct/expected.js +37 -0
  84. package/test/destructure-to-direct/expects.json +11 -0
  85. package/test/destructure-to-direct/input.js +28 -0
  86. package/test/destructure-to-direct/scenario.json +22 -0
  87. package/test/destructure-to-direct-bail/expected.js +36 -0
  88. package/test/destructure-to-direct-bail/expects.json +11 -0
  89. package/test/destructure-to-direct-bail/input.js +25 -0
  90. package/test/differential.test.mjs +267 -0
  91. package/test/dynprop-hoist/expected.js +24 -0
  92. package/test/dynprop-hoist/expects.json +11 -0
  93. package/test/dynprop-hoist/input.js +25 -0
  94. package/test/dynprop-hoist/scenario.json +24 -0
  95. package/test/example-report-snapshot.json +107 -0
  96. package/test/example.test.mjs +115 -0
  97. package/test/fetch-corpus.mjs +92 -0
  98. package/test/for-of-to-indexed/expected.js +59 -0
  99. package/test/for-of-to-indexed/expects.json +11 -0
  100. package/test/for-of-to-indexed/input.js +43 -0
  101. package/test/for-of-to-indexed-bail/expected.js +25 -0
  102. package/test/for-of-to-indexed-bail/expects.json +11 -0
  103. package/test/for-of-to-indexed-bail/input.js +29 -0
  104. package/test/gen-expected.mjs +33 -0
  105. package/test/getter-cache-cross-barrier/config.json +1 -0
  106. package/test/getter-cache-cross-barrier/expected.js +10 -0
  107. package/test/getter-cache-cross-barrier/expects.json +6 -0
  108. package/test/getter-cache-cross-barrier/input.js +11 -0
  109. package/test/getter-cache-cross-barrier/scenario.json +28 -0
  110. package/test/harness/fixture.js +117 -0
  111. package/test/hoist-invariant-members/expected.js +37 -0
  112. package/test/hoist-invariant-members/expects.json +11 -0
  113. package/test/hoist-invariant-members/input.js +34 -0
  114. package/test/hoist-invariant-members/scenario.json +35 -0
  115. package/test/hoist-invariant-members-bail/expected.js +19 -0
  116. package/test/hoist-invariant-members-bail/expects.json +5 -0
  117. package/test/hoist-invariant-members-bail/input.js +22 -0
  118. package/test/infra.test.mjs +270 -0
  119. package/test/json-fold/config.json +1 -0
  120. package/test/json-fold/expected.js +14 -0
  121. package/test/json-fold/expects.json +12 -0
  122. package/test/json-fold/input.js +17 -0
  123. package/test/json-fold-bail/config.json +1 -0
  124. package/test/json-fold-bail/expected.js +10 -0
  125. package/test/json-fold-bail/expects.json +5 -0
  126. package/test/json-fold-bail/input.js +14 -0
  127. package/test/null-chain-flatten/expected.js +22 -0
  128. package/test/null-chain-flatten/expects.json +5 -0
  129. package/test/null-chain-flatten/input.js +22 -0
  130. package/test/null-chain-flatten/scenario.json +24 -0
  131. package/test/server-ui-forms/expected.js +15 -0
  132. package/test/server-ui-forms/expects.json +1 -0
  133. package/test/server-ui-forms/input.js +25 -0
  134. package/test/server-ui-forms/scenario.json +1 -0
  135. package/test/string-build/expected.js +29 -0
  136. package/test/string-build/expects.json +5 -0
  137. package/test/string-build/input.js +29 -0
  138. package/test/string-build/scenario.json +27 -0
  139. package/test/string-build-bail/expected.js +10 -0
  140. package/test/string-build-bail/input.js +21 -0
  141. package/test/transforms.test.mjs +43 -0
  142. package/test/try-catch-strip/config.json +1 -0
  143. package/test/try-catch-strip/expected.js +57 -0
  144. package/test/try-catch-strip/expects.json +1 -0
  145. package/test/try-catch-strip/input.js +34 -0
  146. package/test/try-catch-strip/scenario.json +28 -0
  147. package/test/update-example-snapshot.mjs +19 -0
  148. package/test/update-mock-stubs.mjs +91 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Functional mock of @minecraft/server-ui for the differential harness.
3
+ *
4
+ * Models the widely used form surface accurately: ActionFormData,
5
+ * MessageFormData, ModalFormData (chainable builders), uiManager.showPlayer,
6
+ * and the response types. `.show()`/`showPlayer()` record a harness effect
7
+ * carrying the form's kind and title, then resolve with a scripted response
8
+ * from the recorder (key: `uiResponse:<entityId>`, default: a non-canceled
9
+ * selection). Names present in the real 2.2.0 module but not modeled in depth
10
+ * (CustomForm, MessageBox, observables, error classes) are bound to permissive
11
+ * stubs so any fixture using them still links and runs.
12
+ */
13
+ import { permissive } from '../permissive.mjs';
14
+
15
+ const rec = () => globalThis.__opkRecorder;
16
+
17
+ function scriptedResponse(player) {
18
+ const r = rec();
19
+ const key = 'uiResponse:' + (player && player.id !== undefined ? player.id : 'unknown');
20
+ const base = r ? r.scriptedRead(key, undefined) : undefined;
21
+ if (base) return base;
22
+ return { selection: 0, formValues: [0], canceled: false, cancelationReason: undefined };
23
+ }
24
+
25
+ class FormBase {
26
+ constructor(kind) {
27
+ this.__kind = kind;
28
+ this.__data = { title: undefined, body: undefined, buttons: [], fields: [] };
29
+ }
30
+ __chain(mutator) {
31
+ mutator();
32
+ return this;
33
+ }
34
+ title(text) { return this.__chain(() => { this.__data.title = String(text); }); }
35
+ body(text) { return this.__chain(() => { this.__data.body = String(text); }); }
36
+ __show(player) {
37
+ const r = rec();
38
+ if (r) r.effect('ui.show', { kind: this.__kind, title: this.__data.title, player: player && player.id !== undefined ? player.id : null });
39
+ const response = scriptedResponse(player);
40
+ return Promise.resolve(response);
41
+ }
42
+ }
43
+
44
+ export class ActionFormData extends FormBase {
45
+ constructor() { super('ActionFormData'); }
46
+ button(text, iconPath) { return this.__chain(() => { this.__data.buttons.push({ text: String(text), iconPath }); }); }
47
+ divider() { return this.__chain(() => { this.__data.buttons.push({ divider: true }); }); }
48
+ header(text) { return this.__chain(() => { this.__data.header = String(text); }); }
49
+ label(text) { return this.__chain(() => { this.__data.fields.push({ type: 'label', text: String(text) }); }); }
50
+ show(player) { return this.__show(player); }
51
+ }
52
+
53
+ export class MessageFormData extends FormBase {
54
+ constructor() { super('MessageFormData'); }
55
+ button1(text) { return this.__chain(() => { this.__data.buttons[0] = { text: String(text) }; }); }
56
+ button2(text) { return this.__chain(() => { this.__data.buttons[1] = { text: String(text) }; }); }
57
+ show(player) { return this.__show(player); }
58
+ }
59
+
60
+ export class ModalFormData extends FormBase {
61
+ constructor() { super('ModalFormData'); }
62
+ textField(label, placeholder, options) { return this.__chain(() => { this.__data.fields.push({ type: 'textField', label: String(label), placeholder, options }); }); }
63
+ slider(label, min, max, step, deflt) { return this.__chain(() => { this.__data.fields.push({ type: 'slider', label: String(label), min, max, step, default: deflt }); }); }
64
+ toggle(label, deflt) { return this.__chain(() => { this.__data.fields.push({ type: 'toggle', label: String(label), default: deflt }); }); }
65
+ dropdown(label, options, deflt) { return this.__chain(() => { this.__data.fields.push({ type: 'dropdown', label: String(label), options, default: deflt }); }); }
66
+ divider() { return this.__chain(() => { this.__data.fields.push({ type: 'divider' }); }); }
67
+ header(text) { return this.__chain(() => { this.__data.header = String(text); }); }
68
+ label(text) { return this.__chain(() => { this.__data.fields.push({ type: 'label', text: String(text) }); }); }
69
+ submitButton(text) { return this.__chain(() => { this.__data.submitButtonText = String(text); }); }
70
+ show(player) { return this.__show(player); }
71
+ }
72
+
73
+ class UIManagerImpl {
74
+ showPlayer(player, form) {
75
+ const r = rec();
76
+ if (r) r.effect('ui.showPlayer', { kind: form && form.__kind, title: form && form.__data && form.__data.title, player: player && player.id !== undefined ? player.id : null });
77
+ const response = scriptedResponse(player);
78
+ return Promise.resolve(response);
79
+ }
80
+ close(player) {
81
+ const r = rec();
82
+ if (r) r.effect('ui.close', { player: player && player.id !== undefined ? player.id : null });
83
+ }
84
+ }
85
+
86
+ export const uiManager = new UIManagerImpl();
87
+
88
+ // Response constructors: usable with `new`, mirror the scripted/plain shape.
89
+ export class FormResponse {
90
+ constructor(init = {}) { Object.assign(this, { canceled: false, cancelationReason: undefined }, init); }
91
+ }
92
+ export class ActionFormResponse extends FormResponse {
93
+ constructor(init = {}) { super({ selection: 0, ...init }); }
94
+ }
95
+ export class MessageFormResponse extends FormResponse {
96
+ constructor(init = {}) { super({ selection: 0, ...init }); }
97
+ }
98
+ export class ModalFormResponse extends FormResponse {
99
+ constructor(init = {}) { super({ formValues: [], ...init }); }
100
+ }
101
+
102
+ export const FormCancelationReason = Object.freeze({
103
+ UserBusy: 'UserBusy',
104
+ UserClosed: 'UserClosed',
105
+ ServerCrashed: 'ServerCrashed',
106
+ });
107
+
108
+ // Present in the real module but not modeled in depth: permissive stubs so
109
+ // named imports link and recorded effects stay identical on both sides.
110
+ export const CustomForm = permissive('server-ui.CustomForm');
111
+ export const MessageBox = permissive('server-ui.MessageBox');
112
+ export const ObservableBoolean = permissive('server-ui.ObservableBoolean');
113
+ export const ObservableNumber = permissive('server-ui.ObservableNumber');
114
+ export const ObservableString = permissive('server-ui.ObservableString');
115
+ export const ObservableUIRawMessage = permissive('server-ui.ObservableUIRawMessage');
116
+ export const DataDrivenScreenClosedReason = permissive('server-ui.DataDrivenScreenClosedReason');
117
+ export const FormRejectError = permissive('server-ui.FormRejectError');
118
+ export const FormRejectReason = permissive('server-ui.FormRejectReason');
119
+ export const FormVisibilityError = permissive('server-ui.FormVisibilityError');
120
+ export const FormVisibilityErrorReason = permissive('server-ui.FormVisibilityErrorReason');
121
+ export const InvalidFormError = permissive('server-ui.InvalidFormError');
122
+ export const InvalidFormModificationError = permissive('server-ui.InvalidFormModificationError');
123
+ export const InvalidObservableError = permissive('server-ui.InvalidObservableError');
124
+ export const PlayerLeftError = permissive('server-ui.PlayerLeftError');
125
+ export const TextFilteringError = permissive('server-ui.TextFilteringError');
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Mock @minecraft/server for the differential harness and `optipack bench`.
3
+ *
4
+ * Effect-recorder contract: only MUTATING calls and registrations are recorded
5
+ * as effects. Pure reads return scripted sequences and are intentionally not
6
+ * recorded — transforms may legitimately eliminate reads, that is the point.
7
+ *
8
+ * Boundary counters: the optipack-relevant metric — host-boundary crossings
9
+ * (location reads, getComponent, getDynamicProperty, ...) — is counted on the
10
+ * recorder (`r.counters`). Location reads return a FRESH object per read, like
11
+ * the engine (each read allocates a new Vector3), so allocation pressure is
12
+ * visible in the counter too.
13
+ *
14
+ * Tolerance: arbitrary real-world scripts use wider API surface than any mock
15
+ * can enumerate. world/Dimension/Entity instances are wrapped so unknown
16
+ * members resolve to permissive values that record `stub.*` effects (identical
17
+ * on both sides of a comparison), and event collections accept ANY event name.
18
+ * Known hot-path members keep real implementations with counters.
19
+ */
20
+
21
+ const rec = () => globalThis.__opkRecorder;
22
+
23
+ function scriptedRead(key, fallback) {
24
+ const r = rec();
25
+ if (!r) return fallback;
26
+ return r.scriptedRead(key, fallback);
27
+ }
28
+
29
+ function effect(call, args) {
30
+ const r = rec();
31
+ if (r) r.effect(call, args === undefined ? null : args);
32
+ }
33
+
34
+ function count(kind) {
35
+ const r = rec();
36
+ if (r) r.count(kind);
37
+ }
38
+
39
+ function serialize(v) {
40
+ if (v === null) return null;
41
+ const t = typeof v;
42
+ if (t === 'string' || t === 'number' || t === 'boolean' || t === 'undefined') return v;
43
+ if (v instanceof Entity) return { __entity: v.id };
44
+ if (v instanceof Dimension) return { __dimension: v.id };
45
+ if (Array.isArray(v)) return v.map(serialize);
46
+ if (t === 'object') {
47
+ const out = {};
48
+ for (const k of Object.keys(v)) out[k] = serialize(v[k]);
49
+ return out;
50
+ }
51
+ return String(v);
52
+ }
53
+
54
+ /** Permissive fallback for unknown members on wrapped host objects. */
55
+ function unknownMember(label) {
56
+ const asPrimitive = (hint) => (hint === 'number' ? 0 : label);
57
+ const fn = function (...args) {
58
+ effect('stub.call', { target: label, args: serialize(args) });
59
+ return unknownMember(label + '()');
60
+ };
61
+ fn.toString = () => label;
62
+ fn.valueOf = () => label;
63
+ return new Proxy(fn, {
64
+ get(target, prop, receiver) {
65
+ if (prop === Symbol.toPrimitive) return asPrimitive;
66
+ if (typeof prop === 'symbol') return undefined;
67
+ if (prop === 'then' || prop === 'catch' || prop === 'finally' || prop === 'toJSON') return undefined;
68
+ if (prop === 'toString' || prop === 'valueOf') return Reflect.get(target, prop, receiver);
69
+ return unknownMember(label + '.' + String(prop));
70
+ },
71
+ construct(target, args) {
72
+ effect('stub.new', { target: label, args: serialize(args) });
73
+ return unknownMember('new ' + label);
74
+ },
75
+ apply(target, thisArg, args) {
76
+ effect('stub.call', { target: label, args: serialize(args) });
77
+ return unknownMember(label + '()');
78
+ },
79
+ has() { return true; },
80
+ });
81
+ }
82
+
83
+ class Block {
84
+ constructor(location, dimensionId) {
85
+ this.location = location;
86
+ this.__dim = dimensionId;
87
+ this.typeId = scriptedRead(`blockType:${this.__dim}`, 'minecraft:stone');
88
+ }
89
+ get permutation() {
90
+ count('boundary.block.permutation');
91
+ return scriptedRead(`blockPerm:${this.__dim}`, { type: this.typeId });
92
+ }
93
+ setPermutation(permutation) {
94
+ effect('block.setPermutation', { dimension: this.__dim, location: serialize(this.location), permutation: serialize(permutation) });
95
+ }
96
+ setType(blockType) {
97
+ effect('block.setType', { dimension: this.__dim, location: serialize(this.location), type: serialize(blockType) });
98
+ }
99
+ getComponent(name) {
100
+ count('boundary.getComponent');
101
+ return scriptedRead(`component:block:${name}`, undefined);
102
+ }
103
+ }
104
+
105
+ function wrapHost(instance, label) {
106
+ return new Proxy(instance, {
107
+ get(target, prop, receiver) {
108
+ if (typeof prop === 'symbol' || prop === 'then' || prop === 'catch' || prop === 'finally') {
109
+ return typeof prop === 'symbol' ? undefined : Reflect.get(target, prop, receiver);
110
+ }
111
+ if (prop in target) return Reflect.get(target, prop, receiver);
112
+ return unknownMember(label + '.' + String(prop));
113
+ },
114
+ });
115
+ }
116
+
117
+ class Dimension {
118
+ constructor(id) {
119
+ this.id = id;
120
+ }
121
+ getEntities(options) {
122
+ count('boundary.getEntities');
123
+ effect('dimension.getEntities', { dimension: this.id, options: serialize(options) });
124
+ return scriptedRead(`entities:${this.id}`, []);
125
+ }
126
+ getPlayers() {
127
+ count('boundary.getPlayers');
128
+ effect('dimension.getPlayers', { dimension: this.id });
129
+ return scriptedRead(`players:${this.id}`, []);
130
+ }
131
+ get players() {
132
+ count('boundary.players');
133
+ return scriptedRead(`players:${this.id}`, []);
134
+ }
135
+ spawnEntity(type, location) {
136
+ effect('dimension.spawnEntity', { dimension: this.id, type, location: serialize(location) });
137
+ return scriptedRead(`spawn:${this.id}:${type}`, null);
138
+ }
139
+ spawnParticle(effectName, location, molangVariables) {
140
+ effect('dimension.spawnParticle', { dimension: this.id, effectName, location: serialize(location), molang: serialize(molangVariables) });
141
+ }
142
+ runCommand(commandString) {
143
+ effect('dimension.runCommand', { dimension: this.id, command: commandString });
144
+ }
145
+ getBlock(location) {
146
+ count('boundary.getBlock');
147
+ return new Block({ x: location.x, y: location.y, z: location.z }, this.id);
148
+ }
149
+ getEntitiesAtBlockLocation(location) {
150
+ count('boundary.getEntitiesAtBlockLocation');
151
+ effect('dimension.getEntitiesAtBlockLocation', { dimension: this.id, location: serialize(location) });
152
+ return scriptedRead(`entitiesAt:${this.id}`, []);
153
+ }
154
+ }
155
+
156
+ function wrapDimension(dim) {
157
+ return wrapHost(dim, 'dimension.' + dim.id);
158
+ }
159
+
160
+ class Entity {
161
+ constructor(id, typeId, dimension) {
162
+ this._id = id;
163
+ this.typeId = typeId;
164
+ this._dimension = dimension;
165
+ return wrapHost(this, 'entity.' + id);
166
+ }
167
+ get id() {
168
+ return this._id;
169
+ }
170
+ get name() {
171
+ return scriptedRead(`name:${this._id}`, 'player' + this._id);
172
+ }
173
+ get location() {
174
+ count('boundary.location');
175
+ const v = scriptedRead(`location:${this._id}`, { x: 0, y: 0, z: 0 });
176
+ // The engine allocates a fresh Vector3 on every read; mirror that.
177
+ return { x: v.x, y: v.y, z: v.z };
178
+ }
179
+ get dimension() {
180
+ count('boundary.dimension');
181
+ return this._dimension;
182
+ }
183
+ getComponent(componentId) {
184
+ count('boundary.getComponent');
185
+ const v = scriptedRead(`component:${this._id}:${componentId}`, undefined);
186
+ // Real-world scripts assume queried components exist; default to a
187
+ // chainable permissive component instead of undefined (identical on both
188
+ // sides of a comparison).
189
+ return v === undefined ? unknownMember('entity.' + this._id + '.component.' + componentId) : v;
190
+ }
191
+ getComponents() {
192
+ count('boundary.getComponents');
193
+ effect('entity.getComponents', { entity: this._id });
194
+ return [];
195
+ }
196
+ getDynamicProperty(identifier) {
197
+ count('boundary.getDynamicProperty');
198
+ return scriptedRead(`dp:${this._id}:${identifier}`, undefined);
199
+ }
200
+ setDynamicProperty(identifier, value) {
201
+ effect('entity.setDynamicProperty', { entity: this._id, key: identifier, value: serialize(value) });
202
+ }
203
+ sendMessage(message) {
204
+ effect('entity.sendMessage', { entity: this._id, message: serialize(message) });
205
+ }
206
+ runCommand(commandString) {
207
+ effect('entity.runCommand', { entity: this._id, command: commandString });
208
+ }
209
+ teleport(location, teleportOptions) {
210
+ effect('entity.teleport', { entity: this._id, location: serialize(location), options: serialize(teleportOptions) });
211
+ }
212
+ applyDamage(amount, options) {
213
+ effect('entity.applyDamage', { entity: this._id, amount, options: serialize(options) });
214
+ return scriptedRead(`damage:${this._id}`, true);
215
+ }
216
+ applyKnockback(direction, horizontalStrength, verticalStrength) {
217
+ effect('entity.applyKnockback', { entity: this._id, direction: serialize(direction), horizontalStrength, verticalStrength });
218
+ }
219
+ applyImpulse(impulse) {
220
+ effect('entity.applyImpulse', { entity: this._id, impulse: serialize(impulse) });
221
+ }
222
+ kill() {
223
+ effect('entity.kill', { entity: this._id });
224
+ }
225
+ addTag(tag) {
226
+ effect('entity.addTag', { entity: this._id, tag });
227
+ return true;
228
+ }
229
+ removeTag(tag) {
230
+ effect('entity.removeTag', { entity: this._id, tag });
231
+ return true;
232
+ }
233
+ hasTag(tag) {
234
+ return scriptedRead(`tag:${this._id}:${tag}`, false);
235
+ }
236
+ playAnimation(animationName, options) {
237
+ effect('entity.playAnimation', { entity: this._id, animationName, options: serialize(options) });
238
+ }
239
+ triggerEvent(eventName) {
240
+ effect('entity.triggerEvent', { entity: this._id, eventName });
241
+ }
242
+ getRotation() {
243
+ count('boundary.getRotation');
244
+ return scriptedRead(`rotation:${this._id}`, { x: 0, y: 0 });
245
+ }
246
+ getVelocity() {
247
+ count('boundary.getVelocity');
248
+ return scriptedRead(`velocity:${this._id}`, { x: 0, y: 0, z: 0 });
249
+ }
250
+ }
251
+
252
+ function makeEvent(fullName) {
253
+ return {
254
+ subscribe(callback, options) {
255
+ effect('subscribe', { event: fullName, options: serialize(options) });
256
+ const r = rec();
257
+ if (r) r.addHandler(fullName, callback);
258
+ },
259
+ unsubscribe(callback) {
260
+ effect('unsubscribe', { event: fullName });
261
+ const r = rec();
262
+ if (r) r.removeHandler(fullName, callback);
263
+ },
264
+ };
265
+ }
266
+
267
+ // Event collections accept ANY event name (real-world scripts use the full
268
+ // API surface; unknown names record the same effect shape).
269
+ function eventCollection(prefix) {
270
+ return new Proxy({}, {
271
+ get(target, prop) {
272
+ if (typeof prop === 'symbol' || prop === 'then') return undefined;
273
+ if (prop in target) return target[prop];
274
+ const ev = makeEvent(`${prefix}.${String(prop)}`);
275
+ target[prop] = ev;
276
+ return ev;
277
+ },
278
+ });
279
+ }
280
+
281
+ function currentPlayers() {
282
+ const r = rec();
283
+ if (!r) return [];
284
+ if (!r._players) {
285
+ const specs = (r.state && r.state.players) || [];
286
+ r._players = specs.map((s) => new Entity(s.id, s.typeId || 'minecraft:player', wrapDimension(new Dimension(s.dimension || 'minecraft:overworld'))));
287
+ }
288
+ return r._players;
289
+ }
290
+
291
+ function ensurePlayers(r) {
292
+ if (r._players) return r._players;
293
+ const specs = (r.state && r.state.players) || [];
294
+ r._players = specs.map((s) => new Entity(s.id, s.typeId || 'minecraft:player', wrapDimension(new Dimension(s.dimension || 'minecraft:overworld'))));
295
+ return r._players;
296
+ }
297
+
298
+ // The differential driver hydrates event payloads via these hooks.
299
+ if (typeof globalThis !== 'undefined') {
300
+ globalThis.__opkEnsurePlayers = ensurePlayers;
301
+ globalThis.__opkMakeDimension = (id) => wrapDimension(new Dimension(id));
302
+ globalThis.__opkMakeBlock = (location, dimensionId) => new Block(location, dimensionId || 'minecraft:overworld');
303
+ }
304
+
305
+ const worldImpl = {
306
+ get afterEvents() {
307
+ return eventCollection('world.afterEvents');
308
+ },
309
+ get beforeEvents() {
310
+ return eventCollection('world.beforeEvents');
311
+ },
312
+ getAllPlayers() {
313
+ count('boundary.getAllPlayers');
314
+ effect('world.getAllPlayers', null);
315
+ return currentPlayers();
316
+ },
317
+ getPlayers() {
318
+ count('boundary.getPlayers');
319
+ effect('world.getPlayers', null);
320
+ return currentPlayers();
321
+ },
322
+ getDynamicProperty(identifier) {
323
+ count('boundary.getDynamicProperty');
324
+ return scriptedRead(`world.dp:${identifier}`, undefined);
325
+ },
326
+ setDynamicProperty(identifier, value) {
327
+ effect('world.setDynamicProperty', { key: identifier, value: serialize(value) });
328
+ },
329
+ sendMessage(message) {
330
+ effect('world.sendMessage', { message: serialize(message) });
331
+ },
332
+ getEntity(identifier) {
333
+ effect('world.getEntity', { id: identifier });
334
+ return scriptedRead(`world.entity:${identifier}`, undefined);
335
+ },
336
+ getDimension(dimensionId) {
337
+ effect('world.getDimension', { id: dimensionId });
338
+ return wrapDimension(new Dimension(dimensionId));
339
+ },
340
+ playSound(soundId, options) {
341
+ effect('world.playSound', { soundId, options: serialize(options) });
342
+ },
343
+ };
344
+
345
+ export const world = wrapHost(worldImpl, 'world');
346
+
347
+ const systemBeforeEvents = eventCollection('system.beforeEvents');
348
+ const systemAfterEvents = eventCollection('system.afterEvents');
349
+
350
+ export const system = {
351
+ get beforeEvents() {
352
+ return systemBeforeEvents;
353
+ },
354
+ get afterEvents() {
355
+ return systemAfterEvents;
356
+ },
357
+ runInterval(callback, tickInterval) {
358
+ const interval = tickInterval === undefined ? 1 : tickInterval;
359
+ effect('system.runInterval', { interval });
360
+ const r = rec();
361
+ return r ? r.addInterval(callback, interval) : -1;
362
+ },
363
+ runTimeout(callback, timeoutTicks) {
364
+ const delay = timeoutTicks === undefined ? 1 : timeoutTicks;
365
+ effect('system.runTimeout', { delay });
366
+ const r = rec();
367
+ return r ? r.addTimeout(callback, delay) : -1;
368
+ },
369
+ run(callback) {
370
+ effect('system.run', null);
371
+ const r = rec();
372
+ return r ? r.addTimeout(callback, 1) : -1;
373
+ },
374
+ runJob(generator) {
375
+ effect('system.runJob', null);
376
+ const r = rec();
377
+ if (r) r.addJob(generator);
378
+ return { cancel() { effect('system.clearRun', { id: 'job' }); } };
379
+ },
380
+ clearRun(runId) {
381
+ effect('system.clearRun', { id: serialize(runId) });
382
+ },
383
+ };
384
+
385
+ export { Entity, Dimension, Block };
386
+ export const TicksPerSecond = 20;
387
+ export const GameMode = { survival: 'survival', creative: 'creative', adventure: 'adventure' };
388
+
389
+ // Full API surface: every other runtime export of the real module is a
390
+ // permissive value (generated from the real index.d.ts) so real-world
391
+ // scripts' named imports link under the harness.
392
+ export * from './server-extras.mjs';
393
+
394
+ export default { world, system, Entity, Dimension, TicksPerSecond, GameMode };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Permissive stub factory for @minecraft/* runtime modules that optipack's
3
+ * differential harness does not model in depth.
4
+ *
5
+ * Every exported name from a module's real index.d.ts is bound to a permissive
6
+ * value: callable, constructable, and property-accessible indefinitely. Calls
7
+ * and constructions are recorded as harness effects (identically on the
8
+ * original and optimized sides), so differential equivalence still holds while
9
+ * scripts exercise these modules. Property GETS are silent — only calls and
10
+ * `new` produce effects — so chained accessors do not spam the trace.
11
+ *
12
+ * Not a behavioral model: it exists so fixtures importing these modules link
13
+ * and run. Behavior differences between stub and engine are identical on both
14
+ * sides of a differential comparison, which is the only claim it supports.
15
+ */
16
+ const rec = () => globalThis.__opkRecorder;
17
+
18
+ function record(call, args) {
19
+ const r = rec();
20
+ if (r) r.effect(call, args);
21
+ }
22
+
23
+ function shallow(v, depth = 0) {
24
+ if (v === null) return null;
25
+ const t = typeof v;
26
+ if (t === 'string' || t === 'number' || t === 'boolean' || t === 'undefined') return v;
27
+ if (depth > 2) return String(t);
28
+ if (Array.isArray(v)) return v.map((x) => shallow(x, depth + 1));
29
+ if (t === 'object') {
30
+ const out = {};
31
+ for (const k of Object.keys(v)) out[k] = shallow(v[k], depth + 1);
32
+ return out;
33
+ }
34
+ return String(t);
35
+ }
36
+
37
+ const NON_VALUE_SYMBOLS = new Set([
38
+ 'then', 'catch', 'finally', 'toJSON', 'inspect', 'constructor',
39
+ Symbol.toPrimitive, Symbol.toStringTag, Symbol.iterator,
40
+ 'nodejs.util.inspect.custom', 'asymmetricMatcher',
41
+ ]);
42
+
43
+ export function permissive(label) {
44
+ const asPrimitive = (hint) => (hint === 'number' ? 0 : label);
45
+ const fn = function (...args) {
46
+ record('stub.call', { target: label, args: shallow(args) });
47
+ return permissive(label + '()');
48
+ };
49
+ fn.toString = () => label;
50
+ fn.valueOf = () => label;
51
+ return new Proxy(fn, {
52
+ get(target, prop, receiver) {
53
+ if (prop === Symbol.toPrimitive) return asPrimitive;
54
+ if (typeof prop === 'symbol') return undefined;
55
+ if (prop === 'then' || prop === 'catch' || prop === 'finally' || prop === 'toJSON') return undefined;
56
+ if (prop === 'toString' || prop === 'valueOf') return Reflect.get(target, prop, receiver);
57
+ return permissive(label + '.' + String(prop));
58
+ },
59
+ construct(target, args) {
60
+ record('stub.new', { target: label, args: shallow(args) });
61
+ return permissive('new ' + label);
62
+ },
63
+ apply(target, thisArg, args) {
64
+ record('stub.call', { target: label, args: shallow(args) });
65
+ return permissive(label + '()');
66
+ },
67
+ has() {
68
+ return true;
69
+ },
70
+ });
71
+ }