@wildo-ai/zod-decorators 1.1.11 → 1.1.13

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.
Files changed (78) hide show
  1. package/dist/esm/commons.d.ts.map +1 -1
  2. package/dist/esm/commons.js.map +1 -1
  3. package/dist/esm/currencies.shared.schemas.d.ts.map +1 -1
  4. package/dist/esm/currencies.shared.schemas.js.map +1 -1
  5. package/dist/esm/files/agent-file-formats.d.ts.map +1 -1
  6. package/dist/esm/files/agent-file-formats.js.map +1 -1
  7. package/dist/esm/files/extension-nature-map.d.ts.map +1 -1
  8. package/dist/esm/files/extension-nature-map.js.map +1 -1
  9. package/dist/esm/files/file-extensions.enum.d.ts.map +1 -1
  10. package/dist/esm/files/file-extensions.enum.js.map +1 -1
  11. package/dist/esm/files/mime-registry.d.ts.map +1 -1
  12. package/dist/esm/files/mime-registry.js.map +1 -1
  13. package/dist/esm/files/unified-registry.d.ts.map +1 -1
  14. package/dist/esm/files/unified-registry.js.map +1 -1
  15. package/dist/esm/index.d.ts +2 -0
  16. package/dist/esm/index.d.ts.map +1 -1
  17. package/dist/esm/index.js +2 -0
  18. package/dist/esm/index.js.map +1 -1
  19. package/dist/esm/install-bigint-json-encoding.d.ts +1 -1
  20. package/dist/esm/install-bigint-json-encoding.d.ts.map +1 -1
  21. package/dist/esm/install-bigint-json-encoding.js +1 -1
  22. package/dist/esm/install-bigint-json-encoding.js.map +1 -1
  23. package/dist/esm/testing/decorated-test-schemas.d.ts +7 -7
  24. package/dist/esm/testing/decorated-test-schemas.d.ts.map +1 -1
  25. package/dist/esm/testing/decorated-test-schemas.js.map +1 -1
  26. package/dist/esm/testing.d.ts.map +1 -1
  27. package/dist/esm/testing.js.map +1 -1
  28. package/dist/esm/zod-address.d.ts.map +1 -1
  29. package/dist/esm/zod-address.js.map +1 -1
  30. package/dist/esm/zod-bigdecimal.d.ts +4 -4
  31. package/dist/esm/zod-bigdecimal.d.ts.map +1 -1
  32. package/dist/esm/zod-bigdecimal.js.map +1 -1
  33. package/dist/esm/zod-code.d.ts +4 -4
  34. package/dist/esm/zod-code.d.ts.map +1 -1
  35. package/dist/esm/zod-code.js.map +1 -1
  36. package/dist/esm/zod-color.d.ts.map +1 -1
  37. package/dist/esm/zod-color.js.map +1 -1
  38. package/dist/esm/zod-decorators.types.d.ts +116 -9
  39. package/dist/esm/zod-decorators.types.d.ts.map +1 -1
  40. package/dist/esm/zod-decorators.types.js +40 -1
  41. package/dist/esm/zod-decorators.types.js.map +1 -1
  42. package/dist/esm/zod-document-sequence.d.ts.map +1 -1
  43. package/dist/esm/zod-document-sequence.js.map +1 -1
  44. package/dist/esm/zod-duration.d.ts +6 -6
  45. package/dist/esm/zod-duration.d.ts.map +1 -1
  46. package/dist/esm/zod-duration.js.map +1 -1
  47. package/dist/esm/zod-file.d.ts +5 -4
  48. package/dist/esm/zod-file.d.ts.map +1 -1
  49. package/dist/esm/zod-file.js +1 -1
  50. package/dist/esm/zod-file.js.map +1 -1
  51. package/dist/esm/zod-money.d.ts +4 -4
  52. package/dist/esm/zod-money.d.ts.map +1 -1
  53. package/dist/esm/zod-money.js.map +1 -1
  54. package/dist/esm/zod-phone.d.ts +4 -4
  55. package/dist/esm/zod-phone.d.ts.map +1 -1
  56. package/dist/esm/zod-phone.js.map +1 -1
  57. package/dist/esm/zod-range.d.ts.map +1 -1
  58. package/dist/esm/zod-range.js.map +1 -1
  59. package/dist/esm/zod-utils.shared.d.ts +47 -12
  60. package/dist/esm/zod-utils.shared.d.ts.map +1 -1
  61. package/dist/esm/zod-utils.shared.js +143 -40
  62. package/dist/esm/zod-utils.shared.js.map +1 -1
  63. package/dist/esm/zod-versioned.d.ts.map +1 -1
  64. package/dist/esm/zod-versioned.js.map +1 -1
  65. package/dist/esm/zod-wire-date.d.ts +107 -0
  66. package/dist/esm/zod-wire-date.d.ts.map +1 -0
  67. package/dist/esm/zod-wire-date.js +162 -0
  68. package/dist/esm/zod-wire-date.js.map +1 -0
  69. package/dist/esm/zod-year-month.d.ts +76 -0
  70. package/dist/esm/zod-year-month.d.ts.map +1 -0
  71. package/dist/esm/zod-year-month.js +115 -0
  72. package/dist/esm/zod-year-month.js.map +1 -0
  73. package/dist/esm/zod.module-override.d.ts +139 -156
  74. package/dist/esm/zod.module-override.d.ts.map +1 -1
  75. package/dist/esm/zod.module-override.js +176 -6
  76. package/dist/esm/zod.module-override.js.map +1 -1
  77. package/dist/tsconfig.build.tsbuildinfo +1 -1
  78. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"zod-versioned.d.ts","sourceRoot":"","sources":["../../../src/zod-versioned.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,cAAc,EAKd,YAAY,EACZ,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,gCAAqC,GAC7C,kCAAkC,CAyBpC;AAED;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,SAAS,GAAG;IAC7D,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,kCAAkC,CAAC;IACnE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,kCAAkC,CAAC;IACvH,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,kCAAkC,CAAC;IASnI,aAAa,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,kCAAkC,CAAC;CACjF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,gCAAgC,GACzC,kCAAkC,CAEpC;AAMD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,CAChE,CAAC,GAAG;IACF,cAAc,EAAE,CAAC,CAAC,SAAS,CAAC;IAC5B,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C,CACF,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAC/C,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EACtB,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAAC,CAAC,CAAC,CA6CpB;AASD;;;;;;;GAOG;AACH;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG;IACnC,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,oBAAoB,CAAC,EAAE,IAAI,CAAC;CAC7B,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,WAAW,EAAE,CAAC,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,CAAC,EAAE,CAAA;CAAE,GAC/D;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;CACtC,CA6BA;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,cAAc,EAAE,CAAC,EAAE,EACnB,aAAa,EAAE,MAAM,GACpB,CAAC,GAAG,SAAS,CAGf;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,cAAc,EAAE,CAAC,EAAE,GAClB,CAAC,GAAG,SAAS,CAGf"}
