@qcobro/common 1.19.5 → 1.20.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.
- package/dist/schemas/email.d.ts +10 -5
- package/dist/schemas/email.d.ts.map +1 -1
- package/dist/schemas/email.js +10 -5
- package/dist/schemas/email.js.map +1 -1
- package/dist/types/email.d.ts +4 -4
- package/dist/types/email.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/normalizePhone.d.ts +12 -0
- package/dist/utils/normalizePhone.d.ts.map +1 -0
- package/dist/utils/normalizePhone.js +17 -0
- package/dist/utils/normalizePhone.js.map +1 -0
- package/package.json +2 -1
package/dist/schemas/email.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* The structured decision the EMAIL
|
|
4
|
-
* model can't drive the loop with malformed output. `outcome` mirrors the
|
|
5
|
-
* outcomes; `objective` carries promise details when the reply implies one.
|
|
3
|
+
* The structured decision the EMAIL/WHATSAPP autopilots return for an inbound reply.
|
|
4
|
+
* Validated so a model can't drive the loop with malformed output. `outcome` mirrors the
|
|
5
|
+
* contact-log outcomes; `objective` carries promise details when the reply implies one.
|
|
6
|
+
*
|
|
7
|
+
* `replyBody`/`outcome`/`objective` use `.nullish()`, not `.optional()`: Gemini's
|
|
8
|
+
* `responseMimeType: "application/json"` mode fills every key it considered rather than
|
|
9
|
+
* omitting the ones that don't apply, so a plain reply with no outcome comes back as
|
|
10
|
+
* `"outcome": null` (not an absent key) — `.optional()` alone rejects that.
|
|
6
11
|
*/
|
|
7
12
|
export declare const emailAutopilotDecisionSchema: z.ZodObject<{
|
|
8
13
|
action: z.ZodEnum<{
|
|
@@ -11,8 +16,8 @@ export declare const emailAutopilotDecisionSchema: z.ZodObject<{
|
|
|
11
16
|
resolve: "resolve";
|
|
12
17
|
escalate: "escalate";
|
|
13
18
|
}>;
|
|
14
|
-
replyBody: z.ZodOptional<z.ZodString
|
|
15
|
-
outcome: z.ZodOptional<z.ZodString
|
|
19
|
+
replyBody: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
20
|
+
outcome: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
21
|
objective: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
17
22
|
type: z.ZodString;
|
|
18
23
|
amount: z.ZodOptional<z.ZodNumber>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/schemas/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/schemas/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;iBAYvC,CAAC;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;iBAS7B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
package/dist/schemas/email.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
* The structured decision the EMAIL
|
|
4
|
-
* model can't drive the loop with malformed output. `outcome` mirrors the
|
|
5
|
-
* outcomes; `objective` carries promise details when the reply implies one.
|
|
3
|
+
* The structured decision the EMAIL/WHATSAPP autopilots return for an inbound reply.
|
|
4
|
+
* Validated so a model can't drive the loop with malformed output. `outcome` mirrors the
|
|
5
|
+
* contact-log outcomes; `objective` carries promise details when the reply implies one.
|
|
6
|
+
*
|
|
7
|
+
* `replyBody`/`outcome`/`objective` use `.nullish()`, not `.optional()`: Gemini's
|
|
8
|
+
* `responseMimeType: "application/json"` mode fills every key it considered rather than
|
|
9
|
+
* omitting the ones that don't apply, so a plain reply with no outcome comes back as
|
|
10
|
+
* `"outcome": null` (not an absent key) — `.optional()` alone rejects that.
|
|
6
11
|
*/
|
|
7
12
|
export const emailAutopilotDecisionSchema = z.object({
|
|
8
13
|
action: z.enum(["reply", "ignore", "resolve", "escalate"]),
|
|
9
|
-
replyBody: z.string().
|
|
10
|
-
outcome: z.string().
|
|
14
|
+
replyBody: z.string().nullish(),
|
|
15
|
+
outcome: z.string().nullish(),
|
|
11
16
|
objective: z
|
|
12
17
|
.object({
|
|
13
18
|
type: z.string(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/schemas/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB
|
|
1
|
+
{"version":3,"file":"email.js","sourceRoot":"","sources":["../../src/schemas/email.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;IAC1D,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE;IAC7B,SAAS,EAAE,CAAC;SACT,MAAM,CAAC;QACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC7B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KAC5B,CAAC;SACD,OAAO,EAAE;CACb,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;IACnC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAChC,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC1C,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC"}
|
package/dist/types/email.d.ts
CHANGED
|
@@ -27,10 +27,10 @@ export type EmailAutopilotAction = "reply" | "ignore" | "resolve" | "escalate";
|
|
|
27
27
|
/** Structured result of the autopilot decision step over a thread. */
|
|
28
28
|
export interface EmailAutopilotDecision {
|
|
29
29
|
action: EmailAutopilotAction;
|
|
30
|
-
/** Present when `action === "reply"`. */
|
|
31
|
-
replyBody?: string;
|
|
32
|
-
/** Outcome to record on the gestión, when the reply implies one. */
|
|
33
|
-
outcome?: string;
|
|
30
|
+
/** Present when `action === "reply"`. Gemini's JSON mode may send `null` instead of omitting. */
|
|
31
|
+
replyBody?: string | null;
|
|
32
|
+
/** Outcome to record on the gestión, when the reply implies one. May come back `null`. */
|
|
33
|
+
outcome?: string | null;
|
|
34
34
|
/** Promise/objective details to capture, when applicable. */
|
|
35
35
|
objective?: {
|
|
36
36
|
type: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/types/email.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sCAAsC;AACtC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,UAAU,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/E,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/types/email.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sCAAsC;AACtC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,UAAU,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,WAAW;IAC1B,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,gEAAgE;AAChE,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/E,sEAAsE;AACtE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,6DAA6D;IAC7D,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACvF;AAED,gDAAgD;AAChD,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,kBAAkB,EAAE,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;CACrE;AAED,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,yFAAyF;IACzF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { withErrorHandlingAndValidation } from "./withErrorHandlingAndValidation
|
|
|
2
2
|
export { renderTemplate, extractTemplateTokens, buildOutreachContext, pickRandomNumber, snakeToCamel, renderWhatsAppTemplate } from "./outreach.js";
|
|
3
3
|
export { localParts, localDateString, isSameLocalDay, localWeekdayISO, localTimeHHMM, isWithinScheduleWindow, type ScheduleWindow } from "./time.js";
|
|
4
4
|
export { bucketOf, perTickCapacity, type PacingBucket } from "./pacing.js";
|
|
5
|
+
export { normalizePhoneE164 } from "./normalizePhone.js";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,sBAAsB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,sBAAsB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,KAAK,cAAc,EACpB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/utils/index.js
CHANGED
|
@@ -2,4 +2,5 @@ export { withErrorHandlingAndValidation } from "./withErrorHandlingAndValidation
|
|
|
2
2
|
export { renderTemplate, extractTemplateTokens, buildOutreachContext, pickRandomNumber, snakeToCamel, renderWhatsAppTemplate } from "./outreach.js";
|
|
3
3
|
export { localParts, localDateString, isSameLocalDay, localWeekdayISO, localTimeHHMM, isWithinScheduleWindow } from "./time.js";
|
|
4
4
|
export { bucketOf, perTickCapacity } from "./pacing.js";
|
|
5
|
+
export { normalizePhoneE164 } from "./normalizePhone.js";
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,sBAAsB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,aAAa,EACb,sBAAsB,EAEvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,EACZ,sBAAsB,EACvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EACd,eAAe,EACf,aAAa,EACb,sBAAsB,EAEvB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAqB,MAAM,aAAa,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes a phone number to E.164 (e.g. "+18091234567"), accepting input with or
|
|
3
|
+
* without a leading `+`. Returns null instead of throwing when the number isn't a valid
|
|
4
|
+
* international number — callers may be comparing against untrusted webhook input, where
|
|
5
|
+
* "doesn't parse" should mean "no match," not a crash.
|
|
6
|
+
*
|
|
7
|
+
* Ported from mikro's `validatePhone` (mods/common/src/utils/validatePhone.ts), which
|
|
8
|
+
* throws a ValidationError instead; that stricter variant belongs at input boundaries
|
|
9
|
+
* (e.g. account import) and isn't needed here yet.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizePhoneE164(input: string): string | null;
|
|
12
|
+
//# sourceMappingURL=normalizePhone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizePhone.d.ts","sourceRoot":"","sources":["../../src/utils/normalizePhone.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAI/D"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { phone } from "phone";
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes a phone number to E.164 (e.g. "+18091234567"), accepting input with or
|
|
4
|
+
* without a leading `+`. Returns null instead of throwing when the number isn't a valid
|
|
5
|
+
* international number — callers may be comparing against untrusted webhook input, where
|
|
6
|
+
* "doesn't parse" should mean "no match," not a crash.
|
|
7
|
+
*
|
|
8
|
+
* Ported from mikro's `validatePhone` (mods/common/src/utils/validatePhone.ts), which
|
|
9
|
+
* throws a ValidationError instead; that stricter variant belongs at input boundaries
|
|
10
|
+
* (e.g. account import) and isn't needed here yet.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizePhoneE164(input) {
|
|
13
|
+
const candidate = input.startsWith("+") ? input : `+${input}`;
|
|
14
|
+
const result = phone(candidate);
|
|
15
|
+
return result.isValid ? result.phoneNumber : null;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=normalizePhone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalizePhone.js","sourceRoot":"","sources":["../../src/utils/normalizePhone.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;IAC9D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qcobro/common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist"
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"handlebars": "^4.7.9",
|
|
26
26
|
"pg": "^8.13.0",
|
|
27
|
+
"phone": "^3.1.71",
|
|
27
28
|
"zod": "^4.0.0"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|