@warlock.js/notifications 5.26.0 → 5.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- //#region ../node_modules/.pnpm/@mongez+supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs
1
+ //#region ../node_modules/.pnpm/@mongez+supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs
2
2
  /**
3
3
  * Determine whether the given value is a number whatever if its data type is String or Number
4
4
  */
@@ -19,6 +19,7 @@ const isFloat = (value) => typeof value === "number" && Number.isFinite(value) &
19
19
  const isRegex = (value) => Boolean(value) && value.constructor?.name === "RegExp";
20
20
  /**
21
21
  * Check if the given value is an object
22
+ * Arrays, dates and class instances are objects; `null` and functions are not
22
23
  */
23
24
  const isObject = (value) => Boolean(value) && typeof value === "object";
24
25
  /**
@@ -56,7 +57,8 @@ const isScalar = (value) => [
56
57
  "bigint"
57
58
  ].includes(typeof value);
58
59
  /**
59
- * Check if the given value is a pr value
60
+ * Check if the given value is a primitive value
61
+ * (string, boolean, number or bigint — symbols, `null` and `undefined` are not)
60
62
  */
61
63
  const isPrimitive = (value) => [
62
64
  "string",
@@ -74,6 +76,9 @@ const isPromise = (value) => Boolean(value) && value instanceof Promise;
74
76
  const isDate = (value) => Boolean(value) && typeof value === "object" && value instanceof Date;
75
77
  /**
76
78
  * Check if the given value is a generator
79
+ * Duck-typed: any object with a `next()` method that is its own iterator, so
80
+ * the predicate is `IterableIterator` (not `Generator`, whose `return` and
81
+ * `throw` methods are not verified)
77
82
  */
78
83
  const isGenerator = (value) => Boolean(value) && typeof value === "object" && typeof value.next === "function" && typeof value[Symbol.iterator] === "function" && value[Symbol.iterator]() === value;
79
84
  /**
@@ -106,7 +111,7 @@ const isEmpty = (value) => {
106
111
  */
107
112
  const isJson = (value) => {
108
113
  if (!value || typeof value !== "string") return false;
109
- if (!["[", "{"].includes(value?.[0])) return false;
114
+ if (!["[", "{"].includes(value[0])) return false;
110
115
  try {
111
116
  JSON.parse(value);
112
117
  return true;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../../../../../../node_modules/.pnpm/@mongez+supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs"],"sourcesContent":["//#region ../supportive-is/src/index.ts\n/**\n* Determine whether the given value is a number whatever if its data type is String or Number\n*/\nfunction isNumeric(value) {\n\treturn /^[+-]?\\d+(\\.\\d+)?([Ee][+-]?\\d+)?$/.test(String(value));\n}\n/**\n* Check if the given value is int\n*/\nconst isInt = (value) => typeof value === \"number\" && Number.isInteger(value);\n/**\n* Check if the given value is float\n*/\nconst isFloat = (value) => typeof value === \"number\" && Number.isFinite(value) && /^-?\\d+\\.\\d+$/.test(String(value));\n/**\n* Check if the given value is a regex\n*/\nconst isRegex = (value) => Boolean(value) && value.constructor?.name === \"RegExp\";\n/**\n* Check if the given value is an object\n* Arrays, dates and class instances are objects; `null` and functions are not\n*/\nconst isObject = (value) => Boolean(value) && typeof value === \"object\";\n/**\n* Check if the given value is a plain object\n*/\nconst isPlainObject = (value) => {\n\tif (!value || typeof value !== \"object\") return false;\n\tif (Object.getPrototypeOf(value) === null) return true;\n\treturn value.constructor?.name === \"Object\";\n};\n/**\n* Check if the given value is validId\n*/\nconst isValidId = (value) => Boolean(value && /^[A-Za-z][\\w\\-:.]*$/.test(String(value)));\n/**\n* Check if the given value is a form element\n*/\nconst isFormElement = (value) => typeof HTMLFormElement === \"undefined\" ? false : value instanceof HTMLFormElement;\n/**\n* Check if the given value is a form data\n*/\nconst isFormData = (value) => value instanceof FormData;\n/**\n* Check if the given value is iterable\n*/\nconst isIterable = (value) => value !== null && value !== void 0 && typeof value[Symbol.iterator] === \"function\";\n/**\n* Check if the given value is a scalar value\n*/\nconst isScalar = (value) => [\n\t\"string\",\n\t\"boolean\",\n\t\"number\",\n\t\"symbol\",\n\t\"bigint\"\n].includes(typeof value);\n/**\n* Check if the given value is a string\n*/\nconst isString = (value) => typeof value === \"string\";\n/**\n* Check if the given value is a primitive value\n* (string, boolean, number or bigint — symbols, `null` and `undefined` are not)\n*/\nconst isPrimitive = (value) => [\n\t\"string\",\n\t\"boolean\",\n\t\"number\",\n\t\"bigint\"\n].includes(typeof value);\n/**\n* Check if the given value is a promise\n*/\nconst isPromise = (value) => Boolean(value) && value instanceof Promise;\n/**\n* Check if the given value is a date\n*/\nconst isDate = (value) => Boolean(value) && typeof value === \"object\" && value instanceof Date;\n/**\n* Check if the given value is a generator\n* Duck-typed: any object with a `next()` method that is its own iterator, so\n* the predicate is `IterableIterator` (not `Generator`, whose `return` and\n* `throw` methods are not verified)\n*/\nconst isGenerator = (value) => Boolean(value) && typeof value === \"object\" && typeof value.next === \"function\" && typeof value[Symbol.iterator] === \"function\" && value[Symbol.iterator]() === value;\n/**\n* Check if the given value is empty\n* Zero does not considered empty\n* Empty object is considered empty\n* Empty array is considered empty\n*/\nconst isEmpty = (value) => {\n\tif ([\n\t\t0,\n\t\ttrue,\n\t\tfalse\n\t].includes(value)) return false;\n\tif ([\n\t\t\"\",\n\t\tnull,\n\t\tvoid 0\n\t].includes(value)) return true;\n\tif (typeof value === \"number\" && Number.isNaN(value)) return false;\n\tif (value instanceof Date) return false;\n\tif (value instanceof Map || value instanceof Set) return value.size === 0;\n\tif (typeof value === \"object\" && !Array.isArray(value) && typeof value[Symbol.iterator] !== \"function\") return Object.keys(value).length === 0;\n\tif (isIterable(value)) return value.length === 0;\n\tif (isNumeric(value)) return false;\n\treturn true;\n};\n/**\n* Check if the given value is a valid json\n*/\nconst isJson = (value) => {\n\tif (!value || typeof value !== \"string\") return false;\n\tif (![\"[\", \"{\"].includes(value[0])) return false;\n\ttry {\n\t\tJSON.parse(value);\n\t\treturn true;\n\t} catch (e) {\n\t\treturn false;\n\t}\n};\n/**\n* Check if the given value is valid url\n*/\nconst isUrl = (value) => {\n\tif (!value || typeof value !== \"string\") return false;\n\ttry {\n\t\tconst url = new URL(value);\n\t\tif (![\"http:\", \"https:\"].includes(url.protocol)) return false;\n\t\tconst hostname = url.hostname;\n\t\tif (!hostname.includes(\".\")) return false;\n\t\tif (hostname.split(\".\").some((label) => label.length === 0)) return false;\n\t\treturn true;\n\t} catch (e) {\n\t\treturn false;\n\t}\n};\n/**\n* Check if the given value is a valid email\n*/\nconst isEmail = (value) => typeof value === \"string\" && /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/.test(value);\n/**\n* Check if current user is opening from mobile\n*/\nconst isMobile = {\n\t/**\n\t* Determine whether the current visitor is opening from an Android device\n\t*/\n\tandroid: () => navigator.userAgent.match(/Android/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from an ios device\n\t*/\n\tios: () => navigator.userAgent.match(/iPhone|iPad|iPod/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from an Iphone\n\t*/\n\tiphone: () => navigator.userAgent.match(/iPhone/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from an ipad\n\t*/\n\tipad: () => navigator.userAgent.match(/iPad/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from an ipod\n\t*/\n\tipod: () => navigator.userAgent.match(/iPod/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from an windows mobile\n\t*/\n\twindows: () => navigator.userAgent.match(/IEMobile/i) !== null,\n\t/**\n\t* Determine whether the current visitor is opening from mobile whatever its type\n\t*/\n\tany: () => isMobile.android() || isMobile.ios() || isMobile.windows()\n};\n/**\n* Determine if current user is opening from macOS\n*/\nconst isMac = () => navigator.userAgent.match(/mac/i) !== null;\n/**\n* Check if current browser is the given name\n*/\nconst isBrowser = (browser) => {\n\treturn {\n\t\tchrome: isChrome(),\n\t\tfirefox: isFirefox(),\n\t\topera: isOpera(),\n\t\tedge: isEdge(),\n\t\tie: isIE(),\n\t\tsafari: isSafari()\n\t}[browser.toLowerCase()] === true;\n};\n/**\n* Check if current browser is chrome browser\n*/\nconst isChrome = () => !!window.chrome && (!!window.chrome.webstore || !!window.chrome.runtime);\n/**\n* Check if current browser is firefox\n*/\nconst isFirefox = () => typeof window.InstallTrigger !== \"undefined\";\n/**\n* Check if current browser is safari\n*/\nconst isSafari = () => {\n\tif (typeof navigator === \"undefined\") return false;\n\tconst ua = navigator.userAgent || \"\";\n\treturn /Safari/i.test(ua) && !/Chrome|Chromium|Edg|OPR|Android/i.test(ua);\n};\n/**\n* Check if current browser is opera\n*/\nconst isOpera = () => {\n\tif (typeof window === \"undefined\") return false;\n\tconst w = window;\n\treturn !!w.opr && !!w.opr.addons || !!w.opera || typeof navigator !== \"undefined\" && navigator.userAgent.indexOf(\" OPR/\") >= 0;\n};\n/**\n* Check if current browser is internet explorer\n*/\nconst isIE = () => !!document.documentMode;\n/**\n* Check if current browser is edge\n*/\nconst isEdge = () => {\n\treturn !!!document.documentMode && !!window.StyleMedia;\n};\n/**\n* Determine whether the current visitor is opening from desktop\n*/\nconst isDesktop = () => !isMobile.any();\n/**\n* A simple lightweight library to validate values against certain types of data\n* For full documentation on github\n* Repo: https://github.com/hassanzohdy/supportive-is\n*\n* First released 01/12/2017\n* Licensed under MIT (http://opensource.org/licenses/MIT)\n*/\nconst Is = {\n\t/**\n\t* Determine whether the given value is a number whatever if its data type is String or Number\n\t*/\n\tnumeric: isNumeric,\n\t/**\n\t* Determine whether the given value is an integer and its data type is number\n\t* As Number.isInteger considers 1.0 to be valid integer, regex will be used instead\n\t*/\n\tint: isInt,\n\t/**\n\t* Determine whether the given value is a float number and its data type is number\n\t*/\n\tfloat: isFloat,\n\t/**\n\t* Determine whether the given value is a regular expression\n\t*/\n\tregex: isRegex,\n\t/**\n\t* Determine whether the given value is an object\n\t*/\n\tobject: isObject,\n\t/**\n\t* Determine whether the given value is a plain object\n\t*/\n\tplainObject: isPlainObject,\n\t/**\n\t* Determine whether the given value is an array\n\t*/\n\tarray: Array.isArray,\n\t/**\n\t* Determine whether the given value is valid html attribute `id`\n\t*/\n\tvalidHtmlId: isValidId,\n\t/**\n\t* determine if the given value is an instance of form element\n\t*/\n\tformElement: isFormElement,\n\t/**\n\t* @alias formElement\n\t*/\n\tform: isFormElement,\n\t/**\n\t* determine if the given value is an instance of form data\n\t*/\n\tformData: isFormData,\n\t/**\n\t* Determine whether the given value is iterable\n\t*/\n\titerable: isIterable,\n\t/**\n\t* Determine whether the given value is a scalar\n\t* Scalar types are strings, booleans, integers and\n\t*/\n\tscalar: isScalar,\n\t/**\n\t* Determine if the given value is a primitive value\n\t*/\n\tprimitive: isPrimitive,\n\t/**\n\t* Determine whether the given value is a promise\n\t*/\n\tpromise: isPromise,\n\t/**\n\t* Determine whether the given value is a date\n\t*/\n\tdate: isDate,\n\t/**\n\t* Determine whether the given value is a generator\n\t*/\n\tgenerator: isGenerator,\n\t/**\n\t* Determine whether the given value if an empty\n\t* This can be validated against any type of values\n\t* undefined or nullable values will be considered empty\n\t* Objects that doesn't have any properties will be considered empty\n\t* Arrays and Strings will be empty based on its length\n\t* The 0 'Zero\" number will not be considered empty\n\t*/\n\tempty: isEmpty,\n\t/**\n\t* Determine whether the given value is a valid json string\n\t*/\n\tjson: isJson,\n\t/**\n\t* Determine whether the given value is a valid url\n\t*/\n\turl: isUrl,\n\t/**\n\t* Determine whether the given value is a valid email\n\t*/\n\temail: isEmail,\n\t/**\n\t* Determine the current visitor is opening from mobile\n\t* Please Note this method depends on the user agent\n\t*/\n\tmobile: isMobile,\n\t/**\n\t* Determine if current user is opening from macOS\n\t*/\n\tmac: isMac,\n\t/**\n\t* Determine whether the current visitor is opening from desktop\n\t*/\n\tdesktop: isDesktop,\n\t/**\n\t* Check if the current browser is the given name\n\t* To detect browser version as well, pass the version number as the second argument\n\t*/\n\tbrowser: isBrowser,\n\tchrome: isChrome,\n\tfirefox: isFirefox,\n\tsafari: isSafari,\n\topera: isOpera,\n\tie: isIE,\n\tedge: isEdge\n};\n\n//#endregion\nexport { Is, Is as default, isBrowser, isChrome, isDate, isDesktop, isEdge, isEmail, isEmpty, isFirefox, isFloat, isFormData, isFormElement, isGenerator, isIE, isInt, isIterable, isJson, isMac, isMobile, isNumeric, isObject, isOpera, isPlainObject, isPrimitive, isPromise, isRegex, isSafari, isScalar, isString, isUrl, isValidId };\n//# sourceMappingURL=index.mjs.map"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { isEmpty, isObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
1
+ import { isEmpty, isObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
2
2
 
3
3
  //#region ../seal/src/helpers/is-empty-value.ts
4
4
  function isEmptyValue(value) {
@@ -1,6 +1,6 @@
1
1
  import { round } from "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/number/round.mjs";
2
2
  import "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/index.mjs";
3
- import { isNumeric } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
3
+ import { isNumeric } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
4
4
 
5
5
  //#region ../seal/src/mutators/number-mutators.ts
6
6
  /** Convert value to number */
@@ -1,6 +1,6 @@
1
1
  import { trim } from "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/trim.mjs";
2
2
  import "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/index.mjs";
3
- import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
3
+ import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
4
4
 
5
5
  //#region ../seal/src/mutators/object-mutators.ts
6
6
  /** Strip unknown keys from object */
@@ -1,5 +1,5 @@
1
1
  import { VALID_RULE, invalidRule } from "../../helpers/validation-helpers.mjs";
2
- import { isObject, isPlainObject } from "../../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
2
+ import { isObject, isPlainObject } from "../../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
3
3
  import "../../helpers/index.mjs";
4
4
 
5
5
  //#region ../seal/src/rules/common/type-rules.ts
@@ -1,5 +1,5 @@
1
1
  import { VALID_RULE, invalidRule } from "../../helpers/validation-helpers.mjs";
2
- import { isEmail } from "../../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
2
+ import { isEmail } from "../../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
3
3
  import "../../helpers/index.mjs";
4
4
 
5
5
  //#region ../seal/src/rules/string/email.ts
@@ -1,9 +1,13 @@
1
1
  import { applyNullable, wrapNullableStrict } from "../standard-schema/json-schema.mjs";
2
- import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
2
+ import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
3
3
  import { BaseValidator } from "./base-validator.mjs";
4
4
  import { LiteralValidator } from "./literal-validator.mjs";
5
5
 
6
6
  //#region ../seal/src/validators/discriminated-union-validator.ts
7
+ /** Whether a value read from the payload can be a discriminator key. */
8
+ function isDiscriminatorValue(value) {
9
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
10
+ }
7
11
  /**
8
12
  * Discriminated union validator — routes payloads by a shared discriminator field.
9
13
  *
@@ -83,7 +87,7 @@ var DiscriminatedUnionValidator = class DiscriminatedUnionValidator extends Base
83
87
  data: void 0
84
88
  };
85
89
  const discriminatorValue = data[this.discriminator];
86
- const branch = this.branches.get(discriminatorValue);
90
+ const branch = isDiscriminatorValue(discriminatorValue) ? this.branches.get(discriminatorValue) : void 0;
87
91
  if (!branch) {
88
92
  const allowed = [...this.branches.keys()].map((k) => String(k)).join(", ");
89
93
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"discriminated-union-validator.mjs","names":[],"sources":["../../../../../../../../../seal/src/validators/discriminated-union-validator.ts"],"sourcesContent":["import { isPlainObject } from \"@mongez/supportive-is\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { SchemaContext, ValidationResult } from \"../types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { LiteralValidator } from \"./literal-validator\";\nimport { ObjectValidator } from \"./object-validator\";\n\n/**\n * Discriminated union validator — routes payloads by a shared discriminator field.\n *\n * Plain `v.union()` falls back to `matchesType()` to pick a branch, which is\n * coarse for object-vs-object unions (every branch matches \"is plain object\").\n * Discriminated union reads a known field's value and routes directly to the\n * matching branch, producing precise errors instead of confused mash from the\n * wrong branch.\n *\n * **Construction-time validation.** Every branch must:\n * - Be an `ObjectValidator`\n * - Declare the discriminator field\n * - Type the discriminator as `v.literal(...)` (single or multi-literal both work)\n * - Not collide with another branch's literal values\n *\n * Misconfigurations throw eagerly so tests catch them at schema-build time.\n *\n * @example\n * ```ts\n * const email = v.object({ type: v.literal(\"email\"), email: v.string().email() });\n * const sms = v.object({ type: v.literal(\"sms\"), phone: v.string() });\n * const push = v.object({ type: v.literal(\"push\"), deviceId: v.string() });\n *\n * const notif = v.discriminatedUnion(\"type\", [email, sms, push]);\n *\n * await validate(notif, { type: \"sms\", phone: \"555-1234\" });\n * // → routes to sms branch only; errors (if any) come from sms\n * ```\n *\n * @see `domains/seal/plans/2026-05-12-discriminated-union.md`\n */\nexport class DiscriminatedUnionValidator<\n K extends string = string,\n Branches extends ReadonlyArray<ObjectValidator<any>> = ReadonlyArray<ObjectValidator<any>>,\n> extends BaseValidator {\n /** Map from discriminator literal value → matching branch validator. */\n private branches: Map<string | number | boolean, ObjectValidator<any>>;\n\n public constructor(\n public discriminator: K,\n public validators: Branches,\n ) {\n super();\n\n this.branches = DiscriminatedUnionValidator.buildBranchMap(discriminator, validators);\n }\n\n /**\n * Walk every branch, pull out the discriminator's literal values, and build\n * the lookup map. Throws on misconfiguration (missing discriminator,\n * non-literal discriminator, duplicate literal value).\n */\n private static buildBranchMap(\n discriminator: string,\n validators: ReadonlyArray<ObjectValidator<any>>,\n ): Map<string | number | boolean, ObjectValidator<any>> {\n const map = new Map<string | number | boolean, ObjectValidator<any>>();\n\n for (const branch of validators) {\n const discriminatorValidator = branch.schema?.[discriminator];\n\n if (!discriminatorValidator) {\n throw new Error(\n `[Seal] discriminatedUnion: branch missing discriminator field \"${discriminator}\"`,\n );\n }\n\n if (!(discriminatorValidator instanceof LiteralValidator)) {\n throw new Error(\n `[Seal] discriminatedUnion: discriminator \"${discriminator}\" must be v.literal(...) on every branch`,\n );\n }\n\n for (const value of discriminatorValidator.values) {\n if (map.has(value)) {\n throw new Error(\n `[Seal] discriminatedUnion: duplicate discriminator value \"${String(value)}\"`,\n );\n }\n map.set(value, branch);\n }\n }\n\n return map;\n }\n\n public override matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n public override async validate(data: any, context: SchemaContext): Promise<ValidationResult> {\n if (data === null && this.isNullable) {\n return { isValid: true, errors: [], data: null };\n }\n\n if (!isPlainObject(data)) {\n return {\n isValid: false,\n errors: [\n {\n type: \"discriminatedUnion\",\n error: `Expected object with discriminator field \"${this.discriminator}\"`,\n input: context.key || context.path || \"value\",\n },\n ],\n data: undefined,\n };\n }\n\n const discriminatorValue = data[this.discriminator];\n const branch = this.branches.get(discriminatorValue);\n\n if (!branch) {\n const allowed = [...this.branches.keys()].map((k) => String(k)).join(\", \");\n return {\n isValid: false,\n errors: [\n {\n type: \"discriminatedUnion\",\n error: `Field \"${this.discriminator}\" must be one of: ${allowed}`,\n input: this.discriminator,\n },\n ],\n data: undefined,\n };\n }\n\n return branch.validate(data, context);\n }\n\n public override clone(): this {\n const cloned = super.clone() as any;\n cloned.discriminator = this.discriminator;\n cloned.validators = this.validators.map((v: ObjectValidator<any>) => v.clone());\n cloned.branches = DiscriminatedUnionValidator.buildBranchMap(\n cloned.discriminator,\n cloned.validators,\n );\n return cloned;\n }\n\n /**\n * Emit `oneOf` of branch schemas. Each branch's own `toJsonSchema()` handles\n * its `properties.{discriminator}.const` and the surrounding required/optional\n * structure — we just enumerate.\n *\n * For `openai-strict`: the per-branch ObjectValidator already inflates\n * `required` to include every field; `oneOf` is OpenAI-accepted as long as\n * each branch passes strict rules independently.\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const oneOf = this.validators.map((v) => v.toJsonSchema(target));\n const schema: JsonSchemaResult = { oneOf };\n\n if (this.isNullable) {\n if (target === \"openai-strict\") {\n return wrapNullableStrict(schema);\n }\n applyNullable(schema, target);\n }\n\n return schema;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,8BAAb,MAAa,oCAGH,cAAc;CAKb;CACA;;CAJT,AAAQ;CAER,AAAO,YACL,AAAO,eACP,AAAO,YACP;EACA,MAAM;EAHC;EACA;EAIP,KAAK,WAAW,4BAA4B,eAAe,eAAe,UAAU;CACtF;;;;;;CAOA,OAAe,eACb,eACA,YACsD;EACtD,MAAM,sBAAM,IAAI,IAAqD;EAErE,KAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,yBAAyB,OAAO,SAAS;GAE/C,IAAI,CAAC,wBACH,MAAM,IAAI,MACR,kEAAkE,cAAc,EAClF;GAGF,IAAI,EAAE,kCAAkC,mBACtC,MAAM,IAAI,MACR,6CAA6C,cAAc,yCAC7D;GAGF,KAAK,MAAM,SAAS,uBAAuB,QAAQ;IACjD,IAAI,IAAI,IAAI,KAAK,GACf,MAAM,IAAI,MACR,6DAA6D,OAAO,KAAK,EAAE,EAC7E;IAEF,IAAI,IAAI,OAAO,MAAM;GACvB;EACF;EAEA,OAAO;CACT;CAEA,AAAgB,YAAY,OAAqB;EAC/C,OAAO,cAAc,KAAK;CAC5B;CAEA,MAAsB,SAAS,MAAW,SAAmD;EAC3F,IAAI,SAAS,QAAQ,KAAK,YACxB,OAAO;GAAE,SAAS;GAAM,QAAQ,CAAC;GAAG,MAAM;EAAK;EAGjD,IAAI,CAAC,cAAc,IAAI,GACrB,OAAO;GACL,SAAS;GACT,QAAQ,CACN;IACE,MAAM;IACN,OAAO,6CAA6C,KAAK,cAAc;IACvE,OAAO,QAAQ,OAAO,QAAQ,QAAQ;GACxC,CACF;GACA,MAAM;EACR;EAGF,MAAM,qBAAqB,KAAK,KAAK;EACrC,MAAM,SAAS,KAAK,SAAS,IAAI,kBAAkB;EAEnD,IAAI,CAAC,QAAQ;GACX,MAAM,UAAU,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;GACzE,OAAO;IACL,SAAS;IACT,QAAQ,CACN;KACE,MAAM;KACN,OAAO,UAAU,KAAK,cAAc,oBAAoB;KACxD,OAAO,KAAK;IACd,CACF;IACA,MAAM;GACR;EACF;EAEA,OAAO,OAAO,SAAS,MAAM,OAAO;CACtC;CAEA,AAAgB,QAAc;EAC5B,MAAM,SAAS,MAAM,MAAM;EAC3B,OAAO,gBAAgB,KAAK;EAC5B,OAAO,aAAa,KAAK,WAAW,KAAK,MAA4B,EAAE,MAAM,CAAC;EAC9E,OAAO,WAAW,4BAA4B,eAC5C,OAAO,eACP,OAAO,UACT;EACA,OAAO;CACT;;;;;;;;;;CAWA,AAAgB,aAAa,SAA2B,iBAAmC;EAEzF,MAAM,SAA2B,EAAE,OADrB,KAAK,WAAW,KAAK,MAAM,EAAE,aAAa,MAAM,CACvB,EAAE;EAEzC,IAAI,KAAK,YAAY;GACnB,IAAI,WAAW,iBACb,OAAO,mBAAmB,MAAM;GAElC,cAAc,QAAQ,MAAM;EAC9B;EAEA,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"discriminated-union-validator.mjs","names":[],"sources":["../../../../../../../../../seal/src/validators/discriminated-union-validator.ts"],"sourcesContent":["import { isPlainObject } from \"@mongez/supportive-is\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { SchemaContext, ValidationResult } from \"../types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { LiteralValidator } from \"./literal-validator\";\nimport { ObjectValidator } from \"./object-validator\";\n\n/** A literal value a discriminator field can hold (the key type of the branch map). */\ntype DiscriminatorValue = string | number | boolean;\n\n/** Whether a value read from the payload can be a discriminator key. */\nfunction isDiscriminatorValue(value: unknown): value is DiscriminatorValue {\n return typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\";\n}\n\n/**\n * Discriminated union validator — routes payloads by a shared discriminator field.\n *\n * Plain `v.union()` falls back to `matchesType()` to pick a branch, which is\n * coarse for object-vs-object unions (every branch matches \"is plain object\").\n * Discriminated union reads a known field's value and routes directly to the\n * matching branch, producing precise errors instead of confused mash from the\n * wrong branch.\n *\n * **Construction-time validation.** Every branch must:\n * - Be an `ObjectValidator`\n * - Declare the discriminator field\n * - Type the discriminator as `v.literal(...)` (single or multi-literal both work)\n * - Not collide with another branch's literal values\n *\n * Misconfigurations throw eagerly so tests catch them at schema-build time.\n *\n * @example\n * ```ts\n * const email = v.object({ type: v.literal(\"email\"), email: v.string().email() });\n * const sms = v.object({ type: v.literal(\"sms\"), phone: v.string() });\n * const push = v.object({ type: v.literal(\"push\"), deviceId: v.string() });\n *\n * const notif = v.discriminatedUnion(\"type\", [email, sms, push]);\n *\n * await validate(notif, { type: \"sms\", phone: \"555-1234\" });\n * // → routes to sms branch only; errors (if any) come from sms\n * ```\n *\n * @see `domains/seal/plans/2026-05-12-discriminated-union.md`\n */\nexport class DiscriminatedUnionValidator<\n K extends string = string,\n Branches extends ReadonlyArray<ObjectValidator<any>> = ReadonlyArray<ObjectValidator<any>>,\n> extends BaseValidator {\n /** Map from discriminator literal value → matching branch validator. */\n private branches: Map<DiscriminatorValue, ObjectValidator<any>>;\n\n public constructor(\n public discriminator: K,\n public validators: Branches,\n ) {\n super();\n\n this.branches = DiscriminatedUnionValidator.buildBranchMap(discriminator, validators);\n }\n\n /**\n * Walk every branch, pull out the discriminator's literal values, and build\n * the lookup map. Throws on misconfiguration (missing discriminator,\n * non-literal discriminator, duplicate literal value).\n */\n private static buildBranchMap(\n discriminator: string,\n validators: ReadonlyArray<ObjectValidator<any>>,\n ): Map<DiscriminatorValue, ObjectValidator<any>> {\n const map = new Map<DiscriminatorValue, ObjectValidator<any>>();\n\n for (const branch of validators) {\n const discriminatorValidator = branch.schema?.[discriminator];\n\n if (!discriminatorValidator) {\n throw new Error(\n `[Seal] discriminatedUnion: branch missing discriminator field \"${discriminator}\"`,\n );\n }\n\n if (!(discriminatorValidator instanceof LiteralValidator)) {\n throw new Error(\n `[Seal] discriminatedUnion: discriminator \"${discriminator}\" must be v.literal(...) on every branch`,\n );\n }\n\n for (const value of discriminatorValidator.values) {\n if (map.has(value)) {\n throw new Error(\n `[Seal] discriminatedUnion: duplicate discriminator value \"${String(value)}\"`,\n );\n }\n map.set(value, branch);\n }\n }\n\n return map;\n }\n\n public override matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n public override async validate(data: any, context: SchemaContext): Promise<ValidationResult> {\n if (data === null && this.isNullable) {\n return { isValid: true, errors: [], data: null };\n }\n\n if (!isPlainObject(data)) {\n return {\n isValid: false,\n errors: [\n {\n type: \"discriminatedUnion\",\n error: `Expected object with discriminator field \"${this.discriminator}\"`,\n input: context.key || context.path || \"value\",\n },\n ],\n data: undefined,\n };\n }\n\n const discriminatorValue = data[this.discriminator];\n const branch = isDiscriminatorValue(discriminatorValue)\n ? this.branches.get(discriminatorValue)\n : undefined;\n\n if (!branch) {\n const allowed = [...this.branches.keys()].map((k) => String(k)).join(\", \");\n return {\n isValid: false,\n errors: [\n {\n type: \"discriminatedUnion\",\n error: `Field \"${this.discriminator}\" must be one of: ${allowed}`,\n input: this.discriminator,\n },\n ],\n data: undefined,\n };\n }\n\n return branch.validate(data, context);\n }\n\n public override clone(): this {\n const cloned = super.clone() as any;\n cloned.discriminator = this.discriminator;\n cloned.validators = this.validators.map((v: ObjectValidator<any>) => v.clone());\n cloned.branches = DiscriminatedUnionValidator.buildBranchMap(\n cloned.discriminator,\n cloned.validators,\n );\n return cloned;\n }\n\n /**\n * Emit `oneOf` of branch schemas. Each branch's own `toJsonSchema()` handles\n * its `properties.{discriminator}.const` and the surrounding required/optional\n * structure — we just enumerate.\n *\n * For `openai-strict`: the per-branch ObjectValidator already inflates\n * `required` to include every field; `oneOf` is OpenAI-accepted as long as\n * each branch passes strict rules independently.\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const oneOf = this.validators.map((v) => v.toJsonSchema(target));\n const schema: JsonSchemaResult = { oneOf };\n\n if (this.isNullable) {\n if (target === \"openai-strict\") {\n return wrapNullableStrict(schema);\n }\n applyNullable(schema, target);\n }\n\n return schema;\n }\n}\n"],"mappings":";;;;;;;AAYA,SAAS,qBAAqB,OAA6C;CACzE,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU;AACpF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCA,IAAa,8BAAb,MAAa,oCAGH,cAAc;CAKb;CACA;;CAJT,AAAQ;CAER,AAAO,YACL,AAAO,eACP,AAAO,YACP;EACA,MAAM;EAHC;EACA;EAIP,KAAK,WAAW,4BAA4B,eAAe,eAAe,UAAU;CACtF;;;;;;CAOA,OAAe,eACb,eACA,YAC+C;EAC/C,MAAM,sBAAM,IAAI,IAA8C;EAE9D,KAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,yBAAyB,OAAO,SAAS;GAE/C,IAAI,CAAC,wBACH,MAAM,IAAI,MACR,kEAAkE,cAAc,EAClF;GAGF,IAAI,EAAE,kCAAkC,mBACtC,MAAM,IAAI,MACR,6CAA6C,cAAc,yCAC7D;GAGF,KAAK,MAAM,SAAS,uBAAuB,QAAQ;IACjD,IAAI,IAAI,IAAI,KAAK,GACf,MAAM,IAAI,MACR,6DAA6D,OAAO,KAAK,EAAE,EAC7E;IAEF,IAAI,IAAI,OAAO,MAAM;GACvB;EACF;EAEA,OAAO;CACT;CAEA,AAAgB,YAAY,OAAqB;EAC/C,OAAO,cAAc,KAAK;CAC5B;CAEA,MAAsB,SAAS,MAAW,SAAmD;EAC3F,IAAI,SAAS,QAAQ,KAAK,YACxB,OAAO;GAAE,SAAS;GAAM,QAAQ,CAAC;GAAG,MAAM;EAAK;EAGjD,IAAI,CAAC,cAAc,IAAI,GACrB,OAAO;GACL,SAAS;GACT,QAAQ,CACN;IACE,MAAM;IACN,OAAO,6CAA6C,KAAK,cAAc;IACvE,OAAO,QAAQ,OAAO,QAAQ,QAAQ;GACxC,CACF;GACA,MAAM;EACR;EAGF,MAAM,qBAAqB,KAAK,KAAK;EACrC,MAAM,SAAS,qBAAqB,kBAAkB,IAClD,KAAK,SAAS,IAAI,kBAAkB,IACpC;EAEJ,IAAI,CAAC,QAAQ;GACX,MAAM,UAAU,CAAC,GAAG,KAAK,SAAS,KAAK,CAAC,CAAC,CAAC,KAAK,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;GACzE,OAAO;IACL,SAAS;IACT,QAAQ,CACN;KACE,MAAM;KACN,OAAO,UAAU,KAAK,cAAc,oBAAoB;KACxD,OAAO,KAAK;IACd,CACF;IACA,MAAM;GACR;EACF;EAEA,OAAO,OAAO,SAAS,MAAM,OAAO;CACtC;CAEA,AAAgB,QAAc;EAC5B,MAAM,SAAS,MAAM,MAAM;EAC3B,OAAO,gBAAgB,KAAK;EAC5B,OAAO,aAAa,KAAK,WAAW,KAAK,MAA4B,EAAE,MAAM,CAAC;EAC9E,OAAO,WAAW,4BAA4B,eAC5C,OAAO,eACP,OAAO,UACT;EACA,OAAO;CACT;;;;;;;;;;CAWA,AAAgB,aAAa,SAA2B,iBAAmC;EAEzF,MAAM,SAA2B,EAAE,OADrB,KAAK,WAAW,KAAK,MAAM,EAAE,aAAa,MAAM,CACvB,EAAE;EAEzC,IAAI,KAAK,YAAY;GACnB,IAAI,WAAW,iBACb,OAAO,mBAAmB,MAAM;GAElC,cAAc,QAAQ,MAAM;EAC9B;EAEA,OAAO;CACT;AACF"}
@@ -1,4 +1,4 @@
1
- import { isNumeric } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
1
+ import { isNumeric } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
2
2
  import { numericMutator } from "../mutators/number-mutators.mjs";
3
3
  import { NumberValidator } from "./number-validator.mjs";
4
4
 
@@ -1,7 +1,7 @@
1
1
  import { applyNullable, wrapNullableStrict } from "../standard-schema/json-schema.mjs";
2
2
  import { except } from "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/except.mjs";
3
3
  import "../../../node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/index.mjs";
4
- import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
4
+ import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
5
5
  import { setKeyPath } from "../helpers/path-helpers.mjs";
6
6
  import { BaseValidator } from "./base-validator.mjs";
7
7
  import { objectTrimMutator, stripUnknownMutator } from "../mutators/object-mutators.mjs";
@@ -300,7 +300,8 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
300
300
  if (mutatedData === void 0 || mutatedData === null) return result;
301
301
  const errors = [];
302
302
  const validatedData = {};
303
- const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator]) => {
303
+ const fieldErrors = [];
304
+ const validationPromises = Object.entries(this.schema).filter(([, validator]) => !this.isComputedValidator(validator)).map(async ([key, validator], index) => {
304
305
  const value = mutatedData?.[key] !== void 0 ? mutatedData[key] : validator.getDefaultValue();
305
306
  const childContext = {
306
307
  ...context,
@@ -311,16 +312,18 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
311
312
  };
312
313
  const childResult = await validator.validate(value, childContext);
313
314
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
314
- if (childResult.isValid === false) errors.push(...childResult.errors);
315
+ if (childResult.isValid === false) fieldErrors[index] = childResult.errors;
315
316
  });
316
317
  await Promise.all(validationPromises);
318
+ for (const childErrors of fieldErrors) if (childErrors) errors.push(...childErrors);
317
319
  if (errors.length > 0) return {
318
320
  isValid: false,
319
321
  errors,
320
322
  data: void 0
321
323
  };
322
324
  const computedFields = this.getComputedFields();
323
- const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {
325
+ const computedErrors = [];
326
+ const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {
324
327
  const childContext = {
325
328
  ...context,
326
329
  parent: validatedData,
@@ -328,15 +331,18 @@ var ObjectValidator = class ObjectValidator extends BaseValidator {
328
331
  };
329
332
  const childResult = await validator.validate(validatedData, childContext);
330
333
  if (childResult.data !== void 0 && !validator.isOmitted()) validatedData[key] = childResult.data;
331
- if (childResult.isValid === false) errors.push(...childResult.errors);
334
+ if (childResult.isValid === false) computedErrors[index] = childResult.errors;
332
335
  });
333
336
  await Promise.all(computedPromises);
337
+ for (const childErrors of computedErrors) if (childErrors) errors.push(...childErrors);
334
338
  if (errors.length > 0) return {
335
339
  isValid: false,
336
340
  errors,
337
341
  data: void 0
338
342
  };
339
- const cleanedData = removeUndefinedValues(validatedData);
343
+ const orderedData = {};
344
+ for (const key of Object.keys(this.schema)) if (key in validatedData) orderedData[key] = validatedData[key];
345
+ const cleanedData = removeUndefinedValues(orderedData);
340
346
  const transformedData = await this.startTransformationPipeline(cleanedData, context);
341
347
  return {
342
348
  isValid: true,
@@ -1 +1 @@
1
- {"version":3,"file":"object-validator.mjs","names":[],"sources":["../../../../../../../../../seal/src/validators/object-validator.ts"],"sourcesContent":["import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator]) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(validationPromises);\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator]) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n errors.push(...childResult.errors);\n }\n });\n\n await Promise.all(computedPromises);\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(validatedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"object-validator.mjs","names":[],"sources":["../../../../../../../../../seal/src/validators/object-validator.ts"],"sourcesContent":["import { except } from \"@mongez/reinforcements\";\nimport { isPlainObject } from \"@mongez/supportive-is\";\nimport { setKeyPath } from \"../helpers/path-helpers\";\nimport { objectTrimMutator, stripUnknownMutator } from \"../mutators/object-mutators\";\nimport { objectRule } from \"../rules/common/type-rules\";\nimport { unknownKeyRule } from \"../rules/common/unknown-key\";\nimport type { JsonSchemaResult, JsonSchemaTarget } from \"../standard-schema/json-schema\";\nimport { applyNullable, wrapNullableStrict } from \"../standard-schema/json-schema\";\nimport type { Schema, SchemaContext, ValidationResult } from \"../types\";\nimport type { InferInputObjectShape, InferOutputObjectShape } from \"../types/inference-types\";\nimport { BaseValidator } from \"./base-validator\";\nimport { ComputedValidator } from \"./computed-validator\";\n\n/**\n * Object validator class with generic schema type for proper type inference.\n *\n * Threads two distinct inferred shapes into `BaseValidator`'s `TInput`/`TOutput`:\n *\n * - `TInput` = `InferInputObjectShape<TSchema>` — what `~standard.validate()`\n * accepts (caller's pre-validation payload; defaults/catches make\n * keys optional)\n * - `TOutput` = `InferOutputObjectShape<TSchema>` — what `~standard.validate()`\n * returns on success (post-default, post-catch shape; guaranteed\n * keys are required)\n *\n * Both helpers are used directly (not the full `Infer.*` walker) to avoid a\n * recursive class-base evaluation that would otherwise occur when the\n * brand-aware widening tries to inspect the class type.\n */\nexport class ObjectValidator<TSchema extends Schema = Schema> extends BaseValidator<\n InferInputObjectShape<TSchema>,\n InferOutputObjectShape<TSchema>\n> {\n protected shouldAllowUnknown = false;\n protected allowedKeys: string[] = [];\n protected hasUnknownKeyRule = false;\n\n public constructor(\n public schema: TSchema,\n errorMessage?: string,\n ) {\n super();\n this.addMutableRule(objectRule, errorMessage);\n }\n\n /**\n * Check if value is an object type (plain object, not array or date)\n */\n public matchesType(value: any): boolean {\n return isPlainObject(value);\n }\n\n /** Strip unknown keys from the data */\n public stripUnknown() {\n const validator = this.instance;\n return validator.addMutator(stripUnknownMutator, {\n get allowedKeys() {\n return validator.allowedKeys;\n },\n });\n }\n\n /** Add list of allowed keys that could be in the data but not necessarily validated */\n public allow(...keys: string[]) {\n const validator = this.instance;\n validator.allowedKeys.push(...keys);\n return validator;\n }\n\n /** Trim values of the object properties */\n public trim(recursive = true) {\n const validator = this.instance;\n return validator.addMutator(objectTrimMutator, { recursive });\n }\n\n /** Whether to allow unknown properties\n * Please note it will allow only unknown direct children keys, not nested children keys\n */\n public allowUnknown(allow = true) {\n const validator = this.instance;\n validator.shouldAllowUnknown = allow;\n return validator;\n }\n\n /**\n * Create a copy of this object validator with the same configuration\n * Copies schema, rules, mutators, transformers, and object-specific settings\n *\n * @returns A new ObjectValidator instance with copied configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({ name: v.string() }).allowUnknown();\n * const userCopy = baseUser.clone();\n * // userCopy has the same schema and allowUnknown setting\n * ```\n */\n public override clone(keys?: string[]): this {\n // Get cloned instance with all BaseValidator properties\n const cloned = super.clone();\n\n // Clone schema with deep copy of validators\n const newSchema = {} as TSchema;\n for (const [key, validator] of Object.entries(this.schema)) {\n if (keys && !keys.includes(key)) continue;\n (newSchema as Schema)[key] = validator.clone();\n }\n\n cloned.schema = newSchema;\n\n // Add ObjectValidator-specific properties\n cloned.shouldAllowUnknown = this.shouldAllowUnknown;\n cloned.allowedKeys = [...this.allowedKeys];\n // NOTE: hasUnknownKeyRule is intentionally NOT copied.\n // Each clone must add its own unknownKeyRule on first validate()\n // so it holds references to its own schema/allowedKeys, not the original's.\n\n return cloned;\n }\n\n /**\n * Extend this schema with additional fields\n * Clones the current validator and adds new fields to the schema\n * **Keeps original configuration** (allowUnknown, stripUnknown, etc.)\n *\n * If an ObjectValidator is provided, only its schema is used - its configuration is ignored.\n * This is useful for creating reusable field collections that can be added to different schemas.\n *\n * @param schemaOrValidator - Plain schema object or ObjectValidator to extend with\n * @returns A new ObjectValidator with merged schema and original configuration\n *\n * @example\n * ```ts\n * // Extend with plain schema\n * const baseUser = v.object({\n * name: v.string().required(),\n * email: v.string().email().required()\n * }).allowUnknown();\n *\n * const adminUser = baseUser.extend({\n * role: v.string().in(['admin', 'superadmin']).required()\n * });\n * // adminUser has: name, email, role\n * // adminUser keeps: allowUnknown() from base ✅\n *\n * // Extend with ObjectValidator (only schema is used)\n * const auditFields = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown(); // This config is ignored!\n *\n * const fullUser = baseUser.extend(auditFields);\n * // fullUser has: name, email, createdAt, updatedAt\n * // fullUser keeps: allowUnknown() from base (NOT stripUnknown from auditFields) ✅\n *\n * // Chain multiple extends\n * const complexSchema = baseUser\n * .extend(auditFields)\n * .extend({ metadata: v.object({}) });\n * ```\n */\n public extend<TExtension extends Schema>(\n schemaOrValidator: TExtension | ObjectValidator<TExtension>,\n ): ObjectValidator<TSchema & TExtension> {\n // Clone current validator to preserve original\n const extended = this.clone() as any;\n\n // Extract schema from parameter\n const schemaToAdd =\n schemaOrValidator instanceof ObjectValidator ? schemaOrValidator.schema : schemaOrValidator;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, validator] of Object.entries(schemaToAdd)) {\n (extended.schema as Schema)[key] = validator.clone();\n }\n\n return extended as ObjectValidator<TSchema & TExtension>;\n }\n\n /**\n * Merge with another ObjectValidator\n * Clones current validator, merges schemas, and **overrides configuration** with other validator's config\n *\n * Unlike extend(), merge() combines both schemas AND configurations.\n * The other validator's configuration (allowUnknown, stripUnknown, etc.) takes precedence.\n *\n * @param validator - Another ObjectValidator to merge with\n * @returns A new ObjectValidator with merged schema and configuration\n *\n * @example\n * ```ts\n * const baseUser = v.object({\n * name: v.string().required()\n * }).allowUnknown();\n *\n * const timestamps = v.object({\n * createdAt: v.date().required(),\n * updatedAt: v.date().required()\n * }).stripUnknown();\n *\n * const merged = baseUser.merge(timestamps);\n * // merged has: name, createdAt, updatedAt\n * // merged config: stripUnknown() from timestamps (overrides allowUnknown) ✅\n *\n * // Chain multiple merges\n * const full = baseUser.merge(timestamps).merge(softDeleteSchema);\n * ```\n */\n public merge<TMerge extends Schema>(\n validator: ObjectValidator<TMerge>,\n ): ObjectValidator<TSchema & TMerge> {\n // Clone current validator\n const merged = this.clone() as any;\n\n // Merge schemas with cloned validators (later fields override earlier ones)\n for (const [key, schemaValidator] of Object.entries(validator.schema)) {\n (merged.schema as Schema)[key] = schemaValidator.clone();\n }\n\n // Override configuration with other validator's config\n merged.shouldAllowUnknown = validator.shouldAllowUnknown;\n merged.allowedKeys = [...merged.allowedKeys, ...validator.allowedKeys];\n\n // Append rules, mutators, transformers from other validator\n merged.rules.push(...validator.rules);\n merged.mutators.push(...validator.mutators);\n merged.dataTransformers.push(...validator.dataTransformers);\n\n // Merge attributes text (later wins)\n merged.attributesText = {\n ...merged.attributesText,\n ...validator.attributesText,\n };\n\n merged.translatedAttributes = {\n ...merged.translatedAttributes,\n ...validator.translatedAttributes,\n };\n\n return merged as ObjectValidator<TSchema & TMerge>;\n }\n\n /**\n * Create a new schema with only the specified fields\n * Clones the current validator and keeps only the selected fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to keep in the schema\n * @returns A new ObjectValidator with only the picked fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For login - only need email and password\n * const loginSchema = fullUser.pick('email', 'password');\n * // loginSchema has: { email, password }\n * // loginSchema keeps: allowUnknown() ✅\n *\n * // For public profile\n * const publicSchema = fullUser.pick('id', 'name', 'role');\n * // publicSchema has: { id, name, role }\n * ```\n */\n public pick<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Pick<TSchema, K>> {\n // Clone current validator\n const picked = this.clone() as any;\n\n // Create new schema with only picked keys\n const newSchema = {} as Pick<TSchema, K>;\n for (const key of keys) {\n if (key in picked.schema) {\n (newSchema as any)[key] = picked.schema[key];\n }\n }\n\n picked.schema = newSchema;\n\n return picked as ObjectValidator<Pick<TSchema, K>>;\n }\n\n /**\n * Mark all or the given schema fields as optional\n */\n public partial<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.optional();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Make the all or the given fields as required\n */\n public requiredFields<K extends keyof TSchema>(...keys: K[]) {\n const validationSchema = this.clone();\n\n if (keys.length === 0) {\n keys = Object.keys(validationSchema.schema) as K[];\n }\n\n for (const [key, validator] of Object.entries(validationSchema.schema)) {\n if (keys.length === 0 || keys.includes(key as K)) {\n (validationSchema.schema as Schema)[key] = validator.required();\n }\n }\n\n return validationSchema;\n }\n\n /**\n * Create a new schema excluding the specified fields\n * Clones the current validator and removes the specified fields\n * **Preserves all configuration** (allowUnknown, stripUnknown, etc.)\n *\n * @param keys - Field names to exclude from the schema\n * @returns A new ObjectValidator without the excluded fields\n *\n * @example\n * ```ts\n * const fullUser = v.object({\n * id: v.int().required(),\n * name: v.string().required(),\n * email: v.string().email().required(),\n * password: v.string().required(),\n * role: v.string()\n * }).allowUnknown();\n *\n * // For updates - exclude id\n * const updateSchema = fullUser.without('id');\n * // updateSchema has: { name, email, password, role }\n * // updateSchema keeps: allowUnknown() ✅\n *\n * // For public API - exclude sensitive fields\n * const publicSchema = fullUser.without('password', 'role');\n * // publicSchema has: { id, name, email }\n *\n * // Combine with other methods\n * const patchSchema = fullUser.without('id', 'password');\n * // patchSchema has: { name, email, role }\n * ```\n */\n public without<K extends keyof TSchema>(...keys: K[]): ObjectValidator<Omit<TSchema, K>> {\n // Clone current validator\n const filtered = this.clone() as any;\n\n // Create new schema excluding specified keys\n const newSchema = {} as Omit<TSchema, K>;\n for (const key in filtered.schema) {\n if (!keys.includes(key as any)) {\n (newSchema as any)[key] = filtered.schema[key];\n }\n }\n\n filtered.schema = newSchema;\n\n return filtered as ObjectValidator<Omit<TSchema, K>>;\n }\n\n /** Mutate the data */\n public mutate(data: any, context: SchemaContext) {\n if (!isPlainObject(data)) return data;\n return super.mutate({ ...data }, context);\n }\n\n /** Validate the data */\n public async validate(\n data: any,\n context: SchemaContext = { path: \"\" } as SchemaContext,\n ): Promise<ValidationResult> {\n context.schema = this.schema;\n const mutatedData = await this.mutate(data, context);\n\n // Check for unknown properties\n if (this.shouldAllowUnknown === false && !this.hasUnknownKeyRule) {\n this.hasUnknownKeyRule = true;\n const rule = this.addMutableRule(unknownKeyRule, undefined, {\n allowedKeys: this.allowedKeys,\n schema: this.schema,\n });\n\n this.setRuleAttributesList(rule);\n }\n\n const result = await super.validate(mutatedData, context);\n\n if (result.isValid === false) return result;\n\n // Absent (no default) or null (nullable) inputs short-circuit here —\n // BaseValidator.validate already resolved the outcome, so recursing into\n // the shape below would wrongly re-run required checks against `undefined`.\n if (mutatedData === undefined || mutatedData === null) return result;\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 1: Validate user input fields (skip computed/managed)\n // ═══════════════════════════════════════════════════════════\n const errors: ValidationResult[\"errors\"] = [];\n const validatedData: any = {};\n\n // Child validators settle in completion order, so errors are collected per\n // field and flattened in declared schema order for deterministic output.\n const fieldErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const userInputEntries = Object.entries(this.schema).filter(\n ([, validator]) => !this.isComputedValidator(validator),\n );\n\n const validationPromises = userInputEntries.map(async ([key, validator], index) => {\n const value =\n mutatedData?.[key] !== undefined ? mutatedData[key] : validator.getDefaultValue();\n\n const childContext: SchemaContext = {\n ...context,\n parent: mutatedData,\n value,\n key,\n path: setKeyPath(context.path, key),\n };\n\n const childResult = await validator.validate(value, childContext);\n\n // Only include in validated data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n fieldErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(validationPromises);\n\n for (const childErrors of fieldErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 1 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // ═══════════════════════════════════════════════════════════\n // PHASE 2: Execute computed/managed fields with validated data\n // ═══════════════════════════════════════════════════════════\n const computedFields = this.getComputedFields();\n\n const computedErrors: Array<ValidationResult[\"errors\"]> = [];\n\n const computedPromises = Object.entries(computedFields).map(async ([key, validator], index) => {\n const childContext: SchemaContext = {\n ...context,\n parent: validatedData,\n value: undefined, // Computed fields don't have input value\n };\n\n // Execute computed callback with validated data\n const childResult = await validator.validate(validatedData, childContext);\n\n // Only include in final data if not omitted\n if (childResult.data !== undefined && !validator.isOmitted()) {\n validatedData[key] = childResult.data;\n }\n\n if (childResult.isValid === false) {\n computedErrors[index] = childResult.errors;\n }\n });\n\n await Promise.all(computedPromises);\n\n for (const childErrors of computedErrors) {\n if (childErrors) {\n errors.push(...childErrors);\n }\n }\n\n // If Phase 2 failed, return early\n if (errors.length > 0) {\n return {\n isValid: false,\n errors,\n data: undefined,\n };\n }\n\n // Fields were assigned in the order their validators finished; re-emit\n // them in declared schema order so the output shape is deterministic.\n const orderedData: any = {};\n\n for (const key of Object.keys(this.schema)) {\n if (key in validatedData) {\n orderedData[key] = validatedData[key];\n }\n }\n\n // Remove undefined values\n const cleanedData = removeUndefinedValues(orderedData);\n\n const transformedData = await this.startTransformationPipeline(cleanedData, context);\n\n const output =\n this.shouldAllowUnknown === false\n ? transformedData\n : {\n ...transformedData,\n ...except(mutatedData, Object.keys(this.schema)),\n };\n\n return {\n isValid: true,\n errors: [],\n data: output,\n };\n }\n\n /**\n * Check if a validator is a computed or managed field\n * ManagedValidator extends ComputedValidator, so instanceof catches both\n */\n private isComputedValidator(validator: BaseValidator): boolean {\n return validator instanceof ComputedValidator;\n }\n\n /**\n * Get all computed/managed fields from the schema\n */\n private getComputedFields(): Record<string, ComputedValidator> {\n const computed: Record<string, ComputedValidator> = {};\n\n for (const [key, validator] of Object.entries(this.schema)) {\n if (validator instanceof ComputedValidator) {\n computed[key] = validator;\n }\n }\n\n return computed;\n }\n\n /**\n * @inheritdoc\n *\n * Recursively generates JSON Schema for all input fields in the schema.\n * Computed/managed fields are skipped — they have no input representation.\n *\n * **Standard targets** (`draft-2020-12`, `draft-07`, `openapi-3.0`):\n * - Fields marked `.optional()` are excluded from `required`.\n *\n * **`openai-strict` target** (OpenAI Structured Outputs):\n * - ALL fields appear in `required` — OpenAI rejects schemas with optional fields.\n * - Optional fields are instead expressed as nullable types:\n * `{ type: [\"string\", \"null\"] }` so the model can output `null` for them.\n * - Recursively applies `openai-strict` to all nested objects.\n *\n * @example\n * ```ts\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"draft-2020-12\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: \"integer\" } },\n * // required: [\"name\"], additionalProperties: false }\n *\n * v.object({\n * name: v.string().required(),\n * age: v.int().optional(),\n * }).toJsonSchema(\"openai-strict\")\n * // → { type: \"object\",\n * // properties: { name: { type: \"string\" }, age: { type: [\"integer\", \"null\"] } },\n * // required: [\"name\", \"age\"], ← all fields\n * // additionalProperties: false }\n * ```\n */\n public override toJsonSchema(target: JsonSchemaTarget = \"draft-2020-12\"): JsonSchemaResult {\n const properties: Record<string, JsonSchemaResult> = {};\n const required: string[] = [];\n const isOpenAIStrict = target === \"openai-strict\";\n\n for (const [key, validator] of Object.entries(this.schema)) {\n // Skip computed/managed — runtime-only, no input schema\n if (validator instanceof ComputedValidator) continue;\n\n let fieldSchema = validator.toJsonSchema(target);\n\n if (isOpenAIStrict) {\n // OpenAI strict: every field must be in required.\n // Optional fields are expressed as nullable rather than absent.\n if (validator.isOptional) {\n fieldSchema = wrapNullableStrict(fieldSchema);\n }\n required.push(key);\n } else {\n // Standard JSON Schema: only non-optional fields go in required\n if (!validator.isOptional) {\n required.push(key);\n }\n }\n\n properties[key] = fieldSchema;\n }\n\n const schema: JsonSchemaResult = { type: \"object\", properties };\n\n if (required.length > 0) schema.required = required;\n if (!this.shouldAllowUnknown) schema.additionalProperties = false;\n if (this.isNullable) applyNullable(schema, target);\n\n return schema;\n }\n}\n\n/** Recursively remove undefined values from an object */\nfunction removeUndefinedValues(obj: any, visited = new WeakMap<object, any>()): any {\n // Handle primitives and null\n if (obj === null) {\n return obj;\n }\n\n // Handle arrays\n if (Array.isArray(obj)) {\n return obj.map((item) => removeUndefinedValues(item, visited));\n }\n\n // Skip non-plain objects (class instances, Dates, Buffers, etc.)\n if (!isPlainObject(obj)) {\n return obj;\n }\n\n // Handle circular references - return already processed result\n if (visited.has(obj)) {\n return visited.get(obj);\n }\n\n // Process plain objects\n const result: any = {};\n visited.set(obj, result); // Mark as processing BEFORE recursion\n\n for (const [key, value] of Object.entries(obj)) {\n if (value !== undefined) {\n result[key] = removeUndefinedValues(value, visited);\n }\n }\n\n return result;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { applyNullable } from "../standard-schema/json-schema.mjs";
2
- import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@2.1.5/node_modules/@mongez/supportive-is/esm/index.mjs";
2
+ import { isPlainObject } from "../../../node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs";
3
3
  import { setKeyPath } from "../helpers/path-helpers.mjs";
4
4
  import "../helpers/index.mjs";
5
5
  import { BaseValidator } from "./base-validator.mjs";
package/llms.txt CHANGED
@@ -6,12 +6,12 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [configure-notifications](@warlock.js/notifications/configure-notifications/SKILL.md): (no description)
10
- - [define-channel](@warlock.js/notifications/define-channel/SKILL.md): (no description)
11
- - [define-notification](@warlock.js/notifications/define-notification/SKILL.md): (no description)
9
+ - [configure-notifications](@warlock.js/notifications/configure-notifications/SKILL.md): Wire `@warlock.js/notifications` via a declarative `src/config/notifications.ts` that exports a `NotificationConfig` default — the notifications connector registers it at boot, so app code never calls `setNotificationConfig`. `channels`: registry mapping name → factory result (`mailChannel`, `inApp.configure`, custom `defineChannel`); `queue`: optional `QueueDispatcher` (`.queue()` throws without it); `preferences`: optional `PreferenceProvider` (drops channels the recipient muted); `rateLimit`: optional `RateLimiter` (drops channels exceeding per-recipient budgets). Triggers: `config/notifications.ts`, `NotificationConfig`, `setNotificationConfig`, `getNotificationConfig`, `mailChannel`, `inApp.configure`, "configure notifications", "wire notifications at boot", "set preferences provider", "set rate limit"; typical import `import { type NotificationConfig, mailChannel, inApp } from "@warlock.js/notifications"`. Skip: building a reusable notification — `@warlock.js/notifications/define-notification/SKILL.md`; the in-app read API — `@warlock.js/notifications/use-in-app/SKILL.md`; adding a custom channel — `@warlock.js/notifications/define-channel/SKILL.md`.
10
+ - [define-channel](@warlock.js/notifications/define-channel/SKILL.md): Add a custom notification channel — `defineChannel<P>({ name, route?, send })` returns a `Channel<P>` you register in the `channels` map of `setNotificationConfig`. `name` matches the key in `NotificationChannels` (extend via `declare module` for typing); `route(notifiable)` resolves the recipient's address (defaults to `{ id }` when omitted); `send({ payload, route, notifiable, options })` does the actual transport. Use for Slack/Discord/internal-webhook/anything else — `fetch`-based with NO SDK is the simplest variant. Channels that load a heavy SDK should follow the lazy-import pattern from the develop-feature playbook. Triggers: `defineChannel`, `Channel<P>`, "custom notification channel", "add discord channel", "slack channel", "webhook channel", "new channel type", `declare module "@warlock.js/notifications"`; typical import `import { defineChannel } from "@warlock.js/notifications"`. Skip: lazy-loading an optional SDK behind the channel — `D:/xampp/htdocs/mongez/node/.claude/skills/develop-warlock.js-feature/SKILL.md`; using built-in channels — `@warlock.js/notifications/configure-notifications/SKILL.md`.
11
+ - [define-notification](@warlock.js/notifications/define-notification/SKILL.md): Build a reusable multi-channel notification with `defineNotification<Data>({ type, via, ...renderers })`. `type` is the stable identifier preference/rate-limit gates use AND defaults the database channel's `type`. `via` is `ChannelName[]` OR `(data, to) => ChannelName[]` for per-recipient channel selection. Renderers are `(data, to, ctx) => NotificationChannels[C]`; `ctx` carries `locale` + `meta` from `SendOptions`. The returned object exposes `.send(to|to[], data, options?)`, `.queue(to|to[], data, options?)`, and `.only(...channels)`. Per-recipient + per-channel `Promise.allSettled` isolates failures; the database channel's `type` is auto-defaulted from `def.type`; `SendOptions.idempotencyKey` is injected into the database payload. Triggers: `defineNotification`, `via`, "reusable notification", "multi-channel notification", "send through several channels at once", "render per channel", `RenderContext`, `def.type`; "force on a notification", "queue with delay"; typical import `import { defineNotification } from "@warlock.js/notifications"`. Skip: single-channel ad-hoc sends — `@warlock.js/notifications/send-ad-hoc/SKILL.md`; adding a brand-new channel — `@warlock.js/notifications/define-channel/SKILL.md`; observing dispatch outcomes — `@warlock.js/notifications/observe-notifications/SKILL.md`.
12
12
  - [notifications-basics](@warlock.js/notifications/notifications-basics/SKILL.md): Start here for @warlock.js/notifications channels, notification definitions, delivery, and application setup.
13
- - [observe-notifications](@warlock.js/notifications/observe-notifications/SKILL.md): (no description)
14
- - [queue-notifications](@warlock.js/notifications/queue-notifications/SKILL.md): (no description)
15
- - [send-ad-hoc](@warlock.js/notifications/send-ad-hoc/SKILL.md): (no description)
16
- - [use-in-app](@warlock.js/notifications/use-in-app/SKILL.md): (no description)
17
- - [write-notification-migration](@warlock.js/notifications/write-notification-migration/SKILL.md): (no description)
13
+ - [observe-notifications](@warlock.js/notifications/observe-notifications/SKILL.md): Wire metrics, logging, tracing, and audits via `notifications.on(event, handler)` — four events fire per (channel, recipient): `sending` (gates passed, about to hit the transport), `sent` (succeeded), `failed` (channel threw — carries the Error), `skipped` (a pre-send gate dropped it — carries `reason: "preference" | "rate-limit"`). Every event carries the recipient as `notifiable?` (undefined only for raw-target ad-hoc sends) and a `dispatchId` — `sending` and its terminal `sent`/`failed` share it, so observers can pair them (spans, latency, hung-send detection). `sent`/`failed` also carry `durationMs`. Handler exceptions are logged-and-swallowed so one bad listener can not break the dispatcher. `on(...)` returns an unsubscribe function; `off(event, handler)` exists for symmetry. Triggers: `notifications.on`, `notifications.off`, `"sending"` event, `"sent"` event, `"failed"` event, `"skipped"` event, `dispatchId`, `durationMs`, `NotificationEvents`, "notification metrics", "notification latency", "trace notifications", "notification audit log", "track notification drops"; typical import `import { notifications } from "@warlock.js/notifications"`. Skip: building notifications — `@warlock.js/notifications/define-notification/SKILL.md`; configuring the preference/rate-limit gates that produce `skipped` — `@warlock.js/notifications/configure-notifications/SKILL.md`.
14
+ - [queue-notifications](@warlock.js/notifications/queue-notifications/SKILL.md): Send notifications asynchronously through the herald backend or the BullMQ driver. Configure a QueueDispatcher in config/notifications.ts, then use notif.queue(to, data, options?) to enqueue rendered jobs. Herald uses heraldQueue() and startNotificationsWorker(); bullmqQueue({ attempts, backoff }) from @warlock.js/notifications uses BullMQ (lazy-loading the optional @warlock.js/queue peer), supports retries, and honours SendOptions.delay. Without a queue configured, .queue() rejects NoQueueDispatcherError. Triggers: heraldQueue, bullmqQueue, startNotificationsWorker, QueueDispatcher, .queue(, async notifications, queue notifications, notification worker, background notifications, notifications.dispatch. Skip: synchronous sends - @warlock.js/notifications/define-notification/SKILL.md; herald itself - @warlock.js/herald/*.
15
+ - [send-ad-hoc](@warlock.js/notifications/send-ad-hoc/SKILL.md): Ad-hoc per-channel send via the `notify.<channel>(to, payload, options?)` Proxy facade. Works for any channel registered in `NotificationChannels` (built-in or custom). `to` accepts a `Notifiable` model OR a raw route string (`"x@y.com"`); `payload` is typed per channel via the registry; `options` is the standard `SendOptions`. `notify.channel(name).send(...)` is the runtime escape when the channel name is not a compile-time literal. Type-based preference gating applies when a type is known — explicit via `options.type` OR auto-detected from a database payload's `type` field. For multi-channel sends use `defineNotification` instead — there is no inline `notify(to, { via, ... })` form on purpose. Triggers: `notify.<channel>`, `notify.channel(name)`, `notify.mail`, `notify.database`, "send a one-off notification", "ad-hoc notification", "send an email without defining a notification", "raw email target", "dynamic channel name"; typical import `import { notify } from "@warlock.js/notifications"`. Skip: reusable multi-channel notifications — `@warlock.js/notifications/define-notification/SKILL.md`; configuring channels — `@warlock.js/notifications/configure-notifications/SKILL.md`.
16
+ - [use-in-app](@warlock.js/notifications/use-in-app/SKILL.md): Use the `inApp` facade for the in-app/database channel's READ side: `inApp.configure({ model: Notification })` or `{ repository: myRepo }` binds the store AND returns the channel; `inApp.list / listUnread / countUnread / markAsRead / markAsUnread` are RECIPIENT-SCOPED by construction (no IDOR — `markAsRead(user, id)` cannot flip another user's row even with a foreign id). `countUnread` uses `RepositoryManager.countCached` for the badge fast-path. Triggers: `inApp.configure`, `inApp.list`, `inApp.listUnread`, `inApp.countUnread`, `inApp.markAsRead`, `inApp.markAsUnread`, `BaseNotificationsRepository`, `DatabaseNotification`, `NotificationsFilter`, "in-app notifications", "mark notification as read", "unread count", "notification badge", "IDOR-safe markAsRead"; typical import `import { inApp } from "@warlock.js/notifications"`. Skip: writing notifications (use `defineNotification` or `notify.database`) — `@warlock.js/notifications/define-notification/SKILL.md`; the migration columns — `@warlock.js/notifications/write-notification-migration/SKILL.md`; wiring it into config — `@warlock.js/notifications/configure-notifications/SKILL.md`.
17
+ - [write-notification-migration](@warlock.js/notifications/write-notification-migration/SKILL.md): Create the notifications table with the `notificationColumns(Notification)` factory + cascade `Migration.create`. Column NAMES come from the model's `columnMap` (recipient / tenant / readAt / isRead); the fixed columns are `type` / `title` / `body` / `payload` / `idempotency_key`. Set `{ idType: "bigint" }` for integer-key apps; UUID remains the default. Read-state follows columnMap presence: `readAt` → nullable timestamp, `isRead` → indexed boolean, both → both. Defaults to `user_id` + `read_at` when no columnMap. Spread + extend for extras (FK references, composite indexes). Triggers: `notificationColumns`, `Migration.create` for notifications, "create the notifications table", "notification migration", "add organization_id to notifications", `is_read` column, `read_at` column, `idempotency_key` column; typical import `import { Migration } from "@warlock.js/cascade"; import { notificationColumns } from "@warlock.js/notifications"`. Skip: generic cascade migration writing — `@warlock.js/cascade/write-migration/SKILL.md`; the model itself — `@warlock.js/notifications/use-in-app/SKILL.md`.
package/package.json CHANGED
@@ -5,16 +5,16 @@
5
5
  "environment": "server"
6
6
  },
7
7
  "dependencies": {
8
- "@warlock.js/cache": "5.26.0",
9
- "@warlock.js/cascade": "5.26.0",
10
- "@warlock.js/core": "5.26.0",
11
- "@warlock.js/logger": "5.26.0"
8
+ "@warlock.js/cache": "5.28.0",
9
+ "@warlock.js/cascade": "5.28.0",
10
+ "@warlock.js/core": "5.28.0",
11
+ "@warlock.js/logger": "5.28.0"
12
12
  },
13
13
  "optionalDependencies": {
14
- "@warlock.js/herald": "5.26.0"
14
+ "@warlock.js/herald": "5.28.0"
15
15
  },
16
16
  "peerDependencies": {
17
- "@warlock.js/queue": "5.26.0"
17
+ "@warlock.js/queue": "5.28.0"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "@warlock.js/queue": {
@@ -35,7 +35,7 @@
35
35
  ],
36
36
  "author": "hassanzohdy",
37
37
  "license": "MIT",
38
- "version": "5.26.0",
38
+ "version": "5.28.0",
39
39
  "main": "./cjs/index.cjs",
40
40
  "module": "./esm/index.mjs",
41
41
  "types": "./esm/index.d.mts",
@@ -1,3 +0,0 @@
1
- const require_src = require('./src-BVlqtCR2.cjs');
2
-
3
- exports.RabbitMQDriver = require_src.RabbitMQDriver;