@talkpilot/core-db 1.3.9 → 1.3.12

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 (82) hide show
  1. package/.cursor/rules/development.mdc +65 -65
  2. package/DEVELOPMENT.md +141 -141
  3. package/README.md +297 -257
  4. package/README_OLD.md +160 -160
  5. package/dist/municipal/tickets/index.d.ts +1 -1
  6. package/dist/municipal/tickets/index.d.ts.map +1 -1
  7. package/dist/municipal/tickets/tickets.statistics.aggregation.d.ts +34 -0
  8. package/dist/municipal/tickets/tickets.statistics.aggregation.d.ts.map +1 -1
  9. package/dist/municipal/tickets/tickets.statistics.aggregation.js +14 -1
  10. package/dist/municipal/tickets/tickets.statistics.aggregation.js.map +1 -1
  11. package/dist/municipal/tickets/tickets.statistics.getters.d.ts.map +1 -1
  12. package/dist/municipal/tickets/tickets.statistics.getters.js +15 -6
  13. package/dist/municipal/tickets/tickets.statistics.getters.js.map +1 -1
  14. package/dist/municipal/tickets/tickets.types.d.ts +1 -0
  15. package/dist/municipal/tickets/tickets.types.d.ts.map +1 -1
  16. package/dist/talkpilot/calls/calls.dashboard.d.ts +3 -0
  17. package/dist/talkpilot/calls/calls.dashboard.d.ts.map +1 -0
  18. package/dist/talkpilot/calls/calls.dashboard.js +191 -0
  19. package/dist/talkpilot/calls/calls.dashboard.js.map +1 -0
  20. package/dist/talkpilot/calls/calls.statistics.getters.d.ts.map +1 -1
  21. package/dist/talkpilot/calls/calls.statistics.getters.js +39 -10
  22. package/dist/talkpilot/calls/calls.statistics.getters.js.map +1 -1
  23. package/dist/talkpilot/calls/calls.statistics.types.d.ts +1 -0
  24. package/dist/talkpilot/calls/calls.statistics.types.d.ts.map +1 -1
  25. package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts +1 -1
  26. package/dist/talkpilot/clientsConfig/clientsConfig.types.d.ts.map +1 -1
  27. package/dist/talkpilot/contextNotes/contextNotes.getters.d.ts.map +1 -1
  28. package/dist/talkpilot/contextNotes/contextNotes.getters.js +1 -3
  29. package/dist/talkpilot/contextNotes/contextNotes.getters.js.map +1 -1
  30. package/dist/test-utils/factories/talkpilot/contextNotes.d.ts.map +1 -1
  31. package/dist/test-utils/factories/talkpilot/contextNotes.js +4 -1
  32. package/dist/test-utils/factories/talkpilot/contextNotes.js.map +1 -1
  33. package/dist/utils/date.utils.d.ts.map +1 -1
  34. package/dist/utils/date.utils.js +7 -2
  35. package/dist/utils/date.utils.js.map +1 -1
  36. package/jest.config.js +20 -20
  37. package/package.json +46 -46
  38. package/src/municipal/muniIssues/__tests__/muniIssues.setters.spec.ts +94 -92
  39. package/src/municipal/tickets/__tests__/tickets.statistics.spec.ts +99 -68
  40. package/src/municipal/tickets/index.ts +9 -3
  41. package/src/municipal/tickets/tickets.statistics.aggregation.ts +110 -96
  42. package/src/municipal/tickets/tickets.statistics.getters.ts +132 -109
  43. package/src/municipal/tickets/tickets.types.ts +54 -53
  44. package/src/talkpilot/calls/__tests__/calls.statistics.spec.ts +481 -375
  45. package/src/talkpilot/calls/calls.statistics.getters.ts +668 -590
  46. package/src/talkpilot/calls/calls.statistics.types.ts +48 -47
  47. package/src/talkpilot/clientsConfig/clientsConfig.types.ts +127 -127
  48. package/src/talkpilot/contextNotes/__tests__/contextNotes.getters.spec.ts +217 -176
  49. package/src/talkpilot/contextNotes/contextNotes.getters.ts +85 -86
  50. package/src/talkpilot/products/__tests__/products.getters.spec.ts +44 -45
  51. package/src/test-utils/factories/talkpilot/contextNotes.ts +40 -33
  52. package/src/utils/date.utils.ts +126 -116
  53. package/tsconfig.json +23 -23
  54. package/.claude/settings.local.json +0 -11
  55. package/dist/municipal/tickets/tickets.deprecated.getters.d.ts +0 -12
  56. package/dist/municipal/tickets/tickets.deprecated.getters.d.ts.map +0 -1
  57. package/dist/municipal/tickets/tickets.deprecated.getters.js +0 -131
  58. package/dist/municipal/tickets/tickets.deprecated.getters.js.map +0 -1
  59. package/dist/municipal/tickets/tickets.statistics.dates.d.ts +0 -7
  60. package/dist/municipal/tickets/tickets.statistics.dates.d.ts.map +0 -1
  61. package/dist/municipal/tickets/tickets.statistics.dates.js +0 -40
  62. package/dist/municipal/tickets/tickets.statistics.dates.js.map +0 -1
  63. package/dist/municipal/tickets/tickets.statistics.pipeline.d.ts +0 -53
  64. package/dist/municipal/tickets/tickets.statistics.pipeline.d.ts.map +0 -1
  65. package/dist/municipal/tickets/tickets.statistics.pipeline.js +0 -112
  66. package/dist/municipal/tickets/tickets.statistics.pipeline.js.map +0 -1
  67. package/dist/municipal/tickets/tickets.statistics.utils.d.ts +0 -7
  68. package/dist/municipal/tickets/tickets.statistics.utils.d.ts.map +0 -1
  69. package/dist/municipal/tickets/tickets.statistics.utils.js +0 -40
  70. package/dist/municipal/tickets/tickets.statistics.utils.js.map +0 -1
  71. package/dist/talkpilot/calls/calls.statistics.ticketScope.d.ts +0 -12
  72. package/dist/talkpilot/calls/calls.statistics.ticketScope.d.ts.map +0 -1
  73. package/dist/talkpilot/calls/calls.statistics.ticketScope.js +0 -37
  74. package/dist/talkpilot/calls/calls.statistics.ticketScope.js.map +0 -1
  75. package/dist/talkpilot/calls/calls.statistics.tickets.d.ts +0 -17
  76. package/dist/talkpilot/calls/calls.statistics.tickets.d.ts.map +0 -1
  77. package/dist/talkpilot/calls/calls.statistics.tickets.js +0 -33
  78. package/dist/talkpilot/calls/calls.statistics.tickets.js.map +0 -1
  79. package/dist/utils/statistics.aggregation.d.ts +0 -20
  80. package/dist/utils/statistics.aggregation.d.ts.map +0 -1
  81. package/dist/utils/statistics.aggregation.js +0 -43
  82. package/dist/utils/statistics.aggregation.js.map +0 -1
