@powly/sdk 0.4.1 → 0.8.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  The SDK for games on [Powly](https://powly.gg): the launch handshake with Powly, the player's
4
4
  profile, session tokens and the platform services (saves, leaderboards, achievements, friends,
5
- invites, multiplayer rooms, Twitch chat).
5
+ invites, multiplayer rooms, Twitch chat, sound settings).
6
6
 
7
7
  ## Install
8
8
 
@@ -31,7 +31,7 @@ await powly.leaderboards.submit("high-score", 1200);
31
31
  Without a bundler, load the classic script instead; it defines `window.Powly`:
32
32
 
33
33
  ```html
34
- <script src="https://cdn.jsdelivr.net/npm/@powly/sdk@0.4.1/dist/powly.js"></script>
34
+ <script src="https://cdn.jsdelivr.net/npm/@powly/sdk@0.8.0/dist/powly.js"></script>
35
35
  ```
36
36
 
37
37
  ## Testing
@@ -54,3 +54,18 @@ await Powly.init({ powlyOrigin: "https://games.example.com" });
54
54
  The game runs in the player's browser, so nothing here is a security boundary. Saves and
55
55
  leaderboard scores are written by your backend unless the studio allows browser writes for them;
56
56
  your backend verifies session tokens or exchanges them for an access token.
57
+
58
+ ## This repository
59
+
60
+ The SDK is developed in Powly's main repository and mirrored here with each release; this is
61
+ `@powly/sdk` 0.8.0. Issues are welcome; changes land through the main repository.
62
+
63
+ To build it yourself (Node.js 24 or newer):
64
+
65
+ ```bash
66
+ npm install
67
+ npm run build
68
+ ```
69
+
70
+ `dist/powly.mjs`, `dist/powly.js` and `dist/powly.d.ts` are what npm ships. `examples/` has
71
+ Starfall, a small example game, and the SDK playground (every SDK function behind a button).
package/dist/powly.d.ts CHANGED
@@ -1,9 +1,51 @@
1
- /* Powly SDK 0.4.1: types for powly.js and powly.mjs */
1
+ /* Powly SDK 0.8.0: types for powly.js and powly.mjs */
2
2
  /** This SDK's version; Powly's Studio shows it next to the downloads. */
3
- export declare const SDK_VERSION = "0.4.1";
3
+ export declare const SDK_VERSION = "0.8.0";
4
4
 
5
5
  export declare const PROTOCOL = "powly-sdk";
6
6
  export declare const PROTOCOL_VERSION = 1;
