@simpleapps-com/augur-api
Version:
TypeScript client library for Augur microservices API endpoints
165 lines • 6.86 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.flexibleProductFields = exports.flexibleUserFields = exports.makeFlexible = exports.flexibleMetadataFields = exports.flexibleNested = exports.flexibleObject = exports.flexibleProfileData = exports.flexibleDate = exports.flexibleBoolean = exports.flexibleStringOrNumber = exports.flexibleCollection = exports.flexibleArrayOrObject = void 0;
const zod_1 = require("zod");
/**
* Flexible Schema Utilities for Handling Inconsistent API Outputs
*
* During microservice API refactoring, many endpoints return inconsistent formats.
* These utilities provide standardized patterns for handling common inconsistencies.
*/
/**
* Creates a schema that accepts both array and object formats
* Common pattern: server sometimes returns [] and sometimes {}
*/
const flexibleArrayOrObject = (itemSchema) => zod_1.z.union([
zod_1.z.array(itemSchema), // Preferred format
zod_1.z.record(zod_1.z.unknown()), // Legacy/inconsistent format
zod_1.z.record(zod_1.z.never()).optional(), // Empty object fallback
]);
exports.flexibleArrayOrObject = flexibleArrayOrObject;
/**
* Creates a schema that accepts both array and record formats for collections
* Handles cases where APIs return either array of items or object with key-value pairs
*/
const flexibleCollection = (itemSchema) => zod_1.z.union([
zod_1.z.array(itemSchema), // Standard array format
zod_1.z.record(itemSchema), // Object with typed values
zod_1.z.array(zod_1.z.unknown()), // Fallback array with unknown items
zod_1.z.record(zod_1.z.unknown()), // Fallback object with unknown values
]);
exports.flexibleCollection = flexibleCollection;
/**
* Creates a schema for fields that might be string, number, or null
* Common during API migrations when data types are being standardized
*/
const flexibleStringOrNumber = () => zod_1.z.union([zod_1.z.string(), zod_1.z.number(), zod_1.z.null(), zod_1.z.undefined()]).transform(val => {
if (val === null || val === undefined)
return null;
return String(val);
});
exports.flexibleStringOrNumber = flexibleStringOrNumber;
/**
* Creates a schema for boolean fields that might come as string, number, or boolean
* Handles legacy APIs that return "1"/"0", true/false, or "true"/"false"
*/
const flexibleBoolean = () => zod_1.z.union([zod_1.z.boolean(), zod_1.z.string(), zod_1.z.number(), zod_1.z.null(), zod_1.z.undefined()]).transform(val => {
if (typeof val === 'boolean')
return val;
if (typeof val === 'string')
return val.toLowerCase() === 'true' || val === '1';
if (typeof val === 'number')
return val === 1;
return false;
});
exports.flexibleBoolean = flexibleBoolean;
/**
* Creates a schema for date fields that might be string, Date, or null
* Handles various date format inconsistencies
*/
const flexibleDate = () => zod_1.z.union([zod_1.z.string(), zod_1.z.date(), zod_1.z.null(), zod_1.z.undefined()]).transform(val => {
if (val === null || val === undefined)
return null;
if (val instanceof Date)
return val.toISOString();
return String(val);
});
exports.flexibleDate = flexibleDate;
/**
* Creates a flexible schema for profile/metadata fields that vary between endpoints
* This is the pattern we found with profileValues - sometimes array, sometimes object
*/
const flexibleProfileData = () => zod_1.z
.union([
zod_1.z.record(zod_1.z.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])), // Object format with string or array values
zod_1.z.array(zod_1.z.unknown()), // Array format (common case)
zod_1.z.null(), // No profile data
zod_1.z.undefined(), // Field missing
])
.optional();
exports.flexibleProfileData = flexibleProfileData;
/**
* Creates a flexible schema that gracefully handles unexpected extra fields
* Uses passthrough to allow additional properties during API evolution
*/
const flexibleObject = (shape) => zod_1.z.object(shape).passthrough();
exports.flexibleObject = flexibleObject;
/**
* Creates a schema that handles nested data that might be normalized or denormalized
* Common when APIs are being refactored from flat to nested structures
*/
const flexibleNested = (simpleSchema, complexSchema) => zod_1.z.union([
simpleSchema, // Simple/flat format
complexSchema, // Complex/nested format
]);
exports.flexibleNested = flexibleNested;
/**
* Pre-built schema for common metadata fields that are inconsistent during refactoring
* Handles count normalization and bidirectional sync between total and totalResults
*/
exports.flexibleMetadataFields = zod_1.z
.object({
count: zod_1.z.number().optional().default(0),
total: zod_1.z.number().optional().default(0),
totalResults: zod_1.z.number().optional().default(0),
})
.transform(data => {
// Apply the same normalization logic as BaseResponseSchema
const count = data.count || 0;
// Bidirectional sync between total and totalResults
let total = data.total;
let totalResults = data.totalResults || 0;
// If total is 0 and totalResults > 0, set total to totalResults
if (total === 0 && totalResults > 0) {
total = totalResults;
}
// If totalResults is 0 and total > 0, set totalResults to total
else if (totalResults === 0 && total > 0) {
totalResults = total;
}
return {
count,
total,
totalResults,
};
});
/**
* Utility for wrapping existing schemas to be more flexible during API refactoring
* Adds .catch() to provide fallback values instead of throwing validation errors
*/
const makeFlexible = (schema, fallbackValue) => schema.catch(fallbackValue);
exports.makeFlexible = makeFlexible;
/**
* Common patterns for user-related data that varies across services
*/
exports.flexibleUserFields = {
profileValues: (0, exports.flexibleProfileData)(),
customFields: (0, exports.flexibleArrayOrObject)(zod_1.z.unknown()),
permissions: (0, exports.flexibleCollection)(zod_1.z.string()),
groups: (0, exports.flexibleArrayOrObject)(zod_1.z
.object({
id: zod_1.z.number(),
title: zod_1.z.string(),
})
.passthrough()),
};
/**
* Common patterns for product/item data that varies across services
*/
exports.flexibleProductFields = {
attributes: (0, exports.flexibleCollection)(zod_1.z
.object({
attributeId: zod_1.z.string(),
value: zod_1.z.string(),
})
.passthrough()),
categories: (0, exports.flexibleArrayOrObject)(zod_1.z
.object({
categoryUid: zod_1.z.number(),
categoryDesc: zod_1.z.string(),
})
.passthrough()),
metadata: (0, exports.flexibleProfileData)(),
customData: (0, exports.flexibleArrayOrObject)(zod_1.z.unknown()),
};
//# sourceMappingURL=flexible-schemas.js.map