@theunwalked/cardigantime 0.0.15 → 0.0.16
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/cardigantime.cjs +80 -85
- package/dist/cardigantime.cjs.map +1 -1
- package/dist/types.d.ts +3 -14
- package/dist/util/schema-defaults.d.ts +27 -9
- package/dist/util/schema-defaults.js +48 -45
- package/dist/util/schema-defaults.js.map +1 -1
- package/dist/validate.js +31 -39
- package/dist/validate.js.map +1 -1
- package/package.json +7 -10
package/dist/cardigantime.cjs
CHANGED
|
@@ -1444,22 +1444,22 @@ class ConfigurationError extends Error {
|
|
|
1444
1444
|
|
|
1445
1445
|
/**
|
|
1446
1446
|
* Recursively extracts all keys from a Zod schema in dot notation.
|
|
1447
|
-
*
|
|
1447
|
+
*
|
|
1448
1448
|
* This function traverses a Zod schema structure and builds a flat list
|
|
1449
1449
|
* of all possible keys, using dot notation for nested objects. It handles
|
|
1450
1450
|
* optional/nullable types by unwrapping them and supports arrays by
|
|
1451
1451
|
* introspecting their element type.
|
|
1452
|
-
*
|
|
1452
|
+
*
|
|
1453
1453
|
* Special handling for:
|
|
1454
1454
|
* - ZodOptional/ZodNullable: Unwraps to get the underlying type
|
|
1455
1455
|
* - ZodAny/ZodRecord: Accepts any keys, returns the prefix or empty array
|
|
1456
1456
|
* - ZodArray: Introspects the element type
|
|
1457
1457
|
* - ZodObject: Recursively processes all shape properties
|
|
1458
|
-
*
|
|
1458
|
+
*
|
|
1459
1459
|
* @param schema - The Zod schema to introspect
|
|
1460
1460
|
* @param prefix - Internal parameter for building nested key paths
|
|
1461
1461
|
* @returns Array of strings representing all possible keys in dot notation
|
|
1462
|
-
*
|
|
1462
|
+
*
|
|
1463
1463
|
* @example
|
|
1464
1464
|
* ```typescript
|
|
1465
1465
|
* const schema = z.object({
|
|
@@ -1469,32 +1469,26 @@ class ConfigurationError extends Error {
|
|
|
1469
1469
|
* }),
|
|
1470
1470
|
* debug: z.boolean()
|
|
1471
1471
|
* });
|
|
1472
|
-
*
|
|
1472
|
+
*
|
|
1473
1473
|
* const keys = listZodKeys(schema);
|
|
1474
1474
|
* // Returns: ['user.name', 'user.settings.theme', 'debug']
|
|
1475
1475
|
* ```
|
|
1476
1476
|
*/ const listZodKeys = (schema, prefix = '')=>{
|
|
1477
|
-
//
|
|
1478
|
-
if (schema
|
|
1479
|
-
|
|
1480
|
-
const unwrappable = schema;
|
|
1481
|
-
return listZodKeys(unwrappable.unwrap(), prefix);
|
|
1477
|
+
// Handle ZodOptional and ZodNullable - unwrap to get the underlying type
|
|
1478
|
+
if (schema instanceof zod.z.ZodOptional || schema instanceof zod.z.ZodNullable) {
|
|
1479
|
+
return listZodKeys(schema.unwrap(), prefix);
|
|
1482
1480
|
}
|
|
1483
1481
|
// Handle ZodAny and ZodRecord - these accept any keys, so don't introspect
|
|
1484
|
-
if (schema
|
|
1482
|
+
if (schema instanceof zod.z.ZodAny || schema instanceof zod.z.ZodRecord) {
|
|
1485
1483
|
return prefix ? [
|
|
1486
1484
|
prefix
|
|
1487
1485
|
] : [];
|
|
1488
1486
|
}
|
|
1489
|
-
if (schema
|
|
1490
|
-
|
|
1491
|
-
const arraySchema = schema;
|
|
1492
|
-
return listZodKeys(arraySchema.element, prefix);
|
|
1487
|
+
if (schema instanceof zod.z.ZodArray) {
|
|
1488
|
+
return listZodKeys(schema.element, prefix);
|
|
1493
1489
|
}
|
|
1494
|
-
if (schema
|
|
1495
|
-
|
|
1496
|
-
const objectSchema = schema;
|
|
1497
|
-
return Object.entries(objectSchema.shape).flatMap(([key, subschema])=>{
|
|
1490
|
+
if (schema instanceof zod.z.ZodObject) {
|
|
1491
|
+
return Object.entries(schema.shape).flatMap(([key, subschema])=>{
|
|
1498
1492
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
1499
1493
|
const nested = listZodKeys(subschema, fullKey);
|
|
1500
1494
|
return nested.length ? nested : fullKey;
|
|
@@ -1504,7 +1498,7 @@ class ConfigurationError extends Error {
|
|
|
1504
1498
|
};
|
|
1505
1499
|
/**
|
|
1506
1500
|
* Type guard to check if a value is a plain object (not array, null, or other types).
|
|
1507
|
-
*
|
|
1501
|
+
*
|
|
1508
1502
|
* @param value - The value to check
|
|
1509
1503
|
* @returns True if the value is a plain object
|
|
1510
1504
|
*/ const isPlainObject = (value)=>{
|
|
@@ -1552,26 +1546,26 @@ class ConfigurationError extends Error {
|
|
|
1552
1546
|
};
|
|
1553
1547
|
/**
|
|
1554
1548
|
* Validates that the configuration object contains only keys allowed by the schema.
|
|
1555
|
-
*
|
|
1549
|
+
*
|
|
1556
1550
|
* This function prevents configuration errors by detecting typos or extra keys
|
|
1557
1551
|
* that aren't defined in the Zod schema. It intelligently handles:
|
|
1558
1552
|
* - ZodRecord types that accept arbitrary keys
|
|
1559
1553
|
* - Nested objects and their key structures
|
|
1560
1554
|
* - Arrays and their element key structures
|
|
1561
|
-
*
|
|
1555
|
+
*
|
|
1562
1556
|
* The function throws a ConfigurationError if extra keys are found, providing
|
|
1563
1557
|
* helpful information about what keys are allowed vs. what was found.
|
|
1564
|
-
*
|
|
1558
|
+
*
|
|
1565
1559
|
* @param mergedSources - The merged configuration object to validate
|
|
1566
1560
|
* @param fullSchema - The complete Zod schema including base and user schemas
|
|
1567
1561
|
* @param logger - Logger for error reporting
|
|
1568
1562
|
* @throws {ConfigurationError} When extra keys are found that aren't in the schema
|
|
1569
|
-
*
|
|
1563
|
+
*
|
|
1570
1564
|
* @example
|
|
1571
1565
|
* ```typescript
|
|
1572
1566
|
* const schema = z.object({ name: z.string(), age: z.number() });
|
|
1573
1567
|
* const config = { name: 'John', age: 30, typo: 'invalid' };
|
|
1574
|
-
*
|
|
1568
|
+
*
|
|
1575
1569
|
* checkForExtraKeys(config, schema, console);
|
|
1576
1570
|
* // Throws: ConfigurationError with details about 'typo' being an extra key
|
|
1577
1571
|
* ```
|
|
@@ -1582,18 +1576,16 @@ class ConfigurationError extends Error {
|
|
|
1582
1576
|
const recordPrefixes = new Set();
|
|
1583
1577
|
// Find all prefixes that are ZodRecord types
|
|
1584
1578
|
const findRecordPrefixes = (schema, prefix = '')=>{
|
|
1585
|
-
if (schema
|
|
1586
|
-
|
|
1587
|
-
findRecordPrefixes(unwrappable.unwrap(), prefix);
|
|
1579
|
+
if (schema instanceof zod.z.ZodOptional || schema instanceof zod.z.ZodNullable) {
|
|
1580
|
+
findRecordPrefixes(schema.unwrap(), prefix);
|
|
1588
1581
|
return;
|
|
1589
1582
|
}
|
|
1590
|
-
if (schema
|
|
1583
|
+
if (schema instanceof zod.z.ZodAny || schema instanceof zod.z.ZodRecord) {
|
|
1591
1584
|
if (prefix) recordPrefixes.add(prefix);
|
|
1592
1585
|
return;
|
|
1593
1586
|
}
|
|
1594
|
-
if (schema
|
|
1595
|
-
|
|
1596
|
-
Object.entries(objectSchema.shape).forEach(([key, subschema])=>{
|
|
1587
|
+
if (schema instanceof zod.z.ZodObject) {
|
|
1588
|
+
Object.entries(schema.shape).forEach(([key, subschema])=>{
|
|
1597
1589
|
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
1598
1590
|
findRecordPrefixes(subschema, fullKey);
|
|
1599
1591
|
});
|
|
@@ -1620,11 +1612,11 @@ class ConfigurationError extends Error {
|
|
|
1620
1612
|
};
|
|
1621
1613
|
/**
|
|
1622
1614
|
* Validates that a configuration directory exists and is accessible.
|
|
1623
|
-
*
|
|
1615
|
+
*
|
|
1624
1616
|
* This function performs file system checks to ensure the configuration
|
|
1625
1617
|
* directory can be used. It handles the isRequired flag to determine
|
|
1626
1618
|
* whether a missing directory should cause an error or be silently ignored.
|
|
1627
|
-
*
|
|
1619
|
+
*
|
|
1628
1620
|
* @param configDirectory - Path to the configuration directory
|
|
1629
1621
|
* @param isRequired - Whether the directory must exist
|
|
1630
1622
|
* @param logger - Optional logger for debug information
|
|
@@ -1647,30 +1639,30 @@ class ConfigurationError extends Error {
|
|
|
1647
1639
|
};
|
|
1648
1640
|
/**
|
|
1649
1641
|
* Validates a configuration object against the combined Zod schema.
|
|
1650
|
-
*
|
|
1642
|
+
*
|
|
1651
1643
|
* This is the main validation function that:
|
|
1652
1644
|
* 1. Validates the configuration directory (if config feature enabled)
|
|
1653
1645
|
* 2. Combines the base ConfigSchema with user-provided schema shape
|
|
1654
1646
|
* 3. Checks for extra keys not defined in the schema
|
|
1655
1647
|
* 4. Validates all values against their schema definitions
|
|
1656
1648
|
* 5. Provides detailed error reporting for validation failures
|
|
1657
|
-
*
|
|
1649
|
+
*
|
|
1658
1650
|
* The validation is comprehensive and catches common configuration errors
|
|
1659
1651
|
* including typos, missing required fields, wrong types, and invalid values.
|
|
1660
|
-
*
|
|
1652
|
+
*
|
|
1661
1653
|
* @template T - The Zod schema shape type for configuration validation
|
|
1662
1654
|
* @param config - The merged configuration object to validate
|
|
1663
1655
|
* @param options - Cardigantime options containing schema, defaults, and logger
|
|
1664
1656
|
* @throws {ConfigurationError} When configuration validation fails
|
|
1665
1657
|
* @throws {FileSystemError} When configuration directory validation fails
|
|
1666
|
-
*
|
|
1658
|
+
*
|
|
1667
1659
|
* @example
|
|
1668
1660
|
* ```typescript
|
|
1669
1661
|
* const schema = z.object({
|
|
1670
1662
|
* apiKey: z.string().min(1),
|
|
1671
1663
|
* timeout: z.number().positive(),
|
|
1672
1664
|
* });
|
|
1673
|
-
*
|
|
1665
|
+
*
|
|
1674
1666
|
* await validate(config, {
|
|
1675
1667
|
* configShape: schema.shape,
|
|
1676
1668
|
* defaults: { configDirectory: './config', isRequired: true },
|
|
@@ -1703,18 +1695,17 @@ class ConfigurationError extends Error {
|
|
|
1703
1695
|
};
|
|
1704
1696
|
|
|
1705
1697
|
/**
|
|
1706
|
-
* Extracts default values from a Zod schema recursively.
|
|
1707
|
-
*
|
|
1708
|
-
* This function
|
|
1709
|
-
*
|
|
1710
|
-
*
|
|
1711
|
-
*
|
|
1712
|
-
*
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1698
|
+
* Extracts default values from a Zod schema recursively using Zod v4's parsing mechanisms.
|
|
1699
|
+
*
|
|
1700
|
+
* This function leverages Zod's own parsing behavior to extract defaults rather than
|
|
1701
|
+
* accessing internal properties. It works by:
|
|
1702
|
+
* 1. For ZodDefault types: parsing undefined to trigger the default
|
|
1703
|
+
* 2. For ZodObject types: creating a minimal object and parsing to get all defaults
|
|
1704
|
+
* 3. For wrapped types: unwrapping and recursing
|
|
1705
|
+
*
|
|
1715
1706
|
* @param schema - The Zod schema to extract defaults from
|
|
1716
1707
|
* @returns An object containing all default values from the schema
|
|
1717
|
-
*
|
|
1708
|
+
*
|
|
1718
1709
|
* @example
|
|
1719
1710
|
* ```typescript
|
|
1720
1711
|
* const schema = z.object({
|
|
@@ -1726,68 +1717,72 @@ class ConfigurationError extends Error {
|
|
|
1726
1717
|
* port: z.number().default(5432)
|
|
1727
1718
|
* })
|
|
1728
1719
|
* });
|
|
1729
|
-
*
|
|
1720
|
+
*
|
|
1730
1721
|
* const defaults = extractSchemaDefaults(schema);
|
|
1731
1722
|
* // Returns: { name: 'app', port: 3000, debug: false, database: { host: 'localhost', port: 5432 } }
|
|
1732
1723
|
* ```
|
|
1733
1724
|
*/ const extractSchemaDefaults = (schema)=>{
|
|
1734
|
-
// Handle ZodDefault -
|
|
1735
|
-
if (schema
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
const unwrappable = schema;
|
|
1742
|
-
const unwrapped = unwrappable.unwrap();
|
|
1743
|
-
// Only provide defaults if the unwrapped schema has explicit defaults
|
|
1744
|
-
// This prevents optional arrays/objects from automatically getting [] or {} defaults
|
|
1745
|
-
if (unwrapped._def && unwrapped._def.typeName === 'ZodDefault') {
|
|
1746
|
-
return extractSchemaDefaults(unwrapped);
|
|
1725
|
+
// Handle ZodDefault - parse undefined to get the default value
|
|
1726
|
+
if (schema instanceof zod.z.ZodDefault) {
|
|
1727
|
+
try {
|
|
1728
|
+
return schema.parse(undefined);
|
|
1729
|
+
} catch {
|
|
1730
|
+
// If parsing undefined fails, return undefined
|
|
1731
|
+
return undefined;
|
|
1747
1732
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1733
|
+
}
|
|
1734
|
+
// Handle ZodOptional and ZodNullable by unwrapping
|
|
1735
|
+
if (schema instanceof zod.z.ZodOptional || schema instanceof zod.z.ZodNullable) {
|
|
1736
|
+
return extractSchemaDefaults(schema.unwrap());
|
|
1737
|
+
}
|
|
1738
|
+
// Handle ZodObject - create an object with defaults by parsing an empty object
|
|
1739
|
+
if (schema instanceof zod.z.ZodObject) {
|
|
1740
|
+
const defaults = {};
|
|
1741
|
+
const shape = schema.shape;
|
|
1742
|
+
// First, try to extract defaults from individual fields
|
|
1743
|
+
for (const [key, subschema] of Object.entries(shape)){
|
|
1756
1744
|
const defaultValue = extractSchemaDefaults(subschema);
|
|
1757
1745
|
if (defaultValue !== undefined) {
|
|
1758
|
-
|
|
1746
|
+
defaults[key] = defaultValue;
|
|
1759
1747
|
}
|
|
1760
1748
|
}
|
|
1761
|
-
|
|
1749
|
+
// Then parse an empty object to trigger any schema-level defaults
|
|
1750
|
+
const result = schema.safeParse({});
|
|
1751
|
+
if (result.success) {
|
|
1752
|
+
// Merge the parsed result with our extracted defaults
|
|
1753
|
+
return {
|
|
1754
|
+
...defaults,
|
|
1755
|
+
...result.data
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
return Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
1762
1759
|
}
|
|
1763
|
-
// Handle ZodArray -
|
|
1764
|
-
if (schema
|
|
1765
|
-
const
|
|
1766
|
-
const elementDefaults = extractSchemaDefaults(arraySchema.element);
|
|
1767
|
-
// Return an empty array, or an array with one example element if it has defaults
|
|
1760
|
+
// Handle ZodArray - return empty array as a reasonable default
|
|
1761
|
+
if (schema instanceof zod.z.ZodArray) {
|
|
1762
|
+
const elementDefaults = extractSchemaDefaults(schema.element);
|
|
1768
1763
|
return elementDefaults !== undefined ? [
|
|
1769
1764
|
elementDefaults
|
|
1770
1765
|
] : [];
|
|
1771
1766
|
}
|
|
1772
|
-
// Handle ZodRecord -
|
|
1773
|
-
if (schema
|
|
1767
|
+
// Handle ZodRecord - return empty object as default
|
|
1768
|
+
if (schema instanceof zod.z.ZodRecord) {
|
|
1774
1769
|
return {};
|
|
1775
1770
|
}
|
|
1776
|
-
//
|
|
1771
|
+
// No default available for other schema types
|
|
1777
1772
|
return undefined;
|
|
1778
1773
|
};
|
|
1779
1774
|
/**
|
|
1780
1775
|
* Generates a complete configuration object with all default values populated.
|
|
1781
|
-
*
|
|
1776
|
+
*
|
|
1782
1777
|
* This function combines the base ConfigSchema with a user-provided schema shape
|
|
1783
1778
|
* and extracts all available default values to create a complete configuration
|
|
1784
1779
|
* example that can be serialized to YAML.
|
|
1785
|
-
*
|
|
1780
|
+
*
|
|
1786
1781
|
* @template T - The Zod schema shape type
|
|
1787
1782
|
* @param configShape - The user's configuration schema shape
|
|
1788
1783
|
* @param configDirectory - The configuration directory to include in the defaults
|
|
1789
1784
|
* @returns An object containing all default values suitable for YAML serialization
|
|
1790
|
-
*
|
|
1785
|
+
*
|
|
1791
1786
|
* @example
|
|
1792
1787
|
* ```typescript
|
|
1793
1788
|
* const shape = z.object({
|
|
@@ -1795,7 +1790,7 @@ class ConfigurationError extends Error {
|
|
|
1795
1790
|
* timeout: z.number().default(5000).describe('Request timeout in milliseconds'),
|
|
1796
1791
|
* features: z.array(z.string()).default(['auth', 'logging'])
|
|
1797
1792
|
* }).shape;
|
|
1798
|
-
*
|
|
1793
|
+
*
|
|
1799
1794
|
* const config = generateDefaultConfig(shape, './config');
|
|
1800
1795
|
* // Returns: { timeout: 5000, features: ['auth', 'logging'] }
|
|
1801
1796
|
* // Note: apiKey is not included since it has no default
|