@voyantjs/crm 0.25.0 → 0.26.1
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/index.d.ts +18 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -6
- package/dist/route-runtime.d.ts +21 -0
- package/dist/route-runtime.d.ts.map +1 -0
- package/dist/route-runtime.js +28 -0
- package/dist/routes/accounts.d.ts +50 -2
- package/dist/routes/accounts.d.ts.map +1 -1
- package/dist/routes/index.d.ts +496 -2
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +2 -0
- package/dist/routes/person-documents.d.ts +458 -0
- package/dist/routes/person-documents.d.ts.map +1 -0
- package/dist/routes/person-documents.js +160 -0
- package/dist/schema-accounts.d.ts +367 -0
- package/dist/schema-accounts.d.ts.map +1 -1
- package/dist/schema-accounts.js +68 -2
- package/dist/schema-activities.js +2 -2
- package/dist/schema-relations.js +3 -3
- package/dist/schema-sales.js +2 -2
- package/dist/schema.d.ts +6 -6
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +6 -6
- package/dist/service/accounts-people.d.ts +49 -1
- package/dist/service/accounts-people.d.ts.map +1 -1
- package/dist/service/accounts-shared.d.ts +12 -0
- package/dist/service/accounts-shared.d.ts.map +1 -1
- package/dist/service/accounts-shared.js +4 -0
- package/dist/service/accounts.d.ts +49 -1
- package/dist/service/accounts.d.ts.map +1 -1
- package/dist/service/index.d.ts +1172 -1
- package/dist/service/index.d.ts.map +1 -1
- package/dist/service/index.js +3 -0
- package/dist/service/person-documents.d.ts +1188 -0
- package/dist/service/person-documents.d.ts.map +1 -0
- package/dist/service/person-documents.js +216 -0
- package/dist/validation.d.ts +168 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +68 -0
- package/package.json +6 -5
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person-documents.d.ts","sourceRoot":"","sources":["../../src/service/person-documents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { decryptOptionalJsonEnvelope } from "@voyantjs/utils";
|
|
2
|
+
import { and, asc, eq, gte, isNotNull, lte, sql } from "drizzle-orm";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { people, personDocuments } from "../schema.js";
|
|
5
|
+
/**
|
|
6
|
+
* Canonical plaintext shape for free-text PII blobs encrypted as
|
|
7
|
+
* `accessibilityEncrypted` / `dietaryEncrypted` / `loyaltyEncrypted` /
|
|
8
|
+
* `insuranceEncrypted` on `crm.people`. Wrapped so future fields can
|
|
9
|
+
* be added without breaking existing ciphertexts.
|
|
10
|
+
*
|
|
11
|
+
* Writers (customer-portal) and readers (crm + bookings snapshot)
|
|
12
|
+
* must use this shape — drift between sides means decryption fails
|
|
13
|
+
* silently and pre-fill stops working.
|
|
14
|
+
*/
|
|
15
|
+
export const personPiiBlobPlaintextSchema = z.object({ text: z.string() });
|
|
16
|
+
/**
|
|
17
|
+
* Canonical plaintext shape for `numberEncrypted` on
|
|
18
|
+
* `crm.person_documents`. Same compatibility contract as
|
|
19
|
+
* `personPiiBlobPlaintextSchema`.
|
|
20
|
+
*/
|
|
21
|
+
export const personDocumentNumberPlaintextSchema = z.object({ number: z.string() });
|
|
22
|
+
async function personExists(db, personId) {
|
|
23
|
+
const [row] = await db
|
|
24
|
+
.select({ id: people.id })
|
|
25
|
+
.from(people)
|
|
26
|
+
.where(eq(people.id, personId))
|
|
27
|
+
.limit(1);
|
|
28
|
+
return Boolean(row);
|
|
29
|
+
}
|
|
30
|
+
async function clearPrimaryForType(db, personId, type, exceptDocumentId) {
|
|
31
|
+
const conditions = [
|
|
32
|
+
eq(personDocuments.personId, personId),
|
|
33
|
+
eq(personDocuments.type, type),
|
|
34
|
+
eq(personDocuments.isPrimary, true),
|
|
35
|
+
];
|
|
36
|
+
if (exceptDocumentId) {
|
|
37
|
+
conditions.push(sql `${personDocuments.id} <> ${exceptDocumentId}`);
|
|
38
|
+
}
|
|
39
|
+
await db
|
|
40
|
+
.update(personDocuments)
|
|
41
|
+
.set({ isPrimary: false, updatedAt: new Date() })
|
|
42
|
+
.where(and(...conditions));
|
|
43
|
+
}
|
|
44
|
+
export const personDocumentsService = {
|
|
45
|
+
listPersonDocuments(db, personId, query) {
|
|
46
|
+
const conditions = [eq(personDocuments.personId, personId)];
|
|
47
|
+
if (query?.type)
|
|
48
|
+
conditions.push(eq(personDocuments.type, query.type));
|
|
49
|
+
if (query?.expiringBefore) {
|
|
50
|
+
conditions.push(isNotNull(personDocuments.expiryDate));
|
|
51
|
+
conditions.push(lte(personDocuments.expiryDate, query.expiringBefore));
|
|
52
|
+
}
|
|
53
|
+
const limit = query?.limit ?? 50;
|
|
54
|
+
const offset = query?.offset ?? 0;
|
|
55
|
+
return db
|
|
56
|
+
.select()
|
|
57
|
+
.from(personDocuments)
|
|
58
|
+
.where(and(...conditions))
|
|
59
|
+
.orderBy(asc(personDocuments.type), asc(personDocuments.createdAt))
|
|
60
|
+
.limit(limit)
|
|
61
|
+
.offset(offset);
|
|
62
|
+
},
|
|
63
|
+
async getPersonDocument(db, documentId) {
|
|
64
|
+
const [row] = await db
|
|
65
|
+
.select()
|
|
66
|
+
.from(personDocuments)
|
|
67
|
+
.where(eq(personDocuments.id, documentId))
|
|
68
|
+
.limit(1);
|
|
69
|
+
return row ?? null;
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* Returns the primary document of a given type for a person, or
|
|
73
|
+
* `null` if no primary is set. Used by booking-traveler create to
|
|
74
|
+
* snapshot the person's passport.
|
|
75
|
+
*/
|
|
76
|
+
async getPrimaryPersonDocument(db, personId, type) {
|
|
77
|
+
const [row] = await db
|
|
78
|
+
.select()
|
|
79
|
+
.from(personDocuments)
|
|
80
|
+
.where(and(eq(personDocuments.personId, personId), eq(personDocuments.type, type), eq(personDocuments.isPrimary, true)))
|
|
81
|
+
.limit(1);
|
|
82
|
+
return row ?? null;
|
|
83
|
+
},
|
|
84
|
+
async createPersonDocument(db, personId, data) {
|
|
85
|
+
if (!(await personExists(db, personId)))
|
|
86
|
+
return null;
|
|
87
|
+
return db.transaction(async (tx) => {
|
|
88
|
+
if (data.isPrimary) {
|
|
89
|
+
await clearPrimaryForType(tx, personId, data.type);
|
|
90
|
+
}
|
|
91
|
+
const [row] = await tx
|
|
92
|
+
.insert(personDocuments)
|
|
93
|
+
.values({ ...data, personId })
|
|
94
|
+
.returning();
|
|
95
|
+
return row ?? null;
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
async updatePersonDocument(db, documentId, data) {
|
|
99
|
+
return db.transaction(async (tx) => {
|
|
100
|
+
const [existing] = await tx
|
|
101
|
+
.select()
|
|
102
|
+
.from(personDocuments)
|
|
103
|
+
.where(eq(personDocuments.id, documentId))
|
|
104
|
+
.limit(1);
|
|
105
|
+
if (!existing)
|
|
106
|
+
return null;
|
|
107
|
+
// Clear prior primary of the *target* type whenever the row
|
|
108
|
+
// will end up primary after this update — including the case
|
|
109
|
+
// where `isPrimary` is unchanged but `type` is being switched
|
|
110
|
+
// and the existing row is already primary. Without this, the
|
|
111
|
+
// partial unique index `(person_id, type) WHERE is_primary`
|
|
112
|
+
// rejects type-only edits on a primary doc.
|
|
113
|
+
const effectiveIsPrimary = data.isPrimary ?? existing.isPrimary;
|
|
114
|
+
if (effectiveIsPrimary) {
|
|
115
|
+
const targetType = data.type ?? existing.type;
|
|
116
|
+
await clearPrimaryForType(tx, existing.personId, targetType, documentId);
|
|
117
|
+
}
|
|
118
|
+
const [row] = await tx
|
|
119
|
+
.update(personDocuments)
|
|
120
|
+
.set({ ...data, updatedAt: new Date() })
|
|
121
|
+
.where(eq(personDocuments.id, documentId))
|
|
122
|
+
.returning();
|
|
123
|
+
return row ?? null;
|
|
124
|
+
});
|
|
125
|
+
},
|
|
126
|
+
async deletePersonDocument(db, documentId) {
|
|
127
|
+
const [row] = await db
|
|
128
|
+
.delete(personDocuments)
|
|
129
|
+
.where(eq(personDocuments.id, documentId))
|
|
130
|
+
.returning({ id: personDocuments.id });
|
|
131
|
+
return row ?? null;
|
|
132
|
+
},
|
|
133
|
+
/**
|
|
134
|
+
* Atomically promotes a document to primary, demoting any prior
|
|
135
|
+
* primary of the same type for the same person.
|
|
136
|
+
*/
|
|
137
|
+
async setPrimaryPersonDocument(db, documentId) {
|
|
138
|
+
return db.transaction(async (tx) => {
|
|
139
|
+
const [existing] = await tx
|
|
140
|
+
.select()
|
|
141
|
+
.from(personDocuments)
|
|
142
|
+
.where(eq(personDocuments.id, documentId))
|
|
143
|
+
.limit(1);
|
|
144
|
+
if (!existing)
|
|
145
|
+
return null;
|
|
146
|
+
await clearPrimaryForType(tx, existing.personId, existing.type, documentId);
|
|
147
|
+
const [row] = await tx
|
|
148
|
+
.update(personDocuments)
|
|
149
|
+
.set({ isPrimary: true, updatedAt: new Date() })
|
|
150
|
+
.where(eq(personDocuments.id, documentId))
|
|
151
|
+
.returning();
|
|
152
|
+
return row ?? null;
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
/**
|
|
156
|
+
* Plaintext snapshot of the fields a booking-traveler creation
|
|
157
|
+
* pulls from a person record: dietary, accessibility, primary
|
|
158
|
+
* passport (number + expiry + country + authority + provenance id),
|
|
159
|
+
* and date-of-birth from `people.birthday`.
|
|
160
|
+
*
|
|
161
|
+
* Caller passes a KMS provider so decryption happens in-process.
|
|
162
|
+
* Missing person → returns null. Missing document or blob → that
|
|
163
|
+
* field returns null in the snapshot.
|
|
164
|
+
*/
|
|
165
|
+
async loadPersonTravelSnapshot(db, personId, options) {
|
|
166
|
+
const [personRow] = await db
|
|
167
|
+
.select({
|
|
168
|
+
birthday: people.birthday,
|
|
169
|
+
accessibilityEncrypted: people.accessibilityEncrypted,
|
|
170
|
+
dietaryEncrypted: people.dietaryEncrypted,
|
|
171
|
+
})
|
|
172
|
+
.from(people)
|
|
173
|
+
.where(eq(people.id, personId))
|
|
174
|
+
.limit(1);
|
|
175
|
+
if (!personRow)
|
|
176
|
+
return null;
|
|
177
|
+
const keyRef = options.keyRef ?? { keyType: "people" };
|
|
178
|
+
const [accessibilityBlob, dietaryBlob, primaryPassport] = await Promise.all([
|
|
179
|
+
decryptOptionalJsonEnvelope(options.kms, keyRef, personRow.accessibilityEncrypted, personPiiBlobPlaintextSchema),
|
|
180
|
+
decryptOptionalJsonEnvelope(options.kms, keyRef, personRow.dietaryEncrypted, personPiiBlobPlaintextSchema),
|
|
181
|
+
personDocumentsService.getPrimaryPersonDocument(db, personId, "passport"),
|
|
182
|
+
]);
|
|
183
|
+
let passportNumber = null;
|
|
184
|
+
if (primaryPassport?.numberEncrypted) {
|
|
185
|
+
const decrypted = await decryptOptionalJsonEnvelope(options.kms, keyRef, primaryPassport.numberEncrypted, personDocumentNumberPlaintextSchema);
|
|
186
|
+
passportNumber = decrypted?.number ?? null;
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
dateOfBirth: personRow.birthday ?? null,
|
|
190
|
+
dietaryRequirements: dietaryBlob?.text ?? null,
|
|
191
|
+
accessibilityNeeds: accessibilityBlob?.text ?? null,
|
|
192
|
+
passportNumber,
|
|
193
|
+
passportExpiry: primaryPassport?.expiryDate ?? null,
|
|
194
|
+
passportIssuingCountry: primaryPassport?.issuingCountry ?? null,
|
|
195
|
+
passportIssuingAuthority: primaryPassport?.issuingAuthority ?? null,
|
|
196
|
+
passportPersonDocumentId: primaryPassport?.id ?? null,
|
|
197
|
+
};
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* Documents whose `expiryDate` falls within the next `daysAhead`
|
|
201
|
+
* days. Used by the future `crm.detect-expiring-documents` cron;
|
|
202
|
+
* shipped now since the helper is free.
|
|
203
|
+
*/
|
|
204
|
+
listExpiringPersonDocuments(db, daysAhead = 90) {
|
|
205
|
+
const today = new Date();
|
|
206
|
+
const horizon = new Date(today);
|
|
207
|
+
horizon.setUTCDate(horizon.getUTCDate() + daysAhead);
|
|
208
|
+
const todayIso = today.toISOString().slice(0, 10);
|
|
209
|
+
const horizonIso = horizon.toISOString().slice(0, 10);
|
|
210
|
+
return db
|
|
211
|
+
.select()
|
|
212
|
+
.from(personDocuments)
|
|
213
|
+
.where(and(isNotNull(personDocuments.expiryDate), gte(personDocuments.expiryDate, todayIso), lte(personDocuments.expiryDate, horizonIso)))
|
|
214
|
+
.orderBy(asc(personDocuments.expiryDate));
|
|
215
|
+
},
|
|
216
|
+
};
|
package/dist/validation.d.ts
CHANGED
|
@@ -205,6 +205,18 @@ export declare const personCoreSchema: z.ZodObject<{
|
|
|
205
205
|
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
206
206
|
birthday: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
207
207
|
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
208
|
+
accessibilityEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
209
|
+
enc: z.ZodString;
|
|
210
|
+
}, z.core.$strip>>>;
|
|
211
|
+
dietaryEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
212
|
+
enc: z.ZodString;
|
|
213
|
+
}, z.core.$strip>>>;
|
|
214
|
+
loyaltyEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
215
|
+
enc: z.ZodString;
|
|
216
|
+
}, z.core.$strip>>>;
|
|
217
|
+
insuranceEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
218
|
+
enc: z.ZodString;
|
|
219
|
+
}, z.core.$strip>>>;
|
|
208
220
|
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
209
221
|
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
210
222
|
website: z.ZodPipe<z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
@@ -239,6 +251,18 @@ export declare const insertPersonSchema: z.ZodObject<{
|
|
|
239
251
|
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
240
252
|
birthday: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
241
253
|
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
254
|
+
accessibilityEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
255
|
+
enc: z.ZodString;
|
|
256
|
+
}, z.core.$strip>>>;
|
|
257
|
+
dietaryEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
258
|
+
enc: z.ZodString;
|
|
259
|
+
}, z.core.$strip>>>;
|
|
260
|
+
loyaltyEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
261
|
+
enc: z.ZodString;
|
|
262
|
+
}, z.core.$strip>>>;
|
|
263
|
+
insuranceEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
264
|
+
enc: z.ZodString;
|
|
265
|
+
}, z.core.$strip>>>;
|
|
242
266
|
email: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
243
267
|
phone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
244
268
|
website: z.ZodPipe<z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>, z.ZodTransform<string | null, string | null | undefined>>;
|
|
@@ -273,6 +297,18 @@ export declare const updatePersonSchema: z.ZodObject<{
|
|
|
273
297
|
tags: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
|
|
274
298
|
birthday: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
275
299
|
notes: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
300
|
+
accessibilityEncrypted: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
301
|
+
enc: z.ZodString;
|
|
302
|
+
}, z.core.$strip>>>>;
|
|
303
|
+
dietaryEncrypted: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
304
|
+
enc: z.ZodString;
|
|
305
|
+
}, z.core.$strip>>>>;
|
|
306
|
+
loyaltyEncrypted: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
307
|
+
enc: z.ZodString;
|
|
308
|
+
}, z.core.$strip>>>>;
|
|
309
|
+
insuranceEncrypted: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
310
|
+
enc: z.ZodString;
|
|
311
|
+
}, z.core.$strip>>>>;
|
|
276
312
|
email: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
277
313
|
phone: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
278
314
|
website: z.ZodOptional<z.ZodPipe<z.ZodUnion<[z.ZodOptional<z.ZodNullable<z.ZodString>>, z.ZodLiteral<"">]>, z.ZodTransform<string | null, string | null | undefined>>>;
|
|
@@ -816,6 +852,138 @@ export declare const customFieldValueListQuerySchema: z.ZodObject<{
|
|
|
816
852
|
entityId: z.ZodOptional<z.ZodString>;
|
|
817
853
|
definitionId: z.ZodOptional<z.ZodString>;
|
|
818
854
|
}, z.core.$strip>;
|
|
855
|
+
export declare const personDocumentTypeSchema: z.ZodEnum<{
|
|
856
|
+
passport: "passport";
|
|
857
|
+
visa: "visa";
|
|
858
|
+
other: "other";
|
|
859
|
+
id_card: "id_card";
|
|
860
|
+
driver_license: "driver_license";
|
|
861
|
+
}>;
|
|
862
|
+
export declare const personDocumentCoreSchema: z.ZodObject<{
|
|
863
|
+
type: z.ZodEnum<{
|
|
864
|
+
passport: "passport";
|
|
865
|
+
visa: "visa";
|
|
866
|
+
other: "other";
|
|
867
|
+
id_card: "id_card";
|
|
868
|
+
driver_license: "driver_license";
|
|
869
|
+
}>;
|
|
870
|
+
numberEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
871
|
+
enc: z.ZodString;
|
|
872
|
+
}, z.core.$strip>>>;
|
|
873
|
+
issuingAuthority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
874
|
+
issuingCountry: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
875
|
+
issueDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
876
|
+
expiryDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
877
|
+
attachmentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
878
|
+
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
879
|
+
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
880
|
+
metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
881
|
+
}, z.core.$strip>;
|
|
882
|
+
export declare const insertPersonDocumentSchema: z.ZodObject<{
|
|
883
|
+
type: z.ZodEnum<{
|
|
884
|
+
passport: "passport";
|
|
885
|
+
visa: "visa";
|
|
886
|
+
other: "other";
|
|
887
|
+
id_card: "id_card";
|
|
888
|
+
driver_license: "driver_license";
|
|
889
|
+
}>;
|
|
890
|
+
numberEncrypted: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
891
|
+
enc: z.ZodString;
|
|
892
|
+
}, z.core.$strip>>>;
|
|
893
|
+
issuingAuthority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
894
|
+
issuingCountry: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
895
|
+
issueDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
896
|
+
expiryDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
897
|
+
attachmentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
898
|
+
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
899
|
+
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
900
|
+
metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
901
|
+
}, z.core.$strip>;
|
|
902
|
+
export declare const updatePersonDocumentSchema: z.ZodObject<{
|
|
903
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
904
|
+
passport: "passport";
|
|
905
|
+
visa: "visa";
|
|
906
|
+
other: "other";
|
|
907
|
+
id_card: "id_card";
|
|
908
|
+
driver_license: "driver_license";
|
|
909
|
+
}>>;
|
|
910
|
+
numberEncrypted: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
911
|
+
enc: z.ZodString;
|
|
912
|
+
}, z.core.$strip>>>>;
|
|
913
|
+
issuingAuthority: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
914
|
+
issuingCountry: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
915
|
+
issueDate: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
916
|
+
expiryDate: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
917
|
+
attachmentId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
918
|
+
isPrimary: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
919
|
+
notes: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
920
|
+
metadata: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
|
|
921
|
+
}, z.core.$strip>;
|
|
922
|
+
export declare const personDocumentListQuerySchema: z.ZodObject<{
|
|
923
|
+
limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
924
|
+
offset: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
925
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
926
|
+
passport: "passport";
|
|
927
|
+
visa: "visa";
|
|
928
|
+
other: "other";
|
|
929
|
+
id_card: "id_card";
|
|
930
|
+
driver_license: "driver_license";
|
|
931
|
+
}>>;
|
|
932
|
+
expiringBefore: z.ZodOptional<z.ZodString>;
|
|
933
|
+
}, z.core.$strip>;
|
|
934
|
+
export declare const insertPersonDocumentFromPlaintextSchema: z.ZodObject<{
|
|
935
|
+
type: z.ZodEnum<{
|
|
936
|
+
passport: "passport";
|
|
937
|
+
visa: "visa";
|
|
938
|
+
other: "other";
|
|
939
|
+
id_card: "id_card";
|
|
940
|
+
driver_license: "driver_license";
|
|
941
|
+
}>;
|
|
942
|
+
number: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
943
|
+
issuingAuthority: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
944
|
+
issuingCountry: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
945
|
+
issueDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
946
|
+
expiryDate: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
947
|
+
attachmentId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
948
|
+
isPrimary: z.ZodDefault<z.ZodBoolean>;
|
|
949
|
+
notes: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
950
|
+
metadata: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
951
|
+
}, z.core.$strip>;
|
|
952
|
+
export declare const updatePersonDocumentFromPlaintextSchema: z.ZodObject<{
|
|
953
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
954
|
+
passport: "passport";
|
|
955
|
+
visa: "visa";
|
|
956
|
+
other: "other";
|
|
957
|
+
id_card: "id_card";
|
|
958
|
+
driver_license: "driver_license";
|
|
959
|
+
}>>;
|
|
960
|
+
number: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
961
|
+
issuingAuthority: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
962
|
+
issuingCountry: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
963
|
+
issueDate: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
964
|
+
expiryDate: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
965
|
+
attachmentId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
966
|
+
isPrimary: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
967
|
+
notes: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
|
|
968
|
+
metadata: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>>>;
|
|
969
|
+
}, z.core.$strip>;
|
|
970
|
+
/**
|
|
971
|
+
* Plaintext input shape for the four free-text PII slots on
|
|
972
|
+
* `crm.people` (accessibility / dietary / loyalty / insurance). The
|
|
973
|
+
* admin endpoint encrypts each provided value server-side; `null`
|
|
974
|
+
* clears the slot.
|
|
975
|
+
*/
|
|
976
|
+
export declare const updatePersonProfilePiiSchema: z.ZodObject<{
|
|
977
|
+
accessibility: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
978
|
+
dietary: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
979
|
+
loyalty: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
980
|
+
insurance: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
981
|
+
}, z.core.$strip>;
|
|
982
|
+
export type PersonDocumentInput = z.infer<typeof insertPersonDocumentSchema>;
|
|
983
|
+
export type PersonDocumentUpdate = z.infer<typeof updatePersonDocumentSchema>;
|
|
984
|
+
export type PersonDocumentPlaintextInput = z.infer<typeof insertPersonDocumentFromPlaintextSchema>;
|
|
985
|
+
export type PersonDocumentPlaintextUpdate = z.infer<typeof updatePersonDocumentFromPlaintextSchema>;
|
|
986
|
+
export type UpdatePersonProfilePiiInput = z.infer<typeof updatePersonProfilePiiSchema>;
|
|
819
987
|
export declare const insertPersonNoteSchema: z.ZodObject<{
|
|
820
988
|
content: z.ZodString;
|
|
821
989
|
}, z.core.$strip>;
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/dist/validation.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { kmsEnvelopeSchema } from "@voyantjs/db/schema/iam/kms";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
const paginationSchema = z.object({
|
|
3
4
|
limit: z.coerce.number().int().min(1).max(200).default(50),
|
|
@@ -101,6 +102,11 @@ export const personCoreSchema = z.object({
|
|
|
101
102
|
tags: z.array(z.string()).default([]),
|
|
102
103
|
birthday: z.string().date().nullable().optional(),
|
|
103
104
|
notes: z.string().nullable().optional(),
|
|
105
|
+
// Encrypted PII slots (canonical store; documents live in person_documents)
|
|
106
|
+
accessibilityEncrypted: kmsEnvelopeSchema.optional(),
|
|
107
|
+
dietaryEncrypted: kmsEnvelopeSchema.optional(),
|
|
108
|
+
loyaltyEncrypted: kmsEnvelopeSchema.optional(),
|
|
109
|
+
insuranceEncrypted: kmsEnvelopeSchema.optional(),
|
|
104
110
|
// Inline identity fields — synced to identity module on create/update
|
|
105
111
|
email: z.string().email().nullable().optional(),
|
|
106
112
|
phone: z.string().nullable().optional(),
|
|
@@ -279,6 +285,68 @@ export const customFieldValueListQuerySchema = paginationSchema.extend({
|
|
|
279
285
|
entityId: z.string().optional(),
|
|
280
286
|
definitionId: z.string().optional(),
|
|
281
287
|
});
|
|
288
|
+
// ---------- person documents ----------
|
|
289
|
+
export const personDocumentTypeSchema = z.enum([
|
|
290
|
+
"passport",
|
|
291
|
+
"id_card",
|
|
292
|
+
"driver_license",
|
|
293
|
+
"visa",
|
|
294
|
+
"other",
|
|
295
|
+
]);
|
|
296
|
+
export const personDocumentCoreSchema = z.object({
|
|
297
|
+
type: personDocumentTypeSchema,
|
|
298
|
+
numberEncrypted: kmsEnvelopeSchema.optional(),
|
|
299
|
+
issuingAuthority: z.string().nullable().optional(),
|
|
300
|
+
issuingCountry: z.string().nullable().optional(),
|
|
301
|
+
issueDate: z.string().date().nullable().optional(),
|
|
302
|
+
expiryDate: z.string().date().nullable().optional(),
|
|
303
|
+
attachmentId: z.string().nullable().optional(),
|
|
304
|
+
isPrimary: z.boolean().default(false),
|
|
305
|
+
notes: z.string().nullable().optional(),
|
|
306
|
+
metadata: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
307
|
+
});
|
|
308
|
+
export const insertPersonDocumentSchema = personDocumentCoreSchema;
|
|
309
|
+
export const updatePersonDocumentSchema = personDocumentCoreSchema.partial();
|
|
310
|
+
export const personDocumentListQuerySchema = paginationSchema.extend({
|
|
311
|
+
type: personDocumentTypeSchema.optional(),
|
|
312
|
+
expiringBefore: z.string().date().optional(),
|
|
313
|
+
});
|
|
314
|
+
/**
|
|
315
|
+
* Plaintext input shape for admin-facing create/update endpoints —
|
|
316
|
+
* the route encrypts `number` server-side using the people KMS key
|
|
317
|
+
* before persisting. Operator UIs prefer this over hand-encrypted
|
|
318
|
+
* envelopes.
|
|
319
|
+
*/
|
|
320
|
+
const personDocumentPlaintextCoreSchema = z.object({
|
|
321
|
+
type: personDocumentTypeSchema,
|
|
322
|
+
number: z.string().max(255).nullable().optional(),
|
|
323
|
+
issuingAuthority: z.string().nullable().optional(),
|
|
324
|
+
issuingCountry: z.string().nullable().optional(),
|
|
325
|
+
issueDate: z.string().date().nullable().optional(),
|
|
326
|
+
expiryDate: z.string().date().nullable().optional(),
|
|
327
|
+
attachmentId: z.string().nullable().optional(),
|
|
328
|
+
isPrimary: z.boolean().default(false),
|
|
329
|
+
notes: z.string().nullable().optional(),
|
|
330
|
+
metadata: z.record(z.string(), z.unknown()).nullable().optional(),
|
|
331
|
+
});
|
|
332
|
+
export const insertPersonDocumentFromPlaintextSchema = personDocumentPlaintextCoreSchema;
|
|
333
|
+
export const updatePersonDocumentFromPlaintextSchema = personDocumentPlaintextCoreSchema.partial();
|
|
334
|
+
/**
|
|
335
|
+
* Plaintext input shape for the four free-text PII slots on
|
|
336
|
+
* `crm.people` (accessibility / dietary / loyalty / insurance). The
|
|
337
|
+
* admin endpoint encrypts each provided value server-side; `null`
|
|
338
|
+
* clears the slot.
|
|
339
|
+
*/
|
|
340
|
+
export const updatePersonProfilePiiSchema = z
|
|
341
|
+
.object({
|
|
342
|
+
accessibility: z.string().max(4000).nullable().optional(),
|
|
343
|
+
dietary: z.string().max(4000).nullable().optional(),
|
|
344
|
+
loyalty: z.string().max(4000).nullable().optional(),
|
|
345
|
+
insurance: z.string().max(4000).nullable().optional(),
|
|
346
|
+
})
|
|
347
|
+
.refine((value) => Object.keys(value).length > 0, {
|
|
348
|
+
message: "At least one field must be provided",
|
|
349
|
+
});
|
|
282
350
|
// ---------- notes ----------
|
|
283
351
|
export const insertPersonNoteSchema = z.object({
|
|
284
352
|
content: z.string().min(1).max(10000),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/crm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
"drizzle-orm": "^0.45.2",
|
|
35
35
|
"hono": "^4.12.10",
|
|
36
36
|
"zod": "^4.3.6",
|
|
37
|
-
"@voyantjs/core": "0.
|
|
38
|
-
"@voyantjs/db": "0.
|
|
39
|
-
"@voyantjs/hono": "0.
|
|
40
|
-
"@voyantjs/identity": "0.
|
|
37
|
+
"@voyantjs/core": "0.26.1",
|
|
38
|
+
"@voyantjs/db": "0.26.1",
|
|
39
|
+
"@voyantjs/hono": "0.26.1",
|
|
40
|
+
"@voyantjs/identity": "0.26.1",
|
|
41
|
+
"@voyantjs/utils": "0.26.1"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"typescript": "^6.0.2",
|