js-game-zero 0.0.0-stage → 0.3.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.
- package/LICENSE +22 -0
- package/README.md +138 -2
- package/dist/active-service.d.ts +21 -0
- package/dist/actor.d.ts +182 -0
- package/dist/animation.d.ts +61 -0
- package/dist/assets.d.ts +148 -0
- package/dist/audio.d.ts +150 -0
- package/dist/browser-asset-backends.d.ts +8 -0
- package/dist/browser.d.ts +10 -0
- package/dist/chunk-brk0rjcy.js +7846 -0
- package/dist/clock.d.ts +29 -0
- package/dist/color.d.ts +15 -0
- package/dist/events.d.ts +145 -0
- package/dist/friendly-names.d.ts +43 -0
- package/dist/geometry.d.ts +125 -0
- package/dist/helpers.d.ts +28 -0
- package/dist/host.d.ts +58 -0
- package/dist/host.js +435 -0
- package/dist/index.d.ts +340 -0
- package/dist/index.js +258 -0
- package/dist/input.d.ts +200 -0
- package/dist/inspection.d.ts +58 -0
- package/dist/jgz.js +8419 -0
- package/dist/node.d.ts +14 -0
- package/dist/node.js +145 -0
- package/dist/pixel-density.d.ts +23 -0
- package/dist/random.d.ts +31 -0
- package/dist/reference/actor.md +197 -0
- package/dist/reference/audio.md +128 -0
- package/dist/reference/clock.md +150 -0
- package/dist/reference/helpers.md +46 -0
- package/dist/reference/hooks.md +379 -0
- package/dist/reference/index.d.ts +2 -0
- package/dist/reference/index.js +39 -0
- package/dist/reference/loaders.md +164 -0
- package/dist/reference/rect.md +129 -0
- package/dist/reference/runtime.md +478 -0
- package/dist/reference/screen.md +313 -0
- package/dist/reference/storage.md +90 -0
- package/dist/rendering.d.ts +188 -0
- package/dist/runtime-contracts.d.ts +117 -0
- package/dist/runtime-diagnostics.d.ts +37 -0
- package/dist/runtime-persistence.d.ts +32 -0
- package/dist/runtime-source.d.ts +82 -0
- package/dist/runtime.d.ts +81 -0
- package/dist/source-scan.d.ts +49 -0
- package/dist/storage-path.d.ts +3 -0
- package/dist/storage.d.ts +88 -0
- package/dist/tone.d.ts +81 -0
- package/dist/touch-controls.d.ts +24 -0
- package/dist/touch-pointer.d.ts +11 -0
- package/dist/util.d.ts +62 -0
- package/package.json +69 -4
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { AssetLoaders, AssetUrls, FontAsset, MusicAsset, SoundAsset, SurfaceLike } from "./assets.js";
|
|
2
|
+
import type { InputHooks, MappedKeyboardEvent, MappedMouseEvent } from "./events.js";
|
|
3
|
+
import type { Storage, StorageAdapter } from "./storage.js";
|
|
4
|
+
import type { TouchControlsConfig } from "./touch-controls.js";
|
|
5
|
+
export type GameModule = InputHooks & {
|
|
6
|
+
readonly WIDTH?: number;
|
|
7
|
+
readonly HEIGHT?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Canvas pixels per logical pixel. Defaults to the device's pixel ratio,
|
|
10
|
+
* at most 3, so the game is sharp on high-density screens.
|
|
11
|
+
*/
|
|
12
|
+
readonly PIXEL_DENSITY?: number;
|
|
13
|
+
readonly TITLE?: string;
|
|
14
|
+
readonly ICON?: string | null;
|
|
15
|
+
readonly TOUCH_CONTROLS?: TouchControlsConfig;
|
|
16
|
+
readonly draw?: () => void;
|
|
17
|
+
/** Called every frame with the seconds since the last one. */
|
|
18
|
+
readonly update?: (dt: number) => void;
|
|
19
|
+
};
|
|
20
|
+
export type RuntimeMode = "global" | "import";
|
|
21
|
+
export type RuntimeDiagnostic = {
|
|
22
|
+
readonly code: "asset-name" | "constant-typo" | "duplicate-config" | "hook-arity" | "hook-parameter" | "hook-typo" | "invalid-config" | "runtime-error";
|
|
23
|
+
readonly severity: "warning" | "error";
|
|
24
|
+
readonly message: string;
|
|
25
|
+
readonly name?: string;
|
|
26
|
+
readonly suggestion?: string;
|
|
27
|
+
readonly phase?: RuntimeFailurePhase;
|
|
28
|
+
/**
|
|
29
|
+
* The 1-based line in the game source the diagnostic is about, when known:
|
|
30
|
+
* where a `runtime-error` was thrown, or where a misspelled hook or
|
|
31
|
+
* setting is declared.
|
|
32
|
+
*/
|
|
33
|
+
readonly line?: number;
|
|
34
|
+
readonly recoverable?: boolean;
|
|
35
|
+
};
|
|
36
|
+
export type RuntimeFailurePhase = "startup" | "input" | "clock" | "update" | "draw" | "asset" | "storage" | "audio";
|
|
37
|
+
export type RuntimeFailure = {
|
|
38
|
+
readonly phase: RuntimeFailurePhase;
|
|
39
|
+
readonly message: string;
|
|
40
|
+
readonly cause: unknown;
|
|
41
|
+
/** 1-based line in the game source where the error was thrown, when the engine reports one. */
|
|
42
|
+
readonly line?: number;
|
|
43
|
+
};
|
|
44
|
+
export type GameHandleState = "starting" | "running" | "paused" | "failed" | "stopped";
|
|
45
|
+
export type RuntimeScheduler = {
|
|
46
|
+
requestAnimationFrame(callback: (timestamp: number) => void): unknown;
|
|
47
|
+
cancelAnimationFrame(handle: unknown): void;
|
|
48
|
+
now?(): number;
|
|
49
|
+
};
|
|
50
|
+
export type RuntimeCanvas = HTMLCanvasElement & {
|
|
51
|
+
getBoundingClientRect?: () => {
|
|
52
|
+
left: number;
|
|
53
|
+
top: number;
|
|
54
|
+
width?: number;
|
|
55
|
+
height?: number;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export type RunOptions = Partial<GameModule> & {
|
|
59
|
+
readonly assetBaseUrl?: string;
|
|
60
|
+
/**
|
|
61
|
+
* The game's assets by path (`images/cat.svg`), each with its URL, such as
|
|
62
|
+
* a data URL. When set, assets load only from here, never from
|
|
63
|
+
* `assetBaseUrl`, so a page can run a game with no server.
|
|
64
|
+
*/
|
|
65
|
+
readonly assetUrls?: AssetUrls;
|
|
66
|
+
readonly autostart?: boolean;
|
|
67
|
+
readonly canvas?: RuntimeCanvas;
|
|
68
|
+
readonly container?: HTMLElement;
|
|
69
|
+
readonly diagnostics?: (diagnostic: RuntimeDiagnostic) => void;
|
|
70
|
+
readonly keyboardTarget?: EventTarget;
|
|
71
|
+
/**
|
|
72
|
+
* Where mouse releases (`mouseup`) are heard. Defaults to `window`, so a
|
|
73
|
+
* drag released outside the canvas still ends; a host running a game it
|
|
74
|
+
* drives itself passes the canvas, so its synthetic releases reach no
|
|
75
|
+
* other game on the page.
|
|
76
|
+
*/
|
|
77
|
+
readonly mouseReleaseTarget?: EventTarget;
|
|
78
|
+
readonly preload?: readonly string[];
|
|
79
|
+
readonly preloadedAssets?: PreloadedAssets;
|
|
80
|
+
readonly projectId?: string;
|
|
81
|
+
readonly scheduler?: RuntimeScheduler;
|
|
82
|
+
/**
|
|
83
|
+
* Makes the game's randomness repeatable: `Math.random` as a zero-boilerplate
|
|
84
|
+
* game sees it, and `randomInt` and `randomChoice`, follow one sequence for
|
|
85
|
+
* this seed, the same one `jgz test --seed` uses. Only this game's: the
|
|
86
|
+
* page's `Math.random` and other games are untouched. Up to nine plain
|
|
87
|
+
* digits are the number they spell; anything else is hashed.
|
|
88
|
+
*/
|
|
89
|
+
readonly seed?: string | number;
|
|
90
|
+
readonly storage?: Storage | StorageAdapter;
|
|
91
|
+
/** Context-owned loader overrides for embedding hosts and deterministic tests. */
|
|
92
|
+
readonly loaders?: Partial<AssetLoaders>;
|
|
93
|
+
};
|
|
94
|
+
export type PreloadedAssets = {
|
|
95
|
+
readonly images?: Record<string, SurfaceLike>;
|
|
96
|
+
readonly sounds?: Record<string, SoundAsset>;
|
|
97
|
+
readonly music?: Record<string, MusicAsset>;
|
|
98
|
+
readonly fonts?: Record<string, FontAsset>;
|
|
99
|
+
};
|
|
100
|
+
export type GameKeyEvent = Readonly<MappedKeyboardEvent>;
|
|
101
|
+
export type GameMouseEvent = Readonly<MappedMouseEvent>;
|
|
102
|
+
export type DefinedGameModule = {
|
|
103
|
+
readonly WIDTH?: number;
|
|
104
|
+
readonly HEIGHT?: number;
|
|
105
|
+
readonly PIXEL_DENSITY?: number;
|
|
106
|
+
readonly TITLE?: string;
|
|
107
|
+
readonly ICON?: string | null;
|
|
108
|
+
readonly TOUCH_CONTROLS?: TouchControlsConfig;
|
|
109
|
+
readonly draw?: () => void;
|
|
110
|
+
readonly update?: (dt: number) => void;
|
|
111
|
+
readonly onKeyDown?: (event: GameKeyEvent) => void;
|
|
112
|
+
readonly onKeyUp?: (event: GameKeyEvent) => void;
|
|
113
|
+
readonly onMouseDown?: (event: GameMouseEvent) => void;
|
|
114
|
+
readonly onMouseUp?: (event: GameMouseEvent) => void;
|
|
115
|
+
readonly onMouseMove?: (event: GameMouseEvent) => void;
|
|
116
|
+
readonly onMusicEnd?: () => void;
|
|
117
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { GameModule, RunOptions, RuntimeDiagnostic, RuntimeMode } from "./runtime-contracts.js";
|
|
2
|
+
export declare const RUNTIME_CONFIGURATION_NAMES: readonly ["WIDTH", "HEIGHT", "PIXEL_DENSITY", "TITLE", "ICON", "TOUCH_CONTROLS"];
|
|
3
|
+
/** Every hook a zero-boilerplate script can define. */
|
|
4
|
+
export declare const RUNTIME_SOURCE_HOOK_NAMES: readonly ["draw", "update", "onMouseDown", "onMouseUp", "onMouseMove", "onKeyDown", "onKeyUp", "onMusicEnd"];
|
|
5
|
+
export declare const RUNTIME_MODULE_OPTION_NAMES: readonly ["WIDTH", "HEIGHT", "PIXEL_DENSITY", "TITLE", "ICON", "TOUCH_CONTROLS", "draw", "update", "onMouseDown", "onMouseUp", "onMouseMove", "onKeyDown", "onKeyUp", "onMusicEnd"];
|
|
6
|
+
export declare function collectRuntimeDiagnostics(moduleOrSource: GameModule | string, options?: RunOptions): RuntimeDiagnostic[];
|
|
7
|
+
export declare function diagnoseGameModule(module: GameModule, options: RunOptions, emit: (diagnostic: RuntimeDiagnostic) => void, mode: RuntimeMode,
|
|
8
|
+
/** run() stops a game whose draw() takes parameters, and says so: a
|
|
9
|
+
* warning as well would be the same mistake twice. */
|
|
10
|
+
drawArityIsFatal?: boolean): void;
|
|
11
|
+
/**
|
|
12
|
+
* Static checks on zero-boilerplate source. Typo checks only look at what the
|
|
13
|
+
* script defines at top level — a top-level function whose name is nearly a
|
|
14
|
+
* hook name, or a top-level assignment whose name is nearly a configuration
|
|
15
|
+
* constant — never at identifiers the script merely uses (`row`, `col`,
|
|
16
|
+
* `rows[y]`), and never when the real name is also defined (`draw` plus a
|
|
17
|
+
* `drawn` helper is not a typo). Asset-name checks scan every quoted string.
|
|
18
|
+
*/
|
|
19
|
+
export declare function diagnoseSource(source: string, emit: (diagnostic: RuntimeDiagnostic) => void): void;
|
|
20
|
+
/**
|
|
21
|
+
* The 1-based line where a game's source declares `name` at the start of a
|
|
22
|
+
* line: `function name(`, `name = ` or `let name = `.
|
|
23
|
+
*/
|
|
24
|
+
export declare function declarationLine(source: string, name: string): number | undefined;
|
|
25
|
+
export declare function createDiagnosticEmitter(diagnostics: RuntimeDiagnostic[], callback?: (diagnostic: RuntimeDiagnostic) => void): (diagnostic: RuntimeDiagnostic) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Ambient diagnostic sink for whichever run is active at call time, mirroring
|
|
28
|
+
* the active-service pattern used for loaders/storage/clock. Only valid for
|
|
29
|
+
* the duration of the synchronous call that reads it — callers whose work
|
|
30
|
+
* continues asynchronously (e.g. a loader promise that may reject later)
|
|
31
|
+
* must capture the returned function and close over it rather than reading
|
|
32
|
+
* the ambient value again once the run may have moved on.
|
|
33
|
+
*/
|
|
34
|
+
export declare function setActiveDiagnosticEmit(emit: ((diagnostic: RuntimeDiagnostic) => void) | null): ((diagnostic: RuntimeDiagnostic) => void) | null;
|
|
35
|
+
export declare function getActiveDiagnosticEmit(): ((diagnostic: RuntimeDiagnostic) => void) | null;
|
|
36
|
+
export declare function describeRuntimeError(error: unknown): string;
|
|
37
|
+
export declare function isValidRuntimeDimension(value: unknown): value is number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Storage } from "./storage.js";
|
|
2
|
+
type PersistenceDiagnostic = {
|
|
3
|
+
readonly code: "runtime-error";
|
|
4
|
+
readonly severity: "warning";
|
|
5
|
+
readonly phase: "storage";
|
|
6
|
+
readonly recoverable: true;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
};
|
|
9
|
+
type PersistenceDiagnosticEmitter = (diagnostic: PersistenceDiagnostic) => void;
|
|
10
|
+
export type Autosave = {
|
|
11
|
+
/**
|
|
12
|
+
* Cancels any pending debounce timer and saves immediately, if anything
|
|
13
|
+
* changed since the last save or load, inside stored objects and lists too.
|
|
14
|
+
*/
|
|
15
|
+
flush(): Promise<void>;
|
|
16
|
+
/** Unsubscribes from storage changes and browser lifecycle events. */
|
|
17
|
+
detach(): void;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Coordinates debounced and lifecycle-triggered persistence for one runtime.
|
|
21
|
+
* Writes are serialized and mutations that arrive while a save is active are
|
|
22
|
+
* folded into a subsequent write.
|
|
23
|
+
*/
|
|
24
|
+
export declare function attachAutosave(storage: Storage, emit: PersistenceDiagnosticEmitter): Autosave;
|
|
25
|
+
/** Loads exactly once, preferring an adapter's truly synchronous path. */
|
|
26
|
+
export declare function primeStorage(storage: Storage, emit: PersistenceDiagnosticEmitter): {
|
|
27
|
+
state: {
|
|
28
|
+
current: boolean;
|
|
29
|
+
};
|
|
30
|
+
promise: Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { AssetLoaders } from "./assets.js";
|
|
2
|
+
import type { MusicPlayer } from "./audio.js";
|
|
3
|
+
import { Clock } from "./clock.js";
|
|
4
|
+
import { type KeyboardState, type MouseState } from "./input.js";
|
|
5
|
+
import { type RandomSource } from "./random.js";
|
|
6
|
+
import { Screen } from "./rendering.js";
|
|
7
|
+
import type { GameModule, RuntimeDiagnostic } from "./runtime-contracts.js";
|
|
8
|
+
import { Storage } from "./storage.js";
|
|
9
|
+
import type { ToneGenerator } from "./tone.js";
|
|
10
|
+
export type SourceEvaluationContext = {
|
|
11
|
+
readonly screen: Screen;
|
|
12
|
+
readonly clock: Clock;
|
|
13
|
+
readonly keyboard: KeyboardState;
|
|
14
|
+
readonly mouseState: MouseState;
|
|
15
|
+
readonly loaders: AssetLoaders;
|
|
16
|
+
readonly music: MusicPlayer;
|
|
17
|
+
readonly storage: Storage;
|
|
18
|
+
readonly tone: ToneGenerator;
|
|
19
|
+
/** The run's seeded random source, or null to leave `Math` as it is. */
|
|
20
|
+
readonly random: RandomSource | null;
|
|
21
|
+
stop: (() => void) | null;
|
|
22
|
+
};
|
|
23
|
+
export type SourceInspector = {
|
|
24
|
+
/** Non-function top-level declarations and assignments. */
|
|
25
|
+
bindings(): readonly string[];
|
|
26
|
+
/** Top-level function declarations and function-valued bindings. */
|
|
27
|
+
functions(): readonly string[];
|
|
28
|
+
read(name: string): unknown;
|
|
29
|
+
};
|
|
30
|
+
type SourceEvaluation = {
|
|
31
|
+
readonly module: GameModule;
|
|
32
|
+
readonly inspector: SourceInspector;
|
|
33
|
+
};
|
|
34
|
+
/** Evaluates one zero-boilerplate script inside its run-owned service scope. */
|
|
35
|
+
export declare function executeZeroBoilerplateSource(source: string, context: SourceEvaluationContext, emit: (diagnostic: RuntimeDiagnostic) => void): SourceEvaluation;
|
|
36
|
+
/**
|
|
37
|
+
* The one line of `source` that reads `name` (outside strings and comments,
|
|
38
|
+
* and not as a property after a dot), or undefined when it's on none or on
|
|
39
|
+
* more than one.
|
|
40
|
+
*/
|
|
41
|
+
export declare function onlyLineOf(name: string, source: string): number | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* How this engine numbers lines in `new Function` code: from the place of a
|
|
44
|
+
* stack frame (V8, SpiderMonkey), or, where a frame there has no location
|
|
45
|
+
* (JavaScriptCore), from the error's own `line`.
|
|
46
|
+
*/
|
|
47
|
+
export type FrameCalibration = {
|
|
48
|
+
readonly kind: "stack";
|
|
49
|
+
readonly marker: string;
|
|
50
|
+
readonly offset: number;
|
|
51
|
+
} | {
|
|
52
|
+
readonly kind: "line";
|
|
53
|
+
readonly offset: number;
|
|
54
|
+
/** The `sourceURL` errors from game code carry: none, on JavaScriptCore. */
|
|
55
|
+
readonly url: string | undefined;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The calibration a throw from the first line of game source gives. The
|
|
59
|
+
* throwing frame is the stack's first frame, after V8's `Error: message` line:
|
|
60
|
+
* when it has a location, its place marks game frames; when it has none
|
|
61
|
+
* (JavaScriptCore's `anonymous@`), the error's own `line` is used instead.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readCalibration(error: unknown): FrameCalibration | null;
|
|
64
|
+
/** The game source's line for `error` under `calibration`, or undefined when it wasn't thrown there. */
|
|
65
|
+
export declare function lineWith(calibration: FrameCalibration, error: unknown): number | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* The 1-based line of the syntax error `message` in `source`, for engines that
|
|
68
|
+
* give none: the first line whose prefix of the source fails with the same
|
|
69
|
+
* message. An error at the end of the source (something left open) is placed
|
|
70
|
+
* on the line after the last prefix that compiles, where the open part starts.
|
|
71
|
+
*/
|
|
72
|
+
export declare function syntaxErrorLine(source: string, message: string): number | undefined;
|
|
73
|
+
/** Records the source line of a mistake the runtime itself finds in a
|
|
74
|
+
* game, which no stack trace could place. */
|
|
75
|
+
export declare function noteSourceLine(error: object, line: number): void;
|
|
76
|
+
/**
|
|
77
|
+
* The 1-based line in the game source where `error` was thrown, or undefined
|
|
78
|
+
* when it wasn't thrown in game code (the error came from the library, or the
|
|
79
|
+
* engine gives no usable position).
|
|
80
|
+
*/
|
|
81
|
+
export declare function sourceLineOf(error: unknown): number | undefined;
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Actor } from "./actor.js";
|
|
2
|
+
import { Animation, animate } from "./animation.js";
|
|
3
|
+
import { type AssetLoaders } from "./assets.js";
|
|
4
|
+
import { Clock } from "./clock.js";
|
|
5
|
+
import { Rect, ZRect } from "./geometry.js";
|
|
6
|
+
import { KeyboardState, keymods, keys, type LiveMouse } from "./input.js";
|
|
7
|
+
import { createMusicPlayer } from "./audio.js";
|
|
8
|
+
import { Screen } from "./rendering.js";
|
|
9
|
+
import { Storage } from "./storage.js";
|
|
10
|
+
import { createToneGenerator } from "./tone.js";
|
|
11
|
+
import { type SourceInspector } from "./runtime-source.js";
|
|
12
|
+
import type { DefinedGameModule, GameHandleState, GameModule, RunOptions, RuntimeCanvas, RuntimeDiagnostic, RuntimeFailure, RuntimeMode } from "./runtime-contracts.js";
|
|
13
|
+
export type { DefinedGameModule, GameHandleState, GameKeyEvent, GameModule, GameMouseEvent, PreloadedAssets, RunOptions, RuntimeCanvas, RuntimeDiagnostic, RuntimeFailure, RuntimeFailurePhase, RuntimeMode, RuntimeScheduler, } from "./runtime-contracts.js";
|
|
14
|
+
export { collectRuntimeDiagnostics } from "./runtime-diagnostics.js";
|
|
15
|
+
export type GameContext = {
|
|
16
|
+
readonly Actor: typeof Actor;
|
|
17
|
+
readonly Animation: typeof Animation;
|
|
18
|
+
readonly Rect: typeof Rect;
|
|
19
|
+
readonly ZRect: typeof ZRect;
|
|
20
|
+
readonly Screen: typeof Screen;
|
|
21
|
+
readonly Storage: typeof Storage;
|
|
22
|
+
readonly screen: Screen;
|
|
23
|
+
readonly clock: Clock;
|
|
24
|
+
readonly keyboard: KeyboardState;
|
|
25
|
+
readonly keymods: typeof keymods;
|
|
26
|
+
readonly keys: typeof keys;
|
|
27
|
+
readonly mouse: LiveMouse;
|
|
28
|
+
readonly images: AssetLoaders["images"];
|
|
29
|
+
readonly sounds: AssetLoaders["sounds"];
|
|
30
|
+
readonly music: ReturnType<typeof createMusicPlayer>;
|
|
31
|
+
readonly fonts: AssetLoaders["fonts"];
|
|
32
|
+
readonly storage: Storage;
|
|
33
|
+
readonly tone: ReturnType<typeof createToneGenerator>;
|
|
34
|
+
readonly animate: typeof animate;
|
|
35
|
+
readonly exit: typeof exit;
|
|
36
|
+
};
|
|
37
|
+
declare const definedGameBrand: unique symbol;
|
|
38
|
+
export type DefinedGame = {
|
|
39
|
+
readonly [definedGameBrand]: true;
|
|
40
|
+
readonly create: (context: GameContext) => DefinedGameModule;
|
|
41
|
+
};
|
|
42
|
+
export declare function defineGame(factory: (context: GameContext) => DefinedGameModule): DefinedGame;
|
|
43
|
+
export type GameHandle = {
|
|
44
|
+
readonly mode: RuntimeMode;
|
|
45
|
+
readonly canvas: RuntimeCanvas;
|
|
46
|
+
readonly screen: Screen;
|
|
47
|
+
readonly clock: Clock;
|
|
48
|
+
readonly keyboard: KeyboardState;
|
|
49
|
+
readonly loaders: AssetLoaders;
|
|
50
|
+
readonly storage: Storage;
|
|
51
|
+
readonly diagnostics: readonly RuntimeDiagnostic[];
|
|
52
|
+
readonly module: GameModule;
|
|
53
|
+
readonly state: GameHandleState;
|
|
54
|
+
readonly failure: RuntimeFailure | null;
|
|
55
|
+
/**
|
|
56
|
+
* Resolves once the "preload gate" is satisfied: storage has been
|
|
57
|
+
* loaded and every asset requested while the game module was
|
|
58
|
+
* evaluated (Actor constructions, `images.x` touches, etc.) has
|
|
59
|
+
* settled. The runtime itself already holds frames until this is
|
|
60
|
+
* true; this is exposed for tests and host integrations that want to
|
|
61
|
+
* await readiness explicitly instead of polling `step()`.
|
|
62
|
+
*/
|
|
63
|
+
readonly ready: Promise<void>;
|
|
64
|
+
readonly done: Promise<void>;
|
|
65
|
+
pause(): void;
|
|
66
|
+
resume(): void;
|
|
67
|
+
step(dt: number): void;
|
|
68
|
+
stop(): void;
|
|
69
|
+
dispose(): Promise<void>;
|
|
70
|
+
restart(source?: GameModule | string | DefinedGame): Promise<GameHandle>;
|
|
71
|
+
/** @deprecated Prefer restart(), which awaits cleanup before starting again. */
|
|
72
|
+
reload(source?: GameModule | string | DefinedGame): GameHandle;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* The source bindings of a live zero-boilerplate game, or null. Hosts use
|
|
76
|
+
* `getGameInspector` from `js-game-zero/host`, which reads paths and
|
|
77
|
+
* serialises values on top of this.
|
|
78
|
+
*/
|
|
79
|
+
export declare function getSourceInspector(handle: GameHandle): SourceInspector | null;
|
|
80
|
+
export declare function run(moduleOrSource: GameModule | string | DefinedGame, options?: RunOptions): GameHandle;
|
|
81
|
+
export declare function exit(): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small lexical scan of zero-boilerplate game source, used by the typo
|
|
3
|
+
* diagnostics to find the names a script actually *defines* at top level
|
|
4
|
+
* (as opposed to every identifier it merely mentions). It is deliberately
|
|
5
|
+
* not a parser: it tokenizes comments, strings, template literals (with
|
|
6
|
+
* nested substitutions), regex literals, identifiers and punctuators while
|
|
7
|
+
* tracking brace depth, and pattern-matches definitions on the token stream.
|
|
8
|
+
*
|
|
9
|
+
* On anything it cannot make sense of (an unterminated string, comment,
|
|
10
|
+
* template or regex, or unbalanced braces/parens) the scan reports itself
|
|
11
|
+
* incomplete and callers emit nothing: no diagnostic beats a wrong one.
|
|
12
|
+
*/
|
|
13
|
+
export type SourceTokenType = "identifier" | "punctuator" | "literal";
|
|
14
|
+
export type SourceToken = {
|
|
15
|
+
readonly type: SourceTokenType;
|
|
16
|
+
readonly value: string;
|
|
17
|
+
/** Brace depth at the token: 0 means outside every `{…}` and `${…}`. */
|
|
18
|
+
readonly depth: number;
|
|
19
|
+
/** Parenthesis depth at the token: 0 means outside every `(…)`. */
|
|
20
|
+
readonly parens: number;
|
|
21
|
+
/** Square-bracket depth at the token: 0 means outside every `[…]`. */
|
|
22
|
+
readonly brackets: number;
|
|
23
|
+
};
|
|
24
|
+
export type SourceScan = {
|
|
25
|
+
/** False when the tokenizer gave up; the name lists are then empty. */
|
|
26
|
+
readonly complete: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Names given a function value at depth 0: `function NAME(`, and
|
|
29
|
+
* `NAME = function…`, `NAME = async …`, `NAME = (…) =>`, `NAME = x =>`
|
|
30
|
+
* (with or without `const`/`let`/`var`).
|
|
31
|
+
*/
|
|
32
|
+
readonly topLevelFunctions: readonly string[];
|
|
33
|
+
/** Depth-0 assignment and declaration targets (`NAME = …`, `let NAME`). */
|
|
34
|
+
readonly topLevelAssignments: readonly string[];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Splits `source` into tokens, or returns null when it cannot be tokenized
|
|
38
|
+
* confidently. Comments vanish; strings, templates, regexes and numbers
|
|
39
|
+
* become `literal` tokens (the code inside template substitutions is
|
|
40
|
+
* tokenized normally, one brace deeper than the template).
|
|
41
|
+
*/
|
|
42
|
+
export declare function tokenizeSource(source: string,
|
|
43
|
+
/** Return the tokens of source that leaves a bracket open at the end,
|
|
44
|
+
* rather than null, to find out what's open. */
|
|
45
|
+
{ allowUnclosed }?: {
|
|
46
|
+
readonly allowUnclosed?: boolean;
|
|
47
|
+
}): SourceToken[] | null;
|
|
48
|
+
/** Finds the names `source` defines at top level; see {@link SourceScan}. */
|
|
49
|
+
export declare function scanSource(source: string): SourceScan;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
2
|
+
readonly [key: string]: JsonValue;
|
|
3
|
+
};
|
|
4
|
+
export type StorageAdapter = {
|
|
5
|
+
readonly path: string;
|
|
6
|
+
load(): Promise<string | null>;
|
|
7
|
+
loadSync?(): string | null;
|
|
8
|
+
save(text: string): Promise<void> | void;
|
|
9
|
+
};
|
|
10
|
+
export type BrowserStorageLike = {
|
|
11
|
+
getItem(key: string): string | null;
|
|
12
|
+
setItem(key: string, value: string): void;
|
|
13
|
+
};
|
|
14
|
+
export type LocalStorageAdapterOptions = {
|
|
15
|
+
readonly projectId?: string;
|
|
16
|
+
readonly path?: string;
|
|
17
|
+
readonly localStorage?: BrowserStorageLike;
|
|
18
|
+
};
|
|
19
|
+
export type StorageOptions = {
|
|
20
|
+
readonly adapter?: StorageAdapter;
|
|
21
|
+
readonly projectId?: string;
|
|
22
|
+
readonly path?: string;
|
|
23
|
+
};
|
|
24
|
+
export declare class JsonStorageError extends Error {
|
|
25
|
+
readonly path: string;
|
|
26
|
+
constructor(path: string, message: string);
|
|
27
|
+
}
|
|
28
|
+
/** Keyed by a symbol so no storage key can collide with it. */
|
|
29
|
+
declare const savedText: unique symbol;
|
|
30
|
+
export declare class Storage {
|
|
31
|
+
private readonly adapter;
|
|
32
|
+
private readonly values;
|
|
33
|
+
private readonly listeners;
|
|
34
|
+
/** A load() that loadSync() started and found to be asynchronous. */
|
|
35
|
+
private pendingLoad;
|
|
36
|
+
/** The JSON last saved or loaded: what the adapter holds, as far as this storage knows. */
|
|
37
|
+
[savedText]: string;
|
|
38
|
+
constructor(options?: StorageOptions);
|
|
39
|
+
get path(): string;
|
|
40
|
+
get(key: string, defaultValue?: unknown): unknown;
|
|
41
|
+
set(key: string, value: unknown): void;
|
|
42
|
+
setDefault(key: string, defaultValue: unknown): unknown;
|
|
43
|
+
has(key: string): boolean;
|
|
44
|
+
delete(key: string): boolean;
|
|
45
|
+
clear(): void;
|
|
46
|
+
keys(): string[];
|
|
47
|
+
toJSON(): Record<string, JsonValue>;
|
|
48
|
+
/**
|
|
49
|
+
* Subscribes to any mutation (set/delete/clear/setDefault-insert),
|
|
50
|
+
* including mutations made through the PGZero-style proxy (e.g.
|
|
51
|
+
* `storage.score = 5`). Returns an unsubscribe function. Used by the
|
|
52
|
+
* runtime to drive debounced auto-save; loading does not notify.
|
|
53
|
+
*/
|
|
54
|
+
onChange(listener: () => void): () => void;
|
|
55
|
+
save(): Promise<void>;
|
|
56
|
+
load(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Loads synchronously when the adapter can: with its `loadSync()`, or
|
|
59
|
+
* with a `load()` that returns its text rather than a Promise. A `load()`
|
|
60
|
+
* that turns out to be asynchronous is kept for `load()` to finish, so
|
|
61
|
+
* falling back to it still reads the store only once.
|
|
62
|
+
*/
|
|
63
|
+
loadSync(): boolean;
|
|
64
|
+
private applyLoadedText;
|
|
65
|
+
private notify;
|
|
66
|
+
private jsonObject;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Whether `storage` holds anything its last save or load didn't, including a
|
|
70
|
+
* change inside a stored object or list (`storage.highScores.push(...)`),
|
|
71
|
+
* which no set() announces. Throws a JsonStorageError, naming the key, when
|
|
72
|
+
* a stored value has since become one JSON can't hold.
|
|
73
|
+
*/
|
|
74
|
+
export declare function hasUnsavedChanges(storage: Storage): boolean;
|
|
75
|
+
export declare function createStorage(options?: StorageOptions): Storage;
|
|
76
|
+
export declare function createLocalStorageAdapter(options?: LocalStorageAdapterOptions): StorageAdapter;
|
|
77
|
+
export declare function validateJsonValue(value: unknown, path?: string): void;
|
|
78
|
+
/** Default backing instance used when no runtime-scoped storage is active. */
|
|
79
|
+
export declare const defaultStorage: Storage;
|
|
80
|
+
export declare function setActiveStorage(nextStorage: Storage | null): Storage | null;
|
|
81
|
+
export declare function getActiveStorage(): Storage;
|
|
82
|
+
/**
|
|
83
|
+
* PGZero-style process-level convenience proxy. Runtime callbacks scope this
|
|
84
|
+
* proxy to their own Storage instance; outside a runtime it uses a default
|
|
85
|
+
* local-storage-backed instance.
|
|
86
|
+
*/
|
|
87
|
+
export declare const storage: Storage;
|
|
88
|
+
export {};
|
package/dist/tone.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type AudioUnlockState } from "./audio.js";
|
|
2
|
+
export type Waveform = OscillatorType;
|
|
3
|
+
/** pgzero's tone.Waveform names that don't match the Web Audio OscillatorType spelling. */
|
|
4
|
+
export type WaveformAlias = "sin" | "saw";
|
|
5
|
+
export type WaveformInput = Waveform | WaveformAlias;
|
|
6
|
+
export type AudioParamLike = {
|
|
7
|
+
value: number;
|
|
8
|
+
setValueAtTime?: (value: number, startTime: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export type OscillatorNodeLike = {
|
|
11
|
+
type: Waveform;
|
|
12
|
+
frequency: AudioParamLike;
|
|
13
|
+
connect(destination: unknown): unknown;
|
|
14
|
+
start(when?: number): void;
|
|
15
|
+
stop(when?: number): void;
|
|
16
|
+
};
|
|
17
|
+
export type GainNodeLike = {
|
|
18
|
+
gain: AudioParamLike;
|
|
19
|
+
connect(destination: unknown): unknown;
|
|
20
|
+
};
|
|
21
|
+
export type AudioContextLike = {
|
|
22
|
+
readonly destination: unknown;
|
|
23
|
+
currentTime: number;
|
|
24
|
+
state?: string;
|
|
25
|
+
resume?: () => Promise<void>;
|
|
26
|
+
createOscillator(): OscillatorNodeLike;
|
|
27
|
+
createGain(): GainNodeLike;
|
|
28
|
+
};
|
|
29
|
+
export type ToneOptions = {
|
|
30
|
+
readonly audioContext?: AudioContextLike;
|
|
31
|
+
readonly unlockState?: AudioUnlockState;
|
|
32
|
+
readonly waveform?: WaveformInput;
|
|
33
|
+
readonly volume?: number;
|
|
34
|
+
};
|
|
35
|
+
export type TonePlayOptions = Omit<ToneOptions, "audioContext" | "unlockState">;
|
|
36
|
+
/** pgzero's tone module defaults to volume 0.8 (see pgzero.tone._convert_args). */
|
|
37
|
+
export declare const DEFAULT_TONE_VOLUME = 0.8;
|
|
38
|
+
export type ToneSpec = {
|
|
39
|
+
readonly frequency: number;
|
|
40
|
+
readonly duration: number;
|
|
41
|
+
readonly waveform: Waveform;
|
|
42
|
+
readonly volume: number;
|
|
43
|
+
};
|
|
44
|
+
export type TonePlayback = ToneSpec & {
|
|
45
|
+
readonly startedAt: number;
|
|
46
|
+
readonly stoppedAt: number;
|
|
47
|
+
};
|
|
48
|
+
export type ToneDebugState = {
|
|
49
|
+
readonly unlocked: boolean;
|
|
50
|
+
readonly contextState: string | null;
|
|
51
|
+
readonly scheduled: readonly TonePlayback[];
|
|
52
|
+
};
|
|
53
|
+
export declare class CreatedTone implements ToneSpec {
|
|
54
|
+
readonly frequency: number;
|
|
55
|
+
readonly duration: number;
|
|
56
|
+
readonly waveform: Waveform;
|
|
57
|
+
readonly volume: number;
|
|
58
|
+
private readonly generator;
|
|
59
|
+
constructor(generator: ToneGenerator, spec: ToneSpec);
|
|
60
|
+
play(options?: TonePlayOptions): Promise<TonePlayback>;
|
|
61
|
+
}
|
|
62
|
+
export declare class ToneGenerator {
|
|
63
|
+
private contextValue;
|
|
64
|
+
private readonly unlockState;
|
|
65
|
+
private readonly scheduledTones;
|
|
66
|
+
constructor(options?: ToneOptions);
|
|
67
|
+
create(noteOrFrequency: string | number, duration?: number, options?: TonePlayOptions): CreatedTone;
|
|
68
|
+
play(noteOrFrequency: string | number, duration?: number, options?: TonePlayOptions): Promise<TonePlayback>;
|
|
69
|
+
private start;
|
|
70
|
+
unlock(): Promise<boolean>;
|
|
71
|
+
isUnlocked(): boolean;
|
|
72
|
+
debugState(): ToneDebugState;
|
|
73
|
+
private createSpec;
|
|
74
|
+
private getContext;
|
|
75
|
+
}
|
|
76
|
+
export declare function createToneGenerator(options?: ToneOptions): ToneGenerator;
|
|
77
|
+
export declare const tone: ToneGenerator;
|
|
78
|
+
export declare function parseToneFrequency(noteOrFrequency: string | number): number;
|
|
79
|
+
export declare function noteToFrequency(noteName: string): number;
|
|
80
|
+
export declare function requireToneDuration(duration: number): number;
|
|
81
|
+
export declare function normalizeWaveform(waveform: WaveformInput): Waveform;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { InputDispatcher } from "./events.js";
|
|
2
|
+
export type TouchButton = "up" | "down" | "left" | "right" | "a" | "b" | "start" | "select";
|
|
3
|
+
export type TouchControlsOptions = Partial<Record<TouchButton, number>> & {
|
|
4
|
+
/** Auto shows controls on devices with touch input; always also shows them on desktop. */
|
|
5
|
+
readonly show?: "auto" | "always";
|
|
6
|
+
};
|
|
7
|
+
export type TouchControlsConfig = boolean | TouchControlsOptions;
|
|
8
|
+
export type TouchControlsSettings = {
|
|
9
|
+
readonly show: "auto" | "always";
|
|
10
|
+
readonly buttons: ReadonlyArray<readonly [TouchButton, number]>;
|
|
11
|
+
};
|
|
12
|
+
export declare function normalizeTouchControls(value: unknown): TouchControlsSettings | null;
|
|
13
|
+
/**
|
|
14
|
+
* Mount a responsive control dock at the bottom of the game's available
|
|
15
|
+
* space. The canvas is fitted by its logical `screen` size, since density
|
|
16
|
+
* multiplies its pixel size.
|
|
17
|
+
*/
|
|
18
|
+
export declare function mountTouchControls(canvas: HTMLCanvasElement, settings: TouchControlsSettings | null, dispatcher: InputDispatcher, invoke: (callback: () => void) => void, screen?: {
|
|
19
|
+
readonly width: number;
|
|
20
|
+
readonly height: number;
|
|
21
|
+
}): {
|
|
22
|
+
reset(): void;
|
|
23
|
+
detach(): void;
|
|
24
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InputDispatcher } from "./events.js";
|
|
2
|
+
type PointerTarget = Parameters<InputDispatcher["handleMouseDown"]>[1];
|
|
3
|
+
/**
|
|
4
|
+
* Touch and pen use the existing mouse hooks, with one canvas pointer at a
|
|
5
|
+
* time. Positions map onto `target`, which reports the logical screen size.
|
|
6
|
+
*/
|
|
7
|
+
export declare function attachTouchPointer(canvas: HTMLCanvasElement, dispatcher: InputDispatcher, invoke: (callback: () => void) => void, target?: PointerTarget): {
|
|
8
|
+
reset(): void;
|
|
9
|
+
detach(): void;
|
|
10
|
+
};
|
|
11
|
+
export {};
|