hanzio 3.0.0 → 3.1.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.
Files changed (112) hide show
  1. package/README.md +114 -3
  2. package/dist/api-wrapper/connection-auth.d.ts +61 -0
  3. package/dist/cli/fields.d.ts +30 -0
  4. package/dist/cli/index.d.ts +122 -0
  5. package/dist/format/index.d.ts +91 -0
  6. package/dist/format/phone.d.ts +19 -0
  7. package/dist/http/index.d.ts +14 -0
  8. package/dist/i18n/catalog.d.ts +47 -0
  9. package/dist/i18n/check.d.ts +45 -0
  10. package/dist/i18n/cli.d.ts +2 -0
  11. package/dist/i18n/content-rules.d.ts +55 -0
  12. package/dist/i18n/i18next.d.ts +39 -0
  13. package/dist/i18n/index.d.ts +16 -1
  14. package/dist/i18n/locales.d.ts +62 -0
  15. package/dist/i18n/vite.d.ts +16 -0
  16. package/dist/i18n/vue.d.ts +93 -0
  17. package/dist/{index-kj1phgrx.js → index-29mmh343.js} +24 -1
  18. package/dist/{index-zbj9s3s3.js → index-2kz392vw.js} +1 -1
  19. package/dist/index-5n2e326q.js +409 -0
  20. package/dist/{index-hy7kbb99.js → index-5tzqfmnj.js} +1 -1
  21. package/dist/{index-0zvtcbzy.js → index-6f2w90pp.js} +69 -1
  22. package/dist/index-6ragggbs.js +473 -0
  23. package/dist/index-mq2qgc20.js +116 -0
  24. package/dist/index-najbyzpr.js +99 -0
  25. package/dist/index-ww09bm49.js +250 -0
  26. package/dist/iso/countries.d.ts +262 -0
  27. package/dist/iso/currencies.d.ts +165 -0
  28. package/dist/iso/index.d.ts +4 -0
  29. package/dist/iso/languages.d.ts +195 -0
  30. package/dist/iso/phone-language.d.ts +5 -0
  31. package/dist/promise/index.d.ts +31 -0
  32. package/dist/redis/client.d.ts +49 -0
  33. package/dist/redis/concurrency-limit.d.ts +63 -0
  34. package/dist/redis/index.d.ts +3 -0
  35. package/dist/redis/lua-script.d.ts +13 -0
  36. package/dist/secrets/infisical-identity-file.d.ts +12 -0
  37. package/dist/src/api-wrapper/connection-auth.js +82 -0
  38. package/dist/src/api-wrapper/index.js +1 -1
  39. package/dist/src/cli/index.js +10 -0
  40. package/dist/src/format/index.js +79 -2
  41. package/dist/src/format/phone.js +21 -0
  42. package/dist/src/http/index.js +69 -0
  43. package/dist/src/i18n/check.js +15 -0
  44. package/dist/src/i18n/cli.js +71 -0
  45. package/dist/src/i18n/i18next.js +48 -0
  46. package/dist/src/i18n/index.js +16 -34
  47. package/dist/src/i18n/vite.js +25 -0
  48. package/dist/src/i18n/vue.js +51 -0
  49. package/dist/src/index.js +14 -4
  50. package/dist/src/iso/index.js +655 -0
  51. package/dist/src/redis/index.js +577 -0
  52. package/dist/src/secrets/index.js +78 -35
  53. package/dist/src/sitemap/index.js +1 -1
  54. package/dist/src/storage/index.js +8 -117
  55. package/dist/src/testing/index.js +348 -0
  56. package/dist/src/zod/index.js +20 -253
  57. package/dist/string/index.d.ts +21 -0
  58. package/dist/testing/connection-auth-contract.d.ts +43 -0
  59. package/dist/testing/helpers.d.ts +26 -0
  60. package/dist/testing/index.d.ts +5 -0
  61. package/dist/testing/module-mocker.d.ts +30 -0
  62. package/dist/testing/prisma.d.ts +30 -0
  63. package/dist/testing/window.d.ts +18 -0
  64. package/dist/zod/index.d.ts +10 -0
  65. package/dist/zod/introspect.d.ts +2 -1
  66. package/package.json +96 -2
  67. package/src/api-wrapper/README.md +52 -0
  68. package/src/api-wrapper/connection-auth.ts +142 -0
  69. package/src/cli/README.md +220 -0
  70. package/src/cli/fields.ts +213 -0
  71. package/src/cli/index.ts +532 -0
  72. package/src/format/README.md +82 -2
  73. package/src/format/index.ts +148 -0
  74. package/src/format/phone.ts +43 -0
  75. package/src/http/README.md +25 -0
  76. package/src/http/index.ts +94 -0
  77. package/src/i18n/README.md +206 -3
  78. package/src/i18n/catalog.ts +139 -0
  79. package/src/i18n/check.ts +376 -0
  80. package/src/i18n/cli.ts +90 -0
  81. package/src/i18n/content-rules.ts +179 -0
  82. package/src/i18n/i18next.ts +89 -0
  83. package/src/i18n/index.ts +34 -0
  84. package/src/i18n/locales.ts +135 -0
  85. package/src/i18n/vite.ts +40 -0
  86. package/src/i18n/vue.ts +82 -0
  87. package/src/index.ts +1 -1
  88. package/src/iso/README.md +44 -0
  89. package/src/iso/countries.ts +272 -0
  90. package/src/iso/currencies.ts +172 -0
  91. package/src/iso/index.ts +8 -0
  92. package/src/iso/languages.ts +208 -0
  93. package/src/iso/phone-language.ts +46 -0
  94. package/src/promise/index.ts +100 -0
  95. package/src/redis/README.md +112 -0
  96. package/src/redis/client.ts +239 -0
  97. package/src/redis/concurrency-limit.ts +591 -0
  98. package/src/redis/index.ts +15 -0
  99. package/src/redis/lua-script.ts +43 -0
  100. package/src/secrets/README.md +5 -1
  101. package/src/secrets/infisical-identity-file.ts +69 -0
  102. package/src/secrets/infisical.ts +18 -3
  103. package/src/string/index.ts +59 -0
  104. package/src/testing/README.md +119 -0
  105. package/src/testing/connection-auth-contract.ts +303 -0
  106. package/src/testing/helpers.ts +41 -0
  107. package/src/testing/index.ts +12 -0
  108. package/src/testing/module-mocker.ts +50 -0
  109. package/src/testing/prisma.ts +78 -0
  110. package/src/testing/window.ts +119 -0
  111. package/src/zod/index.ts +20 -0
  112. package/src/zod/introspect.ts +1 -1