package/jest.config.js CHANGED
@@ -1,20 +1,20 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- module.exports = {
3
- preset: 'ts-jest',
4
- testEnvironment: 'node',
5
- roots: ['<rootDir>/src'],
6
- testPathIgnorePatterns: ['<rootDir>/src/__tests__/legacy/'],
7
- testMatch: ['**/__tests__/**/*.spec.ts', '**/*.spec.ts'],
8
- transform: {
9
- '^.+\\.tsx?$': ['ts-jest', {
10
- tsconfig: 'tsconfig.json',
11
- }],
12
- },
13
- setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
14
- maxWorkers: 1,
15
- verbose: true,
16
- forceExit: true,
17
- clearMocks: true,
18
- resetMocks: true,
19
- restoreMocks: true,
20
- };
1
+ /** @type {import('ts-jest').JestConfigWithTsJest} */
2
+ module.exports = {
3
+ preset: 'ts-jest',
4
+ testEnvironment: 'node',
5
+ roots: ['<rootDir>/src'],
6
+ testPathIgnorePatterns: ['<rootDir>/src/__tests__/legacy/'],
7
+ testMatch: ['**/__tests__/**/*.spec.ts', '**/*.spec.ts'],
8
+ transform: {
9
+ '^.+\\.tsx?$': ['ts-jest', {
10
+ tsconfig: 'tsconfig.json',
11
+ }],
12
+ },
13
+ setupFilesAfterEnv: ['<rootDir>/src/__tests__/setup.ts'],
14
+ maxWorkers: 1,
15
+ verbose: true,
16
+ forceExit: true,
17
+ clearMocks: true,
18
+ resetMocks: true,
19
+ restoreMocks: true,
20
+ };
package/package.json CHANGED
@@ -1,46 +1,46 @@
1
- {
2
- "name": "@talkpilot/core-db",
3
- "version": "1.3.9",
4
- "description": "Core database package for centralized connections and ORM integration.",
5
- "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
7
- "scripts": {
8
- "build": "tsc",
9
- "dev": "tsc --watch",
10
- "test": "jest",
11
- "lint": "eslint 'src/**/*.{ts,tsx}'",
12
- "format": "prettier --write \"src/**/*.{ts,tsx}\"",
13
- "prepare": "npm run build"
14
- },
15
- "keywords": [
16
- "database",
17
- "orm",
18
- "typescript",
19
- "postgres",
20
- "db-client"
21
- ],
22
- "author": "",
23
- "license": "MIT",
24
- "dependencies": {
25
- "dayjs": "^1.11.21",
26
- "express": "^4.18.0",
27
- "google-libphonenumber": "^3.2.0",
28
- "mongodb": "^6.11.0"
29
- },
30
- "devDependencies": {
31
- "@faker-js/faker": "^8.0.0",
32
- "@types/express": "^4.17.0",
33
- "@types/google-libphonenumber": "^7.4.0",
34
- "@types/jest": "^29.0.0",
35
- "@types/node": "^20.0.0",
36
- "fishery": "^2.4.0",
37
- "jest": "^29.0.0",
38
- "mongodb-memory-server": "^10.0.0",
39
- "prettier": "^3.8.2",
40
- "ts-jest": "^29.0.0",
41
- "typescript": "^5.0.0"
42
- },
43
- "publishConfig": {
44
- "access": "public"
45
- }
46
- }
1
+ {
2
+ "name": "@talkpilot/core-db",
3
+ "version": "1.3.12",
4
+ "description": "Core database package for centralized connections and ORM integration.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "dev": "tsc --watch",
10
+ "test": "jest",
11
+ "lint": "eslint 'src/**/*.{ts,tsx}'",
12
+ "format": "prettier --write \"src/**/*.{ts,tsx}\"",
13
+ "prepare": "npm run build"
14
+ },
15
+ "keywords": [
16
+ "database",
17
+ "orm",
18
+ "typescript",
19
+ "postgres",
20
+ "db-client"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "dependencies": {
25
+ "dayjs": "^1.11.21",
26
+ "express": "^4.18.0",
27
+ "google-libphonenumber": "^3.2.0",
28
+ "mongodb": "^6.11.0"
29
+ },
30
+ "devDependencies": {
31
+ "@faker-js/faker": "^8.0.0",
32
+ "@types/express": "^4.17.0",
33
+ "@types/google-libphonenumber": "^7.4.0",
34
+ "@types/jest": "^29.0.0",
35
+ "@types/node": "^20.0.0",
36
+ "fishery": "^2.4.0",
37
+ "jest": "^29.0.0",
38
+ "mongodb-memory-server": "^10.0.0",
39
+ "prettier": "^3.8.2",
40
+ "ts-jest": "^29.0.0",
41
+ "typescript": "^5.0.0"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ }
46
+ }
@@ -1,92 +1,94 @@
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
- });
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(
75
+ await getMuniIssueById(generateMuniIssueId().toString()),
76
+ ).toBeNull();
77
+ expect(await getMuniIssueById("not-a-valid-id")).toBeNull();
78
+ });
79
+ });
80
+
81
+ describe("getMuniIssueByJiraKey", () => {
82
+ it("should find the issue with the given Jira key", async () => {
83
+ await createMuniIssue(
84
+ createMuniIssueInput({ jira: { key: "CP-555" } }),
85
+ generateMuniIssueId(),
86
+ );
87
+
88
+ const found = await getMuniIssueByJiraKey("CP-555");
89
+
90
+ expect(found?.jira.key).toBe("CP-555");
91
+ expect(await getMuniIssueByJiraKey("CP-nope")).toBeNull();
92
+ });
93
+ });
94
+ });
@@ -1,68 +1,99 @@
1
- import {
2
- findCallSidDraftTicketCountsByCity,
3
- findCallSidTicketCountsByCity,
4
- getTicketsCollection,
5
- ticketStatsDateScopeFromYmd,
6
- } from "../../../index";
7
- import { createTicket } from "../../../test-utils/factories";
8
-
9
- const CITY = "tests" as const;
10
- const TZ = "Asia/Jerusalem";
11
- const mayScope = () => ticketStatsDateScopeFromYmd("2026-05-01", "2026-05-31", TZ);
12
-
13
- describe("tickets statistics getters", () => {
14
- it("should scope callSid lookups to the provided createdAt range", async () => {
15
- await getTicketsCollection().insertMany([
16
- createTicket({
17
- cityName: CITY,
18
- callSid: "CA-in-range",
19
- createdAt: new Date("2026-05-15T10:00:00.000Z"),
20
- updatedAt: new Date("2026-05-15T10:00:00.000Z"),
21
- }),
22
- createTicket({
23
- cityName: CITY,
24
- callSid: "CA-out-of-range",
25
- createdAt: new Date("2024-01-01T10:00:00.000Z"),
26
- updatedAt: new Date("2024-01-01T10:00:00.000Z"),
27
- }),
28
- ]);
29
- expect(await findCallSidTicketCountsByCity(CITY, mayScope())).toEqual(new Map([["CA-in-range", 1]]));
30
- });
31
-
32
- it("should treat whitespace-only event_subject_id as draft", async () => {
33
- await getTicketsCollection().insertMany([
34
- createTicket({
35
- cityName: CITY,
36
- callSid: "CA-draft-whitespace",
37
- createdAt: new Date("2026-05-15T10:00:00.000Z"),
38
- updatedAt: new Date("2026-05-15T10:00:00.000Z"),
39
- externalCallFields: { event_subject_id: " \t " },
40
- }),
41
- createTicket({
42
- cityName: CITY,
43
- callSid: "CA-not-draft",
44
- createdAt: new Date("2026-05-16T10:00:00.000Z"),
45
- updatedAt: new Date("2026-05-16T10:00:00.000Z"),
46
- externalCallFields: { event_subject_id: "100" },
47
- }),
48
- ]);
49
- expect(await findCallSidDraftTicketCountsByCity(CITY, mayScope())).toEqual(new Map([["CA-draft-whitespace", 1]]));
50
- });
51
- it("should count multiple tickets on the same callSid", async () => {
52
- await getTicketsCollection().insertMany([
53
- createTicket({
54
- cityName: CITY,
55
- callSid: "CA-multi",
56
- createdAt: new Date("2026-05-15T10:00:00.000Z"),
57
- updatedAt: new Date("2026-05-15T10:00:00.000Z"),
58
- }),
59
- createTicket({
60
- cityName: CITY,
61
- callSid: "CA-multi",
62
- createdAt: new Date("2026-05-15T11:00:00.000Z"),
63
- updatedAt: new Date("2026-05-15T11:00:00.000Z"),
64
- }),
65
- ]);
66
- expect(await findCallSidTicketCountsByCity(CITY, mayScope())).toEqual(new Map([["CA-multi", 2]]));
67
- });
68
- });
1
+ import {
2
+ findCallSidDraftTicketCountsByCity,
3
+ findCallSidTicketCountsByCity,
4
+ getTicketsCollection,
5
+ ticketStatsDateScopeFromYmd,
6
+ } from "../../../index";
7
+ import { createTicket } from "../../../test-utils/factories";
8
+
9
+ const CITY = "tests" as const;
10
+ const TZ = "Asia/Jerusalem";
11
+ const mayScope = () =>
12
+ ticketStatsDateScopeFromYmd("2026-05-01", "2026-05-31", TZ);
13
+
14
+ describe("tickets statistics getters", () => {
15
+ it("should scope callSid lookups to the provided createdAt range", async () => {
16
+ await getTicketsCollection().insertMany([
17
+ createTicket({
18
+ cityName: CITY,
19
+ callSid: "CA-in-range",
20
+ createdAt: new Date("2026-05-15T10:00:00.000Z"),
21
+ updatedAt: new Date("2026-05-15T10:00:00.000Z"),
22
+ }),
23
+ createTicket({
24
+ cityName: CITY,
25
+ callSid: "CA-out-of-range",
26
+ createdAt: new Date("2024-01-01T10:00:00.000Z"),
27
+ updatedAt: new Date("2024-01-01T10:00:00.000Z"),
28
+ }),
29
+ ]);
30
+ expect(await findCallSidTicketCountsByCity(CITY, mayScope())).toEqual(
31
+ new Map([["CA-in-range", 1]]),
32
+ );
33
+ });
34
+
35
+ it("should treat whitespace-only event_subject_id as draft for legacy tickets", async () => {
36
+ await getTicketsCollection().insertMany([
37
+ createTicket({
38
+ cityName: CITY,
39
+ callSid: "CA-draft-whitespace",
40
+ createdAt: new Date("2026-05-15T10:00:00.000Z"),
41
+ updatedAt: new Date("2026-05-15T10:00:00.000Z"),
42
+ externalCallFields: { event_subject_id: " \t " },
43
+ }),
44
+ createTicket({
45
+ cityName: CITY,
46
+ callSid: "CA-not-draft",
47
+ createdAt: new Date("2026-05-16T10:00:00.000Z"),
48
+ updatedAt: new Date("2026-05-16T10:00:00.000Z"),
49
+ externalCallFields: { event_subject_id: "100" },
50
+ }),
51
+ ]);
52
+ expect(await findCallSidDraftTicketCountsByCity(CITY, mayScope())).toEqual(
53
+ new Map([["CA-draft-whitespace", 1]]),
54
+ );
55
+ });
56
+
57
+ it("should use isDraft when set, overriding legacy heuristics", async () => {
58
+ await getTicketsCollection().insertMany([
59
+ createTicket({
60
+ cityName: CITY,
61
+ callSid: "CA-explicit-draft",
62
+ isDraft: true,
63
+ createdAt: new Date("2026-05-15T10:00:00.000Z"),
64
+ updatedAt: new Date("2026-05-15T10:00:00.000Z"),
65
+ externalCallFields: { event_subject_id: "100" },
66
+ }),
67
+ createTicket({
68
+ cityName: CITY,
69
+ callSid: "CA-explicit-not-draft",
70
+ isDraft: false,
71
+ createdAt: new Date("2026-05-16T10:00:00.000Z"),
72
+ updatedAt: new Date("2026-05-16T10:00:00.000Z"),
73
+ externalCallFields: {},
74
+ }),
75
+ ]);
76
+ expect(await findCallSidDraftTicketCountsByCity(CITY, mayScope())).toEqual(
77
+ new Map([["CA-explicit-draft", 1]]),
78
+ );
79
+ });
80
+ it("should count multiple tickets on the same callSid", async () => {
81
+ await getTicketsCollection().insertMany([
82
+ createTicket({
83
+ cityName: CITY,
84
+ callSid: "CA-multi",
85
+ createdAt: new Date("2026-05-15T10:00:00.000Z"),
86
+ updatedAt: new Date("2026-05-15T10:00:00.000Z"),
87
+ }),
88
+ createTicket({
89
+ cityName: CITY,
90
+ callSid: "CA-multi",
91
+ createdAt: new Date("2026-05-15T11:00:00.000Z"),
92
+ updatedAt: new Date("2026-05-15T11:00:00.000Z"),
93
+ }),
94
+ ]);
95
+ expect(await findCallSidTicketCountsByCity(CITY, mayScope())).toEqual(
96
+ new Map([["CA-multi", 2]]),
97
+ );
98
+ });
99
+ });
@@ -1,3 +1,9 @@
1
- export * from "./tickets.getters";
2
- export * from "./tickets.statistics.getters";
3
- export type { Ticket, TicketDoc, SubjectItem, TicketStatsDateScope, TicketSubjectRow } from "./tickets.types";
1
+ export * from "./tickets.getters";
2
+ export * from "./tickets.statistics.getters";
3
+ export type {
4
+ Ticket,
5
+ TicketDoc,
6
+ SubjectItem,
7
+ TicketStatsDateScope,
8
+ TicketSubjectRow,
9
+ } from "./tickets.types";