@playcademy/sandbox 0.12.2-beta.4 → 0.12.2-beta.5
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 +25 -8
- package/dist/server.js +25 -8
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2517,7 +2517,7 @@ var package_default;
|
|
|
2517
2517
|
var init_package = __esm(() => {
|
|
2518
2518
|
package_default = {
|
|
2519
2519
|
name: "@playcademy/sandbox",
|
|
2520
|
-
version: "0.12.2-beta.
|
|
2520
|
+
version: "0.12.2-beta.5",
|
|
2521
2521
|
description: "Local development server for Playcademy game development",
|
|
2522
2522
|
type: "module",
|
|
2523
2523
|
exports: {
|
|
@@ -20917,6 +20917,7 @@ var init_table8 = __esm(() => {
|
|
|
20917
20917
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
20918
20918
|
parentOrigin: text("parent_origin").notNull(),
|
|
20919
20919
|
timebackCourseId: text("timeback_course_id"),
|
|
20920
|
+
contextId: text("context_id"),
|
|
20920
20921
|
passHash: text("pass_hash").unique(),
|
|
20921
20922
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
20922
20923
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -50823,13 +50824,16 @@ function isCanonicalOrigin(value) {
|
|
|
50823
50824
|
}
|
|
50824
50825
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
50825
50826
|
}
|
|
50826
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50827
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50827
50828
|
var init_schemas5 = __esm(() => {
|
|
50828
50829
|
init_esm();
|
|
50829
50830
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
50830
50831
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
50831
50832
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
50832
50833
|
});
|
|
50834
|
+
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
50835
|
+
message: "contextId must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
50836
|
+
});
|
|
50833
50837
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
50834
50838
|
EnsureEmbedPartnerSchema = objectType({
|
|
50835
50839
|
name: stringType().trim().min(1).max(100)
|
|
@@ -50841,7 +50845,8 @@ var init_schemas5 = __esm(() => {
|
|
|
50841
50845
|
timebackId: stringType().trim().min(1).max(200),
|
|
50842
50846
|
gameSlug: stringType().trim().min(1).max(100),
|
|
50843
50847
|
parentOrigin: EmbedOriginSchema,
|
|
50844
|
-
timebackCourseId: stringType().trim().min(1).max(200).optional()
|
|
50848
|
+
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
50849
|
+
contextId: EmbedContextIdSchema.optional()
|
|
50845
50850
|
}).strict();
|
|
50846
50851
|
RedeemEmbedPassSchema = objectType({
|
|
50847
50852
|
pass: stringType().min(1).max(200),
|
|
@@ -95683,6 +95688,13 @@ var init_embed_util = __esm(() => {
|
|
|
95683
95688
|
});
|
|
95684
95689
|
|
|
95685
95690
|
// ../api-core/src/services/embed.service.ts
|
|
95691
|
+
async function scopedContextId(partnerId, contextId) {
|
|
95692
|
+
return sha256Hex(`${partnerId}:${contextId}`);
|
|
95693
|
+
}
|
|
95694
|
+
function launchTokenClaims(launch) {
|
|
95695
|
+
return launch.contextId ? { contextId: launch.contextId } : {};
|
|
95696
|
+
}
|
|
95697
|
+
|
|
95686
95698
|
class EmbedService {
|
|
95687
95699
|
deps;
|
|
95688
95700
|
constructor(deps) {
|
|
@@ -95726,6 +95738,7 @@ class EmbedService {
|
|
|
95726
95738
|
gameId: game.id,
|
|
95727
95739
|
parentOrigin: input.parentOrigin,
|
|
95728
95740
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
95741
|
+
contextId: input.contextId ? await scopedContextId(partner.id, input.contextId) : null,
|
|
95729
95742
|
passHash: await sha256Hex(pass),
|
|
95730
95743
|
passExpiresAt,
|
|
95731
95744
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -95733,7 +95746,11 @@ class EmbedService {
|
|
|
95733
95746
|
if (!launch) {
|
|
95734
95747
|
throw new InternalError("Failed to create embed launch");
|
|
95735
95748
|
}
|
|
95736
|
-
setAttributes({
|
|
95749
|
+
setAttributes({
|
|
95750
|
+
"app.launch.id": launch.id,
|
|
95751
|
+
"app.user.id": user.id,
|
|
95752
|
+
"app.embed.has_context": input.contextId !== undefined
|
|
95753
|
+
});
|
|
95737
95754
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
95738
95755
|
}
|
|
95739
95756
|
async redeemLaunch(input, timeZone) {
|
|
@@ -95748,7 +95765,7 @@ class EmbedService {
|
|
|
95748
95765
|
const [game, profile, token] = await Promise.all([
|
|
95749
95766
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
95750
95767
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
95751
|
-
this.deps.mintGameToken(launch.gameId, user.id)
|
|
95768
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
95752
95769
|
]);
|
|
95753
95770
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
95754
95771
|
return {
|
|
@@ -95780,7 +95797,7 @@ class EmbedService {
|
|
|
95780
95797
|
if (outcome) {
|
|
95781
95798
|
throw new AccessDeniedError("This launch has ended");
|
|
95782
95799
|
}
|
|
95783
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId);
|
|
95800
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
95784
95801
|
}
|
|
95785
95802
|
async endLaunch(launchId) {
|
|
95786
95803
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -96882,10 +96899,10 @@ function createSandboxAuthProvider() {
|
|
|
96882
96899
|
}
|
|
96883
96900
|
return deleted;
|
|
96884
96901
|
},
|
|
96885
|
-
async mintGameToken(gameId, userId) {
|
|
96902
|
+
async mintGameToken(gameId, userId, claims) {
|
|
96886
96903
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
96887
96904
|
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
96888
|
-
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
96905
|
+
const payload = btoa(JSON.stringify({ ...claims, sub: gameId, uid: userId, exp }));
|
|
96889
96906
|
const token = `${header}.${payload}.sandbox`;
|
|
96890
96907
|
return { token, exp: exp * 1000 };
|
|
96891
96908
|
},
|
package/dist/server.js
CHANGED
|
@@ -697,7 +697,7 @@ var package_default;
|
|
|
697
697
|
var init_package = __esm(() => {
|
|
698
698
|
package_default = {
|
|
699
699
|
name: "@playcademy/sandbox",
|
|
700
|
-
version: "0.12.2-beta.
|
|
700
|
+
version: "0.12.2-beta.5",
|
|
701
701
|
description: "Local development server for Playcademy game development",
|
|
702
702
|
type: "module",
|
|
703
703
|
exports: {
|
|
@@ -19097,6 +19097,7 @@ var init_table8 = __esm(() => {
|
|
|
19097
19097
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
19098
19098
|
parentOrigin: text("parent_origin").notNull(),
|
|
19099
19099
|
timebackCourseId: text("timeback_course_id"),
|
|
19100
|
+
contextId: text("context_id"),
|
|
19100
19101
|
passHash: text("pass_hash").unique(),
|
|
19101
19102
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
19102
19103
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -49003,13 +49004,16 @@ function isCanonicalOrigin(value) {
|
|
|
49003
49004
|
}
|
|
49004
49005
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
49005
49006
|
}
|
|
49006
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49007
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49007
49008
|
var init_schemas5 = __esm(() => {
|
|
49008
49009
|
init_esm();
|
|
49009
49010
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
49010
49011
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
49011
49012
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
49012
49013
|
});
|
|
49014
|
+
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
49015
|
+
message: "contextId must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
49016
|
+
});
|
|
49013
49017
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
49014
49018
|
EnsureEmbedPartnerSchema = objectType({
|
|
49015
49019
|
name: stringType().trim().min(1).max(100)
|
|
@@ -49021,7 +49025,8 @@ var init_schemas5 = __esm(() => {
|
|
|
49021
49025
|
timebackId: stringType().trim().min(1).max(200),
|
|
49022
49026
|
gameSlug: stringType().trim().min(1).max(100),
|
|
49023
49027
|
parentOrigin: EmbedOriginSchema,
|
|
49024
|
-
timebackCourseId: stringType().trim().min(1).max(200).optional()
|
|
49028
|
+
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
49029
|
+
contextId: EmbedContextIdSchema.optional()
|
|
49025
49030
|
}).strict();
|
|
49026
49031
|
RedeemEmbedPassSchema = objectType({
|
|
49027
49032
|
pass: stringType().min(1).max(200),
|
|
@@ -93863,6 +93868,13 @@ var init_embed_util = __esm(() => {
|
|
|
93863
93868
|
});
|
|
93864
93869
|
|
|
93865
93870
|
// ../api-core/src/services/embed.service.ts
|
|
93871
|
+
async function scopedContextId(partnerId, contextId) {
|
|
93872
|
+
return sha256Hex(`${partnerId}:${contextId}`);
|
|
93873
|
+
}
|
|
93874
|
+
function launchTokenClaims(launch) {
|
|
93875
|
+
return launch.contextId ? { contextId: launch.contextId } : {};
|
|
93876
|
+
}
|
|
93877
|
+
|
|
93866
93878
|
class EmbedService {
|
|
93867
93879
|
deps;
|
|
93868
93880
|
constructor(deps) {
|
|
@@ -93906,6 +93918,7 @@ class EmbedService {
|
|
|
93906
93918
|
gameId: game.id,
|
|
93907
93919
|
parentOrigin: input.parentOrigin,
|
|
93908
93920
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
93921
|
+
contextId: input.contextId ? await scopedContextId(partner.id, input.contextId) : null,
|
|
93909
93922
|
passHash: await sha256Hex(pass),
|
|
93910
93923
|
passExpiresAt,
|
|
93911
93924
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -93913,7 +93926,11 @@ class EmbedService {
|
|
|
93913
93926
|
if (!launch) {
|
|
93914
93927
|
throw new InternalError("Failed to create embed launch");
|
|
93915
93928
|
}
|
|
93916
|
-
setAttributes({
|
|
93929
|
+
setAttributes({
|
|
93930
|
+
"app.launch.id": launch.id,
|
|
93931
|
+
"app.user.id": user.id,
|
|
93932
|
+
"app.embed.has_context": input.contextId !== undefined
|
|
93933
|
+
});
|
|
93917
93934
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
93918
93935
|
}
|
|
93919
93936
|
async redeemLaunch(input, timeZone) {
|
|
@@ -93928,7 +93945,7 @@ class EmbedService {
|
|
|
93928
93945
|
const [game, profile, token] = await Promise.all([
|
|
93929
93946
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
93930
93947
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
93931
|
-
this.deps.mintGameToken(launch.gameId, user.id)
|
|
93948
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
93932
93949
|
]);
|
|
93933
93950
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
93934
93951
|
return {
|
|
@@ -93960,7 +93977,7 @@ class EmbedService {
|
|
|
93960
93977
|
if (outcome) {
|
|
93961
93978
|
throw new AccessDeniedError("This launch has ended");
|
|
93962
93979
|
}
|
|
93963
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId);
|
|
93980
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
93964
93981
|
}
|
|
93965
93982
|
async endLaunch(launchId) {
|
|
93966
93983
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -95062,10 +95079,10 @@ function createSandboxAuthProvider() {
|
|
|
95062
95079
|
}
|
|
95063
95080
|
return deleted;
|
|
95064
95081
|
},
|
|
95065
|
-
async mintGameToken(gameId, userId) {
|
|
95082
|
+
async mintGameToken(gameId, userId, claims) {
|
|
95066
95083
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
95067
95084
|
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
95068
|
-
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
95085
|
+
const payload = btoa(JSON.stringify({ ...claims, sub: gameId, uid: userId, exp }));
|
|
95069
95086
|
const token = `${header}.${payload}.sandbox`;
|
|
95070
95087
|
return { token, exp: exp * 1000 };
|
|
95071
95088
|
},
|