@playcademy/sandbox 0.8.1-beta.7 → 0.9.1-beta.1
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/cli.js +92 -148
- package/dist/server.js +92 -148
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1128,7 +1128,7 @@ var package_default;
|
|
|
1128
1128
|
var init_package = __esm(() => {
|
|
1129
1129
|
package_default = {
|
|
1130
1130
|
name: "@playcademy/sandbox",
|
|
1131
|
-
version: "0.
|
|
1131
|
+
version: "0.9.1-beta.1",
|
|
1132
1132
|
description: "Local development server for Playcademy game development",
|
|
1133
1133
|
type: "module",
|
|
1134
1134
|
exports: {
|
|
@@ -100429,6 +100429,20 @@ function assessmentAttemptId(input) {
|
|
|
100429
100429
|
String(input.attemptNumber)
|
|
100430
100430
|
].join("\x00"));
|
|
100431
100431
|
}
|
|
100432
|
+
function buildAssessmentAttemptResultWhere(scope) {
|
|
100433
|
+
return {
|
|
100434
|
+
"student.sourcedId": scope.studentId,
|
|
100435
|
+
status: ONEROSTER_STATUS2.active,
|
|
100436
|
+
[`metadata.${PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY}.activityId`]: scope.activityId
|
|
100437
|
+
};
|
|
100438
|
+
}
|
|
100439
|
+
function buildAssessmentChildResultWhere(scope) {
|
|
100440
|
+
return {
|
|
100441
|
+
"student.sourcedId": scope.studentId,
|
|
100442
|
+
status: ONEROSTER_STATUS2.active,
|
|
100443
|
+
[`metadata.${PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY}.payload.attempt.activityId`]: scope.activityId
|
|
100444
|
+
};
|
|
100445
|
+
}
|
|
100432
100446
|
async function findLatestAssessmentResult(results, match) {
|
|
100433
100447
|
let latest = null;
|
|
100434
100448
|
let latestTimestamp = 0;
|
|
@@ -100868,6 +100882,7 @@ var PLAYABLE_SHAPE_SET;
|
|
|
100868
100882
|
var init_timeback_assessment_runtime_util = __esm(() => {
|
|
100869
100883
|
init_src();
|
|
100870
100884
|
init_assessment_runtime2();
|
|
100885
|
+
init_constants3();
|
|
100871
100886
|
init_qti();
|
|
100872
100887
|
init_types2();
|
|
100873
100888
|
init_utils6();
|
|
@@ -103728,7 +103743,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103728
103743
|
const startedAt = Date.now();
|
|
103729
103744
|
const [candidates, results] = await Promise.all([
|
|
103730
103745
|
this.candidateIntegrations(params.gameId, params.studentId, params.input),
|
|
103731
|
-
this.
|
|
103746
|
+
this.activityAssessmentResults({
|
|
103747
|
+
studentId: params.studentId,
|
|
103748
|
+
activityId: params.input.activityId,
|
|
103749
|
+
purpose: params.input.purpose
|
|
103750
|
+
})
|
|
103732
103751
|
]);
|
|
103733
103752
|
const context2 = await this.selectIntegration(candidates, params.input);
|
|
103734
103753
|
const attemptScope = {
|
|
@@ -104791,12 +104810,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104791
104810
|
standard: assessmentStandardForRow(row)
|
|
104792
104811
|
})).filter((test) => !matchesMasteryStandard || matchesMasteryStandard(test.standard));
|
|
104793
104812
|
}
|
|
104794
|
-
async
|
|
104813
|
+
async activityAssessmentResults(scope) {
|
|
104814
|
+
const [parents, children] = await Promise.all([
|
|
104815
|
+
this.collectAssessmentResults(buildAssessmentAttemptResultWhere(scope)),
|
|
104816
|
+
scope.purpose === "review" ? this.activityChildResults(scope) : []
|
|
104817
|
+
]);
|
|
104818
|
+
return [...parents, ...children];
|
|
104819
|
+
}
|
|
104820
|
+
activityChildResults(scope) {
|
|
104821
|
+
return this.collectAssessmentResults(buildAssessmentChildResultWhere(scope));
|
|
104822
|
+
}
|
|
104823
|
+
async collectAssessmentResults(where) {
|
|
104795
104824
|
const stream = this.requireClient().api.oneroster.assessmentResults.stream({
|
|
104796
|
-
where
|
|
104797
|
-
"student.sourcedId": studentId,
|
|
104798
|
-
status: ONEROSTER_STATUS2.active
|
|
104799
|
-
}
|
|
104825
|
+
where
|
|
104800
104826
|
});
|
|
104801
104827
|
const results = [];
|
|
104802
104828
|
for await (const result of stream) {
|
|
@@ -104805,7 +104831,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104805
104831
|
return results;
|
|
104806
104832
|
}
|
|
104807
104833
|
async listAttempts(scope) {
|
|
104808
|
-
return this.attemptListing(await this.
|
|
104834
|
+
return this.attemptListing(await this.activityAssessmentResults(scope), scope);
|
|
104809
104835
|
}
|
|
104810
104836
|
async attemptListing(results, scope) {
|
|
104811
104837
|
const attempts = new Map;
|
|
@@ -105570,7 +105596,10 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105570
105596
|
const graded = await resumeAssessmentTranscript({
|
|
105571
105597
|
suppliedTranscript,
|
|
105572
105598
|
checkpointTranscript: metadata2.transcript,
|
|
105573
|
-
recover: async () => recoverAssessmentTranscript(context2, await this.
|
|
105599
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.activityChildResults({
|
|
105600
|
+
studentId: context2.studentId,
|
|
105601
|
+
activityId: context2.metadata.activityId
|
|
105602
|
+
})),
|
|
105574
105603
|
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
105575
105604
|
});
|
|
105576
105605
|
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
@@ -110720,10 +110749,10 @@ function createSandboxAuthProvider() {
|
|
|
110720
110749
|
},
|
|
110721
110750
|
async mintGameToken(gameId, userId) {
|
|
110722
110751
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
110723
|
-
const exp = Date.now() + 15 * 60
|
|
110752
|
+
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
110724
110753
|
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
110725
110754
|
const token = `${header}.${payload}.sandbox`;
|
|
110726
|
-
return { token, exp };
|
|
110755
|
+
return { token, exp: exp * 1000 };
|
|
110727
110756
|
},
|
|
110728
110757
|
async validateGameToken(token) {
|
|
110729
110758
|
try {
|
|
@@ -110731,18 +110760,9 @@ function createSandboxAuthProvider() {
|
|
|
110731
110760
|
if (parts2.length !== 3) {
|
|
110732
110761
|
return null;
|
|
110733
110762
|
}
|
|
110734
|
-
if (parts2[2] === "sandbox") {
|
|
110735
|
-
const payload2 = JSON.parse(atob(parts2[1]));
|
|
110736
|
-
if (payload2.sub && payload2.uid) {
|
|
110737
|
-
if (payload2.exp && payload2.exp < Date.now()) {
|
|
110738
|
-
log.debug("[SandboxAuthProvider] Token expired");
|
|
110739
|
-
return null;
|
|
110740
|
-
}
|
|
110741
|
-
return payload2;
|
|
110742
|
-
}
|
|
110743
|
-
}
|
|
110744
110763
|
const payload = JSON.parse(atob(parts2[1]));
|
|
110745
|
-
|
|
110764
|
+
const nowSeconds = Date.now() / 1000;
|
|
110765
|
+
if (typeof payload?.sub === "string" && payload.sub.length > 0 && typeof payload.uid === "string" && payload.uid.length > 0 && typeof payload.exp === "number" && Number.isFinite(payload.exp) && payload.exp > nowSeconds && payload.exp < nowSeconds + MAX_TOKEN_LIFETIME_SECONDS) {
|
|
110746
110766
|
return payload;
|
|
110747
110767
|
}
|
|
110748
110768
|
return null;
|
|
@@ -110840,7 +110860,7 @@ function createSandboxAuthProvider() {
|
|
|
110840
110860
|
}
|
|
110841
110861
|
};
|
|
110842
110862
|
}
|
|
110843
|
-
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, sandboxAssessmentTokens;
|
|
110863
|
+
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, MAX_TOKEN_LIFETIME_SECONDS, sandboxAssessmentTokens;
|
|
110844
110864
|
var init_auth_provider = __esm(async () => {
|
|
110845
110865
|
init_src6();
|
|
110846
110866
|
init_constants();
|
|
@@ -110848,6 +110868,7 @@ var init_auth_provider = __esm(async () => {
|
|
|
110848
110868
|
sandboxApiKeys = new Map;
|
|
110849
110869
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
110850
110870
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 5 * 60 * 1000;
|
|
110871
|
+
MAX_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
|
|
110851
110872
|
sandboxAssessmentTokens = new TimebackCache({
|
|
110852
110873
|
defaultTTL: 2 * 60 * 60 * 1000,
|
|
110853
110874
|
maxSize: 1000
|
|
@@ -112875,56 +112896,13 @@ function extractBearerToken(authHeader) {
|
|
|
112875
112896
|
}
|
|
112876
112897
|
return authHeader.substring(7);
|
|
112877
112898
|
}
|
|
112878
|
-
function
|
|
112879
|
-
try {
|
|
112880
|
-
const parts2 = token.split(".");
|
|
112881
|
-
if (parts2.length !== 3 || parts2[2] !== "sandbox") {
|
|
112882
|
-
return null;
|
|
112883
|
-
}
|
|
112884
|
-
const header = JSON.parse(atob(parts2[0]));
|
|
112885
|
-
if (header.typ !== "sandbox") {
|
|
112886
|
-
return null;
|
|
112887
|
-
}
|
|
112888
|
-
const payload = JSON.parse(atob(parts2[1]));
|
|
112889
|
-
if (!payload.uid || !payload.sub) {
|
|
112890
|
-
return null;
|
|
112891
|
-
}
|
|
112892
|
-
return {
|
|
112893
|
-
userId: payload.uid,
|
|
112894
|
-
gameSlug: payload.sub
|
|
112895
|
-
};
|
|
112896
|
-
} catch {
|
|
112897
|
-
return null;
|
|
112898
|
-
}
|
|
112899
|
-
}
|
|
112900
|
-
function parseJwtClaims(token) {
|
|
112901
|
-
try {
|
|
112902
|
-
const parts2 = token.split(".");
|
|
112903
|
-
if (parts2.length === 3 && parts2[1]) {
|
|
112904
|
-
const payload = JSON.parse(atob(parts2[1]));
|
|
112905
|
-
if (payload.uid) {
|
|
112906
|
-
return {
|
|
112907
|
-
userId: payload.uid,
|
|
112908
|
-
gameId: payload.sub
|
|
112909
|
-
};
|
|
112910
|
-
}
|
|
112911
|
-
}
|
|
112912
|
-
} catch {}
|
|
112913
|
-
return null;
|
|
112914
|
-
}
|
|
112915
|
-
function resolveAuth(token) {
|
|
112899
|
+
async function resolveAuth(token) {
|
|
112916
112900
|
const demoUser = DEMO_TOKENS[token];
|
|
112917
112901
|
if (demoUser) {
|
|
112918
112902
|
return { userId: demoUser.id };
|
|
112919
112903
|
}
|
|
112920
|
-
|
|
112921
|
-
|
|
112922
|
-
if (sandboxClaims) {
|
|
112923
|
-
return sandboxClaims;
|
|
112924
|
-
}
|
|
112925
|
-
return parseJwtClaims(token);
|
|
112926
|
-
}
|
|
112927
|
-
return null;
|
|
112904
|
+
const payload = await sandboxAuth.validateGameToken(token);
|
|
112905
|
+
return payload ? { userId: payload.uid, gameId: payload.sub } : null;
|
|
112928
112906
|
}
|
|
112929
112907
|
async function fetchUserFromDatabase(db2, userId) {
|
|
112930
112908
|
try {
|
|
@@ -112937,18 +112915,6 @@ async function fetchUserFromDatabase(db2, userId) {
|
|
|
112937
112915
|
throw error88;
|
|
112938
112916
|
}
|
|
112939
112917
|
}
|
|
112940
|
-
async function resolveGameIdFromSlug(db2, slug2) {
|
|
112941
|
-
try {
|
|
112942
|
-
const game2 = await db2.query.games.findFirst({
|
|
112943
|
-
where: eq(games.slug, slug2),
|
|
112944
|
-
columns: { id: true }
|
|
112945
|
-
});
|
|
112946
|
-
return game2?.id || null;
|
|
112947
|
-
} catch (error88) {
|
|
112948
|
-
console.error("[Auth] Error looking up game by slug:", error88);
|
|
112949
|
-
return null;
|
|
112950
|
-
}
|
|
112951
|
-
}
|
|
112952
112918
|
function isPublicRoute(path, exceptions) {
|
|
112953
112919
|
return exceptions.some((exception) => {
|
|
112954
112920
|
if (path === exception) {
|
|
@@ -112966,35 +112932,23 @@ async function authenticateRequest(c) {
|
|
|
112966
112932
|
const apiKey = c.req.header("x-api-key");
|
|
112967
112933
|
const token = bearerToken || apiKey;
|
|
112968
112934
|
if (!token) {
|
|
112969
|
-
return {
|
|
112970
|
-
success: false,
|
|
112971
|
-
error: "No authorization token provided",
|
|
112972
|
-
shouldReturn404: true
|
|
112973
|
-
};
|
|
112935
|
+
return { success: false, error: "No authorization token provided" };
|
|
112974
112936
|
}
|
|
112975
|
-
const resolved = resolveAuth(token);
|
|
112937
|
+
const resolved = await resolveAuth(token);
|
|
112976
112938
|
if (!resolved) {
|
|
112977
|
-
return {
|
|
112978
|
-
success: false,
|
|
112979
|
-
error: "Invalid or expired token",
|
|
112980
|
-
shouldReturn404: true
|
|
112981
|
-
};
|
|
112939
|
+
return { success: false, error: "Invalid or expired token" };
|
|
112982
112940
|
}
|
|
112983
112941
|
const claims = resolved;
|
|
112984
112942
|
const db2 = c.get("db");
|
|
112985
112943
|
if (!db2) {
|
|
112986
112944
|
console.error("[Auth] Database not available in context");
|
|
112987
|
-
return { success: false, error: "Internal server error"
|
|
112945
|
+
return { success: false, error: "Internal server error" };
|
|
112988
112946
|
}
|
|
112989
112947
|
const user = await fetchUserFromDatabase(db2, claims.userId);
|
|
112990
112948
|
if (!user) {
|
|
112991
|
-
return { success: false, error: "User not found or token invalid"
|
|
112949
|
+
return { success: false, error: "User not found or token invalid" };
|
|
112992
112950
|
}
|
|
112993
|
-
|
|
112994
|
-
if (!gameId && claims.gameSlug) {
|
|
112995
|
-
gameId = await resolveGameIdFromSlug(db2, claims.gameSlug) ?? undefined;
|
|
112996
|
-
}
|
|
112997
|
-
return { success: true, user, gameId };
|
|
112951
|
+
return { success: true, user, gameId: claims.gameId };
|
|
112998
112952
|
}
|
|
112999
112953
|
function setupAuth(options = {}) {
|
|
113000
112954
|
const { exceptions = [] } = options;
|
|
@@ -113014,20 +112968,19 @@ function setupAuth(options = {}) {
|
|
|
113014
112968
|
}
|
|
113015
112969
|
c.set("authError", result.error);
|
|
113016
112970
|
await next();
|
|
113017
|
-
if (result.shouldReturn404 && c.res.status === 404) {
|
|
113018
|
-
return;
|
|
113019
|
-
}
|
|
113020
|
-
return c.json({ error: "Unauthorized", message: result.error }, 401);
|
|
113021
112971
|
} catch (error88) {
|
|
113022
112972
|
console.error("[Auth] Unexpected error in authentication middleware:", error88);
|
|
113023
112973
|
return c.json({ error: "Internal Server Error", message: "Authentication failed" }, 500);
|
|
113024
112974
|
}
|
|
113025
112975
|
};
|
|
113026
112976
|
}
|
|
113027
|
-
var
|
|
112977
|
+
var sandboxAuth;
|
|
112978
|
+
var init_auth2 = __esm(async () => {
|
|
113028
112979
|
init_drizzle_orm();
|
|
113029
112980
|
init_tables_index();
|
|
113030
112981
|
init_constants();
|
|
112982
|
+
await init_providers();
|
|
112983
|
+
sandboxAuth = createSandboxAuthProvider();
|
|
113031
112984
|
});
|
|
113032
112985
|
|
|
113033
112986
|
// src/server/app.ts
|
|
@@ -113046,11 +112999,11 @@ function createApp(db2, options) {
|
|
|
113046
112999
|
}));
|
|
113047
113000
|
return app;
|
|
113048
113001
|
}
|
|
113049
|
-
var init_app = __esm(() => {
|
|
113002
|
+
var init_app = __esm(async () => {
|
|
113050
113003
|
init_dist7();
|
|
113051
113004
|
init_cors();
|
|
113052
113005
|
init_logger2();
|
|
113053
|
-
init_auth2();
|
|
113006
|
+
await init_auth2();
|
|
113054
113007
|
});
|
|
113055
113008
|
|
|
113056
113009
|
// ../../node_modules/.bun/@electric-sql+pglite@0.3.16/node_modules/@electric-sql/pglite/dist/chunk-QY3QWFKW.js
|
|
@@ -169179,18 +169132,33 @@ var init_sessions = __esm(async () => {
|
|
|
169179
169132
|
});
|
|
169180
169133
|
|
|
169181
169134
|
// src/routes/platform/games/verify.ts
|
|
169182
|
-
|
|
169135
|
+
function toUserInfo(userData) {
|
|
169136
|
+
return {
|
|
169137
|
+
sub: userData.id,
|
|
169138
|
+
email: userData.email || "",
|
|
169139
|
+
name: userData.name || userData.username || "",
|
|
169140
|
+
email_verified: true,
|
|
169141
|
+
given_name: undefined,
|
|
169142
|
+
family_name: undefined,
|
|
169143
|
+
timeback_id: userData.timebackId || undefined
|
|
169144
|
+
};
|
|
169145
|
+
}
|
|
169146
|
+
var gameVerifyRouter, sandboxAuth2;
|
|
169183
169147
|
var init_verify = __esm(async () => {
|
|
169184
169148
|
init_dist7();
|
|
169185
169149
|
init_errors2();
|
|
169186
169150
|
init_constants();
|
|
169187
169151
|
init_error_handler();
|
|
169188
|
-
await
|
|
169152
|
+
await __promiseAll([
|
|
169153
|
+
init_api3(),
|
|
169154
|
+
init_providers()
|
|
169155
|
+
]);
|
|
169189
169156
|
gameVerifyRouter = new Hono2;
|
|
169157
|
+
sandboxAuth2 = createSandboxAuthProvider();
|
|
169190
169158
|
gameVerifyRouter.post("/verify", async (c2) => {
|
|
169191
169159
|
const body2 = await c2.req.json().catch(() => ({}));
|
|
169192
169160
|
const token = body2?.token;
|
|
169193
|
-
if (!token) {
|
|
169161
|
+
if (typeof token !== "string" || !token) {
|
|
169194
169162
|
return c2.json({ error: "Missing token" }, 400);
|
|
169195
169163
|
}
|
|
169196
169164
|
const demoUser = DEMO_TOKENS[token];
|
|
@@ -169203,49 +169171,25 @@ var init_verify = __esm(async () => {
|
|
|
169203
169171
|
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169204
169172
|
}
|
|
169205
169173
|
return c2.json({
|
|
169206
|
-
claims: { sandbox: true },
|
|
169174
|
+
claims: { sandbox: true, exp: Math.floor(Date.now() / 1000) + 15 * 60 },
|
|
169207
169175
|
gameId: MOCK_GAME_ID,
|
|
169208
|
-
user:
|
|
169209
|
-
sub: userData.id,
|
|
169210
|
-
email: userData.email || "",
|
|
169211
|
-
name: userData.name || userData.username || "",
|
|
169212
|
-
email_verified: true,
|
|
169213
|
-
given_name: undefined,
|
|
169214
|
-
family_name: undefined,
|
|
169215
|
-
timeback_id: userData.timebackId || undefined
|
|
169216
|
-
}
|
|
169176
|
+
user: toUserInfo(userData)
|
|
169217
169177
|
});
|
|
169218
169178
|
}
|
|
169219
169179
|
if (token.endsWith(".sandbox")) {
|
|
169180
|
+
const payload = await sandboxAuth2.validateGameToken(token);
|
|
169181
|
+
if (!payload) {
|
|
169182
|
+
return c2.json({ error: "Invalid or expired sandbox token" }, 400);
|
|
169183
|
+
}
|
|
169220
169184
|
try {
|
|
169221
|
-
const
|
|
169222
|
-
|
|
169223
|
-
|
|
169224
|
-
|
|
169225
|
-
|
|
169226
|
-
|
|
169227
|
-
const userData = await db2.query.users.findFirst({
|
|
169228
|
-
where: (users3, { eq: eq3 }) => eq3(users3.id, userId)
|
|
169229
|
-
});
|
|
169230
|
-
if (!userData) {
|
|
169231
|
-
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169232
|
-
}
|
|
169233
|
-
return c2.json({
|
|
169234
|
-
claims: payload,
|
|
169235
|
-
gameId: gameIdOrSlug,
|
|
169236
|
-
user: {
|
|
169237
|
-
sub: userData.id,
|
|
169238
|
-
email: userData.email || "",
|
|
169239
|
-
name: userData.name || userData.username || "",
|
|
169240
|
-
email_verified: true,
|
|
169241
|
-
given_name: undefined,
|
|
169242
|
-
family_name: undefined,
|
|
169243
|
-
timeback_id: userData.timebackId || undefined
|
|
169244
|
-
}
|
|
169245
|
-
});
|
|
169246
|
-
} else {
|
|
169247
|
-
throw new Error("Malformed sandbox token: expected 3 parts");
|
|
169185
|
+
const db2 = c2.get("db");
|
|
169186
|
+
const userData = await db2.query.users.findFirst({
|
|
169187
|
+
where: (users3, { eq: eq3 }) => eq3(users3.id, payload.uid)
|
|
169188
|
+
});
|
|
169189
|
+
if (!userData) {
|
|
169190
|
+
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169248
169191
|
}
|
|
169192
|
+
return c2.json({ claims: payload, gameId: payload.sub, user: toUserInfo(userData) });
|
|
169249
169193
|
} catch {
|
|
169250
169194
|
return c2.json({ error: "Invalid sandbox token" }, 400);
|
|
169251
169195
|
}
|
|
@@ -169753,13 +169697,13 @@ var init_server = __esm(async () => {
|
|
|
169753
169697
|
init_port();
|
|
169754
169698
|
init_package();
|
|
169755
169699
|
init_config();
|
|
169756
|
-
init_app();
|
|
169757
169700
|
init_database2();
|
|
169758
169701
|
init_options();
|
|
169759
169702
|
await __promiseAll([
|
|
169760
169703
|
init_api3(),
|
|
169761
169704
|
init_context(),
|
|
169762
|
-
init_providers()
|
|
169705
|
+
init_providers(),
|
|
169706
|
+
init_app()
|
|
169763
169707
|
]);
|
|
169764
169708
|
version5 = package_default.version;
|
|
169765
169709
|
});
|
package/dist/server.js
CHANGED
|
@@ -1127,7 +1127,7 @@ var package_default;
|
|
|
1127
1127
|
var init_package = __esm(() => {
|
|
1128
1128
|
package_default = {
|
|
1129
1129
|
name: "@playcademy/sandbox",
|
|
1130
|
-
version: "0.
|
|
1130
|
+
version: "0.9.1-beta.1",
|
|
1131
1131
|
description: "Local development server for Playcademy game development",
|
|
1132
1132
|
type: "module",
|
|
1133
1133
|
exports: {
|
|
@@ -100428,6 +100428,20 @@ function assessmentAttemptId(input) {
|
|
|
100428
100428
|
String(input.attemptNumber)
|
|
100429
100429
|
].join("\x00"));
|
|
100430
100430
|
}
|
|
100431
|
+
function buildAssessmentAttemptResultWhere(scope) {
|
|
100432
|
+
return {
|
|
100433
|
+
"student.sourcedId": scope.studentId,
|
|
100434
|
+
status: ONEROSTER_STATUS2.active,
|
|
100435
|
+
[`metadata.${PLAYCADEMY_ASSESSMENT_RESULT_METADATA_KEY}.activityId`]: scope.activityId
|
|
100436
|
+
};
|
|
100437
|
+
}
|
|
100438
|
+
function buildAssessmentChildResultWhere(scope) {
|
|
100439
|
+
return {
|
|
100440
|
+
"student.sourcedId": scope.studentId,
|
|
100441
|
+
status: ONEROSTER_STATUS2.active,
|
|
100442
|
+
[`metadata.${PLAYCADEMY_ASSESSMENT_ITEM_RESULT_METADATA_KEY}.payload.attempt.activityId`]: scope.activityId
|
|
100443
|
+
};
|
|
100444
|
+
}
|
|
100431
100445
|
async function findLatestAssessmentResult(results, match) {
|
|
100432
100446
|
let latest = null;
|
|
100433
100447
|
let latestTimestamp = 0;
|
|
@@ -100867,6 +100881,7 @@ var PLAYABLE_SHAPE_SET;
|
|
|
100867
100881
|
var init_timeback_assessment_runtime_util = __esm(() => {
|
|
100868
100882
|
init_src();
|
|
100869
100883
|
init_assessment_runtime2();
|
|
100884
|
+
init_constants3();
|
|
100870
100885
|
init_qti();
|
|
100871
100886
|
init_types2();
|
|
100872
100887
|
init_utils6();
|
|
@@ -103727,7 +103742,11 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
103727
103742
|
const startedAt = Date.now();
|
|
103728
103743
|
const [candidates, results] = await Promise.all([
|
|
103729
103744
|
this.candidateIntegrations(params.gameId, params.studentId, params.input),
|
|
103730
|
-
this.
|
|
103745
|
+
this.activityAssessmentResults({
|
|
103746
|
+
studentId: params.studentId,
|
|
103747
|
+
activityId: params.input.activityId,
|
|
103748
|
+
purpose: params.input.purpose
|
|
103749
|
+
})
|
|
103731
103750
|
]);
|
|
103732
103751
|
const context2 = await this.selectIntegration(candidates, params.input);
|
|
103733
103752
|
const attemptScope = {
|
|
@@ -104790,12 +104809,19 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104790
104809
|
standard: assessmentStandardForRow(row)
|
|
104791
104810
|
})).filter((test) => !matchesMasteryStandard || matchesMasteryStandard(test.standard));
|
|
104792
104811
|
}
|
|
104793
|
-
async
|
|
104812
|
+
async activityAssessmentResults(scope) {
|
|
104813
|
+
const [parents, children] = await Promise.all([
|
|
104814
|
+
this.collectAssessmentResults(buildAssessmentAttemptResultWhere(scope)),
|
|
104815
|
+
scope.purpose === "review" ? this.activityChildResults(scope) : []
|
|
104816
|
+
]);
|
|
104817
|
+
return [...parents, ...children];
|
|
104818
|
+
}
|
|
104819
|
+
activityChildResults(scope) {
|
|
104820
|
+
return this.collectAssessmentResults(buildAssessmentChildResultWhere(scope));
|
|
104821
|
+
}
|
|
104822
|
+
async collectAssessmentResults(where) {
|
|
104794
104823
|
const stream = this.requireClient().api.oneroster.assessmentResults.stream({
|
|
104795
|
-
where
|
|
104796
|
-
"student.sourcedId": studentId,
|
|
104797
|
-
status: ONEROSTER_STATUS2.active
|
|
104798
|
-
}
|
|
104824
|
+
where
|
|
104799
104825
|
});
|
|
104800
104826
|
const results = [];
|
|
104801
104827
|
for await (const result of stream) {
|
|
@@ -104804,7 +104830,7 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
104804
104830
|
return results;
|
|
104805
104831
|
}
|
|
104806
104832
|
async listAttempts(scope) {
|
|
104807
|
-
return this.attemptListing(await this.
|
|
104833
|
+
return this.attemptListing(await this.activityAssessmentResults(scope), scope);
|
|
104808
104834
|
}
|
|
104809
104835
|
async attemptListing(results, scope) {
|
|
104810
104836
|
const attempts = new Map;
|
|
@@ -105569,7 +105595,10 @@ var init_timeback_assessment_runtime_service = __esm(async () => {
|
|
|
105569
105595
|
const graded = await resumeAssessmentTranscript({
|
|
105570
105596
|
suppliedTranscript,
|
|
105571
105597
|
checkpointTranscript: metadata2.transcript,
|
|
105572
|
-
recover: async () => recoverAssessmentTranscript(context2, await this.
|
|
105598
|
+
recover: async () => recoverAssessmentTranscript(context2, await this.activityChildResults({
|
|
105599
|
+
studentId: context2.studentId,
|
|
105600
|
+
activityId: context2.metadata.activityId
|
|
105601
|
+
})),
|
|
105573
105602
|
grade: (transcript) => this.gradeTranscript(context2, transcript)
|
|
105574
105603
|
});
|
|
105575
105604
|
const session2 = await this.assessmentSession(context2, graded.transcript);
|
|
@@ -110719,10 +110748,10 @@ function createSandboxAuthProvider() {
|
|
|
110719
110748
|
},
|
|
110720
110749
|
async mintGameToken(gameId, userId) {
|
|
110721
110750
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
110722
|
-
const exp = Date.now() + 15 * 60
|
|
110751
|
+
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
110723
110752
|
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
110724
110753
|
const token = `${header}.${payload}.sandbox`;
|
|
110725
|
-
return { token, exp };
|
|
110754
|
+
return { token, exp: exp * 1000 };
|
|
110726
110755
|
},
|
|
110727
110756
|
async validateGameToken(token) {
|
|
110728
110757
|
try {
|
|
@@ -110730,18 +110759,9 @@ function createSandboxAuthProvider() {
|
|
|
110730
110759
|
if (parts2.length !== 3) {
|
|
110731
110760
|
return null;
|
|
110732
110761
|
}
|
|
110733
|
-
if (parts2[2] === "sandbox") {
|
|
110734
|
-
const payload2 = JSON.parse(atob(parts2[1]));
|
|
110735
|
-
if (payload2.sub && payload2.uid) {
|
|
110736
|
-
if (payload2.exp && payload2.exp < Date.now()) {
|
|
110737
|
-
log.debug("[SandboxAuthProvider] Token expired");
|
|
110738
|
-
return null;
|
|
110739
|
-
}
|
|
110740
|
-
return payload2;
|
|
110741
|
-
}
|
|
110742
|
-
}
|
|
110743
110762
|
const payload = JSON.parse(atob(parts2[1]));
|
|
110744
|
-
|
|
110763
|
+
const nowSeconds = Date.now() / 1000;
|
|
110764
|
+
if (typeof payload?.sub === "string" && payload.sub.length > 0 && typeof payload.uid === "string" && payload.uid.length > 0 && typeof payload.exp === "number" && Number.isFinite(payload.exp) && payload.exp > nowSeconds && payload.exp < nowSeconds + MAX_TOKEN_LIFETIME_SECONDS) {
|
|
110745
110765
|
return payload;
|
|
110746
110766
|
}
|
|
110747
110767
|
return null;
|
|
@@ -110839,7 +110859,7 @@ function createSandboxAuthProvider() {
|
|
|
110839
110859
|
}
|
|
110840
110860
|
};
|
|
110841
110861
|
}
|
|
110842
|
-
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, sandboxAssessmentTokens;
|
|
110862
|
+
var sandboxApiKeys, sandboxAssessmentPreparationReceipts, ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS, MAX_TOKEN_LIFETIME_SECONDS, sandboxAssessmentTokens;
|
|
110843
110863
|
var init_auth_provider = __esm(async () => {
|
|
110844
110864
|
init_src6();
|
|
110845
110865
|
init_constants();
|
|
@@ -110847,6 +110867,7 @@ var init_auth_provider = __esm(async () => {
|
|
|
110847
110867
|
sandboxApiKeys = new Map;
|
|
110848
110868
|
sandboxAssessmentPreparationReceipts = new Map;
|
|
110849
110869
|
ASSESSMENT_PREPARATION_RECEIPT_LIFETIME_MS = 5 * 60 * 1000;
|
|
110870
|
+
MAX_TOKEN_LIFETIME_SECONDS = 30 * 24 * 60 * 60;
|
|
110850
110871
|
sandboxAssessmentTokens = new TimebackCache({
|
|
110851
110872
|
defaultTTL: 2 * 60 * 60 * 1000,
|
|
110852
110873
|
maxSize: 1000
|
|
@@ -112874,56 +112895,13 @@ function extractBearerToken(authHeader) {
|
|
|
112874
112895
|
}
|
|
112875
112896
|
return authHeader.substring(7);
|
|
112876
112897
|
}
|
|
112877
|
-
function
|
|
112878
|
-
try {
|
|
112879
|
-
const parts2 = token.split(".");
|
|
112880
|
-
if (parts2.length !== 3 || parts2[2] !== "sandbox") {
|
|
112881
|
-
return null;
|
|
112882
|
-
}
|
|
112883
|
-
const header = JSON.parse(atob(parts2[0]));
|
|
112884
|
-
if (header.typ !== "sandbox") {
|
|
112885
|
-
return null;
|
|
112886
|
-
}
|
|
112887
|
-
const payload = JSON.parse(atob(parts2[1]));
|
|
112888
|
-
if (!payload.uid || !payload.sub) {
|
|
112889
|
-
return null;
|
|
112890
|
-
}
|
|
112891
|
-
return {
|
|
112892
|
-
userId: payload.uid,
|
|
112893
|
-
gameSlug: payload.sub
|
|
112894
|
-
};
|
|
112895
|
-
} catch {
|
|
112896
|
-
return null;
|
|
112897
|
-
}
|
|
112898
|
-
}
|
|
112899
|
-
function parseJwtClaims(token) {
|
|
112900
|
-
try {
|
|
112901
|
-
const parts2 = token.split(".");
|
|
112902
|
-
if (parts2.length === 3 && parts2[1]) {
|
|
112903
|
-
const payload = JSON.parse(atob(parts2[1]));
|
|
112904
|
-
if (payload.uid) {
|
|
112905
|
-
return {
|
|
112906
|
-
userId: payload.uid,
|
|
112907
|
-
gameId: payload.sub
|
|
112908
|
-
};
|
|
112909
|
-
}
|
|
112910
|
-
}
|
|
112911
|
-
} catch {}
|
|
112912
|
-
return null;
|
|
112913
|
-
}
|
|
112914
|
-
function resolveAuth(token) {
|
|
112898
|
+
async function resolveAuth(token) {
|
|
112915
112899
|
const demoUser = DEMO_TOKENS[token];
|
|
112916
112900
|
if (demoUser) {
|
|
112917
112901
|
return { userId: demoUser.id };
|
|
112918
112902
|
}
|
|
112919
|
-
|
|
112920
|
-
|
|
112921
|
-
if (sandboxClaims) {
|
|
112922
|
-
return sandboxClaims;
|
|
112923
|
-
}
|
|
112924
|
-
return parseJwtClaims(token);
|
|
112925
|
-
}
|
|
112926
|
-
return null;
|
|
112903
|
+
const payload = await sandboxAuth.validateGameToken(token);
|
|
112904
|
+
return payload ? { userId: payload.uid, gameId: payload.sub } : null;
|
|
112927
112905
|
}
|
|
112928
112906
|
async function fetchUserFromDatabase(db2, userId) {
|
|
112929
112907
|
try {
|
|
@@ -112936,18 +112914,6 @@ async function fetchUserFromDatabase(db2, userId) {
|
|
|
112936
112914
|
throw error88;
|
|
112937
112915
|
}
|
|
112938
112916
|
}
|
|
112939
|
-
async function resolveGameIdFromSlug(db2, slug2) {
|
|
112940
|
-
try {
|
|
112941
|
-
const game2 = await db2.query.games.findFirst({
|
|
112942
|
-
where: eq(games.slug, slug2),
|
|
112943
|
-
columns: { id: true }
|
|
112944
|
-
});
|
|
112945
|
-
return game2?.id || null;
|
|
112946
|
-
} catch (error88) {
|
|
112947
|
-
console.error("[Auth] Error looking up game by slug:", error88);
|
|
112948
|
-
return null;
|
|
112949
|
-
}
|
|
112950
|
-
}
|
|
112951
112917
|
function isPublicRoute(path, exceptions) {
|
|
112952
112918
|
return exceptions.some((exception) => {
|
|
112953
112919
|
if (path === exception) {
|
|
@@ -112965,35 +112931,23 @@ async function authenticateRequest(c) {
|
|
|
112965
112931
|
const apiKey = c.req.header("x-api-key");
|
|
112966
112932
|
const token = bearerToken || apiKey;
|
|
112967
112933
|
if (!token) {
|
|
112968
|
-
return {
|
|
112969
|
-
success: false,
|
|
112970
|
-
error: "No authorization token provided",
|
|
112971
|
-
shouldReturn404: true
|
|
112972
|
-
};
|
|
112934
|
+
return { success: false, error: "No authorization token provided" };
|
|
112973
112935
|
}
|
|
112974
|
-
const resolved = resolveAuth(token);
|
|
112936
|
+
const resolved = await resolveAuth(token);
|
|
112975
112937
|
if (!resolved) {
|
|
112976
|
-
return {
|
|
112977
|
-
success: false,
|
|
112978
|
-
error: "Invalid or expired token",
|
|
112979
|
-
shouldReturn404: true
|
|
112980
|
-
};
|
|
112938
|
+
return { success: false, error: "Invalid or expired token" };
|
|
112981
112939
|
}
|
|
112982
112940
|
const claims = resolved;
|
|
112983
112941
|
const db2 = c.get("db");
|
|
112984
112942
|
if (!db2) {
|
|
112985
112943
|
console.error("[Auth] Database not available in context");
|
|
112986
|
-
return { success: false, error: "Internal server error"
|
|
112944
|
+
return { success: false, error: "Internal server error" };
|
|
112987
112945
|
}
|
|
112988
112946
|
const user = await fetchUserFromDatabase(db2, claims.userId);
|
|
112989
112947
|
if (!user) {
|
|
112990
|
-
return { success: false, error: "User not found or token invalid"
|
|
112948
|
+
return { success: false, error: "User not found or token invalid" };
|
|
112991
112949
|
}
|
|
112992
|
-
|
|
112993
|
-
if (!gameId && claims.gameSlug) {
|
|
112994
|
-
gameId = await resolveGameIdFromSlug(db2, claims.gameSlug) ?? undefined;
|
|
112995
|
-
}
|
|
112996
|
-
return { success: true, user, gameId };
|
|
112950
|
+
return { success: true, user, gameId: claims.gameId };
|
|
112997
112951
|
}
|
|
112998
112952
|
function setupAuth(options = {}) {
|
|
112999
112953
|
const { exceptions = [] } = options;
|
|
@@ -113013,20 +112967,19 @@ function setupAuth(options = {}) {
|
|
|
113013
112967
|
}
|
|
113014
112968
|
c.set("authError", result.error);
|
|
113015
112969
|
await next();
|
|
113016
|
-
if (result.shouldReturn404 && c.res.status === 404) {
|
|
113017
|
-
return;
|
|
113018
|
-
}
|
|
113019
|
-
return c.json({ error: "Unauthorized", message: result.error }, 401);
|
|
113020
112970
|
} catch (error88) {
|
|
113021
112971
|
console.error("[Auth] Unexpected error in authentication middleware:", error88);
|
|
113022
112972
|
return c.json({ error: "Internal Server Error", message: "Authentication failed" }, 500);
|
|
113023
112973
|
}
|
|
113024
112974
|
};
|
|
113025
112975
|
}
|
|
113026
|
-
var
|
|
112976
|
+
var sandboxAuth;
|
|
112977
|
+
var init_auth2 = __esm(async () => {
|
|
113027
112978
|
init_drizzle_orm();
|
|
113028
112979
|
init_tables_index();
|
|
113029
112980
|
init_constants();
|
|
112981
|
+
await init_providers();
|
|
112982
|
+
sandboxAuth = createSandboxAuthProvider();
|
|
113030
112983
|
});
|
|
113031
112984
|
|
|
113032
112985
|
// src/server/app.ts
|
|
@@ -113045,11 +112998,11 @@ function createApp(db2, options) {
|
|
|
113045
112998
|
}));
|
|
113046
112999
|
return app;
|
|
113047
113000
|
}
|
|
113048
|
-
var init_app = __esm(() => {
|
|
113001
|
+
var init_app = __esm(async () => {
|
|
113049
113002
|
init_dist7();
|
|
113050
113003
|
init_cors();
|
|
113051
113004
|
init_logger2();
|
|
113052
|
-
init_auth2();
|
|
113005
|
+
await init_auth2();
|
|
113053
113006
|
});
|
|
113054
113007
|
|
|
113055
113008
|
// ../../node_modules/.bun/@electric-sql+pglite@0.3.16/node_modules/@electric-sql/pglite/dist/chunk-QY3QWFKW.js
|
|
@@ -169178,18 +169131,33 @@ var init_sessions = __esm(async () => {
|
|
|
169178
169131
|
});
|
|
169179
169132
|
|
|
169180
169133
|
// src/routes/platform/games/verify.ts
|
|
169181
|
-
|
|
169134
|
+
function toUserInfo(userData) {
|
|
169135
|
+
return {
|
|
169136
|
+
sub: userData.id,
|
|
169137
|
+
email: userData.email || "",
|
|
169138
|
+
name: userData.name || userData.username || "",
|
|
169139
|
+
email_verified: true,
|
|
169140
|
+
given_name: undefined,
|
|
169141
|
+
family_name: undefined,
|
|
169142
|
+
timeback_id: userData.timebackId || undefined
|
|
169143
|
+
};
|
|
169144
|
+
}
|
|
169145
|
+
var gameVerifyRouter, sandboxAuth2;
|
|
169182
169146
|
var init_verify = __esm(async () => {
|
|
169183
169147
|
init_dist7();
|
|
169184
169148
|
init_errors2();
|
|
169185
169149
|
init_constants();
|
|
169186
169150
|
init_error_handler();
|
|
169187
|
-
await
|
|
169151
|
+
await __promiseAll([
|
|
169152
|
+
init_api3(),
|
|
169153
|
+
init_providers()
|
|
169154
|
+
]);
|
|
169188
169155
|
gameVerifyRouter = new Hono2;
|
|
169156
|
+
sandboxAuth2 = createSandboxAuthProvider();
|
|
169189
169157
|
gameVerifyRouter.post("/verify", async (c2) => {
|
|
169190
169158
|
const body2 = await c2.req.json().catch(() => ({}));
|
|
169191
169159
|
const token = body2?.token;
|
|
169192
|
-
if (!token) {
|
|
169160
|
+
if (typeof token !== "string" || !token) {
|
|
169193
169161
|
return c2.json({ error: "Missing token" }, 400);
|
|
169194
169162
|
}
|
|
169195
169163
|
const demoUser = DEMO_TOKENS[token];
|
|
@@ -169202,49 +169170,25 @@ var init_verify = __esm(async () => {
|
|
|
169202
169170
|
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169203
169171
|
}
|
|
169204
169172
|
return c2.json({
|
|
169205
|
-
claims: { sandbox: true },
|
|
169173
|
+
claims: { sandbox: true, exp: Math.floor(Date.now() / 1000) + 15 * 60 },
|
|
169206
169174
|
gameId: MOCK_GAME_ID,
|
|
169207
|
-
user:
|
|
169208
|
-
sub: userData.id,
|
|
169209
|
-
email: userData.email || "",
|
|
169210
|
-
name: userData.name || userData.username || "",
|
|
169211
|
-
email_verified: true,
|
|
169212
|
-
given_name: undefined,
|
|
169213
|
-
family_name: undefined,
|
|
169214
|
-
timeback_id: userData.timebackId || undefined
|
|
169215
|
-
}
|
|
169175
|
+
user: toUserInfo(userData)
|
|
169216
169176
|
});
|
|
169217
169177
|
}
|
|
169218
169178
|
if (token.endsWith(".sandbox")) {
|
|
169179
|
+
const payload = await sandboxAuth2.validateGameToken(token);
|
|
169180
|
+
if (!payload) {
|
|
169181
|
+
return c2.json({ error: "Invalid or expired sandbox token" }, 400);
|
|
169182
|
+
}
|
|
169219
169183
|
try {
|
|
169220
|
-
const
|
|
169221
|
-
|
|
169222
|
-
|
|
169223
|
-
|
|
169224
|
-
|
|
169225
|
-
|
|
169226
|
-
const userData = await db2.query.users.findFirst({
|
|
169227
|
-
where: (users3, { eq: eq3 }) => eq3(users3.id, userId)
|
|
169228
|
-
});
|
|
169229
|
-
if (!userData) {
|
|
169230
|
-
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169231
|
-
}
|
|
169232
|
-
return c2.json({
|
|
169233
|
-
claims: payload,
|
|
169234
|
-
gameId: gameIdOrSlug,
|
|
169235
|
-
user: {
|
|
169236
|
-
sub: userData.id,
|
|
169237
|
-
email: userData.email || "",
|
|
169238
|
-
name: userData.name || userData.username || "",
|
|
169239
|
-
email_verified: true,
|
|
169240
|
-
given_name: undefined,
|
|
169241
|
-
family_name: undefined,
|
|
169242
|
-
timeback_id: userData.timebackId || undefined
|
|
169243
|
-
}
|
|
169244
|
-
});
|
|
169245
|
-
} else {
|
|
169246
|
-
throw new Error("Malformed sandbox token: expected 3 parts");
|
|
169184
|
+
const db2 = c2.get("db");
|
|
169185
|
+
const userData = await db2.query.users.findFirst({
|
|
169186
|
+
where: (users3, { eq: eq3 }) => eq3(users3.id, payload.uid)
|
|
169187
|
+
});
|
|
169188
|
+
if (!userData) {
|
|
169189
|
+
return c2.json({ error: "User not found in sandbox" }, 500);
|
|
169247
169190
|
}
|
|
169191
|
+
return c2.json({ claims: payload, gameId: payload.sub, user: toUserInfo(userData) });
|
|
169248
169192
|
} catch {
|
|
169249
169193
|
return c2.json({ error: "Invalid sandbox token" }, 400);
|
|
169250
169194
|
}
|
|
@@ -169752,13 +169696,13 @@ var init_server = __esm(async () => {
|
|
|
169752
169696
|
init_port();
|
|
169753
169697
|
init_package();
|
|
169754
169698
|
init_config();
|
|
169755
|
-
init_app();
|
|
169756
169699
|
init_database2();
|
|
169757
169700
|
init_options();
|
|
169758
169701
|
await __promiseAll([
|
|
169759
169702
|
init_api3(),
|
|
169760
169703
|
init_context(),
|
|
169761
|
-
init_providers()
|
|
169704
|
+
init_providers(),
|
|
169705
|
+
init_app()
|
|
169762
169706
|
]);
|
|
169763
169707
|
version5 = package_default.version;
|
|
169764
169708
|
});
|