@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/cli.js
CHANGED
|
@@ -60743,7 +60743,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60743
60743
|
var CAPTURE = 1;
|
|
60744
60744
|
var BUBBLE = 2;
|
|
60745
60745
|
var ATTRIBUTE = 3;
|
|
60746
|
-
function
|
|
60746
|
+
function isObject4(x6) {
|
|
60747
60747
|
return x6 !== null && typeof x6 === "object";
|
|
60748
60748
|
}
|
|
60749
60749
|
function getListeners(eventTarget) {
|
|
@@ -60767,7 +60767,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60767
60767
|
return null;
|
|
60768
60768
|
},
|
|
60769
60769
|
set(listener) {
|
|
60770
|
-
if (typeof listener !== "function" && !
|
|
60770
|
+
if (typeof listener !== "function" && !isObject4(listener)) {
|
|
60771
60771
|
listener = null;
|
|
60772
60772
|
}
|
|
60773
60773
|
const listeners = getListeners(this);
|
|
@@ -60847,11 +60847,11 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60847
60847
|
if (listener == null) {
|
|
60848
60848
|
return;
|
|
60849
60849
|
}
|
|
60850
|
-
if (typeof listener !== "function" && !
|
|
60850
|
+
if (typeof listener !== "function" && !isObject4(listener)) {
|
|
60851
60851
|
throw new TypeError("'listener' should be a function or an object.");
|
|
60852
60852
|
}
|
|
60853
60853
|
const listeners = getListeners(this);
|
|
60854
|
-
const optionsIsObj =
|
|
60854
|
+
const optionsIsObj = isObject4(options);
|
|
60855
60855
|
const capture = optionsIsObj ? Boolean(options.capture) : Boolean(options);
|
|
60856
60856
|
const listenerType = capture ? CAPTURE : BUBBLE;
|
|
60857
60857
|
const newNode = {
|
|
@@ -60881,7 +60881,7 @@ var require_event_target_shim = __commonJS((exports, module2) => {
|
|
|
60881
60881
|
return;
|
|
60882
60882
|
}
|
|
60883
60883
|
const listeners = getListeners(this);
|
|
60884
|
-
const capture =
|
|
60884
|
+
const capture = isObject4(options) ? Boolean(options.capture) : Boolean(options);
|
|
60885
60885
|
const listenerType = capture ? CAPTURE : BUBBLE;
|
|
60886
60886
|
let prev = null;
|
|
60887
60887
|
let node = listeners.get(eventName);
|
|
@@ -66891,7 +66891,7 @@ var init_block_senders = __esm(() => {
|
|
|
66891
66891
|
|
|
66892
66892
|
// ../../node_modules/cloudflare/resources/email-security/settings/domains.mjs
|
|
66893
66893
|
var Domains, DomainListResponsesV4PagePaginationArray, DomainBulkDeleteResponsesSinglePage;
|
|
66894
|
-
var
|
|
66894
|
+
var init_domains3 = __esm(() => {
|
|
66895
66895
|
init_pagination();
|
|
66896
66896
|
Domains = class Domains extends APIResource {
|
|
66897
66897
|
list(params, options) {
|
|
@@ -67001,8 +67001,8 @@ var init_settings4 = __esm(() => {
|
|
|
67001
67001
|
init_allow_policies();
|
|
67002
67002
|
init_block_senders();
|
|
67003
67003
|
init_block_senders();
|
|
67004
|
-
|
|
67005
|
-
|
|
67004
|
+
init_domains3();
|
|
67005
|
+
init_domains3();
|
|
67006
67006
|
init_impersonation_registry();
|
|
67007
67007
|
init_impersonation_registry();
|
|
67008
67008
|
init_trusted_domains();
|
|
@@ -68292,7 +68292,7 @@ var init_bulks = __esm(() => {
|
|
|
68292
68292
|
|
|
68293
68293
|
// ../../node_modules/cloudflare/resources/intel/domains/domains.mjs
|
|
68294
68294
|
var Domains2;
|
|
68295
|
-
var
|
|
68295
|
+
var init_domains4 = __esm(() => {
|
|
68296
68296
|
init_bulks();
|
|
68297
68297
|
init_bulks();
|
|
68298
68298
|
Domains2 = class Domains2 extends APIResource {
|
|
@@ -68427,8 +68427,8 @@ var init_intel = __esm(() => {
|
|
|
68427
68427
|
init_asn3();
|
|
68428
68428
|
init_attack_surface_report();
|
|
68429
68429
|
init_attack_surface_report();
|
|
68430
|
-
|
|
68431
|
-
|
|
68430
|
+
init_domains4();
|
|
68431
|
+
init_domains4();
|
|
68432
68432
|
init_indicator_feeds();
|
|
68433
68433
|
init_indicator_feeds();
|
|
68434
68434
|
Intel = class Intel extends APIResource {
|
|
@@ -71306,7 +71306,7 @@ var init_page_shield = __esm(() => {
|
|
|
71306
71306
|
|
|
71307
71307
|
// ../../node_modules/cloudflare/resources/pages/projects/domains.mjs
|
|
71308
71308
|
var Domains3, DomainListResponsesSinglePage;
|
|
71309
|
-
var
|
|
71309
|
+
var init_domains5 = __esm(() => {
|
|
71310
71310
|
init_pagination();
|
|
71311
71311
|
Domains3 = class Domains3 extends APIResource {
|
|
71312
71312
|
create(projectName, params, options) {
|
|
@@ -71409,8 +71409,8 @@ var init_deployments = __esm(() => {
|
|
|
71409
71409
|
// ../../node_modules/cloudflare/resources/pages/projects/projects.mjs
|
|
71410
71410
|
var Projects, DeploymentsSinglePage;
|
|
71411
71411
|
var init_projects = __esm(() => {
|
|
71412
|
-
|
|
71413
|
-
|
|
71412
|
+
init_domains5();
|
|
71413
|
+
init_domains5();
|
|
71414
71414
|
init_deployments();
|
|
71415
71415
|
init_deployments();
|
|
71416
71416
|
init_pagination();
|
|
@@ -71977,7 +71977,7 @@ var init_managed = __esm(() => {
|
|
|
71977
71977
|
|
|
71978
71978
|
// ../../node_modules/cloudflare/resources/r2/buckets/domains/domains.mjs
|
|
71979
71979
|
var Domains4;
|
|
71980
|
-
var
|
|
71980
|
+
var init_domains6 = __esm(() => {
|
|
71981
71981
|
init_custom5();
|
|
71982
71982
|
init_custom5();
|
|
71983
71983
|
init_managed();
|
|
@@ -72008,8 +72008,8 @@ var init_buckets = __esm(() => {
|
|
|
72008
72008
|
init_metrics();
|
|
72009
72009
|
init_sippy();
|
|
72010
72010
|
init_sippy();
|
|
72011
|
-
|
|
72012
|
-
|
|
72011
|
+
init_domains6();
|
|
72012
|
+
init_domains6();
|
|
72013
72013
|
Buckets = class Buckets extends APIResource {
|
|
72014
72014
|
constructor() {
|
|
72015
72015
|
super(...arguments);
|
|
@@ -75361,7 +75361,7 @@ var init_rate_limits = __esm(() => {
|
|
|
75361
75361
|
|
|
75362
75362
|
// ../../node_modules/cloudflare/resources/registrar/domains.mjs
|
|
75363
75363
|
var Domains5, DomainsSinglePage;
|
|
75364
|
-
var
|
|
75364
|
+
var init_domains7 = __esm(() => {
|
|
75365
75365
|
init_pagination();
|
|
75366
75366
|
Domains5 = class Domains5 extends APIResource {
|
|
75367
75367
|
update(domainName, params, options) {
|
|
@@ -75388,8 +75388,8 @@ var init_domains5 = __esm(() => {
|
|
|
75388
75388
|
// ../../node_modules/cloudflare/resources/registrar/registrar.mjs
|
|
75389
75389
|
var Registrar;
|
|
75390
75390
|
var init_registrar = __esm(() => {
|
|
75391
|
-
|
|
75392
|
-
|
|
75391
|
+
init_domains7();
|
|
75392
|
+
init_domains7();
|
|
75393
75393
|
Registrar = class Registrar extends APIResource {
|
|
75394
75394
|
constructor() {
|
|
75395
75395
|
super(...arguments);
|
|
@@ -78355,7 +78355,7 @@ var init_account_settings = __esm(() => {
|
|
|
78355
78355
|
|
|
78356
78356
|
// ../../node_modules/cloudflare/resources/workers/domains.mjs
|
|
78357
78357
|
var Domains6, DomainsSinglePage2;
|
|
78358
|
-
var
|
|
78358
|
+
var init_domains8 = __esm(() => {
|
|
78359
78359
|
init_pagination();
|
|
78360
78360
|
Domains6 = class Domains6 extends APIResource {
|
|
78361
78361
|
update(params, options) {
|
|
@@ -78497,7 +78497,7 @@ var init_versions3 = __esm(() => {
|
|
|
78497
78497
|
|
|
78498
78498
|
// ../../node_modules/cloudflare/resources/workers/beta/workers/workers.mjs
|
|
78499
78499
|
var Workers, WorkersV4PagePaginationArray;
|
|
78500
|
-
var
|
|
78500
|
+
var init_workers3 = __esm(() => {
|
|
78501
78501
|
init_versions3();
|
|
78502
78502
|
init_versions3();
|
|
78503
78503
|
init_pagination();
|
|
@@ -78551,8 +78551,8 @@ var init_workers = __esm(() => {
|
|
|
78551
78551
|
// ../../node_modules/cloudflare/resources/workers/beta/beta.mjs
|
|
78552
78552
|
var Beta;
|
|
78553
78553
|
var init_beta = __esm(() => {
|
|
78554
|
-
|
|
78555
|
-
|
|
78554
|
+
init_workers3();
|
|
78555
|
+
init_workers3();
|
|
78556
78556
|
Beta = class Beta extends APIResource {
|
|
78557
78557
|
constructor() {
|
|
78558
78558
|
super(...arguments);
|
|
@@ -78944,11 +78944,11 @@ var init_scripts2 = __esm(() => {
|
|
|
78944
78944
|
|
|
78945
78945
|
// ../../node_modules/cloudflare/resources/workers/workers.mjs
|
|
78946
78946
|
var Workers2;
|
|
78947
|
-
var
|
|
78947
|
+
var init_workers4 = __esm(() => {
|
|
78948
78948
|
init_account_settings();
|
|
78949
78949
|
init_account_settings();
|
|
78950
|
-
|
|
78951
|
-
|
|
78950
|
+
init_domains8();
|
|
78951
|
+
init_domains8();
|
|
78952
78952
|
init_routes3();
|
|
78953
78953
|
init_routes3();
|
|
78954
78954
|
init_subdomains();
|
|
@@ -83973,7 +83973,7 @@ var init_resources3 = __esm(() => {
|
|
|
83973
83973
|
init_vectorize();
|
|
83974
83974
|
init_waiting_rooms();
|
|
83975
83975
|
init_web3();
|
|
83976
|
-
|
|
83976
|
+
init_workers4();
|
|
83977
83977
|
init_workers_for_platforms();
|
|
83978
83978
|
init_workflows();
|
|
83979
83979
|
init_zaraz();
|
|
@@ -84080,7 +84080,7 @@ var init_cloudflare = __esm(() => {
|
|
|
84080
84080
|
init_waiting_rooms();
|
|
84081
84081
|
init_web3();
|
|
84082
84082
|
init_workers_for_platforms();
|
|
84083
|
-
|
|
84083
|
+
init_workers4();
|
|
84084
84084
|
init_workflows();
|
|
84085
84085
|
init_zaraz();
|
|
84086
84086
|
init_zero_trust();
|
|
@@ -84549,7 +84549,7 @@ var require_dist_cjs2 = __commonJS((exports, module2) => {
|
|
|
84549
84549
|
Fields: () => Fields3,
|
|
84550
84550
|
HttpRequest: () => HttpRequest,
|
|
84551
84551
|
HttpResponse: () => HttpResponse,
|
|
84552
|
-
IHttpRequest: () =>
|
|
84552
|
+
IHttpRequest: () => import_types6.HttpRequest,
|
|
84553
84553
|
getHttpHandlerExtensionConfiguration: () => getHttpHandlerExtensionConfiguration,
|
|
84554
84554
|
isValidHostname: () => isValidHostname,
|
|
84555
84555
|
resolveHttpHandlerRuntimeConfig: () => resolveHttpHandlerRuntimeConfig
|
|
@@ -84576,12 +84576,12 @@ var require_dist_cjs2 = __commonJS((exports, module2) => {
|
|
|
84576
84576
|
httpHandler: httpHandlerExtensionConfiguration.httpHandler()
|
|
84577
84577
|
};
|
|
84578
84578
|
}, "resolveHttpHandlerRuntimeConfig");
|
|
84579
|
-
var
|
|
84579
|
+
var import_types6 = require_dist_cjs();
|
|
84580
84580
|
var Field = class {
|
|
84581
84581
|
static {
|
|
84582
84582
|
__name(this, "Field");
|
|
84583
84583
|
}
|
|
84584
|
-
constructor({ name: name4, kind: kind2 =
|
|
84584
|
+
constructor({ name: name4, kind: kind2 = import_types6.FieldPosition.HEADER, values = [] }) {
|
|
84585
84585
|
this.name = name4;
|
|
84586
84586
|
this.kind = kind2;
|
|
84587
84587
|
this.values = values;
|
|
@@ -85450,8 +85450,8 @@ var require_dist_cjs4 = __commonJS((exports, module2) => {
|
|
|
85450
85450
|
normalizeProvider: () => normalizeProvider
|
|
85451
85451
|
});
|
|
85452
85452
|
module2.exports = __toCommonJS(src_exports);
|
|
85453
|
-
var
|
|
85454
|
-
var getSmithyContext = /* @__PURE__ */ __name((context) => context[
|
|
85453
|
+
var import_types6 = require_dist_cjs();
|
|
85454
|
+
var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
|
|
85455
85455
|
var normalizeProvider = /* @__PURE__ */ __name((input) => {
|
|
85456
85456
|
if (typeof input === "function")
|
|
85457
85457
|
return input;
|
|
@@ -89531,8 +89531,8 @@ var require_dist_cjs16 = __commonJS((exports, module2) => {
|
|
|
89531
89531
|
setFeature: () => setFeature
|
|
89532
89532
|
});
|
|
89533
89533
|
module2.exports = __toCommonJS(src_exports);
|
|
89534
|
-
var
|
|
89535
|
-
var getSmithyContext = /* @__PURE__ */ __name((context) => context[
|
|
89534
|
+
var import_types6 = require_dist_cjs();
|
|
89535
|
+
var getSmithyContext = /* @__PURE__ */ __name((context) => context[import_types6.SMITHY_CONTEXT_KEY] || (context[import_types6.SMITHY_CONTEXT_KEY] = {}), "getSmithyContext");
|
|
89536
89536
|
var import_util_middleware = require_dist_cjs4();
|
|
89537
89537
|
var resolveAuthOptions = /* @__PURE__ */ __name((candidateAuthOptions, authSchemePreference) => {
|
|
89538
89538
|
if (!authSchemePreference || authSchemePreference.length === 0) {
|
|
@@ -89768,9 +89768,9 @@ var require_dist_cjs16 = __commonJS((exports, module2) => {
|
|
|
89768
89768
|
throw new Error("request could not be signed with `apiKey` since the `apiKey` is not defined");
|
|
89769
89769
|
}
|
|
89770
89770
|
const clonedRequest = import_protocol_http.HttpRequest.clone(httpRequest);
|
|
89771
|
-
if (signingProperties.in ===
|
|
89771
|
+
if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.QUERY) {
|
|
89772
89772
|
clonedRequest.query[signingProperties.name] = identity.apiKey;
|
|
89773
|
-
} else if (signingProperties.in ===
|
|
89773
|
+
} else if (signingProperties.in === import_types6.HttpApiKeyAuthLocation.HEADER) {
|
|
89774
89774
|
clonedRequest.headers[signingProperties.name] = signingProperties.scheme ? `${signingProperties.scheme} ${identity.apiKey}` : identity.apiKey;
|
|
89775
89775
|
} else {
|
|
89776
89776
|
throw new Error("request can only be signed with `apiKey` locations `query` or `header`, but found: `" + signingProperties.in + "`");
|
|
@@ -90853,7 +90853,7 @@ var require_httpAuthSchemes = __commonJS((exports, module2) => {
|
|
|
90853
90853
|
},
|
|
90854
90854
|
default: undefined
|
|
90855
90855
|
};
|
|
90856
|
-
var
|
|
90856
|
+
var import_client2 = require_client();
|
|
90857
90857
|
var import_core210 = require_dist_cjs16();
|
|
90858
90858
|
var import_signature_v4 = require_dist_cjs20();
|
|
90859
90859
|
var resolveAwsSdkSigV4Config = /* @__PURE__ */ __name((config2) => {
|
|
@@ -90872,7 +90872,7 @@ var require_httpAuthSchemes = __commonJS((exports, module2) => {
|
|
|
90872
90872
|
});
|
|
90873
90873
|
const boundProvider = bindCallerConfig(config2, memoizedProvider);
|
|
90874
90874
|
if (isUserSupplied && !boundProvider.attributed) {
|
|
90875
|
-
resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0,
|
|
90875
|
+
resolvedCredentials = /* @__PURE__ */ __name(async (options) => boundProvider(options).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_CODE", "e")), "resolvedCredentials");
|
|
90876
90876
|
resolvedCredentials.memoized = boundProvider.memoized;
|
|
90877
90877
|
resolvedCredentials.configBound = boundProvider.configBound;
|
|
90878
90878
|
resolvedCredentials.attributed = true;
|
|
@@ -91420,7 +91420,7 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91420
91420
|
}
|
|
91421
91421
|
};
|
|
91422
91422
|
var import_protocols = require_protocols();
|
|
91423
|
-
var
|
|
91423
|
+
var import_types6 = require_dist_cjs();
|
|
91424
91424
|
var Command = class {
|
|
91425
91425
|
constructor() {
|
|
91426
91426
|
this.middlewareStack = (0, import_middleware_stack.constructStack)();
|
|
@@ -91452,7 +91452,7 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91452
91452
|
commandName,
|
|
91453
91453
|
inputFilterSensitiveLog,
|
|
91454
91454
|
outputFilterSensitiveLog,
|
|
91455
|
-
[
|
|
91455
|
+
[import_types6.SMITHY_CONTEXT_KEY]: {
|
|
91456
91456
|
commandInstance: this,
|
|
91457
91457
|
...smithyContext
|
|
91458
91458
|
},
|
|
@@ -91677,8 +91677,8 @@ var require_dist_cjs23 = __commonJS((exports, module2) => {
|
|
|
91677
91677
|
}, "emitWarningIfUnsupportedVersion");
|
|
91678
91678
|
var getChecksumConfiguration = /* @__PURE__ */ __name((runtimeConfig) => {
|
|
91679
91679
|
const checksumAlgorithms = [];
|
|
91680
|
-
for (const id in
|
|
91681
|
-
const algorithmId =
|
|
91680
|
+
for (const id in import_types6.AlgorithmId) {
|
|
91681
|
+
const algorithmId = import_types6.AlgorithmId[id];
|
|
91682
91682
|
if (runtimeConfig[algorithmId] === undefined) {
|
|
91683
91683
|
continue;
|
|
91684
91684
|
}
|
|
@@ -93862,7 +93862,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93862
93862
|
return this._read(schema4, data);
|
|
93863
93863
|
}
|
|
93864
93864
|
_read(schema4, value) {
|
|
93865
|
-
const
|
|
93865
|
+
const isObject4 = value !== null && typeof value === "object";
|
|
93866
93866
|
const ns = import_schema2.NormalizedSchema.of(schema4);
|
|
93867
93867
|
if (ns.isListSchema() && Array.isArray(value)) {
|
|
93868
93868
|
const listMember = ns.getValueSchema();
|
|
@@ -93874,7 +93874,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93874
93874
|
}
|
|
93875
93875
|
}
|
|
93876
93876
|
return out2;
|
|
93877
|
-
} else if (ns.isMapSchema() &&
|
|
93877
|
+
} else if (ns.isMapSchema() && isObject4) {
|
|
93878
93878
|
const mapMember = ns.getValueSchema();
|
|
93879
93879
|
const out2 = {};
|
|
93880
93880
|
const sparse = !!ns.getMergedTraits().sparse;
|
|
@@ -93884,7 +93884,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
93884
93884
|
}
|
|
93885
93885
|
}
|
|
93886
93886
|
return out2;
|
|
93887
|
-
} else if (ns.isStructSchema() &&
|
|
93887
|
+
} else if (ns.isStructSchema() && isObject4) {
|
|
93888
93888
|
const out2 = {};
|
|
93889
93889
|
for (const [memberName, memberSchema] of ns.structIterator()) {
|
|
93890
93890
|
const fromKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
|
|
@@ -94016,7 +94016,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94016
94016
|
return this.buffer;
|
|
94017
94017
|
}
|
|
94018
94018
|
_write(schema4, value, container) {
|
|
94019
|
-
const
|
|
94019
|
+
const isObject4 = value !== null && typeof value === "object";
|
|
94020
94020
|
const ns = import_schema22.NormalizedSchema.of(schema4);
|
|
94021
94021
|
if (ns.isListSchema() && Array.isArray(value)) {
|
|
94022
94022
|
const listMember = ns.getValueSchema();
|
|
@@ -94028,7 +94028,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94028
94028
|
}
|
|
94029
94029
|
}
|
|
94030
94030
|
return out2;
|
|
94031
|
-
} else if (ns.isMapSchema() &&
|
|
94031
|
+
} else if (ns.isMapSchema() && isObject4) {
|
|
94032
94032
|
const mapMember = ns.getValueSchema();
|
|
94033
94033
|
const out2 = {};
|
|
94034
94034
|
const sparse = !!ns.getMergedTraits().sparse;
|
|
@@ -94038,7 +94038,7 @@ var require_protocols2 = __commonJS((exports, module2) => {
|
|
|
94038
94038
|
}
|
|
94039
94039
|
}
|
|
94040
94040
|
return out2;
|
|
94041
|
-
} else if (ns.isStructSchema() &&
|
|
94041
|
+
} else if (ns.isStructSchema() && isObject4) {
|
|
94042
94042
|
const out2 = {};
|
|
94043
94043
|
for (const [memberName, memberSchema] of ns.structIterator()) {
|
|
94044
94044
|
const targetKey = this.settings.jsonName ? memberSchema.getMergedTraits().jsonName ?? memberName : memberName;
|
|
@@ -101588,16 +101588,16 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101588
101588
|
var getProfileName = /* @__PURE__ */ __name((init3) => init3.profile || process.env[ENV_PROFILE] || DEFAULT_PROFILE, "getProfileName");
|
|
101589
101589
|
__reExport(src_exports, require_getSSOTokenFilepath(), module2.exports);
|
|
101590
101590
|
__reExport(src_exports, require_getSSOTokenFromFile(), module2.exports);
|
|
101591
|
-
var
|
|
101591
|
+
var import_types6 = require_dist_cjs();
|
|
101592
101592
|
var getConfigData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => {
|
|
101593
101593
|
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
|
101594
101594
|
if (indexOfSeparator === -1) {
|
|
101595
101595
|
return false;
|
|
101596
101596
|
}
|
|
101597
|
-
return Object.values(
|
|
101597
|
+
return Object.values(import_types6.IniSectionType).includes(key.substring(0, indexOfSeparator));
|
|
101598
101598
|
}).reduce((acc, [key, value]) => {
|
|
101599
101599
|
const indexOfSeparator = key.indexOf(CONFIG_PREFIX_SEPARATOR);
|
|
101600
|
-
const updatedKey = key.substring(0, indexOfSeparator) ===
|
|
101600
|
+
const updatedKey = key.substring(0, indexOfSeparator) === import_types6.IniSectionType.PROFILE ? key.substring(indexOfSeparator + 1) : key;
|
|
101601
101601
|
acc[updatedKey] = value;
|
|
101602
101602
|
return acc;
|
|
101603
101603
|
}, {
|
|
@@ -101627,7 +101627,7 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101627
101627
|
const matches = prefixKeyRegex.exec(sectionName);
|
|
101628
101628
|
if (matches) {
|
|
101629
101629
|
const [, prefix2, , name4] = matches;
|
|
101630
|
-
if (Object.values(
|
|
101630
|
+
if (Object.values(import_types6.IniSectionType).includes(prefix2)) {
|
|
101631
101631
|
currentSection = [prefix2, name4].join(CONFIG_PREFIX_SEPARATOR);
|
|
101632
101632
|
}
|
|
101633
101633
|
} else {
|
|
@@ -101686,7 +101686,7 @@ var require_dist_cjs52 = __commonJS((exports, module2) => {
|
|
|
101686
101686
|
credentialsFile: parsedFiles[1]
|
|
101687
101687
|
};
|
|
101688
101688
|
}, "loadSharedConfigFiles");
|
|
101689
|
-
var getSsoSessionData = /* @__PURE__ */ __name((data) => Object.entries(data).filter(([key]) => key.startsWith(
|
|
101689
|
+
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");
|
|
101690
101690
|
var import_slurpFile2 = require_slurpFile();
|
|
101691
101691
|
var swallowError2 = /* @__PURE__ */ __name(() => ({}), "swallowError");
|
|
101692
101692
|
var loadSsoSessionData = /* @__PURE__ */ __name(async (init3 = {}) => (0, import_slurpFile2.slurpFile)(init3.configFilepath ?? getConfigFilepath()).then(parseIni).then(getSsoSessionData).catch(swallowError2), "loadSsoSessionData");
|
|
@@ -102685,7 +102685,7 @@ var require_dist_cjs57 = __commonJS((exports, module2) => {
|
|
|
102685
102685
|
fromEnv: () => fromEnv
|
|
102686
102686
|
});
|
|
102687
102687
|
module2.exports = __toCommonJS(index_exports);
|
|
102688
|
-
var
|
|
102688
|
+
var import_client2 = require_client();
|
|
102689
102689
|
var import_property_provider = require_dist_cjs17();
|
|
102690
102690
|
var ENV_KEY = "AWS_ACCESS_KEY_ID";
|
|
102691
102691
|
var ENV_SECRET = "AWS_SECRET_ACCESS_KEY";
|
|
@@ -102710,7 +102710,7 @@ var require_dist_cjs57 = __commonJS((exports, module2) => {
|
|
|
102710
102710
|
...credentialScope && { credentialScope },
|
|
102711
102711
|
...accountId && { accountId }
|
|
102712
102712
|
};
|
|
102713
|
-
(0,
|
|
102713
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_ENV_VARS", "g");
|
|
102714
102714
|
return credentials2;
|
|
102715
102715
|
}
|
|
102716
102716
|
throw new import_property_provider.CredentialsProviderError("Unable to find environment variable credentials.", { logger: init3?.logger });
|
|
@@ -105631,7 +105631,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105631
105631
|
var __getOwnPropNames3 = Object.getOwnPropertyNames;
|
|
105632
105632
|
var __hasOwnProp3 = Object.prototype.hasOwnProperty;
|
|
105633
105633
|
var __name = (target, value) => __defProp4(target, "name", { value, configurable: true });
|
|
105634
|
-
var
|
|
105634
|
+
var __esm5 = (fn2, res) => function __init() {
|
|
105635
105635
|
return fn2 && (res = (0, fn2[__getOwnPropNames3(fn2)[0]])(fn2 = 0)), res;
|
|
105636
105636
|
};
|
|
105637
105637
|
var __export4 = (target, all) => {
|
|
@@ -105653,7 +105653,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105653
105653
|
SSOClient: () => import_client_sso.SSOClient
|
|
105654
105654
|
});
|
|
105655
105655
|
var import_client_sso;
|
|
105656
|
-
var init_loadSso =
|
|
105656
|
+
var init_loadSso = __esm5({
|
|
105657
105657
|
"src/loadSso.ts"() {
|
|
105658
105658
|
import_client_sso = require_dist_cjs65();
|
|
105659
105659
|
}
|
|
@@ -105666,7 +105666,7 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105666
105666
|
});
|
|
105667
105667
|
module2.exports = __toCommonJS(index_exports);
|
|
105668
105668
|
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");
|
|
105669
|
-
var
|
|
105669
|
+
var import_client2 = require_client();
|
|
105670
105670
|
var import_token_providers = require_dist_cjs66();
|
|
105671
105671
|
var import_property_provider = require_dist_cjs17();
|
|
105672
105672
|
var import_shared_ini_file_loader = require_dist_cjs52();
|
|
@@ -105751,9 +105751,9 @@ var require_dist_cjs67 = __commonJS((exports, module2) => {
|
|
|
105751
105751
|
...accountId && { accountId }
|
|
105752
105752
|
};
|
|
105753
105753
|
if (ssoSession) {
|
|
105754
|
-
(0,
|
|
105754
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO", "s");
|
|
105755
105755
|
} else {
|
|
105756
|
-
(0,
|
|
105756
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_SSO_LEGACY", "u");
|
|
105757
105757
|
}
|
|
105758
105758
|
return credentials2;
|
|
105759
105759
|
}, "resolveSSOCredentials");
|
|
@@ -106953,7 +106953,7 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
106953
106953
|
};
|
|
106954
106954
|
(0, import_smithy_client6.createAggregatedClient)(commands, STS);
|
|
106955
106955
|
var import_EndpointParameters3 = require_EndpointParameters();
|
|
106956
|
-
var
|
|
106956
|
+
var import_client2 = require_client();
|
|
106957
106957
|
var ASSUME_ROLE_DEFAULT_REGION = "us-east-1";
|
|
106958
106958
|
var getAccountIdFromAssumedRoleUser = /* @__PURE__ */ __name((assumedRoleUser) => {
|
|
106959
106959
|
if (typeof assumedRoleUser?.Arn === "string") {
|
|
@@ -107005,7 +107005,7 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
107005
107005
|
...Credentials2.CredentialScope && { credentialScope: Credentials2.CredentialScope },
|
|
107006
107006
|
...accountId && { accountId }
|
|
107007
107007
|
};
|
|
107008
|
-
(0,
|
|
107008
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE", "i");
|
|
107009
107009
|
return credentials2;
|
|
107010
107010
|
};
|
|
107011
107011
|
}, "getDefaultRoleAssumer");
|
|
@@ -107042,9 +107042,9 @@ var require_sts = __commonJS((exports, module2) => {
|
|
|
107042
107042
|
...accountId && { accountId }
|
|
107043
107043
|
};
|
|
107044
107044
|
if (accountId) {
|
|
107045
|
-
(0,
|
|
107045
|
+
(0, import_client2.setCredentialFeature)(credentials2, "RESOLVED_ACCOUNT_ID", "T");
|
|
107046
107046
|
}
|
|
107047
|
-
(0,
|
|
107047
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k");
|
|
107048
107048
|
return credentials2;
|
|
107049
107049
|
};
|
|
107050
107050
|
}, "getDefaultRoleAssumerWithWebIdentity");
|
|
@@ -107106,7 +107106,7 @@ var require_dist_cjs68 = __commonJS((exports, module2) => {
|
|
|
107106
107106
|
var import_property_provider = require_dist_cjs17();
|
|
107107
107107
|
var import_child_process = __require("child_process");
|
|
107108
107108
|
var import_util3 = __require("util");
|
|
107109
|
-
var
|
|
107109
|
+
var import_client2 = require_client();
|
|
107110
107110
|
var getValidatedProcessCredentials = /* @__PURE__ */ __name((profileName, data, profiles) => {
|
|
107111
107111
|
if (data.Version !== 1) {
|
|
107112
107112
|
throw Error(`Profile ${profileName} credential_process did not return Version 1.`);
|
|
@@ -107133,7 +107133,7 @@ var require_dist_cjs68 = __commonJS((exports, module2) => {
|
|
|
107133
107133
|
...data.CredentialScope && { credentialScope: data.CredentialScope },
|
|
107134
107134
|
...accountId && { accountId }
|
|
107135
107135
|
};
|
|
107136
|
-
(0,
|
|
107136
|
+
(0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROCESS", "w");
|
|
107137
107137
|
return credentials2;
|
|
107138
107138
|
}, "getValidatedProcessCredentials");
|
|
107139
107139
|
var resolveProcessCredentials = /* @__PURE__ */ __name(async (profileName, profiles, logger3) => {
|
|
@@ -107334,7 +107334,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107334
107334
|
});
|
|
107335
107335
|
module2.exports = __toCommonJS(index_exports);
|
|
107336
107336
|
var import_shared_ini_file_loader = require_dist_cjs52();
|
|
107337
|
-
var
|
|
107337
|
+
var import_client2 = require_client();
|
|
107338
107338
|
var import_property_provider = require_dist_cjs17();
|
|
107339
107339
|
var resolveCredentialSource = /* @__PURE__ */ __name((credentialSource, profileName, logger3) => {
|
|
107340
107340
|
const sourceProvidersMap = {
|
|
@@ -107361,7 +107361,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107361
107361
|
throw new import_property_provider.CredentialsProviderError(`Unsupported credential source in profile ${profileName}. Got ${credentialSource}, expected EcsContainer or Ec2InstanceMetadata or Environment.`, { logger: logger3 });
|
|
107362
107362
|
}
|
|
107363
107363
|
}, "resolveCredentialSource");
|
|
107364
|
-
var setNamedProvider = /* @__PURE__ */ __name((creds) => (0,
|
|
107364
|
+
var setNamedProvider = /* @__PURE__ */ __name((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_NAMED_PROVIDER", "p"), "setNamedProvider");
|
|
107365
107365
|
var isAssumeRoleProfile = /* @__PURE__ */ __name((arg, { profile = "default", logger: logger3 } = {}) => {
|
|
107366
107366
|
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 }));
|
|
107367
107367
|
}, "isAssumeRoleProfile");
|
|
@@ -107403,7 +107403,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107403
107403
|
[source_profile]: true
|
|
107404
107404
|
}, isCredentialSourceWithoutRoleArn(profiles[source_profile] ?? {})) : (await resolveCredentialSource(profileData.credential_source, profileName, options.logger)(options))();
|
|
107405
107405
|
if (isCredentialSourceWithoutRoleArn(profileData)) {
|
|
107406
|
-
return sourceCredsProvider.then((creds) => (0,
|
|
107406
|
+
return sourceCredsProvider.then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
|
|
107407
107407
|
} else {
|
|
107408
107408
|
const params = {
|
|
107409
107409
|
RoleArn: profileData.role_arn,
|
|
@@ -107420,7 +107420,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107420
107420
|
params.TokenCode = await options.mfaCodeProvider(mfa_serial);
|
|
107421
107421
|
}
|
|
107422
107422
|
const sourceCreds = await sourceCredsProvider;
|
|
107423
|
-
return options.roleAssumer(sourceCreds, params).then((creds) => (0,
|
|
107423
|
+
return options.roleAssumer(sourceCreds, params).then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o"));
|
|
107424
107424
|
}
|
|
107425
107425
|
}, "resolveAssumeRoleCredentials");
|
|
107426
107426
|
var isCredentialSourceWithoutRoleArn = /* @__PURE__ */ __name((section) => {
|
|
@@ -107430,7 +107430,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107430
107430
|
var resolveProcessCredentials = /* @__PURE__ */ __name(async (options, profile) => Promise.resolve().then(() => __toESM3(require_dist_cjs68())).then(({ fromProcess }) => fromProcess({
|
|
107431
107431
|
...options,
|
|
107432
107432
|
profile
|
|
107433
|
-
})().then((creds) => (0,
|
|
107433
|
+
})().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_PROCESS", "v"))), "resolveProcessCredentials");
|
|
107434
107434
|
var resolveSsoCredentials = /* @__PURE__ */ __name(async (profile, profileData, options = {}) => {
|
|
107435
107435
|
const { fromSSO } = await Promise.resolve().then(() => __toESM3(require_dist_cjs67()));
|
|
107436
107436
|
return fromSSO({
|
|
@@ -107440,9 +107440,9 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107440
107440
|
clientConfig: options.clientConfig
|
|
107441
107441
|
})().then((creds) => {
|
|
107442
107442
|
if (profileData.sso_session) {
|
|
107443
|
-
return (0,
|
|
107443
|
+
return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO", "r");
|
|
107444
107444
|
} else {
|
|
107445
|
-
return (0,
|
|
107445
|
+
return (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_SSO_LEGACY", "t");
|
|
107446
107446
|
}
|
|
107447
107447
|
});
|
|
107448
107448
|
}, "resolveSsoCredentials");
|
|
@@ -107457,7 +107457,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107457
107457
|
...profile.aws_credential_scope && { credentialScope: profile.aws_credential_scope },
|
|
107458
107458
|
...profile.aws_account_id && { accountId: profile.aws_account_id }
|
|
107459
107459
|
};
|
|
107460
|
-
return (0,
|
|
107460
|
+
return (0, import_client2.setCredentialFeature)(credentials2, "CREDENTIALS_PROFILE", "n");
|
|
107461
107461
|
}, "resolveStaticCredentials");
|
|
107462
107462
|
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");
|
|
107463
107463
|
var resolveWebIdentityCredentials = /* @__PURE__ */ __name(async (profile, options) => Promise.resolve().then(() => __toESM3(require_dist_cjs69())).then(({ fromTokenFile: fromTokenFile2 }) => fromTokenFile2({
|
|
@@ -107467,7 +107467,7 @@ var require_dist_cjs70 = __commonJS((exports, module2) => {
|
|
|
107467
107467
|
roleAssumerWithWebIdentity: options.roleAssumerWithWebIdentity,
|
|
107468
107468
|
logger: options.logger,
|
|
107469
107469
|
parentClientConfig: options.parentClientConfig
|
|
107470
|
-
})().then((creds) => (0,
|
|
107470
|
+
})().then((creds) => (0, import_client2.setCredentialFeature)(creds, "CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN", "q"))), "resolveWebIdentityCredentials");
|
|
107471
107471
|
var resolveProfileData = /* @__PURE__ */ __name(async (profileName, profiles, options, visitedProfiles = {}, isAssumeRoleRecursiveCall = false) => {
|
|
107472
107472
|
const data = profiles[profileName];
|
|
107473
107473
|
if (Object.keys(visitedProfiles).length > 0 && isStaticCredsProfile(data)) {
|
|
@@ -126309,7 +126309,7 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126309
126309
|
}
|
|
126310
126310
|
}
|
|
126311
126311
|
function baseKeysIn(object) {
|
|
126312
|
-
if (!
|
|
126312
|
+
if (!isObject4(object)) {
|
|
126313
126313
|
return nativeKeysIn(object);
|
|
126314
126314
|
}
|
|
126315
126315
|
var isProto = isPrototype(object), result = [];
|
|
@@ -126369,7 +126369,7 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126369
126369
|
return !!length && (typeof value == "number" || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
|
|
126370
126370
|
}
|
|
126371
126371
|
function isIterateeCall(value, index6, object) {
|
|
126372
|
-
if (!
|
|
126372
|
+
if (!isObject4(object)) {
|
|
126373
126373
|
return false;
|
|
126374
126374
|
}
|
|
126375
126375
|
var type = typeof index6;
|
|
@@ -126405,13 +126405,13 @@ var require_lodash2 = __commonJS((exports, module2) => {
|
|
|
126405
126405
|
return isObjectLike2(value) && isArrayLike(value);
|
|
126406
126406
|
}
|
|
126407
126407
|
function isFunction3(value) {
|
|
126408
|
-
var tag2 =
|
|
126408
|
+
var tag2 = isObject4(value) ? objectToString.call(value) : "";
|
|
126409
126409
|
return tag2 == funcTag || tag2 == genTag;
|
|
126410
126410
|
}
|
|
126411
126411
|
function isLength(value) {
|
|
126412
126412
|
return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
|
|
126413
126413
|
}
|
|
126414
|
-
function
|
|
126414
|
+
function isObject4(value) {
|
|
126415
126415
|
var type = typeof value;
|
|
126416
126416
|
return !!value && (type == "object" || type == "function");
|
|
126417
126417
|
}
|
|
@@ -126451,13 +126451,13 @@ var require_lodash3 = __commonJS((exports, module2) => {
|
|
|
126451
126451
|
return isObjectLike2(value) && isArrayLike(value);
|
|
126452
126452
|
}
|
|
126453
126453
|
function isFunction3(value) {
|
|
126454
|
-
var tag2 =
|
|
126454
|
+
var tag2 = isObject4(value) ? objectToString.call(value) : "";
|
|
126455
126455
|
return tag2 == funcTag || tag2 == genTag;
|
|
126456
126456
|
}
|
|
126457
126457
|
function isLength(value) {
|
|
126458
126458
|
return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
|
|
126459
126459
|
}
|
|
126460
|
-
function
|
|
126460
|
+
function isObject4(value) {
|
|
126461
126461
|
var type = typeof value;
|
|
126462
126462
|
return !!value && (type == "object" || type == "function");
|
|
126463
126463
|
}
|
|
@@ -131608,7 +131608,7 @@ var require_built3 = __commonJS((exports, module2) => {
|
|
|
131608
131608
|
|
|
131609
131609
|
// ../realtime/src/server/domain/constants.ts
|
|
131610
131610
|
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;
|
|
131611
|
-
var
|
|
131611
|
+
var init_constants3 = __esm(() => {
|
|
131612
131612
|
HUB_PREFIX = process.env.HUB_PREFIX ?? "hub:";
|
|
131613
131613
|
MAP_PREFIX = process.env.MAP_PREFIX ?? "map_";
|
|
131614
131614
|
PRESENCE_CHANNEL_NAME = process.env.PRESENCE_CHANNEL_NAME ?? "presence";
|
|
@@ -131757,7 +131757,7 @@ function isRedisConnected() {
|
|
|
131757
131757
|
var import_ioredis, client = null;
|
|
131758
131758
|
var init_client = __esm(() => {
|
|
131759
131759
|
init_src();
|
|
131760
|
-
|
|
131760
|
+
init_constants3();
|
|
131761
131761
|
import_ioredis = __toESM(require_built3(), 1);
|
|
131762
131762
|
});
|
|
131763
131763
|
|
|
@@ -131870,7 +131870,7 @@ async function updateUserNameplateBg(userId, color) {
|
|
|
131870
131870
|
}
|
|
131871
131871
|
var init_presence = __esm(() => {
|
|
131872
131872
|
init_src();
|
|
131873
|
-
|
|
131873
|
+
init_constants3();
|
|
131874
131874
|
init_client();
|
|
131875
131875
|
});
|
|
131876
131876
|
|
|
@@ -131921,7 +131921,7 @@ function extractTokenFromUrl(url) {
|
|
|
131921
131921
|
return null;
|
|
131922
131922
|
}
|
|
131923
131923
|
}
|
|
131924
|
-
var
|
|
131924
|
+
var init_auth3 = __esm(() => {
|
|
131925
131925
|
init_esm2();
|
|
131926
131926
|
init_src();
|
|
131927
131927
|
});
|
|
@@ -132057,7 +132057,7 @@ var init_sandbox = __esm(() => {
|
|
|
132057
132057
|
init_wrapper();
|
|
132058
132058
|
init_src();
|
|
132059
132059
|
init_infrastructure2();
|
|
132060
|
-
|
|
132060
|
+
init_auth3();
|
|
132061
132061
|
init_config4();
|
|
132062
132062
|
});
|
|
132063
132063
|
// ../realtime/src/server/application/services/presence.ts
|
|
@@ -132144,7 +132144,7 @@ var init_presence2 = __esm(() => {
|
|
|
132144
132144
|
var playerPositions, PlayerPositionService;
|
|
132145
132145
|
var init_player_position = __esm(() => {
|
|
132146
132146
|
init_src();
|
|
132147
|
-
|
|
132147
|
+
init_constants3();
|
|
132148
132148
|
init_infrastructure2();
|
|
132149
132149
|
playerPositions = new Map;
|
|
132150
132150
|
PlayerPositionService = {
|
|
@@ -132276,7 +132276,7 @@ function computeInitialChannelKey(gameId, requested) {
|
|
|
132276
132276
|
return `${HUB_PREFIX}${channel}`;
|
|
132277
132277
|
}
|
|
132278
132278
|
var init_channel_keys = __esm(() => {
|
|
132279
|
-
|
|
132279
|
+
init_constants3();
|
|
132280
132280
|
});
|
|
132281
132281
|
|
|
132282
132282
|
// ../realtime/src/server/shared/utils/websocket.ts
|
|
@@ -132297,7 +132297,7 @@ function checkMatchingPlayerId(ws, payload) {
|
|
|
132297
132297
|
}
|
|
132298
132298
|
var init_websocket = __esm(() => {
|
|
132299
132299
|
init_src();
|
|
132300
|
-
|
|
132300
|
+
init_constants3();
|
|
132301
132301
|
});
|
|
132302
132302
|
|
|
132303
132303
|
// ../realtime/src/server/shared/utils/route.ts
|
|
@@ -132652,7 +132652,7 @@ async function handleWebSocketUpgrade(req, server, config2) {
|
|
|
132652
132652
|
}
|
|
132653
132653
|
var init_websocket2 = __esm(() => {
|
|
132654
132654
|
init_src();
|
|
132655
|
-
|
|
132655
|
+
init_auth3();
|
|
132656
132656
|
init_response();
|
|
132657
132657
|
});
|
|
132658
132658
|
|
|
@@ -132839,7 +132839,7 @@ var init_open = __esm(() => {
|
|
|
132839
132839
|
init_src();
|
|
132840
132840
|
init_presence4();
|
|
132841
132841
|
init_services2();
|
|
132842
|
-
|
|
132842
|
+
init_constants3();
|
|
132843
132843
|
init_events();
|
|
132844
132844
|
init_infrastructure2();
|
|
132845
132845
|
init_utils13();
|
|
@@ -135157,6 +135157,7 @@ var config = {
|
|
|
135157
135157
|
};
|
|
135158
135158
|
process.env.BETTER_AUTH_SECRET = config.auth.betterAuthSecret;
|
|
135159
135159
|
process.env.GAME_JWT_SECRET = config.auth.gameJwtSecret;
|
|
135160
|
+
process.env.PUBLIC_IS_LOCAL = "true";
|
|
135160
135161
|
|
|
135161
135162
|
// ../../node_modules/@hono/node-server/dist/index.mjs
|
|
135162
135163
|
import { createServer as createServerHTTP } from "http";
|
|
@@ -135702,7 +135703,7 @@ var serve = (options, listeningListener) => {
|
|
|
135702
135703
|
// package.json
|
|
135703
135704
|
var package_default = {
|
|
135704
135705
|
name: "@playcademy/sandbox",
|
|
135705
|
-
version: "0.1
|
|
135706
|
+
version: "0.2.1",
|
|
135706
135707
|
description: "Local development server for Playcademy game development",
|
|
135707
135708
|
type: "module",
|
|
135708
135709
|
exports: {
|
|
@@ -142782,12 +142783,17 @@ var timebackXpEvents = pgTable("timeback_xp_event", {
|
|
|
142782
142783
|
}, (table) => [uniqueIndex("timeback_xp_events_source_id_idx").on(table.source, table.sourceId)]);
|
|
142783
142784
|
var gameTimebackIntegrations = pgTable("game_timeback_integrations", {
|
|
142784
142785
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
142785
|
-
gameId: uuid("game_id").notNull().
|
|
142786
|
+
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
142786
142787
|
courseId: text("course_id").notNull(),
|
|
142788
|
+
grade: integer("grade").notNull(),
|
|
142789
|
+
subject: text("subject").notNull(),
|
|
142790
|
+
totalXp: integer("total_xp"),
|
|
142787
142791
|
lastVerifiedAt: timestamp("last_verified_at", { withTimezone: true }),
|
|
142788
142792
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
142789
142793
|
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
142790
|
-
})
|
|
142794
|
+
}, (table) => [
|
|
142795
|
+
uniqueIndex("game_timeback_integrations_game_grade_subject_idx").on(table.gameId, table.grade, table.subject)
|
|
142796
|
+
]);
|
|
142791
142797
|
// ../data/src/domains/achievement/table.ts
|
|
142792
142798
|
var achievementScopeEnum = pgEnum("achievement_scope", [
|
|
142793
142799
|
"daily",
|
|
@@ -149370,6 +149376,292 @@ var __export3 = (target, all) => {
|
|
|
149370
149376
|
set: (newValue) => all[name3] = () => newValue
|
|
149371
149377
|
});
|
|
149372
149378
|
};
|
|
149379
|
+
var __esm3 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
|
|
149380
|
+
var init_auth = () => {};
|
|
149381
|
+
var PLAYCADEMY_BASE_URLS;
|
|
149382
|
+
var init_domains = __esm3(() => {
|
|
149383
|
+
PLAYCADEMY_BASE_URLS = {
|
|
149384
|
+
production: "https://hub.playcademy.net",
|
|
149385
|
+
staging: "https://hub.dev.playcademy.net"
|
|
149386
|
+
};
|
|
149387
|
+
});
|
|
149388
|
+
var init_env_vars = () => {};
|
|
149389
|
+
var ITEM_SLUGS2;
|
|
149390
|
+
var CURRENCIES2;
|
|
149391
|
+
var BADGES2;
|
|
149392
|
+
var init_overworld = __esm3(() => {
|
|
149393
|
+
ITEM_SLUGS2 = {
|
|
149394
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
149395
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
149396
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
149397
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
149398
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
149399
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
149400
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
149401
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
149402
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
149403
|
+
BOOMBOX: "BOOMBOX",
|
|
149404
|
+
CABIN_BED: "CABIN_BED"
|
|
149405
|
+
};
|
|
149406
|
+
CURRENCIES2 = {
|
|
149407
|
+
PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
|
|
149408
|
+
XP: ITEM_SLUGS2.PLAYCADEMY_XP
|
|
149409
|
+
};
|
|
149410
|
+
BADGES2 = {
|
|
149411
|
+
FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
|
|
149412
|
+
EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
|
|
149413
|
+
FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
|
|
149414
|
+
};
|
|
149415
|
+
});
|
|
149416
|
+
var init_timeback = () => {};
|
|
149417
|
+
var init_workers = () => {};
|
|
149418
|
+
var init_src2 = __esm3(() => {
|
|
149419
|
+
init_auth();
|
|
149420
|
+
init_domains();
|
|
149421
|
+
init_env_vars();
|
|
149422
|
+
init_overworld();
|
|
149423
|
+
init_timeback();
|
|
149424
|
+
init_workers();
|
|
149425
|
+
});
|
|
149426
|
+
var TIMEBACK_API_URLS;
|
|
149427
|
+
var TIMEBACK_AUTH_URLS;
|
|
149428
|
+
var CALIPER_API_URLS;
|
|
149429
|
+
var ONEROSTER_ENDPOINTS;
|
|
149430
|
+
var CALIPER_ENDPOINTS;
|
|
149431
|
+
var createOneRosterUrls = (baseUrl) => {
|
|
149432
|
+
const effective = baseUrl || TIMEBACK_API_URLS.production;
|
|
149433
|
+
const base = effective.replace(/\/$/, "");
|
|
149434
|
+
return {
|
|
149435
|
+
user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
|
|
149436
|
+
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
|
|
149437
|
+
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
|
|
149438
|
+
};
|
|
149439
|
+
};
|
|
149440
|
+
var CALIPER_CONSTANTS;
|
|
149441
|
+
var TIMEBACK_EVENT_TYPES;
|
|
149442
|
+
var TIMEBACK_ACTIONS;
|
|
149443
|
+
var TIMEBACK_TYPES;
|
|
149444
|
+
var ACTIVITY_METRIC_TYPES;
|
|
149445
|
+
var TIME_METRIC_TYPES;
|
|
149446
|
+
var TIMEBACK_SUBJECTS;
|
|
149447
|
+
var TIMEBACK_GRADE_LEVELS;
|
|
149448
|
+
var TIMEBACK_GRADE_LEVEL_LABELS;
|
|
149449
|
+
var CALIPER_SUBJECTS;
|
|
149450
|
+
var ONEROSTER_STATUS;
|
|
149451
|
+
var SCORE_STATUS;
|
|
149452
|
+
var ENV_VARS;
|
|
149453
|
+
var HTTP_DEFAULTS;
|
|
149454
|
+
var AUTH_DEFAULTS;
|
|
149455
|
+
var CACHE_DEFAULTS;
|
|
149456
|
+
var CONFIG_DEFAULTS;
|
|
149457
|
+
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID;
|
|
149458
|
+
var PLAYCADEMY_DEFAULTS;
|
|
149459
|
+
var RESOURCE_DEFAULTS;
|
|
149460
|
+
var HTTP_STATUS;
|
|
149461
|
+
var ERROR_NAMES;
|
|
149462
|
+
var init_constants = __esm3(() => {
|
|
149463
|
+
init_src2();
|
|
149464
|
+
TIMEBACK_API_URLS = {
|
|
149465
|
+
production: "https://api.alpha-1edtech.ai",
|
|
149466
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
149467
|
+
};
|
|
149468
|
+
TIMEBACK_AUTH_URLS = {
|
|
149469
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
149470
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
149471
|
+
};
|
|
149472
|
+
CALIPER_API_URLS = {
|
|
149473
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
149474
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
149475
|
+
};
|
|
149476
|
+
ONEROSTER_ENDPOINTS = {
|
|
149477
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
149478
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
149479
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
149480
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
149481
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
149482
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
149483
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
149484
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
149485
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
149486
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
149487
|
+
};
|
|
149488
|
+
CALIPER_ENDPOINTS = {
|
|
149489
|
+
events: "/caliper/event",
|
|
149490
|
+
validate: "/caliper/event/validate"
|
|
149491
|
+
};
|
|
149492
|
+
CALIPER_CONSTANTS = {
|
|
149493
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
149494
|
+
profile: "TimebackProfile",
|
|
149495
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
149496
|
+
};
|
|
149497
|
+
TIMEBACK_EVENT_TYPES = {
|
|
149498
|
+
activityEvent: "ActivityEvent",
|
|
149499
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
149500
|
+
};
|
|
149501
|
+
TIMEBACK_ACTIONS = {
|
|
149502
|
+
completed: "Completed",
|
|
149503
|
+
spentTime: "SpentTime"
|
|
149504
|
+
};
|
|
149505
|
+
TIMEBACK_TYPES = {
|
|
149506
|
+
user: "TimebackUser",
|
|
149507
|
+
activityContext: "TimebackActivityContext",
|
|
149508
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
149509
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
149510
|
+
};
|
|
149511
|
+
ACTIVITY_METRIC_TYPES = {
|
|
149512
|
+
totalQuestions: "totalQuestions",
|
|
149513
|
+
correctQuestions: "correctQuestions",
|
|
149514
|
+
xpEarned: "xpEarned",
|
|
149515
|
+
masteredUnits: "masteredUnits"
|
|
149516
|
+
};
|
|
149517
|
+
TIME_METRIC_TYPES = {
|
|
149518
|
+
active: "active",
|
|
149519
|
+
inactive: "inactive",
|
|
149520
|
+
waste: "waste",
|
|
149521
|
+
unknown: "unknown",
|
|
149522
|
+
antiPattern: "anti-pattern"
|
|
149523
|
+
};
|
|
149524
|
+
TIMEBACK_SUBJECTS = [
|
|
149525
|
+
"Math",
|
|
149526
|
+
"FastMath",
|
|
149527
|
+
"Science",
|
|
149528
|
+
"Social Studies",
|
|
149529
|
+
"Language",
|
|
149530
|
+
"Reading",
|
|
149531
|
+
"Vocabulary",
|
|
149532
|
+
"Writing"
|
|
149533
|
+
];
|
|
149534
|
+
TIMEBACK_GRADE_LEVELS = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
149535
|
+
TIMEBACK_GRADE_LEVEL_LABELS = {
|
|
149536
|
+
"-1": "pre-k",
|
|
149537
|
+
"0": "kindergarten",
|
|
149538
|
+
"1": "1st grade",
|
|
149539
|
+
"2": "2nd grade",
|
|
149540
|
+
"3": "3rd grade",
|
|
149541
|
+
"4": "4th grade",
|
|
149542
|
+
"5": "5th grade",
|
|
149543
|
+
"6": "6th grade",
|
|
149544
|
+
"7": "7th grade",
|
|
149545
|
+
"8": "8th grade",
|
|
149546
|
+
"9": "9th grade",
|
|
149547
|
+
"10": "10th grade",
|
|
149548
|
+
"11": "11th grade",
|
|
149549
|
+
"12": "12th grade",
|
|
149550
|
+
"13": "AP"
|
|
149551
|
+
};
|
|
149552
|
+
CALIPER_SUBJECTS = {
|
|
149553
|
+
Reading: "Reading",
|
|
149554
|
+
Language: "Language",
|
|
149555
|
+
Vocabulary: "Vocabulary",
|
|
149556
|
+
SocialStudies: "Social Studies",
|
|
149557
|
+
Writing: "Writing",
|
|
149558
|
+
Science: "Science",
|
|
149559
|
+
FastMath: "FastMath",
|
|
149560
|
+
Math: "Math",
|
|
149561
|
+
None: "None"
|
|
149562
|
+
};
|
|
149563
|
+
ONEROSTER_STATUS = {
|
|
149564
|
+
active: "active",
|
|
149565
|
+
toBeDeleted: "tobedeleted"
|
|
149566
|
+
};
|
|
149567
|
+
SCORE_STATUS = {
|
|
149568
|
+
exempt: "exempt",
|
|
149569
|
+
fullyGraded: "fully graded",
|
|
149570
|
+
notSubmitted: "not submitted",
|
|
149571
|
+
partiallyGraded: "partially graded",
|
|
149572
|
+
submitted: "submitted"
|
|
149573
|
+
};
|
|
149574
|
+
ENV_VARS = {
|
|
149575
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
149576
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
149577
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
149578
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
149579
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
149580
|
+
launchBaseUrl: "GAME_URL"
|
|
149581
|
+
};
|
|
149582
|
+
HTTP_DEFAULTS = {
|
|
149583
|
+
timeout: 30000,
|
|
149584
|
+
retries: 3,
|
|
149585
|
+
retryBackoffBase: 2
|
|
149586
|
+
};
|
|
149587
|
+
AUTH_DEFAULTS = {
|
|
149588
|
+
tokenCacheDuration: 50000
|
|
149589
|
+
};
|
|
149590
|
+
CACHE_DEFAULTS = {
|
|
149591
|
+
defaultTTL: 10 * 60 * 1000,
|
|
149592
|
+
defaultMaxSize: 500,
|
|
149593
|
+
defaultName: "TimebackCache",
|
|
149594
|
+
studentTTL: 10 * 60 * 1000,
|
|
149595
|
+
studentMaxSize: 500,
|
|
149596
|
+
assessmentTTL: 30 * 60 * 1000,
|
|
149597
|
+
assessmentMaxSize: 200,
|
|
149598
|
+
enrollmentTTL: 5 * 1000,
|
|
149599
|
+
enrollmentMaxSize: 100
|
|
149600
|
+
};
|
|
149601
|
+
CONFIG_DEFAULTS = {
|
|
149602
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
149603
|
+
};
|
|
149604
|
+
DEFAULT_PLAYCADEMY_ORGANIZATION_ID = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
149605
|
+
PLAYCADEMY_DEFAULTS = {
|
|
149606
|
+
organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID,
|
|
149607
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
149608
|
+
};
|
|
149609
|
+
RESOURCE_DEFAULTS = {
|
|
149610
|
+
organization: {
|
|
149611
|
+
name: "Playcademy Studios",
|
|
149612
|
+
type: "department"
|
|
149613
|
+
},
|
|
149614
|
+
course: {
|
|
149615
|
+
gradingScheme: "STANDARD",
|
|
149616
|
+
level: {
|
|
149617
|
+
elementary: "Elementary",
|
|
149618
|
+
middle: "Middle",
|
|
149619
|
+
high: "High",
|
|
149620
|
+
ap: "AP"
|
|
149621
|
+
},
|
|
149622
|
+
metadata: {
|
|
149623
|
+
goals: {
|
|
149624
|
+
dailyXp: 50,
|
|
149625
|
+
dailyLessons: 3
|
|
149626
|
+
},
|
|
149627
|
+
metrics: {
|
|
149628
|
+
totalXp: 1000,
|
|
149629
|
+
totalLessons: 50
|
|
149630
|
+
}
|
|
149631
|
+
}
|
|
149632
|
+
},
|
|
149633
|
+
component: {
|
|
149634
|
+
sortOrder: 1,
|
|
149635
|
+
prerequisiteCriteria: "ALL"
|
|
149636
|
+
},
|
|
149637
|
+
resource: {
|
|
149638
|
+
vendorId: "playcademy",
|
|
149639
|
+
roles: ["primary"],
|
|
149640
|
+
importance: "primary",
|
|
149641
|
+
metadata: {
|
|
149642
|
+
type: "interactive",
|
|
149643
|
+
toolProvider: "Playcademy",
|
|
149644
|
+
instructionalMethod: "exploratory",
|
|
149645
|
+
language: "en-US"
|
|
149646
|
+
}
|
|
149647
|
+
},
|
|
149648
|
+
componentResource: {
|
|
149649
|
+
sortOrder: 1,
|
|
149650
|
+
lessonType: "quiz"
|
|
149651
|
+
}
|
|
149652
|
+
};
|
|
149653
|
+
HTTP_STATUS = {
|
|
149654
|
+
CLIENT_ERROR_MIN: 400,
|
|
149655
|
+
CLIENT_ERROR_MAX: 500,
|
|
149656
|
+
SERVER_ERROR_MIN: 500
|
|
149657
|
+
};
|
|
149658
|
+
ERROR_NAMES = {
|
|
149659
|
+
timebackAuth: "TimebackAuthError",
|
|
149660
|
+
timebackApi: "TimebackApiError",
|
|
149661
|
+
timebackConfig: "TimebackConfigError",
|
|
149662
|
+
timebackSdk: "TimebackSDKError"
|
|
149663
|
+
};
|
|
149664
|
+
});
|
|
149373
149665
|
function deriveSourcedIds(courseId) {
|
|
149374
149666
|
return {
|
|
149375
149667
|
course: courseId,
|
|
@@ -149385,7 +149677,8 @@ __export3(exports_verify, {
|
|
|
149385
149677
|
});
|
|
149386
149678
|
async function fetchTimebackConfig(client, courseId) {
|
|
149387
149679
|
const sourcedIds = deriveSourcedIds(courseId);
|
|
149388
|
-
const [course, component, resource, componentResource] = await Promise.all([
|
|
149680
|
+
const [org, course, component, resource, componentResource] = await Promise.all([
|
|
149681
|
+
client.oneroster.organizations.get(PLAYCADEMY_DEFAULTS.organization),
|
|
149389
149682
|
client.oneroster.courses.get(sourcedIds.course),
|
|
149390
149683
|
client.oneroster.courseComponents.get(sourcedIds.component),
|
|
149391
149684
|
client.oneroster.resources.get(sourcedIds.resource),
|
|
@@ -149393,9 +149686,9 @@ async function fetchTimebackConfig(client, courseId) {
|
|
|
149393
149686
|
]);
|
|
149394
149687
|
return {
|
|
149395
149688
|
organization: {
|
|
149396
|
-
name:
|
|
149397
|
-
type:
|
|
149398
|
-
identifier:
|
|
149689
|
+
name: org.name,
|
|
149690
|
+
type: org.type,
|
|
149691
|
+
identifier: org.identifier || PLAYCADEMY_DEFAULTS.organization
|
|
149399
149692
|
},
|
|
149400
149693
|
course: {
|
|
149401
149694
|
title: course.title || "",
|
|
@@ -149455,145 +149748,10 @@ async function verifyTimebackResources(client, courseId) {
|
|
|
149455
149748
|
}
|
|
149456
149749
|
};
|
|
149457
149750
|
}
|
|
149458
|
-
var init_verify5 = () => {
|
|
149459
|
-
|
|
149460
|
-
|
|
149461
|
-
|
|
149462
|
-
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
149463
|
-
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
149464
|
-
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
149465
|
-
COMMON_SWORD: "COMMON_SWORD",
|
|
149466
|
-
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
149467
|
-
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
149468
|
-
LAVA_LAMP: "LAVA_LAMP",
|
|
149469
|
-
BOOMBOX: "BOOMBOX",
|
|
149470
|
-
CABIN_BED: "CABIN_BED"
|
|
149471
|
-
};
|
|
149472
|
-
var CURRENCIES2 = {
|
|
149473
|
-
PRIMARY: ITEM_SLUGS2.PLAYCADEMY_CREDITS,
|
|
149474
|
-
XP: ITEM_SLUGS2.PLAYCADEMY_XP
|
|
149475
|
-
};
|
|
149476
|
-
var BADGES2 = {
|
|
149477
|
-
FOUNDING_MEMBER: ITEM_SLUGS2.FOUNDING_MEMBER_BADGE,
|
|
149478
|
-
EARLY_ADOPTER: ITEM_SLUGS2.EARLY_ADOPTER_BADGE,
|
|
149479
|
-
FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
|
|
149480
|
-
};
|
|
149481
|
-
var TIMEBACK_API_URLS = {
|
|
149482
|
-
production: "https://api.alpha-1edtech.ai",
|
|
149483
|
-
staging: "https://api.staging.alpha-1edtech.com"
|
|
149484
|
-
};
|
|
149485
|
-
var TIMEBACK_AUTH_URLS = {
|
|
149486
|
-
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
149487
|
-
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
149488
|
-
};
|
|
149489
|
-
var CALIPER_API_URLS = {
|
|
149490
|
-
production: "https://caliper.alpha-1edtech.ai",
|
|
149491
|
-
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
149492
|
-
};
|
|
149493
|
-
var ONEROSTER_ENDPOINTS = {
|
|
149494
|
-
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
149495
|
-
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
149496
|
-
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
149497
|
-
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
149498
|
-
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
149499
|
-
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
149500
|
-
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
149501
|
-
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
149502
|
-
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
149503
|
-
users: "/ims/oneroster/rostering/v1p2/users"
|
|
149504
|
-
};
|
|
149505
|
-
var CALIPER_ENDPOINTS = {
|
|
149506
|
-
events: "/caliper/event",
|
|
149507
|
-
validate: "/caliper/event/validate"
|
|
149508
|
-
};
|
|
149509
|
-
var createOneRosterUrls = (baseUrl) => {
|
|
149510
|
-
const effective = baseUrl || TIMEBACK_API_URLS.production;
|
|
149511
|
-
const base = effective.replace(/\/$/, "");
|
|
149512
|
-
return {
|
|
149513
|
-
user: (userId) => `${base}${ONEROSTER_ENDPOINTS.users}/${userId}`,
|
|
149514
|
-
course: (courseId) => `${base}${ONEROSTER_ENDPOINTS.courses}/${courseId}`,
|
|
149515
|
-
componentResource: (resourceId) => `${base}${ONEROSTER_ENDPOINTS.componentResources}/${resourceId}`
|
|
149516
|
-
};
|
|
149517
|
-
};
|
|
149518
|
-
var CALIPER_CONSTANTS = {
|
|
149519
|
-
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
149520
|
-
profile: "TimebackProfile",
|
|
149521
|
-
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
149522
|
-
};
|
|
149523
|
-
var TIMEBACK_EVENT_TYPES = {
|
|
149524
|
-
activityEvent: "ActivityEvent",
|
|
149525
|
-
timeSpentEvent: "TimeSpentEvent"
|
|
149526
|
-
};
|
|
149527
|
-
var TIMEBACK_ACTIONS = {
|
|
149528
|
-
completed: "Completed",
|
|
149529
|
-
spentTime: "SpentTime"
|
|
149530
|
-
};
|
|
149531
|
-
var TIMEBACK_TYPES = {
|
|
149532
|
-
user: "TimebackUser",
|
|
149533
|
-
activityContext: "TimebackActivityContext",
|
|
149534
|
-
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
149535
|
-
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
149536
|
-
};
|
|
149537
|
-
var ACTIVITY_METRIC_TYPES = {
|
|
149538
|
-
totalQuestions: "totalQuestions",
|
|
149539
|
-
correctQuestions: "correctQuestions",
|
|
149540
|
-
xpEarned: "xpEarned",
|
|
149541
|
-
masteredUnits: "masteredUnits"
|
|
149542
|
-
};
|
|
149543
|
-
var TIME_METRIC_TYPES = {
|
|
149544
|
-
active: "active",
|
|
149545
|
-
inactive: "inactive",
|
|
149546
|
-
waste: "waste",
|
|
149547
|
-
unknown: "unknown",
|
|
149548
|
-
antiPattern: "anti-pattern"
|
|
149549
|
-
};
|
|
149550
|
-
var ONEROSTER_STATUS = {
|
|
149551
|
-
active: "active",
|
|
149552
|
-
toBeDeleted: "tobedeleted"
|
|
149553
|
-
};
|
|
149554
|
-
var SCORE_STATUS = {
|
|
149555
|
-
exempt: "exempt",
|
|
149556
|
-
fullyGraded: "fully graded",
|
|
149557
|
-
notSubmitted: "not submitted",
|
|
149558
|
-
partiallyGraded: "partially graded",
|
|
149559
|
-
submitted: "submitted"
|
|
149560
|
-
};
|
|
149561
|
-
var ENV_VARS = {
|
|
149562
|
-
clientId: "TIMEBACK_CLIENT_ID",
|
|
149563
|
-
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
149564
|
-
baseUrl: "TIMEBACK_BASE_URL",
|
|
149565
|
-
environment: "TIMEBACK_ENVIRONMENT",
|
|
149566
|
-
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
149567
|
-
launchBaseUrl: "GAME_URL"
|
|
149568
|
-
};
|
|
149569
|
-
var HTTP_DEFAULTS = {
|
|
149570
|
-
timeout: 30000,
|
|
149571
|
-
retries: 3,
|
|
149572
|
-
retryBackoffBase: 2
|
|
149573
|
-
};
|
|
149574
|
-
var AUTH_DEFAULTS = {
|
|
149575
|
-
tokenCacheDuration: 50000
|
|
149576
|
-
};
|
|
149577
|
-
var CACHE_DEFAULTS = {
|
|
149578
|
-
defaultTTL: 10 * 60 * 1000,
|
|
149579
|
-
defaultMaxSize: 500,
|
|
149580
|
-
defaultName: "TimebackCache",
|
|
149581
|
-
studentTTL: 10 * 60 * 1000,
|
|
149582
|
-
studentMaxSize: 500,
|
|
149583
|
-
assessmentTTL: 30 * 60 * 1000,
|
|
149584
|
-
assessmentMaxSize: 200
|
|
149585
|
-
};
|
|
149586
|
-
var HTTP_STATUS = {
|
|
149587
|
-
CLIENT_ERROR_MIN: 400,
|
|
149588
|
-
CLIENT_ERROR_MAX: 500,
|
|
149589
|
-
SERVER_ERROR_MIN: 500
|
|
149590
|
-
};
|
|
149591
|
-
var ERROR_NAMES = {
|
|
149592
|
-
timebackAuth: "TimebackAuthError",
|
|
149593
|
-
timebackApi: "TimebackApiError",
|
|
149594
|
-
timebackConfig: "TimebackConfigError",
|
|
149595
|
-
timebackSdk: "TimebackSDKError"
|
|
149596
|
-
};
|
|
149751
|
+
var init_verify5 = __esm3(() => {
|
|
149752
|
+
init_constants();
|
|
149753
|
+
});
|
|
149754
|
+
init_constants();
|
|
149597
149755
|
|
|
149598
149756
|
class TimebackError extends Error {
|
|
149599
149757
|
constructor(message2) {
|
|
@@ -149655,6 +149813,25 @@ class ResourceNotFoundError extends TimebackError {
|
|
|
149655
149813
|
Object.setPrototypeOf(this, ResourceNotFoundError.prototype);
|
|
149656
149814
|
}
|
|
149657
149815
|
}
|
|
149816
|
+
init_constants();
|
|
149817
|
+
var isObject2 = (value) => typeof value === "object" && value !== null;
|
|
149818
|
+
var SUBJECT_VALUES = TIMEBACK_SUBJECTS;
|
|
149819
|
+
var GRADE_VALUES = TIMEBACK_GRADE_LEVELS;
|
|
149820
|
+
function isPlaycademyResourceMetadata(value) {
|
|
149821
|
+
if (!isObject2(value)) {
|
|
149822
|
+
return false;
|
|
149823
|
+
}
|
|
149824
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
149825
|
+
return true;
|
|
149826
|
+
}
|
|
149827
|
+
return isObject2(value.mastery);
|
|
149828
|
+
}
|
|
149829
|
+
function isTimebackSubject(value) {
|
|
149830
|
+
return typeof value === "string" && SUBJECT_VALUES.includes(value);
|
|
149831
|
+
}
|
|
149832
|
+
function isTimebackGrade(value) {
|
|
149833
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
|
|
149834
|
+
}
|
|
149658
149835
|
var isBrowser2 = () => {
|
|
149659
149836
|
const g5 = globalThis;
|
|
149660
149837
|
return typeof g5.window !== "undefined" && typeof g5.document !== "undefined";
|
|
@@ -150103,6 +150280,8 @@ async function updateTimebackResources(client, courseId, config2) {
|
|
|
150103
150280
|
updateComponentResourceLink(client, config2, sourcedIds)
|
|
150104
150281
|
]);
|
|
150105
150282
|
}
|
|
150283
|
+
init_constants();
|
|
150284
|
+
init_constants();
|
|
150106
150285
|
if (process.env.DEBUG === "true") {
|
|
150107
150286
|
process.env.TERM = "dumb";
|
|
150108
150287
|
}
|
|
@@ -150202,6 +150381,7 @@ async function getTimebackTokenResponse(config2) {
|
|
|
150202
150381
|
function getAuthUrl(environment = "production") {
|
|
150203
150382
|
return TIMEBACK_AUTH_URLS[environment];
|
|
150204
150383
|
}
|
|
150384
|
+
init_constants();
|
|
150205
150385
|
async function request({
|
|
150206
150386
|
path: path2,
|
|
150207
150387
|
baseUrl,
|
|
@@ -150307,6 +150487,154 @@ async function requestCaliper(options) {
|
|
|
150307
150487
|
baseUrl: caliperBase
|
|
150308
150488
|
});
|
|
150309
150489
|
}
|
|
150490
|
+
init_constants();
|
|
150491
|
+
function createCaliperNamespace(client) {
|
|
150492
|
+
const urls = createOneRosterUrls(client.getBaseUrl());
|
|
150493
|
+
const caliper = {
|
|
150494
|
+
emit: async (event, sensorUrl) => {
|
|
150495
|
+
const envelope = {
|
|
150496
|
+
sensor: sensorUrl,
|
|
150497
|
+
sendTime: new Date().toISOString(),
|
|
150498
|
+
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
150499
|
+
data: [event]
|
|
150500
|
+
};
|
|
150501
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
150502
|
+
},
|
|
150503
|
+
emitBatch: async (events, sensorUrl) => {
|
|
150504
|
+
if (events.length === 0)
|
|
150505
|
+
return;
|
|
150506
|
+
const envelope = {
|
|
150507
|
+
sensor: sensorUrl,
|
|
150508
|
+
sendTime: new Date().toISOString(),
|
|
150509
|
+
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
150510
|
+
data: events
|
|
150511
|
+
};
|
|
150512
|
+
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
150513
|
+
},
|
|
150514
|
+
emitActivityEvent: async (data) => {
|
|
150515
|
+
const event = {
|
|
150516
|
+
"@context": CALIPER_CONSTANTS.context,
|
|
150517
|
+
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
150518
|
+
type: TIMEBACK_EVENT_TYPES.activityEvent,
|
|
150519
|
+
eventTime: new Date().toISOString(),
|
|
150520
|
+
profile: CALIPER_CONSTANTS.profile,
|
|
150521
|
+
actor: {
|
|
150522
|
+
id: urls.user(data.studentId),
|
|
150523
|
+
type: TIMEBACK_TYPES.user,
|
|
150524
|
+
email: data.studentEmail
|
|
150525
|
+
},
|
|
150526
|
+
action: TIMEBACK_ACTIONS.completed,
|
|
150527
|
+
object: {
|
|
150528
|
+
id: caliper.buildActivityUrl(data),
|
|
150529
|
+
type: TIMEBACK_TYPES.activityContext,
|
|
150530
|
+
subject: data.subject,
|
|
150531
|
+
app: {
|
|
150532
|
+
name: data.appName
|
|
150533
|
+
},
|
|
150534
|
+
activity: {
|
|
150535
|
+
name: data.activityName
|
|
150536
|
+
},
|
|
150537
|
+
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
150538
|
+
process: false
|
|
150539
|
+
},
|
|
150540
|
+
generated: {
|
|
150541
|
+
id: `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
150542
|
+
type: TIMEBACK_TYPES.activityMetricsCollection,
|
|
150543
|
+
attempt: data.attemptNumber || 1,
|
|
150544
|
+
items: [
|
|
150545
|
+
...data.totalQuestions !== undefined ? [
|
|
150546
|
+
{
|
|
150547
|
+
type: ACTIVITY_METRIC_TYPES.totalQuestions,
|
|
150548
|
+
value: data.totalQuestions
|
|
150549
|
+
}
|
|
150550
|
+
] : [],
|
|
150551
|
+
...data.correctQuestions !== undefined ? [
|
|
150552
|
+
{
|
|
150553
|
+
type: ACTIVITY_METRIC_TYPES.correctQuestions,
|
|
150554
|
+
value: data.correctQuestions
|
|
150555
|
+
}
|
|
150556
|
+
] : [],
|
|
150557
|
+
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES.xpEarned, value: data.xpEarned }] : [],
|
|
150558
|
+
...data.masteredUnits !== undefined ? [
|
|
150559
|
+
{
|
|
150560
|
+
type: ACTIVITY_METRIC_TYPES.masteredUnits,
|
|
150561
|
+
value: data.masteredUnits
|
|
150562
|
+
}
|
|
150563
|
+
] : []
|
|
150564
|
+
],
|
|
150565
|
+
...data.extensions ? { extensions: data.extensions } : {}
|
|
150566
|
+
}
|
|
150567
|
+
};
|
|
150568
|
+
return caliper.emit(event, data.sensorUrl);
|
|
150569
|
+
},
|
|
150570
|
+
emitTimeSpentEvent: async (data) => {
|
|
150571
|
+
const event = {
|
|
150572
|
+
"@context": CALIPER_CONSTANTS.context,
|
|
150573
|
+
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
150574
|
+
type: TIMEBACK_EVENT_TYPES.timeSpentEvent,
|
|
150575
|
+
eventTime: new Date().toISOString(),
|
|
150576
|
+
profile: CALIPER_CONSTANTS.profile,
|
|
150577
|
+
actor: {
|
|
150578
|
+
id: urls.user(data.studentId),
|
|
150579
|
+
type: TIMEBACK_TYPES.user,
|
|
150580
|
+
email: data.studentEmail
|
|
150581
|
+
},
|
|
150582
|
+
action: TIMEBACK_ACTIONS.spentTime,
|
|
150583
|
+
object: {
|
|
150584
|
+
id: caliper.buildActivityUrl(data),
|
|
150585
|
+
type: TIMEBACK_TYPES.activityContext,
|
|
150586
|
+
subject: data.subject,
|
|
150587
|
+
app: {
|
|
150588
|
+
name: data.appName
|
|
150589
|
+
},
|
|
150590
|
+
activity: {
|
|
150591
|
+
name: data.activityName
|
|
150592
|
+
},
|
|
150593
|
+
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
150594
|
+
process: false
|
|
150595
|
+
},
|
|
150596
|
+
generated: {
|
|
150597
|
+
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
150598
|
+
type: TIMEBACK_TYPES.timeSpentMetricsCollection,
|
|
150599
|
+
items: [
|
|
150600
|
+
{ type: TIME_METRIC_TYPES.active, value: data.activeTimeSeconds },
|
|
150601
|
+
...data.inactiveTimeSeconds !== undefined ? [
|
|
150602
|
+
{
|
|
150603
|
+
type: TIME_METRIC_TYPES.inactive,
|
|
150604
|
+
value: data.inactiveTimeSeconds
|
|
150605
|
+
}
|
|
150606
|
+
] : [],
|
|
150607
|
+
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES.waste, value: data.wasteTimeSeconds }] : []
|
|
150608
|
+
]
|
|
150609
|
+
}
|
|
150610
|
+
};
|
|
150611
|
+
return caliper.emit(event, data.sensorUrl);
|
|
150612
|
+
},
|
|
150613
|
+
buildActivityUrl: (data) => {
|
|
150614
|
+
const base = data.sensorUrl.replace(/\/$/, "");
|
|
150615
|
+
return `${base}/activities/${data.courseId}/${data.activityId}/${crypto.randomUUID()}`;
|
|
150616
|
+
}
|
|
150617
|
+
};
|
|
150618
|
+
return caliper;
|
|
150619
|
+
}
|
|
150620
|
+
function createEduBridgeNamespace(client) {
|
|
150621
|
+
const enrollments = {
|
|
150622
|
+
listByUser: async (userId) => {
|
|
150623
|
+
const response = await client["request"](`/edubridge/enrollments/user/${userId}`, "GET");
|
|
150624
|
+
return response.data;
|
|
150625
|
+
}
|
|
150626
|
+
};
|
|
150627
|
+
const analytics = {
|
|
150628
|
+
getEnrollmentFacts: async (enrollmentId) => {
|
|
150629
|
+
return client["request"](`/edubridge/analytics/enrollment/${enrollmentId}`, "GET");
|
|
150630
|
+
}
|
|
150631
|
+
};
|
|
150632
|
+
return {
|
|
150633
|
+
enrollments,
|
|
150634
|
+
analytics
|
|
150635
|
+
};
|
|
150636
|
+
}
|
|
150637
|
+
init_constants();
|
|
150310
150638
|
function logTimebackError(operation, error2, context) {
|
|
150311
150639
|
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
150312
150640
|
if (error2 instanceof TimebackApiError) {
|
|
@@ -150342,6 +150670,17 @@ function createOneRosterNamespace(client) {
|
|
|
150342
150670
|
listByCourse: async (courseSourcedId) => {
|
|
150343
150671
|
const res = await client["request"](`${ONEROSTER_ENDPOINTS.courses}/${courseSourcedId}/classes`, "GET");
|
|
150344
150672
|
return res.classes;
|
|
150673
|
+
},
|
|
150674
|
+
listByStudent: async (userSourcedId, options) => {
|
|
150675
|
+
const queryParams = new URLSearchParams;
|
|
150676
|
+
if (options?.limit)
|
|
150677
|
+
queryParams.set("limit", String(options.limit));
|
|
150678
|
+
if (options?.offset)
|
|
150679
|
+
queryParams.set("offset", String(options.offset));
|
|
150680
|
+
const endpoint = `${ONEROSTER_ENDPOINTS.users}/${userSourcedId}/classes`;
|
|
150681
|
+
const url = queryParams.toString() ? `${endpoint}?${queryParams}` : endpoint;
|
|
150682
|
+
const res = await client["request"](url, "GET");
|
|
150683
|
+
return res.classes || [];
|
|
150345
150684
|
}
|
|
150346
150685
|
},
|
|
150347
150686
|
organizations: {
|
|
@@ -150349,7 +150688,7 @@ function createOneRosterNamespace(client) {
|
|
|
150349
150688
|
return client["request"](ONEROSTER_ENDPOINTS.organizations, "POST", data);
|
|
150350
150689
|
},
|
|
150351
150690
|
get: async (sourcedId) => {
|
|
150352
|
-
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET");
|
|
150691
|
+
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "GET").then((res) => res.org);
|
|
150353
150692
|
},
|
|
150354
150693
|
update: async (sourcedId, data) => {
|
|
150355
150694
|
return client["request"](`${ONEROSTER_ENDPOINTS.organizations}/${sourcedId}`, "PUT", data);
|
|
@@ -150457,14 +150796,30 @@ function createOneRosterNamespace(client) {
|
|
|
150457
150796
|
update: async (sourcedId, data) => {
|
|
150458
150797
|
return client["request"](`${ONEROSTER_ENDPOINTS.assessmentResults}/${sourcedId}`, "PUT", data);
|
|
150459
150798
|
},
|
|
150460
|
-
|
|
150799
|
+
getAttemptStats: async (studentId, lineItemId) => {
|
|
150461
150800
|
try {
|
|
150462
150801
|
const filter2 = `student.sourcedId='${studentId}' AND assessmentLineItem.sourcedId='${lineItemId}'`;
|
|
150463
|
-
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}
|
|
150802
|
+
const url = `${ONEROSTER_ENDPOINTS.assessmentResults}?filter=${encodeURIComponent(filter2)}`;
|
|
150464
150803
|
const response = await client["request"](url, "GET");
|
|
150465
|
-
|
|
150804
|
+
const results = response.assessmentResults || [];
|
|
150805
|
+
if (results.length === 0)
|
|
150806
|
+
return null;
|
|
150807
|
+
let maxAttemptResult = results[0];
|
|
150808
|
+
let maxAttemptNumber = maxAttemptResult.metadata?.attemptNumber || 0;
|
|
150809
|
+
let activeAttemptCount = 0;
|
|
150810
|
+
for (const result of results) {
|
|
150811
|
+
const attemptNumber = result.metadata?.attemptNumber || 0;
|
|
150812
|
+
if (attemptNumber > maxAttemptNumber) {
|
|
150813
|
+
maxAttemptNumber = attemptNumber;
|
|
150814
|
+
maxAttemptResult = result;
|
|
150815
|
+
}
|
|
150816
|
+
if (result.status === "active") {
|
|
150817
|
+
activeAttemptCount++;
|
|
150818
|
+
}
|
|
150819
|
+
}
|
|
150820
|
+
return { maxAttemptNumber, activeAttemptCount, maxAttemptResult };
|
|
150466
150821
|
} catch (error2) {
|
|
150467
|
-
logTimebackError("query
|
|
150822
|
+
logTimebackError("query attempt stats", error2, {
|
|
150468
150823
|
studentId,
|
|
150469
150824
|
lineItemId
|
|
150470
150825
|
});
|
|
@@ -150477,7 +150832,7 @@ function createOneRosterNamespace(client) {
|
|
|
150477
150832
|
return client["request"](ONEROSTER_ENDPOINTS.users, "POST", { user: data });
|
|
150478
150833
|
},
|
|
150479
150834
|
get: async (sourcedId) => {
|
|
150480
|
-
return client["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET");
|
|
150835
|
+
return client["request"](`${ONEROSTER_ENDPOINTS.users}/${sourcedId}`, "GET").then((res) => res.user);
|
|
150481
150836
|
},
|
|
150482
150837
|
findByEmail: async (email) => {
|
|
150483
150838
|
const params = new URLSearchParams({ filter: `email='${email}'` });
|
|
@@ -150493,130 +150848,8 @@ function createOneRosterNamespace(client) {
|
|
|
150493
150848
|
}
|
|
150494
150849
|
};
|
|
150495
150850
|
}
|
|
150496
|
-
|
|
150497
|
-
|
|
150498
|
-
const caliper = {
|
|
150499
|
-
emit: async (event, sensorUrl) => {
|
|
150500
|
-
const envelope = {
|
|
150501
|
-
sensor: sensorUrl,
|
|
150502
|
-
sendTime: new Date().toISOString(),
|
|
150503
|
-
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
150504
|
-
data: [event]
|
|
150505
|
-
};
|
|
150506
|
-
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
150507
|
-
},
|
|
150508
|
-
emitBatch: async (events, sensorUrl) => {
|
|
150509
|
-
if (events.length === 0)
|
|
150510
|
-
return;
|
|
150511
|
-
const envelope = {
|
|
150512
|
-
sensor: sensorUrl,
|
|
150513
|
-
sendTime: new Date().toISOString(),
|
|
150514
|
-
dataVersion: CALIPER_CONSTANTS.dataVersion,
|
|
150515
|
-
data: events
|
|
150516
|
-
};
|
|
150517
|
-
return client["requestCaliper"](CALIPER_ENDPOINTS.events, "POST", envelope);
|
|
150518
|
-
},
|
|
150519
|
-
emitActivityEvent: async (data) => {
|
|
150520
|
-
const event = {
|
|
150521
|
-
"@context": CALIPER_CONSTANTS.context,
|
|
150522
|
-
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
150523
|
-
type: TIMEBACK_EVENT_TYPES.activityEvent,
|
|
150524
|
-
eventTime: new Date().toISOString(),
|
|
150525
|
-
profile: CALIPER_CONSTANTS.profile,
|
|
150526
|
-
actor: {
|
|
150527
|
-
id: urls.user(data.studentId),
|
|
150528
|
-
type: TIMEBACK_TYPES.user,
|
|
150529
|
-
email: data.studentEmail
|
|
150530
|
-
},
|
|
150531
|
-
action: TIMEBACK_ACTIONS.completed,
|
|
150532
|
-
object: {
|
|
150533
|
-
id: urls.componentResource(data.activityId),
|
|
150534
|
-
type: TIMEBACK_TYPES.activityContext,
|
|
150535
|
-
subject: data.subject,
|
|
150536
|
-
app: {
|
|
150537
|
-
name: data.appName
|
|
150538
|
-
},
|
|
150539
|
-
activity: {
|
|
150540
|
-
name: data.activityName
|
|
150541
|
-
},
|
|
150542
|
-
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
150543
|
-
process: true
|
|
150544
|
-
},
|
|
150545
|
-
generated: {
|
|
150546
|
-
id: `urn:timeback:metrics:activity-completion-${crypto.randomUUID()}`,
|
|
150547
|
-
type: TIMEBACK_TYPES.activityMetricsCollection,
|
|
150548
|
-
attempt: data.attemptNumber || 1,
|
|
150549
|
-
items: [
|
|
150550
|
-
...data.totalQuestions !== undefined ? [
|
|
150551
|
-
{
|
|
150552
|
-
type: ACTIVITY_METRIC_TYPES.totalQuestions,
|
|
150553
|
-
value: data.totalQuestions
|
|
150554
|
-
}
|
|
150555
|
-
] : [],
|
|
150556
|
-
...data.correctQuestions !== undefined ? [
|
|
150557
|
-
{
|
|
150558
|
-
type: ACTIVITY_METRIC_TYPES.correctQuestions,
|
|
150559
|
-
value: data.correctQuestions
|
|
150560
|
-
}
|
|
150561
|
-
] : [],
|
|
150562
|
-
...data.xpEarned !== undefined ? [{ type: ACTIVITY_METRIC_TYPES.xpEarned, value: data.xpEarned }] : [],
|
|
150563
|
-
...data.masteredUnits !== undefined ? [
|
|
150564
|
-
{
|
|
150565
|
-
type: ACTIVITY_METRIC_TYPES.masteredUnits,
|
|
150566
|
-
value: data.masteredUnits
|
|
150567
|
-
}
|
|
150568
|
-
] : []
|
|
150569
|
-
]
|
|
150570
|
-
}
|
|
150571
|
-
};
|
|
150572
|
-
return caliper.emit(event, data.sensorUrl);
|
|
150573
|
-
},
|
|
150574
|
-
emitTimeSpentEvent: async (data) => {
|
|
150575
|
-
const event = {
|
|
150576
|
-
"@context": CALIPER_CONSTANTS.context,
|
|
150577
|
-
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
150578
|
-
type: TIMEBACK_EVENT_TYPES.timeSpentEvent,
|
|
150579
|
-
eventTime: new Date().toISOString(),
|
|
150580
|
-
profile: CALIPER_CONSTANTS.profile,
|
|
150581
|
-
actor: {
|
|
150582
|
-
id: urls.user(data.studentId),
|
|
150583
|
-
type: TIMEBACK_TYPES.user,
|
|
150584
|
-
email: data.studentEmail
|
|
150585
|
-
},
|
|
150586
|
-
action: TIMEBACK_ACTIONS.spentTime,
|
|
150587
|
-
object: {
|
|
150588
|
-
id: urls.componentResource(data.activityId),
|
|
150589
|
-
type: TIMEBACK_TYPES.activityContext,
|
|
150590
|
-
subject: data.subject,
|
|
150591
|
-
app: {
|
|
150592
|
-
name: data.appName
|
|
150593
|
-
},
|
|
150594
|
-
activity: {
|
|
150595
|
-
name: data.activityName
|
|
150596
|
-
},
|
|
150597
|
-
course: { id: urls.course(data.courseId), name: data.activityName },
|
|
150598
|
-
process: false
|
|
150599
|
-
},
|
|
150600
|
-
generated: {
|
|
150601
|
-
id: `urn:timeback:metrics:time-spent-${crypto.randomUUID()}`,
|
|
150602
|
-
type: TIMEBACK_TYPES.timeSpentMetricsCollection,
|
|
150603
|
-
items: [
|
|
150604
|
-
{ type: TIME_METRIC_TYPES.active, value: data.activeTimeSeconds },
|
|
150605
|
-
...data.inactiveTimeSeconds !== undefined ? [
|
|
150606
|
-
{
|
|
150607
|
-
type: TIME_METRIC_TYPES.inactive,
|
|
150608
|
-
value: data.inactiveTimeSeconds
|
|
150609
|
-
}
|
|
150610
|
-
] : [],
|
|
150611
|
-
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES.waste, value: data.wasteTimeSeconds }] : []
|
|
150612
|
-
]
|
|
150613
|
-
}
|
|
150614
|
-
};
|
|
150615
|
-
return caliper.emit(event, data.sensorUrl);
|
|
150616
|
-
}
|
|
150617
|
-
};
|
|
150618
|
-
return caliper;
|
|
150619
|
-
}
|
|
150851
|
+
init_constants();
|
|
150852
|
+
init_constants();
|
|
150620
150853
|
|
|
150621
150854
|
class TimebackCache {
|
|
150622
150855
|
cache = new Map;
|
|
@@ -150722,6 +150955,8 @@ class TimebackCache {
|
|
|
150722
150955
|
class TimebackCacheManager {
|
|
150723
150956
|
studentCache;
|
|
150724
150957
|
assessmentLineItemCache;
|
|
150958
|
+
resourceMasteryCache;
|
|
150959
|
+
enrollmentCache;
|
|
150725
150960
|
constructor() {
|
|
150726
150961
|
this.studentCache = new TimebackCache({
|
|
150727
150962
|
defaultTTL: CACHE_DEFAULTS.studentTTL,
|
|
@@ -150733,6 +150968,16 @@ class TimebackCacheManager {
|
|
|
150733
150968
|
maxSize: CACHE_DEFAULTS.assessmentMaxSize,
|
|
150734
150969
|
name: "AssessmentLineItemCache"
|
|
150735
150970
|
});
|
|
150971
|
+
this.resourceMasteryCache = new TimebackCache({
|
|
150972
|
+
defaultTTL: CACHE_DEFAULTS.assessmentTTL,
|
|
150973
|
+
maxSize: CACHE_DEFAULTS.assessmentMaxSize,
|
|
150974
|
+
name: "ResourceMasteryCache"
|
|
150975
|
+
});
|
|
150976
|
+
this.enrollmentCache = new TimebackCache({
|
|
150977
|
+
defaultTTL: CACHE_DEFAULTS.enrollmentTTL,
|
|
150978
|
+
maxSize: CACHE_DEFAULTS.enrollmentMaxSize,
|
|
150979
|
+
name: "EnrollmentCache"
|
|
150980
|
+
});
|
|
150736
150981
|
}
|
|
150737
150982
|
getStudent(key) {
|
|
150738
150983
|
return this.studentCache.get(key);
|
|
@@ -150746,26 +150991,188 @@ class TimebackCacheManager {
|
|
|
150746
150991
|
setAssessmentLineItem(key, lineItemId) {
|
|
150747
150992
|
this.assessmentLineItemCache.set(key, lineItemId);
|
|
150748
150993
|
}
|
|
150994
|
+
getResourceMasterableUnits(key) {
|
|
150995
|
+
return this.resourceMasteryCache.get(key);
|
|
150996
|
+
}
|
|
150997
|
+
setResourceMasterableUnits(key, value) {
|
|
150998
|
+
this.resourceMasteryCache.set(key, value);
|
|
150999
|
+
}
|
|
151000
|
+
getEnrollments(studentId) {
|
|
151001
|
+
return this.enrollmentCache.get(studentId);
|
|
151002
|
+
}
|
|
151003
|
+
setEnrollments(studentId, enrollments) {
|
|
151004
|
+
this.enrollmentCache.set(studentId, enrollments);
|
|
151005
|
+
}
|
|
150749
151006
|
clearAll() {
|
|
150750
151007
|
this.studentCache.clear();
|
|
150751
151008
|
this.assessmentLineItemCache.clear();
|
|
151009
|
+
this.resourceMasteryCache.clear();
|
|
151010
|
+
this.enrollmentCache.clear();
|
|
150752
151011
|
log3.info("[TimebackCacheManager] All caches cleared");
|
|
150753
151012
|
}
|
|
150754
151013
|
getStats() {
|
|
150755
151014
|
return {
|
|
150756
151015
|
studentCache: this.studentCache.stats(),
|
|
150757
|
-
assessmentLineItemCache: this.assessmentLineItemCache.stats()
|
|
151016
|
+
assessmentLineItemCache: this.assessmentLineItemCache.stats(),
|
|
151017
|
+
resourceMasteryCache: this.resourceMasteryCache.stats(),
|
|
151018
|
+
enrollmentCache: this.enrollmentCache.stats()
|
|
150758
151019
|
};
|
|
150759
151020
|
}
|
|
150760
151021
|
cleanup() {
|
|
150761
151022
|
this.studentCache.cleanup();
|
|
150762
151023
|
this.assessmentLineItemCache.cleanup();
|
|
151024
|
+
this.resourceMasteryCache.cleanup();
|
|
151025
|
+
this.enrollmentCache.cleanup();
|
|
150763
151026
|
log3.debug("[TimebackCacheManager] Cache cleanup completed");
|
|
150764
151027
|
}
|
|
150765
151028
|
}
|
|
151029
|
+
init_constants();
|
|
151030
|
+
|
|
151031
|
+
class MasteryTracker {
|
|
151032
|
+
cacheManager;
|
|
151033
|
+
onerosterNamespace;
|
|
151034
|
+
edubridgeNamespace;
|
|
151035
|
+
constructor(cacheManager, onerosterNamespace, edubridgeNamespace) {
|
|
151036
|
+
this.cacheManager = cacheManager;
|
|
151037
|
+
this.onerosterNamespace = onerosterNamespace;
|
|
151038
|
+
this.edubridgeNamespace = edubridgeNamespace;
|
|
151039
|
+
}
|
|
151040
|
+
async checkProgress(input) {
|
|
151041
|
+
const { studentId, courseId, resourceId, masteredUnits } = input;
|
|
151042
|
+
if (typeof masteredUnits !== "number" || masteredUnits <= 0) {
|
|
151043
|
+
return;
|
|
151044
|
+
}
|
|
151045
|
+
const masterableUnits = await this.resolveMasterableUnits(resourceId);
|
|
151046
|
+
if (!masterableUnits || masterableUnits <= 0) {
|
|
151047
|
+
log3.warn("[MasteryTracker] No masterableUnits configured for course", {
|
|
151048
|
+
courseId,
|
|
151049
|
+
resourceId
|
|
151050
|
+
});
|
|
151051
|
+
return;
|
|
151052
|
+
}
|
|
151053
|
+
const facts = await this.fetchEnrollmentAnalyticsFacts(studentId, courseId);
|
|
151054
|
+
if (!facts) {
|
|
151055
|
+
log3.warn("[MasteryTracker] Unable to retrieve analytics for mastery-based completion", {
|
|
151056
|
+
studentId,
|
|
151057
|
+
courseId
|
|
151058
|
+
});
|
|
151059
|
+
return;
|
|
151060
|
+
}
|
|
151061
|
+
const historicalMasteredUnits = this.sumAnalyticsMetric(facts, "masteredUnits");
|
|
151062
|
+
const totalMastered = historicalMasteredUnits + masteredUnits;
|
|
151063
|
+
const rawPct = totalMastered / masterableUnits * 100;
|
|
151064
|
+
const pctCompleteApp = Math.min(100, Math.max(0, Math.round(rawPct)));
|
|
151065
|
+
const masteryAchieved = totalMastered >= masterableUnits;
|
|
151066
|
+
return { pctCompleteApp, masteryAchieved };
|
|
151067
|
+
}
|
|
151068
|
+
async createCompletionEntry(studentId, courseId, classId, appName) {
|
|
151069
|
+
const ids = deriveSourcedIds(courseId);
|
|
151070
|
+
const lineItemId = `${ids.course}-mastery-completion-assessment`;
|
|
151071
|
+
const resultId = `${lineItemId}:${studentId}:completion`;
|
|
151072
|
+
try {
|
|
151073
|
+
await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
151074
|
+
sourcedId: lineItemId,
|
|
151075
|
+
title: "Mastery Completion",
|
|
151076
|
+
status: ONEROSTER_STATUS.active,
|
|
151077
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } },
|
|
151078
|
+
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : {}
|
|
151079
|
+
});
|
|
151080
|
+
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
151081
|
+
sourcedId: resultId,
|
|
151082
|
+
status: ONEROSTER_STATUS.active,
|
|
151083
|
+
assessmentLineItem: { sourcedId: lineItemId },
|
|
151084
|
+
student: { sourcedId: studentId },
|
|
151085
|
+
score: 100,
|
|
151086
|
+
scoreDate: new Date().toISOString(),
|
|
151087
|
+
scoreStatus: SCORE_STATUS.fullyGraded,
|
|
151088
|
+
inProgress: "false",
|
|
151089
|
+
metadata: {
|
|
151090
|
+
isMasteryCompletion: true,
|
|
151091
|
+
completedAt: new Date().toISOString(),
|
|
151092
|
+
appName
|
|
151093
|
+
}
|
|
151094
|
+
});
|
|
151095
|
+
log3.info("[MasteryTracker] Created mastery completion entry", {
|
|
151096
|
+
studentId,
|
|
151097
|
+
lineItemId,
|
|
151098
|
+
resultId
|
|
151099
|
+
});
|
|
151100
|
+
} catch (error2) {
|
|
151101
|
+
log3.error("[MasteryTracker] Failed to create mastery completion entry", {
|
|
151102
|
+
studentId,
|
|
151103
|
+
lineItemId,
|
|
151104
|
+
error: error2
|
|
151105
|
+
});
|
|
151106
|
+
}
|
|
151107
|
+
}
|
|
151108
|
+
async resolveMasterableUnits(resourceId) {
|
|
151109
|
+
if (!resourceId) {
|
|
151110
|
+
return;
|
|
151111
|
+
}
|
|
151112
|
+
const cached = this.cacheManager.getResourceMasterableUnits(resourceId);
|
|
151113
|
+
if (cached !== undefined) {
|
|
151114
|
+
return cached === null ? undefined : cached;
|
|
151115
|
+
}
|
|
151116
|
+
try {
|
|
151117
|
+
const resource = await this.onerosterNamespace.resources.get(resourceId);
|
|
151118
|
+
const playcademyMetadata = resource.metadata?.playcademy;
|
|
151119
|
+
if (!playcademyMetadata) {
|
|
151120
|
+
return;
|
|
151121
|
+
}
|
|
151122
|
+
const masterableUnits = isPlaycademyResourceMetadata(playcademyMetadata) ? playcademyMetadata.mastery?.masterableUnits : undefined;
|
|
151123
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, masterableUnits ?? null);
|
|
151124
|
+
return masterableUnits;
|
|
151125
|
+
} catch (error2) {
|
|
151126
|
+
log3.error("[MasteryTracker] Failed to fetch resource metadata for mastery config", {
|
|
151127
|
+
resourceId,
|
|
151128
|
+
error: error2
|
|
151129
|
+
});
|
|
151130
|
+
this.cacheManager.setResourceMasterableUnits(resourceId, null);
|
|
151131
|
+
return;
|
|
151132
|
+
}
|
|
151133
|
+
}
|
|
151134
|
+
async fetchEnrollmentAnalyticsFacts(studentId, courseId) {
|
|
151135
|
+
try {
|
|
151136
|
+
const enrollments = await this.edubridgeNamespace.enrollments.listByUser(studentId);
|
|
151137
|
+
const enrollment = enrollments.find((e) => e.course.id === courseId);
|
|
151138
|
+
if (!enrollment) {
|
|
151139
|
+
log3.warn("[MasteryTracker] Enrollment not found for student/course", {
|
|
151140
|
+
studentId,
|
|
151141
|
+
courseId
|
|
151142
|
+
});
|
|
151143
|
+
return;
|
|
151144
|
+
}
|
|
151145
|
+
const analytics = await this.edubridgeNamespace.analytics.getEnrollmentFacts(enrollment.id);
|
|
151146
|
+
return analytics.facts;
|
|
151147
|
+
} catch (error2) {
|
|
151148
|
+
log3.error("[MasteryTracker] Failed to load enrollment analytics facts", {
|
|
151149
|
+
studentId,
|
|
151150
|
+
courseId,
|
|
151151
|
+
error: error2
|
|
151152
|
+
});
|
|
151153
|
+
return;
|
|
151154
|
+
}
|
|
151155
|
+
}
|
|
151156
|
+
sumAnalyticsMetric(facts, metric) {
|
|
151157
|
+
if (!facts) {
|
|
151158
|
+
return 0;
|
|
151159
|
+
}
|
|
151160
|
+
let total = 0;
|
|
151161
|
+
Object.values(facts).forEach((dateFacts) => {
|
|
151162
|
+
Object.values(dateFacts).forEach((subjectFacts) => {
|
|
151163
|
+
const metrics = subjectFacts.activityMetrics;
|
|
151164
|
+
if (metrics && typeof metrics[metric] === "number") {
|
|
151165
|
+
total += metrics[metric];
|
|
151166
|
+
}
|
|
151167
|
+
});
|
|
151168
|
+
});
|
|
151169
|
+
return total;
|
|
151170
|
+
}
|
|
151171
|
+
}
|
|
150766
151172
|
function kebabToTitleCase(kebabStr) {
|
|
150767
151173
|
return kebabStr.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
150768
151174
|
}
|
|
151175
|
+
init_constants();
|
|
150769
151176
|
function validateProgressData(progressData) {
|
|
150770
151177
|
if (!progressData.subject) {
|
|
150771
151178
|
throw new ConfigurationError("subject", "Subject is required for Caliper events. Provide it in progressData.subject");
|
|
@@ -150819,36 +151226,47 @@ class ProgressRecorder {
|
|
|
150819
151226
|
cacheManager;
|
|
150820
151227
|
onerosterNamespace;
|
|
150821
151228
|
caliperNamespace;
|
|
150822
|
-
|
|
151229
|
+
masteryTracker;
|
|
151230
|
+
constructor(studentResolver, cacheManager, onerosterNamespace, caliperNamespace, masteryTracker) {
|
|
150823
151231
|
this.studentResolver = studentResolver;
|
|
150824
151232
|
this.cacheManager = cacheManager;
|
|
150825
151233
|
this.onerosterNamespace = onerosterNamespace;
|
|
150826
151234
|
this.caliperNamespace = caliperNamespace;
|
|
151235
|
+
this.masteryTracker = masteryTracker;
|
|
150827
151236
|
}
|
|
150828
151237
|
async record(courseId, studentIdentifier, progressData) {
|
|
150829
151238
|
validateProgressData(progressData);
|
|
150830
|
-
const ids =
|
|
150831
|
-
const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
|
|
150832
|
-
const activityName = progressData.activityName || kebabToTitleCase(activityId);
|
|
150833
|
-
const classId = progressData.classId;
|
|
150834
|
-
const courseName = progressData.courseName || "Game Course";
|
|
150835
|
-
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
151239
|
+
const { ids, activityId, activityName, courseName, student } = await this.resolveContext(courseId, studentIdentifier, progressData);
|
|
150836
151240
|
const { id: studentId, email: studentEmail } = student;
|
|
150837
151241
|
const { score, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber } = progressData;
|
|
150838
|
-
const
|
|
150839
|
-
|
|
150840
|
-
|
|
150841
|
-
|
|
150842
|
-
|
|
150843
|
-
|
|
150844
|
-
|
|
150845
|
-
|
|
150846
|
-
|
|
150847
|
-
|
|
151242
|
+
const actualLineItemId = await this.resolveAssessmentLineItem(activityId, activityName, progressData.classId, ids);
|
|
151243
|
+
const currentAttemptNumber = await this.resolveAttemptNumber(attemptNumber, score, studentId, actualLineItemId);
|
|
151244
|
+
const isFirstActiveAttempt = currentAttemptNumber === 1;
|
|
151245
|
+
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstActiveAttempt);
|
|
151246
|
+
let extensions = progressData.extensions;
|
|
151247
|
+
const masteryProgress = await this.masteryTracker.checkProgress({
|
|
151248
|
+
studentId,
|
|
151249
|
+
courseId,
|
|
151250
|
+
resourceId: ids.resource,
|
|
151251
|
+
masteredUnits: progressData.masteredUnits ?? 0
|
|
151252
|
+
});
|
|
151253
|
+
let pctCompleteApp;
|
|
151254
|
+
let masteryAchieved = false;
|
|
151255
|
+
let scoreStatus = SCORE_STATUS.fullyGraded;
|
|
151256
|
+
const inProgress = "false";
|
|
151257
|
+
if (masteryProgress) {
|
|
151258
|
+
masteryAchieved = masteryProgress.masteryAchieved;
|
|
151259
|
+
pctCompleteApp = masteryProgress.pctCompleteApp;
|
|
151260
|
+
extensions = {
|
|
151261
|
+
...extensions || {},
|
|
151262
|
+
...pctCompleteApp !== undefined ? { pctCompleteApp } : {}
|
|
151263
|
+
};
|
|
151264
|
+
if (masteryAchieved) {
|
|
151265
|
+
scoreStatus = SCORE_STATUS.fullyGraded;
|
|
151266
|
+
}
|
|
150848
151267
|
}
|
|
150849
|
-
const calculatedXp = this.calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt);
|
|
150850
151268
|
if (score !== undefined) {
|
|
150851
|
-
await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp);
|
|
151269
|
+
await this.createGradebookEntry(actualLineItemId, studentId, currentAttemptNumber, score, totalQuestions, correctQuestions, calculatedXp, masteredUnits, scoreStatus, inProgress, progressData.appName);
|
|
150852
151270
|
} else {
|
|
150853
151271
|
log3.warn("[ProgressRecorder] Score not provided, skipping gradebook entry", {
|
|
150854
151272
|
studentId,
|
|
@@ -150856,20 +151274,70 @@ class ProgressRecorder {
|
|
|
150856
151274
|
attemptNumber: currentAttemptNumber
|
|
150857
151275
|
});
|
|
150858
151276
|
}
|
|
150859
|
-
|
|
151277
|
+
if (masteryAchieved) {
|
|
151278
|
+
await this.masteryTracker.createCompletionEntry(studentId, courseId, progressData.classId, progressData.appName);
|
|
151279
|
+
}
|
|
151280
|
+
await this.emitCaliperEvent(studentId, studentEmail, activityId, activityName, ids.course, courseName, totalQuestions, correctQuestions, calculatedXp, masteredUnits, currentAttemptNumber, progressData, extensions);
|
|
150860
151281
|
return {
|
|
150861
151282
|
xpAwarded: calculatedXp,
|
|
150862
|
-
attemptNumber: currentAttemptNumber
|
|
151283
|
+
attemptNumber: currentAttemptNumber,
|
|
151284
|
+
masteredUnitsApplied: progressData.masteredUnits ?? 0,
|
|
151285
|
+
pctCompleteApp,
|
|
151286
|
+
scoreStatus,
|
|
151287
|
+
inProgress
|
|
150863
151288
|
};
|
|
150864
151289
|
}
|
|
151290
|
+
async resolveContext(courseId, studentIdentifier, progressData) {
|
|
151291
|
+
const ids = deriveSourcedIds(courseId);
|
|
151292
|
+
const activityId = progressData.activityId || ids.componentResource || ids.resource || "unknown-activity";
|
|
151293
|
+
const activityName = progressData.activityName || kebabToTitleCase(activityId);
|
|
151294
|
+
const courseName = progressData.courseName || "Game Course";
|
|
151295
|
+
const student = await this.studentResolver.resolve(studentIdentifier, progressData.studentEmail);
|
|
151296
|
+
return { ids, activityId, activityName, courseName, student };
|
|
151297
|
+
}
|
|
151298
|
+
async resolveAssessmentLineItem(activityId, activityName, classId, ids) {
|
|
151299
|
+
const lineItemId = `${ids.course}-${activityId}-assessment`;
|
|
151300
|
+
let actualLineItemId = this.cacheManager.getAssessmentLineItem(lineItemId);
|
|
151301
|
+
if (!actualLineItemId) {
|
|
151302
|
+
actualLineItemId = await this.getOrCreateLineItem(lineItemId, activityName, classId, ids);
|
|
151303
|
+
this.cacheManager.setAssessmentLineItem(lineItemId, actualLineItemId);
|
|
151304
|
+
}
|
|
151305
|
+
return actualLineItemId;
|
|
151306
|
+
}
|
|
151307
|
+
async resolveAttemptNumber(providedAttemptNumber, score, studentId, lineItemId) {
|
|
151308
|
+
if (providedAttemptNumber) {
|
|
151309
|
+
return providedAttemptNumber;
|
|
151310
|
+
}
|
|
151311
|
+
if (score !== undefined) {
|
|
151312
|
+
return this.determineAttemptNumber(studentId, lineItemId);
|
|
151313
|
+
}
|
|
151314
|
+
return 1;
|
|
151315
|
+
}
|
|
151316
|
+
calculateXpForProgress(progressData, totalQuestions, correctQuestions, xpEarned, isFirstAttempt) {
|
|
151317
|
+
if (xpEarned !== undefined) {
|
|
151318
|
+
log3.debug("[ProgressRecorder] Using provided XP", { xpEarned });
|
|
151319
|
+
return xpEarned;
|
|
151320
|
+
}
|
|
151321
|
+
if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
|
|
151322
|
+
const accuracy = correctQuestions / totalQuestions;
|
|
151323
|
+
const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
|
|
151324
|
+
log3.debug("[ProgressRecorder] Calculated XP", {
|
|
151325
|
+
durationSeconds: progressData.sessionDurationSeconds,
|
|
151326
|
+
accuracy,
|
|
151327
|
+
isFirstAttempt,
|
|
151328
|
+
calculatedXp
|
|
151329
|
+
});
|
|
151330
|
+
return calculatedXp;
|
|
151331
|
+
}
|
|
151332
|
+
return 0;
|
|
151333
|
+
}
|
|
150865
151334
|
async getOrCreateLineItem(lineItemId, activityName, classId, ids) {
|
|
150866
151335
|
try {
|
|
150867
151336
|
const lineItem = await this.onerosterNamespace.assessmentLineItems.findOrCreate(lineItemId, {
|
|
150868
151337
|
sourcedId: lineItemId,
|
|
150869
151338
|
title: activityName,
|
|
150870
151339
|
status: ONEROSTER_STATUS.active,
|
|
150871
|
-
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
150872
|
-
...ids.componentResource ? { componentResource: { sourcedId: ids.componentResource } } : ids.component ? { component: { sourcedId: ids.component } } : {}
|
|
151340
|
+
...classId ? { class: { sourcedId: classId } } : { course: { sourcedId: ids.course } }
|
|
150873
151341
|
});
|
|
150874
151342
|
if (!lineItem.sourcedId) {
|
|
150875
151343
|
throw new TimebackError(`Assessment line item created but has no sourcedId. ` + `This should not happen and indicates an upstream API issue.`);
|
|
@@ -150887,39 +151355,15 @@ class ProgressRecorder {
|
|
|
150887
151355
|
}
|
|
150888
151356
|
}
|
|
150889
151357
|
async determineAttemptNumber(studentId, lineItemId) {
|
|
150890
|
-
const
|
|
150891
|
-
if (
|
|
150892
|
-
|
|
150893
|
-
const newAttemptNumber = previousAttemptNumber + 1;
|
|
150894
|
-
log3.debug("[ProgressRecorder] Found previous attempt, incrementing", {
|
|
150895
|
-
previousAttemptNumber,
|
|
150896
|
-
newAttemptNumber,
|
|
150897
|
-
previousScore: latestAttempt.score
|
|
150898
|
-
});
|
|
150899
|
-
return newAttemptNumber;
|
|
151358
|
+
const stats = await this.onerosterNamespace.assessmentResults.getAttemptStats(studentId, lineItemId);
|
|
151359
|
+
if (stats) {
|
|
151360
|
+
return stats.activeAttemptCount + 1;
|
|
150900
151361
|
}
|
|
150901
151362
|
return 1;
|
|
150902
151363
|
}
|
|
150903
|
-
|
|
150904
|
-
|
|
150905
|
-
|
|
150906
|
-
return xpEarned;
|
|
150907
|
-
}
|
|
150908
|
-
if (progressData.sessionDurationSeconds && totalQuestions && correctQuestions) {
|
|
150909
|
-
const accuracy = correctQuestions / totalQuestions;
|
|
150910
|
-
const calculatedXp = calculateXp(progressData.sessionDurationSeconds, accuracy, isFirstAttempt);
|
|
150911
|
-
log3.debug("[ProgressRecorder] Calculated XP", {
|
|
150912
|
-
durationSeconds: progressData.sessionDurationSeconds,
|
|
150913
|
-
accuracy,
|
|
150914
|
-
isFirstAttempt,
|
|
150915
|
-
calculatedXp
|
|
150916
|
-
});
|
|
150917
|
-
return calculatedXp;
|
|
150918
|
-
}
|
|
150919
|
-
return 0;
|
|
150920
|
-
}
|
|
150921
|
-
async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp) {
|
|
150922
|
-
const resultId = `${lineItemId}:${studentId}:attempt-${attemptNumber}`;
|
|
151364
|
+
async createGradebookEntry(lineItemId, studentId, attemptNumber, score, totalQuestions, correctQuestions, xp, masteredUnits, scoreStatus, inProgress, appName) {
|
|
151365
|
+
const timestamp4 = Date.now().toString(36);
|
|
151366
|
+
const resultId = `${lineItemId}:${studentId}:${timestamp4}`;
|
|
150923
151367
|
await this.onerosterNamespace.assessmentResults.upsert(resultId, {
|
|
150924
151368
|
sourcedId: resultId,
|
|
150925
151369
|
status: ONEROSTER_STATUS.active,
|
|
@@ -150927,18 +151371,21 @@ class ProgressRecorder {
|
|
|
150927
151371
|
student: { sourcedId: studentId },
|
|
150928
151372
|
score,
|
|
150929
151373
|
scoreDate: new Date().toISOString(),
|
|
150930
|
-
scoreStatus
|
|
151374
|
+
scoreStatus,
|
|
151375
|
+
inProgress,
|
|
150931
151376
|
metadata: {
|
|
150932
151377
|
xp,
|
|
150933
151378
|
totalQuestions,
|
|
150934
151379
|
correctQuestions,
|
|
150935
151380
|
accuracy: totalQuestions && correctQuestions ? correctQuestions / totalQuestions * 100 : undefined,
|
|
150936
151381
|
attemptNumber,
|
|
150937
|
-
lastUpdated: new Date().toISOString()
|
|
151382
|
+
lastUpdated: new Date().toISOString(),
|
|
151383
|
+
masteredUnits,
|
|
151384
|
+
appName
|
|
150938
151385
|
}
|
|
150939
151386
|
});
|
|
150940
151387
|
}
|
|
150941
|
-
async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData) {
|
|
151388
|
+
async emitCaliperEvent(studentId, studentEmail, activityId, activityName, courseId, courseName, totalQuestions, correctQuestions, xpEarned, masteredUnits, attemptNumber, progressData, extensions) {
|
|
150942
151389
|
await this.caliperNamespace.emitActivityEvent({
|
|
150943
151390
|
studentId,
|
|
150944
151391
|
studentEmail,
|
|
@@ -150953,7 +151400,8 @@ class ProgressRecorder {
|
|
|
150953
151400
|
attemptNumber,
|
|
150954
151401
|
subject: progressData.subject,
|
|
150955
151402
|
appName: progressData.appName,
|
|
150956
|
-
sensorUrl: progressData.sensorUrl
|
|
151403
|
+
sensorUrl: progressData.sensorUrl,
|
|
151404
|
+
extensions: extensions || progressData.extensions
|
|
150957
151405
|
}).catch((error2) => {
|
|
150958
151406
|
log3.error("[ProgressRecorder] Failed to emit activity event", { error: error2 });
|
|
150959
151407
|
});
|
|
@@ -153686,9 +154134,9 @@ class ZodUnion2 extends ZodType2 {
|
|
|
153686
154134
|
return this._def.options;
|
|
153687
154135
|
}
|
|
153688
154136
|
}
|
|
153689
|
-
ZodUnion2.create = (
|
|
154137
|
+
ZodUnion2.create = (types22, params) => {
|
|
153690
154138
|
return new ZodUnion2({
|
|
153691
|
-
options:
|
|
154139
|
+
options: types22,
|
|
153692
154140
|
typeName: ZodFirstPartyTypeKind2.ZodUnion,
|
|
153693
154141
|
...processCreateParams2(params)
|
|
153694
154142
|
});
|
|
@@ -155068,9 +155516,11 @@ class TimebackClient {
|
|
|
155068
155516
|
};
|
|
155069
155517
|
this.oneroster = createOneRosterNamespace(this);
|
|
155070
155518
|
this.caliper = createCaliperNamespace(this);
|
|
155519
|
+
this.edubridge = createEduBridgeNamespace(this);
|
|
155071
155520
|
this.cacheManager = new TimebackCacheManager;
|
|
155072
155521
|
this.studentResolver = new StudentResolver(this.cacheManager, this.oneroster);
|
|
155073
|
-
|
|
155522
|
+
const masteryTracker = new MasteryTracker(this.cacheManager, this.oneroster, this.edubridge);
|
|
155523
|
+
this.progressRecorder = new ProgressRecorder(this.studentResolver, this.cacheManager, this.oneroster, this.caliper, masteryTracker);
|
|
155074
155524
|
this.sessionRecorder = new SessionRecorder(this.studentResolver, this.caliper);
|
|
155075
155525
|
if (this.credentials) {
|
|
155076
155526
|
this._ensureAuthenticated().catch((error2) => {
|
|
@@ -155179,6 +155629,9 @@ class TimebackClient {
|
|
|
155179
155629
|
}
|
|
155180
155630
|
await this.authenticate();
|
|
155181
155631
|
}
|
|
155632
|
+
async resolveStudent(studentIdentifier, providedEmail) {
|
|
155633
|
+
return this.studentResolver.resolve(studentIdentifier, providedEmail);
|
|
155634
|
+
}
|
|
155182
155635
|
async recordProgress(courseId, studentIdentifier, progressData) {
|
|
155183
155636
|
await this._ensureAuthenticated();
|
|
155184
155637
|
return this.progressRecorder.record(courseId, studentIdentifier, progressData);
|
|
@@ -155187,6 +155640,28 @@ class TimebackClient {
|
|
|
155187
155640
|
await this._ensureAuthenticated();
|
|
155188
155641
|
return this.sessionRecorder.record(courseId, studentIdentifier, sessionData);
|
|
155189
155642
|
}
|
|
155643
|
+
async getEnrollments(studentId) {
|
|
155644
|
+
const cached = this.cacheManager.getEnrollments(studentId);
|
|
155645
|
+
if (cached) {
|
|
155646
|
+
return cached;
|
|
155647
|
+
}
|
|
155648
|
+
await this._ensureAuthenticated();
|
|
155649
|
+
const edubridgeEnrollments = await this.edubridge.enrollments.listByUser(studentId);
|
|
155650
|
+
const enrollments = edubridgeEnrollments.map((enrollment) => {
|
|
155651
|
+
const grades = enrollment.course.grades ? enrollment.course.grades.map((g5) => parseInt(g5, 10)).filter(isTimebackGrade) : null;
|
|
155652
|
+
const subjects = enrollment.course.subjects ? enrollment.course.subjects.filter(isTimebackSubject) : null;
|
|
155653
|
+
return {
|
|
155654
|
+
sourcedId: enrollment.id,
|
|
155655
|
+
title: enrollment.course.title,
|
|
155656
|
+
courseId: enrollment.course.id,
|
|
155657
|
+
status: "active",
|
|
155658
|
+
grades,
|
|
155659
|
+
subjects
|
|
155660
|
+
};
|
|
155661
|
+
});
|
|
155662
|
+
this.cacheManager.setEnrollments(studentId, enrollments);
|
|
155663
|
+
return enrollments;
|
|
155664
|
+
}
|
|
155190
155665
|
clearCaches() {
|
|
155191
155666
|
this.cacheManager.clearAll();
|
|
155192
155667
|
}
|
|
@@ -155198,6 +155673,7 @@ class TimebackClient {
|
|
|
155198
155673
|
}
|
|
155199
155674
|
oneroster;
|
|
155200
155675
|
caliper;
|
|
155676
|
+
edubridge;
|
|
155201
155677
|
async setup(config2, options) {
|
|
155202
155678
|
return setupTimebackResources(this, config2, options);
|
|
155203
155679
|
}
|
|
@@ -155215,6 +155691,310 @@ class TimebackClient {
|
|
|
155215
155691
|
}
|
|
155216
155692
|
}
|
|
155217
155693
|
|
|
155694
|
+
// ../timeback/dist/types.js
|
|
155695
|
+
var __esm4 = (fn2, res) => () => (fn2 && (res = fn2(fn2 = 0)), res);
|
|
155696
|
+
var init_auth2 = () => {};
|
|
155697
|
+
var PLAYCADEMY_BASE_URLS2;
|
|
155698
|
+
var init_domains2 = __esm4(() => {
|
|
155699
|
+
PLAYCADEMY_BASE_URLS2 = {
|
|
155700
|
+
production: "https://hub.playcademy.net",
|
|
155701
|
+
staging: "https://hub.dev.playcademy.net"
|
|
155702
|
+
};
|
|
155703
|
+
});
|
|
155704
|
+
var init_env_vars2 = () => {};
|
|
155705
|
+
var ITEM_SLUGS3;
|
|
155706
|
+
var CURRENCIES3;
|
|
155707
|
+
var BADGES3;
|
|
155708
|
+
var init_overworld2 = __esm4(() => {
|
|
155709
|
+
ITEM_SLUGS3 = {
|
|
155710
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
155711
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
155712
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
155713
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
155714
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
155715
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
155716
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
155717
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
155718
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
155719
|
+
BOOMBOX: "BOOMBOX",
|
|
155720
|
+
CABIN_BED: "CABIN_BED"
|
|
155721
|
+
};
|
|
155722
|
+
CURRENCIES3 = {
|
|
155723
|
+
PRIMARY: ITEM_SLUGS3.PLAYCADEMY_CREDITS,
|
|
155724
|
+
XP: ITEM_SLUGS3.PLAYCADEMY_XP
|
|
155725
|
+
};
|
|
155726
|
+
BADGES3 = {
|
|
155727
|
+
FOUNDING_MEMBER: ITEM_SLUGS3.FOUNDING_MEMBER_BADGE,
|
|
155728
|
+
EARLY_ADOPTER: ITEM_SLUGS3.EARLY_ADOPTER_BADGE,
|
|
155729
|
+
FIRST_GAME: ITEM_SLUGS3.FIRST_GAME_BADGE
|
|
155730
|
+
};
|
|
155731
|
+
});
|
|
155732
|
+
var init_timeback2 = () => {};
|
|
155733
|
+
var init_workers2 = () => {};
|
|
155734
|
+
var init_src3 = __esm4(() => {
|
|
155735
|
+
init_auth2();
|
|
155736
|
+
init_domains2();
|
|
155737
|
+
init_env_vars2();
|
|
155738
|
+
init_overworld2();
|
|
155739
|
+
init_timeback2();
|
|
155740
|
+
init_workers2();
|
|
155741
|
+
});
|
|
155742
|
+
var TIMEBACK_API_URLS2;
|
|
155743
|
+
var TIMEBACK_AUTH_URLS2;
|
|
155744
|
+
var CALIPER_API_URLS2;
|
|
155745
|
+
var ONEROSTER_ENDPOINTS2;
|
|
155746
|
+
var CALIPER_ENDPOINTS2;
|
|
155747
|
+
var CALIPER_CONSTANTS2;
|
|
155748
|
+
var TIMEBACK_EVENT_TYPES2;
|
|
155749
|
+
var TIMEBACK_ACTIONS2;
|
|
155750
|
+
var TIMEBACK_TYPES2;
|
|
155751
|
+
var ACTIVITY_METRIC_TYPES2;
|
|
155752
|
+
var TIME_METRIC_TYPES2;
|
|
155753
|
+
var TIMEBACK_SUBJECTS2;
|
|
155754
|
+
var TIMEBACK_GRADE_LEVELS2;
|
|
155755
|
+
var TIMEBACK_GRADE_LEVEL_LABELS2;
|
|
155756
|
+
var CALIPER_SUBJECTS2;
|
|
155757
|
+
var ONEROSTER_STATUS2;
|
|
155758
|
+
var SCORE_STATUS2;
|
|
155759
|
+
var ENV_VARS2;
|
|
155760
|
+
var HTTP_DEFAULTS2;
|
|
155761
|
+
var AUTH_DEFAULTS2;
|
|
155762
|
+
var CACHE_DEFAULTS2;
|
|
155763
|
+
var CONFIG_DEFAULTS2;
|
|
155764
|
+
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID2;
|
|
155765
|
+
var PLAYCADEMY_DEFAULTS2;
|
|
155766
|
+
var RESOURCE_DEFAULTS2;
|
|
155767
|
+
var HTTP_STATUS2;
|
|
155768
|
+
var ERROR_NAMES2;
|
|
155769
|
+
var init_constants2 = __esm4(() => {
|
|
155770
|
+
init_src3();
|
|
155771
|
+
TIMEBACK_API_URLS2 = {
|
|
155772
|
+
production: "https://api.alpha-1edtech.ai",
|
|
155773
|
+
staging: "https://api.staging.alpha-1edtech.com"
|
|
155774
|
+
};
|
|
155775
|
+
TIMEBACK_AUTH_URLS2 = {
|
|
155776
|
+
production: "https://prod-beyond-timeback-api-2-idp.auth.us-east-1.amazoncognito.com",
|
|
155777
|
+
staging: "https://alpha-auth-development-idp.auth.us-west-2.amazoncognito.com"
|
|
155778
|
+
};
|
|
155779
|
+
CALIPER_API_URLS2 = {
|
|
155780
|
+
production: "https://caliper.alpha-1edtech.ai",
|
|
155781
|
+
staging: "https://caliper-staging.alpha-1edtech.com"
|
|
155782
|
+
};
|
|
155783
|
+
ONEROSTER_ENDPOINTS2 = {
|
|
155784
|
+
organizations: "/ims/oneroster/rostering/v1p2/orgs",
|
|
155785
|
+
courses: "/ims/oneroster/rostering/v1p2/courses",
|
|
155786
|
+
courseComponents: "/ims/oneroster/rostering/v1p2/courses/components",
|
|
155787
|
+
resources: "/ims/oneroster/resources/v1p2/resources",
|
|
155788
|
+
componentResources: "/ims/oneroster/rostering/v1p2/courses/component-resources",
|
|
155789
|
+
classes: "/ims/oneroster/rostering/v1p2/classes",
|
|
155790
|
+
enrollments: "/ims/oneroster/rostering/v1p2/enrollments",
|
|
155791
|
+
assessmentLineItems: "/ims/oneroster/gradebook/v1p2/assessmentLineItems",
|
|
155792
|
+
assessmentResults: "/ims/oneroster/gradebook/v1p2/assessmentResults",
|
|
155793
|
+
users: "/ims/oneroster/rostering/v1p2/users"
|
|
155794
|
+
};
|
|
155795
|
+
CALIPER_ENDPOINTS2 = {
|
|
155796
|
+
events: "/caliper/event",
|
|
155797
|
+
validate: "/caliper/event/validate"
|
|
155798
|
+
};
|
|
155799
|
+
CALIPER_CONSTANTS2 = {
|
|
155800
|
+
context: "http://purl.imsglobal.org/ctx/caliper/v1p2",
|
|
155801
|
+
profile: "TimebackProfile",
|
|
155802
|
+
dataVersion: "http://purl.imsglobal.org/ctx/caliper/v1p2"
|
|
155803
|
+
};
|
|
155804
|
+
TIMEBACK_EVENT_TYPES2 = {
|
|
155805
|
+
activityEvent: "ActivityEvent",
|
|
155806
|
+
timeSpentEvent: "TimeSpentEvent"
|
|
155807
|
+
};
|
|
155808
|
+
TIMEBACK_ACTIONS2 = {
|
|
155809
|
+
completed: "Completed",
|
|
155810
|
+
spentTime: "SpentTime"
|
|
155811
|
+
};
|
|
155812
|
+
TIMEBACK_TYPES2 = {
|
|
155813
|
+
user: "TimebackUser",
|
|
155814
|
+
activityContext: "TimebackActivityContext",
|
|
155815
|
+
activityMetricsCollection: "TimebackActivityMetricsCollection",
|
|
155816
|
+
timeSpentMetricsCollection: "TimebackTimeSpentMetricsCollection"
|
|
155817
|
+
};
|
|
155818
|
+
ACTIVITY_METRIC_TYPES2 = {
|
|
155819
|
+
totalQuestions: "totalQuestions",
|
|
155820
|
+
correctQuestions: "correctQuestions",
|
|
155821
|
+
xpEarned: "xpEarned",
|
|
155822
|
+
masteredUnits: "masteredUnits"
|
|
155823
|
+
};
|
|
155824
|
+
TIME_METRIC_TYPES2 = {
|
|
155825
|
+
active: "active",
|
|
155826
|
+
inactive: "inactive",
|
|
155827
|
+
waste: "waste",
|
|
155828
|
+
unknown: "unknown",
|
|
155829
|
+
antiPattern: "anti-pattern"
|
|
155830
|
+
};
|
|
155831
|
+
TIMEBACK_SUBJECTS2 = [
|
|
155832
|
+
"Math",
|
|
155833
|
+
"FastMath",
|
|
155834
|
+
"Science",
|
|
155835
|
+
"Social Studies",
|
|
155836
|
+
"Language",
|
|
155837
|
+
"Reading",
|
|
155838
|
+
"Vocabulary",
|
|
155839
|
+
"Writing"
|
|
155840
|
+
];
|
|
155841
|
+
TIMEBACK_GRADE_LEVELS2 = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
|
155842
|
+
TIMEBACK_GRADE_LEVEL_LABELS2 = {
|
|
155843
|
+
"-1": "pre-k",
|
|
155844
|
+
"0": "kindergarten",
|
|
155845
|
+
"1": "1st grade",
|
|
155846
|
+
"2": "2nd grade",
|
|
155847
|
+
"3": "3rd grade",
|
|
155848
|
+
"4": "4th grade",
|
|
155849
|
+
"5": "5th grade",
|
|
155850
|
+
"6": "6th grade",
|
|
155851
|
+
"7": "7th grade",
|
|
155852
|
+
"8": "8th grade",
|
|
155853
|
+
"9": "9th grade",
|
|
155854
|
+
"10": "10th grade",
|
|
155855
|
+
"11": "11th grade",
|
|
155856
|
+
"12": "12th grade",
|
|
155857
|
+
"13": "AP"
|
|
155858
|
+
};
|
|
155859
|
+
CALIPER_SUBJECTS2 = {
|
|
155860
|
+
Reading: "Reading",
|
|
155861
|
+
Language: "Language",
|
|
155862
|
+
Vocabulary: "Vocabulary",
|
|
155863
|
+
SocialStudies: "Social Studies",
|
|
155864
|
+
Writing: "Writing",
|
|
155865
|
+
Science: "Science",
|
|
155866
|
+
FastMath: "FastMath",
|
|
155867
|
+
Math: "Math",
|
|
155868
|
+
None: "None"
|
|
155869
|
+
};
|
|
155870
|
+
ONEROSTER_STATUS2 = {
|
|
155871
|
+
active: "active",
|
|
155872
|
+
toBeDeleted: "tobedeleted"
|
|
155873
|
+
};
|
|
155874
|
+
SCORE_STATUS2 = {
|
|
155875
|
+
exempt: "exempt",
|
|
155876
|
+
fullyGraded: "fully graded",
|
|
155877
|
+
notSubmitted: "not submitted",
|
|
155878
|
+
partiallyGraded: "partially graded",
|
|
155879
|
+
submitted: "submitted"
|
|
155880
|
+
};
|
|
155881
|
+
ENV_VARS2 = {
|
|
155882
|
+
clientId: "TIMEBACK_CLIENT_ID",
|
|
155883
|
+
clientSecret: "TIMEBACK_CLIENT_SECRET",
|
|
155884
|
+
baseUrl: "TIMEBACK_BASE_URL",
|
|
155885
|
+
environment: "TIMEBACK_ENVIRONMENT",
|
|
155886
|
+
vendorResourceId: "TIMEBACK_VENDOR_RESOURCE_ID",
|
|
155887
|
+
launchBaseUrl: "GAME_URL"
|
|
155888
|
+
};
|
|
155889
|
+
HTTP_DEFAULTS2 = {
|
|
155890
|
+
timeout: 30000,
|
|
155891
|
+
retries: 3,
|
|
155892
|
+
retryBackoffBase: 2
|
|
155893
|
+
};
|
|
155894
|
+
AUTH_DEFAULTS2 = {
|
|
155895
|
+
tokenCacheDuration: 50000
|
|
155896
|
+
};
|
|
155897
|
+
CACHE_DEFAULTS2 = {
|
|
155898
|
+
defaultTTL: 10 * 60 * 1000,
|
|
155899
|
+
defaultMaxSize: 500,
|
|
155900
|
+
defaultName: "TimebackCache",
|
|
155901
|
+
studentTTL: 10 * 60 * 1000,
|
|
155902
|
+
studentMaxSize: 500,
|
|
155903
|
+
assessmentTTL: 30 * 60 * 1000,
|
|
155904
|
+
assessmentMaxSize: 200,
|
|
155905
|
+
enrollmentTTL: 5 * 1000,
|
|
155906
|
+
enrollmentMaxSize: 100
|
|
155907
|
+
};
|
|
155908
|
+
CONFIG_DEFAULTS2 = {
|
|
155909
|
+
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
155910
|
+
};
|
|
155911
|
+
DEFAULT_PLAYCADEMY_ORGANIZATION_ID2 = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
155912
|
+
PLAYCADEMY_DEFAULTS2 = {
|
|
155913
|
+
organization: DEFAULT_PLAYCADEMY_ORGANIZATION_ID2,
|
|
155914
|
+
launchBaseUrls: PLAYCADEMY_BASE_URLS2
|
|
155915
|
+
};
|
|
155916
|
+
RESOURCE_DEFAULTS2 = {
|
|
155917
|
+
organization: {
|
|
155918
|
+
name: "Playcademy Studios",
|
|
155919
|
+
type: "department"
|
|
155920
|
+
},
|
|
155921
|
+
course: {
|
|
155922
|
+
gradingScheme: "STANDARD",
|
|
155923
|
+
level: {
|
|
155924
|
+
elementary: "Elementary",
|
|
155925
|
+
middle: "Middle",
|
|
155926
|
+
high: "High",
|
|
155927
|
+
ap: "AP"
|
|
155928
|
+
},
|
|
155929
|
+
metadata: {
|
|
155930
|
+
goals: {
|
|
155931
|
+
dailyXp: 50,
|
|
155932
|
+
dailyLessons: 3
|
|
155933
|
+
},
|
|
155934
|
+
metrics: {
|
|
155935
|
+
totalXp: 1000,
|
|
155936
|
+
totalLessons: 50
|
|
155937
|
+
}
|
|
155938
|
+
}
|
|
155939
|
+
},
|
|
155940
|
+
component: {
|
|
155941
|
+
sortOrder: 1,
|
|
155942
|
+
prerequisiteCriteria: "ALL"
|
|
155943
|
+
},
|
|
155944
|
+
resource: {
|
|
155945
|
+
vendorId: "playcademy",
|
|
155946
|
+
roles: ["primary"],
|
|
155947
|
+
importance: "primary",
|
|
155948
|
+
metadata: {
|
|
155949
|
+
type: "interactive",
|
|
155950
|
+
toolProvider: "Playcademy",
|
|
155951
|
+
instructionalMethod: "exploratory",
|
|
155952
|
+
language: "en-US"
|
|
155953
|
+
}
|
|
155954
|
+
},
|
|
155955
|
+
componentResource: {
|
|
155956
|
+
sortOrder: 1,
|
|
155957
|
+
lessonType: "quiz"
|
|
155958
|
+
}
|
|
155959
|
+
};
|
|
155960
|
+
HTTP_STATUS2 = {
|
|
155961
|
+
CLIENT_ERROR_MIN: 400,
|
|
155962
|
+
CLIENT_ERROR_MAX: 500,
|
|
155963
|
+
SERVER_ERROR_MIN: 500
|
|
155964
|
+
};
|
|
155965
|
+
ERROR_NAMES2 = {
|
|
155966
|
+
timebackAuth: "TimebackAuthError",
|
|
155967
|
+
timebackApi: "TimebackApiError",
|
|
155968
|
+
timebackConfig: "TimebackConfigError",
|
|
155969
|
+
timebackSdk: "TimebackSDKError"
|
|
155970
|
+
};
|
|
155971
|
+
});
|
|
155972
|
+
init_constants2();
|
|
155973
|
+
var isObject3 = (value) => typeof value === "object" && value !== null;
|
|
155974
|
+
var SUBJECT_VALUES2 = TIMEBACK_SUBJECTS2;
|
|
155975
|
+
var GRADE_VALUES2 = TIMEBACK_GRADE_LEVELS2;
|
|
155976
|
+
function isCourseMetadata(value) {
|
|
155977
|
+
return isObject3(value);
|
|
155978
|
+
}
|
|
155979
|
+
function isResourceMetadata(value) {
|
|
155980
|
+
return isObject3(value);
|
|
155981
|
+
}
|
|
155982
|
+
function isPlaycademyResourceMetadata2(value) {
|
|
155983
|
+
if (!isObject3(value)) {
|
|
155984
|
+
return false;
|
|
155985
|
+
}
|
|
155986
|
+
if (!("mastery" in value) || value.mastery === undefined) {
|
|
155987
|
+
return true;
|
|
155988
|
+
}
|
|
155989
|
+
return isObject3(value.mastery);
|
|
155990
|
+
}
|
|
155991
|
+
function isTimebackSubject2(value) {
|
|
155992
|
+
return typeof value === "string" && SUBJECT_VALUES2.includes(value);
|
|
155993
|
+
}
|
|
155994
|
+
function isTimebackGrade2(value) {
|
|
155995
|
+
return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES2.includes(value);
|
|
155996
|
+
}
|
|
155997
|
+
|
|
155218
155998
|
// ../utils/src/uuid.ts
|
|
155219
155999
|
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}$/;
|
|
155220
156000
|
function isValidUUID(value) {
|
|
@@ -155500,6 +156280,95 @@ async function getTimebackClient() {
|
|
|
155500
156280
|
}
|
|
155501
156281
|
return timebackClient;
|
|
155502
156282
|
}
|
|
156283
|
+
function buildResourceMetadata({
|
|
156284
|
+
baseMetadata,
|
|
156285
|
+
subject,
|
|
156286
|
+
grade,
|
|
156287
|
+
totalXp,
|
|
156288
|
+
masterableUnits
|
|
156289
|
+
}) {
|
|
156290
|
+
const normalizedBaseMetadata = isResourceMetadata(baseMetadata) ? baseMetadata : undefined;
|
|
156291
|
+
const metadata2 = {
|
|
156292
|
+
...normalizedBaseMetadata || {}
|
|
156293
|
+
};
|
|
156294
|
+
metadata2.subject = subject;
|
|
156295
|
+
metadata2.grades = [grade];
|
|
156296
|
+
metadata2.xp = totalXp;
|
|
156297
|
+
if (masterableUnits !== undefined && masterableUnits !== null) {
|
|
156298
|
+
const existingPlaycademy = isPlaycademyResourceMetadata2(metadata2.playcademy) ? metadata2.playcademy : undefined;
|
|
156299
|
+
metadata2.playcademy = {
|
|
156300
|
+
...existingPlaycademy || {},
|
|
156301
|
+
mastery: {
|
|
156302
|
+
...existingPlaycademy?.mastery || {},
|
|
156303
|
+
masterableUnits
|
|
156304
|
+
}
|
|
156305
|
+
};
|
|
156306
|
+
}
|
|
156307
|
+
return metadata2;
|
|
156308
|
+
}
|
|
156309
|
+
// ../api-core/src/utils/timeback-enrollments.ts
|
|
156310
|
+
init_src();
|
|
156311
|
+
async function fetchEnrollmentsForUser(timebackId) {
|
|
156312
|
+
const db = getDatabase();
|
|
156313
|
+
const isLocal = process.env.PUBLIC_IS_LOCAL === "true";
|
|
156314
|
+
if (isLocal) {
|
|
156315
|
+
const allIntegrations = await db.query.gameTimebackIntegrations.findMany();
|
|
156316
|
+
return allIntegrations.map((integration) => ({
|
|
156317
|
+
gameId: integration.gameId,
|
|
156318
|
+
grade: integration.grade,
|
|
156319
|
+
subject: integration.subject,
|
|
156320
|
+
courseId: integration.courseId
|
|
156321
|
+
}));
|
|
156322
|
+
}
|
|
156323
|
+
log2.debug("[timeback-enrollments] Fetching student enrollments from TimeBack", { timebackId });
|
|
156324
|
+
try {
|
|
156325
|
+
const client = await getTimebackClient();
|
|
156326
|
+
const classes = await client.getEnrollments(timebackId);
|
|
156327
|
+
const courseIds = classes.map((cls) => cls.courseId).filter((id) => Boolean(id));
|
|
156328
|
+
if (courseIds.length === 0) {
|
|
156329
|
+
return [];
|
|
156330
|
+
}
|
|
156331
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
156332
|
+
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
156333
|
+
});
|
|
156334
|
+
return integrations.map((integration) => ({
|
|
156335
|
+
gameId: integration.gameId,
|
|
156336
|
+
grade: integration.grade,
|
|
156337
|
+
subject: integration.subject,
|
|
156338
|
+
courseId: integration.courseId
|
|
156339
|
+
}));
|
|
156340
|
+
} catch (error2) {
|
|
156341
|
+
log2.warn("[timeback-enrollments] Failed to fetch TimeBack enrollments:", {
|
|
156342
|
+
error: error2,
|
|
156343
|
+
timebackId
|
|
156344
|
+
});
|
|
156345
|
+
return [];
|
|
156346
|
+
}
|
|
156347
|
+
}
|
|
156348
|
+
async function fetchUserRole(timebackId) {
|
|
156349
|
+
log2.debug("[timeback] Fetching user role from TimeBack", { timebackId });
|
|
156350
|
+
try {
|
|
156351
|
+
const client = await getTimebackClient();
|
|
156352
|
+
const user = await client.oneroster.users.get(timebackId);
|
|
156353
|
+
const primaryRole = user.roles.find((r2) => r2.roleType === "primary");
|
|
156354
|
+
const role = primaryRole?.role ?? user.roles[0]?.role ?? "student";
|
|
156355
|
+
log2.debug("[timeback] Resolved user role", { timebackId, role });
|
|
156356
|
+
return role;
|
|
156357
|
+
} catch (error2) {
|
|
156358
|
+
log2.warn("[timeback] Failed to fetch user role, defaulting to student:", {
|
|
156359
|
+
error: error2,
|
|
156360
|
+
timebackId
|
|
156361
|
+
});
|
|
156362
|
+
return "student";
|
|
156363
|
+
}
|
|
156364
|
+
}
|
|
156365
|
+
async function fetchUserTimebackData(timebackId) {
|
|
156366
|
+
const [role, enrollments] = await Promise.all([
|
|
156367
|
+
fetchUserRole(timebackId),
|
|
156368
|
+
fetchEnrollmentsForUser(timebackId)
|
|
156369
|
+
]);
|
|
156370
|
+
return { role, enrollments };
|
|
156371
|
+
}
|
|
155503
156372
|
// ../data/src/domains/achievement/types.ts
|
|
155504
156373
|
var AchievementCompletionType;
|
|
155505
156374
|
((AchievementCompletionType2) => {
|
|
@@ -156035,6 +156904,249 @@ var AchievementService = {
|
|
|
156035
156904
|
checkInteractionAchievement,
|
|
156036
156905
|
generateAchievementMessage
|
|
156037
156906
|
};
|
|
156907
|
+
// ../alerts/src/discord/embed.ts
|
|
156908
|
+
class DiscordEmbedBuilder {
|
|
156909
|
+
embed = {};
|
|
156910
|
+
setTitle(title) {
|
|
156911
|
+
this.embed.title = title;
|
|
156912
|
+
return this;
|
|
156913
|
+
}
|
|
156914
|
+
setDescription(description) {
|
|
156915
|
+
this.embed.description = description;
|
|
156916
|
+
return this;
|
|
156917
|
+
}
|
|
156918
|
+
setUrl(url) {
|
|
156919
|
+
this.embed.url = url;
|
|
156920
|
+
return this;
|
|
156921
|
+
}
|
|
156922
|
+
setColor(color) {
|
|
156923
|
+
this.embed.color = color;
|
|
156924
|
+
return this;
|
|
156925
|
+
}
|
|
156926
|
+
setTimestamp(date4) {
|
|
156927
|
+
this.embed.timestamp = (date4 || new Date).toISOString();
|
|
156928
|
+
return this;
|
|
156929
|
+
}
|
|
156930
|
+
setAuthor(name4, options) {
|
|
156931
|
+
this.embed.author = {
|
|
156932
|
+
name: name4,
|
|
156933
|
+
url: options?.url,
|
|
156934
|
+
icon_url: options?.iconUrl
|
|
156935
|
+
};
|
|
156936
|
+
return this;
|
|
156937
|
+
}
|
|
156938
|
+
setFooter(text5, iconUrl) {
|
|
156939
|
+
this.embed.footer = {
|
|
156940
|
+
text: text5,
|
|
156941
|
+
icon_url: iconUrl
|
|
156942
|
+
};
|
|
156943
|
+
return this;
|
|
156944
|
+
}
|
|
156945
|
+
setThumbnail(url) {
|
|
156946
|
+
this.embed.thumbnail = { url };
|
|
156947
|
+
return this;
|
|
156948
|
+
}
|
|
156949
|
+
setImage(url) {
|
|
156950
|
+
this.embed.image = { url };
|
|
156951
|
+
return this;
|
|
156952
|
+
}
|
|
156953
|
+
addField(name4, value, inline = false) {
|
|
156954
|
+
if (!this.embed.fields) {
|
|
156955
|
+
this.embed.fields = [];
|
|
156956
|
+
}
|
|
156957
|
+
this.embed.fields.push({ name: name4, value, inline });
|
|
156958
|
+
return this;
|
|
156959
|
+
}
|
|
156960
|
+
addFields(...fields) {
|
|
156961
|
+
for (const field of fields) {
|
|
156962
|
+
this.addField(field.name, field.value, field.inline);
|
|
156963
|
+
}
|
|
156964
|
+
return this;
|
|
156965
|
+
}
|
|
156966
|
+
build() {
|
|
156967
|
+
return this.embed;
|
|
156968
|
+
}
|
|
156969
|
+
static create() {
|
|
156970
|
+
return new DiscordEmbedBuilder;
|
|
156971
|
+
}
|
|
156972
|
+
}
|
|
156973
|
+
|
|
156974
|
+
// ../alerts/src/discord/client.ts
|
|
156975
|
+
class DiscordClient {
|
|
156976
|
+
config;
|
|
156977
|
+
constructor(config2) {
|
|
156978
|
+
if (!config2.url) {
|
|
156979
|
+
throw new Error("Discord webhook URL is required");
|
|
156980
|
+
}
|
|
156981
|
+
if (!config2.url.startsWith("https://discord.com/api/webhooks/")) {
|
|
156982
|
+
throw new Error("Invalid Discord webhook URL format");
|
|
156983
|
+
}
|
|
156984
|
+
this.config = config2;
|
|
156985
|
+
}
|
|
156986
|
+
async send(message2) {
|
|
156987
|
+
const payload = {
|
|
156988
|
+
content: message2,
|
|
156989
|
+
username: this.config.username,
|
|
156990
|
+
avatar_url: this.config.avatarUrl
|
|
156991
|
+
};
|
|
156992
|
+
await this.sendPayload(payload);
|
|
156993
|
+
}
|
|
156994
|
+
async sendEmbed(embed) {
|
|
156995
|
+
const payload = {
|
|
156996
|
+
embeds: [embed],
|
|
156997
|
+
username: this.config.username,
|
|
156998
|
+
avatar_url: this.config.avatarUrl
|
|
156999
|
+
};
|
|
157000
|
+
await this.sendPayload(payload);
|
|
157001
|
+
}
|
|
157002
|
+
async sendEmbeds(embeds) {
|
|
157003
|
+
if (embeds.length > 10) {
|
|
157004
|
+
throw new Error("Discord allows maximum 10 embeds per message");
|
|
157005
|
+
}
|
|
157006
|
+
const payload = {
|
|
157007
|
+
embeds,
|
|
157008
|
+
username: this.config.username,
|
|
157009
|
+
avatar_url: this.config.avatarUrl
|
|
157010
|
+
};
|
|
157011
|
+
await this.sendPayload(payload);
|
|
157012
|
+
}
|
|
157013
|
+
async sendWithEmbeds(message2, embeds) {
|
|
157014
|
+
const payload = {
|
|
157015
|
+
content: message2,
|
|
157016
|
+
embeds,
|
|
157017
|
+
username: this.config.username,
|
|
157018
|
+
avatar_url: this.config.avatarUrl
|
|
157019
|
+
};
|
|
157020
|
+
await this.sendPayload(payload);
|
|
157021
|
+
}
|
|
157022
|
+
async sendRich(message2) {
|
|
157023
|
+
const builder = new DiscordEmbedBuilder;
|
|
157024
|
+
if (message2.title)
|
|
157025
|
+
builder.setTitle(message2.title);
|
|
157026
|
+
if (message2.description)
|
|
157027
|
+
builder.setDescription(message2.description);
|
|
157028
|
+
if (message2.url)
|
|
157029
|
+
builder.setUrl(message2.url);
|
|
157030
|
+
if (message2.color) {
|
|
157031
|
+
const color = typeof message2.color === "string" ? parseInt(message2.color.replace("#", ""), 16) : message2.color;
|
|
157032
|
+
builder.setColor(color);
|
|
157033
|
+
}
|
|
157034
|
+
if (message2.author) {
|
|
157035
|
+
builder.setAuthor(message2.author.name, {
|
|
157036
|
+
url: message2.author.url,
|
|
157037
|
+
iconUrl: message2.author.iconUrl
|
|
157038
|
+
});
|
|
157039
|
+
}
|
|
157040
|
+
if (message2.fields) {
|
|
157041
|
+
for (const field of message2.fields) {
|
|
157042
|
+
builder.addField(field.name, field.value, field.inline);
|
|
157043
|
+
}
|
|
157044
|
+
}
|
|
157045
|
+
if (message2.footer) {
|
|
157046
|
+
builder.setFooter(message2.footer.text, message2.footer.iconUrl);
|
|
157047
|
+
}
|
|
157048
|
+
if (message2.timestamp) {
|
|
157049
|
+
const date4 = message2.timestamp instanceof Date ? message2.timestamp : new Date(message2.timestamp);
|
|
157050
|
+
builder.setTimestamp(date4);
|
|
157051
|
+
}
|
|
157052
|
+
if (message2.thumbnail) {
|
|
157053
|
+
builder.setThumbnail(message2.thumbnail.url);
|
|
157054
|
+
}
|
|
157055
|
+
if (message2.image) {
|
|
157056
|
+
builder.setImage(message2.image.url);
|
|
157057
|
+
}
|
|
157058
|
+
await this.sendEmbed(builder.build());
|
|
157059
|
+
}
|
|
157060
|
+
async sendPayload(payload) {
|
|
157061
|
+
const response = await fetch(this.config.url, {
|
|
157062
|
+
method: "POST",
|
|
157063
|
+
headers: {
|
|
157064
|
+
"Content-Type": "application/json"
|
|
157065
|
+
},
|
|
157066
|
+
body: JSON.stringify(payload)
|
|
157067
|
+
});
|
|
157068
|
+
if (!response.ok) {
|
|
157069
|
+
const errorText = await response.text().catch(() => "Unknown error");
|
|
157070
|
+
throw new Error(`Discord webhook request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
157071
|
+
}
|
|
157072
|
+
}
|
|
157073
|
+
getRedactedUrl() {
|
|
157074
|
+
const parts2 = this.config.url.split("/");
|
|
157075
|
+
const token = parts2[parts2.length - 1];
|
|
157076
|
+
return `${token?.slice(0, 9)}...`;
|
|
157077
|
+
}
|
|
157078
|
+
}
|
|
157079
|
+
// ../alerts/src/discord/types.ts
|
|
157080
|
+
var DiscordColors = {
|
|
157081
|
+
DEFAULT: 0,
|
|
157082
|
+
WHITE: 16777215,
|
|
157083
|
+
AQUA: 1752220,
|
|
157084
|
+
GREEN: 5763719,
|
|
157085
|
+
BLUE: 3447003,
|
|
157086
|
+
YELLOW: 16705372,
|
|
157087
|
+
PURPLE: 10181046,
|
|
157088
|
+
LUMINOUS_VIVID_PINK: 15277667,
|
|
157089
|
+
FUCHSIA: 15418782,
|
|
157090
|
+
GOLD: 15844367,
|
|
157091
|
+
ORANGE: 15105570,
|
|
157092
|
+
RED: 15548997,
|
|
157093
|
+
GREY: 9807270,
|
|
157094
|
+
NAVY: 3426654,
|
|
157095
|
+
DARK_AQUA: 1146986,
|
|
157096
|
+
DARK_GREEN: 2067276,
|
|
157097
|
+
DARK_BLUE: 2123412,
|
|
157098
|
+
DARK_PURPLE: 7419530,
|
|
157099
|
+
DARK_VIVID_PINK: 11342935,
|
|
157100
|
+
DARK_GOLD: 12745742,
|
|
157101
|
+
DARK_ORANGE: 11027200,
|
|
157102
|
+
DARK_RED: 10038562,
|
|
157103
|
+
DARK_GREY: 9936031,
|
|
157104
|
+
DARKER_GREY: 8359053,
|
|
157105
|
+
LIGHT_GREY: 12370112,
|
|
157106
|
+
DARK_NAVY: 2899536,
|
|
157107
|
+
BLURPLE: 5793266,
|
|
157108
|
+
GREYPLE: 10070709,
|
|
157109
|
+
DARK_BUT_NOT_BLACK: 2895667,
|
|
157110
|
+
NOT_QUITE_BLACK: 2303786
|
|
157111
|
+
};
|
|
157112
|
+
// ../api-core/src/utils/alerts.ts
|
|
157113
|
+
init_src();
|
|
157114
|
+
function getDiscordClient() {
|
|
157115
|
+
const webhookUrl = process.env.DISCORD_WEBHOOK_PLATFORM;
|
|
157116
|
+
if (!webhookUrl) {
|
|
157117
|
+
log2.debug("[API] Discord webhook not configured, skipping notification");
|
|
157118
|
+
return null;
|
|
157119
|
+
}
|
|
157120
|
+
return new DiscordClient({
|
|
157121
|
+
url: webhookUrl,
|
|
157122
|
+
username: "Playcademy Platform",
|
|
157123
|
+
avatarUrl: process.env.DISCORD_WEBHOOK_AVATAR_URL
|
|
157124
|
+
});
|
|
157125
|
+
}
|
|
157126
|
+
function getEnvironment() {
|
|
157127
|
+
const stage = process.env.SST_STAGE;
|
|
157128
|
+
return stage === "production" ? "production" : stage === "dev" ? "staging" : "test";
|
|
157129
|
+
}
|
|
157130
|
+
async function sendDeveloperApplicationAlert(user) {
|
|
157131
|
+
const discord = getDiscordClient();
|
|
157132
|
+
if (!discord)
|
|
157133
|
+
return;
|
|
157134
|
+
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();
|
|
157135
|
+
await discord.sendEmbed(embed);
|
|
157136
|
+
log2.debug("[API] Discord notification sent successfully", {
|
|
157137
|
+
userId: user.id
|
|
157138
|
+
});
|
|
157139
|
+
}
|
|
157140
|
+
async function sendGameDeletionAlert(game) {
|
|
157141
|
+
const discord = getDiscordClient();
|
|
157142
|
+
if (!discord)
|
|
157143
|
+
return;
|
|
157144
|
+
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();
|
|
157145
|
+
await discord.sendEmbed(embed);
|
|
157146
|
+
log2.debug("[API] Discord game deletion notification sent", {
|
|
157147
|
+
slug: game.slug
|
|
157148
|
+
});
|
|
157149
|
+
}
|
|
156038
157150
|
// ../../node_modules/aws-jwt-verify/dist/esm/error.js
|
|
156039
157151
|
class JwtBaseError extends Error {
|
|
156040
157152
|
}
|
|
@@ -157172,10 +158284,8 @@ async function publishPersonalBestNotification(userId, gameId, rank, newScore, p
|
|
|
157172
158284
|
});
|
|
157173
158285
|
}
|
|
157174
158286
|
// ../cloudflare/src/playcademy/provider.ts
|
|
157175
|
-
import {
|
|
157176
|
-
import {
|
|
157177
|
-
import { tmpdir } from "node:os";
|
|
157178
|
-
import { join as join5, relative } from "node:path";
|
|
158287
|
+
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
158288
|
+
import { join as join4, relative } from "node:path";
|
|
157179
158289
|
init_src();
|
|
157180
158290
|
|
|
157181
158291
|
// ../cloudflare/src/core/client.ts
|
|
@@ -158004,7 +159114,6 @@ function normalizeDeploymentOptions(options) {
|
|
|
158004
159114
|
assets: options?.bindings?.assets ?? false
|
|
158005
159115
|
},
|
|
158006
159116
|
assetsPath: options?.assetsPath,
|
|
158007
|
-
assetsZip: options?.assetsZip,
|
|
158008
159117
|
keepAssets: options?.keepAssets
|
|
158009
159118
|
};
|
|
158010
159119
|
}
|
|
@@ -158054,41 +159163,6 @@ var DEFAULT_COMPATIBILITY_DATE2 = new Date().toISOString().slice(0, 10);
|
|
|
158054
159163
|
var GAME_WORKER_DOMAIN_PRODUCTION = GAME_WORKER_DOMAINS.production;
|
|
158055
159164
|
var GAME_WORKER_DOMAIN_STAGING = GAME_WORKER_DOMAINS.staging;
|
|
158056
159165
|
|
|
158057
|
-
// ../cloudflare/src/playcademy/workers/index.ts
|
|
158058
|
-
import { dirname as dirname2, join as join4 } from "node:path";
|
|
158059
|
-
import { fileURLToPath } from "node:url";
|
|
158060
|
-
|
|
158061
|
-
// ../cloudflare/src/utils/bundler.ts
|
|
158062
|
-
var esbuild = __toESM(require_main(), 1);
|
|
158063
|
-
async function bundleWorker(filePath) {
|
|
158064
|
-
const result = await esbuild.build({
|
|
158065
|
-
entryPoints: [filePath],
|
|
158066
|
-
bundle: true,
|
|
158067
|
-
format: "esm",
|
|
158068
|
-
platform: "browser",
|
|
158069
|
-
target: "es2022",
|
|
158070
|
-
write: false,
|
|
158071
|
-
minify: false,
|
|
158072
|
-
sourcemap: false,
|
|
158073
|
-
logLevel: "error"
|
|
158074
|
-
});
|
|
158075
|
-
if (!result.outputFiles?.[0]) {
|
|
158076
|
-
throw new Error(`No output generated for worker ${filePath}`);
|
|
158077
|
-
}
|
|
158078
|
-
return result.outputFiles[0].text;
|
|
158079
|
-
}
|
|
158080
|
-
// ../cloudflare/src/playcademy/workers/index.ts
|
|
158081
|
-
var _cachedStubWorker = null;
|
|
158082
|
-
async function getStubWorkerCode() {
|
|
158083
|
-
if (_cachedStubWorker) {
|
|
158084
|
-
return _cachedStubWorker;
|
|
158085
|
-
}
|
|
158086
|
-
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
158087
|
-
const workerPath = join4(currentDir, "stub-worker.ts");
|
|
158088
|
-
_cachedStubWorker = await bundleWorker(workerPath);
|
|
158089
|
-
return _cachedStubWorker;
|
|
158090
|
-
}
|
|
158091
|
-
|
|
158092
159166
|
// ../cloudflare/src/playcademy/provider.ts
|
|
158093
159167
|
class CloudflareProvider {
|
|
158094
159168
|
client;
|
|
@@ -158130,7 +159204,7 @@ class CloudflareProvider {
|
|
|
158130
159204
|
async function scanDirectory(dir) {
|
|
158131
159205
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
158132
159206
|
for (const entry of entries) {
|
|
158133
|
-
const fullPath =
|
|
159207
|
+
const fullPath = join4(dir, entry.name);
|
|
158134
159208
|
if (entry.isDirectory()) {
|
|
158135
159209
|
if (await scanDirectory(fullPath))
|
|
158136
159210
|
return true;
|
|
@@ -158152,7 +159226,7 @@ class CloudflareProvider {
|
|
|
158152
159226
|
async resolveAssetBasePath(dirPath) {
|
|
158153
159227
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
158154
159228
|
if (entries.length === 1 && entries[0]?.isDirectory()) {
|
|
158155
|
-
const unwrappedPath =
|
|
159229
|
+
const unwrappedPath = join4(dirPath, entries[0].name);
|
|
158156
159230
|
log2.debug("[CloudflareProvider] Unwrapping wrapper directory", {
|
|
158157
159231
|
wrapper: entries[0].name
|
|
158158
159232
|
});
|
|
@@ -158163,7 +159237,7 @@ class CloudflareProvider {
|
|
|
158163
159237
|
async uploadFilesToR2(dir, baseDir, bucketName) {
|
|
158164
159238
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
158165
159239
|
for (const entry of entries) {
|
|
158166
|
-
const fullPath =
|
|
159240
|
+
const fullPath = join4(dir, entry.name);
|
|
158167
159241
|
if (entry.isDirectory()) {
|
|
158168
159242
|
await this.uploadFilesToR2(fullPath, baseDir, bucketName);
|
|
158169
159243
|
} else {
|
|
@@ -158186,38 +159260,15 @@ class CloudflareProvider {
|
|
|
158186
159260
|
async deploy(deploymentId, code, env2, options) {
|
|
158187
159261
|
const opts = normalizeDeploymentOptions(options);
|
|
158188
159262
|
const isFirstDeploy = env2.PLAYCADEMY_API_KEY !== undefined;
|
|
158189
|
-
const hasAssets = !!
|
|
158190
|
-
let finalCode = code ?? "";
|
|
159263
|
+
const hasAssets = !!opts.assetsPath;
|
|
158191
159264
|
if (!code) {
|
|
158192
|
-
|
|
158193
|
-
log2.debug("[CloudflareProvider] Fetching existing script", {
|
|
158194
|
-
deploymentId,
|
|
158195
|
-
namespace: this.config.dispatchNamespace
|
|
158196
|
-
});
|
|
158197
|
-
finalCode = await this.client.workers.getScriptContent(this.config.dispatchNamespace, deploymentId);
|
|
158198
|
-
} catch {
|
|
158199
|
-
log2.debug("[CloudflareProvider] No existing script, using stub worker", {
|
|
158200
|
-
deploymentId
|
|
158201
|
-
});
|
|
158202
|
-
finalCode = await getStubWorkerCode();
|
|
158203
|
-
}
|
|
159265
|
+
throw new Error(`No worker code provided for deployment. ` + `Frontend-only deployments should include a stub worker from the CLI.`);
|
|
158204
159266
|
}
|
|
159267
|
+
const finalCode = code;
|
|
158205
159268
|
if (opts.keepAssets === undefined) {
|
|
158206
159269
|
opts.keepAssets = true;
|
|
158207
159270
|
}
|
|
158208
|
-
|
|
158209
|
-
let finalAssetsPath = opts.assetsPath;
|
|
158210
|
-
if (opts.assetsZip && !opts.assetsPath) {
|
|
158211
|
-
tempDir = join5(tmpdir(), `playcademy-assets-${Date.now()}`);
|
|
158212
|
-
finalAssetsPath = join5(tempDir, "dist");
|
|
158213
|
-
await mkdir(finalAssetsPath, { recursive: true });
|
|
158214
|
-
await writeFile(join5(tempDir, "temp.zip"), opts.assetsZip);
|
|
158215
|
-
execSync(`unzip -q ${join5(tempDir, "temp.zip")} -d ${finalAssetsPath}`);
|
|
158216
|
-
log2.debug("[CloudflareProvider] Extracted ZIP to temp directory", {
|
|
158217
|
-
tempDir,
|
|
158218
|
-
size: opts.assetsZip.length
|
|
158219
|
-
});
|
|
158220
|
-
}
|
|
159271
|
+
const finalAssetsPath = opts.assetsPath;
|
|
158221
159272
|
log2.info("[CloudflareProvider] Deploying worker to dispatch namespace", {
|
|
158222
159273
|
deploymentId,
|
|
158223
159274
|
namespace: this.config.dispatchNamespace,
|
|
@@ -158346,10 +159397,6 @@ class CloudflareProvider {
|
|
|
158346
159397
|
error: error2
|
|
158347
159398
|
});
|
|
158348
159399
|
throw new Error(`Failed to deploy to Cloudflare Workers for Platforms: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
158349
|
-
} finally {
|
|
158350
|
-
if (tempDir) {
|
|
158351
|
-
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
158352
|
-
}
|
|
158353
159400
|
}
|
|
158354
159401
|
}
|
|
158355
159402
|
async delete(deploymentId, options) {
|
|
@@ -158429,6 +159476,8 @@ class CloudflareProvider {
|
|
|
158429
159476
|
});
|
|
158430
159477
|
}
|
|
158431
159478
|
}
|
|
159479
|
+
// ../cloudflare/src/utils/bundler.ts
|
|
159480
|
+
var esbuild = __toESM(require_main(), 1);
|
|
158432
159481
|
// ../api-core/src/utils/cloudflare.ts
|
|
158433
159482
|
init_src();
|
|
158434
159483
|
var cloudflareProvider = null;
|
|
@@ -158725,20 +159774,6 @@ async function seedCoreGames(db) {
|
|
|
158725
159774
|
console.error(`Error seeding core game '${gameData.slug}':`, error2);
|
|
158726
159775
|
}
|
|
158727
159776
|
}
|
|
158728
|
-
if (config.timeback.courseId && hasTimebackCredentials()) {
|
|
158729
|
-
try {
|
|
158730
|
-
await db.insert(gameTimebackIntegrations).values({
|
|
158731
|
-
id: crypto.randomUUID(),
|
|
158732
|
-
gameId: CORE_GAME_UUIDS.PLAYGROUND,
|
|
158733
|
-
courseId: config.timeback.courseId,
|
|
158734
|
-
lastVerifiedAt: null,
|
|
158735
|
-
createdAt: now2,
|
|
158736
|
-
updatedAt: now2
|
|
158737
|
-
}).onConflictDoNothing();
|
|
158738
|
-
} catch (error2) {
|
|
158739
|
-
console.error("Error seeding TimeBack integration for playground:", error2);
|
|
158740
|
-
}
|
|
158741
|
-
}
|
|
158742
159777
|
}
|
|
158743
159778
|
async function seedCurrentProjectGame(db, project) {
|
|
158744
159779
|
const now2 = new Date;
|
|
@@ -158767,19 +159802,6 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
158767
159802
|
updatedAt: now2
|
|
158768
159803
|
};
|
|
158769
159804
|
const [newGame] = await db.insert(games).values(gameRecord).returning();
|
|
158770
|
-
if (config.timeback.courseId && hasTimebackCredentials()) {
|
|
158771
|
-
await db.insert(gameTimebackIntegrations).values({
|
|
158772
|
-
id: crypto.randomUUID(),
|
|
158773
|
-
gameId: newGame.id,
|
|
158774
|
-
courseId: config.timeback.courseId,
|
|
158775
|
-
lastVerifiedAt: null,
|
|
158776
|
-
createdAt: now2,
|
|
158777
|
-
updatedAt: now2
|
|
158778
|
-
}).onConflictDoNothing();
|
|
158779
|
-
} else if (hasTimebackCredentials() && !config.timeback.courseId) {
|
|
158780
|
-
logger3.info(" ⚠ TimeBack credentials found but SANDBOX_TIMEBACK_COURSE_ID not set");
|
|
158781
|
-
logger3.info(" Set SANDBOX_TIMEBACK_COURSE_ID to your real TimeBack course ID");
|
|
158782
|
-
}
|
|
158783
159805
|
return newGame;
|
|
158784
159806
|
} catch (error2) {
|
|
158785
159807
|
console.error("❌ Error seeding project game:", error2);
|
|
@@ -158841,13 +159863,26 @@ async function seedSpriteTemplates(db) {
|
|
|
158841
159863
|
}
|
|
158842
159864
|
}
|
|
158843
159865
|
|
|
159866
|
+
// src/database/seed/timeback.ts
|
|
159867
|
+
function resolveStudentId(studentId) {
|
|
159868
|
+
if (!studentId)
|
|
159869
|
+
return null;
|
|
159870
|
+
if (studentId === "mock")
|
|
159871
|
+
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
159872
|
+
return studentId;
|
|
159873
|
+
}
|
|
159874
|
+
function getAdminTimebackId() {
|
|
159875
|
+
return resolveStudentId(config.timeback.studentId);
|
|
159876
|
+
}
|
|
159877
|
+
|
|
158844
159878
|
// src/database/seed/index.ts
|
|
158845
159879
|
async function seedDemoData(db) {
|
|
158846
159880
|
try {
|
|
159881
|
+
const adminTimebackId = getAdminTimebackId();
|
|
158847
159882
|
for (const [role, user] of Object.entries(DEMO_USERS)) {
|
|
158848
159883
|
const userValues = {
|
|
158849
159884
|
...user,
|
|
158850
|
-
timebackId: role === "admin"
|
|
159885
|
+
timebackId: role === "admin" ? adminTimebackId : null
|
|
158851
159886
|
};
|
|
158852
159887
|
await db.insert(users).values(userValues).onConflictDoNothing();
|
|
158853
159888
|
}
|
|
@@ -159122,6 +160157,12 @@ async function applyForDeveloperStatus(ctx) {
|
|
|
159122
160157
|
userId: user.id,
|
|
159123
160158
|
newStatus: "pending"
|
|
159124
160159
|
});
|
|
160160
|
+
sendDeveloperApplicationAlert(fullUser).catch((error2) => {
|
|
160161
|
+
log2.error("[API] Failed to send Discord notification for developer application", {
|
|
160162
|
+
userId: user.id,
|
|
160163
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
160164
|
+
});
|
|
160165
|
+
});
|
|
159125
160166
|
} catch (error2) {
|
|
159126
160167
|
log2.error(`Error updating developer status for user ${user.id}:`, {
|
|
159127
160168
|
error: error2
|
|
@@ -163862,6 +164903,68 @@ var NotificationStatsSchema = exports_external2.object({
|
|
|
163862
164903
|
startDate: exports_external2.date().optional(),
|
|
163863
164904
|
endDate: exports_external2.date().optional()
|
|
163864
164905
|
});
|
|
164906
|
+
// ../data/src/domains/timeback/schemas.ts
|
|
164907
|
+
var InsertTimebackDailyXpSchema = createInsertSchema(timebackDailyXp, {
|
|
164908
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number"),
|
|
164909
|
+
date: exports_external2.date()
|
|
164910
|
+
}).omit({ createdAt: true, updatedAt: true });
|
|
164911
|
+
var SelectTimebackDailyXpSchema = createSelectSchema(timebackDailyXp);
|
|
164912
|
+
var UpdateTimebackDailyXpSchema = createUpdateSchema(timebackDailyXp, {
|
|
164913
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number")
|
|
164914
|
+
}).omit({ userId: true, createdAt: true });
|
|
164915
|
+
var UpdateTimebackXpRequestSchema = exports_external2.object({
|
|
164916
|
+
xp: exports_external2.number().min(0, "XP must be a non-negative number"),
|
|
164917
|
+
userTimestamp: exports_external2.string().datetime().optional()
|
|
164918
|
+
});
|
|
164919
|
+
var TimebackDateQuerySchema = exports_external2.object({
|
|
164920
|
+
date: exports_external2.string().datetime().optional()
|
|
164921
|
+
});
|
|
164922
|
+
var TimebackHistoryQuerySchema = exports_external2.object({
|
|
164923
|
+
startDate: exports_external2.string().date().optional(),
|
|
164924
|
+
endDate: exports_external2.string().date().optional()
|
|
164925
|
+
});
|
|
164926
|
+
var InsertTimebackXpEventSchema = createInsertSchema(timebackXpEvents, {
|
|
164927
|
+
occurredAt: exports_external2.date(),
|
|
164928
|
+
xpDelta: exports_external2.number()
|
|
164929
|
+
}).omit({ createdAt: true, updatedAt: true });
|
|
164930
|
+
var SelectTimebackXpEventSchema = createSelectSchema(timebackXpEvents);
|
|
164931
|
+
var InsertGameTimebackIntegrationSchema = createInsertSchema(gameTimebackIntegrations, {
|
|
164932
|
+
gameId: exports_external2.string().uuid(),
|
|
164933
|
+
courseId: exports_external2.string().min(1),
|
|
164934
|
+
grade: exports_external2.number().int(),
|
|
164935
|
+
subject: exports_external2.string().min(1),
|
|
164936
|
+
totalXp: exports_external2.number().int().positive().optional()
|
|
164937
|
+
}).omit({ id: true, createdAt: true, updatedAt: true });
|
|
164938
|
+
var SelectGameTimebackIntegrationSchema = createSelectSchema(gameTimebackIntegrations);
|
|
164939
|
+
var UpdateGameTimebackIntegrationSchema = createUpdateSchema(gameTimebackIntegrations, {
|
|
164940
|
+
courseId: exports_external2.string().min(1).optional(),
|
|
164941
|
+
totalXp: exports_external2.number().int().positive().optional(),
|
|
164942
|
+
lastVerifiedAt: exports_external2.date().optional()
|
|
164943
|
+
}).omit({ id: true, gameId: true, grade: true, subject: true, createdAt: true });
|
|
164944
|
+
var EndActivityRequestSchema = exports_external2.object({
|
|
164945
|
+
gameId: exports_external2.string().uuid(),
|
|
164946
|
+
studentId: exports_external2.string().min(1),
|
|
164947
|
+
activityData: exports_external2.object({
|
|
164948
|
+
activityId: exports_external2.string().min(1),
|
|
164949
|
+
activityName: exports_external2.string().optional(),
|
|
164950
|
+
grade: exports_external2.number().int(),
|
|
164951
|
+
subject: exports_external2.string().min(1),
|
|
164952
|
+
appName: exports_external2.string().optional(),
|
|
164953
|
+
sensorUrl: exports_external2.string().url().optional(),
|
|
164954
|
+
courseId: exports_external2.string().optional(),
|
|
164955
|
+
courseName: exports_external2.string().optional(),
|
|
164956
|
+
studentEmail: exports_external2.string().email().optional()
|
|
164957
|
+
}),
|
|
164958
|
+
scoreData: exports_external2.object({
|
|
164959
|
+
correctQuestions: exports_external2.number().int().min(0),
|
|
164960
|
+
totalQuestions: exports_external2.number().int().min(0)
|
|
164961
|
+
}),
|
|
164962
|
+
timingData: exports_external2.object({
|
|
164963
|
+
durationSeconds: exports_external2.number().positive()
|
|
164964
|
+
}),
|
|
164965
|
+
xpEarned: exports_external2.number().optional(),
|
|
164966
|
+
masteredUnits: exports_external2.number().nonnegative().optional()
|
|
164967
|
+
});
|
|
163865
164968
|
// ../api-core/src/handlers/games/leaderboard.ts
|
|
163866
164969
|
init_src();
|
|
163867
164970
|
async function submitScore(ctx) {
|
|
@@ -164030,6 +165133,26 @@ async function getUserAllScores(ctx) {
|
|
|
164030
165133
|
throw ApiError.internal("Failed to fetch user scores");
|
|
164031
165134
|
}
|
|
164032
165135
|
}
|
|
165136
|
+
async function getUserScores(ctx) {
|
|
165137
|
+
const user = ctx.user;
|
|
165138
|
+
if (!user) {
|
|
165139
|
+
throw ApiError.unauthorized("Must be logged in to view user scores");
|
|
165140
|
+
}
|
|
165141
|
+
const { params, url } = ctx;
|
|
165142
|
+
const gameId = params.gameId;
|
|
165143
|
+
const userId = params.userId;
|
|
165144
|
+
if (!gameId || !userId) {
|
|
165145
|
+
throw ApiError.badRequest("Game ID and User ID are required");
|
|
165146
|
+
}
|
|
165147
|
+
const limit2 = Math.min(Number(url.searchParams.get("limit") || "10"), 100);
|
|
165148
|
+
const db = getDatabase();
|
|
165149
|
+
try {
|
|
165150
|
+
const scores = await db.select().from(gameScores).where(and(eq(gameScores.gameId, gameId), eq(gameScores.userId, userId))).orderBy(desc(gameScores.achievedAt)).limit(limit2);
|
|
165151
|
+
return scores;
|
|
165152
|
+
} catch {
|
|
165153
|
+
throw ApiError.internal("Failed to fetch user scores");
|
|
165154
|
+
}
|
|
165155
|
+
}
|
|
164033
165156
|
|
|
164034
165157
|
// ../api-core/src/handlers/levels/index.ts
|
|
164035
165158
|
init_src();
|
|
@@ -164166,9 +165289,11 @@ async function getUserMe(ctx) {
|
|
|
164166
165289
|
const timebackAccount = await db.query.accounts.findFirst({
|
|
164167
165290
|
where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
|
|
164168
165291
|
});
|
|
165292
|
+
const timeback3 = userData.timebackId ? await fetchUserTimebackData(userData.timebackId) : undefined;
|
|
164169
165293
|
return {
|
|
164170
165294
|
...userData,
|
|
164171
|
-
hasTimebackAccount: !!timebackAccount
|
|
165295
|
+
hasTimebackAccount: !!timebackAccount,
|
|
165296
|
+
timeback: timeback3
|
|
164172
165297
|
};
|
|
164173
165298
|
} catch (error2) {
|
|
164174
165299
|
if (error2 instanceof ApiError)
|
|
@@ -164698,7 +165823,7 @@ async function deleteGame(ctx) {
|
|
|
164698
165823
|
const db = getDatabase();
|
|
164699
165824
|
const gameToDelete = await db.query.games.findFirst({
|
|
164700
165825
|
where: eq(games.id, gameId),
|
|
164701
|
-
columns: { id: true, slug: true }
|
|
165826
|
+
columns: { id: true, slug: true, displayName: true }
|
|
164702
165827
|
});
|
|
164703
165828
|
if (!gameToDelete || !gameToDelete.slug) {
|
|
164704
165829
|
throw ApiError.notFound("Game not found for deletion");
|
|
@@ -164720,6 +165845,16 @@ async function deleteGame(ctx) {
|
|
|
164720
165845
|
hadActiveDeployment: !!activeDeployment,
|
|
164721
165846
|
customDomainsCount: customDomains.length
|
|
164722
165847
|
});
|
|
165848
|
+
sendGameDeletionAlert({
|
|
165849
|
+
slug: gameToDelete.slug,
|
|
165850
|
+
displayName: gameToDelete.displayName,
|
|
165851
|
+
developer: {
|
|
165852
|
+
id: user.id,
|
|
165853
|
+
email: user.email
|
|
165854
|
+
}
|
|
165855
|
+
}).catch((err2) => {
|
|
165856
|
+
log2.warn("[API] Failed to send Discord game deletion notification", { err: err2 });
|
|
165857
|
+
});
|
|
164723
165858
|
if (activeDeployment?.provider === "cloudflare") {
|
|
164724
165859
|
try {
|
|
164725
165860
|
const cloudflare2 = getCloudflareProvider();
|
|
@@ -166301,6 +167436,26 @@ gameScoresRouter.post("/:gameId/scores", async (c3) => {
|
|
|
166301
167436
|
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
166302
167437
|
}
|
|
166303
167438
|
});
|
|
167439
|
+
gameScoresRouter.get("/:gameId/users/:userId/scores", async (c3) => {
|
|
167440
|
+
const gameId = c3.req.param("gameId");
|
|
167441
|
+
const userId = c3.req.param("userId");
|
|
167442
|
+
const ctx = {
|
|
167443
|
+
user: c3.get("user"),
|
|
167444
|
+
params: { gameId, userId },
|
|
167445
|
+
url: new URL(c3.req.url),
|
|
167446
|
+
request: c3.req.raw
|
|
167447
|
+
};
|
|
167448
|
+
try {
|
|
167449
|
+
const result = await getUserScores(ctx);
|
|
167450
|
+
return c3.json(result, 200);
|
|
167451
|
+
} catch (error2) {
|
|
167452
|
+
if (error2 instanceof ApiError) {
|
|
167453
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
167454
|
+
}
|
|
167455
|
+
console.error("Error in getUserScores:", error2);
|
|
167456
|
+
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
167457
|
+
}
|
|
167458
|
+
});
|
|
166304
167459
|
|
|
166305
167460
|
// ../api-core/src/handlers/games/sessions.ts
|
|
166306
167461
|
init_src();
|
|
@@ -169088,39 +170243,21 @@ async function endActivity(ctx) {
|
|
|
169088
170243
|
throw ApiError.unauthorized("Must be logged in to end activity");
|
|
169089
170244
|
}
|
|
169090
170245
|
log2.debug("[API] Ending activity", { userId: user.id });
|
|
169091
|
-
|
|
169092
|
-
|
|
169093
|
-
|
|
169094
|
-
|
|
169095
|
-
} catch (error2) {
|
|
169096
|
-
log2.error("[API] Failed to parse request body:", { error: error2 });
|
|
169097
|
-
throw ApiError.badRequest("Invalid JSON body");
|
|
169098
|
-
}
|
|
169099
|
-
const { gameId, studentId, activityData, scoreData, timingData, xpEarned } = request3;
|
|
169100
|
-
if (!activityData?.activityId) {
|
|
169101
|
-
throw ApiError.badRequest("activityId is required");
|
|
169102
|
-
}
|
|
169103
|
-
if (typeof scoreData?.correctQuestions !== "number" || typeof scoreData?.totalQuestions !== "number") {
|
|
169104
|
-
throw ApiError.badRequest("correctQuestions and totalQuestions are required");
|
|
169105
|
-
}
|
|
169106
|
-
if (typeof timingData?.durationSeconds !== "number") {
|
|
169107
|
-
throw ApiError.badRequest("durationSeconds is required");
|
|
170246
|
+
const requestBody = await ctx.request.json();
|
|
170247
|
+
const validation2 = EndActivityRequestSchema.safeParse(requestBody);
|
|
170248
|
+
if (!validation2.success) {
|
|
170249
|
+
throw ApiError.badRequest(formatValidationErrors(validation2.error));
|
|
169108
170250
|
}
|
|
170251
|
+
const { gameId, studentId, activityData, scoreData, timingData, xpEarned, masteredUnits } = validation2.data;
|
|
170252
|
+
await verifyGameAccessById(gameId, user);
|
|
169109
170253
|
const db = getDatabase();
|
|
169110
|
-
const
|
|
169111
|
-
where: eq(games.id, gameId)
|
|
169112
|
-
});
|
|
169113
|
-
if (!game) {
|
|
169114
|
-
throw ApiError.notFound("Game not found");
|
|
169115
|
-
}
|
|
169116
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169117
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169118
|
-
}
|
|
170254
|
+
const { grade, subject } = activityData;
|
|
169119
170255
|
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
169120
|
-
where: eq(
|
|
170256
|
+
where: (table14, { eq: eq3, and: and3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, grade), eq3(table14.subject, subject))
|
|
169121
170257
|
});
|
|
169122
170258
|
if (!integration) {
|
|
169123
|
-
|
|
170259
|
+
log2.error("[API] No TimeBack integration found", { gameId, grade, subject });
|
|
170260
|
+
throw ApiError.notFound(`No TimeBack integration found for this game (grade ${grade}, subject ${subject}). Run setup first.`);
|
|
169124
170261
|
}
|
|
169125
170262
|
const client2 = await getTimebackClient();
|
|
169126
170263
|
try {
|
|
@@ -169131,6 +170268,7 @@ async function endActivity(ctx) {
|
|
|
169131
170268
|
correctQuestions: scoreData.correctQuestions,
|
|
169132
170269
|
sessionDurationSeconds: timingData.durationSeconds,
|
|
169133
170270
|
xpEarned,
|
|
170271
|
+
masteredUnits,
|
|
169134
170272
|
activityId: activityData.activityId,
|
|
169135
170273
|
activityName: activityData.activityName,
|
|
169136
170274
|
subject: activityData.subject,
|
|
@@ -169138,8 +170276,11 @@ async function endActivity(ctx) {
|
|
|
169138
170276
|
sensorUrl: activityData.sensorUrl,
|
|
169139
170277
|
courseId: activityData.courseId,
|
|
169140
170278
|
courseName: activityData.courseName,
|
|
169141
|
-
studentEmail: activityData.studentEmail
|
|
170279
|
+
studentEmail: activityData.studentEmail,
|
|
170280
|
+
courseTotalXp: integration.totalXp
|
|
169142
170281
|
});
|
|
170282
|
+
const studentData = await client2.resolveStudent(studentId);
|
|
170283
|
+
const studentEmail = studentData.email;
|
|
169143
170284
|
await client2.recordSessionEnd(integration.courseId, studentId, {
|
|
169144
170285
|
activeTimeSeconds: timingData.durationSeconds,
|
|
169145
170286
|
activityId: activityData.activityId,
|
|
@@ -169155,17 +170296,26 @@ async function endActivity(ctx) {
|
|
|
169155
170296
|
gameId,
|
|
169156
170297
|
courseId: integration.courseId,
|
|
169157
170298
|
studentId,
|
|
170299
|
+
studentEmail,
|
|
169158
170300
|
activityId: activityData.activityId,
|
|
169159
170301
|
score: scorePercentage,
|
|
169160
170302
|
durationSeconds: timingData.durationSeconds,
|
|
169161
170303
|
attemptNumber: result.attemptNumber,
|
|
169162
170304
|
isFirstAttempt: result.attemptNumber === 1,
|
|
169163
|
-
xpAwarded: result.xpAwarded
|
|
170305
|
+
xpAwarded: result.xpAwarded,
|
|
170306
|
+
masteredUnits,
|
|
170307
|
+
pctCompleteApp: result.pctCompleteApp,
|
|
170308
|
+
scoreStatus: result.scoreStatus,
|
|
170309
|
+
inProgress: result.inProgress
|
|
169164
170310
|
});
|
|
169165
170311
|
return {
|
|
169166
170312
|
status: "ok",
|
|
169167
170313
|
courseId: integration.courseId,
|
|
169168
|
-
xpAwarded: result.xpAwarded
|
|
170314
|
+
xpAwarded: result.xpAwarded,
|
|
170315
|
+
masteredUnits: result.masteredUnitsApplied,
|
|
170316
|
+
pctCompleteApp: result.pctCompleteApp,
|
|
170317
|
+
scoreStatus: result.scoreStatus,
|
|
170318
|
+
inProgress: result.inProgress
|
|
169169
170319
|
};
|
|
169170
170320
|
} catch (error2) {
|
|
169171
170321
|
logTimebackError2("end activity", error2, {
|
|
@@ -169192,68 +170342,160 @@ async function setupTimebackIntegration(ctx) {
|
|
|
169192
170342
|
log2.error("Failed to parse request body:", { error: error2 });
|
|
169193
170343
|
throw ApiError.badRequest("Invalid JSON body");
|
|
169194
170344
|
}
|
|
169195
|
-
const { gameId,
|
|
170345
|
+
const { gameId, courses, baseConfig, verbose } = request3;
|
|
169196
170346
|
log2.debug("[API] TimeBack setup request", {
|
|
169197
170347
|
gameId,
|
|
169198
|
-
|
|
170348
|
+
courseCount: courses.length
|
|
169199
170349
|
});
|
|
170350
|
+
await verifyGameAccessById(gameId, user);
|
|
169200
170351
|
const db = getDatabase();
|
|
169201
|
-
const
|
|
169202
|
-
where: eq(games.id, gameId)
|
|
169203
|
-
});
|
|
169204
|
-
if (!game) {
|
|
169205
|
-
throw ApiError.notFound("Game not found");
|
|
169206
|
-
}
|
|
169207
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169208
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169209
|
-
}
|
|
169210
|
-
const existing = await db.query.gameTimebackIntegrations.findFirst({
|
|
170352
|
+
const existing = await db.query.gameTimebackIntegrations.findMany({
|
|
169211
170353
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
169212
170354
|
});
|
|
169213
170355
|
const client2 = await getTimebackClient();
|
|
169214
|
-
|
|
169215
|
-
|
|
169216
|
-
|
|
169217
|
-
|
|
169218
|
-
|
|
169219
|
-
|
|
169220
|
-
|
|
169221
|
-
|
|
169222
|
-
|
|
169223
|
-
|
|
169224
|
-
|
|
169225
|
-
|
|
169226
|
-
|
|
169227
|
-
|
|
169228
|
-
|
|
169229
|
-
|
|
169230
|
-
|
|
170356
|
+
const integrations = [];
|
|
170357
|
+
const verboseData = [];
|
|
170358
|
+
for (const courseConfig of courses) {
|
|
170359
|
+
const {
|
|
170360
|
+
subject,
|
|
170361
|
+
grade,
|
|
170362
|
+
title,
|
|
170363
|
+
courseCode,
|
|
170364
|
+
level,
|
|
170365
|
+
metadata: metadata2,
|
|
170366
|
+
totalXp: derivedTotalXp,
|
|
170367
|
+
masterableUnits: derivedMasterableUnits
|
|
170368
|
+
} = courseConfig;
|
|
170369
|
+
if (!isTimebackSubject2(subject)) {
|
|
170370
|
+
log2.error("[API] Invalid subject in TimeBack request", { subject });
|
|
170371
|
+
throw ApiError.badRequest(`Invalid subject "${subject}" provided for course "${title}".`);
|
|
170372
|
+
}
|
|
170373
|
+
if (!isTimebackGrade2(grade)) {
|
|
170374
|
+
log2.error("[API] Invalid grade in TimeBack request", { grade });
|
|
170375
|
+
throw ApiError.badRequest(`Invalid grade "${grade}" provided for course "${title}".`);
|
|
170376
|
+
}
|
|
170377
|
+
const courseSubject = subject;
|
|
170378
|
+
const courseGrade = grade;
|
|
170379
|
+
const courseMetadata = isCourseMetadata(metadata2) ? metadata2 : undefined;
|
|
170380
|
+
const totalXp = derivedTotalXp ?? courseMetadata?.metrics?.totalXp;
|
|
170381
|
+
const masterableUnits = derivedMasterableUnits ?? (isPlaycademyResourceMetadata2(courseMetadata?.playcademy) ? courseMetadata?.playcademy?.mastery?.masterableUnits : undefined);
|
|
170382
|
+
if (typeof totalXp !== "number") {
|
|
170383
|
+
log2.error("[API] Missing totalXp in course metadata", { gameId, grade, subject, title });
|
|
170384
|
+
throw ApiError.badRequest(`Course "${title}" (grade ${grade}, subject ${subject}) is missing \`metadata.metrics.totalXp\``);
|
|
170385
|
+
}
|
|
170386
|
+
const suffix = baseConfig.component.titleSuffix || "";
|
|
170387
|
+
const applySuffix = (text5) => suffix ? `${text5} ${suffix}` : text5;
|
|
170388
|
+
const fullConfig = {
|
|
170389
|
+
organization: baseConfig.organization,
|
|
170390
|
+
course: {
|
|
170391
|
+
title,
|
|
170392
|
+
subjects: [courseSubject],
|
|
170393
|
+
grades: [courseGrade],
|
|
170394
|
+
courseCode,
|
|
170395
|
+
level,
|
|
170396
|
+
gradingScheme: "STANDARD",
|
|
170397
|
+
metadata: metadata2
|
|
170398
|
+
},
|
|
170399
|
+
component: {
|
|
170400
|
+
...baseConfig.component,
|
|
170401
|
+
title: applySuffix(baseConfig.component.title || `${title} Activities`)
|
|
170402
|
+
},
|
|
170403
|
+
resource: {
|
|
170404
|
+
...baseConfig.resource,
|
|
170405
|
+
title: applySuffix(baseConfig.resource.title || `${title} Game`),
|
|
170406
|
+
metadata: buildResourceMetadata({
|
|
170407
|
+
baseMetadata: baseConfig.resource.metadata,
|
|
170408
|
+
subject: courseSubject,
|
|
170409
|
+
grade: courseGrade,
|
|
170410
|
+
totalXp,
|
|
170411
|
+
masterableUnits
|
|
170412
|
+
})
|
|
170413
|
+
},
|
|
170414
|
+
componentResource: {
|
|
170415
|
+
...baseConfig.componentResource,
|
|
170416
|
+
title: applySuffix(baseConfig.componentResource.title || "")
|
|
170417
|
+
}
|
|
169231
170418
|
};
|
|
169232
|
-
|
|
169233
|
-
|
|
169234
|
-
|
|
169235
|
-
|
|
169236
|
-
|
|
169237
|
-
|
|
169238
|
-
|
|
169239
|
-
|
|
169240
|
-
|
|
169241
|
-
|
|
169242
|
-
|
|
169243
|
-
|
|
169244
|
-
|
|
169245
|
-
|
|
169246
|
-
|
|
170419
|
+
const existingIntegration = existing.find((i4) => i4.grade === grade && i4.subject === subject);
|
|
170420
|
+
if (existingIntegration) {
|
|
170421
|
+
try {
|
|
170422
|
+
await client2.update(existingIntegration.courseId, fullConfig);
|
|
170423
|
+
} catch (error2) {
|
|
170424
|
+
logTimebackError2("update", error2, {
|
|
170425
|
+
gameId,
|
|
170426
|
+
grade,
|
|
170427
|
+
subject,
|
|
170428
|
+
courseId: existingIntegration.courseId
|
|
170429
|
+
});
|
|
170430
|
+
throw error2;
|
|
170431
|
+
}
|
|
170432
|
+
const [updated] = await db.update(gameTimebackIntegrations).set({
|
|
170433
|
+
totalXp,
|
|
170434
|
+
updatedAt: new Date
|
|
170435
|
+
}).where(eq(gameTimebackIntegrations.id, existingIntegration.id)).returning();
|
|
170436
|
+
if (!updated) {
|
|
170437
|
+
log2.error("[API] Failed to update TimeBack integration record", {
|
|
170438
|
+
gameId,
|
|
170439
|
+
grade,
|
|
170440
|
+
subject,
|
|
170441
|
+
integrationId: existingIntegration.id
|
|
170442
|
+
});
|
|
170443
|
+
throw ApiError.internal("Failed to update TimeBack integration record");
|
|
170444
|
+
}
|
|
170445
|
+
integrations.push(updated);
|
|
170446
|
+
log2.info("[API] Updated TimeBack course", {
|
|
170447
|
+
gameId,
|
|
170448
|
+
courseId: updated.courseId,
|
|
170449
|
+
grade,
|
|
170450
|
+
subject
|
|
170451
|
+
});
|
|
170452
|
+
} else {
|
|
170453
|
+
let result;
|
|
170454
|
+
try {
|
|
170455
|
+
result = await client2.setup(fullConfig, { verbose });
|
|
170456
|
+
} catch (error2) {
|
|
170457
|
+
logTimebackError2("setup", error2, { gameId, grade, subject });
|
|
170458
|
+
throw error2;
|
|
170459
|
+
}
|
|
170460
|
+
const integrationData = {
|
|
170461
|
+
gameId,
|
|
170462
|
+
courseId: result.courseId,
|
|
170463
|
+
grade,
|
|
170464
|
+
subject,
|
|
170465
|
+
totalXp
|
|
170466
|
+
};
|
|
170467
|
+
const [integration] = await db.insert(gameTimebackIntegrations).values(integrationData).returning();
|
|
170468
|
+
if (!integration) {
|
|
170469
|
+
log2.error("[API] Failed to create TimeBack integration record", {
|
|
170470
|
+
gameId,
|
|
170471
|
+
grade,
|
|
170472
|
+
subject,
|
|
170473
|
+
courseId: result.courseId
|
|
170474
|
+
});
|
|
170475
|
+
throw ApiError.internal("Failed to create TimeBack integration record");
|
|
170476
|
+
}
|
|
170477
|
+
integrations.push(integration);
|
|
170478
|
+
if (verbose && result.verboseData) {
|
|
170479
|
+
verboseData.push({
|
|
170480
|
+
integration,
|
|
170481
|
+
config: result.verboseData
|
|
170482
|
+
});
|
|
170483
|
+
}
|
|
170484
|
+
log2.info("[API] Created TimeBack course", {
|
|
170485
|
+
gameId,
|
|
170486
|
+
courseId: integration.courseId,
|
|
170487
|
+
grade,
|
|
170488
|
+
subject
|
|
170489
|
+
});
|
|
170490
|
+
}
|
|
169247
170491
|
}
|
|
169248
170492
|
log2.info("[API] Created TimeBack integration", {
|
|
169249
170493
|
gameId,
|
|
169250
|
-
|
|
169251
|
-
...result.verboseData && { verbose: result.verboseData }
|
|
170494
|
+
courseCount: integrations.length
|
|
169252
170495
|
});
|
|
169253
170496
|
return {
|
|
169254
|
-
|
|
169255
|
-
|
|
169256
|
-
...result.verboseData && { verbose: result.verboseData }
|
|
170497
|
+
integrations,
|
|
170498
|
+
...verbose && verboseData.length > 0 && { verbose: verboseData }
|
|
169257
170499
|
};
|
|
169258
170500
|
}
|
|
169259
170501
|
async function getTimebackIntegration(ctx) {
|
|
@@ -169265,21 +170507,13 @@ async function getTimebackIntegration(ctx) {
|
|
|
169265
170507
|
if (!gameId) {
|
|
169266
170508
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
169267
170509
|
}
|
|
169268
|
-
log2.debug("[API] Getting TimeBack
|
|
170510
|
+
log2.debug("[API] Getting TimeBack integrations", { userId: user.id, gameId });
|
|
170511
|
+
await verifyGameAccessById(gameId, user);
|
|
169269
170512
|
const db = getDatabase();
|
|
169270
|
-
const
|
|
169271
|
-
where: eq(games.id, gameId)
|
|
169272
|
-
});
|
|
169273
|
-
if (!game) {
|
|
169274
|
-
throw ApiError.notFound("Game not found");
|
|
169275
|
-
}
|
|
169276
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169277
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169278
|
-
}
|
|
169279
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
170513
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
169280
170514
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
169281
170515
|
});
|
|
169282
|
-
return
|
|
170516
|
+
return integrations;
|
|
169283
170517
|
}
|
|
169284
170518
|
async function verifyTimebackIntegration(ctx) {
|
|
169285
170519
|
const user = ctx.user;
|
|
@@ -169291,39 +170525,40 @@ async function verifyTimebackIntegration(ctx) {
|
|
|
169291
170525
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
169292
170526
|
}
|
|
169293
170527
|
log2.debug("[API] Verifying TimeBack integration", { userId: user.id, gameId });
|
|
170528
|
+
await verifyGameAccessById(gameId, user);
|
|
169294
170529
|
const db = getDatabase();
|
|
169295
|
-
const
|
|
169296
|
-
where: eq(games.id, gameId)
|
|
169297
|
-
});
|
|
169298
|
-
if (!game) {
|
|
169299
|
-
throw ApiError.notFound("Game not found");
|
|
169300
|
-
}
|
|
169301
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169302
|
-
log2.error("[API] User does not own game", { userId: user.id, gameId });
|
|
169303
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169304
|
-
}
|
|
169305
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
170530
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
169306
170531
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
169307
170532
|
});
|
|
169308
|
-
if (
|
|
170533
|
+
if (integrations.length === 0) {
|
|
170534
|
+
log2.error("[API] No TimeBack integration found", { gameId });
|
|
169309
170535
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
169310
170536
|
}
|
|
169311
170537
|
const client2 = await getTimebackClient();
|
|
169312
|
-
const
|
|
169313
|
-
const
|
|
169314
|
-
|
|
169315
|
-
|
|
169316
|
-
|
|
169317
|
-
|
|
170538
|
+
const now2 = new Date;
|
|
170539
|
+
const results = await Promise.all(integrations.map(async (integration) => {
|
|
170540
|
+
const resources = await client2.verify(integration.courseId);
|
|
170541
|
+
const resourceValues = Object.values(resources);
|
|
170542
|
+
const allFound = resourceValues.every((r3) => r3.found);
|
|
170543
|
+
const errors3 = Object.entries(resources).filter(([_5, r3]) => !r3.found).map(([name4]) => `${name4} not found`);
|
|
170544
|
+
const status = allFound ? "success" : "error";
|
|
170545
|
+
return {
|
|
170546
|
+
integration: { ...integration, lastVerifiedAt: now2 },
|
|
170547
|
+
resources,
|
|
170548
|
+
status,
|
|
170549
|
+
...errors3.length > 0 && { errors: errors3 }
|
|
170550
|
+
};
|
|
170551
|
+
}));
|
|
170552
|
+
await db.update(gameTimebackIntegrations).set({ lastVerifiedAt: now2 }).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
170553
|
+
const overallSuccess = results.every((r3) => r3.status === "success");
|
|
170554
|
+
log2.info("[API] Verified TimeBack integrations", {
|
|
169318
170555
|
gameId,
|
|
169319
|
-
|
|
169320
|
-
status:
|
|
170556
|
+
courseCount: integrations.length,
|
|
170557
|
+
status: overallSuccess ? "success" : "error"
|
|
169321
170558
|
});
|
|
169322
170559
|
return {
|
|
169323
|
-
status:
|
|
169324
|
-
|
|
169325
|
-
resources,
|
|
169326
|
-
...errors3.length > 0 && { errors: errors3 }
|
|
170560
|
+
status: overallSuccess ? "success" : "error",
|
|
170561
|
+
results
|
|
169327
170562
|
};
|
|
169328
170563
|
}
|
|
169329
170564
|
async function getTimebackConfig(ctx) {
|
|
@@ -169336,20 +170571,13 @@ async function getTimebackConfig(ctx) {
|
|
|
169336
170571
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
169337
170572
|
}
|
|
169338
170573
|
log2.debug("[API] Getting TimeBack config", { userId: user.id, gameId });
|
|
170574
|
+
await verifyGameAccessById(gameId, user);
|
|
169339
170575
|
const db = getDatabase();
|
|
169340
|
-
const game = await db.query.games.findFirst({
|
|
169341
|
-
where: eq(games.id, gameId)
|
|
169342
|
-
});
|
|
169343
|
-
if (!game) {
|
|
169344
|
-
throw ApiError.notFound("Game not found");
|
|
169345
|
-
}
|
|
169346
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169347
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169348
|
-
}
|
|
169349
170576
|
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
169350
170577
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
169351
170578
|
});
|
|
169352
170579
|
if (!integration) {
|
|
170580
|
+
log2.error("[API] No TimeBack integration found", { gameId });
|
|
169353
170581
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
169354
170582
|
}
|
|
169355
170583
|
const client2 = await getTimebackClient();
|
|
@@ -169364,34 +170592,40 @@ async function deleteTimebackIntegration(ctx) {
|
|
|
169364
170592
|
if (!gameId) {
|
|
169365
170593
|
throw ApiError.badRequest("Missing gameId parameter");
|
|
169366
170594
|
}
|
|
169367
|
-
log2.debug("[API] Deleting TimeBack
|
|
170595
|
+
log2.debug("[API] Deleting TimeBack integrations", { userId: user.id, gameId });
|
|
170596
|
+
await verifyGameAccessById(gameId, user);
|
|
169368
170597
|
const db = getDatabase();
|
|
169369
|
-
const
|
|
169370
|
-
where: eq(games.id, gameId)
|
|
169371
|
-
});
|
|
169372
|
-
if (!game) {
|
|
169373
|
-
throw ApiError.notFound("Game not found");
|
|
169374
|
-
}
|
|
169375
|
-
if (user.role !== "admin" && game.developerId !== user.id) {
|
|
169376
|
-
throw ApiError.forbidden("You do not own this game");
|
|
169377
|
-
}
|
|
169378
|
-
const integration = await db.query.gameTimebackIntegrations.findFirst({
|
|
170598
|
+
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
169379
170599
|
where: eq(gameTimebackIntegrations.gameId, gameId)
|
|
169380
170600
|
});
|
|
169381
|
-
if (
|
|
170601
|
+
if (integrations.length === 0) {
|
|
170602
|
+
log2.error("[API] No TimeBack integrations found", { gameId });
|
|
169382
170603
|
throw ApiError.notFound("No TimeBack integration found for this game");
|
|
169383
170604
|
}
|
|
169384
170605
|
const client2 = await getTimebackClient();
|
|
169385
|
-
|
|
169386
|
-
|
|
169387
|
-
|
|
169388
|
-
|
|
169389
|
-
|
|
170606
|
+
for (const integration of integrations) {
|
|
170607
|
+
try {
|
|
170608
|
+
await client2.cleanup(integration.courseId);
|
|
170609
|
+
log2.info("[API] Cleaned up TimeBack course", {
|
|
170610
|
+
gameId,
|
|
170611
|
+
courseId: integration.courseId,
|
|
170612
|
+
grade: integration.grade,
|
|
170613
|
+
subject: integration.subject
|
|
170614
|
+
});
|
|
170615
|
+
} catch (error2) {
|
|
170616
|
+
logTimebackError2("cleanup", error2, {
|
|
170617
|
+
gameId,
|
|
170618
|
+
courseId: integration.courseId,
|
|
170619
|
+
grade: integration.grade,
|
|
170620
|
+
subject: integration.subject
|
|
170621
|
+
});
|
|
170622
|
+
throw error2;
|
|
170623
|
+
}
|
|
169390
170624
|
}
|
|
169391
|
-
await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.
|
|
169392
|
-
log2.info("[API] Deleted TimeBack
|
|
170625
|
+
await db.delete(gameTimebackIntegrations).where(eq(gameTimebackIntegrations.gameId, gameId));
|
|
170626
|
+
log2.info("[API] Deleted TimeBack integrations", {
|
|
169393
170627
|
gameId,
|
|
169394
|
-
|
|
170628
|
+
coursesDeleted: integrations.length
|
|
169395
170629
|
});
|
|
169396
170630
|
}
|
|
169397
170631
|
// ../api-core/src/handlers/timeback/xp.ts
|
|
@@ -169523,6 +170757,26 @@ async function getTimeBackXpHistory(ctx) {
|
|
|
169523
170757
|
throw ApiError.internal("Failed to get TimeBack XP history", error2);
|
|
169524
170758
|
}
|
|
169525
170759
|
}
|
|
170760
|
+
// ../api-core/src/handlers/timeback/enrollments.ts
|
|
170761
|
+
init_src();
|
|
170762
|
+
async function getStudentEnrollments(ctx) {
|
|
170763
|
+
const user = ctx.user;
|
|
170764
|
+
if (!user) {
|
|
170765
|
+
throw ApiError.unauthorized("Must be logged in to get enrollments");
|
|
170766
|
+
}
|
|
170767
|
+
const timebackId = ctx.params.timebackId;
|
|
170768
|
+
if (!timebackId) {
|
|
170769
|
+
throw ApiError.badRequest("Missing timebackId parameter");
|
|
170770
|
+
}
|
|
170771
|
+
log2.debug("[API] Getting student enrollments", { userId: user.id, timebackId });
|
|
170772
|
+
const enrollments = await fetchEnrollmentsForUser(timebackId);
|
|
170773
|
+
log2.info("[API] Retrieved student enrollments", {
|
|
170774
|
+
userId: user.id,
|
|
170775
|
+
timebackId,
|
|
170776
|
+
enrollmentCount: enrollments.length
|
|
170777
|
+
});
|
|
170778
|
+
return { enrollments };
|
|
170779
|
+
}
|
|
169526
170780
|
// src/routes/integrations/timeback.ts
|
|
169527
170781
|
var timebackRouter = new Hono2;
|
|
169528
170782
|
timebackRouter.post("/populate-student", async (c3) => {
|
|
@@ -169714,6 +170968,25 @@ timebackRouter.post("/end-activity", async (c3) => {
|
|
|
169714
170968
|
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
169715
170969
|
}
|
|
169716
170970
|
});
|
|
170971
|
+
timebackRouter.get("/enrollments/:timebackId", async (c3) => {
|
|
170972
|
+
const timebackId = c3.req.param("timebackId");
|
|
170973
|
+
const ctx = {
|
|
170974
|
+
user: c3.get("user"),
|
|
170975
|
+
params: { timebackId },
|
|
170976
|
+
url: new URL(c3.req.url),
|
|
170977
|
+
request: c3.req.raw
|
|
170978
|
+
};
|
|
170979
|
+
try {
|
|
170980
|
+
const result = await getStudentEnrollments(ctx);
|
|
170981
|
+
return c3.json(result);
|
|
170982
|
+
} catch (error2) {
|
|
170983
|
+
if (error2 instanceof ApiError) {
|
|
170984
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
170985
|
+
}
|
|
170986
|
+
console.error("Error in getStudentEnrollments:", error2);
|
|
170987
|
+
return c3.json(createUnknownErrorResponse(error2), 500);
|
|
170988
|
+
}
|
|
170989
|
+
});
|
|
169717
170990
|
// ../api-core/src/handlers/lti-timeback/index.ts
|
|
169718
170991
|
import * as crypto7 from "node:crypto";
|
|
169719
170992
|
init_src();
|
|
@@ -170060,7 +171333,7 @@ var {
|
|
|
170060
171333
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
|
|
170061
171334
|
import { createServer } from "node:net";
|
|
170062
171335
|
import { homedir } from "node:os";
|
|
170063
|
-
import { join as
|
|
171336
|
+
import { join as join5 } from "node:path";
|
|
170064
171337
|
async function isPortAvailableOnHost(port, host) {
|
|
170065
171338
|
return new Promise((resolve2) => {
|
|
170066
171339
|
const server2 = createServer();
|
|
@@ -170099,11 +171372,11 @@ async function findAvailablePort(startPort = 4321) {
|
|
|
170099
171372
|
}
|
|
170100
171373
|
function getRegistryPath() {
|
|
170101
171374
|
const home = homedir();
|
|
170102
|
-
const dir =
|
|
171375
|
+
const dir = join5(home, ".playcademy");
|
|
170103
171376
|
if (!existsSync2(dir)) {
|
|
170104
171377
|
mkdirSync2(dir, { recursive: true });
|
|
170105
171378
|
}
|
|
170106
|
-
return
|
|
171379
|
+
return join5(dir, ".proc");
|
|
170107
171380
|
}
|
|
170108
171381
|
function readRegistry() {
|
|
170109
171382
|
const registryPath = getRegistryPath();
|