@playcademy/vite-plugin 0.1.32 → 0.1.34

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/index.js CHANGED
@@ -41199,7 +41199,7 @@ var import_picocolors7 = __toESM(require_picocolors(), 1);
41199
41199
  // package.json
41200
41200
  var package_default = {
41201
41201
  name: "@playcademy/vite-plugin",
41202
- version: "0.1.31",
41202
+ version: "0.1.33",
41203
41203
  type: "module",
41204
41204
  exports: {
41205
41205
  ".": {
@@ -41236,7 +41236,7 @@ var package_default = {
41236
41236
 
41237
41237
  // src/lib/backend/server.ts
41238
41238
  import {
41239
- loadPlaycademyConfig as loadConfig,
41239
+ loadPlaycademyConfigAndSetWorkspace as loadConfigAndSetWorkspace,
41240
41240
  startPlaycademyDevServer,
41241
41241
  startPlaycademyHotReload
41242
41242
  } from "playcademy/utils";
@@ -41306,9 +41306,9 @@ function createHotReloadCallbacks(viteConfig) {
41306
41306
  }
41307
41307
 
41308
41308
  // src/lib/backend/server.ts
41309
- async function tryLoadConfig(viteConfig) {
41309
+ async function tryLoadConfig(viteConfig, configPath) {
41310
41310
  try {
41311
- return await loadConfig();
41311
+ return await loadConfigAndSetWorkspace(configPath);
41312
41312
  } catch (error) {
41313
41313
  if (error instanceof Error && !error.message.includes("Could not find")) {
41314
41314
  viteConfig.logger.warn(`Could not load playcademy.config.js: ${error.message}`);
@@ -41337,8 +41337,8 @@ function setupHotReload(serverRef, options) {
41337
41337
  return () => watcher.close();
41338
41338
  }
41339
41339
  async function setupCliDevServer(options) {
41340
- const { preferredPort, viteConfig, platformUrl } = options;
41341
- const config = await tryLoadConfig(viteConfig);
41340
+ const { preferredPort, viteConfig, platformUrl, configPath } = options;
41341
+ const config = await tryLoadConfig(viteConfig, configPath);
41342
41342
  if (!config)
41343
41343
  return null;
41344
41344
  if (!needsCliDevServer(config))
@@ -104539,7 +104539,7 @@ var require_event_target_shim2 = __commonJS2((exports, module2) => {
104539
104539
  var CAPTURE = 1;
104540
104540
  var BUBBLE = 2;
104541
104541
  var ATTRIBUTE = 3;
104542
- function isObject2(x6) {
104542
+ function isObject4(x6) {
104543
104543
  return x6 !== null && typeof x6 === "object";
104544
104544
  }
104545
104545
  function getListeners(eventTarget) {
@@ -104563,7 +104563,7 @@ var require_event_target_shim2 = __commonJS2((exports, module2) => {
104563
104563
  return null;
104564
104564
  },
104565
104565
  set(listener) {
104566
- if (typeof listener !== "function" && !isObject2(listener)) {
104566
+ if (typeof listener !== "function" && !isObject4(listener)) {
104567
104567
  listener = null;
104568
104568
  }
104569
104569
  const listeners = getListeners(this);
@@ -104643,11 +104643,11 @@ var require_event_target_shim2 = __commonJS2((exports, module2) => {
104643
104643
  if (listener == null) {
104644
104644
  return;
104645
104645
  }
104646
- if (typeof listener !== "function" && !isObject2(listener)) {
104646
+ if (typeof listener !== "function" && !isObject4(listener)) {
104647
104647
  throw new TypeError("'listener' should be a function or an object.");
104648
104648
  }
104649
104649
  const listeners = getListeners(this);
104650
- const optionsIsObj = isObject2(options);
104650
+ const optionsIsObj = isObject4(options);
104651
104651
  const capture = optionsIsObj ? Boolean(options.capture) : Boolean(options);
104652
104652
  const listenerType = capture ? CAPTURE : BUBBLE;
104653
104653
  const newNode = {
@@ -104677,7 +104677,7 @@ var require_event_target_shim2 = __commonJS2((exports, module2) => {
104677
104677
  return;
104678
104678
  }
104679
104679
  const listeners = getListeners(this);
104680
- const capture = isObject2(options) ? Boolean(options.capture) : Boolean(options);
104680
+ const capture = isObject4(options) ? Boolean(options.capture) : Boolean(options);
104681
104681
  const listenerType = capture ? CAPTURE : BUBBLE;
104682
104682
  let prev = null;
104683
104683
  let node = listeners.get(eventName);
@@ -110418,7 +110418,7 @@ var init_block_senders = __esm(() => {
110418
110418
  var Domains;
110419
110419
  var DomainListResponsesV4PagePaginationArray;
110420
110420
  var DomainBulkDeleteResponsesSinglePage;
110421
- var init_domains = __esm(() => {
110421
+ var init_domains3 = __esm(() => {
110422
110422
  init_pagination();
110423
110423
  Domains = class Domains2 extends APIResource {
110424
110424
  list(params, options) {
@@ -110524,8 +110524,8 @@ var init_settings4 = __esm(() => {
110524
110524
  init_allow_policies();
110525
110525
  init_block_senders();
110526
110526
  init_block_senders();
110527
- init_domains();
110528
- init_domains();
110527
+ init_domains3();
110528
+ init_domains3();
110529
110529
  init_impersonation_registry();
110530
110530
  init_impersonation_registry();
110531
110531
  init_trusted_domains();
@@ -111746,7 +111746,7 @@ var init_bulks = __esm(() => {
111746
111746
  };
111747
111747
  });
111748
111748
  var Domains2;
111749
- var init_domains2 = __esm(() => {
111749
+ var init_domains4 = __esm(() => {
111750
111750
  init_bulks();
111751
111751
  init_bulks();
111752
111752
  Domains2 = class Domains22 extends APIResource {
@@ -111872,8 +111872,8 @@ var init_intel = __esm(() => {
111872
111872
  init_asn3();
111873
111873
  init_attack_surface_report();
111874
111874
  init_attack_surface_report();
111875
- init_domains2();
111876
- init_domains2();
111875
+ init_domains4();
111876
+ init_domains4();
111877
111877
  init_indicator_feeds();
111878
111878
  init_indicator_feeds();
111879
111879
  Intel = class Intel2 extends APIResource {
@@ -114612,7 +114612,7 @@ var init_page_shield = __esm(() => {
114612
114612
  });
114613
114613
  var Domains3;
114614
114614
  var DomainListResponsesSinglePage;
114615
- var init_domains3 = __esm(() => {
114615
+ var init_domains5 = __esm(() => {
114616
114616
  init_pagination();
114617
114617
  Domains3 = class Domains32 extends APIResource {
114618
114618
  create(projectName, params, options) {
@@ -114708,8 +114708,8 @@ var init_deployments = __esm(() => {
114708
114708
  var Projects;
114709
114709
  var DeploymentsSinglePage;
114710
114710
  var init_projects = __esm(() => {
114711
- init_domains3();
114712
- init_domains3();
114711
+ init_domains5();
114712
+ init_domains5();
114713
114713
  init_deployments();
114714
114714
  init_deployments();
114715
114715
  init_pagination();
@@ -115245,7 +115245,7 @@ var init_managed = __esm(() => {
115245
115245
  };
115246
115246
  });
115247
115247
  var Domains4;
115248
- var init_domains4 = __esm(() => {
115248
+ var init_domains6 = __esm(() => {
115249
115249
  init_custom5();
115250
115250
  init_custom5();
115251
115251
  init_managed();
@@ -115274,8 +115274,8 @@ var init_buckets = __esm(() => {
115274
115274
  init_metrics();
115275
115275
  init_sippy();
115276
115276
  init_sippy();
115277
- init_domains4();
115278
- init_domains4();
115277
+ init_domains6();
115278
+ init_domains6();
115279
115279
  Buckets = class Buckets2 extends APIResource {
115280
115280
  constructor() {
115281
115281
  super(...arguments);
@@ -118405,7 +118405,7 @@ var init_rate_limits = __esm(() => {
118405
118405
  });
118406
118406
  var Domains5;
118407
118407
  var DomainsSinglePage;
118408
- var init_domains5 = __esm(() => {
118408
+ var init_domains7 = __esm(() => {
118409
118409
  init_pagination();
118410
118410
  Domains5 = class Domains52 extends APIResource {
118411
118411
  update(domainName, params, options) {
@@ -118430,8 +118430,8 @@ var init_domains5 = __esm(() => {
118430
118430
  });
118431
118431
  var Registrar;
118432
118432
  var init_registrar = __esm(() => {
118433
- init_domains5();
118434
- init_domains5();
118433
+ init_domains7();
118434
+ init_domains7();
118435
118435
  Registrar = class Registrar2 extends APIResource {
118436
118436
  constructor() {
118437
118437
  super(...arguments);
@@ -121232,7 +121232,7 @@ var init_account_settings = __esm(() => {
121232
121232
  });
121233
121233
  var Domains6;
121234
121234
  var DomainsSinglePage2;
121235
- var init_domains6 = __esm(() => {
121235
+ var init_domains8 = __esm(() => {
121236
121236
  init_pagination();
121237
121237
  Domains6 = class Domains62 extends APIResource {
121238
121238
  update(params, options) {
@@ -121365,7 +121365,7 @@ var init_versions3 = __esm(() => {
121365
121365
  });
121366
121366
  var Workers;
121367
121367
  var WorkersV4PagePaginationArray;
121368
- var init_workers = __esm(() => {
121368
+ var init_workers3 = __esm(() => {
121369
121369
  init_versions3();
121370
121370
  init_versions3();
121371
121371
  init_pagination();
@@ -121417,8 +121417,8 @@ var init_workers = __esm(() => {
121417
121417
  });
121418
121418
  var Beta;
121419
121419
  var init_beta = __esm(() => {
121420
- init_workers();
121421
- init_workers();
121420
+ init_workers3();
121421
+ init_workers3();
121422
121422
  Beta = class Beta2 extends APIResource {
121423
121423
  constructor() {
121424
121424
  super(...arguments);
@@ -121785,11 +121785,11 @@ var init_scripts2 = __esm(() => {
121785
121785
  Scripts2.ScriptAndVersionSettings = ScriptAndVersionSettings;
121786
121786
  });
121787
121787
  var Workers2;
121788
- var init_workers2 = __esm(() => {
121788
+ var init_workers4 = __esm(() => {
121789
121789
  init_account_settings();
121790
121790
  init_account_settings();
121791
- init_domains6();
121792
- init_domains6();
121791
+ init_domains8();
121792
+ init_domains8();
121793
121793
  init_routes3();
121794
121794
  init_routes3();
121795
121795
  init_subdomains();
@@ -126569,7 +126569,7 @@ var init_resources3 = __esm(() => {
126569
126569
  init_vectorize();
126570
126570
  init_waiting_rooms();
126571
126571
  init_web3();
126572
- init_workers2();
126572
+ init_workers4();
126573
126573
  init_workers_for_platforms();
126574
126574
  init_workflows();
126575
126575
  init_zaraz();
@@ -126678,7 +126678,7 @@ var init_cloudflare = __esm(() => {
126678
126678
  init_waiting_rooms();
126679
126679
  init_web3();
126680
126680
  init_workers_for_platforms();
126681
- init_workers2();
126681
+ init_workers4();
126682
126682
  init_workflows();
126683
126683
  init_zaraz();
126684
126684
  init_zero_trust();
@@ -127143,7 +127143,7 @@ var require_dist_cjs2 = __commonJS2((exports, module2) => {
127143
127143
  Fields: () => Fields3,
127144
127144
  HttpRequest: () => HttpRequest,
127145
127145
  HttpResponse: () => HttpResponse,
127146
- IHttpRequest: () => import_types4.HttpRequest,
127146
+ IHttpRequest: () => import_types6.HttpRequest,
127147
127147
  getHttpHandlerExtensionConfiguration: () => getHttpHandlerExtensionConfiguration,
127148
127148
  isValidHostname: () => isValidHostname,
127149
127149
  resolveHttpHandlerRuntimeConfig: () => resolveHttpHandlerRuntimeConfig
@@ -127170,12 +127170,12 @@ var require_dist_cjs2 = __commonJS2((exports, module2) => {
127170
127170
  httpHandler: httpHandlerExtensionConfiguration.httpHandler()
127171
127171
  };
127172
127172
  }, "resolveHttpHandlerRuntimeConfig");
127173
- var import_types4 = require_dist_cjs();
127173
+ var import_types6 = require_dist_cjs();
127174
127174
  var Field = class {
127175
127175
  static {
127176
127176
  __name(this, "Field");
127177
127177
  }
127178
- constructor({ name: name4, kind: kind2 = import_types4.FieldPosition.HEADER, values = [] }) {
127178
+ constructor({ name: name4, kind: kind2 = import_types6.FieldPosition.HEADER, values = [] }) {
127179
127179
  this.name = name4;
127180
127180
  this.kind = kind2;
127181
127181
  this.values = values;
@@ -128036,8 +128036,8 @@ var require_dist_cjs4 = __commonJS2((exports, module2) => {
128036
128036
  normalizeProvider: () => normalizeProvider
128037
128037
  });
128038
128038
  module2.exports = __toCommonJS(src_exports);
128039
- var import_types4 = require_dist_cjs();
128040
- var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types4.SMITHY_CONTEXT_KEY] || (context[import_types4.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
128039
+ var import_types6 = require_dist_cjs();
128040
+ var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
128041
128041
  var normalizeProvider = /* @__PURE__ */ __name((input) => {
128042
128042
  if (typeof input === "function")
128043
128043
  return input;
@@ -132053,8 +132053,8 @@ var require_dist_cjs16 = __commonJS2((exports, module2) => {
132053
132053
  setFeature: () => setFeature
132054
132054
  });
132055
132055
  module2.exports = __toCommonJS(src_exports);
132056
- var import_types4 = require_dist_cjs();
132057
- var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types4.SMITHY_CONTEXT_KEY] || (context[import_types4.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
132056
+ var import_types6 = require_dist_cjs();
132057
+ var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
132058
132058
  var import_util_middleware = require_dist_cjs4();
132059
132059
  var resolveAuthOptions = /* @__PURE__ */ __name((candidateAuthOptions, authSchemePreference) => {
132060
132060
  if (!authSchemePreference || authSchemePreference.length === 0) {
@@ -132290,9 +132290,9 @@ var require_dist_cjs16 = __commonJS2((exports, module2) => {
132290
132290
  throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
132291
132291
  }
132292
132292
  const clonedRequest = import_protocol_http.HttpRequest.clone(httpRequest);
132293
- if (signingProperties.in === import_types4.HttpApiKeyAuthLocation.QUERY) {
132293
+ if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.QUERY) {
132294
132294
  clonedRequest.query[signingProperties.name] = identity.apiKey;
132295
- } else if (signingProperties.in === import_types4.HttpApiKeyAuthLocation.HEADER) {
132295
+ } else if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.HEADER) {
132296
132296
  clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
132297
132297
  } else {
132298
132298
  throw new Error("request can only be signed with `apiKey` locations `query` or `header`, but found: `" + signingProperties.in + "`");
@@ -133365,7 +133365,7 @@ var require_httpAuthSchemes = __commonJS2((exports, module2) => {
133365
133365
  },
133366
133366
  default: undefined
133367
133367
  };
133368
- var import_client = require_client();
133368
+ var import_client2 = require_client();
133369
133369
  var import_core210 = require_dist_cjs16();
133370
133370
  var import_signature_v4 = require_dist_cjs20();
133371
133371
  var resolveAwsSdkSigV4Config = /* @__PURE__ */ __name((config2) => {
@@ -133384,7 +133384,7 @@ var require_httpAuthSchemes = __commonJS2((exports, module2) => {
133384
133384
  });
133385
133385
  const boundProvider = bindCallerConfig(config2, memoizedProvider);
133386
133386
  if (isUserSupplied && !boundProvider.attributed) {
133387
- resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_CODE", "e")), "resolvedCredentials");
133387
+ resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_CODE", "e")), "resolvedCredentials");
133388
133388
  resolvedCredentials.memoized = boundProvider.memoized;
133389
133389
  resolvedCredentials.configBound = boundProvider.configBound;
133390
133390
  resolvedCredentials.attributed = true;
@@ -133926,7 +133926,7 @@ var require_dist_cjs23 = __commonJS2((exports, module2) => {
133926
133926
  }
133927
133927
  };
133928
133928
  var import_protocols = require_protocols();
133929
- var import_types4 = require_dist_cjs();
133929
+ var import_types6 = require_dist_cjs();
133930
133930
  var Command = class {
133931
133931
  constructor() {
133932
133932
  this.middlewareStack = (0, import_middleware_stack.constructStack)();
@@ -133958,7 +133958,7 @@ var require_dist_cjs23 = __commonJS2((exports, module2) => {
133958
133958
  commandName,
133959
133959
  inputFilterSensitiveLog,
133960
133960
  outputFilterSensitiveLog,
133961
- [import_types4.SMITHY_CONTEXT_KEY]: {
133961
+ [import_types6.SMITHY_CONTEXT_KEY]: {
133962
133962
  commandInstance: this,
133963
133963
  ...smithyContext
133964
133964
  },
@@ -134183,8 +134183,8 @@ var require_dist_cjs23 = __commonJS2((exports, module2) => {
134183
134183
  }, "emitWarningIfUnsupportedVersion");
134184
134184
  var getChecksumConfiguration = /* @__PURE__ */ __name((runtimeConfig) => {
134185
134185
  const checksumAlgorithms = [];
134186
- for (const id in import_types4.AlgorithmId) {
134187
- const algorithmId = import_types4.AlgorithmId[id];
134186
+ for (const id in import_types6.AlgorithmId) {
134187
+ const algorithmId = import_types6.AlgorithmId[id];
134188
134188
  if (runtimeConfig[algorithmId] === undefined) {
134189
134189
  continue;
134190
134190
  }
@@ -136341,7 +136341,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136341
136341
  return this._read(schema4, data);
136342
136342
  }
136343
136343
  _read(schema4, value) {
136344
- const isObject2 = value !== null && typeof value === "object";
136344
+ const isObject4 = value !== null && typeof value === "object";
136345
136345
  const ns = import_schema2.NormalizedSchema.of(schema4);
136346
136346
  if (ns.isListSchema() && Array.isArray(value)) {
136347
136347
  const listMember = ns.getValueSchema();
@@ -136353,7 +136353,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136353
136353
  }
136354
136354
  }
136355
136355
  return out2;
136356
- } else if (ns.isMapSchema() && isObject2) {
136356
+ } else if (ns.isMapSchema() && isObject4) {
136357
136357
  const mapMember = ns.getValueSchema();
136358
136358
  const out2 = {};
136359
136359
  const sparse = !!ns.getMergedTraits().sparse;
@@ -136363,7 +136363,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136363
136363
  }
136364
136364
  }
136365
136365
  return out2;
136366
- } else if (ns.isStructSchema() && isObject2) {
136366
+ } else if (ns.isStructSchema() && isObject4) {
136367
136367
  const out2 = {};
136368
136368
  for (const [memberName, memberSchema] of ns.structIterator()) {
136369
136369
  const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
@@ -136495,7 +136495,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136495
136495
  return this.buffer;
136496
136496
  }
136497
136497
  _write(schema4, value, container) {
136498
- const isObject2 = value !== null && typeof value === "object";
136498
+ const isObject4 = value !== null && typeof value === "object";
136499
136499
  const ns = import_schema22.NormalizedSchema.of(schema4);
136500
136500
  if (ns.isListSchema() && Array.isArray(value)) {
136501
136501
  const listMember = ns.getValueSchema();
@@ -136507,7 +136507,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136507
136507
  }
136508
136508
  }
136509
136509
  return out2;
136510
- } else if (ns.isMapSchema() && isObject2) {
136510
+ } else if (ns.isMapSchema() && isObject4) {
136511
136511
  const mapMember = ns.getValueSchema();
136512
136512
  const out2 = {};
136513
136513
  const sparse = !!ns.getMergedTraits().sparse;
@@ -136517,7 +136517,7 @@ var require_protocols2 = __commonJS2((exports, module2) => {
136517
136517
  }
136518
136518
  }
136519
136519
  return out2;
136520
- } else if (ns.isStructSchema() && isObject2) {
136520
+ } else if (ns.isStructSchema() && isObject4) {
136521
136521
  const out2 = {};
136522
136522
  for (const [memberName, memberSchema] of ns.structIterator()) {
136523
136523
  const targetKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
@@ -143949,16 +143949,16 @@ var require_dist_cjs52 = __commonJS2((exports, module2) => {
143949
143949
  var getProfileName = /* @__PURE__ */ __name((init3) => init3.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE, "getProfileName");
143950
143950
  __reExport(src_exports, require_getSSOTokenFilepath(), module2.exports);
143951
143951
  __reExport(src_exports, require_getSSOTokenFromFile(), module2.exports);
143952
- var import_types4 = require_dist_cjs();
143952
+ var import_types6 = require_dist_cjs();
143953
143953
  var getConfigData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => {
143954
143954
  const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
143955
143955
  if (indexOfSeparator === -1) {
143956
143956
  return false;
143957
143957
  }
143958
- return Object.values(import_types4.IniSectionType).includes(key.substring(0, indexOfSeparator));
143958
+ return Object.values(import_types6.IniSectionType).includes(key.substring(0, indexOfSeparator));
143959
143959
  }).reduce((acc, [key, value]) => {
143960
143960
  const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
143961
- const updatedKey = key.substring(0, indexOfSeparator) === import_types4.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
143961
+ const updatedKey = key.substring(0, indexOfSeparator) === import_types6.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
143962
143962
  acc[updatedKey] = value;
143963
143963
  return acc;
143964
143964
  }, {
@@ -143988,7 +143988,7 @@ var require_dist_cjs52 = __commonJS2((exports, module2) => {
143988
143988
  const matches = prefixKeyRegex.exec(sectionName);
143989
143989
  if (matches) {
143990
143990
  const [, prefix2, , name4] = matches;
143991
- if (Object.values(import_types4.IniSectionType).includes(prefix2)) {
143991
+ if (Object.values(import_types6.IniSectionType).includes(prefix2)) {
143992
143992
  currentSection = [prefix2, name4].join(CONFIG_PREFIX_SEPARATOR);
143993
143993
  }
143994
143994
  } else {
@@ -144047,7 +144047,7 @@ var require_dist_cjs52 = __commonJS2((exports, module2) => {
144047
144047
  credentialsFile: parsedFiles[1]
144048
144048
  };
144049
144049
  }, "loadSharedConfigFiles");
144050
- var getSsoSessionData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => key.startsWith(import_types4.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {}), "getSsoSessionData");
144050
+ var getSsoSessionData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => key.startsWith(import_types6.IniSectionType.SSO_SESSION + CONFIG_PREFIX_SEPARATOR)).reduce((acc, [key, value]) => ({ ...acc, [key.substring(key.indexOf(CONFIG_PREFIX_SEPARATOR) + 1)]: value }), {}), "getSsoSessionData");
144051
144051
  var import_slurpFile2 = require_slurpFile();
144052
144052
  var swallowError2 = /* @__PURE__ */ __name(() => ({}), "swallowError");
144053
144053
  var loadSsoSessionData = /* @__PURE__ */ __name(async (init3 = {}) => (0, import_slurpFile2.slurpFile)(init3.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError2), "loadSsoSessionData");
@@ -144090,7 +144090,7 @@ var require_dist_cjs53 = __commonJS2((exports, module2) => {
144090
144090
  var __toCommonJS = (mod) => __copyProps2(__defProp4({}, "__esModule", { value: true }), mod);
144091
144091
  var src_exports = {};
144092
144092
  __export4(src_exports, {
144093
- loadConfig: () => loadConfig2
144093
+ loadConfig: () => loadConfig
144094
144094
  });
144095
144095
  module2.exports = __toCommonJS(src_exports);
144096
144096
  var import_property_provider = require_dist_cjs17();
@@ -144137,7 +144137,7 @@ var require_dist_cjs53 = __commonJS2((exports, module2) => {
144137
144137
  }, "fromSharedConfigFiles");
144138
144138
  var isFunction3 = /* @__PURE__ */ __name((func2) => typeof func2 === "function", "isFunction");
144139
144139
  var fromStatic = /* @__PURE__ */ __name((defaultValue) => isFunction3(defaultValue) ? async () => await defaultValue() : (0, import_property_provider.fromStatic)(defaultValue), "fromStatic");
144140
- var loadConfig2 = /* @__PURE__ */ __name(({ environmentVariableSelector, configFileSelector, default: defaultValue }, configuration = {}) => {
144140
+ var loadConfig = /* @__PURE__ */ __name(({ environmentVariableSelector, configFileSelector, default: defaultValue }, configuration = {}) => {
144141
144141
  const { signingName, logger: logger3 } = configuration;
144142
144142
  const envOptions = { signingName, logger: logger3 };
144143
144143
  return (0, import_property_provider.memoize)((0, import_property_provider.chain)(fromEnv(environmentVariableSelector, envOptions), fromSharedConfigFiles(configFileSelector, configuration), fromStatic(defaultValue)));
@@ -145024,7 +145024,7 @@ var require_dist_cjs57 = __commonJS2((exports, module2) => {
145024
145024
  fromEnv: () => fromEnv
145025
145025
  });
145026
145026
  module2.exports = __toCommonJS(index_exports);
145027
- var import_client = require_client();
145027
+ var import_client2 = require_client();
145028
145028
  var import_property_provider = require_dist_cjs17();
145029
145029
  var ENV_KEY = "AWS_ACCESS_KEY_ID";
145030
145030
  var ENV_SECRET = "AWS_SECRET_ACCESS_KEY";
@@ -145049,7 +145049,7 @@ var require_dist_cjs57 = __commonJS2((exports, module2) => {
145049
145049
  ...credentialScope && { credentialScope },
145050
145050
  ...accountId && { accountId }
145051
145051
  };
145052
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_ENV_VARS", "g");
145052
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_ENV_VARS", "g");
145053
145053
  return credentials2;
145054
145054
  }
145055
145055
  throw new import_property_provider.CredentialsProviderError("Unable to find environment variable credentials.", { logger: init3?.logger });
@@ -147916,7 +147916,7 @@ var require_dist_cjs67 = __commonJS2((exports, module2) => {
147916
147916
  var __getOwnPropNames3 = Object.getOwnPropertyNames;
147917
147917
  var __hasOwnProp3 = Object.prototype.hasOwnProperty;
147918
147918
  var __name = (target, value) => __defProp4(target, "name", { value, configurable: true });
147919
- var __esm3 = (fn2, res) => function __init() {
147919
+ var __esm5 = (fn2, res) => function __init() {
147920
147920
  return fn2 && (res = (0, fn2[__getOwnPropNames3(fn2)[0]])(fn2 = 0)), res;
147921
147921
  };
147922
147922
  var __export4 = (target, all) => {
@@ -147938,7 +147938,7 @@ var require_dist_cjs67 = __commonJS2((exports, module2) => {
147938
147938
  SSOClient: () => import_client_sso.SSOClient
147939
147939
  });
147940
147940
  var import_client_sso;
147941
- var init_loadSso = __esm3({
147941
+ var init_loadSso = __esm5({
147942
147942
  "src/loadSso.ts"() {
147943
147943
  import_client_sso = require_dist_cjs65();
147944
147944
  }
@@ -147951,7 +147951,7 @@ var require_dist_cjs67 = __commonJS2((exports, module2) => {
147951
147951
  });
147952
147952
  module2.exports = __toCommonJS(index_exports);
147953
147953
  var isSsoProfile = /* @__PURE__ */ __name((arg) => arg && (typeof arg.sso_start_url === "string" || typeof arg.sso_account_id === "string" || typeof arg.sso_session === "string" || typeof arg.sso_region === "string" || typeof arg.sso_role_name === "string"), "isSsoProfile");
147954
- var import_client = require_client();
147954
+ var import_client2 = require_client();
147955
147955
  var import_token_providers = require_dist_cjs66();
147956
147956
  var import_property_provider = require_dist_cjs17();
147957
147957
  var import_shared_ini_file_loader = require_dist_cjs52();
@@ -148036,9 +148036,9 @@ var require_dist_cjs67 = __commonJS2((exports, module2) => {
148036
148036
  ...accountId && { accountId }
148037
148037
  };
148038
148038
  if (ssoSession) {
148039
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_SSO", "s");
148039
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO", "s");
148040
148040
  } else {
148041
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_SSO_LEGACY", "u");
148041
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO_LEGACY", "u");
148042
148042
  }
148043
148043
  return credentials2;
148044
148044
  }, "resolveSSOCredentials");
@@ -149218,7 +149218,7 @@ var require_sts = __commonJS2((exports, module2) => {
149218
149218
  };
149219
149219
  (0, import_smithy_client6.createAggregatedClient)(commands, STS);
149220
149220
  var import_EndpointParameters3 = require_EndpointParameters();
149221
- var import_client = require_client();
149221
+ var import_client2 = require_client();
149222
149222
  var ASSUME_ROLE_DEFAULT_REGION = "us-east-1";
149223
149223
  var getAccountIdFromAssumedRoleUser = /* @__PURE__ */ __name((assumedRoleUser) => {
149224
149224
  if (typeof assumedRoleUser?.Arn === "string") {
@@ -149270,7 +149270,7 @@ var require_sts = __commonJS2((exports, module2) => {
149270
149270
  ...Credentials2.CredentialScope && { credentialScope: Credentials2.CredentialScope },
149271
149271
  ...accountId && { accountId }
149272
149272
  };
149273
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE", "i");
149273
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE", "i");
149274
149274
  return credentials2;
149275
149275
  };
149276
149276
  }, "getDefaultRoleAssumer");
@@ -149307,9 +149307,9 @@ var require_sts = __commonJS2((exports, module2) => {
149307
149307
  ...accountId && { accountId }
149308
149308
  };
149309
149309
  if (accountId) {
149310
- (0, import_client.setCredentialFeature)(credentials2, "RESOLVED_ACCOUNT_ID", "T");
149310
+ (0, import_client2.setCredentialFeature)(credentials2, "RESOLVED_ACCOUNT_ID", "T");
149311
149311
  }
149312
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k");
149312
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k");
149313
149313
  return credentials2;
149314
149314
  };
149315
149315
  }, "getDefaultRoleAssumerWithWebIdentity");
@@ -149369,7 +149369,7 @@ var require_dist_cjs68 = __commonJS2((exports, module2) => {
149369
149369
  var import_property_provider = require_dist_cjs17();
149370
149370
  var import_child_process = __require2("child_process");
149371
149371
  var import_util3 = __require2("util");
149372
- var import_client = require_client();
149372
+ var import_client2 = require_client();
149373
149373
  var getValidatedProcessCredentials = /* @__PURE__ */ __name((profileName, data, profiles) => {
149374
149374
  if (data.Version !== 1) {
149375
149375
  throw Error(`Profile ${profileName} credential_process did not return Version 1.`);
@@ -149396,7 +149396,7 @@ var require_dist_cjs68 = __commonJS2((exports, module2) => {
149396
149396
  ...data.CredentialScope && { credentialScope: data.CredentialScope },
149397
149397
  ...accountId && { accountId }
149398
149398
  };
149399
- (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_PROCESS", "w");
149399
+ (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROCESS", "w");
149400
149400
  return credentials2;
149401
149401
  }, "getValidatedProcessCredentials");
149402
149402
  var resolveProcessCredentials = /* @__PURE__ */ __name(async (profileName, profiles, logger3) => {
@@ -149589,7 +149589,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149589
149589
  });
149590
149590
  module2.exports = __toCommonJS(index_exports);
149591
149591
  var import_shared_ini_file_loader = require_dist_cjs52();
149592
- var import_client = require_client();
149592
+ var import_client2 = require_client();
149593
149593
  var import_property_provider = require_dist_cjs17();
149594
149594
  var resolveCredentialSource = /* @__PURE__ */ __name((credentialSource, profileName, logger3) => {
149595
149595
  const sourceProvidersMap = {
@@ -149616,7 +149616,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149616
149616
  throw new import_property_provider.CredentialsProviderError(`Unsupported credential source in profile ${profileName}. Got ${credentialSource}, expected EcsContainer or Ec2InstanceMetadata or Environment.`, { logger: logger3 });
149617
149617
  }
149618
149618
  }, "resolveCredentialSource");
149619
- var setNamedProvider = /* @__PURE__ */ __name((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_NAMED_PROVIDER", "p"), "setNamedProvider");
149619
+ var setNamedProvider = /* @__PURE__ */ __name((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_NAMED_PROVIDER", "p"), "setNamedProvider");
149620
149620
  var isAssumeRoleProfile = /* @__PURE__ */ __name((arg, { profile = "default", logger: logger3 } = {}) => {
149621
149621
  return Boolean(arg) && typeof arg === "object" && typeof arg.role_arn === "string" && ["undefined", "string"].indexOf(typeof arg.role_session_name) > -1 && ["undefined", "string"].indexOf(typeof arg.external_id) > -1 && ["undefined", "string"].indexOf(typeof arg.mfa_serial) > -1 && (isAssumeRoleWithSourceProfile(arg, { profile, logger: logger3 }) || isCredentialSourceProfile(arg, { profile, logger: logger3 }));
149622
149622
  }, "isAssumeRoleProfile");
@@ -149658,7 +149658,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149658
149658
  [source_profile]: true
149659
149659
  }, isCredentialSourceWithoutRoleArn(profiles[source_profile] ?? {})) : (await resolveCredentialSource(profileData.credential_source, profileName, options.logger)(options))();
149660
149660
  if (isCredentialSourceWithoutRoleArn(profileData)) {
149661
- return sourceCredsProvider.then((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
149661
+ return sourceCredsProvider.then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
149662
149662
  } else {
149663
149663
  const params = {
149664
149664
  RoleArn: profileData.role_arn,
@@ -149675,7 +149675,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149675
149675
  params.TokenCode = await options.mfaCodeProvider(mfa_serial);
149676
149676
  }
149677
149677
  const sourceCreds = await sourceCredsProvider;
149678
- return options.roleAssumer(sourceCreds, params).then((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
149678
+ return options.roleAssumer(sourceCreds, params).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
149679
149679
  }
149680
149680
  }, "resolveAssumeRoleCredentials");
149681
149681
  var isCredentialSourceWithoutRoleArn = /* @__PURE__ */ __name((section) => {
@@ -149685,7 +149685,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149685
149685
  var resolveProcessCredentials = /* @__PURE__ */ __name(async (options, profile) => Promise.resolve().then(() => __toESM3(require_dist_cjs68())).then(({ fromProcess }) => fromProcess({
149686
149686
  ...options,
149687
149687
  profile
149688
- })().then((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_PROCESS", "v"))), "resolveProcessCredentials");
149688
+ })().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_PROCESS", "v"))), "resolveProcessCredentials");
149689
149689
  var resolveSsoCredentials = /* @__PURE__ */ __name(async (profile, profileData, options = {}) => {
149690
149690
  const { fromSSO } = await Promise.resolve().then(() => __toESM3(require_dist_cjs67()));
149691
149691
  return fromSSO({
@@ -149695,9 +149695,9 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149695
149695
  clientConfig: options.clientConfig
149696
149696
  })().then((creds) => {
149697
149697
  if (profileData.sso_session) {
149698
- return (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO", "r");
149698
+ return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO", "r");
149699
149699
  } else {
149700
- return (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO_LEGACY", "t");
149700
+ return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO_LEGACY", "t");
149701
149701
  }
149702
149702
  });
149703
149703
  }, "resolveSsoCredentials");
@@ -149712,7 +149712,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149712
149712
  ...profile.aws_credential_scope && { credentialScope: profile.aws_credential_scope },
149713
149713
  ...profile.aws_account_id && { accountId: profile.aws_account_id }
149714
149714
  };
149715
- return (0, import_client.setCredentialFeature)(credentials2, "CREDENTIALS_PROFILE", "n");
149715
+ return (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROFILE", "n");
149716
149716
  }, "resolveStaticCredentials");
149717
149717
  var isWebIdentityProfile = /* @__PURE__ */ __name((arg) => Boolean(arg) && typeof arg === "object" && typeof arg.web_identity_token_file === "string" && typeof arg.role_arn === "string" && ["undefined", "string"].indexOf(typeof arg.role_session_name) > -1, "isWebIdentityProfile");
149718
149718
  var resolveWebIdentityCredentials = /* @__PURE__ */ __name(async (profile, options) => Promise.resolve().then(() => __toESM3(require_dist_cjs69())).then(({ fromTokenFile: fromTokenFile2 }) => fromTokenFile2({
@@ -149722,7 +149722,7 @@ var require_dist_cjs70 = __commonJS2((exports, module2) => {
149722
149722
  roleAssumerWithWebIdentity: options.roleAssumerWithWebIdentity,
149723
149723
  logger: options.logger,
149724
149724
  parentClientConfig: options.parentClientConfig
149725
- })().then((creds) => (0, import_client.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN", "q"))), "resolveWebIdentityCredentials");
149725
+ })().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN", "q"))), "resolveWebIdentityCredentials");
149726
149726
  var resolveProfileData = /* @__PURE__ */ __name(async (profileName, profiles, options, visitedProfiles = {}, isAssumeRoleRecursiveCall = false) => {
149727
149727
  const data = profiles[profileName];
149728
149728
  if (Object.keys(visitedProfiles).length > 0 && isStaticCredsProfile(data)) {
@@ -168486,7 +168486,7 @@ var require_lodash2 = __commonJS2((exports, module2) => {
168486
168486
  }
168487
168487
  }
168488
168488
  function baseKeysIn(object) {
168489
- if (!isObject2(object)) {
168489
+ if (!isObject4(object)) {
168490
168490
  return nativeKeysIn(object);
168491
168491
  }
168492
168492
  var isProto = isPrototype(object), result = [];
@@ -168546,7 +168546,7 @@ var require_lodash2 = __commonJS2((exports, module2) => {
168546
168546
  return !!length && (typeof value == "number" || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
168547
168547
  }
168548
168548
  function isIterateeCall(value, index6, object) {
168549
- if (!isObject2(object)) {
168549
+ if (!isObject4(object)) {
168550
168550
  return false;
168551
168551
  }
168552
168552
  var type = typeof index6;
@@ -168582,13 +168582,13 @@ var require_lodash2 = __commonJS2((exports, module2) => {
168582
168582
  return isObjectLike2(value) && isArrayLike(value);
168583
168583
  }
168584
168584
  function isFunction3(value) {
168585
- var tag2 = isObject2(value) ? objectToString.call(value) : "";
168585
+ var tag2 = isObject4(value) ? objectToString.call(value) : "";
168586
168586
  return tag2 == funcTag || tag2 == genTag;
168587
168587
  }
168588
168588
  function isLength(value) {
168589
168589
  return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
168590
168590
  }
168591
- function isObject2(value) {
168591
+ function isObject4(value) {
168592
168592
  var type = typeof value;
168593
168593
  return !!value && (type == "object" || type == "function");
168594
168594
  }
@@ -168626,13 +168626,13 @@ var require_lodash3 = __commonJS2((exports, module2) => {
168626
168626
  return isObjectLike2(value) && isArrayLike(value);
168627
168627
  }
168628
168628
  function isFunction3(value) {
168629
- var tag2 = isObject2(value) ? objectToString.call(value) : "";
168629
+ var tag2 = isObject4(value) ? objectToString.call(value) : "";
168630
168630
  return tag2 == funcTag || tag2 == genTag;
168631
168631
  }
168632
168632
  function isLength(value) {
168633
168633
  return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
168634
168634
  }
168635
- function isObject2(value) {
168635
+ function isObject4(value) {
168636
168636
  var type = typeof value;
168637
168637
  return !!value && (type == "object" || type == "function");
168638
168638
  }
@@ -173723,7 +173723,7 @@ var REDIS_PRESENCE_KEY_PREFIX;
173723
173723
  var REDIS_ONLINE_USERS_KEY;
173724
173724
  var REDIS_POSITION_KEY_PREFIX;
173725
173725
  var REDIS_MAP_PLAYERS_KEY_PREFIX;
173726
- var init_constants = __esm(() => {
173726
+ var init_constants3 = __esm(() => {
173727
173727
  HUB_PREFIX = process.env.HUB_PREFIX ?? "hub:";
173728
173728
  MAP_PREFIX = process.env.MAP_PREFIX ?? "map_";
173729
173729
  PRESENCE_CHANNEL_NAME = process.env.PRESENCE_CHANNEL_NAME ?? "presence";
@@ -173871,7 +173871,7 @@ var import_ioredis;
173871
173871
  var client = null;
173872
173872
  var init_client = __esm(() => {
173873
173873
  init_src();
173874
- init_constants();
173874
+ init_constants3();
173875
173875
  import_ioredis = __toESM2(require_built3(), 1);
173876
173876
  });
173877
173877
  async function setUserOnline(userId, metadata2) {
@@ -173982,7 +173982,7 @@ async function updateUserNameplateBg(userId, color) {
173982
173982
  }
173983
173983
  var init_presence = __esm(() => {
173984
173984
  init_src();
173985
- init_constants();
173985
+ init_constants3();
173986
173986
  init_client();
173987
173987
  });
173988
173988
  var init_redis = __esm(() => {
@@ -174027,11 +174027,11 @@ function extractTokenFromUrl(url) {
174027
174027
  return null;
174028
174028
  }
174029
174029
  }
174030
- var init_auth = __esm(() => {
174030
+ var init_auth3 = __esm(() => {
174031
174031
  init_esm2();
174032
174032
  init_src();
174033
174033
  });
174034
- function loadConfig2(options = {}) {
174034
+ function loadConfig(options = {}) {
174035
174035
  const rawRedisHost = process.env.REDIS_HOST || "localhost";
174036
174036
  const rawRedisPort = parseInt(process.env.REDIS_PORT || "6379", 10);
174037
174037
  const rawRedisPassword = process.env.REDIS_PASSWORD;
@@ -174076,7 +174076,7 @@ __export(exports_sandbox, {
174076
174076
  createSandboxRealtimeServer: () => createSandboxRealtimeServer
174077
174077
  });
174078
174078
  async function createSandboxRealtimeServer(options = {}) {
174079
- const config2 = await loadConfig2(options);
174079
+ const config2 = await loadConfig(options);
174080
174080
  const httpServer = http.createServer((req, res) => {
174081
174081
  if (req.url?.endsWith(config2.healthCheckPath)) {
174082
174082
  const body2 = JSON.stringify({
@@ -174158,7 +174158,7 @@ var init_sandbox = __esm(() => {
174158
174158
  init_wrapper();
174159
174159
  init_src();
174160
174160
  init_infrastructure2();
174161
- init_auth();
174161
+ init_auth3();
174162
174162
  init_config4();
174163
174163
  });
174164
174164
  var inMemoryUsers;
@@ -174244,7 +174244,7 @@ var playerPositions;
174244
174244
  var PlayerPositionService;
174245
174245
  var init_player_position = __esm(() => {
174246
174246
  init_src();
174247
- init_constants();
174247
+ init_constants3();
174248
174248
  init_infrastructure2();
174249
174249
  playerPositions = new Map;
174250
174250
  PlayerPositionService = {
@@ -174374,7 +174374,7 @@ function computeInitialChannelKey(gameId, requested) {
174374
174374
  return `${HUB_PREFIX}${channel}`;
174375
174375
  }
174376
174376
  var init_channel_keys = __esm(() => {
174377
- init_constants();
174377
+ init_constants3();
174378
174378
  });
174379
174379
  function ensureAuthenticated(ws) {
174380
174380
  if (!ws.data.isAuthenticated || !ws.data.userId) {
@@ -174393,7 +174393,7 @@ function checkMatchingPlayerId(ws, payload) {
174393
174393
  }
174394
174394
  var init_websocket = __esm(() => {
174395
174395
  init_src();
174396
- init_constants();
174396
+ init_constants3();
174397
174397
  });
174398
174398
  function extractUserId(pathname) {
174399
174399
  const match2 = pathname.match(/^\/presence\/([^/]+)$/);
@@ -174729,7 +174729,7 @@ async function handleWebSocketUpgrade(req, server, config2) {
174729
174729
  }
174730
174730
  var init_websocket2 = __esm(() => {
174731
174731
  init_src();
174732
- init_auth();
174732
+ init_auth3();
174733
174733
  init_response();
174734
174734
  });
174735
174735
  var init_http2 = __esm(() => {
@@ -174908,7 +174908,7 @@ var init_open = __esm(() => {
174908
174908
  init_src();
174909
174909
  init_presence4();
174910
174910
  init_services2();
174911
- init_constants();
174911
+ init_constants3();
174912
174912
  init_events();
174913
174913
  init_infrastructure2();
174914
174914
  init_utils13();
@@ -175132,7 +175132,7 @@ var init_websocket3 = __esm(() => {
175132
175132
  init_close();
175133
175133
  });
175134
175134
  async function createRealtimeServer(options = {}) {
175135
- const config2 = loadConfig2(options);
175135
+ const config2 = loadConfig(options);
175136
175136
  const { redisEnabled, positionSyncInterval } = await initializeServer(config2);
175137
175137
  const server = Bun.serve({
175138
175138
  port: config2.port,
@@ -182750,12 +182750,17 @@ var timebackXpEvents = pgTable("timeback_xp_event", {
182750
182750
  }, (table) => [uniqueIndex("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
182751
182751
  var gameTimebackIntegrations = pgTable("game_timeback_integrations", {
182752
182752
  id: uuid("id").primaryKey().defaultRandom(),
182753
- gameId: uuid("game_id").notNull().unique().references(() => games.id, { onDelete: "cascade" }),
182753
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
182754
182754
  courseId: text("course_id").notNull(),
182755
+ grade: integer("grade").notNull(),
182756
+ subject: text("subject").notNull(),
182757
+ totalXp: integer("total_xp"),
182755
182758
  lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
182756
182759
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
182757
182760
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
182758
- });
182761
+ }, (table) => [
182762
+ uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table.gameId, table.grade, table.subject)
182763
+ ]);
182759
182764
  var achievementScopeEnum = pgEnum("achievement_scope", [
182760
182765
  "daily",
182761
182766
  "weekly",
@@ -189292,6 +189297,290 @@ var __export3 = (target, all) => {
189292
189297
  set: (newValue) => all[name3] = () => newValue
189293
189298
  });
189294
189299
  };
189300
+ var __esm3 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
189301
+ var init_auth = () => {};
189302
+ var PLAYCADEMY_BASE_URLS;
189303
+ var init_domains = __esm3(() => {
189304
+ PLAYCADEMY_BASE_URLS = {
189305
+ production: "https://hub.playcademy.net",
189306
+ staging: "https://hub.dev.playcademy.net"
189307
+ };
189308
+ });
189309
+ var init_env_vars = () => {};
189310
+ var ITEM_SLUGS2;
189311
+ var CURRENCIES2;
189312
+ var BADGES2;
189313
+ var init_overworld = __esm3(() => {
189314
+ ITEM_SLUGS2 = {
189315
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
189316
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
189317
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
189318
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
189319
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
189320
+ COMMON_SWORD: "COMMON_SWORD",
189321
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
189322
+ SMALL_BACKPACK: "SMALL_BACKPACK",
189323
+ LAVA_LAMP: "LAVA_LAMP",
189324
+ BOOMBOX: "BOOMBOX",
189325
+ CABIN_BED: "CABIN_BED"
189326
+ };
189327
+ CURRENCIES2 = {
189328
+ PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
189329
+ XP: ITEM_SLUGS2.PLAYCADEMY_XP
189330
+ };
189331
+ BADGES2 = {
189332
+ FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
189333
+ EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
189334
+ FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
189335
+ };
189336
+ });
189337
+ var init_timeback = () => {};
189338
+ var init_workers = () => {};
189339
+ var init_src2 = __esm3(() => {
189340
+ init_auth();
189341
+ init_domains();
189342
+ init_env_vars();
189343
+ init_overworld();
189344
+ init_timeback();
189345
+ init_workers();
189346
+ });
189347
+ var TIMEBACK_API_URLS;
189348
+ var TIMEBACK_AUTH_URLS;
189349
+ var CALIPER_API_URLS;
189350
+ var ONEROSTER_ENDPOINTS;
189351
+ var CALIPER_ENDPOINTS;
189352
+ var createOneRosterUrls = (baseUrl) => {
189353
+ const effective = baseUrl || TIMEBACK_API_URLS.production;
189354
+ const base = effective.replace(/\/$/, "");
189355
+ return {
189356
+ user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
189357
+ course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
189358
+ componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
189359
+ };
189360
+ };
189361
+ var CALIPER_CONSTANTS;
189362
+ var TIMEBACK_EVENT_TYPES;
189363
+ var TIMEBACK_ACTIONS;
189364
+ var TIMEBACK_TYPES;
189365
+ var ACTIVITY_METRIC_TYPES;
189366
+ var TIME_METRIC_TYPES;
189367
+ var TIMEBACK_SUBJECTS;
189368
+ var TIMEBACK_GRADE_LEVELS;
189369
+ var TIMEBACK_GRADE_LEVEL_LABELS;
189370
+ var CALIPER_SUBJECTS;
189371
+ var ONEROSTER_STATUS;
189372
+ var SCORE_STATUS;
189373
+ var ENV_VARS;
189374
+ var HTTP_DEFAULTS;
189375
+ var AUTH_DEFAULTS;
189376
+ var CACHE_DEFAULTS;
189377
+ var CONFIG_DEFAULTS;
189378
+ var DEFAULT_PLAYCADEMY_ORGANIZATION_ID;
189379
+ var PLAYCADEMY_DEFAULTS;
189380
+ var RESOURCE_DEFAULTS;
189381
+ var HTTP_STATUS;
189382
+ var ERROR_NAMES;
189383
+ var init_constants = __esm3(() => {
189384
+ init_src2();
189385
+ TIMEBACK_API_URLS = {
189386
+ production: "https://api.alpha-1edtech.ai",
189387
+ staging: "https://api.staging.alpha-1edtech.com"
189388
+ };
189389
+ TIMEBACK_AUTH_URLS = {
189390
+ production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
189391
+ staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
189392
+ };
189393
+ CALIPER_API_URLS = {
189394
+ production: "https://caliper.alpha-1edtech.ai",
189395
+ staging: "https://caliper-staging.alpha-1edtech.com"
189396
+ };
189397
+ ONEROSTER_ENDPOINTS = {
189398
+ organizations: "/ims/oneroster/rostering/v1p2/orgs",
189399
+ courses: "/ims/oneroster/rostering/v1p2/courses",
189400
+ courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
189401
+ resources: "/ims/oneroster/resources/v1p2/resources",
189402
+ componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
189403
+ classes: "/ims/oneroster/rostering/v1p2/classes",
189404
+ enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
189405
+ assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
189406
+ assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
189407
+ users: "/ims/oneroster/rostering/v1p2/users"
189408
+ };
189409
+ CALIPER_ENDPOINTS = {
189410
+ events: "/caliper/event",
189411
+ validate: "/caliper/event/validate"
189412
+ };
189413
+ CALIPER_CONSTANTS = {
189414
+ context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
189415
+ profile: "TimebackProfile",
189416
+ dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
189417
+ };
189418
+ TIMEBACK_EVENT_TYPES = {
189419
+ activityEvent: "ActivityEvent",
189420
+ timeSpentEvent: "TimeSpentEvent"
189421
+ };
189422
+ TIMEBACK_ACTIONS = {
189423
+ completed: "Completed",
189424
+ spentTime: "SpentTime"
189425
+ };
189426
+ TIMEBACK_TYPES = {
189427
+ user: "TimebackUser",
189428
+ activityContext: "TimebackActivityContext",
189429
+ activityMetricsCollection: "TimebackActivityMetricsCollection",
189430
+ timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
189431
+ };
189432
+ ACTIVITY_METRIC_TYPES = {
189433
+ totalQuestions: "totalQuestions",
189434
+ correctQuestions: "correctQuestions",
189435
+ xpEarned: "xpEarned",
189436
+ masteredUnits: "masteredUnits"
189437
+ };
189438
+ TIME_METRIC_TYPES = {
189439
+ active: "active",
189440
+ inactive: "inactive",
189441
+ waste: "waste",
189442
+ unknown: "unknown",
189443
+ antiPattern: "anti-pattern"
189444
+ };
189445
+ TIMEBACK_SUBJECTS = [
189446
+ "Math",
189447
+ "FastMath",
189448
+ "Science",
189449
+ "Social Studies",
189450
+ "Language",
189451
+ "Reading",
189452
+ "Vocabulary",
189453
+ "Writing"
189454
+ ];
189455
+ TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
189456
+ TIMEBACK_GRADE_LEVEL_LABELS = {
189457
+ "-1": "pre-k",
189458
+ "0": "kindergarten",
189459
+ "1": "1st grade",
189460
+ "2": "2nd grade",
189461
+ "3": "3rd grade",
189462
+ "4": "4th grade",
189463
+ "5": "5th grade",
189464
+ "6": "6th grade",
189465
+ "7": "7th grade",
189466
+ "8": "8th grade",
189467
+ "9": "9th grade",
189468
+ "10": "10th grade",
189469
+ "11": "11th grade",
189470
+ "12": "12th grade",
189471
+ "13": "AP"
189472
+ };
189473
+ CALIPER_SUBJECTS = {
189474
+ Reading: "Reading",
189475
+ Language: "Language",
189476
+ Vocabulary: "Vocabulary",
189477
+ SocialStudies: "Social Studies",
189478
+ Writing: "Writing",
189479
+ Science: "Science",
189480
+ FastMath: "FastMath",
189481
+ Math: "Math",
189482
+ None: "None"
189483
+ };
189484
+ ONEROSTER_STATUS = {
189485
+ active: "active",
189486
+ toBeDeleted: "tobedeleted"
189487
+ };
189488
+ SCORE_STATUS = {
189489
+ exempt: "exempt",
189490
+ fullyGraded: "fully graded",
189491
+ notSubmitted: "not submitted",
189492
+ partiallyGraded: "partially graded",
189493
+ submitted: "submitted"
189494
+ };
189495
+ ENV_VARS = {
189496
+ clientId: "TIMEBACK_CLIENT_ID",
189497
+ clientSecret: "TIMEBACK_CLIENT_SECRET",
189498
+ baseUrl: "TIMEBACK_BASE_URL",
189499
+ environment: "TIMEBACK_ENVIRONMENT",
189500
+ vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
189501
+ launchBaseUrl: "GAME_URL"
189502
+ };
189503
+ HTTP_DEFAULTS = {
189504
+ timeout: 30000,
189505
+ retries: 3,
189506
+ retryBackoffBase: 2
189507
+ };
189508
+ AUTH_DEFAULTS = {
189509
+ tokenCacheDuration: 50000
189510
+ };
189511
+ CACHE_DEFAULTS = {
189512
+ defaultTTL: 600000,
189513
+ defaultMaxSize: 500,
189514
+ defaultName: "TimebackCache",
189515
+ studentTTL: 600000,
189516
+ studentMaxSize: 500,
189517
+ assessmentTTL: 1800000,
189518
+ assessmentMaxSize: 200
189519
+ };
189520
+ CONFIG_DEFAULTS = {
189521
+ fileNames: ["timeback.config.js", "timeback.config.json"]
189522
+ };
189523
+ DEFAULT_PLAYCADEMY_ORGANIZATION_ID = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
189524
+ PLAYCADEMY_DEFAULTS = {
189525
+ organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID,
189526
+ launchBaseUrls: PLAYCADEMY_BASE_URLS
189527
+ };
189528
+ RESOURCE_DEFAULTS = {
189529
+ organization: {
189530
+ name: "Playcademy Studios",
189531
+ type: "department"
189532
+ },
189533
+ course: {
189534
+ gradingScheme: "STANDARD",
189535
+ level: {
189536
+ elementary: "Elementary",
189537
+ middle: "Middle",
189538
+ high: "High",
189539
+ ap: "AP"
189540
+ },
189541
+ metadata: {
189542
+ goals: {
189543
+ dailyXp: 50,
189544
+ dailyLessons: 3
189545
+ },
189546
+ metrics: {
189547
+ totalXp: 1000,
189548
+ totalLessons: 50
189549
+ }
189550
+ }
189551
+ },
189552
+ component: {
189553
+ sortOrder: 1,
189554
+ prerequisiteCriteria: "ALL"
189555
+ },
189556
+ resource: {
189557
+ vendorId: "playcademy",
189558
+ roles: ["primary"],
189559
+ importance: "primary",
189560
+ metadata: {
189561
+ type: "interactive",
189562
+ toolProvider: "Playcademy",
189563
+ instructionalMethod: "exploratory",
189564
+ language: "en-US"
189565
+ }
189566
+ },
189567
+ componentResource: {
189568
+ sortOrder: 1,
189569
+ lessonType: "quiz"
189570
+ }
189571
+ };
189572
+ HTTP_STATUS = {
189573
+ CLIENT_ERROR_MIN: 400,
189574
+ CLIENT_ERROR_MAX: 500,
189575
+ SERVER_ERROR_MIN: 500
189576
+ };
189577
+ ERROR_NAMES = {
189578
+ timebackAuth: "TimebackAuthError",
189579
+ timebackApi: "TimebackApiError",
189580
+ timebackConfig: "TimebackConfigError",
189581
+ timebackSdk: "TimebackSDKError"
189582
+ };
189583
+ });
189295
189584
  function deriveSourcedIds(courseId) {
189296
189585
  return {
189297
189586
  course: courseId,
@@ -189307,7 +189596,8 @@ __export3(exports_verify, {
189307
189596
  });
189308
189597
  async function fetchTimebackConfig(client2, courseId) {
189309
189598
  const sourcedIds = deriveSourcedIds(courseId);
189310
- const [course, component, resource, componentResource] = await Promise.all([
189599
+ const [org, course, component, resource, componentResource] = await Promise.all([
189600
+ client2.oneroster.organizations.get(PLAYCADEMY_DEFAULTS.organization),
189311
189601
  client2.oneroster.courses.get(sourcedIds.course),
189312
189602
  client2.oneroster.courseComponents.get(sourcedIds.component),
189313
189603
  client2.oneroster.resources.get(sourcedIds.resource),
@@ -189315,9 +189605,9 @@ async function fetchTimebackConfig(client2, courseId) {
189315
189605
  ]);
189316
189606
  return {
189317
189607
  organization: {
189318
- name: "Playcademy Studios",
189319
- type: "department",
189320
- identifier: "PLAYCADEMY"
189608
+ name: org.name,
189609
+ type: org.type,
189610
+ identifier: org.identifier || PLAYCADEMY_DEFAULTS.organization
189321
189611
  },
189322
189612
  course: {
189323
189613
  title: course.title || "",
@@ -189377,145 +189667,10 @@ async function verifyTimebackResources(client2, courseId) {
189377
189667
  }
189378
189668
  };
189379
189669
  }
189380
- var init_verify5 = () => {};
189381
- var ITEM_SLUGS2 = {
189382
- PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
189383
- PLAYCADEMY_XP: "PLAYCADEMY_XP",
189384
- FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
189385
- EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
189386
- FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
189387
- COMMON_SWORD: "COMMON_SWORD",
189388
- SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
189389
- SMALL_BACKPACK: "SMALL_BACKPACK",
189390
- LAVA_LAMP: "LAVA_LAMP",
189391
- BOOMBOX: "BOOMBOX",
189392
- CABIN_BED: "CABIN_BED"
189393
- };
189394
- var CURRENCIES2 = {
189395
- PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
189396
- XP: ITEM_SLUGS2.PLAYCADEMY_XP
189397
- };
189398
- var BADGES2 = {
189399
- FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
189400
- EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
189401
- FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
189402
- };
189403
- var TIMEBACK_API_URLS = {
189404
- production: "https://api.alpha-1edtech.ai",
189405
- staging: "https://api.staging.alpha-1edtech.com"
189406
- };
189407
- var TIMEBACK_AUTH_URLS = {
189408
- production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
189409
- staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
189410
- };
189411
- var CALIPER_API_URLS = {
189412
- production: "https://caliper.alpha-1edtech.ai",
189413
- staging: "https://caliper-staging.alpha-1edtech.com"
189414
- };
189415
- var ONEROSTER_ENDPOINTS = {
189416
- organizations: "/ims/oneroster/rostering/v1p2/orgs",
189417
- courses: "/ims/oneroster/rostering/v1p2/courses",
189418
- courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
189419
- resources: "/ims/oneroster/resources/v1p2/resources",
189420
- componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
189421
- classes: "/ims/oneroster/rostering/v1p2/classes",
189422
- enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
189423
- assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
189424
- assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
189425
- users: "/ims/oneroster/rostering/v1p2/users"
189426
- };
189427
- var CALIPER_ENDPOINTS = {
189428
- events: "/caliper/event",
189429
- validate: "/caliper/event/validate"
189430
- };
189431
- var createOneRosterUrls = (baseUrl) => {
189432
- const effective = baseUrl || TIMEBACK_API_URLS.production;
189433
- const base = effective.replace(/\/$/, "");
189434
- return {
189435
- user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
189436
- course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
189437
- componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
189438
- };
189439
- };
189440
- var CALIPER_CONSTANTS = {
189441
- context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
189442
- profile: "TimebackProfile",
189443
- dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
189444
- };
189445
- var TIMEBACK_EVENT_TYPES = {
189446
- activityEvent: "ActivityEvent",
189447
- timeSpentEvent: "TimeSpentEvent"
189448
- };
189449
- var TIMEBACK_ACTIONS = {
189450
- completed: "Completed",
189451
- spentTime: "SpentTime"
189452
- };
189453
- var TIMEBACK_TYPES = {
189454
- user: "TimebackUser",
189455
- activityContext: "TimebackActivityContext",
189456
- activityMetricsCollection: "TimebackActivityMetricsCollection",
189457
- timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
189458
- };
189459
- var ACTIVITY_METRIC_TYPES = {
189460
- totalQuestions: "totalQuestions",
189461
- correctQuestions: "correctQuestions",
189462
- xpEarned: "xpEarned",
189463
- masteredUnits: "masteredUnits"
189464
- };
189465
- var TIME_METRIC_TYPES = {
189466
- active: "active",
189467
- inactive: "inactive",
189468
- waste: "waste",
189469
- unknown: "unknown",
189470
- antiPattern: "anti-pattern"
189471
- };
189472
- var ONEROSTER_STATUS = {
189473
- active: "active",
189474
- toBeDeleted: "tobedeleted"
189475
- };
189476
- var SCORE_STATUS = {
189477
- exempt: "exempt",
189478
- fullyGraded: "fully graded",
189479
- notSubmitted: "not submitted",
189480
- partiallyGraded: "partially graded",
189481
- submitted: "submitted"
189482
- };
189483
- var ENV_VARS = {
189484
- clientId: "TIMEBACK_CLIENT_ID",
189485
- clientSecret: "TIMEBACK_CLIENT_SECRET",
189486
- baseUrl: "TIMEBACK_BASE_URL",
189487
- environment: "TIMEBACK_ENVIRONMENT",
189488
- vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
189489
- launchBaseUrl: "GAME_URL"
189490
- };
189491
- var HTTP_DEFAULTS = {
189492
- timeout: 30000,
189493
- retries: 3,
189494
- retryBackoffBase: 2
189495
- };
189496
- var AUTH_DEFAULTS = {
189497
- tokenCacheDuration: 50000
189498
- };
189499
- var CACHE_DEFAULTS = {
189500
- defaultTTL: 600000,
189501
- defaultMaxSize: 500,
189502
- defaultName: "TimebackCache",
189503
- studentTTL: 600000,
189504
- studentMaxSize: 500,
189505
- assessmentTTL: 1800000,
189506
- assessmentMaxSize: 200
189507
- };
189508
- var HTTP_STATUS = {
189509
- CLIENT_ERROR_MIN: 400,
189510
- CLIENT_ERROR_MAX: 500,
189511
- SERVER_ERROR_MIN: 500
189512
- };
189513
- var ERROR_NAMES = {
189514
- timebackAuth: "TimebackAuthError",
189515
- timebackApi: "TimebackApiError",
189516
- timebackConfig: "TimebackConfigError",
189517
- timebackSdk: "TimebackSDKError"
189518
- };
189670
+ var init_verify5 = __esm3(() => {
189671
+ init_constants();
189672
+ });
189673
+ init_constants();
189519
189674
 
189520
189675
  class TimebackError extends Error {
189521
189676
  constructor(message22) {
@@ -190026,6 +190181,8 @@ async function updateTimebackResources(client2, courseId, config2) {
190026
190181
  updateComponentResourceLink(client2, config2, sourcedIds)
190027
190182
  ]);
190028
190183
  }
190184
+ init_constants();
190185
+ init_constants();
190029
190186
  if (process.env.DEBUG === "true") {
190030
190187
  process.env.TERM = "dumb";
190031
190188
  }
@@ -190125,6 +190282,7 @@ async function getTimebackTokenResponse(config2) {
190125
190282
  function getAuthUrl(environment = "production") {
190126
190283
  return TIMEBACK_AUTH_URLS[environment];
190127
190284
  }
190285
+ init_constants();
190128
190286
  async function request({
190129
190287
  path: path22,
190130
190288
  baseUrl,
@@ -190230,6 +190388,7 @@ async function requestCaliper(options) {
190230
190388
  baseUrl: caliperBase
190231
190389
  });
190232
190390
  }
190391
+ init_constants();
190233
190392
  function logTimebackError(operation, error2, context) {
190234
190393
  const errorMessage = error2 instanceof Error ? error2.message : String(error2);
190235
190394
  if (error2 instanceof TimebackApiError) {
@@ -190265,6 +190424,17 @@ function createOneRosterNamespace(client2) {
190265
190424
  listByCourse: async (courseSourcedId) => {
190266
190425
  const res = await client2["request"](`${ONEROSTER_ENDPOINTS.courses}/${courseSourcedId}/classes`, "GET");
190267
190426
  return res.classes;
190427
+ },
190428
+ listByStudent: async (userSourcedId, options) => {
190429
+ const queryParams = new URLSearchParams;
190430
+ if (options?.limit)
190431
+ queryParams.set("limit", String(options.limit));
190432
+ if (options?.offset)
190433
+ queryParams.set("offset", String(options.offset));
190434
+ const endpoint = `${ONEROSTER_ENDPOINTS.users}/${userSourcedId}/classes`;
190435
+ const url = queryParams.toString() ? `${endpoint}?${queryParams}` : endpoint;
190436
+ const res = await client2["request"](url, "GET");
190437
+ return res.classes || [];
190268
190438
  }
190269
190439
  },
190270
190440
  organizations: {
@@ -190272,7 +190442,7 @@ function createOneRosterNamespace(client2) {
190272
190442
  return client2["request"](ONEROSTER_ENDPOINTS.organizations, "POST", data);
190273
190443
  },
190274
190444
  get: async (sourcedId) => {
190275
- return client2["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET");
190445
+ return client2["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET").then((res) => res.org);
190276
190446
  },
190277
190447
  update: async (sourcedId, data) => {
190278
190448
  return client2["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "PUT", data);
@@ -190400,7 +190570,7 @@ function createOneRosterNamespace(client2) {
190400
190570
  return client2["request"](ONEROSTER_ENDPOINTS.users, "POST", { user: data });
190401
190571
  },
190402
190572
  get: async (sourcedId) => {
190403
- return client2["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET");
190573
+ return client2["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET").then((res) => res.user);
190404
190574
  },
190405
190575
  findByEmail: async (email) => {
190406
190576
  const params = new URLSearchParams({ filter: `email='${email}'` });
@@ -190416,6 +190586,7 @@ function createOneRosterNamespace(client2) {
190416
190586
  }
190417
190587
  };
190418
190588
  }
190589
+ init_constants();
190419
190590
  function createCaliperNamespace(client2) {
190420
190591
  const urls = createOneRosterUrls(client2.getBaseUrl());
190421
190592
  const caliper = {
@@ -190453,7 +190624,7 @@ function createCaliperNamespace(client2) {
190453
190624
  },
190454
190625
  action: TIMEBACK_ACTIONS.completed,
190455
190626
  object: {
190456
- id: urls.componentResource(data.activityId),
190627
+ id: caliper.buildActivityUrl(data),
190457
190628
  type: TIMEBACK_TYPES.activityContext,
190458
190629
  subject: data.subject,
190459
190630
  app: {
@@ -190463,7 +190634,7 @@ function createCaliperNamespace(client2) {
190463
190634
  name: data.activityName
190464
190635
  },
190465
190636
  course: { id: urls.course(data.courseId), name: data.activityName },
190466
- process: true
190637
+ process: false
190467
190638
  },
190468
190639
  generated: {
190469
190640
  id: `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
@@ -190489,7 +190660,8 @@ function createCaliperNamespace(client2) {
190489
190660
  value: data.masteredUnits
190490
190661
  }
190491
190662
  ] : []
190492
- ]
190663
+ ],
190664
+ ...data.extensions ? { extensions: data.extensions } : {}
190493
190665
  }
190494
190666
  };
190495
190667
  return caliper.emit(event, data.sensorUrl);
@@ -190508,7 +190680,7 @@ function createCaliperNamespace(client2) {
190508
190680
  },
190509
190681
  action: TIMEBACK_ACTIONS.spentTime,
190510
190682
  object: {
190511
- id: urls.componentResource(data.activityId),
190683
+ id: caliper.buildActivityUrl(data),
190512
190684
  type: TIMEBACK_TYPES.activityContext,
190513
190685
  subject: data.subject,
190514
190686
  app: {
@@ -190536,10 +190708,33 @@ function createCaliperNamespace(client2) {
190536
190708
  }
190537
190709
  };
190538
190710
  return caliper.emit(event, data.sensorUrl);
190711
+ },
190712
+ buildActivityUrl: (data) => {
190713
+ const base = data.sensorUrl.replace(/\/$/, "");
190714
+ return `${base}/activities/${data.courseId}/${data.activityId}/${crypto.randomUUID()}`;
190539
190715
  }
190540
190716
  };
190541
190717
  return caliper;
190542
190718
  }
190719
+ function createEduBridgeNamespace(client2) {
190720
+ const enrollments = {
190721
+ listByUser: async (userId) => {
190722
+ const response = await client2["request"](`/edubridge/enrollments/user/${userId}`, "GET");
190723
+ return response.data;
190724
+ }
190725
+ };
190726
+ const analytics = {
190727
+ getEnrollmentFacts: async (enrollmentId) => {
190728
+ return client2["request"](`/edubridge/analytics/enrollment/${enrollmentId}`, "GET");
190729
+ }
190730
+ };
190731
+ return {
190732
+ enrollments,
190733
+ analytics
190734
+ };
190735
+ }
190736
+ init_constants();
190737
+ init_constants();
190543
190738
 
190544
190739
  class TimebackCache {
190545
190740
  cache = new Map;
@@ -190645,6 +190840,7 @@ class TimebackCache {
190645
190840
  class TimebackCacheManager {
190646
190841
  studentCache;
190647
190842
  assessmentLineItemCache;
190843
+ resourceMasteryCache;
190648
190844
  constructor() {
190649
190845
  this.studentCache = new TimebackCache({
190650
190846
  defaultTTL: CACHE_DEFAULTS.studentTTL,
@@ -190656,6 +190852,11 @@ class TimebackCacheManager {
190656
190852
  maxSize: CACHE_DEFAULTS.assessmentMaxSize,
190657
190853
  name: "AssessmentLineItemCache"
190658
190854
  });
190855
+ this.resourceMasteryCache = new TimebackCache({
190856
+ defaultTTL: CACHE_DEFAULTS.assessmentTTL,
190857
+ maxSize: CACHE_DEFAULTS.assessmentMaxSize,
190858
+ name: "ResourceMasteryCache"
190859
+ });
190659
190860
  }
190660
190861
  getStudent(key) {
190661
190862
  return this.studentCache.get(key);
@@ -190669,6 +190870,12 @@ class TimebackCacheManager {
190669
190870
  setAssessmentLineItem(key, lineItemId) {
190670
190871
  this.assessmentLineItemCache.set(key, lineItemId);
190671
190872
  }
190873
+ getResourceMasterableUnits(key) {
190874
+ return this.resourceMasteryCache.get(key);
190875
+ }
190876
+ setResourceMasterableUnits(key, value) {
190877
+ this.resourceMasteryCache.set(key, value);
190878
+ }
190672
190879
  clearAll() {
190673
190880
  this.studentCache.clear();
190674
190881
  this.assessmentLineItemCache.clear();
@@ -190677,18 +190884,32 @@ class TimebackCacheManager {
190677
190884
  getStats() {
190678
190885
  return {
190679
190886
  studentCache: this.studentCache.stats(),
190680
- assessmentLineItemCache: this.assessmentLineItemCache.stats()
190887
+ assessmentLineItemCache: this.assessmentLineItemCache.stats(),
190888
+ resourceMasteryCache: this.resourceMasteryCache.stats()
190681
190889
  };
190682
190890
  }
190683
190891
  cleanup() {
190684
190892
  this.studentCache.cleanup();
190685
190893
  this.assessmentLineItemCache.cleanup();
190894
+ this.resourceMasteryCache.cleanup();
190686
190895
  log32.debug("[TimebackCacheManager] Cache cleanup completed");
190687
190896
  }
190688
190897
  }
190689
190898
  function kebabToTitleCase(kebabStr) {
190690
190899
  return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
190691
190900
  }
190901
+ init_constants();
190902
+ init_constants();
190903
+ var isObject2 = (value) => typeof value === "object" && value !== null;
190904
+ function isPlaycademyResourceMetadata(value) {
190905
+ if (!isObject2(value)) {
190906
+ return false;
190907
+ }
190908
+ if (!("mastery" in value) || value.mastery === undefined) {
190909
+ return true;
190910
+ }
190911
+ return isObject2(value.mastery);
190912
+ }
190692
190913
  function validateProgressData(progressData) {
190693
190914
  if (!progressData.subject) {
190694
190915
  throw new ConfigurationError("subject", "Subject is required for Caliper events. Provide it in progressData.subject");
@@ -190742,36 +190963,47 @@ class ProgressRecorder {
190742
190963
  cacheManager;
190743
190964
  onerosterNamespace;
190744
190965
  caliperNamespace;
190745
- constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace) {
190966
+ edubridgeNamespace;
190967
+ constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace, edubridgeNamespace) {
190746
190968
  this.studentResolver = studentResolver;
190747
190969
  this.cacheManager = cacheManager;
190748
190970
  this.onerosterNamespace = onerosterNamespace;
190749
190971
  this.caliperNamespace = caliperNamespace;
190972
+ this.edubridgeNamespace = edubridgeNamespace;
190750
190973
  }
190751
190974
  async record(courseId, studentIdentifier, progressData) {
190752
190975
  validateProgressData(progressData);
190753
- const ids = deriveSourcedIds(courseId);
190754
- const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
190755
- const activityName = progressData.activityName || kebabToTitleCase(activityId);
190756
- const classId = progressData.classId;
190757
- const courseName = progressData.courseName || "Game Course";
190758
- const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
190976
+ const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
190759
190977
  const { id: studentId, email: studentEmail } = student;
190760
190978
  const { score, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber } = progressData;
190761
- const lineItemId = `${activityId}-assessment`;
190762
- let actualLineItemId = this.cacheManager.getAssessmentLineItem(lineItemId);
190763
- if (!actualLineItemId) {
190764
- actualLineItemId = await this.getOrCreateLineItem(lineItemId, activityName, classId, ids);
190765
- this.cacheManager.setAssessmentLineItem(lineItemId, actualLineItemId);
190766
- }
190767
- let currentAttemptNumber = attemptNumber || 1;
190979
+ const actualLineItemId = await this.resolveAssessmentLineItem(activityId, activityName, progressData.classId, ids);
190980
+ const currentAttemptNumber = await this.resolveAttemptNumber(attemptNumber, score, studentId, actualLineItemId);
190768
190981
  const isFirstAttempt = currentAttemptNumber === 1;
190769
- if (!attemptNumber && score !== undefined) {
190770
- currentAttemptNumber = await this.determineAttemptNumber(studentId, actualLineItemId);
190771
- }
190772
190982
  const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt);
190983
+ let extensions = progressData.extensions;
190984
+ const completionProgress = await this.buildCompletionProgress({
190985
+ studentId,
190986
+ courseId,
190987
+ progressData,
190988
+ resourceId: ids.resource
190989
+ });
190990
+ let pctCompleteApp;
190991
+ let masteryAchieved = false;
190992
+ let scoreStatus = SCORE_STATUS.partiallyGraded;
190993
+ const inProgress = "false";
190994
+ if (completionProgress) {
190995
+ masteryAchieved = completionProgress.masteryAchieved;
190996
+ pctCompleteApp = completionProgress.pctCompleteApp;
190997
+ extensions = {
190998
+ ...extensions || {},
190999
+ ...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
191000
+ };
191001
+ if (masteryAchieved) {
191002
+ scoreStatus = SCORE_STATUS.fullyGraded;
191003
+ }
191004
+ }
190773
191005
  if (score !== undefined) {
190774
- await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp);
191006
+ await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp, masteredUnits, scoreStatus, inProgress, progressData.appName);
190775
191007
  } else {
190776
191008
  log32.warn("[ProgressRecorder] Score not provided, skipping gradebook entry", {
190777
191009
  studentId,
@@ -190779,12 +191011,151 @@ class ProgressRecorder {
190779
191011
  attemptNumber: currentAttemptNumber
190780
191012
  });
190781
191013
  }
190782
- await this.emitCaliperEvent(studentId, studentEmail, activityId, activityName, ids.course, courseName, totalQuestions, correctQuestions, calculatedXp, masteredUnits, currentAttemptNumber, progressData);
191014
+ await this.emitCaliperEvent(studentId, studentEmail, activityId, activityName, ids.course, courseName, totalQuestions, correctQuestions, calculatedXp, masteredUnits, currentAttemptNumber, progressData, extensions);
190783
191015
  return {
190784
191016
  xpAwarded: calculatedXp,
190785
- attemptNumber: currentAttemptNumber
191017
+ attemptNumber: currentAttemptNumber,
191018
+ masteredUnitsApplied: progressData.masteredUnits ?? 0,
191019
+ pctCompleteApp,
191020
+ scoreStatus,
191021
+ inProgress
190786
191022
  };
190787
191023
  }
191024
+ async resolveContext(courseId, studentIdentifier, progressData) {
191025
+ const ids = deriveSourcedIds(courseId);
191026
+ const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
191027
+ const activityName = progressData.activityName || kebabToTitleCase(activityId);
191028
+ const courseName = progressData.courseName || "Game Course";
191029
+ const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
191030
+ return { ids, activityId, activityName, courseName, student };
191031
+ }
191032
+ async resolveAssessmentLineItem(activityId, activityName, classId, ids) {
191033
+ const lineItemId = `${ids.course}-${activityId}-assessment`;
191034
+ let actualLineItemId = this.cacheManager.getAssessmentLineItem(lineItemId);
191035
+ if (!actualLineItemId) {
191036
+ actualLineItemId = await this.getOrCreateLineItem(lineItemId, activityName, classId, ids);
191037
+ this.cacheManager.setAssessmentLineItem(lineItemId, actualLineItemId);
191038
+ }
191039
+ return actualLineItemId;
191040
+ }
191041
+ async resolveAttemptNumber(providedAttemptNumber, score, studentId, lineItemId) {
191042
+ if (providedAttemptNumber)
191043
+ return providedAttemptNumber;
191044
+ if (score !== undefined) {
191045
+ return this.determineAttemptNumber(studentId, lineItemId);
191046
+ }
191047
+ return 1;
191048
+ }
191049
+ calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt) {
191050
+ if (xpEarned !== undefined) {
191051
+ log32.debug("[ProgressRecorder] Using provided XP", { xpEarned });
191052
+ return xpEarned;
191053
+ }
191054
+ if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
191055
+ const accuracy = correctQuestions / totalQuestions;
191056
+ const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
191057
+ log32.debug("[ProgressRecorder] Calculated XP", {
191058
+ durationSeconds: progressData.sessionDurationSeconds,
191059
+ accuracy,
191060
+ isFirstAttempt,
191061
+ calculatedXp
191062
+ });
191063
+ return calculatedXp;
191064
+ }
191065
+ return 0;
191066
+ }
191067
+ async buildCompletionProgress({
191068
+ studentId,
191069
+ courseId,
191070
+ progressData,
191071
+ resourceId
191072
+ }) {
191073
+ if (typeof progressData.masteredUnits !== "number" || progressData.masteredUnits <= 0) {
191074
+ return;
191075
+ }
191076
+ const masterableUnits = await this.resolveMasterableUnits(resourceId);
191077
+ if (!masterableUnits || masterableUnits <= 0) {
191078
+ log32.warn("[ProgressRecorder] No masterableUnits configured for course", {
191079
+ courseId,
191080
+ resourceId
191081
+ });
191082
+ return;
191083
+ }
191084
+ const facts = await this.fetchEnrollmentAnalyticsFacts(studentId, courseId);
191085
+ if (!facts) {
191086
+ log32.warn("[ProgressRecorder] Unable to retrieve analytics for mastery-based completion", { studentId, courseId });
191087
+ return;
191088
+ }
191089
+ const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
191090
+ const totalMastered = historicalMasteredUnits + progressData.masteredUnits;
191091
+ const rawPct = totalMastered / masterableUnits * 100;
191092
+ const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
191093
+ const masteryAchieved = totalMastered >= masterableUnits;
191094
+ return { pctCompleteApp, masteryAchieved };
191095
+ }
191096
+ async fetchEnrollmentAnalyticsFacts(studentId, courseId) {
191097
+ try {
191098
+ const enrollments = await this.edubridgeNamespace.enrollments.listByUser(studentId);
191099
+ const enrollment = enrollments.find((e2) => e2.course.id === courseId);
191100
+ if (!enrollment) {
191101
+ log32.warn("[ProgressRecorder] Enrollment not found for student/course", {
191102
+ studentId,
191103
+ courseId
191104
+ });
191105
+ return;
191106
+ }
191107
+ const analytics = await this.edubridgeNamespace.analytics.getEnrollmentFacts(enrollment.id);
191108
+ return analytics.facts;
191109
+ } catch (error2) {
191110
+ log32.error("[ProgressRecorder] Failed to load enrollment analytics facts", {
191111
+ studentId,
191112
+ courseId,
191113
+ error: error2
191114
+ });
191115
+ return;
191116
+ }
191117
+ }
191118
+ sumAnalyticsMetric(facts, metric) {
191119
+ if (!facts) {
191120
+ return 0;
191121
+ }
191122
+ let total = 0;
191123
+ Object.values(facts).forEach((dateFacts) => {
191124
+ Object.values(dateFacts).forEach((subjectFacts) => {
191125
+ const metrics = subjectFacts.activityMetrics;
191126
+ if (metrics && typeof metrics[metric] === "number") {
191127
+ total += metrics[metric];
191128
+ }
191129
+ });
191130
+ });
191131
+ return total;
191132
+ }
191133
+ async resolveMasterableUnits(resourceId) {
191134
+ if (!resourceId) {
191135
+ return;
191136
+ }
191137
+ const cached = this.cacheManager.getResourceMasterableUnits(resourceId);
191138
+ if (cached !== undefined) {
191139
+ return cached === null ? undefined : cached;
191140
+ }
191141
+ try {
191142
+ const resource = await this.onerosterNamespace.resources.get(resourceId);
191143
+ const playcademyMetadata = resource.metadata?.playcademy;
191144
+ if (!playcademyMetadata) {
191145
+ return;
191146
+ }
191147
+ const masterableUnits = isPlaycademyResourceMetadata(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
191148
+ this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
191149
+ return masterableUnits;
191150
+ } catch (error2) {
191151
+ log32.error("[ProgressRecorder] Failed to fetch resource metadata for mastery config", {
191152
+ resourceId,
191153
+ error: error2
191154
+ });
191155
+ this.cacheManager.setResourceMasterableUnits(resourceId, null);
191156
+ return;
191157
+ }
191158
+ }
190788
191159
  async getOrCreateLineItem(lineItemId, activityName, classId, ids) {
190789
191160
  try {
190790
191161
  const lineItem = await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
@@ -190823,25 +191194,7 @@ class ProgressRecorder {
190823
191194
  }
190824
191195
  return 1;
190825
191196
  }
190826
- calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt) {
190827
- if (xpEarned !== undefined) {
190828
- log32.debug("[ProgressRecorder] Using provided XP", { xpEarned });
190829
- return xpEarned;
190830
- }
190831
- if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
190832
- const accuracy = correctQuestions / totalQuestions;
190833
- const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
190834
- log32.debug("[ProgressRecorder] Calculated XP", {
190835
- durationSeconds: progressData.sessionDurationSeconds,
190836
- accuracy,
190837
- isFirstAttempt,
190838
- calculatedXp
190839
- });
190840
- return calculatedXp;
190841
- }
190842
- return 0;
190843
- }
190844
- async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp) {
191197
+ async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp, masteredUnits, scoreStatus, inProgress, appName) {
190845
191198
  const resultId = `${lineItemId}:${studentId}:attempt-${attemptNumber}`;
190846
191199
  await this.onerosterNamespace.assessmentResults.upsert(resultId, {
190847
191200
  sourcedId: resultId,
@@ -190850,18 +191203,21 @@ class ProgressRecorder {
190850
191203
  student: { sourcedId: studentId },
190851
191204
  score,
190852
191205
  scoreDate: new Date().toISOString(),
190853
- scoreStatus: SCORE_STATUS.fullyGraded,
191206
+ scoreStatus,
191207
+ inProgress,
190854
191208
  metadata: {
190855
191209
  xp,
190856
191210
  totalQuestions,
190857
191211
  correctQuestions,
190858
191212
  accuracy: totalQuestions && correctQuestions ? correctQuestions / totalQuestions * 100 : undefined,
190859
191213
  attemptNumber,
190860
- lastUpdated: new Date().toISOString()
191214
+ lastUpdated: new Date().toISOString(),
191215
+ masteredUnits,
191216
+ appName
190861
191217
  }
190862
191218
  });
190863
191219
  }
190864
- async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData) {
191220
+ async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData, extensions) {
190865
191221
  await this.caliperNamespace.emitActivityEvent({
190866
191222
  studentId,
190867
191223
  studentEmail,
@@ -190876,7 +191232,8 @@ class ProgressRecorder {
190876
191232
  attemptNumber,
190877
191233
  subject: progressData.subject,
190878
191234
  appName: progressData.appName,
190879
- sensorUrl: progressData.sensorUrl
191235
+ sensorUrl: progressData.sensorUrl,
191236
+ extensions: extensions || progressData.extensions
190880
191237
  }).catch((error2) => {
190881
191238
  log32.error("[ProgressRecorder] Failed to emit activity event", { error: error2 });
190882
191239
  });
@@ -194991,9 +195348,10 @@ class TimebackClient {
194991
195348
  };
194992
195349
  this.oneroster = createOneRosterNamespace(this);
194993
195350
  this.caliper = createCaliperNamespace(this);
195351
+ this.edubridge = createEduBridgeNamespace(this);
194994
195352
  this.cacheManager = new TimebackCacheManager;
194995
195353
  this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
194996
- this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper);
195354
+ this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, this.edubridge);
194997
195355
  this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
194998
195356
  if (this.credentials) {
194999
195357
  this._ensureAuthenticated().catch((error2) => {
@@ -195102,6 +195460,9 @@ class TimebackClient {
195102
195460
  }
195103
195461
  await this.authenticate();
195104
195462
  }
195463
+ async resolveStudent(studentIdentifier, providedEmail) {
195464
+ return this.studentResolver.resolve(studentIdentifier, providedEmail);
195465
+ }
195105
195466
  async recordProgress(courseId, studentIdentifier, progressData) {
195106
195467
  await this._ensureAuthenticated();
195107
195468
  return this.progressRecorder.record(courseId, studentIdentifier, progressData);
@@ -195110,6 +195471,19 @@ class TimebackClient {
195110
195471
  await this._ensureAuthenticated();
195111
195472
  return this.sessionRecorder.record(courseId, studentIdentifier, sessionData);
195112
195473
  }
195474
+ async getEnrollments(studentId, options) {
195475
+ await this._ensureAuthenticated();
195476
+ const classes = await this.oneroster.classes.listByStudent(studentId, options);
195477
+ return classes.filter((cls) => cls.sourcedId && cls.status && cls.course?.sourcedId).map((cls) => ({
195478
+ sourcedId: cls.sourcedId,
195479
+ title: cls.title,
195480
+ classCode: cls.classCode ?? null,
195481
+ courseId: cls.course.sourcedId,
195482
+ status: cls.status,
195483
+ grades: cls.grades ?? null,
195484
+ subjects: cls.subjects ?? null
195485
+ }));
195486
+ }
195113
195487
  clearCaches() {
195114
195488
  this.cacheManager.clearAll();
195115
195489
  }
@@ -195121,6 +195495,7 @@ class TimebackClient {
195121
195495
  }
195122
195496
  oneroster;
195123
195497
  caliper;
195498
+ edubridge;
195124
195499
  async setup(config2, options) {
195125
195500
  return setupTimebackResources(this, config2, options);
195126
195501
  }
@@ -195137,6 +195512,306 @@ class TimebackClient {
195137
195512
  return deleteTimebackResources(this, courseId);
195138
195513
  }
195139
195514
  }
195515
+ var __esm4 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
195516
+ var init_auth2 = () => {};
195517
+ var PLAYCADEMY_BASE_URLS2;
195518
+ var init_domains2 = __esm4(() => {
195519
+ PLAYCADEMY_BASE_URLS2 = {
195520
+ production: "https://hub.playcademy.net",
195521
+ staging: "https://hub.dev.playcademy.net"
195522
+ };
195523
+ });
195524
+ var init_env_vars2 = () => {};
195525
+ var ITEM_SLUGS3;
195526
+ var CURRENCIES3;
195527
+ var BADGES3;
195528
+ var init_overworld2 = __esm4(() => {
195529
+ ITEM_SLUGS3 = {
195530
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
195531
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
195532
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
195533
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
195534
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
195535
+ COMMON_SWORD: "COMMON_SWORD",
195536
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
195537
+ SMALL_BACKPACK: "SMALL_BACKPACK",
195538
+ LAVA_LAMP: "LAVA_LAMP",
195539
+ BOOMBOX: "BOOMBOX",
195540
+ CABIN_BED: "CABIN_BED"
195541
+ };
195542
+ CURRENCIES3 = {
195543
+ PRIMARY: ITEM_SLUGS3.PLAYCADEMY_CREDITS,
195544
+ XP: ITEM_SLUGS3.PLAYCADEMY_XP
195545
+ };
195546
+ BADGES3 = {
195547
+ FOUNDING_MEMBER: ITEM_SLUGS3.FOUNDING_MEMBER_BADGE,
195548
+ EARLY_ADOPTER: ITEM_SLUGS3.EARLY_ADOPTER_BADGE,
195549
+ FIRST_GAME: ITEM_SLUGS3.FIRST_GAME_BADGE
195550
+ };
195551
+ });
195552
+ var init_timeback2 = () => {};
195553
+ var init_workers2 = () => {};
195554
+ var init_src3 = __esm4(() => {
195555
+ init_auth2();
195556
+ init_domains2();
195557
+ init_env_vars2();
195558
+ init_overworld2();
195559
+ init_timeback2();
195560
+ init_workers2();
195561
+ });
195562
+ var TIMEBACK_API_URLS2;
195563
+ var TIMEBACK_AUTH_URLS2;
195564
+ var CALIPER_API_URLS2;
195565
+ var ONEROSTER_ENDPOINTS2;
195566
+ var CALIPER_ENDPOINTS2;
195567
+ var CALIPER_CONSTANTS2;
195568
+ var TIMEBACK_EVENT_TYPES2;
195569
+ var TIMEBACK_ACTIONS2;
195570
+ var TIMEBACK_TYPES2;
195571
+ var ACTIVITY_METRIC_TYPES2;
195572
+ var TIME_METRIC_TYPES2;
195573
+ var TIMEBACK_SUBJECTS2;
195574
+ var TIMEBACK_GRADE_LEVELS2;
195575
+ var TIMEBACK_GRADE_LEVEL_LABELS2;
195576
+ var CALIPER_SUBJECTS2;
195577
+ var ONEROSTER_STATUS2;
195578
+ var SCORE_STATUS2;
195579
+ var ENV_VARS2;
195580
+ var HTTP_DEFAULTS2;
195581
+ var AUTH_DEFAULTS2;
195582
+ var CACHE_DEFAULTS2;
195583
+ var CONFIG_DEFAULTS2;
195584
+ var DEFAULT_PLAYCADEMY_ORGANIZATION_ID2;
195585
+ var PLAYCADEMY_DEFAULTS2;
195586
+ var RESOURCE_DEFAULTS2;
195587
+ var HTTP_STATUS2;
195588
+ var ERROR_NAMES2;
195589
+ var init_constants2 = __esm4(() => {
195590
+ init_src3();
195591
+ TIMEBACK_API_URLS2 = {
195592
+ production: "https://api.alpha-1edtech.ai",
195593
+ staging: "https://api.staging.alpha-1edtech.com"
195594
+ };
195595
+ TIMEBACK_AUTH_URLS2 = {
195596
+ production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
195597
+ staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
195598
+ };
195599
+ CALIPER_API_URLS2 = {
195600
+ production: "https://caliper.alpha-1edtech.ai",
195601
+ staging: "https://caliper-staging.alpha-1edtech.com"
195602
+ };
195603
+ ONEROSTER_ENDPOINTS2 = {
195604
+ organizations: "/ims/oneroster/rostering/v1p2/orgs",
195605
+ courses: "/ims/oneroster/rostering/v1p2/courses",
195606
+ courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
195607
+ resources: "/ims/oneroster/resources/v1p2/resources",
195608
+ componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
195609
+ classes: "/ims/oneroster/rostering/v1p2/classes",
195610
+ enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
195611
+ assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
195612
+ assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
195613
+ users: "/ims/oneroster/rostering/v1p2/users"
195614
+ };
195615
+ CALIPER_ENDPOINTS2 = {
195616
+ events: "/caliper/event",
195617
+ validate: "/caliper/event/validate"
195618
+ };
195619
+ CALIPER_CONSTANTS2 = {
195620
+ context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
195621
+ profile: "TimebackProfile",
195622
+ dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
195623
+ };
195624
+ TIMEBACK_EVENT_TYPES2 = {
195625
+ activityEvent: "ActivityEvent",
195626
+ timeSpentEvent: "TimeSpentEvent"
195627
+ };
195628
+ TIMEBACK_ACTIONS2 = {
195629
+ completed: "Completed",
195630
+ spentTime: "SpentTime"
195631
+ };
195632
+ TIMEBACK_TYPES2 = {
195633
+ user: "TimebackUser",
195634
+ activityContext: "TimebackActivityContext",
195635
+ activityMetricsCollection: "TimebackActivityMetricsCollection",
195636
+ timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
195637
+ };
195638
+ ACTIVITY_METRIC_TYPES2 = {
195639
+ totalQuestions: "totalQuestions",
195640
+ correctQuestions: "correctQuestions",
195641
+ xpEarned: "xpEarned",
195642
+ masteredUnits: "masteredUnits"
195643
+ };
195644
+ TIME_METRIC_TYPES2 = {
195645
+ active: "active",
195646
+ inactive: "inactive",
195647
+ waste: "waste",
195648
+ unknown: "unknown",
195649
+ antiPattern: "anti-pattern"
195650
+ };
195651
+ TIMEBACK_SUBJECTS2 = [
195652
+ "Math",
195653
+ "FastMath",
195654
+ "Science",
195655
+ "Social Studies",
195656
+ "Language",
195657
+ "Reading",
195658
+ "Vocabulary",
195659
+ "Writing"
195660
+ ];
195661
+ TIMEBACK_GRADE_LEVELS2 = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
195662
+ TIMEBACK_GRADE_LEVEL_LABELS2 = {
195663
+ "-1": "pre-k",
195664
+ "0": "kindergarten",
195665
+ "1": "1st grade",
195666
+ "2": "2nd grade",
195667
+ "3": "3rd grade",
195668
+ "4": "4th grade",
195669
+ "5": "5th grade",
195670
+ "6": "6th grade",
195671
+ "7": "7th grade",
195672
+ "8": "8th grade",
195673
+ "9": "9th grade",
195674
+ "10": "10th grade",
195675
+ "11": "11th grade",
195676
+ "12": "12th grade",
195677
+ "13": "AP"
195678
+ };
195679
+ CALIPER_SUBJECTS2 = {
195680
+ Reading: "Reading",
195681
+ Language: "Language",
195682
+ Vocabulary: "Vocabulary",
195683
+ SocialStudies: "Social Studies",
195684
+ Writing: "Writing",
195685
+ Science: "Science",
195686
+ FastMath: "FastMath",
195687
+ Math: "Math",
195688
+ None: "None"
195689
+ };
195690
+ ONEROSTER_STATUS2 = {
195691
+ active: "active",
195692
+ toBeDeleted: "tobedeleted"
195693
+ };
195694
+ SCORE_STATUS2 = {
195695
+ exempt: "exempt",
195696
+ fullyGraded: "fully graded",
195697
+ notSubmitted: "not submitted",
195698
+ partiallyGraded: "partially graded",
195699
+ submitted: "submitted"
195700
+ };
195701
+ ENV_VARS2 = {
195702
+ clientId: "TIMEBACK_CLIENT_ID",
195703
+ clientSecret: "TIMEBACK_CLIENT_SECRET",
195704
+ baseUrl: "TIMEBACK_BASE_URL",
195705
+ environment: "TIMEBACK_ENVIRONMENT",
195706
+ vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
195707
+ launchBaseUrl: "GAME_URL"
195708
+ };
195709
+ HTTP_DEFAULTS2 = {
195710
+ timeout: 30000,
195711
+ retries: 3,
195712
+ retryBackoffBase: 2
195713
+ };
195714
+ AUTH_DEFAULTS2 = {
195715
+ tokenCacheDuration: 50000
195716
+ };
195717
+ CACHE_DEFAULTS2 = {
195718
+ defaultTTL: 600000,
195719
+ defaultMaxSize: 500,
195720
+ defaultName: "TimebackCache",
195721
+ studentTTL: 600000,
195722
+ studentMaxSize: 500,
195723
+ assessmentTTL: 1800000,
195724
+ assessmentMaxSize: 200
195725
+ };
195726
+ CONFIG_DEFAULTS2 = {
195727
+ fileNames: ["timeback.config.js", "timeback.config.json"]
195728
+ };
195729
+ DEFAULT_PLAYCADEMY_ORGANIZATION_ID2 = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
195730
+ PLAYCADEMY_DEFAULTS2 = {
195731
+ organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID2,
195732
+ launchBaseUrls: PLAYCADEMY_BASE_URLS2
195733
+ };
195734
+ RESOURCE_DEFAULTS2 = {
195735
+ organization: {
195736
+ name: "Playcademy Studios",
195737
+ type: "department"
195738
+ },
195739
+ course: {
195740
+ gradingScheme: "STANDARD",
195741
+ level: {
195742
+ elementary: "Elementary",
195743
+ middle: "Middle",
195744
+ high: "High",
195745
+ ap: "AP"
195746
+ },
195747
+ metadata: {
195748
+ goals: {
195749
+ dailyXp: 50,
195750
+ dailyLessons: 3
195751
+ },
195752
+ metrics: {
195753
+ totalXp: 1000,
195754
+ totalLessons: 50
195755
+ }
195756
+ }
195757
+ },
195758
+ component: {
195759
+ sortOrder: 1,
195760
+ prerequisiteCriteria: "ALL"
195761
+ },
195762
+ resource: {
195763
+ vendorId: "playcademy",
195764
+ roles: ["primary"],
195765
+ importance: "primary",
195766
+ metadata: {
195767
+ type: "interactive",
195768
+ toolProvider: "Playcademy",
195769
+ instructionalMethod: "exploratory",
195770
+ language: "en-US"
195771
+ }
195772
+ },
195773
+ componentResource: {
195774
+ sortOrder: 1,
195775
+ lessonType: "quiz"
195776
+ }
195777
+ };
195778
+ HTTP_STATUS2 = {
195779
+ CLIENT_ERROR_MIN: 400,
195780
+ CLIENT_ERROR_MAX: 500,
195781
+ SERVER_ERROR_MIN: 500
195782
+ };
195783
+ ERROR_NAMES2 = {
195784
+ timebackAuth: "TimebackAuthError",
195785
+ timebackApi: "TimebackApiError",
195786
+ timebackConfig: "TimebackConfigError",
195787
+ timebackSdk: "TimebackSDKError"
195788
+ };
195789
+ });
195790
+ init_constants2();
195791
+ var isObject3 = (value) => typeof value === "object" && value !== null;
195792
+ var SUBJECT_VALUES = TIMEBACK_SUBJECTS2;
195793
+ var GRADE_VALUES = TIMEBACK_GRADE_LEVELS2;
195794
+ function isCourseMetadata(value) {
195795
+ return isObject3(value);
195796
+ }
195797
+ function isResourceMetadata(value) {
195798
+ return isObject3(value);
195799
+ }
195800
+ function isPlaycademyResourceMetadata2(value) {
195801
+ if (!isObject3(value)) {
195802
+ return false;
195803
+ }
195804
+ if (!("mastery" in value) || value.mastery === undefined) {
195805
+ return true;
195806
+ }
195807
+ return isObject3(value.mastery);
195808
+ }
195809
+ function isTimebackSubject(value) {
195810
+ return typeof value === "string" && SUBJECT_VALUES.includes(value);
195811
+ }
195812
+ function isTimebackGrade(value) {
195813
+ return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
195814
+ }
195140
195815
  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}$/;
195141
195816
  function isValidUUID(value) {
195142
195817
  if (!value || typeof value !== "string") {
@@ -195413,6 +196088,32 @@ async function getTimebackClient() {
195413
196088
  }
195414
196089
  return timebackClient;
195415
196090
  }
196091
+ function buildResourceMetadata({
196092
+ baseMetadata,
196093
+ subject,
196094
+ grade,
196095
+ totalXp,
196096
+ masterableUnits
196097
+ }) {
196098
+ const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
196099
+ const metadata2 = {
196100
+ ...normalizedBaseMetadata || {}
196101
+ };
196102
+ metadata2.subject = subject;
196103
+ metadata2.grades = [grade];
196104
+ metadata2.xp = totalXp;
196105
+ if (masterableUnits !== undefined && masterableUnits !== null) {
196106
+ const existingPlaycademy = isPlaycademyResourceMetadata2(metadata2.playcademy) ? metadata2.playcademy : undefined;
196107
+ metadata2.playcademy = {
196108
+ ...existingPlaycademy || {},
196109
+ mastery: {
196110
+ ...existingPlaycademy?.mastery || {},
196111
+ masterableUnits
196112
+ }
196113
+ };
196114
+ }
196115
+ return metadata2;
196116
+ }
195416
196117
  var AchievementCompletionType;
195417
196118
  ((AchievementCompletionType2) => {
195418
196119
  AchievementCompletionType2["TIME_PLAYED_SESSION"] = "time_played_session";
@@ -195936,6 +196637,246 @@ var AchievementService = {
195936
196637
  generateAchievementMessage
195937
196638
  };
195938
196639
 
196640
+ class DiscordEmbedBuilder {
196641
+ embed = {};
196642
+ setTitle(title) {
196643
+ this.embed.title = title;
196644
+ return this;
196645
+ }
196646
+ setDescription(description) {
196647
+ this.embed.description = description;
196648
+ return this;
196649
+ }
196650
+ setUrl(url) {
196651
+ this.embed.url = url;
196652
+ return this;
196653
+ }
196654
+ setColor(color) {
196655
+ this.embed.color = color;
196656
+ return this;
196657
+ }
196658
+ setTimestamp(date4) {
196659
+ this.embed.timestamp = (date4 || new Date).toISOString();
196660
+ return this;
196661
+ }
196662
+ setAuthor(name4, options) {
196663
+ this.embed.author = {
196664
+ name: name4,
196665
+ url: options?.url,
196666
+ icon_url: options?.iconUrl
196667
+ };
196668
+ return this;
196669
+ }
196670
+ setFooter(text5, iconUrl) {
196671
+ this.embed.footer = {
196672
+ text: text5,
196673
+ icon_url: iconUrl
196674
+ };
196675
+ return this;
196676
+ }
196677
+ setThumbnail(url) {
196678
+ this.embed.thumbnail = { url };
196679
+ return this;
196680
+ }
196681
+ setImage(url) {
196682
+ this.embed.image = { url };
196683
+ return this;
196684
+ }
196685
+ addField(name4, value, inline = false) {
196686
+ if (!this.embed.fields) {
196687
+ this.embed.fields = [];
196688
+ }
196689
+ this.embed.fields.push({ name: name4, value, inline });
196690
+ return this;
196691
+ }
196692
+ addFields(...fields) {
196693
+ for (const field of fields) {
196694
+ this.addField(field.name, field.value, field.inline);
196695
+ }
196696
+ return this;
196697
+ }
196698
+ build() {
196699
+ return this.embed;
196700
+ }
196701
+ static create() {
196702
+ return new DiscordEmbedBuilder;
196703
+ }
196704
+ }
196705
+
196706
+ class DiscordClient {
196707
+ config;
196708
+ constructor(config2) {
196709
+ if (!config2.url) {
196710
+ throw new Error("Discord webhook URL is required");
196711
+ }
196712
+ if (!config2.url.startsWith("https://discord.com/api/webhooks/")) {
196713
+ throw new Error("Invalid Discord webhook URL format");
196714
+ }
196715
+ this.config = config2;
196716
+ }
196717
+ async send(message22) {
196718
+ const payload = {
196719
+ content: message22,
196720
+ username: this.config.username,
196721
+ avatar_url: this.config.avatarUrl
196722
+ };
196723
+ await this.sendPayload(payload);
196724
+ }
196725
+ async sendEmbed(embed) {
196726
+ const payload = {
196727
+ embeds: [embed],
196728
+ username: this.config.username,
196729
+ avatar_url: this.config.avatarUrl
196730
+ };
196731
+ await this.sendPayload(payload);
196732
+ }
196733
+ async sendEmbeds(embeds) {
196734
+ if (embeds.length > 10) {
196735
+ throw new Error("Discord allows maximum 10 embeds per message");
196736
+ }
196737
+ const payload = {
196738
+ embeds,
196739
+ username: this.config.username,
196740
+ avatar_url: this.config.avatarUrl
196741
+ };
196742
+ await this.sendPayload(payload);
196743
+ }
196744
+ async sendWithEmbeds(message22, embeds) {
196745
+ const payload = {
196746
+ content: message22,
196747
+ embeds,
196748
+ username: this.config.username,
196749
+ avatar_url: this.config.avatarUrl
196750
+ };
196751
+ await this.sendPayload(payload);
196752
+ }
196753
+ async sendRich(message22) {
196754
+ const builder = new DiscordEmbedBuilder;
196755
+ if (message22.title)
196756
+ builder.setTitle(message22.title);
196757
+ if (message22.description)
196758
+ builder.setDescription(message22.description);
196759
+ if (message22.url)
196760
+ builder.setUrl(message22.url);
196761
+ if (message22.color) {
196762
+ const color = typeof message22.color === "string" ? parseInt(message22.color.replace("#", ""), 16) : message22.color;
196763
+ builder.setColor(color);
196764
+ }
196765
+ if (message22.author) {
196766
+ builder.setAuthor(message22.author.name, {
196767
+ url: message22.author.url,
196768
+ iconUrl: message22.author.iconUrl
196769
+ });
196770
+ }
196771
+ if (message22.fields) {
196772
+ for (const field of message22.fields) {
196773
+ builder.addField(field.name, field.value, field.inline);
196774
+ }
196775
+ }
196776
+ if (message22.footer) {
196777
+ builder.setFooter(message22.footer.text, message22.footer.iconUrl);
196778
+ }
196779
+ if (message22.timestamp) {
196780
+ const date4 = message22.timestamp instanceof Date ? message22.timestamp : new Date(message22.timestamp);
196781
+ builder.setTimestamp(date4);
196782
+ }
196783
+ if (message22.thumbnail) {
196784
+ builder.setThumbnail(message22.thumbnail.url);
196785
+ }
196786
+ if (message22.image) {
196787
+ builder.setImage(message22.image.url);
196788
+ }
196789
+ await this.sendEmbed(builder.build());
196790
+ }
196791
+ async sendPayload(payload) {
196792
+ const response = await fetch(this.config.url, {
196793
+ method: "POST",
196794
+ headers: {
196795
+ "Content-Type": "application/json"
196796
+ },
196797
+ body: JSON.stringify(payload)
196798
+ });
196799
+ if (!response.ok) {
196800
+ const errorText = await response.text().catch(() => "Unknown error");
196801
+ throw new Error(`Discord webhook request failed: ${response.status} ${response.statusText} - ${errorText}`);
196802
+ }
196803
+ }
196804
+ getRedactedUrl() {
196805
+ const parts2 = this.config.url.split("/");
196806
+ const token = parts2[parts2.length - 1];
196807
+ return `${token?.slice(0, 9)}...`;
196808
+ }
196809
+ }
196810
+ var DiscordColors = {
196811
+ DEFAULT: 0,
196812
+ WHITE: 16777215,
196813
+ AQUA: 1752220,
196814
+ GREEN: 5763719,
196815
+ BLUE: 3447003,
196816
+ YELLOW: 16705372,
196817
+ PURPLE: 10181046,
196818
+ LUMINOUS_VIVID_PINK: 15277667,
196819
+ FUCHSIA: 15418782,
196820
+ GOLD: 15844367,
196821
+ ORANGE: 15105570,
196822
+ RED: 15548997,
196823
+ GREY: 9807270,
196824
+ NAVY: 3426654,
196825
+ DARK_AQUA: 1146986,
196826
+ DARK_GREEN: 2067276,
196827
+ DARK_BLUE: 2123412,
196828
+ DARK_PURPLE: 7419530,
196829
+ DARK_VIVID_PINK: 11342935,
196830
+ DARK_GOLD: 12745742,
196831
+ DARK_ORANGE: 11027200,
196832
+ DARK_RED: 10038562,
196833
+ DARK_GREY: 9936031,
196834
+ DARKER_GREY: 8359053,
196835
+ LIGHT_GREY: 12370112,
196836
+ DARK_NAVY: 2899536,
196837
+ BLURPLE: 5793266,
196838
+ GREYPLE: 10070709,
196839
+ DARK_BUT_NOT_BLACK: 2895667,
196840
+ NOT_QUITE_BLACK: 2303786
196841
+ };
196842
+ init_src();
196843
+ function getDiscordClient() {
196844
+ const webhookUrl = process.env.DISCORD_WEBHOOK_PLATFORM;
196845
+ if (!webhookUrl) {
196846
+ log2.debug("[API] Discord webhook not configured, skipping notification");
196847
+ return null;
196848
+ }
196849
+ return new DiscordClient({
196850
+ url: webhookUrl,
196851
+ username: "Playcademy Platform",
196852
+ avatarUrl: process.env.DISCORD_WEBHOOK_AVATAR_URL
196853
+ });
196854
+ }
196855
+ function getEnvironment() {
196856
+ const stage = process.env.SST_STAGE;
196857
+ return stage === "production" ? "production" : stage === "dev" ? "staging" : "test";
196858
+ }
196859
+ async function sendDeveloperApplicationAlert(user) {
196860
+ const discord = getDiscordClient();
196861
+ if (!discord)
196862
+ return;
196863
+ const embed = new DiscordEmbedBuilder().setTitle("\uD83C\uDFAE New Developer Application").setDescription(`A user has applied for developer status (**${getEnvironment()}**).`).setColor(DiscordColors.GREEN).addField("User ID", user.id, true).addField("Email", user.email || "Not provided", true).setFooter("Playcademy Developer Platform").setTimestamp().build();
196864
+ await discord.sendEmbed(embed);
196865
+ log2.debug("[API] Discord notification sent successfully", {
196866
+ userId: user.id
196867
+ });
196868
+ }
196869
+ async function sendGameDeletionAlert(game) {
196870
+ const discord = getDiscordClient();
196871
+ if (!discord)
196872
+ return;
196873
+ const embed = new DiscordEmbedBuilder().setTitle("\uD83D\uDDD1️ Game Deleted").setDescription(`**${game.displayName || game.slug}** has been deleted (**${getEnvironment()}**).`).setColor(DiscordColors.ORANGE).addField("Slug", game.slug, true).addField("Developer", game.developer.email || game.developer.id, true).setFooter("Playcademy Developer Platform").setTimestamp().build();
196874
+ await discord.sendEmbed(embed);
196875
+ log2.debug("[API] Discord game deletion notification sent", {
196876
+ slug: game.slug
196877
+ });
196878
+ }
196879
+
195939
196880
  class JwtBaseError extends Error {
195940
196881
  }
195941
196882
 
@@ -198478,6 +199419,21 @@ var logger3 = {
198478
199419
  warn: (msg) => getLogger().warn(msg),
198479
199420
  error: (msg) => getLogger().error(msg)
198480
199421
  };
199422
+ function detectTimebackCourses() {
199423
+ const coursePattern = /^SANDBOX_TIMEBACK_COURSE_(\d+)_([A-Z_]+)$/i;
199424
+ const courses = [];
199425
+ for (const [key, value] of Object.entries(process.env)) {
199426
+ const match2 = key.match(coursePattern);
199427
+ if (match2 && value) {
199428
+ const gradeStr = match2[1];
199429
+ const subjectStr = match2[2];
199430
+ const grade = parseInt(gradeStr, 10);
199431
+ const subject = subjectStr.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
199432
+ courses.push({ grade, subject, courseId: value });
199433
+ }
199434
+ }
199435
+ return courses;
199436
+ }
198481
199437
  async function seedCoreGames(db) {
198482
199438
  const now2 = new Date;
198483
199439
  const coreGames = [
@@ -198502,18 +199458,26 @@ async function seedCoreGames(db) {
198502
199458
  console.error(`Error seeding core game '${gameData.slug}':`, error2);
198503
199459
  }
198504
199460
  }
198505
- if (config.timeback.courseId && hasTimebackCredentials()) {
198506
- try {
198507
- await db.insert(gameTimebackIntegrations).values({
198508
- id: crypto.randomUUID(),
198509
- gameId: CORE_GAME_UUIDS.PLAYGROUND,
198510
- courseId: config.timeback.courseId,
198511
- lastVerifiedAt: null,
198512
- createdAt: now2,
198513
- updatedAt: now2
198514
- }).onConflictDoNothing();
198515
- } catch (error2) {
198516
- console.error("Error seeding TimeBack integration for playground:", error2);
199461
+ if (hasTimebackCredentials()) {
199462
+ const courses = detectTimebackCourses();
199463
+ if (courses.length > 0) {
199464
+ for (const course of courses) {
199465
+ try {
199466
+ await db.insert(gameTimebackIntegrations).values({
199467
+ id: crypto.randomUUID(),
199468
+ gameId: CORE_GAME_UUIDS.PLAYGROUND,
199469
+ courseId: course.courseId,
199470
+ grade: course.grade,
199471
+ subject: course.subject,
199472
+ totalXp: null,
199473
+ lastVerifiedAt: null,
199474
+ createdAt: now2,
199475
+ updatedAt: now2
199476
+ }).onConflictDoNothing();
199477
+ } catch (error2) {
199478
+ console.error(`Error seeding TimeBack integration for playground (${course.subject} grade ${course.grade}):`, error2);
199479
+ }
199480
+ }
198517
199481
  }
198518
199482
  }
198519
199483
  }
@@ -198544,18 +199508,23 @@ async function seedCurrentProjectGame(db, project) {
198544
199508
  updatedAt: now2
198545
199509
  };
198546
199510
  const [newGame] = await db.insert(games).values(gameRecord).returning();
198547
- if (config.timeback.courseId && hasTimebackCredentials()) {
198548
- await db.insert(gameTimebackIntegrations).values({
198549
- id: crypto.randomUUID(),
198550
- gameId: newGame.id,
198551
- courseId: config.timeback.courseId,
198552
- lastVerifiedAt: null,
198553
- createdAt: now2,
198554
- updatedAt: now2
198555
- }).onConflictDoNothing();
198556
- } else if (hasTimebackCredentials() && !config.timeback.courseId) {
198557
- logger3.info(" ⚠ TimeBack credentials found but SANDBOX_TIMEBACK_COURSE_ID not set");
198558
- logger3.info(" Set SANDBOX_TIMEBACK_COURSE_ID to your real TimeBack course ID");
199511
+ if (hasTimebackCredentials()) {
199512
+ const courses = detectTimebackCourses();
199513
+ if (courses.length > 0) {
199514
+ for (const course of courses) {
199515
+ await db.insert(gameTimebackIntegrations).values({
199516
+ id: crypto.randomUUID(),
199517
+ gameId: newGame.id,
199518
+ courseId: course.courseId,
199519
+ grade: course.grade,
199520
+ subject: course.subject,
199521
+ totalXp: null,
199522
+ lastVerifiedAt: null,
199523
+ createdAt: now2,
199524
+ updatedAt: now2
199525
+ }).onConflictDoNothing();
199526
+ }
199527
+ }
198559
199528
  }
198560
199529
  return newGame;
198561
199530
  } catch (error2) {
@@ -198883,6 +199852,12 @@ async function applyForDeveloperStatus(ctx) {
198883
199852
  userId: user.id,
198884
199853
  newStatus: "pending"
198885
199854
  });
199855
+ sendDeveloperApplicationAlert(fullUser).catch((error2) => {
199856
+ log2.error("[API] Failed to send Discord notification for developer application", {
199857
+ userId: user.id,
199858
+ error: error2 instanceof Error ? error2.message : String(error2)
199859
+ });
199860
+ });
198886
199861
  } catch (error2) {
198887
199862
  log2.error(`Error updating developer status for user ${user.id}:`, {
198888
199863
  error: error2
@@ -203595,6 +204570,67 @@ var NotificationStatsSchema = exports_external2.object({
203595
204570
  startDate: exports_external2.date().optional(),
203596
204571
  endDate: exports_external2.date().optional()
203597
204572
  });
204573
+ var InsertTimebackDailyXpSchema = createInsertSchema(timebackDailyXp, {
204574
+ xp: exports_external2.number().min(0, "XP must be a non-negative number"),
204575
+ date: exports_external2.date()
204576
+ }).omit({ createdAt: true, updatedAt: true });
204577
+ var SelectTimebackDailyXpSchema = createSelectSchema(timebackDailyXp);
204578
+ var UpdateTimebackDailyXpSchema = createUpdateSchema(timebackDailyXp, {
204579
+ xp: exports_external2.number().min(0, "XP must be a non-negative number")
204580
+ }).omit({ userId: true, createdAt: true });
204581
+ var UpdateTimebackXpRequestSchema = exports_external2.object({
204582
+ xp: exports_external2.number().min(0, "XP must be a non-negative number"),
204583
+ userTimestamp: exports_external2.string().datetime().optional()
204584
+ });
204585
+ var TimebackDateQuerySchema = exports_external2.object({
204586
+ date: exports_external2.string().datetime().optional()
204587
+ });
204588
+ var TimebackHistoryQuerySchema = exports_external2.object({
204589
+ startDate: exports_external2.string().date().optional(),
204590
+ endDate: exports_external2.string().date().optional()
204591
+ });
204592
+ var InsertTimebackXpEventSchema = createInsertSchema(timebackXpEvents, {
204593
+ occurredAt: exports_external2.date(),
204594
+ xpDelta: exports_external2.number()
204595
+ }).omit({ createdAt: true, updatedAt: true });
204596
+ var SelectTimebackXpEventSchema = createSelectSchema(timebackXpEvents);
204597
+ var InsertGameTimebackIntegrationSchema = createInsertSchema(gameTimebackIntegrations, {
204598
+ gameId: exports_external2.string().uuid(),
204599
+ courseId: exports_external2.string().min(1),
204600
+ grade: exports_external2.number().int(),
204601
+ subject: exports_external2.string().min(1),
204602
+ totalXp: exports_external2.number().int().positive().optional()
204603
+ }).omit({ id: true, createdAt: true, updatedAt: true });
204604
+ var SelectGameTimebackIntegrationSchema = createSelectSchema(gameTimebackIntegrations);
204605
+ var UpdateGameTimebackIntegrationSchema = createUpdateSchema(gameTimebackIntegrations, {
204606
+ courseId: exports_external2.string().min(1).optional(),
204607
+ totalXp: exports_external2.number().int().positive().optional(),
204608
+ lastVerifiedAt: exports_external2.date().optional()
204609
+ }).omit({ id: true, gameId: true, grade: true, subject: true, createdAt: true });
204610
+ var EndActivityRequestSchema = exports_external2.object({
204611
+ gameId: exports_external2.string().uuid(),
204612
+ studentId: exports_external2.string().min(1),
204613
+ activityData: exports_external2.object({
204614
+ activityId: exports_external2.string().min(1),
204615
+ activityName: exports_external2.string().optional(),
204616
+ grade: exports_external2.number().int(),
204617
+ subject: exports_external2.string().min(1),
204618
+ appName: exports_external2.string().optional(),
204619
+ sensorUrl: exports_external2.string().url().optional(),
204620
+ courseId: exports_external2.string().optional(),
204621
+ courseName: exports_external2.string().optional(),
204622
+ studentEmail: exports_external2.string().email().optional()
204623
+ }),
204624
+ scoreData: exports_external2.object({
204625
+ correctQuestions: exports_external2.number().int().min(0),
204626
+ totalQuestions: exports_external2.number().int().min(0)
204627
+ }),
204628
+ timingData: exports_external2.object({
204629
+ durationSeconds: exports_external2.number().positive()
204630
+ }),
204631
+ xpEarned: exports_external2.number().optional(),
204632
+ masteredUnits: exports_external2.number().nonnegative().optional()
204633
+ });
203598
204634
  init_src();
203599
204635
  async function submitScore(ctx) {
203600
204636
  const { user, params, request: request3 } = ctx;
@@ -203762,6 +204798,26 @@ async function getUserAllScores(ctx) {
203762
204798
  throw ApiError.internal("Failed to fetch user scores");
203763
204799
  }
203764
204800
  }
204801
+ async function getUserScores(ctx) {
204802
+ const user = ctx.user;
204803
+ if (!user) {
204804
+ throw ApiError.unauthorized("Must be logged in to view user scores");
204805
+ }
204806
+ const { params, url } = ctx;
204807
+ const gameId = params.gameId;
204808
+ const userId = params.userId;
204809
+ if (!gameId || !userId) {
204810
+ throw ApiError.badRequest("Game ID and User ID are required");
204811
+ }
204812
+ const limit2 = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
204813
+ const db = getDatabase();
204814
+ try {
204815
+ const scores = await db.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(limit2);
204816
+ return scores;
204817
+ } catch {
204818
+ throw ApiError.internal("Failed to fetch user scores");
204819
+ }
204820
+ }
203765
204821
  init_src();
203766
204822
  async function getUserLevel(ctx) {
203767
204823
  const user = ctx.user;
@@ -204405,7 +205461,7 @@ async function deleteGame(ctx) {
204405
205461
  const db = getDatabase();
204406
205462
  const gameToDelete = await db.query.games.findFirst({
204407
205463
  where: eq(games.id, gameId),
204408
- columns: { id: true, slug: true }
205464
+ columns: { id: true, slug: true, displayName: true }
204409
205465
  });
204410
205466
  if (!gameToDelete || !gameToDelete.slug) {
204411
205467
  throw ApiError.notFound("Game not found for deletion");
@@ -204427,6 +205483,16 @@ async function deleteGame(ctx) {
204427
205483
  hadActiveDeployment: !!activeDeployment,
204428
205484
  customDomainsCount: customDomains.length
204429
205485
  });
205486
+ sendGameDeletionAlert({
205487
+ slug: gameToDelete.slug,
205488
+ displayName: gameToDelete.displayName,
205489
+ developer: {
205490
+ id: user.id,
205491
+ email: user.email
205492
+ }
205493
+ }).catch((err2) => {
205494
+ log2.warn("[API] Failed to send Discord game deletion notification", { err: err2 });
205495
+ });
204430
205496
  if (activeDeployment?.provider === "cloudflare") {
204431
205497
  try {
204432
205498
  const cloudflare2 = getCloudflareProvider();
@@ -205990,6 +207056,26 @@ gameScoresRouter.post("/:gameId/scores", async (c3) => {
205990
207056
  return c3.json(createUnknownErrorResponse(error2), 500);
205991
207057
  }
205992
207058
  });
207059
+ gameScoresRouter.get("/:gameId/users/:userId/scores", async (c3) => {
207060
+ const gameId = c3.req.param("gameId");
207061
+ const userId = c3.req.param("userId");
207062
+ const ctx = {
207063
+ user: c3.get("user"),
207064
+ params: { gameId, userId },
207065
+ url: new URL(c3.req.url),
207066
+ request: c3.req.raw
207067
+ };
207068
+ try {
207069
+ const result = await getUserScores(ctx);
207070
+ return c3.json(result, 200);
207071
+ } catch (error2) {
207072
+ if (error2 instanceof ApiError) {
207073
+ return c3.json(createErrorResponse(error2), error2.statusCode);
207074
+ }
207075
+ console.error("Error in getUserScores:", error2);
207076
+ return c3.json(createUnknownErrorResponse(error2), 500);
207077
+ }
207078
+ });
205993
207079
  init_src();
205994
207080
  async function startGameSession(ctx) {
205995
207081
  const user = ctx.user;
@@ -208726,39 +209812,21 @@ async function endActivity(ctx) {
208726
209812
  throw ApiError.unauthorized("Must be logged in to end activity");
208727
209813
  }
208728
209814
  log2.debug("[API] Ending activity", { userId: user.id });
208729
- let request3;
208730
- try {
208731
- const requestBody = await ctx.request.json();
208732
- request3 = requestBody;
208733
- } catch (error2) {
208734
- log2.error("[API] Failed to parse request body:", { error: error2 });
208735
- throw ApiError.badRequest("Invalid JSON body");
208736
- }
208737
- const { gameId, studentId, activityData, scoreData, timingData, xpEarned } = request3;
208738
- if (!activityData?.activityId) {
208739
- throw ApiError.badRequest("activityId is required");
208740
- }
208741
- if (typeof scoreData?.correctQuestions !== "number" || typeof scoreData?.totalQuestions !== "number") {
208742
- throw ApiError.badRequest("correctQuestions and totalQuestions are required");
208743
- }
208744
- if (typeof timingData?.durationSeconds !== "number") {
208745
- throw ApiError.badRequest("durationSeconds is required");
209815
+ const requestBody = await ctx.request.json();
209816
+ const validation2 = EndActivityRequestSchema.safeParse(requestBody);
209817
+ if (!validation2.success) {
209818
+ throw ApiError.badRequest(formatValidationErrors(validation2.error));
208746
209819
  }
209820
+ const { gameId, studentId, activityData, scoreData, timingData, xpEarned, masteredUnits } = validation2.data;
209821
+ await verifyGameAccessById(gameId, user);
208747
209822
  const db = getDatabase();
208748
- const game = await db.query.games.findFirst({
208749
- where: eq(games.id, gameId)
208750
- });
208751
- if (!game) {
208752
- throw ApiError.notFound("Game not found");
208753
- }
208754
- if (user.role !== "admin" && game.developerId !== user.id) {
208755
- throw ApiError.forbidden("You do not own this game");
208756
- }
209823
+ const { grade, subject } = activityData;
208757
209824
  const integration = await db.query.gameTimebackIntegrations.findFirst({
208758
- where: eq(gameTimebackIntegrations.gameId, gameId)
209825
+ where: (table14, { eq: eq3, and: and3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, grade), eq3(table14.subject, subject))
208759
209826
  });
208760
209827
  if (!integration) {
208761
- throw ApiError.notFound("No TimeBack integration found for this game. Run setup first.");
209828
+ log2.error("[API] No TimeBack integration found", { gameId, grade, subject });
209829
+ throw ApiError.notFound(`No TimeBack integration found for this game (grade ${grade}, subject ${subject}). Run setup first.`);
208762
209830
  }
208763
209831
  const client2 = await getTimebackClient();
208764
209832
  try {
@@ -208769,6 +209837,7 @@ async function endActivity(ctx) {
208769
209837
  correctQuestions: scoreData.correctQuestions,
208770
209838
  sessionDurationSeconds: timingData.durationSeconds,
208771
209839
  xpEarned,
209840
+ masteredUnits,
208772
209841
  activityId: activityData.activityId,
208773
209842
  activityName: activityData.activityName,
208774
209843
  subject: activityData.subject,
@@ -208776,8 +209845,11 @@ async function endActivity(ctx) {
208776
209845
  sensorUrl: activityData.sensorUrl,
208777
209846
  courseId: activityData.courseId,
208778
209847
  courseName: activityData.courseName,
208779
- studentEmail: activityData.studentEmail
209848
+ studentEmail: activityData.studentEmail,
209849
+ courseTotalXp: integration.totalXp
208780
209850
  });
209851
+ const studentData = await client2.resolveStudent(studentId);
209852
+ const studentEmail = studentData.email;
208781
209853
  await client2.recordSessionEnd(integration.courseId, studentId, {
208782
209854
  activeTimeSeconds: timingData.durationSeconds,
208783
209855
  activityId: activityData.activityId,
@@ -208793,17 +209865,26 @@ async function endActivity(ctx) {
208793
209865
  gameId,
208794
209866
  courseId: integration.courseId,
208795
209867
  studentId,
209868
+ studentEmail,
208796
209869
  activityId: activityData.activityId,
208797
209870
  score: scorePercentage,
208798
209871
  durationSeconds: timingData.durationSeconds,
208799
209872
  attemptNumber: result.attemptNumber,
208800
209873
  isFirstAttempt: result.attemptNumber === 1,
208801
- xpAwarded: result.xpAwarded
209874
+ xpAwarded: result.xpAwarded,
209875
+ masteredUnits,
209876
+ pctCompleteApp: result.pctCompleteApp,
209877
+ scoreStatus: result.scoreStatus,
209878
+ inProgress: result.inProgress
208802
209879
  });
208803
209880
  return {
208804
209881
  status: "ok",
208805
209882
  courseId: integration.courseId,
208806
- xpAwarded: result.xpAwarded
209883
+ xpAwarded: result.xpAwarded,
209884
+ masteredUnits: result.masteredUnitsApplied,
209885
+ pctCompleteApp: result.pctCompleteApp,
209886
+ scoreStatus: result.scoreStatus,
209887
+ inProgress: result.inProgress
208807
209888
  };
208808
209889
  } catch (error2) {
208809
209890
  logTimebackError2("end activity", error2, {
@@ -208829,68 +209910,160 @@ async function setupTimebackIntegration(ctx) {
208829
209910
  log2.error("Failed to parse request body:", { error: error2 });
208830
209911
  throw ApiError.badRequest("Invalid JSON body");
208831
209912
  }
208832
- const { gameId, config: config2, verbose } = request3;
209913
+ const { gameId, courses, baseConfig, verbose } = request3;
208833
209914
  log2.debug("[API] TimeBack setup request", {
208834
209915
  gameId,
208835
- courseTitle: config2.course.title
209916
+ courseCount: courses.length
208836
209917
  });
209918
+ await verifyGameAccessById(gameId, user);
208837
209919
  const db = getDatabase();
208838
- const game = await db.query.games.findFirst({
208839
- where: eq(games.id, gameId)
208840
- });
208841
- if (!game) {
208842
- throw ApiError.notFound("Game not found");
208843
- }
208844
- if (user.role !== "admin" && game.developerId !== user.id) {
208845
- throw ApiError.forbidden("You do not own this game");
208846
- }
208847
- const existing = await db.query.gameTimebackIntegrations.findFirst({
209920
+ const existing = await db.query.gameTimebackIntegrations.findMany({
208848
209921
  where: eq(gameTimebackIntegrations.gameId, gameId)
208849
209922
  });
208850
209923
  const client2 = await getTimebackClient();
208851
- if (existing) {
208852
- try {
208853
- await client2.update(existing.courseId, config2);
208854
- } catch (error2) {
208855
- logTimebackError2("update", error2, { gameId, courseId: existing.courseId });
208856
- throw error2;
208857
- }
208858
- const [updated] = await db.update(gameTimebackIntegrations).set({
208859
- updatedAt: new Date
208860
- }).where(eq(gameTimebackIntegrations.id, existing.id)).returning();
208861
- log2.info("[API] Updated TimeBack integration", {
208862
- gameId,
208863
- courseId: updated.courseId
208864
- });
208865
- return {
208866
- integration: updated,
208867
- courseId: updated.courseId
209924
+ const integrations = [];
209925
+ const verboseData = [];
209926
+ for (const courseConfig of courses) {
209927
+ const {
209928
+ subject,
209929
+ grade,
209930
+ title,
209931
+ courseCode,
209932
+ level,
209933
+ metadata: metadata2,
209934
+ totalXp: derivedTotalXp,
209935
+ masterableUnits: derivedMasterableUnits
209936
+ } = courseConfig;
209937
+ if (!isTimebackSubject(subject)) {
209938
+ log2.error("[API] Invalid subject in TimeBack request", { subject });
209939
+ throw ApiError.badRequest(`Invalid subject "${subject}" provided for course "${title}".`);
209940
+ }
209941
+ if (!isTimebackGrade(grade)) {
209942
+ log2.error("[API] Invalid grade in TimeBack request", { grade });
209943
+ throw ApiError.badRequest(`Invalid grade "${grade}" provided for course "${title}".`);
209944
+ }
209945
+ const courseSubject = subject;
209946
+ const courseGrade = grade;
209947
+ const courseMetadata = isCourseMetadata(metadata2) ? metadata2 : undefined;
209948
+ const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
209949
+ const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata2(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
209950
+ if (typeof totalXp !== "number") {
209951
+ log2.error("[API] Missing totalXp in course metadata", { gameId, grade, subject, title });
209952
+ throw ApiError.badRequest(`Course "${title}" (grade ${grade}, subject ${subject}) is missing \`metadata.metrics.totalXp\``);
209953
+ }
209954
+ const suffix = baseConfig.component.titleSuffix || "";
209955
+ const applySuffix = (text5) => suffix ? `${text5} ${suffix}` : text5;
209956
+ const fullConfig = {
209957
+ organization: baseConfig.organization,
209958
+ course: {
209959
+ title,
209960
+ subjects: [courseSubject],
209961
+ grades: [courseGrade],
209962
+ courseCode,
209963
+ level,
209964
+ gradingScheme: "STANDARD",
209965
+ metadata: metadata2
209966
+ },
209967
+ component: {
209968
+ ...baseConfig.component,
209969
+ title: applySuffix(baseConfig.component.title || `${title} Activities`)
209970
+ },
209971
+ resource: {
209972
+ ...baseConfig.resource,
209973
+ title: applySuffix(baseConfig.resource.title || `${title} Game`),
209974
+ metadata: buildResourceMetadata({
209975
+ baseMetadata: baseConfig.resource.metadata,
209976
+ subject: courseSubject,
209977
+ grade: courseGrade,
209978
+ totalXp,
209979
+ masterableUnits
209980
+ })
209981
+ },
209982
+ componentResource: {
209983
+ ...baseConfig.componentResource,
209984
+ title: applySuffix(baseConfig.componentResource.title || "")
209985
+ }
208868
209986
  };
208869
- }
208870
- let result;
208871
- try {
208872
- result = await client2.setup(config2, { verbose });
208873
- } catch (error2) {
208874
- logTimebackError2("setup", error2, { gameId });
208875
- throw error2;
208876
- }
208877
- const integrationData = {
208878
- gameId,
208879
- courseId: result.courseId
208880
- };
208881
- const [integration] = await db.insert(gameTimebackIntegrations).values(integrationData).returning();
208882
- if (!integration) {
208883
- throw ApiError.internal("Failed to create TimeBack integration record");
209987
+ const existingIntegration = existing.find((i4) => i4.grade === grade && i4.subject === subject);
209988
+ if (existingIntegration) {
209989
+ try {
209990
+ await client2.update(existingIntegration.courseId, fullConfig);
209991
+ } catch (error2) {
209992
+ logTimebackError2("update", error2, {
209993
+ gameId,
209994
+ grade,
209995
+ subject,
209996
+ courseId: existingIntegration.courseId
209997
+ });
209998
+ throw error2;
209999
+ }
210000
+ const [updated] = await db.update(gameTimebackIntegrations).set({
210001
+ totalXp,
210002
+ updatedAt: new Date
210003
+ }).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
210004
+ if (!updated) {
210005
+ log2.error("[API] Failed to update TimeBack integration record", {
210006
+ gameId,
210007
+ grade,
210008
+ subject,
210009
+ integrationId: existingIntegration.id
210010
+ });
210011
+ throw ApiError.internal("Failed to update TimeBack integration record");
210012
+ }
210013
+ integrations.push(updated);
210014
+ log2.info("[API] Updated TimeBack course", {
210015
+ gameId,
210016
+ courseId: updated.courseId,
210017
+ grade,
210018
+ subject
210019
+ });
210020
+ } else {
210021
+ let result;
210022
+ try {
210023
+ result = await client2.setup(fullConfig, { verbose });
210024
+ } catch (error2) {
210025
+ logTimebackError2("setup", error2, { gameId, grade, subject });
210026
+ throw error2;
210027
+ }
210028
+ const integrationData = {
210029
+ gameId,
210030
+ courseId: result.courseId,
210031
+ grade,
210032
+ subject,
210033
+ totalXp
210034
+ };
210035
+ const [integration] = await db.insert(gameTimebackIntegrations).values(integrationData).returning();
210036
+ if (!integration) {
210037
+ log2.error("[API] Failed to create TimeBack integration record", {
210038
+ gameId,
210039
+ grade,
210040
+ subject,
210041
+ courseId: result.courseId
210042
+ });
210043
+ throw ApiError.internal("Failed to create TimeBack integration record");
210044
+ }
210045
+ integrations.push(integration);
210046
+ if (verbose && result.verboseData) {
210047
+ verboseData.push({
210048
+ integration,
210049
+ config: result.verboseData
210050
+ });
210051
+ }
210052
+ log2.info("[API] Created TimeBack course", {
210053
+ gameId,
210054
+ courseId: integration.courseId,
210055
+ grade,
210056
+ subject
210057
+ });
210058
+ }
208884
210059
  }
208885
210060
  log2.info("[API] Created TimeBack integration", {
208886
210061
  gameId,
208887
- courseId: integration.courseId,
208888
- ...result.verboseData && { verbose: result.verboseData }
210062
+ courseCount: integrations.length
208889
210063
  });
208890
210064
  return {
208891
- integration,
208892
- courseId: integration.courseId,
208893
- ...result.verboseData && { verbose: result.verboseData }
210065
+ integrations,
210066
+ ...verbose && verboseData.length > 0 && { verbose: verboseData }
208894
210067
  };
208895
210068
  }
208896
210069
  async function getTimebackIntegration(ctx) {
@@ -208902,21 +210075,13 @@ async function getTimebackIntegration(ctx) {
208902
210075
  if (!gameId) {
208903
210076
  throw ApiError.badRequest("Missing gameId parameter");
208904
210077
  }
208905
- log2.debug("[API] Getting TimeBack integration", { userId: user.id, gameId });
210078
+ log2.debug("[API] Getting TimeBack integrations", { userId: user.id, gameId });
210079
+ await verifyGameAccessById(gameId, user);
208906
210080
  const db = getDatabase();
208907
- const game = await db.query.games.findFirst({
208908
- where: eq(games.id, gameId)
208909
- });
208910
- if (!game) {
208911
- throw ApiError.notFound("Game not found");
208912
- }
208913
- if (user.role !== "admin" && game.developerId !== user.id) {
208914
- throw ApiError.forbidden("You do not own this game");
208915
- }
208916
- const integration = await db.query.gameTimebackIntegrations.findFirst({
210081
+ const integrations = await db.query.gameTimebackIntegrations.findMany({
208917
210082
  where: eq(gameTimebackIntegrations.gameId, gameId)
208918
210083
  });
208919
- return integration || null;
210084
+ return integrations;
208920
210085
  }
208921
210086
  async function verifyTimebackIntegration(ctx) {
208922
210087
  const user = ctx.user;
@@ -208928,39 +210093,40 @@ async function verifyTimebackIntegration(ctx) {
208928
210093
  throw ApiError.badRequest("Missing gameId parameter");
208929
210094
  }
208930
210095
  log2.debug("[API] Verifying TimeBack integration", { userId: user.id, gameId });
210096
+ await verifyGameAccessById(gameId, user);
208931
210097
  const db = getDatabase();
208932
- const game = await db.query.games.findFirst({
208933
- where: eq(games.id, gameId)
208934
- });
208935
- if (!game) {
208936
- throw ApiError.notFound("Game not found");
208937
- }
208938
- if (user.role !== "admin" && game.developerId !== user.id) {
208939
- log2.error("[API] User does not own game", { userId: user.id, gameId });
208940
- throw ApiError.forbidden("You do not own this game");
208941
- }
208942
- const integration = await db.query.gameTimebackIntegrations.findFirst({
210098
+ const integrations = await db.query.gameTimebackIntegrations.findMany({
208943
210099
  where: eq(gameTimebackIntegrations.gameId, gameId)
208944
210100
  });
208945
- if (!integration) {
210101
+ if (integrations.length === 0) {
210102
+ log2.error("[API] No TimeBack integration found", { gameId });
208946
210103
  throw ApiError.notFound("No TimeBack integration found for this game");
208947
210104
  }
208948
210105
  const client2 = await getTimebackClient();
208949
- const resources = await client2.verify(integration.courseId);
208950
- const resourceValues = Object.values(resources);
208951
- const allFound = resourceValues.every((r3) => r3.found);
208952
- const errors3 = Object.entries(resources).filter(([_5, r3]) => !r3.found).map(([name4]) => `${name4} not found`);
208953
- await db.update(gameTimebackIntegrations).set({ lastVerifiedAt: new Date }).where(eq(gameTimebackIntegrations.id, integration.id));
208954
- log2.info("[API] Verified TimeBack integration", {
210106
+ const now2 = new Date;
210107
+ const results = await Promise.all(integrations.map(async (integration) => {
210108
+ const resources = await client2.verify(integration.courseId);
210109
+ const resourceValues = Object.values(resources);
210110
+ const allFound = resourceValues.every((r3) => r3.found);
210111
+ const errors3 = Object.entries(resources).filter(([_5, r3]) => !r3.found).map(([name4]) => `${name4} not found`);
210112
+ const status = allFound ? "success" : "error";
210113
+ return {
210114
+ integration: { ...integration, lastVerifiedAt: now2 },
210115
+ resources,
210116
+ status,
210117
+ ...errors3.length > 0 && { errors: errors3 }
210118
+ };
210119
+ }));
210120
+ await db.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(eq(gameTimebackIntegrations.gameId, gameId));
210121
+ const overallSuccess = results.every((r3) => r3.status === "success");
210122
+ log2.info("[API] Verified TimeBack integrations", {
208955
210123
  gameId,
208956
- courseId: integration.courseId,
208957
- status: allFound ? "success" : "error"
210124
+ courseCount: integrations.length,
210125
+ status: overallSuccess ? "success" : "error"
208958
210126
  });
208959
210127
  return {
208960
- status: allFound ? "success" : "error",
208961
- integration: { ...integration, lastVerifiedAt: new Date },
208962
- resources,
208963
- ...errors3.length > 0 && { errors: errors3 }
210128
+ status: overallSuccess ? "success" : "error",
210129
+ results
208964
210130
  };
208965
210131
  }
208966
210132
  async function getTimebackConfig(ctx) {
@@ -208973,20 +210139,13 @@ async function getTimebackConfig(ctx) {
208973
210139
  throw ApiError.badRequest("Missing gameId parameter");
208974
210140
  }
208975
210141
  log2.debug("[API] Getting TimeBack config", { userId: user.id, gameId });
210142
+ await verifyGameAccessById(gameId, user);
208976
210143
  const db = getDatabase();
208977
- const game = await db.query.games.findFirst({
208978
- where: eq(games.id, gameId)
208979
- });
208980
- if (!game) {
208981
- throw ApiError.notFound("Game not found");
208982
- }
208983
- if (user.role !== "admin" && game.developerId !== user.id) {
208984
- throw ApiError.forbidden("You do not own this game");
208985
- }
208986
210144
  const integration = await db.query.gameTimebackIntegrations.findFirst({
208987
210145
  where: eq(gameTimebackIntegrations.gameId, gameId)
208988
210146
  });
208989
210147
  if (!integration) {
210148
+ log2.error("[API] No TimeBack integration found", { gameId });
208990
210149
  throw ApiError.notFound("No TimeBack integration found for this game");
208991
210150
  }
208992
210151
  const client2 = await getTimebackClient();
@@ -209001,34 +210160,40 @@ async function deleteTimebackIntegration(ctx) {
209001
210160
  if (!gameId) {
209002
210161
  throw ApiError.badRequest("Missing gameId parameter");
209003
210162
  }
209004
- log2.debug("[API] Deleting TimeBack integration", { userId: user.id, gameId });
210163
+ log2.debug("[API] Deleting TimeBack integrations", { userId: user.id, gameId });
210164
+ await verifyGameAccessById(gameId, user);
209005
210165
  const db = getDatabase();
209006
- const game = await db.query.games.findFirst({
209007
- where: eq(games.id, gameId)
209008
- });
209009
- if (!game) {
209010
- throw ApiError.notFound("Game not found");
209011
- }
209012
- if (user.role !== "admin" && game.developerId !== user.id) {
209013
- throw ApiError.forbidden("You do not own this game");
209014
- }
209015
- const integration = await db.query.gameTimebackIntegrations.findFirst({
210166
+ const integrations = await db.query.gameTimebackIntegrations.findMany({
209016
210167
  where: eq(gameTimebackIntegrations.gameId, gameId)
209017
210168
  });
209018
- if (!integration) {
210169
+ if (integrations.length === 0) {
210170
+ log2.error("[API] No TimeBack integrations found", { gameId });
209019
210171
  throw ApiError.notFound("No TimeBack integration found for this game");
209020
210172
  }
209021
210173
  const client2 = await getTimebackClient();
209022
- try {
209023
- await client2.cleanup(integration.courseId);
209024
- } catch (error2) {
209025
- logTimebackError2("cleanup", error2, { gameId, courseId: integration.courseId });
209026
- throw error2;
210174
+ for (const integration of integrations) {
210175
+ try {
210176
+ await client2.cleanup(integration.courseId);
210177
+ log2.info("[API] Cleaned up TimeBack course", {
210178
+ gameId,
210179
+ courseId: integration.courseId,
210180
+ grade: integration.grade,
210181
+ subject: integration.subject
210182
+ });
210183
+ } catch (error2) {
210184
+ logTimebackError2("cleanup", error2, {
210185
+ gameId,
210186
+ courseId: integration.courseId,
210187
+ grade: integration.grade,
210188
+ subject: integration.subject
210189
+ });
210190
+ throw error2;
210191
+ }
209027
210192
  }
209028
- await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.id, integration.id));
209029
- log2.info("[API] Deleted TimeBack integration", {
210193
+ await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
210194
+ log2.info("[API] Deleted TimeBack integrations", {
209030
210195
  gameId,
209031
- courseId: integration.courseId
210196
+ coursesDeleted: integrations.length
209032
210197
  });
209033
210198
  }
209034
210199
  init_src();
@@ -209159,6 +210324,41 @@ async function getTimeBackXpHistory(ctx) {
209159
210324
  throw ApiError.internal("Failed to get TimeBack XP history", error2);
209160
210325
  }
209161
210326
  }
210327
+ init_src();
210328
+ async function getStudentEnrollments(ctx) {
210329
+ const user = ctx.user;
210330
+ if (!user) {
210331
+ throw ApiError.unauthorized("Must be logged in to get enrollments");
210332
+ }
210333
+ const timebackId = ctx.params.timebackId;
210334
+ if (!timebackId) {
210335
+ throw ApiError.badRequest("Missing timebackId parameter");
210336
+ }
210337
+ log2.debug("[API] Getting student enrollments", { userId: user.id, timebackId });
210338
+ const client2 = await getTimebackClient();
210339
+ const classes = await client2.getEnrollments(timebackId, { limit: 100 });
210340
+ const courseIds = classes.map((cls) => cls.courseId).filter((id) => Boolean(id));
210341
+ if (courseIds.length === 0) {
210342
+ return { enrollments: [] };
210343
+ }
210344
+ const db = getDatabase();
210345
+ const integrations = await db.query.gameTimebackIntegrations.findMany({
210346
+ where: inArray(gameTimebackIntegrations.courseId, courseIds)
210347
+ });
210348
+ const enrollments = integrations.map((integration) => ({
210349
+ gameId: integration.gameId,
210350
+ grade: integration.grade,
210351
+ subject: integration.subject,
210352
+ courseId: integration.courseId
210353
+ }));
210354
+ log2.info("[API] Retrieved student enrollments", {
210355
+ userId: user.id,
210356
+ timebackId,
210357
+ totalClasses: classes.length,
210358
+ mappedEnrollments: enrollments.length
210359
+ });
210360
+ return { enrollments };
210361
+ }
209162
210362
  var timebackRouter = new Hono2;
209163
210363
  timebackRouter.post("/populate-student", async (c3) => {
209164
210364
  return c3.json({
@@ -209349,6 +210549,25 @@ timebackRouter.post("/end-activity", async (c3) => {
209349
210549
  return c3.json(createUnknownErrorResponse(error2), 500);
209350
210550
  }
209351
210551
  });
210552
+ timebackRouter.get("/enrollments/:studentId", async (c3) => {
210553
+ const studentId = c3.req.param("studentId");
210554
+ const ctx = {
210555
+ user: c3.get("user"),
210556
+ params: { studentId },
210557
+ url: new URL(c3.req.url),
210558
+ request: c3.req.raw
210559
+ };
210560
+ try {
210561
+ const result = await getStudentEnrollments(ctx);
210562
+ return c3.json(result);
210563
+ } catch (error2) {
210564
+ if (error2 instanceof ApiError) {
210565
+ return c3.json(createErrorResponse(error2), error2.statusCode);
210566
+ }
210567
+ console.error("Error in getStudentEnrollments:", error2);
210568
+ return c3.json(createUnknownErrorResponse(error2), 500);
210569
+ }
210570
+ });
209352
210571
  init_src();
209353
210572
  async function createLtiSession(userId) {
209354
210573
  const db = getDatabase();
@@ -210319,7 +211538,8 @@ async function configurePlatformMode(server, viteConfig, options) {
210319
211538
  const backend = await setupCliDevServer({
210320
211539
  preferredPort: options.preferredBackendPort,
210321
211540
  viteConfig,
210322
- platformUrl: sandbox.baseUrl
211541
+ platformUrl: sandbox.baseUrl,
211542
+ configPath: options.configPath
210323
211543
  });
210324
211544
  serverState.backend = backend;
210325
211545
  if (sandbox.project) {
@@ -210342,11 +211562,12 @@ async function configurePlatformMode(server, viteConfig, options) {
210342
211562
 
210343
211563
  // src/server/standalone-mode.ts
210344
211564
  var import_picocolors6 = __toESM(require_picocolors(), 1);
210345
- async function configureStandaloneMode(server, viteConfig, preferredPort) {
211565
+ async function configureStandaloneMode(server, viteConfig, options) {
210346
211566
  const backend = await setupCliDevServer({
210347
- preferredPort,
211567
+ preferredPort: options.preferredPort,
210348
211568
  viteConfig,
210349
- platformUrl: undefined
211569
+ platformUrl: undefined,
211570
+ configPath: options.configPath
210350
211571
  });
210351
211572
  if (!backend) {
210352
211573
  viteConfig.logger.error("Failed to start backend server");
@@ -210366,25 +211587,85 @@ async function configureStandaloneMode(server, viteConfig, preferredPort) {
210366
211587
  }
210367
211588
 
210368
211589
  // src/server/mode-switcher.ts
211590
+ var { bold: bold3, cyan: cyan3, dim: dim4, green: green3, red } = import_picocolors7.default;
211591
+ function formatTimestamp2() {
211592
+ const now2 = new Date;
211593
+ const hours = now2.getHours();
211594
+ const minutes = now2.getMinutes().toString().padStart(2, "0");
211595
+ const seconds = now2.getSeconds().toString().padStart(2, "0");
211596
+ const ampm = hours >= 12 ? "PM" : "AM";
211597
+ const displayHours = hours % 12 || 12;
211598
+ return dim4(`${displayHours}:${minutes}:${seconds} ${ampm}`);
211599
+ }
210369
211600
  async function toggleMode(options) {
210370
211601
  const currentMode = getCurrentMode();
210371
211602
  const newMode = currentMode === "platform" ? "standalone" : "platform";
210372
211603
  const viteServer = getViteServerRef();
210373
211604
  if (!viteServer) {
210374
- options.viteConfig.logger.error("[Playcademy] Cannot toggle mode: no Vite server reference");
211605
+ options.viteConfig.logger.error(`${formatTimestamp2()} ${red(bold3("[playcademy]"))} ${red("Cannot toggle mode: no Vite server reference")}`);
210375
211606
  return;
210376
211607
  }
210377
211608
  await cleanupServers();
210378
211609
  await new Promise((resolve2) => setTimeout(resolve2, 100));
210379
211610
  setCurrentMode(newMode);
210380
211611
  if (newMode === "standalone") {
210381
- await configureStandaloneMode(viteServer, options.viteConfig, options.platformModeOptions.preferredBackendPort);
211612
+ await configureStandaloneMode(viteServer, options.viteConfig, {
211613
+ preferredPort: options.platformModeOptions.preferredBackendPort,
211614
+ configPath: options.platformModeOptions.configPath
211615
+ });
210382
211616
  } else {
210383
211617
  await configurePlatformMode(viteServer, options.viteConfig, options.platformModeOptions);
210384
211618
  }
210385
- options.viteConfig.logger.info(`
210386
- ${import_picocolors7.default.green(`✓ Switched to ${import_picocolors7.default.bold(newMode)} mode`)}
210387
- `);
211619
+ options.viteConfig.logger.info(`${formatTimestamp2()} ${cyan3(bold3("[playcademy]"))} ${green3("switched to")} ${green3(bold3(newMode))} ${green3("mode")}`);
211620
+ }
211621
+
211622
+ // src/server/recreate-sandbox-database.ts
211623
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
211624
+ var { bold: bold4, cyan: cyan4, dim: dim5, green: green4, red: red2, yellow } = import_picocolors8.default;
211625
+ function formatTimestamp3() {
211626
+ const now2 = new Date;
211627
+ const hours = now2.getHours();
211628
+ const minutes = now2.getMinutes().toString().padStart(2, "0");
211629
+ const seconds = now2.getSeconds().toString().padStart(2, "0");
211630
+ const ampm = hours >= 12 ? "PM" : "AM";
211631
+ const displayHours = hours % 12 || 12;
211632
+ return dim5(`${displayHours}:${minutes}:${seconds} ${ampm}`);
211633
+ }
211634
+ async function recreateSandboxDatabase(options) {
211635
+ const currentMode = getCurrentMode();
211636
+ const viteServer = getViteServerRef();
211637
+ if (!viteServer) {
211638
+ options.viteConfig.logger.error(`${formatTimestamp3()} ${red2(bold4("[playcademy]"))} ${dim5("(sandbox)")} ${red2("Cannot recreate sandbox database: no Vite server reference")}`);
211639
+ return;
211640
+ }
211641
+ if (currentMode !== "platform") {
211642
+ options.viteConfig.logger.warn(`${formatTimestamp3()} ${yellow(bold4("[playcademy]"))} ${dim5("(sandbox)")} ${yellow("can only recreate sandbox database in platform mode (m + enter)")}`);
211643
+ return;
211644
+ }
211645
+ options.viteConfig.logger.info(`${formatTimestamp3()} ${cyan4(bold4("[playcademy]"))} ${dim5("(sandbox)")} recreating database...`);
211646
+ if (serverState.sandbox) {
211647
+ serverState.sandbox.cleanup();
211648
+ serverState.sandbox = null;
211649
+ }
211650
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
211651
+ const sandbox = await startSandbox(options.viteConfig, options.platformModeOptions.startSandbox, {
211652
+ verbose: options.platformModeOptions.verbose,
211653
+ logLevel: options.platformModeOptions.logLevel,
211654
+ customUrl: options.platformModeOptions.sandboxUrl,
211655
+ quiet: true,
211656
+ recreateDb: true,
211657
+ seed: options.platformModeOptions.seed,
211658
+ memoryOnly: options.platformModeOptions.memoryOnly,
211659
+ databasePath: options.platformModeOptions.databasePath,
211660
+ realtimeEnabled: options.platformModeOptions.realtimeEnabled,
211661
+ realtimePort: options.platformModeOptions.realtimePort
211662
+ });
211663
+ serverState.sandbox = sandbox;
211664
+ if (sandbox.project && serverState.backend) {
211665
+ const gameUrl = `http://localhost:${serverState.backend.port}`;
211666
+ devServerMiddleware(viteServer, sandbox, gameUrl, options.platformModeOptions.showBadge);
211667
+ }
211668
+ options.viteConfig.logger.info(`${formatTimestamp3()} ${cyan4(bold4("[playcademy]"))} ${dim5("(sandbox)")} ${green4("database recreated")}`);
210388
211669
  }
210389
211670
 
210390
211671
  // src/hooks/configure-server.ts
@@ -210412,10 +211693,14 @@ async function configureServerHook(server, context) {
210412
211693
  realtimeEnabled: context.options.realtimeEnabled,
210413
211694
  realtimePort: context.options.realtimePort,
210414
211695
  showBadge: context.options.showBadge,
210415
- preferredBackendPort: preferredPort
211696
+ preferredBackendPort: preferredPort,
211697
+ configPath: context.options.configPath
210416
211698
  };
210417
211699
  if (context.options.mode === "standalone") {
210418
- await configureStandaloneMode(server, context.viteConfig, preferredPort);
211700
+ await configureStandaloneMode(server, context.viteConfig, {
211701
+ preferredPort,
211702
+ configPath: context.options.configPath
211703
+ });
210419
211704
  } else {
210420
211705
  await configurePlatformMode(server, context.viteConfig, platformModeOptions);
210421
211706
  }
@@ -210435,6 +211720,16 @@ async function configureServerHook(server, context) {
210435
211720
  platformModeOptions
210436
211721
  });
210437
211722
  }
211723
+ },
211724
+ {
211725
+ key: "d",
211726
+ description: "recreate sandbox database",
211727
+ action: async () => {
211728
+ await recreateSandboxDatabase({
211729
+ viteConfig: context.viteConfig,
211730
+ platformModeOptions
211731
+ });
211732
+ }
210438
211733
  }
210439
211734
  ]
210440
211735
  });
@@ -210470,6 +211765,7 @@ function resolveOptions(options) {
210470
211765
  const shellOptions = options.shell ?? {};
210471
211766
  const realtimeOptions = sandboxOptions.realtime ?? {};
210472
211767
  return {
211768
+ configPath: options.configPath,
210473
211769
  mode: options.mode ?? "platform",
210474
211770
  autoZip: exportOptions.autoZip ?? true,
210475
211771
  sandboxUrl: sandboxOptions.url ?? `http://localhost:${DEFAULT_PORTS4.SANDBOX}`,