hanzio 3.0.0 → 3.1.1

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-mq2qgc20.js +116 -0
  23. package/dist/index-najbyzpr.js +99 -0
  24. package/dist/index-nfvq3zax.js +475 -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 +533 -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
@@ -0,0 +1,213 @@
1
+ import { z } from 'zod'
2
+ import { unwrapOnce } from '../zod/introspect'
3
+ import { defOf, type Schema } from '../zod/zodToTypeString'
4
+
5
+ /** Thrown for bad argv. The CLI prints it with a `--help` hint and exits 2. */
6
+ export class UsageError extends Error {}
7
+
8
+ /** One key of an input or globals object, read as a flag or a positional. */
9
+ export type Field = {
10
+ key: string
11
+ flag: string
12
+ kind: 'string' | 'number' | 'boolean'
13
+ multiple: boolean
14
+ positional: boolean
15
+ optional: boolean
16
+ hasDefault: boolean
17
+ defaultValue: unknown
18
+ description?: string
19
+ alias?: string
20
+ env?: string
21
+ choices?: string[]
22
+ }
23
+
24
+ const kebab = (key: string) =>
25
+ key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
26
+
27
+ export function fieldsOf(schema: z.ZodObject | undefined): Field[] {
28
+ if (!schema) return []
29
+ return Object.entries(schema.shape).map(([key, child]) => toField(key, child))
30
+ }
31
+
32
+ function toField(key: string, schema: Schema): Field {
33
+ // Metadata can sit on any wrapper; the outermost `.meta()`/`.describe()` wins.
34
+ const metas: Record<string, unknown>[] = []
35
+ let optional = false
36
+ let hasDefault = false
37
+ let defaultValue: unknown
38
+ let current = schema
39
+ for (;;) {
40
+ const meta = z.globalRegistry.get(current)
41
+ if (meta) metas.unshift(meta)
42
+ const def = defOf(current)
43
+ if (def.type === 'optional') optional = true
44
+ if ((def.type === 'default' || def.type === 'prefault') && !hasDefault) {
45
+ hasDefault = true
46
+ defaultValue = def.defaultValue
47
+ }
48
+ const inner = unwrapOnce(def)
49
+ if (!inner) break
50
+ current = inner
51
+ }
52
+ const meta: Record<string, unknown> = Object.assign({}, ...metas)
53
+
54
+ let def = defOf(current)
55
+ const multiple = def.type === 'array'
56
+ if (def.type === 'array') {
57
+ let element = def.element
58
+ for (let inner = unwrapOnce(defOf(element)); inner; ) {
59
+ element = inner
60
+ inner = unwrapOnce(defOf(element))
61
+ }
62
+ def = defOf(element)
63
+ }
64
+ const alias = meta.alias
65
+ if (alias !== undefined && (typeof alias !== 'string' || alias.length !== 1))
66
+ throw new Error(
67
+ `hanzio/cli: alias ${JSON.stringify(alias)} of "${key}" must be one character`
68
+ )
69
+ return {
70
+ key,
71
+ flag: kebab(key),
72
+ kind:
73
+ def.type === 'boolean'
74
+ ? 'boolean'
75
+ : def.type === 'number'
76
+ ? 'number'
77
+ : 'string',
78
+ multiple,
79
+ positional: meta.positional === true,
80
+ optional,
81
+ hasDefault,
82
+ defaultValue,
83
+ description:
84
+ typeof meta.description === 'string' ? meta.description : undefined,
85
+ alias,
86
+ env: typeof meta.env === 'string' ? meta.env : undefined,
87
+ choices:
88
+ def.type === 'enum'
89
+ ? Object.values(def.entries).map(String)
90
+ : def.type === 'literal'
91
+ ? def.values.map(String)
92
+ : undefined
93
+ }
94
+ }
95
+
96
+ function coerce(field: Field, value: string | boolean): unknown {
97
+ if (typeof value !== 'string') return value
98
+ if (field.kind === 'number' && value.trim() !== '') {
99
+ const number = Number(value)
100
+ return Number.isNaN(number) ? value : number
101
+ }
102
+ if (field.kind === 'boolean') {
103
+ if (value === 'true' || value === '1') return true
104
+ if (value === 'false' || value === '0' || value === '') return false
105
+ }
106
+ return value
107
+ }
108
+
109
+ /**
110
+ * Builds the raw object Zod validates: flag values and positionals by key,
111
+ * numbers coerced, `.meta({ env })` as fallback, and a missing required
112
+ * boolean or array read as `false` or `[]`.
113
+ */
114
+ export function readFields(
115
+ fields: Field[],
116
+ values: Record<string, string | boolean | (string | boolean)[] | undefined>,
117
+ positionals: string[],
118
+ env: Record<string, string | undefined>
119
+ ): Record<string, unknown> {
120
+ const result: Record<string, unknown> = {}
121
+ const queue = [...positionals]
122
+ for (const field of fields) {
123
+ let raw: string | boolean | (string | boolean)[] | undefined
124
+ if (field.positional)
125
+ raw = field.multiple
126
+ ? queue.length
127
+ ? queue.splice(0)
128
+ : undefined
129
+ : queue.shift()
130
+ else raw = values[field.flag]
131
+ if (raw === undefined && field.env && env[field.env] !== undefined) {
132
+ const value = env[field.env] as string
133
+ raw = field.multiple ? [value] : value
134
+ }
135
+ if (raw === undefined) {
136
+ if (field.optional || field.hasDefault) continue
137
+ if (field.multiple) raw = []
138
+ else if (field.kind === 'boolean') raw = false
139
+ else continue
140
+ }
141
+ result[field.key] = Array.isArray(raw)
142
+ ? raw.map((item) => coerce(field, item))
143
+ : coerce(field, raw)
144
+ }
145
+ if (queue.length) throw new UsageError(`Unexpected argument "${queue[0]}"`)
146
+ return result
147
+ }
148
+
149
+ export function fieldLabel(field: Field): string {
150
+ if (field.positional)
151
+ return field.multiple
152
+ ? `<${field.flag}...>`
153
+ : field.optional || field.hasDefault
154
+ ? `[${field.flag}]`
155
+ : `<${field.flag}>`
156
+ const name =
157
+ field.kind === 'boolean' && field.defaultValue === true
158
+ ? `--[no-]${field.flag}`
159
+ : `--${field.flag}`
160
+ const value =
161
+ field.kind === 'boolean'
162
+ ? ''
163
+ : ` <${field.choices?.join('|') ?? field.kind}>${field.multiple ? '...' : ''}`
164
+ return `${field.alias ? `-${field.alias}, ` : ''}${name}${value}`
165
+ }
166
+
167
+ export function fieldHelp(field: Field): string {
168
+ const showDefault =
169
+ field.hasDefault &&
170
+ field.defaultValue !== undefined &&
171
+ !(field.kind === 'boolean' && field.defaultValue === false)
172
+ return [
173
+ field.description,
174
+ showDefault && `(default: ${JSON.stringify(field.defaultValue)})`,
175
+ field.env && `[env: ${field.env}]`
176
+ ]
177
+ .filter(Boolean)
178
+ .join(' ')
179
+ }
180
+
181
+ /** Closest candidate within a few edits, for "Did you mean ...?" hints. */
182
+ export function closest(
183
+ input: string,
184
+ candidates: readonly string[]
185
+ ): string | undefined {
186
+ let best: string | undefined
187
+ let bestDistance = Math.max(2, Math.floor(input.length / 3)) + 1
188
+ for (const candidate of candidates) {
189
+ const distance = candidate.startsWith(input)
190
+ ? 1
191
+ : levenshtein(input, candidate)
192
+ if (distance < bestDistance) {
193
+ best = candidate
194
+ bestDistance = distance
195
+ }
196
+ }
197
+ return best
198
+ }
199
+
200
+ function levenshtein(a: string, b: string): number {
201
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index)
202
+ for (let i = 1; i <= a.length; i++) {
203
+ const current = [i]
204
+ for (let j = 1; j <= b.length; j++)
205
+ current[j] = Math.min(
206
+ (previous[j] ?? 0) + 1,
207
+ (current[j - 1] ?? 0) + 1,
208
+ (previous[j - 1] ?? 0) + (a[i - 1] === b[j - 1] ? 0 : 1)
209
+ )
210
+ previous = current
211
+ }
212
+ return previous[b.length] ?? 0
213
+ }