Migrating to 1.0.0
A list of every breaking change in 1.0.0 with a before and after snippet. Each section is mechanical: find the pattern, apply the rewrite. The page is intentionally greppable. every pre-1.0 pattern appears verbatim in a Before code block so a git grep skip() or git grep visibleSymbols in your repo lands on the matching rewrite here.
visibleSymbols is gone
The deprecated visibleSymbols builder setter (an alias of visibleRows) is removed. Replace every call:
Before:
// Pre-1.0 code. .visibleSymbols(3) is removed in 1.0.
builder
.reels(5)
.visibleSymbols(3);
After:
builder.reels(5).visibleRows(3);
setResult and initialFrame accept only ColumnTarget[]
The legacy string[][] form, including the negative-index buffer-above mutation pattern, is removed. Both ReelSet.setResult and ReelSetBuilder.initialFrame now accept only ColumnTarget[].
Before (legacy form with a buffer-above prefill):
// Pre-1.0 code. Will not type-check against 1.0.
const grid: string[][] = [
['A', 'B', 'C'],
['A', 'B', 'C'],
];
// Buffer-above slot 0 was set via negative-index property assignment:
(grid[1] as any)[-1] = 'COIN';
reelSet.setResult(grid);
After:
reelSet.setResult([
{ visible: ['A', 'B', 'C'] },
{ visible: ['A', 'B', 'C'], bufferAbove: ['COIN'] },
]);
If your server returns a plain string[][] of visible rows, convert at the boundary:
const response: string[][] = await server.spin();
reelSet.setResult(response.map((visible) => ({ visible })));
The same conversion applies to ReelSetBuilder.initialFrame(...) and to the grid field passed to ReelSet.refill(...).
Hidden exports from the package entry
Eight types and classes are no longer re-exported from pixi-reels. They remain in the source tree but are not part of the public API.
| Removed export | What it was | Replacement |
|---|---|---|
OCCUPIED_SENTINEL | The cross-reel stub the engine paints into big-symbol non-anchor cells | Never produce or consume directly. ReelSet.getVisibleGrid() resolves stubs to anchor ids. |
ReelMotion | Per-Reel Y-displacement and wrap helper | Wired by Reel itself. |
StopSequencer | Per-Reel target-frame manager | Wired by Reel itself. |
ReelSetInternalConfig | Shape internal to the builder-to-engine handoff | Use ReelSetBuilder to wire a ReelSet. |
ResolvedReelGridConfig | Same | Same. |
SymbolFactory | Pooling layer for symbol instances | Register symbol classes via builder.symbols(registry => ...). |
RandomSymbolProvider | Weighted symbol picker | Pass weights via builder.weights({...}). |
OffsetCalculator | Geometry helper for ReelViewport | Internal. |
If you imported any of these from pixi-reels, remove the import. If you genuinely need the internal, import from the source path directly (acknowledging that internals can change without a major bump).
SpinController and the built-in phase classes are hidden
SpinController, SpinControllerHooks, and the built-in phase classes (StartPhase, SpinPhase, StopPhase, AnticipationPhase, AdjustPhase, CascadeFallPhase, CascadePlacePhase, CascadeDropInPhase) are no longer exported. Consumers never construct a SpinController directly. it’s wired by ReelSet. and custom phases extend ReelPhase rather than the built-ins (the built-ins were not designed for safe subclassing).
The phase Config TYPES stay exported (StartPhaseConfig, SpinPhaseConfig, etc.) as stable shape descriptions.
Before:
// Pre-1.0 code. Removed in 1.0.
import { SpinController, StartPhase, SpinPhase } from 'pixi-reels';
class MyStartPhase extends StartPhase {
// ...
}
After:
import { ReelPhase, type StartPhaseConfig } from 'pixi-reels';
class MyStartPhase extends ReelPhase<StartPhaseConfig> {
// ...
}
builder.phases((f) => f.register('start', MyStartPhase));
Internal-leaking methods lost their leading underscore
Three methods on Reel and ReelSet were called across files via the underscore convention. They are renamed to match how they are reached, with JSDoc that documents the call sites.
| Before | After |
|---|---|
Reel._getAnchorRow(visibleRow) | Reel.getAnchorRow(visibleRow) |
ReelSet._peekTargetShape() | ReelSet.peekTargetShape() |
ReelSet._clearTargetShape() | ReelSet.clearTargetShape() |
Consumers should not call these directly. They are marked @internal and stripped from the published .d.ts (see next section).
Internal methods stripped from .d.ts (stripInternal)
tsconfig.json now has "stripInternal": true. Methods marked @internal in JSDoc remain in the runtime JS (so cross-file internal callers still work) but are absent from the published .d.ts. TypeScript consumers no longer see them on autocomplete.
Affected methods on Reel:
setStopFrame,setCrossReelResolver,getAnchorRow,notifySpinStart,notifySpinEnd,notifyLanded,snapToGrid,reshape.
If your code typechecked against Reel.reshape(...) or any of the above, switch to the public alternative:
- For reshape:
reelSet.setShape([...]). it gates on a MultiWays slot and migrates pins atomically. - For grid inspection:
reelSet.getVisibleGrid(),reelSet.getSymbolFootprint(),reelSet.getBlockBounds().
skip is now skipSpin
skipSpin() reads cleanly alongside skipNudge(). slamStop() stays distinct (unconditional land-now, no boost).
Before:
// Pre-1.0 code. reelSet.skip() is removed in 1.0.
reelSet.skip();
After:
reelSet.skipSpin();
The new naming makes the three actions readable side by side:
reelSet.skipSpin(); // Round-aware spin land. Applies the boost / auto-slam side effect.
reelSet.skipNudge(); // Fast-forward an in-flight nudge() to its landed position.
reelSet.slamStop(); // Unconditional land-now. No boost. For tests, anti-cheat.
refill() returns RefillResult, accepts a signal
reelSet.refill() now takes a typed RefillOptions object and returns a typed RefillResult (mirroring runCascade’s shape) instead of the misnamed SpinResult. The shape change is purely additive at the call boundary (existing callers that discard the return value keep working).
Before:
// Pre-1.0. refill returned SpinResult.
const result: SpinResult = await reelSet.refill({ winners, grid });
console.log(result.symbols); // string[][]
After:
import type { RefillOptions, RefillResult } from 'pixi-reels';
const result: RefillResult = await reelSet.refill({
winners,
grid,
signal: ac.signal, // new: abort mid-refill, the await unblocks immediately
});
console.log(result.finalGrid, result.winnersRefilled, result.wasSkipped);
The new fields (winnersRefilled, finalGrid, wasSkipped, duration) match RunCascadeResult. one stage’s worth.
Testing utilities moved to pixi-reels/testing
The headless harness is now a separate subpath export so production bundles never pull it in, even with imperfect tree-shaking.
Before:
// Pre-1.0. Removed from the main entry in 1.0.
import {
createTestReelSet,
FakeTicker,
HeadlessSymbol,
spinAndLand,
captureEvents,
expectGrid,
countSymbol,
} from 'pixi-reels';
After:
import {
createTestReelSet,
FakeTicker,
HeadlessSymbol,
spinAndLand,
captureEvents,
expectGrid,
countSymbol,
} from 'pixi-reels/testing';
The types (TestReelSetOptions, TestReelSetHandle) move with them. The Spine subpath (pixi-reels/spine) is unchanged.
Concurrent calls during nudge() now throw
spin(), setResult(), pin(), and setShape() throw when called while an in-flight nudge() is still tweening. The contract is now explicit: await the nudge() promise before calling any of those methods.
Before (undefined behavior, no error):
const nudging = reelSet.nudge(2, { distance: 1, direction: 'down', incoming: ['wild'] });
// reelSet.spin() here used to race the nudge tween silently.
After (the engine throws “ReelSet.spin: cannot be called while nudge() is in flight.”):
const nudging = reelSet.nudge(2, { distance: 1, direction: 'down', incoming: ['wild'] });
await nudging;
await reelSet.spin();
If you have UI code that allows the player to press spin during a nudge, route through reelSet.skipNudge() first to fast-forward the nudge to its landed position, then call spin().
symbol:recycled event removed
The 'symbol:recycled' field on the per-Reel ReelEvents interface is removed. It was never emitted by any code path. If you had a listener registered for it, delete the listener.
/guides/spine-pins/ renamed to /guides/pins/
The pin primitive is general (works with SpriteSymbol, AnimatedSpriteSymbol, SpineSymbol, or any subclass of ReelSymbol); the old URL is misleading. The new canonical URL is /guides/pins/. The site emits a permanent redirect from the old path, so existing bookmarks keep working.