@powly/sdk 0.0.0-stage → 0.4.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.
@@ -0,0 +1,668 @@
1
+ /* Powly SDK 0.4.0: types for powly.js and powly.mjs */
2
+ /** This SDK's version; Powly's Studio shows it next to the downloads. */
3
+ export declare const SDK_VERSION = "0.4.0";
4
+
5
+ export declare const PROTOCOL = "powly-sdk";
6
+ export declare const PROTOCOL_VERSION = 1;
7
+ export type InitMessage = {
8
+ protocol: typeof PROTOCOL;
9
+ version: number;
10
+ type: "init";
11
+ sdkVersion: string;
12
+ /**
13
+ * The SDK reports to Powly Anti-Cheat (src/pac.ts: `PacSample` messages over the port). Older
14
+ * SDKs leave it out.
15
+ */
16
+ pac?: 1;
17
+ };
18
+ export type PlayerProfile = {
19
+ /** This player's ID in this game: stable, and different in every game. */
20
+ playerId: string;
21
+ username: string;
22
+ avatarUrl: string | null;
23
+ };
24
+ export type ReadyMessage = {
25
+ type: "ready";
26
+ launchId: string;
27
+ profile: PlayerProfile;
28
+ scopes: string[];
29
+ /** Why the game was opened and the player's preferences (e.g. `reason: "invite"`, `muted`). */
30
+ params: Record<string, string>;
31
+ /** A session token and its lifetime in seconds. */
32
+ token: string;
33
+ expiresIn: number;
34
+ /** Base URL of the platform API, e.g. "https://powly.gg/api/platform/v1". */
35
+ apiBase: string;
36
+ /**
37
+ * Powly Anti-Cheat: guard the game's code (src/pac.ts) in this run. Off in developer mode.
38
+ * Missing from older Powly hosts: no guard.
39
+ */
40
+ pac?: {
41
+ guard: boolean;
42
+ };
43
+ };
44
+ export type FailedMessage = {
45
+ type: "failed";
46
+ error: string;
47
+ };
48
+ export type HostMethod = "getSessionToken" | "purchases.open" | "fullscreen.toggle" | "invites.open";
49
+ export type RequestMessage = {
50
+ type: "request";
51
+ id: number;
52
+ method: HostMethod;
53
+ /** "invites.open": launch params every invite sent from Powly's dialog carries (e.g. a room). */
54
+ params?: Record<string, string>;
55
+ /**
56
+ * "invites.open": player IDs already playing with the player (the room's members): Powly's
57
+ * dialog shows them as in the party, without an Invite button.
58
+ */
59
+ members?: string[];
60
+ };
61
+ /** What "invites.open" answers once Powly's dialog closed: the friends the player invited. */
62
+ export type InvitesOpenResult = {
63
+ invited: string[];
64
+ };
65
+ export type ResponseMessage = {
66
+ type: "response";
67
+ id: number;
68
+ ok: true;
69
+ result: unknown;
70
+ } | {
71
+ type: "response";
72
+ id: number;
73
+ ok: false;
74
+ error: string;
75
+ };
76
+ export type HostEventName = "pause" | "resume" | "ended";
77
+ export type EventMessage = {
78
+ type: "event";
79
+ event: HostEventName;
80
+ };
81
+ export type HostMessage = ReadyMessage | FailedMessage | ResponseMessage | EventMessage;
82
+ export declare const isInitMessage: (data: unknown) => data is InitMessage;
83
+ export declare const isRequestMessage: (data: unknown) => data is RequestMessage;
84
+
85
+ export declare const POWLY_ORIGINS: readonly string[];
86
+
87
+ export declare class PowlyError extends Error {
88
+ status: number | undefined;
89
+ /** For realtime calls (rooms): what went wrong, e.g. "full", "locked", "not_found". */
90
+ code: string | undefined;
91
+ constructor(message: string, status?: number, code?: string);
92
+ }
93
+
94
+ /** How often the SDK reports (ms). */
95
+ export declare const PAC_SAMPLE_MS = 5000;
96
+ export type PacSample = {
97
+ type: "pac";
98
+ /** The game's performance.now() and Date.now() when sampled. */
99
+ clock: number;
100
+ date: number;
101
+ /** The largest jump of Date.now() against performance.now() since the last sample (ms). */
102
+ dateJump: number;
103
+ /** Developer tools were open on the game (the debugger trap fired). */
104
+ devtools: boolean;
105
+ tampered: string[];
106
+ automation: string[];
107
+ input: {
108
+ trusted: number;
109
+ synthetic: number;
110
+ clicks: number;
111
+ teleportClicks: number;
112
+ keyIntervals: number;
113
+ keyIntervalSd: number | null;
114
+ clickIntervals: number;
115
+ clickIntervalSd: number | null;
116
+ };
117
+ };
118
+ export declare const isPacSample: (data: unknown) => data is PacSample;
119
+ /**
120
+ * Browser functions a speed hack or cheat script replaces, by name, when they aren't the
121
+ * browser's own. The clocks ("performance.now", "Date.now") and Function.prototype.toString (which
122
+ * hides patches) are what PAC treats as critical.
123
+ */
124
+ export declare function tamperedFunctions(win: Window & typeof globalThis): string[];
125
+ /** Traces of an automated browser (WebDriver, DevTools Protocol drivers, headless browsers). */
126
+ export declare function detectAutomation(win: Window & typeof globalThis): string[];
127
+ /**
128
+ * Shortcuts that open view-source, save the page or open developer tools: F12, Ctrl+U/S,
129
+ * Ctrl+Shift+I/J/C, and their Cmd(+Option) versions on macOS.
130
+ */
131
+ export declare function isInspectShortcut(event: Pick<KeyboardEvent, "key" | "code" | "ctrlKey" | "metaKey" | "shiftKey" | "altKey">): boolean;
132
+ /**
133
+ * Starts reporting to the Powly page over `port`. Returns a function that stops it.
134
+ */
135
+ export declare function startPacSensor(port: MessagePort, { guard }?: {
136
+ guard?: boolean;
137
+ }, win?: Window & typeof globalThis): () => void;
138
+
139
+ export declare const REALTIME_PROTOCOL_VERSION = 1;
140
+ /** Close codes the service uses (4000–4999 are free for applications). */
141
+ export declare const REALTIME_CLOSE: {
142
+ /** The ticket was invalid, or expired without a fresh one. Get a new ticket to reconnect. */
143
+ readonly unauthorized: 4001;
144
+ /** The same player connected too many times; the oldest connection is closed. */
145
+ readonly replaced: 4002;
146
+ /** Too many messages, or the client couldn't keep up with what it was sent. */
147
+ readonly overloaded: 4008;
148
+ /** A frame that isn't valid JSON or protocol. */
149
+ readonly invalid: 4009;
150
+ };
151
+ export type RoomMember = {
152
+ playerId: string;
153
+ username: string;
154
+ avatarUrl: string | null;
155
+ /** What the member shares about themselves (ready, character…), set with `room.me`. */
156
+ data: unknown;
157
+ /** False for a few seconds after their connection dropped, while they may come back. */
158
+ connected: boolean;
159
+ joinedAt: string;
160
+ };
161
+ export type RoomState = {
162
+ id: string;
163
+ /** What friends type (or get in an invite) to join: 6 letters and digits. */
164
+ code: string;
165
+ hostId: string;
166
+ maxPlayers: number;
167
+ /** Locked rooms take nobody new (members who dropped can still come back). */
168
+ locked: boolean;
169
+ /** Listed by `room.list` for anyone in the game to join. */
170
+ listed: boolean;
171
+ /** The room's shared state, set by the host. */
172
+ data: unknown;
173
+ members: RoomMember[];
174
+ createdAt: string;
175
+ };
176
+ /** A listed room, as `room.list` shows it. */
177
+ export type ListedRoom = {
178
+ code: string;
179
+ host: string;
180
+ players: number;
181
+ maxPlayers: number;
182
+ data: unknown;
183
+ };
184
+ export type RealtimeInvite = {
185
+ id: string;
186
+ from: {
187
+ playerId: string;
188
+ username: string;
189
+ };
190
+ params: Record<string, string>;
191
+ createdAt: string;
192
+ };
193
+ export type ClientMessage = {
194
+ op: "auth";
195
+ id?: number;
196
+ ticket: string;
197
+ } | {
198
+ op: "room.create";
199
+ id: number;
200
+ maxPlayers?: number;
201
+ listed?: boolean;
202
+ data?: unknown;
203
+ me?: unknown;
204
+ } | {
205
+ op: "room.join";
206
+ id: number;
207
+ code: string;
208
+ me?: unknown;
209
+ } | {
210
+ op: "room.leave";
211
+ id: number;
212
+ } | {
213
+ op: "room.list";
214
+ id: number;
215
+ } | {
216
+ op: "room.update";
217
+ id: number;
218
+ data?: unknown;
219
+ locked?: boolean;
220
+ listed?: boolean;
221
+ } | {
222
+ op: "room.me";
223
+ id: number;
224
+ data: unknown;
225
+ } | {
226
+ op: "room.kick";
227
+ id: number;
228
+ playerId: string;
229
+ } | {
230
+ op: "room.host";
231
+ id: number;
232
+ playerId: string;
233
+ } | {
234
+ op: "room.send";
235
+ event: string;
236
+ data?: unknown;
237
+ to?: string;
238
+ };
239
+ export type RealtimeErrorCode = "invalid" | "unauthorized" | "not_found" | "full" | "locked" | "forbidden" | "not_in_room" | "too_large" | "rate_limited" | "unavailable"
240
+ /** The game's Multiplayer setting is off (Studio): no rooms, only invites. */
241
+ | "multiplayer_off";
242
+ export type ServerMessage = {
243
+ type: "welcome";
244
+ protocol: number;
245
+ /** "game": a player in a game; "site": Powly's own pages. */
246
+ kind: "game" | "site";
247
+ /** Seconds the ticket is still good for. */
248
+ expiresIn: number;
249
+ } | {
250
+ type: "reply";
251
+ id: number;
252
+ ok: true;
253
+ result: unknown;
254
+ } | {
255
+ type: "reply";
256
+ id: number;
257
+ ok: false;
258
+ error: {
259
+ code: RealtimeErrorCode;
260
+ message: string;
261
+ };
262
+ } | {
263
+ type: "room.message";
264
+ from: string;
265
+ event: string;
266
+ data: unknown;
267
+ } | {
268
+ type: "room.member_joined";
269
+ member: RoomMember;
270
+ } | {
271
+ type: "room.member_updated";
272
+ member: RoomMember;
273
+ } | {
274
+ type: "room.member_left";
275
+ playerId: string;
276
+ reason: "left" | "kicked" | "dropped";
277
+ } | {
278
+ type: "room.updated";
279
+ hostId: string;
280
+ data: unknown;
281
+ locked: boolean;
282
+ listed: boolean;
283
+ }
284
+ /** You're no longer in the room. */
285
+ | {
286
+ type: "room.left";
287
+ reason: "kicked" | "replaced" | "closed";
288
+ }
289
+ /** A friend invited the player to this game while they're playing it. */
290
+ | {
291
+ type: "invite";
292
+ invite: RealtimeInvite;
293
+ }
294
+ /** Powly's pages: a platform event (apps/web/lib/social/types.ts RealtimeEvent). */
295
+ | {
296
+ type: "event";
297
+ event: unknown;
298
+ } | {
299
+ type: "error";
300
+ code: RealtimeErrorCode;
301
+ message: string;
302
+ };
303
+
304
+
305
+
306
+ export type RoomCreateOptions = {
307
+ /** From 2 up to the game's players per room (Studio, Multiplayer), which is also the default. */
308
+ maxPlayers?: number;
309
+ /** Listed rooms show up in `rooms.list()` for anyone in the game to join. Default false. */
310
+ listed?: boolean;
311
+ /** The room's shared state (up to 4 KB of JSON); only the host changes it. */
312
+ data?: unknown;
313
+ /** What you share about yourself (up to 1 KB of JSON), e.g. { ready: false }. */
314
+ me?: unknown;
315
+ };
316
+ export type RoomEvents = {
317
+ /** Another member sent something (`room.send`). */
318
+ message: {
319
+ from: string;
320
+ event: string;
321
+ data: unknown;
322
+ };
323
+ /** Someone joined. */
324
+ join: RoomMember;
325
+ /** Someone left, was kicked, or didn't come back after their connection dropped. */
326
+ leave: {
327
+ playerId: string;
328
+ reason: "left" | "kicked" | "dropped";
329
+ };
330
+ /** A member's data changed, or their connection dropped (`connected: false`) or came back. */
331
+ member: RoomMember;
332
+ /** The host, the room's data, `locked` or `listed` changed. */
333
+ update: Room;
334
+ /**
335
+ * You're out of the room: you left (or created or joined another one), were kicked, the same
336
+ * player joined from elsewhere ("replaced"), or the connection couldn't get back in
337
+ * ("connection", also when the launch ended).
338
+ */
339
+ closed: {
340
+ reason: "left" | "kicked" | "replaced" | "connection";
341
+ };
342
+ };
343
+ export type Room = {
344
+ readonly id: string;
345
+ /** What friends join with: 6 letters and digits. */
346
+ readonly code: string;
347
+ readonly hostId: string;
348
+ /** Whether you're the host. */
349
+ readonly isHost: boolean;
350
+ readonly maxPlayers: number;
351
+ readonly locked: boolean;
352
+ readonly listed: boolean;
353
+ readonly data: unknown;
354
+ /** Everyone in the room, you included, in the order they joined. */
355
+ readonly members: readonly RoomMember[];
356
+ /** Whether you're still in it. */
357
+ readonly active: boolean;
358
+ /**
359
+ * Sends to everyone else in the room, or to one member (`to`). `event`: 1–32 letters, digits or
360
+ * _ . : -; `data`: up to 8 KB of JSON. Returns false when it couldn't be sent (reconnecting).
361
+ */
362
+ send(event: string, data?: unknown, options?: {
363
+ to?: string;
364
+ }): boolean;
365
+ on<K extends keyof RoomEvents>(event: K, listener: (payload: RoomEvents[K]) => void): () => void;
366
+ /** Host only. */
367
+ update(changes: {
368
+ data?: unknown;
369
+ locked?: boolean;
370
+ listed?: boolean;
371
+ }): Promise<void>;
372
+ /** What you share about yourself (up to 1 KB of JSON). */
373
+ setMyData(data: unknown): Promise<void>;
374
+ /** Host only: removes a player, who can't come back. */
375
+ kick(playerId: string): Promise<void>;
376
+ /** Host only: hands hosting to another member. */
377
+ makeHost(playerId: string): Promise<void>;
378
+ /** Invites a friend (scope invites.write); opening it launches the game with `room` = the code. */
379
+ invite(playerId: string): Promise<void>;
380
+ leave(): Promise<void>;
381
+ };
382
+ export type RealtimeStatus = "offline" | "connecting" | "connected" | "reconnecting";
383
+ export type RoomsApi = {
384
+ /** Creates a room with you as its host (leaving the one you're in). */
385
+ create(options?: RoomCreateOptions): Promise<Room>;
386
+ /** Joins by code (leaving the one you're in). Fails with code "not_found", "full" or "locked". */
387
+ join(code: string, options?: {
388
+ me?: unknown;
389
+ }): Promise<Room>;
390
+ /** Listed rooms that still take players, newest first (up to 50). */
391
+ list(): Promise<ListedRoom[]>;
392
+ /** The room you're in, or null. */
393
+ readonly current: Room | null;
394
+ /** The connection to Powly's realtime service. */
395
+ readonly status: RealtimeStatus;
396
+ on(event: "status", listener: (status: RealtimeStatus) => void): () => void;
397
+ };
398
+ type Ticket = {
399
+ url: string;
400
+ ticket: string;
401
+ expiresIn: number;
402
+ };
403
+ /**
404
+ * The game's realtime connection. `getTicket` asks the platform API for a ticket (POST
405
+ * /realtime); `invite` sends a Powly invite.
406
+ */
407
+ export declare function createRooms(options: {
408
+ playerId: string;
409
+ getTicket: () => Promise<Ticket>;
410
+ invite: (playerId: string, params: Record<string, string>) => Promise<void>;
411
+ }): {
412
+ rooms: RoomsApi;
413
+ onInvite(listener: (invite: RealtimeInvite) => void): () => void;
414
+ stop(): void;
415
+ };
416
+
417
+ export declare const TWITCH_CHAT_URL = "wss://irc-ws.chat.twitch.tv:443";
418
+ export type TwitchStatus = "connected" | "not_connected";
419
+ /** What GET /twitch answers: the game's Twitch chat setting and the player's channel. */
420
+ export type TwitchInfo = {
421
+ /** The game's Twitch chat setting in Studio. */
422
+ mode: "optional" | "required";
423
+ /** "connected": `channel` is the player's. "not_connected": they haven't connected Twitch. */
424
+ status: TwitchStatus;
425
+ channel: {
426
+ login: string;
427
+ displayName: string;
428
+ } | null;
429
+ };
430
+ export type TwitchChatMessage = {
431
+ /** Twitch's message ID (what "delete" events name). */
432
+ id: string;
433
+ /** The chatter's Twitch user ID, login and display name. */
434
+ userId: string;
435
+ login: string;
436
+ displayName: string;
437
+ /** Their name colour as "#RRGGBB", or null when they never picked one. */
438
+ color: string | null;
439
+ text: string;
440
+ /** A "/me" message. */
441
+ action: boolean;
442
+ /** Bits cheered with the message (0 if none). */
443
+ bits: number;
444
+ /** Badge name → version, e.g. { subscriber: "12", moderator: "1" }. */
445
+ badges: Record<string, string>;
446
+ broadcaster: boolean;
447
+ moderator: boolean;
448
+ subscriber: boolean;
449
+ vip: boolean;
450
+ /** The chatter's first message in this channel ever. */
451
+ firstMessage: boolean;
452
+ /** When Twitch received it (ISO 8601). */
453
+ sentAt: string;
454
+ };
455
+ /** The chat connection: "idle" until `connect()`, "closed" after `disconnect()`. */
456
+ export type TwitchChatState = "idle" | "connecting" | "connected" | "reconnecting" | "closed";
457
+ type Events = {
458
+ message: TwitchChatMessage;
459
+ /** A moderator deleted one message. */
460
+ delete: {
461
+ id: string;
462
+ };
463
+ /** A moderator cleared the chat (`userId` null) or one chatter's messages (timeout, ban). */
464
+ clear: {
465
+ userId: string | null;
466
+ };
467
+ state: TwitchChatState;
468
+ };
469
+ export type TwitchApi = {
470
+ /** The game's setting and the player's channel, without opening chat. */
471
+ info(): Promise<TwitchInfo>;
472
+ /**
473
+ * Opens the player's chat when they have a channel to read (`status: "connected"`). Resolves
474
+ * with the info, once chat is joined (or failed to; it keeps retrying in the background).
475
+ */
476
+ connect(): Promise<TwitchInfo>;
477
+ /** Closes chat; `connect()` opens it again. */
478
+ disconnect(): void;
479
+ readonly state: TwitchChatState;
480
+ /** The channel chat is open for, or null. */
481
+ readonly channel: TwitchInfo["channel"];
482
+ on<E extends keyof Events>(event: E, listener: (value: Events[E]) => void): () => void;
483
+ };
484
+ export type IrcMessage = {
485
+ tags: Record<string, string>;
486
+ /** The sender's nick (the part of the prefix before "!"), or null. */
487
+ nick: string | null;
488
+ command: string;
489
+ params: string[];
490
+ };
491
+ /** One IRC line (without its CRLF), or null when it isn't one. */
492
+ export declare function parseIrcLine(line: string): IrcMessage | null;
493
+ /** A PRIVMSG as a chat message, or null. */
494
+ export declare function chatMessageOf(message: IrcMessage): TwitchChatMessage | null;
495
+ export declare function createTwitch(options: {
496
+ getInfo: () => Promise<TwitchInfo>;
497
+ /** Twitch chat's WebSocket URL (tests point it elsewhere). */
498
+ url?: string;
499
+ }): TwitchApi & {
500
+ stop(): void;
501
+ };
502
+
503
+
504
+
505
+
506
+
507
+
508
+
509
+ export type InitOptions = {
510
+ /**
511
+ * Powly origins the game accepts, e.g. ["https://powly.gg"]. Defaults to the Powly origins
512
+ * built into the SDK (POWLY_SDK_ORIGINS at build time, see src/origins.ts); the launch URL's
513
+ * `powly_origin` only picks one of those, and is ignored otherwise.
514
+ */
515
+ powlyOrigin?: string | string[];
516
+ /** How long to wait for Powly to answer (ms). Default 10 s. */
517
+ timeoutMs?: number;
518
+ };
519
+ export type LeaderboardEntry = {
520
+ rank: number;
521
+ playerId: string;
522
+ username: string;
523
+ score: number;
524
+ };
525
+ export type Friend = {
526
+ playerId: string;
527
+ username: string;
528
+ /** Their picture (by player ID), or null; missing from older Powly servers. */
529
+ avatarUrl?: string | null;
530
+ };
531
+ export type Achievement = {
532
+ key: string;
533
+ /** "Hidden achievement" for hidden ones the player hasn't unlocked. */
534
+ name: string;
535
+ description: string;
536
+ rarity: "common" | "uncommon" | "rare" | "epic" | "legendary";
537
+ hidden: boolean;
538
+ unlocked: boolean;
539
+ unlockedAt: string | null;
540
+ };
541
+ export type Ownership = {
542
+ owned: boolean;
543
+ since: string | null;
544
+ addOns: string[];
545
+ };
546
+ export type Invite = {
547
+ id: string;
548
+ from: Friend;
549
+ params: Record<string, string>;
550
+ createdAt: string;
551
+ };
552
+ export type PowlySession = {
553
+ profile: PlayerProfile;
554
+ scopes: string[];
555
+ /** Why the game was opened (`reason: "invite"`, `invitedBy`…) and the player's preferences. */
556
+ launchParams: Record<string, string>;
557
+ launchId: string;
558
+ /** The platform API's base URL, e.g. "https://powly.gg/api/platform/v1". */
559
+ apiBase: string;
560
+ /** A valid session token (refreshed automatically, valid for at least 10 more seconds). */
561
+ getSessionToken(): Promise<string>;
562
+ /** Calls the platform API with a session token: `api("/me")`. */
563
+ api<T = unknown>(path: string, init?: {
564
+ method?: string;
565
+ body?: unknown;
566
+ }): Promise<T>;
567
+ friends: {
568
+ list(): Promise<Friend[]>;
569
+ };
570
+ presence: {
571
+ set(text: string | null): Promise<void>;
572
+ };
573
+ invites: {
574
+ list(): Promise<Invite[]>;
575
+ send(playerId: string, params?: Record<string, string>): Promise<void>;
576
+ /** A friend invited the player to this game while they're playing it (live, realtime). */
577
+ on(event: "received", listener: (invite: Invite) => void): () => void;
578
+ /**
579
+ * Opens Powly's own invite dialog over the game (drawn by Powly, not the game): the player's
580
+ * friends who play it, each with an Invite button. Every invite sent from it carries `params`
581
+ * (e.g. `{ room: code }`), like `send`. With the code of the room the player is in, its members
582
+ * show as already in the party (no Invite button). Resolves when the player closes it, with
583
+ * the player IDs they invited. Needs the friends.read and invites.write scopes.
584
+ */
585
+ open(params?: Record<string, string>): Promise<{
586
+ invited: string[];
587
+ }>;
588
+ };
589
+ /** Realtime rooms (parties) with other players of this game. */
590
+ rooms: RoomsApi;
591
+ /**
592
+ * The chat of the player's Twitch channel, for games with Twitch chat on (Studio → Configuration
593
+ * → Twitch chat). Read-only; calls fail with a 403 PowlyError while the setting is off.
594
+ */
595
+ twitch: TwitchApi;
596
+ leaderboards: {
597
+ submit(board: string, score: number): Promise<{
598
+ best: number;
599
+ improved: boolean;
600
+ }>;
601
+ top(board: string, limit?: number): Promise<LeaderboardEntry[]>;
602
+ };
603
+ /**
604
+ * The player's saves, stored by Powly: each key is created in Studio (game → Saves) with a simple
605
+ * type (number, text, true/false, object, array). Reading always works; `set` and `delete` too,
606
+ * except for saves the studio made server-only (403; the game's backend writes those). Unknown
607
+ * keys fail with a 404, values of another type with a 422.
608
+ */
609
+ saves: {
610
+ /** The player's value, or null if they have none. */
611
+ get<T = unknown>(key: string): Promise<T | null>;
612
+ /** All of the player's saves, by key. */
613
+ all(): Promise<Record<string, unknown>>;
614
+ set(key: string, value: unknown): Promise<void>;
615
+ delete(key: string): Promise<void>;
616
+ /** The keys the player has a value for. */
617
+ list(): Promise<string[]>;
618
+ };
619
+ achievements: {
620
+ /** The game's achievements (defined in Studio), with the player's unlocks. */
621
+ list(): Promise<Achievement[]>;
622
+ /**
623
+ * Unlocks one from the browser: only achievements the studio marked "can be unlocked from the
624
+ * browser" (others answer 403; the game's backend unlocks those). `granted` is false when the
625
+ * player already had it.
626
+ */
627
+ unlock(key: string): Promise<{
628
+ key: string;
629
+ granted: boolean;
630
+ }>;
631
+ };
632
+ /** @deprecated Settings are saves now: use `saves` (`reset` is `saves.delete`). */
633
+ settings: {
634
+ get<T = unknown>(key: string): Promise<T | null>;
635
+ all(): Promise<Record<string, unknown>>;
636
+ set(key: string, value: unknown): Promise<void>;
637
+ reset(key: string): Promise<void>;
638
+ };
639
+ /** Whether the player owns the game (scope ownership.read). */
640
+ ownership: {
641
+ get(): Promise<Ownership>;
642
+ };
643
+ /** Asks Powly to open its own purchase dialog (drawn by Powly, outside the game). */
644
+ purchases: {
645
+ open(): Promise<void>;
646
+ };
647
+ fullscreen: {
648
+ toggle(): Promise<void>;
649
+ };
650
+ /** Powly UI opened over the game ("pause"), closed ("resume"), or the launch ended ("ended"). */
651
+ on(event: HostEventName, listener: () => void): () => void;
652
+ };
653
+ /** Starts the SDK. Rejects when the game wasn't launched from Powly. */
654
+ export declare function init(options?: InitOptions): Promise<PowlySession>;
655
+ declare const Powly: {
656
+ init: typeof init;
657
+ SDK_VERSION: string;
658
+ PowlyError: typeof PowlyError;
659
+ };
660
+
661
+ export default Powly;
662
+
663
+ declare global {
664
+ interface Window {
665
+ /** Set by powly.js (the classic script). */
666
+ Powly: typeof Powly;
667
+ }
668
+ }