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