playcademy 0.28.1-beta.2 → 0.28.1-beta.20

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/bin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
2
3
 
3
4
  // src/bin.ts
4
5
  import { spawnSync } from "node:child_process";
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
 
@@ -137,6 +138,19 @@ interface DashboardThemeConfig {
137
138
  /** Secondary/accent color as a hex string, e.g. '#ffd166' */
138
139
  secondary?: string;
139
140
  }
141
+ /**
142
+ * Child-catalog contract generation (see @playcademy/sdk/contracts).
143
+ * The extractor is a repo-local script (run with the CLI's runtime) that
144
+ * prints `{ generatedFrom, deliveries }` as JSON on stdout; the CLI
145
+ * validates, stamps the contract fields, and writes
146
+ * `.playcademy/catalog.json` deterministically. The document carries no
147
+ * app identity — consuming repos assign the namespace key from sync
148
+ * provenance.
149
+ */
150
+ interface CatalogConfig {
151
+ /** Path to the extractor script, relative to the config file. */
152
+ extractor: string;
153
+ }
140
154
  /**
141
155
  * Unified Playcademy configuration
142
156
  * Used for playcademy.config.{js,json}
@@ -148,6 +162,13 @@ interface PlaycademyConfig {
148
162
  description?: string;
149
163
  /** Game emoji icon */
150
164
  emoji?: string;
165
+ /**
166
+ * Browser permissions to request for the game's iframe (e.g.
167
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
168
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
169
+ * every game automatically.
170
+ */
171
+ permissions?: GamePermission[];
151
172
  /** Build command to run before deployment */
152
173
  buildCommand?: string[];
153
174
  /** Path to build output */
@@ -162,6 +183,8 @@ interface PlaycademyConfig {
162
183
  dashboard?: DashboardConfig | boolean;
163
184
  /** Integrations (database, custom routes, external services) */
164
185
  integrations?: IntegrationsConfig;
186
+ /** Child-catalog contract generation (`playcademy catalog`) */
187
+ catalog?: CatalogConfig;
165
188
  }
166
189
 