7
+ /** The corners Powly's options button (its mascot, which opens the player's options) can be in. */
8
+ export declare const OPTIONS_BUTTON_CORNERS: readonly ["top-left", "top-right", "bottom-left", "bottom-right"];
9
+ export type OptionsButtonCorner = (typeof OPTIONS_BUTTON_CORNERS)[number];
10
+ export declare const isOptionsButtonCorner: (value: unknown) => value is OptionsButtonCorner;
11
+ /**
12
+ * A kind of sound the game has (music, drawing sounds…), which Powly's sound menu shows with a
13
+ * volume slider (`volume`, default true), an on/off switch (`toggle`, default true), or both.
14
+ */
15
+ export type AudioChannel = {
16
+ id: string;
17
+ label: string;
18
+ volume?: boolean;
19
+ toggle?: boolean;
20
+ };
21
+ /** One channel's setting: its volume, 0 to 1, and whether it's on. */
22
+ export type ChannelState = {
23
+ volume: number;
24
+ on: boolean;
25
+ };
26
+ /** Channel ids: letters, digits, _ and -, starting with a letter, up to 32. */
27
+ export declare const AUDIO_CHANNEL_ID: RegExp;
28
+ export declare const MAX_AUDIO_CHANNELS = 8;
29
+ export declare const MAX_AUDIO_LABEL = 32;
30
+ /**
31
+ * The player's sound settings from Powly's sound menu. `volume` (0 to 1) is the player's overall
32
+ * volume, `channels` the game's own (by channel id); `muted` silences everything.
33
+ */
34
+ export type AudioState = {
35
+ volume: number;
36
+ muted: boolean;
37
+ channels: Record<string, ChannelState>;
38
+ };
39
+ /** The channels a game sent, cleaned up: valid ids, short labels, no repeats, at most 8. */
40
+ export declare function audioChannelsOf(value: unknown): AudioChannel[];
41
+ /** What a game changes from its own sound menu; channels by id, only the fields given. */
42
+ export type AudioChanges = {
43
+ volume?: number;
44
+ muted?: boolean;
45
+ channels?: Record<string, Partial<ChannelState>>;
46
+ };
47
+ /** A volume between 0 and 1, or null. */
48
+ export declare const volumeOf: (value: unknown) => number | null;
7
49
  export type InitMessage = {
8
50
  protocol: typeof PROTOCOL;
9
51
  version: number;
@@ -14,6 +56,18 @@ export type InitMessage = {
14
56
  * SDKs leave it out.
15
57
  */
16
58
  pac?: 1;
59
+ /**
60
+ * Where the game wants Powly's options button to start (`Powly.init({ optionsButton })`); the
61
+ * player's own choice wins. Left out: Powly's default (top left).
62
+ */
63
+ optionsButton?: OptionsButtonCorner;
64
+ /**
65
+ * The SDK follows Powly's sound menu (SDK 0.8.0 and later): `channels` are the game's own
66
+ * volume controls, shown as sliders. Older SDKs leave it out.
67
+ */
68
+ audio?: {
69
+ channels: AudioChannel[];
70
+ };
17
71
  };
18
72
  export type PlayerProfile = {
19
73
  /** This player's ID in this game: stable, and different in every game. */
@@ -40,12 +94,19 @@ export type ReadyMessage = {
40
94
  pac?: {
41
95
  guard: boolean;
42
96
  };
97
+ /** The player's sound settings when the game starts. Missing from older Powly hosts. */
98
+ audio?: AudioState;
43
99
  };
44
100
  export type FailedMessage = {
45
101
  type: "failed";
46
102
  error: string;
47
103
  };
48
- export type HostMethod = "getSessionToken" | "purchases.open" | "fullscreen.toggle" | "invites.open";
104
+ /** What a game may ask the Powly page for. */
105
+ export declare const HOST_METHODS: readonly ["getSessionToken", "purchases.open", "fullscreen.toggle", "invites.open", "players.open", "twitch.connect", "audio.set", "audio.channels", "quit"];
106
+ export type HostMethod = (typeof HOST_METHODS)[number];
107
+ /** "players.open": what Powly's player dialog opens on. */
108
+ export declare const PLAYER_VIEWS: readonly ["profile", "report"];
109
+ export type PlayerView = (typeof PLAYER_VIEWS)[number];
49
110
  export type RequestMessage = {
50
111
  type: "request";
51
112
  id: number;
@@ -57,11 +118,33 @@ export type RequestMessage = {
57
118
  * dialog shows them as in the party, without an Invite button.
58
119
  */
59
120
  members?: string[];
121
+ /** "players.open": the player (their player ID in this game) to show. */
122
+ playerId?: string;
123
+ /** "players.open": open on the profile (the default) or straight on reporting them. */
124
+ view?: PlayerView;
125
+ /** "audio.set": what the game's own sound menu changed. */
126
+ audio?: AudioChanges;
127
+ /** "audio.channels": every channel the game has now (registered later than init). */
128
+ channels?: AudioChannel[];
60
129
  };
61
130
  /** What "invites.open" answers once Powly's dialog closed: the friends the player invited. */
62
131
  export type InvitesOpenResult = {
63
132
  invited: string[];
64
133
  };
134
+ /**
135
+ * What "players.open" answers once Powly's player dialog closed: whether the player has that
136
+ * player blocked now (the game may hide their chat, say). Whether they reported them stays private.
137
+ */
138
+ export type PlayersOpenResult = {
139
+ blocked: boolean;
140
+ };
141
+ /**
142
+ * What "twitch.connect" answers once Powly's Connect Twitch dialog closed: whether the player's
143
+ * Twitch is connected now (the SDK then reads GET /twitch again).
144
+ */
145
+ export type TwitchConnectResult = {
146
+ connected: boolean;
147
+ };
65
148
  export type ResponseMessage = {
66
149
  type: "response";
67
150
  id: number;
@@ -73,13 +156,28 @@ export type ResponseMessage = {
73
156
  ok: false;
74
157
  error: string;
75
158
  };
76
- export type HostEventName = "pause" | "resume" | "ended";
159
+ /**
160
+ * "shown": Powly's loading screen is gone and the game is on screen (once per launch). "pause" /
161
+ * "resume": Powly UI covers the game or the tab is hidden, and back. "ended": the launch ended.
162
+ */
163
+ export type HostEventName = "shown" | "pause" | "resume" | "ended";
77
164
  export type EventMessage = {
78
165
  type: "event";
79
166
  event: HostEventName;
167
+ }
168
+ /** The player changed the sound settings (Powly's sound menu, or its M key). */
169
+ | {
170
+ type: "event";
171
+ event: "audio";
172
+ audio: AudioState;
80
173
  };
81
174
  export type HostMessage = ReadyMessage | FailedMessage | ResponseMessage | EventMessage;
82
175
  export declare const isInitMessage: (data: unknown) => data is InitMessage;
176
+ /** A request with a method this Powly doesn't know (from a newer SDK): answered with an error. */
177
+ export declare const isRequestShaped: (data: unknown) => data is {
178
+ type: "request";
179
+ id: number;
180
+ };
83
181
  export declare const isRequestMessage: (data: unknown) => data is RequestMessage;
84
182
 
85
183
  export declare const POWLY_ORIGINS: readonly string[];
@@ -113,6 +211,8 @@ export type PacSample = {
113
211
  keyIntervalSd: number | null;
114
212
  clickIntervals: number;
115
213
  clickIntervalSd: number | null;
214
+ /** Every trusted interaction: key presses, clicks, taps, pointer movement, wheel, gamepads. */
215
+ activity: number;
116
216
  };
117
217
  };
118
218
  export declare const isPacSample: (data: unknown) => data is PacSample;
@@ -466,14 +566,21 @@ type Events = {
466
566
  };
467
567
  state: TwitchChatState;
468
568
  };
569
+ export type ConnectOptions = {
570
+ /** Ask the player to connect Twitch in Powly's dialog when they haven't. Default false. */
571
+ prompt?: boolean;
572
+ };
469
573
  export type TwitchApi = {
470
574
  /** The game's setting and the player's channel, without opening chat. */
471
575
  info(): Promise<TwitchInfo>;
472
576
  /**
473
577
  * Opens the player's chat when they have a channel to read (`status: "connected"`). Resolves
474
578
  * with the info, once chat is joined (or failed to; it keeps retrying in the background).
579
+ * `prompt`: when the player hasn't connected Twitch, Powly first asks them to in its own dialog
580
+ * (they can connect it there without leaving the game); `status` is still "not_connected" if
581
+ * they closed it.
475
582
  */
476
- connect(): Promise<TwitchInfo>;
583
+ connect(options?: ConnectOptions): Promise<TwitchInfo>;
477
584
  /** Closes chat; `connect()` opens it again. */
478
585
  disconnect(): void;
479
586
  readonly state: TwitchChatState;
@@ -494,6 +601,8 @@ export declare function parseIrcLine(line: string): IrcMessage | null;
494
601
  export declare function chatMessageOf(message: IrcMessage): TwitchChatMessage | null;
495
602
  export declare function createTwitch(options: {
496
603
  getInfo: () => Promise<TwitchInfo>;
604
+ /** Opens Powly's Connect Twitch dialog; resolves with whether Twitch is connected now. */
605
+ prompt?: () => Promise<boolean>;
497
606
  /** Twitch chat's WebSocket URL (tests point it elsewhere). */
498
607
  url?: string;
499
608
  }): TwitchApi & {
@@ -501,6 +610,58 @@ export declare function createTwitch(options: {
501
610
  };
502
611
 
503
612
 
613
+ export type AudioOptions = {
614
+ /**
615
+ * The game's kinds of sound, shown in Powly's sound menu (up to 8): `{ id, label }`, with a
616
+ * volume slider (`volume: false` leaves it out) and an on/off switch (`toggle: false`).
617
+ */
618
+ channels?: AudioChannel[];
619
+ /**
620
+ * Apply the overall volume and mute to the page's sound by itself. Default true; turn it off
621
+ * when the game applies `level()` to everything itself.
622
+ */
623
+ auto?: boolean;
624
+ };
625
+ export type AudioApi = {
626
+ /** The player's overall volume, 0 to 1. */
627
+ readonly volume: number;
628
+ /** Everything silenced (Powly's mute, or its M key). */
629
+ readonly muted: boolean;
630
+ /** The game's channels by id: their volume (0 to 1) and whether they're on. */
631
+ readonly channels: Record<string, ChannelState>;
632
+ /**
633
+ * What to play at, 0 to 1: a channel's volume (0 while it's off), or (no channel) full volume.
634
+ * With `auto` off, the overall volume and mute are in it too.
635
+ */
636
+ level(channel?: string): number;
637
+ /**
638
+ * Adds a channel to Powly's sound menu after init (or changes its label or controls), e.g.
639
+ * once a mode with its own sounds starts. Up to 8 channels.
640
+ */
641
+ register(channel: AudioChannel): Promise<void>;
642
+ /**
643
+ * Changes the settings from the game's own sound menu, so Powly's stays the same:
644
+ * `{ volume, muted, channels: { music: { volume: 0.5 }, drawing: { on: false } } }`. Unknown
645
+ * channels are ignored.
646
+ */
647
+ set(changes: AudioChanges): Promise<void>;
648
+ /** The settings changed (in Powly's sound menu, or with `set`). */
649
+ on(event: "change", listener: (audio: AudioState) => void): () => void;
650
+ };
651
+ /** Volume of the page's sound overall: 0 when muted. */
652
+ export declare const masterLevel: (state: AudioState) => number;
653
+ /**
654
+ * Puts a volume control in front of the page's speakers: Web Audio connections to a real-time
655
+ * AudioContext's destination go through a gain node per context, and media elements' volume is
656
+ * scaled (the volume the game set still reads back unchanged; elements played through Web Audio
657
+ * are left to the gain node). Returns how to set it.
658
+ */
659
+ export declare function installMasterGain(win?: Window & typeof globalThis): {
660
+ set(level: number): void;
661
+ };
662
+
663
+
664
+
504
665
 
505
666
 
506
667
 
@@ -515,6 +676,18 @@ export type InitOptions = {
515
676
  powlyOrigin?: string | string[];
516
677
  /** How long to wait for Powly to answer (ms). Default 10 s. */
517
678
  timeoutMs?: number;
679
+ /**
680
+ * Which corner Powly's options button (its mascot, which opens the player's options) starts in:
681
+ * "top-left" (the default), "top-right", "bottom-left" or "bottom-right". Pick the one your own
682
+ * UI leaves free. Players can still move it, and their choice wins.
683
+ */
684
+ optionsButton?: OptionsButtonCorner;
685
+ /**
686
+ * Powly's sound menu (src/audio.ts): the game's own volume `channels` (sliders next to the
687
+ * player's overall volume), and whether the SDK applies the overall volume and mute to the
688
+ * page's sound itself (`auto`, default true).
689
+ */
690
+ audio?: AudioOptions;
518
691
  };
519
692
  export type LeaderboardEntry = {
520
693
  rank: number;
@@ -586,6 +759,25 @@ export type PowlySession = {
586
759
  invited: string[];
587
760
  }>;
588
761
  };
762
+ /**
763
+ * Other players of this game, by their player ID (from rooms, leaderboards, friends…).
764
+ * `open` shows Powly's own dialog over the game (drawn by Powly, not the game) with their
765
+ * profile, where the player can add them as a friend, block or report them; with
766
+ * `{ view: "report" }` it opens straight on reporting them. Games can't file reports
767
+ * themselves: the player always confirms in Powly's dialog. Resolves when it closes, with
768
+ * whether the player has them blocked now (hide their chat, say). Fails for player IDs that
769
+ * aren't from this game.
770
+ */
771
+ players: {
772
+ open(playerId: string, options?: {
773
+ view?: PlayerView;
774
+ }): Promise<PlayersOpenResult>;
775
+ };
776
+ /**
777
+ * The player's sound settings from Powly's options bar: overall volume, mute and the game's
778
+ * channels (`Powly.init({ audio: { channels } })`). Play each channel at `level(channel)`.
779
+ */
780
+ audio: AudioApi;
589
781
  /** Realtime rooms (parties) with other players of this game. */
590
782
  rooms: RoomsApi;
591
783
  /**
@@ -647,8 +839,20 @@ export type PowlySession = {
647
839
  fullscreen: {
648
840
  toggle(): Promise<void>;
649
841
  };
650
- /** Powly UI opened over the game ("pause"), closed ("resume"), or the launch ended ("ended"). */
842
+ /**
843
+ * Closes the game, like the exit button in Powly's options bar: the player goes back to the
844
+ * game's store page and the launch ends ("ended" fires). Save the player's progress first.
845
+ */
846
+ quit(): Promise<void>;
847
+ /**
848
+ * Powly's loading screen is gone and the game is on screen ("shown": start the intro or music
849
+ * here), Powly UI opened over the game ("pause"), closed ("resume"), or the launch ended
850
+ * ("ended"). "shown" comes once per launch; listening for it later calls the listener right
851
+ * away.
852
+ */
651
853
  on(event: HostEventName, listener: () => void): () => void;
854
+ /** Whether Powly's loading screen is gone and the game is on screen ("shown" came). */
855
+ readonly shown: boolean;
652
856
  };
653
857
  /** Starts the SDK. Rejects when the game wasn't launched from Powly. */
654
858
  export declare function init(options?: InitOptions): Promise<PowlySession>;