@bwi-mentorspaces/application-utils-npm 0.0.137 → 0.0.139

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.
Files changed (50) hide show
  1. package/chat/stream/enum/index.d.ts +2 -0
  2. package/chat/stream/enum/index.js +2 -0
  3. package/chat/stream/enum/index.js.map +1 -1
  4. package/chat/stream/models/_/event.data.d.ts +11 -0
  5. package/package.json +3 -1
  6. package/program-engagement/index.d.ts +3 -0
  7. package/program-engagement/index.js +16 -0
  8. package/program-engagement/index.js.map +1 -0
  9. package/program-engagement/model.d.ts +93 -0
  10. package/program-engagement/model.js +6 -0
  11. package/program-engagement/model.js.map +1 -0
  12. package/program-engagement/period.d.ts +50 -0
  13. package/program-engagement/period.js +193 -0
  14. package/program-engagement/period.js.map +1 -0
  15. package/program-engagement/query.d.ts +11 -0
  16. package/program-engagement/query.js +129 -0
  17. package/program-engagement/query.js.map +1 -0
  18. package/meet/index.d.ts +0 -36
  19. package/meet/index.js +0 -74
  20. package/meet/index.js.map +0 -1
  21. package/meet/tests/unit/twilio/twilio.room.test.d.ts +0 -1
  22. package/meet/tests/unit/twilio/twilio.room.test.js +0 -33
  23. package/meet/tests/unit/twilio/twilio.room.test.js.map +0 -1
  24. package/meet/twilio/enum/index.d.ts +0 -6
  25. package/meet/twilio/enum/index.js +0 -11
  26. package/meet/twilio/enum/index.js.map +0 -1
  27. package/meet/twilio/index.d.ts +0 -5
  28. package/meet/twilio/index.js +0 -32
  29. package/meet/twilio/index.js.map +0 -1
  30. package/meet/twilio/models/_/room.data.d.ts +0 -6
  31. package/meet/twilio/models/_/room.data.js +0 -3
  32. package/meet/twilio/models/_/room.data.js.map +0 -1
  33. package/meet/twilio/models/_/twilio.config.d.ts +0 -29
  34. package/meet/twilio/models/_/twilio.config.js +0 -3
  35. package/meet/twilio/models/_/twilio.config.js.map +0 -1
  36. package/meet/twilio/models/index.d.ts +0 -3
  37. package/meet/twilio/models/index.js +0 -3
  38. package/meet/twilio/models/index.js.map +0 -1
  39. package/meet/twilio/twilio.api.d.ts +0 -28
  40. package/meet/twilio/twilio.api.js +0 -46
  41. package/meet/twilio/twilio.api.js.map +0 -1
  42. package/meet/twilio/twilio.auth.d.ts +0 -55
  43. package/meet/twilio/twilio.auth.js +0 -116
  44. package/meet/twilio/twilio.auth.js.map +0 -1
  45. package/meet/twilio/twilio.base.d.ts +0 -41
  46. package/meet/twilio/twilio.base.js +0 -54
  47. package/meet/twilio/twilio.base.js.map +0 -1
  48. package/meet/twilio/twilio.room.d.ts +0 -58
  49. package/meet/twilio/twilio.room.js +0 -173
  50. package/meet/twilio/twilio.room.js.map +0 -1