@@ -1,261 +1,24 @@
1
+ import {
2
+ zodToTypeString2,
3
+ zodToExample2,
4
+ unwrapSchema2,
5
+ getEnumValues2,
6
+ getObjectShape2,
7
+ zodDefaults2
8
+ } from "../../index-ww09bm49.js";
1
9
  import {
2
10
  toArray2
3
- } from "../../index-hy7kbb99.js";
11
+ } from "../../index-5tzqfmnj.js";
4
12
  import {
5
13
  ok2,
6
14
  err2
7
- } from "../../index-0zvtcbzy.js";
15
+ } from "../../index-6f2w90pp.js";
8
16
  import {
9
17
  isRecord2
10
18
  } from "../../index-8r2vdzjg.js";
11
19
 
12
20
  // src/zod/index.ts
13
21
  import { z as z3 } from "zod";
14
-
15
- // src/zod/zodToTypeString.ts
16
- var visiting = new Set;
17
- function walk(schema, onCycle, fn) {
18
- if (visiting.has(schema))
19
- return onCycle;
20
- visiting.add(schema);
21
- try {
22
- return fn(defOf(schema));
23
- } finally {
24
- visiting.delete(schema);
25
- }
26
- }
27
- function defOf(schema) {
28
- return schema._zod.def;
29
- }
30
- function pipeTarget(def) {
31
- return defOf(def.out).type === "transform" ? def.in : def.out;
32
- }
33
- function literal(value) {
34
- if (typeof value === "string")
35
- return JSON.stringify(value);
36
- return typeof value === "bigint" ? `${value}n` : String(value);
37
- }
38
- function wrap(type) {
39
- return /[|&]/.test(type) ? `(${type})` : type;
40
- }
41
- function zodToTypeString(schema, indent = 0) {
42
- return walk(schema, "unknown", (def) => {
43
- const pad = " ".repeat(indent);
44
- const innerPad = " ".repeat(indent + 1);
45
- const render = (inner) => zodToTypeString(inner, indent);
46
- switch (def.type) {
47
- case "object": {
48
- const entries = Object.entries(def.shape);
49
- if (entries.length === 0)
50
- return "{}";
51
- const fields = entries.map(([key, value]) => {
52
- const valueDef = defOf(value);
53
- const isOptional = valueDef.type === "optional";
54
- const typeStr = zodToTypeString(isOptional ? valueDef.innerType : value, indent + 1);
55
- return `${innerPad}${key}${isOptional ? "?" : ""}: ${typeStr}`;
56
- });
57
- return `{
58
- ${fields.join(`
59
- `)}
60
- ${pad}}`;
61
- }
62
- case "array":
63
- return `${wrap(render(def.element))}[]`;
64
- case "tuple": {
65
- const items = def.items.map(render);
66
- if (def.rest)
67
- items.push(`...${wrap(render(def.rest))}[]`);
68
- return `[${items.join(", ")}]`;
69
- }
70
- case "string":
71
- case "number":
72
- case "bigint":
73
- case "boolean":
74
- case "symbol":
75
- case "null":
76
- case "undefined":
77
- case "void":
78
- case "never":
79
- case "any":
80
- case "unknown":
81
- return def.type;
82
- case "date":
83
- return "Date";
84
- case "record":
85
- return `Record<${render(def.keyType)}, ${render(def.valueType)}>`;
86
- case "map":
87
- return `Map<${render(def.keyType)}, ${render(def.valueType)}>`;
88
- case "set":
89
- return `Set<${render(def.valueType)}>`;
90
- case "promise":
91
- return `Promise<${render(def.innerType)}>`;
92
- case "enum":
93
- return Object.values(def.entries).map(literal).join(" | ");
94
- case "literal":
95
- return def.values.map(literal).join(" | ");
96
- case "union":
97
- return def.options.map(render).join(" | ");
98
- case "intersection":
99
- return `${wrap(render(def.left))} & ${wrap(render(def.right))}`;
100
- case "optional":
101
- return `${render(def.innerType)} | undefined`;
102
- case "nullable":
103
- return `${render(def.innerType)} | null`;
104
- case "nonoptional": {
105
- const innerDef = defOf(def.innerType);
106
- return render(innerDef.type === "optional" ? innerDef.innerType : def.innerType);
107
- }
108
- case "default":
109
- case "prefault":
110
- case "catch":
111
- case "readonly":
112
- return render(def.innerType);
113
- case "lazy":
114
- return render(def.getter());
115
- case "pipe":
116
- return render(pipeTarget(def));
117
- default:
118
- return "unknown";
119
- }
120
- });
121
- }
122
- function zodToExample(schema) {
123
- return walk(schema, null, (def) => {
124
- switch (def.type) {
125
- case "object": {
126
- const result = {};
127
- for (const [key, value] of Object.entries(def.shape)) {
128
- if (defOf(value).type !== "optional") {
129
- result[key] = zodToExample(value);
130
- }
131
- }
132
- return result;
133
- }
134
- case "array":
135
- return [zodToExample(def.element)];
136
- case "set":
137
- return [zodToExample(def.valueType)];
138
- case "tuple":
139
- return def.items.map(zodToExample);
140
- case "string":
141
- return "";
142
- case "number":
143
- case "bigint":
144
- return 0;
145
- case "boolean":
146
- return false;
147
- case "date":
148
- return new Date(0).toISOString();
149
- case "enum":
150
- return Object.values(def.entries)[0];
151
- case "literal":
152
- return def.values[0];
153
- case "union":
154
- return zodToExample(def.options[0]);
155
- case "intersection": {
156
- const left = zodToExample(def.left);
157
- const right = zodToExample(def.right);
158
- return isRecord2(left) && isRecord2(right) ? { ...left, ...right } : left;
159
- }
160
- case "optional":
161
- case "nullable":
162
- case "nonoptional":
163
- case "default":
164
- case "prefault":
165
- case "catch":
166
- case "readonly":
167
- case "promise":
168
- return zodToExample(def.innerType);
169
- case "lazy":
170
- return zodToExample(def.getter());
171
- case "pipe":
172
- return zodToExample(pipeTarget(def));
173
- case "record":
174
- case "map":
175
- return {};
176
- default:
177
- return null;
178
- }
179
- });
180
- }
181
-
182
- // src/zod/introspect.ts
183
- function pipeInput(def) {
184
- return defOf(def.in).type === "transform" ? def.out : def.in;
185
- }
186
- function unwrapOnce(def) {
187
- switch (def.type) {
188
- case "optional":
189
- case "nullable":
190
- case "default":
191
- case "prefault":
192
- case "nonoptional":
193
- case "catch":
194
- case "readonly":
195
- return def.innerType;
196
- case "lazy":
197
- return def.getter();
198
- case "pipe":
199
- return pipeInput(def);
200
- default:
201
- return;
202
- }
203
- }
204
- function unwrapSchema(schema) {
205
- const inner = unwrapOnce(defOf(schema));
206
- return inner ? unwrapSchema(inner) : schema;
207
- }
208
- function getEnumValues(schema) {
209
- const def = defOf(unwrapSchema(schema));
210
- return def.type === "enum" ? Object.values(def.entries).map(String) : undefined;
211
- }
212
- function getObjectShape(schema) {
213
- const def = defOf(unwrapSchema(schema));
214
- return def.type === "object" ? def.shape : undefined;
215
- }
216
- function zodDefaults(schema) {
217
- return defaultFor(schema);
218
- }
219
- function defaultFor(schema) {
220
- return walk(schema, undefined, (def) => {
221
- switch (def.type) {
222
- case "default":
223
- case "prefault":
224
- return def.defaultValue;
225
- case "optional":
226
- return;
227
- case "nullable":
228
- return null;
229
- case "object": {
230
- const result = {};
231
- for (const [key, child] of Object.entries(def.shape)) {
232
- result[key] = defaultFor(child);
233
- }
234
- return result;
235
- }
236
- case "array":
237
- return [];
238
- case "string":
239
- return "";
240
- case "number":
241
- return 0;
242
- case "bigint":
243
- return 0n;
244
- case "boolean":
245
- return false;
246
- case "enum":
247
- return Object.values(def.entries)[0];
248
- case "literal":
249
- return def.values[0];
250
- case "union":
251
- return def.options[0] && defaultFor(def.options[0]);
252
- default: {
253
- const inner = unwrapOnce(def);
254
- return inner && defaultFor(inner);
255
- }
256
- }
257
- });
258
- }
259
22
  // src/zod/jsonToZodSchemaMapper.ts
