hanzio 1.2.0 → 2.0.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 (145) hide show
  1. package/README.md +786 -239
  2. package/dist/api-wrapper/builders.d.ts +4 -3
  3. package/dist/api-wrapper/create-client.d.ts +1 -1
  4. package/dist/api-wrapper/describe.d.ts +16 -0
  5. package/dist/api-wrapper/endpoint.d.ts +18 -0
  6. package/dist/api-wrapper/errors.d.ts +39 -2
  7. package/dist/api-wrapper/index.d.ts +5 -0
  8. package/dist/api-wrapper/otel.d.ts +44 -0
  9. package/dist/api-wrapper/paginate.d.ts +12 -0
  10. package/dist/api-wrapper/redaction.d.ts +2 -0
  11. package/dist/api-wrapper/shared.d.ts +16 -2
  12. package/dist/api-wrapper/testing.d.ts +23 -0
  13. package/dist/api-wrapper/transport.d.ts +4 -5
  14. package/dist/api-wrapper/types.d.ts +103 -18
  15. package/dist/array/index.d.ts +50 -4
  16. package/dist/cache/createCache.d.ts +31 -0
  17. package/dist/cache/index.d.ts +19 -2
  18. package/dist/color/contrast.d.ts +40 -0
  19. package/dist/color/index.d.ts +2 -0
  20. package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
  21. package/dist/cool-console-log/index.d.ts +34 -5
  22. package/dist/crypto/encoding.d.ts +18 -0
  23. package/dist/crypto/hash.d.ts +33 -0
  24. package/dist/crypto/index.d.ts +4 -0
  25. package/dist/crypto/sealer.d.ts +25 -0
  26. package/dist/crypto/webhook.d.ts +20 -0
  27. package/dist/date/index.d.ts +46 -2
  28. package/dist/emitter/index.d.ts +21 -0
  29. package/dist/error/index.d.ts +12 -0
  30. package/dist/format/index.d.ts +102 -0
  31. package/dist/function/index.d.ts +60 -0
  32. package/dist/guard/index.d.ts +20 -0
  33. package/dist/i18n/index.d.ts +37 -0
  34. package/dist/index-0zvtcbzy.js +224 -0
  35. package/dist/index-181e6vfw.js +29 -0
  36. package/dist/index-8r2vdzjg.js +87 -0
  37. package/dist/index-hy7kbb99.js +157 -0
  38. package/dist/{src/secrets/infisical.js → index-j1ybktbr.js} +500 -538
  39. package/dist/index-k9yx6bne.js +89 -0
  40. package/dist/index-kj1phgrx.js +169 -0
  41. package/dist/index-mnkcfyx8.js +78 -0
  42. package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
  43. package/dist/index-rp5pqghz.js +157 -0
  44. package/dist/index-srt06xd2.js +15 -0
  45. package/dist/index-wjyb9bsd.js +263 -0
  46. package/dist/index.d.ts +7 -7
  47. package/dist/jwt/index.d.ts +50 -2
  48. package/dist/math/index.d.ts +2 -2
  49. package/dist/math/linearRegressionTrend.d.ts +14 -1
  50. package/dist/math/scale.d.ts +8 -0
  51. package/dist/object/index.d.ts +19 -0
  52. package/dist/p-queue/index.d.ts +1 -1
  53. package/dist/promise/index.d.ts +97 -2
  54. package/dist/sitemap/index.d.ts +48 -6
  55. package/dist/src/api-wrapper/index.js +60 -1130
  56. package/dist/src/api-wrapper/testing.js +20 -0
  57. package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
  58. package/dist/src/crypto/index.js +195 -0
  59. package/dist/src/format/index.js +211 -0
  60. package/dist/src/i18n/index.js +36 -0
  61. package/dist/src/index.js +739 -3145
  62. package/dist/src/jwt/index.js +138 -92
  63. package/dist/src/p-queue/index.js +9 -437
  64. package/dist/src/secrets/index.js +44 -1428
  65. package/dist/src/secrets/vite.js +6 -39
  66. package/dist/src/secrets/worker.js +10 -273
  67. package/dist/src/sitemap/index.js +183 -511
  68. package/dist/src/storage/index.js +119 -0
  69. package/dist/src/zod/index.js +340 -167
  70. package/dist/state/index.d.ts +21 -0
  71. package/dist/storage/index.d.ts +58 -0
  72. package/dist/string/index.d.ts +75 -2
  73. package/dist/typedswitch/index.d.ts +31 -24
  74. package/dist/types/index.d.ts +11 -0
  75. package/dist/url/index.d.ts +50 -1
  76. package/dist/zod/index.d.ts +32 -3
  77. package/dist/zod/introspect.d.ts +19 -0
  78. package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
  79. package/dist/zod/zodMinMaxFilter.d.ts +22 -3
  80. package/dist/zod/zodToTypeString.d.ts +8 -3
  81. package/package.json +78 -7
  82. package/src/api-wrapper/actions-impl.ts +11 -72
  83. package/src/api-wrapper/builders.ts +32 -25
  84. package/src/api-wrapper/create-client.ts +171 -41
  85. package/src/api-wrapper/describe.ts +157 -0
  86. package/src/api-wrapper/endpoint.ts +26 -0
  87. package/src/api-wrapper/error-reporting.ts +7 -1
  88. package/src/api-wrapper/errors.ts +107 -2
  89. package/src/api-wrapper/headers.ts +11 -6
  90. package/src/api-wrapper/index.ts +5 -0
  91. package/src/api-wrapper/otel.ts +139 -0
  92. package/src/api-wrapper/paginate.ts +28 -0
  93. package/src/api-wrapper/redaction.ts +42 -0
  94. package/src/api-wrapper/shared.ts +21 -2
  95. package/src/api-wrapper/testing.ts +48 -0
  96. package/src/api-wrapper/transport.ts +41 -43
  97. package/src/api-wrapper/types.ts +185 -18
  98. package/src/array/index.ts +195 -42
  99. package/src/cache/createCache.ts +157 -0
  100. package/src/cache/index.ts +132 -147
  101. package/src/color/contrast.ts +163 -0
  102. package/src/color/index.ts +22 -0
  103. package/src/{math → color}/metricsSeriesColors.ts +47 -49
  104. package/src/cool-console-log/index.ts +128 -41
  105. package/src/crypto/encoding.ts +83 -0
  106. package/src/crypto/hash.ts +170 -0
  107. package/src/crypto/index.ts +26 -0
  108. package/src/crypto/sealer.ts +168 -0
  109. package/src/crypto/webhook.ts +86 -0
  110. package/src/date/index.ts +109 -34
  111. package/src/emitter/index.ts +82 -0
  112. package/src/error/index.ts +63 -0
  113. package/src/format/index.ts +389 -0
  114. package/src/function/index.ts +201 -0
  115. package/src/guard/index.ts +80 -0
  116. package/src/i18n/index.ts +125 -0
  117. package/src/index.ts +11 -7
  118. package/src/jwt/index.ts +199 -125
  119. package/src/math/index.ts +5 -10
  120. package/src/math/linearRegressionTrend.ts +33 -8
  121. package/src/math/scale.ts +17 -0
  122. package/src/object/index.ts +99 -0
  123. package/src/p-queue/index.ts +2 -0
  124. package/src/promise/index.ts +304 -9
  125. package/src/sitemap/index.ts +293 -130
  126. package/src/state/index.ts +133 -81
  127. package/src/storage/index.ts +187 -0
  128. package/src/string/index.ts +261 -23
  129. package/src/typedswitch/index.ts +58 -38
  130. package/src/types/index.ts +14 -0
  131. package/src/url/index.ts +146 -1
  132. package/src/zod/index.ts +105 -9
  133. package/src/zod/introspect.ts +103 -0
  134. package/src/zod/jsonToZodSchemaMapper.ts +44 -21
  135. package/src/zod/zodMinMaxFilter.ts +37 -13
  136. package/src/zod/zodToTypeString.ts +184 -181
  137. package/dist/api-wrapper/index.js +0 -250
  138. package/dist/gzip/index.d.ts +0 -2
  139. package/dist/gzip/index.js +0 -16
  140. package/dist/index.js +0 -1823
  141. package/dist/jwt/index.js +0 -19
  142. package/dist/sitemap/index.js +0 -552
  143. package/dist/src/gzip/index.js +0 -16
  144. package/dist/src/secrets/loaders.js +0 -1529
  145. package/dist/zod/index.js +0 -210
