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,80 +1,18 @@
1
+ import { createCache } from '../cache/createCache'
1
2
  import type { ActionCache, DefineAction } from './types'
2
3
 
3
- type CacheEntry = {
4
- value?: unknown
5
- hasValue: boolean
6
- expiresAt?: number
7
- inflight?: Promise<unknown>
8
- }
9
-
10
4
  export function createActionCache(): ActionCache {
11
- const store = new Map<string, CacheEntry>()
12
-
13
- const isExpired = (entry: CacheEntry) =>
14
- entry.expiresAt !== undefined && entry.expiresAt <= Date.now()
15
-
16
- const cache = (async <T>(
5
+ const store = createCache()
6
+ const cache = (<T>(
17
7
  key: string,
18
8
  fn: () => Promise<T> | T,
19
9
  options?: { ttlMs?: number }
20
- ): Promise<T> => {
21
- const existing = store.get(key)
22
- if (existing && !isExpired(existing)) {
23
- if (existing.inflight) return existing.inflight as Promise<T>
24
- if (existing.hasValue) return existing.value as T
25
- }
10
+ ): Promise<T> => store.getOrSet(key, fn, options)) as ActionCache
26
11
 
27
- const promise = (async () => fn())()
28
- const entry: CacheEntry = {
29
- hasValue: false,
30
- inflight: promise,
31
- expiresAt:
32
- options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
33
- }
34
- store.set(key, entry)
35
-
36
- try {
37
- const value = await promise
38
- entry.value = value
39
- entry.hasValue = true
40
- entry.inflight = undefined
41
- return value
42
- } catch (error) {
43
- store.delete(key)
44
- throw error
45
- }
46
- }) as ActionCache
47
-
48
- cache.get = <T>(key: string): T | undefined => {
49
- const entry = store.get(key)
50
- if (!entry || !entry.hasValue) return undefined
51
- if (isExpired(entry)) {
52
- store.delete(key)
53
- return undefined
54
- }
55
- return entry.value as T
56
- }
57
-
58
- cache.set = <T>(
59
- key: string,
60
- value: T,
61
- options?: { ttlMs?: number }
62
- ): void => {
63
- store.set(key, {
64
- value,
65
- hasValue: true,
66
- expiresAt:
67
- options?.ttlMs !== undefined ? Date.now() + options.ttlMs : undefined
68
- })
69
- }
70
-
71
- cache.invalidate = (key: string): void => {
72
- store.delete(key)
73
- }
74
-
75
- cache.clear = (): void => {
76
- store.clear()
77
- }
12
+ cache.get = (key) => store.get(key)
13
+ cache.set = (key, value, options) => store.set(key, value, options)
14
+ cache.invalidate = (key) => store.delete(key)
15
+ cache.clear = () => store.clear()
78
16
 
79
17
  return cache
80
18
  }
@@ -87,10 +25,11 @@ export function makeDefineAction<TApi>(): DefineAction<TApi> {
87
25
  noRuntimeInput: false as const
88
26
  })
89
27
  }
90
- const def = args[0] as { handler: unknown }
28
+ const def = args[0] as { handler: unknown; input?: unknown }
91
29
  return {
92
30
  ...def,
93
- noRuntimeInput: true as const
31
+ // A schema-validated action takes input; a bare handler doesn't.
32
+ noRuntimeInput: def.input === undefined
94
33
  }
95
34
  }
96
35
  return defineAction as DefineAction<TApi>
@@ -1,4 +1,5 @@
1
1
  import { ZodError } from 'zod'
2
+ import { buildQueryString } from '../url'
2
3
  import { type ConfigError, RequestValidationError } from './errors'
3
4
  import { normalizeHeaders } from './headers'
