@playcademy/sandbox 0.1.11 → 0.2.1
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/README.md +43 -69
- package/dist/cli.js +1955 -682
- package/dist/config.js +1 -0
- package/dist/server.js +1952 -679
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -60742,7 +60742,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60742
60742
|
var CAPTURE = 1;
|
|
60743
60743
|
var BUBBLE = 2;
|
|
60744
60744
|
var ATTRIBUTE = 3;
|
|
60745
|
-
function
|
|
60745
|
+
function isObject4(x6) {
|
|
60746
60746
|
return x6 !== null && typeof x6 === "object";
|
|
60747
60747
|
}
|
|
60748
60748
|
function getListeners(eventTarget) {
|
|
@@ -60766,7 +60766,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60766
60766
|
return null;
|
|
60767
60767
|
},
|
|
60768
60768
|
set(listener) {
|
|
60769
|
-
if (typeof listener !== "function" && !
|
|
60769
|
+
if (typeof listener !== "function" && !isObject4(listener)) {
|
|
60770
60770
|
listener = null;
|
|
60771
60771
|
}
|
|
60772
60772
|
const listeners = getListeners(this);
|
|
@@ -60846,11 +60846,11 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60846
60846
|
if (listener == null) {
|
|
60847
60847
|
return;
|
|
60848
60848
|
}
|
|
60849
|
-
if (typeof listener !== "function" && !
|
|
60849
|
+
if (typeof listener !== "function" && !isObject4(listener)) {
|
|
60850
60850
|
throw new TypeError("'listener' should be a function or an object.");
|
|
60851
60851
|
}
|
|
60852
60852
|
const listeners = getListeners(this);
|
|
60853
|
-
const optionsIsObj =
|
|
60853
|
+
const optionsIsObj = isObject4(options);
|
|
60854
60854
|
const capture = optionsIsObj ? Boolean(options.capture) : Boolean(options);
|
|
60855
60855
|
const listenerType = capture ? CAPTURE : BUBBLE;
|
|
60856
60856
|
const newNode = {
|
|
@@ -60880,7 +60880,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60880
60880
|
return;
|
|
60881
60881
|
}
|
|
60882
60882
|
const listeners = getListeners(this);
|
|
60883
|
-
const capture =
|
|
60883
|
+
const capture = isObject4(options) ? Boolean(options.capture) : Boolean(options);
|
|
60884
60884
|
const listenerType = capture ? CAPTURE : BUBBLE;
|
|
60885
60885
|
let prev = null;
|
|
60886
60886
|
let node = listeners.get(eventName);
|
|
@@ -66890,7 +66890,7 @@ var init_block_senders = __esm(() => {
|
|
|
66890
66890
|
|
|
66891
66891
|
// ../../node_modules/cloudflare/resources/email-security/settings/domains.mjs
|
|
66892
66892
|
var Domains, DomainListResponsesV4PagePaginationArray, DomainBulkDeleteResponsesSinglePage;
|
|
66893
|
-
var
|
|
66893
|
+
var init_domains3 = __esm(() => {
|
|
66894
66894
|
init_pagination();
|
|
66895
66895
|
Domains = class Domains extends APIResource {
|
|
66896
66896
|
list(params, options) {
|
|
@@ -67000,8 +67000,8 @@ var init_settings4 = __esm(() => {
|
|
|
67000
67000
|
init_allow_policies();
|
|
67001
67001
|
init_block_senders();
|
|
67002
67002
|
init_block_senders();
|
|
67003
|
-
|
|
67004
|
-
|
|
67003
|
+
init_domains3();
|
|
67004
|
+
init_domains3();
|
|
67005
67005
|
init_impersonation_registry();
|
|
67006
67006
|
init_impersonation_registry();
|
|
67007
67007
|
init_trusted_domains();
|
|
@@ -68291,7 +68291,7 @@ var init_bulks = __esm(() => {
|
|
|
68291
68291
|
|
|
68292
68292
|
// ../../node_modules/cloudflare/resources/intel/domains/domains.mjs
|
|
68293
68293
|
var Domains2;
|
|
68294
|
-
var
|
|
68294
|
+
var init_domains4 = __esm(() => {
|
|
68295
68295
|
init_bulks();
|
|
68296
68296
|
init_bulks();
|
|
68297
68297
|
Domains2 = class Domains2 extends APIResource {
|
|
@@ -68426,8 +68426,8 @@ var init_intel = __esm(() => {
|
|
|
68426
68426
|
init_asn3();
|
|
68427
68427
|
init_attack_surface_report();
|
|
68428
68428
|
init_attack_surface_report();
|
|
68429
|
-
|
|
68430
|
-
|
|
68429
|
+
init_domains4();
|
|
68430
|
+
init_domains4();
|
|
68431
68431
|
init_indicator_feeds();
|
|
68432
68432
|
init_indicator_feeds();
|
|
68433
68433
|
Intel = class Intel extends APIResource {
|
|
@@ -71305,7 +71305,7 @@ var init_page_shield = __esm(() => {
|
|
|
71305
71305
|
|
|
71306
71306
|
// ../../node_modules/cloudflare/resources/pages/projects/domains.mjs
|
|
71307
71307
|
var Domains3, DomainListResponsesSinglePage;
|
|
71308
|
-
var
|
|
71308
|
+
var init_domains5 = __esm(() => {
|
|
71309
71309
|
init_pagination();
|
|
71310
71310
|
Domains3 = class Domains3 extends APIResource {
|
|
71311
71311
|
create(projectName, params, options) {
|
|
@@ -71408,8 +71408,8 @@ var init_deployments = __esm(() => {
|
|
|
71408
71408
|
// ../../node_modules/cloudflare/resources/pages/projects/projects.mjs
|
|
71409
71409
|
var Projects, DeploymentsSinglePage;
|
|
71410
71410
|
var init_projects = __esm(() => {
|
|
71411
|
-
|
|
71412
|
-
|
|
71411
|
+
init_domains5();
|
|
71412
|
+
init_domains5();
|
|
71413
71413
|
init_deployments();
|
|
71414
71414
|
init_deployments();
|
|
71415
71415
|
init_pagination();
|
|
@@ -71976,7 +71976,7 @@ var init_managed = __esm(() => {
|
|
|
71976
71976
|
|
|
71977
71977
|
// ../../node_modules/cloudflare/resources/r2/buckets/domains/domains.mjs
|
|
71978
71978
|
var Domains4;
|
|
71979
|
-
var
|
|
71979
|
+
var init_domains6 = __esm(() => {
|
|
71980
71980
|
init_custom5();
|
|
71981
71981
|
init_custom5();
|
|
71982
71982
|
init_managed();
|
|
@@ -72007,8 +72007,8 @@ var init_buckets = __esm(() => {
|
|
|
72007
72007
|
init_metrics();
|
|
72008
72008
|
init_sippy();
|
|
72009
72009
|
init_sippy();
|
|
72010
|
-
|
|
72011
|
-
|
|
72010
|
+
init_domains6();
|
|
72011
|
+
init_domains6();
|
|
72012
72012
|
Buckets = class Buckets extends APIResource {
|
|
72013
72013
|
constructor() {
|
|
72014
72014
|
super(...arguments);
|
|
@@ -75360,7 +75360,7 @@ var init_rate_limits = __esm(() => {
|
|
|
75360
75360
|
|
|
75361
75361
|
// ../../node_modules/cloudflare/resources/registrar/domains.mjs
|
|
75362
75362
|
var Domains5, DomainsSinglePage;
|
|
75363
|
-
var
|
|
75363
|
+
var init_domains7 = __esm(() => {
|
|
75364
75364
|
init_pagination();
|
|
75365
75365
|
Domains5 = class Domains5 extends APIResource {
|
|
75366
75366
|
update(domainName, params, options) {
|
|
@@ -75387,8 +75387,8 @@ var init_domains5 = __esm(() => {
|
|
|
75387
75387
|
// ../../node_modules/cloudflare/resources/registrar/registrar.mjs
|
|
75388
75388
|
var Registrar;
|
|
75389
75389
|
var init_registrar = __esm(() => {
|
|
75390
|
-
|
|
75391
|
-
|
|
75390
|
+
init_domains7();
|
|
75391
|
+
init_domains7();
|
|
75392
75392
|
Registrar = class Registrar extends APIResource {
|
|
75393
75393
|
constructor() {
|
|
75394
75394
|
super(...arguments);
|
|
@@ -78354,7 +78354,7 @@ var init_account_settings = __esm(() => {
|
|
|
78354
78354
|
|
|
78355
78355
|
// ../../node_modules/cloudflare/resources/workers/domains.mjs
|
|
78356
78356
|
var Domains6, DomainsSinglePage2;
|
|
78357
|
-
var
|
|
78357
|
+
var init_domains8 = __esm(() => {
|
|
78358
78358
|
init_pagination();
|
|
78359
78359
|
Domains6 = class Domains6 extends APIResource {
|
|
78360
78360
|
update(params, options) {
|
|
@@ -78496,7 +78496,7 @@ var init_versions3 = __esm(() => {
|
|
|
78496
78496
|
|
|
78497
78497
|
// ../../node_modules/cloudflare/resources/workers/beta/workers/workers.mjs
|
|
78498
78498
|
var Workers, WorkersV4PagePaginationArray;
|
|
78499
|
-
var
|
|
78499
|
+
var init_workers3 = __esm(() => {
|
|
78500
78500
|
init_versions3();
|
|
78501
78501
|
init_versions3();
|
|
78502
78502
|
init_pagination();
|
|
@@ -78550,8 +78550,8 @@ var init_workers = __esm(() => {
|
|
|
78550
78550
|
// ../../node_modules/cloudflare/resources/workers/beta/beta.mjs
|
|
78551
78551
|
var Beta;
|
|
78552
78552
|
var init_beta = __esm(() => {
|
|
78553
|
-
|
|
78554
|
-
|
|
78553
|
+
init_workers3();
|
|
78554
|
+
init_workers3();
|
|
78555
78555
|
Beta = class Beta extends APIResource {
|
|
78556
78556
|
constructor() {
|
|
78557
78557
|
super(...arguments);
|
|
@@ -78943,11 +78943,11 @@ var init_scripts2 = __esm(() => {
|
|
|
78943
78943
|
|
|
78944
78944
|
// ../../node_modules/cloudflare/resources/workers/workers.mjs
|
|
78945
78945
|
var Workers2;
|
|
78946
|
-
var
|
|
78946
|
+
var init_workers4 = __esm(() => {
|
|
78947
78947
|
init_account_settings();
|
|
78948
78948
|
init_account_settings();
|
|
78949
|
-
|
|
78950
|
-
|
|
78949
|
+
init_domains8();
|
|
78950
|
+
init_domains8();
|
|
78951
78951
|
init_routes3();
|
|
78952
78952
|
init_routes3();
|
|
78953
78953
|
init_subdomains();
|
|
@@ -83972,7 +83972,7 @@ var init_resources3 = __esm(() => {
|
|
|
83972
83972
|
init_vectorize();
|
|
83973
83973
|
init_waiting_rooms();
|
|
83974
83974
|
init_web3();
|
|
83975
|
-
|
|
83975
|
+
init_workers4();
|
|
83976
83976
|
init_workers_for_platforms();
|
|
83977
83977
|
init_workflows();
|
|
83978
83978
|
init_zaraz();
|
|
@@ -84079,7 +84079,7 @@ var init_cloudflare = __esm(() => {
|
|
|
84079
84079
|
init_waiting_rooms();
|
|
84080
84080
|
init_web3();
|
|
84081
84081
|
init_workers_for_platforms();
|
|
84082
|
-
|
|
84082
|
+
init_workers4();
|
|
84083
84083
|
init_workflows();
|
|
84084
84084
|
init_zaraz();
|
|
84085
84085
|
init_zero_trust();
|
|
@@ -84548,7 +84548,7 @@ var require_dist_cjs2 = __commonJS((exports, module2) => {
|
|
|
84548
84548
|
Fields: () => Fields3,
|
|
84549
84549
|
HttpRequest: () => HttpRequest,
|
|
84550
84550
|
HttpResponse: () => HttpResponse,
|
|
84551
|
-
IHttpRequest: () =>
|
|
84551
|
+
IHttpRequest: () => import_types6.HttpRequest,
|
|
84552
84552
|
getHttpHandlerExtensionConfiguration: () => getHttpHandlerExtensionConfiguration,
|
|
84553
84553
|
isValidHostname: () => isValidHostname,
|
|
84554
84554
|
resolveHttpHandlerRuntimeConfig: () => resolveHttpHandlerRuntimeConfig
|
|
@@ -84575,12 +84575,12 @@ var require_dist_cjs2 = __commonJS((exports, module2) => {
|
|
|
84575
84575
|
httpHandler: httpHandlerExtensionConfiguration.httpHandler()
|
|
84576
84576
|
};
|
|
84577
84577
|
}, "resolveHttpHandlerRuntimeConfig");
|
|
84578
|
-
var
|
|
84578
|
+
var import_types6 = require_dist_cjs();
|
|
84579
84579
|
var Field = class {
|
|
84580
84580
|
static {
|
|
84581
84581
|
__name(this, "Field");
|
|
84582
84582
|
}
|
|
84583
|
-
constructor({ name: name4, kind: kind2 =
|
|
84583
|
+
constructor({ name: name4, kind: kind2 = import_types6.FieldPosition.HEADER, values = [] }) {
|
|
84584
84584
|
this.name = name4;
|
|
84585
84585
|
this.kind = kind2;
|
|
84586
84586
|
this.values = values;
|
|
@@ -85449,8 +85449,8 @@ var require_dist_cjs4 = __commonJS((exports, module2) => {
|
|
|
85449
85449
|
normalizeProvider: () => normalizeProvider
|
|
85450
85450
|
});
|
|
85451
85451
|
module2.exports = __toCommonJS(src_exports);
|
|
85452
|
-
var
|
|
85453
|
-
var getSmithyContext = /* @__PURE__ */ __name((context) => context[
|
|
85452
|
+
var import_types6 = require_dist_cjs();
|
|
85453
|
+
var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
|
|
85454
85454
|
var normalizeProvider = /* @__PURE__ */ __name((input) => {
|
|
85455
85455
|
if (typeof input === "function")
|
|
85456
85456
|
return input;
|
|
@@ -89530,8 +89530,8 @@ var require_dist_cjs16 = __commonJS((exports, module2) => {
|
|
|
89530
89530
|
setFeature: () => setFeature
|
|
89531
89531
|
});
|
|
89532
89532
|
module2.exports = __toCommonJS(src_exports);
|
|
89533
|
-
var
|
|
89534
|
-
var getSmithyContext = /* @__PURE__ */ __name((context) => context[
|
|
89533
|
+
var import_types6 = require_dist_cjs();
|
|
89534
|
+
var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
|
|
89535
89535
|
var import_util_middleware = require_dist_cjs4();
|
|
89536
89536
|
var resolveAuthOptions = /* @__PURE__ */ __name((candidateAuthOptions, authSchemePreference) => {
|
|
89537
89537
|
if (!authSchemePreference || authSchemePreference.length === 0) {
|
|
@@ -89767,9 +89767,9 @@ var require_dist_cjs16 = __commonJS((exports, module2) => {
|
|
|
89767
89767
|
throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
|
|
89768
89768
|
}
|
|
89769
89769
|
const clonedRequest = import_protocol_http.HttpRequest.clone(httpRequest);
|
|
89770
|
-
if (signingProperties.in ===
|
|
89770
|
+
if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.QUERY) {
|
|
89771
89771
|
clonedRequest.query[signingProperties.name] = identity.apiKey;
|
|
89772
|
-
} else if (signingProperties.in ===
|
|
89772
|
+
} else if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.HEADER) {
|
|
89773
89773
|
clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
|
|
89774
89774
|
} else {
|
|
89775
89775
|
throw new Error("request can only be signed with `apiKey` locations `query` or `header`, but found: `" + signingProperties.in + "`");
|
|
@@ -90852,7 +90852,7 @@ var require_httpAuthSchemes = __commonJS((exports, module2) => {
|
|
|
90852
90852
|
},
|
|
90853
90853
|
default: undefined
|
|
90854
90854
|
};
|
|
90855
|
-
var
|
|
90855
|
+
var import_client2 = require_client();
|
|
90856
90856
|
var import_core210 = require_dist_cjs16();
|
|
90857
90857
|
var import_signature_v4 = require_dist_cjs20();
|
|
90858
90858
|
var resolveAwsSdkSigV4Config = /* @__PURE__ */ __name((config2) => {
|
|
@@ -90871,7 +90871,7 @@ var require_httpAuthSchemes = __commonJS((exports, module2) => {
|
|
|
90871
90871
|
});
|
|
90872
90872
|
const boundProvider = bindCallerConfig(config2, memoizedProvider);
|
|
90873
90873
|
if (isUserSupplied && !boundProvider.attributed) {
|
|
90874
|
-
resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0,
|
|
90874
|
+
resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_CODE", "e")), "resolvedCredentials");
|
|
90875
90875
|
resolvedCredentials.memoized = boundProvider.memoized;
|
|
90876
90876
|
resolvedCredentials.configBound = boundProvider.configBound;
|
|
90877
90877
|
resolvedCredentials.attributed = true;
|
|
@@ -91419,7 +91419,7 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91419
91419
|
}
|
|
91420
91420
|
};
|
|
91421
91421
|
var import_protocols = require_protocols();
|
|
91422
|
-
var
|
|
91422
|
+
var import_types6 = require_dist_cjs();
|
|
91423
91423
|
var Command = class {
|
|
91424
91424
|
constructor() {
|
|
91425
91425
|
this.middlewareStack = (0, import_middleware_stack.constructStack)();
|
|
@@ -91451,7 +91451,7 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91451
91451
|
commandName,
|
|
91452
91452
|
inputFilterSensitiveLog,
|
|
91453
91453
|
outputFilterSensitiveLog,
|
|
91454
|
-
[
|
|
91454
|
+
[import_types6.SMITHY_CONTEXT_KEY]: {
|
|
91455
91455
|
commandInstance: this,
|
|
91456
91456
|
...smithyContext
|
|
91457
91457
|
},
|
|
@@ -91676,8 +91676,8 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91676
91676
|
}, "emitWarningIfUnsupportedVersion");
|
|
91677
91677
|
var getChecksumConfiguration = /* @__PURE__ */ __name((runtimeConfig) => {
|
|
91678
91678
|
const checksumAlgorithms = [];
|
|
91679
|
-
for (const id in
|
|
91680
|
-
const algorithmId =
|
|
91679
|
+
for (const id in import_types6.AlgorithmId) {
|
|
91680
|
+
const algorithmId = import_types6.AlgorithmId[id];
|
|
91681
91681
|
if (runtimeConfig[algorithmId] === undefined) {
|
|
91682
91682
|
continue;
|
|
91683
91683
|
}
|
|
@@ -93861,7 +93861,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93861
93861
|
return this._read(schema4, data);
|
|
93862
93862
|
}
|
|
93863
93863
|
_read(schema4, value) {
|
|
93864
|
-
const
|
|
93864
|
+
const isObject4 = value !== null && typeof value === "object";
|
|
93865
93865
|
const ns = import_schema2.NormalizedSchema.of(schema4);
|
|
93866
93866
|
if (ns.isListSchema() && Array.isArray(value)) {
|
|
93867
93867
|
const listMember = ns.getValueSchema();
|
|
@@ -93873,7 +93873,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93873
93873
|
}
|
|
93874
93874
|
}
|
|
93875
93875
|
return out2;
|
|
93876
|
-
} else if (ns.isMapSchema() &&
|
|
93876
|
+
} else if (ns.isMapSchema() && isObject4) {
|
|
93877
93877
|
const mapMember = ns.getValueSchema();
|
|
93878
93878
|
const out2 = {};
|
|
93879
93879
|
const sparse = !!ns.getMergedTraits().sparse;
|
|
@@ -93883,7 +93883,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93883
93883
|
}
|
|
93884
93884
|
}
|
|
93885
93885
|
return out2;
|
|
93886
|
-
} else if (ns.isStructSchema() &&
|
|
93886
|
+
} else if (ns.isStructSchema() && isObject4) {
|
|
93887
93887
|
const out2 = {};
|
|
93888
93888
|
for (const [memberName, memberSchema] of ns.structIterator()) {
|
|
93889
93889
|
const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
|
|
@@ -94015,7 +94015,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94015
94015
|
return this.buffer;
|
|
94016
94016
|
}
|
|
94017
94017
|
_write(schema4, value, container) {
|
|
94018
|
-
const
|
|
94018
|
+
const isObject4 = value !== null && typeof value === "object";
|
|
94019
94019
|
const ns = import_schema22.NormalizedSchema.of(schema4);
|
|
94020
94020
|
if (ns.isListSchema() && Array.isArray(value)) {
|
|
94021
94021
|
const listMember = ns.getValueSchema();
|
|
@@ -94027,7 +94027,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94027
94027
|
}
|
|
94028
94028
|
}
|
|
94029
94029
|
return out2;
|
|
94030
|
-
} else if (ns.isMapSchema() &&
|
|
94030
|
+
} else if (ns.isMapSchema() && isObject4) {
|
|
94031
94031
|
const mapMember = ns.getValueSchema();
|
|
94032
94032
|
const out2 = {};
|
|
94033
94033
|
const sparse = !!ns.getMergedTraits().sparse;
|
|
@@ -94037,7 +94037,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94037
94037
|
}
|
|
94038
94038
|
}
|
|
94039
94039
|
return out2;
|
|
94040
|
-
} else if (ns.isStructSchema() &&
|
|
94040
|
+
} else if (ns.isStructSchema() && isObject4) {
|
|
94041
94041
|
const out2 = {};
|
|
94042
94042
|
for (const [memberName, memberSchema] of ns.structIterator()) {
|
|
94043
94043
|
const targetKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
|
|
@@ -101587,16 +101587,16 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101587
101587
|
var getProfileName = /* @__PURE__ */ __name((init3) => init3.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE, "getProfileName");
|
|
101588
101588
|
__reExport(src_exports, require_getSSOTokenFilepath(), module2.exports);
|
|
101589
101589
|
__reExport(src_exports, require_getSSOTokenFromFile(), module2.exports);
|
|
101590
|
-
var
|
|
101590
|
+
var import_types6 = require_dist_cjs();
|
|
101591
101591
|
var getConfigData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => {
|
|
101592
101592
|
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
|
101593
101593
|
if (indexOfSeparator === -1) {
|
|
101594
101594
|
return false;
|
|
101595
101595
|
}
|
|
101596
|
-
return Object.values(
|
|
101596
|
+
return Object.values(import_types6.IniSectionType).includes(key.substring(0, indexOfSeparator));
|
|
101597
101597
|
}).reduce((acc, [key, value]) => {
|
|
101598
101598
|
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
|
101599
|
-
const updatedKey = key.substring(0, indexOfSeparator) ===
|
|
101599
|
+
const updatedKey = key.substring(0, indexOfSeparator) === import_types6.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
|
|
101600
101600
|
acc[updatedKey] = value;
|
|
101601
101601
|
return acc;
|
|
101602
101602
|
}, {
|
|
@@ -101626,7 +101626,7 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101626
101626
|
const matches = prefixKeyRegex.exec(sectionName);
|
|
101627
101627
|
if (matches) {
|
|
101628
101628
|
const [, prefix2, , name4] = matches;
|
|
101629
|
-
if (Object.values(
|
|
101629
|
+
if (Object.values(import_types6.IniSectionType).includes(prefix2)) {
|
|
101630
101630
|
currentSection = [prefix2, name4].join(CONFIG_PREFIX_SEPARATOR);
|
|
101631
101631
|
}
|
|
101632
101632
|
} else {
|
|
@@ -101685,7 +101685,7 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101685
101685
|
credentialsFile: parsedFiles[1]
|
|
101686
101686
|
};
|
|
101687
101687
|
}, "loadSharedConfigFiles");
|
|
101688
|
-
var getSsoSessionData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => key.startsWith(
|
|
101688
|
+
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");
|
|
101689
101689
|
var import_slurpFile2 = require_slurpFile();
|
|
101690
101690
|
var swallowError2 = /* @__PURE__ */ __name(() => ({}), "swallowError");
|
|
101691
101691
|
var loadSsoSessionData = /* @__PURE__ */ __name(async (init3 = {}) => (0, import_slurpFile2.slurpFile)(init3.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError2), "loadSsoSessionData");
|
|
@@ -102684,7 +102684,7 @@ var require_dist_cjs57 = __commonJS((exports, module2) => {
|
|
|
102684
102684
|
fromEnv: () => fromEnv
|
|
102685
102685
|
});
|
|
102686
102686
|
module2.exports = __toCommonJS(index_exports);
|
|
102687
|
-
var
|
|
102687
|
+
var import_client2 = require_client();
|
|
102688
102688
|
var import_property_provider = require_dist_cjs17();
|
|
102689
102689
|
var ENV_KEY = "AWS_ACCESS_KEY_ID";
|
|
102690
102690
|
var ENV_SECRET = "AWS_SECRET_ACCESS_KEY";
|
|
@@ -102709,7 +102709,7 @@ var require_dist_cjs57 = __commonJS((exports, module2) => {
|
|
|
102709
102709
|
...credentialScope && { credentialScope },
|
|
102710
102710
|
...accountId && { accountId }
|
|
102711
102711
|
};
|
|
102712
|
-
(0,
|
|
102712
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_ENV_VARS", "g");
|
|
102713
102713
|
return credentials2;
|
|
102714
102714
|
}
|
|
102715
102715
|
throw new import_property_provider.CredentialsProviderError("Unable to find environment variable credentials.", { logger: init3?.logger });
|
|
@@ -105630,7 +105630,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105630
105630
|
var __getOwnPropNames3 = Object.getOwnPropertyNames;
|
|
105631
105631
|
var __hasOwnProp3 = Object.prototype.hasOwnProperty;
|
|
105632
105632
|
var __name = (target, value) => __defProp4(target, "name", { value, configurable: true });
|
|
105633
|
-
var
|
|
105633
|
+
var __esm5 = (fn2, res) => function __init() {
|
|
105634
105634
|
return fn2 && (res = (0, fn2[__getOwnPropNames3(fn2)[0]])(fn2 = 0)), res;
|
|
105635
105635
|
};
|
|
105636
105636
|
var __export4 = (target, all) => {
|
|
@@ -105652,7 +105652,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105652
105652
|
SSOClient: () => import_client_sso.SSOClient
|
|
105653
105653
|
});
|
|
105654
105654
|
var import_client_sso;
|
|
105655
|
-
var init_loadSso =
|
|
105655
|
+
var init_loadSso = __esm5({
|
|
105656
105656
|
"src/loadSso.ts"() {
|
|
105657
105657
|
import_client_sso = require_dist_cjs65();
|
|
105658
105658
|
}
|
|
@@ -105665,7 +105665,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105665
105665
|
});
|
|
105666
105666
|
module2.exports = __toCommonJS(index_exports);
|
|
105667
105667
|
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");
|
|
105668
|
-
var
|
|
105668
|
+
var import_client2 = require_client();
|
|
105669
105669
|
var import_token_providers = require_dist_cjs66();
|
|
105670
105670
|
var import_property_provider = require_dist_cjs17();
|
|
105671
105671
|
var import_shared_ini_file_loader = require_dist_cjs52();
|
|
@@ -105750,9 +105750,9 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105750
105750
|
...accountId && { accountId }
|
|
105751
105751
|
};
|
|
105752
105752
|
if (ssoSession) {
|
|
105753
|
-
(0,
|
|
105753
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO", "s");
|
|
105754
105754
|
} else {
|
|
105755
|
-
(0,
|
|
105755
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO_LEGACY", "u");
|
|
105756
105756
|
}
|
|
105757
105757
|
return credentials2;
|
|
105758
105758
|
}, "resolveSSOCredentials");
|
|
@@ -106952,7 +106952,7 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
106952
106952
|
};
|
|
106953
106953
|
(0, import_smithy_client6.createAggregatedClient)(commands, STS);
|
|
106954
106954
|
var import_EndpointParameters3 = require_EndpointParameters();
|
|
106955
|
-
var
|
|
106955
|
+
var import_client2 = require_client();
|
|
106956
106956
|
var ASSUME_ROLE_DEFAULT_REGION = "us-east-1";
|
|
106957
106957
|
var getAccountIdFromAssumedRoleUser = /* @__PURE__ */ __name((assumedRoleUser) => {
|
|
106958
106958
|
if (typeof assumedRoleUser?.Arn === "string") {
|
|
@@ -107004,7 +107004,7 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
107004
107004
|
...Credentials2.CredentialScope && { credentialScope: Credentials2.CredentialScope },
|
|
107005
107005
|
...accountId && { accountId }
|
|
107006
107006
|
};
|
|
107007
|
-
(0,
|
|
107007
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE", "i");
|
|
107008
107008
|
return credentials2;
|
|
107009
107009
|
};
|
|
107010
107010
|
}, "getDefaultRoleAssumer");
|
|
@@ -107041,9 +107041,9 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
107041
107041
|
...accountId && { accountId }
|
|
107042
107042
|
};
|
|
107043
107043
|
if (accountId) {
|
|
107044
|
-
(0,
|
|
107044
|
+
(0, import_client2.setCredentialFeature)(credentials2, "RESOLVED_ACCOUNT_ID", "T");
|
|
107045
107045
|
}
|
|
107046
|
-
(0,
|
|
107046
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k");
|
|
107047
107047
|
return credentials2;
|
|
107048
107048
|
};
|
|
107049
107049
|
}, "getDefaultRoleAssumerWithWebIdentity");
|
|
@@ -107105,7 +107105,7 @@ var require_dist_cjs68 = __commonJS((exports, module2) => {
|
|
|
107105
107105
|
var import_property_provider = require_dist_cjs17();
|
|
107106
107106
|
var import_child_process = __require("child_process");
|
|
107107
107107
|
var import_util3 = __require("util");
|
|
107108
|
-
var
|
|
107108
|
+
var import_client2 = require_client();
|
|
107109
107109
|
var getValidatedProcessCredentials = /* @__PURE__ */ __name((profileName, data, profiles) => {
|
|
107110
107110
|
if (data.Version !== 1) {
|
|
107111
107111
|
throw Error(`Profile ${profileName} credential_process did not return Version 1.`);
|
|
@@ -107132,7 +107132,7 @@ var require_dist_cjs68 = __commonJS((exports, module2) => {
|
|
|
107132
107132
|
...data.CredentialScope && { credentialScope: data.CredentialScope },
|
|
107133
107133
|
...accountId && { accountId }
|
|
107134
107134
|
};
|
|
107135
|
-
(0,
|
|
107135
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROCESS", "w");
|
|
107136
107136
|
return credentials2;
|
|
107137
107137
|
}, "getValidatedProcessCredentials");
|
|
107138
107138
|
var resolveProcessCredentials = /* @__PURE__ */ __name(async (profileName, profiles, logger3) => {
|
|
@@ -107333,7 +107333,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107333
107333
|
});
|
|
107334
107334
|
module2.exports = __toCommonJS(index_exports);
|
|
107335
107335
|
var import_shared_ini_file_loader = require_dist_cjs52();
|
|
107336
|
-
var
|
|
107336
|
+
var import_client2 = require_client();
|
|
107337
107337
|
var import_property_provider = require_dist_cjs17();
|
|
107338
107338
|
var resolveCredentialSource = /* @__PURE__ */ __name((credentialSource, profileName, logger3) => {
|
|
107339
107339
|
const sourceProvidersMap = {
|
|
@@ -107360,7 +107360,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107360
107360
|
throw new import_property_provider.CredentialsProviderError(`Unsupported credential source in profile ${profileName}. Got ${credentialSource}, expected EcsContainer or Ec2InstanceMetadata or Environment.`, { logger: logger3 });
|
|
107361
107361
|
}
|
|
107362
107362
|
}, "resolveCredentialSource");
|
|
107363
|
-
var setNamedProvider = /* @__PURE__ */ __name((creds) => (0,
|
|
107363
|
+
var setNamedProvider = /* @__PURE__ */ __name((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_NAMED_PROVIDER", "p"), "setNamedProvider");
|
|
107364
107364
|
var isAssumeRoleProfile = /* @__PURE__ */ __name((arg, { profile = "default", logger: logger3 } = {}) => {
|
|
107365
107365
|
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 }));
|
|
107366
107366
|
}, "isAssumeRoleProfile");
|
|
@@ -107402,7 +107402,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107402
107402
|
[source_profile]: true
|
|
107403
107403
|
}, isCredentialSourceWithoutRoleArn(profiles[source_profile] ?? {})) : (await resolveCredentialSource(profileData.credential_source, profileName, options.logger)(options))();
|
|
107404
107404
|
if (isCredentialSourceWithoutRoleArn(profileData)) {
|
|
107405
|
-
return sourceCredsProvider.then((creds) => (0,
|
|
107405
|
+
return sourceCredsProvider.then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
|
|
107406
107406
|
} else {
|
|
107407
107407
|
const params = {
|
|
107408
107408
|
RoleArn: profileData.role_arn,
|
|
@@ -107419,7 +107419,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107419
107419
|
params.TokenCode = await options.mfaCodeProvider(mfa_serial);
|
|
107420
107420
|
}
|
|
107421
107421
|
const sourceCreds = await sourceCredsProvider;
|
|
107422
|
-
return options.roleAssumer(sourceCreds, params).then((creds) => (0,
|
|
107422
|
+
return options.roleAssumer(sourceCreds, params).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
|
|
107423
107423
|
}
|
|
107424
107424
|
}, "resolveAssumeRoleCredentials");
|
|
107425
107425
|
var isCredentialSourceWithoutRoleArn = /* @__PURE__ */ __name((section) => {
|
|
@@ -107429,7 +107429,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107429
107429
|
var resolveProcessCredentials = /* @__PURE__ */ __name(async (options, profile) => Promise.resolve().then(() => __toESM3(require_dist_cjs68())).then(({ fromProcess }) => fromProcess({
|
|
107430
107430
|
...options,
|
|
107431
107431
|
profile
|
|
107432
|
-
})().then((creds) => (0,
|
|
107432
|
+
})().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_PROCESS", "v"))), "resolveProcessCredentials");
|
|
107433
107433
|
var resolveSsoCredentials = /* @__PURE__ */ __name(async (profile, profileData, options = {}) => {
|
|
107434
107434
|
const { fromSSO } = await Promise.resolve().then(() => __toESM3(require_dist_cjs67()));
|
|
107435
107435
|
return fromSSO({
|
|
@@ -107439,9 +107439,9 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107439
107439
|
clientConfig: options.clientConfig
|
|
107440
107440
|
})().then((creds) => {
|
|
107441
107441
|
if (profileData.sso_session) {
|
|
107442
|
-
return (0,
|
|
107442
|
+
return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO", "r");
|
|
107443
107443
|
} else {
|
|
107444
|
-
return (0,
|
|
107444
|
+
return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO_LEGACY", "t");
|
|
107445
107445
|
}
|
|
107446
107446
|
});
|
|
107447
107447
|
}, "resolveSsoCredentials");
|
|
@@ -107456,7 +107456,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107456
107456
|
...profile.aws_credential_scope && { credentialScope: profile.aws_credential_scope },
|
|
107457
107457
|
...profile.aws_account_id && { accountId: profile.aws_account_id }
|
|
107458
107458
|
};
|
|
107459
|
-
return (0,
|
|
107459
|
+
return (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROFILE", "n");
|
|
107460
107460
|
}, "resolveStaticCredentials");
|
|
107461
107461
|
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");
|
|
107462
107462
|
var resolveWebIdentityCredentials = /* @__PURE__ */ __name(async (profile, options) => Promise.resolve().then(() => __toESM3(require_dist_cjs69())).then(({ fromTokenFile: fromTokenFile2 }) => fromTokenFile2({
|
|
@@ -107466,7 +107466,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107466
107466
|
roleAssumerWithWebIdentity: options.roleAssumerWithWebIdentity,
|
|
107467
107467
|
logger: options.logger,
|
|
107468
107468
|
parentClientConfig: options.parentClientConfig
|
|
107469
|
-
})().then((creds) => (0,
|
|
107469
|
+
})().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN", "q"))), "resolveWebIdentityCredentials");
|
|
107470
107470
|
var resolveProfileData = /* @__PURE__ */ __name(async (profileName, profiles, options, visitedProfiles = {}, isAssumeRoleRecursiveCall = false) => {
|
|
107471
107471
|
const data = profiles[profileName];
|
|
107472
107472
|
if (Object.keys(visitedProfiles).length > 0 && isStaticCredsProfile(data)) {
|
|
@@ -126308,7 +126308,7 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126308
126308
|
}
|
|
126309
126309
|
}
|
|
126310
126310
|
function baseKeysIn(object) {
|
|
126311
|
-
if (!
|
|
126311
|
+
if (!isObject4(object)) {
|
|
126312
126312
|
return nativeKeysIn(object);
|
|
126313
126313
|
}
|
|
126314
126314
|
var isProto = isPrototype(object), result = [];
|
|
@@ -126368,7 +126368,7 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126368
126368
|
return !!length && (typeof value == "number" || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
|
|
126369
126369
|
}
|
|
126370
126370
|
function isIterateeCall(value, index6, object) {
|
|
126371
|
-
if (!
|
|
126371
|
+
if (!isObject4(object)) {
|
|
126372
126372
|
return false;
|
|
126373
126373
|
}
|
|
126374
126374
|
var type = typeof index6;
|
|
@@ -126404,13 +126404,13 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126404
126404
|
return isObjectLike2(value) && isArrayLike(value);
|
|
126405
126405
|
}
|
|
126406
126406
|
function isFunction3(value) {
|
|
126407
|
-
var tag2 =
|
|
126407
|
+
var tag2 = isObject4(value) ? objectToString.call(value) : "";
|
|
126408
126408
|
return tag2 == funcTag || tag2 == genTag;
|
|
126409
126409
|
}
|
|
126410
126410
|
function isLength(value) {
|
|
126411
126411
|
return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
|
|
126412
126412
|
}
|
|
126413
|
-
function
|
|
126413
|
+
function isObject4(value) {
|
|
126414
126414
|
var type = typeof value;
|
|
126415
126415
|
return !!value && (type == "object" || type == "function");
|
|
126416
126416
|
}
|
|
@@ -126450,13 +126450,13 @@ var require_lodash3 = __commonJS((exports, module2) => {
|
|
|
126450
126450
|
return isObjectLike2(value) && isArrayLike(value);
|
|
126451
126451
|
}
|
|
126452
126452
|
function isFunction3(value) {
|
|
126453
|
-
var tag2 =
|
|
126453
|
+
var tag2 = isObject4(value) ? objectToString.call(value) : "";
|
|
126454
126454
|
return tag2 == funcTag || tag2 == genTag;
|
|
126455
126455
|
}
|
|
126456
126456
|
function isLength(value) {
|
|
126457
126457
|
return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
|
|
126458
126458
|
}
|
|
126459
|
-
function
|
|
126459
|
+
function isObject4(value) {
|
|
126460
126460
|
var type = typeof value;
|
|
126461
126461
|
return !!value && (type == "object" || type == "function");
|
|
126462
126462
|
}
|
|
@@ -131607,7 +131607,7 @@ var require_built3 = __commonJS((exports, module2) => {
|
|
|
131607
131607
|
|
|
131608
131608
|
// ../realtime/src/server/domain/constants.ts
|
|
131609
131609
|
var HUB_PREFIX, MAP_PREFIX, PRESENCE_CHANNEL_NAME, DEFAULT_GAME_CHANNEL, WebSocketCloseCode, WebSocketCloseReason, REDIS_PRESENCE_TTL = 300, REDIS_RECONNECTION_ATTEMPTS = 10, REDIS_MAX_RECONNECTION_DELAY = 3000, REDIS_HASH_TAG_PRESENCE = "{presence}", REDIS_HASH_TAG_POSITION = "{position}", REDIS_PRESENCE_KEY_PREFIX, REDIS_ONLINE_USERS_KEY, REDIS_POSITION_KEY_PREFIX, REDIS_MAP_PLAYERS_KEY_PREFIX;
|
|
131610
|
-
var
|
|
131610
|
+
var init_constants3 = __esm(() => {
|
|
131611
131611
|
HUB_PREFIX = process.env.HUB_PREFIX ?? "hub:";
|
|
131612
131612
|
MAP_PREFIX = process.env.MAP_PREFIX ?? "map_";
|
|
131613
131613
|
PRESENCE_CHANNEL_NAME = process.env.PRESENCE_CHANNEL_NAME ?? "presence";
|
|
@@ -131756,7 +131756,7 @@ function isRedisConnected() {
|
|
|
131756
131756
|
var import_ioredis, client = null;
|
|
131757
131757
|
var init_client = __esm(() => {
|
|
131758
131758
|
init_src();
|
|
131759
|
-
|
|
131759
|
+
init_constants3();
|
|
131760
131760
|
import_ioredis = __toESM(require_built3(), 1);
|
|
131761
131761
|
});
|
|
131762
131762
|
|
|
@@ -131869,7 +131869,7 @@ async function updateUserNameplateBg(userId, color) {
|
|
|
131869
131869
|
}
|
|
131870
131870
|
var init_presence = __esm(() => {
|
|
131871
131871
|
init_src();
|
|
131872
|
-
|
|
131872
|
+
init_constants3();
|
|
131873
131873
|
init_client();
|
|
131874
131874
|
});
|
|
131875
131875
|
|
|
@@ -131920,7 +131920,7 @@ function extractTokenFromUrl(url) {
|
|
|
131920
131920
|
return null;
|
|
131921
131921
|
}
|
|
131922
131922
|
}
|
|
131923
|
-
var
|
|
131923
|
+
var init_auth3 = __esm(() => {
|
|
131924
131924
|
init_esm2();
|
|
131925
131925
|
init_src();
|
|
131926
131926
|
});
|
|
@@ -132056,7 +132056,7 @@ var init_sandbox = __esm(() => {
|
|
|
132056
132056
|
init_wrapper();
|
|
132057
132057
|
init_src();
|
|
132058
132058
|
init_infrastructure2();
|
|
132059
|
-
|
|
132059
|
+
init_auth3();
|
|
132060
132060
|
init_config4();
|
|
132061
132061
|
});
|
|
132062
132062
|
// ../realtime/src/server/application/services/presence.ts
|
|
@@ -132143,7 +132143,7 @@ var init_presence2 = __esm(() => {
|
|
|
132143
132143
|
var playerPositions, PlayerPositionService;
|
|
132144
132144
|
var init_player_position = __esm(() => {
|
|
132145
132145
|
init_src();
|
|
132146
|
-
|
|
132146
|
+
init_constants3();
|
|
132147
132147
|
init_infrastructure2();
|
|
132148
132148
|
playerPositions = new Map;
|
|
132149
132149
|
PlayerPositionService = {
|
|
@@ -132275,7 +132275,7 @@ function computeInitialChannelKey(gameId, requested) {
|
|
|
132275
132275
|
return `${HUB_PREFIX}${channel}`;
|
|
132276
132276
|
}
|
|
132277
132277
|
var init_channel_keys = __esm(() => {
|
|
132278
|
-
|
|
132278
|
+
init_constants3();
|
|
132279
132279
|
});
|
|
132280
132280
|
|
|
132281
132281
|
// ../realtime/src/server/shared/utils/websocket.ts
|
|
@@ -132296,7 +132296,7 @@ function checkMatchingPlayerId(ws, payload) {
|
|
|
132296
132296
|
}
|
|
132297
132297
|
var init_websocket = __esm(() => {
|
|
132298
132298
|
init_src();
|
|
132299
|
-
|
|
132299
|
+
init_constants3();
|
|
132300
132300
|
});
|
|
132301
132301
|
|
|
132302
132302
|
// ../realtime/src/server/shared/utils/route.ts
|
|
@@ -132651,7 +132651,7 @@ async function handleWebSocketUpgrade(req, server, config2) {
|
|
|
132651
132651
|
}
|
|
132652
132652
|
var init_websocket2 = __esm(() => {
|
|
132653
132653
|
init_src();
|
|
132654
|
-
|
|
132654
|
+
init_auth3();
|
|
132655
132655
|
init_response();
|
|
132656
132656
|
});
|
|
132657
132657
|
|
|
@@ -132838,7 +132838,7 @@ var init_open = __esm(() => {
|
|
|
132838
132838
|
init_src();
|
|
132839
132839
|
init_presence4();
|
|
132840
132840
|
init_services2();
|
|
132841
|
-
|
|
132841
|
+
init_constants3();
|
|
132842
132842
|
init_events();
|
|
132843
132843
|
init_infrastructure2();
|
|
132844
132844
|
init_utils13();
|
|
@@ -133247,6 +133247,7 @@ var config = {
|
|
|
133247
133247
|
};
|
|
133248
133248
|
process.env.BETTER_AUTH_SECRET = config.auth.betterAuthSecret;
|
|
133249
133249
|
process.env.GAME_JWT_SECRET = config.auth.gameJwtSecret;
|
|
133250
|
+
process.env.PUBLIC_IS_LOCAL = "true";
|
|
133250
133251
|
|
|
133251
133252
|
// ../../node_modules/@hono/node-server/dist/index.mjs
|
|
133252
133253
|
import { createServer as createServerHTTP } from "http";
|
|
@@ -133792,7 +133793,7 @@ var serve = (options, listeningListener) => {
|
|
|
133792
133793
|
// package.json
|
|
133793
133794
|
var package_default = {
|
|
133794
133795
|
name: "@playcademy/sandbox",
|
|
133795
|
-
version: "0.1
|
|
133796
|
+
version: "0.2.1",
|
|
133796
133797
|
description: "Local development server for Playcademy game development",
|
|
133797
133798
|
type: "module",
|
|
133798
133799
|
exports: {
|
|
@@ -140872,12 +140873,17 @@ var timebackXpEvents = pgTable("timeback_xp_event", {
|
|
|
140872
140873
|
}, (table) => [uniqueIndex("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
|
|
140873
140874
|
var gameTimebackIntegrations = pgTable("game_timeback_integrations", {
|
|
140874
140875
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
140875
|
-
gameId: uuid("game_id").notNull().
|
|
140876
|
+
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
140876
140877
|
courseId: text("course_id").notNull(),
|
|
140878
|
+
grade: integer("grade").notNull(),
|
|
140879
|
+
subject: text("subject").notNull(),
|
|
140880
|
+
totalXp: integer("total_xp"),
|
|
140877
140881
|
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
|
|
140878
140882
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
140879
140883
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
140880
|
-
})
|
|
140884
|
+
}, (table) => [
|
|
140885
|
+
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table.gameId, table.grade, table.subject)
|
|
140886
|
+
]);
|
|
140881
140887
|
// ../data/src/domains/achievement/table.ts
|
|
140882
140888
|
var achievementScopeEnum = pgEnum("achievement_scope", [
|
|
140883
140889
|
"daily",
|
|
@@ -147460,6 +147466,292 @@ var __export3 = (target, all) => {
|
|
|
147460
147466
|
set: (newValue) => all[name3] = () => newValue
|
|
147461
147467
|
});
|
|
147462
147468
|
};
|
|
147469
|
+
var __esm3 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
|
|
147470
|
+
var init_auth = () => {};
|
|
147471
|
+
var PLAYCADEMY_BASE_URLS;
|
|
147472
|
+
var init_domains = __esm3(() => {
|
|
147473
|
+
PLAYCADEMY_BASE_URLS = {
|
|
147474
|
+
production: "https://hub.playcademy.net",
|
|
147475
|
+
staging: "https://hub.dev.playcademy.net"
|
|
147476
|
+
};
|
|
147477
|
+
});
|
|
147478
|
+
var init_env_vars = () => {};
|
|
147479
|
+
var ITEM_SLUGS2;
|
|
147480
|
+
var CURRENCIES2;
|
|
147481
|
+
var BADGES2;
|
|
147482
|
+
var init_overworld = __esm3(() => {
|
|
147483
|
+
ITEM_SLUGS2 = {
|
|
147484
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
147485
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
147486
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
147487
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
147488
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
147489
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
147490
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
147491
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
147492
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
147493
|
+
BOOMBOX: "BOOMBOX",
|
|
147494
|
+
CABIN_BED: "CABIN_BED"
|
|
147495
|
+
};
|
|
147496
|
+
CURRENCIES2 = {
|
|
147497
|
+
PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
|
|
147498
|
+
XP: ITEM_SLUGS2.PLAYCADEMY_XP
|
|
147499
|
+
};
|
|
147500
|
+
BADGES2 = {
|
|
147501
|
+
FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
|
|
147502
|
+
EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
|
|
147503
|
+
FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
|
|
147504
|
+
};
|
|
147505
|
+
});
|
|
147506
|
+
var init_timeback = () => {};
|
|
147507
|
+
var init_workers = () => {};
|
|
147508
|
+
var init_src2 = __esm3(() => {
|
|
147509
|
+
init_auth();
|
|
147510
|
+
init_domains();
|
|
147511
|
+
init_env_vars();
|
|
147512
|
+
init_overworld();
|
|
147513
|
+
init_timeback();
|
|
147514
|
+
init_workers();
|
|
147515
|
+
});
|
|
147516
|
+
var TIMEBACK_API_URLS;
|
|
147517
|
+
var TIMEBACK_AUTH_URLS;
|
|
147518
|
+
var CALIPER_API_URLS;
|
|
147519
|
+
var ONEROSTER_ENDPOINTS;
|
|
147520
|
+
var CALIPER_ENDPOINTS;
|
|
147521
|
+
var createOneRosterUrls = (baseUrl) => {
|
|
147522
|
+
const effective = baseUrl || TIMEBACK_API_URLS.production;
|
|
147523
|
+
const base = effective.replace(/\/$/, "");
|
|
147524
|
+
return {
|
|
147525
|
+
user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
|
|
147526
|
+
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
|
|
147527
|
+
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
|
|
147528
|
+
};
|
|
147529
|
+
};
|
|
147530
|
+
var CALIPER_CONSTANTS;
|
|
147531
|
+
var TIMEBACK_EVENT_TYPES;
|
|
147532
|
+
var TIMEBACK_ACTIONS;
|
|
147533
|
+
var TIMEBACK_TYPES;
|
|
147534
|
+
var ACTIVITY_METRIC_TYPES;
|
|
147535
|
+
var TIME_METRIC_TYPES;
|
|
147536
|
+
var TIMEBACK_SUBJECTS;
|
|
147537
|
+
var TIMEBACK_GRADE_LEVELS;
|
|
147538
|
+
var TIMEBACK_GRADE_LEVEL_LABELS;
|
|
147539
|
+
var CALIPER_SUBJECTS;
|
|
147540
|
+
var ONEROSTER_STATUS;
|
|
147541
|
+
var SCORE_STATUS;
|
|
147542
|
+
var ENV_VARS;
|
|
147543
|
+
var HTTP_DEFAULTS;
|
|
147544
|
+
var AUTH_DEFAULTS;
|
|
147545
|
+
var CACHE_DEFAULTS;
|
|
147546
|
+
var CONFIG_DEFAULTS;
|
|
147547
|
+
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID;
|
|
147548
|
+
var PLAYCADEMY_DEFAULTS;
|
|
147549
|
+
var RESOURCE_DEFAULTS;
|
|
147550
|
+
var HTTP_STATUS;
|
|
147551
|
+
var ERROR_NAMES;
|
|
147552
|
+
var init_constants = __esm3(() => {
|
|
147553
|
+
init_src2();
|
|
147554
|
+
TIMEBACK_API_URLS = {
|
|
147555
|
+
production: "https://api.alpha-1edtech.ai",
|
|
147556
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
147557
|
+
};
|
|
147558
|
+
TIMEBACK_AUTH_URLS = {
|
|
147559
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
147560
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
147561
|
+
};
|
|
147562
|
+
CALIPER_API_URLS = {
|
|
147563
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
147564
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
147565
|
+
};
|
|
147566
|
+
ONEROSTER_ENDPOINTS = {
|
|
147567
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
147568
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
147569
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
147570
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
147571
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
147572
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
147573
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
147574
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
147575
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
147576
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
147577
|
+
};
|
|
147578
|
+
CALIPER_ENDPOINTS = {
|
|
147579
|
+
events: "/caliper/event",
|
|
147580
|
+
validate: "/caliper/event/validate"
|
|
147581
|
+
};
|
|
147582
|
+
CALIPER_CONSTANTS = {
|
|
147583
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
147584
|
+
profile: "TimebackProfile",
|
|
147585
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
147586
|
+
};
|
|
147587
|
+
TIMEBACK_EVENT_TYPES = {
|
|
147588
|
+
activityEvent: "ActivityEvent",
|
|
147589
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
147590
|
+
};
|
|
147591
|
+
TIMEBACK_ACTIONS = {
|
|
147592
|
+
completed: "Completed",
|
|
147593
|
+
spentTime: "SpentTime"
|
|
147594
|
+
};
|
|
147595
|
+
TIMEBACK_TYPES = {
|
|
147596
|
+
user: "TimebackUser",
|
|
147597
|
+
activityContext: "TimebackActivityContext",
|
|
147598
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
147599
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
147600
|
+
};
|
|
147601
|
+
ACTIVITY_METRIC_TYPES = {
|
|
147602
|
+
totalQuestions: "totalQuestions",
|
|
147603
|
+
correctQuestions: "correctQuestions",
|
|
147604
|
+
xpEarned: "xpEarned",
|
|
147605
|
+
masteredUnits: "masteredUnits"
|
|
147606
|
+
};
|
|
147607
|
+
TIME_METRIC_TYPES = {
|
|
147608
|
+
active: "active",
|
|
147609
|
+
inactive: "inactive",
|
|
147610
|
+
waste: "waste",
|
|
147611
|
+
unknown: "unknown",
|
|
147612
|
+
antiPattern: "anti-pattern"
|
|
147613
|
+
};
|
|
147614
|
+
TIMEBACK_SUBJECTS = [
|
|
147615
|
+
"Math",
|
|
147616
|
+
"FastMath",
|
|
147617
|
+
"Science",
|
|
147618
|
+
"Social Studies",
|
|
147619
|
+
"Language",
|
|
147620
|
+
"Reading",
|
|
147621
|
+
"Vocabulary",
|
|
147622
|
+
"Writing"
|
|
147623
|
+
];
|
|
147624
|
+
TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
147625
|
+
TIMEBACK_GRADE_LEVEL_LABELS = {
|
|
147626
|
+
"-1": "pre-k",
|
|
147627
|
+
"0": "kindergarten",
|
|
147628
|
+
"1": "1st grade",
|
|
147629
|
+
"2": "2nd grade",
|
|
147630
|
+
"3": "3rd grade",
|
|
147631
|
+
"4": "4th grade",
|
|
147632
|
+
"5": "5th grade",
|
|
147633
|
+
"6": "6th grade",
|
|
147634
|
+
"7": "7th grade",
|
|
147635
|
+
"8": "8th grade",
|
|
147636
|
+
"9": "9th grade",
|
|
147637
|
+
"10": "10th grade",
|
|
147638
|
+
"11": "11th grade",
|
|
147639
|
+
"12": "12th grade",
|
|
147640
|
+
"13": "AP"
|
|
147641
|
+
};
|
|
147642
|
+
CALIPER_SUBJECTS = {
|
|
147643
|
+
Reading: "Reading",
|
|
147644
|
+
Language: "Language",
|
|
147645
|
+
Vocabulary: "Vocabulary",
|
|
147646
|
+
SocialStudies: "Social Studies",
|
|
147647
|
+
Writing: "Writing",
|
|
147648
|
+
Science: "Science",
|
|
147649
|
+
FastMath: "FastMath",
|
|
147650
|
+
Math: "Math",
|
|
147651
|
+
None: "None"
|
|
147652
|
+
};
|
|
147653
|
+
ONEROSTER_STATUS = {
|
|
147654
|
+
active: "active",
|
|
147655
|
+
toBeDeleted: "tobedeleted"
|
|
147656
|
+
};
|
|
147657
|
+
SCORE_STATUS = {
|
|
147658
|
+
exempt: "exempt",
|
|
147659
|
+
fullyGraded: "fully graded",
|
|
147660
|
+
notSubmitted: "not submitted",
|
|
147661
|
+
partiallyGraded: "partially graded",
|
|
147662
|
+
submitted: "submitted"
|
|
147663
|
+
};
|
|
147664
|
+
ENV_VARS = {
|
|
147665
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
147666
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
147667
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
147668
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
147669
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
147670
|
+
launchBaseUrl: "GAME_URL"
|
|
147671
|
+
};
|
|
147672
|
+
HTTP_DEFAULTS = {
|
|
147673
|
+
timeout: 30000,
|
|
147674
|
+
retries: 3,
|
|
147675
|
+
retryBackoffBase: 2
|
|
147676
|
+
};
|
|
147677
|
+
AUTH_DEFAULTS = {
|
|
147678
|
+
tokenCacheDuration: 50000
|
|
147679
|
+
};
|
|
147680
|
+
CACHE_DEFAULTS = {
|
|
147681
|
+
defaultTTL: 10 * 60 * 1000,
|
|
147682
|
+
defaultMaxSize: 500,
|
|
147683
|
+
defaultName: "TimebackCache",
|
|
147684
|
+
studentTTL: 10 * 60 * 1000,
|
|
147685
|
+
studentMaxSize: 500,
|
|
147686
|
+
assessmentTTL: 30 * 60 * 1000,
|
|
147687
|
+
assessmentMaxSize: 200,
|
|
147688
|
+
enrollmentTTL: 5 * 1000,
|
|
147689
|
+
enrollmentMaxSize: 100
|
|
147690
|
+
};
|
|
147691
|
+
CONFIG_DEFAULTS = {
|
|
147692
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
147693
|
+
};
|
|
147694
|
+
DEFAULT_PLAYCADEMY_ORGANIZATION_ID = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
147695
|
+
PLAYCADEMY_DEFAULTS = {
|
|
147696
|
+
organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID,
|
|
147697
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
147698
|
+
};
|
|
147699
|
+
RESOURCE_DEFAULTS = {
|
|
147700
|
+
organization: {
|
|
147701
|
+
name: "Playcademy Studios",
|
|
147702
|
+
type: "department"
|
|
147703
|
+
},
|
|
147704
|
+
course: {
|
|
147705
|
+
gradingScheme: "STANDARD",
|
|
147706
|
+
level: {
|
|
147707
|
+
elementary: "Elementary",
|
|
147708
|
+
middle: "Middle",
|
|
147709
|
+
high: "High",
|
|
147710
|
+
ap: "AP"
|
|
147711
|
+
},
|
|
147712
|
+
metadata: {
|
|
147713
|
+
goals: {
|
|
147714
|
+
dailyXp: 50,
|
|
147715
|
+
dailyLessons: 3
|
|
147716
|
+
},
|
|
147717
|
+
metrics: {
|
|
147718
|
+
totalXp: 1000,
|
|
147719
|
+
totalLessons: 50
|
|
147720
|
+
}
|
|
147721
|
+
}
|
|
147722
|
+
},
|
|
147723
|
+
component: {
|
|
147724
|
+
sortOrder: 1,
|
|
147725
|
+
prerequisiteCriteria: "ALL"
|
|
147726
|
+
},
|
|
147727
|
+
resource: {
|
|
147728
|
+
vendorId: "playcademy",
|
|
147729
|
+
roles: ["primary"],
|
|
147730
|
+
importance: "primary",
|
|
147731
|
+
metadata: {
|
|
147732
|
+
type: "interactive",
|
|
147733
|
+
toolProvider: "Playcademy",
|
|
147734
|
+
instructionalMethod: "exploratory",
|
|
147735
|
+
language: "en-US"
|
|
147736
|
+
}
|
|
147737
|
+
},
|
|
147738
|
+
componentResource: {
|
|
147739
|
+
sortOrder: 1,
|
|
147740
|
+
lessonType: "quiz"
|
|
147741
|
+
}
|
|
147742
|
+
};
|
|
147743
|
+
HTTP_STATUS = {
|
|
147744
|
+
CLIENT_ERROR_MIN: 400,
|
|
147745
|
+
CLIENT_ERROR_MAX: 500,
|
|
147746
|
+
SERVER_ERROR_MIN: 500
|
|
147747
|
+
};
|
|
147748
|
+
ERROR_NAMES = {
|
|
147749
|
+
timebackAuth: "TimebackAuthError",
|
|
147750
|
+
timebackApi: "TimebackApiError",
|
|
147751
|
+
timebackConfig: "TimebackConfigError",
|
|
147752
|
+
timebackSdk: "TimebackSDKError"
|
|
147753
|
+
};
|
|
147754
|
+
});
|
|
147463
147755
|
function deriveSourcedIds(courseId) {
|
|
147464
147756
|
return {
|
|
147465
147757
|
course: courseId,
|
|
@@ -147475,7 +147767,8 @@ __export3(exports_verify, {
|
|
|
147475
147767
|
});
|
|
147476
147768
|
async function fetchTimebackConfig(client, courseId) {
|
|
147477
147769
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
147478
|
-
const [course, component, resource, componentResource] = await Promise.all([
|
|
147770
|
+
const [org, course, component, resource, componentResource] = await Promise.all([
|
|
147771
|
+
client.oneroster.organizations.get(PLAYCADEMY_DEFAULTS.organization),
|
|
147479
147772
|
client.oneroster.courses.get(sourcedIds.course),
|
|
147480
147773
|
client.oneroster.courseComponents.get(sourcedIds.component),
|
|
147481
147774
|
client.oneroster.resources.get(sourcedIds.resource),
|
|
@@ -147483,9 +147776,9 @@ async function fetchTimebackConfig(client, courseId) {
|
|
|
147483
147776
|
]);
|
|
147484
147777
|
return {
|
|
147485
147778
|
organization: {
|
|
147486
|
-
name:
|
|
147487
|
-
type:
|
|
147488
|
-
identifier:
|
|
147779
|
+
name: org.name,
|
|
147780
|
+
type: org.type,
|
|
147781
|
+
identifier: org.identifier || PLAYCADEMY_DEFAULTS.organization
|
|
147489
147782
|
},
|
|
147490
147783
|
course: {
|
|
147491
147784
|
title: course.title || "",
|
|
@@ -147545,145 +147838,10 @@ async function verifyTimebackResources(client, courseId) {
|
|
|
147545
147838
|
}
|
|
147546
147839
|
};
|
|
147547
147840
|
}
|
|
147548
|
-
var init_verify5 = () => {
|
|
147549
|
-
|
|
147550
|
-
|
|
147551
|
-
|
|
147552
|
-
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
147553
|
-
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
147554
|
-
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
147555
|
-
COMMON_SWORD: "COMMON_SWORD",
|
|
147556
|
-
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
147557
|
-
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
147558
|
-
LAVA_LAMP: "LAVA_LAMP",
|
|
147559
|
-
BOOMBOX: "BOOMBOX",
|
|
147560
|
-
CABIN_BED: "CABIN_BED"
|
|
147561
|
-
};
|
|
147562
|
-
var CURRENCIES2 = {
|
|
147563
|
-
PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
|
|
147564
|
-
XP: ITEM_SLUGS2.PLAYCADEMY_XP
|
|
147565
|
-
};
|
|
147566
|
-
var BADGES2 = {
|
|
147567
|
-
FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
|
|
147568
|
-
EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
|
|
147569
|
-
FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
|
|
147570
|
-
};
|
|
147571
|
-
var TIMEBACK_API_URLS = {
|
|
147572
|
-
production: "https://api.alpha-1edtech.ai",
|
|
147573
|
-
staging: "https://api.staging.alpha-1edtech.com"
|
|
147574
|
-
};
|
|
147575
|
-
var TIMEBACK_AUTH_URLS = {
|
|
147576
|
-
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
147577
|
-
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
147578
|
-
};
|
|
147579
|
-
var CALIPER_API_URLS = {
|
|
147580
|
-
production: "https://caliper.alpha-1edtech.ai",
|
|
147581
|
-
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
147582
|
-
};
|
|
147583
|
-
var ONEROSTER_ENDPOINTS = {
|
|
147584
|
-
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
147585
|
-
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
147586
|
-
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
147587
|
-
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
147588
|
-
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
147589
|
-
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
147590
|
-
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
147591
|
-
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
147592
|
-
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
147593
|
-
users: "/ims/oneroster/rostering/v1p2/users"
|
|
147594
|
-
};
|
|
147595
|
-
var CALIPER_ENDPOINTS = {
|
|
147596
|
-
events: "/caliper/event",
|
|
147597
|
-
validate: "/caliper/event/validate"
|
|
147598
|
-
};
|
|
147599
|
-
var createOneRosterUrls = (baseUrl) => {
|
|
147600
|
-
const effective = baseUrl || TIMEBACK_API_URLS.production;
|
|
147601
|
-
const base = effective.replace(/\/$/, "");
|
|
147602
|
-
return {
|
|
147603
|
-
user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
|
|
147604
|
-
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
|
|
147605
|
-
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
|
|
147606
|
-
};
|
|
147607
|
-
};
|
|
147608
|
-
var CALIPER_CONSTANTS = {
|
|
147609
|
-
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
147610
|
-
profile: "TimebackProfile",
|
|
147611
|
-
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
147612
|
-
};
|
|
147613
|
-
var TIMEBACK_EVENT_TYPES = {
|
|
147614
|
-
activityEvent: "ActivityEvent",
|
|
147615
|
-
timeSpentEvent: "TimeSpentEvent"
|
|
147616
|
-
};
|
|
147617
|
-
var TIMEBACK_ACTIONS = {
|
|
147618
|
-
completed: "Completed",
|
|
147619
|
-
spentTime: "SpentTime"
|
|
147620
|
-
};
|
|
147621
|
-
var TIMEBACK_TYPES = {
|
|
147622
|
-
user: "TimebackUser",
|
|
147623
|
-
activityContext: "TimebackActivityContext",
|
|
147624
|
-
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
147625
|
-
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
147626
|
-
};
|
|
147627
|
-
var ACTIVITY_METRIC_TYPES = {
|
|
147628
|
-
totalQuestions: "totalQuestions",
|
|
147629
|
-
correctQuestions: "correctQuestions",
|
|
147630
|
-
xpEarned: "xpEarned",
|
|
147631
|
-
masteredUnits: "masteredUnits"
|
|
147632
|
-
};
|
|
147633
|
-
var TIME_METRIC_TYPES = {
|
|
147634
|
-
active: "active",
|
|
147635
|
-
inactive: "inactive",
|
|
147636
|
-
waste: "waste",
|
|
147637
|
-
unknown: "unknown",
|
|
147638
|
-
antiPattern: "anti-pattern"
|
|
147639
|
-
};
|
|
147640
|
-
var ONEROSTER_STATUS = {
|
|
147641
|
-
active: "active",
|
|
147642
|
-
toBeDeleted: "tobedeleted"
|
|
147643
|
-
};
|
|
147644
|
-
var SCORE_STATUS = {
|
|
147645
|
-
exempt: "exempt",
|
|
147646
|
-
fullyGraded: "fully graded",
|
|
147647
|
-
notSubmitted: "not submitted",
|
|
147648
|
-
partiallyGraded: "partially graded",
|
|
147649
|
-
submitted: "submitted"
|
|
147650
|
-
};
|
|
147651
|
-
var ENV_VARS = {
|
|
147652
|
-
clientId: "TIMEBACK_CLIENT_ID",
|
|
147653
|
-
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
147654
|
-
baseUrl: "TIMEBACK_BASE_URL",
|
|
147655
|
-
environment: "TIMEBACK_ENVIRONMENT",
|
|
147656
|
-
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
147657
|
-
launchBaseUrl: "GAME_URL"
|
|
147658
|
-
};
|
|
147659
|
-
var HTTP_DEFAULTS = {
|
|
147660
|
-
timeout: 30000,
|
|
147661
|
-
retries: 3,
|
|
147662
|
-
retryBackoffBase: 2
|
|
147663
|
-
};
|
|
147664
|
-
var AUTH_DEFAULTS = {
|
|
147665
|
-
tokenCacheDuration: 50000
|
|
147666
|
-
};
|
|
147667
|
-
var CACHE_DEFAULTS = {
|
|
147668
|
-
defaultTTL: 10 * 60 * 1000,
|
|
147669
|
-
defaultMaxSize: 500,
|
|
147670
|
-
defaultName: "TimebackCache",
|
|
147671
|
-
studentTTL: 10 * 60 * 1000,
|
|
147672
|
-
studentMaxSize: 500,
|
|
147673
|
-
assessmentTTL: 30 * 60 * 1000,
|
|
147674
|
-
assessmentMaxSize: 200
|
|
147675
|
-
};
|
|
147676
|
-
var HTTP_STATUS = {
|
|
147677
|
-
CLIENT_ERROR_MIN: 400,
|
|
147678
|
-
CLIENT_ERROR_MAX: 500,
|
|
147679
|
-
SERVER_ERROR_MIN: 500
|
|
147680
|
-
};
|
|
147681
|
-
var ERROR_NAMES = {
|
|
147682
|
-
timebackAuth: "TimebackAuthError",
|
|
147683
|
-
timebackApi: "TimebackApiError",
|
|
147684
|
-
timebackConfig: "TimebackConfigError",
|
|
147685
|
-
timebackSdk: "TimebackSDKError"
|
|
147686
|
-
};
|
|
147841
|
+
var init_verify5 = __esm3(() => {
|
|
147842
|
+
init_constants();
|
|
147843
|
+
});
|
|
147844
|
+
init_constants();
|
|
147687
147845
|
|
|
147688
147846
|
class TimebackError extends Error {
|
|
147689
147847
|
constructor(message2) {
|
|
@@ -147745,6 +147903,25 @@ class ResourceNotFoundError extends TimebackError {
|
|
|
147745
147903
|
Object.setPrototypeOf(this, ResourceNotFoundError.prototype);
|
|
147746
147904
|
}
|
|
147747
147905
|
}
|
|
147906
|
+
init_constants();
|
|
147907
|
+
var isObject2 = (value) => typeof value === "object" && value !== null;
|
|
147908
|
+
var SUBJECT_VALUES = TIMEBACK_SUBJECTS;
|
|
147909
|
+
var GRADE_VALUES = TIMEBACK_GRADE_LEVELS;
|
|
147910
|
+
function isPlaycademyResourceMetadata(value) {
|
|
147911
|
+
if (!isObject2(value)) {
|
|
147912
|
+
return false;
|
|
147913
|
+
}
|
|
147914
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
147915
|
+
return true;
|
|
147916
|
+
}
|
|
147917
|
+
return isObject2(value.mastery);
|
|
147918
|
+
}
|
|
147919
|
+
function isTimebackSubject(value) {
|
|
147920
|
+
return typeof value === "string" && SUBJECT_VALUES.includes(value);
|
|
147921
|
+
}
|
|
147922
|
+
function isTimebackGrade(value) {
|
|
147923
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
147924
|
+
}
|
|
147748
147925
|
var isBrowser2 = () => {
|
|
147749
147926
|
const g5 = globalThis;
|
|
147750
147927
|
return typeof g5.window !== "undefined" && typeof g5.document !== "undefined";
|
|
@@ -148193,6 +148370,8 @@ async function updateTimebackResources(client, courseId, config2) {
|
|
|
148193
148370
|
updateComponentResourceLink(client, config2, sourcedIds)
|
|
148194
148371
|
]);
|
|
148195
148372
|
}
|
|
148373
|
+
init_constants();
|
|
148374
|
+
init_constants();
|
|
148196
148375
|
if (process.env.DEBUG === "true") {
|
|
148197
148376
|
process.env.TERM = "dumb";
|
|
148198
148377
|
}
|
|
@@ -148292,6 +148471,7 @@ async function getTimebackTokenResponse(config2) {
|
|
|
148292
148471
|
function getAuthUrl(environment = "production") {
|
|
148293
148472
|
return TIMEBACK_AUTH_URLS[environment];
|
|
148294
148473
|
}
|
|
148474
|
+
init_constants();
|
|
148295
148475
|
async function request({
|
|
148296
148476
|
path: path2,
|
|
148297
148477
|
baseUrl,
|
|
@@ -148397,6 +148577,154 @@ async function requestCaliper(options) {
|
|
|
148397
148577
|
baseUrl: caliperBase
|
|
148398
148578
|
});
|
|
148399
148579
|
}
|
|
148580
|
+
init_constants();
|
|
148581
|
+
function createCaliperNamespace(client) {
|
|
148582
|
+
const urls = createOneRosterUrls(client.getBaseUrl());
|
|
148583
|
+
const caliper = {
|
|
148584
|
+
emit: async (event, sensorUrl) => {
|
|
148585
|
+
const envelope = {
|
|
148586
|
+
sensor: sensorUrl,
|
|
148587
|
+
sendTime: new Date().toISOString(),
|
|
148588
|
+
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
148589
|
+
data: [event]
|
|
148590
|
+
};
|
|
148591
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
148592
|
+
},
|
|
148593
|
+
emitBatch: async (events, sensorUrl) => {
|
|
148594
|
+
if (events.length === 0)
|
|
148595
|
+
return;
|
|
148596
|
+
const envelope = {
|
|
148597
|
+
sensor: sensorUrl,
|
|
148598
|
+
sendTime: new Date().toISOString(),
|
|
148599
|
+
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
148600
|
+
data: events
|
|
148601
|
+
};
|
|
148602
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
148603
|
+
},
|
|
148604
|
+
emitActivityEvent: async (data) => {
|
|
148605
|
+
const event = {
|
|
148606
|
+
"@context": CALIPER_CONSTANTS.context,
|
|
148607
|
+
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
148608
|
+
type: TIMEBACK_EVENT_TYPES.activityEvent,
|
|
148609
|
+
eventTime: new Date().toISOString(),
|
|
148610
|
+
profile: CALIPER_CONSTANTS.profile,
|
|
148611
|
+
actor: {
|
|
148612
|
+
id: urls.user(data.studentId),
|
|
148613
|
+
type: TIMEBACK_TYPES.user,
|
|
148614
|
+
email: data.studentEmail
|
|
148615
|
+
},
|
|
148616
|
+
action: TIMEBACK_ACTIONS.completed,
|
|
148617
|
+
object: {
|
|
148618
|
+
id: caliper.buildActivityUrl(data),
|
|
148619
|
+
type: TIMEBACK_TYPES.activityContext,
|
|
148620
|
+
subject: data.subject,
|
|
148621
|
+
app: {
|
|
148622
|
+
name: data.appName
|
|
148623
|
+
},
|
|
148624
|
+
activity: {
|
|
148625
|
+
name: data.activityName
|
|
148626
|
+
},
|
|
148627
|
+
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
148628
|
+
process: false
|
|
148629
|
+
},
|
|
148630
|
+
generated: {
|
|
148631
|
+
id: `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
148632
|
+
type: TIMEBACK_TYPES.activityMetricsCollection,
|
|
148633
|
+
attempt: data.attemptNumber || 1,
|
|
148634
|
+
items: [
|
|
148635
|
+
...data.totalQuestions !== undefined ? [
|
|
148636
|
+
{
|
|
148637
|
+
type: ACTIVITY_METRIC_TYPES.totalQuestions,
|
|
148638
|
+
value: data.totalQuestions
|
|
148639
|
+
}
|
|
148640
|
+
] : [],
|
|
148641
|
+
...data.correctQuestions !== undefined ? [
|
|
148642
|
+
{
|
|
148643
|
+
type: ACTIVITY_METRIC_TYPES.correctQuestions,
|
|
148644
|
+
value: data.correctQuestions
|
|
148645
|
+
}
|
|
148646
|
+
] : [],
|
|
148647
|
+
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES.xpEarned, value: data.xpEarned }] : [],
|
|
148648
|
+
...data.masteredUnits !== undefined ? [
|
|
148649
|
+
{
|
|
148650
|
+
type: ACTIVITY_METRIC_TYPES.masteredUnits,
|
|
148651
|
+
value: data.masteredUnits
|
|
148652
|
+
}
|
|
148653
|
+
] : []
|
|
148654
|
+
],
|
|
148655
|
+
...data.extensions ? { extensions: data.extensions } : {}
|
|
148656
|
+
}
|
|
148657
|
+
};
|
|
148658
|
+
return caliper.emit(event, data.sensorUrl);
|
|
148659
|
+
},
|
|
148660
|
+
emitTimeSpentEvent: async (data) => {
|
|
148661
|
+
const event = {
|
|
148662
|
+
"@context": CALIPER_CONSTANTS.context,
|
|
148663
|
+
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
148664
|
+
type: TIMEBACK_EVENT_TYPES.timeSpentEvent,
|
|
148665
|
+
eventTime: new Date().toISOString(),
|
|
148666
|
+
profile: CALIPER_CONSTANTS.profile,
|
|
148667
|
+
actor: {
|
|
148668
|
+
id: urls.user(data.studentId),
|
|
148669
|
+
type: TIMEBACK_TYPES.user,
|
|
148670
|
+
email: data.studentEmail
|
|
148671
|
+
},
|
|
148672
|
+
action: TIMEBACK_ACTIONS.spentTime,
|
|
148673
|
+
object: {
|
|
148674
|
+
id: caliper.buildActivityUrl(data),
|
|
148675
|
+
type: TIMEBACK_TYPES.activityContext,
|
|
148676
|
+
subject: data.subject,
|
|
148677
|
+
app: {
|
|
148678
|
+
name: data.appName
|
|
148679
|
+
},
|
|
148680
|
+
activity: {
|
|
148681
|
+
name: data.activityName
|
|
148682
|
+
},
|
|
148683
|
+
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
148684
|
+
process: false
|
|
148685
|
+
},
|
|
148686
|
+
generated: {
|
|
148687
|
+
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
148688
|
+
type: TIMEBACK_TYPES.timeSpentMetricsCollection,
|
|
148689
|
+
items: [
|
|
148690
|
+
{ type: TIME_METRIC_TYPES.active, value: data.activeTimeSeconds },
|
|
148691
|
+
...data.inactiveTimeSeconds !== undefined ? [
|
|
148692
|
+
{
|
|
148693
|
+
type: TIME_METRIC_TYPES.inactive,
|
|
148694
|
+
value: data.inactiveTimeSeconds
|
|
148695
|
+
}
|
|
148696
|
+
] : [],
|
|
148697
|
+
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES.waste, value: data.wasteTimeSeconds }] : []
|
|
148698
|
+
]
|
|
148699
|
+
}
|
|
148700
|
+
};
|
|
148701
|
+
return caliper.emit(event, data.sensorUrl);
|
|
148702
|
+
},
|
|
148703
|
+
buildActivityUrl: (data) => {
|
|
148704
|
+
const base = data.sensorUrl.replace(/\/$/, "");
|
|
148705
|
+
return `${base}/activities/${data.courseId}/${data.activityId}/${crypto.randomUUID()}`;
|
|
148706
|
+
}
|
|
148707
|
+
};
|
|
148708
|
+
return caliper;
|
|
148709
|
+
}
|
|
148710
|
+
function createEduBridgeNamespace(client) {
|
|
148711
|
+
const enrollments = {
|
|
148712
|
+
listByUser: async (userId) => {
|
|
148713
|
+
const response = await client["request"](`/edubridge/enrollments/user/${userId}`, "GET");
|
|
148714
|
+
return response.data;
|
|
148715
|
+
}
|
|
148716
|
+
};
|
|
148717
|
+
const analytics = {
|
|
148718
|
+
getEnrollmentFacts: async (enrollmentId) => {
|
|
148719
|
+
return client["request"](`/edubridge/analytics/enrollment/${enrollmentId}`, "GET");
|
|
148720
|
+
}
|
|
148721
|
+
};
|
|
148722
|
+
return {
|
|
148723
|
+
enrollments,
|
|
148724
|
+
analytics
|
|
148725
|
+
};
|
|
148726
|
+
}
|
|
148727
|
+
init_constants();
|
|
148400
148728
|
function logTimebackError(operation, error2, context) {
|
|
148401
148729
|
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
148402
148730
|
if (error2 instanceof TimebackApiError) {
|
|
@@ -148432,6 +148760,17 @@ function createOneRosterNamespace(client) {
|
|
|
148432
148760
|
listByCourse: async (courseSourcedId) => {
|
|
148433
148761
|
const res = await client["request"](`${ONEROSTER_ENDPOINTS.courses}/${courseSourcedId}/classes`, "GET");
|
|
148434
148762
|
return res.classes;
|
|
148763
|
+
},
|
|
148764
|
+
listByStudent: async (userSourcedId, options) => {
|
|
148765
|
+
const queryParams = new URLSearchParams;
|
|
148766
|
+
if (options?.limit)
|
|
148767
|
+
queryParams.set("limit", String(options.limit));
|
|
148768
|
+
if (options?.offset)
|
|
148769
|
+
queryParams.set("offset", String(options.offset));
|
|
148770
|
+
const endpoint = `${ONEROSTER_ENDPOINTS.users}/${userSourcedId}/classes`;
|
|
148771
|
+
const url = queryParams.toString() ? `${endpoint}?${queryParams}` : endpoint;
|
|
148772
|
+
const res = await client["request"](url, "GET");
|
|
148773
|
+
return res.classes || [];
|
|
148435
148774
|
}
|
|
148436
148775
|
},
|
|
148437
148776
|
organizations: {
|
|
@@ -148439,7 +148778,7 @@ function createOneRosterNamespace(client) {
|
|
|
148439
148778
|
return client["request"](ONEROSTER_ENDPOINTS.organizations, "POST", data);
|
|
148440
148779
|
},
|
|
148441
148780
|
get: async (sourcedId) => {
|
|
148442
|
-
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET");
|
|
148781
|
+
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET").then((res) => res.org);
|
|
148443
148782
|
},
|
|
148444
148783
|
update: async (sourcedId, data) => {
|
|
148445
148784
|
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "PUT", data);
|
|
@@ -148547,14 +148886,30 @@ function createOneRosterNamespace(client) {
|
|
|
148547
148886
|
update: async (sourcedId, data) => {
|
|
148548
148887
|
return client["request"](`${ONEROSTER_ENDPOINTS.assessmentResults}/${sourcedId}`, "PUT", data);
|
|
148549
148888
|
},
|
|
148550
|
-
|
|
148889
|
+
getAttemptStats: async (studentId, lineItemId) => {
|
|
148551
148890
|
try {
|
|
148552
148891
|
const filter2 = `student.sourcedId='${studentId}' AND assessmentLineItem.sourcedId='${lineItemId}'`;
|
|
148553
|
-
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}
|
|
148892
|
+
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}`;
|
|
148554
148893
|
const response = await client["request"](url, "GET");
|
|
148555
|
-
|
|
148894
|
+
const results = response.assessmentResults || [];
|
|
148895
|
+
if (results.length === 0)
|
|
148896
|
+
return null;
|
|
148897
|
+
let maxAttemptResult = results[0];
|
|
148898
|
+
let maxAttemptNumber = maxAttemptResult.metadata?.attemptNumber || 0;
|
|
148899
|
+
let activeAttemptCount = 0;
|
|
148900
|
+
for (const result of results) {
|
|
148901
|
+
const attemptNumber = result.metadata?.attemptNumber || 0;
|
|
148902
|
+
if (attemptNumber > maxAttemptNumber) {
|
|
148903
|
+
maxAttemptNumber = attemptNumber;
|
|
148904
|
+
maxAttemptResult = result;
|
|
148905
|
+
}
|
|
148906
|
+
if (result.status === "active") {
|
|
148907
|
+
activeAttemptCount++;
|
|
148908
|
+
}
|
|
148909
|
+
}
|
|
148910
|
+
return { maxAttemptNumber, activeAttemptCount, maxAttemptResult };
|
|
148556
148911
|
} catch (error2) {
|
|
148557
|
-
logTimebackError("query
|
|
148912
|
+
logTimebackError("query attempt stats", error2, {
|
|
148558
148913
|
studentId,
|
|
148559
148914
|
lineItemId
|
|
148560
148915
|
});
|
|
@@ -148567,7 +148922,7 @@ function createOneRosterNamespace(client) {
|
|
|
148567
148922
|
return client["request"](ONEROSTER_ENDPOINTS.users, "POST", { user: data });
|
|
148568
148923
|
},
|
|
148569
148924
|
get: async (sourcedId) => {
|
|
148570
|
-
return client["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET");
|
|
148925
|
+
return client["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET").then((res) => res.user);
|
|
148571
148926
|
},
|
|
148572
148927
|
findByEmail: async (email) => {
|
|
148573
148928
|
const params = new URLSearchParams({ filter: `email='${email}'` });
|
|
@@ -148583,130 +148938,8 @@ function createOneRosterNamespace(client) {
|
|
|
148583
148938
|
}
|
|
148584
148939
|
};
|
|
148585
148940
|
}
|
|
148586
|
-
|
|
148587
|
-
|
|
148588
|
-
const caliper = {
|
|
148589
|
-
emit: async (event, sensorUrl) => {
|
|
148590
|
-
const envelope = {
|
|
148591
|
-
sensor: sensorUrl,
|
|
148592
|
-
sendTime: new Date().toISOString(),
|
|
148593
|
-
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
148594
|
-
data: [event]
|
|
148595
|
-
};
|
|
148596
|
-
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
148597
|
-
},
|
|
148598
|
-
emitBatch: async (events, sensorUrl) => {
|
|
148599
|
-
if (events.length === 0)
|
|
148600
|
-
return;
|
|
148601
|
-
const envelope = {
|
|
148602
|
-
sensor: sensorUrl,
|
|
148603
|
-
sendTime: new Date().toISOString(),
|
|
148604
|
-
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
148605
|
-
data: events
|
|
148606
|
-
};
|
|
148607
|
-
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
148608
|
-
},
|
|
148609
|
-
emitActivityEvent: async (data) => {
|
|
148610
|
-
const event = {
|
|
148611
|
-
"@context": CALIPER_CONSTANTS.context,
|
|
148612
|
-
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
148613
|
-
type: TIMEBACK_EVENT_TYPES.activityEvent,
|
|
148614
|
-
eventTime: new Date().toISOString(),
|
|
148615
|
-
profile: CALIPER_CONSTANTS.profile,
|
|
148616
|
-
actor: {
|
|
148617
|
-
id: urls.user(data.studentId),
|
|
148618
|
-
type: TIMEBACK_TYPES.user,
|
|
148619
|
-
email: data.studentEmail
|
|
148620
|
-
},
|
|
148621
|
-
action: TIMEBACK_ACTIONS.completed,
|
|
148622
|
-
object: {
|
|
148623
|
-
id: urls.componentResource(data.activityId),
|
|
148624
|
-
type: TIMEBACK_TYPES.activityContext,
|
|
148625
|
-
subject: data.subject,
|
|
148626
|
-
app: {
|
|
148627
|
-
name: data.appName
|
|
148628
|
-
},
|
|
148629
|
-
activity: {
|
|
148630
|
-
name: data.activityName
|
|
148631
|
-
},
|
|
148632
|
-
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
148633
|
-
process: true
|
|
148634
|
-
},
|
|
148635
|
-
generated: {
|
|
148636
|
-
id: `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
148637
|
-
type: TIMEBACK_TYPES.activityMetricsCollection,
|
|
148638
|
-
attempt: data.attemptNumber || 1,
|
|
148639
|
-
items: [
|
|
148640
|
-
...data.totalQuestions !== undefined ? [
|
|
148641
|
-
{
|
|
148642
|
-
type: ACTIVITY_METRIC_TYPES.totalQuestions,
|
|
148643
|
-
value: data.totalQuestions
|
|
148644
|
-
}
|
|
148645
|
-
] : [],
|
|
148646
|
-
...data.correctQuestions !== undefined ? [
|
|
148647
|
-
{
|
|
148648
|
-
type: ACTIVITY_METRIC_TYPES.correctQuestions,
|
|
148649
|
-
value: data.correctQuestions
|
|
148650
|
-
}
|
|
148651
|
-
] : [],
|
|
148652
|
-
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES.xpEarned, value: data.xpEarned }] : [],
|
|
148653
|
-
...data.masteredUnits !== undefined ? [
|
|
148654
|
-
{
|
|
148655
|
-
type: ACTIVITY_METRIC_TYPES.masteredUnits,
|
|
148656
|
-
value: data.masteredUnits
|
|
148657
|
-
}
|
|
148658
|
-
] : []
|
|
148659
|
-
]
|
|
148660
|
-
}
|
|
148661
|
-
};
|
|
148662
|
-
return caliper.emit(event, data.sensorUrl);
|
|
148663
|
-
},
|
|
148664
|
-
emitTimeSpentEvent: async (data) => {
|
|
148665
|
-
const event = {
|
|
148666
|
-
"@context": CALIPER_CONSTANTS.context,
|
|
148667
|
-
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
148668
|
-
type: TIMEBACK_EVENT_TYPES.timeSpentEvent,
|
|
148669
|
-
eventTime: new Date().toISOString(),
|
|
148670
|
-
profile: CALIPER_CONSTANTS.profile,
|
|
148671
|
-
actor: {
|
|
148672
|
-
id: urls.user(data.studentId),
|
|
148673
|
-
type: TIMEBACK_TYPES.user,
|
|
148674
|
-
email: data.studentEmail
|
|
148675
|
-
},
|
|
148676
|
-
action: TIMEBACK_ACTIONS.spentTime,
|
|
148677
|
-
object: {
|
|
148678
|
-
id: urls.componentResource(data.activityId),
|
|
148679
|
-
type: TIMEBACK_TYPES.activityContext,
|
|
148680
|
-
subject: data.subject,
|
|
148681
|
-
app: {
|
|
148682
|
-
name: data.appName
|
|
148683
|
-
},
|
|
148684
|
-
activity: {
|
|
148685
|
-
name: data.activityName
|
|
148686
|
-
},
|
|
148687
|
-
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
148688
|
-
process: false
|
|
148689
|
-
},
|
|
148690
|
-
generated: {
|
|
148691
|
-
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
148692
|
-
type: TIMEBACK_TYPES.timeSpentMetricsCollection,
|
|
148693
|
-
items: [
|
|
148694
|
-
{ type: TIME_METRIC_TYPES.active, value: data.activeTimeSeconds },
|
|
148695
|
-
...data.inactiveTimeSeconds !== undefined ? [
|
|
148696
|
-
{
|
|
148697
|
-
type: TIME_METRIC_TYPES.inactive,
|
|
148698
|
-
value: data.inactiveTimeSeconds
|
|
148699
|
-
}
|
|
148700
|
-
] : [],
|
|
148701
|
-
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES.waste, value: data.wasteTimeSeconds }] : []
|
|
148702
|
-
]
|
|
148703
|
-
}
|
|
148704
|
-
};
|
|
148705
|
-
return caliper.emit(event, data.sensorUrl);
|
|
148706
|
-
}
|
|
148707
|
-
};
|
|
148708
|
-
return caliper;
|
|
148709
|
-
}
|
|
148941
|
+
init_constants();
|
|
148942
|
+
init_constants();
|
|
148710
148943
|
|
|
148711
148944
|
class TimebackCache {
|
|
148712
148945
|
cache = new Map;
|
|
@@ -148812,6 +149045,8 @@ class TimebackCache {
|
|
|
148812
149045
|
class TimebackCacheManager {
|
|
148813
149046
|
studentCache;
|
|
148814
149047
|
assessmentLineItemCache;
|
|
149048
|
+
resourceMasteryCache;
|
|
149049
|
+
enrollmentCache;
|
|
148815
149050
|
constructor() {
|
|
148816
149051
|
this.studentCache = new TimebackCache({
|
|
148817
149052
|
defaultTTL: CACHE_DEFAULTS.studentTTL,
|
|
@@ -148823,6 +149058,16 @@ class TimebackCacheManager {
|
|
|
148823
149058
|
maxSize: CACHE_DEFAULTS.assessmentMaxSize,
|
|
148824
149059
|
name: "AssessmentLineItemCache"
|
|
148825
149060
|
});
|
|
149061
|
+
this.resourceMasteryCache = new TimebackCache({
|
|
149062
|
+
defaultTTL: CACHE_DEFAULTS.assessmentTTL,
|
|
149063
|
+
maxSize: CACHE_DEFAULTS.assessmentMaxSize,
|
|
149064
|
+
name: "ResourceMasteryCache"
|
|
149065
|
+
});
|
|
149066
|
+
this.enrollmentCache = new TimebackCache({
|
|
149067
|
+
defaultTTL: CACHE_DEFAULTS.enrollmentTTL,
|
|
149068
|
+
maxSize: CACHE_DEFAULTS.enrollmentMaxSize,
|
|
149069
|
+
name: "EnrollmentCache"
|
|
149070
|
+
});
|
|
148826
149071
|
}
|
|
148827
149072
|
getStudent(key) {
|
|
148828
149073
|
return this.studentCache.get(key);
|
|
@@ -148836,26 +149081,188 @@ class TimebackCacheManager {
|
|
|
148836
149081
|
setAssessmentLineItem(key, lineItemId) {
|
|
148837
149082
|
this.assessmentLineItemCache.set(key, lineItemId);
|
|
148838
149083
|
}
|
|
149084
|
+
getResourceMasterableUnits(key) {
|
|
149085
|
+
return this.resourceMasteryCache.get(key);
|
|
149086
|
+
}
|
|
149087
|
+
setResourceMasterableUnits(key, value) {
|
|
149088
|
+
this.resourceMasteryCache.set(key, value);
|
|
149089
|
+
}
|
|
149090
|
+
getEnrollments(studentId) {
|
|
149091
|
+
return this.enrollmentCache.get(studentId);
|
|
149092
|
+
}
|
|
149093
|
+
setEnrollments(studentId, enrollments) {
|
|
149094
|
+
this.enrollmentCache.set(studentId, enrollments);
|
|
149095
|
+
}
|
|
148839
149096
|
clearAll() {
|
|
148840
149097
|
this.studentCache.clear();
|
|
148841
149098
|
this.assessmentLineItemCache.clear();
|
|
149099
|
+
this.resourceMasteryCache.clear();
|
|
149100
|
+
this.enrollmentCache.clear();
|
|
148842
149101
|
log3.info("[TimebackCacheManager] All caches cleared");
|
|
148843
149102
|
}
|
|
148844
149103
|
getStats() {
|
|
148845
149104
|
return {
|
|
148846
149105
|
studentCache: this.studentCache.stats(),
|
|
148847
|
-
assessmentLineItemCache: this.assessmentLineItemCache.stats()
|
|
149106
|
+
assessmentLineItemCache: this.assessmentLineItemCache.stats(),
|
|
149107
|
+
resourceMasteryCache: this.resourceMasteryCache.stats(),
|
|
149108
|
+
enrollmentCache: this.enrollmentCache.stats()
|
|
148848
149109
|
};
|
|
148849
149110
|
}
|
|
148850
149111
|
cleanup() {
|
|
148851
149112
|
this.studentCache.cleanup();
|
|
148852
149113
|
this.assessmentLineItemCache.cleanup();
|
|
149114
|
+
this.resourceMasteryCache.cleanup();
|
|
149115
|
+
this.enrollmentCache.cleanup();
|
|
148853
149116
|
log3.debug("[TimebackCacheManager] Cache cleanup completed");
|
|
148854
149117
|
}
|
|
148855
149118
|
}
|
|
149119
|
+
init_constants();
|
|
149120
|
+
|
|
149121
|
+
class MasteryTracker {
|
|
149122
|
+
cacheManager;
|
|
149123
|
+
onerosterNamespace;
|
|
149124
|
+
edubridgeNamespace;
|
|
149125
|
+
constructor(cacheManager, onerosterNamespace, edubridgeNamespace) {
|
|
149126
|
+
this.cacheManager = cacheManager;
|
|
149127
|
+
this.onerosterNamespace = onerosterNamespace;
|
|
149128
|
+
this.edubridgeNamespace = edubridgeNamespace;
|
|
149129
|
+
}
|
|
149130
|
+
async checkProgress(input) {
|
|
149131
|
+
const { studentId, courseId, resourceId, masteredUnits } = input;
|
|
149132
|
+
if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
|
|
149133
|
+
return;
|
|
149134
|
+
}
|
|
149135
|
+
const masterableUnits = await this.resolveMasterableUnits(resourceId);
|
|
149136
|
+
if (!masterableUnits || masterableUnits <= 0) {
|
|
149137
|
+
log3.warn("[MasteryTracker] No masterableUnits configured for course", {
|
|
149138
|
+
courseId,
|
|
149139
|
+
resourceId
|
|
149140
|
+
});
|
|
149141
|
+
return;
|
|
149142
|
+
}
|
|
149143
|
+
const facts = await this.fetchEnrollmentAnalyticsFacts(studentId, courseId);
|
|
149144
|
+
if (!facts) {
|
|
149145
|
+
log3.warn("[MasteryTracker] Unable to retrieve analytics for mastery-based completion", {
|
|
149146
|
+
studentId,
|
|
149147
|
+
courseId
|
|
149148
|
+
});
|
|
149149
|
+
return;
|
|
149150
|
+
}
|
|
149151
|
+
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
149152
|
+
const totalMastered = historicalMasteredUnits + masteredUnits;
|
|
149153
|
+
const rawPct = totalMastered / masterableUnits * 100;
|
|
149154
|
+
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
149155
|
+
const masteryAchieved = totalMastered >= masterableUnits;
|
|
149156
|
+
return { pctCompleteApp, masteryAchieved };
|
|
149157
|
+
}
|
|
149158
|
+
async createCompletionEntry(studentId, courseId, classId, appName) {
|
|
149159
|
+
const ids = deriveSourcedIds(courseId);
|
|
149160
|
+
const lineItemId = `${ids.course}-mastery-completion-assessment`;
|
|
149161
|
+
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
149162
|
+
try {
|
|
149163
|
+
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
149164
|
+
sourcedId: lineItemId,
|
|
149165
|
+
title: "Mastery Completion",
|
|
149166
|
+
status: ONEROSTER_STATUS.active,
|
|
149167
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
149168
|
+
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
149169
|
+
});
|
|
149170
|
+
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
149171
|
+
sourcedId: resultId,
|
|
149172
|
+
status: ONEROSTER_STATUS.active,
|
|
149173
|
+
assessmentLineItem: { sourcedId: lineItemId },
|
|
149174
|
+
student: { sourcedId: studentId },
|
|
149175
|
+
score: 100,
|
|
149176
|
+
scoreDate: new Date().toISOString(),
|
|
149177
|
+
scoreStatus: SCORE_STATUS.fullyGraded,
|
|
149178
|
+
inProgress: "false",
|
|
149179
|
+
metadata: {
|
|
149180
|
+
isMasteryCompletion: true,
|
|
149181
|
+
completedAt: new Date().toISOString(),
|
|
149182
|
+
appName
|
|
149183
|
+
}
|
|
149184
|
+
});
|
|
149185
|
+
log3.info("[MasteryTracker] Created mastery completion entry", {
|
|
149186
|
+
studentId,
|
|
149187
|
+
lineItemId,
|
|
149188
|
+
resultId
|
|
149189
|
+
});
|
|
149190
|
+
} catch (error2) {
|
|
149191
|
+
log3.error("[MasteryTracker] Failed to create mastery completion entry", {
|
|
149192
|
+
studentId,
|
|
149193
|
+
lineItemId,
|
|
149194
|
+
error: error2
|
|
149195
|
+
});
|
|
149196
|
+
}
|
|
149197
|
+
}
|
|
149198
|
+
async resolveMasterableUnits(resourceId) {
|
|
149199
|
+
if (!resourceId) {
|
|
149200
|
+
return;
|
|
149201
|
+
}
|
|
149202
|
+
const cached = this.cacheManager.getResourceMasterableUnits(resourceId);
|
|
149203
|
+
if (cached !== undefined) {
|
|
149204
|
+
return cached === null ? undefined : cached;
|
|
149205
|
+
}
|
|
149206
|
+
try {
|
|
149207
|
+
const resource = await this.onerosterNamespace.resources.get(resourceId);
|
|
149208
|
+
const playcademyMetadata = resource.metadata?.playcademy;
|
|
149209
|
+
if (!playcademyMetadata) {
|
|
149210
|
+
return;
|
|
149211
|
+
}
|
|
149212
|
+
const masterableUnits = isPlaycademyResourceMetadata(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
149213
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
149214
|
+
return masterableUnits;
|
|
149215
|
+
} catch (error2) {
|
|
149216
|
+
log3.error("[MasteryTracker] Failed to fetch resource metadata for mastery config", {
|
|
149217
|
+
resourceId,
|
|
149218
|
+
error: error2
|
|
149219
|
+
});
|
|
149220
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, null);
|
|
149221
|
+
return;
|
|
149222
|
+
}
|
|
149223
|
+
}
|
|
149224
|
+
async fetchEnrollmentAnalyticsFacts(studentId, courseId) {
|
|
149225
|
+
try {
|
|
149226
|
+
const enrollments = await this.edubridgeNamespace.enrollments.listByUser(studentId);
|
|
149227
|
+
const enrollment = enrollments.find((e) => e.course.id === courseId);
|
|
149228
|
+
if (!enrollment) {
|
|
149229
|
+
log3.warn("[MasteryTracker] Enrollment not found for student/course", {
|
|
149230
|
+
studentId,
|
|
149231
|
+
courseId
|
|
149232
|
+
});
|
|
149233
|
+
return;
|
|
149234
|
+
}
|
|
149235
|
+
const analytics = await this.edubridgeNamespace.analytics.getEnrollmentFacts(enrollment.id);
|
|
149236
|
+
return analytics.facts;
|
|
149237
|
+
} catch (error2) {
|
|
149238
|
+
log3.error("[MasteryTracker] Failed to load enrollment analytics facts", {
|
|
149239
|
+
studentId,
|
|
149240
|
+
courseId,
|
|
149241
|
+
error: error2
|
|
149242
|
+
});
|
|
149243
|
+
return;
|
|
149244
|
+
}
|
|
149245
|
+
}
|
|
149246
|
+
sumAnalyticsMetric(facts, metric) {
|
|
149247
|
+
if (!facts) {
|
|
149248
|
+
return 0;
|
|
149249
|
+
}
|
|
149250
|
+
let total = 0;
|
|
149251
|
+
Object.values(facts).forEach((dateFacts) => {
|
|
149252
|
+
Object.values(dateFacts).forEach((subjectFacts) => {
|
|
149253
|
+
const metrics = subjectFacts.activityMetrics;
|
|
149254
|
+
if (metrics && typeof metrics[metric] === "number") {
|
|
149255
|
+
total += metrics[metric];
|
|
149256
|
+
}
|
|
149257
|
+
});
|
|
149258
|
+
});
|
|
149259
|
+
return total;
|
|
149260
|
+
}
|
|
149261
|
+
}
|
|
148856
149262
|
function kebabToTitleCase(kebabStr) {
|
|
148857
149263
|
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
148858
149264
|
}
|
|
149265
|
+
init_constants();
|
|
148859
149266
|
function validateProgressData(progressData) {
|
|
148860
149267
|
if (!progressData.subject) {
|
|
148861
149268
|
throw new ConfigurationError("subject", "Subject is required for Caliper events. Provide it in progressData.subject");
|
|
@@ -148909,36 +149316,47 @@ class ProgressRecorder {
|
|
|
148909
149316
|
cacheManager;
|
|
148910
149317
|
onerosterNamespace;
|
|
148911
149318
|
caliperNamespace;
|
|
148912
|
-
|
|
149319
|
+
masteryTracker;
|
|
149320
|
+
constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace, masteryTracker) {
|
|
148913
149321
|
this.studentResolver = studentResolver;
|
|
148914
149322
|
this.cacheManager = cacheManager;
|
|
148915
149323
|
this.onerosterNamespace = onerosterNamespace;
|
|
148916
149324
|
this.caliperNamespace = caliperNamespace;
|
|
149325
|
+
this.masteryTracker = masteryTracker;
|
|
148917
149326
|
}
|
|
148918
149327
|
async record(courseId, studentIdentifier, progressData) {
|
|
148919
149328
|
validateProgressData(progressData);
|
|
148920
|
-
const ids =
|
|
148921
|
-
const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
|
|
148922
|
-
const activityName = progressData.activityName || kebabToTitleCase(activityId);
|
|
148923
|
-
const classId = progressData.classId;
|
|
148924
|
-
const courseName = progressData.courseName || "Game Course";
|
|
148925
|
-
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
149329
|
+
const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
|
|
148926
149330
|
const { id: studentId, email: studentEmail } = student;
|
|
148927
149331
|
const { score, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber } = progressData;
|
|
148928
|
-
const
|
|
148929
|
-
|
|
148930
|
-
|
|
148931
|
-
|
|
148932
|
-
|
|
148933
|
-
|
|
148934
|
-
|
|
148935
|
-
|
|
148936
|
-
|
|
148937
|
-
|
|
149332
|
+
const actualLineItemId = await this.resolveAssessmentLineItem(activityId, activityName, progressData.classId, ids);
|
|
149333
|
+
const currentAttemptNumber = await this.resolveAttemptNumber(attemptNumber, score, studentId, actualLineItemId);
|
|
149334
|
+
const isFirstActiveAttempt = currentAttemptNumber === 1;
|
|
149335
|
+
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstActiveAttempt);
|
|
149336
|
+
let extensions = progressData.extensions;
|
|
149337
|
+
const masteryProgress = await this.masteryTracker.checkProgress({
|
|
149338
|
+
studentId,
|
|
149339
|
+
courseId,
|
|
149340
|
+
resourceId: ids.resource,
|
|
149341
|
+
masteredUnits: progressData.masteredUnits ?? 0
|
|
149342
|
+
});
|
|
149343
|
+
let pctCompleteApp;
|
|
149344
|
+
let masteryAchieved = false;
|
|
149345
|
+
let scoreStatus = SCORE_STATUS.fullyGraded;
|
|
149346
|
+
const inProgress = "false";
|
|
149347
|
+
if (masteryProgress) {
|
|
149348
|
+
masteryAchieved = masteryProgress.masteryAchieved;
|
|
149349
|
+
pctCompleteApp = masteryProgress.pctCompleteApp;
|
|
149350
|
+
extensions = {
|
|
149351
|
+
...extensions || {},
|
|
149352
|
+
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
149353
|
+
};
|
|
149354
|
+
if (masteryAchieved) {
|
|
149355
|
+
scoreStatus = SCORE_STATUS.fullyGraded;
|
|
149356
|
+
}
|
|
148938
149357
|
}
|
|
148939
|
-
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt);
|
|
148940
149358
|
if (score !== undefined) {
|
|
148941
|
-
await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp);
|
|
149359
|
+
await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp, masteredUnits, scoreStatus, inProgress, progressData.appName);
|
|
148942
149360
|
} else {
|
|
148943
149361
|
log3.warn("[ProgressRecorder] Score not provided, skipping gradebook entry", {
|
|
148944
149362
|
studentId,
|
|
@@ -148946,20 +149364,70 @@ class ProgressRecorder {
|
|
|
148946
149364
|
attemptNumber: currentAttemptNumber
|
|
148947
149365
|
});
|
|
148948
149366
|
}
|
|
148949
|
-
|
|
149367
|
+
if (masteryAchieved) {
|
|
149368
|
+
await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
149369
|
+
}
|
|
149370
|
+
await this.emitCaliperEvent(studentId, studentEmail, activityId, activityName, ids.course, courseName, totalQuestions, correctQuestions, calculatedXp, masteredUnits, currentAttemptNumber, progressData, extensions);
|
|
148950
149371
|
return {
|
|
148951
149372
|
xpAwarded: calculatedXp,
|
|
148952
|
-
attemptNumber: currentAttemptNumber
|
|
149373
|
+
attemptNumber: currentAttemptNumber,
|
|
149374
|
+
masteredUnitsApplied: progressData.masteredUnits ?? 0,
|
|
149375
|
+
pctCompleteApp,
|
|
149376
|
+
scoreStatus,
|
|
149377
|
+
inProgress
|
|
148953
149378
|
};
|
|
148954
149379
|
}
|
|
149380
|
+
async resolveContext(courseId, studentIdentifier, progressData) {
|
|
149381
|
+
const ids = deriveSourcedIds(courseId);
|
|
149382
|
+
const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
|
|
149383
|
+
const activityName = progressData.activityName || kebabToTitleCase(activityId);
|
|
149384
|
+
const courseName = progressData.courseName || "Game Course";
|
|
149385
|
+
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
149386
|
+
return { ids, activityId, activityName, courseName, student };
|
|
149387
|
+
}
|
|
149388
|
+
async resolveAssessmentLineItem(activityId, activityName, classId, ids) {
|
|
149389
|
+
const lineItemId = `${ids.course}-${activityId}-assessment`;
|
|
149390
|
+
let actualLineItemId = this.cacheManager.getAssessmentLineItem(lineItemId);
|
|
149391
|
+
if (!actualLineItemId) {
|
|
149392
|
+
actualLineItemId = await this.getOrCreateLineItem(lineItemId, activityName, classId, ids);
|
|
149393
|
+
this.cacheManager.setAssessmentLineItem(lineItemId, actualLineItemId);
|
|
149394
|
+
}
|
|
149395
|
+
return actualLineItemId;
|
|
149396
|
+
}
|
|
149397
|
+
async resolveAttemptNumber(providedAttemptNumber, score, studentId, lineItemId) {
|
|
149398
|
+
if (providedAttemptNumber) {
|
|
149399
|
+
return providedAttemptNumber;
|
|
149400
|
+
}
|
|
149401
|
+
if (score !== undefined) {
|
|
149402
|
+
return this.determineAttemptNumber(studentId, lineItemId);
|
|
149403
|
+
}
|
|
149404
|
+
return 1;
|
|
149405
|
+
}
|
|
149406
|
+
calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt) {
|
|
149407
|
+
if (xpEarned !== undefined) {
|
|
149408
|
+
log3.debug("[ProgressRecorder] Using provided XP", { xpEarned });
|
|
149409
|
+
return xpEarned;
|
|
149410
|
+
}
|
|
149411
|
+
if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
|
|
149412
|
+
const accuracy = correctQuestions / totalQuestions;
|
|
149413
|
+
const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
|
|
149414
|
+
log3.debug("[ProgressRecorder] Calculated XP", {
|
|
149415
|
+
durationSeconds: progressData.sessionDurationSeconds,
|
|
149416
|
+
accuracy,
|
|
149417
|
+
isFirstAttempt,
|
|
149418
|
+
calculatedXp
|
|
149419
|
+
});
|
|
149420
|
+
return calculatedXp;
|
|
149421
|
+
}
|
|
149422
|
+
return 0;
|
|
149423
|
+
}
|
|
148955
149424
|
async getOrCreateLineItem(lineItemId, activityName, classId, ids) {
|
|
148956
149425
|
try {
|
|
148957
149426
|
const lineItem = await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
148958
149427
|
sourcedId: lineItemId,
|
|
148959
149428
|
title: activityName,
|
|
148960
149429
|
status: ONEROSTER_STATUS.active,
|
|
148961
|
-
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
148962
|
-
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : ids.component ? { component: { sourcedId: ids.component } } : {}
|
|
149430
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
148963
149431
|
});
|
|
148964
149432
|
if (!lineItem.sourcedId) {
|
|
148965
149433
|
throw new TimebackError(`Assessment line item created but has no sourcedId. ` + `This should not happen and indicates an upstream API issue.`);
|
|
@@ -148977,39 +149445,15 @@ class ProgressRecorder {
|
|
|
148977
149445
|
}
|
|
148978
149446
|
}
|
|
148979
149447
|
async determineAttemptNumber(studentId, lineItemId) {
|
|
148980
|
-
const
|
|
148981
|
-
if (
|
|
148982
|
-
|
|
148983
|
-
const newAttemptNumber = previousAttemptNumber + 1;
|
|
148984
|
-
log3.debug("[ProgressRecorder] Found previous attempt, incrementing", {
|
|
148985
|
-
previousAttemptNumber,
|
|
148986
|
-
newAttemptNumber,
|
|
148987
|
-
previousScore: latestAttempt.score
|
|
148988
|
-
});
|
|
148989
|
-
return newAttemptNumber;
|
|
149448
|
+
const stats = await this.onerosterNamespace.assessmentResults.getAttemptStats(studentId, lineItemId);
|
|
149449
|
+
if (stats) {
|
|
149450
|
+
return stats.activeAttemptCount + 1;
|
|
148990
149451
|
}
|
|
148991
149452
|
return 1;
|
|
148992
149453
|
}
|
|
148993
|
-
|
|
148994
|
-
|
|
148995
|
-
|
|
148996
|
-
return xpEarned;
|
|
148997
|
-
}
|
|
148998
|
-
if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
|
|
148999
|
-
const accuracy = correctQuestions / totalQuestions;
|
|
149000
|
-
const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
|
|
149001
|
-
log3.debug("[ProgressRecorder] Calculated XP", {
|
|
149002
|
-
durationSeconds: progressData.sessionDurationSeconds,
|
|
149003
|
-
accuracy,
|
|
149004
|
-
isFirstAttempt,
|
|
149005
|
-
calculatedXp
|
|
149006
|
-
});
|
|
149007
|
-
return calculatedXp;
|
|
149008
|
-
}
|
|
149009
|
-
return 0;
|
|
149010
|
-
}
|
|
149011
|
-
async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp) {
|
|
149012
|
-
const resultId = `${lineItemId}:${studentId}:attempt-${attemptNumber}`;
|
|
149454
|
+
async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp, masteredUnits, scoreStatus, inProgress, appName) {
|
|
149455
|
+
const timestamp4 = Date.now().toString(36);
|
|
149456
|
+
const resultId = `${lineItemId}:${studentId}:${timestamp4}`;
|
|
149013
149457
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
149014
149458
|
sourcedId: resultId,
|
|
149015
149459
|
status: ONEROSTER_STATUS.active,
|
|
@@ -149017,18 +149461,21 @@ class ProgressRecorder {
|
|
|
149017
149461
|
student: { sourcedId: studentId },
|
|
149018
149462
|
score,
|
|
149019
149463
|
scoreDate: new Date().toISOString(),
|
|
149020
|
-
scoreStatus
|
|
149464
|
+
scoreStatus,
|
|
149465
|
+
inProgress,
|
|
149021
149466
|
metadata: {
|
|
149022
149467
|
xp,
|
|
149023
149468
|
totalQuestions,
|
|
149024
149469
|
correctQuestions,
|
|
149025
149470
|
accuracy: totalQuestions && correctQuestions ? correctQuestions / totalQuestions * 100 : undefined,
|
|
149026
149471
|
attemptNumber,
|
|
149027
|
-
lastUpdated: new Date().toISOString()
|
|
149472
|
+
lastUpdated: new Date().toISOString(),
|
|
149473
|
+
masteredUnits,
|
|
149474
|
+
appName
|
|
149028
149475
|
}
|
|
149029
149476
|
});
|
|
149030
149477
|
}
|
|
149031
|
-
async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData) {
|
|
149478
|
+
async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData, extensions) {
|
|
149032
149479
|
await this.caliperNamespace.emitActivityEvent({
|
|
149033
149480
|
studentId,
|
|
149034
149481
|
studentEmail,
|
|
@@ -149043,7 +149490,8 @@ class ProgressRecorder {
|
|
|
149043
149490
|
attemptNumber,
|
|
149044
149491
|
subject: progressData.subject,
|
|
149045
149492
|
appName: progressData.appName,
|
|
149046
|
-
sensorUrl: progressData.sensorUrl
|
|
149493
|
+
sensorUrl: progressData.sensorUrl,
|
|
149494
|
+
extensions: extensions || progressData.extensions
|
|
149047
149495
|
}).catch((error2) => {
|
|
149048
149496
|
log3.error("[ProgressRecorder] Failed to emit activity event", { error: error2 });
|
|
149049
149497
|
});
|
|
@@ -151776,9 +152224,9 @@ class ZodUnion2 extends ZodType2 {
|
|
|
151776
152224
|
return this._def.options;
|
|
151777
152225
|
}
|
|
151778
152226
|
}
|
|
151779
|
-
ZodUnion2.create = (
|
|
152227
|
+
ZodUnion2.create = (types22, params) => {
|
|
151780
152228
|
return new ZodUnion2({
|
|
151781
|
-
options:
|
|
152229
|
+
options: types22,
|
|
151782
152230
|
typeName: ZodFirstPartyTypeKind2.ZodUnion,
|
|
151783
152231
|
...processCreateParams2(params)
|
|
151784
152232
|
});
|
|
@@ -153158,9 +153606,11 @@ class TimebackClient {
|
|
|
153158
153606
|
};
|
|
153159
153607
|
this.oneroster = createOneRosterNamespace(this);
|
|
153160
153608
|
this.caliper = createCaliperNamespace(this);
|
|
153609
|
+
this.edubridge = createEduBridgeNamespace(this);
|
|
153161
153610
|
this.cacheManager = new TimebackCacheManager;
|
|
153162
153611
|
this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
|
|
153163
|
-
|
|
153612
|
+
const masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
|
|
153613
|
+
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, masteryTracker);
|
|
153164
153614
|
this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
|
|
153165
153615
|
if (this.credentials) {
|
|
153166
153616
|
this._ensureAuthenticated().catch((error2) => {
|
|
@@ -153269,6 +153719,9 @@ class TimebackClient {
|
|
|
153269
153719
|
}
|
|
153270
153720
|
await this.authenticate();
|
|
153271
153721
|
}
|
|
153722
|
+
async resolveStudent(studentIdentifier, providedEmail) {
|
|
153723
|
+
return this.studentResolver.resolve(studentIdentifier, providedEmail);
|
|
153724
|
+
}
|
|
153272
153725
|
async recordProgress(courseId, studentIdentifier, progressData) {
|
|
153273
153726
|
await this._ensureAuthenticated();
|
|
153274
153727
|
return this.progressRecorder.record(courseId, studentIdentifier, progressData);
|
|
@@ -153277,6 +153730,28 @@ class TimebackClient {
|
|
|
153277
153730
|
await this._ensureAuthenticated();
|
|
153278
153731
|
return this.sessionRecorder.record(courseId, studentIdentifier, sessionData);
|
|
153279
153732
|
}
|
|
153733
|
+
async getEnrollments(studentId) {
|
|
153734
|
+
const cached = this.cacheManager.getEnrollments(studentId);
|
|
153735
|
+
if (cached) {
|
|
153736
|
+
return cached;
|
|
153737
|
+
}
|
|
153738
|
+
await this._ensureAuthenticated();
|
|
153739
|
+
const edubridgeEnrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
153740
|
+
const enrollments = edubridgeEnrollments.map((enrollment) => {
|
|
153741
|
+
const grades = enrollment.course.grades ? enrollment.course.grades.map((g5) => parseInt(g5, 10)).filter(isTimebackGrade) : null;
|
|
153742
|
+
const subjects = enrollment.course.subjects ? enrollment.course.subjects.filter(isTimebackSubject) : null;
|
|
153743
|
+
return {
|
|
153744
|
+
sourcedId: enrollment.id,
|
|
153745
|
+
title: enrollment.course.title,
|
|
153746
|
+
courseId: enrollment.course.id,
|
|
153747
|
+
status: "active",
|
|
153748
|
+
grades,
|
|
153749
|
+
subjects
|
|
153750
|
+
};
|
|
153751
|
+
});
|
|
153752
|
+
this.cacheManager.setEnrollments(studentId, enrollments);
|
|
153753
|
+
return enrollments;
|
|
153754
|
+
}
|
|
153280
153755
|
clearCaches() {
|
|
153281
153756
|
this.cacheManager.clearAll();
|
|
153282
153757
|
}
|
|
@@ -153288,6 +153763,7 @@ class TimebackClient {
|
|
|
153288
153763
|
}
|
|
153289
153764
|
oneroster;
|
|
153290
153765
|
caliper;
|
|
153766
|
+
edubridge;
|
|
153291
153767
|
async setup(config2, options) {
|
|
153292
153768
|
return setupTimebackResources(this, config2, options);
|
|
153293
153769
|
}
|
|
@@ -153305,6 +153781,310 @@ class TimebackClient {
|
|
|
153305
153781
|
}
|
|
153306
153782
|
}
|
|
153307
153783
|
|
|
153784
|
+
// ../timeback/dist/types.js
|
|
153785
|
+
var __esm4 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
|
|
153786
|
+
var init_auth2 = () => {};
|
|
153787
|
+
var PLAYCADEMY_BASE_URLS2;
|
|
153788
|
+
var init_domains2 = __esm4(() => {
|
|
153789
|
+
PLAYCADEMY_BASE_URLS2 = {
|
|
153790
|
+
production: "https://hub.playcademy.net",
|
|
153791
|
+
staging: "https://hub.dev.playcademy.net"
|
|
153792
|
+
};
|
|
153793
|
+
});
|
|
153794
|
+
var init_env_vars2 = () => {};
|
|
153795
|
+
var ITEM_SLUGS3;
|
|
153796
|
+
var CURRENCIES3;
|
|
153797
|
+
var BADGES3;
|
|
153798
|
+
var init_overworld2 = __esm4(() => {
|
|
153799
|
+
ITEM_SLUGS3 = {
|
|
153800
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
153801
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
153802
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
153803
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
153804
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
153805
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
153806
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
153807
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
153808
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
153809
|
+
BOOMBOX: "BOOMBOX",
|
|
153810
|
+
CABIN_BED: "CABIN_BED"
|
|
153811
|
+
};
|
|
153812
|
+
CURRENCIES3 = {
|
|
153813
|
+
PRIMARY: ITEM_SLUGS3.PLAYCADEMY_CREDITS,
|
|
153814
|
+
XP: ITEM_SLUGS3.PLAYCADEMY_XP
|
|
153815
|
+
};
|
|
153816
|
+
BADGES3 = {
|
|
153817
|
+
FOUNDING_MEMBER: ITEM_SLUGS3.FOUNDING_MEMBER_BADGE,
|
|
153818
|
+
EARLY_ADOPTER: ITEM_SLUGS3.EARLY_ADOPTER_BADGE,
|
|
153819
|
+
FIRST_GAME: ITEM_SLUGS3.FIRST_GAME_BADGE
|
|
153820
|
+
};
|
|
153821
|
+
});
|
|
153822
|
+
var init_timeback2 = () => {};
|
|
153823
|
+
var init_workers2 = () => {};
|
|
153824
|
+
var init_src3 = __esm4(() => {
|
|
153825
|
+
init_auth2();
|
|
153826
|
+
init_domains2();
|
|
153827
|
+
init_env_vars2();
|
|
153828
|
+
init_overworld2();
|
|
153829
|
+
init_timeback2();
|
|
153830
|
+
init_workers2();
|
|
153831
|
+
});
|
|
153832
|
+
var TIMEBACK_API_URLS2;
|
|
153833
|
+
var TIMEBACK_AUTH_URLS2;
|
|
153834
|
+
var CALIPER_API_URLS2;
|
|
153835
|
+
var ONEROSTER_ENDPOINTS2;
|
|
153836
|
+
var CALIPER_ENDPOINTS2;
|
|
153837
|
+
var CALIPER_CONSTANTS2;
|
|
153838
|
+
var TIMEBACK_EVENT_TYPES2;
|
|
153839
|
+
var TIMEBACK_ACTIONS2;
|
|
153840
|
+
var TIMEBACK_TYPES2;
|
|
153841
|
+
var ACTIVITY_METRIC_TYPES2;
|
|
153842
|
+
var TIME_METRIC_TYPES2;
|
|
153843
|
+
var TIMEBACK_SUBJECTS2;
|
|
153844
|
+
var TIMEBACK_GRADE_LEVELS2;
|
|
153845
|
+
var TIMEBACK_GRADE_LEVEL_LABELS2;
|
|
153846
|
+
var CALIPER_SUBJECTS2;
|
|
153847
|
+
var ONEROSTER_STATUS2;
|
|
153848
|
+
var SCORE_STATUS2;
|
|
153849
|
+
var ENV_VARS2;
|
|
153850
|
+
var HTTP_DEFAULTS2;
|
|
153851
|
+
var AUTH_DEFAULTS2;
|
|
153852
|
+
var CACHE_DEFAULTS2;
|
|
153853
|
+
var CONFIG_DEFAULTS2;
|
|
153854
|
+
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID2;
|
|
153855
|
+
var PLAYCADEMY_DEFAULTS2;
|
|
153856
|
+
var RESOURCE_DEFAULTS2;
|
|
153857
|
+
var HTTP_STATUS2;
|
|
153858
|
+
var ERROR_NAMES2;
|
|
153859
|
+
var init_constants2 = __esm4(() => {
|
|
153860
|
+
init_src3();
|
|
153861
|
+
TIMEBACK_API_URLS2 = {
|
|
153862
|
+
production: "https://api.alpha-1edtech.ai",
|
|
153863
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
153864
|
+
};
|
|
153865
|
+
TIMEBACK_AUTH_URLS2 = {
|
|
153866
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
153867
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
153868
|
+
};
|
|
153869
|
+
CALIPER_API_URLS2 = {
|
|
153870
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
153871
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
153872
|
+
};
|
|
153873
|
+
ONEROSTER_ENDPOINTS2 = {
|
|
153874
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
153875
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
153876
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
153877
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
153878
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
153879
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
153880
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
153881
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
153882
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
153883
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
153884
|
+
};
|
|
153885
|
+
CALIPER_ENDPOINTS2 = {
|
|
153886
|
+
events: "/caliper/event",
|
|
153887
|
+
validate: "/caliper/event/validate"
|
|
153888
|
+
};
|
|
153889
|
+
CALIPER_CONSTANTS2 = {
|
|
153890
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
153891
|
+
profile: "TimebackProfile",
|
|
153892
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
153893
|
+
};
|
|
153894
|
+
TIMEBACK_EVENT_TYPES2 = {
|
|
153895
|
+
activityEvent: "ActivityEvent",
|
|
153896
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
153897
|
+
};
|
|
153898
|
+
TIMEBACK_ACTIONS2 = {
|
|
153899
|
+
completed: "Completed",
|
|
153900
|
+
spentTime: "SpentTime"
|
|
153901
|
+
};
|
|
153902
|
+
TIMEBACK_TYPES2 = {
|
|
153903
|
+
user: "TimebackUser",
|
|
153904
|
+
activityContext: "TimebackActivityContext",
|
|
153905
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
153906
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
153907
|
+
};
|
|
153908
|
+
ACTIVITY_METRIC_TYPES2 = {
|
|
153909
|
+
totalQuestions: "totalQuestions",
|
|
153910
|
+
correctQuestions: "correctQuestions",
|
|
153911
|
+
xpEarned: "xpEarned",
|
|
153912
|
+
masteredUnits: "masteredUnits"
|
|
153913
|
+
};
|
|
153914
|
+
TIME_METRIC_TYPES2 = {
|
|
153915
|
+
active: "active",
|
|
153916
|
+
inactive: "inactive",
|
|
153917
|
+
waste: "waste",
|
|
153918
|
+
unknown: "unknown",
|
|
153919
|
+
antiPattern: "anti-pattern"
|
|
153920
|
+
};
|
|
153921
|
+
TIMEBACK_SUBJECTS2 = [
|
|
153922
|
+
"Math",
|
|
153923
|
+
"FastMath",
|
|
153924
|
+
"Science",
|
|
153925
|
+
"Social Studies",
|
|
153926
|
+
"Language",
|
|
153927
|
+
"Reading",
|
|
153928
|
+
"Vocabulary",
|
|
153929
|
+
"Writing"
|
|
153930
|
+
];
|
|
153931
|
+
TIMEBACK_GRADE_LEVELS2 = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
153932
|
+
TIMEBACK_GRADE_LEVEL_LABELS2 = {
|
|
153933
|
+
"-1": "pre-k",
|
|
153934
|
+
"0": "kindergarten",
|
|
153935
|
+
"1": "1st grade",
|
|
153936
|
+
"2": "2nd grade",
|
|
153937
|
+
"3": "3rd grade",
|
|
153938
|
+
"4": "4th grade",
|
|
153939
|
+
"5": "5th grade",
|
|
153940
|
+
"6": "6th grade",
|
|
153941
|
+
"7": "7th grade",
|
|
153942
|
+
"8": "8th grade",
|
|
153943
|
+
"9": "9th grade",
|
|
153944
|
+
"10": "10th grade",
|
|
153945
|
+
"11": "11th grade",
|
|
153946
|
+
"12": "12th grade",
|
|
153947
|
+
"13": "AP"
|
|
153948
|
+
};
|
|
153949
|
+
CALIPER_SUBJECTS2 = {
|
|
153950
|
+
Reading: "Reading",
|
|
153951
|
+
Language: "Language",
|
|
153952
|
+
Vocabulary: "Vocabulary",
|
|
153953
|
+
SocialStudies: "Social Studies",
|
|
153954
|
+
Writing: "Writing",
|
|
153955
|
+
Science: "Science",
|
|
153956
|
+
FastMath: "FastMath",
|
|
153957
|
+
Math: "Math",
|
|
153958
|
+
None: "None"
|
|
153959
|
+
};
|
|
153960
|
+
ONEROSTER_STATUS2 = {
|
|
153961
|
+
active: "active",
|
|
153962
|
+
toBeDeleted: "tobedeleted"
|
|
153963
|
+
};
|
|
153964
|
+
SCORE_STATUS2 = {
|
|
153965
|
+
exempt: "exempt",
|
|
153966
|
+
fullyGraded: "fully graded",
|
|
153967
|
+
notSubmitted: "not submitted",
|
|
153968
|
+
partiallyGraded: "partially graded",
|
|
153969
|
+
submitted: "submitted"
|
|
153970
|
+
};
|
|
153971
|
+
ENV_VARS2 = {
|
|
153972
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
153973
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
153974
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
153975
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
153976
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
153977
|
+
launchBaseUrl: "GAME_URL"
|
|
153978
|
+
};
|
|
153979
|
+
HTTP_DEFAULTS2 = {
|
|
153980
|
+
timeout: 30000,
|
|
153981
|
+
retries: 3,
|
|
153982
|
+
retryBackoffBase: 2
|
|
153983
|
+
};
|
|
153984
|
+
AUTH_DEFAULTS2 = {
|
|
153985
|
+
tokenCacheDuration: 50000
|
|
153986
|
+
};
|
|
153987
|
+
CACHE_DEFAULTS2 = {
|
|
153988
|
+
defaultTTL: 10 * 60 * 1000,
|
|
153989
|
+
defaultMaxSize: 500,
|
|
153990
|
+
defaultName: "TimebackCache",
|
|
153991
|
+
studentTTL: 10 * 60 * 1000,
|
|
153992
|
+
studentMaxSize: 500,
|
|
153993
|
+
assessmentTTL: 30 * 60 * 1000,
|
|
153994
|
+
assessmentMaxSize: 200,
|
|
153995
|
+
enrollmentTTL: 5 * 1000,
|
|
153996
|
+
enrollmentMaxSize: 100
|
|
153997
|
+
};
|
|
153998
|
+
CONFIG_DEFAULTS2 = {
|
|
153999
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
154000
|
+
};
|
|
154001
|
+
DEFAULT_PLAYCADEMY_ORGANIZATION_ID2 = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
154002
|
+
PLAYCADEMY_DEFAULTS2 = {
|
|
154003
|
+
organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID2,
|
|
154004
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS2
|
|
154005
|
+
};
|
|
154006
|
+
RESOURCE_DEFAULTS2 = {
|
|
154007
|
+
organization: {
|
|
154008
|
+
name: "Playcademy Studios",
|
|
154009
|
+
type: "department"
|
|
154010
|
+
},
|
|
154011
|
+
course: {
|
|
154012
|
+
gradingScheme: "STANDARD",
|
|
154013
|
+
level: {
|
|
154014
|
+
elementary: "Elementary",
|
|
154015
|
+
middle: "Middle",
|
|
154016
|
+
high: "High",
|
|
154017
|
+
ap: "AP"
|
|
154018
|
+
},
|
|
154019
|
+
metadata: {
|
|
154020
|
+
goals: {
|
|
154021
|
+
dailyXp: 50,
|
|
154022
|
+
dailyLessons: 3
|
|
154023
|
+
},
|
|
154024
|
+
metrics: {
|
|
154025
|
+
totalXp: 1000,
|
|
154026
|
+
totalLessons: 50
|
|
154027
|
+
}
|
|
154028
|
+
}
|
|
154029
|
+
},
|
|
154030
|
+
component: {
|
|
154031
|
+
sortOrder: 1,
|
|
154032
|
+
prerequisiteCriteria: "ALL"
|
|
154033
|
+
},
|
|
154034
|
+
resource: {
|
|
154035
|
+
vendorId: "playcademy",
|
|
154036
|
+
roles: ["primary"],
|
|
154037
|
+
importance: "primary",
|
|
154038
|
+
metadata: {
|
|
154039
|
+
type: "interactive",
|
|
154040
|
+
toolProvider: "Playcademy",
|
|
154041
|
+
instructionalMethod: "exploratory",
|
|
154042
|
+
language: "en-US"
|
|
154043
|
+
}
|
|
154044
|
+
},
|
|
154045
|
+
componentResource: {
|
|
154046
|
+
sortOrder: 1,
|
|
154047
|
+
lessonType: "quiz"
|
|
154048
|
+
}
|
|
154049
|
+
};
|
|
154050
|
+
HTTP_STATUS2 = {
|
|
154051
|
+
CLIENT_ERROR_MIN: 400,
|
|
154052
|
+
CLIENT_ERROR_MAX: 500,
|
|
154053
|
+
SERVER_ERROR_MIN: 500
|
|
154054
|
+
};
|
|
154055
|
+
ERROR_NAMES2 = {
|
|
154056
|
+
timebackAuth: "TimebackAuthError",
|
|
154057
|
+
timebackApi: "TimebackApiError",
|
|
154058
|
+
timebackConfig: "TimebackConfigError",
|
|
154059
|
+
timebackSdk: "TimebackSDKError"
|
|
154060
|
+
};
|
|
154061
|
+
});
|
|
154062
|
+
init_constants2();
|
|
154063
|
+
var isObject3 = (value) => typeof value === "object" && value !== null;
|
|
154064
|
+
var SUBJECT_VALUES2 = TIMEBACK_SUBJECTS2;
|
|
154065
|
+
var GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS2;
|
|
154066
|
+
function isCourseMetadata(value) {
|
|
154067
|
+
return isObject3(value);
|
|
154068
|
+
}
|
|
154069
|
+
function isResourceMetadata(value) {
|
|
154070
|
+
return isObject3(value);
|
|
154071
|
+
}
|
|
154072
|
+
function isPlaycademyResourceMetadata2(value) {
|
|
154073
|
+
if (!isObject3(value)) {
|
|
154074
|
+
return false;
|
|
154075
|
+
}
|
|
154076
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
154077
|
+
return true;
|
|
154078
|
+
}
|
|
154079
|
+
return isObject3(value.mastery);
|
|
154080
|
+
}
|
|
154081
|
+
function isTimebackSubject2(value) {
|
|
154082
|
+
return typeof value === "string" && SUBJECT_VALUES2.includes(value);
|
|
154083
|
+
}
|
|
154084
|
+
function isTimebackGrade2(value) {
|
|
154085
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
154086
|
+
}
|
|
154087
|
+
|
|
153308
154088
|
// ../utils/src/uuid.ts
|
|
153309
154089
|
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}$/;
|
|
153310
154090
|
function isValidUUID(value) {
|
|
@@ -153590,6 +154370,95 @@ async function getTimebackClient() {
|
|
|
153590
154370
|
}
|
|
153591
154371
|
return timebackClient;
|
|
153592
154372
|
}
|
|
154373
|
+
function buildResourceMetadata({
|
|
154374
|
+
baseMetadata,
|
|
154375
|
+
subject,
|
|
154376
|
+
grade,
|
|
154377
|
+
totalXp,
|
|
154378
|
+
masterableUnits
|
|
154379
|
+
}) {
|
|
154380
|
+
const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
|
|
154381
|
+
const metadata2 = {
|
|
154382
|
+
...normalizedBaseMetadata || {}
|
|
154383
|
+
};
|
|
154384
|
+
metadata2.subject = subject;
|
|
154385
|
+
metadata2.grades = [grade];
|
|
154386
|
+
metadata2.xp = totalXp;
|
|
154387
|
+
if (masterableUnits !== undefined && masterableUnits !== null) {
|
|
154388
|
+
const existingPlaycademy = isPlaycademyResourceMetadata2(metadata2.playcademy) ? metadata2.playcademy : undefined;
|
|
154389
|
+
metadata2.playcademy = {
|
|
154390
|
+
...existingPlaycademy || {},
|
|
154391
|
+
mastery: {
|
|
154392
|
+
...existingPlaycademy?.mastery || {},
|
|
154393
|
+
masterableUnits
|
|
154394
|
+
}
|
|
154395
|
+
};
|
|
154396
|
+
}
|
|
154397
|
+
return metadata2;
|
|
154398
|
+
}
|
|
154399
|
+
// ../api-core/src/utils/timeback-enrollments.ts
|
|
154400
|
+
init_src();
|
|
154401
|
+
async function fetchEnrollmentsForUser(timebackId) {
|
|
154402
|
+
const db = getDatabase();
|
|
154403
|
+
const isLocal = process.env.PUBLIC_IS_LOCAL === "true";
|
|
154404
|
+
if (isLocal) {
|
|
154405
|
+
const allIntegrations = await db.query.gameTimebackIntegrations.findMany();
|
|
154406
|
+
return allIntegrations.map((integration) => ({
|
|
154407
|
+
gameId: integration.gameId,
|
|
154408
|
+
grade: integration.grade,
|
|
154409
|
+
subject: integration.subject,
|
|
154410
|
+
courseId: integration.courseId
|
|
154411
|
+
}));
|
|
154412
|
+
}
|
|
154413
|
+
log2.debug("[timeback-enrollments] Fetching student enrollments from TimeBack", { timebackId });
|
|
154414
|
+
try {
|
|
154415
|
+
const client = await getTimebackClient();
|
|
154416
|
+
const classes = await client.getEnrollments(timebackId);
|
|
154417
|
+
const courseIds = classes.map((cls) => cls.courseId).filter((id) => Boolean(id));
|
|
154418
|
+
if (courseIds.length === 0) {
|
|
154419
|
+
return [];
|
|
154420
|
+
}
|
|
154421
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
154422
|
+
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
154423
|
+
});
|
|
154424
|
+
return integrations.map((integration) => ({
|
|
154425
|
+
gameId: integration.gameId,
|
|
154426
|
+
grade: integration.grade,
|
|
154427
|
+
subject: integration.subject,
|
|
154428
|
+
courseId: integration.courseId
|
|
154429
|
+
}));
|
|
154430
|
+
} catch (error2) {
|
|
154431
|
+
log2.warn("[timeback-enrollments] Failed to fetch TimeBack enrollments:", {
|
|
154432
|
+
error: error2,
|
|
154433
|
+
timebackId
|
|
154434
|
+
});
|
|
154435
|
+
return [];
|
|
154436
|
+
}
|
|
154437
|
+
}
|
|
154438
|
+
async function fetchUserRole(timebackId) {
|
|
154439
|
+
log2.debug("[timeback] Fetching user role from TimeBack", { timebackId });
|
|
154440
|
+
try {
|
|
154441
|
+
const client = await getTimebackClient();
|
|
154442
|
+
const user = await client.oneroster.users.get(timebackId);
|
|
154443
|
+
const primaryRole = user.roles.find((r2) => r2.roleType === "primary");
|
|
154444
|
+
const role = primaryRole?.role ?? user.roles[0]?.role ?? "student";
|
|
154445
|
+
log2.debug("[timeback] Resolved user role", { timebackId, role });
|
|
154446
|
+
return role;
|
|
154447
|
+
} catch (error2) {
|
|
154448
|
+
log2.warn("[timeback] Failed to fetch user role, defaulting to student:", {
|
|
154449
|
+
error: error2,
|
|
154450
|
+
timebackId
|
|
154451
|
+
});
|
|
154452
|
+
return "student";
|
|
154453
|
+
}
|
|
154454
|
+
}
|
|
154455
|
+
async function fetchUserTimebackData(timebackId) {
|
|
154456
|
+
const [role, enrollments] = await Promise.all([
|
|
154457
|
+
fetchUserRole(timebackId),
|
|
154458
|
+
fetchEnrollmentsForUser(timebackId)
|
|
154459
|
+
]);
|
|
154460
|
+
return { role, enrollments };
|
|
154461
|
+
}
|
|
153593
154462
|
// ../data/src/domains/achievement/types.ts
|
|
153594
154463
|
var AchievementCompletionType;
|
|
153595
154464
|
((AchievementCompletionType2) => {
|
|
@@ -154125,6 +154994,249 @@ var AchievementService = {
|
|
|
154125
154994
|
checkInteractionAchievement,
|
|
154126
154995
|
generateAchievementMessage
|
|
154127
154996
|
};
|
|
154997
|
+
// ../alerts/src/discord/embed.ts
|
|
154998
|
+
class DiscordEmbedBuilder {
|
|
154999
|
+
embed = {};
|
|
155000
|
+
setTitle(title) {
|
|
155001
|
+
this.embed.title = title;
|
|
155002
|
+
return this;
|
|
155003
|
+
}
|
|
155004
|
+
setDescription(description) {
|
|
155005
|
+
this.embed.description = description;
|
|
155006
|
+
return this;
|
|
155007
|
+
}
|
|
155008
|
+
setUrl(url) {
|
|
155009
|
+
this.embed.url = url;
|
|
155010
|
+
return this;
|
|
155011
|
+
}
|
|
155012
|
+
setColor(color) {
|
|
155013
|
+
this.embed.color = color;
|
|
155014
|
+
return this;
|
|
155015
|
+
}
|
|
155016
|
+
setTimestamp(date4) {
|
|
155017
|
+
this.embed.timestamp = (date4 || new Date).toISOString();
|
|
155018
|
+
return this;
|
|
155019
|
+
}
|
|
155020
|
+
setAuthor(name4, options) {
|
|
155021
|
+
this.embed.author = {
|
|
155022
|
+
name: name4,
|
|
155023
|
+
url: options?.url,
|
|
155024
|
+
icon_url: options?.iconUrl
|
|
155025
|
+
};
|
|
155026
|
+
return this;
|
|
155027
|
+
}
|
|
155028
|
+
setFooter(text5, iconUrl) {
|
|
155029
|
+
this.embed.footer = {
|
|
155030
|
+
text: text5,
|
|
155031
|
+
icon_url: iconUrl
|
|
155032
|
+
};
|
|
155033
|
+
return this;
|
|
155034
|
+
}
|
|
155035
|
+
setThumbnail(url) {
|
|
155036
|
+
this.embed.thumbnail = { url };
|
|
155037
|
+
return this;
|
|
155038
|
+
}
|
|
155039
|
+
setImage(url) {
|
|
155040
|
+
this.embed.image = { url };
|
|
155041
|
+
return this;
|
|
155042
|
+
}
|
|
155043
|
+
addField(name4, value, inline = false) {
|
|
155044
|
+
if (!this.embed.fields) {
|
|
155045
|
+
this.embed.fields = [];
|
|
155046
|
+
}
|
|
155047
|
+
this.embed.fields.push({ name: name4, value, inline });
|
|
155048
|
+
return this;
|
|
155049
|
+
}
|
|
155050
|
+
addFields(...fields) {
|
|
155051
|
+
for (const field of fields) {
|
|
155052
|
+
this.addField(field.name, field.value, field.inline);
|
|
155053
|
+
}
|
|
155054
|
+
return this;
|
|
155055
|
+
}
|
|
155056
|
+
build() {
|
|
155057
|
+
return this.embed;
|
|
155058
|
+
}
|
|
155059
|
+
static create() {
|
|
155060
|
+
return new DiscordEmbedBuilder;
|
|
155061
|
+
}
|
|
155062
|
+
}
|
|
155063
|
+
|
|
155064
|
+
// ../alerts/src/discord/client.ts
|
|
155065
|
+
class DiscordClient {
|
|
155066
|
+
config;
|
|
155067
|
+
constructor(config2) {
|
|
155068
|
+
if (!config2.url) {
|
|
155069
|
+
throw new Error("Discord webhook URL is required");
|
|
155070
|
+
}
|
|
155071
|
+
if (!config2.url.startsWith("https://discord.com/api/webhooks/")) {
|
|
155072
|
+
throw new Error("Invalid Discord webhook URL format");
|
|
155073
|
+
}
|
|
155074
|
+
this.config = config2;
|
|
155075
|
+
}
|
|
155076
|
+
async send(message2) {
|
|
155077
|
+
const payload = {
|
|
155078
|
+
content: message2,
|
|
155079
|
+
username: this.config.username,
|
|
155080
|
+
avatar_url: this.config.avatarUrl
|
|
155081
|
+
};
|
|
155082
|
+
await this.sendPayload(payload);
|
|
155083
|
+
}
|
|
155084
|
+
async sendEmbed(embed) {
|
|
155085
|
+
const payload = {
|
|
155086
|
+
embeds: [embed],
|
|
155087
|
+
username: this.config.username,
|
|
155088
|
+
avatar_url: this.config.avatarUrl
|
|
155089
|
+
};
|
|
155090
|
+
await this.sendPayload(payload);
|
|
155091
|
+
}
|
|
155092
|
+
async sendEmbeds(embeds) {
|
|
155093
|
+
if (embeds.length > 10) {
|
|
155094
|
+
throw new Error("Discord allows maximum 10 embeds per message");
|
|
155095
|
+
}
|
|
155096
|
+
const payload = {
|
|
155097
|
+
embeds,
|
|
155098
|
+
username: this.config.username,
|
|
155099
|
+
avatar_url: this.config.avatarUrl
|
|
155100
|
+
};
|
|
155101
|
+
await this.sendPayload(payload);
|
|
155102
|
+
}
|
|
155103
|
+
async sendWithEmbeds(message2, embeds) {
|
|
155104
|
+
const payload = {
|
|
155105
|
+
content: message2,
|
|
155106
|
+
embeds,
|
|
155107
|
+
username: this.config.username,
|
|
155108
|
+
avatar_url: this.config.avatarUrl
|
|
155109
|
+
};
|
|
155110
|
+
await this.sendPayload(payload);
|
|
155111
|
+
}
|
|
155112
|
+
async sendRich(message2) {
|
|
155113
|
+
const builder = new DiscordEmbedBuilder;
|
|
155114
|
+
if (message2.title)
|
|
155115
|
+
builder.setTitle(message2.title);
|
|
155116
|
+
if (message2.description)
|
|
155117
|
+
builder.setDescription(message2.description);
|
|
155118
|
+
if (message2.url)
|
|
155119
|
+
builder.setUrl(message2.url);
|
|
155120
|
+
if (message2.color) {
|
|
155121
|
+
const color = typeof message2.color === "string" ? parseInt(message2.color.replace("#", ""), 16) : message2.color;
|
|
155122
|
+
builder.setColor(color);
|
|
155123
|
+
}
|
|
155124
|
+
if (message2.author) {
|
|
155125
|
+
builder.setAuthor(message2.author.name, {
|
|
155126
|
+
url: message2.author.url,
|
|
155127
|
+
iconUrl: message2.author.iconUrl
|
|
155128
|
+
});
|
|
155129
|
+
}
|
|
155130
|
+
if (message2.fields) {
|
|
155131
|
+
for (const field of message2.fields) {
|
|
155132
|
+
builder.addField(field.name, field.value, field.inline);
|
|
155133
|
+
}
|
|
155134
|
+
}
|
|
155135
|
+
if (message2.footer) {
|
|
155136
|
+
builder.setFooter(message2.footer.text, message2.footer.iconUrl);
|
|
155137
|
+
}
|
|
155138
|
+
if (message2.timestamp) {
|
|
155139
|
+
const date4 = message2.timestamp instanceof Date ? message2.timestamp : new Date(message2.timestamp);
|
|
155140
|
+
builder.setTimestamp(date4);
|
|
155141
|
+
}
|
|
155142
|
+
if (message2.thumbnail) {
|
|
155143
|
+
builder.setThumbnail(message2.thumbnail.url);
|
|
155144
|
+
}
|
|
155145
|
+
if (message2.image) {
|
|
155146
|
+
builder.setImage(message2.image.url);
|
|
155147
|
+
}
|
|
155148
|
+
await this.sendEmbed(builder.build());
|
|
155149
|
+
}
|
|
155150
|
+
async sendPayload(payload) {
|
|
155151
|
+
const response = await fetch(this.config.url, {
|
|
155152
|
+
method: "POST",
|
|
155153
|
+
headers: {
|
|
155154
|
+
"Content-Type": "application/json"
|
|
155155
|
+
},
|
|
155156
|
+
body: JSON.stringify(payload)
|
|
155157
|
+
});
|
|
155158
|
+
if (!response.ok) {
|
|
155159
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
155160
|
+
throw new Error(`Discord webhook request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
155161
|
+
}
|
|
155162
|
+
}
|
|
155163
|
+
getRedactedUrl() {
|
|
155164
|
+
const parts2 = this.config.url.split("/");
|
|
155165
|
+
const token = parts2[parts2.length - 1];
|
|
155166
|
+
return `${token?.slice(0, 9)}...`;
|
|
155167
|
+
}
|
|
155168
|
+
}
|
|
155169
|
+
// ../alerts/src/discord/types.ts
|
|
155170
|
+
var DiscordColors = {
|
|
155171
|
+
DEFAULT: 0,
|
|
155172
|
+
WHITE: 16777215,
|
|
155173
|
+
AQUA: 1752220,
|
|
155174
|
+
GREEN: 5763719,
|
|
155175
|
+
BLUE: 3447003,
|
|
155176
|
+
YELLOW: 16705372,
|
|
155177
|
+
PURPLE: 10181046,
|
|
155178
|
+
LUMINOUS_VIVID_PINK: 15277667,
|
|
155179
|
+
FUCHSIA: 15418782,
|
|
155180
|
+
GOLD: 15844367,
|
|
155181
|
+
ORANGE: 15105570,
|
|
155182
|
+
RED: 15548997,
|
|
155183
|
+
GREY: 9807270,
|
|
155184
|
+
NAVY: 3426654,
|
|
155185
|
+
DARK_AQUA: 1146986,
|
|
155186
|
+
DARK_GREEN: 2067276,
|
|
155187
|
+
DARK_BLUE: 2123412,
|
|
155188
|
+
DARK_PURPLE: 7419530,
|
|
155189
|
+
DARK_VIVID_PINK: 11342935,
|
|
155190
|
+
DARK_GOLD: 12745742,
|
|
155191
|
+
DARK_ORANGE: 11027200,
|
|
155192
|
+
DARK_RED: 10038562,
|
|
155193
|
+
DARK_GREY: 9936031,
|
|
155194
|
+
DARKER_GREY: 8359053,
|
|
155195
|
+
LIGHT_GREY: 12370112,
|
|
155196
|
+
DARK_NAVY: 2899536,
|
|
155197
|
+
BLURPLE: 5793266,
|
|
155198
|
+
GREYPLE: 10070709,
|
|
155199
|
+
DARK_BUT_NOT_BLACK: 2895667,
|
|
155200
|
+
NOT_QUITE_BLACK: 2303786
|
|
155201
|
+
};
|
|
155202
|
+
// ../api-core/src/utils/alerts.ts
|
|
155203
|
+
init_src();
|
|
155204
|
+
function getDiscordClient() {
|
|
155205
|
+
const webhookUrl = process.env.DISCORD_WEBHOOK_PLATFORM;
|
|
155206
|
+
if (!webhookUrl) {
|
|
155207
|
+
log2.debug("[API] Discord webhook not configured, skipping notification");
|
|
155208
|
+
return null;
|
|
155209
|
+
}
|
|
155210
|
+
return new DiscordClient({
|
|
155211
|
+
url: webhookUrl,
|
|
155212
|
+
username: "Playcademy Platform",
|
|
155213
|
+
avatarUrl: process.env.DISCORD_WEBHOOK_AVATAR_URL
|
|
155214
|
+
});
|
|
155215
|
+
}
|
|
155216
|
+
function getEnvironment() {
|
|
155217
|
+
const stage = process.env.SST_STAGE;
|
|
155218
|
+
return stage === "production" ? "production" : stage === "dev" ? "staging" : "test";
|
|
155219
|
+
}
|
|
155220
|
+
async function sendDeveloperApplicationAlert(user) {
|
|
155221
|
+
const discord = getDiscordClient();
|
|
155222
|
+
if (!discord)
|
|
155223
|
+
return;
|
|
155224
|
+
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();
|
|
155225
|
+
await discord.sendEmbed(embed);
|
|
155226
|
+
log2.debug("[API] Discord notification sent successfully", {
|
|
155227
|
+
userId: user.id
|
|
155228
|
+
});
|
|
155229
|
+
}
|
|
155230
|
+
async function sendGameDeletionAlert(game) {
|
|
155231
|
+
const discord = getDiscordClient();
|
|
155232
|
+
if (!discord)
|
|
155233
|
+
return;
|
|
155234
|
+
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();
|
|
155235
|
+
await discord.sendEmbed(embed);
|
|
155236
|
+
log2.debug("[API] Discord game deletion notification sent", {
|
|
155237
|
+
slug: game.slug
|
|
155238
|
+
});
|
|
155239
|
+
}
|
|
154128
155240
|
// ../../node_modules/aws-jwt-verify/dist/esm/error.js
|
|
154129
155241
|
class JwtBaseError extends Error {
|
|
154130
155242
|
}
|
|
@@ -155262,10 +156374,8 @@ async function publishPersonalBestNotification(userId, gameId, rank, newScore, p
|
|
|
155262
156374
|
});
|
|
155263
156375
|
}
|
|
155264
156376
|
// ../cloudflare/src/playcademy/provider.ts
|
|
155265
|
-
import {
|
|
155266
|
-
import {
|
|
155267
|
-
import { tmpdir } from "node:os";
|
|
155268
|
-
import { join as join5, relative } from "node:path";
|
|
156377
|
+
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
156378
|
+
import { join as join4, relative } from "node:path";
|
|
155269
156379
|
init_src();
|
|
155270
156380
|
|
|
155271
156381
|
// ../cloudflare/src/core/client.ts
|
|
@@ -156094,7 +157204,6 @@ function normalizeDeploymentOptions(options) {
|
|
|
156094
157204
|
assets: options?.bindings?.assets ?? false
|
|
156095
157205
|
},
|
|
156096
157206
|
assetsPath: options?.assetsPath,
|
|
156097
|
-
assetsZip: options?.assetsZip,
|
|
156098
157207
|
keepAssets: options?.keepAssets
|
|
156099
157208
|
};
|
|
156100
157209
|
}
|
|
@@ -156144,41 +157253,6 @@ var DEFAULT_COMPATIBILITY_DATE2 = new Date().toISOString().slice(0, 10);
|
|
|
156144
157253
|
var GAME_WORKER_DOMAIN_PRODUCTION = GAME_WORKER_DOMAINS.production;
|
|
156145
157254
|
var GAME_WORKER_DOMAIN_STAGING = GAME_WORKER_DOMAINS.staging;
|
|
156146
157255
|
|
|
156147
|
-
// ../cloudflare/src/playcademy/workers/index.ts
|
|
156148
|
-
import { dirname as dirname2, join as join4 } from "node:path";
|
|
156149
|
-
import { fileURLToPath } from "node:url";
|
|
156150
|
-
|
|
156151
|
-
// ../cloudflare/src/utils/bundler.ts
|
|
156152
|
-
var esbuild = __toESM(require_main(), 1);
|
|
156153
|
-
async function bundleWorker(filePath) {
|
|
156154
|
-
const result = await esbuild.build({
|
|
156155
|
-
entryPoints: [filePath],
|
|
156156
|
-
bundle: true,
|
|
156157
|
-
format: "esm",
|
|
156158
|
-
platform: "browser",
|
|
156159
|
-
target: "es2022",
|
|
156160
|
-
write: false,
|
|
156161
|
-
minify: false,
|
|
156162
|
-
sourcemap: false,
|
|
156163
|
-
logLevel: "error"
|
|
156164
|
-
});
|
|
156165
|
-
if (!result.outputFiles?.[0]) {
|
|
156166
|
-
throw new Error(`No output generated for worker ${filePath}`);
|
|
156167
|
-
}
|
|
156168
|
-
return result.outputFiles[0].text;
|
|
156169
|
-
}
|
|
156170
|
-
// ../cloudflare/src/playcademy/workers/index.ts
|
|
156171
|
-
var _cachedStubWorker = null;
|
|
156172
|
-
async function getStubWorkerCode() {
|
|
156173
|
-
if (_cachedStubWorker) {
|
|
156174
|
-
return _cachedStubWorker;
|
|
156175
|
-
}
|
|
156176
|
-
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
156177
|
-
const workerPath = join4(currentDir, "stub-worker.ts");
|
|
156178
|
-
_cachedStubWorker = await bundleWorker(workerPath);
|
|
156179
|
-
return _cachedStubWorker;
|
|
156180
|
-
}
|
|
156181
|
-
|
|
156182
157256
|
// ../cloudflare/src/playcademy/provider.ts
|
|
156183
157257
|
class CloudflareProvider {
|
|
156184
157258
|
client;
|
|
@@ -156220,7 +157294,7 @@ class CloudflareProvider {
|
|
|
156220
157294
|
async function scanDirectory(dir) {
|
|
156221
157295
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
156222
157296
|
for (const entry of entries) {
|
|
156223
|
-
const fullPath =
|
|
157297
|
+
const fullPath = join4(dir, entry.name);
|
|
156224
157298
|
if (entry.isDirectory()) {
|
|
156225
157299
|
if (await scanDirectory(fullPath))
|
|
156226
157300
|
return true;
|
|
@@ -156242,7 +157316,7 @@ class CloudflareProvider {
|
|
|
156242
157316
|
async resolveAssetBasePath(dirPath) {
|
|
156243
157317
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
156244
157318
|
if (entries.length === 1 && entries[0]?.isDirectory()) {
|
|
156245
|
-
const unwrappedPath =
|
|
157319
|
+
const unwrappedPath = join4(dirPath, entries[0].name);
|
|
156246
157320
|
log2.debug("[CloudflareProvider] Unwrapping wrapper directory", {
|
|
156247
157321
|
wrapper: entries[0].name
|
|
156248
157322
|
});
|
|
@@ -156253,7 +157327,7 @@ class CloudflareProvider {
|
|
|
156253
157327
|
async uploadFilesToR2(dir, baseDir, bucketName) {
|
|
156254
157328
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
156255
157329
|
for (const entry of entries) {
|
|
156256
|
-
const fullPath =
|
|
157330
|
+
const fullPath = join4(dir, entry.name);
|
|
156257
157331
|
if (entry.isDirectory()) {
|
|
156258
157332
|
await this.uploadFilesToR2(fullPath, baseDir, bucketName);
|
|
156259
157333
|
} else {
|
|
@@ -156276,38 +157350,15 @@ class CloudflareProvider {
|
|
|
156276
157350
|
async deploy(deploymentId, code, env2, options) {
|
|
156277
157351
|
const opts = normalizeDeploymentOptions(options);
|
|
156278
157352
|
const isFirstDeploy = env2.PLAYCADEMY_API_KEY !== undefined;
|
|
156279
|
-
const hasAssets = !!
|
|
156280
|
-
let finalCode = code ?? "";
|
|
157353
|
+
const hasAssets = !!opts.assetsPath;
|
|
156281
157354
|
if (!code) {
|
|
156282
|
-
|
|
156283
|
-
log2.debug("[CloudflareProvider] Fetching existing script", {
|
|
156284
|
-
deploymentId,
|
|
156285
|
-
namespace: this.config.dispatchNamespace
|
|
156286
|
-
});
|
|
156287
|
-
finalCode = await this.client.workers.getScriptContent(this.config.dispatchNamespace, deploymentId);
|
|
156288
|
-
} catch {
|
|
156289
|
-
log2.debug("[CloudflareProvider] No existing script, using stub worker", {
|
|
156290
|
-
deploymentId
|
|
156291
|
-
});
|
|
156292
|
-
finalCode = await getStubWorkerCode();
|
|
156293
|
-
}
|
|
157355
|
+
throw new Error(`No worker code provided for deployment. ` + `Frontend-only deployments should include a stub worker from the CLI.`);
|
|
156294
157356
|
}
|
|
157357
|
+
const finalCode = code;
|
|
156295
157358
|
if (opts.keepAssets === undefined) {
|
|
156296
157359
|
opts.keepAssets = true;
|
|
156297
157360
|
}
|
|
156298
|
-
|
|
156299
|
-
let finalAssetsPath = opts.assetsPath;
|
|
156300
|
-
if (opts.assetsZip && !opts.assetsPath) {
|
|
156301
|
-
tempDir = join5(tmpdir(), `playcademy-assets-${Date.now()}`);
|
|
156302
|
-
finalAssetsPath = join5(tempDir, "dist");
|
|
156303
|
-
await mkdir(finalAssetsPath, { recursive: true });
|
|
156304
|
-
await writeFile(join5(tempDir, "temp.zip"), opts.assetsZip);
|
|
156305
|
-
execSync(`unzip -q ${join5(tempDir, "temp.zip")} -d ${finalAssetsPath}`);
|
|
156306
|
-
log2.debug("[CloudflareProvider] Extracted ZIP to temp directory", {
|
|
156307
|
-
tempDir,
|
|
156308
|
-
size: opts.assetsZip.length
|
|
156309
|
-
});
|
|
156310
|
-
}
|
|
157361
|
+
const finalAssetsPath = opts.assetsPath;
|
|
156311
157362
|
log2.info("[CloudflareProvider] Deploying worker to dispatch namespace", {
|
|
156312
157363
|
deploymentId,
|
|
156313
157364
|
namespace: this.config.dispatchNamespace,
|
|
@@ -156436,10 +157487,6 @@ class CloudflareProvider {
|
|
|
156436
157487
|
error: error2
|
|
156437
157488
|
});
|
|
156438
157489
|
throw new Error(`Failed to deploy to Cloudflare Workers for Platforms: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
156439
|
-
} finally {
|
|
156440
|
-
if (tempDir) {
|
|
156441
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
156442
|
-
}
|
|
156443
157490
|
}
|
|
156444
157491
|
}
|
|
156445
157492
|
async delete(deploymentId, options) {
|
|
@@ -156519,6 +157566,8 @@ class CloudflareProvider {
|
|
|
156519
157566
|
});
|
|
156520
157567
|
}
|
|
156521
157568
|
}
|
|
157569
|
+
// ../cloudflare/src/utils/bundler.ts
|
|
157570
|
+
var esbuild = __toESM(require_main(), 1);
|
|
156522
157571
|
// ../api-core/src/utils/cloudflare.ts
|
|
156523
157572
|
init_src();
|
|
156524
157573
|
var cloudflareProvider = null;
|
|
@@ -156815,20 +157864,6 @@ async function seedCoreGames(db) {
|
|
|
156815
157864
|
console.error(`Error seeding core game '${gameData.slug}':`, error2);
|
|
156816
157865
|
}
|
|
156817
157866
|
}
|
|
156818
|
-
if (config.timeback.courseId && hasTimebackCredentials()) {
|
|
156819
|
-
try {
|
|
156820
|
-
await db.insert(gameTimebackIntegrations).values({
|
|
156821
|
-
id: crypto.randomUUID(),
|
|
156822
|
-
gameId: CORE_GAME_UUIDS.PLAYGROUND,
|
|
156823
|
-
courseId: config.timeback.courseId,
|
|
156824
|
-
lastVerifiedAt: null,
|
|
156825
|
-
createdAt: now2,
|
|
156826
|
-
updatedAt: now2
|
|
156827
|
-
}).onConflictDoNothing();
|
|
156828
|
-
} catch (error2) {
|
|
156829
|
-
console.error("Error seeding TimeBack integration for playground:", error2);
|
|
156830
|
-
}
|
|
156831
|
-
}
|
|
156832
157867
|
}
|
|
156833
157868
|
async function seedCurrentProjectGame(db, project) {
|
|
156834
157869
|
const now2 = new Date;
|
|
@@ -156857,19 +157892,6 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
156857
157892
|
updatedAt: now2
|
|
156858
157893
|
};
|
|
156859
157894
|
const [newGame] = await db.insert(games).values(gameRecord).returning();
|
|
156860
|
-
if (config.timeback.courseId && hasTimebackCredentials()) {
|
|
156861
|
-
await db.insert(gameTimebackIntegrations).values({
|
|
156862
|
-
id: crypto.randomUUID(),
|
|
156863
|
-
gameId: newGame.id,
|
|
156864
|
-
courseId: config.timeback.courseId,
|
|
156865
|
-
lastVerifiedAt: null,
|
|
156866
|
-
createdAt: now2,
|
|
156867
|
-
updatedAt: now2
|
|
156868
|
-
}).onConflictDoNothing();
|
|
156869
|
-
} else if (hasTimebackCredentials() && !config.timeback.courseId) {
|
|
156870
|
-
logger3.info(" ⚠ TimeBack credentials found but SANDBOX_TIMEBACK_COURSE_ID not set");
|
|
156871
|
-
logger3.info(" Set SANDBOX_TIMEBACK_COURSE_ID to your real TimeBack course ID");
|
|
156872
|
-
}
|
|
156873
157895
|
return newGame;
|
|
156874
157896
|
} catch (error2) {
|
|
156875
157897
|
console.error("❌ Error seeding project game:", error2);
|
|
@@ -156931,13 +157953,26 @@ async function seedSpriteTemplates(db) {
|
|
|
156931
157953
|
}
|
|
156932
157954
|
}
|
|
156933
157955
|
|
|
157956
|
+
// src/database/seed/timeback.ts
|
|
157957
|
+
function resolveStudentId(studentId) {
|
|
157958
|
+
if (!studentId)
|
|
157959
|
+
return null;
|
|
157960
|
+
if (studentId === "mock")
|
|
157961
|
+
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
157962
|
+
return studentId;
|
|
157963
|
+
}
|
|
157964
|
+
function getAdminTimebackId() {
|
|
157965
|
+
return resolveStudentId(config.timeback.studentId);
|
|
157966
|
+
}
|
|
157967
|
+
|
|
156934
157968
|
// src/database/seed/index.ts
|
|
156935
157969
|
async function seedDemoData(db) {
|
|
156936
157970
|
try {
|
|
157971
|
+
const adminTimebackId = getAdminTimebackId();
|
|
156937
157972
|
for (const [role, user] of Object.entries(DEMO_USERS)) {
|
|
156938
157973
|
const userValues = {
|
|
156939
157974
|
...user,
|
|
156940
|
-
timebackId: role === "admin"
|
|
157975
|
+
timebackId: role === "admin" ? adminTimebackId : null
|
|
156941
157976
|
};
|
|
156942
157977
|
await db.insert(users).values(userValues).onConflictDoNothing();
|
|
156943
157978
|
}
|
|
@@ -157212,6 +158247,12 @@ async function applyForDeveloperStatus(ctx) {
|
|
|
157212
158247
|
userId: user.id,
|
|
157213
158248
|
newStatus: "pending"
|
|
157214
158249
|
});
|
|
158250
|
+
sendDeveloperApplicationAlert(fullUser).catch((error2) => {
|
|
158251
|
+
log2.error("[API] Failed to send Discord notification for developer application", {
|
|
158252
|
+
userId: user.id,
|
|
158253
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
158254
|
+
});
|
|
158255
|
+
});
|
|
157215
158256
|
} catch (error2) {
|
|
157216
158257
|
log2.error(`Error updating developer status for user ${user.id}:`, {
|
|
157217
158258
|
error: error2
|
|
@@ -161952,6 +162993,68 @@ var NotificationStatsSchema = exports_external2.object({
|
|
|
161952
162993
|
startDate: exports_external2.date().optional(),
|
|
161953
162994
|
endDate: exports_external2.date().optional()
|
|
161954
162995
|
});
|
|
162996
|
+
// ../data/src/domains/timeback/schemas.ts
|
|
162997
|
+
var InsertTimebackDailyXpSchema = createInsertSchema(timebackDailyXp, {
|
|
162998
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number"),
|
|
162999
|
+
date: exports_external2.date()
|
|
163000
|
+
}).omit({ createdAt: true, updatedAt: true });
|
|
163001
|
+
var SelectTimebackDailyXpSchema = createSelectSchema(timebackDailyXp);
|
|
163002
|
+
var UpdateTimebackDailyXpSchema = createUpdateSchema(timebackDailyXp, {
|
|
163003
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number")
|
|
163004
|
+
}).omit({ userId: true, createdAt: true });
|
|
163005
|
+
var UpdateTimebackXpRequestSchema = exports_external2.object({
|
|
163006
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number"),
|
|
163007
|
+
userTimestamp: exports_external2.string().datetime().optional()
|
|
163008
|
+
});
|
|
163009
|
+
var TimebackDateQuerySchema = exports_external2.object({
|
|
163010
|
+
date: exports_external2.string().datetime().optional()
|
|
163011
|
+
});
|
|
163012
|
+
var TimebackHistoryQuerySchema = exports_external2.object({
|
|
163013
|
+
startDate: exports_external2.string().date().optional(),
|
|
163014
|
+
endDate: exports_external2.string().date().optional()
|
|
163015
|
+
});
|
|
163016
|
+
var InsertTimebackXpEventSchema = createInsertSchema(timebackXpEvents, {
|
|
163017
|
+
occurredAt: exports_external2.date(),
|
|
163018
|
+
xpDelta: exports_external2.number()
|
|
163019
|
+
}).omit({ createdAt: true, updatedAt: true });
|
|
163020
|
+
var SelectTimebackXpEventSchema = createSelectSchema(timebackXpEvents);
|
|
163021
|
+
var InsertGameTimebackIntegrationSchema = createInsertSchema(gameTimebackIntegrations, {
|
|
163022
|
+
gameId: exports_external2.string().uuid(),
|
|
163023
|
+
courseId: exports_external2.string().min(1),
|
|
163024
|
+
grade: exports_external2.number().int(),
|
|
163025
|
+
subject: exports_external2.string().min(1),
|
|
163026
|
+
totalXp: exports_external2.number().int().positive().optional()
|
|
163027
|
+
}).omit({ id: true, createdAt: true, updatedAt: true });
|
|
163028
|
+
var SelectGameTimebackIntegrationSchema = createSelectSchema(gameTimebackIntegrations);
|
|
163029
|
+
var UpdateGameTimebackIntegrationSchema = createUpdateSchema(gameTimebackIntegrations, {
|
|
163030
|
+
courseId: exports_external2.string().min(1).optional(),
|
|
163031
|
+
totalXp: exports_external2.number().int().positive().optional(),
|
|
163032
|
+
lastVerifiedAt: exports_external2.date().optional()
|
|
163033
|
+
}).omit({ id: true, gameId: true, grade: true, subject: true, createdAt: true });
|
|
163034
|
+
var EndActivityRequestSchema = exports_external2.object({
|
|
163035
|
+
gameId: exports_external2.string().uuid(),
|
|
163036
|
+
studentId: exports_external2.string().min(1),
|
|
163037
|
+
activityData: exports_external2.object({
|
|
163038
|
+
activityId: exports_external2.string().min(1),
|
|
163039
|
+
activityName: exports_external2.string().optional(),
|
|
163040
|
+
grade: exports_external2.number().int(),
|
|
163041
|
+
subject: exports_external2.string().min(1),
|
|
163042
|
+
appName: exports_external2.string().optional(),
|
|
163043
|
+
sensorUrl: exports_external2.string().url().optional(),
|
|
163044
|
+
courseId: exports_external2.string().optional(),
|
|
163045
|
+
courseName: exports_external2.string().optional(),
|
|
163046
|
+
studentEmail: exports_external2.string().email().optional()
|
|
163047
|
+
}),
|
|
163048
|
+
scoreData: exports_external2.object({
|
|
163049
|
+
correctQuestions: exports_external2.number().int().min(0),
|
|
163050
|
+
totalQuestions: exports_external2.number().int().min(0)
|
|
163051
|
+
}),
|
|
163052
|
+
timingData: exports_external2.object({
|
|
163053
|
+
durationSeconds: exports_external2.number().positive()
|
|
163054
|
+
}),
|
|
163055
|
+
xpEarned: exports_external2.number().optional(),
|
|
163056
|
+
masteredUnits: exports_external2.number().nonnegative().optional()
|
|
163057
|
+
});
|
|
161955
163058
|
// ../api-core/src/handlers/games/leaderboard.ts
|
|
161956
163059
|
init_src();
|
|
161957
163060
|
async function submitScore(ctx) {
|
|
@@ -162120,6 +163223,26 @@ async function getUserAllScores(ctx) {
|
|
|
162120
163223
|
throw ApiError.internal("Failed to fetch user scores");
|
|
162121
163224
|
}
|
|
162122
163225
|
}
|
|
163226
|
+
async function getUserScores(ctx) {
|
|
163227
|
+
const user = ctx.user;
|
|
163228
|
+
if (!user) {
|
|
163229
|
+
throw ApiError.unauthorized("Must be logged in to view user scores");
|
|
163230
|
+
}
|
|
163231
|
+
const { params, url } = ctx;
|
|
163232
|
+
const gameId = params.gameId;
|
|
163233
|
+
const userId = params.userId;
|
|
163234
|
+
if (!gameId || !userId) {
|
|
163235
|
+
throw ApiError.badRequest("Game ID and User ID are required");
|
|
163236
|
+
}
|
|
163237
|
+
const limit2 = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
|
|
163238
|
+
const db = getDatabase();
|
|
163239
|
+
try {
|
|
163240
|
+
const scores = await db.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(limit2);
|
|
163241
|
+
return scores;
|
|
163242
|
+
} catch {
|
|
163243
|
+
throw ApiError.internal("Failed to fetch user scores");
|
|
163244
|
+
}
|
|
163245
|
+
}
|
|
162123
163246
|
|
|
162124
163247
|
// ../api-core/src/handlers/levels/index.ts
|
|
162125
163248
|
init_src();
|
|
@@ -162256,9 +163379,11 @@ async function getUserMe(ctx) {
|
|
|
162256
163379
|
const timebackAccount = await db.query.accounts.findFirst({
|
|
162257
163380
|
where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
|
|
162258
163381
|
});
|
|
163382
|
+
const timeback3 = userData.timebackId ? await fetchUserTimebackData(userData.timebackId) : undefined;
|
|
162259
163383
|
return {
|
|
162260
163384
|
...userData,
|
|
162261
|
-
hasTimebackAccount: !!timebackAccount
|
|
163385
|
+
hasTimebackAccount: !!timebackAccount,
|
|
163386
|
+
timeback: timeback3
|
|
162262
163387
|
};
|
|
162263
163388
|
} catch (error2) {
|
|
162264
163389
|
if (error2 instanceof ApiError)
|
|
@@ -162788,7 +163913,7 @@ async function deleteGame(ctx) {
|
|
|
162788
163913
|
const db = getDatabase();
|
|
162789
163914
|
const gameToDelete = await db.query.games.findFirst({
|
|
162790
163915
|
where: eq(games.id, gameId),
|
|
162791
|
-
columns: { id: true, slug: true }
|
|
163916
|
+
columns: { id: true, slug: true, displayName: true }
|
|
162792
163917
|
});
|
|
162793
163918
|
if (!gameToDelete || !gameToDelete.slug) {
|
|
162794
163919
|
throw ApiError.notFound("Game not found for deletion");
|
|
@@ -162810,6 +163935,16 @@ async function deleteGame(ctx) {
|
|
|
162810
163935
|
hadActiveDeployment: !!activeDeployment,
|
|
162811
163936
|
customDomainsCount: customDomains.length
|
|
162812
163937
|
});
|
|
163938
|
+
sendGameDeletionAlert({
|
|
163939
|
+
slug: gameToDelete.slug,
|
|
163940
|
+
displayName: gameToDelete.displayName,
|
|
163941
|
+
developer: {
|
|
163942
|
+
id: user.id,
|
|
163943
|
+
email: user.email
|
|
163944
|
+
}
|
|
163945
|
+
}).catch((err2) => {
|
|
163946
|
+
log2.warn("[API] Failed to send Discord game deletion notification", { err: err2 });
|
|
163947
|
+
});
|
|
162813
163948
|
if (activeDeployment?.provider === "cloudflare") {
|
|
162814
163949
|
try {
|
|
162815
163950
|
const cloudflare2 = getCloudflareProvider();
|
|
@@ -164391,6 +165526,26 @@ gameScoresRouter.post("/:gameId/scores", async (c3) => {
|
|
|
164391
165526
|
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
164392
165527
|
}
|
|
164393
165528
|
});
|
|
165529
|
+
gameScoresRouter.get("/:gameId/users/:userId/scores", async (c3) => {
|
|
165530
|
+
const gameId = c3.req.param("gameId");
|
|
165531
|
+
const userId = c3.req.param("userId");
|
|
165532
|
+
const ctx = {
|
|
165533
|
+
user: c3.get("user"),
|
|
165534
|
+
params: { gameId, userId },
|
|
165535
|
+
url: new URL(c3.req.url),
|
|
165536
|
+
request: c3.req.raw
|
|
165537
|
+
};
|
|
165538
|
+
try {
|
|
165539
|
+
const result = await getUserScores(ctx);
|
|
165540
|
+
return c3.json(result, 200);
|
|
165541
|
+
} catch (error2) {
|
|
165542
|
+
if (error2 instanceof ApiError) {
|
|
165543
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
165544
|
+
}
|
|
165545
|
+
console.error("Error in getUserScores:", error2);
|
|
165546
|
+
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
165547
|
+
}
|
|
165548
|
+
});
|
|
164394
165549
|
|
|
164395
165550
|
// ../api-core/src/handlers/games/sessions.ts
|
|
164396
165551
|
init_src();
|
|
@@ -167178,39 +168333,21 @@ async function endActivity(ctx) {
|
|
|
167178
168333
|
throw ApiError.unauthorized("Must be logged in to end activity");
|
|
167179
168334
|
}
|
|
167180
168335
|
log2.debug("[API] Ending activity", { userId: user.id });
|
|
167181
|
-
|
|
167182
|
-
|
|
167183
|
-
|
|
167184
|
-
|
|
167185
|
-
} catch (error2) {
|
|
167186
|
-
log2.error("[API] Failed to parse request body:", { error: error2 });
|
|
167187
|
-
throw ApiError.badRequest("Invalid JSON body");
|
|
167188
|
-
}
|
|
167189
|
-
const { gameId, studentId, activityData, scoreData, timingData, xpEarned } = request3;
|
|
167190
|
-
if (!activityData?.activityId) {
|
|
167191
|
-
throw ApiError.badRequest("activityId is required");
|
|
167192
|
-
}
|
|
167193
|
-
if (typeof scoreData?.correctQuestions !== "number" || typeof scoreData?.totalQuestions !== "number") {
|
|
167194
|
-
throw ApiError.badRequest("correctQuestions and totalQuestions are required");
|
|
167195
|
-
}
|
|
167196
|
-
if (typeof timingData?.durationSeconds !== "number") {
|
|
167197
|
-
throw ApiError.badRequest("durationSeconds is required");
|
|
168336
|
+
const requestBody = await ctx.request.json();
|
|
168337
|
+
const validation2 = EndActivityRequestSchema.safeParse(requestBody);
|
|
168338
|
+
if (!validation2.success) {
|
|
168339
|
+
throw ApiError.badRequest(formatValidationErrors(validation2.error));
|
|
167198
168340
|
}
|
|
168341
|
+
const { gameId, studentId, activityData, scoreData, timingData, xpEarned, masteredUnits } = validation2.data;
|
|
168342
|
+
await verifyGameAccessById(gameId, user);
|
|
167199
168343
|
const db = getDatabase();
|
|
167200
|
-
const
|
|
167201
|
-
where: eq(games.id, gameId)
|
|
167202
|
-
});
|
|
167203
|
-
if (!game) {
|
|
167204
|
-
throw ApiError.notFound("Game not found");
|
|
167205
|
-
}
|
|
167206
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167207
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167208
|
-
}
|
|
168344
|
+
const { grade, subject } = activityData;
|
|
167209
168345
|
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
167210
|
-
where: eq(
|
|
168346
|
+
where: (table14, { eq: eq3, and: and3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, grade), eq3(table14.subject, subject))
|
|
167211
168347
|
});
|
|
167212
168348
|
if (!integration) {
|
|
167213
|
-
|
|
168349
|
+
log2.error("[API] No TimeBack integration found", { gameId, grade, subject });
|
|
168350
|
+
throw ApiError.notFound(`No TimeBack integration found for this game (grade ${grade}, subject ${subject}). Run setup first.`);
|
|
167214
168351
|
}
|
|
167215
168352
|
const client2 = await getTimebackClient();
|
|
167216
168353
|
try {
|
|
@@ -167221,6 +168358,7 @@ async function endActivity(ctx) {
|
|
|
167221
168358
|
correctQuestions: scoreData.correctQuestions,
|
|
167222
168359
|
sessionDurationSeconds: timingData.durationSeconds,
|
|
167223
168360
|
xpEarned,
|
|
168361
|
+
masteredUnits,
|
|
167224
168362
|
activityId: activityData.activityId,
|
|
167225
168363
|
activityName: activityData.activityName,
|
|
167226
168364
|
subject: activityData.subject,
|
|
@@ -167228,8 +168366,11 @@ async function endActivity(ctx) {
|
|
|
167228
168366
|
sensorUrl: activityData.sensorUrl,
|
|
167229
168367
|
courseId: activityData.courseId,
|
|
167230
168368
|
courseName: activityData.courseName,
|
|
167231
|
-
studentEmail: activityData.studentEmail
|
|
168369
|
+
studentEmail: activityData.studentEmail,
|
|
168370
|
+
courseTotalXp: integration.totalXp
|
|
167232
168371
|
});
|
|
168372
|
+
const studentData = await client2.resolveStudent(studentId);
|
|
168373
|
+
const studentEmail = studentData.email;
|
|
167233
168374
|
await client2.recordSessionEnd(integration.courseId, studentId, {
|
|
167234
168375
|
activeTimeSeconds: timingData.durationSeconds,
|
|
167235
168376
|
activityId: activityData.activityId,
|
|
@@ -167245,17 +168386,26 @@ async function endActivity(ctx) {
|
|
|
167245
168386
|
gameId,
|
|
167246
168387
|
courseId: integration.courseId,
|
|
167247
168388
|
studentId,
|
|
168389
|
+
studentEmail,
|
|
167248
168390
|
activityId: activityData.activityId,
|
|
167249
168391
|
score: scorePercentage,
|
|
167250
168392
|
durationSeconds: timingData.durationSeconds,
|
|
167251
168393
|
attemptNumber: result.attemptNumber,
|
|
167252
168394
|
isFirstAttempt: result.attemptNumber === 1,
|
|
167253
|
-
xpAwarded: result.xpAwarded
|
|
168395
|
+
xpAwarded: result.xpAwarded,
|
|
168396
|
+
masteredUnits,
|
|
168397
|
+
pctCompleteApp: result.pctCompleteApp,
|
|
168398
|
+
scoreStatus: result.scoreStatus,
|
|
168399
|
+
inProgress: result.inProgress
|
|
167254
168400
|
});
|
|
167255
168401
|
return {
|
|
167256
168402
|
status: "ok",
|
|
167257
168403
|
courseId: integration.courseId,
|
|
167258
|
-
xpAwarded: result.xpAwarded
|
|
168404
|
+
xpAwarded: result.xpAwarded,
|
|
168405
|
+
masteredUnits: result.masteredUnitsApplied,
|
|
168406
|
+
pctCompleteApp: result.pctCompleteApp,
|
|
168407
|
+
scoreStatus: result.scoreStatus,
|
|
168408
|
+
inProgress: result.inProgress
|
|
167259
168409
|
};
|
|
167260
168410
|
} catch (error2) {
|
|
167261
168411
|
logTimebackError2("end activity", error2, {
|
|
@@ -167282,68 +168432,160 @@ async function setupTimebackIntegration(ctx) {
|
|
|
167282
168432
|
log2.error("Failed to parse request body:", { error: error2 });
|
|
167283
168433
|
throw ApiError.badRequest("Invalid JSON body");
|
|
167284
168434
|
}
|
|
167285
|
-
const { gameId,
|
|
168435
|
+
const { gameId, courses, baseConfig, verbose } = request3;
|
|
167286
168436
|
log2.debug("[API] TimeBack setup request", {
|
|
167287
168437
|
gameId,
|
|
167288
|
-
|
|
168438
|
+
courseCount: courses.length
|
|
167289
168439
|
});
|
|
168440
|
+
await verifyGameAccessById(gameId, user);
|
|
167290
168441
|
const db = getDatabase();
|
|
167291
|
-
const
|
|
167292
|
-
where: eq(games.id, gameId)
|
|
167293
|
-
});
|
|
167294
|
-
if (!game) {
|
|
167295
|
-
throw ApiError.notFound("Game not found");
|
|
167296
|
-
}
|
|
167297
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167298
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167299
|
-
}
|
|
167300
|
-
const existing = await db.query.gameTimebackIntegrations.findFirst({
|
|
168442
|
+
const existing = await db.query.gameTimebackIntegrations.findMany({
|
|
167301
168443
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
167302
168444
|
});
|
|
167303
168445
|
const client2 = await getTimebackClient();
|
|
167304
|
-
|
|
167305
|
-
|
|
167306
|
-
|
|
167307
|
-
|
|
167308
|
-
|
|
167309
|
-
|
|
167310
|
-
|
|
167311
|
-
|
|
167312
|
-
|
|
167313
|
-
|
|
167314
|
-
|
|
167315
|
-
|
|
167316
|
-
|
|
167317
|
-
|
|
167318
|
-
|
|
167319
|
-
|
|
167320
|
-
|
|
168446
|
+
const integrations = [];
|
|
168447
|
+
const verboseData = [];
|
|
168448
|
+
for (const courseConfig of courses) {
|
|
168449
|
+
const {
|
|
168450
|
+
subject,
|
|
168451
|
+
grade,
|
|
168452
|
+
title,
|
|
168453
|
+
courseCode,
|
|
168454
|
+
level,
|
|
168455
|
+
metadata: metadata2,
|
|
168456
|
+
totalXp: derivedTotalXp,
|
|
168457
|
+
masterableUnits: derivedMasterableUnits
|
|
168458
|
+
} = courseConfig;
|
|
168459
|
+
if (!isTimebackSubject2(subject)) {
|
|
168460
|
+
log2.error("[API] Invalid subject in TimeBack request", { subject });
|
|
168461
|
+
throw ApiError.badRequest(`Invalid subject "${subject}" provided for course "${title}".`);
|
|
168462
|
+
}
|
|
168463
|
+
if (!isTimebackGrade2(grade)) {
|
|
168464
|
+
log2.error("[API] Invalid grade in TimeBack request", { grade });
|
|
168465
|
+
throw ApiError.badRequest(`Invalid grade "${grade}" provided for course "${title}".`);
|
|
168466
|
+
}
|
|
168467
|
+
const courseSubject = subject;
|
|
168468
|
+
const courseGrade = grade;
|
|
168469
|
+
const courseMetadata = isCourseMetadata(metadata2) ? metadata2 : undefined;
|
|
168470
|
+
const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
|
|
168471
|
+
const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata2(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
|
|
168472
|
+
if (typeof totalXp !== "number") {
|
|
168473
|
+
log2.error("[API] Missing totalXp in course metadata", { gameId, grade, subject, title });
|
|
168474
|
+
throw ApiError.badRequest(`Course "${title}" (grade ${grade}, subject ${subject}) is missing \`metadata.metrics.totalXp\``);
|
|
168475
|
+
}
|
|
168476
|
+
const suffix = baseConfig.component.titleSuffix || "";
|
|
168477
|
+
const applySuffix = (text5) => suffix ? `${text5} ${suffix}` : text5;
|
|
168478
|
+
const fullConfig = {
|
|
168479
|
+
organization: baseConfig.organization,
|
|
168480
|
+
course: {
|
|
168481
|
+
title,
|
|
168482
|
+
subjects: [courseSubject],
|
|
168483
|
+
grades: [courseGrade],
|
|
168484
|
+
courseCode,
|
|
168485
|
+
level,
|
|
168486
|
+
gradingScheme: "STANDARD",
|
|
168487
|
+
metadata: metadata2
|
|
168488
|
+
},
|
|
168489
|
+
component: {
|
|
168490
|
+
...baseConfig.component,
|
|
168491
|
+
title: applySuffix(baseConfig.component.title || `${title} Activities`)
|
|
168492
|
+
},
|
|
168493
|
+
resource: {
|
|
168494
|
+
...baseConfig.resource,
|
|
168495
|
+
title: applySuffix(baseConfig.resource.title || `${title} Game`),
|
|
168496
|
+
metadata: buildResourceMetadata({
|
|
168497
|
+
baseMetadata: baseConfig.resource.metadata,
|
|
168498
|
+
subject: courseSubject,
|
|
168499
|
+
grade: courseGrade,
|
|
168500
|
+
totalXp,
|
|
168501
|
+
masterableUnits
|
|
168502
|
+
})
|
|
168503
|
+
},
|
|
168504
|
+
componentResource: {
|
|
168505
|
+
...baseConfig.componentResource,
|
|
168506
|
+
title: applySuffix(baseConfig.componentResource.title || "")
|
|
168507
|
+
}
|
|
167321
168508
|
};
|
|
167322
|
-
|
|
167323
|
-
|
|
167324
|
-
|
|
167325
|
-
|
|
167326
|
-
|
|
167327
|
-
|
|
167328
|
-
|
|
167329
|
-
|
|
167330
|
-
|
|
167331
|
-
|
|
167332
|
-
|
|
167333
|
-
|
|
167334
|
-
|
|
167335
|
-
|
|
167336
|
-
|
|
168509
|
+
const existingIntegration = existing.find((i4) => i4.grade === grade && i4.subject === subject);
|
|
168510
|
+
if (existingIntegration) {
|
|
168511
|
+
try {
|
|
168512
|
+
await client2.update(existingIntegration.courseId, fullConfig);
|
|
168513
|
+
} catch (error2) {
|
|
168514
|
+
logTimebackError2("update", error2, {
|
|
168515
|
+
gameId,
|
|
168516
|
+
grade,
|
|
168517
|
+
subject,
|
|
168518
|
+
courseId: existingIntegration.courseId
|
|
168519
|
+
});
|
|
168520
|
+
throw error2;
|
|
168521
|
+
}
|
|
168522
|
+
const [updated] = await db.update(gameTimebackIntegrations).set({
|
|
168523
|
+
totalXp,
|
|
168524
|
+
updatedAt: new Date
|
|
168525
|
+
}).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
|
|
168526
|
+
if (!updated) {
|
|
168527
|
+
log2.error("[API] Failed to update TimeBack integration record", {
|
|
168528
|
+
gameId,
|
|
168529
|
+
grade,
|
|
168530
|
+
subject,
|
|
168531
|
+
integrationId: existingIntegration.id
|
|
168532
|
+
});
|
|
168533
|
+
throw ApiError.internal("Failed to update TimeBack integration record");
|
|
168534
|
+
}
|
|
168535
|
+
integrations.push(updated);
|
|
168536
|
+
log2.info("[API] Updated TimeBack course", {
|
|
168537
|
+
gameId,
|
|
168538
|
+
courseId: updated.courseId,
|
|
168539
|
+
grade,
|
|
168540
|
+
subject
|
|
168541
|
+
});
|
|
168542
|
+
} else {
|
|
168543
|
+
let result;
|
|
168544
|
+
try {
|
|
168545
|
+
result = await client2.setup(fullConfig, { verbose });
|
|
168546
|
+
} catch (error2) {
|
|
168547
|
+
logTimebackError2("setup", error2, { gameId, grade, subject });
|
|
168548
|
+
throw error2;
|
|
168549
|
+
}
|
|
168550
|
+
const integrationData = {
|
|
168551
|
+
gameId,
|
|
168552
|
+
courseId: result.courseId,
|
|
168553
|
+
grade,
|
|
168554
|
+
subject,
|
|
168555
|
+
totalXp
|
|
168556
|
+
};
|
|
168557
|
+
const [integration] = await db.insert(gameTimebackIntegrations).values(integrationData).returning();
|
|
168558
|
+
if (!integration) {
|
|
168559
|
+
log2.error("[API] Failed to create TimeBack integration record", {
|
|
168560
|
+
gameId,
|
|
168561
|
+
grade,
|
|
168562
|
+
subject,
|
|
168563
|
+
courseId: result.courseId
|
|
168564
|
+
});
|
|
168565
|
+
throw ApiError.internal("Failed to create TimeBack integration record");
|
|
168566
|
+
}
|
|
168567
|
+
integrations.push(integration);
|
|
168568
|
+
if (verbose && result.verboseData) {
|
|
168569
|
+
verboseData.push({
|
|
168570
|
+
integration,
|
|
168571
|
+
config: result.verboseData
|
|
168572
|
+
});
|
|
168573
|
+
}
|
|
168574
|
+
log2.info("[API] Created TimeBack course", {
|
|
168575
|
+
gameId,
|
|
168576
|
+
courseId: integration.courseId,
|
|
168577
|
+
grade,
|
|
168578
|
+
subject
|
|
168579
|
+
});
|
|
168580
|
+
}
|
|
167337
168581
|
}
|
|
167338
168582
|
log2.info("[API] Created TimeBack integration", {
|
|
167339
168583
|
gameId,
|
|
167340
|
-
|
|
167341
|
-
...result.verboseData && { verbose: result.verboseData }
|
|
168584
|
+
courseCount: integrations.length
|
|
167342
168585
|
});
|
|
167343
168586
|
return {
|
|
167344
|
-
|
|
167345
|
-
|
|
167346
|
-
...result.verboseData && { verbose: result.verboseData }
|
|
168587
|
+
integrations,
|
|
168588
|
+
...verbose && verboseData.length > 0 && { verbose: verboseData }
|
|
167347
168589
|
};
|
|
167348
168590
|
}
|
|
167349
168591
|
async function getTimebackIntegration(ctx) {
|
|
@@ -167355,21 +168597,13 @@ async function getTimebackIntegration(ctx) {
|
|
|
167355
168597
|
if (!gameId) {
|
|
167356
168598
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
167357
168599
|
}
|
|
167358
|
-
log2.debug("[API] Getting TimeBack
|
|
168600
|
+
log2.debug("[API] Getting TimeBack integrations", { userId: user.id, gameId });
|
|
168601
|
+
await verifyGameAccessById(gameId, user);
|
|
167359
168602
|
const db = getDatabase();
|
|
167360
|
-
const
|
|
167361
|
-
where: eq(games.id, gameId)
|
|
167362
|
-
});
|
|
167363
|
-
if (!game) {
|
|
167364
|
-
throw ApiError.notFound("Game not found");
|
|
167365
|
-
}
|
|
167366
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167367
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167368
|
-
}
|
|
167369
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
168603
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
167370
168604
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
167371
168605
|
});
|
|
167372
|
-
return
|
|
168606
|
+
return integrations;
|
|
167373
168607
|
}
|
|
167374
168608
|
async function verifyTimebackIntegration(ctx) {
|
|
167375
168609
|
const user = ctx.user;
|
|
@@ -167381,39 +168615,40 @@ async function verifyTimebackIntegration(ctx) {
|
|
|
167381
168615
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
167382
168616
|
}
|
|
167383
168617
|
log2.debug("[API] Verifying TimeBack integration", { userId: user.id, gameId });
|
|
168618
|
+
await verifyGameAccessById(gameId, user);
|
|
167384
168619
|
const db = getDatabase();
|
|
167385
|
-
const
|
|
167386
|
-
where: eq(games.id, gameId)
|
|
167387
|
-
});
|
|
167388
|
-
if (!game) {
|
|
167389
|
-
throw ApiError.notFound("Game not found");
|
|
167390
|
-
}
|
|
167391
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167392
|
-
log2.error("[API] User does not own game", { userId: user.id, gameId });
|
|
167393
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167394
|
-
}
|
|
167395
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
168620
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
167396
168621
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
167397
168622
|
});
|
|
167398
|
-
if (
|
|
168623
|
+
if (integrations.length === 0) {
|
|
168624
|
+
log2.error("[API] No TimeBack integration found", { gameId });
|
|
167399
168625
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
167400
168626
|
}
|
|
167401
168627
|
const client2 = await getTimebackClient();
|
|
167402
|
-
const
|
|
167403
|
-
const
|
|
167404
|
-
|
|
167405
|
-
|
|
167406
|
-
|
|
167407
|
-
|
|
168628
|
+
const now2 = new Date;
|
|
168629
|
+
const results = await Promise.all(integrations.map(async (integration) => {
|
|
168630
|
+
const resources = await client2.verify(integration.courseId);
|
|
168631
|
+
const resourceValues = Object.values(resources);
|
|
168632
|
+
const allFound = resourceValues.every((r3) => r3.found);
|
|
168633
|
+
const errors3 = Object.entries(resources).filter(([_5, r3]) => !r3.found).map(([name4]) => `${name4} not found`);
|
|
168634
|
+
const status = allFound ? "success" : "error";
|
|
168635
|
+
return {
|
|
168636
|
+
integration: { ...integration, lastVerifiedAt: now2 },
|
|
168637
|
+
resources,
|
|
168638
|
+
status,
|
|
168639
|
+
...errors3.length > 0 && { errors: errors3 }
|
|
168640
|
+
};
|
|
168641
|
+
}));
|
|
168642
|
+
await db.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
168643
|
+
const overallSuccess = results.every((r3) => r3.status === "success");
|
|
168644
|
+
log2.info("[API] Verified TimeBack integrations", {
|
|
167408
168645
|
gameId,
|
|
167409
|
-
|
|
167410
|
-
status:
|
|
168646
|
+
courseCount: integrations.length,
|
|
168647
|
+
status: overallSuccess ? "success" : "error"
|
|
167411
168648
|
});
|
|
167412
168649
|
return {
|
|
167413
|
-
status:
|
|
167414
|
-
|
|
167415
|
-
resources,
|
|
167416
|
-
...errors3.length > 0 && { errors: errors3 }
|
|
168650
|
+
status: overallSuccess ? "success" : "error",
|
|
168651
|
+
results
|
|
167417
168652
|
};
|
|
167418
168653
|
}
|
|
167419
168654
|
async function getTimebackConfig(ctx) {
|
|
@@ -167426,20 +168661,13 @@ async function getTimebackConfig(ctx) {
|
|
|
167426
168661
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
167427
168662
|
}
|
|
167428
168663
|
log2.debug("[API] Getting TimeBack config", { userId: user.id, gameId });
|
|
168664
|
+
await verifyGameAccessById(gameId, user);
|
|
167429
168665
|
const db = getDatabase();
|
|
167430
|
-
const game = await db.query.games.findFirst({
|
|
167431
|
-
where: eq(games.id, gameId)
|
|
167432
|
-
});
|
|
167433
|
-
if (!game) {
|
|
167434
|
-
throw ApiError.notFound("Game not found");
|
|
167435
|
-
}
|
|
167436
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167437
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167438
|
-
}
|
|
167439
168666
|
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
167440
168667
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
167441
168668
|
});
|
|
167442
168669
|
if (!integration) {
|
|
168670
|
+
log2.error("[API] No TimeBack integration found", { gameId });
|
|
167443
168671
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
167444
168672
|
}
|
|
167445
168673
|
const client2 = await getTimebackClient();
|
|
@@ -167454,34 +168682,40 @@ async function deleteTimebackIntegration(ctx) {
|
|
|
167454
168682
|
if (!gameId) {
|
|
167455
168683
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
167456
168684
|
}
|
|
167457
|
-
log2.debug("[API] Deleting TimeBack
|
|
168685
|
+
log2.debug("[API] Deleting TimeBack integrations", { userId: user.id, gameId });
|
|
168686
|
+
await verifyGameAccessById(gameId, user);
|
|
167458
168687
|
const db = getDatabase();
|
|
167459
|
-
const
|
|
167460
|
-
where: eq(games.id, gameId)
|
|
167461
|
-
});
|
|
167462
|
-
if (!game) {
|
|
167463
|
-
throw ApiError.notFound("Game not found");
|
|
167464
|
-
}
|
|
167465
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
167466
|
-
throw ApiError.forbidden("You do not own this game");
|
|
167467
|
-
}
|
|
167468
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
168688
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
167469
168689
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
167470
168690
|
});
|
|
167471
|
-
if (
|
|
168691
|
+
if (integrations.length === 0) {
|
|
168692
|
+
log2.error("[API] No TimeBack integrations found", { gameId });
|
|
167472
168693
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
167473
168694
|
}
|
|
167474
168695
|
const client2 = await getTimebackClient();
|
|
167475
|
-
|
|
167476
|
-
|
|
167477
|
-
|
|
167478
|
-
|
|
167479
|
-
|
|
168696
|
+
for (const integration of integrations) {
|
|
168697
|
+
try {
|
|
168698
|
+
await client2.cleanup(integration.courseId);
|
|
168699
|
+
log2.info("[API] Cleaned up TimeBack course", {
|
|
168700
|
+
gameId,
|
|
168701
|
+
courseId: integration.courseId,
|
|
168702
|
+
grade: integration.grade,
|
|
168703
|
+
subject: integration.subject
|
|
168704
|
+
});
|
|
168705
|
+
} catch (error2) {
|
|
168706
|
+
logTimebackError2("cleanup", error2, {
|
|
168707
|
+
gameId,
|
|
168708
|
+
courseId: integration.courseId,
|
|
168709
|
+
grade: integration.grade,
|
|
168710
|
+
subject: integration.subject
|
|
168711
|
+
});
|
|
168712
|
+
throw error2;
|
|
168713
|
+
}
|
|
167480
168714
|
}
|
|
167481
|
-
await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.
|
|
167482
|
-
log2.info("[API] Deleted TimeBack
|
|
168715
|
+
await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
168716
|
+
log2.info("[API] Deleted TimeBack integrations", {
|
|
167483
168717
|
gameId,
|
|
167484
|
-
|
|
168718
|
+
coursesDeleted: integrations.length
|
|
167485
168719
|
});
|
|
167486
168720
|
}
|
|
167487
168721
|
// ../api-core/src/handlers/timeback/xp.ts
|
|
@@ -167613,6 +168847,26 @@ async function getTimeBackXpHistory(ctx) {
|
|
|
167613
168847
|
throw ApiError.internal("Failed to get TimeBack XP history", error2);
|
|
167614
168848
|
}
|
|
167615
168849
|
}
|
|
168850
|
+
// ../api-core/src/handlers/timeback/enrollments.ts
|
|
168851
|
+
init_src();
|
|
168852
|
+
async function getStudentEnrollments(ctx) {
|
|
168853
|
+
const user = ctx.user;
|
|
168854
|
+
if (!user) {
|
|
168855
|
+
throw ApiError.unauthorized("Must be logged in to get enrollments");
|
|
168856
|
+
}
|
|
168857
|
+
const timebackId = ctx.params.timebackId;
|
|
168858
|
+
if (!timebackId) {
|
|
168859
|
+
throw ApiError.badRequest("Missing timebackId parameter");
|
|
168860
|
+
}
|
|
168861
|
+
log2.debug("[API] Getting student enrollments", { userId: user.id, timebackId });
|
|
168862
|
+
const enrollments = await fetchEnrollmentsForUser(timebackId);
|
|
168863
|
+
log2.info("[API] Retrieved student enrollments", {
|
|
168864
|
+
userId: user.id,
|
|
168865
|
+
timebackId,
|
|
168866
|
+
enrollmentCount: enrollments.length
|
|
168867
|
+
});
|
|
168868
|
+
return { enrollments };
|
|
168869
|
+
}
|
|
167616
168870
|
// src/routes/integrations/timeback.ts
|
|
167617
168871
|
var timebackRouter = new Hono2;
|
|
167618
168872
|
timebackRouter.post("/populate-student", async (c3) => {
|
|
@@ -167804,6 +169058,25 @@ timebackRouter.post("/end-activity", async (c3) => {
|
|
|
167804
169058
|
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
167805
169059
|
}
|
|
167806
169060
|
});
|
|
169061
|
+
timebackRouter.get("/enrollments/:timebackId", async (c3) => {
|
|
169062
|
+
const timebackId = c3.req.param("timebackId");
|
|
169063
|
+
const ctx = {
|
|
169064
|
+
user: c3.get("user"),
|
|
169065
|
+
params: { timebackId },
|
|
169066
|
+
url: new URL(c3.req.url),
|
|
169067
|
+
request: c3.req.raw
|
|
169068
|
+
};
|
|
169069
|
+
try {
|
|
169070
|
+
const result = await getStudentEnrollments(ctx);
|
|
169071
|
+
return c3.json(result);
|
|
169072
|
+
} catch (error2) {
|
|
169073
|
+
if (error2 instanceof ApiError) {
|
|
169074
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
169075
|
+
}
|
|
169076
|
+
console.error("Error in getStudentEnrollments:", error2);
|
|
169077
|
+
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
169078
|
+
}
|
|
169079
|
+
});
|
|
167807
169080
|
// ../api-core/src/handlers/lti-timeback/index.ts
|
|
167808
169081
|
import * as crypto7 from "node:crypto";
|
|
167809
169082
|
init_src();
|