package/src/zod/index.ts CHANGED
@@ -1,8 +1,21 @@
1
1
  import { z } from 'zod'
2
+ import { toArray } from '../array'
3
+ import { err, ok, type Result } from '../promise'
4
+ import type { JsonObject, JsonValue } from '../string'
5
+
6
+ export {
7
+ getEnumValues,
8
+ getObjectShape,
9
+ unwrapSchema,
10
+ zodDefaults
11
+ } from './introspect'
2
12
 
3
13
  export { jsonToZodSchemaMapper } from './jsonToZodSchemaMapper'
4
14
  export {
15
+ createMinMaxFilter,
16
+ type MinMaxFilter,
5
17
  toGteLteFilter,
18
+ toGtLtFilter,
6
19
  type ZodMinMaxFilter,
7
20
  zodMinMaxFilter
8
21
  } from './zodMinMaxFilter'
@@ -10,8 +23,96 @@ export { zodToExample, zodToTypeString } from './zodToTypeString'
10
23
 
11
24
  export type GenericId<TypeName extends string> = string & z.$brand<TypeName>
12
25
 
13
- export function createZId<TypeName extends string>(typeName: TypeName) {
14
- return z.string().min(1, `${typeName} ID is required`).brand<TypeName>()
26
+ export function createZId<TypeName extends string>(
27
+ typeName: TypeName,
28
+ { prefix = '' }: { prefix?: string } = {}
29
+ ) {
30
+ return z
31
+ .string()
32
+ .min(1, `${typeName} ID is required`)
33
+ .startsWith(prefix, `${typeName} ID must start with "${prefix}"`)
34
+ .brand<TypeName>()
35
+ }
36
+
37
+ /** Branded string id: `Id<'User'>` is not assignable to `Id<'Organization'>`. */
38
+ export type Id<TypeName extends string> = GenericId<TypeName>
39
+
40
+ /** Brands a string without checking it (fixtures, trusted sources). */
41
+ export function asId<TypeName extends string>(value: string): Id<TypeName> {
42
+ return value as Id<TypeName>
43
+ }
44
+
45
+ /** Validates a non-empty string and brands it; throws a `ZodError` otherwise. */
46
+ export function parseId<TypeName extends string>(
47
+ typeName: TypeName,
48
+ value: unknown
49
+ ): Id<TypeName> {
50
+ return createZId(typeName).parse(value) as Id<TypeName>
51
+ }
52
+
53
+ export function safeParseId<TypeName extends string>(
54
+ typeName: TypeName,
55
+ value: unknown
56
+ ): Result<Id<TypeName>, z.ZodError> {
57
+ // `.brand()` output stays deferred for a generic TypeName
58
+ return toResult(createZId(typeName).safeParse(value)) as Result<
59
+ Id<TypeName>,
60
+ z.ZodError
61
+ >
62
+ }
63
+
64
+ /**
65
+ * Binds the id helpers to a project's union of entity names once:
66
+ * `export const { zId, asId, parseId, safeParseId } = createIdHelpers<'User' | 'Organization'>()`
67
+ */
68
+ export function createIdHelpers<TypeNames extends string>() {
69
+ return {
70
+ zId: <T extends TypeNames>(typeName: T) => createZId(typeName),
71
+ asId: asId as <T extends TypeNames>(value: string) => Id<T>,
72
+ parseId: parseId as <T extends TypeNames>(
73
+ typeName: T,
74
+ value: unknown
75
+ ) => Id<T>,
76
+ safeParseId: safeParseId as <T extends TypeNames>(
77
+ typeName: T,
78
+ value: unknown
79
+ ) => Result<Id<T>, z.ZodError>
80
+ }
81
+ }
82
+
83
+ /** Any JSON value (no `undefined`, `NaN` or `Infinity`). */
84
+ export const zJsonValue: z.ZodType<JsonValue> = z.lazy(() =>
85
+ z.union([
86
+ z.string(),
87
+ z.number(),
88
+ z.boolean(),
89
+ z.null(),
90
+ z.array(zJsonValue),
91
+ zJsonObject
92
+ ])
93
+ )
94
+
95
+ export const zJsonObject: z.ZodType<JsonObject> = z.record(
96
+ z.string(),
97
+ zJsonValue
98
+ )
99
+
100
+ /** `JSON.parse` + `schema.safeParse` without throwing. */
101
+ export function parseJson<S extends z.ZodType>(
102
+ text: string,
103
+ schema: S
104
+ ): Result<z.output<S>, SyntaxError | z.ZodError> {
105
+ let value: unknown
106
+ try {
107
+ value = JSON.parse(text)
108
+ } catch (error) {
109
+ return { ok: false, data: null, error: error as SyntaxError }
110
+ }
111
+ return toResult(schema.safeParse(value))
112
+ }
113
+
114
+ function toResult<T>(result: z.ZodSafeParseResult<T>): Result<T, z.ZodError> {
115
+ return result.success ? ok(result.data) : err(result.error)
15
116
  }
16
117
 
17
118
  export function zSingleOrArray<T extends z.ZodType>(
@@ -20,10 +121,5 @@ export function zSingleOrArray<T extends z.ZodType>(
20
121
  return z.union([z.array(schema), schema])
21
122
  }
22
123
 
23
- export function normalizeSingleOrArray<T>(
24
- value: T | readonly T[] | null | undefined
25
- ): T[] {
26
- if (value == null) return []
27
- if (Array.isArray(value)) return [...(value as readonly T[])]
28
- return [value as T]
29
- }
124
+ /** @deprecated Use `toArray` from `hanzio`. */
125
+ export const normalizeSingleOrArray = toArray
@@ -0,0 +1,103 @@
1
+ import type { z } from 'zod'
2
+ import { type Def, defOf, type Schema, walk } from './zodToTypeString'
3
+
4
+ // Pipes are read from their input side, except preprocess (`in` is a transform)
5
+ function pipeInput(def: z.core.$ZodPipeDef): Schema {
6
+ return defOf(def.in).type === 'transform' ? def.out : def.in
7
+ }
8
+
9
+ function unwrapOnce(def: Def): Schema | undefined {
10
+ switch (def.type) {
11
+ case 'optional':
12
+ case 'nullable':
13
+ case 'default':
14
+ case 'prefault':
15
+ case 'nonoptional':
16
+ case 'catch':
17
+ case 'readonly':
18
+ return def.innerType
19
+ case 'lazy':
20
+ return def.getter()
21
+ case 'pipe':
22
+ return pipeInput(def)
23
+ default:
24
+ return undefined
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Strips optional/nullable/default/prefault/nonoptional/catch/readonly/lazy
30
+ * wrappers and pipes (input side) until the schema that carries the shape.
31
+ */
32
+ export function unwrapSchema(schema: Schema): Schema {
33
+ const inner = unwrapOnce(defOf(schema))
34
+ return inner ? unwrapSchema(inner) : schema
35
+ }
36
+
37
+ /** Values of a (possibly wrapped) `z.enum`, stringified; `undefined` for other schemas. */
38
+ export function getEnumValues(schema: Schema): string[] | undefined {
39
+ const def = defOf(unwrapSchema(schema))
40
+ return def.type === 'enum'
41
+ ? Object.values(def.entries).map(String)
42
+ : undefined
43
+ }
44
+
45
+ /** Shape of a (possibly wrapped) `z.object`; `undefined` for other schemas. */
46
+ export function getObjectShape(
47
+ schema: Schema
48
+ ): Readonly<Record<string, Schema>> | undefined {
49
+ const def = defOf(unwrapSchema(schema))
50
+ return def.type === 'object' ? def.shape : undefined
51
+ }
52
+
53
+ /**
54
+ * Initial form state for a schema: `.default()` values, `undefined` for
55
+ * optional, `null` for nullable, first enum/literal/union option, `''`, `0`,
56
+ * `false`, `[]`, and objects filled per key. Other types give `undefined`.
57
+ * The result is typed as the schema output but is NOT validated (`''` fails
58
+ * `.min(1)`, dates stay `undefined`).
59
+ */
60
+ export function zodDefaults<S extends Schema>(schema: S): z.output<S> {
61
+ return defaultFor(schema) as z.output<S>
62
+ }
63
+
64
+ function defaultFor(schema: Schema): unknown {
65
+ return walk(schema, undefined, (def): unknown => {
66
+ switch (def.type) {
67
+ case 'default':
68
+ case 'prefault':
69
+ return def.defaultValue
70
+ case 'optional':
71
+ return undefined
72
+ case 'nullable':
73
+ return null
74
+ case 'object': {
75
+ const result: Record<string, unknown> = {}
76
+ for (const [key, child] of Object.entries(def.shape)) {
77
+ result[key] = defaultFor(child)
78
+ }
79
+ return result
80
+ }
81
+ case 'array':
82
+ return []
83
+ case 'string':
84
+ return ''
85
+ case 'number':
86
+ return 0
87
+ case 'bigint':
88
+ return 0n
89
+ case 'boolean':
90
+ return false
91
+ case 'enum':
92
+ return Object.values(def.entries)[0]
93
+ case 'literal':
94
+ return def.values[0]
95
+ case 'union':
96
+ return def.options[0] && defaultFor(def.options[0])
97
+ default: {
98
+ const inner = unwrapOnce(def)
99
+ return inner && defaultFor(inner)
100
+ }
101
+ }
102
+ })
103
+ }
@@ -1,7 +1,18 @@
1
1
  import { z } from 'zod'
2
+ import { isRecord } from '../guard'
3
+
4
+ type ObjectConfig<V> =
5
+ V extends Record<string, unknown>
6
+ ? PropertyConfig<V, Record<string, any>>
7
+ : never
8
+
9
+ // Nested objects (and arrays of objects) take a config for the nested JSON value
10
+ type NestedConfig<V> = V extends readonly (infer E)[]
11
+ ? ObjectConfig<E>
12
+ : ObjectConfig<V>
2
13
 
3
14
  type PropertyConfig<T, U> = {
4
- [K in keyof T]?: (jsonObject: U) => T[K]
15
+ [K in keyof T]?: ((jsonObject: U) => T[K]) | NestedConfig<NonNullable<T[K]>>
5
16
  }
6
17
 
7
18
  export function jsonToZodSchemaMapper<
@@ -12,33 +23,45 @@ export function jsonToZodSchemaMapper<
12
23
  zodSchema: T,
13
24
  config: PropertyConfig<z.infer<T>, U>
14
25
  ): z.infer<T> {
15
- const result: Partial<z.infer<T>> = {}
16
-
17
- const shape = zodSchema instanceof z.ZodObject ? zodSchema.shape : {}
26
+ return zodSchema.parse(mapObject(jsonObject, zodSchema, config))
27
+ }
18
28
 
19
- for (const key of Object.keys(shape)) {
20
- const fieldConfig = config[key as keyof z.infer<T>]
21
- let value: any = jsonObject[key as keyof U]
29
+ function mapObject(
30
+ jsonObject: Record<string, any>,
31
+ schema: z.core.$ZodType,
32
+ config: Record<string, unknown>
33
+ ): Record<string, unknown> {
34
+ const shape = schema instanceof z.ZodObject ? schema.shape : {}
35
+ const result: Record<string, unknown> = {}
22
36
 
23
- if (fieldConfig) {
24
- value = fieldConfig(jsonObject)
37
+ for (const [key, fieldSchema] of Object.entries(shape)) {
38
+ const fieldConfig = config[key]
39
+ if (typeof fieldConfig === 'function') {
40
+ result[key] = fieldConfig(jsonObject)
41
+ continue
25
42
  }
26
43
 
27
- if (
28
- shape[key] instanceof z.ZodObject &&
29
- value &&
30
- typeof value === 'object'
31
- ) {
32
- // @ts-expect-error recursive mapper narrows key by shape iteration
33
- result[key] = jsonToZodSchemaMapper(
34
- value,
35
- shape[key] as z.ZodObject,
36
- (config[key as keyof z.infer<T>] || {}) as PropertyConfig<any, any>
44
+ const nested = (fieldConfig ?? {}) as Record<string, unknown>
45
+ const value = jsonObject[key]
46
+ const inner = unwrap(fieldSchema)
47
+ const element = inner instanceof z.ZodArray && unwrap(inner.element)
48
+
49
+ if (inner instanceof z.ZodObject && isRecord(value)) {
50
+ result[key] = mapObject(value, inner, nested)
51
+ } else if (element instanceof z.ZodObject && Array.isArray(value)) {
52
+ result[key] = value.map((item) =>
53
+ isRecord(item) ? mapObject(item, element, nested) : item
37
54
  )
38
55
  } else {
39
- result[key as keyof z.infer<T>] = value
56
+ result[key] = value
40
57
  }
41
58
  }
42
59
 
43
- return zodSchema.parse(result)
60
+ return result
61
+ }
62
+
63
+ function unwrap(schema: z.core.$ZodType): z.core.$ZodType {
64
+ return schema instanceof z.ZodOptional || schema instanceof z.ZodNullable
65
+ ? unwrap(schema.unwrap())
66
+ : schema
44
67
  }
@@ -1,19 +1,24 @@
1
1
  import { z } from 'zod'
2
2
 
3
- export const zodMinMaxFilter = z.object({
4
- min: z.number().optional(),
5
- max: z.number().optional()
6
- })
3
+ /**
4
+ * Creates a `{ min?, max? }` filter schema for any comparable value schema,
5
+ * e.g. `createMinMaxFilter(z.coerce.date())`.
6
+ */
7
+ export function createMinMaxFilter<T extends z.ZodType>(schema: T) {
8
+ return z.object({ min: schema.optional(), max: schema.optional() })
9
+ }
10
+
11
+ export const zodMinMaxFilter = createMinMaxFilter(z.number())
7
12
 
8
13
  export type ZodMinMaxFilter = z.infer<typeof zodMinMaxFilter>
9
14
 
10
- /**
11
- * Maps a min/max filter to a generic `gte`/`lte` range object (e.g. ORM-friendly).
12
- * Returns `undefined` if both bounds are undefined.
13
- */
14
- export function toGteLteFilter(
15
- filter: ZodMinMaxFilter | undefined
16
- ): { gte?: number; lte?: number } | undefined {
15
+ export type MinMaxFilter<T> = { min?: T; max?: T }
16
+
17
+ function toRange<T>(
18
+ filter: MinMaxFilter<T> | undefined,
19
+ lower: string,
20
+ upper: string
21
+ ): Record<string, T> | undefined {
17
22
  if (!filter) return undefined
18
23
 
19
24
  const hasMin = filter.min !== undefined
@@ -22,7 +27,26 @@ export function toGteLteFilter(
22
27
  if (!hasMin && !hasMax) return undefined
23
28
 
24
29
  return {
25
- ...(hasMin && { gte: filter.min }),
26
- ...(hasMax && { lte: filter.max })
30
+ ...(hasMin && { [lower]: filter.min as T }),
31
+ ...(hasMax && { [upper]: filter.max as T })
27
32
  }
28
33
  }
34
+
35
+ /**
36
+ * Maps a min/max filter to a generic `gte`/`lte` range object (e.g. ORM-friendly).
37
+ * Returns `undefined` if both bounds are undefined.
38
+ */
39
+ export function toGteLteFilter<T>(
40
+ filter: MinMaxFilter<T> | undefined
41
+ ): { gte?: T; lte?: T } | undefined {
42
+ return toRange(filter, 'gte', 'lte')
43
+ }
44
+
45
+ /**
46
+ * Exclusive variant of `toGteLteFilter`, mapping min/max to `gt`/`lt`.
47
+ */
48
+ export function toGtLtFilter<T>(
49
+ filter: MinMaxFilter<T> | undefined
50
+ ): { gt?: T; lt?: T } | undefined {
51
+ return toRange(filter, 'gt', 'lt')
52
+ }