@playcademy/vite-plugin 0.0.1-beta.8 → 0.0.1-beta.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 +445 -13
- package/dist/lib/sandbox.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -94242,8 +94242,8 @@ var DEMO_USER = {
|
|
|
94242
94242
|
email: "demo@playcademy.com",
|
|
94243
94243
|
emailVerified: true,
|
|
94244
94244
|
image: null,
|
|
94245
|
-
role: "
|
|
94246
|
-
developerStatus: "
|
|
94245
|
+
role: "developer",
|
|
94246
|
+
developerStatus: "approved",
|
|
94247
94247
|
createdAt: now,
|
|
94248
94248
|
updatedAt: now
|
|
94249
94249
|
};
|
|
@@ -113959,6 +113959,8 @@ __export(exports_schemas, {
|
|
|
113959
113959
|
verification: () => verification,
|
|
113960
113960
|
users: () => users,
|
|
113961
113961
|
userRoleEnum: () => userRoleEnum,
|
|
113962
|
+
userLevelsRelations: () => userLevelsRelations,
|
|
113963
|
+
userLevels: () => userLevels,
|
|
113962
113964
|
shopListingsRelations: () => shopListingsRelations,
|
|
113963
113965
|
shopListings: () => shopListings,
|
|
113964
113966
|
sessions: () => sessions,
|
|
@@ -113966,6 +113968,7 @@ __export(exports_schemas, {
|
|
|
113966
113968
|
maps: () => maps,
|
|
113967
113969
|
mapElementsRelations: () => mapElementsRelations,
|
|
113968
113970
|
mapElements: () => mapElements,
|
|
113971
|
+
levelConfigs: () => levelConfigs,
|
|
113969
113972
|
itemsRelations: () => itemsRelations,
|
|
113970
113973
|
items: () => items,
|
|
113971
113974
|
itemTypeEnum: () => itemTypeEnum,
|
|
@@ -113982,11 +113985,14 @@ __export(exports_schemas, {
|
|
|
113982
113985
|
currenciesRelations: () => currenciesRelations,
|
|
113983
113986
|
currencies: () => currencies,
|
|
113984
113987
|
accounts: () => accounts,
|
|
113988
|
+
XPActionInputSchema: () => XPActionInputSchema,
|
|
113985
113989
|
VersionSchema: () => VersionSchema,
|
|
113986
113990
|
UpsertGameMetadataSchema: () => UpsertGameMetadataSchema,
|
|
113987
113991
|
UpdateUserSchema: () => UpdateUserSchema,
|
|
113992
|
+
UpdateUserLevelSchema: () => UpdateUserLevelSchema,
|
|
113988
113993
|
UpdateShopListingSchema: () => UpdateShopListingSchema,
|
|
113989
113994
|
UpdateMapElementSchema: () => UpdateMapElementSchema,
|
|
113995
|
+
UpdateLevelConfigSchema: () => UpdateLevelConfigSchema,
|
|
113990
113996
|
UpdateItemSchema: () => UpdateItemSchema,
|
|
113991
113997
|
UpdateInventoryItemSchema: () => UpdateInventoryItemSchema,
|
|
113992
113998
|
UpdateGameStateSchema: () => UpdateGameStateSchema,
|
|
@@ -113997,10 +114003,12 @@ __export(exports_schemas, {
|
|
|
113997
114003
|
StartSessionInputSchema: () => StartSessionInputSchema,
|
|
113998
114004
|
SelectVerificationSchema: () => SelectVerificationSchema,
|
|
113999
114005
|
SelectUserSchema: () => SelectUserSchema,
|
|
114006
|
+
SelectUserLevelSchema: () => SelectUserLevelSchema,
|
|
114000
114007
|
SelectShopListingSchema: () => SelectShopListingSchema,
|
|
114001
114008
|
SelectSessionSchema: () => SelectSessionSchema,
|
|
114002
114009
|
SelectMapSchema: () => SelectMapSchema,
|
|
114003
114010
|
SelectMapElementSchema: () => SelectMapElementSchema,
|
|
114011
|
+
SelectLevelConfigSchema: () => SelectLevelConfigSchema,
|
|
114004
114012
|
SelectItemSchema: () => SelectItemSchema,
|
|
114005
114013
|
SelectInventoryItemSchema: () => SelectInventoryItemSchema,
|
|
114006
114014
|
SelectGameStateSchema: () => SelectGameStateSchema,
|
|
@@ -114012,13 +114020,16 @@ __export(exports_schemas, {
|
|
|
114012
114020
|
ProcessZipSchema: () => ProcessZipSchema,
|
|
114013
114021
|
MapElementMetadataZodSchema: () => MapElementMetadataZodSchema,
|
|
114014
114022
|
ManifestV1Schema: () => ManifestV1Schema,
|
|
114023
|
+
MAX_LEVEL: () => MAX_LEVEL,
|
|
114015
114024
|
ItemMetadataSchema: () => ItemMetadataSchema,
|
|
114016
114025
|
InventoryActionInputSchema: () => InventoryActionInputSchema,
|
|
114017
114026
|
InsertVerificationSchema: () => InsertVerificationSchema,
|
|
114018
114027
|
InsertUserSchema: () => InsertUserSchema,
|
|
114028
|
+
InsertUserLevelSchema: () => InsertUserLevelSchema,
|
|
114019
114029
|
InsertShopListingSchema: () => InsertShopListingSchema,
|
|
114020
114030
|
InsertSessionSchema: () => InsertSessionSchema,
|
|
114021
114031
|
InsertMapElementSchema: () => InsertMapElementSchema,
|
|
114032
|
+
InsertLevelConfigSchema: () => InsertLevelConfigSchema,
|
|
114022
114033
|
InsertItemSchema: () => InsertItemSchema,
|
|
114023
114034
|
InsertInventoryItemSchema: () => InsertInventoryItemSchema,
|
|
114024
114035
|
InsertGameStateSchema: () => InsertGameStateSchema,
|
|
@@ -118773,6 +118784,53 @@ var InsertShopListingSchema = createInsertSchema(shopListings, {
|
|
|
118773
118784
|
}).omit({ id: true, createdAt: true, updatedAt: true });
|
|
118774
118785
|
var SelectShopListingSchema = createSelectSchema(shopListings);
|
|
118775
118786
|
var UpdateShopListingSchema = InsertShopListingSchema.partial().extend({});
|
|
118787
|
+
var MAX_LEVEL = 100;
|
|
118788
|
+
var userLevels = pgTable("user_levels", {
|
|
118789
|
+
userId: text("user_id").primaryKey().references(() => users.id, { onDelete: "cascade" }),
|
|
118790
|
+
currentLevel: integer("current_level").notNull().default(1),
|
|
118791
|
+
currentXp: integer("current_xp").notNull().default(0),
|
|
118792
|
+
totalXpEarned: integer("total_xp_earned").notNull().default(0),
|
|
118793
|
+
lastLevelUpAt: timestamp("last_level_up_at", { withTimezone: true }),
|
|
118794
|
+
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
118795
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).defaultNow().$onUpdate(() => new Date)
|
|
118796
|
+
});
|
|
118797
|
+
var levelConfigs = pgTable("level_configs", {
|
|
118798
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
118799
|
+
level: integer("level").notNull().unique(),
|
|
118800
|
+
xpRequired: integer("xp_required").notNull(),
|
|
118801
|
+
creditsReward: integer("credits_reward").notNull().default(0),
|
|
118802
|
+
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
118803
|
+
}, (table6) => [uniqueIndex("unique_level_config_idx").on(table6.level)]);
|
|
118804
|
+
var userLevelsRelations = relations(userLevels, ({ one }) => ({
|
|
118805
|
+
user: one(users, {
|
|
118806
|
+
fields: [userLevels.userId],
|
|
118807
|
+
references: [users.id]
|
|
118808
|
+
})
|
|
118809
|
+
}));
|
|
118810
|
+
var InsertUserLevelSchema = createInsertSchema(userLevels, {
|
|
118811
|
+
userId: exports_external.string().min(1, "User ID is required"),
|
|
118812
|
+
currentLevel: exports_external.number().int().min(1, "Level must be at least 1").default(1),
|
|
118813
|
+
currentXp: exports_external.number().int().min(0, "XP cannot be negative").default(0),
|
|
118814
|
+
totalXpEarned: exports_external.number().int().min(0, "Total XP earned cannot be negative").default(0)
|
|
118815
|
+
}).omit({ createdAt: true, updatedAt: true });
|
|
118816
|
+
var SelectUserLevelSchema = createSelectSchema(userLevels);
|
|
118817
|
+
var UpdateUserLevelSchema = createUpdateSchema(userLevels).omit({
|
|
118818
|
+
userId: true,
|
|
118819
|
+
createdAt: true
|
|
118820
|
+
});
|
|
118821
|
+
var InsertLevelConfigSchema = createInsertSchema(levelConfigs, {
|
|
118822
|
+
level: exports_external.number().int().min(1, "Level must be at least 1"),
|
|
118823
|
+
xpRequired: exports_external.number().int().min(0, "XP required cannot be negative"),
|
|
118824
|
+
creditsReward: exports_external.number().int().min(0, "Credits reward cannot be negative").default(0)
|
|
118825
|
+
}).omit({ id: true, createdAt: true });
|
|
118826
|
+
var SelectLevelConfigSchema = createSelectSchema(levelConfigs);
|
|
118827
|
+
var UpdateLevelConfigSchema = createUpdateSchema(levelConfigs).omit({
|
|
118828
|
+
id: true,
|
|
118829
|
+
createdAt: true
|
|
118830
|
+
});
|
|
118831
|
+
var XPActionInputSchema = exports_external.object({
|
|
118832
|
+
amount: exports_external.number().int("XP amount must be an integer").positive("XP amount must be positive")
|
|
118833
|
+
});
|
|
118776
118834
|
|
|
118777
118835
|
class DatabasePathManager {
|
|
118778
118836
|
static DEFAULT_DB_SUBPATH = join2("@playcademy", "vite-plugin", "node_modules", ".playcademy", "sandbox.db");
|
|
@@ -118845,12 +118903,35 @@ async function seedDemoData(db) {
|
|
|
118845
118903
|
for (const inventory2 of SAMPLE_INVENTORY) {
|
|
118846
118904
|
await db.insert(inventoryItems).values(inventory2);
|
|
118847
118905
|
}
|
|
118906
|
+
const levelConfigsData = generateLevelConfigs();
|
|
118907
|
+
for (const config of levelConfigsData) {
|
|
118908
|
+
await db.insert(levelConfigs).values(config);
|
|
118909
|
+
}
|
|
118848
118910
|
} catch (error2) {
|
|
118849
118911
|
console.error("❌ Error seeding demo data:", error2);
|
|
118850
118912
|
throw error2;
|
|
118851
118913
|
}
|
|
118852
118914
|
return DEMO_USER;
|
|
118853
118915
|
}
|
|
118916
|
+
function generateLevelConfigs() {
|
|
118917
|
+
const configs = [];
|
|
118918
|
+
for (let level = 1;level <= 100; level++) {
|
|
118919
|
+
const xpRequired = level === 1 ? 0 : Math.floor(50 * Math.pow(level - 1, 1.7));
|
|
118920
|
+
let creditsReward = 0;
|
|
118921
|
+
if (level > 1) {
|
|
118922
|
+
creditsReward = 25 + level * 25;
|
|
118923
|
+
if (level % 10 === 1 && level > 1) {
|
|
118924
|
+
creditsReward += 75;
|
|
118925
|
+
}
|
|
118926
|
+
}
|
|
118927
|
+
configs.push({
|
|
118928
|
+
level,
|
|
118929
|
+
xpRequired,
|
|
118930
|
+
creditsReward
|
|
118931
|
+
});
|
|
118932
|
+
}
|
|
118933
|
+
return configs;
|
|
118934
|
+
}
|
|
118854
118935
|
async function seedCurrentProjectGame(db, project) {
|
|
118855
118936
|
const now2 = new Date;
|
|
118856
118937
|
try {
|
|
@@ -118984,6 +119065,267 @@ async function getUserMe(ctx) {
|
|
|
118984
119065
|
throw ApiError.internal("Internal server error", error2);
|
|
118985
119066
|
}
|
|
118986
119067
|
}
|
|
119068
|
+
var levelConfigCache = null;
|
|
119069
|
+
async function getLevelConfig(db, level) {
|
|
119070
|
+
if (level < 1) {
|
|
119071
|
+
throw ApiError.badRequest("Level must be at least 1");
|
|
119072
|
+
}
|
|
119073
|
+
if (levelConfigCache?.has(level)) {
|
|
119074
|
+
return levelConfigCache.get(level) || null;
|
|
119075
|
+
}
|
|
119076
|
+
try {
|
|
119077
|
+
const [config] = await db.select().from(levelConfigs).where(eq(levelConfigs.level, level)).limit(1);
|
|
119078
|
+
if (levelConfigCache && config) {
|
|
119079
|
+
levelConfigCache.set(level, config);
|
|
119080
|
+
}
|
|
119081
|
+
return config || null;
|
|
119082
|
+
} catch (error2) {
|
|
119083
|
+
logger2.error(`Error fetching level config for level ${level}:`, error2);
|
|
119084
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119085
|
+
}
|
|
119086
|
+
}
|
|
119087
|
+
async function calculateXPToNextLevel(db, currentLevel, currentXp) {
|
|
119088
|
+
try {
|
|
119089
|
+
const nextLevelConfig = await getLevelConfig(db, currentLevel + 1);
|
|
119090
|
+
if (!nextLevelConfig) {
|
|
119091
|
+
return 0;
|
|
119092
|
+
}
|
|
119093
|
+
return Math.max(0, nextLevelConfig.xpRequired - currentXp);
|
|
119094
|
+
} catch (error2) {
|
|
119095
|
+
logger2.error(`Error calculating XP to next level:`, error2);
|
|
119096
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119097
|
+
}
|
|
119098
|
+
}
|
|
119099
|
+
async function checkLevelUp(tx, currentLevel, newXp) {
|
|
119100
|
+
let level = currentLevel;
|
|
119101
|
+
let remainingXp = newXp;
|
|
119102
|
+
let totalCreditsAwarded = 0;
|
|
119103
|
+
let leveledUp = false;
|
|
119104
|
+
while (true) {
|
|
119105
|
+
if (level >= MAX_LEVEL) {
|
|
119106
|
+
break;
|
|
119107
|
+
}
|
|
119108
|
+
const nextLevelConfig2 = await tx.select().from(levelConfigs).where(eq(levelConfigs.level, level + 1)).limit(1);
|
|
119109
|
+
const [nextLevel2] = nextLevelConfig2;
|
|
119110
|
+
if (!nextLevel2) {
|
|
119111
|
+
break;
|
|
119112
|
+
}
|
|
119113
|
+
if (remainingXp >= nextLevel2.xpRequired) {
|
|
119114
|
+
level += 1;
|
|
119115
|
+
remainingXp -= nextLevel2.xpRequired;
|
|
119116
|
+
totalCreditsAwarded += nextLevel2.creditsReward;
|
|
119117
|
+
leveledUp = true;
|
|
119118
|
+
} else {
|
|
119119
|
+
break;
|
|
119120
|
+
}
|
|
119121
|
+
}
|
|
119122
|
+
const nextLevelConfig = await tx.select().from(levelConfigs).where(eq(levelConfigs.level, level + 1)).limit(1);
|
|
119123
|
+
const [nextLevel] = nextLevelConfig;
|
|
119124
|
+
const xpToNextLevel = nextLevel ? Math.max(0, nextLevel.xpRequired - remainingXp) : 0;
|
|
119125
|
+
return {
|
|
119126
|
+
newLevel: level,
|
|
119127
|
+
remainingXp,
|
|
119128
|
+
leveledUp,
|
|
119129
|
+
creditsAwarded: totalCreditsAwarded,
|
|
119130
|
+
xpToNextLevel
|
|
119131
|
+
};
|
|
119132
|
+
}
|
|
119133
|
+
function formatValidationErrors(error2) {
|
|
119134
|
+
const flattened = error2.flatten();
|
|
119135
|
+
const fieldErrors = Object.entries(flattened.fieldErrors).map(([field, errors2]) => `${field}: ${errors2?.join(", ") || "Invalid"}`).join("; ");
|
|
119136
|
+
const formErrors = flattened.formErrors.join("; ");
|
|
119137
|
+
const allErrors = [fieldErrors, formErrors].filter(Boolean).join("; ");
|
|
119138
|
+
return allErrors || "Validation failed";
|
|
119139
|
+
}
|
|
119140
|
+
var AddXPSchema = XPActionInputSchema;
|
|
119141
|
+
async function getUserLevel(ctx) {
|
|
119142
|
+
const user = ctx.user;
|
|
119143
|
+
if (!user) {
|
|
119144
|
+
throw ApiError.unauthorized("Valid session or bearer token required");
|
|
119145
|
+
}
|
|
119146
|
+
try {
|
|
119147
|
+
const db = getDatabase();
|
|
119148
|
+
let [userLevel] = await db.select().from(userLevels).where(eq(userLevels.userId, user.id)).limit(1);
|
|
119149
|
+
if (!userLevel) {
|
|
119150
|
+
const [newUserLevel] = await db.insert(userLevels).values({
|
|
119151
|
+
userId: user.id,
|
|
119152
|
+
currentLevel: 1,
|
|
119153
|
+
currentXp: 0,
|
|
119154
|
+
totalXpEarned: 0
|
|
119155
|
+
}).returning();
|
|
119156
|
+
if (!newUserLevel) {
|
|
119157
|
+
throw ApiError.internal("Failed to create user level record");
|
|
119158
|
+
}
|
|
119159
|
+
userLevel = newUserLevel;
|
|
119160
|
+
}
|
|
119161
|
+
return userLevel;
|
|
119162
|
+
} catch (error2) {
|
|
119163
|
+
if (error2 instanceof ApiError) {
|
|
119164
|
+
throw error2;
|
|
119165
|
+
}
|
|
119166
|
+
logger2.error(`Error fetching user level for user ${user.id}:`, error2);
|
|
119167
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119168
|
+
}
|
|
119169
|
+
}
|
|
119170
|
+
async function addXP(ctx, amount) {
|
|
119171
|
+
const user = ctx.user;
|
|
119172
|
+
if (!user) {
|
|
119173
|
+
throw ApiError.unauthorized("Valid session or bearer token required");
|
|
119174
|
+
}
|
|
119175
|
+
if (user.role !== "developer") {
|
|
119176
|
+
throw ApiError.forbidden("Only developers can award XP to users");
|
|
119177
|
+
}
|
|
119178
|
+
if (amount <= 0) {
|
|
119179
|
+
throw ApiError.badRequest("XP amount must be positive");
|
|
119180
|
+
}
|
|
119181
|
+
try {
|
|
119182
|
+
const db = getDatabase();
|
|
119183
|
+
const result = await db.transaction(async (tx) => {
|
|
119184
|
+
let [userLevel] = await tx.select().from(userLevels).where(eq(userLevels.userId, user.id)).limit(1);
|
|
119185
|
+
if (!userLevel) {
|
|
119186
|
+
const [newUserLevel] = await tx.insert(userLevels).values({
|
|
119187
|
+
userId: user.id,
|
|
119188
|
+
currentLevel: 1,
|
|
119189
|
+
currentXp: 0,
|
|
119190
|
+
totalXpEarned: 0
|
|
119191
|
+
}).returning();
|
|
119192
|
+
if (!newUserLevel) {
|
|
119193
|
+
throw ApiError.internal("Failed to create user level record");
|
|
119194
|
+
}
|
|
119195
|
+
userLevel = newUserLevel;
|
|
119196
|
+
}
|
|
119197
|
+
const newCurrentXp = userLevel.currentXp + amount;
|
|
119198
|
+
const newTotalXpEarned = userLevel.totalXpEarned + amount;
|
|
119199
|
+
const levelUpResult = await checkLevelUp(tx, userLevel.currentLevel, newCurrentXp);
|
|
119200
|
+
const [updatedUserLevel] = await tx.update(userLevels).set({
|
|
119201
|
+
currentLevel: levelUpResult.newLevel,
|
|
119202
|
+
currentXp: levelUpResult.remainingXp,
|
|
119203
|
+
totalXpEarned: newTotalXpEarned,
|
|
119204
|
+
lastLevelUpAt: levelUpResult.leveledUp ? new Date : userLevel.lastLevelUpAt
|
|
119205
|
+
}).where(eq(userLevels.userId, user.id)).returning();
|
|
119206
|
+
if (!updatedUserLevel) {
|
|
119207
|
+
throw ApiError.internal("Failed to update user level");
|
|
119208
|
+
}
|
|
119209
|
+
let creditsAwarded = 0;
|
|
119210
|
+
if (levelUpResult.leveledUp) {
|
|
119211
|
+
const creditsToAward = levelUpResult.creditsAwarded;
|
|
119212
|
+
logger2.debug("User leveled up", {
|
|
119213
|
+
userId: user.id,
|
|
119214
|
+
oldLevel: userLevel.currentLevel,
|
|
119215
|
+
newLevel: levelUpResult.newLevel,
|
|
119216
|
+
xpAdded: amount,
|
|
119217
|
+
totalXpEarned: newTotalXpEarned,
|
|
119218
|
+
creditsAwarded: creditsToAward
|
|
119219
|
+
});
|
|
119220
|
+
if (creditsToAward > 0) {
|
|
119221
|
+
const [creditsItem] = await tx.select({ id: items.id }).from(items).where(eq(items.internalName, "PLAYCADEMY_CREDITS")).limit(1);
|
|
119222
|
+
if (!creditsItem) {
|
|
119223
|
+
throw ApiError.internal("PLAYCADEMY_CREDITS item not found");
|
|
119224
|
+
}
|
|
119225
|
+
await tx.insert(inventoryItems).values({
|
|
119226
|
+
userId: user.id,
|
|
119227
|
+
itemId: creditsItem.id,
|
|
119228
|
+
quantity: creditsToAward
|
|
119229
|
+
}).onConflictDoUpdate({
|
|
119230
|
+
target: [
|
|
119231
|
+
inventoryItems.userId,
|
|
119232
|
+
inventoryItems.itemId
|
|
119233
|
+
],
|
|
119234
|
+
set: {
|
|
119235
|
+
quantity: sql`${inventoryItems.quantity} + ${creditsToAward}`
|
|
119236
|
+
}
|
|
119237
|
+
});
|
|
119238
|
+
}
|
|
119239
|
+
creditsAwarded = creditsToAward;
|
|
119240
|
+
} else {
|
|
119241
|
+
logger2.debug("XP added to user", {
|
|
119242
|
+
userId: user.id,
|
|
119243
|
+
level: userLevel.currentLevel,
|
|
119244
|
+
xpAdded: amount,
|
|
119245
|
+
totalXpEarned: newTotalXpEarned
|
|
119246
|
+
});
|
|
119247
|
+
}
|
|
119248
|
+
return {
|
|
119249
|
+
totalXpEarned: newTotalXpEarned,
|
|
119250
|
+
newLevel: levelUpResult.newLevel,
|
|
119251
|
+
leveledUp: levelUpResult.leveledUp,
|
|
119252
|
+
creditsAwarded,
|
|
119253
|
+
xpToNextLevel: levelUpResult.xpToNextLevel
|
|
119254
|
+
};
|
|
119255
|
+
});
|
|
119256
|
+
return result;
|
|
119257
|
+
} catch (error2) {
|
|
119258
|
+
if (error2 instanceof ApiError) {
|
|
119259
|
+
throw error2;
|
|
119260
|
+
}
|
|
119261
|
+
logger2.error(`Error adding XP for user ${user.id}:`, error2);
|
|
119262
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119263
|
+
}
|
|
119264
|
+
}
|
|
119265
|
+
async function addXPFromRequest(ctx) {
|
|
119266
|
+
let amount;
|
|
119267
|
+
try {
|
|
119268
|
+
const requestBody = await ctx.request.json();
|
|
119269
|
+
const parsed = AddXPSchema.parse(requestBody);
|
|
119270
|
+
amount = parsed.amount;
|
|
119271
|
+
} catch (error2) {
|
|
119272
|
+
if (error2 instanceof ZodError) {
|
|
119273
|
+
throw ApiError.badRequest(`Validation failed: ${formatValidationErrors(error2)}`);
|
|
119274
|
+
}
|
|
119275
|
+
logger2.error("Failed to parse request body:", error2);
|
|
119276
|
+
throw ApiError.badRequest("Invalid JSON body");
|
|
119277
|
+
}
|
|
119278
|
+
return await addXP(ctx, amount);
|
|
119279
|
+
}
|
|
119280
|
+
async function getAllLevelConfigs(_ctx) {
|
|
119281
|
+
try {
|
|
119282
|
+
const db = getDatabase();
|
|
119283
|
+
const configs = await db.select().from(levelConfigs).orderBy(levelConfigs.level);
|
|
119284
|
+
return configs;
|
|
119285
|
+
} catch (error2) {
|
|
119286
|
+
logger2.error("Error fetching all level configs:", error2);
|
|
119287
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119288
|
+
}
|
|
119289
|
+
}
|
|
119290
|
+
async function getUserLevelProgress(ctx) {
|
|
119291
|
+
const userLevel = await getUserLevel(ctx);
|
|
119292
|
+
try {
|
|
119293
|
+
const db = getDatabase();
|
|
119294
|
+
const xpToNextLevel = await calculateXPToNextLevel(db, userLevel.currentLevel, userLevel.currentXp);
|
|
119295
|
+
return {
|
|
119296
|
+
level: userLevel.currentLevel,
|
|
119297
|
+
currentXp: userLevel.currentXp,
|
|
119298
|
+
xpToNextLevel,
|
|
119299
|
+
totalXpEarned: userLevel.totalXpEarned
|
|
119300
|
+
};
|
|
119301
|
+
} catch (error2) {
|
|
119302
|
+
if (error2 instanceof ApiError) {
|
|
119303
|
+
throw error2;
|
|
119304
|
+
}
|
|
119305
|
+
logger2.error(`Error fetching user level progress:`, error2);
|
|
119306
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119307
|
+
}
|
|
119308
|
+
}
|
|
119309
|
+
async function getLevelConfigByPath(ctx) {
|
|
119310
|
+
const levelParam = ctx.params.level;
|
|
119311
|
+
if (!levelParam) {
|
|
119312
|
+
throw ApiError.badRequest("Level parameter is required");
|
|
119313
|
+
}
|
|
119314
|
+
const level = parseInt(levelParam, 10);
|
|
119315
|
+
if (isNaN(level) || level < 1) {
|
|
119316
|
+
throw ApiError.badRequest("Level must be a positive integer");
|
|
119317
|
+
}
|
|
119318
|
+
try {
|
|
119319
|
+
const db = getDatabase();
|
|
119320
|
+
return await getLevelConfig(db, level);
|
|
119321
|
+
} catch (error2) {
|
|
119322
|
+
if (error2 instanceof ApiError) {
|
|
119323
|
+
throw error2;
|
|
119324
|
+
}
|
|
119325
|
+
logger2.error(`Error fetching level config for level ${level}:`, error2);
|
|
119326
|
+
throw ApiError.internal("Internal server error", error2);
|
|
119327
|
+
}
|
|
119328
|
+
}
|
|
118987
119329
|
var usersRouter = new Hono2;
|
|
118988
119330
|
usersRouter.get("/me", async (c2) => {
|
|
118989
119331
|
const ctx = {
|
|
@@ -119004,15 +119346,65 @@ usersRouter.get("/me", async (c2) => {
|
|
|
119004
119346
|
return c2.json({ error: message }, 500);
|
|
119005
119347
|
}
|
|
119006
119348
|
});
|
|
119349
|
+
usersRouter.get("/level", async (c2) => {
|
|
119350
|
+
const ctx = {
|
|
119351
|
+
user: c2.get("user"),
|
|
119352
|
+
params: {},
|
|
119353
|
+
url: new URL(c2.req.url),
|
|
119354
|
+
request: c2.req.raw
|
|
119355
|
+
};
|
|
119356
|
+
try {
|
|
119357
|
+
const levelData = await getUserLevel(ctx);
|
|
119358
|
+
return c2.json(levelData);
|
|
119359
|
+
} catch (error2) {
|
|
119360
|
+
if (error2 instanceof ApiError) {
|
|
119361
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
119362
|
+
}
|
|
119363
|
+
console.error("Error in getUserLevel:", error2);
|
|
119364
|
+
const message = error2 instanceof Error ? error2.message : "Internal server error";
|
|
119365
|
+
return c2.json({ error: message }, 500);
|
|
119366
|
+
}
|
|
119367
|
+
});
|
|
119368
|
+
usersRouter.get("/level/progress", async (c2) => {
|
|
119369
|
+
const ctx = {
|
|
119370
|
+
user: c2.get("user"),
|
|
119371
|
+
params: {},
|
|
119372
|
+
url: new URL(c2.req.url),
|
|
119373
|
+
request: c2.req.raw
|
|
119374
|
+
};
|
|
119375
|
+
try {
|
|
119376
|
+
const progressData = await getUserLevelProgress(ctx);
|
|
119377
|
+
return c2.json(progressData);
|
|
119378
|
+
} catch (error2) {
|
|
119379
|
+
if (error2 instanceof ApiError) {
|
|
119380
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
119381
|
+
}
|
|
119382
|
+
console.error("Error in getUserLevelProgress:", error2);
|
|
119383
|
+
const message = error2 instanceof Error ? error2.message : "Internal server error";
|
|
119384
|
+
return c2.json({ error: message }, 500);
|
|
119385
|
+
}
|
|
119386
|
+
});
|
|
119387
|
+
usersRouter.post("/xp/add", async (c2) => {
|
|
119388
|
+
const ctx = {
|
|
119389
|
+
user: c2.get("user"),
|
|
119390
|
+
params: {},
|
|
119391
|
+
url: new URL(c2.req.url),
|
|
119392
|
+
request: c2.req.raw
|
|
119393
|
+
};
|
|
119394
|
+
try {
|
|
119395
|
+
const result = await addXPFromRequest(ctx);
|
|
119396
|
+
return c2.json(result);
|
|
119397
|
+
} catch (error2) {
|
|
119398
|
+
if (error2 instanceof ApiError) {
|
|
119399
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
119400
|
+
}
|
|
119401
|
+
console.error("Error in addXPFromRequest:", error2);
|
|
119402
|
+
const message = error2 instanceof Error ? error2.message : "Internal server error";
|
|
119403
|
+
return c2.json({ error: message }, 500);
|
|
119404
|
+
}
|
|
119405
|
+
});
|
|
119007
119406
|
var healthRouter = new Hono2;
|
|
119008
119407
|
healthRouter.get("/", (c2) => c2.json({ status: "ok", timestamp: new Date().toISOString() }));
|
|
119009
|
-
function formatValidationErrors(error2) {
|
|
119010
|
-
const flattened = error2.flatten();
|
|
119011
|
-
const fieldErrors = Object.entries(flattened.fieldErrors).map(([field, errors2]) => `${field}: ${errors2?.join(", ") || "Invalid"}`).join("; ");
|
|
119012
|
-
const formErrors = flattened.formErrors.join("; ");
|
|
119013
|
-
const allErrors = [fieldErrors, formErrors].filter(Boolean).join("; ");
|
|
119014
|
-
return allErrors || "Validation failed";
|
|
119015
|
-
}
|
|
119016
119408
|
async function getUserInventory(ctx) {
|
|
119017
119409
|
const user = ctx.user;
|
|
119018
119410
|
if (!user) {
|
|
@@ -123133,6 +123525,45 @@ devRouter.delete("/keys/:keyId", async (c2) => {
|
|
|
123133
123525
|
return c2.json({ error: message2 }, 500);
|
|
123134
123526
|
}
|
|
123135
123527
|
});
|
|
123528
|
+
var levelsRouter = new Hono2;
|
|
123529
|
+
levelsRouter.get("/config", async (c2) => {
|
|
123530
|
+
const ctx = {
|
|
123531
|
+
user: c2.get("user"),
|
|
123532
|
+
params: {},
|
|
123533
|
+
url: new URL(c2.req.url),
|
|
123534
|
+
request: c2.req.raw
|
|
123535
|
+
};
|
|
123536
|
+
try {
|
|
123537
|
+
const configs = await getAllLevelConfigs(ctx);
|
|
123538
|
+
return c2.json(configs);
|
|
123539
|
+
} catch (error2) {
|
|
123540
|
+
if (error2 instanceof ApiError) {
|
|
123541
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
123542
|
+
}
|
|
123543
|
+
console.error("Error in getAllLevelConfigs:", error2);
|
|
123544
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
123545
|
+
return c2.json({ error: message2 }, 500);
|
|
123546
|
+
}
|
|
123547
|
+
});
|
|
123548
|
+
levelsRouter.get("/config/:level", async (c2) => {
|
|
123549
|
+
const ctx = {
|
|
123550
|
+
user: c2.get("user"),
|
|
123551
|
+
params: { level: c2.req.param("level") },
|
|
123552
|
+
url: new URL(c2.req.url),
|
|
123553
|
+
request: c2.req.raw
|
|
123554
|
+
};
|
|
123555
|
+
try {
|
|
123556
|
+
const config = await getLevelConfigByPath(ctx);
|
|
123557
|
+
return c2.json(config);
|
|
123558
|
+
} catch (error2) {
|
|
123559
|
+
if (error2 instanceof ApiError) {
|
|
123560
|
+
return c2.json({ error: error2.message }, error2.statusCode);
|
|
123561
|
+
}
|
|
123562
|
+
console.error("Error in getLevelConfigByPath:", error2);
|
|
123563
|
+
const message2 = error2 instanceof Error ? error2.message : "Internal server error";
|
|
123564
|
+
return c2.json({ error: message2 }, 500);
|
|
123565
|
+
}
|
|
123566
|
+
});
|
|
123136
123567
|
async function startServer(options) {
|
|
123137
123568
|
const { port, verbose, project } = options;
|
|
123138
123569
|
configureLogger(verbose ? "debug" : "error");
|
|
@@ -123158,6 +123589,7 @@ async function startServer(options) {
|
|
|
123158
123589
|
app.route("/api/maps", mapsRouter);
|
|
123159
123590
|
app.route("/api/shop-listings", shopListingsRouter);
|
|
123160
123591
|
app.route("/api/dev", devRouter);
|
|
123592
|
+
app.route("/api/levels", levelsRouter);
|
|
123161
123593
|
return serve({ fetch: app.fetch, port });
|
|
123162
123594
|
}
|
|
123163
123595
|
var version3 = package_default.version;
|
|
@@ -123225,10 +123657,10 @@ function printSandboxInfo(viteConfig, availablePort, projectInfo) {
|
|
|
123225
123657
|
viteConfig.logger.info(` ${import_picocolors.default.green("➜")} ${import_picocolors.default.bold("Sandbox:")} ${import_picocolors.default.cyan(`http://localhost:${import_picocolors.default.bold(availablePort.toString())}/api`)}`);
|
|
123226
123658
|
viteConfig.logger.info("");
|
|
123227
123659
|
}
|
|
123228
|
-
async function startSandbox(viteConfig, autoStart = true, verbose = false) {
|
|
123660
|
+
async function startSandbox(viteConfig, autoStart = true, verbose = false, customUrl) {
|
|
123229
123661
|
if (!autoStart || viteConfig.command !== "serve") {
|
|
123230
123662
|
return {
|
|
123231
|
-
url: "http://localhost:4321/api",
|
|
123663
|
+
url: customUrl ?? "http://localhost:4321/api",
|
|
123232
123664
|
project: null,
|
|
123233
123665
|
cleanup: () => {}
|
|
123234
123666
|
};
|
|
@@ -123609,7 +124041,7 @@ function playcademy(options = {}) {
|
|
|
123609
124041
|
platform: exportOptions.platform ?? "web",
|
|
123610
124042
|
autoZip: exportOptions.autoZip ?? false,
|
|
123611
124043
|
sandboxUrl: sandboxOptions.url ?? "http://localhost:4321/api",
|
|
123612
|
-
startSandbox: sandboxOptions.autoStart ?? true,
|
|
124044
|
+
startSandbox: sandboxOptions.url ? false : sandboxOptions.autoStart ?? true,
|
|
123613
124045
|
verbose: sandboxOptions.verbose ?? false
|
|
123614
124046
|
};
|
|
123615
124047
|
return {
|
|
@@ -123622,7 +124054,7 @@ function playcademy(options = {}) {
|
|
|
123622
124054
|
currentBuildOutputs = {};
|
|
123623
124055
|
},
|
|
123624
124056
|
async configureServer(server) {
|
|
123625
|
-
const sandbox = await startSandbox(viteConfig, _options.startSandbox, _options.verbose);
|
|
124057
|
+
const sandbox = await startSandbox(viteConfig, _options.startSandbox, _options.verbose, _options.sandboxUrl);
|
|
123626
124058
|
sandboxCleanup = sandbox.cleanup;
|
|
123627
124059
|
if (sandbox.project) {
|
|
123628
124060
|
devServerMiddleware(server, sandbox.url, sandbox.project);
|
package/dist/lib/sandbox.d.ts
CHANGED
|
@@ -5,5 +5,5 @@ interface SandboxManager {
|
|
|
5
5
|
project: ProjectInfo | null;
|
|
6
6
|
cleanup: () => void;
|
|
7
7
|
}
|
|
8
|
-
export declare function startSandbox(viteConfig: ResolvedConfig, autoStart?: boolean, verbose?: boolean): Promise<SandboxManager>;
|
|
8
|
+
export declare function startSandbox(viteConfig: ResolvedConfig, autoStart?: boolean, verbose?: boolean, customUrl?: string): Promise<SandboxManager>;
|
|
9
9
|
export {};
|