@playcademy/sdk 0.0.7 → 0.0.9

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.js CHANGED
@@ -14,7 +14,14 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
14
14
  var isBrowser = () => {
15
15
  const g = globalThis;
16
16
  return typeof g.window !== "undefined" && typeof g.document !== "undefined";
17
- }, colors, getLevelColor = (level) => {
17
+ }, colors, shouldUseColor = () => {
18
+ const preference = (process.env.LOG_COLOR ?? "auto").toLowerCase();
19
+ if (preference === "always")
20
+ return true;
21
+ if (preference === "never")
22
+ return false;
23
+ return Boolean(process.stdout && process.stdout.isTTY && true);
24
+ }, getLevelColor = (level) => {
18
25
  switch (level) {
19
26
  case "debug":
20
27
  return colors.blue;
@@ -38,7 +45,7 @@ var isBrowser = () => {
38
45
  }
39
46
  }, logOnServer = (level, message, context) => {
40
47
  const consoleMethod = getConsoleMethod(level);
41
- if (true) {
48
+ if (shouldUseColor()) {
42
49
  const timestamp = new Date().toISOString();
43
50
  const levelColor = getLevelColor(level);
44
51
  const levelUpper = level.toUpperCase().padEnd(5);
@@ -48,7 +55,10 @@ var isBrowser = () => {
48
55
  } else {
49
56
  consoleMethod(`${coloredPrefix} ${message}`);
50
57
  }
51
- } else {}
58
+ } else {
59
+ const formatted = formatLog(level, message, context);
60
+ consoleMethod(formatted);
61
+ }
52
62
  }, getConsoleMethod = (level) => {
53
63
  switch (level) {
54
64
  case "debug":
@@ -62,6 +72,18 @@ var isBrowser = () => {
62
72
  default:
63
73
  return console.log;
64
74
  }
75
+ }, formatLog = (level, message, context) => {
76
+ const timestamp = new Date().toISOString();
77
+ const logEntry = {
78
+ timestamp,
79
+ level: level.toUpperCase(),
80
+ message,
81
+ ...context && Object.keys(context).length > 0 && { context }
82
+ };
83
+ if (true) {
84
+ return JSON.stringify(logEntry, null, 2);
85
+ }
86
+ return JSON.stringify(logEntry);
65
87
  }, performLog = (level, message, context) => {
66
88
  if (level === "debug" && false) {}
67
89
  if (isBrowser()) {
@@ -91,6 +113,85 @@ var init_src = __esm(() => {
91
113
  log = createLogger();
92
114
  });
93
115
 
116
+ // src/core/auth/strategies.ts
117
+ class ApiKeyAuth {
118
+ apiKey;
119
+ constructor(apiKey) {
120
+ this.apiKey = apiKey;
121
+ }
122
+ getToken() {
123
+ return this.apiKey;
124
+ }
125
+ getType() {
126
+ return "apiKey";
127
+ }
128
+ getHeaders() {
129
+ return { "x-api-key": this.apiKey };
130
+ }
131
+ }
132
+
133
+ class SessionAuth {
134
+ sessionToken;
135
+ constructor(sessionToken) {
136
+ this.sessionToken = sessionToken;
137
+ }
138
+ getToken() {
139
+ return this.sessionToken;
140
+ }
141
+ getType() {
142
+ return "session";
143
+ }
144
+ getHeaders() {
145
+ return { Authorization: `Bearer ${this.sessionToken}` };
146
+ }
147
+ }
148
+
149
+ class GameJwtAuth {
150
+ gameToken;
151
+ constructor(gameToken) {
152
+ this.gameToken = gameToken;
153
+ }
154
+ getToken() {
155
+ return this.gameToken;
156
+ }
157
+ getType() {
158
+ return "gameJwt";
159
+ }
160
+ getHeaders() {
161
+ return { Authorization: `Bearer ${this.gameToken}` };
162
+ }
163
+ }
164
+
165
+ class NoAuth {
166
+ getToken() {
167
+ return null;
168
+ }
169
+ getType() {
170
+ return "session";
171
+ }
172
+ getHeaders() {
173
+ return {};
174
+ }
175
+ }
176
+ function createAuthStrategy(token, tokenType) {
177
+ if (!token) {
178
+ return new NoAuth;
179
+ }
180
+ if (tokenType === "apiKey") {
181
+ return new ApiKeyAuth(token);
182
+ }
183
+ if (tokenType === "session") {
184
+ return new SessionAuth(token);
185
+ }
186
+ if (tokenType === "gameJwt") {
187
+ return new GameJwtAuth(token);
188
+ }
189
+ if (token.startsWith("cademy")) {
190
+ return new ApiKeyAuth(token);
191
+ }
192
+ return new GameJwtAuth(token);
193
+ }
194
+
94
195
  // src/core/auth/utils.ts
95
196
  function openPopupWindow(url, name = "auth-popup", width = 500, height = 600) {
96
197
  const left = window.screenX + (window.outerWidth - width) / 2;
@@ -146,9 +247,14 @@ function createAuthNamespace(client) {
146
247
  return {
147
248
  login: async (credentials) => {
148
249
  try {
149
- const response = await client["request"]("/auth/login", "POST", credentials);
150
- client.setToken(response.token);
151
- return { success: true, token: response.token };
250
+ const response = await client["request"]("/auth/sign-in/email", "POST", credentials);
251
+ client.setToken(response.token, "session");
252
+ return {
253
+ success: true,
254
+ token: response.token,
255
+ user: response.user,
256
+ expiresAt: response.expiresAt
257
+ };
152
258
  } catch (error) {
153
259
  return {
154
260
  success: false,
@@ -157,8 +263,29 @@ function createAuthNamespace(client) {
157
263
  }
158
264
  },
159
265
  logout: async () => {
160
- await client["request"]("/auth/logout", "POST");
266
+ try {
267
+ await client["request"]("/auth/sign-out", "POST");
268
+ } catch {}
161
269
  client.setToken(null);
270
+ },
271
+ apiKeys: {
272
+ create: async (options) => {
273
+ return client["request"]("/dev/api-keys", "POST", {
274
+ name: options?.name || `SDK Key - ${new Date().toISOString()}`,
275
+ expiresIn: options?.expiresIn !== undefined ? options.expiresIn : null,
276
+ permissions: options?.permissions || {
277
+ games: ["read", "write", "delete"],
278
+ users: ["read:self", "write:self"],
279
+ dev: ["read", "write"]
280
+ }
281
+ });
282
+ },
283
+ list: async () => {
284
+ return client["request"]("/auth/api-key/list", "GET");
285
+ },
286
+ revoke: async (keyId) => {
287
+ await client["request"]("/auth/api-key/revoke", "POST", { id: keyId });
288
+ }
162
289
  }
163
290
  };
164
291
  }
@@ -736,7 +863,6 @@ function createTTLCache(options) {
736
863
  async function request({
737
864
  path,
738
865
  baseUrl,
739
- token,
740
866
  method = "GET",
741
867
  body,
742
868
  extraHeaders = {}
@@ -750,8 +876,6 @@ async function request({
750
876
  payload = JSON.stringify(body);
751
877
  headers["Content-Type"] = "application/json";
752
878
  }
753
- if (token)
754
- headers["Authorization"] = `Bearer ${token}`;
755
879
  const res = await fetch(url, {
756
880
  method,
757
881
  headers,
@@ -819,7 +943,7 @@ function createGamesNamespace(client) {
819
943
  const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
820
944
  return gameFetchCache.get(gameIdOrSlug, async () => {
821
945
  const baseGameData = await promise;
822
- if (baseGameData.gameType === "hosted" && baseGameData.assetBundleBase !== null) {
946
+ if (baseGameData.gameType === "hosted" && baseGameData.assetBundleBase !== null && baseGameData.assetBundleBase !== "") {
823
947
  const manifestData = await fetchManifest(baseGameData.assetBundleBase);
824
948
  return { ...baseGameData, manifest: manifestData };
825
949
  }
@@ -1009,153 +1133,163 @@ function createDevNamespace(client) {
1009
1133
  }
1010
1134
  },
1011
1135
  games: {
1012
- upsert: async (slug, metadata, file, hooks) => {
1013
- hooks?.onEvent?.({ type: "init" });
1014
- const game = await client["request"](`/games/${slug}`, "PUT", metadata);
1015
- if (metadata.gameType === "external" || file === null) {
1016
- return game;
1017
- }
1018
- const fileName = file instanceof File ? file.name : "game.zip";
1019
- const initiateResponse = await client["request"]("/games/uploads/initiate/", "POST", {
1020
- fileName,
1021
- gameId: game.id
1022
- });
1023
- if (hooks?.onEvent) {
1024
- await new Promise((resolve, reject) => {
1025
- const xhr = new XMLHttpRequest;
1026
- xhr.open("PUT", initiateResponse.presignedUrl, true);
1027
- const contentType = file.type || "application/octet-stream";
1028
- try {
1029
- xhr.setRequestHeader("Content-Type", contentType);
1030
- } catch {}
1031
- xhr.upload.onprogress = (event) => {
1032
- if (event.lengthComputable) {
1033
- const percent = event.loaded / event.total;
1034
- hooks.onEvent?.({
1035
- type: "s3Progress",
1036
- loaded: event.loaded,
1037
- total: event.total,
1038
- percent
1039
- });
1040
- }
1041
- };
1042
- xhr.onload = () => {
1043
- if (xhr.status >= 200 && xhr.status < 300)
1044
- resolve();
1045
- else
1046
- reject(new Error(`File upload failed: ${xhr.status} ${xhr.statusText}`));
1047
- };
1048
- xhr.onerror = () => reject(new Error("File upload failed: network error"));
1049
- xhr.send(file);
1136
+ deploy: {
1137
+ frontend: async (slug, metadata, file, hooks) => {
1138
+ hooks?.onEvent?.({ type: "init" });
1139
+ const game = await client["request"](`/games/${slug}`, "PUT", metadata);
1140
+ if (metadata.gameType === "external" || file === null) {
1141
+ return game;
1142
+ }
1143
+ const fileName = file instanceof File ? file.name : "game.zip";
1144
+ const initiateResponse = await client["request"]("/games/uploads/initiate/", "POST", {
1145
+ fileName,
1146
+ gameId: game.id
1050
1147
  });
1051
- } else {
1052
- const uploadResponse = await fetch(initiateResponse.presignedUrl, {
1053
- method: "PUT",
1054
- body: file,
1055
- headers: {
1056
- "Content-Type": file.type || "application/octet-stream"
1148
+ if (hooks?.onEvent && typeof XMLHttpRequest !== "undefined") {
1149
+ await new Promise((resolve, reject) => {
1150
+ const xhr = new XMLHttpRequest;
1151
+ xhr.open("PUT", initiateResponse.presignedUrl, true);
1152
+ const contentType = file.type || "application/octet-stream";
1153
+ try {
1154
+ xhr.setRequestHeader("Content-Type", contentType);
1155
+ } catch {}
1156
+ xhr.upload.onprogress = (event) => {
1157
+ if (event.lengthComputable) {
1158
+ const percent = event.loaded / event.total;
1159
+ hooks.onEvent?.({
1160
+ type: "s3Progress",
1161
+ loaded: event.loaded,
1162
+ total: event.total,
1163
+ percent
1164
+ });
1165
+ }
1166
+ };
1167
+ xhr.onload = () => {
1168
+ if (xhr.status >= 200 && xhr.status < 300)
1169
+ resolve();
1170
+ else
1171
+ reject(new Error(`File upload failed: ${xhr.status} ${xhr.statusText}`));
1172
+ };
1173
+ xhr.onerror = () => reject(new Error("File upload failed: network error"));
1174
+ xhr.send(file);
1175
+ });
1176
+ } else {
1177
+ const uploadResponse = await fetch(initiateResponse.presignedUrl, {
1178
+ method: "PUT",
1179
+ body: file,
1180
+ headers: {
1181
+ "Content-Type": file.type || "application/octet-stream"
1182
+ }
1183
+ });
1184
+ if (!uploadResponse.ok) {
1185
+ throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
1186
+ }
1187
+ }
1188
+ const baseUrl = (() => {
1189
+ const anyClient = client;
1190
+ try {
1191
+ return typeof anyClient.getBaseUrl === "function" ? anyClient.getBaseUrl() : "/api";
1192
+ } catch {
1193
+ return "/api";
1194
+ }
1195
+ })();
1196
+ const finalizeUrl = baseUrl.replace(/\/$/, "") + "/games/uploads/finalize/";
1197
+ const authToken = client.getToken();
1198
+ const tokenType = client.getTokenType();
1199
+ const headers = {
1200
+ "Content-Type": "application/json"
1201
+ };
1202
+ if (authToken) {
1203
+ if (tokenType === "apiKey") {
1204
+ headers["x-api-key"] = authToken;
1205
+ } else {
1206
+ headers["Authorization"] = `Bearer ${authToken}`;
1057
1207
  }
1208
+ }
1209
+ const finalizeResponse = await fetch(finalizeUrl, {
1210
+ method: "POST",
1211
+ headers,
1212
+ body: JSON.stringify({
1213
+ tempS3Key: initiateResponse.tempS3Key,
1214
+ gameId: initiateResponse.gameId,
1215
+ version: initiateResponse.version,
1216
+ slug,
1217
+ metadata,
1218
+ originalFileName: fileName
1219
+ }),
1220
+ credentials: "omit"
1058
1221
  });
1059
- if (!uploadResponse.ok) {
1060
- throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
1222
+ if (!finalizeResponse.ok) {
1223
+ const errText = await finalizeResponse.text().catch(() => "");
1224
+ throw new Error(`Finalize request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1061
1225
  }
1062
- }
1063
- const baseUrl = (() => {
1064
- const anyClient = client;
1065
- try {
1066
- return typeof anyClient.getBaseUrl === "function" ? anyClient.getBaseUrl() : "/api";
1067
- } catch {
1068
- return "/api";
1226
+ if (!finalizeResponse.body) {
1227
+ throw new Error("Finalize response body missing");
1069
1228
  }
1070
- })();
1071
- const finalizeUrl = baseUrl.replace(/\/$/, "") + "/games/uploads/finalize/";
1072
- const authToken = client.token;
1073
- const finalizeResponse = await fetch(finalizeUrl, {
1074
- method: "POST",
1075
- headers: {
1076
- "Content-Type": "application/json",
1077
- ...authToken ? { Authorization: `Bearer ${authToken}` } : {}
1078
- },
1079
- body: JSON.stringify({
1080
- tempS3Key: initiateResponse.tempS3Key,
1081
- gameId: initiateResponse.gameId,
1082
- version: initiateResponse.version,
1083
- slug,
1084
- metadata,
1085
- originalFileName: fileName
1086
- }),
1087
- credentials: "omit"
1088
- });
1089
- if (!finalizeResponse.ok) {
1090
- const errText = await finalizeResponse.text().catch(() => "");
1091
- throw new Error(`Finalize request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1092
- }
1093
- if (!finalizeResponse.body) {
1094
- throw new Error("Finalize response body missing");
1095
- }
1096
- hooks?.onEvent?.({ type: "finalizeStart" });
1097
- let sawAnyServerEvent = false;
1098
- const reader = finalizeResponse.body.pipeThrough(new TextDecoderStream).getReader();
1099
- let buffer = "";
1100
- while (true) {
1101
- const { done, value } = await reader.read();
1102
- if (done) {
1103
- if (!sawAnyServerEvent) {
1104
- hooks?.onClose?.();
1105
- hooks?.onEvent?.({ type: "close" });
1229
+ hooks?.onEvent?.({ type: "finalizeStart" });
1230
+ let sawAnyServerEvent = false;
1231
+ const reader = finalizeResponse.body.pipeThrough(new TextDecoderStream).getReader();
1232
+ let buffer = "";
1233
+ while (true) {
1234
+ const { done, value } = await reader.read();
1235
+ if (done) {
1236
+ if (!sawAnyServerEvent) {
1237
+ hooks?.onClose?.();
1238
+ hooks?.onEvent?.({ type: "close" });
1239
+ }
1240
+ break;
1106
1241
  }
1107
- break;
1108
- }
1109
- buffer += value;
1110
- let eolIndex;
1111
- while ((eolIndex = buffer.indexOf(`
1242
+ buffer += value;
1243
+ let eolIndex;
1244
+ while ((eolIndex = buffer.indexOf(`
1112
1245
 
1113
1246
  `)) >= 0) {
1114
- const message = buffer.slice(0, eolIndex);
1115
- buffer = buffer.slice(eolIndex + 2);
1116
- const eventLine = message.match(/^event: (.*)$/m);
1117
- const dataLine = message.match(/^data: (.*)$/m);
1118
- if (eventLine && dataLine) {
1119
- const eventType = eventLine[1];
1120
- const eventData = JSON.parse(dataLine[1]);
1121
- if (eventType === "uploadProgress") {
1122
- sawAnyServerEvent = true;
1123
- const percent = (eventData.value ?? 0) / 100;
1124
- hooks?.onEvent?.({
1125
- type: "finalizeProgress",
1126
- percent,
1127
- currentFileLabel: eventData.currentFileLabel || ""
1128
- });
1129
- } else if (eventType === "status") {
1130
- sawAnyServerEvent = true;
1131
- if (eventData.message) {
1247
+ const message = buffer.slice(0, eolIndex);
1248
+ buffer = buffer.slice(eolIndex + 2);
1249
+ const eventLine = message.match(/^event: (.*)$/m);
1250
+ const dataLine = message.match(/^data: (.*)$/m);
1251
+ if (eventLine && dataLine) {
1252
+ const eventType = eventLine[1];
1253
+ const eventData = JSON.parse(dataLine[1]);
1254
+ if (eventType === "uploadProgress") {
1255
+ sawAnyServerEvent = true;
1256
+ const percent = (eventData.value ?? 0) / 100;
1132
1257
  hooks?.onEvent?.({
1133
- type: "finalizeStatus",
1134
- message: eventData.message
1258
+ type: "finalizeProgress",
1259
+ percent,
1260
+ currentFileLabel: eventData.currentFileLabel || ""
1135
1261
  });
1262
+ } else if (eventType === "status") {
1263
+ sawAnyServerEvent = true;
1264
+ if (eventData.message) {
1265
+ hooks?.onEvent?.({
1266
+ type: "finalizeStatus",
1267
+ message: eventData.message
1268
+ });
1269
+ }
1270
+ } else if (eventType === "complete") {
1271
+ sawAnyServerEvent = true;
1272
+ reader.cancel();
1273
+ return eventData;
1274
+ } else if (eventType === "error") {
1275
+ sawAnyServerEvent = true;
1276
+ reader.cancel();
1277
+ throw new Error(eventData.message);
1136
1278
  }
1137
- } else if (eventType === "complete") {
1138
- sawAnyServerEvent = true;
1139
- reader.cancel();
1140
- return eventData;
1141
- } else if (eventType === "error") {
1142
- sawAnyServerEvent = true;
1143
- reader.cancel();
1144
- throw new Error(eventData.message);
1145
1279
  }
1146
1280
  }
1147
1281
  }
1282
+ throw new Error("Upload completed but no final game data received");
1283
+ },
1284
+ backend: async (slug, bundle) => {
1285
+ return client["request"](`/games/${slug}/backend/deploy`, "POST", bundle);
1148
1286
  }
1149
- throw new Error("Upload completed but no final game data received");
1150
1287
  },
1151
- update: (gameId, props) => client["request"](`/games/${gameId}`, "PATCH", props),
1288
+ upsert: async (slug, metadata) => {
1289
+ return client.dev.games.deploy.frontend(slug, metadata, null);
1290
+ },
1152
1291
  delete: (gameId) => client["request"](`/games/${gameId}`, "DELETE")
1153
1292
  },
1154
- keys: {
1155
- create: (label) => client["request"](`/dev/keys`, "POST", { label }),
1156
- list: () => client["request"](`/dev/keys`, "GET"),
1157
- revoke: (keyId) => client["request"](`/dev/keys/${keyId}`, "DELETE")
1158
- },
1159
1293
  items: {
1160
1294
  create: (gameId, slug, itemData) => client["request"](`/games/${gameId}/items`, "POST", {
1161
1295
  slug,
@@ -1191,9 +1325,17 @@ function createDevNamespace(client) {
1191
1325
 
1192
1326
  // src/core/namespaces/maps.ts
1193
1327
  function createMapsNamespace(client) {
1328
+ const mapDataCache = createTTLCache({
1329
+ ttl: 5 * 60 * 1000,
1330
+ keyPrefix: "maps.data"
1331
+ });
1332
+ const mapElementsCache = createTTLCache({
1333
+ ttl: 60 * 1000,
1334
+ keyPrefix: "maps.elements"
1335
+ });
1194
1336
  return {
1195
- get: (identifier) => client["request"](`/maps/${identifier}`, "GET"),
1196
- elements: (mapId) => client["request"](`/map/elements?mapId=${mapId}`, "GET"),
1337
+ get: (identifier, options) => mapDataCache.get(identifier, () => client["request"](`/maps/${identifier}`, "GET"), options),
1338
+ elements: (mapId, options) => mapElementsCache.get(mapId, () => client["request"](`/map/elements?mapId=${mapId}`, "GET"), options),
1197
1339
  objects: {
1198
1340
  list: (mapId) => client["request"](`/maps/${mapId}/objects`, "GET"),
1199
1341
  create: (mapId, objectData) => client["request"](`/maps/${mapId}/objects`, "POST", objectData),
@@ -1201,6 +1343,7 @@ function createMapsNamespace(client) {
1201
1343
  }
1202
1344
  };
1203
1345
  }
1346
+ var init_maps = () => {};
1204
1347
 
1205
1348
  // src/core/namespaces/admin.ts
1206
1349
  function createAdminNamespace(client) {
@@ -1320,6 +1463,7 @@ var init_levels = () => {};
1320
1463
 
1321
1464
  // ../data/src/domains/game/table.ts
1322
1465
  import {
1466
+ boolean,
1323
1467
  jsonb,
1324
1468
  pgEnum,
1325
1469
  pgTable,
@@ -1329,7 +1473,7 @@ import {
1329
1473
  uuid,
1330
1474
  varchar
1331
1475
  } from "drizzle-orm/pg-core";
1332
- var gamePlatformEnum, gameBootModeEnum, gameTypeEnum, games, gameSessions, gameStates;
1476
+ var gamePlatformEnum, gameBootModeEnum, gameTypeEnum, games, gameSessions, gameStates, deploymentProviderEnum, gameBackendDeployments;
1333
1477
  var init_table = __esm(() => {
1334
1478
  init_table3();
1335
1479
  init_table4();
@@ -1368,12 +1512,23 @@ var init_table = __esm(() => {
1368
1512
  data: jsonb("data").default("{}"),
1369
1513
  updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow()
1370
1514
  }, (table) => [uniqueIndex("unique_user_game_idx").on(table.userId, table.gameId)]);
1515
+ deploymentProviderEnum = pgEnum("deployment_provider", ["cloudflare", "aws"]);
1516
+ gameBackendDeployments = pgTable("game_backend_deployments", {
1517
+ id: uuid("id").primaryKey().defaultRandom(),
1518
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
1519
+ deploymentId: text("deployment_id").notNull(),
1520
+ provider: deploymentProviderEnum("provider").notNull(),
1521
+ url: text("url").notNull(),
1522
+ codeHash: text("code_hash"),
1523
+ isActive: boolean("is_active").notNull().default(false),
1524
+ deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow()
1525
+ });
1371
1526
  });
1372
1527
 
1373
1528
  // ../data/src/domains/inventory/table.ts
1374
1529
  import { relations, sql } from "drizzle-orm";
1375
1530
  import {
1376
- boolean,
1531
+ boolean as boolean2,
1377
1532
  integer,
1378
1533
  jsonb as jsonb2,
1379
1534
  pgEnum as pgEnum2,
@@ -1408,7 +1563,7 @@ var init_table2 = __esm(() => {
1408
1563
  displayName: text2("display_name").notNull(),
1409
1564
  description: text2("description"),
1410
1565
  type: itemTypeEnum("type").notNull().default("other"),
1411
- isPlaceable: boolean("is_placeable").default(false).notNull(),
1566
+ isPlaceable: boolean2("is_placeable").default(false).notNull(),
1412
1567
  imageUrl: text2("image_url"),
1413
1568
  metadata: jsonb2("metadata").default({}),
1414
1569
  createdAt: timestamp2("created_at").defaultNow().notNull()
@@ -1427,7 +1582,7 @@ var init_table2 = __esm(() => {
1427
1582
  id: uuid2("id").primaryKey().defaultRandom(),
1428
1583
  itemId: uuid2("item_id").notNull().references(() => items.id, { onDelete: "cascade" }),
1429
1584
  symbol: text2("symbol"),
1430
- isPrimary: boolean("is_primary").default(false).notNull(),
1585
+ isPrimary: boolean2("is_primary").default(false).notNull(),
1431
1586
  createdAt: timestamp2("created_at").defaultNow().notNull(),
1432
1587
  updatedAt: timestamp2("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
1433
1588
  }, (table) => [uniqueIndex2("currency_item_id_idx").on(table.itemId)]);
@@ -1438,7 +1593,7 @@ var init_table2 = __esm(() => {
1438
1593
  price: integer("price").notNull(),
1439
1594
  sellBackPercentage: integer("sell_back_percentage"),
1440
1595
  stock: integer("stock"),
1441
- isActive: boolean("is_active").default(true).notNull(),
1596
+ isActive: boolean2("is_active").default(true).notNull(),
1442
1597
  availableFrom: timestamp2("available_from", { withTimezone: true }),
1443
1598
  availableUntil: timestamp2("available_until", { withTimezone: true }),
1444
1599
  createdAt: timestamp2("created_at").defaultNow().notNull(),
@@ -1572,7 +1727,7 @@ var init_table3 = __esm(() => {
1572
1727
 
1573
1728
  // ../data/src/domains/user/table.ts
1574
1729
  import { relations as relations3 } from "drizzle-orm";
1575
- import { boolean as boolean2, pgEnum as pgEnum4, pgTable as pgTable4, text as text4, timestamp as timestamp4, uniqueIndex as uniqueIndex4 } from "drizzle-orm/pg-core";
1730
+ import { boolean as boolean3, pgEnum as pgEnum4, pgTable as pgTable4, text as text4, timestamp as timestamp4, uniqueIndex as uniqueIndex4 } from "drizzle-orm/pg-core";
1576
1731
  var userRoleEnum, developerStatusEnum, users, accounts, sessions, verification, ssoProvider, usersRelations;
1577
1732
  var init_table4 = __esm(() => {
1578
1733
  init_table3();
@@ -1584,11 +1739,11 @@ var init_table4 = __esm(() => {
1584
1739
  username: text4("username").unique(),
1585
1740
  email: text4("email").notNull().unique(),
1586
1741
  timebackId: text4("timeback_id").unique(),
1587
- emailVerified: boolean2("email_verified").notNull().default(false),
1742
+ emailVerified: boolean3("email_verified").notNull().default(false),
1588
1743
  image: text4("image"),
1589
1744
  role: userRoleEnum("role").notNull().default("player"),
1590
1745
  developerStatus: developerStatusEnum("developer_status").notNull().default("none"),
1591
- characterCreated: boolean2("character_created").notNull().default(false),
1746
+ characterCreated: boolean3("character_created").notNull().default(false),
1592
1747
  createdAt: timestamp4("created_at", {
1593
1748
  mode: "date",
1594
1749
  withTimezone: true
@@ -1677,16 +1832,32 @@ var init_table4 = __esm(() => {
1677
1832
  });
1678
1833
 
1679
1834
  // ../data/src/domains/developer/table.ts
1680
- import { pgTable as pgTable5, text as text5, timestamp as timestamp5, uuid as uuid4, varchar as varchar3 } from "drizzle-orm/pg-core";
1681
- var developerKeys;
1835
+ import { boolean as boolean4, integer as integer3, pgTable as pgTable5, text as text5, timestamp as timestamp5, uuid as uuid4 } from "drizzle-orm/pg-core";
1836
+ var apikey;
1682
1837
  var init_table5 = __esm(() => {
1683
1838
  init_table4();
1684
- developerKeys = pgTable5("developer_keys", {
1839
+ apikey = pgTable5("api_key", {
1685
1840
  id: uuid4("id").primaryKey().defaultRandom(),
1841
+ name: text5("name"),
1842
+ start: text5("start"),
1843
+ prefix: text5("prefix"),
1844
+ key: text5("key").notNull(),
1686
1845
  userId: text5("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1687
- label: varchar3("label", { length: 255 }),
1688
- keyHash: text5("key_hash").notNull().unique(),
1689
- createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow()
1846
+ refillInterval: integer3("refill_interval"),
1847
+ refillAmount: integer3("refill_amount"),
1848
+ lastRefillAt: timestamp5("last_refill_at", { withTimezone: true }),
1849
+ enabled: boolean4("enabled").notNull().default(true),
1850
+ rateLimitEnabled: boolean4("rate_limit_enabled").notNull().default(false),
1851
+ rateLimitTimeWindow: integer3("rate_limit_time_window"),
1852
+ rateLimitMax: integer3("rate_limit_max"),
1853
+ requestCount: integer3("request_count").notNull().default(0),
1854
+ remaining: integer3("remaining"),
1855
+ lastRequest: timestamp5("last_request", { withTimezone: true }),
1856
+ expiresAt: timestamp5("expires_at", { withTimezone: true }),
1857
+ createdAt: timestamp5("created_at", { withTimezone: true }).notNull().defaultNow(),
1858
+ updatedAt: timestamp5("updated_at", { withTimezone: true }).notNull().defaultNow(),
1859
+ permissions: text5("permissions"),
1860
+ metadata: text5("metadata")
1690
1861
  });
1691
1862
  });
1692
1863
 
@@ -1694,7 +1865,7 @@ var init_table5 = __esm(() => {
1694
1865
  import { relations as relations4 } from "drizzle-orm";
1695
1866
  import {
1696
1867
  doublePrecision as doublePrecision2,
1697
- integer as integer3,
1868
+ integer as integer4,
1698
1869
  pgTable as pgTable6,
1699
1870
  text as text6,
1700
1871
  timestamp as timestamp6,
@@ -1706,7 +1877,7 @@ var init_table6 = __esm(() => {
1706
1877
  init_table4();
1707
1878
  userLevels = pgTable6("user_levels", {
1708
1879
  userId: text6("user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
1709
- currentLevel: integer3("current_level").notNull().default(1),
1880
+ currentLevel: integer4("current_level").notNull().default(1),
1710
1881
  currentXp: doublePrecision2("current_xp").notNull().default(0),
1711
1882
  totalXP: doublePrecision2("total_xp").notNull().default(0),
1712
1883
  lastLevelUpAt: timestamp6("last_level_up_at", { withTimezone: true }),
@@ -1715,9 +1886,9 @@ var init_table6 = __esm(() => {
1715
1886
  });
1716
1887
  levelConfigs = pgTable6("level_configs", {
1717
1888
  id: uuid5("id").primaryKey().defaultRandom(),
1718
- level: integer3("level").notNull().unique(),
1719
- xpRequired: integer3("xp_required").notNull(),
1720
- creditsReward: integer3("credits_reward").notNull().default(0),
1889
+ level: integer4("level").notNull().unique(),
1890
+ xpRequired: integer4("xp_required").notNull(),
1891
+ creditsReward: integer4("credits_reward").notNull().default(0),
1721
1892
  createdAt: timestamp6("created_at").defaultNow().notNull()
1722
1893
  }, (table) => [uniqueIndex5("unique_level_config_idx").on(table.level)]);
1723
1894
  userLevelsRelations = relations4(userLevels, ({ one }) => ({
@@ -1730,7 +1901,7 @@ var init_table6 = __esm(() => {
1730
1901
 
1731
1902
  // ../data/src/domains/leaderboard/table.ts
1732
1903
  import { relations as relations5 } from "drizzle-orm";
1733
- import { index as index2, integer as integer4, jsonb as jsonb4, pgTable as pgTable7, text as text7, timestamp as timestamp7, uuid as uuid6 } from "drizzle-orm/pg-core";
1904
+ import { index as index2, integer as integer5, jsonb as jsonb4, pgTable as pgTable7, text as text7, timestamp as timestamp7, uuid as uuid6 } from "drizzle-orm/pg-core";
1734
1905
  var gameScores, gameScoresRelations;
1735
1906
  var init_table7 = __esm(() => {
1736
1907
  init_table();
@@ -1739,7 +1910,7 @@ var init_table7 = __esm(() => {
1739
1910
  id: uuid6("id").primaryKey().defaultRandom(),
1740
1911
  userId: text7("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1741
1912
  gameId: uuid6("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
1742
- score: integer4("score").notNull(),
1913
+ score: integer5("score").notNull(),
1743
1914
  metadata: jsonb4("metadata").default("{}"),
1744
1915
  achievedAt: timestamp7("achieved_at", { withTimezone: true }).defaultNow().notNull(),
1745
1916
  sessionId: uuid6("session_id").references(() => gameSessions.id, { onDelete: "set null" })
@@ -1766,22 +1937,22 @@ var init_table7 = __esm(() => {
1766
1937
 
1767
1938
  // ../data/src/domains/sprite/table.ts
1768
1939
  import { relations as relations6 } from "drizzle-orm";
1769
- import { integer as integer5, pgTable as pgTable8, timestamp as timestamp8, uuid as uuid7, varchar as varchar4 } from "drizzle-orm/pg-core";
1940
+ import { integer as integer6, pgTable as pgTable8, timestamp as timestamp8, uuid as uuid7, varchar as varchar3 } from "drizzle-orm/pg-core";
1770
1941
  var spriteTemplates, spriteSheets, spriteTemplatesRelations, spriteSheetsRelations;
1771
1942
  var init_table8 = __esm(() => {
1772
1943
  spriteTemplates = pgTable8("sprite_templates", {
1773
1944
  id: uuid7("id").primaryKey().defaultRandom(),
1774
- slug: varchar4("slug", { length: 64 }).notNull().unique(),
1775
- url: varchar4("url", { length: 255 }).notNull(),
1945
+ slug: varchar3("slug", { length: 64 }).notNull().unique(),
1946
+ url: varchar3("url", { length: 255 }).notNull(),
1776
1947
  createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
1777
1948
  updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
1778
1949
  });
1779
1950
  spriteSheets = pgTable8("sprite_sheets", {
1780
1951
  id: uuid7("id").primaryKey().defaultRandom(),
1781
1952
  templateId: uuid7("template_id").notNull().references(() => spriteTemplates.id, { onDelete: "cascade" }),
1782
- width: integer5("width").notNull(),
1783
- height: integer5("height").notNull(),
1784
- url: varchar4("url", { length: 255 }).notNull(),
1953
+ width: integer6("width").notNull(),
1954
+ height: integer6("height").notNull(),
1955
+ url: varchar3("url", { length: 255 }).notNull(),
1785
1956
  createdAt: timestamp8("created_at", { withTimezone: true }).notNull().defaultNow(),
1786
1957
  updatedAt: timestamp8("updated_at", { withTimezone: true }).notNull().defaultNow()
1787
1958
  });
@@ -1799,14 +1970,14 @@ var init_table8 = __esm(() => {
1799
1970
  // ../data/src/domains/character/table.ts
1800
1971
  import { relations as relations7 } from "drizzle-orm";
1801
1972
  import {
1802
- integer as integer6,
1973
+ integer as integer7,
1803
1974
  pgEnum as pgEnum5,
1804
1975
  pgTable as pgTable9,
1805
1976
  text as text8,
1806
1977
  timestamp as timestamp9,
1807
1978
  uniqueIndex as uniqueIndex6,
1808
1979
  uuid as uuid8,
1809
- varchar as varchar5
1980
+ varchar as varchar4
1810
1981
  } from "drizzle-orm/pg-core";
1811
1982
  var characterComponentTypeEnum, characterComponents, playerCharacters, playerCharacterAccessories, characterComponentsRelations, playerCharactersRelations, playerCharacterAccessoriesRelations;
1812
1983
  var init_table9 = __esm(() => {
@@ -1822,12 +1993,12 @@ var init_table9 = __esm(() => {
1822
1993
  characterComponents = pgTable9("character_components", {
1823
1994
  id: uuid8("id").primaryKey().defaultRandom(),
1824
1995
  componentType: characterComponentTypeEnum("component_type").notNull(),
1825
- slug: varchar5("slug", { length: 128 }).notNull().unique(),
1826
- displayName: varchar5("display_name", { length: 128 }).notNull(),
1827
- slot: varchar5("slot", { length: 64 }).notNull(),
1996
+ slug: varchar4("slug", { length: 128 }).notNull().unique(),
1997
+ displayName: varchar4("display_name", { length: 128 }).notNull(),
1998
+ slot: varchar4("slot", { length: 64 }).notNull(),
1828
1999
  spriteSheetId: uuid8("sprite_sheet_id").notNull().references(() => spriteSheets.id, { onDelete: "cascade" }),
1829
- unlockLevel: integer6("unlock_level").notNull().default(0),
1830
- variant: integer6("variant").notNull().default(0),
2000
+ unlockLevel: integer7("unlock_level").notNull().default(0),
2001
+ variant: integer7("variant").notNull().default(0),
1831
2002
  createdAt: timestamp9("created_at", { withTimezone: true }).notNull().defaultNow(),
1832
2003
  updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
1833
2004
  });
@@ -1845,7 +2016,7 @@ var init_table9 = __esm(() => {
1845
2016
  id: uuid8("id").primaryKey().defaultRandom(),
1846
2017
  playerCharacterId: uuid8("player_character_id").notNull().references(() => playerCharacters.id, { onDelete: "cascade" }),
1847
2018
  accessoryComponentId: uuid8("accessory_component_id").notNull().references(() => characterComponents.id, { onDelete: "cascade" }),
1848
- slot: varchar5("slot", { length: 64 }).notNull(),
2019
+ slot: varchar4("slot", { length: 64 }).notNull(),
1849
2020
  equippedAt: timestamp9("equipped_at", { withTimezone: true }).notNull().defaultNow(),
1850
2021
  updatedAt: timestamp9("updated_at", { withTimezone: true }).notNull().defaultNow()
1851
2022
  }, (table) => [
@@ -1895,8 +2066,9 @@ var init_table9 = __esm(() => {
1895
2066
 
1896
2067
  // ../data/src/domains/timeback/table.ts
1897
2068
  import { doublePrecision as doublePrecision3, pgTable as pgTable10, text as text9, timestamp as timestamp10, uniqueIndex as uniqueIndex7, uuid as uuid9 } from "drizzle-orm/pg-core";
1898
- var timebackDailyXp, timebackXpEvents;
2069
+ var timebackDailyXp, timebackXpEvents, gameTimebackIntegrations;
1899
2070
  var init_table10 = __esm(() => {
2071
+ init_table();
1900
2072
  init_table4();
1901
2073
  timebackDailyXp = pgTable10("timeback_daily_xp", {
1902
2074
  userId: text9("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
@@ -1917,14 +2089,22 @@ var init_table10 = __esm(() => {
1917
2089
  createdAt: timestamp10("created_at", { withTimezone: true }).notNull().defaultNow(),
1918
2090
  updatedAt: timestamp10("updated_at", { withTimezone: true }).notNull().defaultNow()
1919
2091
  }, (table) => [uniqueIndex7("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
2092
+ gameTimebackIntegrations = pgTable10("game_timeback_integrations", {
2093
+ id: uuid9("id").primaryKey().defaultRandom(),
2094
+ gameId: uuid9("game_id").notNull().unique().references(() => games.id, { onDelete: "cascade" }),
2095
+ courseId: text9("course_id").notNull(),
2096
+ lastVerifiedAt: timestamp10("last_verified_at", { withTimezone: true }),
2097
+ createdAt: timestamp10("created_at", { withTimezone: true }).notNull().defaultNow(),
2098
+ updatedAt: timestamp10("updated_at", { withTimezone: true }).notNull().defaultNow()
2099
+ });
1920
2100
  });
1921
2101
 
1922
2102
  // ../data/src/domains/achievement/table.ts
1923
2103
  import { relations as relations8 } from "drizzle-orm";
1924
2104
  import {
1925
- boolean as boolean3,
2105
+ boolean as boolean5,
1926
2106
  index as index3,
1927
- integer as integer7,
2107
+ integer as integer8,
1928
2108
  jsonb as jsonb5,
1929
2109
  pgEnum as pgEnum6,
1930
2110
  pgTable as pgTable11,
@@ -1932,45 +2112,55 @@ import {
1932
2112
  timestamp as timestamp11,
1933
2113
  uniqueIndex as uniqueIndex8,
1934
2114
  uuid as uuid10,
1935
- varchar as varchar6
2115
+ varchar as varchar5
1936
2116
  } from "drizzle-orm/pg-core";
1937
- var achievementIntervalEnum, achievements, userAchievementProgress, userAchievementClaims, userAchievementProgressRelations, userAchievementClaimsRelations;
2117
+ var achievementScopeEnum, achievements, userAchievementProgress, userAchievementClaims, userAchievementProgressRelations, userAchievementClaimsRelations;
1938
2118
  var init_table11 = __esm(() => {
1939
2119
  init_table4();
1940
- achievementIntervalEnum = pgEnum6("achievement_interval", ["daily", "weekly"]);
2120
+ achievementScopeEnum = pgEnum6("achievement_scope", [
2121
+ "daily",
2122
+ "weekly",
2123
+ "monthly",
2124
+ "yearly",
2125
+ "game",
2126
+ "global",
2127
+ "map",
2128
+ "level",
2129
+ "event"
2130
+ ]);
1941
2131
  achievements = pgTable11("achievements", {
1942
- id: varchar6("id", { length: 255 }).primaryKey(),
1943
- title: varchar6("title", { length: 255 }).notNull(),
2132
+ id: varchar5("id", { length: 255 }).primaryKey(),
2133
+ title: varchar5("title", { length: 255 }).notNull(),
1944
2134
  description: text10("description"),
1945
- intervalType: achievementIntervalEnum("interval_type").notNull(),
1946
- rewardCredits: integer7("reward_credits").notNull().default(0),
1947
- limitPerInterval: integer7("limit_per_interval").notNull().default(1),
1948
- completionType: varchar6("completion_type", { length: 50 }).notNull(),
2135
+ scope: achievementScopeEnum("scope").notNull(),
2136
+ rewardCredits: integer8("reward_credits").notNull().default(0),
2137
+ limit: integer8("limit").notNull().default(1),
2138
+ completionType: varchar5("completion_type", { length: 50 }).notNull(),
1949
2139
  completionConfig: jsonb5("completion_config").notNull().default({}),
1950
- scope: jsonb5("scope").notNull().default({}),
1951
- active: boolean3("active").notNull().default(true),
2140
+ target: jsonb5("target").notNull().default({}),
2141
+ active: boolean5("active").notNull().default(true),
1952
2142
  createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow(),
1953
2143
  updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow()
1954
2144
  });
1955
2145
  userAchievementProgress = pgTable11("user_achievement_progress", {
1956
2146
  id: uuid10("id").primaryKey().defaultRandom(),
1957
2147
  userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1958
- achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
1959
- intervalKey: text10("interval_key").notNull(),
2148
+ achievementId: varchar5("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
2149
+ scopeKey: text10("scope_key").notNull(),
1960
2150
  progress: jsonb5("progress").notNull().default({}),
1961
2151
  updatedAt: timestamp11("updated_at", { withTimezone: true }).defaultNow().notNull()
1962
2152
  }, (table) => [
1963
- index3("user_achievement_progress_idx").on(table.userId, table.achievementId, table.intervalKey)
2153
+ index3("user_achievement_progress_idx").on(table.userId, table.achievementId, table.scopeKey)
1964
2154
  ]);
1965
2155
  userAchievementClaims = pgTable11("user_achievement_claims", {
1966
2156
  id: uuid10("id").primaryKey().defaultRandom(),
1967
2157
  userId: text10("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
1968
- achievementId: varchar6("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
1969
- intervalKey: text10("interval_key").notNull(),
1970
- rewardCredits: integer7("reward_credits").notNull(),
2158
+ achievementId: varchar5("achievement_id", { length: 255 }).notNull().references(() => achievements.id, { onDelete: "cascade" }),
2159
+ scopeKey: text10("scope_key").notNull(),
2160
+ rewardCredits: integer8("reward_credits").notNull(),
1971
2161
  createdAt: timestamp11("created_at", { withTimezone: true }).defaultNow().notNull()
1972
2162
  }, (table) => [
1973
- uniqueIndex8("user_achievement_claims_unique").on(table.userId, table.achievementId, table.intervalKey)
2163
+ uniqueIndex8("user_achievement_claims_unique").on(table.userId, table.achievementId, table.scopeKey)
1974
2164
  ]);
1975
2165
  userAchievementProgressRelations = relations8(userAchievementProgress, ({ one }) => ({
1976
2166
  user: one(users, {
@@ -1994,6 +2184,58 @@ var init_table11 = __esm(() => {
1994
2184
  }));
1995
2185
  });
1996
2186
 
2187
+ // ../data/src/domains/notification/table.ts
2188
+ import { relations as relations9 } from "drizzle-orm";
2189
+ import { index as index4, jsonb as jsonb6, pgEnum as pgEnum7, pgTable as pgTable12, text as text11, timestamp as timestamp12, uuid as uuid11, varchar as varchar6 } from "drizzle-orm/pg-core";
2190
+ var notificationPriorityEnum, notificationStatusEnum, notifications, notificationsRelations;
2191
+ var init_table12 = __esm(() => {
2192
+ init_table4();
2193
+ notificationPriorityEnum = pgEnum7("notification_priority", [
2194
+ "low",
2195
+ "normal",
2196
+ "high",
2197
+ "urgent"
2198
+ ]);
2199
+ notificationStatusEnum = pgEnum7("notification_status", [
2200
+ "pending",
2201
+ "delivered",
2202
+ "seen",
2203
+ "clicked",
2204
+ "dismissed",
2205
+ "expired"
2206
+ ]);
2207
+ notifications = pgTable12("notifications", {
2208
+ id: uuid11("id").primaryKey().defaultRandom(),
2209
+ userId: text11("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
2210
+ type: varchar6("type", { length: 50 }).notNull(),
2211
+ title: varchar6("title", { length: 255 }).notNull(),
2212
+ message: text11("message").notNull(),
2213
+ data: jsonb6("data").notNull().default({}),
2214
+ priority: notificationPriorityEnum("priority").notNull().default("normal"),
2215
+ status: notificationStatusEnum("status").notNull().default("pending"),
2216
+ createdAt: timestamp12("created_at", { withTimezone: true }).defaultNow().notNull(),
2217
+ deliveredAt: timestamp12("delivered_at", { withTimezone: true }),
2218
+ seenAt: timestamp12("seen_at", { withTimezone: true }),
2219
+ clickedAt: timestamp12("clicked_at", { withTimezone: true }),
2220
+ expiresAt: timestamp12("expires_at", { withTimezone: true }),
2221
+ method: varchar6("method", { length: 50 }),
2222
+ clickUrl: text11("click_url"),
2223
+ metadata: jsonb6("metadata").notNull().default({})
2224
+ }, (table) => [
2225
+ index4("notifications_user_id_idx").on(table.userId),
2226
+ index4("notifications_status_idx").on(table.status),
2227
+ index4("notifications_type_idx").on(table.type),
2228
+ index4("notifications_created_at_idx").on(table.createdAt),
2229
+ index4("notifications_user_status_idx").on(table.userId, table.status)
2230
+ ]);
2231
+ notificationsRelations = relations9(notifications, ({ one }) => ({
2232
+ user: one(users, {
2233
+ fields: [notifications.userId],
2234
+ references: [users.id]
2235
+ })
2236
+ }));
2237
+ });
2238
+
1997
2239
  // ../data/src/tables.index.ts
1998
2240
  var init_tables_index = __esm(() => {
1999
2241
  init_table4();
@@ -2007,10 +2249,11 @@ var init_tables_index = __esm(() => {
2007
2249
  init_table9();
2008
2250
  init_table10();
2009
2251
  init_table11();
2252
+ init_table12();
2010
2253
  });
2011
2254
 
2012
2255
  // ../data/src/constants.ts
2013
- var ITEM_SLUGS, CURRENCIES, BADGES, ACHIEVEMENT_COMPLETION_TYPES, ACHIEVEMENT_COMPLETION_TYPE, INTERACTION_TYPE;
2256
+ var ITEM_SLUGS, CURRENCIES, BADGES, INTERACTION_TYPE;
2014
2257
  var init_constants = __esm(() => {
2015
2258
  init_tables_index();
2016
2259
  ITEM_SLUGS = {
@@ -2035,12 +2278,6 @@ var init_constants = __esm(() => {
2035
2278
  EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
2036
2279
  FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
2037
2280
  };
2038
- ACHIEVEMENT_COMPLETION_TYPES = [
2039
- "time_played_session",
2040
- "interaction",
2041
- "leaderboard_rank"
2042
- ];
2043
- ACHIEVEMENT_COMPLETION_TYPE = Object.fromEntries(ACHIEVEMENT_COMPLETION_TYPES.map((value) => [value, value]));
2044
2281
  INTERACTION_TYPE = Object.fromEntries(interactionTypeEnum.enumValues.map((value) => [value, value]));
2045
2282
  });
2046
2283
 
@@ -2517,6 +2754,32 @@ var init_achievements = () => {};
2517
2754
  // src/core/namespaces/timeback.ts
2518
2755
  function createTimebackNamespace(client) {
2519
2756
  return {
2757
+ recordProgress: (progressData) => {
2758
+ return client["request"]("/api/integrations/timeback/progress", "POST", { progressData });
2759
+ },
2760
+ recordSessionEnd: (sessionData) => {
2761
+ return client["request"]("/api/integrations/timeback/session-end", "POST", { sessionData });
2762
+ },
2763
+ awardXP: (xpAmount, metadata) => {
2764
+ return client["request"]("/api/integrations/timeback/award-xp", "POST", { xpAmount, metadata });
2765
+ },
2766
+ management: {
2767
+ setup: (request2) => {
2768
+ return client["request"]("/timeback/setup", "POST", request2);
2769
+ },
2770
+ verify: (gameId) => {
2771
+ return client["request"](`/timeback/verify/${gameId}`, "GET");
2772
+ },
2773
+ cleanup: (gameId) => {
2774
+ return client["request"](`/timeback/integrations/${gameId}`, "DELETE");
2775
+ },
2776
+ get: (gameId) => {
2777
+ return client["request"](`/timeback/integrations/${gameId}`, "GET");
2778
+ },
2779
+ getConfig: (gameId) => {
2780
+ return client["request"](`/timeback/config/${gameId}`, "GET");
2781
+ }
2782
+ },
2520
2783
  xp: {
2521
2784
  today: async (options) => {
2522
2785
  const params = new URLSearchParams;
@@ -2560,18 +2823,88 @@ function createTimebackNamespace(client) {
2560
2823
  };
2561
2824
  }
2562
2825
 
2826
+ // src/core/namespaces/notifications.ts
2827
+ function createNotificationsNamespace(client) {
2828
+ const notificationsListCache = createTTLCache({
2829
+ ttl: 5 * 1000,
2830
+ keyPrefix: "notifications.list"
2831
+ });
2832
+ const notificationStatsCache = createTTLCache({
2833
+ ttl: 30 * 1000,
2834
+ keyPrefix: "notifications.stats"
2835
+ });
2836
+ return {
2837
+ list: async (queryOptions, cacheOptions) => {
2838
+ const params = new URLSearchParams;
2839
+ if (queryOptions?.status)
2840
+ params.append("status", queryOptions.status);
2841
+ if (queryOptions?.type)
2842
+ params.append("type", queryOptions.type);
2843
+ if (queryOptions?.limit)
2844
+ params.append("limit", String(queryOptions.limit));
2845
+ if (queryOptions?.offset)
2846
+ params.append("offset", String(queryOptions.offset));
2847
+ const qs = params.toString();
2848
+ const path = qs ? `/notifications?${qs}` : "/notifications";
2849
+ const cacheKey = qs ? `list-${qs}` : "list";
2850
+ return notificationsListCache.get(cacheKey, () => client["request"](path, "GET"), cacheOptions);
2851
+ },
2852
+ markAsSeen: async (notificationId) => {
2853
+ const result = await client["request"](`/notifications/${notificationId}/status`, "PATCH", {
2854
+ id: notificationId,
2855
+ status: "seen"
2856
+ });
2857
+ notificationsListCache.clear();
2858
+ return result;
2859
+ },
2860
+ markAsClicked: async (notificationId) => {
2861
+ const result = await client["request"](`/notifications/${notificationId}/status`, "PATCH", {
2862
+ id: notificationId,
2863
+ status: "clicked"
2864
+ });
2865
+ notificationsListCache.clear();
2866
+ return result;
2867
+ },
2868
+ dismiss: async (notificationId) => {
2869
+ const result = await client["request"](`/notifications/${notificationId}/status`, "PATCH", {
2870
+ id: notificationId,
2871
+ status: "dismissed"
2872
+ });
2873
+ notificationsListCache.clear();
2874
+ return result;
2875
+ },
2876
+ stats: {
2877
+ get: async (queryOptions, cacheOptions) => {
2878
+ const user = await client.users.me();
2879
+ const params = new URLSearchParams;
2880
+ if (queryOptions?.from)
2881
+ params.append("from", queryOptions.from);
2882
+ if (queryOptions?.to)
2883
+ params.append("to", queryOptions.to);
2884
+ const qs = params.toString();
2885
+ const path = qs ? `/notifications/stats/${user.id}?${qs}` : `/notifications/stats/${user.id}`;
2886
+ const cacheKey = qs ? `stats-${qs}` : "stats";
2887
+ return notificationStatsCache.get(cacheKey, () => client["request"](path, "GET"), cacheOptions);
2888
+ }
2889
+ }
2890
+ };
2891
+ }
2892
+ var init_notifications = () => {};
2893
+
2563
2894
  // src/core/namespaces/index.ts
2564
2895
  var init_namespaces = __esm(() => {
2565
2896
  init_identity();
2566
2897
  init_runtime();
2567
2898
  init_games();
2568
2899
  init_users();
2900
+ init_maps();
2569
2901
  init_levels();
2570
2902
  init_credits();
2571
2903
  init_character();
2572
2904
  init_sprites();
2573
2905
  init_realtime();
2574
2906
  init_achievements();
2907
+ init_notifications();
2575
2908
  });
2576
2909
 
2577
2910
  // src/core/static/init.ts
@@ -2596,11 +2929,12 @@ function getReferrerOrigin() {
2596
2929
  function buildAllowedOrigins(explicit) {
2597
2930
  if (Array.isArray(explicit) && explicit.length > 0)
2598
2931
  return explicit;
2599
- const implicit = [getReferrerOrigin()].filter((v) => !!v);
2600
- return Array.from(new Set(implicit));
2932
+ const ref = getReferrerOrigin();
2933
+ return ref ? [ref] : [];
2601
2934
  }
2602
2935
  function isOriginAllowed(origin, allowlist) {
2603
2936
  if (!allowlist || allowlist.length === 0) {
2937
+ console.error("[Playcademy SDK] No allowed origins configured. Consider passing allowedParentOrigins explicitly to init().");
2604
2938
  return false;
2605
2939
  }
2606
2940
  return allowlist.includes(origin);
@@ -2610,11 +2944,18 @@ async function waitForPlaycademyInit(allowedParentOrigins) {
2610
2944
  let contextReceived = false;
2611
2945
  const timeoutDuration = 5000;
2612
2946
  const allowlist = buildAllowedOrigins(allowedParentOrigins);
2947
+ let hasWarnedAboutUntrustedOrigin = false;
2948
+ function warnAboutUntrustedOrigin(origin) {
2949
+ if (hasWarnedAboutUntrustedOrigin)
2950
+ return;
2951
+ hasWarnedAboutUntrustedOrigin = true;
2952
+ console.warn("[Playcademy SDK] Ignoring INIT from untrusted origin:", origin);
2953
+ }
2613
2954
  const handleMessage = (event) => {
2614
2955
  if (event.data?.type !== "PLAYCADEMY_INIT" /* INIT */)
2615
2956
  return;
2616
2957
  if (!isOriginAllowed(event.origin, allowlist)) {
2617
- console.warn("[Playcademy SDK] Ignoring INIT from untrusted origin:", event.origin);
2958
+ warnAboutUntrustedOrigin(event.origin);
2618
2959
  return;
2619
2960
  }
2620
2961
  contextReceived = true;
@@ -2731,7 +3072,7 @@ var init_client = __esm(() => {
2731
3072
  init_static();
2732
3073
  PlaycademyClient = class PlaycademyClient {
2733
3074
  baseUrl;
2734
- token;
3075
+ authStrategy;
2735
3076
  gameId;
2736
3077
  config;
2737
3078
  listeners = {};
@@ -2739,16 +3080,10 @@ var init_client = __esm(() => {
2739
3080
  authContext;
2740
3081
  initPayload;
2741
3082
  constructor(config) {
2742
- if (!config) {
2743
- this.baseUrl = "/api";
2744
- this.token = undefined;
2745
- this.gameId = undefined;
2746
- } else {
2747
- this.baseUrl = config.baseUrl || "/api";
2748
- this.token = config.token;
2749
- this.gameId = config.gameId;
2750
- }
3083
+ this.baseUrl = config?.baseUrl || "/api";
3084
+ this.gameId = config?.gameId;
2751
3085
  this.config = config || {};
3086
+ this.authStrategy = createAuthStrategy(config?.token ?? null, config?.tokenType);
2752
3087
  this._detectAuthContext();
2753
3088
  this._initializeInternalSession().catch(() => {});
2754
3089
  }
@@ -2760,15 +3095,18 @@ var init_client = __esm(() => {
2760
3095
  ping() {
2761
3096
  return "pong";
2762
3097
  }
2763
- setToken(token) {
2764
- this.token = token ?? undefined;
2765
- this.emit("authChange", { token: this.token ?? null });
3098
+ setToken(token, tokenType) {
3099
+ this.authStrategy = createAuthStrategy(token, tokenType);
3100
+ this.emit("authChange", { token });
3101
+ }
3102
+ getTokenType() {
3103
+ return this.authStrategy.getType();
2766
3104
  }
2767
3105
  getToken() {
2768
- return this.token ?? null;
3106
+ return this.authStrategy.getToken();
2769
3107
  }
2770
3108
  isAuthenticated() {
2771
- return !!this.token;
3109
+ return this.authStrategy.getToken() !== null;
2772
3110
  }
2773
3111
  onAuthChange(callback) {
2774
3112
  this.on("authChange", (payload) => callback(payload.token));
@@ -2786,13 +3124,15 @@ var init_client = __esm(() => {
2786
3124
  });
2787
3125
  }
2788
3126
  async request(path, method, body, headers) {
2789
- const effectiveHeaders = { ...headers };
3127
+ const effectiveHeaders = {
3128
+ ...headers,
3129
+ ...this.authStrategy.getHeaders()
3130
+ };
2790
3131
  return request({
2791
3132
  path,
2792
3133
  method,
2793
3134
  body,
2794
3135
  baseUrl: this.baseUrl,
2795
- token: this.token,
2796
3136
  extraHeaders: effectiveHeaders
2797
3137
  });
2798
3138
  }
@@ -2844,6 +3184,7 @@ var init_client = __esm(() => {
2844
3184
  sprites = createSpritesNamespace(this);
2845
3185
  realtime = createRealtimeNamespace(this);
2846
3186
  achievements = createAchievementsNamespace(this);
3187
+ notifications = createNotificationsNamespace(this);
2847
3188
  static init = init;
2848
3189
  static login = login2;
2849
3190
  static identity = identity;