hanzio 1.1.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 (172) hide show
  1. package/README.md +832 -219
  2. package/dist/api-wrapper/builders.d.ts +15 -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 +40 -2
  7. package/dist/api-wrapper/headers.d.ts +2 -1
  8. package/dist/api-wrapper/index.d.ts +5 -0
  9. package/dist/api-wrapper/otel.d.ts +44 -0
  10. package/dist/api-wrapper/paginate.d.ts +12 -0
  11. package/dist/api-wrapper/redaction.d.ts +2 -0
  12. package/dist/api-wrapper/shared.d.ts +16 -2
  13. package/dist/api-wrapper/testing.d.ts +23 -0
  14. package/dist/api-wrapper/transport.d.ts +12 -10
  15. package/dist/api-wrapper/types.d.ts +104 -18
  16. package/dist/array/index.d.ts +50 -4
  17. package/dist/cache/createCache.d.ts +31 -0
  18. package/dist/cache/index.d.ts +45 -2
  19. package/dist/color/contrast.d.ts +40 -0
  20. package/dist/color/index.d.ts +2 -0
  21. package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
  22. package/dist/cool-console-log/index.d.ts +34 -5
  23. package/dist/crypto/encoding.d.ts +18 -0
  24. package/dist/crypto/hash.d.ts +33 -0
  25. package/dist/crypto/index.d.ts +4 -0
  26. package/dist/crypto/sealer.d.ts +25 -0
  27. package/dist/crypto/webhook.d.ts +20 -0
  28. package/dist/date/index.d.ts +46 -2
  29. package/dist/emitter/index.d.ts +21 -0
  30. package/dist/error/index.d.ts +12 -0
  31. package/dist/format/index.d.ts +102 -0
  32. package/dist/function/index.d.ts +60 -0
  33. package/dist/guard/index.d.ts +20 -0
  34. package/dist/i18n/index.d.ts +37 -0
  35. package/dist/index-0zvtcbzy.js +224 -0
  36. package/dist/index-181e6vfw.js +29 -0
  37. package/dist/index-8r2vdzjg.js +87 -0
  38. package/dist/index-hy7kbb99.js +157 -0
  39. package/dist/{src/secrets/loaders.js → index-j1ybktbr.js} +608 -405
  40. package/dist/index-k9yx6bne.js +89 -0
  41. package/dist/index-kj1phgrx.js +169 -0
  42. package/dist/index-mnkcfyx8.js +78 -0
  43. package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
  44. package/dist/index-rp5pqghz.js +157 -0
  45. package/dist/index-srt06xd2.js +15 -0
  46. package/dist/index-wjyb9bsd.js +263 -0
  47. package/dist/index.d.ts +7 -7
  48. package/dist/jwt/index.d.ts +50 -2
  49. package/dist/math/index.d.ts +2 -2
  50. package/dist/math/linearRegressionTrend.d.ts +14 -1
  51. package/dist/math/scale.d.ts +8 -0
  52. package/dist/object/index.d.ts +19 -0
  53. package/dist/p-queue/index.d.ts +1 -1
  54. package/dist/promise/index.d.ts +97 -2
  55. package/dist/secrets/core.d.ts +47 -0
  56. package/dist/secrets/deadline.d.ts +6 -0
  57. package/dist/secrets/environment.d.ts +9 -0
  58. package/dist/secrets/errors.d.ts +11 -0
  59. package/dist/secrets/index.d.ts +13 -25
  60. package/dist/secrets/infisical-cli.d.ts +6 -0
  61. package/dist/secrets/infisical-config.d.ts +17 -0
  62. package/dist/secrets/infisical-session.d.ts +4 -0
  63. package/dist/secrets/infisical.d.ts +8 -13
  64. package/dist/secrets/loaders.d.ts +5 -12
  65. package/dist/secrets/vite.d.ts +7 -3
  66. package/dist/secrets/worker-loader.d.ts +9 -0
  67. package/dist/secrets/worker.d.ts +3 -0
  68. package/dist/sitemap/index.d.ts +48 -6
  69. package/dist/src/api-wrapper/index.js +60 -1100
  70. package/dist/src/api-wrapper/testing.js +20 -0
  71. package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
  72. package/dist/src/crypto/index.js +195 -0
  73. package/dist/src/format/index.js +211 -0
  74. package/dist/src/i18n/index.js +36 -0
  75. package/dist/src/index.js +863 -2809
  76. package/dist/src/jwt/index.js +138 -92
  77. package/dist/src/p-queue/index.js +9 -437
  78. package/dist/src/secrets/index.js +261 -1377
  79. package/dist/src/secrets/vite.js +6 -17
  80. package/dist/src/secrets/worker.js +12 -0
  81. package/dist/src/sitemap/index.js +183 -511
  82. package/dist/src/storage/index.js +119 -0
  83. package/dist/src/zod/index.js +347 -174
  84. package/dist/state/index.d.ts +21 -0
  85. package/dist/storage/index.d.ts +58 -0
  86. package/dist/string/index.d.ts +75 -2
  87. package/dist/typedswitch/index.d.ts +37 -26
  88. package/dist/types/index.d.ts +11 -0
  89. package/dist/url/index.d.ts +50 -1
  90. package/dist/zod/index.d.ts +32 -3
  91. package/dist/zod/introspect.d.ts +19 -0
  92. package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
  93. package/dist/zod/zodMinMaxFilter.d.ts +22 -3
  94. package/dist/zod/zodToTypeString.d.ts +8 -3
  95. package/package.json +87 -7
  96. package/src/api-wrapper/actions-impl.ts +11 -72
  97. package/src/api-wrapper/builders.ts +84 -52
  98. package/src/api-wrapper/create-client.ts +246 -96
  99. package/src/api-wrapper/describe.ts +157 -0
  100. package/src/api-wrapper/endpoint.ts +26 -0
  101. package/src/api-wrapper/error-reporting.ts +7 -1
  102. package/src/api-wrapper/errors.ts +117 -2
  103. package/src/api-wrapper/headers.ts +19 -1
  104. package/src/api-wrapper/index.ts +5 -0
  105. package/src/api-wrapper/otel.ts +139 -0
  106. package/src/api-wrapper/paginate.ts +28 -0
  107. package/src/api-wrapper/redaction.ts +43 -0
  108. package/src/api-wrapper/shared.ts +21 -2
  109. package/src/api-wrapper/testing.ts +48 -0
  110. package/src/api-wrapper/transport.ts +68 -74
  111. package/src/api-wrapper/types.ts +186 -18
  112. package/src/array/index.ts +195 -42
  113. package/src/cache/createCache.ts +157 -0
  114. package/src/cache/index.ts +235 -83
  115. package/src/color/contrast.ts +163 -0
  116. package/src/color/index.ts +22 -0
  117. package/src/{math → color}/metricsSeriesColors.ts +47 -49
  118. package/src/cool-console-log/index.ts +128 -41
  119. package/src/crypto/encoding.ts +83 -0
  120. package/src/crypto/hash.ts +170 -0
  121. package/src/crypto/index.ts +26 -0
  122. package/src/crypto/sealer.ts +168 -0
  123. package/src/crypto/webhook.ts +86 -0
  124. package/src/date/index.ts +109 -34
  125. package/src/emitter/index.ts +82 -0
  126. package/src/error/index.ts +63 -0
  127. package/src/format/index.ts +389 -0
  128. package/src/function/index.ts +201 -0
  129. package/src/guard/index.ts +80 -0
  130. package/src/i18n/index.ts +125 -0
  131. package/src/index.ts +11 -7
  132. package/src/jwt/index.ts +199 -125
  133. package/src/math/index.ts +5 -10
  134. package/src/math/linearRegressionTrend.ts +33 -8
  135. package/src/math/scale.ts +17 -0
  136. package/src/object/index.ts +99 -0
  137. package/src/p-queue/index.ts +2 -0
  138. package/src/promise/index.ts +304 -9
  139. package/src/secrets/core.ts +348 -0
  140. package/src/secrets/deadline.ts +69 -0
  141. package/src/secrets/environment.ts +69 -0
  142. package/src/secrets/errors.ts +29 -0
  143. package/src/secrets/index.ts +79 -210
  144. package/src/secrets/infisical-cli.ts +99 -0
  145. package/src/secrets/infisical-config.ts +70 -0
  146. package/src/secrets/infisical-session.ts +42 -0
  147. package/src/secrets/infisical.ts +214 -66
  148. package/src/secrets/loaders.ts +24 -48
  149. package/src/secrets/vite.ts +44 -15
  150. package/src/secrets/worker-loader.ts +56 -0
  151. package/src/secrets/worker.ts +4 -0
  152. package/src/sitemap/index.ts +293 -130
  153. package/src/state/index.ts +133 -81
  154. package/src/storage/index.ts +187 -0
  155. package/src/string/index.ts +261 -23
  156. package/src/typedswitch/index.ts +134 -103
  157. package/src/types/index.ts +14 -0
  158. package/src/url/index.ts +146 -1
  159. package/src/zod/index.ts +105 -9
  160. package/src/zod/introspect.ts +103 -0
  161. package/src/zod/jsonToZodSchemaMapper.ts +44 -21
  162. package/src/zod/zodMinMaxFilter.ts +37 -13
  163. package/src/zod/zodToTypeString.ts +184 -181
  164. package/dist/api-wrapper/index.js +0 -250
  165. package/dist/gzip/index.d.ts +0 -2
  166. package/dist/gzip/index.js +0 -16
  167. package/dist/index.js +0 -1823
  168. package/dist/jwt/index.js +0 -19
  169. package/dist/sitemap/index.js +0 -552
  170. package/dist/src/gzip/index.js +0 -16
  171. package/dist/src/secrets/infisical.js +0 -1194
  172. package/dist/zod/index.js +0 -210
