@playcademy/sandbox 0.12.2-beta.4 → 0.12.2-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +44 -9
- package/dist/server.js +44 -9
- 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.6",
|
|
2521
2521
|
description: "Local development server for Playcademy game development",
|
|
2522
2522
|
type: "module",
|
|
2523
2523
|
exports: {
|
|
@@ -20895,12 +20895,13 @@ var init_table7 = __esm(() => {
|
|
|
20895
20895
|
});
|
|
20896
20896
|
|
|
20897
20897
|
// ../data/src/domains/embed/table.ts
|
|
20898
|
-
var embedPartners, embedLaunches, embedLaunchesRelations;
|
|
20898
|
+
var embedContextRoleEnum, embedPartners, embedLaunches, embedLaunchesRelations;
|
|
20899
20899
|
var init_table8 = __esm(() => {
|
|
20900
20900
|
init_drizzle_orm();
|
|
20901
20901
|
init_pg_core();
|
|
20902
20902
|
init_table5();
|
|
20903
20903
|
init_table3();
|
|
20904
|
+
embedContextRoleEnum = pgEnum("embed_context_role", ["host"]);
|
|
20904
20905
|
embedPartners = pgTable("embed_partners", {
|
|
20905
20906
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
20906
20907
|
name: text("name").notNull(),
|
|
@@ -20917,6 +20918,8 @@ var init_table8 = __esm(() => {
|
|
|
20917
20918
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
20918
20919
|
parentOrigin: text("parent_origin").notNull(),
|
|
20919
20920
|
timebackCourseId: text("timeback_course_id"),
|
|
20921
|
+
contextId: text("context_id"),
|
|
20922
|
+
contextRole: embedContextRoleEnum("context_role"),
|
|
20920
20923
|
passHash: text("pass_hash").unique(),
|
|
20921
20924
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
20922
20925
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -20956,6 +20959,7 @@ __export(exports_tables_index, {
|
|
|
20956
20959
|
deploymentProviderEnum: () => deploymentProviderEnum,
|
|
20957
20960
|
deploymentTargetEnum: () => deploymentTargetEnum,
|
|
20958
20961
|
developerStatusEnum: () => developerStatusEnum,
|
|
20962
|
+
embedContextRoleEnum: () => embedContextRoleEnum,
|
|
20959
20963
|
embedLaunches: () => embedLaunches,
|
|
20960
20964
|
embedLaunchesRelations: () => embedLaunchesRelations,
|
|
20961
20965
|
embedPartners: () => embedPartners,
|
|
@@ -50823,13 +50827,21 @@ function isCanonicalOrigin(value) {
|
|
|
50823
50827
|
}
|
|
50824
50828
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
50825
50829
|
}
|
|
50826
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50830
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedContextSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
50827
50831
|
var init_schemas5 = __esm(() => {
|
|
50828
50832
|
init_esm();
|
|
50833
|
+
init_table8();
|
|
50829
50834
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
50830
50835
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
50831
50836
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
50832
50837
|
});
|
|
50838
|
+
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
50839
|
+
message: "context.id must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
50840
|
+
});
|
|
50841
|
+
EmbedContextSchema = objectType({
|
|
50842
|
+
id: EmbedContextIdSchema,
|
|
50843
|
+
role: enumType(embedContextRoleEnum.enumValues).optional()
|
|
50844
|
+
}).strict();
|
|
50833
50845
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
50834
50846
|
EnsureEmbedPartnerSchema = objectType({
|
|
50835
50847
|
name: stringType().trim().min(1).max(100)
|
|
@@ -50841,7 +50853,8 @@ var init_schemas5 = __esm(() => {
|
|
|
50841
50853
|
timebackId: stringType().trim().min(1).max(200),
|
|
50842
50854
|
gameSlug: stringType().trim().min(1).max(100),
|
|
50843
50855
|
parentOrigin: EmbedOriginSchema,
|
|
50844
|
-
timebackCourseId: stringType().trim().min(1).max(200).optional()
|
|
50856
|
+
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
50857
|
+
context: EmbedContextSchema.optional()
|
|
50845
50858
|
}).strict();
|
|
50846
50859
|
RedeemEmbedPassSchema = objectType({
|
|
50847
50860
|
pass: stringType().min(1).max(200),
|
|
@@ -95683,6 +95696,21 @@ var init_embed_util = __esm(() => {
|
|
|
95683
95696
|
});
|
|
95684
95697
|
|
|
95685
95698
|
// ../api-core/src/services/embed.service.ts
|
|
95699
|
+
async function scopedContextId(partnerId, contextId) {
|
|
95700
|
+
return sha256Hex(`${partnerId}:${contextId}`);
|
|
95701
|
+
}
|
|
95702
|
+
function launchTokenClaims(launch) {
|
|
95703
|
+
if (!launch.contextId) {
|
|
95704
|
+
return {};
|
|
95705
|
+
}
|
|
95706
|
+
return {
|
|
95707
|
+
context: {
|
|
95708
|
+
id: launch.contextId,
|
|
95709
|
+
...launch.contextRole && { role: launch.contextRole }
|
|
95710
|
+
}
|
|
95711
|
+
};
|
|
95712
|
+
}
|
|
95713
|
+
|
|
95686
95714
|
class EmbedService {
|
|
95687
95715
|
deps;
|
|
95688
95716
|
constructor(deps) {
|
|
@@ -95726,6 +95754,8 @@ class EmbedService {
|
|
|
95726
95754
|
gameId: game.id,
|
|
95727
95755
|
parentOrigin: input.parentOrigin,
|
|
95728
95756
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
95757
|
+
contextId: input.context ? await scopedContextId(partner.id, input.context.id) : null,
|
|
95758
|
+
contextRole: input.context?.role ?? null,
|
|
95729
95759
|
passHash: await sha256Hex(pass),
|
|
95730
95760
|
passExpiresAt,
|
|
95731
95761
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -95733,7 +95763,12 @@ class EmbedService {
|
|
|
95733
95763
|
if (!launch) {
|
|
95734
95764
|
throw new InternalError("Failed to create embed launch");
|
|
95735
95765
|
}
|
|
95736
|
-
setAttributes({
|
|
95766
|
+
setAttributes({
|
|
95767
|
+
"app.launch.id": launch.id,
|
|
95768
|
+
"app.user.id": user.id,
|
|
95769
|
+
"app.embed.has_context": input.context !== undefined,
|
|
95770
|
+
...input.context?.role && { "app.embed.context_role": input.context.role }
|
|
95771
|
+
});
|
|
95737
95772
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
95738
95773
|
}
|
|
95739
95774
|
async redeemLaunch(input, timeZone) {
|
|
@@ -95748,7 +95783,7 @@ class EmbedService {
|
|
|
95748
95783
|
const [game, profile, token] = await Promise.all([
|
|
95749
95784
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
95750
95785
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
95751
|
-
this.deps.mintGameToken(launch.gameId, user.id)
|
|
95786
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
95752
95787
|
]);
|
|
95753
95788
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
95754
95789
|
return {
|
|
@@ -95780,7 +95815,7 @@ class EmbedService {
|
|
|
95780
95815
|
if (outcome) {
|
|
95781
95816
|
throw new AccessDeniedError("This launch has ended");
|
|
95782
95817
|
}
|
|
95783
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId);
|
|
95818
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
95784
95819
|
}
|
|
95785
95820
|
async endLaunch(launchId) {
|
|
95786
95821
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -96882,10 +96917,10 @@ function createSandboxAuthProvider() {
|
|
|
96882
96917
|
}
|
|
96883
96918
|
return deleted;
|
|
96884
96919
|
},
|
|
96885
|
-
async mintGameToken(gameId, userId) {
|
|
96920
|
+
async mintGameToken(gameId, userId, claims) {
|
|
96886
96921
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
96887
96922
|
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
96888
|
-
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
96923
|
+
const payload = btoa(JSON.stringify({ ...claims, sub: gameId, uid: userId, exp }));
|
|
96889
96924
|
const token = `${header}.${payload}.sandbox`;
|
|
96890
96925
|
return { token, exp: exp * 1000 };
|
|
96891
96926
|
},
|
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.6",
|
|
701
701
|
description: "Local development server for Playcademy game development",
|
|
702
702
|
type: "module",
|
|
703
703
|
exports: {
|
|
@@ -19075,12 +19075,13 @@ var init_table7 = __esm(() => {
|
|
|
19075
19075
|
});
|
|
19076
19076
|
|
|
19077
19077
|
// ../data/src/domains/embed/table.ts
|
|
19078
|
-
var embedPartners, embedLaunches, embedLaunchesRelations;
|
|
19078
|
+
var embedContextRoleEnum, embedPartners, embedLaunches, embedLaunchesRelations;
|
|
19079
19079
|
var init_table8 = __esm(() => {
|
|
19080
19080
|
init_drizzle_orm();
|
|
19081
19081
|
init_pg_core();
|
|
19082
19082
|
init_table5();
|
|
19083
19083
|
init_table3();
|
|
19084
|
+
embedContextRoleEnum = pgEnum("embed_context_role", ["host"]);
|
|
19084
19085
|
embedPartners = pgTable("embed_partners", {
|
|
19085
19086
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
19086
19087
|
name: text("name").notNull(),
|
|
@@ -19097,6 +19098,8 @@ var init_table8 = __esm(() => {
|
|
|
19097
19098
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
19098
19099
|
parentOrigin: text("parent_origin").notNull(),
|
|
19099
19100
|
timebackCourseId: text("timeback_course_id"),
|
|
19101
|
+
contextId: text("context_id"),
|
|
19102
|
+
contextRole: embedContextRoleEnum("context_role"),
|
|
19100
19103
|
passHash: text("pass_hash").unique(),
|
|
19101
19104
|
passExpiresAt: timestamp("pass_expires_at", { withTimezone: true }).notNull(),
|
|
19102
19105
|
redeemedAt: timestamp("redeemed_at", { withTimezone: true }),
|
|
@@ -19136,6 +19139,7 @@ __export(exports_tables_index, {
|
|
|
19136
19139
|
deploymentProviderEnum: () => deploymentProviderEnum,
|
|
19137
19140
|
deploymentTargetEnum: () => deploymentTargetEnum,
|
|
19138
19141
|
developerStatusEnum: () => developerStatusEnum,
|
|
19142
|
+
embedContextRoleEnum: () => embedContextRoleEnum,
|
|
19139
19143
|
embedLaunches: () => embedLaunches,
|
|
19140
19144
|
embedLaunchesRelations: () => embedLaunchesRelations,
|
|
19141
19145
|
embedPartners: () => embedPartners,
|
|
@@ -49003,13 +49007,21 @@ function isCanonicalOrigin(value) {
|
|
|
49003
49007
|
}
|
|
49004
49008
|
return url.protocol === "https:" || url.protocol === "http:" && isLocalHostname(url.hostname);
|
|
49005
49009
|
}
|
|
49006
|
-
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49010
|
+
var LOCAL_HOSTNAMES, EmbedOriginSchema, EmbedContextIdSchema, EmbedContextSchema, EmbedPartnerClientIdSchema, EnsureEmbedPartnerSchema, AddEmbedPartnerOriginSchema, CreateEmbedLaunchSchema, RedeemEmbedPassSchema;
|
|
49007
49011
|
var init_schemas5 = __esm(() => {
|
|
49008
49012
|
init_esm();
|
|
49013
|
+
init_table8();
|
|
49009
49014
|
LOCAL_HOSTNAMES = new Set(["localhost", "127.0.0.1"]);
|
|
49010
49015
|
EmbedOriginSchema = stringType().refine(isCanonicalOrigin, {
|
|
49011
49016
|
message: "Origin must be a bare https origin like https://example.com, with no path or slash"
|
|
49012
49017
|
});
|
|
49018
|
+
EmbedContextIdSchema = stringType().regex(/^[A-Za-z0-9._~-]{1,128}$/, {
|
|
49019
|
+
message: "context.id must be 1-128 URL-safe characters (letters, digits, . _ ~ -)"
|
|
49020
|
+
});
|
|
49021
|
+
EmbedContextSchema = objectType({
|
|
49022
|
+
id: EmbedContextIdSchema,
|
|
49023
|
+
role: enumType(embedContextRoleEnum.enumValues).optional()
|
|
49024
|
+
}).strict();
|
|
49013
49025
|
EmbedPartnerClientIdSchema = stringType().trim().min(1).max(200);
|
|
49014
49026
|
EnsureEmbedPartnerSchema = objectType({
|
|
49015
49027
|
name: stringType().trim().min(1).max(100)
|
|
@@ -49021,7 +49033,8 @@ var init_schemas5 = __esm(() => {
|
|
|
49021
49033
|
timebackId: stringType().trim().min(1).max(200),
|
|
49022
49034
|
gameSlug: stringType().trim().min(1).max(100),
|
|
49023
49035
|
parentOrigin: EmbedOriginSchema,
|
|
49024
|
-
timebackCourseId: stringType().trim().min(1).max(200).optional()
|
|
49036
|
+
timebackCourseId: stringType().trim().min(1).max(200).optional(),
|
|
49037
|
+
context: EmbedContextSchema.optional()
|
|
49025
49038
|
}).strict();
|
|
49026
49039
|
RedeemEmbedPassSchema = objectType({
|
|
49027
49040
|
pass: stringType().min(1).max(200),
|
|
@@ -93863,6 +93876,21 @@ var init_embed_util = __esm(() => {
|
|
|
93863
93876
|
});
|
|
93864
93877
|
|
|
93865
93878
|
// ../api-core/src/services/embed.service.ts
|
|
93879
|
+
async function scopedContextId(partnerId, contextId) {
|
|
93880
|
+
return sha256Hex(`${partnerId}:${contextId}`);
|
|
93881
|
+
}
|
|
93882
|
+
function launchTokenClaims(launch) {
|
|
93883
|
+
if (!launch.contextId) {
|
|
93884
|
+
return {};
|
|
93885
|
+
}
|
|
93886
|
+
return {
|
|
93887
|
+
context: {
|
|
93888
|
+
id: launch.contextId,
|
|
93889
|
+
...launch.contextRole && { role: launch.contextRole }
|
|
93890
|
+
}
|
|
93891
|
+
};
|
|
93892
|
+
}
|
|
93893
|
+
|
|
93866
93894
|
class EmbedService {
|
|
93867
93895
|
deps;
|
|
93868
93896
|
constructor(deps) {
|
|
@@ -93906,6 +93934,8 @@ class EmbedService {
|
|
|
93906
93934
|
gameId: game.id,
|
|
93907
93935
|
parentOrigin: input.parentOrigin,
|
|
93908
93936
|
timebackCourseId: input.timebackCourseId ?? null,
|
|
93937
|
+
contextId: input.context ? await scopedContextId(partner.id, input.context.id) : null,
|
|
93938
|
+
contextRole: input.context?.role ?? null,
|
|
93909
93939
|
passHash: await sha256Hex(pass),
|
|
93910
93940
|
passExpiresAt,
|
|
93911
93941
|
expiresAt: new Date(now + EMBED_LAUNCH_TTL_MS)
|
|
@@ -93913,7 +93943,12 @@ class EmbedService {
|
|
|
93913
93943
|
if (!launch) {
|
|
93914
93944
|
throw new InternalError("Failed to create embed launch");
|
|
93915
93945
|
}
|
|
93916
|
-
setAttributes({
|
|
93946
|
+
setAttributes({
|
|
93947
|
+
"app.launch.id": launch.id,
|
|
93948
|
+
"app.user.id": user.id,
|
|
93949
|
+
"app.embed.has_context": input.context !== undefined,
|
|
93950
|
+
...input.context?.role && { "app.embed.context_role": input.context.role }
|
|
93951
|
+
});
|
|
93917
93952
|
return { launchId: launch.id, pass, passExpiresAt, gameSlug: game.slug };
|
|
93918
93953
|
}
|
|
93919
93954
|
async redeemLaunch(input, timeZone) {
|
|
@@ -93928,7 +93963,7 @@ class EmbedService {
|
|
|
93928
93963
|
const [game, profile, token] = await Promise.all([
|
|
93929
93964
|
this.deps.getGameForStudent(launch.gameId, user),
|
|
93930
93965
|
this.deps.getGameProfile(user, launch.gameId, timeZone),
|
|
93931
|
-
this.deps.mintGameToken(launch.gameId, user.id)
|
|
93966
|
+
this.deps.mintGameToken(launch.gameId, user.id, launchTokenClaims(launch))
|
|
93932
93967
|
]);
|
|
93933
93968
|
await this.deps.db.update(embedLaunches).set({ redeemedAt: new Date }).where(eq(embedLaunches.id, launch.id));
|
|
93934
93969
|
return {
|
|
@@ -93960,7 +93995,7 @@ class EmbedService {
|
|
|
93960
93995
|
if (outcome) {
|
|
93961
93996
|
throw new AccessDeniedError("This launch has ended");
|
|
93962
93997
|
}
|
|
93963
|
-
return this.deps.mintGameToken(launch.gameId, launch.userId);
|
|
93998
|
+
return this.deps.mintGameToken(launch.gameId, launch.userId, launchTokenClaims(launch));
|
|
93964
93999
|
}
|
|
93965
94000
|
async endLaunch(launchId) {
|
|
93966
94001
|
await this.deps.db.update(embedLaunches).set({ endedAt: sql`coalesce(${embedLaunches.endedAt}, now())`, passHash: null }).where(eq(embedLaunches.id, launchId));
|
|
@@ -95062,10 +95097,10 @@ function createSandboxAuthProvider() {
|
|
|
95062
95097
|
}
|
|
95063
95098
|
return deleted;
|
|
95064
95099
|
},
|
|
95065
|
-
async mintGameToken(gameId, userId) {
|
|
95100
|
+
async mintGameToken(gameId, userId, claims) {
|
|
95066
95101
|
const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
|
|
95067
95102
|
const exp = Math.floor(Date.now() / 1000) + 15 * 60;
|
|
95068
|
-
const payload = btoa(JSON.stringify({ sub: gameId, uid: userId, exp }));
|
|
95103
|
+
const payload = btoa(JSON.stringify({ ...claims, sub: gameId, uid: userId, exp }));
|
|
95069
95104
|
const token = `${header}.${payload}.sandbox`;
|
|
95070
95105
|
return { token, exp: exp * 1000 };
|
|
95071
95106
|
},
|