4
5
  import type {
@@ -9,70 +10,88 @@ import type {
9
10
  } from './shared'
10
11
  import type { ApiEndpoint } from './types'
11
12
 
12
- export function buildUrl(
13
- endpoint: ApiEndpoint,
14
- params: PathParams | undefined,
15
- reqQuery: QueryParams | undefined,
16
- baseApiUrls: Record<string, BaseApiUrl>,
17
- defaultBaseApiUrl: string | undefined,
18
- url: string | undefined,
13
+ export type BuildUrlOptions = {
14
+ endpoint: ApiEndpoint
15
+ params: PathParams | undefined
16
+ queryParams: QueryParams
17
+ baseApiUrls: Record<string, BaseApiUrl>
18
+ defaultBaseApiUrl: string | undefined
19
+ url: string | undefined
20
+ doNotEncodeQueryParams?: boolean
19
21
  configError: (msg: string) => ConfigError
20
- ) {
22
+ }
23
+
24
+ export function buildUrl(options: BuildUrlOptions) {
25
+ const {
26
+ endpoint,
27
+ params,
28
+ queryParams,
29
+ baseApiUrls,
30
+ defaultBaseApiUrl,
31
+ url,
32
+ doNotEncodeQueryParams,
33
+ configError
34
+ } = options
35
+
36
+ let finalUrl: string
21
37
  if (url) {
22
- const finalUrl = replacePathParams(url, params, configError)
23
- const queryString = new URLSearchParams(
24
- buildQueryParamsRaw(endpoint, reqQuery)
25
- ).toString()
26
- return {
27
- finalUrl,
28
- fullUrl: queryString ? `${finalUrl}?${queryString}` : finalUrl
38
+ finalUrl = replaceUrlPathParams(url, params, configError)
39
+ } else {
40
+ const defaultBaseUrl = defaultBaseApiUrl ?? Object.keys(baseApiUrls)[0]
41
+ if (!defaultBaseUrl) {
42
+ throw configError('At least one base API URL is required')
29
43
  }
30
- }
31
44
 
32
- const defaultBaseUrl = defaultBaseApiUrl ?? Object.keys(baseApiUrls)[0]
33
- if (!defaultBaseUrl) {
34
- throw configError('At least one base API URL is required')
35
- }
45
+ const baseUrlKey = endpoint.baseApiUrl ?? defaultBaseUrl
46
+ const configBaseUrl = baseApiUrls[baseUrlKey]
47
+ if (!configBaseUrl) {
48
+ throw configError(`Unknown base API URL: ${baseUrlKey}`)
49
+ }
36
50
 
37
- const baseUrlKey = endpoint.baseApiUrl ?? defaultBaseUrl
38
- const configBaseUrl = baseApiUrls[baseUrlKey]
39
- if (!configBaseUrl) {
40
- throw configError(`Unknown base API URL: ${baseUrlKey}`)
51
+ const baseUrl =
52
+ typeof configBaseUrl === 'function' ? configBaseUrl() : configBaseUrl
53
+ const path =
54
+ typeof endpoint.path === 'function'
55
+ ? endpoint.path(baseUrl)
56
+ : endpoint.path
57
+ // Substitute endpoint parameters without interpreting a port or IPv6 host as one.
58
+ finalUrl = joinUrlPath(
59
+ baseUrl,
60
+ replacePathParams(path, params, configError)
61
+ )
41
62
  }
42
63
 
43
- const baseUrl =
44
- typeof configBaseUrl === 'function' ? configBaseUrl() : configBaseUrl
45
- const path =
46
- typeof endpoint.path === 'function' ? endpoint.path(baseUrl) : endpoint.path
47
- const finalUrl = replacePathParams(`${baseUrl}${path}`, params, configError)
48
- const queryString = new URLSearchParams(
49
- buildQueryParamsRaw(endpoint, reqQuery)
50
- ).toString()
51
-
64
+ const queryString = buildQueryString(queryParams, {
65
+ encode: !doNotEncodeQueryParams
66
+ })
52
67
  return {
53
68
  finalUrl,
54
69
  fullUrl: queryString ? `${finalUrl}?${queryString}` : finalUrl
55
70
  }
56
71
  }
57
72
 
58
- export function buildQueryParamsRaw(
73
+ export function buildPathParams(
59
74
  endpoint: ApiEndpoint,
60
- query: QueryParams | undefined
61
- ): Record<string, string> {
62
- const defaultParams = endpoint.reqDefaultQueryParams ?? {}
63
- const merged = { ...defaultParams, ...(query ?? {}) }
64
- return Object.fromEntries(
65
- Object.entries(merged)
66
- .filter(([, value]) => value !== undefined)
67
- .map(([key, value]) => [key, String(value)])
68
- )
75
+ params: PathParams | undefined,
76
+ ctx: (over?: Partial<ApiErrorContext>) => ApiErrorContext
77
+ ): PathParams | undefined {
78
+ if (!endpoint.reqParamsSchema) return params
79
+
80
+ try {
81
+ return endpoint.reqParamsSchema.parse(params) as PathParams
82
+ } catch (error) {
83
+ if (error instanceof ZodError) {
84
+ throw new RequestValidationError(error, 'params', params, ctx())
85
+ }
86
+ throw error
87
+ }
69
88
  }
70
89
 
71
90
  export function buildQueryParams(
72
91
  endpoint: ApiEndpoint,
73
92
  query: QueryParams | undefined,
74
93
  ctx: (over?: Partial<ApiErrorContext>) => ApiErrorContext
75
- ): Record<string, string> {
94
+ ): QueryParams {
76
95
  const defaultParams = endpoint.reqDefaultQueryParams ?? {}
77
96
  let parsedQuery: QueryParams
78
97
  if (endpoint.reqQuerySchema) {
@@ -87,13 +106,7 @@ export function buildQueryParams(
87
106
  } else {
88
107
  parsedQuery = query ?? {}
89
108
  }
90
- const merged = { ...defaultParams, ...parsedQuery }
91
-
92
- return Object.fromEntries(
93
- Object.entries(merged)
94
- .filter(([, value]) => value !== undefined)
95
- .map(([key, value]) => [key, String(value)])
96
- )
109
+ return { ...defaultParams, ...parsedQuery }
97
110
  }
98
111
 
99
112
  export function buildRequestBody(
@@ -160,12 +173,31 @@ export function buildHeaders(
160
173
  })
161
174
  }
162
175
 
176
+ /** Joins with exactly one `/`; empty paths and query-only paths are appended as-is. */
177
+ export function joinUrlPath(base: string, path: string): string {
178
+ if (!path || path.startsWith('?')) return `${base}${path}`
179
+ return `${base.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`
180
+ }
181
+
182
+ /** Substitutes `:params` in the path of a full URL only, so ports are left alone. */
183
+ function replaceUrlPathParams(
184
+ url: string,
185
+ params: PathParams | undefined,
186
+ configError: (msg: string) => ConfigError
187
+ ): string {
188
+ const origin = url.match(/^[a-z][a-z\d+.-]*:\/\/[^/?#]*/i)?.[0] ?? ''
189
+ const rest = url.slice(origin.length)
190
+ const suffixIndex = rest.search(/[?#]/)
191
+ const path = suffixIndex === -1 ? rest : rest.slice(0, suffixIndex)
192
+ return `${origin}${replacePathParams(path, params, configError)}${rest.slice(path.length)}`
193
+ }
194
+
163
195
  export function replacePathParams(
164
196
  path: string,
165
197
  params: PathParams | undefined,
166
198
  configError: (msg: string) => ConfigError
167
199
  ): string {
168
- return path.replace(/:(\w+)/g, (_, key: string) => {
200
+ return path.replace(/:([A-Za-z_]\w*)/g, (_, key: string) => {
169
201
  const value = params?.[key]
170
202
  if (value === undefined) {
171
203
  throw configError(`Missing required path parameter: ${key}`)