@talkpilot/core-db 1.3.3 → 1.3.5

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 (150) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/DEVELOPMENT.md +28 -0
  3. package/README.md +51 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/municipal/index.d.ts +1 -0
  9. package/dist/municipal/index.d.ts.map +1 -1
  10. package/dist/municipal/index.js +1 -0
  11. package/dist/municipal/index.js.map +1 -1
  12. package/dist/municipal/muniIssues/index.d.ts +7 -0
  13. package/dist/municipal/muniIssues/index.d.ts.map +1 -0
  14. package/dist/municipal/muniIssues/index.js +29 -0
  15. package/dist/municipal/muniIssues/index.js.map +1 -0
  16. package/dist/municipal/muniIssues/muniIssues.constants.d.ts +14 -0
  17. package/dist/municipal/muniIssues/muniIssues.constants.d.ts.map +1 -0
  18. package/dist/municipal/muniIssues/muniIssues.constants.js +17 -0
  19. package/dist/municipal/muniIssues/muniIssues.constants.js.map +1 -0
  20. package/dist/municipal/muniIssues/muniIssues.getters.d.ts +7 -0
  21. package/dist/municipal/muniIssues/muniIssues.getters.d.ts.map +1 -0
  22. package/dist/municipal/muniIssues/muniIssues.getters.js +24 -0
  23. package/dist/municipal/muniIssues/muniIssues.getters.js.map +1 -0
  24. package/dist/municipal/muniIssues/muniIssues.schema.d.ts +4 -0
  25. package/dist/municipal/muniIssues/muniIssues.schema.d.ts.map +1 -0
  26. package/dist/municipal/muniIssues/muniIssues.schema.js +63 -0
  27. package/dist/municipal/muniIssues/muniIssues.schema.js.map +1 -0
  28. package/dist/municipal/muniIssues/muniIssues.setters.d.ts +4 -0
  29. package/dist/municipal/muniIssues/muniIssues.setters.d.ts.map +1 -0
  30. package/dist/municipal/muniIssues/muniIssues.setters.js +22 -0
  31. package/dist/municipal/muniIssues/muniIssues.setters.js.map +1 -0
  32. package/dist/municipal/muniIssues/muniIssues.setup.d.ts +3 -0
  33. package/dist/municipal/muniIssues/muniIssues.setup.d.ts.map +1 -0
  34. package/dist/municipal/muniIssues/muniIssues.setup.js +61 -0
  35. package/dist/municipal/muniIssues/muniIssues.setup.js.map +1 -0
  36. package/dist/municipal/muniIssues/muniIssues.types.d.ts +30 -0
  37. package/dist/municipal/muniIssues/muniIssues.types.d.ts.map +1 -0
  38. package/dist/municipal/muniIssues/muniIssues.types.js +3 -0
  39. package/dist/municipal/muniIssues/muniIssues.types.js.map +1 -0
  40. package/dist/municipal/tickets/index.d.ts +2 -1
  41. package/dist/municipal/tickets/index.d.ts.map +1 -1
  42. package/dist/municipal/tickets/index.js +1 -0
  43. package/dist/municipal/tickets/index.js.map +1 -1
  44. package/dist/municipal/tickets/tickets.constants.d.ts +7 -0
  45. package/dist/municipal/tickets/tickets.constants.d.ts.map +1 -0
  46. package/dist/municipal/tickets/tickets.constants.js +10 -0
  47. package/dist/municipal/tickets/tickets.constants.js.map +1 -0
  48. package/dist/municipal/tickets/tickets.deprecated.getters.d.ts +12 -0
  49. package/dist/municipal/tickets/tickets.deprecated.getters.d.ts.map +1 -0
  50. package/dist/municipal/tickets/tickets.deprecated.getters.js +131 -0
  51. package/dist/municipal/tickets/tickets.deprecated.getters.js.map +1 -0
  52. package/dist/municipal/tickets/tickets.getters.d.ts +0 -11
  53. package/dist/municipal/tickets/tickets.getters.d.ts.map +1 -1
  54. package/dist/municipal/tickets/tickets.getters.js +0 -128
  55. package/dist/municipal/tickets/tickets.getters.js.map +1 -1
  56. package/dist/municipal/tickets/tickets.statistics.aggregation.d.ts +35 -0
  57. package/dist/municipal/tickets/tickets.statistics.aggregation.d.ts.map +1 -0
  58. package/dist/municipal/tickets/tickets.statistics.aggregation.js +86 -0
  59. package/dist/municipal/tickets/tickets.statistics.aggregation.js.map +1 -0
  60. package/dist/municipal/tickets/tickets.statistics.dates.d.ts +7 -0
  61. package/dist/municipal/tickets/tickets.statistics.dates.d.ts.map +1 -0
  62. package/dist/municipal/tickets/tickets.statistics.dates.js +40 -0
  63. package/dist/municipal/tickets/tickets.statistics.dates.js.map +1 -0
  64. package/dist/municipal/tickets/tickets.statistics.getters.d.ts +8 -0
  65. package/dist/municipal/tickets/tickets.statistics.getters.d.ts.map +1 -0
  66. package/dist/municipal/tickets/tickets.statistics.getters.js +44 -0
  67. package/dist/municipal/tickets/tickets.statistics.getters.js.map +1 -0
  68. package/dist/municipal/tickets/tickets.statistics.pipeline.d.ts +53 -0
  69. package/dist/municipal/tickets/tickets.statistics.pipeline.d.ts.map +1 -0
  70. package/dist/municipal/tickets/tickets.statistics.pipeline.js +112 -0
  71. package/dist/municipal/tickets/tickets.statistics.pipeline.js.map +1 -0
  72. package/dist/municipal/tickets/tickets.statistics.utils.d.ts +7 -0
  73. package/dist/municipal/tickets/tickets.statistics.utils.d.ts.map +1 -0
  74. package/dist/municipal/tickets/tickets.statistics.utils.js +40 -0
  75. package/dist/municipal/tickets/tickets.statistics.utils.js.map +1 -0
  76. package/dist/municipal/tickets/tickets.types.d.ts +14 -5
  77. package/dist/municipal/tickets/tickets.types.d.ts.map +1 -1
  78. package/dist/talkpilot/calls/calls.constants.d.ts +17 -0
  79. package/dist/talkpilot/calls/calls.constants.d.ts.map +1 -0
  80. package/dist/talkpilot/calls/calls.constants.js +20 -0
  81. package/dist/talkpilot/calls/calls.constants.js.map +1 -0
  82. package/dist/talkpilot/calls/calls.statistics.getters.d.ts +28 -0
  83. package/dist/talkpilot/calls/calls.statistics.getters.d.ts.map +1 -0
  84. package/dist/talkpilot/calls/calls.statistics.getters.js +424 -0
  85. package/dist/talkpilot/calls/calls.statistics.getters.js.map +1 -0
  86. package/dist/talkpilot/calls/calls.statistics.ticketScope.d.ts +12 -0
  87. package/dist/talkpilot/calls/calls.statistics.ticketScope.d.ts.map +1 -0
  88. package/dist/talkpilot/calls/calls.statistics.ticketScope.js +37 -0
  89. package/dist/talkpilot/calls/calls.statistics.ticketScope.js.map +1 -0
  90. package/dist/talkpilot/calls/calls.statistics.tickets.d.ts +17 -0
  91. package/dist/talkpilot/calls/calls.statistics.tickets.d.ts.map +1 -0
  92. package/dist/talkpilot/calls/calls.statistics.tickets.js +33 -0
  93. package/dist/talkpilot/calls/calls.statistics.tickets.js.map +1 -0
  94. package/dist/talkpilot/calls/calls.statistics.types.d.ts +39 -0
  95. package/dist/talkpilot/calls/calls.statistics.types.d.ts.map +1 -0
  96. package/dist/talkpilot/calls/calls.statistics.types.js +3 -0
  97. package/dist/talkpilot/calls/calls.statistics.types.js.map +1 -0
  98. package/dist/talkpilot/calls/calls.types.d.ts +1 -2
  99. package/dist/talkpilot/calls/calls.types.d.ts.map +1 -1
  100. package/dist/talkpilot/calls/calls.types.js +0 -3
  101. package/dist/talkpilot/calls/calls.types.js.map +1 -1
  102. package/dist/talkpilot/calls/index.d.ts +3 -0
  103. package/dist/talkpilot/calls/index.d.ts.map +1 -1
  104. package/dist/talkpilot/calls/index.js +3 -0
  105. package/dist/talkpilot/calls/index.js.map +1 -1
  106. package/dist/test-utils/factories/index.d.ts +1 -0
  107. package/dist/test-utils/factories/index.d.ts.map +1 -1
  108. package/dist/test-utils/factories/index.js +1 -0
  109. package/dist/test-utils/factories/index.js.map +1 -1
  110. package/dist/test-utils/factories/municipal/muniIssues.d.ts +10 -0
  111. package/dist/test-utils/factories/municipal/muniIssues.d.ts.map +1 -0
  112. package/dist/test-utils/factories/municipal/muniIssues.js +30 -0
  113. package/dist/test-utils/factories/municipal/muniIssues.js.map +1 -0
  114. package/dist/utils/date.utils.d.ts +49 -0
  115. package/dist/utils/date.utils.d.ts.map +1 -0
  116. package/dist/utils/date.utils.js +103 -0
  117. package/dist/utils/date.utils.js.map +1 -0
  118. package/dist/utils/statistics.aggregation.d.ts +20 -0
  119. package/dist/utils/statistics.aggregation.d.ts.map +1 -0
  120. package/dist/utils/statistics.aggregation.js +43 -0
  121. package/dist/utils/statistics.aggregation.js.map +1 -0
  122. package/package.json +1 -1
  123. package/src/index.ts +1 -0
  124. package/src/municipal/index.ts +1 -0
  125. package/src/municipal/muniIssues/__tests__/muniIssues.setters.spec.ts +92 -0
  126. package/src/municipal/muniIssues/__tests__/muniIssues.setup.spec.ts +92 -0
  127. package/src/municipal/muniIssues/index.ts +17 -0
  128. package/src/municipal/muniIssues/muniIssues.constants.ts +18 -0
  129. package/src/municipal/muniIssues/muniIssues.getters.ts +24 -0
  130. package/src/municipal/muniIssues/muniIssues.schema.ts +66 -0
  131. package/src/municipal/muniIssues/muniIssues.setters.ts +24 -0
  132. package/src/municipal/muniIssues/muniIssues.setup.ts +65 -0
  133. package/src/municipal/muniIssues/muniIssues.types.ts +37 -0
  134. package/src/municipal/tickets/__tests__/tickets.getters.spec.ts +1 -37
  135. package/src/municipal/tickets/__tests__/tickets.statistics.spec.ts +51 -0
  136. package/src/municipal/tickets/index.ts +2 -1
  137. package/src/municipal/tickets/tickets.constants.ts +8 -0
  138. package/src/municipal/tickets/tickets.getters.ts +0 -140
  139. package/src/municipal/tickets/tickets.statistics.aggregation.ts +96 -0
  140. package/src/municipal/tickets/tickets.statistics.getters.ts +71 -0
  141. package/src/municipal/tickets/tickets.types.ts +19 -9
  142. package/src/talkpilot/calls/__tests__/calls.statistics.spec.ts +344 -0
  143. package/src/talkpilot/calls/calls.constants.ts +20 -0
  144. package/src/talkpilot/calls/calls.statistics.getters.ts +587 -0
  145. package/src/talkpilot/calls/calls.statistics.types.ts +44 -0
  146. package/src/talkpilot/calls/calls.types.ts +4 -2
  147. package/src/talkpilot/calls/index.ts +3 -0
  148. package/src/test-utils/factories/index.ts +1 -0
  149. package/src/test-utils/factories/municipal/muniIssues.ts +32 -0
  150. package/src/utils/date.utils.ts +116 -0
