@playcademy/sdk 0.0.5 → 0.0.7

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 (48) hide show
  1. package/dist/index.d.ts +4013 -7
  2. package/dist/index.js +1820 -1785
  3. package/dist/server.d.ts +53 -0
  4. package/dist/server.js +50 -0
  5. package/dist/types.d.ts +4243 -41
  6. package/dist/types.js +1 -748
  7. package/package.json +10 -3
  8. package/dist/core/auth/flows/popup.d.ts +0 -14
  9. package/dist/core/auth/flows/redirect.d.ts +0 -15
  10. package/dist/core/auth/flows/unified.d.ts +0 -11
  11. package/dist/core/auth/login.d.ts +0 -20
  12. package/dist/core/auth/oauth.d.ts +0 -115
  13. package/dist/core/auth/utils.d.ts +0 -23
  14. package/dist/core/cache/cooldown-cache.d.ts +0 -31
  15. package/dist/core/cache/index.d.ts +0 -14
  16. package/dist/core/cache/permanent-cache.d.ts +0 -39
  17. package/dist/core/cache/singleton-cache.d.ts +0 -29
  18. package/dist/core/cache/ttl-cache.d.ts +0 -54
  19. package/dist/core/cache/types.d.ts +0 -23
  20. package/dist/core/client.d.ts +0 -521
  21. package/dist/core/errors.d.ts +0 -11
  22. package/dist/core/namespaces/achievements.d.ts +0 -84
  23. package/dist/core/namespaces/admin.d.ts +0 -385
  24. package/dist/core/namespaces/auth.d.ts +0 -54
  25. package/dist/core/namespaces/character.d.ts +0 -205
  26. package/dist/core/namespaces/credits.d.ts +0 -51
  27. package/dist/core/namespaces/dev.d.ts +0 -323
  28. package/dist/core/namespaces/games.d.ts +0 -173
  29. package/dist/core/namespaces/identity.d.ts +0 -98
  30. package/dist/core/namespaces/index.d.ts +0 -19
  31. package/dist/core/namespaces/leaderboard.d.ts +0 -48
  32. package/dist/core/namespaces/levels.d.ts +0 -90
  33. package/dist/core/namespaces/maps.d.ts +0 -93
  34. package/dist/core/namespaces/realtime.client.d.ts +0 -129
  35. package/dist/core/namespaces/realtime.d.ts +0 -90
  36. package/dist/core/namespaces/runtime.d.ts +0 -222
  37. package/dist/core/namespaces/scores.d.ts +0 -55
  38. package/dist/core/namespaces/shop.d.ts +0 -25
  39. package/dist/core/namespaces/sprites.d.ts +0 -35
  40. package/dist/core/namespaces/telemetry.d.ts +0 -28
  41. package/dist/core/namespaces/timeback.d.ts +0 -111
  42. package/dist/core/namespaces/users.d.ts +0 -172
  43. package/dist/core/request.d.ts +0 -24
  44. package/dist/core/static/identity.d.ts +0 -37
  45. package/dist/core/static/index.d.ts +0 -3
  46. package/dist/core/static/init.d.ts +0 -21
  47. package/dist/core/static/login.d.ts +0 -34
  48. package/dist/messaging.d.ts +0 -544
package/dist/index.js CHANGED
@@ -10,1736 +10,1002 @@ var __export = (target, all) => {
10
10
  };
11
11
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
12
12
 
