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.
Files changed (53) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +138 -2
  3. package/dist/active-service.d.ts +21 -0
  4. package/dist/actor.d.ts +182 -0
  5. package/dist/animation.d.ts +61 -0
  6. package/dist/assets.d.ts +148 -0
  7. package/dist/audio.d.ts +150 -0
  8. package/dist/browser-asset-backends.d.ts +8 -0
  9. package/dist/browser.d.ts +10 -0
  10. package/dist/chunk-brk0rjcy.js +7846 -0
  11. package/dist/clock.d.ts +29 -0
  12. package/dist/color.d.ts +15 -0
  13. package/dist/events.d.ts +145 -0
  14. package/dist/friendly-names.d.ts +43 -0
  15. package/dist/geometry.d.ts +125 -0
  16. package/dist/helpers.d.ts +28 -0
  17. package/dist/host.d.ts +58 -0
  18. package/dist/host.js +435 -0
  19. package/dist/index.d.ts +340 -0
  20. package/dist/index.js +258 -0
  21. package/dist/input.d.ts +200 -0
  22. package/dist/inspection.d.ts +58 -0
  23. package/dist/jgz.js +8419 -0
  24. package/dist/node.d.ts +14 -0
  25. package/dist/node.js +145 -0
  26. package/dist/pixel-density.d.ts +23 -0
  27. package/dist/random.d.ts +31 -0
  28. package/dist/reference/actor.md +197 -0
  29. package/dist/reference/audio.md +128 -0
  30. package/dist/reference/clock.md +150 -0
  31. package/dist/reference/helpers.md +46 -0
  32. package/dist/reference/hooks.md +379 -0
  33. package/dist/reference/index.d.ts +2 -0
  34. package/dist/reference/index.js +39 -0
  35. package/dist/reference/loaders.md +164 -0
  36. package/dist/reference/rect.md +129 -0
  37. package/dist/reference/runtime.md +478 -0
  38. package/dist/reference/screen.md +313 -0
  39. package/dist/reference/storage.md +90 -0
  40. package/dist/rendering.d.ts +188 -0
  41. package/dist/runtime-contracts.d.ts +117 -0
  42. package/dist/runtime-diagnostics.d.ts +37 -0
  43. package/dist/runtime-persistence.d.ts +32 -0
  44. package/dist/runtime-source.d.ts +82 -0
  45. package/dist/runtime.d.ts +81 -0
  46. package/dist/source-scan.d.ts +49 -0
  47. package/dist/storage-path.d.ts +3 -0
  48. package/dist/storage.d.ts +88 -0
  49. package/dist/tone.d.ts +81 -0
  50. package/dist/touch-controls.d.ts +24 -0
  51. package/dist/touch-pointer.d.ts +11 -0
  52. package/dist/util.d.ts +62 -0
  53. 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,3 @@
1
+ export declare function normalizeStoragePath(path: string): string;
2
+ export declare function normalizeSandboxStoragePath(path: string): string;
3
+ export declare function sandboxStoragePath(projectRoot: string, safePath: string): string;
@@ -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 {};