@playcademy/sdk 0.0.1-beta.3 → 0.0.1-beta.5

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/src/runtime.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { bus, type BusEventMap, BusEvents } from './bus'
2
+ import { PlaycademyClient } from './core/client'
3
+
4
+ /** For Node, SSR, dashboards, etc. */
5
+ export { PlaycademyClient } from './core/client'
6
+
7
+ /** Factory for code running *inside* the CADEMY loader (games) */
8
+ export async function initFromWindow(): Promise<PlaycademyClient> {
9
+ // ------------------------------------------------------------------
10
+ // 0. Runtime guard
11
+ // ------------------------------------------------------------------
12
+ if (typeof window === 'undefined') {
13
+ throw new Error('initFromWindow must run in a browser context')
14
+ }
15
+
16
+ // ------------------------------------------------------------------
17
+ // 1. Await bootstrap from loader (handles both module + iframe modes)
18
+ // ------------------------------------------------------------------
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ const preloaded = (window as any).PLAYCADEMY as
21
+ | Partial<BusEventMap[BusEvents.INIT]>
22
+ | undefined
23
+
24
+ const config: BusEventMap[BusEvents.INIT] = preloaded?.token
25
+ ? (preloaded as BusEventMap[BusEvents.INIT])
26
+ : await new Promise<BusEventMap[BusEvents.INIT]>(resolve =>
27
+ bus.on(BusEvents.INIT, resolve),
28
+ )
29
+
30
+ // ------------------------------------------------------------------
31
+ // 2. Spin up API client with the short‑lived Game‑JWT
32
+ // ------------------------------------------------------------------
33
+ const client = new PlaycademyClient({
34
+ baseUrl: config.baseUrl,
35
+ token: config.token,
36
+ gameId: config.gameId,
37
+ })
38
+
39
+ // ------------------------------------------------------------------
40
+ // 3. React to runtime events automatically
41
+ // ------------------------------------------------------------------
42
+ bus.on(BusEvents.TOKEN_REFRESH, ({ token }) => client.setToken(token))
43
+
44
+ // ------------------------------------------------------------------
45
+ // 4. Tell the parent loader we're alive
46
+ // ------------------------------------------------------------------
47
+ bus.emit(BusEvents.READY, undefined)
48
+
49
+ // ------------------------------------------------------------------
50
+ // (Expose for debugging if dev tools are open)
51
+ // ------------------------------------------------------------------
52
+ if (import.meta.env?.MODE === 'development') {
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ ;(window as any).PLAYCADEMY_CLIENT = client
55
+ }
56
+
57
+ return client
58
+ }
59
+
60
+ export { bus, BusEvents } from './bus'
61
+ export { PlaycademyError } from './core/errors'
package/src/types.ts ADDED
@@ -0,0 +1,55 @@
1
+ import type {
2
+ User,
3
+ InventoryItemWithReward,
4
+ Game,
5
+ DeveloperKey,
6
+ DeveloperStatusResponse,
7
+ MapElement,
8
+ Reward,
9
+ InsertReward,
10
+ ManifestV1,
11
+ UpdateReward,
12
+ } from '@playcademy/types'
13
+
14
+ export interface ClientConfig {
15
+ baseUrl: string
16
+ token?: string
17
+ gameId?: string
18
+ }
19
+
20
+ export interface ClientEvents {
21
+ authChange: { token: string | null }
22
+ inventoryChange: { rewardId: string; delta: number; newTotal: number }
23
+ }
24
+
25
+ export type GameContextPayload = {
26
+ token: string
27
+ baseUrl: string
28
+ gameId: string
29
+ }
30
+
31
+ export type EventListeners = {
32
+ [E in keyof ClientEvents]?: Array<(payload: ClientEvents[E]) => void>
33
+ }
34
+
35
+ export type GameWithManifest = Game & { manifest: ManifestV1 }
36
+ export type DeveloperStatusValue = DeveloperStatusResponse['status']
37
+ export type GameState = Record<string, unknown>
38
+
39
+ export type LoginResponse = { token: string }
40
+ export type GameTokenResponse = { token: string; exp: number }
41
+ export type StartSessionResponse = { sessionId: string }
42
+ export type InventoryMutationResponse = { newTotal: number }
43
+
44
+ export type {
45
+ User,
46
+ InventoryItemWithReward,
47
+ Game,
48
+ ManifestV1,
49
+ DeveloperKey,
50
+ DeveloperStatusResponse,
51
+ MapElement,
52
+ Reward,
53
+ InsertReward,
54
+ UpdateReward,
55
+ }
package/sst-env.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ /* This file is auto-generated by SST. Do not edit. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ /* deno-fmt-ignore-file */
5
+
6
+ /// <reference path="../../sst-env.d.ts" />
7
+
8
+ import "sst"
9
+ export {}
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Enable latest features
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "module": "ESNext",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+
22
+ // Some stricter flags (disabled by default)
23
+ "noUnusedLocals": false,
24
+ "noUnusedParameters": false,
25
+ "noPropertyAccessFromIndexSignature": false
26
+ }
27
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "emitDeclarationOnly": true,
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "skipLibCheck": true
10
+ },
11
+ "include": ["src/**/*.ts"],
12
+ "exclude": ["node_modules", "**/*.test.ts"]
13
+ }
package/dist/bus.d.ts DELETED
@@ -1,37 +0,0 @@
1
- import type { GameContextPayload } from './types';
2
- export declare enum BusEvents {
3
- INIT = "PLAYCADEMY_INIT",
4
- TOKEN_REFRESH = "PLAYCADEMY_TOKEN_REFRESH",
5
- PAUSE = "PLAYCADEMY_PAUSE",
6
- RESUME = "PLAYCADEMY_RESUME",
7
- FORCE_EXIT = "PLAYCADEMY_FORCE_EXIT",
8
- OVERLAY = "PLAYCADEMY_OVERLAY",
9
- READY = "PLAYCADEMY_READY",
10
- EXIT = "PLAYCADEMY_EXIT",
11
- TELEMETRY = "PLAYCADEMY_TELEMETRY"
12
- }
13
- type BusHandler<T = unknown> = (payload: T) => void;
14
- export type BusEventMap = {
15
- [BusEvents.INIT]: GameContextPayload;
16
- [BusEvents.TOKEN_REFRESH]: {
17
- token: string;
18
- exp: number;
19
- };
20
- [BusEvents.PAUSE]: void;
21
- [BusEvents.RESUME]: void;
22
- [BusEvents.FORCE_EXIT]: void;
23
- [BusEvents.OVERLAY]: boolean;
24
- [BusEvents.READY]: void;
25
- [BusEvents.EXIT]: void;
26
- [BusEvents.TELEMETRY]: {
27
- fps: number;
28
- mem: number;
29
- };
30
- };
31
- interface Bus {
32
- emit<K extends BusEvents>(type: K, payload: BusEventMap[K]): void;
33
- on<K extends BusEvents>(type: K, handler: BusHandler<BusEventMap[K]>): void;
34
- off<K extends BusEvents>(type: K, handler: BusHandler<BusEventMap[K]>): void;
35
- }
36
- export declare const bus: Bus;
37
- export {};
@@ -1,144 +0,0 @@
1
- import { type Method } from './request';
2
- import { type Game, type GameWithManifest, type UpsertGameMetadataInput, type InsertReward, type UpdateReward } from '@playcademy/data/schemas';
3
- import type { GameState, InventoryItemWithReward, ClientConfig, ClientEvents, LoginResponse, GameTokenResponse, StartSessionResponse, InventoryMutationResponse, DeveloperStatusValue } from '../types';
4
- export declare class PlaycademyClient {
5
- private baseUrl;
6
- private token?;
7
- private gameId?;
8
- private listeners;
9
- private internalClientSessionId?;
10
- constructor(config: ClientConfig);
11
- private _initializeInternalSession;
12
- getBaseUrl(): string;
13
- on<E extends keyof ClientEvents>(event: E, callback: (payload: ClientEvents[E]) => void): void;
14
- private emit;
15
- setToken(token: string | null): void;
16
- onAuthChange(callback: (token: string | null) => void): void;
17
- protected request<T>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
18
- private _ensureGameId;
19
- auth: {
20
- logout: () => Promise<void>;
21
- };
22
- runtime: {
23
- getGameToken: (gameId: string, options?: {
24
- apply?: boolean;
25
- }) => Promise<GameTokenResponse>;
26
- exit: () => Promise<void>;
27
- };
28
- games: {
29
- fetch: (gameIdOrSlug: string) => Promise<GameWithManifest>;
30
- list: () => Promise<Array<Game>>;
31
- saveState: (state: Record<string, unknown>) => Promise<void>;
32
- loadState: () => Promise<GameState>;
33
- startSession: (gameId?: string) => Promise<StartSessionResponse>;
34
- endSession: (sessionId: string, gameId?: string) => Promise<void>;
35
- };
36
- users: {
37
- me: () => Promise<{
38
- id: string;
39
- name: string;
40
- username: string | null;
41
- email: string;
42
- emailVerified: boolean;
43
- image: string | null;
44
- role: "admin" | "player" | "developer";
45
- developerStatus: "none" | "pending" | "approved";
46
- createdAt: Date;
47
- updatedAt: Date;
48
- }>;
49
- inventory: {
50
- get: () => Promise<InventoryItemWithReward[]>;
51
- add: (rewardId: string, qty: number) => Promise<InventoryMutationResponse>;
52
- spend: (rewardId: string, qty: number) => Promise<InventoryMutationResponse>;
53
- };
54
- };
55
- dev: {
56
- auth: {
57
- applyForDeveloper: () => Promise<void>;
58
- getDeveloperStatus: () => Promise<DeveloperStatusValue>;
59
- };
60
- games: {
61
- upsert: (slug: string, metadata: UpsertGameMetadataInput, file: File | Blob) => Promise<Game>;
62
- update: (gameId: string, props: Partial<Game>) => Promise<void>;
63
- delete: (gameId: string) => Promise<void>;
64
- };
65
- keys: {
66
- createKey: (gameId: string, label?: string) => Promise<{
67
- id: string;
68
- createdAt: Date;
69
- userId: string;
70
- label: string | null;
71
- keyHash: string;
72
- }>;
73
- listKeys: (gameId: string) => Promise<{
74
- id: string;
75
- createdAt: Date;
76
- userId: string;
77
- label: string | null;
78
- keyHash: string;
79
- }[]>;
80
- revokeKey: (keyId: string) => Promise<void>;
81
- };
82
- };
83
- maps: {
84
- elements: (mapId: string) => Promise<{
85
- id: string;
86
- mapId: string | null;
87
- elementSlug: string;
88
- interactionType: "game_entry" | "game_registry" | "info" | "teleport" | "door_in" | "door_out" | "npc_interaction" | "quest_trigger";
89
- metadata: ({
90
- description?: string | undefined;
91
- sourceTiledObjects?: Record<string, unknown>[] | undefined;
92
- } & {
93
- [k: string]: unknown;
94
- }) | null;
95
- gameId: string | null;
96
- }[]>;
97
- };
98
- admin: {
99
- games: {
100
- pauseGame: (gameId: string) => Promise<void>;
101
- resumeGame: (gameId: string) => Promise<void>;
102
- };
103
- rewards: {
104
- createReward: (props: InsertReward) => Promise<{
105
- id: string;
106
- type: "currency" | "badge" | "trophy" | "unlock" | "upgrade" | "other";
107
- displayName: string;
108
- description: string | null;
109
- metadata: unknown;
110
- internalName: string;
111
- }>;
112
- getReward: (rewardId: string) => Promise<{
113
- id: string;
114
- type: "currency" | "badge" | "trophy" | "unlock" | "upgrade" | "other";
115
- displayName: string;
116
- description: string | null;
117
- metadata: unknown;
118
- internalName: string;
119
- }>;
120
- listRewards: () => Promise<{
121
- id: string;
122
- type: "currency" | "badge" | "trophy" | "unlock" | "upgrade" | "other";
123
- displayName: string;
124
- description: string | null;
125
- metadata: unknown;
126
- internalName: string;
127
- }[]>;
128
- updateReward: (rewardId: string, props: UpdateReward) => Promise<{
129
- id: string;
130
- type: "currency" | "badge" | "trophy" | "unlock" | "upgrade" | "other";
131
- displayName: string;
132
- description: string | null;
133
- metadata: unknown;
134
- internalName: string;
135
- }>;
136
- deleteReward: (rewardId: string) => Promise<void>;
137
- };
138
- };
139
- telemetry: {
140
- pushMetrics: (metrics: Record<string, number>) => Promise<void>;
141
- };
142
- ping(): string;
143
- static login(baseUrl: string, email: string, password: string): Promise<LoginResponse>;
144
- }
@@ -1,11 +0,0 @@
1
- /**
2
- * Base error class for Cademy SDK specific errors.
3
- */
4
- export declare class PlaycademyError extends Error {
5
- constructor(message: string);
6
- }
7
- export declare class ApiError extends Error {
8
- status: number;
9
- details: unknown;
10
- constructor(status: number, message: string, details: unknown);
11
- }
@@ -1,24 +0,0 @@
1
- import { type ManifestV1 } from '@playcademy/data/schemas';
2
- /** Permitted HTTP verbs */
3
- export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
4
- export interface RequestOptions {
5
- path: string;
6
- baseUrl: string;
7
- token?: string | null;
8
- method?: Method;
9
- body?: unknown;
10
- extraHeaders?: Record<string, string>;
11
- }
12
- /**
13
- * Thin wrapper around `fetch` that:
14
- * • attaches Bearer token if provided
15
- * • stringifies JSON bodies and sets Content‑Type
16
- * • passes FormData untouched so the browser adds multipart boundary
17
- * • normalises non‑2xx responses into ApiError
18
- * • auto‑parses JSON responses, falls back to text/void
19
- */
20
- export declare function request<T = unknown>({ path, baseUrl, token, method, body, extraHeaders, }: RequestOptions): Promise<T>;
21
- /**
22
- * Fetches, parses, and validates the playcademy.manifest.json file from a given base URL.
23
- */
24
- export declare function fetchManifest(assetBundleBase: string): Promise<ManifestV1>;
package/dist/runtime.d.ts DELETED
@@ -1,7 +0,0 @@
1
- import { PlaycademyClient } from './core/client';
2
- /** For Node, SSR, dashboards, etc. */
3
- export { PlaycademyClient } from './core/client';
4
- /** Factory for code running *inside* the CADEMY loader (games) */
5
- export declare function initFromWindow(): Promise<PlaycademyClient>;
6
- export { bus, BusEvents } from './bus';
7
- export { PlaycademyError } from './core/errors';