Comprehensive refactoring across 70 files (net -298 lines) improving type safety, error handling, and code organization: - Replace .passthrough()/.catchall(z.unknown()) with .strip() in all Zod schemas - Tighten Record<string, unknown> to bounded union types where possible - Replace throw new Error with domain-specific exceptions (OrderException, FulfillmentException, WhmcsOperationException, SalesforceOperationException, etc.) - Split AuthTokenService (625 lines) into TokenGeneratorService and TokenRefreshService with thin orchestrator - Deduplicate FreebitClientService with shared makeRequest() method - Add typed interfaces to WHMCS facade, order service, and fulfillment mapper - Externalize hardcoded config values to ConfigService with env fallbacks - Consolidate duplicate billing cycle enums into shared billingCycleSchema - Standardize logger usage (nestjs-pino @Inject(Logger) everywhere) - Move shared WHMCS number coercion helpers to whmcs-utils/schema.ts
57 lines
2.0 KiB
TypeScript
57 lines
2.0 KiB
TypeScript
/**
|
|
* WHMCS Zod Schema Primitives (domain-internal)
|
|
*
|
|
* Coercing schema helpers for WHMCS API responses.
|
|
* WHMCS (PHP) is loosely typed — fields documented as strings may arrive
|
|
* as numbers (and vice-versa). These primitives absorb that inconsistency
|
|
* at the parsing boundary so the rest of the codebase sees clean types.
|
|
*/
|
|
|
|
import { z } from "zod";
|
|
|
|
/**
|
|
* Coercing string — accepts string or number, always outputs string.
|
|
* Use for any WHMCS response field that should be a string.
|
|
*/
|
|
export const whmcsString = z.coerce.string();
|
|
|
|
/**
|
|
* Accepts number or string (e.g. "123"), keeps the raw union type.
|
|
* Use when downstream code handles both types (e.g. IDs you'll parse later).
|
|
*/
|
|
export const whmcsNumberLike = z.union([z.number(), z.string()]);
|
|
|
|
/**
|
|
* Accepts boolean, number (0/1), or string ("true"/"false"/etc).
|
|
* Use for WHMCS boolean flags that arrive in varying formats.
|
|
*/
|
|
export const whmcsBooleanLike = z.union([z.boolean(), z.number(), z.string()]);
|
|
|
|
/**
|
|
* Coercing required number — accepts number or numeric string, always outputs number.
|
|
* Use for WHMCS fields that must be a number but may arrive as a string.
|
|
*/
|
|
export const whmcsRequiredNumber = z.preprocess(value => {
|
|
if (typeof value === "number") return value;
|
|
if (typeof value === "string" && value.trim().length > 0) {
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : value;
|
|
}
|
|
return value;
|
|
}, z.number());
|
|
|
|
/**
|
|
* Coercing optional number — accepts number, numeric string, null, undefined, or empty string.
|
|
* Returns undefined for missing/empty values.
|
|
* Use for WHMCS fields that are optional numbers but may arrive as strings.
|
|
*/
|
|
export const whmcsOptionalNumber = z.preprocess((value): number | undefined => {
|
|
if (value === undefined || value === null || value === "") return undefined;
|
|
if (typeof value === "number") return value;
|
|
if (typeof value === "string") {
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
}
|
|
return undefined;
|
|
}, z.number().optional());
|