util.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // functions
  2. export function assertEqual(val) {
  3. return val;
  4. }
  5. export function assertNotEqual(val) {
  6. return val;
  7. }
  8. export function assertIs(_arg) { }
  9. export function assertNever(_x) {
  10. throw new Error("Unexpected value in exhaustive check");
  11. }
  12. export function assert(_) { }
  13. export function getEnumValues(entries) {
  14. const numericValues = Object.values(entries).filter((v) => typeof v === "number");
  15. const values = Object.entries(entries)
  16. .filter(([k, _]) => numericValues.indexOf(+k) === -1)
  17. .map(([_, v]) => v);
  18. return values;
  19. }
  20. export function joinValues(array, separator = "|") {
  21. return array.map((val) => stringifyPrimitive(val)).join(separator);
  22. }
  23. export function jsonStringifyReplacer(_, value) {
  24. if (typeof value === "bigint")
  25. return value.toString();
  26. return value;
  27. }
  28. export function cached(getter) {
  29. const set = false;
  30. return {
  31. get value() {
  32. if (!set) {
  33. const value = getter();
  34. Object.defineProperty(this, "value", { value });
  35. return value;
  36. }
  37. throw new Error("cached value already set");
  38. },
  39. };
  40. }
  41. export function nullish(input) {
  42. return input === null || input === undefined;
  43. }
  44. export function cleanRegex(source) {
  45. const start = source.startsWith("^") ? 1 : 0;
  46. const end = source.endsWith("$") ? source.length - 1 : source.length;
  47. return source.slice(start, end);
  48. }
  49. export function floatSafeRemainder(val, step) {
  50. const valDecCount = (val.toString().split(".")[1] || "").length;
  51. const stepString = step.toString();
  52. let stepDecCount = (stepString.split(".")[1] || "").length;
  53. if (stepDecCount === 0 && /\d?e-\d?/.test(stepString)) {
  54. const match = stepString.match(/\d?e-(\d?)/);
  55. if (match?.[1]) {
  56. stepDecCount = Number.parseInt(match[1]);
  57. }
  58. }
  59. const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
  60. const valInt = Number.parseInt(val.toFixed(decCount).replace(".", ""));
  61. const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
  62. return (valInt % stepInt) / 10 ** decCount;
  63. }
  64. const EVALUATING = Symbol("evaluating");
  65. export function defineLazy(object, key, getter) {
  66. let value = undefined;
  67. Object.defineProperty(object, key, {
  68. get() {
  69. if (value === EVALUATING) {
  70. // Circular reference detected, return undefined to break the cycle
  71. return undefined;
  72. }
  73. if (value === undefined) {
  74. value = EVALUATING;
  75. value = getter();
  76. }
  77. return value;
  78. },
  79. set(v) {
  80. Object.defineProperty(object, key, {
  81. value: v,
  82. // configurable: true,
  83. });
  84. // object[key] = v;
  85. },
  86. configurable: true,
  87. });
  88. }
  89. export function objectClone(obj) {
  90. return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
  91. }
  92. export function assignProp(target, prop, value) {
  93. Object.defineProperty(target, prop, {
  94. value,
  95. writable: true,
  96. enumerable: true,
  97. configurable: true,
  98. });
  99. }
  100. export function mergeDefs(...defs) {
  101. const mergedDescriptors = {};
  102. for (const def of defs) {
  103. const descriptors = Object.getOwnPropertyDescriptors(def);
  104. Object.assign(mergedDescriptors, descriptors);
  105. }
  106. return Object.defineProperties({}, mergedDescriptors);
  107. }
  108. export function cloneDef(schema) {
  109. return mergeDefs(schema._zod.def);
  110. }
  111. export function getElementAtPath(obj, path) {
  112. if (!path)
  113. return obj;
  114. return path.reduce((acc, key) => acc?.[key], obj);
  115. }
  116. export function promiseAllObject(promisesObj) {
  117. const keys = Object.keys(promisesObj);
  118. const promises = keys.map((key) => promisesObj[key]);
  119. return Promise.all(promises).then((results) => {
  120. const resolvedObj = {};
  121. for (let i = 0; i < keys.length; i++) {
  122. resolvedObj[keys[i]] = results[i];
  123. }
  124. return resolvedObj;
  125. });
  126. }
  127. export function randomString(length = 10) {
  128. const chars = "abcdefghijklmnopqrstuvwxyz";
  129. let str = "";
  130. for (let i = 0; i < length; i++) {
  131. str += chars[Math.floor(Math.random() * chars.length)];
  132. }
  133. return str;
  134. }
  135. export function esc(str) {
  136. return JSON.stringify(str);
  137. }
  138. export function slugify(input) {
  139. return input
  140. .toLowerCase()
  141. .trim()
  142. .replace(/[^\w\s-]/g, "")
  143. .replace(/[\s_-]+/g, "-")
  144. .replace(/^-+|-+$/g, "");
  145. }
  146. export const captureStackTrace = ("captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => { });
  147. export function isObject(data) {
  148. return typeof data === "object" && data !== null && !Array.isArray(data);
  149. }
  150. export const allowsEval = cached(() => {
  151. // @ts-ignore
  152. if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
  153. return false;
  154. }
  155. try {
  156. const F = Function;
  157. new F("");
  158. return true;
  159. }
  160. catch (_) {
  161. return false;
  162. }
  163. });
  164. export function isPlainObject(o) {
  165. if (isObject(o) === false)
  166. return false;
  167. // modified constructor
  168. const ctor = o.constructor;
  169. if (ctor === undefined)
  170. return true;
  171. if (typeof ctor !== "function")
  172. return true;
  173. // modified prototype
  174. const prot = ctor.prototype;
  175. if (isObject(prot) === false)
  176. return false;
  177. // ctor doesn't have static `isPrototypeOf`
  178. if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
  179. return false;
  180. }
  181. return true;
  182. }
  183. export function shallowClone(o) {
  184. if (isPlainObject(o))
  185. return { ...o };
  186. if (Array.isArray(o))
  187. return [...o];
  188. return o;
  189. }
  190. export function numKeys(data) {
  191. let keyCount = 0;
  192. for (const key in data) {
  193. if (Object.prototype.hasOwnProperty.call(data, key)) {
  194. keyCount++;
  195. }
  196. }
  197. return keyCount;
  198. }
  199. export const getParsedType = (data) => {
  200. const t = typeof data;
  201. switch (t) {
  202. case "undefined":
  203. return "undefined";
  204. case "string":
  205. return "string";
  206. case "number":
  207. return Number.isNaN(data) ? "nan" : "number";
  208. case "boolean":
  209. return "boolean";
  210. case "function":
  211. return "function";
  212. case "bigint":
  213. return "bigint";
  214. case "symbol":
  215. return "symbol";
  216. case "object":
  217. if (Array.isArray(data)) {
  218. return "array";
  219. }
  220. if (data === null) {
  221. return "null";
  222. }
  223. if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") {
  224. return "promise";
  225. }
  226. if (typeof Map !== "undefined" && data instanceof Map) {
  227. return "map";
  228. }
  229. if (typeof Set !== "undefined" && data instanceof Set) {
  230. return "set";
  231. }
  232. if (typeof Date !== "undefined" && data instanceof Date) {
  233. return "date";
  234. }
  235. // @ts-ignore
  236. if (typeof File !== "undefined" && data instanceof File) {
  237. return "file";
  238. }
  239. return "object";
  240. default:
  241. throw new Error(`Unknown data type: ${t}`);
  242. }
  243. };
  244. export const propertyKeyTypes = new Set(["string", "number", "symbol"]);
  245. export const primitiveTypes = new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]);
  246. export function escapeRegex(str) {
  247. return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  248. }
  249. // zod-specific utils
  250. export function clone(inst, def, params) {
  251. const cl = new inst._zod.constr(def ?? inst._zod.def);
  252. if (!def || params?.parent)
  253. cl._zod.parent = inst;
  254. return cl;
  255. }
  256. export function normalizeParams(_params) {
  257. const params = _params;
  258. if (!params)
  259. return {};
  260. if (typeof params === "string")
  261. return { error: () => params };
  262. if (params?.message !== undefined) {
  263. if (params?.error !== undefined)
  264. throw new Error("Cannot specify both `message` and `error` params");
  265. params.error = params.message;
  266. }
  267. delete params.message;
  268. if (typeof params.error === "string")
  269. return { ...params, error: () => params.error };
  270. return params;
  271. }
  272. export function createTransparentProxy(getter) {
  273. let target;
  274. return new Proxy({}, {
  275. get(_, prop, receiver) {
  276. target ?? (target = getter());
  277. return Reflect.get(target, prop, receiver);
  278. },
  279. set(_, prop, value, receiver) {
  280. target ?? (target = getter());
  281. return Reflect.set(target, prop, value, receiver);
  282. },
  283. has(_, prop) {
  284. target ?? (target = getter());
  285. return Reflect.has(target, prop);
  286. },
  287. deleteProperty(_, prop) {
  288. target ?? (target = getter());
  289. return Reflect.deleteProperty(target, prop);
  290. },
  291. ownKeys(_) {
  292. target ?? (target = getter());
  293. return Reflect.ownKeys(target);
  294. },
  295. getOwnPropertyDescriptor(_, prop) {
  296. target ?? (target = getter());
  297. return Reflect.getOwnPropertyDescriptor(target, prop);
  298. },
  299. defineProperty(_, prop, descriptor) {
  300. target ?? (target = getter());
  301. return Reflect.defineProperty(target, prop, descriptor);
  302. },
  303. });
  304. }
  305. export function stringifyPrimitive(value) {
  306. if (typeof value === "bigint")
  307. return value.toString() + "n";
  308. if (typeof value === "string")
  309. return `"${value}"`;
  310. return `${value}`;
  311. }
  312. export function optionalKeys(shape) {
  313. return Object.keys(shape).filter((k) => {
  314. return shape[k]._zod.optin === "optional" && shape[k]._zod.optout === "optional";
  315. });
  316. }
  317. export const NUMBER_FORMAT_RANGES = {
  318. safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
  319. int32: [-2147483648, 2147483647],
  320. uint32: [0, 4294967295],
  321. float32: [-3.4028234663852886e38, 3.4028234663852886e38],
  322. float64: [-Number.MAX_VALUE, Number.MAX_VALUE],
  323. };
  324. export const BIGINT_FORMAT_RANGES = {
  325. int64: [/* @__PURE__*/ BigInt("-9223372036854775808"), /* @__PURE__*/ BigInt("9223372036854775807")],
  326. uint64: [/* @__PURE__*/ BigInt(0), /* @__PURE__*/ BigInt("18446744073709551615")],
  327. };
  328. export function pick(schema, mask) {
  329. const currDef = schema._zod.def;
  330. const checks = currDef.checks;
  331. const hasChecks = checks && checks.length > 0;
  332. if (hasChecks) {
  333. throw new Error(".pick() cannot be used on object schemas containing refinements");
  334. }
  335. const def = mergeDefs(schema._zod.def, {
  336. get shape() {
  337. const newShape = {};
  338. for (const key in mask) {
  339. if (!(key in currDef.shape)) {
  340. throw new Error(`Unrecognized key: "${key}"`);
  341. }
  342. if (!mask[key])
  343. continue;
  344. newShape[key] = currDef.shape[key];
  345. }
  346. assignProp(this, "shape", newShape); // self-caching
  347. return newShape;
  348. },
  349. checks: [],
  350. });
  351. return clone(schema, def);
  352. }
  353. export function omit(schema, mask) {
  354. const currDef = schema._zod.def;
  355. const checks = currDef.checks;
  356. const hasChecks = checks && checks.length > 0;
  357. if (hasChecks) {
  358. throw new Error(".omit() cannot be used on object schemas containing refinements");
  359. }
  360. const def = mergeDefs(schema._zod.def, {
  361. get shape() {
  362. const newShape = { ...schema._zod.def.shape };
  363. for (const key in mask) {
  364. if (!(key in currDef.shape)) {
  365. throw new Error(`Unrecognized key: "${key}"`);
  366. }
  367. if (!mask[key])
  368. continue;
  369. delete newShape[key];
  370. }
  371. assignProp(this, "shape", newShape); // self-caching
  372. return newShape;
  373. },
  374. checks: [],
  375. });
  376. return clone(schema, def);
  377. }
  378. export function extend(schema, shape) {
  379. if (!isPlainObject(shape)) {
  380. throw new Error("Invalid input to extend: expected a plain object");
  381. }
  382. const checks = schema._zod.def.checks;
  383. const hasChecks = checks && checks.length > 0;
  384. if (hasChecks) {
  385. // Only throw if new shape overlaps with existing shape
  386. // Use getOwnPropertyDescriptor to check key existence without accessing values
  387. const existingShape = schema._zod.def.shape;
  388. for (const key in shape) {
  389. if (Object.getOwnPropertyDescriptor(existingShape, key) !== undefined) {
  390. throw new Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.");
  391. }
  392. }
  393. }
  394. const def = mergeDefs(schema._zod.def, {
  395. get shape() {
  396. const _shape = { ...schema._zod.def.shape, ...shape };
  397. assignProp(this, "shape", _shape); // self-caching
  398. return _shape;
  399. },
  400. });
  401. return clone(schema, def);
  402. }
  403. export function safeExtend(schema, shape) {
  404. if (!isPlainObject(shape)) {
  405. throw new Error("Invalid input to safeExtend: expected a plain object");
  406. }
  407. const def = mergeDefs(schema._zod.def, {
  408. get shape() {
  409. const _shape = { ...schema._zod.def.shape, ...shape };
  410. assignProp(this, "shape", _shape); // self-caching
  411. return _shape;
  412. },
  413. });
  414. return clone(schema, def);
  415. }
  416. export function merge(a, b) {
  417. const def = mergeDefs(a._zod.def, {
  418. get shape() {
  419. const _shape = { ...a._zod.def.shape, ...b._zod.def.shape };
  420. assignProp(this, "shape", _shape); // self-caching
  421. return _shape;
  422. },
  423. get catchall() {
  424. return b._zod.def.catchall;
  425. },
  426. checks: [], // delete existing checks
  427. });
  428. return clone(a, def);
  429. }
  430. export function partial(Class, schema, mask) {
  431. const currDef = schema._zod.def;
  432. const checks = currDef.checks;
  433. const hasChecks = checks && checks.length > 0;
  434. if (hasChecks) {
  435. throw new Error(".partial() cannot be used on object schemas containing refinements");
  436. }
  437. const def = mergeDefs(schema._zod.def, {
  438. get shape() {
  439. const oldShape = schema._zod.def.shape;
  440. const shape = { ...oldShape };
  441. if (mask) {
  442. for (const key in mask) {
  443. if (!(key in oldShape)) {
  444. throw new Error(`Unrecognized key: "${key}"`);
  445. }
  446. if (!mask[key])
  447. continue;
  448. // if (oldShape[key]!._zod.optin === "optional") continue;
  449. shape[key] = Class
  450. ? new Class({
  451. type: "optional",
  452. innerType: oldShape[key],
  453. })
  454. : oldShape[key];
  455. }
  456. }
  457. else {
  458. for (const key in oldShape) {
  459. // if (oldShape[key]!._zod.optin === "optional") continue;
  460. shape[key] = Class
  461. ? new Class({
  462. type: "optional",
  463. innerType: oldShape[key],
  464. })
  465. : oldShape[key];
  466. }
  467. }
  468. assignProp(this, "shape", shape); // self-caching
  469. return shape;
  470. },
  471. checks: [],
  472. });
  473. return clone(schema, def);
  474. }
  475. export function required(Class, schema, mask) {
  476. const def = mergeDefs(schema._zod.def, {
  477. get shape() {
  478. const oldShape = schema._zod.def.shape;
  479. const shape = { ...oldShape };
  480. if (mask) {
  481. for (const key in mask) {
  482. if (!(key in shape)) {
  483. throw new Error(`Unrecognized key: "${key}"`);
  484. }
  485. if (!mask[key])
  486. continue;
  487. // overwrite with non-optional
  488. shape[key] = new Class({
  489. type: "nonoptional",
  490. innerType: oldShape[key],
  491. });
  492. }
  493. }
  494. else {
  495. for (const key in oldShape) {
  496. // overwrite with non-optional
  497. shape[key] = new Class({
  498. type: "nonoptional",
  499. innerType: oldShape[key],
  500. });
  501. }
  502. }
  503. assignProp(this, "shape", shape); // self-caching
  504. return shape;
  505. },
  506. });
  507. return clone(schema, def);
  508. }
  509. // invalid_type | too_big | too_small | invalid_format | not_multiple_of | unrecognized_keys | invalid_union | invalid_key | invalid_element | invalid_value | custom
  510. export function aborted(x, startIndex = 0) {
  511. if (x.aborted === true)
  512. return true;
  513. for (let i = startIndex; i < x.issues.length; i++) {
  514. if (x.issues[i]?.continue !== true) {
  515. return true;
  516. }
  517. }
  518. return false;
  519. }
  520. export function prefixIssues(path, issues) {
  521. return issues.map((iss) => {
  522. var _a;
  523. (_a = iss).path ?? (_a.path = []);
  524. iss.path.unshift(path);
  525. return iss;
  526. });
  527. }
  528. export function unwrapMessage(message) {
  529. return typeof message === "string" ? message : message?.message;
  530. }
  531. export function finalizeIssue(iss, ctx, config) {
  532. const full = { ...iss, path: iss.path ?? [] };
  533. // for backwards compatibility
  534. if (!iss.message) {
  535. const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ??
  536. unwrapMessage(ctx?.error?.(iss)) ??
  537. unwrapMessage(config.customError?.(iss)) ??
  538. unwrapMessage(config.localeError?.(iss)) ??
  539. "Invalid input";
  540. full.message = message;
  541. }
  542. // delete (full as any).def;
  543. delete full.inst;
  544. delete full.continue;
  545. if (!ctx?.reportInput) {
  546. delete full.input;
  547. }
  548. return full;
  549. }
  550. export function getSizableOrigin(input) {
  551. if (input instanceof Set)
  552. return "set";
  553. if (input instanceof Map)
  554. return "map";
  555. // @ts-ignore
  556. if (input instanceof File)
  557. return "file";
  558. return "unknown";
  559. }
  560. export function getLengthableOrigin(input) {
  561. if (Array.isArray(input))
  562. return "array";
  563. if (typeof input === "string")
  564. return "string";
  565. return "unknown";
  566. }
  567. export function parsedType(data) {
  568. const t = typeof data;
  569. switch (t) {
  570. case "number": {
  571. return Number.isNaN(data) ? "nan" : "number";
  572. }
  573. case "object": {
  574. if (data === null) {
  575. return "null";
  576. }
  577. if (Array.isArray(data)) {
  578. return "array";
  579. }
  580. const obj = data;
  581. if (obj && Object.getPrototypeOf(obj) !== Object.prototype && "constructor" in obj && obj.constructor) {
  582. return obj.constructor.name;
  583. }
  584. }
  585. }
  586. return t;
  587. }
  588. export function issue(...args) {
  589. const [iss, input, inst] = args;
  590. if (typeof iss === "string") {
  591. return {
  592. message: iss,
  593. code: "custom",
  594. input,
  595. inst,
  596. };
  597. }
  598. return { ...iss };
  599. }
  600. export function cleanEnum(obj) {
  601. return Object.entries(obj)
  602. .filter(([k, _]) => {
  603. // return true if NaN, meaning it's not a number, thus a string key
  604. return Number.isNaN(Number.parseInt(k, 10));
  605. })
  606. .map((el) => el[1]);
  607. }
  608. // Codec utility functions
  609. export function base64ToUint8Array(base64) {
  610. const binaryString = atob(base64);
  611. const bytes = new Uint8Array(binaryString.length);
  612. for (let i = 0; i < binaryString.length; i++) {
  613. bytes[i] = binaryString.charCodeAt(i);
  614. }
  615. return bytes;
  616. }
  617. export function uint8ArrayToBase64(bytes) {
  618. let binaryString = "";
  619. for (let i = 0; i < bytes.length; i++) {
  620. binaryString += String.fromCharCode(bytes[i]);
  621. }
  622. return btoa(binaryString);
  623. }
  624. export function base64urlToUint8Array(base64url) {
  625. const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
  626. const padding = "=".repeat((4 - (base64.length % 4)) % 4);
  627. return base64ToUint8Array(base64 + padding);
  628. }
  629. export function uint8ArrayToBase64url(bytes) {
  630. return uint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
  631. }
  632. export function hexToUint8Array(hex) {
  633. const cleanHex = hex.replace(/^0x/, "");
  634. if (cleanHex.length % 2 !== 0) {
  635. throw new Error("Invalid hex string length");
  636. }
  637. const bytes = new Uint8Array(cleanHex.length / 2);
  638. for (let i = 0; i < cleanHex.length; i += 2) {
  639. bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
  640. }
  641. return bytes;
  642. }
  643. export function uint8ArrayToHex(bytes) {
  644. return Array.from(bytes)
  645. .map((b) => b.toString(16).padStart(2, "0"))
  646. .join("");
  647. }
  648. // instanceof
  649. export class Class {
  650. constructor(..._args) { }
  651. }