@@ -1,8 +1,7 @@
1
1
  import { ObjectId, Sort, WithId } from "mongodb";
2
2
  import { TranscriptionSegment } from "../results";
3
3
  import { LeadProperty } from "../leads";
4
- export declare const CONFERENCE_ROLE_CUSTOMER: "customer";
5
- export declare const CONFERENCE_ROLE_SUPERVISOR: "supervisor";
4
+ import { CONFERENCE_ROLE_CUSTOMER, CONFERENCE_ROLE_SUPERVISOR } from "./calls.constants";
6
5
  export type ConferenceRole = typeof CONFERENCE_ROLE_CUSTOMER | typeof CONFERENCE_ROLE_SUPERVISOR | null;
7
6
  export type Call = {
8
7
  callSid: string;
@@ -1 +1 @@
1
- {"version":3,"file":"calls.types.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"calls.types.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.types.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,3 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONFERENCE_ROLE_SUPERVISOR = exports.CONFERENCE_ROLE_CUSTOMER = void 0;
4
- exports.CONFERENCE_ROLE_CUSTOMER = "customer";
5
- exports.CONFERENCE_ROLE_SUPERVISOR = "supervisor";
6
3
  //# sourceMappingURL=calls.types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"calls.types.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.types.ts"],"names":[],"mappings":";;;AAIa,QAAA,wBAAwB,GAAG,UAAmB,CAAC;AAC/C,QAAA,0BAA0B,GAAG,YAAqB,CAAC"}
1
+ {"version":3,"file":"calls.types.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.types.ts"],"names":[],"mappings":""}
@@ -1,4 +1,7 @@
1
+ export * from "./calls.constants";
1
2
  export * from "./calls.types";
2
3
  export * from "./calls.getters";
4
+ export * from "./calls.statistics.types";
5
+ export * from "./calls.statistics.getters";
3
6
  export * from "./dashboard/calls.dashboard";
4
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC"}
@@ -14,7 +14,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./calls.constants"), exports);
17
18
  __exportStar(require("./calls.types"), exports);
18
19
  __exportStar(require("./calls.getters"), exports);
20
+ __exportStar(require("./calls.statistics.types"), exports);
21
+ __exportStar(require("./calls.statistics.getters"), exports);
19
22
  __exportStar(require("./dashboard/calls.dashboard"), exports);
20
23
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B;AAC9B,kDAAgC;AAChC,8DAA4C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,gDAA8B;AAC9B,kDAAgC;AAChC,2DAAyC;AACzC,6DAA2C;AAC3C,8DAA4C"}
@@ -8,6 +8,7 @@ export * from "./talkpilot/phone_numbers";
8
8
  export * from "./talkpilot/sessions";
9
9
  export * from "./municipal/cities";
10
10
  export * from "./municipal/departmentsSubjects";
11
+ export * from "./municipal/muniIssues";
11
12
  export * from "./municipal/streets";
12
13
  export * from "./municipal/tickets";
13
14
  export * from "./websitalk/scans";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/factories/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/factories/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC;AAChD,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -24,6 +24,7 @@ __exportStar(require("./talkpilot/phone_numbers"), exports);
24
24
  __exportStar(require("./talkpilot/sessions"), exports);
25
25
  __exportStar(require("./municipal/cities"), exports);
26
26
  __exportStar(require("./municipal/departmentsSubjects"), exports);
27
+ __exportStar(require("./municipal/muniIssues"), exports);
27
28
  __exportStar(require("./municipal/streets"), exports);
28
29
  __exportStar(require("./municipal/tickets"), exports);
29
30
  __exportStar(require("./websitalk/scans"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/test-utils/factories/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,oDAAkC;AAClC,iEAA+C;AAC/C,4DAA0C;AAC1C,oDAAkC;AAClC,qDAAmC;AACnC,4DAA0C;AAC1C,uDAAqC;AACrC,qDAAmC;AACnC,kEAAgD;AAChD,sDAAoC;AACpC,sDAAoC;AACpC,oDAAkC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/test-utils/factories/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,qDAAmC;AACnC,oDAAkC;AAClC,iEAA+C;AAC/C,4DAA0C;AAC1C,oDAAkC;AAClC,qDAAmC;AACnC,4DAA0C;AAC1C,uDAAqC;AACrC,qDAAmC;AACnC,kEAAgD;AAChD,yDAAuC;AACvC,sDAAoC;AACpC,sDAAoC;AACpC,oDAAkC"}
@@ -0,0 +1,10 @@
1
+ import { Factory } from "fishery";
2
+ import type { CreateMuniIssueInput } from "../../../municipal";
3
+ /**
4
+ * Test factory for `createMuniIssue` input.
5
+ * `jira.key` uses `sequence` so each build gets a unique key (the real key
6
+ * comes from Jira in production).
7
+ */
8
+ export declare const muniIssueInputFactory: Factory<CreateMuniIssueInput, any, CreateMuniIssueInput, import("fishery").DeepPartialObject<CreateMuniIssueInput>>;
9
+ export declare function createMuniIssueInput(overrides?: Partial<CreateMuniIssueInput>): CreateMuniIssueInput;
10
+ //# sourceMappingURL=muniIssues.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"muniIssues.d.ts","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/muniIssues.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,qHAgBjC,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,SAAS,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACxC,oBAAoB,CAEtB"}
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.muniIssueInputFactory = void 0;
4
+ exports.createMuniIssueInput = createMuniIssueInput;
5
+ const fishery_1 = require("fishery");
6
+ const faker_1 = require("@faker-js/faker");
7
+ /**
8
+ * Test factory for `createMuniIssue` input.
9
+ * `jira.key` uses `sequence` so each build gets a unique key (the real key
10
+ * comes from Jira in production).
11
+ */
12
+ exports.muniIssueInputFactory = fishery_1.Factory.define(({ sequence }) => ({
13
+ callSid: `CA${faker_1.faker.string.alphanumeric(10)}`,
14
+ clientId: faker_1.faker.string.uuid(),
15
+ cityName: faker_1.faker.location.city(),
16
+ issueContent: {
17
+ issuerName: faker_1.faker.person.fullName(),
18
+ issuerRole: faker_1.faker.person.jobTitle(),
19
+ issueDescription: faker_1.faker.lorem.sentence(),
20
+ desiredBehavior: faker_1.faker.lorem.sentence(),
21
+ },
22
+ jira: {
23
+ key: `CP-${sequence}`,
24
+ syncStatus: "created",
25
+ },
26
+ }));
27
+ function createMuniIssueInput(overrides) {
28
+ return exports.muniIssueInputFactory.build(overrides);
29
+ }
30
+ //# sourceMappingURL=muniIssues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"muniIssues.js","sourceRoot":"","sources":["../../../../src/test-utils/factories/municipal/muniIssues.ts"],"names":[],"mappings":";;;AA2BA,oDAIC;AA/BD,qCAAkC;AAClC,2CAAwC;AAGxC;;;;GAIG;AACU,QAAA,qBAAqB,GAAG,iBAAO,CAAC,MAAM,CACjD,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IACjB,OAAO,EAAE,KAAK,aAAK,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE;IAC7C,QAAQ,EAAE,aAAK,CAAC,MAAM,CAAC,IAAI,EAAE;IAC7B,QAAQ,EAAE,aAAK,CAAC,QAAQ,CAAC,IAAI,EAAE;IAC/B,YAAY,EAAE;QACZ,UAAU,EAAE,aAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;QACnC,UAAU,EAAE,aAAK,CAAC,MAAM,CAAC,QAAQ,EAAE;QACnC,gBAAgB,EAAE,aAAK,CAAC,KAAK,CAAC,QAAQ,EAAE;QACxC,eAAe,EAAE,aAAK,CAAC,KAAK,CAAC,QAAQ,EAAE;KACxC;IACD,IAAI,EAAE;QACJ,GAAG,EAAE,MAAM,QAAQ,EAAE;QACrB,UAAU,EAAE,SAAS;KACtB;CACF,CAAC,CACH,CAAC;AAEF,SAAgB,oBAAoB,CAClC,SAAyC;IAEzC,OAAO,6BAAqB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Shared timezone-aware date helpers for statistics aggregations.
3
+ */
4
+ /** Milliseconds in a single day. */
5
+ export declare const DAY_MS: number;
6
+ /** Maps a short weekday name (as produced by Intl) to its index (Sun = 0). */
7
+ export declare const WEEKDAY: Record<string, number>;
8
+ /** Parses a `YYYY-MM-DD` string into `[year, month, day]` numbers. */
9
+ export declare const parseYmd: (dateStr: string) => [number, number, number];
10
+ /** Builds a zero-padded `YYYY-MM-DD` string from numeric parts. */
11
+ export declare const ymdToStr: (y: number, m: number, d: number) => string;
12
+ /** Parses a `YYYY-MM-DD` string as midnight UTC. */
13
+ export declare const toUtcDate: (dateStr: string) => Date;
14
+ /** Formats a date as a `YYYY-MM-DD` string in UTC. */
15
+ export declare const formatDate: (date: Date) => string;
16
+ /** Adds `days` to a `YYYY-MM-DD` string, returning the resulting `YYYY-MM-DD`. */
17
+ export declare const addDaysToDateStr: (dateStr: string, days: number) => string;
18
+ /** Next civil calendar day (proleptic Gregorian). Avoids DST 24h steps that can stall iteration. */
19
+ export declare const incrementYmd: (y: number, m: number, d: number) => [number, number, number];
20
+ /**
21
+ * Formats a date as the calendar day in the given timezone.
22
+ * `en-CA` yields ISO-8601 `YYYY-MM-DD`, which is lexicographically sortable
23
+ * (string compare == chronological compare) and matches MongoDB's `%Y-%m-%d`.
24
+ */
25
+ export declare const formatYmdInTz: (date: Date, timezone: string) => string;
26
+ /**
27
+ * Returns the calendar day in the given timezone as numeric parts.
28
+ * Reads parts by `type` (not string order), so `en-US` is fine here.
29
+ */
30
+ export declare const getYmdInTz: (date: Date, timezone: string) => {
31
+ y: number;
32
+ m: number;
33
+ d: number;
34
+ };
35
+ /** Weekday index (Sun = 0) of a date in the given timezone. */
36
+ export declare const getWeekdayInTz: (date: Date, timezone: string) => number;
37
+ /**
38
+ * Returns the given `YYYY-MM-DD` day at noon UTC — a stable anchor inside the
39
+ * calendar day, far from midnight so timezone/DST shifts can't cross a date boundary.
40
+ */
41
+ export declare const dateAtNoonUtc: (dateStr: string) => Date;
42
+ /**
43
+ * UTC instant at the start of the given `YYYY-MM-DD` calendar day in `timezone`.
44
+ * Binary-searches the boundary so it stays correct across DST transitions.
45
+ */
46
+ export declare const startOfCalendarDayInTz: (dateStr: string, timezone: string) => Date;
47
+ /** UTC instant at the last millisecond of the given calendar day in `timezone`. */
48
+ export declare const endOfCalendarDayInTz: (dateStr: string, timezone: string) => Date;
49
+ //# sourceMappingURL=date.utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.utils.d.ts","sourceRoot":"","sources":["../../src/utils/date.utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oCAAoC;AACpC,eAAO,MAAM,MAAM,QAAsB,CAAC;AAE1C,8EAA8E;AAC9E,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAQ1C,CAAC;AAEF,sEAAsE;AACtE,eAAO,MAAM,QAAQ,GAAI,SAAS,MAAM,KAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAGjE,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,KAAG,MACS,CAAC;AAErE,oDAAoD;AACpD,eAAO,MAAM,SAAS,GAAI,SAAS,MAAM,KAAG,IACN,CAAC;AAEvC,sDAAsD;AACtD,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,KAAG,MAAyC,CAAC;AAElF,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,GAAI,SAAS,MAAM,EAAE,MAAM,MAAM,KAAG,MACG,CAAC;AAErE,oGAAoG;AACpG,eAAO,MAAM,YAAY,GACvB,GAAG,MAAM,EACT,GAAG,MAAM,EACT,GAAG,MAAM,KACR,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAGzB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,MAAM,IAAI,EAAE,UAAU,MAAM,KAAG,MACU,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,MAAM,IAAI,EAAE,UAAU,MAAM;;;;CAYtD,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,cAAc,GAAI,MAAM,IAAI,EAAE,UAAU,MAAM,KAAG,MAKtD,CAAC;AAET;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,KAAG,IAG/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,IAY1E,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,EAAE,UAAU,MAAM,KAAG,IAIxE,CAAC"}
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ /**
3
+ * Shared timezone-aware date helpers for statistics aggregations.
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.endOfCalendarDayInTz = exports.startOfCalendarDayInTz = exports.dateAtNoonUtc = exports.getWeekdayInTz = exports.getYmdInTz = exports.formatYmdInTz = exports.incrementYmd = exports.addDaysToDateStr = exports.formatDate = exports.toUtcDate = exports.ymdToStr = exports.parseYmd = exports.WEEKDAY = exports.DAY_MS = void 0;
7
+ /** Milliseconds in a single day. */
8
+ exports.DAY_MS = 24 * 60 * 60 * 1000;
9
+ /** Maps a short weekday name (as produced by Intl) to its index (Sun = 0). */
10
+ exports.WEEKDAY = {
11
+ Sun: 0,
12
+ Mon: 1,
13
+ Tue: 2,
14
+ Wed: 3,
15
+ Thu: 4,
16
+ Fri: 5,
17
+ Sat: 6,
18
+ };
19
+ /** Parses a `YYYY-MM-DD` string into `[year, month, day]` numbers. */
20
+ const parseYmd = (dateStr) => {
21
+ const [y, m, d] = dateStr.split("-").map(Number);
22
+ return [y, m, d];
23
+ };
24
+ exports.parseYmd = parseYmd;
25
+ /** Builds a zero-padded `YYYY-MM-DD` string from numeric parts. */
26
+ const ymdToStr = (y, m, d) => `${y}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
27
+ exports.ymdToStr = ymdToStr;
28
+ /** Parses a `YYYY-MM-DD` string as midnight UTC. */
29
+ const toUtcDate = (dateStr) => new Date(`${dateStr}T00:00:00.000Z`);
30
+ exports.toUtcDate = toUtcDate;
31
+ /** Formats a date as a `YYYY-MM-DD` string in UTC. */
32
+ const formatDate = (date) => date.toISOString().slice(0, 10);
33
+ exports.formatDate = formatDate;
34
+ /** Adds `days` to a `YYYY-MM-DD` string, returning the resulting `YYYY-MM-DD`. */
35
+ const addDaysToDateStr = (dateStr, days) => (0, exports.formatDate)(new Date((0, exports.toUtcDate)(dateStr).getTime() + days * exports.DAY_MS));
36
+ exports.addDaysToDateStr = addDaysToDateStr;
37
+ /** Next civil calendar day (proleptic Gregorian). Avoids DST 24h steps that can stall iteration. */
38
+ const incrementYmd = (y, m, d) => {
39
+ const next = new Date(Date.UTC(y, m - 1, d + 1));
40
+ return [next.getUTCFullYear(), next.getUTCMonth() + 1, next.getUTCDate()];
41
+ };
42
+ exports.incrementYmd = incrementYmd;
43
+ /**
44
+ * Formats a date as the calendar day in the given timezone.
45
+ * `en-CA` yields ISO-8601 `YYYY-MM-DD`, which is lexicographically sortable
46
+ * (string compare == chronological compare) and matches MongoDB's `%Y-%m-%d`.
47
+ */
48
+ const formatYmdInTz = (date, timezone) => new Intl.DateTimeFormat("en-CA", { timeZone: timezone }).format(date);
49
+ exports.formatYmdInTz = formatYmdInTz;
50
+ /**
51
+ * Returns the calendar day in the given timezone as numeric parts.
52
+ * Reads parts by `type` (not string order), so `en-US` is fine here.
53
+ */
54
+ const getYmdInTz = (date, timezone) => {
55
+ const parts = Object.fromEntries(new Intl.DateTimeFormat("en-US", {
56
+ timeZone: timezone,
57
+ year: "numeric",
58
+ month: "numeric",
59
+ day: "numeric",
60
+ })
61
+ .formatToParts(date)
62
+ .map((p) => [p.type, Number(p.value)]));
63
+ return { y: parts.year, m: parts.month, d: parts.day };
64
+ };
65
+ exports.getYmdInTz = getYmdInTz;
66
+ /** Weekday index (Sun = 0) of a date in the given timezone. */
67
+ const getWeekdayInTz = (date, timezone) => exports.WEEKDAY[new Intl.DateTimeFormat("en-US", { timeZone: timezone, weekday: "short" }).format(date)] ?? 0;
68
+ exports.getWeekdayInTz = getWeekdayInTz;
69
+ /**
70
+ * Returns the given `YYYY-MM-DD` day at noon UTC — a stable anchor inside the
71
+ * calendar day, far from midnight so timezone/DST shifts can't cross a date boundary.
72
+ */
73
+ const dateAtNoonUtc = (dateStr) => {
74
+ const [y, m, d] = (0, exports.parseYmd)(dateStr);
75
+ return new Date(Date.UTC(y, m - 1, d, 12, 0, 0));
76
+ };
77
+ exports.dateAtNoonUtc = dateAtNoonUtc;
78
+ /**
79
+ * UTC instant at the start of the given `YYYY-MM-DD` calendar day in `timezone`.
80
+ * Binary-searches the boundary so it stays correct across DST transitions.
81
+ */
82
+ const startOfCalendarDayInTz = (dateStr, timezone) => {
83
+ const anchor = (0, exports.dateAtNoonUtc)(dateStr).getTime();
84
+ let low = anchor - 2 * exports.DAY_MS;
85
+ let high = anchor + exports.DAY_MS;
86
+ while (high - low > 1 /* ms */) {
87
+ const mid = Math.floor((low + high) / 2);
88
+ if ((0, exports.formatYmdInTz)(new Date(mid), timezone) >= dateStr)
89
+ high = mid;
90
+ else
91
+ low = mid;
92
+ }
93
+ return new Date(high);
94
+ };
95
+ exports.startOfCalendarDayInTz = startOfCalendarDayInTz;
96
+ /** UTC instant at the last millisecond of the given calendar day in `timezone`. */
97
+ const endOfCalendarDayInTz = (dateStr, timezone) => {
98
+ const [y, m, d] = (0, exports.parseYmd)(dateStr);
99
+ const nextDay = new Date(Date.UTC(y, m - 1, d + 1)).toISOString().slice(0, 10);
100
+ return new Date((0, exports.startOfCalendarDayInTz)(nextDay, timezone).getTime() - 1);
101
+ };
102
+ exports.endOfCalendarDayInTz = endOfCalendarDayInTz;
103
+ //# sourceMappingURL=date.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date.utils.js","sourceRoot":"","sources":["../../src/utils/date.utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** Default server-side cap for statistics aggregations (30s). */
2
+ export declare const DEFAULT_STATS_MAX_TIME_MS = 30000;
3
+ /** Log a warning when SID arrays exceed this size (BSON / pipeline pressure). */
4
+ export declare const MAX_RECOMMENDED_SID_ARRAY_SIZE = 10000;
5
+ export type StatisticsAggregationOptions = {
6
+ maxTimeMS?: number;
7
+ };
8
+ export declare class StatisticsTimeoutError extends Error {
9
+ constructor(maxTimeMS: number);
10
+ }
11
+ export declare const resolveMaxTimeMS: (options?: StatisticsAggregationOptions) => number;
12
+ /** Builds a constant lookup object for O(1) membership checks via $getField. */
13
+ export declare const buildCallSidLookup: (callSids: string[]) => Record<string, true>;
14
+ /**
15
+ * $expr that is true when `$callSid` is in `callSids`.
16
+ * Prefer over `$in` for large arrays embedded in aggregation pipelines.
17
+ */
18
+ export declare const callSidInSetExpr: (callSids: string[]) => Record<string, unknown>;
19
+ export declare const warnIfLargeSidArray: (label: string, callSids: string[]) => void;
20
+ //# sourceMappingURL=statistics.aggregation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.aggregation.d.ts","sourceRoot":"","sources":["../../src/utils/statistics.aggregation.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD,iFAAiF;AACjF,eAAO,MAAM,8BAA8B,QAAS,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG;IACzC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,SAAS,EAAE,MAAM;CAI9B;AAED,eAAO,MAAM,gBAAgB,GAAI,UAAU,4BAA4B,KAAG,MACzB,CAAC;AAElD,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,GAAI,UAAU,MAAM,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CACK,CAAC;AAEjF;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAW1E,CAAC;AAEH,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,EAAE,UAAU,MAAM,EAAE,KAAG,IAMvE,CAAC"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.warnIfLargeSidArray = exports.callSidInSetExpr = exports.buildCallSidLookup = exports.resolveMaxTimeMS = exports.StatisticsTimeoutError = exports.MAX_RECOMMENDED_SID_ARRAY_SIZE = exports.DEFAULT_STATS_MAX_TIME_MS = void 0;
4
+ /** Default server-side cap for statistics aggregations (30s). */
5
+ exports.DEFAULT_STATS_MAX_TIME_MS = 30_000;
6
+ /** Log a warning when SID arrays exceed this size (BSON / pipeline pressure). */
7
+ exports.MAX_RECOMMENDED_SID_ARRAY_SIZE = 10_000;
8
+ class StatisticsTimeoutError extends Error {
9
+ constructor(maxTimeMS) {
10
+ super(`Statistics aggregation exceeded maxTimeMS (${maxTimeMS})`);
11
+ this.name = "StatisticsTimeoutError";
12
+ }
13
+ }
14
+ exports.StatisticsTimeoutError = StatisticsTimeoutError;
15
+ const resolveMaxTimeMS = (options) => options?.maxTimeMS ?? exports.DEFAULT_STATS_MAX_TIME_MS;
16
+ exports.resolveMaxTimeMS = resolveMaxTimeMS;
17
+ /** Builds a constant lookup object for O(1) membership checks via $getField. */
18
+ const buildCallSidLookup = (callSids) => Object.fromEntries(callSids.map((sid) => [sid, true]));
19
+ exports.buildCallSidLookup = buildCallSidLookup;
20
+ /**
21
+ * $expr that is true when `$callSid` is in `callSids`.
22
+ * Prefer over `$in` for large arrays embedded in aggregation pipelines.
23
+ */
24
+ const callSidInSetExpr = (callSids) => ({
25
+ $eq: [
26
+ {
27
+ $getField: {
28
+ field: "$callSid",
29
+ input: (0, exports.buildCallSidLookup)(callSids),
30
+ default: false,
31
+ },
32
+ },
33
+ true,
34
+ ],
35
+ });
36
+ exports.callSidInSetExpr = callSidInSetExpr;
37
+ const warnIfLargeSidArray = (label, callSids) => {
38
+ if (callSids.length > exports.MAX_RECOMMENDED_SID_ARRAY_SIZE) {
39
+ console.warn(`[core-db statistics] ${label}: ${callSids.length} call SIDs exceeds recommended max (${exports.MAX_RECOMMENDED_SID_ARRAY_SIZE}). Narrow the date range or cache SIDs once per request.`);
40
+ }
41
+ };
42
+ exports.warnIfLargeSidArray = warnIfLargeSidArray;
43
+ //# sourceMappingURL=statistics.aggregation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statistics.aggregation.js","sourceRoot":"","sources":["../../src/utils/statistics.aggregation.ts"],"names":[],"mappings":";;;AAAA,iEAAiE;AACpD,QAAA,yBAAyB,GAAG,MAAM,CAAC;AAEhD,iFAAiF;AACpE,QAAA,8BAA8B,GAAG,MAAM,CAAC;AAMrD,MAAa,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,SAAiB;QAC3B,KAAK,CAAC,8CAA8C,SAAS,GAAG,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AALD,wDAKC;AAEM,MAAM,gBAAgB,GAAG,CAAC,OAAsC,EAAU,EAAE,CACjF,OAAO,EAAE,SAAS,IAAI,iCAAyB,CAAC;AADrC,QAAA,gBAAgB,oBACqB;AAElD,gFAAgF;AACzE,MAAM,kBAAkB,GAAG,CAAC,QAAkB,EAAwB,EAAE,CAC7E,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAyB,CAAC;AADpE,QAAA,kBAAkB,sBACkD;AAEjF;;;GAGG;AACI,MAAM,gBAAgB,GAAG,CAAC,QAAkB,EAA2B,EAAE,CAAC,CAAC;IAChF,GAAG,EAAE;QACH;YACE,SAAS,EAAE;gBACT,KAAK,EAAE,UAAU;gBACjB,KAAK,EAAE,IAAA,0BAAkB,EAAC,QAAQ,CAAC;gBACnC,OAAO,EAAE,KAAK;aACf;SACF;QACD,IAAI;KACL;CACF,CAAC,CAAC;AAXU,QAAA,gBAAgB,oBAW1B;AAEI,MAAM,mBAAmB,GAAG,CAAC,KAAa,EAAE,QAAkB,EAAQ,EAAE;IAC7E,IAAI,QAAQ,CAAC,MAAM,GAAG,sCAA8B,EAAE,CAAC;QACrD,OAAO,CAAC,IAAI,CACV,wBAAwB,KAAK,KAAK,QAAQ,CAAC,MAAM,uCAAuC,sCAA8B,0DAA0D,CACjL,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AANW,QAAA,mBAAmB,uBAM9B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talkpilot/core-db",
3
- "version": "1.3.3",
3
+ "version": "1.3.5",
4
4
  "description": "Core database package for centralized connections and ORM integration.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export * from "./municipal/streets";
13
13
  export * from "./municipal/departmentsSubjects";
14
14
  export * from "./municipal/tickets";
15
15
  export * from "./municipal/systemInstructions";
16
+ export * from "./municipal/muniIssues";
16
17
  export * from "./municipal/utils/types";
17
18
 
18
19
  export {
@@ -5,6 +5,7 @@ export * from "./streets";
5
5
  export * from "./departmentsSubjects";
6
6
  export * from "./tickets";
7
7
  export * from "./systemInstructions";
8
+ export * from "./muniIssues";
8
9
  export * from "./utils/types";
9
10
  export { CityName } from "./utils/types";
10
11
  export { municipalDataMongodbClient } from "./mongodb-client";
@@ -0,0 +1,92 @@
1
+ import { createMuniIssue } from "../muniIssues.setters";
2
+ import {
3
+ generateMuniIssueId,
4
+ getMuniIssueById,
5
+ getMuniIssueByJiraKey,
6
+ } from "../muniIssues.getters";
7
+ import { createMuniIssueInput } from "../../../test-utils/factories";
8
+
9
+ describe("muniIssues setters", () => {
10
+ describe("createMuniIssue", () => {
11
+ it("should insert a well-formed document with createdAt and nested content/jira", async () => {
12
+ const input = createMuniIssueInput();
13
+ const id = generateMuniIssueId();
14
+
15
+ const insertedId = await createMuniIssue(input, id);
16
+
17
+ const saved = await getMuniIssueById(insertedId.toString());
18
+ expect(saved).toMatchObject({
19
+ callSid: input.callSid,
20
+ clientId: input.clientId,
21
+ cityName: input.cityName,
22
+ issueContent: input.issueContent,
23
+ jira: input.jira,
24
+ });
25
+ expect(saved?.createdAt).toBeInstanceOf(Date);
26
+ });
27
+
28
+ it("should default jira.syncStatus to created when not provided", async () => {
29
+ const id = generateMuniIssueId();
30
+
31
+ const insertedId = await createMuniIssue(
32
+ createMuniIssueInput({ jira: { key: "CP-9" } }),
33
+ id,
34
+ );
35
+
36
+ const saved = await getMuniIssueById(insertedId.toString());
37
+ expect(saved?.jira.syncStatus).toBe("created");
38
+ });
39
+
40
+ it("should store long issueDescription and desiredBehavior in full", async () => {
41
+ const issueDescription = "a".repeat(2000);
42
+ const desiredBehavior = "b".repeat(2000);
43
+ const id = generateMuniIssueId();
44
+
45
+ const insertedId = await createMuniIssue(
46
+ createMuniIssueInput({
47
+ issueContent: {
48
+ issuerName: "Dana",
49
+ issuerRole: "Agent",
50
+ issueDescription,
51
+ desiredBehavior,
52
+ },
53
+ }),
54
+ id,
55
+ );
56
+
57
+ const saved = await getMuniIssueById(insertedId.toString());
58
+ expect(saved?.issueContent.issueDescription).toHaveLength(2000);
59
+ expect(saved?.issueContent.desiredBehavior).toBe(desiredBehavior);
60
+ });
61
+
62
+ it("should persist the caller-supplied _id so the Jira ticket can link back", async () => {
63
+ const id = generateMuniIssueId();
64
+
65
+ const insertedId = await createMuniIssue(createMuniIssueInput(), id);
66
+
67
+ expect(insertedId.toString()).toBe(id.toString());
68
+ expect(await getMuniIssueById(id.toString())).not.toBeNull();
69
+ });
70
+ });
71
+
72
+ describe("getMuniIssueById", () => {
73
+ it("should return null for an unknown or malformed id instead of throwing", async () => {
74
+ expect(await getMuniIssueById(generateMuniIssueId().toString())).toBeNull();
75
+ expect(await getMuniIssueById("not-a-valid-id")).toBeNull();
76
+ });
77
+ });
78
+
79
+ describe("getMuniIssueByJiraKey", () => {
80
+ it("should find the issue with the given Jira key", async () => {
81
+ await createMuniIssue(
82
+ createMuniIssueInput({ jira: { key: "CP-555" } }),
83
+ generateMuniIssueId(),
84
+ );
85
+
86
+ const found = await getMuniIssueByJiraKey("CP-555");
87
+
88
+ expect(found?.jira.key).toBe("CP-555");
89
+ expect(await getMuniIssueByJiraKey("CP-nope")).toBeNull();
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,92 @@
1
+ import {
2
+ ensureMuniIssuesCollection,
3
+ ensureMuniIssuesCollectionOnce,
4
+ } from "../muniIssues.setup";
5
+ import {
6
+ generateMuniIssueId,
7
+ getMuniIssuesCollection,
8
+ } from "../muniIssues.getters";
9
+ import { createMuniIssue } from "../muniIssues.setters";
10
+ import { getDb } from "../../index";
11
+ import { MUNI_ISSUES_COLLECTION } from "../muniIssues.constants";
12
+ import { createMuniIssueInput } from "../../../test-utils/factories";
13
+
14
+ describe("muniIssues setup", () => {
15
+ describe("ensureMuniIssuesCollection", () => {
16
+ it("should create the collection with a validator and the declared indexes", async () => {
17
+ await ensureMuniIssuesCollection();
18
+
19
+ const [collection] = (await getDb()
20
+ .listCollections({ name: MUNI_ISSUES_COLLECTION })
21
+ .toArray()) as { options?: { validator?: unknown } }[];
22
+ expect(collection?.options?.validator).toHaveProperty("$jsonSchema");
23
+
24
+ const indexNames = (await getMuniIssuesCollection().indexes()).map(
25
+ (i) => i.name,
26
+ );
27
+ expect(indexNames).toEqual(
28
+ expect.arrayContaining([
29
+ "callSid_1",
30
+ "createdAt_-1",
31
+ "jira_key_unique",
32
+ ]),
33
+ );
34
+ });
35
+
36
+ it("should accept a valid document and reject ones that violate the schema", async () => {
37
+ await ensureMuniIssuesCollection();
38
+
39
+ await expect(
40
+ createMuniIssue(createMuniIssueInput(), generateMuniIssueId()),
41
+ ).resolves.toBeDefined();
42
+
43
+ // Missing required issueContent fields → DB validation rejects it.
44
+ await expect(
45
+ getMuniIssuesCollection().insertOne({
46
+ callSid: "CA-bad",
47
+ createdAt: new Date(),
48
+ } as never),
49
+ ).rejects.toThrow();
50
+
51
+ // jira.syncStatus outside the enum → DB validation rejects it.
52
+ await expect(
53
+ createMuniIssue(
54
+ createMuniIssueInput({
55
+ jira: { key: "CP-99", syncStatus: "weird" as never },
56
+ }),
57
+ generateMuniIssueId(),
58
+ ),
59
+ ).rejects.toThrow();
60
+ });
61
+
62
+ it("should enforce the unique jira.key index", async () => {
63
+ await ensureMuniIssuesCollection();
64
+ const input = createMuniIssueInput();
65
+ await createMuniIssue(input, generateMuniIssueId());
66
+
67
+ // Same jira.key on a second insert violates the unique index.
68
+ await expect(
69
+ createMuniIssue(input, generateMuniIssueId()),
70
+ ).rejects.toThrow();
71
+ });
72
+
73
+ it("should be idempotent when run twice", async () => {
74
+ await ensureMuniIssuesCollection();
75
+ await expect(ensureMuniIssuesCollection()).resolves.toBeUndefined();
76
+ });
77
+ });
78
+
79
+ describe("ensureMuniIssuesCollectionOnce", () => {
80
+ it("should run the provisioning work at most once per process", async () => {
81
+ const createSpy = jest.spyOn(getDb(), "createCollection");
82
+
83
+ await Promise.all([
84
+ ensureMuniIssuesCollectionOnce(),
85
+ ensureMuniIssuesCollectionOnce(),
86
+ ]);
87
+ await ensureMuniIssuesCollectionOnce();
88
+
89
+ expect(createSpy).toHaveBeenCalledTimes(1);
90
+ });
91
+ });
92
+ });
@@ -0,0 +1,17 @@
1
+ export * from "./muniIssues.getters";
2
+ export * from "./muniIssues.setters";
3
+ export * from "./muniIssues.setup";
4
+ export {
5
+ MUNI_ISSUES_COLLECTION,
6
+ MUNI_ISSUE_SYNC_STATUSES,
7
+ MUNI_ISSUE_TEXT_MAX_LENGTH,
8
+ MUNI_ISSUE_NAME_MAX_LENGTH,
9
+ } from "./muniIssues.constants";
10
+ export { muniIssuesJsonSchema, muniIssuesIndexes } from "./muniIssues.schema";
11
+ export type {
12
+ MuniIssue,
13
+ MuniIssueContent,
14
+ MuniIssueJira,
15
+ MuniIssueSyncStatus,
16
+ CreateMuniIssueInput,
17
+ } from "./muniIssues.types";