167
190
  /**
package/dist/bucket.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
1
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
2
3
  var __esm = (fn, res) => function __init() {
3
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -44,7 +45,7 @@ var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
44
45
  // ../better-auth/package.json
45
46
  var package_default = {
46
47
  name: "@playcademy/better-auth",
47
- version: "0.0.21-beta.2",
48
+ version: "0.0.21-beta.19",
48
49
  type: "module",
49
50
  exports: {
50
51
  "./server": {
@@ -87,18 +88,7 @@ var package_default = {
87
88
  // src/constants/auth.ts
88
89
  var BETTER_AUTH_VERSION = package_default.dependencies["better-auth"];
89
90
 
90
- // src/constants/config.ts
91
- var ENV_FILES = [
92
- ".env",
93
- // Loaded first
94
- ".env.development",
95
- // Overrides .env
96
- ".env.local"
97
- // Overrides all (highest priority)
98
- ];
99
-
100
91
  // src/constants/bucket.ts
101
- var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
102
92
  var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
103
93
 
104
94
  // src/constants/cloudflare.ts
@@ -282,11 +272,44 @@ var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES = [
282
272
  "text-entry",
283
273
  "order",
284
274
  "match",
285
- "hottext"
275
+ "hottext",
276
+ "hotspot",
277
+ "gap-match",
278
+ "graphic-gap-match",
279
+ "select-point",
280
+ "graphic-associate",
281
+ "position-object"
286
282
  ];
287
283
  var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPE_SET = new Set(
288
284
  PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES
289
285
  );
286
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES = [
287
+ "choice",
288
+ "inline-choice",
289
+ "text-entry",
290
+ "order",
291
+ "match",
292
+ "hottext",
293
+ "gap-match",
294
+ "hotspot",
295
+ "graphic-associate",
296
+ "graphic-gap-match"
297
+ ];
298
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPE_SET = new Set(
299
+ PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES
300
+ );
301
+ var QTI_RASTER_IMAGE_MIME_TYPES = [
302
+ "image/png",
303
+ "image/jpeg",
304
+ // Some browsers and operating systems still report the legacy jpg spelling.
305
+ "image/jpg",
306
+ "image/gif",
307
+ "image/webp",
308
+ "image/bmp"
309
+ ];
310
+ var QTI_RASTER_IMAGE_SUBTYPE_PATTERN = QTI_RASTER_IMAGE_MIME_TYPES.map(
311
+ (type) => type.slice("image/".length)
312
+ ).join("|");
290
313
  var TIMEBACK_DISCREPANCY_QUEUE_WINDOW_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_WINDOWS);
291
314
  var TIMEBACK_DISCREPANCY_QUEUE_METRIC_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_METRICS);
292
315
 
package/dist/cli.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
4
  var __esm = (fn, res) => function __init() {
@@ -514,7 +515,10 @@ function nestColor(content, color2) {
514
515
  function customTransform(text) {
515
516
  let result = text;
516
517
  result = result.replace(/<<([^>]+)>>/g, (_, url) => blueBright(underline(url)));
517
- result = result.replace(/<([^>]+)>/g, (_, inner) => blueBright(`<${inner}>`));
518
+ result = result.replace(
519
+ /<([^>]+)>/g,
520
+ (match, inner) => blueBright(/[./]/.test(inner) ? inner : match)
521
+ );
518
522
  result = result.replace(/`([^`]+)`/g, (_, code) => nestColor(code, greenBright));
519
523
  return result;
520
524
  }
@@ -1207,7 +1211,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
1207
1211
  // ../better-auth/package.json
1208
1212
  var package_default = {
1209
1213
  name: "@playcademy/better-auth",
1210
- version: "0.0.21-beta.2",
1214
+ version: "0.0.21-beta.19",
1211
1215
  type: "module",
1212
1216
  exports: {
1213
1217
  "./server": {
@@ -1252,6 +1256,12 @@ var BETTER_AUTH_VERSION = package_default.dependencies["better-auth"];
1252
1256
  var PLAYCADEMY_AUTH_VERSION = "latest";
1253
1257
  var AUTH_CONFIG_FILE = "auth.ts";
1254
1258
 
1259
+ // src/constants/bucket.ts
1260
+ var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
1261
+
1262
+ // src/constants/cloudflare.ts
1263
+ var QUEUE_NAME_PREFIX = "playcademy";
1264
+
1255
1265
  // src/constants/config.ts
1256
1266
  var ENV_FILES = [
1257
1267
  ".env",
@@ -1277,13 +1287,6 @@ var TSCONFIG_REQUIRED_INCLUDES = [
1277
1287
  // Server-side code
1278
1288
  ];
1279
1289
 
1280
- // src/constants/bucket.ts
1281
- var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
1282
- var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
1283
-
1284
- // src/constants/cloudflare.ts
1285
- var QUEUE_NAME_PREFIX = "playcademy";
1286
-
1287
1290
  // src/constants/database.ts
1288
1291
  import { join as join4 } from "path";
1289
1292
  var DEFAULT_DATABASE_DIRECTORY = join4("server", "db");
@@ -2123,11 +2126,44 @@ var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES = [
2123
2126
  "text-entry",
2124
2127
  "order",
2125
2128
  "match",
2126
- "hottext"
2129
+ "hottext",
2130
+ "hotspot",
2131
+ "gap-match",
2132
+ "graphic-gap-match",
2133
+ "select-point",
2134
+ "graphic-associate",
2135
+ "position-object"
2127
2136
  ];
2128
2137
  var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPE_SET = new Set(
2129
2138
  PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES
2130
2139
  );
2140
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES = [
2141
+ "choice",
2142
+ "inline-choice",
2143
+ "text-entry",
2144
+ "order",
2145
+ "match",
2146
+ "hottext",
2147
+ "gap-match",
2148
+ "hotspot",
2149
+ "graphic-associate",
2150
+ "graphic-gap-match"
2151
+ ];
2152
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPE_SET = new Set(
2153
+ PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES
2154
+ );
2155
+ var QTI_RASTER_IMAGE_MIME_TYPES = [
2156
+ "image/png",
2157
+ "image/jpeg",
2158
+ // Some browsers and operating systems still report the legacy jpg spelling.
2159
+ "image/jpg",
2160
+ "image/gif",
2161
+ "image/webp",
2162
+ "image/bmp"
2163
+ ];
2164
+ var QTI_RASTER_IMAGE_SUBTYPE_PATTERN = QTI_RASTER_IMAGE_MIME_TYPES.map(
2165
+ (type) => type.slice("image/".length)
2166
+ ).join("|");
2131
2167
  var TIMEBACK_DISCREPANCY_QUEUE_WINDOW_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_WINDOWS);
2132
2168
  var TIMEBACK_DISCREPANCY_QUEUE_METRIC_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_METRICS);
2133
2169
 
@@ -3009,7 +3045,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as re
3009
3045
  import { join as join13 } from "node:path";
3010
3046
 
3011
3047
  // src/version.ts
3012
- var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.2";
3048
+ var cliVersion = false ? "0.0.0-dev" : "0.28.1-beta.20";
3013
3049
 
3014
3050
  // src/lib/init/database.ts
3015
3051
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -59,15 +59,18 @@ declare const AUTH_CONFIG_FILE = "auth.ts";
59
59
  * Bucket-related constants
60
60
  */