@@ -44,6 +44,8 @@ export declare enum CustomEventType {
44
44
  MatchCreated = "match_created",
45
45
  AdminMatchCreated = "admin_match_created",
46
46
  AdminMatchRemoved = "admin_match_removed",
47
+ MatchRemovalRequested = "match_removal_requested",
48
+ MatchRemovalDeclined = "match_removal_declined",
47
49
  ProgramMemberRemoved = "program_member_removed",
48
50
  SurveyPrompt = "program_survey_prompt",
49
51
  ProgramAnnouncement = "program_announcement",
@@ -52,6 +52,8 @@ var CustomEventType;
52
52
  CustomEventType["MatchCreated"] = "match_created";
53
53
  CustomEventType["AdminMatchCreated"] = "admin_match_created";
54
54
  CustomEventType["AdminMatchRemoved"] = "admin_match_removed";
55
+ CustomEventType["MatchRemovalRequested"] = "match_removal_requested";
56
+ CustomEventType["MatchRemovalDeclined"] = "match_removal_declined";
55
57
  CustomEventType["ProgramMemberRemoved"] = "program_member_removed";
56
58
  CustomEventType["SurveyPrompt"] = "program_survey_prompt";
57
59
  CustomEventType["ProgramAnnouncement"] = "program_announcement";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../chat/stream/enum/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,IAAY,UAGX;AAHD,WAAY,UAAU;IAClB,+BAAiB,CAAA;IACjB,+BAAiB,CAAA;AACrB,CAAC,EAHW,UAAU,GAAV,kBAAU,KAAV,kBAAU,QAGrB;AAED;;GAEG;AACH,IAAY,YAEX;AAFD,WAAY,YAAY;IACpB,gEAAiB,CAAA;AACrB,CAAC,EAFW,YAAY,GAAZ,oBAAY,KAAZ,oBAAY,QAEvB;AAED;;GAEG;AACH,IAAY,WAGX;AAHD,WAAY,WAAW;IACnB,4BAAa,CAAA;IACb,sCAAuB,CAAA;AAC3B,CAAC,EAHW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAGtB;AAED;;GAEG;AACH,IAAY,QAKX;AALD,WAAY,QAAQ;IAChB,yBAAa,CAAA;IACb,2BAAe,CAAA;IACf,mCAAuB,CAAA;IACvB,2BAAe,CAAA;AACnB,CAAC,EALW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAKnB;AAED;;GAEG;AACH,IAAY,eAkBX;AAlBD,WAAY,eAAe;IACvB,qDAAkC,CAAA;IAClC,qDAAkC,CAAA;IAClC,sEAAmD,CAAA;IACnD,qDAAkC,CAAA;IAClC,qEAAkD,CAAA;IAClD,uEAAoD,CAAA;IACpD,+CAA4B,CAAA;IAC5B,uDAAoC,CAAA;IACpC,uDAAoC,CAAA;IACpC,qDAAkC,CAAA;IAClC,iDAA8B,CAAA;IAC9B,4DAAyC,CAAA;IACzC,4DAAyC,CAAA;IACzC,kEAA+C,CAAA;IAC/C,yDAAsC,CAAA;IACtC,+DAA4C,CAAA;IAC5C,8EAA2D,CAAA;AAC/D,CAAC,EAlBW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAkB1B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../chat/stream/enum/index.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,IAAY,UAGX;AAHD,WAAY,UAAU;IAClB,+BAAiB,CAAA;IACjB,+BAAiB,CAAA;AACrB,CAAC,EAHW,UAAU,GAAV,kBAAU,KAAV,kBAAU,QAGrB;AAED;;GAEG;AACH,IAAY,YAEX;AAFD,WAAY,YAAY;IACpB,gEAAiB,CAAA;AACrB,CAAC,EAFW,YAAY,GAAZ,oBAAY,KAAZ,oBAAY,QAEvB;AAED;;GAEG;AACH,IAAY,WAGX;AAHD,WAAY,WAAW;IACnB,4BAAa,CAAA;IACb,sCAAuB,CAAA;AAC3B,CAAC,EAHW,WAAW,GAAX,mBAAW,KAAX,mBAAW,QAGtB;AAED;;GAEG;AACH,IAAY,QAKX;AALD,WAAY,QAAQ;IAChB,yBAAa,CAAA;IACb,2BAAe,CAAA;IACf,mCAAuB,CAAA;IACvB,2BAAe,CAAA;AACnB,CAAC,EALW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAKnB;AAED;;GAEG;AACH,IAAY,eAoBX;AApBD,WAAY,eAAe;IACvB,qDAAkC,CAAA;IAClC,qDAAkC,CAAA;IAClC,sEAAmD,CAAA;IACnD,qDAAkC,CAAA;IAClC,qEAAkD,CAAA;IAClD,uEAAoD,CAAA;IACpD,+CAA4B,CAAA;IAC5B,uDAAoC,CAAA;IACpC,uDAAoC,CAAA;IACpC,qDAAkC,CAAA;IAClC,iDAA8B,CAAA;IAC9B,4DAAyC,CAAA;IACzC,4DAAyC,CAAA;IACzC,oEAAiD,CAAA;IACjD,kEAA+C,CAAA;IAC/C,kEAA+C,CAAA;IAC/C,yDAAsC,CAAA;IACtC,+DAA4C,CAAA;IAC5C,8EAA2D,CAAA;AAC/D,CAAC,EApBW,eAAe,GAAf,uBAAe,KAAf,uBAAe,QAoB1B"}
@@ -205,6 +205,17 @@ export interface IEventTypesData {
205
205
  mentorRef: string;
206
206
  menteeRef: string;
207
207
  };
208
+ [CustomEventType.MatchRemovalRequested]: {
209
+ programRef: string;
210
+ programName: string;
211
+ matchRef: string;
212
+ memberName: string;
213
+ };
214
+ [CustomEventType.MatchRemovalDeclined]: {
215
+ programRef: string;
216
+ programName: string;
217
+ declineExplanation: string;
218
+ };
208
219
  [CustomEventType.ProgramMemberRemoved]: {
209
220
  programRef: string;
210
221
  programName: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bwi-mentorspaces/application-utils-npm",
3
- "version": "0.0.137",
3
+ "version": "0.0.139",
4
4
  "description": "Mentor Spaces npm packages for reusable models, utils, and abstracted, commonly used processes for DB Repo, Logging, etc.",
5
5
  "author": {
6
6
  "name": "Mentor Spaces",
@@ -21,6 +21,7 @@
21
21
  "@types/crypto-js": "^4.1.0",
22
22
  "@types/express": "^4.17.13",
23
23
  "@types/jest": "^27.4.0",
24
+ "@types/luxon": "^3.7.6",
24
25
  "@types/node": "^17.0.8",
25
26
  "@types/supertest": "^2.0.11",
26
27
  "@types/uuid": "^8.3.4",
@@ -45,6 +46,7 @@
45
46
  "crypto-js": "^4.1.1",
46
47
  "express": "^4.17.2",
47
48
  "guid-typescript": "^1.0.9",
49
+ "luxon": "^3.7.2",
48
50
  "reflect-metadata": "^0.1.13",
49
51
  "serverless-http": "^2.7.0",
50
52
  "stream-chat": "^6.6.0",
@@ -0,0 +1,3 @@
1
+ export * from "./model";
2
+ export * from "./period";
3
+ export * from "./query";
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./model"), exports);
14
+ __exportStar(require("./period"), exports);
15
+ __exportStar(require("./query"), exports);
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../program-engagement/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,0CAAwB;AACxB,2CAAyB;AACzB,0CAAwB"}
@@ -0,0 +1,93 @@
1
+ /** Program fields needed for engagement, reusable by callers with an existing program. */
2
+ export interface IEngagementProgram {
3
+ Ref: string;
4
+ StartDate: Date | null;
5
+ EndDate: Date | null;
6
+ TimeZone: string | null;
7
+ EngagementType: "MEETINGS" | "MINUTES" | null;
8
+ EngagementAmount: number | null;
9
+ EngagementInterval: number | null;
10
+ EngagementPeriodUnit: "DAY" | "WEEK" | "MONTH" | null;
11
+ }
12
+ /** Validated, complete program engagement configuration. */
13
+ export interface IEngagementConfiguration {
14
+ timeZone: string;
15
+ type: NonNullable<IEngagementProgram["EngagementType"]>;
16
+ amount: number;
17
+ interval: number;
18
+ periodUnit: NonNullable<IEngagementProgram["EngagementPeriodUnit"]>;
19
+ }
20
+ /** Calendar boundaries and program-clipped boundaries; end timestamps are exclusive. */
21
+ export interface IEngagementPeriod {
22
+ calendarStart: Date;
23
+ calendarEndExclusive: Date;
24
+ start: Date;
25
+ endExclusive: Date;
26
+ fullIntervalDays: number;
27
+ }
28
+ /** Program-level periods resolved using one request timestamp. */
29
+ export interface IEngagementPeriods {
30
+ previous: IEngagementPeriod | null;
31
+ current: IEngagementPeriod | null;
32
+ }
33
+ /** Eligible match and participant identity. */
34
+ export interface IEngagementMatch {
35
+ id: number;
36
+ ref: string;
37
+ publishedOn: Date;
38
+ mentorRef: string;
39
+ menteeRef: string;
40
+ }
41
+ /** Invitation classification for incomplete sessions. */
42
+ export declare type EngagementInvitationState = "accepted" | "pending" | "declined" | "unclassified";
43
+ /** Incomplete sessions grouped by their position relative to the evaluation timestamp. */
44
+ export declare type EngagementSessionTiming = "future" | "underway" | "endedIncomplete";
45
+ /** Count and scheduled duration, without counting these sessions as completed engagement. */
46
+ export interface IEngagementSessionMeasure {
47
+ meetings: number;
48
+ scheduledMinutes: number;
49
+ }
50
+ /** Activity facts for one eligible match-period intersection. */
51
+ export interface IEngagementSessionFacts {
52
+ completedMeetings: number;
53
+ completedOfflineMeetings: number;
54
+ completedMinutes: number;
55
+ incomplete: Record<EngagementSessionTiming, Record<EngagementInvitationState, IEngagementSessionMeasure>>;
56
+ }
57
+ /** Calculated engagement for one match in one period. */
58
+ export interface IMatchPeriodEngagement {
59
+ classification: EngagementClassification | null;
60
+ status: "MET" | "SHORTFALL" | "IN_PROGRESS" | "NOT_APPLICABLE";
61
+ start: Date | null;
62
+ endExclusive: Date | null;
63
+ eligibleDays: number;
64
+ elapsedEligibleDays: number;
65
+ remainingEligibleDays: number;
66
+ requirement: number | null;
67
+ creditedAmount: number;
68
+ remainingAmount: number | null;
69
+ requirementSatisfied: boolean | null;
70
+ sessions: IEngagementSessionFacts;
71
+ }
72
+ /** Independent engagement results for one match. */
73
+ export interface IMatchEngagement extends IEngagementMatch {
74
+ previous: IMatchPeriodEngagement | null;
75
+ current: IMatchPeriodEngagement | null;
76
+ }
77
+ /** Approved session progress states, evaluated independently from member activity. */
78
+ export declare const ENGAGEMENT_CLASSIFICATIONS: readonly ["GREEN", "YELLOW", "RED", "NO_ACTIVITY"];
79
+ /** One supported session progress classification. */
80
+ export declare type EngagementClassification = typeof ENGAGEMENT_CLASSIFICATIONS[number];
81
+ /** One program-local calendar day, serialized as UTC for parameterized SQL. */
82
+ export interface IEngagementCalendarDay {
83
+ period: "previous" | "current";
84
+ periodStart: string;
85
+ endTime: string;
86
+ dayStart: string;
87
+ dayEnd: string;
88
+ offlineStart: string;
89
+ offlineEnd: string;
90
+ eligibleDays: number;
91
+ elapsedDays: number;
92
+ fullIntervalDays: number;
93
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENGAGEMENT_CLASSIFICATIONS = void 0;
4
+ /** Approved session progress states, evaluated independently from member activity. */
5
+ exports.ENGAGEMENT_CLASSIFICATIONS = ["GREEN", "YELLOW", "RED", "NO_ACTIVITY"];
6
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../program-engagement/model.ts"],"names":[],"mappings":";;;AAuFA,sFAAsF;AACzE,QAAA,0BAA0B,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa,CAAU,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { IEngagementProgram, IEngagementConfiguration, IEngagementPeriods, IEngagementCalendarDay } from "./model";
2
+ /**
3
+ * Determine whether a value is a valid IANA timezone identifier.
4
+ *
5
+ * @param {unknown} value Potential timezone identifier
6
+ * @returns {value is string} Whether the value is a valid timezone identifier
7
+ */
8
+ export declare function isValidTimeZone(value: unknown): value is string;
9
+ /**
10
+ * Determine whether the program's exclusive end boundary has been reached.
11
+ *
12
+ * @param {Pick<IEngagementProgram, "EndDate">} program Loaded program dates
13
+ * @param {Date} evaluatedAt Shared request timestamp
14
+ * @returns {boolean} Whether the program has ended
15
+ */
16
+ export declare function hasProgramEnded(program: Pick<IEngagementProgram, "EndDate">, evaluatedAt: Date): boolean;
17
+ /**
18
+ * Validate populated settings and distinguish programs that are not configured yet.
19
+ *
20
+ * @param {IEngagementProgram} program Already-loaded program
21
+ * @returns {IEngagementConfiguration | null} Complete configuration or null
22
+ */
23
+ export declare function resolveEngagementConfiguration(program: IEngagementProgram): IEngagementConfiguration | null;
24
+ /**
25
+ * Count calendar days touched by a nonempty half-open timestamp interval.
26
+ *
27
+ * @param {Date} start Inclusive lower bound
28
+ * @param {Date} endExclusive Exclusive upper bound
29
+ * @param {string} timeZone Program timezone
30
+ * @returns {number} Whole calendar days; a partial day counts as one
31
+ */
32
+ export declare function engagementCalendarDays(start: Date, endExclusive: Date, timeZone: string): number;
33
+ /**
34
+ * Parameterize calendar-day boundaries once per program, including daylight-saving changes.
35
+ *
36
+ * @param {IEngagementPeriods} periods Program-clipped periods
37
+ * @param {string} timeZone Program timezone
38
+ * @param {Date} evaluatedAt Shared evaluation instant
39
+ * @returns {IEngagementCalendarDay[]} UTC boundaries and whole-day counts
40
+ */
41
+ export declare function engagementCalendarRows(periods: IEngagementPeriods, timeZone: string, evaluatedAt: Date): IEngagementCalendarDay[];
42
+ /**
43
+ * Resolve only the current and immediately preceding program periods.
44
+ *
45
+ * @param {IEngagementProgram} program Already-loaded program with a start date
46
+ * @param {IEngagementConfiguration} configuration Validated settings
47
+ * @param {Date} evaluatedAt Single request timestamp
48
+ * @returns {IEngagementPeriods} Calendar periods clipped to program dates
49
+ */
50
+ export declare function resolveEngagementPeriods(program: IEngagementProgram, configuration: IEngagementConfiguration, evaluatedAt: Date): IEngagementPeriods;
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveEngagementPeriods = exports.engagementCalendarRows = exports.engagementCalendarDays = exports.resolveEngagementConfiguration = exports.hasProgramEnded = exports.isValidTimeZone = void 0;
4
+ const luxon_1 = require("luxon");
5
+ const core_1 = require("../core");
6
+ /**
7
+ * Determine whether a value is a valid IANA timezone identifier.
8
+ *
9
+ * @param {unknown} value Potential timezone identifier
10
+ * @returns {value is string} Whether the value is a valid timezone identifier
11
+ */
12
+ function isValidTimeZone(value) {
13
+ if (typeof value !== "string" || !value.trim()) {
14
+ return false;
15
+ }
16
+ try {
17
+ new Intl.DateTimeFormat("en-US", { timeZone: value.trim() }).format();
18
+ return true;
19
+ }
20
+ catch (_a) {
21
+ return false;
22
+ }
23
+ }
24
+ exports.isValidTimeZone = isValidTimeZone;
25
+ /**
26
+ * Determine whether the program's exclusive end boundary has been reached.
27
+ *
28
+ * @param {Pick<IEngagementProgram, "EndDate">} program Loaded program dates
29
+ * @param {Date} evaluatedAt Shared request timestamp
30
+ * @returns {boolean} Whether the program has ended
31
+ */
32
+ function hasProgramEnded(program, evaluatedAt) {
33
+ return program.EndDate != null && evaluatedAt >= program.EndDate;
34
+ }
35
+ exports.hasProgramEnded = hasProgramEnded;
36
+ /**
37
+ * Validate populated settings and distinguish programs that are not configured yet.
38
+ *
39
+ * @param {IEngagementProgram} program Already-loaded program
40
+ * @returns {IEngagementConfiguration | null} Complete configuration or null
41
+ */
42
+ function resolveEngagementConfiguration(program) {
43
+ const validInteger = (value) => value == null || (Number.isInteger(value) && value >= 1 && value <= 65535);
44
+ if ((program.TimeZone != null && !isValidTimeZone(program.TimeZone)) ||
45
+ (program.EngagementType != null && !["MEETINGS", "MINUTES"].includes(program.EngagementType)) ||
46
+ (program.EngagementPeriodUnit != null && !["DAY", "WEEK", "MONTH"].includes(program.EngagementPeriodUnit)) ||
47
+ !validInteger(program.EngagementAmount) ||
48
+ !validInteger(program.EngagementInterval) ||
49
+ (program.StartDate != null && !Number.isFinite(program.StartDate.getTime())) ||
50
+ (program.EndDate != null && !Number.isFinite(program.EndDate.getTime())) ||
51
+ (program.StartDate != null && program.EndDate != null && program.EndDate <= program.StartDate)) {
52
+ throw new core_1.ApiError(500 /* InternalServerError */, "Invalid program engagement configuration or program dates.");
53
+ }
54
+ if (program.TimeZone == null ||
55
+ program.EngagementType == null ||
56
+ program.EngagementAmount == null ||
57
+ program.EngagementInterval == null ||
58
+ program.EngagementPeriodUnit == null)
59
+ return null;
60
+ return {
61
+ timeZone: program.TimeZone.trim(),
62
+ type: program.EngagementType,
63
+ amount: program.EngagementAmount,
64
+ interval: program.EngagementInterval,
65
+ periodUnit: program.EngagementPeriodUnit
66
+ };
67
+ }
68
+ exports.resolveEngagementConfiguration = resolveEngagementConfiguration;
69
+ /**
70
+ * Count calendar days touched by a nonempty half-open timestamp interval.
71
+ *
72
+ * @param {Date} start Inclusive lower bound
73
+ * @param {Date} endExclusive Exclusive upper bound
74
+ * @param {string} timeZone Program timezone
75
+ * @returns {number} Whole calendar days; a partial day counts as one
76
+ */
77
+ function engagementCalendarDays(start, endExclusive, timeZone) {
78
+ if (endExclusive <= start)
79
+ return 0;
80
+ const firstDay = luxon_1.DateTime.fromJSDate(start, { zone: timeZone }).startOf("day");
81
+ const lastDay = luxon_1.DateTime.fromJSDate(endExclusive, { zone: timeZone }).minus({ milliseconds: 1 }).startOf("day");
82
+ return Math.round(lastDay.diff(firstDay, "days").days) + 1;
83
+ }
84
+ exports.engagementCalendarDays = engagementCalendarDays;
85
+ /**
86
+ * Parameterize calendar-day boundaries once per program, including daylight-saving changes.
87
+ *
88
+ * @param {IEngagementPeriods} periods Program-clipped periods
89
+ * @param {string} timeZone Program timezone
90
+ * @param {Date} evaluatedAt Shared evaluation instant
91
+ * @returns {IEngagementCalendarDay[]} UTC boundaries and whole-day counts
92
+ */
93
+ function engagementCalendarRows(periods, timeZone, evaluatedAt) {
94
+ const rows = [];
95
+ const today = luxon_1.DateTime.fromJSDate(evaluatedAt, { zone: timeZone }).startOf("day").toJSDate();
96
+ const sqlDate = (value) => value.toISOString().slice(0, 19).replace("T", " ");
97
+ for (const key of ["previous", "current"]) {
98
+ const period = periods[key];
99
+ if (!period)
100
+ continue;
101
+ let day = luxon_1.DateTime.fromJSDate(period.start, { zone: timeZone }).startOf("day");
102
+ while (day.toMillis() < period.endExclusive.getTime()) {
103
+ const next = day.plus({ days: 1 });
104
+ const start = new Date(Math.max(day.toMillis(), period.start.getTime()));
105
+ const eligibleDays = engagementCalendarDays(start, period.endExclusive, timeZone);
106
+ rows.push({
107
+ period: key,
108
+ periodStart: sqlDate(period.start),
109
+ endTime: sqlDate(period.endExclusive),
110
+ dayStart: sqlDate(start),
111
+ dayEnd: sqlDate(new Date(Math.min(next.toMillis(), period.endExclusive.getTime()))),
112
+ offlineStart: day.toFormat("yyyy-MM-dd HH:mm:ss"),
113
+ offlineEnd: luxon_1.DateTime.fromJSDate(period.endExclusive, { zone: timeZone })
114
+ .minus({ milliseconds: 1 })
115
+ .startOf("day")
116
+ .plus({ days: 1 })
117
+ .toFormat("yyyy-MM-dd HH:mm:ss"),
118
+ eligibleDays,
119
+ elapsedDays: key === "previous"
120
+ ? eligibleDays
121
+ : engagementCalendarDays(start, new Date(Math.min(today.getTime(), period.endExclusive.getTime())), timeZone),
122
+ fullIntervalDays: period.fullIntervalDays
123
+ });
124
+ day = next;
125
+ }
126
+ }
127
+ return rows;
128
+ }
129
+ exports.engagementCalendarRows = engagementCalendarRows;
130
+ /**
131
+ * Resolve only the current and immediately preceding program periods.
132
+ *
133
+ * @param {IEngagementProgram} program Already-loaded program with a start date
134
+ * @param {IEngagementConfiguration} configuration Validated settings
135
+ * @param {Date} evaluatedAt Single request timestamp
136
+ * @returns {IEngagementPeriods} Calendar periods clipped to program dates
137
+ */
138
+ function resolveEngagementPeriods(program, configuration, evaluatedAt) {
139
+ if (!program.StartDate || evaluatedAt < program.StartDate)
140
+ return { previous: null, current: null };
141
+ const start = luxon_1.DateTime.fromJSDate(program.StartDate, { zone: configuration.timeZone });
142
+ const startDay = start.startOf("day");
143
+ const unit = configuration.periodUnit;
144
+ const interval = configuration.interval;
145
+ const advance = (date, count) => unit === "MONTH"
146
+ ? date.plus({ months: count * interval })
147
+ : date.plus({ days: count * interval * (unit === "WEEK" ? 7 : 1) });
148
+ const containingStart = unit === "MONTH"
149
+ ? startDay.startOf("month")
150
+ : unit === "WEEK"
151
+ ? startDay.minus({ days: startDay.weekday % 7 })
152
+ : startDay;
153
+ const hasStub = containingStart.toMillis() !== startDay.toMillis();
154
+ const firstFullStart = !hasStub
155
+ ? containingStart
156
+ : unit === "MONTH"
157
+ ? containingStart.plus({ months: 1 })
158
+ : containingStart.plus({ weeks: 1 });
159
+ const ended = hasProgramEnded(program, evaluatedAt);
160
+ // The instant before an exclusive end belongs to the final ended period.
161
+ const reference = luxon_1.DateTime.fromJSDate(ended ? program.EndDate : evaluatedAt, { zone: configuration.timeZone });
162
+ const anchor = ended ? reference.minus({ milliseconds: 1 }) : reference;
163
+ const months = (anchor.year - firstFullStart.year) * 12 + anchor.month - firstFullStart.month;
164
+ const index = anchor < firstFullStart
165
+ ? -1
166
+ : unit === "MONTH"
167
+ ? Math.floor(months / interval)
168
+ : Math.floor(anchor.startOf("day").diff(firstFullStart, "days").days / (interval * (unit === "WEEK" ? 7 : 1)));
169
+ const periodAt = (periodIndex) => {
170
+ var _a, _b;
171
+ if (periodIndex < -1 || (periodIndex === -1 && !hasStub))
172
+ return null;
173
+ const nominalStart = periodIndex === -1 ? containingStart : advance(firstFullStart, periodIndex);
174
+ const nominalEnd = advance(nominalStart, 1);
175
+ const calendarEnd = periodIndex === -1 ? firstFullStart : nominalEnd;
176
+ const lower = Math.max(program.StartDate.getTime(), nominalStart.toMillis());
177
+ const upper = Math.min((_b = (_a = program.EndDate) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : Infinity, calendarEnd.toMillis());
178
+ if (upper <= lower)
179
+ return null;
180
+ return {
181
+ calendarStart: nominalStart.toJSDate(),
182
+ calendarEndExclusive: calendarEnd.toJSDate(),
183
+ start: new Date(lower),
184
+ endExclusive: new Date(upper),
185
+ fullIntervalDays: Math.round(nominalEnd.diff(nominalStart, "days").days)
186
+ };
187
+ };
188
+ return ended
189
+ ? { previous: periodAt(index), current: null }
190
+ : { previous: periodAt(index - 1), current: periodAt(index) };
191
+ }
192
+ exports.resolveEngagementPeriods = resolveEngagementPeriods;
193
+ //# sourceMappingURL=period.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"period.js","sourceRoot":"","sources":["../../program-engagement/period.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { IEngagementConfiguration, IEngagementPeriods } from "./model";
2
+ /** Parameterized SQL supplied by the consumer's Prisma client. */
3
+ export interface IEngagementSqlAdapter<TSql> {
4
+ sql: (strings: TemplateStringsArray, ...values: unknown[]) => TSql;
5
+ empty: TSql;
6
+ }
7
+ /** Bind the shared progress queries to a consumer's SQL implementation. */
8
+ export declare function createEngagementQueries<TSql>(adapter: IEngagementSqlAdapter<TSql>): {
9
+ engagementSetSql: (programRef: string, programStartDate: Date, matches: TSql, periods: IEngagementPeriods, configuration: IEngagementConfiguration, evaluatedAt: Date, includeFacts?: boolean) => TSql;
10
+ matchSessionAcceptedSql: (menteeStatus: TSql, mentorStatus: TSql) => TSql;
11
+ };
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEngagementQueries = void 0;
4
+ const luxon_1 = require("luxon");
5
+ const period_1 = require("./period");
6
+ /** Bind the shared progress queries to a consumer's SQL implementation. */
7
+ function createEngagementQueries(adapter) {
8
+ /** Require accepted participation from both members. */
9
+ function matchSessionAcceptedSql(menteeStatus, mentorStatus) {
10
+ return adapter.sql `(${menteeStatus} IN (${"attending"}, ${"attended"})
11
+ AND ${mentorStatus} IN (${"attending"}, ${"attended"}))`;
12
+ }
13
+ const SESSION_PROGRESS_STARTUP_GRACE_DAYS = 7;
14
+ /**
15
+ * Build reusable CTEs for period facts and classification before filtering or pagination.
16
+ * The input relation supplies id, publishedOn, mentorRef and menteeRef for eligible matches.
17
+ *
18
+ * @param {string} programRef Owning program
19
+ * @param {Date} programStartDate Official program start, independent of reporting periods
20
+ * @param {TSql} matches Eligible match relation, optionally restricted to a page
21
+ * @param {IEngagementPeriods} periods Program-clipped periods
22
+ * @param {IEngagementConfiguration} configuration Validated requirement
23
+ * @param {Date} evaluatedAt Shared request timestamp
24
+ * @param {boolean} includeFacts Include grouped supporting facts for the engagement API
25
+ * @returns {TSql} CTE definitions without a leading WITH
26
+ */
27
+ function engagementSetSql(programRef, programStartDate, matches, periods, configuration, evaluatedAt, includeFacts = false) {
28
+ const calendar = JSON.stringify((0, period_1.engagementCalendarRows)(periods, configuration.timeZone, evaluatedAt));
29
+ // Relationship starts on or after this boundary are still within their first seven calendar days.
30
+ const startupGraceCutoff = luxon_1.DateTime.fromJSDate(evaluatedAt, { zone: configuration.timeZone })
31
+ .startOf("day")
32
+ .minus({ days: SESSION_PROGRESS_STARTUP_GRACE_DAYS - 1 })
33
+ .toJSDate();
34
+ const duration = configuration.type === "MINUTES";
35
+ const unit = duration ? 60 : 1;
36
+ const measure = duration ? adapter.sql `seconds` : adapter.sql `meetings`;
37
+ return adapter.sql `
38
+ engagement_matches AS (${matches}),
39
+ engagement_days AS (
40
+ SELECT d.* FROM JSON_TABLE(${calendar}, '$[*]' COLUMNS (
41
+ period VARCHAR(8) PATH '$.period',
42
+ periodStart DATETIME PATH '$.periodStart', endTime DATETIME PATH '$.endTime',
43
+ dayStart DATETIME PATH '$.dayStart', dayEnd DATETIME PATH '$.dayEnd',
44
+ offlineStart DATETIME PATH '$.offlineStart', offlineEnd DATETIME PATH '$.offlineEnd',
45
+ eligibleDays INT PATH '$.eligibleDays', elapsedDays INT PATH '$.elapsedDays',
46
+ fullIntervalDays INT PATH '$.fullIntervalDays'
47
+ )) d
48
+ ),
49
+ engagement_windows AS (
50
+ SELECT m.id AS matchId, m.mentorRef, m.menteeRef, d.period,
51
+ GREATEST(m.publishedOn, d.periodStart) AS startTime, d.endTime,
52
+ GREATEST(m.publishedOn, ${programStartDate}) >= ${startupGraceCutoff} AS inStartupGrace,
53
+ d.eligibleDays, d.elapsedDays, d.offlineStart, d.offlineEnd,
54
+ ROUND(CAST(${configuration.amount} AS DECIMAL(30, 12)) * d.eligibleDays / d.fullIntervalDays) AS requirement
55
+ FROM engagement_matches m JOIN engagement_days d
56
+ ON GREATEST(m.publishedOn, d.periodStart) >= d.dayStart
57
+ AND GREATEST(m.publishedOn, d.periodStart) < d.dayEnd
58
+ ),
59
+ engagement_session_facts AS (
60
+ SELECT /*+ NO_MERGE(w) */ w.matchId, w.period,
61
+ CASE WHEN s.IsCompleted = 1 THEN 'completed'
62
+ WHEN s.StartTime > ${evaluatedAt} THEN 'future'
63
+ WHEN s.EndTime > ${evaluatedAt} THEN 'underway'
64
+ ELSE 'endedIncomplete' END AS timing,
65
+ CASE WHEN s.IsCompleted = 1 THEN 'unclassified'
66
+ WHEN ma.CurrentAttendanceStatus = ${"declined"}
67
+ OR ea.CurrentAttendanceStatus = ${"declined"} THEN 'declined'
68
+ WHEN ma.CurrentAttendanceStatus IN (${"invited"}, ${"requested"})
69
+ OR ea.CurrentAttendanceStatus IN (${"invited"}, ${"requested"}) THEN 'pending'
70
+ WHEN ${matchSessionAcceptedSql(adapter.sql `ea.CurrentAttendanceStatus`, adapter.sql `ma.CurrentAttendanceStatus`)} THEN 'accepted'
71
+ ELSE 'unclassified' END AS invitation,
72
+ CASE WHEN s.IsCompleted = 1 THEN COALESCE(s.DurationSeconds, TIMESTAMPDIFF(SECOND, s.StartTime, s.EndTime))
73
+ ELSE TIMESTAMPDIFF(SECOND, s.StartTime, s.EndTime) END AS seconds, 0 AS offline
74
+ FROM engagement_windows w
75
+ JOIN session s ON s.MatchId = w.matchId AND s.ProgramRef = ${programRef}
76
+ AND s.IsOffline = FALSE AND s.Type = ${"ONEONONE"} AND s.StartTime >= w.startTime AND s.StartTime < w.endTime
77
+ LEFT JOIN sessionattendance ma ON ma.SessionRef = s.Ref AND ma.UserRef = w.mentorRef
78
+ LEFT JOIN sessionattendance ea ON ea.SessionRef = s.Ref AND ea.UserRef = w.menteeRef
79
+ UNION ALL
80
+ SELECT /*+ NO_MERGE(w) */ w.matchId, w.period, 'completed', 'unclassified', 0, 1
81
+ FROM engagement_windows w
82
+ JOIN session s ON s.MatchId = w.matchId AND s.ProgramRef = ${programRef}
83
+ AND s.IsOffline = TRUE AND s.IsCompleted = 1 AND s.Type = ${"ONEONONE"}
84
+ AND s.StartTime >= w.offlineStart AND s.StartTime < w.offlineEnd
85
+ ),
86
+ engagement_aggregates AS (
87
+ SELECT matchId, period, timing, invitation, COUNT(*) AS meetings, SUM(seconds) AS seconds, SUM(offline) AS offlineMeetings,
88
+ SUM(CASE WHEN seconds < 0 THEN 1 ELSE 0 END) AS invalidDurations
89
+ FROM engagement_session_facts GROUP BY matchId, period, timing, invitation
90
+ ),
91
+ engagement_totals AS (
92
+ SELECT matchId, period,
93
+ SUM(CASE WHEN timing = 'completed' THEN ${measure} ELSE 0 END) AS completedMeasure,
94
+ SUM(CASE WHEN timing = 'future' AND invitation = 'accepted' THEN ${measure} ELSE 0 END) AS futureMeasure,
95
+ SUM(CASE WHEN timing = 'completed' OR (timing = 'future' AND invitation = 'accepted')
96
+ THEN meetings ELSE 0 END) AS positiveSessions,
97
+ SUM(invalidDurations) AS invalidDurations, SUM(offlineMeetings) AS completedOfflineMeetings
98
+ ${includeFacts
99
+ ? adapter.sql `, JSON_ARRAYAGG(JSON_OBJECT('timing', timing, 'invitation', invitation,
100
+ 'meetings', meetings, 'seconds', seconds)) AS aggregates`
101
+ : adapter.empty}
102
+ FROM engagement_aggregates GROUP BY matchId, period
103
+ ),
104
+ engagement_values AS (
105
+ SELECT /*+ NO_MERGE(w) */ w.*, COALESCE(t.completedMeasure, 0) AS completedMeasure,
106
+ COALESCE(t.positiveSessions, 0) AS positiveSessions,
107
+ COALESCE(t.completedOfflineMeetings, 0) AS completedOfflineMeetings,
108
+ COALESCE(t.futureMeasure, 0) AS futureMeasure, COALESCE(t.invalidDurations, 0) AS invalidDurations
109
+ ${includeFacts ? adapter.sql `, COALESCE(t.aggregates, JSON_ARRAY()) AS aggregates` : adapter.empty}
110
+ FROM engagement_windows w LEFT JOIN engagement_totals t ON t.matchId = w.matchId AND t.period = w.period
111
+ ),
112
+ engagement_classified AS (
113
+ SELECT v.*, CASE
114
+ WHEN period = 'current' AND inStartupGrace AND positiveSessions = 0 THEN 'NO_ACTIVITY'
115
+ WHEN completedMeasure >= requirement * ${unit} THEN 'GREEN'
116
+ WHEN period = 'previous' THEN CASE WHEN completedMeasure > 0 THEN 'YELLOW' ELSE 'RED' END
117
+ WHEN completedMeasure + futureMeasure >= requirement * ${unit} THEN 'GREEN'
118
+ WHEN inStartupGrace THEN 'YELLOW'
119
+ WHEN elapsedDays * 2 <= eligibleDays THEN 'YELLOW'
120
+ WHEN completedMeasure = 0 THEN 'RED'
121
+ WHEN completedMeasure >= ROUND(CAST(requirement AS DECIMAL(30, 12)) * elapsedDays / eligibleDays) * ${unit} THEN 'GREEN'
122
+ ELSE 'YELLOW' END AS classification
123
+ FROM engagement_values v
124
+ )`;
125
+ }
126
+ return { engagementSetSql, matchSessionAcceptedSql };
127
+ }
128
+ exports.createEngagementQueries = createEngagementQueries;
129
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../program-engagement/query.ts"],"names":[],"mappings":";;;AAAA,iCAAiC;AAEjC,qCAAkD;AAQlD,2EAA2E;AAC3E,SAAgB,uBAAuB,CAAO,OAAoC;IAC9E,wDAAwD;IACxD,SAAS,uBAAuB,CAAC,YAAkB,EAAE,YAAkB;QACnE,OAAO,OAAO,CAAC,GAAG,CAAA,IAAI,YAAY,QAAQ,WAAW,KAAK,UAAU;kBAC1D,YAAY,QAAQ,WAAW,KAAK,UAAU,IAAI,CAAC;IACjE,CAAC;IACD,MAAM,mCAAmC,GAAG,CAAC,CAAC;IAE9C;;;;;;;;;;;;OAYG;IACH,SAAS,gBAAgB,CACrB,UAAkB,EAClB,gBAAsB,EACtB,OAAa,EACb,OAA2B,EAC3B,aAAuC,EACvC,WAAiB,EACjB,eAAwB,KAAK;QAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAA,+BAAsB,EAAC,OAAO,EAAE,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC;QACtG,kGAAkG;QAClG,MAAM,kBAAkB,GAAG,gBAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,aAAa,CAAC,QAAQ,EAAE,CAAC;aACxF,OAAO,CAAC,KAAK,CAAC;aACd,KAAK,CAAC,EAAE,IAAI,EAAE,mCAAmC,GAAG,CAAC,EAAE,CAAC;aACxD,QAAQ,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,KAAK,SAAS,CAAC;QAClD,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,UAAU,CAAC;QACxE,OAAO,OAAO,CAAC,GAAG,CAAA;iCACO,OAAO;;yCAEC,QAAQ;;;;;;;;;;;;0CAYP,gBAAgB,QAAQ,kBAAkB;;6BAGhE,aAAa,CAAC,MAClB;;;;;;;;yCAQyB,WAAW;uCACb,WAAW;;;wDAGM,UAAU;0DACR,UAAU;0DACV,SAAS,KAAK,WAAW;4DACvB,SAAS,KAAK,WAAW;2BAC1D,uBAAuB,CAC1B,OAAO,CAAC,GAAG,CAAA,4BAA4B,EACvC,OAAO,CAAC,GAAG,CAAA,4BAA4B,CAC1C;;;;;yEAKoD,UAAU;uDAC5B,UAAU;;;;;;yEAMQ,UAAU;4EACP,UAAU;;;;;;;;;;0DAU5B,OAAO;mFACkB,OAAO;;;;kBAKtE,YAAY;YACR,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA;6EACwC;YACrD,CAAC,CAAC,OAAO,CAAC,KAClB;;;;;;;;kBAQE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,sDAAsD,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK;;;;;;yDAMzD,IAAI;;yEAEY,IAAI;;;;sHAIyC,IAAI;;;UAGhH,CAAC;IACP,CAAC;IAED,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,CAAC;AACzD,CAAC;AAzID,0DAyIC"}
package/meet/index.d.ts DELETED
@@ -1,36 +0,0 @@
1
- import { ClientType } from "./twilio/enum";
2
- import { IMeetConfig } from "./twilio/models/_/twilio.config";
3
- /**
4
- * Meet declarations
5
- */
6
- interface IMeet<T> {
7
- client: T;
8
- }
9
- /**
10
- * Meet implementation
11
- */
12
- export declare class Meet<T> implements IMeet<T> {
13
- private _client;
14
- private _config;
15
- private _clientType;
16
- /**
17
- * Meet constructor
18
- *
19
- * @param {ClientType} clientType client type
20
- * @param {IMeetConfig} config meet configuration
21
- */
22
- constructor(clientType: ClientType, config: IMeetConfig);
23
- /**
24
- * getter for client
25
- *
26
- * @returns {T} client of type T
27
- */
28
- get client(): T;
29
- /**
30
- * initialize method
31
- *
32
- * @returns {void} promise of type void
33
- */
34
- private init;
35
- }
36
- export {};