playcademy 0.28.1-beta.2 → 0.28.1-beta.3

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/bucket.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { GamePermission } from '@playcademy/constants';
1
2
  import { TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/types/timeback';
2
3
  import { Miniflare } from 'miniflare';
3
4
 
@@ -148,6 +149,13 @@ interface PlaycademyConfig {
148
149
  description?: string;
149
150
  /** Game emoji icon */
150
151
  emoji?: string;
152
+ /**
153
+ * Browser permissions to request for the game's iframe (e.g.
154
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
155
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
156
+ * every game automatically.
157
+ */
158
+ permissions?: GamePermission[];
151
159
  /** Build command to run before deployment */
152
160
  buildCommand?: string[];
153
161
  /** Path to build output */
package/dist/bucket.js CHANGED
@@ -44,7 +44,7 @@ var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
44
44
  // ../better-auth/package.json
45
45
  var package_default = {
46
46
  name: "@playcademy/better-auth",
47
- version: "0.0.21-beta.2",
47
+ version: "0.0.21-beta.3",
48
48
  type: "module",
49
49
  exports: {
50
50
  "./server": {
package/dist/cli.js CHANGED
@@ -1207,7 +1207,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
1207
1207
  // ../better-auth/package.json
1208
1208
  var package_default = {
1209
1209
  name: "@playcademy/better-auth",
1210
- version: "0.0.21-beta.2",
1210
+ version: "0.0.21-beta.3",
1211
1211
  type: "module",
1212
1212
  exports: {
1213
1213
  "./server": {
@@ -3009,7 +3009,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as re
3009
3009
  import { join as join13 } from "node:path";
3010
3010
 
3011
3011
  // src/version.ts
3012
- var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.2";
3012
+ var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.3";
3013
3013
 
3014
3014
  // src/lib/init/database.ts
3015
3015
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
package/dist/constants.js CHANGED
@@ -21,7 +21,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
21
21
  // ../better-auth/package.json
22
22
  var package_default = {
23
23
  name: "@playcademy/better-auth",
24
- version: "0.0.21-beta.2",
24
+ version: "0.0.21-beta.3",
25
25
  type: "module",
26
26
  exports: {
27
27
  "./server": {
package/dist/db.js CHANGED
@@ -2335,7 +2335,7 @@ var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
2335
2335
  // ../better-auth/package.json
2336
2336
  var package_default = {
2337
2337
  name: "@playcademy/better-auth",
2338
- version: "0.0.21-beta.2",
2338
+ version: "0.0.21-beta.3",
2339
2339
  type: "module",
2340
2340
  exports: {
2341
2341
  "./server": {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { ApiKeyRow } from '@playcademy/data/types';
2
2
  import { UserInfo } from '@playcademy/types/user';
3
3
  import { SchemaInfo } from '@playcademy/cloudflare';
4
+ import { GamePermission } from '@playcademy/constants';
4
5
  import { TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/types/timeback';
5
6
  export { ComponentConfig, ComponentResourceConfig, OrganizationConfig, ResourceConfig, TimebackCourseConfig, TimebackGrade, TimebackSourcedIds, TimebackSubject } from '@playcademy/types/timeback';
6
7
  import { PlaycademyClient } from '@playcademy/sdk/internal';
@@ -434,6 +435,13 @@ interface PlaycademyConfig {
434
435
  description?: string;
435
436
  /** Game emoji icon */
436
437
  emoji?: string;
438
+ /**
439
+ * Browser permissions to request for the game's iframe (e.g.
440
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
441
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
442
+ * every game automatically.
443
+ */
444
+ permissions?: GamePermission[];
437
445
  /** Build command to run before deployment */
438
446
  buildCommand?: string[];
439
447
  /** Path to build output */
@@ -485,6 +493,8 @@ interface WorkerDeploymentBundle {
485
493
  interface GameMetadata {
486
494
  description?: string;
487
495
  emoji?: string;
496
+ /** Browser permissions delegated to the game's iframe (opt-in per game). */
497
+ permissions?: GamePermission[];
488
498
  [key: string]: unknown;
489
499
  }
490
500
  declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
@@ -1079,6 +1089,13 @@ interface DeployConfig {
1079
1089
  description?: string;
1080
1090
  /** Emoji icon for the project */
1081
1091
  emoji?: string;
1092
+ /**
1093
+ * Browser permissions the game's iframe should be granted (e.g.
1094
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
1095
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
1096
+ * every game automatically.
1097
+ */
1098
+ permissions?: GamePermission[];
1082
1099
  /** Path to the build directory or zip file */
1083
1100
  buildPath?: string;
1084
1101
  /** Game platform (defaults to 'web') */
@@ -1291,6 +1308,10 @@ interface ConfigDiff {
1291
1308
  old: string | undefined;
1292
1309
  new: string | undefined;
1293
1310
  };
1311
+ permissions?: {
1312
+ old: string[] | undefined;
1313
+ new: string[] | undefined;
1314
+ };
1294
1315
  gameType?: {
1295
1316
  old: string;
1296
1317
  new: string;
package/dist/index.js CHANGED
@@ -2346,7 +2346,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
2346
2346
  // ../better-auth/package.json
2347
2347
  var package_default = {
2348
2348
  name: "@playcademy/better-auth",
2349
- version: "0.0.21-beta.2",
2349
+ version: "0.0.21-beta.3",
2350
2350
  type: "module",
2351
2351
  exports: {
2352
2352
  "./server": {
@@ -5873,7 +5873,13 @@ function buildDeployGameMetadataPayload(config) {
5873
5873
  gameType: "hosted",
5874
5874
  metadata: {
5875
5875
  ...config.description !== void 0 && { description: config.description },
5876
- ...config.emoji !== void 0 && { emoji: config.emoji }
5876
+ ...config.emoji !== void 0 && { emoji: config.emoji },
5877
+ // Empty and absent both mean "no permissions declared", so an empty
5878
+ // array is omitted rather than stored: it keeps the payload aligned
5879
+ // with the empty-is-absent rule the deploy diff uses (diff.ts), so a
5880
+ // game with `permissions: []` never drifts against a platform that
5881
+ // has no permissions key.
5882
+ ...config.permissions !== void 0 && config.permissions.length > 0 && { permissions: config.permissions }
5877
5883
  }
5878
5884
  };
5879
5885
  }
@@ -6390,7 +6396,7 @@ import { existsSync as existsSync10, mkdirSync as mkdirSync2, readFileSync as re
6390
6396
  import { join as join13 } from "node:path";
6391
6397
 
6392
6398
  // src/version.ts
6393
- var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.2";
6399
+ var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.3";
6394
6400
 
6395
6401
  // src/lib/init/database.ts
6396
6402
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -21179,6 +21185,7 @@ function transformToDeployConfig(userConfig) {
21179
21185
  displayName: userConfig.name,
21180
21186
  description: userConfig.description,
21181
21187
  emoji: userConfig.emoji,
21188
+ permissions: userConfig.permissions,
21182
21189
  buildPath: userConfig.buildPath,
21183
21190
  platform: userConfig.platform
21184
21191
  };
@@ -21761,25 +21768,40 @@ var GAME_METADATA_DRIFT_LABELS = {
21761
21768
  platform: "Platform",
21762
21769
  metadataPrefix: "metadata."
21763
21770
  };
21771
+ var RESOLVABLE_METADATA_KEYS = /* @__PURE__ */ new Set(["description", "emoji"]);
21772
+ function isResolvableDriftLabel(label) {
21773
+ if (label === GAME_METADATA_DRIFT_LABELS.displayName) {
21774
+ return true;
21775
+ }
21776
+ if (label === GAME_METADATA_DRIFT_LABELS.platform) {
21777
+ return true;
21778
+ }
21779
+ if (!label.startsWith(GAME_METADATA_DRIFT_LABELS.metadataPrefix)) {
21780
+ return false;
21781
+ }
21782
+ return RESOLVABLE_METADATA_KEYS.has(
21783
+ label.slice(GAME_METADATA_DRIFT_LABELS.metadataPrefix.length)
21784
+ );
21785
+ }
21764
21786
  function metadataValuesEqual(a, b) {
21765
21787
  if (Object.is(a, b)) {
21766
21788
  return true;
21767
21789
  }
21768
21790
  if (typeof a === "object" && a !== null && typeof b === "object" && b !== null) {
21769
- return JSON.stringify(sortJsonKeys(a)) === JSON.stringify(sortJsonKeys(b));
21791
+ return JSON.stringify(canonicalizeJson(a)) === JSON.stringify(canonicalizeJson(b));
21770
21792
  }
21771
21793
  return false;
21772
21794
  }
21773
- function sortJsonKeys(value) {
21795
+ function canonicalizeJson(value) {
21774
21796
  if (value === null || typeof value !== "object") {
21775
21797
  return value;
21776
21798
  }
21777
21799
  if (Array.isArray(value)) {
21778
- return value.map(sortJsonKeys);
21800
+ return value.map(canonicalizeJson).toSorted((x, y) => JSON.stringify(x) < JSON.stringify(y) ? -1 : 1);
21779
21801
  }
21780
21802
  const sorted = {};
21781
21803
  for (const key of Object.keys(value).toSorted()) {
21782
- sorted[key] = sortJsonKeys(value[key]);
21804
+ sorted[key] = canonicalizeJson(value[key]);
21783
21805
  }
21784
21806
  return sorted;
21785
21807
  }
@@ -21826,6 +21848,23 @@ function skipsDeployGameDueToSecretsOnly(context2, plan) {
21826
21848
 
21827
21849
  // src/lib/deploy/game/diff.ts
21828
21850
  import { dim as dim8, green as green7, red as red4, yellow as yellow5 } from "colorette";
21851
+ function toPermissionList(value) {
21852
+ if (!Array.isArray(value)) {
21853
+ return void 0;
21854
+ }
21855
+ return value.filter((entry) => typeof entry === "string");
21856
+ }
21857
+ function formatPermissionList(value) {
21858
+ return value && value.length > 0 ? value.join(", ") : "(none)";
21859
+ }
21860
+ function permissionListsEqual(a, b) {
21861
+ const left = a ?? [];
21862
+ const right = b ?? [];
21863
+ if (left.length !== right.length) {
21864
+ return false;
21865
+ }
21866
+ return left.toSorted().join(",") === right.toSorted().join(",");
21867
+ }
21829
21868
  function calculateConfigDiff(existingGame, newConfig) {
21830
21869
  const diff = {};
21831
21870
  if (newConfig.displayName && existingGame.displayName !== newConfig.displayName) {
@@ -21837,6 +21876,11 @@ function calculateConfigDiff(existingGame, newConfig) {
21837
21876
  if (existingGame.metadata.description !== newConfig.description) {
21838
21877
  diff.description = { old: existingGame.metadata.description, new: newConfig.description };
21839
21878
  }
21879
+ const existingPermissions = toPermissionList(existingGame.metadata.permissions);
21880
+ const newPermissions = newConfig.permissions;
21881
+ if (!permissionListsEqual(existingPermissions, newPermissions)) {
21882
+ diff.permissions = { old: existingPermissions, new: newPermissions };
21883
+ }
21840
21884
  if (newConfig.gameType && existingGame.gameType !== newConfig.gameType) {
21841
21885
  diff.gameType = { old: existingGame.gameType, new: newConfig.gameType };
21842
21886
  }
@@ -22110,7 +22154,10 @@ function displayIntegrationAndSecretsChanges(integrations, secrets, secretsChang
22110
22154
  }
22111
22155
  function displayConfigSection(diff, driftLabels) {
22112
22156
  function driftHint(label) {
22113
- return driftLabels?.has(label) ? ` ${red4("[differs from platform]")}` : "";
22157
+ if (!driftLabels?.has(label) || !isResolvableDriftLabel(label)) {
22158
+ return "";
22159
+ }
22160
+ return ` ${red4("[differs from platform]")}`;
22114
22161
  }
22115
22162
  logger.bold("Config", 1);
22116
22163
  if (diff.displayName) {
@@ -22134,6 +22181,13 @@ function displayConfigSection(diff, driftLabels) {
22134
22181
  2
22135
22182
  );
22136
22183
  }
22184
+ if (diff.permissions) {
22185
+ logger.data(
22186
+ "Permissions",
22187
+ `${red4(formatPermissionList(diff.permissions.old))} \u2192 ${green7(formatPermissionList(diff.permissions.new))}${driftHint(`${GAME_METADATA_DRIFT_LABELS.metadataPrefix}permissions`)}`,
22188
+ 2
22189
+ );
22190
+ }
22137
22191
  logger.newLine();
22138
22192
  }
22139
22193
  function displayDeploymentDiff(options) {
@@ -23097,7 +23151,6 @@ async function promptGameMetadataDriftDecision(args) {
23097
23151
  nonInteractiveHint: NON_INTERACTIVE_HINTS.GAME_METADATA_DRIFT
23098
23152
  });
23099
23153
  }
23100
- var AUTO_RESOLVABLE_METADATA_KEYS = /* @__PURE__ */ new Set(["description", "emoji"]);
23101
23154
  async function applyPlatformValuesToLocalConfig(args) {
23102
23155
  const { context: context2, rows, remoteGame } = args;
23103
23156
  if (!context2.configFileName) {
@@ -23158,7 +23211,7 @@ function classifyDriftRow(row, remoteGame, writeDisplayName) {
23158
23211
  return { kind: "skip" };
23159
23212
  }
23160
23213
  const key = row.label.slice(GAME_METADATA_DRIFT_LABELS.metadataPrefix.length);
23161
- if (!AUTO_RESOLVABLE_METADATA_KEYS.has(key)) {
23214
+ if (!RESOLVABLE_METADATA_KEYS.has(key)) {
23162
23215
  return { kind: "unresolvable" };
23163
23216
  }
23164
23217
  const remoteValue = remoteGame.metadata?.[key];
@@ -28660,6 +28713,7 @@ export {
28660
28713
  NonInteractivePromptError,
28661
28714
  OPTIONAL_FIELDS,
28662
28715
  REQUIRED_FIELDS,
28716
+ RESOLVABLE_METADATA_KEYS,
28663
28717
  addEmailProvider,
28664
28718
  addOAuthProvider,
28665
28719
  addPlaycademySdk,
@@ -28919,6 +28973,7 @@ export {
28919
28973
  isPluginConfigured,
28920
28974
  isPushAdopted,
28921
28975
  isRecord3 as isRecord,
28976
+ isResolvableDriftLabel,
28922
28977
  isSchemaAdopted,
28923
28978
  isStrategyExplicit,
28924
28979
  isVitePluginInstalled,
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.28.1-beta.2",
3
- "sdkVersion": "0.16.1-beta.2",
4
- "runtimeBuildId": "ae26f3f7d729",
5
- "inputFingerprint": "ae26f3f7d7295446ac2eeefeefd3796000b0ff85798ee067d55becd93088a976",
2
+ "cliVersion": "0.28.1-beta.3",
3
+ "sdkVersion": "0.16.1-beta.3",
4
+ "runtimeBuildId": "6ec9298f9df4",
5
+ "inputFingerprint": "6ec9298f9df43628b86e5ccfb039daee7cf4078d8543616aa0c436512c911964",
6
6
  "entry": "index.js"
7
7
  }
@@ -6510,9 +6510,9 @@ var init_list = __esm({
6510
6510
  // ../edge-play/src/game/lib/metadata.ts
6511
6511
  function getRuntimeMetadata() {
6512
6512
  return {
6513
- cliVersion: true ? "0.28.1-beta.2" : "0.0.0-dev",
6514
- sdkVersion: true ? "0.16.1-beta.2" : "0.0.0-dev",
6515
- buildId: true ? "ae26f3f7d729" : "dev-source"
6513
+ cliVersion: true ? "0.28.1-beta.3" : "0.0.0-dev",
6514
+ sdkVersion: true ? "0.16.1-beta.3" : "0.0.0-dev",
6515
+ buildId: true ? "6ec9298f9df4" : "dev-source"
6516
6516
  };
6517
6517
  }
6518
6518
  var init_metadata = __esm({
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.28.1-beta.2",
3
- "sdkVersion": "0.16.1-beta.2",
4
- "runtimeBuildId": "ae26f3f7d729",
5
- "inputFingerprint": "ae26f3f7d7295446ac2eeefeefd3796000b0ff85798ee067d55becd93088a976",
2
+ "cliVersion": "0.28.1-beta.3",
3
+ "sdkVersion": "0.16.1-beta.3",
4
+ "runtimeBuildId": "6ec9298f9df4",
5
+ "inputFingerprint": "6ec9298f9df43628b86e5ccfb039daee7cf4078d8543616aa0c436512c911964",
6
6
  "entry": "index.js"
7
7
  }
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,7 @@
1
+ import { GamePermission, DashboardUserRole } from '@playcademy/constants';
1
2
  import { TimebackCourseConfig, CourseConfig, OrganizationConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/types/timeback';
2
3
  import { Miniflare } from 'miniflare';
3
4
  import * as chokidar from 'chokidar';
4
- import { DashboardUserRole } from '@playcademy/constants';
5
5
  import { PackageManager } from '@playcademy/utils/package-manager';
6
6
  import * as esbuild from 'esbuild';
7
7
 
@@ -152,6 +152,13 @@ interface PlaycademyConfig {
152
152
  description?: string;
153
153
  /** Game emoji icon */
154
154
  emoji?: string;
155
+ /**
156
+ * Browser permissions to request for the game's iframe (e.g.
157
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
158
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
159
+ * every game automatically.
160
+ */
161
+ permissions?: GamePermission[];
155
162
  /** Build command to run before deployment */
156
163
  buildCommand?: string[];
157
164
  /** Path to build output */
package/dist/utils.js CHANGED
@@ -383,7 +383,7 @@ var DASHBOARD_API_ROUTES_DIRECTORY = "api";
383
383
  // ../better-auth/package.json
384
384
  var package_default = {
385
385
  name: "@playcademy/better-auth",
386
- version: "0.0.21-beta.2",
386
+ version: "0.0.21-beta.3",
387
387
  type: "module",
388
388
  exports: {
389
389
  "./server": {
@@ -2795,7 +2795,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as wr
2795
2795
  import { dirname as dirname4, join as join14 } from "node:path";
2796
2796
 
2797
2797
  // src/version.ts
2798
- var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.2";
2798
+ var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.3";
2799
2799
 
2800
2800
  // src/lib/build/binary-resource.ts
2801
2801
  function writeFileTree(baseDir, files) {
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.2";
2
+ var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.3";
3
3
  export {
4
4
  cliVersion
5
5
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playcademy",
3
- "version": "0.28.1-beta.2",
3
+ "version": "0.28.1-beta.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@inquirer/prompts": "^7.8.6",
58
- "@playcademy/sdk": "0.16.1-beta.2",
58
+ "@playcademy/sdk": "0.16.1-beta.3",
59
59
  "chokidar": "^4.0.3",
60
60
  "colorette": "^2.0.20",
61
61
  "commander": "^14.0.1",