61
61
  /**
62
- * Files and directories that are always excluded from bucket sync uploads
62
+ * Files and directories that are always excluded from bucket uploads
63
63
  *
64
- * These are automatically skipped regardless of .gitignore patterns:
64
+ * These exact names are skipped regardless of .gitignore patterns:
65
65
  * - .git - Git repository data
66
66
  * - .DS_Store - macOS filesystem metadata
67
67
  * - .gitignore - Git ignore configuration (shouldn't be uploaded to storage)
68
- * - .env* - Environment files (should never be uploaded to storage)
68
+ *
69
+ * Environment files are excluded separately by the `.env*` name pattern in
70
+ * the collector, so every variant (`.env.production`, `.envrc`, ...) is
71
+ * covered, not just the ones the CLI loads.
69
72
  */
70
- declare const BUCKET_ALWAYS_SKIP: readonly [".git", ".DS_Store", ".gitignore", ".env", ".env.development", ".env.local"];
73
+ declare const BUCKET_ALWAYS_SKIP: readonly ['.git', '.DS_Store', '.gitignore'];
71
74
  declare const PRESIGNED_UPLOAD_THRESHOLD: number;
72
75
  /**
73
76
  * Default directory synced to the bucket by `playcademy bucket sync`.
package/dist/constants.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
2
+
1
3
  // src/constants/api.ts
2
4
  import { join as join2 } from "node:path";
3
5
 
@@ -21,7 +23,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
21
23
  // ../better-auth/package.json
22
24
  var package_default = {
23
25
  name: "@playcademy/better-auth",
24
- version: "0.0.21-beta.2",
26
+ version: "0.0.21-beta.19",
25
27
  type: "module",
26
28
  exports: {
27
29
  "./server": {
@@ -73,6 +75,24 @@ var OAUTH_CALLBACK_URL_PATTERN = "{appUrl}/api/auth/callback/{provider}";
73
75
  var PLACEHOLDER_APP_URL = "https://your-app.playcademy.gg";
74
76
  var AUTH_CONFIG_FILE = "auth.ts";
75
77
 
78
+ // src/constants/bucket.ts
79
+ var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore"];
80
+ var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
81
+ var DEFAULT_BUCKET_DIRECTORY = "bucket";
82
+
83
+ // src/constants/cloudflare.ts
84
+ var CLOUDFLARE_BINDINGS = {
85
+ /** R2 bucket binding name */
86
+ BUCKET: "BUCKET",
87
+ /** KV namespace binding name */
88
+ KV: "KV",
89
+ /** D1 database binding name */
90
+ DB: "DB"
91
+ };
92
+ var MINIFLARE_D1_DIRECTORY = "miniflare-D1DatabaseObject";
93
+ var QUEUE_NAME_PREFIX = "playcademy";
94
+ var SELF_WORKER_NAME_BINDING = "__PLAYCADEMY_WORKER_NAME";
95
+
76
96
  // src/constants/config.ts