13
- // ../data/src/domains/game/table.ts
14
- import {
15
- jsonb,
16
- pgEnum,
17
- pgTable,
18
- text,
19
- timestamp,
20
- uniqueIndex,
21
- uuid,
22
- varchar
23
- } from "drizzle-orm/pg-core";
24
- var gamePlatformEnum, gameBootModeEnum, gameTypeEnum, games, gameSessions, gameStates;
25
- var init_table = __esm(() => {
26
- init_table3();
27
- init_table4();
28
- gamePlatformEnum = pgEnum("game_platform", ["web", "godot", "unity"]);
29
- gameBootModeEnum = pgEnum("game_boot_mode", ["iframe", "module"]);
30
- gameTypeEnum = pgEnum("game_type", ["hosted", "external"]);
31
- games = pgTable("games", {
32
- id: uuid("id").primaryKey().defaultRandom(),
33
- developerId: text("developer_id").references(() => users.id, {
34
- onDelete: "set null"
35
- }),
36
- slug: varchar("slug", { length: 255 }).notNull().unique(),
37
- displayName: varchar("display_name", { length: 255 }).notNull(),
38
- version: varchar("version", { length: 50 }).notNull(),
39
- gameType: gameTypeEnum("game_type").notNull().default("hosted"),
40
- assetBundleBase: text("asset_bundle_base"),
41
- externalUrl: text("external_url"),
42
- platform: gamePlatformEnum("platform").notNull().default("web"),
43
- mapElementId: uuid("map_element_id").references(() => mapElements.id, {
44
- onDelete: "set null"
45
- }),
46
- metadata: jsonb("metadata").$type().notNull().default({}),
47
- createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
48
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
49
- });
50
- gameSessions = pgTable("game_sessions", {
51
- id: uuid("id").primaryKey().defaultRandom(),
52
- userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
53
- gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
54
- startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
55
- endedAt: timestamp("ended_at", { withTimezone: true })
56
- });
57
- gameStates = pgTable("game_states", {
58
- userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
59
- gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
60
- data: jsonb("data").default("{}"),
61
- updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
62
- }, (table) => [uniqueIndex("unique_user_game_idx").on(table.userId, table.gameId)]);
13
+ // ../logger/src/index.ts
14
+ var isBrowser = () => {
15
+ const g = globalThis;
16
+ return typeof g.window !== "undefined" && typeof g.document !== "undefined";
17
+ }, colors, getLevelColor = (level) => {
18
+ switch (level) {
19
+ case "debug":
20
+ return colors.blue;
21
+ case "info":
22
+ return colors.cyan;
23
+ case "warn":
24
+ return colors.yellow;
25
+ case "error":
26
+ return colors.red;
27
+ default:
28
+ return colors.reset;
29
+ }
30
+ }, logInBrowser = (level, message, context) => {
31
+ const timestamp = new Date().toISOString();
32
+ const levelUpper = level.toUpperCase();
33
+ const consoleMethod = getConsoleMethod(level);
34
+ if (context && Object.keys(context).length > 0) {
35
+ consoleMethod(`[${timestamp}] ${levelUpper}`, message, context);
36
+ } else {
37
+ consoleMethod(`[${timestamp}] ${levelUpper}`, message);
38
+ }
39
+ }, logOnServer = (level, message, context) => {
40
+ const consoleMethod = getConsoleMethod(level);
41
+ if (true) {
42
+ const timestamp = new Date().toISOString();
43
+ const levelColor = getLevelColor(level);
44
+ const levelUpper = level.toUpperCase().padEnd(5);
45
+ const coloredPrefix = `${colors.dim}[${timestamp}]${colors.reset} ${levelColor}${levelUpper}${colors.reset}`;
46
+ if (context && Object.keys(context).length > 0) {
47
+ consoleMethod(`${coloredPrefix} ${message}`, context);
48
+ } else {
49
+ consoleMethod(`${coloredPrefix} ${message}`);
50
+ }
51
+ } else {}
52
+ }, getConsoleMethod = (level) => {
53
+ switch (level) {
54
+ case "debug":
55
+ return console.debug;
56
+ case "info":
57
+ return console.info;
58
+ case "warn":
59
+ return console.warn;
60
+ case "error":
61
+ return console.error;
62
+ default:
63
+ return console.log;
64
+ }
65
+ }, performLog = (level, message, context) => {
66
+ if (level === "debug" && false) {}
67
+ if (isBrowser()) {
68
+ logInBrowser(level, message, context);
69
+ } else {
70
+ logOnServer(level, message, context);
71
+ }
72
+ }, createLogger = () => {
73
+ return {
74
+ debug: (message, context) => performLog("debug", message, context),
75
+ info: (message, context) => performLog("info", message, context),
76
+ warn: (message, context) => performLog("warn", message, context),
77
+ error: (message, context) => performLog("error", message, context),
78
+ log: performLog
79
+ };
80
+ }, log;
81
+ var init_src = __esm(() => {
82
+ colors = {
83
+ reset: "\x1B[0m",
84
+ dim: "\x1B[2m",
85
+ red: "\x1B[31m",
86
+ yellow: "\x1B[33m",
87
+ blue: "\x1B[34m",
88
+ cyan: "\x1B[36m",
89
+ gray: "\x1B[90m"
90
+ };
91
+ log = createLogger();
63
92
  });
64
93
 
65
- // ../data/src/domains/inventory/table.ts
66
- import { relations, sql } from "drizzle-orm";
67
- import {
68
- boolean,
69
- integer,
70
- jsonb as jsonb2,
71
- pgEnum as pgEnum2,
72
- pgTable as pgTable2,
73
- text as text2,
74
- timestamp as timestamp2,
75
- uniqueIndex as uniqueIndex2,
76
- uuid as uuid2
77
- } from "drizzle-orm/pg-core";
78
- var itemTypeEnum, items, inventoryItems, currencies, shopListings, itemsRelations, currenciesRelations, shopListingsRelations, inventoryItemsRelations;
79
- var init_table2 = __esm(() => {
80
- init_table();
81
- init_table3();
82
- init_table4();
83
- itemTypeEnum = pgEnum2("item_type", [
84
- "currency",
85
- "badge",
86
- "trophy",
87
- "collectible",
88
- "consumable",
89
- "unlock",
90
- "upgrade",
91
- "accessory",
92
- "other"
93
- ]);
94
- items = pgTable2("items", {
95
- id: uuid2("id").primaryKey().defaultRandom(),
96
- slug: text2("slug").notNull(),
97
- gameId: uuid2("game_id").references(() => games.id, {
98
- onDelete: "cascade"
99
- }),
100
- displayName: text2("display_name").notNull(),
101
- description: text2("description"),
102
- type: itemTypeEnum("type").notNull().default("other"),
103
- isPlaceable: boolean("is_placeable").default(false).notNull(),
104
- imageUrl: text2("image_url"),
105
- metadata: jsonb2("metadata").default({}),
106
- createdAt: timestamp2("created_at").defaultNow().notNull()
107
- }, (table) => [
108
- uniqueIndex2("items_game_slug_idx").on(table.gameId, table.slug),
109
- uniqueIndex2("items_global_slug_idx").on(table.slug).where(sql`game_id IS NULL`)
110
- ]);
111
- inventoryItems = pgTable2("inventory_items", {
112
- id: uuid2("id").primaryKey().defaultRandom(),
113
- userId: text2("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
114
- itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
115
- quantity: integer("quantity").notNull().default(1),
116
- updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow()
117
- }, (table) => [uniqueIndex2("unique_user_item_idx").on(table.userId, table.itemId)]);
118
- currencies = pgTable2("currencies", {
119
- id: uuid2("id").primaryKey().defaultRandom(),
120
- itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
121
- symbol: text2("symbol"),
122
- isPrimary: boolean("is_primary").default(false).notNull(),
123
- createdAt: timestamp2("created_at").defaultNow().notNull(),
124
- updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
125
- }, (table) => [uniqueIndex2("currency_item_id_idx").on(table.itemId)]);
126
- shopListings = pgTable2("shop_listings", {
127
- id: uuid2("id").primaryKey().defaultRandom(),
128
- itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
129
- currencyId: uuid2("currency_id").notNull().references(() => currencies.id, { onDelete: "restrict" }),
130
- price: integer("price").notNull(),
131
- sellBackPercentage: integer("sell_back_percentage"),
132
- stock: integer("stock"),
133
- isActive: boolean("is_active").default(true).notNull(),
134
- availableFrom: timestamp2("available_from", { withTimezone: true }),
135
- availableUntil: timestamp2("available_until", { withTimezone: true }),
136
- createdAt: timestamp2("created_at").defaultNow().notNull(),
137
- updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
138
- }, (table) => [uniqueIndex2("unique_item_currency_listing_idx").on(table.itemId, table.currencyId)]);
139
- itemsRelations = relations(items, ({ many }) => ({
140
- shopListings: many(shopListings),
141
- inventoryItems: many(inventoryItems),
142
- mapObjects: many(mapObjects)
143
- }));
144
- currenciesRelations = relations(currencies, ({ many }) => ({
145
- shopListings: many(shopListings)
146
- }));
147
- shopListingsRelations = relations(shopListings, ({ one }) => ({
148
- item: one(items, {
149
- fields: [shopListings.itemId],
150
- references: [items.id]
151
- }),
152
- currency: one(currencies, {
153
- fields: [shopListings.currencyId],
154
- references: [currencies.id]
155
- })
156
- }));
157
- inventoryItemsRelations = relations(inventoryItems, ({ one }) => ({
158
- item: one(items, {
159
- fields: [inventoryItems.itemId],
160
- references: [items.id]
161
- }),
162
- user: one(users, {
163
- fields: [inventoryItems.userId],
164
- references: [users.id]
165
- })
166
- }));
167
- });
94
+ // src/core/auth/utils.ts
95
+ function openPopupWindow(url, name = "auth-popup", width = 500, height = 600) {
96
+ const left = window.screenX + (window.outerWidth - width) / 2;
97
+ const top = window.screenY + (window.outerHeight - height) / 2;
98
+ const features = [
99
+ `width=${width}`,
100
+ `height=${height}`,
101
+ `left=${left}`,
102
+ `top=${top}`,
103
+ "toolbar=no",
104
+ "menubar=no",
105
+ "location=yes",
106
+ "status=yes",
107
+ "scrollbars=yes",
108
+ "resizable=yes"
109
+ ].join(",");
110
+ return window.open(url, name, features);
111
+ }
112
+ function isInIframe() {
113
+ if (typeof window === "undefined") {
114
+ return false;
115
+ }
116
+ try {
117
+ return window.self !== window.top;
118
+ } catch {
119
+ return true;
120
+ }
121
+ }
168
122
 
169
- // ../data/src/domains/map/table.ts
170
- import { relations as relations2 } from "drizzle-orm";
171
- import {
172
- doublePrecision,
173
- index,
174
- integer as integer2,
175
- jsonb as jsonb3,
176
- pgEnum as pgEnum3,
177
- pgTable as pgTable3,
178
- text as text3,
179
- timestamp as timestamp3,
180
- uniqueIndex as uniqueIndex3,
181
- uuid as uuid3,
182
- varchar as varchar2
183
- } from "drizzle-orm/pg-core";
184
- var interactionTypeEnum, maps, mapElements, mapObjects, mapElementsRelations, mapsRelations, mapObjectsRelations;
185
- var init_table3 = __esm(() => {
186
- init_table();
187
- init_table2();
188
- init_table4();
189
- interactionTypeEnum = pgEnum3("interaction_type", [
190
- "game_entry",
191
- "game_registry",
192
- "info",
193
- "teleport",
194
- "door_in",
195
- "door_out",
196
- "npc_interaction",
197
- "quest_trigger"
198
- ]);
199
- maps = pgTable3("maps", {
200
- id: uuid3("id").primaryKey().defaultRandom(),
201
- identifier: varchar2("identifier", { length: 255 }).notNull().unique(),
202
- displayName: varchar2("display_name", { length: 255 }).notNull(),
203
- filePath: varchar2("file_path", { length: 255 }).notNull(),
204
- tilesetBasePath: varchar2("tileset_base_path", { length: 255 }).notNull().default("/tilesets"),
205
- defaultSpawnTileX: doublePrecision("default_spawn_tile_x").notNull().default(0),
206
- defaultSpawnTileY: doublePrecision("default_spawn_tile_y").notNull().default(0),
207
- description: text3("description")
208
- });
209
- mapElements = pgTable3("map_elements", {
210
- id: uuid3("id").primaryKey().defaultRandom(),
211
- mapId: uuid3("map_id").references(() => maps.id, {
212
- onDelete: "cascade"
213
- }),
214
- elementSlug: varchar2("element_slug", { length: 255 }).notNull(),
215
- interactionType: interactionTypeEnum("interaction_type").notNull(),
216
- gameId: uuid3("game_id").references(() => games.id, {
217
- onDelete: "set null"
218
- }),
219
- metadata: jsonb3("metadata").$type().default({})
220
- }, (table) => [uniqueIndex3("map_id_element_slug_unique_idx").on(table.mapId, table.elementSlug)]);
221
- mapObjects = pgTable3("map_objects", {
222
- id: uuid3("id").primaryKey().defaultRandom(),
223
- userId: text3("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
224
- mapId: uuid3("map_id").notNull().references(() => maps.id, { onDelete: "cascade" }),
225
- itemId: uuid3("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
226
- worldX: doublePrecision("world_x").notNull(),
227
- worldY: doublePrecision("world_y").notNull(),
228
- rotation: integer2("rotation").default(0).notNull(),
229
- scale: doublePrecision("scale").default(1).notNull(),
230
- createdAt: timestamp3("created_at").defaultNow().notNull()
231
- }, (table) => [
232
- index("map_objects_map_idx").on(table.mapId),
233
- index("map_objects_spatial_idx").on(table.mapId, table.worldX, table.worldY)
234
- ]);
235
- mapElementsRelations = relations2(mapElements, ({ one }) => ({
236
- game: one(games, {
237
- fields: [mapElements.gameId],
238
- references: [games.id]
239
- }),
240
- map: one(maps, {
241
- fields: [mapElements.mapId],
242
- references: [maps.id]
243
- })
244
- }));
245
- mapsRelations = relations2(maps, ({ many }) => ({
246
- elements: many(mapElements),
247
- objects: many(mapObjects)
248
- }));
249
- mapObjectsRelations = relations2(mapObjects, ({ one }) => ({
250
- user: one(users, {
251
- fields: [mapObjects.userId],
252
- references: [users.id]
253
- }),
254
- map: one(maps, {
255
- fields: [mapObjects.mapId],
256
- references: [maps.id]
257
- }),
258
- item: one(items, {
259
- fields: [mapObjects.itemId],
260
- references: [items.id]
261
- })
262
- }));
263
- });
264
-
265
- // ../data/src/domains/user/table.ts
266
- import { relations as relations3 } from "drizzle-orm";
267
- import { boolean as boolean2, pgEnum as pgEnum4, pgTable as pgTable4, text as text4, timestamp as timestamp4, uniqueIndex as uniqueIndex4 } from "drizzle-orm/pg-core";
268
- var userRoleEnum, developerStatusEnum, users, accounts, sessions, verification, ssoProvider, usersRelations;
269
- var init_table4 = __esm(() => {
270
- init_table3();
271
- userRoleEnum = pgEnum4("user_role", ["admin", "player", "developer"]);
272
- developerStatusEnum = pgEnum4("developer_status", ["none", "pending", "approved"]);
273
- users = pgTable4("user", {
274
- id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
275
- name: text4("name").notNull(),
276
- username: text4("username").unique(),
277
- email: text4("email").notNull().unique(),
278
- timebackId: text4("timeback_id").unique(),
279
- emailVerified: boolean2("email_verified").notNull().default(false),
280
- image: text4("image"),
281
- role: userRoleEnum("role").notNull().default("player"),
282
- developerStatus: developerStatusEnum("developer_status").notNull().default("none"),
283
- characterCreated: boolean2("character_created").notNull().default(false),
284
- createdAt: timestamp4("created_at", {
285
- mode: "date",
286
- withTimezone: true
287
- }).notNull(),
288
- updatedAt: timestamp4("updated_at", {
289
- mode: "date",
290
- withTimezone: true
291
- }).notNull()
292
- });
293
- accounts = pgTable4("account", {
294
- id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
295
- userId: text4("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
296
- accountId: text4("account_id").notNull(),
297
- providerId: text4("provider_id").notNull(),
298
- accessToken: text4("access_token"),
299
- refreshToken: text4("refresh_token"),
300
- idToken: text4("id_token"),
301
- accessTokenExpiresAt: timestamp4("access_token_expires_at", {
302
- mode: "date",
303
- withTimezone: true
304
- }),
305
- refreshTokenExpiresAt: timestamp4("refresh_token_expires_at", {
306
- mode: "date",
307
- withTimezone: true
308
- }),
309
- scope: text4("scope"),
310
- password: text4("password"),
311
- createdAt: timestamp4("created_at", {
312
- mode: "date",
313
- withTimezone: true
314
- }).notNull(),
315
- updatedAt: timestamp4("updated_at", {
316
- mode: "date",
317
- withTimezone: true
318
- }).notNull()
319
- }, (table) => [uniqueIndex4("account_provider_providerId_idx").on(table.accountId, table.providerId)]);
320
- sessions = pgTable4("session", {
321
- id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
322
- userId: text4("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
323
- expiresAt: timestamp4("expires_at", {
324
- mode: "date",
325
- withTimezone: true
326
- }).notNull(),
327
- token: text4("token").notNull().unique(),
328
- ipAddress: text4("ip_address"),
329
- userAgent: text4("user_agent"),
330
- createdAt: timestamp4("created_at", {
331
- mode: "date",
332
- withTimezone: true
333
- }).notNull(),
334
- updatedAt: timestamp4("updated_at", {
335
- mode: "date",
336
- withTimezone: true
337
- }).notNull()
338
- });
339
- verification = pgTable4("verification", {
340
- id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
341
- identifier: text4("identifier").notNull(),
342
- value: text4("value").notNull(),
343
- expiresAt: timestamp4("expires_at", {
344
- mode: "date",
345
- withTimezone: true
346
- }).notNull(),
347
- createdAt: timestamp4("created_at", {
348
- mode: "date",
349
- withTimezone: true
350
- }).notNull(),
351
- updatedAt: timestamp4("updated_at", {
352
- mode: "date",
353
- withTimezone: true
354
- }).notNull()
355
- });
356
- ssoProvider = pgTable4("sso_provider", {
357
- id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
358
- issuer: text4("issuer").notNull(),
359
- oidcConfig: text4("oidc_config"),
360
- samlConfig: text4("saml_config"),
361
- userId: text4("user_id").references(() => users.id, { onDelete: "cascade" }),
362
- providerId: text4("provider_id").notNull().unique(),
363
- organizationId: text4("organization_id"),
364
- domain: text4("domain").notNull()
365
- });
366
- usersRelations = relations3(users, ({ many }) => ({
367
- mapObjects: many(mapObjects)
368
- }));
123
+ // src/core/errors.ts
124
+ var PlaycademyError, ApiError;
125
+ var init_errors = __esm(() => {
126
+ PlaycademyError = class PlaycademyError extends Error {
127
+ constructor(message) {
128
+ super(message);
129
+ this.name = "PlaycademyError";
130
+ }
131
+ };
132
+ ApiError = class ApiError extends Error {
133
+ status;
134
+ details;
135
+ constructor(status, message, details) {
136
+ super(`${status} ${message}`);
137
+ this.status = status;
138
+ this.details = details;
139
+ Object.setPrototypeOf(this, ApiError.prototype);
140
+ }
141
+ };
369
142
  });
370
143
 
371
- // ../data/src/domains/developer/table.ts
372
- import { pgTable as pgTable5, text as text5, timestamp as timestamp5, uuid as uuid4, varchar as varchar3 } from "drizzle-orm/pg-core";
373
- var developerKeys;
374
- var init_table5 = __esm(() => {
375
- init_table4();
376
- developerKeys = pgTable5("developer_keys", {
377
- id: uuid4("id").primaryKey().defaultRandom(),
378
- userId: text5("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
379
- label: varchar3("label", { length: 255 }),
380
- keyHash: text5("key_hash").notNull().unique(),
381
- createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
382
- });
383
- });
144
+ // src/core/namespaces/auth.ts
145
+ function createAuthNamespace(client) {
146
+ return {
147
+ login: async (credentials) => {
148
+ try {
149
+ const response = await client["request"]("/auth/login", "POST", credentials);
150
+ client.setToken(response.token);
151
+ return { success: true, token: response.token };
152
+ } catch (error) {
153
+ return {
154
+ success: false,
155
+ error: error instanceof Error ? error.message : "Authentication failed"
156
+ };
157
+ }
158
+ },
159
+ logout: async () => {
160
+ await client["request"]("/auth/logout", "POST");
161
+ client.setToken(null);
162
+ }
163
+ };
164
+ }
384
165
 
385
- // ../data/src/domains/level/table.ts
386
- import { relations as relations4 } from "drizzle-orm";
387
- import {
388
- doublePrecision as doublePrecision2,
389
- integer as integer3,
390
- pgTable as pgTable6,
391
- text as text6,
392
- timestamp as timestamp6,
393
- uniqueIndex as uniqueIndex5,
394
- uuid as uuid5
395
- } from "drizzle-orm/pg-core";
396
- var userLevels, levelConfigs, userLevelsRelations;
397
- var init_table6 = __esm(() => {
398
- init_table4();
399
- userLevels = pgTable6("user_levels", {
400
- userId: text6("user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
401
- currentLevel: integer3("current_level").notNull().default(1),
402
- currentXp: doublePrecision2("current_xp").notNull().default(0),
403
- totalXP: doublePrecision2("total_xp").notNull().default(0),
404
- lastLevelUpAt: timestamp6("last_level_up_at", { withTimezone: true }),
405
- createdAt: timestamp6("created_at").defaultNow().notNull(),
406
- updatedAt: timestamp6("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
407
- });
408
- levelConfigs = pgTable6("level_configs", {
409
- id: uuid5("id").primaryKey().defaultRandom(),
410
- level: integer3("level").notNull().unique(),
411
- xpRequired: integer3("xp_required").notNull(),
412
- creditsReward: integer3("credits_reward").notNull().default(0),
413
- createdAt: timestamp6("created_at").defaultNow().notNull()
414
- }, (table) => [uniqueIndex5("unique_level_config_idx").on(table.level)]);
415
- userLevelsRelations = relations4(userLevels, ({ one }) => ({
416
- user: one(users, {
417
- fields: [userLevels.userId],
418
- references: [users.id]
419
- })
420
- }));
421
- });
166
+ // ../utils/src/random.ts
167
+ async function generateSecureRandomString(length) {
168
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
169
+ const randomValues = new Uint8Array(length);
170
+ globalThis.crypto.getRandomValues(randomValues);
171
+ return Array.from(randomValues).map((byte) => charset[byte % charset.length]).join("");
172
+ }
422
173
 
423
- // ../data/src/domains/leaderboard/table.ts
424
- import { relations as relations5 } from "drizzle-orm";
425
- import { index as index2, integer as integer4, jsonb as jsonb4, pgTable as pgTable7, text as text7, timestamp as timestamp7, uuid as uuid6 } from "drizzle-orm/pg-core";
426
- var gameScores, gameScoresRelations;
427
- var init_table7 = __esm(() => {
428
- init_table();
429
- init_table4();
430
- gameScores = pgTable7("game_scores", {
431
- id: uuid6("id").primaryKey().defaultRandom(),
432
- userId: text7("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
433
- gameId: uuid6("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
434
- score: integer4("score").notNull(),
435
- metadata: jsonb4("metadata").default("{}"),
436
- achievedAt: timestamp7("achieved_at", { withTimezone: true }).defaultNow().notNull(),
437
- sessionId: uuid6("session_id").references(() => gameSessions.id, { onDelete: "set null" })
438
- }, (table) => [
439
- index2("game_scores_user_game_idx").on(table.userId, table.gameId),
440
- index2("game_scores_game_score_idx").on(table.gameId, table.score),
441
- index2("game_scores_achieved_at_idx").on(table.achievedAt)
442
- ]);
443
- gameScoresRelations = relations5(gameScores, ({ one }) => ({
444
- user: one(users, {
445
- fields: [gameScores.userId],
446
- references: [users.id]
447
- }),
448
- game: one(games, {
449
- fields: [gameScores.gameId],
450
- references: [games.id]
451
- }),
452
- session: one(gameSessions, {
453
- fields: [gameScores.sessionId],
454
- references: [gameSessions.id]
455
- })
456
- }));
174
+ // src/core/auth/oauth.ts
175
+ function getTimebackConfig() {
176
+ return {
177
+ authorizationEndpoint: "https://alpha-auth-production-idp.auth.us-west-2.amazoncognito.com/oauth2/authorize",
178
+ tokenEndpoint: "https://alpha-auth-production-idp.auth.us-west-2.amazoncognito.com/oauth2/token",
179
+ scope: "openid email phone"
180
+ };
181
+ }
182
+ async function generateOAuthState(data) {
183
+ const csrfToken = await generateSecureRandomString(32);
184
+ if (data && Object.keys(data).length > 0) {
185
+ const jsonStr = JSON.stringify(data);
186
+ const base64 = btoa(jsonStr).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
187
+ return `${csrfToken}.${base64}`;
188
+ }
189
+ return csrfToken;
190
+ }
191
+ function parseOAuthState(state) {
192
+ const lastDotIndex = state.lastIndexOf(".");
193
+ if (lastDotIndex > 0 && lastDotIndex < state.length - 1) {
194
+ try {
195
+ const csrfToken = state.substring(0, lastDotIndex);
196
+ const base64 = state.substring(lastDotIndex + 1);
197
+ const base64WithPadding = base64.replace(/-/g, "+").replace(/_/g, "/");
198
+ const paddedBase64 = base64WithPadding + "=".repeat((4 - base64WithPadding.length % 4) % 4);
199
+ const jsonStr = atob(paddedBase64);
200
+ const data = JSON.parse(jsonStr);
201
+ return { csrfToken, data };
202
+ } catch {}
203
+ }
204
+ return { csrfToken: state };
205
+ }
206
+ function getOAuthConfig(provider) {
207
+ const configGetter = OAUTH_CONFIGS[provider];
208
+ if (!configGetter)
209
+ throw new Error(`Unsupported auth provider: ${provider}`);
210
+ return configGetter();
211
+ }
212
+ var OAUTH_CONFIGS;
213
+ var init_oauth = __esm(() => {
214
+ OAUTH_CONFIGS = {
215
+ get TIMEBACK() {
216
+ return getTimebackConfig;
217
+ }
218
+ };
457
219
  });
458
220
 
459
- // ../data/src/domains/sprite/table.ts
460
- import { relations as relations6 } from "drizzle-orm";
461
- import { integer as integer5, pgTable as pgTable8, timestamp as timestamp8, uuid as uuid7, varchar as varchar4 } from "drizzle-orm/pg-core";
462
- var spriteTemplates, spriteSheets, spriteTemplatesRelations, spriteSheetsRelations;
463
- var init_table8 = __esm(() => {
464
- spriteTemplates = pgTable8("sprite_templates", {
465
- id: uuid7("id").primaryKey().defaultRandom(),
466
- slug: varchar4("slug", { length: 64 }).notNull().unique(),
467
- url: varchar4("url", { length: 255 }).notNull(),
468
- createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
469
- updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
470
- });
471
- spriteSheets = pgTable8("sprite_sheets", {
472
- id: uuid7("id").primaryKey().defaultRandom(),
473
- templateId: uuid7("template_id").notNull().references(() => spriteTemplates.id, { onDelete: "cascade" }),
474
- width: integer5("width").notNull(),
475
- height: integer5("height").notNull(),
476
- url: varchar4("url", { length: 255 }).notNull(),
477
- createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
478
- updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
221
+ // src/core/auth/flows/popup.ts
222
+ async function initiatePopupFlow(options) {
223
+ const { provider, callbackUrl, onStateChange, oauth } = options;
224
+ try {
225
+ onStateChange?.({
226
+ status: "opening_popup",
227
+ message: "Opening authentication window..."
228
+ });
229
+ const defaults = getOAuthConfig(provider);
230
+ const config = oauth ? { ...defaults, ...oauth } : defaults;
231
+ if (!config.clientId) {
232
+ throw new Error(`clientId is required for ${provider} authentication. ` + "Please provide it in the oauth parameter.");
233
+ }
234
+ const stateData = options.stateData;
235
+ const state = await generateOAuthState(stateData);
236
+ const params = new URLSearchParams({
237
+ response_type: "code",
238
+ client_id: config.clientId,
239
+ redirect_uri: callbackUrl,
240
+ state
241
+ });
242
+ if (config.scope) {
243
+ params.set("scope", config.scope);
244
+ }
245
+ const authUrl = `${config.authorizationEndpoint}?${params.toString()}`;
246
+ const popup = openPopupWindow(authUrl, "playcademy-auth");
247
+ if (!popup || popup.closed) {
248
+ throw new Error("Popup blocked. Please enable popups and try again.");
249
+ }
250
+ onStateChange?.({
251
+ status: "exchanging_token",
252
+ message: "Waiting for authentication..."
253
+ });
254
+ return await waitForServerMessage(popup, onStateChange);
255
+ } catch (error) {
256
+ const errorMessage = error instanceof Error ? error.message : "Authentication failed";
257
+ onStateChange?.({
258
+ status: "error",
259
+ message: errorMessage,
260
+ error: error instanceof Error ? error : new Error(errorMessage)
261
+ });
262
+ throw error;
263
+ }
264
+ }
265
+ async function waitForServerMessage(popup, onStateChange) {
266
+ return new Promise((resolve) => {
267
+ let resolved = false;
268
+ const handleMessage = (event) => {
269
+ if (event.origin !== window.location.origin)
270
+ return;
271
+ const data = event.data;
272
+ if (data?.type === "PLAYCADEMY_AUTH_STATE_CHANGE") {
273
+ resolved = true;
274
+ window.removeEventListener("message", handleMessage);
275
+ if (data.authenticated && data.user) {
276
+ onStateChange?.({
277
+ status: "complete",
278
+ message: "Authentication successful"
279
+ });
280
+ resolve({
281
+ success: true,
282
+ user: data.user
283
+ });
284
+ } else {
285
+ const error = new Error(data.error || "Authentication failed");
286
+ onStateChange?.({
287
+ status: "error",
288
+ message: error.message,
289
+ error
290
+ });
291
+ resolve({
292
+ success: false,
293
+ error
294
+ });
295
+ }
296
+ }
297
+ };
298
+ window.addEventListener("message", handleMessage);
299
+ const checkClosed = setInterval(() => {
300
+ if (popup.closed && !resolved) {
301
+ clearInterval(checkClosed);
302
+ window.removeEventListener("message", handleMessage);
303
+ const error = new Error("Authentication cancelled");
304
+ onStateChange?.({
305
+ status: "error",
306
+ message: error.message,
307
+ error
308
+ });
309
+ resolve({
310
+ success: false,
311
+ error
312
+ });
313
+ }
314
+ }, 500);
315
+ setTimeout(() => {
316
+ if (!resolved) {
317
+ window.removeEventListener("message", handleMessage);
318
+ clearInterval(checkClosed);
319
+ const error = new Error("Authentication timeout");
320
+ onStateChange?.({
321
+ status: "error",
322
+ message: error.message,
323
+ error
324
+ });
325
+ resolve({
326
+ success: false,
327
+ error
328
+ });
329
+ }
330
+ }, 5 * 60 * 1000);
479
331
  });
480
- spriteTemplatesRelations = relations6(spriteTemplates, ({ many }) => ({
481
- sheets: many(spriteSheets)
482
- }));
483
- spriteSheetsRelations = relations6(spriteSheets, ({ one }) => ({
484
- template: one(spriteTemplates, {
485
- fields: [spriteSheets.templateId],
486
- references: [spriteTemplates.id]
487
- })
488
- }));
489
- });
490
-
491
- // ../data/src/domains/character/table.ts
492
- import { relations as relations7 } from "drizzle-orm";
493
- import {
494
- integer as integer6,
495
- pgEnum as pgEnum5,
496
- pgTable as pgTable9,
497
- text as text8,
498
- timestamp as timestamp9,
499
- uniqueIndex as uniqueIndex6,
500
- uuid as uuid8,
501
- varchar as varchar5
502
- } from "drizzle-orm/pg-core";
503
- var characterComponentTypeEnum, characterComponents, playerCharacters, playerCharacterAccessories, characterComponentsRelations, playerCharactersRelations, playerCharacterAccessoriesRelations;
504
- var init_table9 = __esm(() => {
505
- init_table8();
506
- init_table4();
507
- characterComponentTypeEnum = pgEnum5("character_component_type", [
508
- "body",
509
- "outfit",
510
- "hairstyle",
511
- "eyes",
512
- "accessory"
513
- ]);
514
- characterComponents = pgTable9("character_components", {
515
- id: uuid8("id").primaryKey().defaultRandom(),
516
- componentType: characterComponentTypeEnum("component_type").notNull(),
517
- slug: varchar5("slug", { length: 128 }).notNull().unique(),
518
- displayName: varchar5("display_name", { length: 128 }).notNull(),
519
- slot: varchar5("slot", { length: 64 }).notNull(),
520
- spriteSheetId: uuid8("sprite_sheet_id").notNull().references(() => spriteSheets.id, { onDelete: "cascade" }),
521
- unlockLevel: integer6("unlock_level").notNull().default(0),
522
- variant: integer6("variant").notNull().default(0),
523
- createdAt: timestamp9("created_at", { withTimezone: true }).notNull().defaultNow(),
524
- updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
525
- });
526
- playerCharacters = pgTable9("player_characters", {
527
- id: uuid8("id").primaryKey().defaultRandom(),
528
- userId: text8("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
529
- bodyComponentId: uuid8("body_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
530
- eyesComponentId: uuid8("eyes_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
531
- hairstyleComponentId: uuid8("hairstyle_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
532
- outfitComponentId: uuid8("outfit_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
533
- createdAt: timestamp9("created_at", { withTimezone: true }).notNull().defaultNow(),
534
- updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
535
- });
536
- playerCharacterAccessories = pgTable9("player_character_accessories", {
537
- id: uuid8("id").primaryKey().defaultRandom(),
538
- playerCharacterId: uuid8("player_character_id").notNull().references(() => playerCharacters.id, { onDelete: "cascade" }),
539
- accessoryComponentId: uuid8("accessory_component_id").notNull().references(() => characterComponents.id, { onDelete: "cascade" }),
540
- slot: varchar5("slot", { length: 64 }).notNull(),
541
- equippedAt: timestamp9("equipped_at", { withTimezone: true }).notNull().defaultNow(),
542
- updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
543
- }, (table) => [
544
- uniqueIndex6("unique_player_character_slot_idx").on(table.playerCharacterId, table.slot),
545
- uniqueIndex6("player_character_accessory_idx").on(table.playerCharacterId, table.accessoryComponentId)
546
- ]);
547
- characterComponentsRelations = relations7(characterComponents, ({ one }) => ({
548
- sheet: one(spriteSheets, {
549
- fields: [characterComponents.spriteSheetId],
550
- references: [spriteSheets.id]
551
- })
552
- }));
553
- playerCharactersRelations = relations7(playerCharacters, ({ one, many }) => ({
554
- user: one(users, {
555
- fields: [playerCharacters.userId],
556
- references: [users.id]
557
- }),
558
- body: one(characterComponents, {
559
- fields: [playerCharacters.bodyComponentId],
560
- references: [characterComponents.id]
561
- }),
562
- eyes: one(characterComponents, {
563
- fields: [playerCharacters.eyesComponentId],
564
- references: [characterComponents.id]
565
- }),
566
- hair: one(characterComponents, {
567
- fields: [playerCharacters.hairstyleComponentId],
568
- references: [characterComponents.id]
569
- }),
570
- outfit: one(characterComponents, {
571
- fields: [playerCharacters.outfitComponentId],
572
- references: [characterComponents.id]
573
- }),
574
- accessories: many(playerCharacterAccessories)
575
- }));
576
- playerCharacterAccessoriesRelations = relations7(playerCharacterAccessories, ({ one }) => ({
577
- playerCharacter: one(playerCharacters, {
578
- fields: [playerCharacterAccessories.playerCharacterId],
579
- references: [playerCharacters.id]
580
- }),
581
- accessoryComponent: one(characterComponents, {
582
- fields: [playerCharacterAccessories.accessoryComponentId],
583
- references: [characterComponents.id]
584
- })
585
- }));
332
+ }
333
+ var init_popup = __esm(() => {
334
+ init_oauth();
586
335
  });
587
336
 
588
- // ../data/src/domains/timeback/table.ts
589
- import { doublePrecision as doublePrecision3, pgTable as pgTable10, text as text9, timestamp as timestamp10, uniqueIndex as uniqueIndex7, uuid as uuid9 } from "drizzle-orm/pg-core";
590
- var timebackDailyXp, timebackXpEvents;
591
- var init_table10 = __esm(() => {
592
- init_table4();
593
- timebackDailyXp = pgTable10("timeback_daily_xp", {
594
- userId: text9("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
595
- date: timestamp10("date", { mode: "date", withTimezone: true }).notNull(),
596
- xp: doublePrecision3("xp").notNull().default(0),
597
- createdAt: timestamp10("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow(),
598
- updatedAt: timestamp10("updated_at", { mode: "date", withTimezone: true }).notNull().defaultNow()
599
- }, (table) => [uniqueIndex7("timeback_daily_xp_user_date_idx").on(table.userId, table.date)]);
600
- timebackXpEvents = pgTable10("timeback_xp_event", {
601
- id: uuid9("id").primaryKey().defaultRandom(),
602
- userId: text9("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
603
- occurredAt: timestamp10("occurred_at", { withTimezone: true }).notNull(),
604
- xpDelta: doublePrecision3("xp_delta").notNull(),
605
- source: text9("source").notNull(),
606
- sourceId: text9("source_id"),
607
- sensor: text9("sensor"),
608
- appName: text9("app_name"),
609
- createdAt: timestamp10("created_at", { withTimezone: true }).notNull().defaultNow(),
610
- updatedAt: timestamp10("updated_at", { withTimezone: true }).notNull().defaultNow()
611
- }, (table) => [uniqueIndex7("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
337
+ // src/core/auth/flows/redirect.ts
338
+ async function initiateRedirectFlow(options) {
339
+ const { provider, callbackUrl, onStateChange, oauth } = options;
340
+ try {
341
+ onStateChange?.({
342
+ status: "opening_popup",
343
+ message: "Redirecting to authentication provider..."
344
+ });
345
+ const defaults = getOAuthConfig(provider);
346
+ const config = oauth ? { ...defaults, ...oauth } : defaults;
347
+ if (!config.clientId) {
348
+ throw new Error(`clientId is required for ${provider} authentication. ` + "Please provide it in the oauth parameter.");
349
+ }
350
+ const stateData = options.stateData;
351
+ const state = await generateOAuthState(stateData);
352
+ const params = new URLSearchParams({
353
+ response_type: "code",
354
+ client_id: config.clientId,
355
+ redirect_uri: callbackUrl,
356
+ state
357
+ });
358
+ if (config.scope) {
359
+ params.set("scope", config.scope);
360
+ }
361
+ const authUrl = `${config.authorizationEndpoint}?${params.toString()}`;
362
+ window.location.href = authUrl;
363
+ return new Promise(() => {});
364
+ } catch (error) {
365
+ const errorMessage = error instanceof Error ? error.message : "Authentication failed";
366
+ onStateChange?.({
367
+ status: "error",
368
+ message: errorMessage,
369
+ error: error instanceof Error ? error : new Error(errorMessage)
370
+ });
371
+ throw error;
372
+ }
373
+ }
374
+ var init_redirect = __esm(() => {
375
+ init_oauth();
612
376
  });
613
377
 
614
- // ../data/src/domains/achievement/table.ts
615
- import { relations as relations8 } from "drizzle-orm";
616
- import {
617
- boolean as boolean3,
618
- index as index3,
619
- integer as integer7,
620
- jsonb as jsonb5,
621
- pgEnum as pgEnum6,
622
- pgTable as pgTable11,
623
- text as text10,
624
- timestamp as timestamp11,
625
- uniqueIndex as uniqueIndex8,
626
- uuid as uuid10,
627
- varchar as varchar6
628
- } from "drizzle-orm/pg-core";
629
- var achievementIntervalEnum, achievements, userAchievementProgress, userAchievementClaims, userAchievementProgressRelations, userAchievementClaimsRelations;
630
- var init_table11 = __esm(() => {
631
- init_table4();
632
- achievementIntervalEnum = pgEnum6("achievement_interval", ["daily", "weekly"]);
633
- achievements = pgTable11("achievements", {
634
- id: varchar6("id", { length: 255 }).primaryKey(),
635
- title: varchar6("title", { length: 255 }).notNull(),
636
- description: text10("description"),
637
- intervalType: achievementIntervalEnum("interval_type").notNull(),
638
- rewardCredits: integer7("reward_credits").notNull().default(0),
639
- limitPerInterval: integer7("limit_per_interval").notNull().default(1),
640
- completionType: varchar6("completion_type", { length: 50 }).notNull(),
641
- completionConfig: jsonb5("completion_config").notNull().default({}),
642
- scope: jsonb5("scope").notNull().default({}),
643
- active: boolean3("active").notNull().default(true),
644
- createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow(),
645
- updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow()
646
- });
647
- userAchievementProgress = pgTable11("user_achievement_progress", {
648
- id: uuid10("id").primaryKey().defaultRandom(),
649
- userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
650
- achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
651
- intervalKey: text10("interval_key").notNull(),
652
- progress: jsonb5("progress").notNull().default({}),
653
- updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow().notNull()
654
- }, (table) => [
655
- index3("user_achievement_progress_idx").on(table.userId, table.achievementId, table.intervalKey)
656
- ]);
657
- userAchievementClaims = pgTable11("user_achievement_claims", {
658
- id: uuid10("id").primaryKey().defaultRandom(),
659
- userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
660
- achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
661
- intervalKey: text10("interval_key").notNull(),
662
- rewardCredits: integer7("reward_credits").notNull(),
663
- createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow().notNull()
664
- }, (table) => [
665
- uniqueIndex8("user_achievement_claims_unique").on(table.userId, table.achievementId, table.intervalKey)
666
- ]);
667
- userAchievementProgressRelations = relations8(userAchievementProgress, ({ one }) => ({
668
- user: one(users, {
669
- fields: [userAchievementProgress.userId],
670
- references: [users.id]
671
- }),
672
- achievement: one(achievements, {
673
- fields: [userAchievementProgress.achievementId],
674
- references: [achievements.id]
675
- })
676
- }));
677
- userAchievementClaimsRelations = relations8(userAchievementClaims, ({ one }) => ({
678
- user: one(users, {
679
- fields: [userAchievementClaims.userId],
680
- references: [users.id]
681
- }),
682
- achievement: one(achievements, {
683
- fields: [userAchievementClaims.achievementId],
684
- references: [achievements.id]
685
- })
686
- }));
378
+ // src/core/auth/flows/unified.ts
379
+ async function initiateUnifiedFlow(options) {
380
+ const { mode = "auto" } = options;
381
+ const effectiveMode = mode === "auto" ? isInIframe() ? "popup" : "redirect" : mode;
382
+ switch (effectiveMode) {
383
+ case "popup":
384
+ return initiatePopupFlow(options);
385
+ case "redirect":
386
+ return initiateRedirectFlow(options);
387
+ default:
388
+ throw new Error(`Unsupported authentication mode: ${effectiveMode}`);
389
+ }
390
+ }
391
+ var init_unified = __esm(() => {
392
+ init_popup();
393
+ init_redirect();
687
394
  });
688
395
 
689
- // ../data/src/tables.index.ts
690
- var init_tables_index = __esm(() => {
691
- init_table4();
692
- init_table5();
693
- init_table();
694
- init_table2();
695
- init_table3();
696
- init_table6();
697
- init_table7();
698
- init_table8();
699
- init_table9();
700
- init_table10();
701
- init_table11();
396
+ // src/core/auth/login.ts
397
+ async function login(client, options) {
398
+ try {
399
+ let stateData = options.stateData;
400
+ if (!stateData) {
401
+ try {
402
+ const currentUser = await client.users.me();
403
+ if (currentUser?.id) {
404
+ stateData = { playcademy_user_id: currentUser.id };
405
+ }
406
+ } catch {
407
+ log.debug("[Playcademy SDK] No current user available for state data");
408
+ }
409
+ }
410
+ log.debug("[Playcademy SDK] Starting OAuth login", {
411
+ provider: options.provider,
412
+ mode: options.mode || "auto",
413
+ callbackUrl: options.callbackUrl,
414
+ hasStateData: !!stateData
415
+ });
416
+ const optionsWithState = {
417
+ ...options,
418
+ stateData
419
+ };
420
+ const result = await initiateUnifiedFlow(optionsWithState);
421
+ if (result.success && result.user) {
422
+ log.debug("[Playcademy SDK] OAuth login successful", {
423
+ userId: result.user.sub
424
+ });
425
+ }
426
+ return result;
427
+ } catch (error) {
428
+ log.error("[Playcademy SDK] OAuth login failed", { error });
429
+ const authError = error instanceof Error ? error : new Error("Authentication failed");
430
+ return {
431
+ success: false,
432
+ error: authError
433
+ };
434
+ }
435
+ }
436
+ var init_login = __esm(() => {
437
+ init_src();
438
+ init_unified();
702
439
  });
703
440
 
704
- // ../data/src/constants.ts
705
- var ITEM_SLUGS, CURRENCIES, BADGES, ACHIEVEMENT_COMPLETION_TYPES, ACHIEVEMENT_COMPLETION_TYPE, INTERACTION_TYPE;
706
- var init_constants = __esm(() => {
707
- init_tables_index();
708
- ITEM_SLUGS = {
709
- PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
710
- PLAYCADEMY_XP: "PLAYCADEMY_XP",
711
- FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
712
- EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
713
- FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
714
- COMMON_SWORD: "COMMON_SWORD",
715
- SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
716
- SMALL_BACKPACK: "SMALL_BACKPACK",
717
- LAVA_LAMP: "LAVA_LAMP",
718
- BOOMBOX: "BOOMBOX",
719
- CABIN_BED: "CABIN_BED"
720
- };
721
- CURRENCIES = {
722
- PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
723
- XP: ITEM_SLUGS.PLAYCADEMY_XP
724
- };
725
- BADGES = {
726
- FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
727
- EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
728
- FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
441
+ // src/core/namespaces/identity.ts
442
+ function createIdentityNamespace(client) {
443
+ return {
444
+ connect: (options) => login(client, options),
445
+ _getContext: () => ({
446
+ isInIframe: client["authContext"]?.isInIframe ?? false
447
+ })
729
448
  };
730
- ACHIEVEMENT_COMPLETION_TYPES = [
731
- "time_played_session",
732
- "interaction",
733
- "leaderboard_rank"
734
- ];
735
- ACHIEVEMENT_COMPLETION_TYPE = Object.fromEntries(ACHIEVEMENT_COMPLETION_TYPES.map((value) => [value, value]));
736
- INTERACTION_TYPE = Object.fromEntries(interactionTypeEnum.enumValues.map((value) => [value, value]));
449
+ }
450
+ var init_identity = __esm(() => {
451
+ init_login();
737
452
  });
738
453
 
739
- // ../logger/src/index.ts
740
- var isBrowser = () => {
741
- const g = globalThis;
742
- return typeof g.window !== "undefined" && typeof g.document !== "undefined";
743
- }, colors, getLevelColor = (level) => {
744
- switch (level) {
745
- case "debug":
746
- return colors.blue;
747
- case "info":
748
- return colors.cyan;
749
- case "warn":
750
- return colors.yellow;
751
- case "error":
752
- return colors.red;
753
- default:
754
- return colors.reset;
454
+ // src/messaging.ts
455
+ class PlaycademyMessaging {
456
+ listeners = new Map;
457
+ send(type, payload, options) {
458
+ if (options?.target) {
459
+ this.sendViaPostMessage(type, payload, options.target, options.origin || "*");
460
+ return;
461
+ }
462
+ const context = this.getMessagingContext(type);
463
+ if (context.shouldUsePostMessage) {
464
+ this.sendViaPostMessage(type, payload, context.target, context.origin);
465
+ } else {
466
+ this.sendViaCustomEvent(type, payload);
467
+ }
755
468
  }
756
- }, logInBrowser = (level, message, context) => {
757
- const timestamp12 = new Date().toISOString();
758
- const levelUpper = level.toUpperCase();
759
- const consoleMethod = getConsoleMethod(level);
760
- if (context && Object.keys(context).length > 0) {
761
- consoleMethod(`[${timestamp12}] ${levelUpper}`, message, context);
762
- } else {
763
- consoleMethod(`[${timestamp12}] ${levelUpper}`, message);
469
+ listen(type, handler) {
470
+ const postMessageListener = (event) => {
471
+ const messageEvent = event;
472
+ if (messageEvent.data?.type === type) {
473
+ handler(messageEvent.data.payload || messageEvent.data);
474
+ }
475
+ };
476
+ const customEventListener = (event) => {
477
+ handler(event.detail);
478
+ };
479
+ if (!this.listeners.has(type)) {
480
+ this.listeners.set(type, new Map);
481
+ }
482
+ const listenerMap = this.listeners.get(type);
483
+ listenerMap.set(handler, {
484
+ postMessage: postMessageListener,
485
+ customEvent: customEventListener
486
+ });
487
+ window.addEventListener("message", postMessageListener);
488
+ window.addEventListener(type, customEventListener);
764
489
  }
765
- }, logOnServer = (level, message, context) => {
766
- const consoleMethod = getConsoleMethod(level);
767
- if (true) {
768
- const timestamp12 = new Date().toISOString();
769
- const levelColor = getLevelColor(level);
770
- const levelUpper = level.toUpperCase().padEnd(5);
771
- const coloredPrefix = `${colors.dim}[${timestamp12}]${colors.reset} ${levelColor}${levelUpper}${colors.reset}`;
772
- if (context && Object.keys(context).length > 0) {
773
- consoleMethod(`${coloredPrefix} ${message}`, context);
774
- } else {
775
- consoleMethod(`${coloredPrefix} ${message}`);
490
+ unlisten(type, handler) {
491
+ const typeListeners = this.listeners.get(type);
492
+ if (!typeListeners || !typeListeners.has(handler)) {
493
+ return;
494
+ }
495
+ const listeners = typeListeners.get(handler);
496
+ window.removeEventListener("message", listeners.postMessage);
497
+ window.removeEventListener(type, listeners.customEvent);
498
+ typeListeners.delete(handler);
499
+ if (typeListeners.size === 0) {
500
+ this.listeners.delete(type);
776
501
  }
777
- } else {}
778
- }, getConsoleMethod = (level) => {
779
- switch (level) {
780
- case "debug":
781
- return console.debug;
782
- case "info":
783
- return console.info;
784
- case "warn":
785
- return console.warn;
786
- case "error":
787
- return console.error;
788
- default:
789
- return console.log;
790
502
  }
791
- }, performLog = (level, message, context) => {
792
- if (level === "debug" && false) {}
793
- if (isBrowser()) {
794
- logInBrowser(level, message, context);
795
- } else {
796
- logOnServer(level, message, context);
503
+ getMessagingContext(eventType) {
504
+ const isIframe = typeof window !== "undefined" && window.self !== window.top;
505
+ const iframeToParentEvents = [
506
+ "PLAYCADEMY_READY" /* READY */,
507
+ "PLAYCADEMY_EXIT" /* EXIT */,
508
+ "PLAYCADEMY_TELEMETRY" /* TELEMETRY */,
509
+ "PLAYCADEMY_KEY_EVENT" /* KEY_EVENT */
510
+ ];
511
+ const shouldUsePostMessage = isIframe && iframeToParentEvents.includes(eventType);
512
+ return {
513
+ shouldUsePostMessage,
514
+ target: shouldUsePostMessage ? window.parent : undefined,
515
+ origin: "*"
516
+ };
797
517
  }
798
- }, createLogger = () => {
799
- return {
800
- debug: (message, context) => performLog("debug", message, context),
801
- info: (message, context) => performLog("info", message, context),
802
- warn: (message, context) => performLog("warn", message, context),
803
- error: (message, context) => performLog("error", message, context),
804
- log: performLog
805
- };
806
- }, log;
807
- var init_src = __esm(() => {
808
- colors = {
809
- reset: "\x1B[0m",
810
- dim: "\x1B[2m",
811
- red: "\x1B[31m",
812
- yellow: "\x1B[33m",
813
- blue: "\x1B[34m",
814
- cyan: "\x1B[36m",
815
- gray: "\x1B[90m"
816
- };
817
- log = createLogger();
818
- });
819
-
820
- // src/core/auth/utils.ts
821
- function openPopupWindow(url, name = "auth-popup", width = 500, height = 600) {
822
- const left = window.screenX + (window.outerWidth - width) / 2;
823
- const top = window.screenY + (window.outerHeight - height) / 2;
824
- const features = [
825
- `width=${width}`,
826
- `height=${height}`,
827
- `left=${left}`,
828
- `top=${top}`,
829
- "toolbar=no",
830
- "menubar=no",
831
- "location=yes",
832
- "status=yes",
833
- "scrollbars=yes",
834
- "resizable=yes"
835
- ].join(",");
836
- return window.open(url, name, features);
837
- }
838
- function isInIframe() {
839
- if (typeof window === "undefined") {
840
- return false;
518
+ sendViaPostMessage(type, payload, target = window.parent, origin = "*") {
519
+ const messageData = { type };
520
+ if (payload !== undefined) {
521
+ messageData.payload = payload;
522
+ }
523
+ target.postMessage(messageData, origin);
841
524
  }
842
- try {
843
- return window.self !== window.top;
844
- } catch {
845
- return true;
525
+ sendViaCustomEvent(type, payload) {
526
+ window.dispatchEvent(new CustomEvent(type, { detail: payload }));
846
527
  }
847
528
  }
848
-
849
- // src/core/errors.ts
850
- var PlaycademyError, ApiError;
851
- var init_errors = __esm(() => {
852
- PlaycademyError = class PlaycademyError extends Error {
853
- constructor(message) {
854
- super(message);
855
- this.name = "PlaycademyError";
529
+ var MessageEvents, messaging;
530
+ var init_messaging = __esm(() => {
531
+ ((MessageEvents2) => {
532
+ MessageEvents2["INIT"] = "PLAYCADEMY_INIT";
533
+ MessageEvents2["TOKEN_REFRESH"] = "PLAYCADEMY_TOKEN_REFRESH";
534
+ MessageEvents2["PAUSE"] = "PLAYCADEMY_PAUSE";
535
+ MessageEvents2["RESUME"] = "PLAYCADEMY_RESUME";
536
+ MessageEvents2["FORCE_EXIT"] = "PLAYCADEMY_FORCE_EXIT";
537
+ MessageEvents2["OVERLAY"] = "PLAYCADEMY_OVERLAY";
538
+ MessageEvents2["READY"] = "PLAYCADEMY_READY";
539
+ MessageEvents2["EXIT"] = "PLAYCADEMY_EXIT";
540
+ MessageEvents2["TELEMETRY"] = "PLAYCADEMY_TELEMETRY";
541
+ MessageEvents2["KEY_EVENT"] = "PLAYCADEMY_KEY_EVENT";
542
+ MessageEvents2["AUTH_STATE_CHANGE"] = "PLAYCADEMY_AUTH_STATE_CHANGE";
543
+ MessageEvents2["AUTH_CALLBACK"] = "PLAYCADEMY_AUTH_CALLBACK";
544
+ })(MessageEvents ||= {});
545
+ messaging = new PlaycademyMessaging;
546
+ });
547
+
548
+ // src/core/namespaces/runtime.ts
549
+ function createRuntimeNamespace(client) {
550
+ const eventListeners = new Map;
551
+ const trackListener = (eventType, handler) => {
552
+ if (!eventListeners.has(eventType)) {
553
+ eventListeners.set(eventType, new Set);
856
554
  }
555
+ eventListeners.get(eventType).add(handler);
857
556
  };
858
- ApiError = class ApiError extends Error {
859
- status;
860
- details;
861
- constructor(status, message, details) {
862
- super(`${status} ${message}`);
863
- this.status = status;
864
- this.details = details;
865
- Object.setPrototypeOf(this, ApiError.prototype);
557
+ const untrackListener = (eventType, handler) => {
558
+ const listeners = eventListeners.get(eventType);
559
+ if (listeners) {
560
+ listeners.delete(handler);
561
+ if (listeners.size === 0) {
562
+ eventListeners.delete(eventType);
563
+ }
866
564
  }
867
565
  };
868
- });
869
-
870
- // src/core/namespaces/auth.ts
871
- function createAuthNamespace(client) {
566
+ if (typeof window !== "undefined" && window.self !== window.top) {
567
+ const playcademyConfig = window.PLAYCADEMY;
568
+ const forwardKeys = Array.isArray(playcademyConfig?.forwardKeys) ? playcademyConfig.forwardKeys : ["Escape"];
569
+ const keySet = new Set(forwardKeys.map((k) => k.toLowerCase()));
570
+ const keyListener = (event) => {
571
+ if (keySet.has(event.key.toLowerCase()) || keySet.has(event.code.toLowerCase())) {
572
+ messaging.send("PLAYCADEMY_KEY_EVENT" /* KEY_EVENT */, {
573
+ key: event.key,
574
+ code: event.code,
575
+ type: event.type
576
+ });
577
+ }
578
+ };
579
+ window.addEventListener("keydown", keyListener);
580
+ window.addEventListener("keyup", keyListener);
581
+ trackListener("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, () => {
582
+ window.removeEventListener("keydown", keyListener);
583
+ window.removeEventListener("keyup", keyListener);
584
+ });
585
+ }
872
586
  return {
873
- login: async (credentials) => {
874
- try {
875
- const response = await client["request"]("/auth/login", "POST", credentials);
876
- client.setToken(response.token);
877
- return { success: true, token: response.token };
878
- } catch (error) {
879
- return {
880
- success: false,
881
- error: error instanceof Error ? error.message : "Authentication failed"
882
- };
587
+ getGameToken: async (gameId, options) => {
588
+ const res = await client["request"](`/games/${gameId}/token`, "POST");
589
+ if (options?.apply) {
590
+ client.setToken(res.token);
883
591
  }
592
+ return res;
884
593
  },
885
- logout: async () => {
886
- await client["request"]("/auth/logout", "POST");
887
- client.setToken(null);
594
+ exit: async () => {
595
+ if (client["internalClientSessionId"] && client["gameId"]) {
596
+ try {
597
+ await client.games.endSession(client["internalClientSessionId"], client["gameId"]);
598
+ } catch (error) {
599
+ log.error("[Playcademy SDK] Failed to auto-end session:", {
600
+ sessionId: client["internalClientSessionId"],
601
+ error
602
+ });
603
+ }
604
+ }
605
+ messaging.send("PLAYCADEMY_EXIT" /* EXIT */, undefined);
606
+ },
607
+ onInit: (handler) => {
608
+ messaging.listen("PLAYCADEMY_INIT" /* INIT */, handler);
609
+ trackListener("PLAYCADEMY_INIT" /* INIT */, handler);
610
+ },
611
+ onTokenRefresh: (handler) => {
612
+ messaging.listen("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, handler);
613
+ trackListener("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, handler);
614
+ },
615
+ onPause: (handler) => {
616
+ messaging.listen("PLAYCADEMY_PAUSE" /* PAUSE */, handler);
617
+ trackListener("PLAYCADEMY_PAUSE" /* PAUSE */, handler);
618
+ },
619
+ onResume: (handler) => {
620
+ messaging.listen("PLAYCADEMY_RESUME" /* RESUME */, handler);
621
+ trackListener("PLAYCADEMY_RESUME" /* RESUME */, handler);
622
+ },
623
+ onForceExit: (handler) => {
624
+ messaging.listen("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, handler);
625
+ trackListener("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, handler);
626
+ },
627
+ onOverlay: (handler) => {
628
+ messaging.listen("PLAYCADEMY_OVERLAY" /* OVERLAY */, handler);
629
+ trackListener("PLAYCADEMY_OVERLAY" /* OVERLAY */, handler);
630
+ },
631
+ ready: () => {
632
+ messaging.send("PLAYCADEMY_READY" /* READY */, undefined);
633
+ },
634
+ sendTelemetry: (data) => {
635
+ messaging.send("PLAYCADEMY_TELEMETRY" /* TELEMETRY */, data);
636
+ },
637
+ removeListener: (eventType, handler) => {
638
+ messaging.unlisten(eventType, handler);
639
+ untrackListener(eventType, handler);
640
+ },
641
+ removeAllListeners: () => {
642
+ for (const [eventType, handlers] of eventListeners.entries()) {
643
+ for (const handler of handlers) {
644
+ messaging.unlisten(eventType, handler);
645
+ }
646
+ }
647
+ eventListeners.clear();
648
+ },
649
+ getListenerCounts: () => {
650
+ const counts = {};
651
+ for (const [eventType, handlers] of eventListeners.entries()) {
652
+ counts[eventType] = handlers.size;
653
+ }
654
+ return counts;
888
655
  }
889
656
  };
890
657
  }
891
-
892
- // ../utils/src/random.ts
893
- async function generateSecureRandomString(length) {
894
- const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
895
- const randomValues = new Uint8Array(length);
896
- globalThis.crypto.getRandomValues(randomValues);
897
- return Array.from(randomValues).map((byte) => charset[byte % charset.length]).join("");
898
- }
899
-
900
- // src/core/auth/oauth.ts
901
- function getTimebackConfig() {
902
- return {
903
- authorizationEndpoint: "https://alpha-auth-production-idp.auth.us-west-2.amazoncognito.com/oauth2/authorize",
904
- tokenEndpoint: "https://alpha-auth-production-idp.auth.us-west-2.amazoncognito.com/oauth2/token",
905
- scope: "openid email phone"
906
- };
907
- }
908
- async function generateOAuthState(data) {
909
- const csrfToken = await generateSecureRandomString(32);
910
- if (data && Object.keys(data).length > 0) {
911
- const jsonStr = JSON.stringify(data);
912
- const base64 = btoa(jsonStr).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
913
- return `${csrfToken}.${base64}`;
914
- }
915
- return csrfToken;
916
- }
917
- function parseOAuthState(state) {
918
- const lastDotIndex = state.lastIndexOf(".");
919
- if (lastDotIndex > 0 && lastDotIndex < state.length - 1) {
920
- try {
921
- const csrfToken = state.substring(0, lastDotIndex);
922
- const base64 = state.substring(lastDotIndex + 1);
923
- const base64WithPadding = base64.replace(/-/g, "+").replace(/_/g, "/");
924
- const paddedBase64 = base64WithPadding + "=".repeat((4 - base64WithPadding.length % 4) % 4);
925
- const jsonStr = atob(paddedBase64);
926
- const data = JSON.parse(jsonStr);
927
- return { csrfToken, data };
928
- } catch {}
929
- }
930
- return { csrfToken: state };
931
- }
932
- function getOAuthConfig(provider) {
933
- const configGetter = OAUTH_CONFIGS[provider];
934
- if (!configGetter)
935
- throw new Error(`Unsupported auth provider: ${provider}`);
936
- return configGetter();
937
- }
938
- var OAUTH_CONFIGS;
939
- var init_oauth = __esm(() => {
940
- OAUTH_CONFIGS = {
941
- get TIMEBACK() {
942
- return getTimebackConfig;
943
- }
944
- };
658
+ var init_runtime = __esm(() => {
659
+ init_src();
660
+ init_messaging();
945
661
  });
946
662
 
947
- // src/core/auth/flows/popup.ts
948
- async function initiatePopupFlow(options) {
949
- const { provider, callbackUrl, onStateChange, oauth } = options;
950
- try {
951
- onStateChange?.({
952
- status: "opening_popup",
953
- message: "Opening authentication window..."
954
- });
955
- const defaults = getOAuthConfig(provider);
956
- const config = oauth ? { ...defaults, ...oauth } : defaults;
957
- if (!config.clientId) {
958
- throw new Error(`clientId is required for ${provider} authentication. ` + "Please provide it in the oauth parameter.");
959
- }
960
- const stateData = options.stateData;
961
- const state = await generateOAuthState(stateData);
962
- const params = new URLSearchParams({
963
- response_type: "code",
964
- client_id: config.clientId,
965
- redirect_uri: callbackUrl,
966
- state
967
- });
968
- if (config.scope) {
969
- params.set("scope", config.scope);
663
+ // src/core/cache/ttl-cache.ts
664
+ function createTTLCache(options) {
665
+ const cache = new Map;
666
+ const { ttl: defaultTTL, keyPrefix = "", onClear } = options;
667
+ async function get(key, loader, config) {
668
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
669
+ const now = Date.now();
670
+ const effectiveTTL = config?.ttl !== undefined ? config.ttl : defaultTTL;
671
+ const force = config?.force || false;
672
+ const skipCache = config?.skipCache || false;
673
+ if (effectiveTTL === 0 || skipCache) {
674
+ return loader();
970
675
  }
971
- const authUrl = `${config.authorizationEndpoint}?${params.toString()}`;
972
- const popup = openPopupWindow(authUrl, "playcademy-auth");
973
- if (!popup || popup.closed) {
974
- throw new Error("Popup blocked. Please enable popups and try again.");
676
+ if (!force) {
677
+ const cached = cache.get(fullKey);
678
+ if (cached && cached.expiresAt > now) {
679
+ return cached.value;
680
+ }
975
681
  }
976
- onStateChange?.({
977
- status: "exchanging_token",
978
- message: "Waiting for authentication..."
979
- });
980
- return await waitForServerMessage(popup, onStateChange);
981
- } catch (error) {
982
- const errorMessage = error instanceof Error ? error.message : "Authentication failed";
983
- onStateChange?.({
984
- status: "error",
985
- message: errorMessage,
986
- error: error instanceof Error ? error : new Error(errorMessage)
682
+ const promise = loader().catch((error) => {
683
+ cache.delete(fullKey);
684
+ throw error;
987
685
  });
988
- throw error;
686
+ cache.set(fullKey, {
687
+ value: promise,
688
+ expiresAt: now + effectiveTTL
689
+ });
690
+ return promise;
989
691
  }
990
- }
991
- async function waitForServerMessage(popup, onStateChange) {
992
- return new Promise((resolve) => {
993
- let resolved = false;
994
- const handleMessage = (event) => {
995
- if (event.origin !== window.location.origin)
996
- return;
997
- const data = event.data;
998
- if (data?.type === "PLAYCADEMY_AUTH_STATE_CHANGE") {
999
- resolved = true;
1000
- window.removeEventListener("message", handleMessage);
1001
- if (data.authenticated && data.user) {
1002
- onStateChange?.({
1003
- status: "complete",
1004
- message: "Authentication successful"
1005
- });
1006
- resolve({
1007
- success: true,
1008
- user: data.user
1009
- });
1010
- } else {
1011
- const error = new Error(data.error || "Authentication failed");
1012
- onStateChange?.({
1013
- status: "error",
1014
- message: error.message,
1015
- error
1016
- });
1017
- resolve({
1018
- success: false,
1019
- error
1020
- });
1021
- }
1022
- }
1023
- };
1024
- window.addEventListener("message", handleMessage);
1025
- const checkClosed = setInterval(() => {
1026
- if (popup.closed && !resolved) {
1027
- clearInterval(checkClosed);
1028
- window.removeEventListener("message", handleMessage);
1029
- const error = new Error("Authentication cancelled");
1030
- onStateChange?.({
1031
- status: "error",
1032
- message: error.message,
1033
- error
1034
- });
1035
- resolve({
1036
- success: false,
1037
- error
1038
- });
1039
- }
1040
- }, 500);
1041
- setTimeout(() => {
1042
- if (!resolved) {
1043
- window.removeEventListener("message", handleMessage);
1044
- clearInterval(checkClosed);
1045
- const error = new Error("Authentication timeout");
1046
- onStateChange?.({
1047
- status: "error",
1048
- message: error.message,
1049
- error
1050
- });
1051
- resolve({
1052
- success: false,
1053
- error
1054
- });
692
+ function clear(key) {
693
+ if (key === undefined) {
694
+ cache.clear();
695
+ onClear?.();
696
+ } else {
697
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
698
+ cache.delete(fullKey);
699
+ }
700
+ }
701
+ function size() {
702
+ return cache.size;
703
+ }
704
+ function prune() {
705
+ const now = Date.now();
706
+ for (const [key, entry] of cache.entries()) {
707
+ if (entry.expiresAt <= now) {
708
+ cache.delete(key);
1055
709
  }
1056
- }, 5 * 60 * 1000);
1057
- });
1058
- }
1059
- var init_popup = __esm(() => {
1060
- init_oauth();
1061
- });
1062
-
1063
- // src/core/auth/flows/redirect.ts
1064
- async function initiateRedirectFlow(options) {
1065
- const { provider, callbackUrl, onStateChange, oauth } = options;
1066
- try {
1067
- onStateChange?.({
1068
- status: "opening_popup",
1069
- message: "Redirecting to authentication provider..."
1070
- });
1071
- const defaults = getOAuthConfig(provider);
1072
- const config = oauth ? { ...defaults, ...oauth } : defaults;
1073
- if (!config.clientId) {
1074
- throw new Error(`clientId is required for ${provider} authentication. ` + "Please provide it in the oauth parameter.");
1075
710
  }
1076
- const stateData = options.stateData;
1077
- const state = await generateOAuthState(stateData);
1078
- const params = new URLSearchParams({
1079
- response_type: "code",
1080
- client_id: config.clientId,
1081
- redirect_uri: callbackUrl,
1082
- state
1083
- });
1084
- if (config.scope) {
1085
- params.set("scope", config.scope);
711
+ }
712
+ function getKeys() {
713
+ const keys = [];
714
+ const prefixLen = keyPrefix ? keyPrefix.length + 1 : 0;
715
+ for (const fullKey of cache.keys()) {
716
+ keys.push(fullKey.substring(prefixLen));
1086
717
  }
1087
- const authUrl = `${config.authorizationEndpoint}?${params.toString()}`;
1088
- window.location.href = authUrl;
1089
- return new Promise(() => {});
1090
- } catch (error) {
1091
- const errorMessage = error instanceof Error ? error.message : "Authentication failed";
1092
- onStateChange?.({
1093
- status: "error",
1094
- message: errorMessage,
1095
- error: error instanceof Error ? error : new Error(errorMessage)
1096
- });
1097
- throw error;
718
+ return keys;
1098
719
  }
720
+ function has(key) {
721
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
722
+ const cached = cache.get(fullKey);
723
+ if (!cached)
724
+ return false;
725
+ const now = Date.now();
726
+ if (cached.expiresAt <= now) {
727
+ cache.delete(fullKey);
728
+ return false;
729
+ }
730
+ return true;
731
+ }
732
+ return { get, clear, size, prune, getKeys, has };
1099
733
  }
1100
- var init_redirect = __esm(() => {
1101
- init_oauth();
1102
- });
1103
734
 
1104
- // src/core/auth/flows/unified.ts
1105
- async function initiateUnifiedFlow(options) {
1106
- const { mode = "auto" } = options;
1107
- const effectiveMode = mode === "auto" ? isInIframe() ? "popup" : "redirect" : mode;
1108
- switch (effectiveMode) {
1109
- case "popup":
1110
- return initiatePopupFlow(options);
1111
- case "redirect":
1112
- return initiateRedirectFlow(options);
1113
- default:
1114
- throw new Error(`Unsupported authentication mode: ${effectiveMode}`);
735
+ // src/core/request.ts
736
+ async function request({
737
+ path,
738
+ baseUrl,
739
+ token,
740
+ method = "GET",
741
+ body,
742
+ extraHeaders = {}
743
+ }) {
744
+ const url = baseUrl.replace(/\/$/, "") + (path.startsWith("/") ? path : `/${path}`);
745
+ const headers = { ...extraHeaders };
746
+ let payload;
747
+ if (body instanceof FormData) {
748
+ payload = body;
749
+ } else if (body !== undefined && body !== null) {
750
+ payload = JSON.stringify(body);
751
+ headers["Content-Type"] = "application/json";
752
+ }
753
+ if (token)
754
+ headers["Authorization"] = `Bearer ${token}`;
755
+ const res = await fetch(url, {
756
+ method,
757
+ headers,
758
+ body: payload,
759
+ credentials: "omit"
760
+ });
761
+ if (!res.ok) {
762
+ const clonedRes = res.clone();
763
+ const errorBody = await clonedRes.json().catch(() => clonedRes.text().catch(() => {
764
+ return;
765
+ })) ?? undefined;
766
+ throw new ApiError(res.status, res.statusText, errorBody);
767
+ }
768
+ if (res.status === 204)
769
+ return;
770
+ const contentType = res.headers.get("content-type") ?? "";
771
+ if (contentType.includes("application/json")) {
772
+ try {
773
+ return await res.json();
774
+ } catch (err) {
775
+ if (err instanceof SyntaxError)
776
+ return;
777
+ throw err;
778
+ }
1115
779
  }
780
+ const raw = await res.text().catch(() => "");
781
+ return raw && raw.length > 0 ? raw : undefined;
1116
782
  }
1117
- var init_unified = __esm(() => {
1118
- init_popup();
1119
- init_redirect();
1120
- });
1121
-
1122
- // src/core/auth/login.ts
1123
- async function login(client, options) {
783
+ async function fetchManifest(assetBundleBase) {
784
+ const manifestUrl = `${assetBundleBase.replace(/\/$/, "")}/playcademy.manifest.json`;
1124
785
  try {
1125
- let stateData = options.stateData;
1126
- if (!stateData) {
1127
- const currentUser = client["initPayload"]?.user;
1128
- if (currentUser?.id) {
1129
- stateData = { playcademy_user_id: currentUser.id };
1130
- } else {
1131
- try {
1132
- const currentUser2 = await client.users.me();
1133
- if (currentUser2?.id) {
1134
- stateData = { playcademy_user_id: currentUser2.id };
1135
- }
1136
- } catch {
1137
- log.debug("[Playcademy SDK] No current user available for state data");
1138
- }
1139
- }
1140
- }
1141
- log.debug("[Playcademy SDK] Starting OAuth login", {
1142
- provider: options.provider,
1143
- mode: options.mode || "auto",
1144
- callbackUrl: options.callbackUrl,
1145
- hasStateData: !!stateData
1146
- });
1147
- const optionsWithState = {
1148
- ...options,
1149
- stateData
1150
- };
1151
- const result = await initiateUnifiedFlow(optionsWithState);
1152
- if (result.success && result.user) {
1153
- log.debug("[Playcademy SDK] OAuth login successful", {
1154
- userId: result.user.sub
1155
- });
786
+ const response = await fetch(manifestUrl);
787
+ if (!response.ok) {
788
+ log.error(`[fetchManifest] Failed to fetch manifest from ${manifestUrl}. Status: ${response.status}`);
789
+ throw new PlaycademyError(`Failed to fetch manifest: ${response.status} ${response.statusText}`);
1156
790
  }
1157
- return result;
791
+ return await response.json();
1158
792
  } catch (error) {
1159
- log.error("[Playcademy SDK] OAuth login failed", { error });
1160
- const authError = error instanceof Error ? error : new Error("Authentication failed");
1161
- return {
1162
- success: false,
1163
- error: authError
1164
- };
793
+ if (error instanceof PlaycademyError) {
794
+ throw error;
795
+ }
796
+ log.error(`[Playcademy SDK] Error fetching or parsing manifest from ${manifestUrl}:`, {
797
+ error
798
+ });
799
+ throw new PlaycademyError("Failed to load or parse game manifest");
1165
800
  }
1166
801
  }
1167
- var init_login = __esm(() => {
802
+ var init_request = __esm(() => {
1168
803
  init_src();
1169
- init_unified();
804
+ init_errors();
1170
805
  });
1171
806
 
1172
- // src/core/namespaces/identity.ts
1173
- function createIdentityNamespace(client) {
807
+ // src/core/namespaces/games.ts
808
+ function createGamesNamespace(client) {
809
+ const gamesListCache = createTTLCache({
810
+ ttl: 60 * 1000,
811
+ keyPrefix: "games.list"
812
+ });
813
+ const gameFetchCache = createTTLCache({
814
+ ttl: 60 * 1000,
815
+ keyPrefix: "games.fetch"
816
+ });
1174
817
  return {
1175
- get user() {
1176
- return client["initPayload"]?.user ?? null;
818
+ fetch: async (gameIdOrSlug, options) => {
819
+ const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
820
+ return gameFetchCache.get(gameIdOrSlug, async () => {
821
+ const baseGameData = await promise;
822
+ if (baseGameData.gameType === "hosted" && baseGameData.assetBundleBase !== null) {
823
+ const manifestData = await fetchManifest(baseGameData.assetBundleBase);
824
+ return { ...baseGameData, manifest: manifestData };
825
+ }
826
+ return baseGameData;
827
+ }, options);
1177
828
  },
1178
- connect: (options) => login(client, options),
1179
- _getContext: () => ({
1180
- isInIframe: client["authContext"]?.isInIframe ?? false
1181
- })
829
+ list: (options) => {
830
+ return gamesListCache.get("all", () => client["request"]("/games", "GET"), options);
831
+ },
832
+ saveState: async (state) => {
833
+ const gameId = client["_ensureGameId"]();
834
+ await client["request"](`/games/${gameId}/state`, "POST", state);
835
+ },
836
+ loadState: async () => {
837
+ const gameId = client["_ensureGameId"]();
838
+ return client["request"](`/games/${gameId}/state`, "GET");
839
+ },
840
+ startSession: async (gameId) => {
841
+ const idToUse = gameId ?? client["_ensureGameId"]();
842
+ return client["request"](`/games/${idToUse}/sessions`, "POST", {});
843
+ },
844
+ endSession: async (sessionId, gameId) => {
845
+ const effectiveGameIdToEnd = gameId ?? client["_ensureGameId"]();
846
+ if (client["internalClientSessionId"] && sessionId === client["internalClientSessionId"] && effectiveGameIdToEnd === client["gameId"]) {
847
+ client["internalClientSessionId"] = undefined;
848
+ }
849
+ await client["request"](`/games/${effectiveGameIdToEnd}/sessions/${sessionId}/end`, "POST");
850
+ },
851
+ token: {
852
+ create: async (gameId, options) => {
853
+ const res = await client["request"](`/games/${gameId}/token`, "POST");
854
+ if (options?.apply) {
855
+ client.setToken(res.token);
856
+ }
857
+ return res;
858
+ }
859
+ },
860
+ leaderboard: {
861
+ get: async (gameId, options) => {
862
+ const params = new URLSearchParams;
863
+ if (options?.limit)
864
+ params.append("limit", String(options.limit));
865
+ if (options?.offset)
866
+ params.append("offset", String(options.offset));
867
+ const queryString = params.toString();
868
+ const path = queryString ? `/games/${gameId}/leaderboard?${queryString}` : `/games/${gameId}/leaderboard`;
869
+ return client["request"](path, "GET");
870
+ }
871
+ }
1182
872
  };
1183
873
  }
1184
- var init_identity = __esm(() => {
1185
- init_login();
874
+ var init_games = __esm(() => {
875
+ init_request();
1186
876
  });
1187
877
 
1188
- // src/messaging.ts
1189
- class PlaycademyMessaging {
1190
- listeners = new Map;
1191
- send(type, payload, options) {
1192
- if (options?.target) {
1193
- this.sendViaPostMessage(type, payload, options.target, options.origin || "*");
1194
- return;
1195
- }
1196
- const context = this.getMessagingContext(type);
1197
- if (context.shouldUsePostMessage) {
1198
- this.sendViaPostMessage(type, payload, context.target, context.origin);
1199
- } else {
1200
- this.sendViaCustomEvent(type, payload);
1201
- }
1202
- }
1203
- listen(type, handler) {
1204
- const postMessageListener = (event) => {
1205
- const messageEvent = event;
1206
- if (messageEvent.data?.type === type) {
1207
- handler(messageEvent.data.payload || messageEvent.data);
1208
- }
1209
- };
1210
- const customEventListener = (event) => {
1211
- handler(event.detail);
1212
- };
1213
- if (!this.listeners.has(type)) {
1214
- this.listeners.set(type, new Map);
1215
- }
1216
- const listenerMap = this.listeners.get(type);
1217
- listenerMap.set(handler, {
1218
- postMessage: postMessageListener,
1219
- customEvent: customEventListener
878
+ // src/core/cache/permanent-cache.ts
879
+ function createPermanentCache(keyPrefix) {
880
+ const cache = new Map;
881
+ async function get(key, loader) {
882
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
883
+ const existing = cache.get(fullKey);
884
+ if (existing)
885
+ return existing;
886
+ const promise = loader().catch((error) => {
887
+ cache.delete(fullKey);
888
+ throw error;
1220
889
  });
1221
- window.addEventListener("message", postMessageListener);
1222
- window.addEventListener(type, customEventListener);
890
+ cache.set(fullKey, promise);
891
+ return promise;
1223
892
  }
1224
- unlisten(type, handler) {
1225
- const typeListeners = this.listeners.get(type);
1226
- if (!typeListeners || !typeListeners.has(handler)) {
1227
- return;
1228
- }
1229
- const listeners = typeListeners.get(handler);
1230
- window.removeEventListener("message", listeners.postMessage);
1231
- window.removeEventListener(type, listeners.customEvent);
1232
- typeListeners.delete(handler);
1233
- if (typeListeners.size === 0) {
1234
- this.listeners.delete(type);
893
+ function clear(key) {
894
+ if (key === undefined) {
895
+ cache.clear();
896
+ } else {
897
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
898
+ cache.delete(fullKey);
1235
899
  }
1236
900
  }
1237
- getMessagingContext(eventType) {
1238
- const isIframe = typeof window !== "undefined" && window.self !== window.top;
1239
- const iframeToParentEvents = [
1240
- "PLAYCADEMY_READY" /* READY */,
1241
- "PLAYCADEMY_EXIT" /* EXIT */,
1242
- "PLAYCADEMY_TELEMETRY" /* TELEMETRY */,
1243
- "PLAYCADEMY_KEY_EVENT" /* KEY_EVENT */
1244
- ];
1245
- const shouldUsePostMessage = isIframe && iframeToParentEvents.includes(eventType);
1246
- return {
1247
- shouldUsePostMessage,
1248
- target: shouldUsePostMessage ? window.parent : undefined,
1249
- origin: "*"
1250
- };
901
+ function has(key) {
902
+ const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
903
+ return cache.has(fullKey);
1251
904
  }
1252
- sendViaPostMessage(type, payload, target = window.parent, origin = "*") {
1253
- const messageData = { type };
1254
- if (payload !== undefined) {
1255
- messageData.payload = payload;
1256
- }
1257
- target.postMessage(messageData, origin);
905
+ function size() {
906
+ return cache.size;
1258
907
  }
1259
- sendViaCustomEvent(type, payload) {
1260
- window.dispatchEvent(new CustomEvent(type, { detail: payload }));
908
+ function keys() {
909
+ const result = [];
910
+ const prefixLen = keyPrefix ? keyPrefix.length + 1 : 0;
911
+ for (const fullKey of cache.keys()) {
912
+ result.push(fullKey.substring(prefixLen));
913
+ }
914
+ return result;
1261
915
  }
916
+ return { get, clear, has, size, keys };
1262
917
  }
1263
- var MessageEvents, messaging;
1264
- var init_messaging = __esm(() => {
1265
- ((MessageEvents2) => {
1266
- MessageEvents2["INIT"] = "PLAYCADEMY_INIT";
1267
- MessageEvents2["TOKEN_REFRESH"] = "PLAYCADEMY_TOKEN_REFRESH";
1268
- MessageEvents2["PAUSE"] = "PLAYCADEMY_PAUSE";
1269
- MessageEvents2["RESUME"] = "PLAYCADEMY_RESUME";
1270
- MessageEvents2["FORCE_EXIT"] = "PLAYCADEMY_FORCE_EXIT";
1271
- MessageEvents2["OVERLAY"] = "PLAYCADEMY_OVERLAY";
1272
- MessageEvents2["READY"] = "PLAYCADEMY_READY";
1273
- MessageEvents2["EXIT"] = "PLAYCADEMY_EXIT";
1274
- MessageEvents2["TELEMETRY"] = "PLAYCADEMY_TELEMETRY";
1275
- MessageEvents2["KEY_EVENT"] = "PLAYCADEMY_KEY_EVENT";
1276
- MessageEvents2["AUTH_STATE_CHANGE"] = "PLAYCADEMY_AUTH_STATE_CHANGE";
1277
- MessageEvents2["AUTH_CALLBACK"] = "PLAYCADEMY_AUTH_CALLBACK";
1278
- })(MessageEvents ||= {});
1279
- messaging = new PlaycademyMessaging;
1280
- });
1281
918
 
1282
- // src/core/namespaces/runtime.ts
1283
- function createRuntimeNamespace(client) {
1284
- const eventListeners = new Map;
1285
- const trackListener = (eventType, handler) => {
1286
- if (!eventListeners.has(eventType)) {
1287
- eventListeners.set(eventType, new Set);
1288
- }
1289
- eventListeners.get(eventType).add(handler);
1290
- };
1291
- const untrackListener = (eventType, handler) => {
1292
- const listeners = eventListeners.get(eventType);
1293
- if (listeners) {
1294
- listeners.delete(handler);
1295
- if (listeners.size === 0) {
1296
- eventListeners.delete(eventType);
1297
- }
1298
- }
919
+ // src/core/namespaces/users.ts
920
+ function createUsersNamespace(client) {
921
+ const itemIdCache = createPermanentCache("items");
922
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
923
+ const resolveItemId = async (identifier) => {
924
+ if (UUID_REGEX.test(identifier))
925
+ return identifier;
926
+ const gameId = client["gameId"];
927
+ const cacheKey = gameId ? `${identifier}:${gameId}` : identifier;
928
+ return itemIdCache.get(cacheKey, async () => {
929
+ const queryParams = new URLSearchParams({ slug: identifier });
930
+ if (gameId)
931
+ queryParams.append("gameId", gameId);
932
+ const item = await client["request"](`/items/resolve?${queryParams.toString()}`, "GET");
933
+ return item.id;
934
+ });
1299
935
  };
1300
- if (typeof window !== "undefined" && window.self !== window.top) {
1301
- const playcademyConfig = window.PLAYCADEMY;
1302
- const forwardKeys = Array.isArray(playcademyConfig?.forwardKeys) ? playcademyConfig.forwardKeys : ["Escape"];
1303
- const keySet = new Set(forwardKeys.map((k) => k.toLowerCase()));
1304
- const keyListener = (event) => {
1305
- if (keySet.has(event.key.toLowerCase()) || keySet.has(event.code.toLowerCase())) {
1306
- messaging.send("PLAYCADEMY_KEY_EVENT" /* KEY_EVENT */, {
1307
- key: event.key,
1308
- code: event.code,
1309
- type: event.type
1310
- });
1311
- }
1312
- };
1313
- window.addEventListener("keydown", keyListener);
1314
- window.addEventListener("keyup", keyListener);
1315
- trackListener("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, () => {
1316
- window.removeEventListener("keydown", keyListener);
1317
- window.removeEventListener("keyup", keyListener);
1318
- });
1319
- }
1320
936
  return {
1321
- getGameToken: async (gameId, options) => {
1322
- const res = await client["request"](`/games/${gameId}/token`, "POST");
1323
- if (options?.apply) {
1324
- client.setToken(res.token);
1325
- }
1326
- return res;
937
+ me: async () => {
938
+ return client["request"]("/users/me", "GET");
1327
939
  },
1328
- exit: async () => {
1329
- if (client["internalClientSessionId"] && client["gameId"]) {
1330
- try {
1331
- await client.games.endSession(client["internalClientSessionId"], client["gameId"]);
1332
- } catch (error) {
1333
- log.error("[Playcademy SDK] Failed to auto-end session:", {
1334
- sessionId: client["internalClientSessionId"],
1335
- error
1336
- });
1337
- }
940
+ inventory: {
941
+ get: async () => client["request"](`/inventory`, "GET"),
942
+ add: async (identifier, qty) => {
943
+ const itemId = await resolveItemId(identifier);
944
+ const res = await client["request"](`/inventory/add`, "POST", { itemId, qty });
945
+ client["emit"]("inventoryChange", {
946
+ itemId,
947
+ delta: qty,
948
+ newTotal: res.newTotal
949
+ });
950
+ return res;
951
+ },
952
+ remove: async (identifier, qty) => {
953
+ const itemId = await resolveItemId(identifier);
954
+ const res = await client["request"](`/inventory/remove`, "POST", { itemId, qty });
955
+ client["emit"]("inventoryChange", {
956
+ itemId,
957
+ delta: -qty,
958
+ newTotal: res.newTotal
959
+ });
960
+ return res;
961
+ },
962
+ quantity: async (identifier) => {
963
+ const itemId = await resolveItemId(identifier);
964
+ const inventory = await client["request"](`/inventory`, "GET");
965
+ const item = inventory.find((inv) => inv.item?.id === itemId);
966
+ return item?.quantity ?? 0;
967
+ },
968
+ has: async (identifier, minQuantity = 1) => {
969
+ const itemId = await resolveItemId(identifier);
970
+ const inventory = await client["request"](`/inventory`, "GET");
971
+ const item = inventory.find((inv) => inv.item?.id === itemId);
972
+ const qty = item?.quantity ?? 0;
973
+ return qty >= minQuantity;
1338
974
  }
1339
- messaging.send("PLAYCADEMY_EXIT" /* EXIT */, undefined);
1340
- },
1341
- onInit: (handler) => {
1342
- messaging.listen("PLAYCADEMY_INIT" /* INIT */, handler);
1343
- trackListener("PLAYCADEMY_INIT" /* INIT */, handler);
1344
- },
1345
- onTokenRefresh: (handler) => {
1346
- messaging.listen("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, handler);
1347
- trackListener("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, handler);
1348
975
  },
1349
- onPause: (handler) => {
1350
- messaging.listen("PLAYCADEMY_PAUSE" /* PAUSE */, handler);
1351
- trackListener("PLAYCADEMY_PAUSE" /* PAUSE */, handler);
1352
- },
1353
- onResume: (handler) => {
1354
- messaging.listen("PLAYCADEMY_RESUME" /* RESUME */, handler);
1355
- trackListener("PLAYCADEMY_RESUME" /* RESUME */, handler);
1356
- },
1357
- onForceExit: (handler) => {
1358
- messaging.listen("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, handler);
1359
- trackListener("PLAYCADEMY_FORCE_EXIT" /* FORCE_EXIT */, handler);
1360
- },
1361
- onOverlay: (handler) => {
1362
- messaging.listen("PLAYCADEMY_OVERLAY" /* OVERLAY */, handler);
1363
- trackListener("PLAYCADEMY_OVERLAY" /* OVERLAY */, handler);
1364
- },
1365
- ready: () => {
1366
- messaging.send("PLAYCADEMY_READY" /* READY */, undefined);
1367
- },
1368
- sendTelemetry: (data) => {
1369
- messaging.send("PLAYCADEMY_TELEMETRY" /* TELEMETRY */, data);
1370
- },
1371
- removeListener: (eventType, handler) => {
1372
- messaging.unlisten(eventType, handler);
1373
- untrackListener(eventType, handler);
1374
- },
1375
- removeAllListeners: () => {
1376
- for (const [eventType, handlers] of eventListeners.entries()) {
1377
- for (const handler of handlers) {
1378
- messaging.unlisten(eventType, handler);
976
+ scores: {
977
+ get: async (userIdOrOptions, options) => {
978
+ let userId;
979
+ let queryOptions;
980
+ if (typeof userIdOrOptions === "string") {
981
+ userId = userIdOrOptions;
982
+ queryOptions = options || {};
983
+ } else {
984
+ queryOptions = userIdOrOptions || {};
985
+ const user = await client["request"]("/users/me", "GET");
986
+ userId = user.id;
1379
987
  }
988
+ const params = new URLSearchParams({
989
+ limit: String(queryOptions.limit || 50)
990
+ });
991
+ if (queryOptions.gameId) {
992
+ params.append("gameId", queryOptions.gameId);
993
+ }
994
+ return client["request"](`/users/${userId}/scores?${params}`, "GET");
1380
995
  }
1381
- eventListeners.clear();
1382
- },
1383
- getListenerCounts: () => {
1384
- const counts = {};
1385
- for (const [eventType, handlers] of eventListeners.entries()) {
1386
- counts[eventType] = handlers.size;
1387
- }
1388
- return counts;
1389
996
  }
1390
997
  };
1391
998
  }
1392
- var init_runtime = __esm(() => {
1393
- init_src();
1394
- init_messaging();
1395
- });
999
+ var init_users = () => {};
1396
1000
 
1397
- // src/core/cache/ttl-cache.ts
1398
- function createTTLCache(options) {
1399
- const cache = new Map;
1400
- const { ttl: defaultTTL, keyPrefix = "", onClear } = options;
1401
- async function get(key, loader, config) {
1402
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1403
- const now = Date.now();
1404
- const effectiveTTL = config?.ttl !== undefined ? config.ttl : defaultTTL;
1405
- const force = config?.force || false;
1406
- const skipCache = config?.skipCache || false;
1407
- if (effectiveTTL === 0 || skipCache) {
1408
- return loader();
1409
- }
1410
- if (!force) {
1411
- const cached = cache.get(fullKey);
1412
- if (cached && cached.expiresAt > now) {
1413
- return cached.value;
1414
- }
1415
- }
1416
- const promise = loader().catch((error) => {
1417
- cache.delete(fullKey);
1418
- throw error;
1419
- });
1420
- cache.set(fullKey, {
1421
- value: promise,
1422
- expiresAt: now + effectiveTTL
1423
- });
1424
- return promise;
1425
- }
1426
- function clear(key) {
1427
- if (key === undefined) {
1428
- cache.clear();
1429
- onClear?.();
1430
- } else {
1431
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1432
- cache.delete(fullKey);
1433
- }
1434
- }
1435
- function size() {
1436
- return cache.size;
1437
- }
1438
- function prune() {
1439
- const now = Date.now();
1440
- for (const [key, entry] of cache.entries()) {
1441
- if (entry.expiresAt <= now) {
1442
- cache.delete(key);
1443
- }
1444
- }
1445
- }
1446
- function getKeys() {
1447
- const keys = [];
1448
- const prefixLen = keyPrefix ? keyPrefix.length + 1 : 0;
1449
- for (const fullKey of cache.keys()) {
1450
- keys.push(fullKey.substring(prefixLen));
1451
- }
1452
- return keys;
1453
- }
1454
- function has(key) {
1455
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1456
- const cached = cache.get(fullKey);
1457
- if (!cached)
1458
- return false;
1459
- const now = Date.now();
1460
- if (cached.expiresAt <= now) {
1461
- cache.delete(fullKey);
1462
- return false;
1463
- }
1464
- return true;
1465
- }
1466
- return { get, clear, size, prune, getKeys, has };
1467
- }
1468
-
1469
- // src/core/request.ts
1470
- async function request({
1471
- path,
1472
- baseUrl,
1473
- token,
1474
- method = "GET",
1475
- body,
1476
- extraHeaders = {}
1477
- }) {
1478
- const url = baseUrl.replace(/\/$/, "") + (path.startsWith("/") ? path : `/${path}`);
1479
- const headers = { ...extraHeaders };
1480
- let payload;
1481
- if (body instanceof FormData) {
1482
- payload = body;
1483
- } else if (body !== undefined && body !== null) {
1484
- payload = JSON.stringify(body);
1485
- headers["Content-Type"] = "application/json";
1486
- }
1487
- if (token)
1488
- headers["Authorization"] = `Bearer ${token}`;
1489
- const res = await fetch(url, {
1490
- method,
1491
- headers,
1492
- body: payload,
1493
- credentials: "omit"
1494
- });
1495
- if (!res.ok) {
1496
- const clonedRes = res.clone();
1497
- const errorBody = await clonedRes.json().catch(() => clonedRes.text().catch(() => {
1498
- return;
1499
- })) ?? undefined;
1500
- throw new ApiError(res.status, res.statusText, errorBody);
1501
- }
1502
- if (res.status === 204)
1503
- return;
1504
- const contentType = res.headers.get("content-type") ?? "";
1505
- if (contentType.includes("application/json")) {
1506
- try {
1507
- return await res.json();
1508
- } catch (err) {
1509
- if (err instanceof SyntaxError)
1510
- return;
1511
- throw err;
1512
- }
1513
- }
1514
- const raw = await res.text().catch(() => "");
1515
- return raw && raw.length > 0 ? raw : undefined;
1516
- }
1517
- async function fetchManifest(assetBundleBase) {
1518
- const manifestUrl = `${assetBundleBase.replace(/\/$/, "")}/playcademy.manifest.json`;
1519
- try {
1520
- const response = await fetch(manifestUrl);
1521
- if (!response.ok) {
1522
- log.error(`[fetchManifest] Failed to fetch manifest from ${manifestUrl}. Status: ${response.status}`);
1523
- throw new PlaycademyError(`Failed to fetch manifest: ${response.status} ${response.statusText}`);
1524
- }
1525
- return await response.json();
1526
- } catch (error) {
1527
- if (error instanceof PlaycademyError) {
1528
- throw error;
1529
- }
1530
- log.error(`[Playcademy SDK] Error fetching or parsing manifest from ${manifestUrl}:`, {
1531
- error
1532
- });
1533
- throw new PlaycademyError("Failed to load or parse game manifest");
1534
- }
1535
- }
1536
- var init_request = __esm(() => {
1537
- init_src();
1538
- init_errors();
1539
- });
1540
-
1541
- // src/core/namespaces/games.ts
1542
- function createGamesNamespace(client) {
1543
- const gamesListCache = createTTLCache({
1544
- ttl: 60 * 1000,
1545
- keyPrefix: "games.list"
1546
- });
1547
- const gameFetchCache = createTTLCache({
1548
- ttl: 60 * 1000,
1549
- keyPrefix: "games.fetch"
1550
- });
1551
- return {
1552
- fetch: async (gameIdOrSlug, options) => {
1553
- const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
1554
- return gameFetchCache.get(gameIdOrSlug, async () => {
1555
- const baseGameData = await promise;
1556
- if (baseGameData.gameType === "hosted" && baseGameData.assetBundleBase !== null) {
1557
- const manifestData = await fetchManifest(baseGameData.assetBundleBase);
1558
- return { ...baseGameData, manifest: manifestData };
1559
- }
1560
- return baseGameData;
1561
- }, options);
1562
- },
1563
- list: (options) => {
1564
- return gamesListCache.get("all", () => client["request"]("/games", "GET"), options);
1565
- },
1566
- saveState: async (state) => {
1567
- const gameId = client["_ensureGameId"]();
1568
- await client["request"](`/games/${gameId}/state`, "POST", state);
1569
- },
1570
- loadState: async () => {
1571
- const gameId = client["_ensureGameId"]();
1572
- return client["request"](`/games/${gameId}/state`, "GET");
1573
- },
1574
- startSession: async (gameId) => {
1575
- const idToUse = gameId ?? client["_ensureGameId"]();
1576
- return client["request"](`/games/${idToUse}/sessions`, "POST", {});
1577
- },
1578
- endSession: async (sessionId, gameId) => {
1579
- const effectiveGameIdToEnd = gameId ?? client["_ensureGameId"]();
1580
- if (client["internalClientSessionId"] && sessionId === client["internalClientSessionId"] && effectiveGameIdToEnd === client["gameId"]) {
1581
- client["internalClientSessionId"] = undefined;
1582
- }
1583
- await client["request"](`/games/${effectiveGameIdToEnd}/sessions/${sessionId}/end`, "POST");
1584
- },
1585
- token: {
1586
- create: async (gameId, options) => {
1587
- const res = await client["request"](`/games/${gameId}/token`, "POST");
1588
- if (options?.apply) {
1589
- client.setToken(res.token);
1590
- }
1591
- return res;
1592
- }
1593
- },
1594
- leaderboard: {
1595
- get: async (gameId, options) => {
1596
- const params = new URLSearchParams;
1597
- if (options?.limit)
1598
- params.append("limit", String(options.limit));
1599
- if (options?.offset)
1600
- params.append("offset", String(options.offset));
1601
- const queryString = params.toString();
1602
- const path = queryString ? `/games/${gameId}/leaderboard?${queryString}` : `/games/${gameId}/leaderboard`;
1603
- return client["request"](path, "GET");
1604
- }
1605
- }
1606
- };
1607
- }
1608
- var init_games = __esm(() => {
1609
- init_request();
1610
- });
1611
-
1612
- // src/core/cache/permanent-cache.ts
1613
- function createPermanentCache(keyPrefix) {
1614
- const cache = new Map;
1615
- async function get(key, loader) {
1616
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1617
- const existing = cache.get(fullKey);
1618
- if (existing)
1619
- return existing;
1620
- const promise = loader().catch((error) => {
1621
- cache.delete(fullKey);
1622
- throw error;
1623
- });
1624
- cache.set(fullKey, promise);
1625
- return promise;
1626
- }
1627
- function clear(key) {
1628
- if (key === undefined) {
1629
- cache.clear();
1630
- } else {
1631
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1632
- cache.delete(fullKey);
1633
- }
1634
- }
1635
- function has(key) {
1636
- const fullKey = keyPrefix ? `${keyPrefix}:${key}` : key;
1637
- return cache.has(fullKey);
1638
- }
1639
- function size() {
1640
- return cache.size;
1641
- }
1642
- function keys() {
1643
- const result = [];
1644
- const prefixLen = keyPrefix ? keyPrefix.length + 1 : 0;
1645
- for (const fullKey of cache.keys()) {
1646
- result.push(fullKey.substring(prefixLen));
1647
- }
1648
- return result;
1649
- }
1650
- return { get, clear, has, size, keys };
1651
- }
1652
-
1653
- // src/core/namespaces/users.ts
1654
- function createUsersNamespace(client) {
1655
- const itemIdCache = createPermanentCache("items");
1656
- const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1657
- const resolveItemId = async (identifier) => {
1658
- if (UUID_REGEX.test(identifier))
1659
- return identifier;
1660
- const gameId = client["gameId"];
1661
- const cacheKey = gameId ? `${identifier}:${gameId}` : identifier;
1662
- return itemIdCache.get(cacheKey, async () => {
1663
- const queryParams = new URLSearchParams({ slug: identifier });
1664
- if (gameId)
1665
- queryParams.append("gameId", gameId);
1666
- const item = await client["request"](`/items/resolve?${queryParams.toString()}`, "GET");
1667
- return item.id;
1668
- });
1669
- };
1670
- return {
1671
- me: async () => {
1672
- return client["request"]("/users/me", "GET");
1673
- },
1674
- inventory: {
1675
- get: async () => client["request"](`/inventory`, "GET"),
1676
- add: async (identifier, qty) => {
1677
- const itemId = await resolveItemId(identifier);
1678
- const res = await client["request"](`/inventory/add`, "POST", { itemId, qty });
1679
- client["emit"]("inventoryChange", {
1680
- itemId,
1681
- delta: qty,
1682
- newTotal: res.newTotal
1683
- });
1684
- return res;
1685
- },
1686
- remove: async (identifier, qty) => {
1687
- const itemId = await resolveItemId(identifier);
1688
- const res = await client["request"](`/inventory/remove`, "POST", { itemId, qty });
1689
- client["emit"]("inventoryChange", {
1690
- itemId,
1691
- delta: -qty,
1692
- newTotal: res.newTotal
1693
- });
1694
- return res;
1695
- },
1696
- quantity: async (identifier) => {
1697
- const itemId = await resolveItemId(identifier);
1698
- const inventory = await client["request"](`/inventory`, "GET");
1699
- const item = inventory.find((inv) => inv.item?.id === itemId);
1700
- return item?.quantity ?? 0;
1701
- },
1702
- has: async (identifier, minQuantity = 1) => {
1703
- const itemId = await resolveItemId(identifier);
1704
- const inventory = await client["request"](`/inventory`, "GET");
1705
- const item = inventory.find((inv) => inv.item?.id === itemId);
1706
- const qty = item?.quantity ?? 0;
1707
- return qty >= minQuantity;
1708
- }
1709
- },
1710
- scores: {
1711
- get: async (userIdOrOptions, options) => {
1712
- let userId;
1713
- let queryOptions;
1714
- if (typeof userIdOrOptions === "string") {
1715
- userId = userIdOrOptions;
1716
- queryOptions = options || {};
1717
- } else {
1718
- queryOptions = userIdOrOptions || {};
1719
- const user = await client["request"]("/users/me", "GET");
1720
- userId = user.id;
1721
- }
1722
- const params = new URLSearchParams({
1723
- limit: String(queryOptions.limit || 50)
1724
- });
1725
- if (queryOptions.gameId) {
1726
- params.append("gameId", queryOptions.gameId);
1727
- }
1728
- return client["request"](`/users/${userId}/scores?${params}`, "GET");
1729
- }
1730
- }
1731
- };
1732
- }
1733
- var init_users = () => {};
1734
-
1735
- // src/core/namespaces/dev.ts
1736
- function createDevNamespace(client) {
1737
- return {
1738
- status: {
1739
- apply: () => client["request"]("/dev/apply", "POST"),
1740
- get: async () => {
1741
- const response = await client["request"]("/dev/status", "GET");
1742
- return response.status;
1001
+ // src/core/namespaces/dev.ts
1002
+ function createDevNamespace(client) {
1003
+ return {
1004
+ status: {
1005
+ apply: () => client["request"]("/dev/apply", "POST"),
1006
+ get: async () => {
1007
+ const response = await client["request"]("/dev/status", "GET");
1008
+ return response.status;
1743
1009
  }
1744
1010
  },
1745
1011
  games: {
@@ -1936,121 +1202,847 @@ function createMapsNamespace(client) {
1936
1202
  };
1937
1203
  }
1938
1204
 
1939
- // src/core/namespaces/admin.ts
1940
- function createAdminNamespace(client) {
1941
- return {
1942
- games: {
1943
- pauseGame: (gameId) => client["request"](`/admin/games/${gameId}/pause`, "POST"),
1944
- resumeGame: (gameId) => client["request"](`/admin/games/${gameId}/resume`, "POST")
1945
- },
1946
- items: {
1947
- create: (props) => client["request"]("/items", "POST", props),
1948
- get: (itemId) => client["request"](`/items/${itemId}`, "GET"),
1949
- list: () => client["request"]("/items", "GET"),
1950
- update: (itemId, props) => client["request"](`/items/${itemId}`, "PATCH", props),
1951
- delete: (itemId) => client["request"](`/items/${itemId}`, "DELETE")
1952
- },
1953
- currencies: {
1954
- create: (props) => client["request"]("/currencies", "POST", props),
1955
- get: (currencyId) => client["request"](`/currencies/${currencyId}`, "GET"),
1956
- list: () => client["request"]("/currencies", "GET"),
1957
- update: (currencyId, props) => client["request"](`/currencies/${currencyId}`, "PATCH", props),
1958
- delete: (currencyId) => client["request"](`/currencies/${currencyId}`, "DELETE")
1959
- },
1960
- shopListings: {
1961
- create: (props) => client["request"]("/shop-listings", "POST", props),
1962
- get: (listingId) => client["request"](`/shop-listings/${listingId}`, "GET"),
1963
- list: () => client["request"]("/shop-listings", "GET"),
1964
- update: (listingId, props) => client["request"](`/shop-listings/${listingId}`, "PATCH", props),
1965
- delete: (listingId) => client["request"](`/shop-listings/${listingId}`, "DELETE")
1966
- }
1967
- };
1968
- }
1205
+ // src/core/namespaces/admin.ts
1206
+ function createAdminNamespace(client) {
1207
+ return {
1208
+ games: {
1209
+ pauseGame: (gameId) => client["request"](`/admin/games/${gameId}/pause`, "POST"),
1210
+ resumeGame: (gameId) => client["request"](`/admin/games/${gameId}/resume`, "POST")
1211
+ },
1212
+ items: {
1213
+ create: (props) => client["request"]("/items", "POST", props),
1214
+ get: (itemId) => client["request"](`/items/${itemId}`, "GET"),
1215
+ list: () => client["request"]("/items", "GET"),
1216
+ update: (itemId, props) => client["request"](`/items/${itemId}`, "PATCH", props),
1217
+ delete: (itemId) => client["request"](`/items/${itemId}`, "DELETE")
1218
+ },
1219
+ currencies: {
1220
+ create: (props) => client["request"]("/currencies", "POST", props),
1221
+ get: (currencyId) => client["request"](`/currencies/${currencyId}`, "GET"),
1222
+ list: () => client["request"]("/currencies", "GET"),
1223
+ update: (currencyId, props) => client["request"](`/currencies/${currencyId}`, "PATCH", props),
1224
+ delete: (currencyId) => client["request"](`/currencies/${currencyId}`, "DELETE")
1225
+ },
1226
+ shopListings: {
1227
+ create: (props) => client["request"]("/shop-listings", "POST", props),
1228
+ get: (listingId) => client["request"](`/shop-listings/${listingId}`, "GET"),
1229
+ list: () => client["request"]("/shop-listings", "GET"),
1230
+ update: (listingId, props) => client["request"](`/shop-listings/${listingId}`, "PATCH", props),
1231
+ delete: (listingId) => client["request"](`/shop-listings/${listingId}`, "DELETE")
1232
+ }
1233
+ };
1234
+ }
1235
+
1236
+ // src/core/namespaces/shop.ts
1237
+ function createShopNamespace(client) {
1238
+ return {
1239
+ view: () => {
1240
+ return client["request"]("/shop/view", "GET");
1241
+ }
1242
+ };
1243
+ }
1244
+
1245
+ // src/core/namespaces/telemetry.ts
1246
+ function createTelemetryNamespace(client) {
1247
+ return {
1248
+ pushMetrics: (metrics) => client["request"](`/telemetry/metrics`, "POST", metrics)
1249
+ };
1250
+ }
1251
+
1252
+ // src/core/cache/cooldown-cache.ts
1253
+ function createCooldownCache(defaultCooldownMs) {
1254
+ const lastFetchTime = new Map;
1255
+ const pendingRequests = new Map;
1256
+ const lastResults = new Map;
1257
+ async function get(key, loader, config) {
1258
+ const now = Date.now();
1259
+ const lastFetch = lastFetchTime.get(key) || 0;
1260
+ const timeSinceLastFetch = now - lastFetch;
1261
+ const effectiveCooldown = config?.cooldown !== undefined ? config.cooldown : defaultCooldownMs;
1262
+ const force = config?.force || false;
1263
+ const pending = pendingRequests.get(key);
1264
+ if (pending) {
1265
+ return pending;
1266
+ }
1267
+ if (!force && timeSinceLastFetch < effectiveCooldown) {
1268
+ const cachedResult = lastResults.get(key);
1269
+ if (cachedResult !== undefined) {
1270
+ return Promise.resolve(cachedResult);
1271
+ }
1272
+ }
1273
+ const promise = loader().then((result) => {
1274
+ pendingRequests.delete(key);
1275
+ lastFetchTime.set(key, Date.now());
1276
+ lastResults.set(key, result);
1277
+ return result;
1278
+ }).catch((error) => {
1279
+ pendingRequests.delete(key);
1280
+ throw error;
1281
+ });
1282
+ pendingRequests.set(key, promise);
1283
+ return promise;
1284
+ }
1285
+ function clear(key) {
1286
+ if (key === undefined) {
1287
+ lastFetchTime.clear();
1288
+ pendingRequests.clear();
1289
+ lastResults.clear();
1290
+ } else {
1291
+ lastFetchTime.delete(key);
1292
+ pendingRequests.delete(key);
1293
+ lastResults.delete(key);
1294
+ }
1295
+ }
1296
+ return { get, clear };
1297
+ }
1298
+
1299
+ // src/core/namespaces/levels.ts
1300
+ function createLevelsNamespace(client) {
1301
+ const progressCache = createCooldownCache(5000);
1302
+ return {
1303
+ get: async () => {
1304
+ return client["request"]("/users/level", "GET");
1305
+ },
1306
+ progress: async (options) => {
1307
+ return progressCache.get("user-progress", () => client["request"]("/users/level/progress", "GET"), options);
1308
+ },
1309
+ config: {
1310
+ list: async () => {
1311
+ return client["request"]("/levels/config", "GET");
1312
+ },
1313
+ get: async (level) => {
1314
+ return client["request"](`/levels/config/${level}`, "GET");
1315
+ }
1316
+ }
1317
+ };
1318
+ }
1319
+ var init_levels = () => {};
1320
+
1321
+ // ../data/src/domains/game/table.ts
1322
+ import {
1323
+ jsonb,
1324
+ pgEnum,
1325
+ pgTable,
1326
+ text,
1327
+ timestamp,
1328
+ uniqueIndex,
1329
+ uuid,
1330
+ varchar
1331
+ } from "drizzle-orm/pg-core";
1332
+ var gamePlatformEnum, gameBootModeEnum, gameTypeEnum, games, gameSessions, gameStates;
1333
+ var init_table = __esm(() => {
1334
+ init_table3();
1335
+ init_table4();
1336
+ gamePlatformEnum = pgEnum("game_platform", ["web", "godot", "unity"]);
1337
+ gameBootModeEnum = pgEnum("game_boot_mode", ["iframe", "module"]);
1338
+ gameTypeEnum = pgEnum("game_type", ["hosted", "external"]);
1339
+ games = pgTable("games", {
1340
+ id: uuid("id").primaryKey().defaultRandom(),
1341
+ developerId: text("developer_id").references(() => users.id, {
1342
+ onDelete: "set null"
1343
+ }),
1344
+ slug: varchar("slug", { length: 255 }).notNull().unique(),
1345
+ displayName: varchar("display_name", { length: 255 }).notNull(),
1346
+ version: varchar("version", { length: 50 }).notNull(),
1347
+ gameType: gameTypeEnum("game_type").notNull().default("hosted"),
1348
+ assetBundleBase: text("asset_bundle_base"),
1349
+ externalUrl: text("external_url"),
1350
+ platform: gamePlatformEnum("platform").notNull().default("web"),
1351
+ mapElementId: uuid("map_element_id").references(() => mapElements.id, {
1352
+ onDelete: "set null"
1353
+ }),
1354
+ metadata: jsonb("metadata").$type().notNull().default({}),
1355
+ createdAt: timestamp("created_at", { withTimezone: true }).defaultNow(),
1356
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
1357
+ });
1358
+ gameSessions = pgTable("game_sessions", {
1359
+ id: uuid("id").primaryKey().defaultRandom(),
1360
+ userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1361
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
1362
+ startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
1363
+ endedAt: timestamp("ended_at", { withTimezone: true })
1364
+ });
1365
+ gameStates = pgTable("game_states", {
1366
+ userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1367
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
1368
+ data: jsonb("data").default("{}"),
1369
+ updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
1370
+ }, (table) => [uniqueIndex("unique_user_game_idx").on(table.userId, table.gameId)]);
1371
+ });
1372
+
1373
+ // ../data/src/domains/inventory/table.ts
1374
+ import { relations, sql } from "drizzle-orm";
1375
+ import {
1376
+ boolean,
1377
+ integer,
1378
+ jsonb as jsonb2,
1379
+ pgEnum as pgEnum2,
1380
+ pgTable as pgTable2,
1381
+ text as text2,
1382
+ timestamp as timestamp2,
1383
+ uniqueIndex as uniqueIndex2,
1384
+ uuid as uuid2
1385
+ } from "drizzle-orm/pg-core";
1386
+ var itemTypeEnum, items, inventoryItems, currencies, shopListings, itemsRelations, currenciesRelations, shopListingsRelations, inventoryItemsRelations;
1387
+ var init_table2 = __esm(() => {
1388
+ init_table();
1389
+ init_table3();
1390
+ init_table4();
1391
+ itemTypeEnum = pgEnum2("item_type", [
1392
+ "currency",
1393
+ "badge",
1394
+ "trophy",
1395
+ "collectible",
1396
+ "consumable",
1397
+ "unlock",
1398
+ "upgrade",
1399
+ "accessory",
1400
+ "other"
1401
+ ]);
1402
+ items = pgTable2("items", {
1403
+ id: uuid2("id").primaryKey().defaultRandom(),
1404
+ slug: text2("slug").notNull(),
1405
+ gameId: uuid2("game_id").references(() => games.id, {
1406
+ onDelete: "cascade"
1407
+ }),
1408
+ displayName: text2("display_name").notNull(),
1409
+ description: text2("description"),
1410
+ type: itemTypeEnum("type").notNull().default("other"),
1411
+ isPlaceable: boolean("is_placeable").default(false).notNull(),
1412
+ imageUrl: text2("image_url"),
1413
+ metadata: jsonb2("metadata").default({}),
1414
+ createdAt: timestamp2("created_at").defaultNow().notNull()
1415
+ }, (table) => [
1416
+ uniqueIndex2("items_game_slug_idx").on(table.gameId, table.slug),
1417
+ uniqueIndex2("items_global_slug_idx").on(table.slug).where(sql`game_id IS NULL`)
1418
+ ]);
1419
+ inventoryItems = pgTable2("inventory_items", {
1420
+ id: uuid2("id").primaryKey().defaultRandom(),
1421
+ userId: text2("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1422
+ itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
1423
+ quantity: integer("quantity").notNull().default(1),
1424
+ updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow()
1425
+ }, (table) => [uniqueIndex2("unique_user_item_idx").on(table.userId, table.itemId)]);
1426
+ currencies = pgTable2("currencies", {
1427
+ id: uuid2("id").primaryKey().defaultRandom(),
1428
+ itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
1429
+ symbol: text2("symbol"),
1430
+ isPrimary: boolean("is_primary").default(false).notNull(),
1431
+ createdAt: timestamp2("created_at").defaultNow().notNull(),
1432
+ updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
1433
+ }, (table) => [uniqueIndex2("currency_item_id_idx").on(table.itemId)]);
1434
+ shopListings = pgTable2("shop_listings", {
1435
+ id: uuid2("id").primaryKey().defaultRandom(),
1436
+ itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
1437
+ currencyId: uuid2("currency_id").notNull().references(() => currencies.id, { onDelete: "restrict" }),
1438
+ price: integer("price").notNull(),
1439
+ sellBackPercentage: integer("sell_back_percentage"),
1440
+ stock: integer("stock"),
1441
+ isActive: boolean("is_active").default(true).notNull(),
1442
+ availableFrom: timestamp2("available_from", { withTimezone: true }),
1443
+ availableUntil: timestamp2("available_until", { withTimezone: true }),
1444
+ createdAt: timestamp2("created_at").defaultNow().notNull(),
1445
+ updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
1446
+ }, (table) => [uniqueIndex2("unique_item_currency_listing_idx").on(table.itemId, table.currencyId)]);
1447
+ itemsRelations = relations(items, ({ many }) => ({
1448
+ shopListings: many(shopListings),
1449
+ inventoryItems: many(inventoryItems),
1450
+ mapObjects: many(mapObjects)
1451
+ }));
1452
+ currenciesRelations = relations(currencies, ({ many }) => ({
1453
+ shopListings: many(shopListings)
1454
+ }));
1455
+ shopListingsRelations = relations(shopListings, ({ one }) => ({
1456
+ item: one(items, {
1457
+ fields: [shopListings.itemId],
1458
+ references: [items.id]
1459
+ }),
1460
+ currency: one(currencies, {
1461
+ fields: [shopListings.currencyId],
1462
+ references: [currencies.id]
1463
+ })
1464
+ }));
1465
+ inventoryItemsRelations = relations(inventoryItems, ({ one }) => ({
1466
+ item: one(items, {
1467
+ fields: [inventoryItems.itemId],
1468
+ references: [items.id]
1469
+ }),
1470
+ user: one(users, {
1471
+ fields: [inventoryItems.userId],
1472
+ references: [users.id]
1473
+ })
1474
+ }));
1475
+ });
1476
+
1477
+ // ../data/src/domains/map/table.ts
1478
+ import { relations as relations2 } from "drizzle-orm";
1479
+ import {
1480
+ doublePrecision,
1481
+ index,
1482
+ integer as integer2,
1483
+ jsonb as jsonb3,
1484
+ pgEnum as pgEnum3,
1485
+ pgTable as pgTable3,
1486
+ text as text3,
1487
+ timestamp as timestamp3,
1488
+ uniqueIndex as uniqueIndex3,
1489
+ uuid as uuid3,
1490
+ varchar as varchar2
1491
+ } from "drizzle-orm/pg-core";
1492
+ var interactionTypeEnum, maps, mapElements, mapObjects, mapElementsRelations, mapsRelations, mapObjectsRelations;
1493
+ var init_table3 = __esm(() => {
1494
+ init_table();
1495
+ init_table2();
1496
+ init_table4();
1497
+ interactionTypeEnum = pgEnum3("interaction_type", [
1498
+ "game_entry",
1499
+ "game_registry",
1500
+ "info",
1501
+ "teleport",
1502
+ "door_in",
1503
+ "door_out",
1504
+ "npc_interaction",
1505
+ "quest_trigger"
1506
+ ]);
1507
+ maps = pgTable3("maps", {
1508
+ id: uuid3("id").primaryKey().defaultRandom(),
1509
+ identifier: varchar2("identifier", { length: 255 }).notNull().unique(),
1510
+ displayName: varchar2("display_name", { length: 255 }).notNull(),
1511
+ filePath: varchar2("file_path", { length: 255 }).notNull(),
1512
+ tilesetBasePath: varchar2("tileset_base_path", { length: 255 }).notNull().default("/tilesets"),
1513
+ defaultSpawnTileX: doublePrecision("default_spawn_tile_x").notNull().default(0),
1514
+ defaultSpawnTileY: doublePrecision("default_spawn_tile_y").notNull().default(0),
1515
+ description: text3("description")
1516
+ });
1517
+ mapElements = pgTable3("map_elements", {
1518
+ id: uuid3("id").primaryKey().defaultRandom(),
1519
+ mapId: uuid3("map_id").references(() => maps.id, {
1520
+ onDelete: "cascade"
1521
+ }),
1522
+ elementSlug: varchar2("element_slug", { length: 255 }).notNull(),
1523
+ interactionType: interactionTypeEnum("interaction_type").notNull(),
1524
+ gameId: uuid3("game_id").references(() => games.id, {
1525
+ onDelete: "set null"
1526
+ }),
1527
+ metadata: jsonb3("metadata").$type().default({})
1528
+ }, (table) => [uniqueIndex3("map_id_element_slug_unique_idx").on(table.mapId, table.elementSlug)]);
1529
+ mapObjects = pgTable3("map_objects", {
1530
+ id: uuid3("id").primaryKey().defaultRandom(),
1531
+ userId: text3("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1532
+ mapId: uuid3("map_id").notNull().references(() => maps.id, { onDelete: "cascade" }),
1533
+ itemId: uuid3("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
1534
+ worldX: doublePrecision("world_x").notNull(),
1535
+ worldY: doublePrecision("world_y").notNull(),
1536
+ rotation: integer2("rotation").default(0).notNull(),
1537
+ scale: doublePrecision("scale").default(1).notNull(),
1538
+ createdAt: timestamp3("created_at").defaultNow().notNull()
1539
+ }, (table) => [
1540
+ index("map_objects_map_idx").on(table.mapId),
1541
+ index("map_objects_spatial_idx").on(table.mapId, table.worldX, table.worldY)
1542
+ ]);
1543
+ mapElementsRelations = relations2(mapElements, ({ one }) => ({
1544
+ game: one(games, {
1545
+ fields: [mapElements.gameId],
1546
+ references: [games.id]
1547
+ }),
1548
+ map: one(maps, {
1549
+ fields: [mapElements.mapId],
1550
+ references: [maps.id]
1551
+ })
1552
+ }));
1553
+ mapsRelations = relations2(maps, ({ many }) => ({
1554
+ elements: many(mapElements),
1555
+ objects: many(mapObjects)
1556
+ }));
1557
+ mapObjectsRelations = relations2(mapObjects, ({ one }) => ({
1558
+ user: one(users, {
1559
+ fields: [mapObjects.userId],
1560
+ references: [users.id]
1561
+ }),
1562
+ map: one(maps, {
1563
+ fields: [mapObjects.mapId],
1564
+ references: [maps.id]
1565
+ }),
1566
+ item: one(items, {
1567
+ fields: [mapObjects.itemId],
1568
+ references: [items.id]
1569
+ })
1570
+ }));
1571
+ });
1572
+
1573
+ // ../data/src/domains/user/table.ts
1574
+ import { relations as relations3 } from "drizzle-orm";
1575
+ import { boolean as boolean2, pgEnum as pgEnum4, pgTable as pgTable4, text as text4, timestamp as timestamp4, uniqueIndex as uniqueIndex4 } from "drizzle-orm/pg-core";
1576
+ var userRoleEnum, developerStatusEnum, users, accounts, sessions, verification, ssoProvider, usersRelations;
1577
+ var init_table4 = __esm(() => {
1578
+ init_table3();
1579
+ userRoleEnum = pgEnum4("user_role", ["admin", "player", "developer"]);
1580
+ developerStatusEnum = pgEnum4("developer_status", ["none", "pending", "approved"]);
1581
+ users = pgTable4("user", {
1582
+ id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
1583
+ name: text4("name").notNull(),
1584
+ username: text4("username").unique(),
1585
+ email: text4("email").notNull().unique(),
1586
+ timebackId: text4("timeback_id").unique(),
1587
+ emailVerified: boolean2("email_verified").notNull().default(false),
1588
+ image: text4("image"),
1589
+ role: userRoleEnum("role").notNull().default("player"),
1590
+ developerStatus: developerStatusEnum("developer_status").notNull().default("none"),
1591
+ characterCreated: boolean2("character_created").notNull().default(false),
1592
+ createdAt: timestamp4("created_at", {
1593
+ mode: "date",
1594
+ withTimezone: true
1595
+ }).notNull(),
1596
+ updatedAt: timestamp4("updated_at", {
1597
+ mode: "date",
1598
+ withTimezone: true
1599
+ }).notNull()
1600
+ });
1601
+ accounts = pgTable4("account", {
1602
+ id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
1603
+ userId: text4("userId").notNull().references(() => users.id, { onDelete: "cascade" }),
1604
+ accountId: text4("account_id").notNull(),
1605
+ providerId: text4("provider_id").notNull(),
1606
+ accessToken: text4("access_token"),
1607
+ refreshToken: text4("refresh_token"),
1608
+ idToken: text4("id_token"),
1609
+ accessTokenExpiresAt: timestamp4("access_token_expires_at", {
1610
+ mode: "date",
1611
+ withTimezone: true
1612
+ }),
1613
+ refreshTokenExpiresAt: timestamp4("refresh_token_expires_at", {
1614
+ mode: "date",
1615
+ withTimezone: true
1616
+ }),
1617
+ scope: text4("scope"),
1618
+ password: text4("password"),
1619
+ createdAt: timestamp4("created_at", {
1620
+ mode: "date",
1621
+ withTimezone: true
1622
+ }).notNull(),
1623
+ updatedAt: timestamp4("updated_at", {
1624
+ mode: "date",
1625
+ withTimezone: true
1626
+ }).notNull()
1627
+ }, (table) => [uniqueIndex4("account_provider_providerId_idx").on(table.accountId, table.providerId)]);
1628
+ sessions = pgTable4("session", {
1629
+ id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
1630
+ userId: text4("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1631
+ expiresAt: timestamp4("expires_at", {
1632
+ mode: "date",
1633
+ withTimezone: true
1634
+ }).notNull(),
1635
+ token: text4("token").notNull().unique(),
1636
+ ipAddress: text4("ip_address"),
1637
+ userAgent: text4("user_agent"),
1638
+ createdAt: timestamp4("created_at", {
1639
+ mode: "date",
1640
+ withTimezone: true
1641
+ }).notNull(),
1642
+ updatedAt: timestamp4("updated_at", {
1643
+ mode: "date",
1644
+ withTimezone: true
1645
+ }).notNull()
1646
+ });
1647
+ verification = pgTable4("verification", {
1648
+ id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
1649
+ identifier: text4("identifier").notNull(),
1650
+ value: text4("value").notNull(),
1651
+ expiresAt: timestamp4("expires_at", {
1652
+ mode: "date",
1653
+ withTimezone: true
1654
+ }).notNull(),
1655
+ createdAt: timestamp4("created_at", {
1656
+ mode: "date",
1657
+ withTimezone: true
1658
+ }).notNull(),
1659
+ updatedAt: timestamp4("updated_at", {
1660
+ mode: "date",
1661
+ withTimezone: true
1662
+ }).notNull()
1663
+ });
1664
+ ssoProvider = pgTable4("sso_provider", {
1665
+ id: text4("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
1666
+ issuer: text4("issuer").notNull(),
1667
+ oidcConfig: text4("oidc_config"),
1668
+ samlConfig: text4("saml_config"),
1669
+ userId: text4("user_id").references(() => users.id, { onDelete: "cascade" }),
1670
+ providerId: text4("provider_id").notNull().unique(),
1671
+ organizationId: text4("organization_id"),
1672
+ domain: text4("domain").notNull()
1673
+ });
1674
+ usersRelations = relations3(users, ({ many }) => ({
1675
+ mapObjects: many(mapObjects)
1676
+ }));
1677
+ });
1678
+
1679
+ // ../data/src/domains/developer/table.ts
1680
+ import { pgTable as pgTable5, text as text5, timestamp as timestamp5, uuid as uuid4, varchar as varchar3 } from "drizzle-orm/pg-core";
1681
+ var developerKeys;
1682
+ var init_table5 = __esm(() => {
1683
+ init_table4();
1684
+ developerKeys = pgTable5("developer_keys", {
1685
+ id: uuid4("id").primaryKey().defaultRandom(),
1686
+ userId: text5("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1687
+ label: varchar3("label", { length: 255 }),
1688
+ keyHash: text5("key_hash").notNull().unique(),
1689
+ createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
1690
+ });
1691
+ });
1692
+
1693
+ // ../data/src/domains/level/table.ts
1694
+ import { relations as relations4 } from "drizzle-orm";
1695
+ import {
1696
+ doublePrecision as doublePrecision2,
1697
+ integer as integer3,
1698
+ pgTable as pgTable6,
1699
+ text as text6,
1700
+ timestamp as timestamp6,
1701
+ uniqueIndex as uniqueIndex5,
1702
+ uuid as uuid5
1703
+ } from "drizzle-orm/pg-core";
1704
+ var userLevels, levelConfigs, userLevelsRelations;
1705
+ var init_table6 = __esm(() => {
1706
+ init_table4();
1707
+ userLevels = pgTable6("user_levels", {
1708
+ userId: text6("user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
1709
+ currentLevel: integer3("current_level").notNull().default(1),
1710
+ currentXp: doublePrecision2("current_xp").notNull().default(0),
1711
+ totalXP: doublePrecision2("total_xp").notNull().default(0),
1712
+ lastLevelUpAt: timestamp6("last_level_up_at", { withTimezone: true }),
1713
+ createdAt: timestamp6("created_at").defaultNow().notNull(),
1714
+ updatedAt: timestamp6("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
1715
+ });
1716
+ levelConfigs = pgTable6("level_configs", {
1717
+ id: uuid5("id").primaryKey().defaultRandom(),
1718
+ level: integer3("level").notNull().unique(),
1719
+ xpRequired: integer3("xp_required").notNull(),
1720
+ creditsReward: integer3("credits_reward").notNull().default(0),
1721
+ createdAt: timestamp6("created_at").defaultNow().notNull()
1722
+ }, (table) => [uniqueIndex5("unique_level_config_idx").on(table.level)]);
1723
+ userLevelsRelations = relations4(userLevels, ({ one }) => ({
1724
+ user: one(users, {
1725
+ fields: [userLevels.userId],
1726
+ references: [users.id]
1727
+ })
1728
+ }));
1729
+ });
1730
+
1731
+ // ../data/src/domains/leaderboard/table.ts
1732
+ import { relations as relations5 } from "drizzle-orm";
1733
+ import { index as index2, integer as integer4, jsonb as jsonb4, pgTable as pgTable7, text as text7, timestamp as timestamp7, uuid as uuid6 } from "drizzle-orm/pg-core";
1734
+ var gameScores, gameScoresRelations;
1735
+ var init_table7 = __esm(() => {
1736
+ init_table();
1737
+ init_table4();
1738
+ gameScores = pgTable7("game_scores", {
1739
+ id: uuid6("id").primaryKey().defaultRandom(),
1740
+ userId: text7("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1741
+ gameId: uuid6("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
1742
+ score: integer4("score").notNull(),
1743
+ metadata: jsonb4("metadata").default("{}"),
1744
+ achievedAt: timestamp7("achieved_at", { withTimezone: true }).defaultNow().notNull(),
1745
+ sessionId: uuid6("session_id").references(() => gameSessions.id, { onDelete: "set null" })
1746
+ }, (table) => [
1747
+ index2("game_scores_user_game_idx").on(table.userId, table.gameId),
1748
+ index2("game_scores_game_score_idx").on(table.gameId, table.score),
1749
+ index2("game_scores_achieved_at_idx").on(table.achievedAt)
1750
+ ]);
1751
+ gameScoresRelations = relations5(gameScores, ({ one }) => ({
1752
+ user: one(users, {
1753
+ fields: [gameScores.userId],
1754
+ references: [users.id]
1755
+ }),
1756
+ game: one(games, {
1757
+ fields: [gameScores.gameId],
1758
+ references: [games.id]
1759
+ }),
1760
+ session: one(gameSessions, {
1761
+ fields: [gameScores.sessionId],
1762
+ references: [gameSessions.id]
1763
+ })
1764
+ }));
1765
+ });
1766
+
1767
+ // ../data/src/domains/sprite/table.ts
1768
+ import { relations as relations6 } from "drizzle-orm";
1769
+ import { integer as integer5, pgTable as pgTable8, timestamp as timestamp8, uuid as uuid7, varchar as varchar4 } from "drizzle-orm/pg-core";
1770
+ var spriteTemplates, spriteSheets, spriteTemplatesRelations, spriteSheetsRelations;
1771
+ var init_table8 = __esm(() => {
1772
+ spriteTemplates = pgTable8("sprite_templates", {
1773
+ id: uuid7("id").primaryKey().defaultRandom(),
1774
+ slug: varchar4("slug", { length: 64 }).notNull().unique(),
1775
+ url: varchar4("url", { length: 255 }).notNull(),
1776
+ createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
1777
+ updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
1778
+ });
1779
+ spriteSheets = pgTable8("sprite_sheets", {
1780
+ id: uuid7("id").primaryKey().defaultRandom(),
1781
+ templateId: uuid7("template_id").notNull().references(() => spriteTemplates.id, { onDelete: "cascade" }),
1782
+ width: integer5("width").notNull(),
1783
+ height: integer5("height").notNull(),
1784
+ url: varchar4("url", { length: 255 }).notNull(),
1785
+ createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
1786
+ updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
1787
+ });
1788
+ spriteTemplatesRelations = relations6(spriteTemplates, ({ many }) => ({
1789
+ sheets: many(spriteSheets)
1790
+ }));
1791
+ spriteSheetsRelations = relations6(spriteSheets, ({ one }) => ({
1792
+ template: one(spriteTemplates, {
1793
+ fields: [spriteSheets.templateId],
1794
+ references: [spriteTemplates.id]
1795
+ })
1796
+ }));
1797
+ });
1798
+
1799
+ // ../data/src/domains/character/table.ts
1800
+ import { relations as relations7 } from "drizzle-orm";
1801
+ import {
1802
+ integer as integer6,
1803
+ pgEnum as pgEnum5,
1804
+ pgTable as pgTable9,
1805
+ text as text8,
1806
+ timestamp as timestamp9,
1807
+ uniqueIndex as uniqueIndex6,
1808
+ uuid as uuid8,
1809
+ varchar as varchar5
1810
+ } from "drizzle-orm/pg-core";
1811
+ var characterComponentTypeEnum, characterComponents, playerCharacters, playerCharacterAccessories, characterComponentsRelations, playerCharactersRelations, playerCharacterAccessoriesRelations;
1812
+ var init_table9 = __esm(() => {
1813
+ init_table8();
1814
+ init_table4();
1815
+ characterComponentTypeEnum = pgEnum5("character_component_type", [
1816
+ "body",
1817
+ "outfit",
1818
+ "hairstyle",
1819
+ "eyes",
1820
+ "accessory"
1821
+ ]);
1822
+ characterComponents = pgTable9("character_components", {
1823
+ id: uuid8("id").primaryKey().defaultRandom(),
1824
+ componentType: characterComponentTypeEnum("component_type").notNull(),
1825
+ slug: varchar5("slug", { length: 128 }).notNull().unique(),
1826
+ displayName: varchar5("display_name", { length: 128 }).notNull(),
1827
+ slot: varchar5("slot", { length: 64 }).notNull(),
1828
+ spriteSheetId: uuid8("sprite_sheet_id").notNull().references(() => spriteSheets.id, { onDelete: "cascade" }),
1829
+ unlockLevel: integer6("unlock_level").notNull().default(0),
1830
+ variant: integer6("variant").notNull().default(0),
1831
+ createdAt: timestamp9("created_at", { withTimezone: true }).notNull().defaultNow(),
1832
+ updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
1833
+ });
1834
+ playerCharacters = pgTable9("player_characters", {
1835
+ id: uuid8("id").primaryKey().defaultRandom(),
1836
+ userId: text8("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1837
+ bodyComponentId: uuid8("body_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
1838
+ eyesComponentId: uuid8("eyes_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
1839
+ hairstyleComponentId: uuid8("hairstyle_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
1840
+ outfitComponentId: uuid8("outfit_component_id").notNull().references(() => characterComponents.id, { onDelete: "restrict" }),
1841
+ createdAt: timestamp9("created_at", { withTimezone: true }).notNull().defaultNow(),
1842
+ updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
1843
+ });
1844
+ playerCharacterAccessories = pgTable9("player_character_accessories", {
1845
+ id: uuid8("id").primaryKey().defaultRandom(),
1846
+ playerCharacterId: uuid8("player_character_id").notNull().references(() => playerCharacters.id, { onDelete: "cascade" }),
1847
+ accessoryComponentId: uuid8("accessory_component_id").notNull().references(() => characterComponents.id, { onDelete: "cascade" }),
1848
+ slot: varchar5("slot", { length: 64 }).notNull(),
1849
+ equippedAt: timestamp9("equipped_at", { withTimezone: true }).notNull().defaultNow(),
1850
+ updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
1851
+ }, (table) => [
1852
+ uniqueIndex6("unique_player_character_slot_idx").on(table.playerCharacterId, table.slot),
1853
+ uniqueIndex6("player_character_accessory_idx").on(table.playerCharacterId, table.accessoryComponentId)
1854
+ ]);
1855
+ characterComponentsRelations = relations7(characterComponents, ({ one }) => ({
1856
+ sheet: one(spriteSheets, {
1857
+ fields: [characterComponents.spriteSheetId],
1858
+ references: [spriteSheets.id]
1859
+ })
1860
+ }));
1861
+ playerCharactersRelations = relations7(playerCharacters, ({ one, many }) => ({
1862
+ user: one(users, {
1863
+ fields: [playerCharacters.userId],
1864
+ references: [users.id]
1865
+ }),
1866
+ body: one(characterComponents, {
1867
+ fields: [playerCharacters.bodyComponentId],
1868
+ references: [characterComponents.id]
1869
+ }),
1870
+ eyes: one(characterComponents, {
1871
+ fields: [playerCharacters.eyesComponentId],
1872
+ references: [characterComponents.id]
1873
+ }),
1874
+ hair: one(characterComponents, {
1875
+ fields: [playerCharacters.hairstyleComponentId],
1876
+ references: [characterComponents.id]
1877
+ }),
1878
+ outfit: one(characterComponents, {
1879
+ fields: [playerCharacters.outfitComponentId],
1880
+ references: [characterComponents.id]
1881
+ }),
1882
+ accessories: many(playerCharacterAccessories)
1883
+ }));
1884
+ playerCharacterAccessoriesRelations = relations7(playerCharacterAccessories, ({ one }) => ({
1885
+ playerCharacter: one(playerCharacters, {
1886
+ fields: [playerCharacterAccessories.playerCharacterId],
1887
+ references: [playerCharacters.id]
1888
+ }),
1889
+ accessoryComponent: one(characterComponents, {
1890
+ fields: [playerCharacterAccessories.accessoryComponentId],
1891
+ references: [characterComponents.id]
1892
+ })
1893
+ }));
1894
+ });
1895
+
1896
+ // ../data/src/domains/timeback/table.ts
1897
+ import { doublePrecision as doublePrecision3, pgTable as pgTable10, text as text9, timestamp as timestamp10, uniqueIndex as uniqueIndex7, uuid as uuid9 } from "drizzle-orm/pg-core";
1898
+ var timebackDailyXp, timebackXpEvents;
1899
+ var init_table10 = __esm(() => {
1900
+ init_table4();
1901
+ timebackDailyXp = pgTable10("timeback_daily_xp", {
1902
+ userId: text9("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1903
+ date: timestamp10("date", { mode: "date", withTimezone: true }).notNull(),
1904
+ xp: doublePrecision3("xp").notNull().default(0),
1905
+ createdAt: timestamp10("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow(),
1906
+ updatedAt: timestamp10("updated_at", { mode: "date", withTimezone: true }).notNull().defaultNow()
1907
+ }, (table) => [uniqueIndex7("timeback_daily_xp_user_date_idx").on(table.userId, table.date)]);
1908
+ timebackXpEvents = pgTable10("timeback_xp_event", {
1909
+ id: uuid9("id").primaryKey().defaultRandom(),
1910
+ userId: text9("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1911
+ occurredAt: timestamp10("occurred_at", { withTimezone: true }).notNull(),
1912
+ xpDelta: doublePrecision3("xp_delta").notNull(),
1913
+ source: text9("source").notNull(),
1914
+ sourceId: text9("source_id"),
1915
+ sensor: text9("sensor"),
1916
+ appName: text9("app_name"),
1917
+ createdAt: timestamp10("created_at", { withTimezone: true }).notNull().defaultNow(),
1918
+ updatedAt: timestamp10("updated_at", { withTimezone: true }).notNull().defaultNow()
1919
+ }, (table) => [uniqueIndex7("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
1920
+ });
1921
+
1922
+ // ../data/src/domains/achievement/table.ts
1923
+ import { relations as relations8 } from "drizzle-orm";
1924
+ import {
1925
+ boolean as boolean3,
1926
+ index as index3,
1927
+ integer as integer7,
1928
+ jsonb as jsonb5,
1929
+ pgEnum as pgEnum6,
1930
+ pgTable as pgTable11,
1931
+ text as text10,
1932
+ timestamp as timestamp11,
1933
+ uniqueIndex as uniqueIndex8,
1934
+ uuid as uuid10,
1935
+ varchar as varchar6
1936
+ } from "drizzle-orm/pg-core";
1937
+ var achievementIntervalEnum, achievements, userAchievementProgress, userAchievementClaims, userAchievementProgressRelations, userAchievementClaimsRelations;
1938
+ var init_table11 = __esm(() => {
1939
+ init_table4();
1940
+ achievementIntervalEnum = pgEnum6("achievement_interval", ["daily", "weekly"]);
1941
+ achievements = pgTable11("achievements", {
1942
+ id: varchar6("id", { length: 255 }).primaryKey(),
1943
+ title: varchar6("title", { length: 255 }).notNull(),
1944
+ description: text10("description"),
1945
+ intervalType: achievementIntervalEnum("interval_type").notNull(),
1946
+ rewardCredits: integer7("reward_credits").notNull().default(0),
1947
+ limitPerInterval: integer7("limit_per_interval").notNull().default(1),
1948
+ completionType: varchar6("completion_type", { length: 50 }).notNull(),
1949
+ completionConfig: jsonb5("completion_config").notNull().default({}),
1950
+ scope: jsonb5("scope").notNull().default({}),
1951
+ active: boolean3("active").notNull().default(true),
1952
+ createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow(),
1953
+ updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow()
1954
+ });
1955
+ userAchievementProgress = pgTable11("user_achievement_progress", {
1956
+ id: uuid10("id").primaryKey().defaultRandom(),
1957
+ userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1958
+ achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
1959
+ intervalKey: text10("interval_key").notNull(),
1960
+ progress: jsonb5("progress").notNull().default({}),
1961
+ updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow().notNull()
1962
+ }, (table) => [
1963
+ index3("user_achievement_progress_idx").on(table.userId, table.achievementId, table.intervalKey)
1964
+ ]);
1965
+ userAchievementClaims = pgTable11("user_achievement_claims", {
1966
+ id: uuid10("id").primaryKey().defaultRandom(),
1967
+ userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1968
+ achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
1969
+ intervalKey: text10("interval_key").notNull(),
1970
+ rewardCredits: integer7("reward_credits").notNull(),
1971
+ createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow().notNull()
1972
+ }, (table) => [
1973
+ uniqueIndex8("user_achievement_claims_unique").on(table.userId, table.achievementId, table.intervalKey)
1974
+ ]);
1975
+ userAchievementProgressRelations = relations8(userAchievementProgress, ({ one }) => ({
1976
+ user: one(users, {
1977
+ fields: [userAchievementProgress.userId],
1978
+ references: [users.id]
1979
+ }),
1980
+ achievement: one(achievements, {
1981
+ fields: [userAchievementProgress.achievementId],
1982
+ references: [achievements.id]
1983
+ })
1984
+ }));
1985
+ userAchievementClaimsRelations = relations8(userAchievementClaims, ({ one }) => ({
1986
+ user: one(users, {
1987
+ fields: [userAchievementClaims.userId],
1988
+ references: [users.id]
1989
+ }),
1990
+ achievement: one(achievements, {
1991
+ fields: [userAchievementClaims.achievementId],
1992
+ references: [achievements.id]
1993
+ })
1994
+ }));
1995
+ });
1996
+
1997
+ // ../data/src/tables.index.ts
1998
+ var init_tables_index = __esm(() => {
1999
+ init_table4();
2000
+ init_table5();
2001
+ init_table();
2002
+ init_table2();
2003
+ init_table3();
2004
+ init_table6();
2005
+ init_table7();
2006
+ init_table8();
2007
+ init_table9();
2008
+ init_table10();
2009
+ init_table11();
2010
+ });
1969
2011
 
1970
- // src/core/namespaces/shop.ts
1971
- function createShopNamespace(client) {
1972
- return {
1973
- view: () => {
1974
- return client["request"]("/shop/view", "GET");
1975
- }
2012
+ // ../data/src/constants.ts
2013
+ var ITEM_SLUGS, CURRENCIES, BADGES, ACHIEVEMENT_COMPLETION_TYPES, ACHIEVEMENT_COMPLETION_TYPE, INTERACTION_TYPE;
2014
+ var init_constants = __esm(() => {
2015
+ init_tables_index();
2016
+ ITEM_SLUGS = {
2017
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
2018
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
2019
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
2020
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
2021
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
2022
+ COMMON_SWORD: "COMMON_SWORD",
2023
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
2024
+ SMALL_BACKPACK: "SMALL_BACKPACK",
2025
+ LAVA_LAMP: "LAVA_LAMP",
2026
+ BOOMBOX: "BOOMBOX",
2027
+ CABIN_BED: "CABIN_BED"
1976
2028
  };
1977
- }
1978
-
1979
- // src/core/namespaces/telemetry.ts
1980
- function createTelemetryNamespace(client) {
1981
- return {
1982
- pushMetrics: (metrics) => client["request"](`/telemetry/metrics`, "POST", metrics)
2029
+ CURRENCIES = {
2030
+ PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
2031
+ XP: ITEM_SLUGS.PLAYCADEMY_XP
1983
2032
  };
1984
- }
1985
-
1986
- // src/core/cache/cooldown-cache.ts
1987
- function createCooldownCache(defaultCooldownMs) {
1988
- const lastFetchTime = new Map;
1989
- const pendingRequests = new Map;
1990
- const lastResults = new Map;
1991
- async function get(key, loader, config) {
1992
- const now = Date.now();
1993
- const lastFetch = lastFetchTime.get(key) || 0;
1994
- const timeSinceLastFetch = now - lastFetch;
1995
- const effectiveCooldown = config?.cooldown !== undefined ? config.cooldown : defaultCooldownMs;
1996
- const force = config?.force || false;
1997
- const pending = pendingRequests.get(key);
1998
- if (pending) {
1999
- return pending;
2000
- }
2001
- if (!force && timeSinceLastFetch < effectiveCooldown) {
2002
- const cachedResult = lastResults.get(key);
2003
- if (cachedResult !== undefined) {
2004
- return Promise.resolve(cachedResult);
2005
- }
2006
- }
2007
- const promise = loader().then((result) => {
2008
- pendingRequests.delete(key);
2009
- lastFetchTime.set(key, Date.now());
2010
- lastResults.set(key, result);
2011
- return result;
2012
- }).catch((error) => {
2013
- pendingRequests.delete(key);
2014
- throw error;
2015
- });
2016
- pendingRequests.set(key, promise);
2017
- return promise;
2018
- }
2019
- function clear(key) {
2020
- if (key === undefined) {
2021
- lastFetchTime.clear();
2022
- pendingRequests.clear();
2023
- lastResults.clear();
2024
- } else {
2025
- lastFetchTime.delete(key);
2026
- pendingRequests.delete(key);
2027
- lastResults.delete(key);
2028
- }
2029
- }
2030
- return { get, clear };
2031
- }
2032
-
2033
- // src/core/namespaces/levels.ts
2034
- function createLevelsNamespace(client) {
2035
- const progressCache = createCooldownCache(5000);
2036
- return {
2037
- get: async () => {
2038
- return client["request"]("/users/level", "GET");
2039
- },
2040
- progress: async (options) => {
2041
- return progressCache.get("user-progress", () => client["request"]("/users/level/progress", "GET"), options);
2042
- },
2043
- config: {
2044
- list: async () => {
2045
- return client["request"]("/levels/config", "GET");
2046
- },
2047
- get: async (level) => {
2048
- return client["request"](`/levels/config/${level}`, "GET");
2049
- }
2050
- }
2033
+ BADGES = {
2034
+ FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
2035
+ EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
2036
+ FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
2051
2037
  };
2052
- }
2053
- var init_levels = () => {};
2038
+ ACHIEVEMENT_COMPLETION_TYPES = [
2039
+ "time_played_session",
2040
+ "interaction",
2041
+ "leaderboard_rank"
2042
+ ];
2043
+ ACHIEVEMENT_COMPLETION_TYPE = Object.fromEntries(ACHIEVEMENT_COMPLETION_TYPES.map((value) => [value, value]));
2044
+ INTERACTION_TYPE = Object.fromEntries(interactionTypeEnum.enumValues.map((value) => [value, value]));
2045
+ });
2054
2046
 
2055
2047
  // src/core/cache/singleton-cache.ts
2056
2048
  function createSingletonCache() {
@@ -2583,29 +2575,53 @@ var init_namespaces = __esm(() => {
2583
2575
  });
2584
2576
 
2585
2577
  // src/core/static/init.ts
2586
- async function getPlaycademyConfig() {
2578
+ async function getPlaycademyConfig(allowedParentOrigins) {
2587
2579
  const preloaded = window.PLAYCADEMY;
2588
2580
  if (preloaded?.token) {
2589
2581
  return preloaded;
2590
2582
  }
2591
2583
  if (window.self !== window.top) {
2592
- return await waitForPlaycademyInit();
2584
+ return await waitForPlaycademyInit(allowedParentOrigins);
2593
2585
  } else {
2594
2586
  return createStandaloneConfig();
2595
2587
  }
2596
2588
  }
2597
- async function waitForPlaycademyInit() {
2589
+ function getReferrerOrigin() {
2590
+ try {
2591
+ return document.referrer ? new URL(document.referrer).origin : null;
2592
+ } catch {
2593
+ return null;
2594
+ }
2595
+ }
2596
+ function buildAllowedOrigins(explicit) {
2597
+ if (Array.isArray(explicit) && explicit.length > 0)
2598
+ return explicit;
2599
+ const implicit = [getReferrerOrigin()].filter((v) => !!v);
2600
+ return Array.from(new Set(implicit));
2601
+ }
2602
+ function isOriginAllowed(origin, allowlist) {
2603
+ if (!allowlist || allowlist.length === 0) {
2604
+ return false;
2605
+ }
2606
+ return allowlist.includes(origin);
2607
+ }
2608
+ async function waitForPlaycademyInit(allowedParentOrigins) {
2598
2609
  return new Promise((resolve, reject) => {
2599
2610
  let contextReceived = false;
2600
2611
  const timeoutDuration = 5000;
2612
+ const allowlist = buildAllowedOrigins(allowedParentOrigins);
2601
2613
  const handleMessage = (event) => {
2602
- if (event.data?.type === "PLAYCADEMY_INIT" /* INIT */) {
2603
- contextReceived = true;
2604
- window.removeEventListener("message", handleMessage);
2605
- clearTimeout(timeoutId);
2606
- window.PLAYCADEMY = event.data.payload;
2607
- resolve(event.data.payload);
2614
+ if (event.data?.type !== "PLAYCADEMY_INIT" /* INIT */)
2615
+ return;
2616
+ if (!isOriginAllowed(event.origin, allowlist)) {
2617
+ console.warn("[Playcademy SDK] Ignoring INIT from untrusted origin:", event.origin);
2618
+ return;
2608
2619
  }
2620
+ contextReceived = true;
2621
+ window.removeEventListener("message", handleMessage);
2622
+ clearTimeout(timeoutId);
2623
+ window.PLAYCADEMY = event.data.payload;
2624
+ resolve(event.data.payload);
2609
2625
  };
2610
2626
  window.addEventListener("message", handleMessage);
2611
2627
  const timeoutId = setTimeout(() => {
@@ -2617,26 +2633,30 @@ async function waitForPlaycademyInit() {
2617
2633
  });
2618
2634
  }
2619
2635
  function createStandaloneConfig() {
2636
+ console.warn("[Playcademy SDK] Standalone mode detected, creating mock context for local development");
2620
2637
  const mockConfig = {
2621
2638
  baseUrl: "/api",
2622
2639
  token: "mock-game-token-for-local-dev",
2623
2640
  gameId: "mock-game-id-from-template",
2624
- realtimeUrl: undefined,
2625
- user: undefined
2641
+ realtimeUrl: undefined
2626
2642
  };
2627
2643
  window.PLAYCADEMY = mockConfig;
2628
2644
  return mockConfig;
2629
2645
  }
2630
- async function init() {
2646
+ async function init(options) {
2631
2647
  const { PlaycademyClient } = await Promise.resolve().then(() => (init_client(), exports_client));
2632
2648
  if (typeof window === "undefined") {
2633
2649
  throw new Error("Playcademy SDK must run in a browser context");
2634
2650
  }
2635
- const config = await getPlaycademyConfig();
2651
+ const config = await getPlaycademyConfig(options?.allowedParentOrigins);
2652
+ if (options?.baseUrl) {
2653
+ config.baseUrl = options.baseUrl;
2654
+ }
2636
2655
  const client = new PlaycademyClient({
2637
2656
  baseUrl: config.baseUrl,
2638
2657
  token: config.token,
2639
- gameId: config.gameId
2658
+ gameId: config.gameId,
2659
+ autoStartSession: window.self !== window.top
2640
2660
  });
2641
2661
  client["initPayload"] = config;
2642
2662
  messaging.listen("PLAYCADEMY_TOKEN_REFRESH" /* TOKEN_REFRESH */, ({ token }) => client.setToken(token));
@@ -2713,6 +2733,7 @@ var init_client = __esm(() => {
2713
2733
  baseUrl;
2714
2734
  token;
2715
2735
  gameId;
2736
+ config;
2716
2737
  listeners = {};
2717
2738
  internalClientSessionId;
2718
2739
  authContext;
@@ -2727,14 +2748,9 @@ var init_client = __esm(() => {
2727
2748
  this.token = config.token;
2728
2749
  this.gameId = config.gameId;
2729
2750
  }
2730
- this.detectAuthContext();
2731
- if (this.gameId) {
2732
- this._initializeInternalSession().catch((error) => {
2733
- log.error("[Playcademy SDK] Background initialization of auto-session failed:", {
2734
- error
2735
- });
2736
- });
2737
- }
2751
+ this.config = config || {};
2752
+ this._detectAuthContext();
2753
+ this._initializeInternalSession().catch(() => {});
2738
2754
  }
2739
2755
  getBaseUrl() {
2740
2756
  const isRelative = this.baseUrl.startsWith("/");
@@ -2748,6 +2764,9 @@ var init_client = __esm(() => {
2748
2764
  this.token = token ?? undefined;
2749
2765
  this.emit("authChange", { token: this.token ?? null });
2750
2766
  }
2767
+ getToken() {
2768
+ return this.token ?? null;
2769
+ }
2751
2770
  isAuthenticated() {
2752
2771
  return !!this.token;
2753
2772
  }
@@ -2783,16 +2802,22 @@ var init_client = __esm(() => {
2783
2802
  }
2784
2803
  return this.gameId;
2785
2804
  }
2786
- detectAuthContext() {
2805
+ _detectAuthContext() {
2787
2806
  this.authContext = { isInIframe: isInIframe() };
2788
2807
  }
2789
2808
  async _initializeInternalSession() {
2790
- if (!this.gameId || this.internalClientSessionId) {
2809
+ if (!this.gameId || this.internalClientSessionId)
2810
+ return;
2811
+ const shouldAutoStart = this.config.autoStartSession ?? true;
2812
+ if (!shouldAutoStart)
2791
2813
  return;
2792
- }
2793
2814
  try {
2794
2815
  const response = await this.games.startSession(this.gameId);
2795
2816
  this.internalClientSessionId = response.sessionId;
2817
+ log.debug("[Playcademy SDK] Auto-started game session", {
2818
+ gameId: this.gameId,
2819
+ sessionId: this.internalClientSessionId
2820
+ });
2796
2821
  } catch (error) {
2797
2822
  log.error("[Playcademy SDK] Auto-starting session failed for game", {
2798
2823
  gameId: this.gameId,
@@ -2825,18 +2850,28 @@ var init_client = __esm(() => {
2825
2850
  };
2826
2851
  });
2827
2852
 
2828
- // src/types.ts
2829
- init_constants();
2830
- var AuthProvider = {
2831
- TIMEBACK: "TIMEBACK"
2832
- };
2833
-
2834
2853
  // src/index.ts
2835
2854
  init_client();
2836
2855
  init_messaging();
2856
+
2857
+ // src/constants.ts
2858
+ var CURRENCIES2 = {
2859
+ PRIMARY: "PLAYCADEMY_CREDITS",
2860
+ XP: "PLAYCADEMY_XP"
2861
+ };
2862
+ var BADGES2 = {
2863
+ FOUNDING_MEMBER: "FOUNDING_MEMBER_BADGE",
2864
+ EARLY_ADOPTER: "EARLY_ADOPTER_BADGE",
2865
+ FIRST_GAME: "FIRST_GAME_BADGE"
2866
+ };
2867
+ var AuthProvider = {
2868
+ TIMEBACK: "TIMEBACK"
2869
+ };
2837
2870
  export {
2838
2871
  messaging,
2839
2872
  PlaycademyClient,
2840
2873
  MessageEvents,
2874
+ CURRENCIES2 as CURRENCIES,
2875
+ BADGES2 as BADGES,
2841
2876
  AuthProvider
2842
2877
  };