1
+ {"version":3,"file":"zod-versioned.d.ts","sourceRoot":"","sources":["../../src/zod-versioned.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,cAAc,EAKd,YAAY,EACZ,uBAAuB,EACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,gCAAgC;IAC/C;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,GAAE,gCAAqC,GAC7C,kCAAkC,CAyBpC;AAED;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAAG,CAAC,CAAC,SAAS,GAAG;IAC7D,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,kCAAkC,CAAC;IACnE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,kCAAkC,CAAC;IACvH,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC,YAAY,CAAC,eAAe,CAAC,GAAG,kCAAkC,CAAC;IASnI,aAAa,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,kCAAkC,CAAC;CACjF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,gCAAgC,GACzC,kCAAkC,CAEpC;AAMD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,SAAS,CAChE,CAAC,GAAG;IACF,cAAc,EAAE,CAAC,CAAC,SAAS,CAAC;IAC5B,cAAc,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C,CACF,CAAC;AAGF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAC/C,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EACtB,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAAC,CAAC,CAAC,CA6CpB;AASD;;;;;;;GAOG;AACH;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG;IACnC,iBAAiB,CAAC,EAAE,IAAI,CAAC;IACzB,oBAAoB,CAAC,EAAE,IAAI,CAAC;CAC7B,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,WAAW,EAAE,CAAC,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,CAAC,EAAE,CAAA;CAAE,GAC/D;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;CACtC,CA6BA;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,cAAc,EAAE,CAAC,EAAE,EACnB,aAAa,EAAE,MAAM,GACpB,CAAC,GAAG,SAAS,CAGf;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,EAC1C,cAAc,EAAE,CAAC,EAAE,GAClB,CAAC,GAAG,SAAS,CAGf"}
@@ -1 +1 @@
1
- {"version":3,"file":"zod-versioned.js","sourceRoot":"","sources":["../../../src/zod-versioned.ts"],"names":[],"mappings":"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","sourcesContent":["\n// ============================================================================\n// VERSIONED SCHEMA HELPERS\n// ============================================================================\n\nimport { \n DecoratorInput, \n SCHEMA_METADATA, \n CustomFieldMetadata, \n VersionedSchemaMetadata, \n VersionedForeignKeySchemaMetadata,\n ZodDecorator, \n ZodDecorator_ParamsType \n} from \"./zod-decorators.types\";\nimport { z } from \"zod\";\nimport { addChainableMethods, addDecorators } from \"./zod.module-override\";\n\n// ============================================================================\n// VERSIONED FOREIGN KEY\n// ============================================================================\n\n/**\n * z_foreignKeyVersioned() - Creates a foreign key reference to versioned data.\n * \n * This creates a string schema representing a reference to a versioned resource.\n * The reference is automatically managed by the resource system and will track\n * which version of the referenced resource is being pointed to.\n * \n * @example\n * ```typescript\n * import { z_foreignKeyVersioned } from '@wildo-ai/zod-decorators';\n * \n * const schema = z.object({\n * documentRef: z_foreignKeyVersioned(),\n * contractRef: z_foreignKeyVersioned({ targetResource: 'Contract' }),\n * });\n * ```\n */\n\nexport interface VersionedForeignKeySchemaOptions {\n /** \n * Optional: The name of the target resource this foreign key references.\n * If not provided, the resource system will infer it from context.\n */\n targetResource?: string;\n}\n\n/**\n * Creates a versioned foreign key schema with metadata for detection.\n * \n * @param options - Configuration options for versioned foreign key\n * @returns A Zod string schema with metadata for versioned foreign key detection\n */\nexport function createVersionedForeignKeySchema(\n options: VersionedForeignKeySchemaOptions = {}\n): DecoratedVersionedForeignKeySchema {\n const { targetResource } = options;\n \n // Create the versioned foreign key schema as a string (object ID reference)\n const versionedFkSchema = z.string();\n \n // Store versioned foreign key metadata in SCHEMA_METADATA\n const versionedFkMetadata: VersionedForeignKeySchemaMetadata = {\n isVersionedForeignKey: true,\n ...(targetResource !== undefined && { targetResource }),\n };\n \n // Initialize or extend SCHEMA_METADATA with versioned foreign key config\n const existingMetadata: CustomFieldMetadata = (versionedFkSchema as any)[SCHEMA_METADATA] || {};\n (versionedFkSchema as any)[SCHEMA_METADATA] = {\n ...existingMetadata,\n versionedForeignKeyConfig: versionedFkMetadata,\n // Also add the IS_FOREIGN_KEY decorator\n [ZodDecorator.IS_FOREIGN_KEY]: true,\n };\n \n // Add chainable decorator methods\n addChainableMethods(versionedFkSchema);\n \n return versionedFkSchema as unknown as DecoratedVersionedForeignKeySchema;\n}\n\n/**\n * Return type for versioned foreign key schemas - includes decorator methods.\n */\nexport type DecoratedVersionedForeignKeySchema = z.ZodString & {\n [ZodDecorator.IS_DB_INDEXED](): DecoratedVersionedForeignKeySchema;\n [ZodDecorator.IS_HIDDEN](params?: ZodDecorator_ParamsType[ZodDecorator.IS_HIDDEN]): DecoratedVersionedForeignKeySchema;\n [ZodDecorator.IS_NOT_EDITABLE](params?: ZodDecorator_ParamsType[ZodDecorator.IS_NOT_EDITABLE]): DecoratedVersionedForeignKeySchema;\n // `foreignKeyUI` / `foreignKeySpec` are NOT declared here, and that is deliberate rather than an\n // omission. This type is an INTERSECTION with `z.ZodString`, and the string module augmentation\n // carries the pair — so a declaration here would be redundant. Measured 2026-09-17 by removing a\n // draft of it: the typecheck stayed clean, which is the definition of an untested claim. The same\n // reasoning retired the `FileSchemaDecoratorMethods` mixin in `zod-file.ts`; see the note there.\n //\n // `DecoratedFileSchema` DOES declare its own pair, because it extends `z.ZodObject`, whose\n // augmentation deliberately does not carry `fileUI` — a bare object is not a file.\n addDecorators(decorators: DecoratorInput[]): DecoratedVersionedForeignKeySchema;\n};\n\n/**\n * Creates a versioned foreign key schema for referencing versioned resources.\n * \n * The versioned foreign key will automatically track which version of the\n * referenced resource is being pointed to. The reference management is\n * handled by the resource system.\n * \n * @example\n * ```typescript\n * import { z_foreignKeyVersioned } from '@wildo-ai/zod-decorators';\n * \n * // Simple usage\n * z_foreignKeyVersioned()\n * \n * // With target resource specified\n * z_foreignKeyVersioned({ targetResource: 'Document' })\n * \n * // With decorators\n * z_foreignKeyVersioned().foreignKeyUI({ display: { tooltip: true } }) // tooltip text comes from i18n label system\n * ```\n */\nexport function z_foreignKeyVersioned(\n options?: VersionedForeignKeySchemaOptions\n): DecoratedVersionedForeignKeySchema {\n return createVersionedForeignKeySchema(options);\n}\n\n// ============================================================================\n// VERSIONED OBJECT DECORATOR\n// ============================================================================\n\n/**\n * Options for the versioned decorator.\n */\nexport interface VersionedSchemaOptions {\n /** Initial version number for new records (default: 1) */\n initialVersion?: number;\n}\n\n/**\n * Inferred type for a versioned schema - adds currentVersion and versionHistory.\n */\nexport type VersionedSchema<T extends z.ZodRawShape> = z.ZodObject<\n T & {\n currentVersion: z.ZodNumber;\n versionHistory: z.ZodArray<z.ZodObject<T>>;\n }\n>;\n\n\n/**\n * versioned() - Adds versioning behavior to a z.object schema.\n * \n * This decorator extends an object schema with:\n * - `currentVersion`: A hidden, non-editable integer field tracking the version number\n * - `versionHistory`: A hidden, non-editable array containing all previous versions\n * \n * The versioning system automatically:\n * - Increments `currentVersion` on each update\n * - Stores the previous state in `versionHistory` before updates\n * \n * @example\n * ```typescript\n * import { versioned } from '@wildo-ai/zod-decorators';\n * \n * const DocumentSchema = versioned(\n * z.object({\n * title: z.string(),\n * content: z.string(),\n * status: z.enum(['draft', 'published', 'archived']),\n * })\n * );\n * \n * // Inferred type:\n * // {\n * // title: string;\n * // content: string;\n * // status: 'draft' | 'published' | 'archived';\n * // currentVersion: number; // hidden, not editable\n * // versionHistory: Array<{ title: string; content: string; status: ... }>; // hidden, not editable\n * // }\n * ```\n */\nexport function versioned<T extends z.ZodRawShape>(\n schema: z.ZodObject<T>,\n options: VersionedSchemaOptions = {}\n): VersionedSchema<T> {\n const { initialVersion = 1 } = options;\n \n // Create the currentVersion field with IS_HIDDEN and IS_NOT_EDITABLE decorators\n const currentVersionSchema = addDecorators(\n z.number().int().min(1).default(initialVersion),\n [\n { ref: ZodDecorator.IS_HIDDEN, params: undefined },\n { ref: ZodDecorator.IS_NOT_EDITABLE, params: undefined },\n ]\n );\n \n // Create the versionHistory field - an array of the original schema\n // with IS_HIDDEN and IS_NOT_EDITABLE decorators\n const versionHistorySchema = addDecorators(\n z.array(schema).default([]),\n [\n { ref: ZodDecorator.IS_HIDDEN, params: undefined },\n { ref: ZodDecorator.IS_NOT_EDITABLE, params: undefined },\n ]\n );\n \n // Extend the original schema with versioning fields\n const versionedSchema = schema.extend({\n currentVersion: currentVersionSchema,\n versionHistory: versionHistorySchema,\n });\n \n // Store versioned metadata in SCHEMA_METADATA\n const versionedMetadata: VersionedSchemaMetadata = {\n isVersioned: true,\n initialVersion,\n };\n \n // Initialize or extend SCHEMA_METADATA with versioned config\n const existingMetadata: CustomFieldMetadata = (versionedSchema as any)[SCHEMA_METADATA] || {};\n (versionedSchema as any)[SCHEMA_METADATA] = {\n ...existingMetadata,\n versionedConfig: versionedMetadata,\n };\n \n // Add chainable decorator methods\n addChainableMethods(versionedSchema);\n \n return versionedSchema as unknown as VersionedSchema<T>;\n}\n\n// ============================================================================\n// VERSIONED UTILITY FUNCTIONS\n// ============================================================================\n// Note: Detection helpers (isVersionedForeignKeySchema, isVersionedObjectSchema, \n// getVersionedForeignKeyMetadata, getVersionedObjectMetadata) are in zod-utils.shared.ts\n// These handle schema unwrapping for optional/nullable wrappers.\n\n/**\n * Create a new version from existing data.\n * This is a utility for creating version snapshots.\n * \n * @param currentData - The current data object\n * @param currentVersion - The current version number\n * @returns Object with updated version and history\n */\n/**\n * Version snapshot with timestamp metadata.\n */\nexport type VersionSnapshot<T> = T & { \n _versionCreatedAt?: Date; \n _versionSupersededAt?: Date; \n};\n\nexport function createVersionSnapshot<T extends Record<string, unknown>>(\n currentData: T & { currentVersion: number; versionHistory: T[] },\n): { \n newVersion: number; \n updatedHistory: VersionSnapshot<T>[];\n} {\n const { currentVersion, versionHistory, ...dataWithoutVersioning } = currentData;\n \n // Create a snapshot of the current state\n // Use unknown intermediate cast for type safety\n const snapshot = {\n ...dataWithoutVersioning,\n _versionCreatedAt: new Date(),\n } as unknown as VersionSnapshot<T>;\n \n // Mark the last entry in history as superseded if exists\n const updatedHistory = [...versionHistory] as unknown as VersionSnapshot<T>[];\n if (updatedHistory.length > 0) {\n const lastEntry = updatedHistory[updatedHistory.length - 1];\n if (lastEntry && !lastEntry._versionSupersededAt) {\n updatedHistory[updatedHistory.length - 1] = {\n ...lastEntry,\n _versionSupersededAt: new Date(),\n };\n }\n }\n \n // Add current state as new history entry\n updatedHistory.push(snapshot);\n \n return {\n newVersion: currentVersion + 1,\n updatedHistory,\n };\n}\n\n/**\n * Get a specific version from history.\n * \n * @param versionHistory - The version history array\n * @param versionNumber - The version number to retrieve (1-based)\n * @returns The version data or undefined if not found\n */\nexport function getVersionFromHistory<T>(\n versionHistory: T[],\n versionNumber: number\n): T | undefined {\n // Version numbers are 1-based, array is 0-based\n return versionHistory[versionNumber - 1];\n}\n\n/**\n * Get the latest version from history (not including current).\n * \n * @param versionHistory - The version history array\n * @returns The most recent historical version or undefined\n */\nexport function getLatestHistoricalVersion<T>(\n versionHistory: T[]\n): T | undefined {\n if (versionHistory.length === 0) return undefined;\n return versionHistory[versionHistory.length - 1];\n}\n\n"]}
1
+ {"version":3,"file":"zod-versioned.js","sourceRoot":"","sources":["../../src/zod-versioned.ts"],"names":[],"mappings":"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","sourcesContent":["\n// ============================================================================\n// VERSIONED SCHEMA HELPERS\n// ============================================================================\n\nimport { \n DecoratorInput, \n SCHEMA_METADATA, \n CustomFieldMetadata, \n VersionedSchemaMetadata, \n VersionedForeignKeySchemaMetadata,\n ZodDecorator, \n ZodDecorator_ParamsType \n} from \"./zod-decorators.types\";\nimport { z } from \"zod\";\nimport { addChainableMethods, addDecorators } from \"./zod.module-override\";\n\n// ============================================================================\n// VERSIONED FOREIGN KEY\n// ============================================================================\n\n/**\n * z_foreignKeyVersioned() - Creates a foreign key reference to versioned data.\n * \n * This creates a string schema representing a reference to a versioned resource.\n * The reference is automatically managed by the resource system and will track\n * which version of the referenced resource is being pointed to.\n * \n * @example\n * ```typescript\n * import { z_foreignKeyVersioned } from '@wildo-ai/zod-decorators';\n * \n * const schema = z.object({\n * documentRef: z_foreignKeyVersioned(),\n * contractRef: z_foreignKeyVersioned({ targetResource: 'Contract' }),\n * });\n * ```\n */\n\nexport interface VersionedForeignKeySchemaOptions {\n /** \n * Optional: The name of the target resource this foreign key references.\n * If not provided, the resource system will infer it from context.\n */\n targetResource?: string;\n}\n\n/**\n * Creates a versioned foreign key schema with metadata for detection.\n * \n * @param options - Configuration options for versioned foreign key\n * @returns A Zod string schema with metadata for versioned foreign key detection\n */\nexport function createVersionedForeignKeySchema(\n options: VersionedForeignKeySchemaOptions = {}\n): DecoratedVersionedForeignKeySchema {\n const { targetResource } = options;\n \n // Create the versioned foreign key schema as a string (object ID reference)\n const versionedFkSchema = z.string();\n \n // Store versioned foreign key metadata in SCHEMA_METADATA\n const versionedFkMetadata: VersionedForeignKeySchemaMetadata = {\n isVersionedForeignKey: true,\n ...(targetResource !== undefined && { targetResource }),\n };\n \n // Initialize or extend SCHEMA_METADATA with versioned foreign key config\n const existingMetadata: CustomFieldMetadata = (versionedFkSchema as any)[SCHEMA_METADATA] || {};\n (versionedFkSchema as any)[SCHEMA_METADATA] = {\n ...existingMetadata,\n versionedForeignKeyConfig: versionedFkMetadata,\n // Also add the IS_FOREIGN_KEY decorator\n [ZodDecorator.IS_FOREIGN_KEY]: true,\n };\n \n // Add chainable decorator methods\n addChainableMethods(versionedFkSchema);\n \n return versionedFkSchema as unknown as DecoratedVersionedForeignKeySchema;\n}\n\n/**\n * Return type for versioned foreign key schemas - includes decorator methods.\n */\nexport type DecoratedVersionedForeignKeySchema = z.ZodString & {\n [ZodDecorator.IS_DB_INDEXED](): DecoratedVersionedForeignKeySchema;\n [ZodDecorator.IS_HIDDEN](params?: ZodDecorator_ParamsType[ZodDecorator.IS_HIDDEN]): DecoratedVersionedForeignKeySchema;\n [ZodDecorator.IS_NOT_EDITABLE](params?: ZodDecorator_ParamsType[ZodDecorator.IS_NOT_EDITABLE]): DecoratedVersionedForeignKeySchema;\n // `foreignKeyUI` / `foreignKeySpec` are NOT declared here, and that is deliberate rather than an\n // omission. This type is an INTERSECTION with `z.ZodString`, and the string module augmentation\n // carries the pair — so a declaration here would be redundant. Measured 2026-09-17 by removing a\n // draft of it: the typecheck stayed clean, which is the definition of an untested claim. The same\n // reasoning retired the `FileSchemaDecoratorMethods` mixin in `zod-file.ts`; see the note there.\n //\n // `DecoratedFileSchema` DOES declare its own pair, because it extends `z.ZodObject`, whose\n // augmentation deliberately does not carry `fileUI` — a bare object is not a file.\n addDecorators(decorators: DecoratorInput[]): DecoratedVersionedForeignKeySchema;\n};\n\n/**\n * Creates a versioned foreign key schema for referencing versioned resources.\n * \n * The versioned foreign key will automatically track which version of the\n * referenced resource is being pointed to. The reference management is\n * handled by the resource system.\n * \n * @example\n * ```typescript\n * import { z_foreignKeyVersioned } from '@wildo-ai/zod-decorators';\n * \n * // Simple usage\n * z_foreignKeyVersioned()\n * \n * // With target resource specified\n * z_foreignKeyVersioned({ targetResource: 'Document' })\n * \n * // With decorators\n * z_foreignKeyVersioned().foreignKeyUI({ display: { tooltip: true } }) // tooltip text comes from i18n label system\n * ```\n */\nexport function z_foreignKeyVersioned(\n options?: VersionedForeignKeySchemaOptions\n): DecoratedVersionedForeignKeySchema {\n return createVersionedForeignKeySchema(options);\n}\n\n// ============================================================================\n// VERSIONED OBJECT DECORATOR\n// ============================================================================\n\n/**\n * Options for the versioned decorator.\n */\nexport interface VersionedSchemaOptions {\n /** Initial version number for new records (default: 1) */\n initialVersion?: number;\n}\n\n/**\n * Inferred type for a versioned schema - adds currentVersion and versionHistory.\n */\nexport type VersionedSchema<T extends z.ZodRawShape> = z.ZodObject<\n T & {\n currentVersion: z.ZodNumber;\n versionHistory: z.ZodArray<z.ZodObject<T>>;\n }\n>;\n\n\n/**\n * versioned() - Adds versioning behavior to a z.object schema.\n * \n * This decorator extends an object schema with:\n * - `currentVersion`: A hidden, non-editable integer field tracking the version number\n * - `versionHistory`: A hidden, non-editable array containing all previous versions\n * \n * The versioning system automatically:\n * - Increments `currentVersion` on each update\n * - Stores the previous state in `versionHistory` before updates\n * \n * @example\n * ```typescript\n * import { versioned } from '@wildo-ai/zod-decorators';\n * \n * const DocumentSchema = versioned(\n * z.object({\n * title: z.string(),\n * content: z.string(),\n * status: z.enum(['draft', 'published', 'archived']),\n * })\n * );\n * \n * // Inferred type:\n * // {\n * // title: string;\n * // content: string;\n * // status: 'draft' | 'published' | 'archived';\n * // currentVersion: number; // hidden, not editable\n * // versionHistory: Array<{ title: string; content: string; status: ... }>; // hidden, not editable\n * // }\n * ```\n */\nexport function versioned<T extends z.ZodRawShape>(\n schema: z.ZodObject<T>,\n options: VersionedSchemaOptions = {}\n): VersionedSchema<T> {\n const { initialVersion = 1 } = options;\n \n // Create the currentVersion field with IS_HIDDEN and IS_NOT_EDITABLE decorators\n const currentVersionSchema = addDecorators(\n z.number().int().min(1).default(initialVersion),\n [\n { ref: ZodDecorator.IS_HIDDEN, params: undefined },\n { ref: ZodDecorator.IS_NOT_EDITABLE, params: undefined },\n ]\n );\n \n // Create the versionHistory field - an array of the original schema\n // with IS_HIDDEN and IS_NOT_EDITABLE decorators\n const versionHistorySchema = addDecorators(\n z.array(schema).default([]),\n [\n { ref: ZodDecorator.IS_HIDDEN, params: undefined },\n { ref: ZodDecorator.IS_NOT_EDITABLE, params: undefined },\n ]\n );\n \n // Extend the original schema with versioning fields\n const versionedSchema = schema.extend({\n currentVersion: currentVersionSchema,\n versionHistory: versionHistorySchema,\n });\n \n // Store versioned metadata in SCHEMA_METADATA\n const versionedMetadata: VersionedSchemaMetadata = {\n isVersioned: true,\n initialVersion,\n };\n \n // Initialize or extend SCHEMA_METADATA with versioned config\n const existingMetadata: CustomFieldMetadata = (versionedSchema as any)[SCHEMA_METADATA] || {};\n (versionedSchema as any)[SCHEMA_METADATA] = {\n ...existingMetadata,\n versionedConfig: versionedMetadata,\n };\n \n // Add chainable decorator methods\n addChainableMethods(versionedSchema);\n \n return versionedSchema as unknown as VersionedSchema<T>;\n}\n\n// ============================================================================\n// VERSIONED UTILITY FUNCTIONS\n// ============================================================================\n// Note: Detection helpers (isVersionedForeignKeySchema, isVersionedObjectSchema, \n// getVersionedForeignKeyMetadata, getVersionedObjectMetadata) are in zod-utils.shared.ts\n// These handle schema unwrapping for optional/nullable wrappers.\n\n/**\n * Create a new version from existing data.\n * This is a utility for creating version snapshots.\n * \n * @param currentData - The current data object\n * @param currentVersion - The current version number\n * @returns Object with updated version and history\n */\n/**\n * Version snapshot with timestamp metadata.\n */\nexport type VersionSnapshot<T> = T & { \n _versionCreatedAt?: Date; \n _versionSupersededAt?: Date; \n};\n\nexport function createVersionSnapshot<T extends Record<string, unknown>>(\n currentData: T & { currentVersion: number; versionHistory: T[] },\n): { \n newVersion: number; \n updatedHistory: VersionSnapshot<T>[];\n} {\n const { currentVersion, versionHistory, ...dataWithoutVersioning } = currentData;\n \n // Create a snapshot of the current state\n // Use unknown intermediate cast for type safety\n const snapshot = {\n ...dataWithoutVersioning,\n _versionCreatedAt: new Date(),\n } as unknown as VersionSnapshot<T>;\n \n // Mark the last entry in history as superseded if exists\n const updatedHistory = [...versionHistory] as unknown as VersionSnapshot<T>[];\n if (updatedHistory.length > 0) {\n const lastEntry = updatedHistory[updatedHistory.length - 1];\n if (lastEntry && !lastEntry._versionSupersededAt) {\n updatedHistory[updatedHistory.length - 1] = {\n ...lastEntry,\n _versionSupersededAt: new Date(),\n };\n }\n }\n \n // Add current state as new history entry\n updatedHistory.push(snapshot);\n \n return {\n newVersion: currentVersion + 1,\n updatedHistory,\n };\n}\n\n/**\n * Get a specific version from history.\n * \n * @param versionHistory - The version history array\n * @param versionNumber - The version number to retrieve (1-based)\n * @returns The version data or undefined if not found\n */\nexport function getVersionFromHistory<T>(\n versionHistory: T[],\n versionNumber: number\n): T | undefined {\n // Version numbers are 1-based, array is 0-based\n return versionHistory[versionNumber - 1];\n}\n\n/**\n * Get the latest version from history (not including current).\n * \n * @param versionHistory - The version history array\n * @returns The most recent historical version or undefined\n */\nexport function getLatestHistoricalVersion<T>(\n versionHistory: T[]\n): T | undefined {\n if (versionHistory.length === 0) return undefined;\n return versionHistory[versionHistory.length - 1];\n}\n\n"]}
@@ -0,0 +1,107 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * A `Date` field that accepts its JSON wire form: the schema to use wherever a date arrives as text
4
+ * (an HTTP body, a WebSocket or queue message, a JSON config file) instead of `z.coerce.date()`.
5
+ *
6
+ * Import from `@wildo-ai/zod-decorators`.
7
+ *
8
+ * ## What it accepts
9
+ *
10
+ * | input | result |
11
+ * |---|---|
12
+ * | a `Date` | kept as it is (an invalid `Date` is refused) |
13
+ * | an RFC 3339 date-time (`"2026-11-06T09:00:00Z"`, `"2026-11-06T09:00:00.5+02:00"`) | that instant |
14
+ * | an RFC 3339 full-date (`"2026-11-06"`) | UTC midnight of that day, how a calendar-day field holds its value |
15
+ * | any other string (`"1"`, `"May 2"`, `"2026-02-30"`, `"2026-11-06T09:00"`, `"2026-11-06 09:00:00"`) | refused |
16
+ * | a number (epoch milliseconds) | `new Date(number)` |
17
+ * | `null`, `undefined`, a boolean, an object | **not coerced**, so the date check refuses it unless a wrapper decides first |
18
+ *
19
+ * ## Why only RFC 3339 (#3482)
20
+ *
21
+ * It read any string `new Date()` accepts, and `new Date()` accepts a great deal: `"1"` is 2001-01-01, `"May 2"` is
22
+ * 2001-05-02, `"123"` is the year 122, `"2026-02-30"` rolls over to March 2, and a time with no offset is read in the
23
+ * server's OWN zone. A wire date is what the API documents as `format: date-time`, the RFC 3339 production, so that is
24
+ * what it reads: a date, `T`, a time WITH seconds, an optional fraction, and `Z` or a `±hh:mm` offset (`t` and `z` in
25
+ * lower case too, which RFC 3339 §5.6 allows), or a full-date alone. The parts are checked (no February 30, no hour 24)
26
+ * and the instant is computed from them rather than handed to `Date.parse`, so a browser and the server read the same
27
+ * string the same way. A leap second (`:60`), which RFC 3339 allows, is refused: a JavaScript `Date` cannot hold it. The engine's own clients send `Date.prototype.toJSON()`, which is always this form.
28
+ *
29
+ * A date another system writes in its own form (Odoo's `"YYYY-MM-DD HH:MM:SS"`, OData v2's `"/Date(ms)/"`) is that
30
+ * system's encoding and is decoded where its transport is spoken, never here.
31
+ *
32
+ * spec-cite: N/A — RFC 3339 §5.6 is named, not quoted; the production is restated in the reader below.
33
+ *
34
+ * So `null` keeps its meaning. On `z_wireDate().nullable()` it is kept as `null`; on `.optional()` or a bare
35
+ * `z_wireDate()` it is refused with `expected date`, the answer every other field type gives for a `null`
36
+ * it does not admit.
37
+ *
38
+ * ## Why not `z.coerce.date()` (#3434)
39
+ *
40
+ * `z.coerce.date()` runs `new Date(input)` on EVERY input before the date check. `new Date(null)` is
41
+ * `1970-01-01T00:00:00.000Z`, a valid date, so a `null` sent for an optional date was stored as the epoch:
42
+ * measured on the promotions CREATE door, where `validUntil: null` persisted an already-expired promotion.
43
+ * `.optional()` does not stop it (Zod short-circuits only `undefined`), and neither does `.default()`.
44
+ * `new Date(true)` is one millisecond past the epoch for the same reason.
45
+ *
46
+ * ## Why it is still a `ZodDate`
47
+ *
48
+ * A `z.preprocess(…, z.date())` would coerce selectively too, but it is a `pipe`: every reader that
49
+ * classifies a field by its Zod type (the field renderer's DATE/DATETIME choice, the request and response
50
+ * date walkers, the OpenAPI writer, `unwrapSchema`, which follows a pipe's INPUT side) would stop seeing a
51
+ * date. So this is a Zod date constructor (`z.core.$constructor`, Zod 4's extension seam, reached through `z.core`
52
+ * because browser bundles refuse a `zod/*` subpath import) that runs the ordinary `ZodDate` initializer. It keeps
53
+ * `def.type === 'date'`, `instanceof z.ZodDate`, every decorator installed on `ZodDate.prototype`, and its own
54
+ * constructor across `.clone()` (which `.describe()`, `.meta()` and `.min()` all go through). It only changes WHICH
55
+ * inputs are converted before the check.
56
+ *
57
+ * One consequence of that seam: a wire date's prototype is its own, and it receives `ZodDate.prototype`'s methods as a
58
+ * COPY made when it is constructed. A decorator installed on `ZodDate.prototype` after a wire date was built is missing
59
+ * from that instance, where a plain `z.date()` would still reach it through the prototype chain. The package installs
60
+ * every decorator when it is imported (`index.ts`), before any schema can be built, so nothing meets this today.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { z } from 'zod';
65
+ * import { z_wireDate } from '@wildo-ai/zod-decorators';
66
+ *
67
+ * const ReminderMessage = z.object({
68
+ * sentAt: z_wireDate(), // "2026-11-06T09:00:00Z" → Date; null → refused
69
+ * snoozedUntil: z_wireDate().nullable(), // null → null
70
+ * });
71
+ * ```
72
+ *
73
+ * The type parameter is the declared INPUT type, as on `z.coerce.date<T>()`: inferred from a `.pipe()` source, `unknown`
74
+ * otherwise.
75
+ */
76
+ export declare function z_wireDate<T = unknown>(params?: string | z.core.$ZodDateParams): z.ZodCoercedDate<T>;
77
+ /**
78
+ * The wire date that reads `source`'s JSON form and keeps everything else `source` says: its checks (`.min()`,
79
+ * `.max()`, `.refine()`, which Zod 4 stores on the date itself, not as wrappers) and its custom error (#3477).
80
+ *
81
+ * Framework-internal: the network-boundary conversion uses it to rebuild an authored `z.date()`. Rebuilding with
82
+ * `z_wireDate()` dropped every check, so a `.min()` declared on a request field was never enforced. Metadata
83
+ * (decorators, description) is the caller's to carry: it lives outside the definition.
84
+ */
85
+ export declare function wireDateFrom(source: z.ZodTypeAny): z.ZodCoercedDate;
86
+ /**
87
+ * Is this schema a {@link z_wireDate} leaf? The mark survives `.clone()`, so a described or decorated wire date
88
+ * still answers true.
89
+ */
90
+ export declare function isWireDateSchema(schema: unknown): boolean;
91
+ /**
92
+ * Converts only the two JSON forms of a date: an RFC 3339 string and epoch milliseconds. A string that is not RFC 3339
93
+ * becomes an invalid `Date`, so the date check refuses it as an invalid date rather than as a wrong type. Everything
94
+ * else is handed to the check as it came, so a wrong type is reported as a wrong type rather than turned into a
95
+ * plausible instant.
96
+ *
97
+ * Exported for the framework's VALUE walks (the request pre-pass, the PostgreSQL jsonb restore, the collection-filter
98
+ * readers), which turn a date's JSON form into a `Date` before or outside a schema parse (#3476). They used
99
+ * `new Date(value)`, which read `"May 2"` as 2001-05-02 and so undid #3482 on every path that ran before the parse.
100
+ * Framework-internal: an application writes `z.date()` and never reads a wire value by hand.
101
+ *
102
+ * Overloaded so a caller holding a string or a number gets a `Date` back (always, possibly invalid), which the pipeline's
103
+ * `ISO_STRING_TO_DATE` coercion relies on (#3485); any other value comes back as it came.
104
+ */
105
+ export declare function readWireDateValue(value: string | number): Date;
106
+ export declare function readWireDateValue(value: unknown): unknown;
107
+ //# sourceMappingURL=zod-wire-date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-wire-date.d.ts","sourceRoot":"","sources":["../../src/zod-wire-date.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEG;AACH,wBAAgB,UAAU,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAEpG;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,cAAc,CAGnE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEzD;AAiCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AAChE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC"}
@@ -0,0 +1,162 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * A `Date` field that accepts its JSON wire form: the schema to use wherever a date arrives as text
4
+ * (an HTTP body, a WebSocket or queue message, a JSON config file) instead of `z.coerce.date()`.
5
+ *
6
+ * Import from `@wildo-ai/zod-decorators`.
7
+ *
8
+ * ## What it accepts
9
+ *
10
+ * | input | result |
11
+ * |---|---|
12
+ * | a `Date` | kept as it is (an invalid `Date` is refused) |
13
+ * | an RFC 3339 date-time (`"2026-11-06T09:00:00Z"`, `"2026-11-06T09:00:00.5+02:00"`) | that instant |
14
+ * | an RFC 3339 full-date (`"2026-11-06"`) | UTC midnight of that day, how a calendar-day field holds its value |
15
+ * | any other string (`"1"`, `"May 2"`, `"2026-02-30"`, `"2026-11-06T09:00"`, `"2026-11-06 09:00:00"`) | refused |
16
+ * | a number (epoch milliseconds) | `new Date(number)` |
17
+ * | `null`, `undefined`, a boolean, an object | **not coerced**, so the date check refuses it unless a wrapper decides first |
18
+ *
19
+ * ## Why only RFC 3339 (#3482)
20
+ *
21
+ * It read any string `new Date()` accepts, and `new Date()` accepts a great deal: `"1"` is 2001-01-01, `"May 2"` is
22
+ * 2001-05-02, `"123"` is the year 122, `"2026-02-30"` rolls over to March 2, and a time with no offset is read in the
23
+ * server's OWN zone. A wire date is what the API documents as `format: date-time`, the RFC 3339 production, so that is
24
+ * what it reads: a date, `T`, a time WITH seconds, an optional fraction, and `Z` or a `±hh:mm` offset (`t` and `z` in
25
+ * lower case too, which RFC 3339 §5.6 allows), or a full-date alone. The parts are checked (no February 30, no hour 24)
26
+ * and the instant is computed from them rather than handed to `Date.parse`, so a browser and the server read the same
27
+ * string the same way. A leap second (`:60`), which RFC 3339 allows, is refused: a JavaScript `Date` cannot hold it. The engine's own clients send `Date.prototype.toJSON()`, which is always this form.
28
+ *
29
+ * A date another system writes in its own form (Odoo's `"YYYY-MM-DD HH:MM:SS"`, OData v2's `"/Date(ms)/"`) is that
30
+ * system's encoding and is decoded where its transport is spoken, never here.
31
+ *
32
+ * spec-cite: N/A — RFC 3339 §5.6 is named, not quoted; the production is restated in the reader below.
33
+ *
34
+ * So `null` keeps its meaning. On `z_wireDate().nullable()` it is kept as `null`; on `.optional()` or a bare
35
+ * `z_wireDate()` it is refused with `expected date`, the answer every other field type gives for a `null`
36
+ * it does not admit.
37
+ *
38
+ * ## Why not `z.coerce.date()` (#3434)
39
+ *
40
+ * `z.coerce.date()` runs `new Date(input)` on EVERY input before the date check. `new Date(null)` is
41
+ * `1970-01-01T00:00:00.000Z`, a valid date, so a `null` sent for an optional date was stored as the epoch:
42
+ * measured on the promotions CREATE door, where `validUntil: null` persisted an already-expired promotion.
43
+ * `.optional()` does not stop it (Zod short-circuits only `undefined`), and neither does `.default()`.
44
+ * `new Date(true)` is one millisecond past the epoch for the same reason.
45
+ *
46
+ * ## Why it is still a `ZodDate`
47
+ *
48
+ * A `z.preprocess(…, z.date())` would coerce selectively too, but it is a `pipe`: every reader that
49
+ * classifies a field by its Zod type (the field renderer's DATE/DATETIME choice, the request and response
50
+ * date walkers, the OpenAPI writer, `unwrapSchema`, which follows a pipe's INPUT side) would stop seeing a
51
+ * date. So this is a Zod date constructor (`z.core.$constructor`, Zod 4's extension seam, reached through `z.core`
52
+ * because browser bundles refuse a `zod/*` subpath import) that runs the ordinary `ZodDate` initializer. It keeps
53
+ * `def.type === 'date'`, `instanceof z.ZodDate`, every decorator installed on `ZodDate.prototype`, and its own
54
+ * constructor across `.clone()` (which `.describe()`, `.meta()` and `.min()` all go through). It only changes WHICH
55
+ * inputs are converted before the check.
56
+ *
57
+ * One consequence of that seam: a wire date's prototype is its own, and it receives `ZodDate.prototype`'s methods as a
58
+ * COPY made when it is constructed. A decorator installed on `ZodDate.prototype` after a wire date was built is missing
59
+ * from that instance, where a plain `z.date()` would still reach it through the prototype chain. The package installs
60
+ * every decorator when it is imported (`index.ts`), before any schema can be built, so nothing meets this today.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { z } from 'zod';
65
+ * import { z_wireDate } from '@wildo-ai/zod-decorators';
66
+ *
67
+ * const ReminderMessage = z.object({
68
+ * sentAt: z_wireDate(), // "2026-11-06T09:00:00Z" → Date; null → refused
69
+ * snoozedUntil: z_wireDate().nullable(), // null → null
70
+ * });
71
+ * ```
72
+ *
73
+ * The type parameter is the declared INPUT type, as on `z.coerce.date<T>()`: inferred from a `.pipe()` source, `unknown`
74
+ * otherwise.
75
+ */
76
+ export function z_wireDate(params) {
77
+ return new ZodWireDate({ type: 'date', ...z.core.util.normalizeParams(params) });
78
+ }
79
+ /**
80
+ * The wire date that reads `source`'s JSON form and keeps everything else `source` says: its checks (`.min()`,
81
+ * `.max()`, `.refine()`, which Zod 4 stores on the date itself, not as wrappers) and its custom error (#3477).
82
+ *
83
+ * Framework-internal: the network-boundary conversion uses it to rebuild an authored `z.date()`. Rebuilding with
84
+ * `z_wireDate()` dropped every check, so a `.min()` declared on a request field was never enforced. Metadata
85
+ * (decorators, description) is the caller's to carry: it lives outside the definition.
86
+ */
87
+ export function wireDateFrom(source) {
88
+ const definition = source._zod.def;
89
+ return new ZodWireDate({ ...definition, coerce: false });
90
+ }
91
+ /**
92
+ * Is this schema a {@link z_wireDate} leaf? The mark survives `.clone()`, so a described or decorated wire date
93
+ * still answers true.
94
+ */
95
+ export function isWireDateSchema(schema) {
96
+ return schema?._zod?.traits?.has(WIRE_DATE_TRAIT) === true;
97
+ }
98
+ const WIRE_DATE_TRAIT = 'ZodWireDate';
99
+ /**
100
+ * RFC 3339 full-date, optionally followed by `T` and a full-time (seconds required, fraction optional, `Z` or a
101
+ * `±hh:mm` offset). Groups: year, month, day, then hour, minute, second, fraction, and the offset (`Z`, or its sign,
102
+ * hours and minutes).
103
+ */
104
+ const RFC3339_DATE_TIME = /^(\d{4})-(\d{2})-(\d{2})(?:[Tt](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:([Zz])|([+-])(\d{2}):(\d{2})))?$/;
105
+ /**
106
+ * The instant an RFC 3339 string names, or `undefined` when it is not one (wrong shape, or a part out of range).
107
+ * Computed from the parts with UTC arithmetic, never `Date.parse`, so no engine's own leniency or zone applies.
108
+ */
109
+ function readRfc3339(text) {
110
+ const match = RFC3339_DATE_TIME.exec(text);
111
+ if (match === null)
112
+ return undefined;
113
+ const [, year, month, day, hour = '00', minute = '00', second = '00', fraction = '', , sign, offsetHours, offsetMinutes] = match;
114
+ const [y, mo, d, h, mi, s] = [year, month, day, hour, minute, second].map(Number);
115
+ if (mo < 1 || mo > 12 || h > 23 || mi > 59 || s > 59)
116
+ return undefined;
117
+ if (offsetHours !== undefined && (Number(offsetHours) > 23 || Number(offsetMinutes) > 59))
118
+ return undefined;
119
+ const instant = new Date(0);
120
+ // `setUTCFullYear`, not `Date.UTC`: the latter reads a year below 100 as 1900 + year.
121
+ instant.setUTCFullYear(y, mo - 1, d);
122
+ // A day past the month's end rolls over (February 30 becomes March 2); comparing the parts back refuses it.
123
+ if (instant.getUTCFullYear() !== y || instant.getUTCMonth() !== mo - 1 || instant.getUTCDate() !== d)
124
+ return undefined;
125
+ instant.setUTCHours(h, mi, s, Number(fraction.slice(0, 3).padEnd(3, '0')));
126
+ if (sign === undefined)
127
+ return instant;
128
+ const offsetMs = (Number(offsetHours) * 60 + Number(offsetMinutes)) * 60_000;
129
+ return new Date(instant.getTime() + (sign === '+' ? -offsetMs : offsetMs));
130
+ }
131
+ export function readWireDateValue(value) {
132
+ if (typeof value === 'number')
133
+ return new Date(value);
134
+ if (typeof value === 'string')
135
+ return readRfc3339(value) ?? new Date(Number.NaN);
136
+ return value;
137
+ }
138
+ /**
139
+ * How a wire date is written in JSON Schema: an RFC 3339 string. Without it `z.toJSONSchema` throws ("Date cannot be
140
+ * represented in JSON Schema") under its default setting, which is the setting the AI SDK and the strict
141
+ * structured-output derivation use, so no agent tool or structured output could declare a date (#3482). The OpenAPI
142
+ * and MCP writers already map every date to this same shape in their own `override`, so their output is unchanged.
143
+ *
144
+ * Installed as `processJSONSchema` (written into the node Zod is building), not `toJSONSchema` (a replacement node):
145
+ * in zod 4.3.6 a `toJSONSchema` node skips processing its `_zod.parent`, and every `.describe()` clone has one, so the
146
+ * finalizer then crashes reading the unprocessed parent (`Cannot read properties of undefined (reading 'ref')`).
147
+ */
148
+ function writeWireDateJsonSchema(_ctx, json) {
149
+ json.type = 'string';
150
+ json.format = 'date-time';
151
+ }
152
+ const ZodWireDate = z.core.$constructor(WIRE_DATE_TRAIT, (inst, def) => {
153
+ // The def never carries `coerce: true`: the conversion is ours, so the inner parse sees an already-read value.
154
+ z.ZodDate.init(inst, { ...def, coerce: false });
155
+ const parseDate = inst._zod.parse;
156
+ inst._zod.parse = (payload, ctx) => {
157
+ payload.value = readWireDateValue(payload.value);
158
+ return parseDate(payload, ctx);
159
+ };
160
+ inst._zod.processJSONSchema = writeWireDateJsonSchema;
161
+ });
162
+ //# sourceMappingURL=zod-wire-date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-wire-date.js","sourceRoot":"","sources":["../../src/zod-wire-date.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\n\n/**\n * A `Date` field that accepts its JSON wire form: the schema to use wherever a date arrives as text\n * (an HTTP body, a WebSocket or queue message, a JSON config file) instead of `z.coerce.date()`.\n *\n * Import from `@wildo-ai/zod-decorators`.\n *\n * ## What it accepts\n *\n * | input | result |\n * |---|---|\n * | a `Date` | kept as it is (an invalid `Date` is refused) |\n * | an RFC 3339 date-time (`\"2026-11-06T09:00:00Z\"`, `\"2026-11-06T09:00:00.5+02:00\"`) | that instant |\n * | an RFC 3339 full-date (`\"2026-11-06\"`) | UTC midnight of that day, how a calendar-day field holds its value |\n * | any other string (`\"1\"`, `\"May 2\"`, `\"2026-02-30\"`, `\"2026-11-06T09:00\"`, `\"2026-11-06 09:00:00\"`) | refused |\n * | a number (epoch milliseconds) | `new Date(number)` |\n * | `null`, `undefined`, a boolean, an object | **not coerced**, so the date check refuses it unless a wrapper decides first |\n *\n * ## Why only RFC 3339 (#3482)\n *\n * It read any string `new Date()` accepts, and `new Date()` accepts a great deal: `\"1\"` is 2001-01-01, `\"May 2\"` is\n * 2001-05-02, `\"123\"` is the year 122, `\"2026-02-30\"` rolls over to March 2, and a time with no offset is read in the\n * server's OWN zone. A wire date is what the API documents as `format: date-time`, the RFC 3339 production, so that is\n * what it reads: a date, `T`, a time WITH seconds, an optional fraction, and `Z` or a `±hh:mm` offset (`t` and `z` in\n * lower case too, which RFC 3339 §5.6 allows), or a full-date alone. The parts are checked (no February 30, no hour 24)\n * and the instant is computed from them rather than handed to `Date.parse`, so a browser and the server read the same\n * string the same way. A leap second (`:60`), which RFC 3339 allows, is refused: a JavaScript `Date` cannot hold it. The engine's own clients send `Date.prototype.toJSON()`, which is always this form.\n *\n * A date another system writes in its own form (Odoo's `\"YYYY-MM-DD HH:MM:SS\"`, OData v2's `\"/Date(ms)/\"`) is that\n * system's encoding and is decoded where its transport is spoken, never here.\n *\n * spec-cite: N/A — RFC 3339 §5.6 is named, not quoted; the production is restated in the reader below.\n *\n * So `null` keeps its meaning. On `z_wireDate().nullable()` it is kept as `null`; on `.optional()` or a bare\n * `z_wireDate()` it is refused with `expected date`, the answer every other field type gives for a `null`\n * it does not admit.\n *\n * ## Why not `z.coerce.date()` (#3434)\n *\n * `z.coerce.date()` runs `new Date(input)` on EVERY input before the date check. `new Date(null)` is\n * `1970-01-01T00:00:00.000Z`, a valid date, so a `null` sent for an optional date was stored as the epoch:\n * measured on the promotions CREATE door, where `validUntil: null` persisted an already-expired promotion.\n * `.optional()` does not stop it (Zod short-circuits only `undefined`), and neither does `.default()`.\n * `new Date(true)` is one millisecond past the epoch for the same reason.\n *\n * ## Why it is still a `ZodDate`\n *\n * A `z.preprocess(…, z.date())` would coerce selectively too, but it is a `pipe`: every reader that\n * classifies a field by its Zod type (the field renderer's DATE/DATETIME choice, the request and response\n * date walkers, the OpenAPI writer, `unwrapSchema`, which follows a pipe's INPUT side) would stop seeing a\n * date. So this is a Zod date constructor (`z.core.$constructor`, Zod 4's extension seam, reached through `z.core`\n * because browser bundles refuse a `zod/*` subpath import) that runs the ordinary `ZodDate` initializer. It keeps\n * `def.type === 'date'`, `instanceof z.ZodDate`, every decorator installed on `ZodDate.prototype`, and its own\n * constructor across `.clone()` (which `.describe()`, `.meta()` and `.min()` all go through). It only changes WHICH\n * inputs are converted before the check.\n *\n * One consequence of that seam: a wire date's prototype is its own, and it receives `ZodDate.prototype`'s methods as a\n * COPY made when it is constructed. A decorator installed on `ZodDate.prototype` after a wire date was built is missing\n * from that instance, where a plain `z.date()` would still reach it through the prototype chain. The package installs\n * every decorator when it is imported (`index.ts`), before any schema can be built, so nothing meets this today.\n *\n * @example\n * ```typescript\n * import { z } from 'zod';\n * import { z_wireDate } from '@wildo-ai/zod-decorators';\n *\n * const ReminderMessage = z.object({\n * sentAt: z_wireDate(), // \"2026-11-06T09:00:00Z\" → Date; null → refused\n * snoozedUntil: z_wireDate().nullable(), // null → null\n * });\n * ```\n *\n * The type parameter is the declared INPUT type, as on `z.coerce.date<T>()`: inferred from a `.pipe()` source, `unknown`\n * otherwise.\n */\nexport function z_wireDate<T = unknown>(params?: string | z.core.$ZodDateParams): z.ZodCoercedDate<T> {\n return new ZodWireDate({ type: 'date', ...z.core.util.normalizeParams(params) }) as unknown as z.ZodCoercedDate<T>;\n}\n\n/**\n * The wire date that reads `source`'s JSON form and keeps everything else `source` says: its checks (`.min()`,\n * `.max()`, `.refine()`, which Zod 4 stores on the date itself, not as wrappers) and its custom error (#3477).\n *\n * Framework-internal: the network-boundary conversion uses it to rebuild an authored `z.date()`. Rebuilding with\n * `z_wireDate()` dropped every check, so a `.min()` declared on a request field was never enforced. Metadata\n * (decorators, description) is the caller's to carry: it lives outside the definition.\n */\nexport function wireDateFrom(source: z.ZodTypeAny): z.ZodCoercedDate {\n const definition = (source as unknown as { _zod: { def: z.core.$ZodDateDef } })._zod.def;\n return new ZodWireDate({ ...definition, coerce: false }) as unknown as z.ZodCoercedDate;\n}\n\n/**\n * Is this schema a {@link z_wireDate} leaf? The mark survives `.clone()`, so a described or decorated wire date\n * still answers true.\n */\nexport function isWireDateSchema(schema: unknown): boolean {\n return (schema as { _zod?: { traits?: Set<string> } } | null)?._zod?.traits?.has(WIRE_DATE_TRAIT) === true;\n}\n\nconst WIRE_DATE_TRAIT = 'ZodWireDate';\n\n/**\n * RFC 3339 full-date, optionally followed by `T` and a full-time (seconds required, fraction optional, `Z` or a\n * `±hh:mm` offset). Groups: year, month, day, then hour, minute, second, fraction, and the offset (`Z`, or its sign,\n * hours and minutes).\n */\nconst RFC3339_DATE_TIME = /^(\\d{4})-(\\d{2})-(\\d{2})(?:[Tt](\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:([Zz])|([+-])(\\d{2}):(\\d{2})))?$/;\n\n/**\n * The instant an RFC 3339 string names, or `undefined` when it is not one (wrong shape, or a part out of range).\n * Computed from the parts with UTC arithmetic, never `Date.parse`, so no engine's own leniency or zone applies.\n */\nfunction readRfc3339(text: string): Date | undefined {\n const match = RFC3339_DATE_TIME.exec(text);\n if (match === null) return undefined;\n const [, year, month, day, hour = '00', minute = '00', second = '00', fraction = '', , sign, offsetHours, offsetMinutes] = match;\n const [y, mo, d, h, mi, s] = [year, month, day, hour, minute, second].map(Number) as [number, number, number, number, number, number];\n if (mo < 1 || mo > 12 || h > 23 || mi > 59 || s > 59) return undefined;\n if (offsetHours !== undefined && (Number(offsetHours) > 23 || Number(offsetMinutes) > 59)) return undefined;\n const instant = new Date(0);\n // `setUTCFullYear`, not `Date.UTC`: the latter reads a year below 100 as 1900 + year.\n instant.setUTCFullYear(y, mo - 1, d);\n // A day past the month's end rolls over (February 30 becomes March 2); comparing the parts back refuses it.\n if (instant.getUTCFullYear() !== y || instant.getUTCMonth() !== mo - 1 || instant.getUTCDate() !== d) return undefined;\n instant.setUTCHours(h, mi, s, Number(fraction.slice(0, 3).padEnd(3, '0')));\n if (sign === undefined) return instant;\n const offsetMs = (Number(offsetHours) * 60 + Number(offsetMinutes)) * 60_000;\n return new Date(instant.getTime() + (sign === '+' ? -offsetMs : offsetMs));\n}\n\n/**\n * Converts only the two JSON forms of a date: an RFC 3339 string and epoch milliseconds. A string that is not RFC 3339\n * becomes an invalid `Date`, so the date check refuses it as an invalid date rather than as a wrong type. Everything\n * else is handed to the check as it came, so a wrong type is reported as a wrong type rather than turned into a\n * plausible instant.\n *\n * Exported for the framework's VALUE walks (the request pre-pass, the PostgreSQL jsonb restore, the collection-filter\n * readers), which turn a date's JSON form into a `Date` before or outside a schema parse (#3476). They used\n * `new Date(value)`, which read `\"May 2\"` as 2001-05-02 and so undid #3482 on every path that ran before the parse.\n * Framework-internal: an application writes `z.date()` and never reads a wire value by hand.\n *\n * Overloaded so a caller holding a string or a number gets a `Date` back (always, possibly invalid), which the pipeline's\n * `ISO_STRING_TO_DATE` coercion relies on (#3485); any other value comes back as it came.\n */\nexport function readWireDateValue(value: string | number): Date;\nexport function readWireDateValue(value: unknown): unknown;\nexport function readWireDateValue(value: unknown): unknown {\n if (typeof value === 'number') return new Date(value);\n if (typeof value === 'string') return readRfc3339(value) ?? new Date(Number.NaN);\n return value;\n}\n\n/**\n * How a wire date is written in JSON Schema: an RFC 3339 string. Without it `z.toJSONSchema` throws (\"Date cannot be\n * represented in JSON Schema\") under its default setting, which is the setting the AI SDK and the strict\n * structured-output derivation use, so no agent tool or structured output could declare a date (#3482). The OpenAPI\n * and MCP writers already map every date to this same shape in their own `override`, so their output is unchanged.\n *\n * Installed as `processJSONSchema` (written into the node Zod is building), not `toJSONSchema` (a replacement node):\n * in zod 4.3.6 a `toJSONSchema` node skips processing its `_zod.parent`, and every `.describe()` clone has one, so the\n * finalizer then crashes reading the unprocessed parent (`Cannot read properties of undefined (reading 'ref')`).\n */\nfunction writeWireDateJsonSchema(_ctx: unknown, json: Record<string, unknown>): void {\n json.type = 'string';\n json.format = 'date-time';\n}\n\nconst ZodWireDate = z.core.$constructor(WIRE_DATE_TRAIT, (inst: z.ZodDate, def: z.core.$ZodDateDef) => {\n // The def never carries `coerce: true`: the conversion is ours, so the inner parse sees an already-read value.\n z.ZodDate.init(inst, { ...def, coerce: false });\n const parseDate = inst._zod.parse;\n inst._zod.parse = (payload, ctx) => {\n payload.value = readWireDateValue(payload.value);\n return parseDate(payload, ctx);\n };\n inst._zod.processJSONSchema = writeWireDateJsonSchema;\n});\n"]}
@@ -0,0 +1,76 @@
1
+ import { z } from 'zod';
2
+ /** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */
3
+ /**
4
+ * The shape of a YEAR-MONTH value: a calendar month written `YYYY-MM` (`2026-10`), month `01`–`12`.
5
+ *
6
+ * A year-month carries no day, no time and no time zone. It is stored as this string because the string is what every
7
+ * party agrees on: it sorts chronologically as text, it makes a natural part of a unique key (one budget per department
8
+ * per month), and no reader's zone can move it into the neighbouring month the way a `Date` at midnight can.
9
+ */
10
+ export declare const YEAR_MONTH_PATTERN: RegExp;
11
+ /**
12
+ * A YEAR-MONTH field: one calendar month, stored `YYYY-MM`. Use it for a period whose grain is the month — a monthly
13
+ * budget, a billing month, a reporting period — instead of a `z.date()` pinned to the first of the month (whose day and
14
+ * zone mean nothing) or a bare `z.string()` the user has to type.
15
+ *
16
+ * What the field gets from the framework:
17
+ * - the stored value is validated against {@link YEAR_MONTH_PATTERN} on every write, server side included;
18
+ * - it is READ as a month and a year in the reader's locale ("October 2026", "octobre 2026"), everywhere the value is
19
+ * shown (`StringDisplayMode.YEAR_MONTH`);
20
+ * - it is EDITED with a month-and-year picker that writes `YYYY-MM` (`StringEditMode.YEAR_MONTH`).
21
+ *
22
+ * Chain the structural decorators as on any string (`.isDBIndexed()`, `.isUnique({ compoundWith })`, `.optional()`).
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { z_yearMonth } from '@wildo-ai/zod-decorators';
27
+ *
28
+ * period: z_yearMonth().isDBIndexed().isUnique({ compoundWith: 'organizationUnitId' }),
29
+ * ```
30
+ */
31
+ export declare function z_yearMonth(): z.ZodString;
32
+ /** @wildo_source:part:end zod.factories.year-month */
33
+ /**
34
+ * The year and the month (1–12) of a `YYYY-MM` value, or `undefined` when the value is not one. The single parser every
35
+ * reader of a year-month goes through, so a display and a picker cannot disagree about what a value means.
36
+ */
37
+ export declare function parseYearMonth(value: string | null | undefined): {
38
+ year: number;
39
+ month: number;
40
+ } | undefined;
41
+ /** The `YYYY-MM` value of a year and a month (1–12). */
42
+ export declare function formatYearMonthValue(year: number, month: number): string;
43
+ /** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */
44
+ /**
45
+ * Does this schema hold a YEAR-MONTH (`YYYY-MM`)? The one question a filter panel, a chip, the companion or a
46
+ * specification asks of a field before treating it as a month, so none of them re-derives the answer from a UI mode.
47
+ *
48
+ * Answered from what the schema ENFORCES first: a string validated against {@link YEAR_MONTH_PATTERN} holds a
49
+ * year-month whatever its UI says, which is the guarantee `z_yearMonth()` makes. That is the structural marker, and it
50
+ * survives an author re-declaring the field's display or edit mode (`.stringUI({ display: … })` replaces the display
51
+ * mode and nothing else). A field that declares the year-month display or edit mode without the pattern counts too:
52
+ * its author said its values are months, and the picker writes nothing else. Wrappers (`.optional()`, `.default()`,
53
+ * `.nullable()`, a pipe's input) are looked through; a year-month on the OUTPUT side of a pipe is not seen.
54
+ *
55
+ * Because the pattern wins, a `z_yearMonth()` whose author re-declares the edit mode as a text input still answers
56
+ * `true`: the form then shows the text input the author chose, and a list filter over it still offers the month picker.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * isYearMonthField(z_yearMonth().optional()); // true
61
+ * isYearMonthField(z.string()); // false
62
+ * ```
63
+ */
64
+ export declare function isYearMonthField(schema: z.ZodTypeAny | undefined): boolean;
65
+ /** @wildo_source:part:end zod.factories.year-month */
66
+ /**
67
+ * Does this schema VALIDATE its value against {@link YEAR_MONTH_PATTERN}? The strict half of {@link isYearMonthField}:
68
+ * a field that only DISPLAYS as a month (its UI says so, nothing checks the value) answers `false`.
69
+ *
70
+ * The question a RANGE of months needs answered before it may be offered: a month range compares the stored text
71
+ * (`>=` / `<=`), which orders months correctly only when every stored value has the pattern's fixed width — a
72
+ * four-digit year and a zero-padded month. `2026-9` sorts after `2026-10` as text. A field whose writes are checked
73
+ * against the pattern holds nothing else, so only such a field may be filtered by a range.
74
+ */
75
+ export declare function enforcesYearMonthPattern(schema: z.ZodTypeAny | undefined): boolean;
76
+ //# sourceMappingURL=zod-year-month.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-year-month.d.ts","sourceRoot":"","sources":["../../src/zod-year-month.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,gGAAgG;AAChG;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAkC,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,CAAC,CAAC,SAAS,CAKzC;AACD,sDAAsD;AAEtD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI5G;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExE;AAKD,gGAAgG;AAChG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,SAAS,GAAG,OAAO,CAM1E;AACD,sDAAsD;AAEtD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,CAAC,CAAC,UAAU,GAAG,SAAS,GAAG,OAAO,CAYlF"}
@@ -0,0 +1,115 @@
1
+ import { z } from 'zod';
2
+ import { StringDisplayMode, StringEditMode } from './zod-decorators.types.js';
3
+ import { getStringUI, getZodDefArrayProperty, getZodType, unwrapSchema } from './zod-utils.shared.js';
4
+ /** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */
5
+ /**
6
+ * The shape of a YEAR-MONTH value: a calendar month written `YYYY-MM` (`2026-10`), month `01`–`12`.
7
+ *
8
+ * A year-month carries no day, no time and no time zone. It is stored as this string because the string is what every
9
+ * party agrees on: it sorts chronologically as text, it makes a natural part of a unique key (one budget per department
10
+ * per month), and no reader's zone can move it into the neighbouring month the way a `Date` at midnight can.
11
+ */
12
+ export const YEAR_MONTH_PATTERN = /^\d{4}-(0[1-9]|1[0-2])$/;
13
+ /**
14
+ * A YEAR-MONTH field: one calendar month, stored `YYYY-MM`. Use it for a period whose grain is the month — a monthly
15
+ * budget, a billing month, a reporting period — instead of a `z.date()` pinned to the first of the month (whose day and
16
+ * zone mean nothing) or a bare `z.string()` the user has to type.
17
+ *
18
+ * What the field gets from the framework:
19
+ * - the stored value is validated against {@link YEAR_MONTH_PATTERN} on every write, server side included;
20
+ * - it is READ as a month and a year in the reader's locale ("October 2026", "octobre 2026"), everywhere the value is
21
+ * shown (`StringDisplayMode.YEAR_MONTH`);
22
+ * - it is EDITED with a month-and-year picker that writes `YYYY-MM` (`StringEditMode.YEAR_MONTH`).
23
+ *
24
+ * Chain the structural decorators as on any string (`.isDBIndexed()`, `.isUnique({ compoundWith })`, `.optional()`).
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * import { z_yearMonth } from '@wildo-ai/zod-decorators';
29
+ *
30
+ * period: z_yearMonth().isDBIndexed().isUnique({ compoundWith: 'organizationUnitId' }),
31
+ * ```
32
+ */
33
+ export function z_yearMonth() {
34
+ return z.string().regex(YEAR_MONTH_PATTERN).stringUI({
35
+ display: { displayMode: StringDisplayMode.YEAR_MONTH },
36
+ edit: { editMode: StringEditMode.YEAR_MONTH },
37
+ });
38
+ }
39
+ /** @wildo_source:part:end zod.factories.year-month */
40
+ /**
41
+ * The year and the month (1–12) of a `YYYY-MM` value, or `undefined` when the value is not one. The single parser every
42
+ * reader of a year-month goes through, so a display and a picker cannot disagree about what a value means.
43
+ */
44
+ export function parseYearMonth(value) {
45
+ if (typeof value !== 'string' || !YEAR_MONTH_PATTERN.test(value))
46
+ return undefined;
47
+ const [year, month] = value.split('-').map(Number);
48
+ return { year, month };
49
+ }
50
+ /** The `YYYY-MM` value of a year and a month (1–12). */
51
+ export function formatYearMonthValue(year, month) {
52
+ return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}`;
53
+ }
54
+ // Rejected: a dedicated `isYearMonth()` decorator. The value check already IS the marker, so a second flag could only
55
+ // ever disagree with it, and a chainable method would join every decorated-type interface for a shape one factory
56
+ // already builds.
57
+ /** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */
58
+ /**
59
+ * Does this schema hold a YEAR-MONTH (`YYYY-MM`)? The one question a filter panel, a chip, the companion or a
60
+ * specification asks of a field before treating it as a month, so none of them re-derives the answer from a UI mode.
61
+ *
62
+ * Answered from what the schema ENFORCES first: a string validated against {@link YEAR_MONTH_PATTERN} holds a
63
+ * year-month whatever its UI says, which is the guarantee `z_yearMonth()` makes. That is the structural marker, and it
64
+ * survives an author re-declaring the field's display or edit mode (`.stringUI({ display: … })` replaces the display
65
+ * mode and nothing else). A field that declares the year-month display or edit mode without the pattern counts too:
66
+ * its author said its values are months, and the picker writes nothing else. Wrappers (`.optional()`, `.default()`,
67
+ * `.nullable()`, a pipe's input) are looked through; a year-month on the OUTPUT side of a pipe is not seen.
68
+ *
69
+ * Because the pattern wins, a `z_yearMonth()` whose author re-declares the edit mode as a text input still answers
70
+ * `true`: the form then shows the text input the author chose, and a list filter over it still offers the month picker.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * isYearMonthField(z_yearMonth().optional()); // true
75
+ * isYearMonthField(z.string()); // false
76
+ * ```
77
+ */
78
+ export function isYearMonthField(schema) {
79
+ if (!schema)
80
+ return false;
81
+ if (getZodType(unwrapSchema(schema)) !== 'string')
82
+ return false;
83
+ if (enforcesYearMonthPattern(schema))
84
+ return true;
85
+ const ui = getStringUI(schema);
86
+ return ui?.edit?.editMode === StringEditMode.YEAR_MONTH || ui?.display?.displayMode === StringDisplayMode.YEAR_MONTH;
87
+ }
88
+ /** @wildo_source:part:end zod.factories.year-month */
89
+ /**
90
+ * Does this schema VALIDATE its value against {@link YEAR_MONTH_PATTERN}? The strict half of {@link isYearMonthField}:
91
+ * a field that only DISPLAYS as a month (its UI says so, nothing checks the value) answers `false`.
92
+ *
93
+ * The question a RANGE of months needs answered before it may be offered: a month range compares the stored text
94
+ * (`>=` / `<=`), which orders months correctly only when every stored value has the pattern's fixed width — a
95
+ * four-digit year and a zero-padded month. `2026-9` sorts after `2026-10` as text. A field whose writes are checked
96
+ * against the pattern holds nothing else, so only such a field may be filtered by a range.
97
+ */
98
+ export function enforcesYearMonthPattern(schema) {
99
+ if (!schema)
100
+ return false;
101
+ const unwrapped = unwrapSchema(schema);
102
+ if (getZodType(unwrapped) !== 'string')
103
+ return false;
104
+ for (const check of getZodDefArrayProperty(unwrapped, 'checks') ?? []) {
105
+ // Read as `getStringFormat` reads a check (`check._zod.def`); its private reader is not widened into the public API for one caller.
106
+ const definition = check?._zod?.def;
107
+ if (definition?.check !== 'string_format' || definition.format !== 'regex')
108
+ continue;
109
+ const pattern = definition.pattern;
110
+ if (pattern instanceof RegExp && pattern.source === YEAR_MONTH_PATTERN.source && pattern.flags === YEAR_MONTH_PATTERN.flags)
111
+ return true;
112
+ }
113
+ return false;
114
+ }
115
+ //# sourceMappingURL=zod-year-month.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-year-month.js","sourceRoot":"","sources":["../../src/zod-year-month.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport { StringDisplayMode, StringEditMode } from './zod-decorators.types';\nimport { getStringUI, getZodDefArrayProperty, getZodType, unwrapSchema } from './zod-utils.shared';\n\n/** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */\n/**\n * The shape of a YEAR-MONTH value: a calendar month written `YYYY-MM` (`2026-10`), month `01`–`12`.\n *\n * A year-month carries no day, no time and no time zone. It is stored as this string because the string is what every\n * party agrees on: it sorts chronologically as text, it makes a natural part of a unique key (one budget per department\n * per month), and no reader's zone can move it into the neighbouring month the way a `Date` at midnight can.\n */\nexport const YEAR_MONTH_PATTERN: RegExp = /^\\d{4}-(0[1-9]|1[0-2])$/;\n\n/**\n * A YEAR-MONTH field: one calendar month, stored `YYYY-MM`. Use it for a period whose grain is the month — a monthly\n * budget, a billing month, a reporting period — instead of a `z.date()` pinned to the first of the month (whose day and\n * zone mean nothing) or a bare `z.string()` the user has to type.\n *\n * What the field gets from the framework:\n * - the stored value is validated against {@link YEAR_MONTH_PATTERN} on every write, server side included;\n * - it is READ as a month and a year in the reader's locale (\"October 2026\", \"octobre 2026\"), everywhere the value is\n * shown (`StringDisplayMode.YEAR_MONTH`);\n * - it is EDITED with a month-and-year picker that writes `YYYY-MM` (`StringEditMode.YEAR_MONTH`).\n *\n * Chain the structural decorators as on any string (`.isDBIndexed()`, `.isUnique({ compoundWith })`, `.optional()`).\n *\n * @example\n * ```typescript\n * import { z_yearMonth } from '@wildo-ai/zod-decorators';\n *\n * period: z_yearMonth().isDBIndexed().isUnique({ compoundWith: 'organizationUnitId' }),\n * ```\n */\nexport function z_yearMonth(): z.ZodString {\n return z.string().regex(YEAR_MONTH_PATTERN).stringUI({\n display: { displayMode: StringDisplayMode.YEAR_MONTH },\n edit: { editMode: StringEditMode.YEAR_MONTH },\n });\n}\n/** @wildo_source:part:end zod.factories.year-month */\n\n/**\n * The year and the month (1–12) of a `YYYY-MM` value, or `undefined` when the value is not one. The single parser every\n * reader of a year-month goes through, so a display and a picker cannot disagree about what a value means.\n */\nexport function parseYearMonth(value: string | null | undefined): { year: number; month: number } | undefined {\n if (typeof value !== 'string' || !YEAR_MONTH_PATTERN.test(value)) return undefined;\n const [year, month] = value.split('-').map(Number) as [number, number];\n return { year, month };\n}\n\n/** The `YYYY-MM` value of a year and a month (1–12). */\nexport function formatYearMonthValue(year: number, month: number): string {\n return `${String(year).padStart(4, '0')}-${String(month).padStart(2, '0')}`;\n}\n\n// Rejected: a dedicated `isYearMonth()` decorator. The value check already IS the marker, so a second flag could only\n// ever disagree with it, and a chainable method would join every decorated-type interface for a shape one factory\n// already builds.\n/** @wildo_source:part:start zod.factories.year-month facet:layer:shared facet:family:factory */\n/**\n * Does this schema hold a YEAR-MONTH (`YYYY-MM`)? The one question a filter panel, a chip, the companion or a\n * specification asks of a field before treating it as a month, so none of them re-derives the answer from a UI mode.\n *\n * Answered from what the schema ENFORCES first: a string validated against {@link YEAR_MONTH_PATTERN} holds a\n * year-month whatever its UI says, which is the guarantee `z_yearMonth()` makes. That is the structural marker, and it\n * survives an author re-declaring the field's display or edit mode (`.stringUI({ display: … })` replaces the display\n * mode and nothing else). A field that declares the year-month display or edit mode without the pattern counts too:\n * its author said its values are months, and the picker writes nothing else. Wrappers (`.optional()`, `.default()`,\n * `.nullable()`, a pipe's input) are looked through; a year-month on the OUTPUT side of a pipe is not seen.\n *\n * Because the pattern wins, a `z_yearMonth()` whose author re-declares the edit mode as a text input still answers\n * `true`: the form then shows the text input the author chose, and a list filter over it still offers the month picker.\n *\n * @example\n * ```typescript\n * isYearMonthField(z_yearMonth().optional()); // true\n * isYearMonthField(z.string()); // false\n * ```\n */\nexport function isYearMonthField(schema: z.ZodTypeAny | undefined): boolean {\n if (!schema) return false;\n if (getZodType(unwrapSchema(schema)) !== 'string') return false;\n if (enforcesYearMonthPattern(schema)) return true;\n const ui = getStringUI(schema);\n return ui?.edit?.editMode === StringEditMode.YEAR_MONTH || ui?.display?.displayMode === StringDisplayMode.YEAR_MONTH;\n}\n/** @wildo_source:part:end zod.factories.year-month */\n\n/**\n * Does this schema VALIDATE its value against {@link YEAR_MONTH_PATTERN}? The strict half of {@link isYearMonthField}:\n * a field that only DISPLAYS as a month (its UI says so, nothing checks the value) answers `false`.\n *\n * The question a RANGE of months needs answered before it may be offered: a month range compares the stored text\n * (`>=` / `<=`), which orders months correctly only when every stored value has the pattern's fixed width — a\n * four-digit year and a zero-padded month. `2026-9` sorts after `2026-10` as text. A field whose writes are checked\n * against the pattern holds nothing else, so only such a field may be filtered by a range.\n */\nexport function enforcesYearMonthPattern(schema: z.ZodTypeAny | undefined): boolean {\n if (!schema) return false;\n const unwrapped = unwrapSchema(schema);\n if (getZodType(unwrapped) !== 'string') return false;\n for (const check of getZodDefArrayProperty(unwrapped, 'checks') ?? []) {\n // Read as `getStringFormat` reads a check (`check._zod.def`); its private reader is not widened into the public API for one caller.\n const definition = (check as { _zod?: { def?: { check?: unknown; format?: unknown; pattern?: unknown } } })?._zod?.def;\n if (definition?.check !== 'string_format' || definition.format !== 'regex') continue;\n const pattern = definition.pattern;\n if (pattern instanceof RegExp && pattern.source === YEAR_MONTH_PATTERN.source && pattern.flags === YEAR_MONTH_PATTERN.flags) return true;\n }\n return false;\n}\n"]}