77
97
  var ENV_FILES = [
78
98
  ".env",
@@ -98,24 +118,6 @@ var TSCONFIG_REQUIRED_INCLUDES = [
98
118
  // Server-side code
99
119
  ];
100
120
 
101
- // src/constants/bucket.ts
102
- var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
103
- var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
104
- var DEFAULT_BUCKET_DIRECTORY = "bucket";
105
-
106
- // src/constants/cloudflare.ts
107
- var CLOUDFLARE_BINDINGS = {
108
- /** R2 bucket binding name */
109
- BUCKET: "BUCKET",
110
- /** KV namespace binding name */
111
- KV: "KV",
112
- /** D1 database binding name */
113
- DB: "DB"
114
- };
115
- var MINIFLARE_D1_DIRECTORY = "miniflare-D1DatabaseObject";
116
- var QUEUE_NAME_PREFIX = "playcademy";
117
- var SELF_WORKER_NAME_BINDING = "__PLAYCADEMY_WORKER_NAME";
118
-
119
121
  // src/constants/database.ts
120
122
  import { join as join3 } from "path";
121
123
  var DEFAULT_DATABASE_DIRECTORY = join3("server", "db");
package/dist/db.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -2335,7 +2336,7 @@ var GAME_API_ROUTES_DIRECTORY = join2(GAME_SERVER_ROOT_DIRECTORY, "api");
2335
2336
  // ../better-auth/package.json
2336
2337
  var package_default = {
2337
2338
  name: "@playcademy/better-auth",
2338
- version: "0.0.21-beta.2",
2339
+ version: "0.0.21-beta.19",
2339
2340
  type: "module",
2340
2341
  exports: {
2341
2342
  "./server": {
@@ -2378,18 +2379,7 @@ var package_default = {
2378
2379
  // src/constants/auth.ts
2379
2380
  var BETTER_AUTH_VERSION = package_default.dependencies["better-auth"];
2380
2381
 
2381
- // src/constants/config.ts
2382
- var ENV_FILES = [
2383
- ".env",
2384
- // Loaded first
2385
- ".env.development",
2386
- // Overrides .env
2387
- ".env.local"
2388
- // Overrides all (highest priority)
2389
- ];
2390
-
2391
2382
  // src/constants/bucket.ts
2392
- var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
2393
2383
  var PRESIGNED_UPLOAD_THRESHOLD = 4 * 1024 * 1024;
2394
2384
 
2395
2385
  // src/constants/cloudflare.ts
@@ -2828,7 +2818,10 @@ function nestColor(content, color2) {
2828
2818
  function customTransform(text) {
2829
2819
  let result = text;
2830
2820
  result = result.replace(/<<([^>]+)>>/g, (_, url) => blueBright(underline(url)));
2831
- result = result.replace(/<([^>]+)>/g, (_, inner) => blueBright(`<${inner}>`));
2821
+ result = result.replace(
2822
+ /<([^>]+)>/g,
2823
+ (match, inner) => blueBright(/[./]/.test(inner) ? inner : match)
2824
+ );
2832
2825
  result = result.replace(/`([^`]+)`/g, (_, code) => nestColor(code, greenBright));
2833
2826
  return result;
2834
2827
  }
@@ -3396,6 +3389,17 @@ async function requireAuthenticatedClient() {
3396
3389
 
3397
3390
  // src/lib/core/errors.ts
3398
3391
  import { ApiError as ApiError2 } from "@playcademy/sdk/internal";
3392
+
3393
+ // src/lib/core/exit.ts
3394
+ function exitAfterStdoutDrain(code) {
3395
+ return new Promise(() => {
3396
+ process.stdout.write("", () => {
3397
+ process.exit(code);
3398
+ });
3399
+ });
3400
+ }
3401
+
3402
+ // src/lib/core/errors.ts
3399
3403
  function getErrorMessage(error) {
3400
3404
  if (error instanceof ApiError2) {
3401
3405
  if (error.message) {
@@ -3420,15 +3424,6 @@ function getErrorMessage(error) {
3420
3424
  return "Unknown error";
3421
3425
  }
3422
3426
 
3423
- // src/lib/core/exit.ts
3424
- function exitAfterStdoutDrain(code) {
3425
- return new Promise(() => {
3426
- process.stdout.write("", () => {
3427
- process.exit(code);
3428
- });
3429
- });
3430
- }
3431
-
3432
3427
  // ../utils/src/uuid.ts
3433
3428
  var UUID_REGEX = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
3434
3429
  function isValidUUID(value) {
@@ -4209,11 +4204,44 @@ var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES = [
4209
4204
  "text-entry",
4210
4205
  "order",
4211
4206
  "match",
4212
- "hottext"
4207
+ "hottext",
4208
+ "hotspot",
4209
+ "gap-match",
4210
+ "graphic-gap-match",
4211
+ "select-point",
4212
+ "graphic-associate",
4213
+ "position-object"
4213
4214
  ];
4214
4215
  var PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPE_SET = new Set(
4215
4216
  PLAYCADEMY_SUPPORTED_QTI_INTERACTION_TYPES
4216
4217
  );
4218
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES = [
4219
+ "choice",
4220
+ "inline-choice",
4221
+ "text-entry",
4222
+ "order",
4223
+ "match",
4224
+ "hottext",
4225
+ "gap-match",
4226
+ "hotspot",
4227
+ "graphic-associate",
4228
+ "graphic-gap-match"
4229
+ ];
4230
+ var PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPE_SET = new Set(
4231
+ PLAYCADEMY_AUTHORABLE_QTI_INTERACTION_TYPES
4232
+ );
4233
+ var QTI_RASTER_IMAGE_MIME_TYPES = [
4234
+ "image/png",
4235
+ "image/jpeg",
4236
+ // Some browsers and operating systems still report the legacy jpg spelling.
4237
+ "image/jpg",
4238
+ "image/gif",
4239
+ "image/webp",
4240
+ "image/bmp"
4241
+ ];
4242
+ var QTI_RASTER_IMAGE_SUBTYPE_PATTERN = QTI_RASTER_IMAGE_MIME_TYPES.map(
4243
+ (type) => type.slice("image/".length)
4244
+ ).join("|");
4217
4245
  var TIMEBACK_DISCREPANCY_QUEUE_WINDOW_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_WINDOWS);
4218
4246
  var TIMEBACK_DISCREPANCY_QUEUE_METRIC_VALUES = new Set(TIMEBACK_DISCREPANCY_QUEUE_METRICS);
4219
4247
 
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';
@@ -185,11 +186,10 @@ interface GameBackendFeatures {
185
186
  * Bucket command types
186
187
  */
187
188
  /**
188
- * Base options shared across bucket commands
189
+ * Base options shared across bucket commands. `raw` is not here on purpose:
190
+ * only the commands that register `--raw` (list, get, put) carry it.
189
191
  */
190
192
  interface BaseBucketOptions {
191
- /** Output in raw format */
192
- raw?: boolean;
193
193
  /** Output in JSON format */
194
194
  json?: boolean;
195
195
  /** Use remote bucket storage */
@@ -201,15 +201,21 @@ interface BaseBucketOptions {
201
201
  * Options for the bucket list command
202
202
  */
203
203
  interface BucketListOptions extends BaseBucketOptions {
204
+ /** Output all file keys one per line, no formatting (implies `recursive`) */
205
+ raw?: boolean;
204
206
  /** Filter files by key prefix */
205
207
  prefix?: string;
206
208
  /** Show the literal stored R2 keys (`_assets/sprites/hero.a1b2c3d4.svg`) instead of logical paths */
207
209
  literal?: boolean;
210
+ /** List every object under the prefix instead of one directory level */
211
+ recursive?: boolean;
208
212
  }
209
213
  /**
210
214
  * Options for the bucket get command
211
215
  */
212
216
  interface BucketGetOptions extends BaseBucketOptions {
217
+ /** Write file content straight to stdout */
218
+ raw?: boolean;
213
219
  /** Output file path */
214
220
  output?: string;
215
221
  /** Skip manifest resolution and fetch the literal key as stored */
@@ -218,7 +224,35 @@ interface BucketGetOptions extends BaseBucketOptions {
218
224
  /**
219
225
  * Options for the bucket put command (local debug only)
220
226
  */
221
- type BucketPutOptions = BaseBucketOptions;
227
+ interface BucketPutOptions extends BaseBucketOptions {
228
+ /** Output minimal confirmation */
229
+ raw?: boolean;
230
+ }
231
+ /**
232
+ * Options for the bucket upload command
233
+ */
234
+ interface BucketUploadOptions extends BaseBucketOptions {
235
+ /** Key prefix every uploaded file lands under */
236
+ prefix?: string;
237
+ /** Show what would be uploaded without uploading */
238
+ dryRun?: boolean;
239
+ /**
240
+ * Re-upload every file, even one already present at the same size — the
241
+ * escape hatch for the size-only diff (a file whose bytes changed but
242
+ * whose size did not is otherwise skipped). Authorizing the resulting
243
+ * replacements is a separate concern (the global `--yes`).
244
+ */
245
+ overwrite?: boolean;
246
+ }
247
+ /**
248
+ * Options for the bucket delete command
249
+ */
250
+ interface BucketDeleteOptions extends BaseBucketOptions {
251
+ /** Delete every object under this key prefix instead of explicit keys */
252
+ prefix?: string;
253
+ /** Show what would be deleted without deleting */
254
+ dryRun?: boolean;
255
+ }
222
256
  /**
223
257
  * How `bucket sync` treats pre-CAS objects already in the bucket that share a
224
258
  * logical path with a file being uploaded (a first-time migration).
@@ -256,6 +290,19 @@ interface BucketObjectRef {
256
290
  key: string;
257
291
  size: number;
258
292
  }
293
+ /** One listed object, as the list command displays it. */
294
+ interface ListedObject {
295
+ key: string;
296
+ size: number;
297
+ lastModified: string;
298
+ }
299
+ /** One directory level of a bucket: directory rows plus the files at that level. */
300
+ interface BucketLevelView {
301
+ /** Directory rows (ending with the delimiter), sorted */
302
+ prefixes: string[];
303
+ /** Files at this level, sorted by key */
304
+ files: ListedObject[];
305
+ }
259
306
  /**
260
307
  * Content-addressable asset manifest: maps logical paths to bare content-hashed
261
308
  * keys. Persisted to the bucket as `_manifest.json` (transport-free — the
@@ -278,11 +325,20 @@ interface HashedFile {
278
325
  content: Buffer;
279
326
  }
280
327
  /**
281
- * Transport-agnostic bucket operations the sync engine drives. Implemented for
282
- * both the remote API (presigned/uploads) and the local Miniflare R2 binding.
328
+ * Transport-agnostic bucket operations the bucket commands drive. Implemented
329
+ * for both the remote API (presigned/uploads) and the local Miniflare R2
330
+ * binding.
283
331
  */
284
332
  interface BucketAdapter {
285
- list(): Promise<BucketObjectRef[]>;
333
+ /** List objects under `prefix` (or everything). */
334
+ list(prefix?: string): Promise<BucketObjectRef[]>;
335
+ /**
336
+ * Whether an object exists at exactly `key`. Implementations must check
337
+ * membership, never listing position: R2's S3 listing does not return
338
+ * keys in byte-lexicographic order, so among prefix-sharing keys the
339
+ * exact key is not guaranteed to appear first.
340
+ */
341
+ exists(key: string): Promise<boolean>;
286
342
  put(key: string, content: Buffer, contentType: string): Promise<void>;
287
343
  delete(key: string): Promise<void>;
288
344
  }
@@ -423,6 +479,19 @@ interface DashboardThemeConfig {
423
479
  /** Secondary/accent color as a hex string, e.g. '#ffd166' */
424
480
  secondary?: string;
425
481
  }
482
+ /**
483
+ * Child-catalog contract generation (see @playcademy/sdk/contracts).
484
+ * The extractor is a repo-local script (run with the CLI's runtime) that
485
+ * prints `{ generatedFrom, deliveries }` as JSON on stdout; the CLI
486
+ * validates, stamps the contract fields, and writes
487
+ * `.playcademy/catalog.json` deterministically. The document carries no
488
+ * app identity — consuming repos assign the namespace key from sync
489
+ * provenance.
490
+ */
491
+ interface CatalogConfig {
492
+ /** Path to the extractor script, relative to the config file. */
493
+ extractor: string;
494
+ }
426
495
  /**
427
496
  * Unified Playcademy configuration
428
497
  * Used for playcademy.config.{js,json}
@@ -434,6 +503,13 @@ interface PlaycademyConfig {
434
503
  description?: string;
435
504
  /** Game emoji icon */
436
505
  emoji?: string;
506
+ /**
507
+ * Browser permissions to request for the game's iframe (e.g.
508
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
509
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
510
+ * every game automatically.
511
+ */
512
+ permissions?: GamePermission[];
437
513
  /** Build command to run before deployment */
438
514
  buildCommand?: string[];
439
515
  /** Path to build output */
@@ -448,6 +524,8 @@ interface PlaycademyConfig {
448
524
  dashboard?: DashboardConfig | boolean;
449
525
  /** Integrations (database, custom routes, external services) */
450
526
  integrations?: IntegrationsConfig;
527
+ /** Child-catalog contract generation (`playcademy catalog`) */
528
+ catalog?: CatalogConfig;
451
529
  }
452
530
 
453
531
  /**
@@ -485,6 +563,8 @@ interface WorkerDeploymentBundle {
485
563
  interface GameMetadata {
486
564
  description?: string;
487
565
  emoji?: string;
566
+ /** Browser permissions delegated to the game's iframe (opt-in per game). */
567
+ permissions?: GamePermission[];
488
568
  [key: string]: unknown;
489
569
  }
490
570
  declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
@@ -1079,6 +1159,13 @@ interface DeployConfig {
1079
1159
  description?: string;
1080
1160
  /** Emoji icon for the project */
1081
1161
  emoji?: string;
1162
+ /**
1163
+ * Browser permissions the game's iframe should be granted (e.g.
1164
+ * `['microphone']`). Opt-in per game; only `microphone` and `camera` are
1165
+ * delegated this way. Fullscreen, autoplay, and gamepad are granted to
1166
+ * every game automatically.
1167
+ */
1168
+ permissions?: GamePermission[];
1082
1169
  /** Path to the build directory or zip file */
1083
1170
  buildPath?: string;
1084
1171
  /** Game platform (defaults to 'web') */
@@ -1291,6 +1378,10 @@ interface ConfigDiff {
1291
1378
  old: string | undefined;
1292
1379
  new: string | undefined;
1293
1380
  };
1381
+ permissions?: {
1382
+ old: string[] | undefined;
1383
+ new: string[] | undefined;
1384
+ };
1294
1385
  gameType?: {
1295
1386
  old: string;
1296
1387
  new: string;
@@ -1766,4 +1857,4 @@ interface RunShellOptions {
1766
1857
  env?: NodeJS.ProcessEnv;
1767
1858
  }
1768
1859
 
1769
- export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, ApplySecretsOptions, AssetManifest, AuthProfile, AuthStore, AuthStrategy, AvailableUpdate, BackendDiff, BaseKVOptions, BlockedCode, BucketAdapter, BucketGetOptions, BucketListOptions, BucketMigrateMode, BucketObjectRef, BucketPutOptions, BucketSyncOptions, BuildDiff, BundleOptions, CallbackServerResult, CheckSecretsOptions, CollectedFile, CompatibilityKey, CompatibilityManifest, ConfigDiff, CreateApiKeyResponse, CustomRoutesIntegrationOptions, DashboardConfig, DatabaseIntegrationOptions, DependencyCheck, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentDiffOptions, DeploymentPlan, DeploymentResult, DiscoveredDependency, EngineConfig, EngineType, EnvironmentAuthProfiles, GameBackendBundle, GameBackendDeploymentMetadata, GameBackendFeatures, GameDevServerOptions, GameRuntimePaths, GameStore, HashedFile, InstalledVersions, IntegrationChangeDetector, IntegrationChangeDetectors, IntegrationConfigChange, IntegrationsConfig, IntegrationsDiff, KVClearOptions, KVDeleteOptions, KVDumpOptions, KVGetOptions, KVInspectOptions, KVListOptions, KVSeedOptions, KVSetOptions, KVStatsOptions, KeyMetadata, KeyStats, LoadConfigResult, LogEntry, LogFilters, LogHistoryQuery, LogHistoryResult, LogStreamConfig, LogStreamUrlOptions, LoginCredentials, LoginResponse, MissingSecret, PlaycademyConfig, PluginLogger, PreviewOptions, PreviewResponse, ProjectDirectoryInfo, QueueConfig, RunShellOptions, RuntimeManifest, ScaffoldResult, SecretCommandOptions, SecretListOptions, SecretPushOptions, SecretsSyncOptions, SecretsSyncResult, SignInResponse, SsoCallbackData, Template, TemplateFramework, TemplateHook, TemplateHookOptions, TemplateSource, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackIntegrationConfig, TokenType, UpdateCommand, UpdateExistingGameOptions, Violation, WorkspaceDependencies };
1860
+ export type { ApiConfig, ApiErrorResponse, ApiKeyListItem, ApiKeyWithSecret, ApiRequestOptions, ApplySecretsOptions, AssetManifest, AuthProfile, AuthStore, AuthStrategy, AvailableUpdate, BackendDiff, BaseKVOptions, BlockedCode, BucketAdapter, BucketDeleteOptions, BucketGetOptions, BucketLevelView, BucketListOptions, BucketMigrateMode, BucketObjectRef, BucketPutOptions, BucketSyncOptions, BucketUploadOptions, BuildDiff, BundleOptions, CallbackServerResult, CheckSecretsOptions, CollectedFile, CompatibilityKey, CompatibilityManifest, ConfigDiff, CreateApiKeyResponse, CustomRoutesIntegrationOptions, DashboardConfig, DatabaseIntegrationOptions, DependencyCheck, DeployConfig, DeployNewGameOptions, DeployedGameInfo, DeploymentChanges, DeploymentContext, DeploymentDiffOptions, DeploymentPlan, DeploymentResult, DiscoveredDependency, EngineConfig, EngineType, EnvironmentAuthProfiles, GameBackendBundle, GameBackendDeploymentMetadata, GameBackendFeatures, GameDevServerOptions, GameRuntimePaths, GameStore, HashedFile, InstalledVersions, IntegrationChangeDetector, IntegrationChangeDetectors, IntegrationConfigChange, IntegrationsConfig, IntegrationsDiff, KVClearOptions, KVDeleteOptions, KVDumpOptions, KVGetOptions, KVInspectOptions, KVListOptions, KVSeedOptions, KVSetOptions, KVStatsOptions, KeyMetadata, KeyStats, ListedObject, LoadConfigResult, LogEntry, LogFilters, LogHistoryQuery, LogHistoryResult, LogStreamConfig, LogStreamUrlOptions, LoginCredentials, LoginResponse, MissingSecret, PlaycademyConfig, PluginLogger, PreviewOptions, PreviewResponse, ProjectDirectoryInfo, QueueConfig, RunShellOptions, RuntimeManifest, ScaffoldResult, SecretCommandOptions, SecretListOptions, SecretPushOptions, SecretsSyncOptions, SecretsSyncResult, SignInResponse, SsoCallbackData, Template, TemplateFramework, TemplateHook, TemplateHookOptions, TemplateSource, TimebackBaseConfig, TimebackCourseConfigWithOverrides, TimebackIntegrationConfig, TokenType, UpdateCommand, UpdateExistingGameOptions, Violation, WorkspaceDependencies };