260
23
  import { z } from "zod";
261
24
  function jsonToZodSchemaMapper(jsonObject, zodSchema, config) {
@@ -342,6 +105,8 @@ var zJsonValue = z3.lazy(() => z3.union([
342
105
  zJsonObject
343
106
  ]));
344
107
  var zJsonObject = z3.record(z3.string(), zJsonValue);
108
+ var E164_REGEX = /^\+[1-9]\d{6,14}$/;
109
+ var zOptionalE164 = z3.string().refine((value) => value === "" || E164_REGEX.test(value), "Phone number must be in E.164 format (e.g. +14155551234)").describe("E.164 phone number (e.g. +14155551234); empty string clears it").nullable().transform((value) => value || null);
345
110
  function parseJson(text, schema) {
346
111
  let value;
347
112
  try {
@@ -359,12 +124,13 @@ function zSingleOrArray(schema) {
359
124
  }
360
125
  var normalizeSingleOrArray = toArray2;
361
126
  export {
127
+ E164_REGEX,
362
128
  asId,
363
129
  createIdHelpers,
364
130
  createMinMaxFilter,
365
131
  createZId,
366
- getEnumValues,
367
- getObjectShape,
132
+ getEnumValues2 as getEnumValues,
133
+ getObjectShape2 as getObjectShape,
368
134
  jsonToZodSchemaMapper,
369
135
  normalizeSingleOrArray,
370
136
  parseId,
@@ -372,12 +138,13 @@ export {
372
138
  safeParseId,
373
139
  toGtLtFilter,
374
140
  toGteLteFilter,
375
- unwrapSchema,
141
+ unwrapSchema2 as unwrapSchema,
376
142
  zJsonObject,
377
143
  zJsonValue,
144
+ zOptionalE164,
378
145
  zSingleOrArray,
379
- zodDefaults,
146
+ zodDefaults2 as zodDefaults,
380
147
  zodMinMaxFilter,
381
- zodToExample,
382
- zodToTypeString
148
+ zodToExample2 as zodToExample,
149
+ zodToTypeString2 as zodToTypeString
383
150
  };
@@ -79,3 +79,24 @@ export declare function initials(name: string | null | undefined, options?: {
79
79
  }): string;
80
80
  /** `JSON.parse` that returns a `Result` instead of throwing. */
81
81
  export declare function safeJsonParse<T = unknown>(text: string): Result<T, SyntaxError>;
82
+ /** Escapes `& < > " '` for HTML or XML text and attribute values. */
83
+ export declare function escapeHtml(text: string): string;
84
+ /**
85
+ * Removes emoji and tidies the spacing they leave, so `'Hi there 👋, welcome'`
86
+ * becomes `'Hi there, welcome'`. Use it for text that is spoken (TTS drops
87
+ * emoji from the audio but not from the transcript). Text without emoji is
88
+ * returned unchanged.
89
+ */
90
+ export declare function stripEmoji(text: string): string;
91
+ /**
92
+ * Lowercased extension of a file name or path, without the dot. `undefined`
93
+ * when there is none; like `path.extname`, a leading dot (`.env`) is not one.
94
+ */
95
+ export declare function fileExtension(filename: string): string | undefined;
96
+ /**
97
+ * Normalizes a value the way a JSON round trip does: `undefined` properties
98
+ * are dropped, `Date`s become ISO strings and `NaN` becomes `null`. Top-level
99
+ * `undefined` becomes `null`. Use it before storing loosely typed data in a
100
+ * JSON column. Throws on `bigint` and circular references.
101
+ */
102
+ export declare function toJsonValue(value: unknown): JsonValue;
@@ -0,0 +1,43 @@
1
+ export type ConnectionAuthContractInput = {
2
+ reset: () => void;
3
+ create: (fetchImpl: typeof fetch, variant?: string) => {
4
+ read: () => Promise<unknown>;
5
+ login: (options?: {
6
+ signal?: AbortSignal;
7
+ }) => Promise<string>;
8
+ };
9
+ identityChanges: string[];
10
+ isLogin: (url: string) => boolean;
11
+ loginResponse: (token: string) => Response;
12
+ readResponse: () => Response;
13
+ tokenHeader: string;
14
+ };
15
+ /**
16
+ * Registers the races every client built on `ConnectionAuthCache`
17
+ * (`hanzio/api-wrapper/connection-auth`) must survive, run through the real
18
+ * client and its transport: shared refreshes, rotated credentials, late 401s,
19
+ * aborted waiters and failed fresh logins. Await it at the top level of a
20
+ * `bun:test` file.
21
+ *
22
+ * - `create(fetch, variant)` builds a client on the given fetch. `variant` is
23
+ * one of `identityChanges` (a credential change that must force a new login),
24
+ * `'settings'` (a non-auth setting that must reuse the login), `'connection'`
25
+ * (another connection) or `'secret'` (a rotated secret).
26
+ * - `read()` makes one authenticated request; on a 401 it calls `reject` and
27
+ * retries once. `login()` forces a fresh login and resolves its token.
28
+ * - `reset()` clears the cache between tests.
29
+ * - `tokenHeader` is where requests carry the token; `'authorization'` means
30
+ * `Bearer <token>`.
31
+ *
32
+ * @example
33
+ * await connectionAuthContract({
34
+ * reset: () => authCache.clear(),
35
+ * create: (fetch, variant) => new ShopApi(credentialsFor(variant), fetch),
36
+ * identityChanges: ['apiKey', 'secret'],
37
+ * isLogin: (url) => url.endsWith('/oauth/token'),
38
+ * loginResponse: (token) => Response.json({ access_token: token, expires_in: 3600 }),
39
+ * readResponse: () => Response.json({ items: [] }),
40
+ * tokenHeader: 'authorization'
41
+ * })
42
+ */
43
+ export declare function connectionAuthContract(input: ConnectionAuthContractInput): Promise<void>;
@@ -0,0 +1,26 @@
1
+ type LogMethod = (...args: unknown[]) => void;
2
+ export type NoopLogger = {
3
+ trace: LogMethod;
4
+ debug: LogMethod;
5
+ info: LogMethod;
6
+ warn: LogMethod;
7
+ error: LogMethod;
8
+ fatal: LogMethod;
9
+ bind: (...args: unknown[]) => NoopLogger;
10
+ child: (...args: unknown[]) => NoopLogger;
11
+ };
12
+ /** A logger that discards everything; `bind` and `child` return itself. */
13
+ export declare const noopLogger: NoopLogger;
14
+ /**
15
+ * Reads one argument from a mock's call history as `T`. Bun types
16
+ * `mock.calls` as `never[]` for loosely typed mocks.
17
+ *
18
+ * @example
19
+ * const input = mockCallArg<CreateUserInput>(createUser) // first call, first argument
20
+ */
21
+ export declare function mockCallArg<T>(fn: {
22
+ mock: {
23
+ calls: unknown[][];
24
+ };
25
+ }, callIndex?: number, argIndex?: number): T;
26
+ export {};
@@ -0,0 +1,5 @@
1
+ export { type ConnectionAuthContractInput, connectionAuthContract } from './connection-auth-contract';
2
+ export { mockCallArg, type NoopLogger, noopLogger } from './helpers';
3
+ export { ModuleMocker } from './module-mocker';
4
+ export { createPrismaMock, type DeepMockedPrisma, type PrismaMethodMock } from './prisma';
5
+ export { createTestStorage, installWindowShim } from './window';
@@ -0,0 +1,30 @@
1
+ type ModuleExports = Record<string, unknown>;
2
+ /**
3
+ * `mock.module` that can be undone. Bun replaces a mocked module for the whole
4
+ * process and `mock.restore()` does not bring it back
5
+ * (https://github.com/oven-sh/bun/issues/7823), so one suite's stub silently
6
+ * leaks into every later suite.
7
+ *
8
+ * Mocks spread the module's real exports first, so exports you don't override
9
+ * keep working, and `clear()` re-registers the originals. Call it in
10
+ * `afterEach` or `afterAll`.
11
+ *
12
+ * Bun resolves specifiers relative to the file that registers the mock, so
13
+ * pass absolute paths or package names. A `resolve` function maps your own
14
+ * shorthand (an `@/` alias, a source-root-relative path) to absolute paths.
15
+ *
16
+ * @example
17
+ * const moduleMocker = new ModuleMocker((specifier) =>
18
+ * specifier.startsWith('@/') ? path.resolve(srcDir, specifier.slice(2)) : specifier
19
+ * )
20
+ * await moduleMocker.mock('@/lib/posthog', () => ({ captureException: mock() }))
21
+ * afterAll(() => moduleMocker.clear())
22
+ */
23
+ export declare class ModuleMocker {
24
+ private readonly resolve;
25
+ private restorers;
26
+ constructor(resolve?: (specifier: string) => string);
27
+ mock(specifier: string, renderMocks: () => ModuleExports): Promise<void>;
28
+ clear(): void;
29
+ }
30
+ export {};
@@ -0,0 +1,30 @@
1
+ import type { Mock, mock } from 'bun:test';
2
+ export type PrismaMethodMock = Mock<(...args: unknown[]) => Promise<unknown>>;
3
+ /**
4
+ * A Prisma client where every model delegate and every method is a Bun mock,
5
+ * created on first access, like `jest-mock-extended`'s `mockDeep()`.
6
+ * `$`-prefixed client methods (`$transaction`, `$queryRaw`) are mocks too.
7
+ */
8
+ export type DeepMockedPrisma<Client> = {
9
+ [K in keyof Client]: Client[K] extends (...args: never[]) => unknown ? PrismaMethodMock : {
10
+ [Method in keyof Client[K]]: PrismaMethodMock;
11
+ };
12
+ } & {
13
+ /** Resets every mock created so far. Call it in `beforeEach`. */
14
+ $reset: () => void;
15
+ };
16
+ /**
17
+ * Builds a deep auto-mock of a Prisma client from Bun's `mock`. Register it
18
+ * with `mock.module` in place of the module that exports your client.
19
+ *
20
+ * @example
21
+ * import { mock } from 'bun:test'
22
+ * import type { prisma } from '../lib/prisma'
23
+ *
24
+ * export const prismaMock = createPrismaMock<typeof prisma>(mock)
25
+ * mock.module(prismaModulePath, () => ({ ...prismaExports, prisma: prismaMock }))
26
+ *
27
+ * prismaMock.user.findUnique.mockResolvedValue({ id: 'u1' })
28
+ * expect(prismaMock.user.findUnique).toHaveBeenCalledWith({ where: { id: 'u1' } })
29
+ */
30
+ export declare function createPrismaMock<Client>(createMock: typeof mock): DeepMockedPrisma<Client>;
@@ -0,0 +1,18 @@
1
+ /** A complete in-memory `Storage` (`localStorage` / `sessionStorage`). */
2
+ export declare function createTestStorage(): Storage;
3
+ /**
4
+ * Installs a minimal `globalThis.window` for unit tests of browser-facing code
5
+ * that only probes `window` (`location`, `localStorage`, event listeners), and
6
+ * returns a `reset` to call in `afterEach`.
7
+ *
8
+ * Tests may assign a partial `window`. The shim keeps its own shape and copies
9
+ * `location`, `localStorage` and the listener functions from the assigned
10
+ * object, because libraries such as axios and PostHog read those at import
11
+ * time. It installs no `document`: Vue and PrismJS treat one as a full DOM.
12
+ *
13
+ * @example
14
+ * // test-setup.ts, preloaded through bunfig.toml
15
+ * const resetWindow = installWindowShim()
16
+ * afterEach(resetWindow)
17
+ */
18
+ export declare function installWindowShim(href?: string): () => void;
@@ -30,6 +30,16 @@ export declare function createIdHelpers<TypeNames extends string>(): {
30
30
  /** Any JSON value (no `undefined`, `NaN` or `Infinity`). */
31
31
  export declare const zJsonValue: z.ZodType<JsonValue>;
32
32
  export declare const zJsonObject: z.ZodType<JsonObject>;
33
+ /** An E.164 phone number: `+`, then 7 to 15 digits with no leading zero. */
34
+ export declare const E164_REGEX: RegExp;
35
+ /**
36
+ * An optional E.164 phone number for forms: a cleared input submits `''`,
37
+ * which means "no number", so `''` and `null` both parse to `null`.
38
+ *
39
+ * Deliberately not `union([regex, literal('')])`: that serializes the empty
40
+ * literal as `enum: [""]`, which Gemini function declarations reject.
41
+ */
42
+ export declare const zOptionalE164: z.ZodPipe<z.ZodNullable<z.ZodString>, z.ZodTransform<string | null, string | null>>;
33
43
  /** `JSON.parse` + `schema.safeParse` without throwing. */
34
44
  export declare function parseJson<S extends z.ZodType>(text: string, schema: S): Result<z.output<S>, SyntaxError | z.ZodError>;
35
45
  export declare function zSingleOrArray<T extends z.ZodType>(schema: T): z.ZodUnion<readonly [z.ZodArray<T>, T]>;
@@ -1,5 +1,6 @@
1
1
  import type { z } from 'zod';
2
- import { type Schema } from './zodToTypeString';
2
+ import { type Def, type Schema } from './zodToTypeString';
3
+ export declare function unwrapOnce(def: Def): Schema | undefined;
3
4
  /**
4
5
  * Strips optional/nullable/default/prefault/nonoptional/catch/readonly/lazy
5
6
  * wrappers and pipes (input side) until the schema that carries the shape.