@@ -1,222 +1,225 @@
1
1
  import type { z } from 'zod'
2
+ import { isRecord } from '../guard'
2
3
 
3
- // Helper to safely get a property from an unknown def object
4
- function getDefProp<T>(def: unknown, prop: string): T | undefined {
5
- return (def as Record<string, unknown>)?.[prop] as T | undefined
4
+ export type Schema = z.core.$ZodType
5
+ export type Def = z.core.$ZodTypes['_zod']['def']
6
+
7
+ // Schemas on the current render path, so recursive (lazy/getter) schemas terminate
8
+ const visiting = new Set<Schema>()
9
+
10
+ export function walk<T>(schema: Schema, onCycle: T, fn: (def: Def) => T): T {
11
+ if (visiting.has(schema)) return onCycle
12
+ visiting.add(schema)
13
+ try {
14
+ return fn(defOf(schema))
15
+ } finally {
16
+ visiting.delete(schema)
17
+ }
18
+ }
19
+
20
+ export function defOf(schema: Schema): Def {
21
+ return (schema as z.core.$ZodTypes)._zod.def
22
+ }
23
+
24
+ // Pipes are described by their output, except transforms, whose output can't be inspected
25
+ function pipeTarget(def: z.core.$ZodPipeDef): Schema {
26
+ return defOf(def.out).type === 'transform' ? def.in : def.out
27
+ }
28
+
29
+ function literal(value: unknown): string {
30
+ if (typeof value === 'string') return JSON.stringify(value)
31
+ return typeof value === 'bigint' ? `${value}n` : String(value)
32
+ }
33
+
34
+ // Parenthesize unions/intersections where they'd otherwise bind wrongly (e.g. `(a | b)[]`)
35
+ function wrap(type: string): string {
36
+ return /[|&]/.test(type) ? `(${type})` : type
6
37
  }
7
38
 
8
39
  /**
9
40
  * Convert a Zod schema to a human-readable TypeScript-like interface string
10
41
  */
11
- export function zodToTypeString(schema: z.ZodType, indent = 0): string {
12
- const pad = ' '.repeat(indent)
13
- const innerPad = ' '.repeat(indent + 1)
14
-
15
- // Get the schema definition - try both Zod 4 and Zod 3 patterns
16
- const def =
17
- (schema as unknown as { _zod?: { def: unknown } })._zod?.def ??
18
- (schema as unknown as { _def: unknown })._def
19
-
20
- if (!def) return 'unknown'
21
-
22
- // Zod 4 uses 'type' property with lowercase values (e.g., 'object', 'string')
23
- // Zod 3 uses 'typeName' with 'Zod' prefix (e.g., 'ZodObject', 'ZodString')
24
- const schemaType =
25
- getDefProp<string>(def, 'type') ?? getDefProp<string>(def, 'typeName')
26
-
27
- switch (schemaType) {
28
- // Zod 4 lowercase types
29
- case 'object': {
30
- const shape = getDefProp<Record<string, z.ZodType>>(def, 'shape')
31
- if (!shape || Object.keys(shape).length === 0) return '{}'
32
-
33
- const fields = Object.entries(shape).map(([key, value]) => {
34
- // Zod 4: check value.type === 'optional', Zod 3: check _def.typeName
35
- const valueType = getDefProp<string>(
36
- (value as unknown as { _zod?: { def: unknown } })._zod?.def ??
37
- (value as unknown as { _def?: unknown })._def,
38
- 'type'
39
- )
40
- const isOptional = valueType === 'optional'
41
- const innerSchema = isOptional
42
- ? (getDefProp<z.ZodType>(
43
- (value as unknown as { _zod?: { def: unknown } })._zod?.def ??
44
- (value as unknown as { _def?: unknown })._def,
45
- 'innerType'
46
- ) ?? value)
47
- : value
48
- const typeStr = zodToTypeString(innerSchema, indent + 1)
49
- return `${innerPad}${key}${isOptional ? '?' : ''}: ${typeStr}`
50
- })
51
-
52
- return `{\n${fields.join('\n')}\n${pad}}`
53
- }
42
+ export function zodToTypeString(schema: Schema, indent = 0): string {
43
+ return walk(schema, 'unknown', (def) => {
44
+ const pad = ' '.repeat(indent)
45
+ const innerPad = ' '.repeat(indent + 1)
46
+ const render = (inner: Schema) => zodToTypeString(inner, indent)
47
+
48
+ switch (def.type) {
49
+ case 'object': {
50
+ const entries = Object.entries(def.shape)
51
+ if (entries.length === 0) return '{}'
52
+
53
+ const fields = entries.map(([key, value]) => {
54
+ const valueDef = defOf(value)
55
+ const isOptional = valueDef.type === 'optional'
56
+ const typeStr = zodToTypeString(
57
+ isOptional ? valueDef.innerType : value,
58
+ indent + 1
59
+ )
60
+ return `${innerPad}${key}${isOptional ? '?' : ''}: ${typeStr}`
61
+ })
62
+
63
+ return `{\n${fields.join('\n')}\n${pad}}`
64
+ }
54
65
 
55
- case 'array': {
56
- // Zod 4 uses 'element', Zod 3 uses 'type'
57
- const itemType =
58
- getDefProp<z.ZodType>(def, 'element') ??
59
- getDefProp<z.ZodType>(def, 'type')
60
- return itemType ? `${zodToTypeString(itemType, indent)}[]` : 'unknown[]'
61
- }
66
+ case 'array':
67
+ return `${wrap(render(def.element))}[]`
62
68
 
63
- case 'string':
64
- return 'string'
69
+ case 'tuple': {
70
+ const items = def.items.map(render)
71
+ if (def.rest) items.push(`...${wrap(render(def.rest))}[]`)
72
+ return `[${items.join(', ')}]`
73
+ }
65
74
 
66
- case 'number':
67
- return 'number'
75
+ case 'string':
76
+ case 'number':
77
+ case 'bigint':
78
+ case 'boolean':
79
+ case 'symbol':
80
+ case 'null':
81
+ case 'undefined':
82
+ case 'void':
83
+ case 'never':
84
+ case 'any':
85
+ case 'unknown':
86
+ return def.type
68
87
 
69
- case 'boolean':
70
- return 'boolean'
88
+ case 'date':
89
+ return 'Date'
71
90
 
72
- case 'date':
73
- return 'Date'
91
+ case 'record':
92
+ return `Record<${render(def.keyType)}, ${render(def.valueType)}>`
74
93
 
75
- case 'null':
76
- return 'null'
94
+ case 'map':
95
+ return `Map<${render(def.keyType)}, ${render(def.valueType)}>`
77
96
 
78
- case 'undefined':
79
- return 'undefined'
97
+ case 'set':
98
+ return `Set<${render(def.valueType)}>`
80
99
 
81
- case 'any':
82
- return 'any'
100
+ case 'promise':
101
+ return `Promise<${render(def.innerType)}>`
83
102
 
84
- case 'unknown':
85
- return 'unknown'
103
+ case 'enum':
104
+ return Object.values(def.entries).map(literal).join(' | ')
86
105
 
87
- case 'record': {
88
- const valueType = getDefProp<z.ZodType>(def, 'valueType')
89
- return valueType
90
- ? `Record<string, ${zodToTypeString(valueType, indent)}>`
91
- : 'Record<string, unknown>'
92
- }
106
+ case 'literal':
107
+ return def.values.map(literal).join(' | ')
93
108
 
94
- case 'enum': {
95
- const values = getDefProp<string[]>(def, 'values')
96
- return values ? values.map((v) => `"${v}"`).join(' | ') : 'string'
97
- }
109
+ case 'union':
110
+ return def.options.map(render).join(' | ')
98
111
 
99
- case 'literal': {
100
- const value = getDefProp<unknown>(def, 'value')
101
- return typeof value === 'string' ? `"${value}"` : String(value)
102
- }
112
+ case 'intersection':
113
+ return `${wrap(render(def.left))} & ${wrap(render(def.right))}`
103
114
 
104
- case 'union': {
105
- const options = getDefProp<z.ZodType[]>(def, 'options')
106
- return options
107
- ? options.map((o) => zodToTypeString(o, indent)).join(' | ')
108
- : 'unknown'
109
- }
115
+ case 'optional':
116
+ return `${render(def.innerType)} | undefined`
110
117
 
111
- case 'optional': {
112
- const inner = getDefProp<z.ZodType>(def, 'innerType')
113
- return inner ? `${zodToTypeString(inner, indent)} | undefined` : 'unknown'
114
- }
118
+ case 'nullable':
119
+ return `${render(def.innerType)} | null`
115
120
 
116
- case 'nullable': {
117
- const inner = getDefProp<z.ZodType>(def, 'innerType')
118
- return inner ? `${zodToTypeString(inner, indent)} | null` : 'unknown'
119
- }
121
+ case 'nonoptional': {
122
+ const innerDef = defOf(def.innerType)
123
+ return render(
124
+ innerDef.type === 'optional' ? innerDef.innerType : def.innerType
125
+ )
126
+ }
120
127
 
121
- case 'default': {
122
- const inner = getDefProp<z.ZodType>(def, 'innerType')
123
- return inner ? zodToTypeString(inner, indent) : 'unknown'
124
- }
128
+ case 'default':
129
+ case 'prefault':
130
+ case 'catch':
131
+ case 'readonly':
132
+ return render(def.innerType)
125
133
 
126
- // Zod 3 compatibility (with 'Zod' prefix)
127
- case 'ZodObject':
128
- case 'ZodArray':
129
- case 'ZodString':
130
- case 'ZodNumber':
131
- case 'ZodBoolean':
132
- case 'ZodDate':
133
- case 'ZodNull':
134
- case 'ZodUndefined':
135
- case 'ZodAny':
136
- case 'ZodUnknown':
137
- case 'ZodRecord':
138
- case 'ZodEnum':
139
- case 'ZodLiteral':
140
- case 'ZodUnion':
141
- case 'ZodOptional':
142
- case 'ZodNullable':
143
- case 'ZodDefault':
144
- // For Zod 3, recurse with the lowercase version
145
- return zodToTypeString(schema, indent)
146
-
147
- default:
148
- return 'unknown'
149
- }
134
+ case 'lazy':
135
+ return render(def.getter())
136
+
137
+ case 'pipe':
138
+ return render(pipeTarget(def))
139
+
140
+ default:
141
+ return 'unknown'
142
+ }
143
+ })
150
144
  }
151
145
 
152
146
  /**
153
- * Generate example JSON from a Zod schema
147
+ * Generate example JSON from a Zod schema. Values are JSON-safe: dates become ISO
148
+ * strings, bigints numbers, sets arrays and maps/records empty objects.
154
149
  */
155
- export function zodToExample(schema: z.ZodType): unknown {
156
- const def =
157
- (schema as unknown as { _zod?: { def: unknown } })._zod?.def ??
158
- (schema as unknown as { _def: unknown })._def
159
- if (!def) return null
160
-
161
- // Zod 4 uses 'type', Zod 3 uses 'typeName'
162
- const schemaType =
163
- getDefProp<string>(def, 'type') ?? getDefProp<string>(def, 'typeName')
164
-
165
- switch (schemaType) {
166
- case 'object': {
167
- const shape = getDefProp<Record<string, z.ZodType>>(def, 'shape')
168
- if (!shape) return {}
169
- const result: Record<string, unknown> = {}
170
- for (const [key, value] of Object.entries(shape)) {
171
- const valueType = getDefProp<string>(
172
- (value as unknown as { _zod?: { def: unknown } })._zod?.def ??
173
- (value as unknown as { _def?: unknown })._def,
174
- 'type'
175
- )
176
- const isOptional = valueType === 'optional'
177
- if (!isOptional) {
178
- result[key] = zodToExample(value)
150
+ export function zodToExample(schema: Schema): unknown {
151
+ return walk(schema, null, (def): unknown => {
152
+ switch (def.type) {
153
+ case 'object': {
154
+ const result: Record<string, unknown> = {}
155
+ for (const [key, value] of Object.entries(def.shape)) {
156
+ if (defOf(value).type !== 'optional') {
157
+ result[key] = zodToExample(value)
158
+ }
179
159
  }
160
+ return result
180
161
  }
181
- return result
182
- }
183
162
 
184
- case 'array': {
185
- // Zod 4 uses 'element', Zod 3 uses 'type'
186
- const itemType =
187
- getDefProp<z.ZodType>(def, 'element') ??
188
- getDefProp<z.ZodType>(def, 'type')
189
163
  // Always return array with one item for better example
190
- return itemType ? [zodToExample(itemType)] : []
191
- }
164
+ case 'array':
165
+ return [zodToExample(def.element)]
192
166
 
193
- case 'string':
194
- return ''
167
+ case 'set':
168
+ return [zodToExample(def.valueType)]
195
169
 
196
- case 'number':
197
- return 0
170
+ case 'tuple':
171
+ return def.items.map(zodToExample)
198
172
 
199
- case 'boolean':
200
- return false
173
+ case 'string':
174
+ return ''
201
175
 
202
- case 'enum': {
203
- const values = getDefProp<string[]>(def, 'values')
204
- return values?.[0] ?? ''
205
- }
176
+ case 'number':
177
+ case 'bigint':
178
+ return 0
206
179
 
207
- case 'literal':
208
- return getDefProp<unknown>(def, 'value')
180
+ case 'boolean':
181
+ return false
209
182
 
210
- case 'optional':
211
- case 'default': {
212
- const inner = getDefProp<z.ZodType>(def, 'innerType')
213
- return inner ? zodToExample(inner) : null
214
- }
183
+ case 'date':
184
+ return new Date(0).toISOString()
215
185
 
216
- case 'record':
217
- return {}
186
+ case 'enum':
187
+ return Object.values(def.entries)[0]
218
188
 
219
- default:
220
- return null
221
- }
189
+ case 'literal':
190
+ return def.values[0]
191
+
192
+ case 'union':
193
+ return zodToExample(def.options[0] as Schema)
194
+
195
+ case 'intersection': {
196
+ const left = zodToExample(def.left)
197
+ const right = zodToExample(def.right)
198
+ return isRecord(left) && isRecord(right) ? { ...left, ...right } : left
199
+ }
200
+
201
+ case 'optional':
202
+ case 'nullable':
203
+ case 'nonoptional':
204
+ case 'default':
205
+ case 'prefault':
206
+ case 'catch':
207
+ case 'readonly':
208
+ case 'promise':
209
+ return zodToExample(def.innerType)
210
+
211
+ case 'lazy':
212
+ return zodToExample(def.getter())
213
+
214
+ case 'pipe':
215
+ return zodToExample(pipeTarget(def))
216
+
217
+ case 'record':
218
+ case 'map':
219
+ return {}
220
+
221
+ default:
222
+ return null
223
+ }
224
+ })
222
225
  }
@@ -1,250 +0,0 @@
1
- // src/api-wrapper/index.ts
2
- import { ZodError } from "zod";
3
-
4
- class ResponseValidationError extends Error {
5
- zodError;
6
- rawResponse;
7
- validationIssues;
8
- constructor(zodError, rawResponse) {
9
- const issues = zodError.issues.map((issue) => `${issue.path.join(".") || "<root>"}: ${issue.message}`).join("; ");
10
- super(`Response validation failed: ${issues}`);
11
- this.name = "ResponseValidationError";
12
- this.zodError = zodError;
13
- this.rawResponse = rawResponse;
14
- this.validationIssues = issues;
15
- }
16
- }
17
-
18
- class HttpResponseError extends Error {
19
- status;
20
- body;
21
- constructor(status, body) {
22
- super(`HTTP error: ${status} - ${body}`);
23
- this.name = "HttpResponseError";
24
- this.status = status;
25
- this.body = body;
26
- }
27
- }
28
- function createApiClient(apiConfig) {
29
- const request = async (endpointKey, input = {}) => {
30
- const endpoint = apiConfig.endpoints[endpointKey];
31
- if (!endpoint) {
32
- throw new Error(`Unknown API endpoint: ${String(endpointKey)}`);
33
- }
34
- const { finalUrl, fullUrl } = buildUrl(endpoint, input.reqParams, input.reqQuery, apiConfig.baseApiUrls, apiConfig.defaultBaseApiUrl, input.url);
35
- const endpointName = apiConfig.name ? `${apiConfig.name}.${String(endpointKey)}` : String(endpointKey);
36
- const startedAt = Date.now();
37
- let retryCount = 0;
38
- let httpStatus = 0;
39
- apiConfig.onRequestStart?.({ endpoint: endpointName, url: fullUrl });
40
- try {
41
- const config = {
42
- method: endpoint.method,
43
- url: finalUrl,
44
- queryParams: buildQueryParams(endpoint, input.reqQuery),
45
- body: buildRequestBody(endpoint, input.reqBody),
46
- headers: buildHeaders(endpoint, input.reqHeaders, apiConfig.defaultHeaders ?? {}),
47
- doNotEncodeQueryParams: endpoint.doNotEncodeQueryParams,
48
- timeoutMs: apiConfig.timeoutMs,
49
- fetchFn: apiConfig.fetch ?? fetch
50
- };
51
- const result = await makeRequestWithRetry(config, apiConfig.retries ?? 3, apiConfig.retryDelayMs ?? 300, (response) => validateAndTransformResponse(endpoint, response), apiConfig.shouldRetry ?? defaultShouldRetry, apiConfig.logger);
52
- retryCount = result.retryCount;
53
- httpStatus = result.httpStatus;
54
- const responseTimeMs = Date.now() - startedAt;
55
- const responseSizeMb = calculateSizeInMb(result.validatedData);
56
- const requestSizeMb = calculateSizeInMb({
57
- method: config.method,
58
- url: fullUrl,
59
- body: config.body,
60
- headers: config.headers
61
- });
62
- apiConfig.onRequestEnd?.({
63
- endpoint: endpointName,
64
- url: fullUrl,
65
- status: httpStatus,
66
- retryCount,
67
- responseTimeMs
68
- });
69
- return {
70
- data: result.validatedData,
71
- requestSizeMb,
72
- responseSizeMb,
73
- requestSizeTotalMb: requestSizeMb + responseSizeMb,
74
- responseTimeMs,
75
- httpStatus,
76
- retryCount
77
- };
78
- } catch (error) {
79
- apiConfig.logger?.error?.(error);
80
- throw error;
81
- }
82
- };
83
- return { request };
84
- }
85
- var createApiWrapper = createApiClient;
86
- async function validateResponse(schema, data) {
87
- try {
88
- return await schema.parseAsync(data);
89
- } catch (error) {
90
- if (error instanceof ZodError) {
91
- throw new ResponseValidationError(error, data);
92
- }
93
- throw error;
94
- }
95
- }
96
- async function httpRequest(config) {
97
- const queryString = config.doNotEncodeQueryParams ? Object.entries(config.queryParams).map(([key, value]) => `${key}=${value}`).join("&") : new URLSearchParams(config.queryParams).toString();
98
- const fullUrl = queryString ? `${config.url}?${queryString}` : config.url;
99
- const abortController = new AbortController;
100
- const timeout = config.timeoutMs === undefined ? undefined : setTimeout(() => abortController.abort(), config.timeoutMs);
101
- try {
102
- return await config.fetchFn(fullUrl, {
103
- method: config.method,
104
- headers: config.headers,
105
- body: buildFetchBody(config.body, config.headers),
106
- signal: abortController.signal
107
- });
108
- } finally {
109
- if (timeout)
110
- clearTimeout(timeout);
111
- }
112
- }
113
- async function makeRequestWithRetry(config, maxRetries, retryDelayMs, validateFn, shouldRetry, logger) {
114
- let retryCount = 0;
115
- while (true) {
116
- try {
117
- logger?.debug?.("API request", config);
118
- const response = await httpRequest(config);
119
- if (response.status >= 400) {
120
- if (retryCount < maxRetries && shouldRetry({ response, retryCount, maxRetries })) {
121
- retryCount++;
122
- await delay(retryDelayMs);
123
- continue;
124
- }
125
- throw new HttpResponseError(response.status, await response.text());
126
- }
127
- return {
128
- validatedData: await validateFn(response),
129
- retryCount,
130
- httpStatus: response.status
131
- };
132
- } catch (error) {
133
- if (error instanceof HttpResponseError)
134
- throw error;
135
- if (retryCount >= maxRetries || !shouldRetry({ error, retryCount, maxRetries })) {
136
- throw error;
137
- }
138
- retryCount++;
139
- await delay(retryDelayMs);
140
- }
141
- }
142
- }
143
- async function validateAndTransformResponse(endpoint, response) {
144
- const responseHeaders = getHeadersAsObject(response.headers);
145
- const contentType = responseHeaders["content-type"] ?? "";
146
- const responseData = contentType.includes("application/json") ? await response.json() : await response.text();
147
- const transformedData = endpoint.resFormatter ? endpoint.resFormatter(responseData, responseHeaders) : responseData;
148
- return validateResponse(endpoint.resSchema, transformedData);
149
- }
150
- function buildUrl(endpoint, params, reqQuery, baseApiUrls, defaultBaseApiUrl, url) {
151
- if (url) {
152
- const finalUrl2 = replacePathParams(url, params);
153
- const queryString2 = new URLSearchParams(buildQueryParams(endpoint, reqQuery)).toString();
154
- return {
155
- finalUrl: finalUrl2,
156
- fullUrl: queryString2 ? `${finalUrl2}?${queryString2}` : finalUrl2
157
- };
158
- }
159
- const defaultBaseUrl = defaultBaseApiUrl ?? Object.keys(baseApiUrls)[0];
160
- if (!defaultBaseUrl) {
161
- throw new Error("At least one base API URL is required");
162
- }
163
- const baseUrlKey = endpoint.baseApiUrl ?? defaultBaseUrl;
164
- const configBaseUrl = baseApiUrls[baseUrlKey];
165
- if (!configBaseUrl) {
166
- throw new Error(`Unknown base API URL: ${baseUrlKey}`);
167
- }
168
- const baseUrl = typeof configBaseUrl === "function" ? configBaseUrl() : configBaseUrl;
169
- const path = typeof endpoint.path === "function" ? endpoint.path(baseUrl) : endpoint.path;
170
- const finalUrl = replacePathParams(`${baseUrl}${path}`, params);
171
- const queryString = new URLSearchParams(buildQueryParams(endpoint, reqQuery)).toString();
172
- return {
173
- finalUrl,
174
- fullUrl: queryString ? `${finalUrl}?${queryString}` : finalUrl
175
- };
176
- }
177
- function buildQueryParams(endpoint, query) {
178
- const defaultParams = endpoint.reqDefaultQueryParams ?? {};
179
- const parsedQuery = endpoint.reqQuerySchema ? endpoint.reqQuerySchema.parse(query) : query ?? {};
180
- const merged = { ...defaultParams, ...parsedQuery };
181
- return Object.fromEntries(Object.entries(merged).filter(([, value]) => value !== undefined).map(([key, value]) => [key, String(value)]));
182
- }
183
- function buildRequestBody(endpoint, body) {
184
- if (!endpoint.reqBodySchema)
185
- return body;
186
- const parsedBody = endpoint.reqBodySchema.parse(body);
187
- if (endpoint.reqBodyFormat !== "form-data") {
188
- return parsedBody;
189
- }
190
- const formData = new FormData;
191
- for (const [key, value] of Object.entries(parsedBody)) {
192
- if (value == null)
193
- continue;
194
- formData.set(key, value instanceof Blob ? value : String(value));
195
- }
196
- return formData;
197
- }
198
- function buildHeaders(endpoint, headers, defaultHeaders) {
199
- const contentType = endpoint.reqBodyFormat === "json" ? "application/json" : undefined;
200
- const parsedHeaders = endpoint.reqHeadersSchema ? endpoint.reqHeadersSchema.parse(headers) : headers ?? {};
201
- return {
202
- ...defaultHeaders,
203
- ...contentType && { "Content-Type": contentType },
204
- ...endpoint.defaultHeaders,
205
- ...parsedHeaders
206
- };
207
- }
208
- function buildFetchBody(body, headers) {
209
- if (body === undefined || body === null)
210
- return;
211
- if (body instanceof FormData || body instanceof Blob)
212
- return body;
213
- if (headers["Content-Type"] === "application/json")
214
- return JSON.stringify(body);
215
- return body;
216
- }
217
- function replacePathParams(path, params) {
218
- return path.replace(/:(\w+)/g, (_, key) => {
219
- const value = params?.[key];
220
- if (value === undefined) {
221
- throw new Error(`Missing required path parameter: ${key}`);
222
- }
223
- return encodeURIComponent(String(value));
224
- });
225
- }
226
- function getHeadersAsObject(headers) {
227
- const result = {};
228
- headers.forEach((value, key) => {
229
- result[key] = value;
230
- });
231
- return result;
232
- }
233
- function calculateSizeInMb(data) {
234
- const sizeInBytes = new TextEncoder().encode(JSON.stringify(data)).length;
235
- return sizeInBytes / (1024 * 1024);
236
- }
237
- function defaultShouldRetry({ error, response }) {
238
- if (error)
239
- return true;
240
- return response ? response.status >= 500 : false;
241
- }
242
- function delay(ms) {
243
- return new Promise((resolve) => setTimeout(resolve, ms));
244
- }
245
- export {
246
- createApiWrapper,
247
- createApiClient,
248
- ResponseValidationError,
249
- HttpResponseError
250
- };
@@ -1,2 +0,0 @@
1
- export declare function compressString(input: string): string;
2
- export declare function decompressString(compressedString: string): string;
@@ -1,16 +0,0 @@
1
- // src/gzip/index.ts
2
- import pako from "pako";
3
- function compressString(input) {
4
- const uint8Array = new TextEncoder().encode(input);
5
- const compressed = pako.deflate(uint8Array);
6
- return btoa(String.fromCharCode.apply(null, compressed));
7
- }
8
- function decompressString(compressedString) {
9
- const compressedData = new Uint8Array(atob(compressedString).split("").map((char) => char.charCodeAt(0)));
10
- const decompressed = pako.inflate(compressedData);
11
- return new TextDecoder().decode(decompressed);
12
- }
13
- export {
14
- decompressString,
15
- compressString
16
- };