timeback-studio 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +360 -60
- package/dist/cli/commands/credentials/create-account.d.ts +37 -0
- package/dist/cli/commands/credentials/create-account.d.ts.map +1 -0
- package/dist/cli/commands/credentials/email.d.ts.map +1 -1
- package/dist/index.js +357 -57
- package/dist/server/controllers/bootstrap.d.ts +3 -0
- package/dist/server/controllers/bootstrap.d.ts.map +1 -1
- package/dist/server/controllers/live.d.ts +5 -3
- package/dist/server/controllers/live.d.ts.map +1 -1
- package/dist/server/lib/sse.d.ts.map +1 -1
- package/dist/server/lib/types.d.ts +2 -0
- package/dist/server/lib/types.d.ts.map +1 -1
- package/dist/server/services/bootstrap.d.ts.map +1 -1
- package/dist/server/services/index.d.ts +2 -2
- package/dist/server/services/index.d.ts.map +1 -1
- package/dist/server/services/live-poller.d.ts +103 -0
- package/dist/server/services/live-poller.d.ts.map +1 -0
- package/dist/server/services/live.d.ts +4 -79
- package/dist/server/services/live.d.ts.map +1 -1
- package/dist/server/services/status.d.ts +7 -4
- package/dist/server/services/status.d.ts.map +1 -1
- package/dist/server/services/types/index.d.ts +1 -1
- package/dist/server/services/types/index.d.ts.map +1 -1
- package/dist/server/services/types/live.d.ts +3 -14
- package/dist/server/services/types/live.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -14744,13 +14744,14 @@ async function validateEmailWithTimeback(environment, clientId, clientSecret, em
|
|
|
14744
14744
|
if (page.data.length === 0) {
|
|
14745
14745
|
return {
|
|
14746
14746
|
valid: false,
|
|
14747
|
+
reason: "not_found",
|
|
14747
14748
|
error: `No user found with email "${email3}" in ${environment}`
|
|
14748
14749
|
};
|
|
14749
14750
|
}
|
|
14750
14751
|
return { valid: true };
|
|
14751
14752
|
} catch (error48) {
|
|
14752
14753
|
const message = error48 instanceof Error ? error48.message : "Unknown error";
|
|
14753
|
-
return { valid: false, error: `Failed to validate email: ${message}` };
|
|
14754
|
+
return { valid: false, reason: "api_error", error: `Failed to validate email: ${message}` };
|
|
14754
14755
|
}
|
|
14755
14756
|
}
|
|
14756
14757
|
|
|
@@ -15052,6 +15053,8 @@ var ActivityCompletedInput = exports_external.object({
|
|
|
15052
15053
|
metricsId: exports_external.string().optional(),
|
|
15053
15054
|
id: exports_external.string().optional(),
|
|
15054
15055
|
extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
|
|
15056
|
+
edApp: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
|
|
15057
|
+
session: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
|
|
15055
15058
|
attempt: exports_external.number().int().min(1).optional(),
|
|
15056
15059
|
generatedExtensions: exports_external.object({
|
|
15057
15060
|
pctCompleteApp: exports_external.number().optional()
|
|
@@ -15064,7 +15067,9 @@ var TimeSpentInput = exports_external.object({
|
|
|
15064
15067
|
eventTime: IsoDateTimeString.optional(),
|
|
15065
15068
|
metricsId: exports_external.string().optional(),
|
|
15066
15069
|
id: exports_external.string().optional(),
|
|
15067
|
-
extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
15070
|
+
extensions: exports_external.record(exports_external.string(), exports_external.unknown()).optional(),
|
|
15071
|
+
edApp: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional(),
|
|
15072
|
+
session: exports_external.union([exports_external.string(), exports_external.record(exports_external.string(), exports_external.unknown())]).optional()
|
|
15068
15073
|
}).strict();
|
|
15069
15074
|
var TimebackEvent = exports_external.union([TimebackActivityEvent, TimebackTimeSpentEvent]);
|
|
15070
15075
|
var CaliperEnvelope = exports_external.object({
|
|
@@ -15254,7 +15259,7 @@ var TimebackConfig = exports_external.object({
|
|
|
15254
15259
|
path: ["courses"]
|
|
15255
15260
|
});
|
|
15256
15261
|
// ../types/src/zod/edubridge.ts
|
|
15257
|
-
var EdubridgeDateString = IsoDateTimeString;
|
|
15262
|
+
var EdubridgeDateString = exports_external.union([IsoDateTimeString, IsoDateString]);
|
|
15258
15263
|
var EduBridgeEnrollment = exports_external.object({
|
|
15259
15264
|
id: exports_external.string(),
|
|
15260
15265
|
role: exports_external.string(),
|
|
@@ -17540,6 +17545,184 @@ async function addCredentials(options = {}) {
|
|
|
17540
17545
|
if (exitOnComplete)
|
|
17541
17546
|
process.exit(0);
|
|
17542
17547
|
}
|
|
17548
|
+
// src/cli/commands/credentials/create-account.ts
|
|
17549
|
+
import { randomUUID } from "node:crypto";
|
|
17550
|
+
import { TimebackClient as TimebackClient3 } from "@timeback/core";
|
|
17551
|
+
async function promptName(label) {
|
|
17552
|
+
const value = await he({
|
|
17553
|
+
message: label,
|
|
17554
|
+
placeholder: "Enter name",
|
|
17555
|
+
validate: (v2) => {
|
|
17556
|
+
if (!v2?.trim())
|
|
17557
|
+
return `${label} is required`;
|
|
17558
|
+
}
|
|
17559
|
+
});
|
|
17560
|
+
if (isCancelled(value))
|
|
17561
|
+
return null;
|
|
17562
|
+
return value.trim();
|
|
17563
|
+
}
|
|
17564
|
+
async function searchOrganizations(client, query) {
|
|
17565
|
+
const allOrgs = await client.oneroster.orgs.listAll({
|
|
17566
|
+
where: { status: "active" },
|
|
17567
|
+
max: 100
|
|
17568
|
+
});
|
|
17569
|
+
if (!query.trim())
|
|
17570
|
+
return allOrgs;
|
|
17571
|
+
const lowerQuery = query.toLowerCase().trim();
|
|
17572
|
+
return allOrgs.filter((org) => org.name?.toLowerCase().includes(lowerQuery));
|
|
17573
|
+
}
|
|
17574
|
+
async function searchForOrganization(client) {
|
|
17575
|
+
const query = await he({
|
|
17576
|
+
message: "Search for organization",
|
|
17577
|
+
placeholder: "Enter organization name to search"
|
|
17578
|
+
});
|
|
17579
|
+
if (isCancelled(query))
|
|
17580
|
+
return null;
|
|
17581
|
+
const s = Y2();
|
|
17582
|
+
s.start("Searching organizations...");
|
|
17583
|
+
let results;
|
|
17584
|
+
try {
|
|
17585
|
+
results = await searchOrganizations(client, query);
|
|
17586
|
+
s.stop(green(`Found ${results.length} result${results.length === 1 ? "" : "s"}`));
|
|
17587
|
+
} catch (error48) {
|
|
17588
|
+
s.stop(red("Failed to search organizations"));
|
|
17589
|
+
M2.error(error48 instanceof Error ? error48.message : "Unknown error");
|
|
17590
|
+
return null;
|
|
17591
|
+
}
|
|
17592
|
+
if (results.length === 0) {
|
|
17593
|
+
M2.warn("No organizations found matching your search");
|
|
17594
|
+
return promptOrganization(client);
|
|
17595
|
+
}
|
|
17596
|
+
const validResults = results.filter((org) => org.sourcedId);
|
|
17597
|
+
if (validResults.length === 0) {
|
|
17598
|
+
M2.warn("No valid organizations found (missing IDs)");
|
|
17599
|
+
return promptOrganization(client);
|
|
17600
|
+
}
|
|
17601
|
+
const options = validResults.map((org) => ({
|
|
17602
|
+
value: org.sourcedId,
|
|
17603
|
+
label: org.name ?? "Unnamed Organization"
|
|
17604
|
+
}));
|
|
17605
|
+
options.push({ value: "__back__", label: `${dim("←")} Back to options` });
|
|
17606
|
+
const selection = await ve({
|
|
17607
|
+
message: "Select an organization",
|
|
17608
|
+
options
|
|
17609
|
+
});
|
|
17610
|
+
if (isCancelled(selection))
|
|
17611
|
+
return null;
|
|
17612
|
+
if (selection === "__back__") {
|
|
17613
|
+
return promptOrganization(client);
|
|
17614
|
+
}
|
|
17615
|
+
return validResults.find((org) => org.sourcedId === selection) ?? null;
|
|
17616
|
+
}
|
|
17617
|
+
async function promptOrganization(client) {
|
|
17618
|
+
const action = await ve({
|
|
17619
|
+
message: "Organization",
|
|
17620
|
+
options: [
|
|
17621
|
+
{ value: "search", label: "Search for existing organization" },
|
|
17622
|
+
{ value: "create", label: "Create new organization" }
|
|
17623
|
+
]
|
|
17624
|
+
});
|
|
17625
|
+
if (isCancelled(action))
|
|
17626
|
+
return null;
|
|
17627
|
+
if (action === "create") {
|
|
17628
|
+
return createNewOrganization(client);
|
|
17629
|
+
}
|
|
17630
|
+
return searchForOrganization(client);
|
|
17631
|
+
}
|
|
17632
|
+
async function createNewOrganization(client) {
|
|
17633
|
+
const name = await he({
|
|
17634
|
+
message: "Organization name",
|
|
17635
|
+
placeholder: "Enter organization name",
|
|
17636
|
+
validate: (v2) => {
|
|
17637
|
+
if (!v2?.trim())
|
|
17638
|
+
return "Organization name is required";
|
|
17639
|
+
}
|
|
17640
|
+
});
|
|
17641
|
+
if (isCancelled(name))
|
|
17642
|
+
return null;
|
|
17643
|
+
const s = Y2();
|
|
17644
|
+
s.start("Creating organization...");
|
|
17645
|
+
const sourcedId = randomUUID();
|
|
17646
|
+
try {
|
|
17647
|
+
await client.oneroster.orgs.create({
|
|
17648
|
+
sourcedId,
|
|
17649
|
+
name: name.trim(),
|
|
17650
|
+
type: "school",
|
|
17651
|
+
status: "active"
|
|
17652
|
+
});
|
|
17653
|
+
const organization = await client.oneroster.orgs.get(sourcedId);
|
|
17654
|
+
s.stop(green(`Organization "${name}" created`));
|
|
17655
|
+
return organization;
|
|
17656
|
+
} catch (error48) {
|
|
17657
|
+
s.stop(red("Failed to create organization"));
|
|
17658
|
+
M2.error(error48 instanceof Error ? error48.message : "Unknown error");
|
|
17659
|
+
return null;
|
|
17660
|
+
}
|
|
17661
|
+
}
|
|
17662
|
+
async function createUser(client, email3, givenName, familyName, organizationId) {
|
|
17663
|
+
const s = Y2();
|
|
17664
|
+
s.start("Creating account...");
|
|
17665
|
+
const sourcedId = randomUUID();
|
|
17666
|
+
try {
|
|
17667
|
+
await client.oneroster.users.create({
|
|
17668
|
+
sourcedId,
|
|
17669
|
+
givenName,
|
|
17670
|
+
familyName,
|
|
17671
|
+
email: email3.toLowerCase(),
|
|
17672
|
+
enabledUser: true,
|
|
17673
|
+
status: "active",
|
|
17674
|
+
roles: [
|
|
17675
|
+
{
|
|
17676
|
+
roleType: "primary",
|
|
17677
|
+
role: "administrator",
|
|
17678
|
+
org: { sourcedId: organizationId }
|
|
17679
|
+
}
|
|
17680
|
+
]
|
|
17681
|
+
});
|
|
17682
|
+
const user = await client.oneroster.users.get(sourcedId);
|
|
17683
|
+
s.stop(green("Account created successfully"));
|
|
17684
|
+
return user;
|
|
17685
|
+
} catch (error48) {
|
|
17686
|
+
s.stop(red("Failed to create account"));
|
|
17687
|
+
M2.error(error48 instanceof Error ? error48.message : "Unknown error");
|
|
17688
|
+
return null;
|
|
17689
|
+
}
|
|
17690
|
+
}
|
|
17691
|
+
async function createAccountFlow(options) {
|
|
17692
|
+
const { environment, clientId, clientSecret, email: email3 } = options;
|
|
17693
|
+
M2.info("");
|
|
17694
|
+
M2.info(`No account found for ${dim(email3)} in ${environment}`);
|
|
17695
|
+
const shouldCreate = await ye({
|
|
17696
|
+
message: "Would you like to create a new account?",
|
|
17697
|
+
initialValue: true
|
|
17698
|
+
});
|
|
17699
|
+
if (isCancelled(shouldCreate)) {
|
|
17700
|
+
return { success: false };
|
|
17701
|
+
}
|
|
17702
|
+
if (!shouldCreate) {
|
|
17703
|
+
return { success: false, declined: true };
|
|
17704
|
+
}
|
|
17705
|
+
const client = new TimebackClient3({
|
|
17706
|
+
env: environment,
|
|
17707
|
+
auth: { clientId, clientSecret }
|
|
17708
|
+
});
|
|
17709
|
+
const givenName = await promptName("First name");
|
|
17710
|
+
if (!givenName)
|
|
17711
|
+
return { success: false };
|
|
17712
|
+
const familyName = await promptName("Last name");
|
|
17713
|
+
if (!familyName)
|
|
17714
|
+
return { success: false };
|
|
17715
|
+
const organization = await promptOrganization(client);
|
|
17716
|
+
if (!organization?.sourcedId)
|
|
17717
|
+
return { success: false };
|
|
17718
|
+
const user = await createUser(client, email3, givenName, familyName, organization.sourcedId);
|
|
17719
|
+
if (!user) {
|
|
17720
|
+
return { success: false };
|
|
17721
|
+
}
|
|
17722
|
+
M2.success(`Account created for ${user.givenName} ${user.familyName}`);
|
|
17723
|
+
return { success: true };
|
|
17724
|
+
}
|
|
17725
|
+
|
|
17543
17726
|
// src/cli/commands/credentials/email.ts
|
|
17544
17727
|
async function updateEmail(options = {}) {
|
|
17545
17728
|
const { exitOnComplete = true, inline = false } = options;
|
|
@@ -17608,32 +17791,58 @@ async function updateEmail(options = {}) {
|
|
|
17608
17791
|
return;
|
|
17609
17792
|
}
|
|
17610
17793
|
const emailUnchanged = email3 === (currentEmail ?? "");
|
|
17611
|
-
if (emailUnchanged) {
|
|
17612
|
-
if (!inline)
|
|
17613
|
-
outro.info("Email unchanged");
|
|
17614
|
-
if (exitOnComplete)
|
|
17615
|
-
process.exit(0);
|
|
17616
|
-
return;
|
|
17617
|
-
}
|
|
17618
17794
|
if (email3) {
|
|
17619
17795
|
const s = Y2();
|
|
17620
|
-
s.start("
|
|
17796
|
+
s.start("Checking account...");
|
|
17621
17797
|
const result = await validateEmailWithTimeback(targetEnv, currentCreds.clientId, currentCreds.clientSecret, email3);
|
|
17622
17798
|
if (!result.valid) {
|
|
17623
|
-
|
|
17624
|
-
|
|
17625
|
-
|
|
17799
|
+
if (result.reason !== "not_found") {
|
|
17800
|
+
s.stop(red("Account check failed"));
|
|
17801
|
+
M2.error(result.error ?? "Unknown error");
|
|
17802
|
+
if (!inline)
|
|
17803
|
+
outro.error("Account check failed");
|
|
17804
|
+
if (exitOnComplete)
|
|
17805
|
+
process.exit(1);
|
|
17806
|
+
return;
|
|
17807
|
+
}
|
|
17808
|
+
s.stop(red("No account found"));
|
|
17809
|
+
const { success: accountCreated, declined } = await createAccountFlow({
|
|
17810
|
+
environment: targetEnv,
|
|
17811
|
+
clientId: currentCreds.clientId,
|
|
17812
|
+
clientSecret: currentCreds.clientSecret,
|
|
17813
|
+
email: email3
|
|
17814
|
+
});
|
|
17815
|
+
if (!emailUnchanged) {
|
|
17816
|
+
await saveCredentials(targetEnv, {
|
|
17817
|
+
...currentCreds,
|
|
17818
|
+
email: email3
|
|
17819
|
+
});
|
|
17820
|
+
M2.success(`Email saved for ${targetEnv}`);
|
|
17821
|
+
}
|
|
17822
|
+
if (!inline) {
|
|
17823
|
+
if (accountCreated || declined) {
|
|
17824
|
+
outro.success();
|
|
17825
|
+
} else {
|
|
17826
|
+
outro.info("Setup incomplete - run this command again to finish");
|
|
17827
|
+
}
|
|
17828
|
+
}
|
|
17829
|
+
if (exitOnComplete)
|
|
17830
|
+
process.exit(0);
|
|
17831
|
+
return;
|
|
17832
|
+
}
|
|
17833
|
+
s.stop(green("Account verified"));
|
|
17834
|
+
if (emailUnchanged) {
|
|
17626
17835
|
if (!inline)
|
|
17627
|
-
outro.
|
|
17836
|
+
outro.info("Email unchanged");
|
|
17628
17837
|
if (exitOnComplete)
|
|
17629
|
-
process.exit(
|
|
17838
|
+
process.exit(0);
|
|
17630
17839
|
return;
|
|
17631
17840
|
}
|
|
17632
17841
|
await saveCredentials(targetEnv, {
|
|
17633
17842
|
...currentCreds,
|
|
17634
17843
|
email: email3
|
|
17635
17844
|
});
|
|
17636
|
-
|
|
17845
|
+
M2.success(`Email updated for ${targetEnv}`);
|
|
17637
17846
|
if (!inline)
|
|
17638
17847
|
outro.success();
|
|
17639
17848
|
if (exitOnComplete)
|
|
@@ -17846,7 +18055,7 @@ async function handleCredentialSetup(options = {}) {
|
|
|
17846
18055
|
};
|
|
17847
18056
|
}
|
|
17848
18057
|
// src/cli/lib/onboarding/import.ts
|
|
17849
|
-
import { TimebackClient as
|
|
18058
|
+
import { TimebackClient as TimebackClient4 } from "@timeback/core";
|
|
17850
18059
|
async function promptImportApp(credentials, configuredEnvs) {
|
|
17851
18060
|
let env2;
|
|
17852
18061
|
if (configuredEnvs.length === 1 && configuredEnvs[0]) {
|
|
@@ -17859,7 +18068,7 @@ async function promptImportApp(credentials, configuredEnvs) {
|
|
|
17859
18068
|
env2 = selectedEnv;
|
|
17860
18069
|
}
|
|
17861
18070
|
const creds = credentials[env2];
|
|
17862
|
-
const client = new
|
|
18071
|
+
const client = new TimebackClient4({
|
|
17863
18072
|
env: env2,
|
|
17864
18073
|
auth: { clientId: creds.clientId, clientSecret: creds.clientSecret }
|
|
17865
18074
|
});
|
|
@@ -20289,7 +20498,8 @@ var cors = (options) => {
|
|
|
20289
20498
|
async function handleBootstrap(c, ctx) {
|
|
20290
20499
|
const { bootstrap } = c.get("services");
|
|
20291
20500
|
const env2 = c.get("env");
|
|
20292
|
-
const
|
|
20501
|
+
const freshCredentials = await getSavedCredentials(env2);
|
|
20502
|
+
const email3 = freshCredentials?.email;
|
|
20293
20503
|
const courseIds = ctx.userConfig.courseIds[env2];
|
|
20294
20504
|
const result = await bootstrap.getBootstrap({ email: email3, courseIds });
|
|
20295
20505
|
return c.json(result);
|
|
@@ -20617,6 +20827,7 @@ function createLogger(options = {}) {
|
|
|
20617
20827
|
}
|
|
20618
20828
|
// src/server/services/bootstrap.ts
|
|
20619
20829
|
var log = createLogger({ scope: "studio:service:bootstrap" });
|
|
20830
|
+
var hasLoggedInitialLoad = false;
|
|
20620
20831
|
|
|
20621
20832
|
class BootstrapService {
|
|
20622
20833
|
client;
|
|
@@ -20647,8 +20858,11 @@ class BootstrapService {
|
|
|
20647
20858
|
stats,
|
|
20648
20859
|
errors: errors3.length
|
|
20649
20860
|
});
|
|
20650
|
-
|
|
20651
|
-
|
|
20861
|
+
if (!hasLoggedInitialLoad) {
|
|
20862
|
+
const message = `Loaded ${underline(stats.totalCourses)} ${pluralize(stats.totalCourses, "course")}, ${underline(stats.totalStudents)} ${pluralize(stats.totalStudents, "student")}`;
|
|
20863
|
+
M2.success(message);
|
|
20864
|
+
hasLoggedInitialLoad = true;
|
|
20865
|
+
}
|
|
20652
20866
|
return {
|
|
20653
20867
|
user,
|
|
20654
20868
|
courses,
|
|
@@ -21128,48 +21342,107 @@ class EventsService {
|
|
|
21128
21342
|
return result;
|
|
21129
21343
|
}
|
|
21130
21344
|
}
|
|
21131
|
-
// src/server/services/live.ts
|
|
21132
|
-
var log4 = createLogger({ scope: "studio:service:live" });
|
|
21345
|
+
// src/server/services/live-poller.ts
|
|
21346
|
+
var log4 = createLogger({ scope: "studio:service:live-poller" });
|
|
21347
|
+
var pollers = new Map;
|
|
21348
|
+
function getOrCreatePoller(environment, statusService, eventsService) {
|
|
21349
|
+
let poller = pollers.get(environment);
|
|
21350
|
+
if (!poller) {
|
|
21351
|
+
poller = new LivePoller({ environment, statusService, eventsService });
|
|
21352
|
+
pollers.set(environment, poller);
|
|
21353
|
+
}
|
|
21354
|
+
return poller;
|
|
21355
|
+
}
|
|
21133
21356
|
|
|
21134
|
-
class
|
|
21357
|
+
class LivePoller {
|
|
21358
|
+
environment;
|
|
21135
21359
|
statusService;
|
|
21136
21360
|
eventsService;
|
|
21361
|
+
subscribers = new Set;
|
|
21362
|
+
interval = null;
|
|
21363
|
+
state;
|
|
21364
|
+
lastBroadcast;
|
|
21365
|
+
initialTickPromise = null;
|
|
21366
|
+
ticking = false;
|
|
21137
21367
|
constructor(options) {
|
|
21368
|
+
this.environment = options.environment;
|
|
21138
21369
|
this.statusService = options.statusService;
|
|
21139
21370
|
this.eventsService = options.eventsService;
|
|
21140
|
-
}
|
|
21141
|
-
static createInitialState() {
|
|
21142
21371
|
const now = Date.now();
|
|
21143
|
-
|
|
21372
|
+
this.state = {
|
|
21144
21373
|
lastStatusHash: "",
|
|
21145
21374
|
lastEventTime: new Date().toISOString(),
|
|
21146
21375
|
lastStatusTick: now,
|
|
21147
21376
|
lastEventsTick: now
|
|
21148
21377
|
};
|
|
21149
21378
|
}
|
|
21150
|
-
|
|
21151
|
-
|
|
21152
|
-
|
|
21153
|
-
|
|
21154
|
-
const statusUpdate = await this.fetchStatusIfChanged(state.lastStatusHash);
|
|
21155
|
-
if (statusUpdate) {
|
|
21156
|
-
updates.push(statusUpdate.update);
|
|
21157
|
-
newState.lastStatusHash = statusUpdate.hash;
|
|
21379
|
+
subscribe(callback) {
|
|
21380
|
+
this.subscribers.add(callback);
|
|
21381
|
+
if (this.subscribers.size === 1) {
|
|
21382
|
+
this.start();
|
|
21158
21383
|
}
|
|
21159
|
-
|
|
21160
|
-
|
|
21161
|
-
|
|
21162
|
-
|
|
21163
|
-
|
|
21164
|
-
updates.push(eventsUpdate.update);
|
|
21165
|
-
newState.lastEventTime = eventsUpdate.newEventTime;
|
|
21384
|
+
return () => {
|
|
21385
|
+
this.subscribers.delete(callback);
|
|
21386
|
+
if (this.subscribers.size === 0) {
|
|
21387
|
+
this.stop();
|
|
21388
|
+
pollers.delete(this.environment);
|
|
21166
21389
|
}
|
|
21167
|
-
|
|
21390
|
+
};
|
|
21391
|
+
}
|
|
21392
|
+
async getLastBroadcastAfterInitialTick() {
|
|
21393
|
+
if (this.initialTickPromise) {
|
|
21394
|
+
await this.initialTickPromise;
|
|
21168
21395
|
}
|
|
21169
|
-
return
|
|
21396
|
+
return this.lastBroadcast;
|
|
21170
21397
|
}
|
|
21171
|
-
|
|
21172
|
-
|
|
21398
|
+
start() {
|
|
21399
|
+
log4.debug("Poller started", { environment: this.environment });
|
|
21400
|
+
this.initialTickPromise = this.tick();
|
|
21401
|
+
this.interval = setInterval(() => this.tick(), constants.sse.defaultTickIntervalMs);
|
|
21402
|
+
}
|
|
21403
|
+
stop() {
|
|
21404
|
+
if (this.interval) {
|
|
21405
|
+
clearInterval(this.interval);
|
|
21406
|
+
this.interval = null;
|
|
21407
|
+
}
|
|
21408
|
+
this.lastBroadcast = undefined;
|
|
21409
|
+
this.initialTickPromise = null;
|
|
21410
|
+
log4.debug("Poller stopped", { environment: this.environment });
|
|
21411
|
+
}
|
|
21412
|
+
async tick() {
|
|
21413
|
+
if (this.ticking)
|
|
21414
|
+
return;
|
|
21415
|
+
this.ticking = true;
|
|
21416
|
+
try {
|
|
21417
|
+
const updates = [];
|
|
21418
|
+
const now = Date.now();
|
|
21419
|
+
const statusUpdate = await this.fetchStatusIfChanged(this.state.lastStatusHash);
|
|
21420
|
+
if (statusUpdate) {
|
|
21421
|
+
updates.push(statusUpdate.update);
|
|
21422
|
+
this.state.lastStatusHash = statusUpdate.hash;
|
|
21423
|
+
}
|
|
21424
|
+
this.state.lastStatusTick = now;
|
|
21425
|
+
const shouldCheckEvents = now - this.state.lastEventsTick >= constants.events.polling.tickIntervalMs;
|
|
21426
|
+
if (shouldCheckEvents) {
|
|
21427
|
+
const eventsUpdate = await this.fetchEventsIfNew(this.state.lastEventTime);
|
|
21428
|
+
if (eventsUpdate) {
|
|
21429
|
+
updates.push(eventsUpdate.update);
|
|
21430
|
+
this.state.lastEventTime = eventsUpdate.newEventTime;
|
|
21431
|
+
}
|
|
21432
|
+
this.state.lastEventsTick = now;
|
|
21433
|
+
}
|
|
21434
|
+
if (updates.length > 0) {
|
|
21435
|
+
const data = JSON.stringify(updates);
|
|
21436
|
+
this.lastBroadcast = data;
|
|
21437
|
+
for (const callback of this.subscribers) {
|
|
21438
|
+
callback(data);
|
|
21439
|
+
}
|
|
21440
|
+
}
|
|
21441
|
+
} catch (error48) {
|
|
21442
|
+
log4.error("Tick failed", { environment: this.environment, error: error48 });
|
|
21443
|
+
} finally {
|
|
21444
|
+
this.ticking = false;
|
|
21445
|
+
}
|
|
21173
21446
|
}
|
|
21174
21447
|
async fetchStatusIfChanged(lastHash) {
|
|
21175
21448
|
try {
|
|
@@ -21196,7 +21469,8 @@ class LiveService {
|
|
|
21196
21469
|
}
|
|
21197
21470
|
const message = `${underline(newEvents.length)} new ${pluralize(newEvents.length, "event")}`;
|
|
21198
21471
|
M2.message(message, { symbol: magenta("◆") });
|
|
21199
|
-
const mostRecentTime = newEvents.map((e2) => e2.eventTime).sort().pop();
|
|
21472
|
+
const mostRecentTime = newEvents.map((e2) => new Date(e2.eventTime).getTime()).sort((a, b3) => a - b3).pop();
|
|
21473
|
+
const newEventTime = mostRecentTime === undefined ? lastEventTime : new Date(mostRecentTime + 1).toISOString();
|
|
21200
21474
|
return {
|
|
21201
21475
|
update: {
|
|
21202
21476
|
type: "events",
|
|
@@ -21205,7 +21479,7 @@ class LiveService {
|
|
|
21205
21479
|
timestamp: new Date().toISOString()
|
|
21206
21480
|
}
|
|
21207
21481
|
},
|
|
21208
|
-
newEventTime
|
|
21482
|
+
newEventTime
|
|
21209
21483
|
};
|
|
21210
21484
|
} catch (error48) {
|
|
21211
21485
|
log4.error("Failed to fetch events", { error: error48 });
|
|
@@ -21221,11 +21495,15 @@ class StatusService {
|
|
|
21221
21495
|
}
|
|
21222
21496
|
async getStatus() {
|
|
21223
21497
|
const configuredEnvironments = await getConfiguredEnvironments();
|
|
21498
|
+
const [stagingCreds, productionCreds] = await Promise.all([
|
|
21499
|
+
getSavedCredentials("staging"),
|
|
21500
|
+
getSavedCredentials("production")
|
|
21501
|
+
]);
|
|
21224
21502
|
return {
|
|
21225
21503
|
config: this.ctx.userConfig,
|
|
21226
21504
|
environment: this.ctx.defaultEnvironment,
|
|
21227
21505
|
configuredEnvironments,
|
|
21228
|
-
hasEmail: !!
|
|
21506
|
+
hasEmail: !!stagingCreds?.email || !!productionCreds?.email
|
|
21229
21507
|
};
|
|
21230
21508
|
}
|
|
21231
21509
|
}
|
|
@@ -21606,6 +21884,7 @@ function runSSE(c, options) {
|
|
|
21606
21884
|
} catch {
|
|
21607
21885
|
abort("write error");
|
|
21608
21886
|
}
|
|
21887
|
+
options.onClose?.();
|
|
21609
21888
|
unregisterConnection(key, () => abort("superseded"));
|
|
21610
21889
|
log8.debug("SSE connection closed", { ...meta3, reason: closeReason });
|
|
21611
21890
|
});
|
|
@@ -21708,20 +21987,41 @@ function handleEventsStream(c) {
|
|
|
21708
21987
|
}
|
|
21709
21988
|
// src/server/controllers/live.ts
|
|
21710
21989
|
function handleLive(c) {
|
|
21990
|
+
const env2 = c.get("env");
|
|
21711
21991
|
const { status: statusService, events: eventsService } = c.get("services");
|
|
21712
|
-
const
|
|
21713
|
-
let
|
|
21992
|
+
const poller = getOrCreatePoller(env2, statusService, eventsService);
|
|
21993
|
+
let pending;
|
|
21994
|
+
let isFirstTick = true;
|
|
21995
|
+
const takePending = () => {
|
|
21996
|
+
const data = pending;
|
|
21997
|
+
pending = undefined;
|
|
21998
|
+
return data;
|
|
21999
|
+
};
|
|
22000
|
+
const unsubscribe = poller.subscribe((data) => {
|
|
22001
|
+
pending = data;
|
|
22002
|
+
});
|
|
21714
22003
|
return runSSE(c, {
|
|
21715
22004
|
event: "live",
|
|
21716
22005
|
intervalMs: constants.sse.defaultTickIntervalMs,
|
|
21717
22006
|
sendInitial: true,
|
|
22007
|
+
onClose: unsubscribe,
|
|
21718
22008
|
tick: async () => {
|
|
21719
|
-
|
|
21720
|
-
|
|
21721
|
-
|
|
21722
|
-
|
|
22009
|
+
if (isFirstTick) {
|
|
22010
|
+
isFirstTick = false;
|
|
22011
|
+
const immediate = takePending();
|
|
22012
|
+
if (immediate !== undefined) {
|
|
22013
|
+
return immediate;
|
|
22014
|
+
}
|
|
22015
|
+
const initial = await poller.getLastBroadcastAfterInitialTick();
|
|
22016
|
+
if (pending !== undefined && pending === initial) {
|
|
22017
|
+
pending = undefined;
|
|
22018
|
+
}
|
|
22019
|
+
if (initial !== undefined) {
|
|
22020
|
+
return initial;
|
|
22021
|
+
}
|
|
22022
|
+
return takePending();
|
|
21723
22023
|
}
|
|
21724
|
-
return
|
|
22024
|
+
return takePending();
|
|
21725
22025
|
}
|
|
21726
22026
|
});
|
|
21727
22027
|
}
|
|
@@ -11,6 +11,9 @@ import type { EnvVariables } from '../lib';
|
|
|
11
11
|
*
|
|
12
12
|
* Requires env middleware to set `env` and `client` context variables.
|
|
13
13
|
*
|
|
14
|
+
* NOTE: We read credentials fresh from disk to pick up changes made via CLI
|
|
15
|
+
* (e.g., email updates, account creation) without requiring a server restart.
|
|
16
|
+
*
|
|
14
17
|
* @param c - Hono context with env variables
|
|
15
18
|
* @param ctx - App context
|
|
16
19
|
* @returns JSON response with user and courses
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/server/controllers/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/server/controllers/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,CAAC,EAAE,OAAO,CAAC;IAAE,SAAS,EAAE,YAAY,CAAA;CAAE,CAAC,EAAE,GAAG,EAAE,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mEAW7F"}
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* GET /api/live - Multiplexed SSE stream combining status and events.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Uses a shared LivePoller per environment so all SSE connections receive
|
|
7
|
+
* the same data from a single polling loop. See LivePoller for details.
|
|
7
8
|
*/
|
|
8
9
|
import type { Context } from 'hono';
|
|
9
10
|
import type { EnvVariables } from '../lib';
|
|
10
11
|
/**
|
|
11
12
|
* GET /api/live - Multiplexed SSE stream for status and events.
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
14
|
+
* Subscribes to the shared LivePoller for the current environment.
|
|
15
|
+
* The poller broadcasts serialized updates which are forwarded to
|
|
16
|
+
* the client via the SSE tick function.
|
|
15
17
|
*
|
|
16
18
|
* @param c - Hono context with env variables
|
|
17
19
|
* @returns SSE stream
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../../src/server/controllers/live.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../../src/server/controllers/live.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAE1C;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC;IAAE,SAAS,EAAE,YAAY,CAAA;CAAE,CAAC,YAiDjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/server/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AA6C5C;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAMD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/server/lib/sse.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AA6C5C;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAMD;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,aAAa,YA6ExD"}
|
|
@@ -60,5 +60,7 @@ export interface RunSSEOptions {
|
|
|
60
60
|
sendInitial?: boolean;
|
|
61
61
|
/** Return a string to emit, or undefined to skip this tick */
|
|
62
62
|
tick: () => Promise<string | undefined>;
|
|
63
|
+
/** Called when the stream closes for any reason (superseded, client disconnect, error) */
|
|
64
|
+
onClose?: () => void;
|
|
63
65
|
}
|
|
64
66
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/lib/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,MAAM,aAAa,CAAA;AAEpB,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAM/D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,gBAAgB,CAAA;IAC3B,UAAU,EAAE,iBAAiB,CAAA;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,aAAa,CAAA;IACrB,aAAa,EAAE,oBAAoB,CAAA;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,+CAA+C;IAC/C,UAAU,EAAE,UAAU,CAAA;IACtB,oCAAoC;IACpC,MAAM,EAAE,cAAc,CAAA;IACtB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAMD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,WAAW,CAAA;IAChB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAC7B,QAAQ,EAAE,cAAc,CAAA;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,KAAK,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,EAAE,CAAA;CACrB;AAMD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/lib/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,MAAM,aAAa,CAAA;AAEpB,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAM/D;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,gBAAgB,CAAA;IAC3B,UAAU,EAAE,iBAAiB,CAAA;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,EAAE,aAAa,CAAA;IACrB,aAAa,EAAE,oBAAoB,CAAA;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,+CAA+C;IAC/C,UAAU,EAAE,UAAU,CAAA;IACtB,oCAAoC;IACpC,MAAM,EAAE,cAAc,CAAA;IACtB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAMD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,GAAG,EAAE,WAAW,CAAA;IAChB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;IAC7B,QAAQ,EAAE,cAAc,CAAA;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,OAAO,EAAE,KAAK,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,WAAW,EAAE,CAAA;CACrB;AAMD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;IACvC,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/server/services/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAGpD,OAAO,KAAK,EACX,eAAe,EAIf,mBAAmB,EACnB,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../../src/server/services/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAGpD,OAAO,KAAK,EACX,eAAe,EAIf,mBAAmB,EACnB,MAAM,SAAS,CAAA;AAiChB;;;;GAIG;AACH,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,YAAY,MAAM,EAAE,cAAc,EAEjC;IAED;;;;;OAKG;IACG,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC,CAiDzE;IAMD;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;YAkBN,oBAAoB;YAoEpB,0BAA0B;YA8F1B,cAAc;YAgGd,YAAY;YAgCZ,gBAAgB;YA2BhB,gBAAgB;CAmH9B"}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
export { BootstrapService } from './bootstrap';
|
|
7
7
|
export { EnrollmentService } from './enrollment';
|
|
8
8
|
export { EventsService } from './events';
|
|
9
|
-
export {
|
|
9
|
+
export { getOrCreatePoller } from './live';
|
|
10
10
|
export { StatusService } from './status';
|
|
11
11
|
export { StudentSearchService } from './student-search';
|
|
12
|
-
export type { BootstrapResult, BootstrapStats, EnrichedComponentResource, EnrichedEnrollment, GetBootstrapOptions, EnrollmentActionOptions, EnrollmentResult, EnrollStudentOptions, GetNewEventsOptions, GetRecentEventsOptions,
|
|
12
|
+
export type { BootstrapResult, BootstrapStats, EnrichedComponentResource, EnrichedEnrollment, GetBootstrapOptions, EnrollmentActionOptions, EnrollmentResult, EnrollStudentOptions, GetNewEventsOptions, GetRecentEventsOptions, TickState, SearchStudentsOptions, StudentSearchResponse, StudentSearchResult, } from './types';
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/services/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAMvD,YAAY,EAEX,eAAe,EACf,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EAEnB,uBAAuB,EACvB,gBAAgB,EAChB,oBAAoB,EAEpB,mBAAmB,EACnB,sBAAsB,EAEtB,SAAS,EAET,qBAAqB,EACrB,qBAAqB,EACrB,mBAAmB,GACnB,MAAM,SAAS,CAAA"}
|