@voyantjs/legal 0.2.0 → 0.3.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-core.d.ts","sourceRoot":"","sources":["../../src/policies/service-core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,357 @@
1
+ import { and, desc, eq, gte, ilike, lte, or, sql } from "drizzle-orm";
2
+ import { policies, policyAcceptances, policyAssignments, policyRules, policyVersions, } from "./schema.js";
3
+ import { evaluateCancellationPolicy, paginate, toDateString, } from "./service-shared.js";
4
+ export const policiesCoreService = {
5
+ async listPolicies(db, query) {
6
+ const conditions = [];
7
+ if (query.kind)
8
+ conditions.push(eq(policies.kind, query.kind));
9
+ if (query.language)
10
+ conditions.push(eq(policies.language, query.language));
11
+ if (query.search) {
12
+ const term = `%${query.search}%`;
13
+ conditions.push(or(ilike(policies.name, term), ilike(policies.slug, term), ilike(policies.description, term)));
14
+ }
15
+ const where = conditions.length ? and(...conditions) : undefined;
16
+ return paginate(db
17
+ .select()
18
+ .from(policies)
19
+ .where(where)
20
+ .limit(query.limit)
21
+ .offset(query.offset)
22
+ .orderBy(desc(policies.updatedAt)), db.select({ total: sql `count(*)::int` }).from(policies).where(where), query.limit, query.offset);
23
+ },
24
+ async getPolicyById(db, id) {
25
+ const [row] = await db.select().from(policies).where(eq(policies.id, id)).limit(1);
26
+ return row ?? null;
27
+ },
28
+ async getPolicyBySlug(db, slug) {
29
+ const [row] = await db.select().from(policies).where(eq(policies.slug, slug)).limit(1);
30
+ return row ?? null;
31
+ },
32
+ async createPolicy(db, data) {
33
+ const [row] = await db.insert(policies).values(data).returning();
34
+ return row ?? null;
35
+ },
36
+ async updatePolicy(db, id, data) {
37
+ const [row] = await db
38
+ .update(policies)
39
+ .set({ ...data, updatedAt: new Date() })
40
+ .where(eq(policies.id, id))
41
+ .returning();
42
+ return row ?? null;
43
+ },
44
+ async deletePolicy(db, id) {
45
+ const [row] = await db
46
+ .delete(policies)
47
+ .where(eq(policies.id, id))
48
+ .returning({ id: policies.id });
49
+ return row ?? null;
50
+ },
51
+ listPolicyVersions(db, policyId) {
52
+ return db
53
+ .select()
54
+ .from(policyVersions)
55
+ .where(eq(policyVersions.policyId, policyId))
56
+ .orderBy(desc(policyVersions.version));
57
+ },
58
+ async getPolicyVersionById(db, id) {
59
+ const [row] = await db.select().from(policyVersions).where(eq(policyVersions.id, id)).limit(1);
60
+ return row ?? null;
61
+ },
62
+ async createPolicyVersion(db, policyId, data) {
63
+ return db.transaction(async (tx) => {
64
+ const [policy] = await tx
65
+ .select({ id: policies.id })
66
+ .from(policies)
67
+ .where(eq(policies.id, policyId))
68
+ .limit(1);
69
+ if (!policy)
70
+ return null;
71
+ const [maxRow] = await tx
72
+ .select({ max: sql `coalesce(max(${policyVersions.version}), 0)::int` })
73
+ .from(policyVersions)
74
+ .where(eq(policyVersions.policyId, policyId));
75
+ const nextVersion = (maxRow?.max ?? 0) + 1;
76
+ const [row] = await tx
77
+ .insert(policyVersions)
78
+ .values({
79
+ policyId,
80
+ version: nextVersion,
81
+ status: "draft",
82
+ title: data.title,
83
+ bodyFormat: data.bodyFormat,
84
+ body: data.body ?? null,
85
+ publishedBy: data.publishedBy ?? null,
86
+ metadata: data.metadata ?? null,
87
+ })
88
+ .returning();
89
+ return row ?? null;
90
+ });
91
+ },
92
+ async updatePolicyVersion(db, versionId, data) {
93
+ const [row] = await db
94
+ .update(policyVersions)
95
+ .set({ ...data, updatedAt: new Date() })
96
+ .where(and(eq(policyVersions.id, versionId), eq(policyVersions.status, "draft")))
97
+ .returning();
98
+ return row ?? null;
99
+ },
100
+ async publishPolicyVersion(db, versionId) {
101
+ return db.transaction(async (tx) => {
102
+ const [version] = await tx
103
+ .select()
104
+ .from(policyVersions)
105
+ .where(eq(policyVersions.id, versionId))
106
+ .limit(1);
107
+ if (!version)
108
+ return { status: "not_found" };
109
+ if (version.status !== "draft")
110
+ return { status: "not_draft" };
111
+ const now = new Date();
112
+ await tx
113
+ .update(policyVersions)
114
+ .set({ status: "retired", retiredAt: now, updatedAt: now })
115
+ .where(and(eq(policyVersions.policyId, version.policyId), eq(policyVersions.status, "published")));
116
+ const [published] = await tx
117
+ .update(policyVersions)
118
+ .set({ status: "published", publishedAt: now, updatedAt: now })
119
+ .where(eq(policyVersions.id, versionId))
120
+ .returning();
121
+ await tx
122
+ .update(policies)
123
+ .set({ currentVersionId: versionId, updatedAt: now })
124
+ .where(eq(policies.id, version.policyId));
125
+ return { status: "published", version: published ?? null };
126
+ });
127
+ },
128
+ async retirePolicyVersion(db, versionId) {
129
+ const [row] = await db
130
+ .update(policyVersions)
131
+ .set({ status: "retired", retiredAt: new Date(), updatedAt: new Date() })
132
+ .where(eq(policyVersions.id, versionId))
133
+ .returning();
134
+ return row ?? null;
135
+ },
136
+ listPolicyRules(db, versionId) {
137
+ return db
138
+ .select()
139
+ .from(policyRules)
140
+ .where(eq(policyRules.policyVersionId, versionId))
141
+ .orderBy(policyRules.sortOrder, policyRules.createdAt);
142
+ },
143
+ async createPolicyRule(db, versionId, data) {
144
+ const [version] = await db
145
+ .select({ id: policyVersions.id })
146
+ .from(policyVersions)
147
+ .where(eq(policyVersions.id, versionId))
148
+ .limit(1);
149
+ if (!version)
150
+ return null;
151
+ const [row] = await db
152
+ .insert(policyRules)
153
+ .values({
154
+ policyVersionId: versionId,
155
+ ruleType: data.ruleType,
156
+ label: data.label ?? null,
157
+ daysBeforeDeparture: data.daysBeforeDeparture ?? null,
158
+ refundPercent: data.refundPercent ?? null,
159
+ refundType: data.refundType ?? null,
160
+ flatAmountCents: data.flatAmountCents ?? null,
161
+ currency: data.currency ?? null,
162
+ validFrom: toDateString(data.validFrom),
163
+ validTo: toDateString(data.validTo),
164
+ conditions: data.conditions ?? null,
165
+ sortOrder: data.sortOrder,
166
+ })
167
+ .returning();
168
+ return row ?? null;
169
+ },
170
+ async updatePolicyRule(db, ruleId, data) {
171
+ const [row] = await db
172
+ .update(policyRules)
173
+ .set({ ...data, updatedAt: new Date() })
174
+ .where(eq(policyRules.id, ruleId))
175
+ .returning();
176
+ return row ?? null;
177
+ },
178
+ async deletePolicyRule(db, ruleId) {
179
+ const [row] = await db
180
+ .delete(policyRules)
181
+ .where(eq(policyRules.id, ruleId))
182
+ .returning({ id: policyRules.id });
183
+ return row ?? null;
184
+ },
185
+ async listPolicyAssignments(db, query) {
186
+ const conditions = [];
187
+ if (query.policyId)
188
+ conditions.push(eq(policyAssignments.policyId, query.policyId));
189
+ if (query.scope)
190
+ conditions.push(eq(policyAssignments.scope, query.scope));
191
+ if (query.productId)
192
+ conditions.push(eq(policyAssignments.productId, query.productId));
193
+ if (query.channelId)
194
+ conditions.push(eq(policyAssignments.channelId, query.channelId));
195
+ if (query.supplierId)
196
+ conditions.push(eq(policyAssignments.supplierId, query.supplierId));
197
+ if (query.marketId)
198
+ conditions.push(eq(policyAssignments.marketId, query.marketId));
199
+ if (query.organizationId)
200
+ conditions.push(eq(policyAssignments.organizationId, query.organizationId));
201
+ const where = conditions.length ? and(...conditions) : undefined;
202
+ return paginate(db
203
+ .select()
204
+ .from(policyAssignments)
205
+ .where(where)
206
+ .limit(query.limit)
207
+ .offset(query.offset)
208
+ .orderBy(desc(policyAssignments.priority), desc(policyAssignments.createdAt)), db.select({ total: sql `count(*)::int` }).from(policyAssignments).where(where), query.limit, query.offset);
209
+ },
210
+ async createPolicyAssignment(db, data) {
211
+ const [row] = await db
212
+ .insert(policyAssignments)
213
+ .values({
214
+ policyId: data.policyId,
215
+ scope: data.scope,
216
+ productId: data.productId ?? null,
217
+ channelId: data.channelId ?? null,
218
+ supplierId: data.supplierId ?? null,
219
+ marketId: data.marketId ?? null,
220
+ organizationId: data.organizationId ?? null,
221
+ validFrom: toDateString(data.validFrom),
222
+ validTo: toDateString(data.validTo),
223
+ priority: data.priority,
224
+ metadata: data.metadata ?? null,
225
+ })
226
+ .returning();
227
+ return row ?? null;
228
+ },
229
+ async updatePolicyAssignment(db, id, data) {
230
+ const [row] = await db
231
+ .update(policyAssignments)
232
+ .set({ ...data, updatedAt: new Date() })
233
+ .where(eq(policyAssignments.id, id))
234
+ .returning();
235
+ return row ?? null;
236
+ },
237
+ async deletePolicyAssignment(db, id) {
238
+ const [row] = await db
239
+ .delete(policyAssignments)
240
+ .where(eq(policyAssignments.id, id))
241
+ .returning({ id: policyAssignments.id });
242
+ return row ?? null;
243
+ },
244
+ async resolvePolicy(db, input) {
245
+ const conditions = [];
246
+ const atDate = input.at ?? new Date().toISOString().slice(0, 10);
247
+ const scopeConditions = [];
248
+ if (input.productId)
249
+ scopeConditions.push(or(eq(policyAssignments.productId, input.productId), sql `${policyAssignments.productId} IS NULL`));
250
+ if (input.channelId)
251
+ scopeConditions.push(or(eq(policyAssignments.channelId, input.channelId), sql `${policyAssignments.channelId} IS NULL`));
252
+ if (input.supplierId)
253
+ scopeConditions.push(or(eq(policyAssignments.supplierId, input.supplierId), sql `${policyAssignments.supplierId} IS NULL`));
254
+ if (input.marketId)
255
+ scopeConditions.push(or(eq(policyAssignments.marketId, input.marketId), sql `${policyAssignments.marketId} IS NULL`));
256
+ if (input.organizationId)
257
+ scopeConditions.push(or(eq(policyAssignments.organizationId, input.organizationId), sql `${policyAssignments.organizationId} IS NULL`));
258
+ const validity = and(or(sql `${policyAssignments.validFrom} IS NULL`, lte(policyAssignments.validFrom, atDate)), or(sql `${policyAssignments.validTo} IS NULL`, gte(policyAssignments.validTo, atDate)));
259
+ const candidates = await db
260
+ .select({ assignment: policyAssignments, policy: policies })
261
+ .from(policyAssignments)
262
+ .innerJoin(policies, eq(policyAssignments.policyId, policies.id))
263
+ .where(and(eq(policies.kind, input.kind), validity, ...conditions, ...(scopeConditions.length ? [and(...scopeConditions)] : [])))
264
+ .orderBy(desc(policyAssignments.priority), desc(policyAssignments.createdAt));
265
+ if (candidates.length === 0)
266
+ return null;
267
+ const scored = candidates.map((c) => {
268
+ const a = c.assignment;
269
+ const specificity = (a.productId ? 1 : 0) +
270
+ (a.channelId ? 1 : 0) +
271
+ (a.supplierId ? 1 : 0) +
272
+ (a.marketId ? 1 : 0) +
273
+ (a.organizationId ? 1 : 0);
274
+ return { ...c, specificity };
275
+ });
276
+ scored.sort((a, b) => {
277
+ if (b.assignment.priority !== a.assignment.priority)
278
+ return b.assignment.priority - a.assignment.priority;
279
+ if (b.specificity !== a.specificity)
280
+ return b.specificity - a.specificity;
281
+ return b.assignment.createdAt.getTime() - a.assignment.createdAt.getTime();
282
+ });
283
+ const winner = scored[0];
284
+ if (!winner)
285
+ return null;
286
+ if (!winner.policy.currentVersionId)
287
+ return { policy: winner.policy, assignment: winner.assignment, version: null, rules: [] };
288
+ const [version] = await db
289
+ .select()
290
+ .from(policyVersions)
291
+ .where(eq(policyVersions.id, winner.policy.currentVersionId))
292
+ .limit(1);
293
+ const rules = version
294
+ ? await db
295
+ .select()
296
+ .from(policyRules)
297
+ .where(eq(policyRules.policyVersionId, version.id))
298
+ .orderBy(policyRules.sortOrder)
299
+ : [];
300
+ return { policy: winner.policy, assignment: winner.assignment, version: version ?? null, rules };
301
+ },
302
+ async evaluateCancellation(db, policyId, input) {
303
+ const [policy] = await db.select().from(policies).where(eq(policies.id, policyId)).limit(1);
304
+ if (!policy?.currentVersionId)
305
+ return null;
306
+ const rules = await db
307
+ .select()
308
+ .from(policyRules)
309
+ .where(eq(policyRules.policyVersionId, policy.currentVersionId));
310
+ const mapped = rules.map((r) => ({
311
+ id: r.id,
312
+ daysBeforeDeparture: r.daysBeforeDeparture,
313
+ refundPercent: r.refundPercent,
314
+ refundType: r.refundType,
315
+ flatAmountCents: r.flatAmountCents,
316
+ label: r.label,
317
+ }));
318
+ return evaluateCancellationPolicy(mapped, input);
319
+ },
320
+ async listPolicyAcceptances(db, query) {
321
+ const conditions = [];
322
+ if (query.policyVersionId)
323
+ conditions.push(eq(policyAcceptances.policyVersionId, query.policyVersionId));
324
+ if (query.personId)
325
+ conditions.push(eq(policyAcceptances.personId, query.personId));
326
+ if (query.bookingId)
327
+ conditions.push(eq(policyAcceptances.bookingId, query.bookingId));
328
+ if (query.orderId)
329
+ conditions.push(eq(policyAcceptances.orderId, query.orderId));
330
+ const where = conditions.length ? and(...conditions) : undefined;
331
+ return paginate(db
332
+ .select()
333
+ .from(policyAcceptances)
334
+ .where(where)
335
+ .limit(query.limit)
336
+ .offset(query.offset)
337
+ .orderBy(desc(policyAcceptances.acceptedAt)), db.select({ total: sql `count(*)::int` }).from(policyAcceptances).where(where), query.limit, query.offset);
338
+ },
339
+ async recordPolicyAcceptance(db, data) {
340
+ const [row] = await db
341
+ .insert(policyAcceptances)
342
+ .values({
343
+ policyVersionId: data.policyVersionId,
344
+ personId: data.personId ?? null,
345
+ bookingId: data.bookingId ?? null,
346
+ orderId: data.orderId ?? null,
347
+ offerId: data.offerId ?? null,
348
+ acceptedBy: data.acceptedBy ?? null,
349
+ method: data.method,
350
+ ipAddress: data.ipAddress ?? null,
351
+ userAgent: data.userAgent ?? null,
352
+ metadata: data.metadata ?? null,
353
+ })
354
+ .returning();
355
+ return row ?? null;
356
+ },
357
+ };
@@ -0,0 +1,43 @@
1
+ import { inArray } from "drizzle-orm";
2
+ import type { z } from "zod";
3
+ import type { evaluateCancellationInputSchema, insertPolicyAcceptanceSchema, insertPolicyAssignmentSchema, insertPolicyRuleSchema, insertPolicySchema, insertPolicyVersionSchema, policyAcceptanceListQuerySchema, policyAssignmentListQuerySchema, policyListQuerySchema, resolvePolicyInputSchema, updatePolicyAssignmentSchema, updatePolicyRuleSchema, updatePolicySchema, updatePolicyVersionSchema } from "./validation.js";
4
+ export type PolicyListQuery = z.infer<typeof policyListQuerySchema>;
5
+ export type CreatePolicyInput = z.infer<typeof insertPolicySchema>;
6
+ export type UpdatePolicyInput = z.infer<typeof updatePolicySchema>;
7
+ export type CreatePolicyVersionInput = z.infer<typeof insertPolicyVersionSchema>;
8
+ export type UpdatePolicyVersionInput = z.infer<typeof updatePolicyVersionSchema>;
9
+ export type CreatePolicyRuleInput = z.infer<typeof insertPolicyRuleSchema>;
10
+ export type UpdatePolicyRuleInput = z.infer<typeof updatePolicyRuleSchema>;
11
+ export type CreatePolicyAssignmentInput = z.infer<typeof insertPolicyAssignmentSchema>;
12
+ export type UpdatePolicyAssignmentInput = z.infer<typeof updatePolicyAssignmentSchema>;
13
+ export type PolicyAssignmentListQuery = z.infer<typeof policyAssignmentListQuerySchema>;
14
+ export type CreatePolicyAcceptanceInput = z.infer<typeof insertPolicyAcceptanceSchema>;
15
+ export type PolicyAcceptanceListQuery = z.infer<typeof policyAcceptanceListQuerySchema>;
16
+ export type EvaluateCancellationInput = z.infer<typeof evaluateCancellationInputSchema>;
17
+ export type ResolvePolicyInput = z.infer<typeof resolvePolicyInputSchema>;
18
+ export declare function paginate<T extends object>(rowsQuery: Promise<T[]>, countQuery: Promise<Array<{
19
+ total: number;
20
+ }>>, limit: number, offset: number): Promise<{
21
+ data: T[];
22
+ total: number;
23
+ limit: number;
24
+ offset: number;
25
+ }>;
26
+ export declare function toDateString(value?: string | null): string | null;
27
+ export type CancellationRule = {
28
+ id?: string;
29
+ daysBeforeDeparture: number | null;
30
+ refundPercent: number | null;
31
+ refundType: "cash" | "credit" | "cash_or_credit" | "none" | null;
32
+ flatAmountCents: number | null;
33
+ label: string | null;
34
+ };
35
+ export type CancellationResult = {
36
+ refundPercent: number;
37
+ refundCents: number;
38
+ refundType: "cash" | "credit" | "cash_or_credit" | "none";
39
+ appliedRule: CancellationRule | null;
40
+ };
41
+ export declare function evaluateCancellationPolicy(rules: CancellationRule[], input: EvaluateCancellationInput): CancellationResult;
42
+ export declare const _unused: typeof inArray;
43
+ //# sourceMappingURL=service-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-shared.d.ts","sourceRoot":"","sources":["../../src/policies/service-shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { inArray } from "drizzle-orm";
2
+ export async function paginate(rowsQuery, countQuery, limit, offset) {
3
+ const [data, countResult] = await Promise.all([rowsQuery, countQuery]);
4
+ return { data, total: countResult[0]?.total ?? 0, limit, offset };
5
+ }
6
+ export function toDateString(value) {
7
+ return value ?? null;
8
+ }
9
+ export function evaluateCancellationPolicy(rules, input) {
10
+ if (rules.length === 0) {
11
+ return { refundPercent: 0, refundCents: 0, refundType: "none", appliedRule: null };
12
+ }
13
+ const sorted = [...rules].sort((a, b) => {
14
+ const ad = a.daysBeforeDeparture ?? Number.NEGATIVE_INFINITY;
15
+ const bd = b.daysBeforeDeparture ?? Number.NEGATIVE_INFINITY;
16
+ return bd - ad;
17
+ });
18
+ const applied = sorted.find((rule) => rule.daysBeforeDeparture !== null && input.daysBeforeDeparture >= rule.daysBeforeDeparture) ??
19
+ sorted[sorted.length - 1] ??
20
+ null;
21
+ if (!applied) {
22
+ return { refundPercent: 0, refundCents: 0, refundType: "none", appliedRule: null };
23
+ }
24
+ const refundPercent = applied.refundPercent ?? 0;
25
+ const refundType = applied.refundType ?? "none";
26
+ const percentageRefundCents = Math.floor((input.totalCents * refundPercent) / 10000);
27
+ const refundCents = applied.flatAmountCents ?? percentageRefundCents;
28
+ return { refundPercent, refundCents, refundType, appliedRule: applied };
29
+ }
30
+ export const _unused = inArray;