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
@@ -0,0 +1,20 @@
1
+ // src/api-wrapper/testing.ts
2
+ function createMockFetch(handler) {
3
+ const calls = [];
4
+ const mockFetch = async (input, init) => {
5
+ const request = input instanceof Request ? new Request(input, init) : new Request(input.toString(), init);
6
+ calls.push(request.clone());
7
+ return handler(request, calls.length - 1);
8
+ };
9
+ return Object.assign(mockFetch, { calls });
10
+ }
11
+ function jsonResponse(body, init = {}) {
12
+ return new Response(JSON.stringify(body), {
13
+ status: init.status ?? 200,
14
+ headers: { "content-type": "application/json", ...init.headers }
15
+ });
16
+ }
17
+ export {
18
+ createMockFetch,
19
+ jsonResponse
20
+ };
@@ -30,57 +30,103 @@ var terminalColors = {
30
30
  bgCyan: "\x1B[46m",
31
31
  bgWhite: "\x1B[47m"
32
32
  };
33
+ var noColors = Object.fromEntries(Object.keys(terminalColors).map((key) => [key, ""]));
34
+ function getProcessEnvironment() {
35
+ return typeof process === "undefined" ? undefined : process.env;
36
+ }
37
+ function isDev() {
38
+ return getProcessEnvironment()?.NODE_ENV !== "production";
39
+ }
40
+ function supportsColor() {
41
+ const env = getProcessEnvironment();
42
+ if (!env || env.NO_COLOR)
43
+ return false;
44
+ if (env.FORCE_COLOR !== undefined && env.FORCE_COLOR !== "0")
45
+ return true;
46
+ return process.stdout?.isTTY === true;
47
+ }
48
+ var levelRank = {
49
+ debug: 0,
50
+ info: 1,
51
+ warn: 2,
52
+ error: 3
53
+ };
33
54
  var levelColors = {
34
55
  debug: { badge: terminalColors.gray, text: terminalColors.dim },
35
56
  info: { badge: terminalColors.cyan, text: terminalColors.reset },
36
57
  warn: { badge: terminalColors.yellow, text: terminalColors.yellow },
37
58
  error: { badge: terminalColors.red, text: terminalColors.brightRed }
38
59
  };
60
+ function formatValue(value) {
61
+ if (value instanceof Error)
62
+ return String(value);
63
+ if (typeof value !== "object" || value === null)
64
+ return String(value);
65
+ try {
66
+ return JSON.stringify(value);
67
+ } catch (_error) {
68
+ return "[Unserializable]";
69
+ }
70
+ }
39
71
  function formatAttributes(attrs) {
40
72
  if (!attrs || Object.keys(attrs).length === 0)
41
73
  return "";
42
- const formatted = Object.entries(attrs).map(([k, v]) => `${terminalColors.dim}${k}=${terminalColors.reset}${v}`).join(" ");
74
+ const formatted = Object.entries(attrs).map(([k, v]) => `${terminalColors.dim}${k}=${terminalColors.reset}${formatValue(v)}`).join(" ");
43
75
  return ` ${formatted}`;
44
76
  }
45
77
  function createCoolLogger(options = {}) {
46
- const { colorsInDevOnly = true, onLog } = options;
47
- const isDev = true;
48
- const useColors = colorsInDevOnly ? isDev : true;
49
- const createLogMethod = (level) => (message, attributes) => {
50
- onLog?.(level, message, attributes);
51
- if (level === "debug" && !isDev)
52
- return;
53
- if (useColors) {
54
- const { badge, text } = levelColors[level];
55
- const label = level.toUpperCase().padEnd(5);
56
- const attrStr = formatAttributes(attributes);
57
- console.log(`${badge}[${label}]${terminalColors.reset} ${text}${message}${terminalColors.reset}${attrStr}`);
58
- } else {
59
- console[level](`[${level.toUpperCase()}]`, message, attributes ?? "");
60
- }
61
- };
62
- return {
63
- debug: createLogMethod("debug"),
64
- info: createLogMethod("info"),
65
- warn: createLogMethod("warn"),
66
- error: createLogMethod("error")
78
+ const dev = isDev();
79
+ const {
80
+ colorsInDevOnly = true,
81
+ minLevel = dev ? "debug" : "info",
82
+ onLog
83
+ } = options;
84
+ const useColors = supportsColor() && (!colorsInDevOnly || dev);
85
+ const createLogger = (base) => {
86
+ const createLogMethod = (level) => (message, callAttributes) => {
87
+ const attributes = base && callAttributes ? { ...base, ...callAttributes } : callAttributes ?? base;
88
+ onLog?.(level, message, attributes);
89
+ if (levelRank[level] < levelRank[minLevel])
90
+ return;
91
+ if (useColors) {
92
+ const { badge, text } = levelColors[level];
93
+ const label = level.toUpperCase().padEnd(5);
94
+ const attrStr = formatAttributes(attributes);
95
+ console.log(`${badge}[${label}]${terminalColors.reset} ${text}${message}${terminalColors.reset}${attrStr}`);
96
+ } else {
97
+ console[level](`[${level.toUpperCase()}]`, message, attributes ?? "");
98
+ }
99
+ };
100
+ return {
101
+ debug: createLogMethod("debug"),
102
+ info: createLogMethod("info"),
103
+ warn: createLogMethod("warn"),
104
+ error: createLogMethod("error"),
105
+ child: (attributes) => createLogger({ ...base, ...attributes })
106
+ };
67
107
  };
108
+ return createLogger();
68
109
  }
69
110
  function logOperationSummary(operationName, durationMs, success, options = {}) {
70
111
  const { devOnly = true, error } = options;
71
- const isDev = true;
72
- if (devOnly && !isDev)
112
+ if (devOnly && !isDev())
73
113
  return;
74
- const c = terminalColors;
114
+ const c = supportsColor() ? terminalColors : noColors;
75
115
  const statusIcon = success ? `${c.brightGreen}✓${c.reset}` : `${c.brightRed}✗${c.reset}`;
76
116
  const statusColor = success ? c.green : c.red;
77
117
  const durationStr = `${c.dim}${durationMs.toFixed(1)}ms${c.reset}`;
78
118
  const errorMessage = error ? ` ${c.dim}·${c.reset} ${c.red}${error instanceof Error ? error.message : String(error)}${c.reset}` : "";
79
119
  console.log(`${statusColor}${c.bold}${operationName}${c.reset} ${statusIcon} ${c.dim}·${c.reset} ${durationStr}${errorMessage}`);
80
120
  }
121
+ function startOperation(operationName, options = {}) {
122
+ const start = performance.now();
123
+ return (success = true, error) => logOperationSummary(operationName, performance.now() - start, success, {
124
+ ...options,
125
+ error
126
+ });
127
+ }
81
128
  function colorize(text, color, ...modifiers) {
82
- const isDev = true;
83
- if (!isDev)
129
+ if (!isDev() || !supportsColor())
84
130
  return text;
85
131
  const colorCodes = [
86
132
  terminalColors[color],
@@ -90,8 +136,7 @@ function colorize(text, color, ...modifiers) {
90
136
  }
91
137
  function logBanner(text, color = "cyan", options = {}) {
92
138
  const { devOnly = true } = options;
93
- const isDev = true;
94
- if (devOnly && !isDev) {
139
+ if (devOnly && !isDev() || !supportsColor()) {
95
140
  console.log(`=== ${text} ===`);
96
141
  return;
97
142
  }
@@ -103,9 +148,11 @@ function logBanner(text, color = "cyan", options = {}) {
103
148
  console.log(`${c[color]}╚${line}╝${c.reset}`);
104
149
  }
105
150
  export {
106
- terminalColors,
107
- logOperationSummary,
108
- logBanner,
151
+ colorize,
109
152
  createCoolLogger,
110
- colorize
153
+ logBanner,
154
+ logOperationSummary,
155
+ startOperation,
156
+ supportsColor,
157
+ terminalColors
111
158
  };
@@ -0,0 +1,195 @@
1
+ import {
2
+ utf8Encode2,
3
+ utf8Decode2,
4
+ encodeBase642,
5
+ decodeBase642,
6
+ encodeBase64Url2,
7
+ decodeBase64Url2,
8
+ bytesToHex2,
9
+ hexToBytes2,
10
+ toBytes,
11
+ safeEqual2,
12
+ randomBytes2,
13
+ randomToken2,
14
+ sha2562,
15
+ sha256Hex2,
16
+ hmacSign2,
17
+ verifyHmacSignature2,
18
+ getSubtleCrypto
19
+ } from "../../index-rp5pqghz.js";
20
+ // src/crypto/sealer.ts
21
+ var ERROR_MESSAGES = {
22
+ malformed: "Malformed sealed value",
23
+ unsupported_version: "Unsupported sealed value version",
24
+ decrypt_failed: "Could not open sealed value: wrong key or tampered data"
25
+ };
26
+
27
+ class SealerError extends Error {
28
+ code;
29
+ constructor(code) {
30
+ super(ERROR_MESSAGES[code]);
31
+ this.name = "SealerError";
32
+ this.code = code;
33
+ }
34
+ }
35
+ var PREFIX = "enc:v1:";
36
+ var IV_BYTES = 12;
37
+ var TAG_BYTES = 16;
38
+ var DEFAULT_INFO = "hanzio.sealer.aes-256-gcm.v1";
39
+ function createSealer(options) {
40
+ const { secret, salt = "", info = DEFAULT_INFO } = options;
41
+ const secretBytes = toBytes(secret);
42
+ if (secretBytes.length === 0) {
43
+ throw new TypeError("Sealer secret must not be empty");
44
+ }
45
+ let keyPromise;
46
+ const getKey = () => {
47
+ if (!keyPromise) {
48
+ keyPromise = deriveKey(secretBytes, salt, info);
49
+ keyPromise.catch(() => {
50
+ keyPromise = undefined;
51
+ });
52
+ }
53
+ return keyPromise;
54
+ };
55
+ const seal = async (plaintext) => {
56
+ const iv = randomBytes2(IV_BYTES);
57
+ const encrypted = new Uint8Array(await getSubtleCrypto().encrypt({ name: "AES-GCM", iv }, await getKey(), utf8Encode2(plaintext)));
58
+ const tagStart = encrypted.length - TAG_BYTES;
59
+ return `${PREFIX}${encodeBase64Url2(iv)}.${encodeBase64Url2(encrypted.subarray(tagStart))}.${encodeBase64Url2(encrypted.subarray(0, tagStart))}`;
60
+ };
61
+ const open = async (sealed) => {
62
+ if (typeof sealed !== "string" || !sealed.startsWith(PREFIX)) {
63
+ const isOtherVersion = typeof sealed === "string" && /^enc:v\d+:/.test(sealed);
64
+ throw new SealerError(isOtherVersion ? "unsupported_version" : "malformed");
65
+ }
66
+ const parts = sealed.slice(PREFIX.length).split(".");
67
+ let iv;
68
+ let tag;
69
+ let ciphertext;
70
+ try {
71
+ if (parts.length !== 3)
72
+ throw new Error;
73
+ iv = decodeBase64Url2(parts[0]);
74
+ tag = decodeBase64Url2(parts[1]);
75
+ ciphertext = decodeBase64Url2(parts[2]);
76
+ } catch (_error) {
77
+ throw new SealerError("malformed");
78
+ }
79
+ if (iv.length !== IV_BYTES || tag.length !== TAG_BYTES) {
80
+ throw new SealerError("malformed");
81
+ }
82
+ const encrypted = new Uint8Array(ciphertext.length + TAG_BYTES);
83
+ encrypted.set(ciphertext);
84
+ encrypted.set(tag, ciphertext.length);
85
+ const key = await getKey();
86
+ let plaintext;
87
+ try {
88
+ plaintext = await getSubtleCrypto().decrypt({ name: "AES-GCM", iv }, key, encrypted);
89
+ } catch (_error) {
90
+ throw new SealerError("decrypt_failed");
91
+ }
92
+ return utf8Decode2(plaintext);
93
+ };
94
+ return {
95
+ seal,
96
+ open,
97
+ async sealJson(value) {
98
+ const json = JSON.stringify(value);
99
+ if (json === undefined) {
100
+ throw new TypeError("sealJson value is not JSON-serializable");
101
+ }
102
+ return seal(json);
103
+ },
104
+ async openJson(sealed) {
105
+ return JSON.parse(await open(sealed));
106
+ }
107
+ };
108
+ }
109
+ async function deriveKey(secret, salt, info) {
110
+ const subtle = getSubtleCrypto();
111
+ const baseKey = await subtle.importKey("raw", secret, "HKDF", false, [
112
+ "deriveKey"
113
+ ]);
114
+ return subtle.deriveKey({
115
+ name: "HKDF",
116
+ hash: "SHA-256",
117
+ salt: utf8Encode2(salt),
118
+ info: utf8Encode2(info)
119
+ }, baseKey, { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"]);
120
+ }
121
+ // src/crypto/webhook.ts
122
+ var TIMESTAMP = /^\d+$/;
123
+ async function verifySvixSignature(options) {
124
+ const {
125
+ secret,
126
+ payload,
127
+ headers,
128
+ toleranceSec = 300,
129
+ now = Date.now()
130
+ } = options;
131
+ let key;
132
+ try {
133
+ key = decodeBase642(secret.startsWith("whsec_") ? secret.slice(6) : secret);
134
+ } catch (_error) {
135
+ throw new TypeError("Invalid webhook secret: expected whsec_<base64>");
136
+ }
137
+ if (key.length === 0)
138
+ throw new TypeError("Webhook secret must not be empty");
139
+ const id = getHeader(headers, "id");
140
+ const timestamp = getHeader(headers, "timestamp");
141
+ const signatureHeader = getHeader(headers, "signature");
142
+ if (!id || !timestamp || !signatureHeader)
143
+ return false;
144
+ if (!TIMESTAMP.test(timestamp))
145
+ return false;
146
+ if (Math.abs(now / 1000 - Number(timestamp)) > toleranceSec)
147
+ return false;
148
+ const expected = await hmacSign2(key, `${id}.${timestamp}.${payload}`, {
149
+ encoding: "base64"
150
+ });
151
+ return signatureHeader.split(" ").some((entry) => {
152
+ const comma = entry.indexOf(",");
153
+ if (comma === -1 || entry.slice(0, comma) !== "v1")
154
+ return false;
155
+ return safeEqual2(entry.slice(comma + 1), expected);
156
+ });
157
+ }
158
+ function getHeader(headers, name) {
159
+ for (const prefix of ["svix-", "webhook-"]) {
160
+ const value = readHeader(headers, prefix + name);
161
+ if (value)
162
+ return value;
163
+ }
164
+ return;
165
+ }
166
+ function readHeader(headers, name) {
167
+ if (typeof headers.get === "function") {
168
+ return headers.get(name) ?? undefined;
169
+ }
170
+ for (const [key, value] of Object.entries(headers)) {
171
+ if (key.toLowerCase() === name)
172
+ return value;
173
+ }
174
+ return;
175
+ }
176
+ export {
177
+ SealerError,
178
+ bytesToHex2 as bytesToHex,
179
+ createSealer,
180
+ decodeBase642 as decodeBase64,
181
+ decodeBase64Url2 as decodeBase64Url,
182
+ encodeBase642 as encodeBase64,
183
+ encodeBase64Url2 as encodeBase64Url,
184
+ hexToBytes2 as hexToBytes,
185
+ hmacSign2 as hmacSign,
186
+ randomBytes2 as randomBytes,
187
+ randomToken2 as randomToken,
188
+ safeEqual2 as safeEqual,
189
+ sha2562 as sha256,
190
+ sha256Hex2 as sha256Hex,
191
+ utf8Decode2 as utf8Decode,
192
+ utf8Encode2 as utf8Encode,
193
+ verifyHmacSignature2 as verifyHmacSignature,
194
+ verifySvixSignature
195
+ };
@@ -0,0 +1,211 @@
1
+ import {
2
+ stableStringify2
3
+ } from "../../index-kj1phgrx.js";
4
+
5
+ // src/format/index.ts
6
+ var MAX_CACHED_FORMATTERS = 100;
7
+ function memoize(create) {
8
+ const cache = new Map;
9
+ return (locale, options) => {
10
+ const key = `${locale}|${options ? stableStringify2(options) : ""}`;
11
+ let formatter = cache.get(key);
12
+ if (formatter) {
13
+ cache.delete(key);
14
+ } else {
15
+ formatter = create(locale, options);
16
+ if (cache.size >= MAX_CACHED_FORMATTERS) {
17
+ const oldest = cache.keys().next().value;
18
+ if (oldest !== undefined)
19
+ cache.delete(oldest);
20
+ }
21
+ }
22
+ cache.set(key, formatter);
23
+ return formatter;
24
+ };
25
+ }
26
+ var getNumberFormat = memoize((locale, options) => new Intl.NumberFormat(locale, options));
27
+ var getDateTimeFormat = memoize((locale, options) => new Intl.DateTimeFormat(locale, options));
28
+ var getRelativeTimeFormat = memoize((locale, options) => new Intl.RelativeTimeFormat(locale, options));
29
+ var getListFormat = memoize((locale, options) => new Intl.ListFormat(locale, options));
30
+ var getPluralRules = memoize((locale, options) => new Intl.PluralRules(locale, options));
31
+ function toTime(value) {
32
+ return value instanceof Date ? value.getTime() : new Date(value).getTime();
33
+ }
34
+ function formatNumber(value, locale, options) {
35
+ return getNumberFormat(locale, options).format(value);
36
+ }
37
+ function formatPercent(value, locale, options) {
38
+ return formatNumber(value, locale, { ...options, style: "percent" });
39
+ }
40
+ function formatCurrency(amount, currency, locale, options) {
41
+ return formatNumber(amount, locale, {
42
+ ...options,
43
+ style: "currency",
44
+ currency
45
+ });
46
+ }
47
+ function formatCompactNumber(value, locale) {
48
+ return formatNumber(value, locale, { notation: "compact" });
49
+ }
50
+ var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"];
51
+ function formatBytes(bytes, options = {}) {
52
+ const { locale = "en-US", decimals, binary = true } = options;
53
+ const base = binary ? 1024 : 1000;
54
+ let exponent = 0;
55
+ while (Math.abs(bytes) >= base ** (exponent + 1) && exponent < BYTE_UNITS.length - 1) {
56
+ exponent++;
57
+ }
58
+ const maximumFractionDigits = exponent === 0 ? 0 : decimals ?? (exponent === 1 ? 0 : 1);
59
+ const value = formatNumber(bytes / base ** exponent, locale, {
60
+ maximumFractionDigits
61
+ });
62
+ return `${value} ${BYTE_UNITS[exponent]}`;
63
+ }
64
+ var SECOND_MS = 1000;
65
+ var MINUTE_MS = 60 * SECOND_MS;
66
+ var HOUR_MS = 60 * MINUTE_MS;
67
+ var DAY_MS = 24 * HOUR_MS;
68
+ function splitDuration(ms) {
69
+ const total = Number.isFinite(ms) && ms > 0 ? Math.floor(ms) : 0;
70
+ return {
71
+ days: Math.floor(total / DAY_MS),
72
+ hours: Math.floor(total % DAY_MS / HOUR_MS),
73
+ minutes: Math.floor(total % HOUR_MS / MINUTE_MS),
74
+ seconds: Math.floor(total % MINUTE_MS / SECOND_MS),
75
+ milliseconds: total % SECOND_MS
76
+ };
77
+ }
78
+ function formatClockDuration(ms) {
79
+ const { days, hours, minutes, seconds } = splitDuration(ms);
80
+ const totalHours = days * 24 + hours;
81
+ const pad = (n) => String(n).padStart(2, "0");
82
+ return totalHours > 0 ? `${totalHours}:${pad(minutes)}:${pad(seconds)}` : `${minutes}:${pad(seconds)}`;
83
+ }
84
+ var DURATION_UNITS = [
85
+ ["days", "day"],
86
+ ["hours", "hour"],
87
+ ["minutes", "minute"],
88
+ ["seconds", "second"],
89
+ ["milliseconds", "millisecond"]
90
+ ];
91
+ function formatDuration(ms, locale, options = {}) {
92
+ const { style = "short", maxUnits = 2 } = options;
93
+ const parts = splitDuration(ms);
94
+ const unit = (value, name) => formatNumber(value, locale, {
95
+ style: "unit",
96
+ unit: name,
97
+ unitDisplay: style
98
+ });
99
+ const first = DURATION_UNITS.findIndex(([key]) => parts[key] > 0);
100
+ if (first === -1)
101
+ return unit(0, "second");
102
+ const shown = DURATION_UNITS.slice(first, first + Math.max(1, maxUnits)).filter(([key]) => parts[key] > 0).map(([key, name]) => unit(parts[key], name));
103
+ return formatList(shown, locale, { type: "unit", style });
104
+ }
105
+ function relativeUnit(ms) {
106
+ const seconds = ms / 1000;
107
+ if (Math.abs(seconds) < 60)
108
+ return { value: Math.round(seconds), unit: "second" };
109
+ const minutes = seconds / 60;
110
+ if (Math.abs(minutes) < 60)
111
+ return { value: Math.round(minutes), unit: "minute" };
112
+ const hours = minutes / 60;
113
+ if (Math.abs(hours) < 24)
114
+ return { value: Math.round(hours), unit: "hour" };
115
+ const days = hours / 24;
116
+ if (Math.abs(days) < 30)
117
+ return { value: Math.round(days), unit: "day" };
118
+ const months = days / 30;
119
+ if (Math.abs(months) < 12)
120
+ return { value: Math.round(months), unit: "month" };
121
+ return { value: Math.round(days / 365), unit: "year" };
122
+ }
123
+ function formatRelativeTime(date, locale, options = {}) {
124
+ const {
125
+ now = Date.now(),
126
+ numeric = "auto",
127
+ style,
128
+ withSuffix = true
129
+ } = options;
130
+ const diff = toTime(date) - toTime(now);
131
+ if (!Number.isFinite(diff))
132
+ return "";
133
+ const { value, unit } = relativeUnit(diff);
134
+ if (withSuffix) {
135
+ return getRelativeTimeFormat(locale, { numeric, style }).format(value, unit);
136
+ }
137
+ return formatNumber(Math.abs(value), locale, {
138
+ style: "unit",
139
+ unit,
140
+ unitDisplay: style ?? "long"
141
+ });
142
+ }
143
+ function formatWait(value, locale, options = {}) {
144
+ const elapsed = typeof value === "number" ? value : toTime(options.now ?? Date.now()) - toTime(value);
145
+ const waited = Math.max(0, elapsed);
146
+ if (!Number.isFinite(waited))
147
+ return "";
148
+ const [amount, unit] = waited < HOUR_MS ? [Math.max(1, Math.floor(waited / MINUTE_MS)), "minute"] : waited < DAY_MS ? [Math.floor(waited / HOUR_MS), "hour"] : [Math.floor(waited / DAY_MS), "day"];
149
+ return formatNumber(amount, locale, {
150
+ style: "unit",
151
+ unit,
152
+ unitDisplay: "short"
153
+ });
154
+ }
155
+ var DATE_COMPONENTS = [
156
+ "weekday",
157
+ "era",
158
+ "year",
159
+ "month",
160
+ "day",
161
+ "dayPeriod",
162
+ "hour",
163
+ "minute",
164
+ "second",
165
+ "fractionalSecondDigits",
166
+ "dateStyle",
167
+ "timeStyle"
168
+ ];
169
+ function formatDateRange(start, end, locale, options = {}) {
170
+ const hasFields = DATE_COMPONENTS.some((key) => options[key] !== undefined);
171
+ const format = getDateTimeFormat(locale, hasFields ? options : { day: "numeric", month: "short", year: "numeric", ...options });
172
+ const from = new Date(toTime(start));
173
+ const to = new Date(toTime(end));
174
+ return typeof format.formatRange === "function" ? format.formatRange(from, to) : `${format.format(from)} – ${format.format(to)}`;
175
+ }
176
+ function formatList(items, locale, options) {
177
+ if (typeof Intl.ListFormat !== "function") {
178
+ return items.join(options?.style === "narrow" ? " " : ", ");
179
+ }
180
+ return getListFormat(locale, options).format(items);
181
+ }
182
+ function formatCappedCount(count, max = 99) {
183
+ if (!(count > 0))
184
+ return "0";
185
+ return count > max ? `${max}+` : String(count);
186
+ }
187
+ function pluralize(count, forms, locale) {
188
+ const form = forms[getPluralRules(locale).select(count)] ?? forms.other;
189
+ return form.replaceAll("{count}", formatNumber(count, locale));
190
+ }
191
+ export {
192
+ formatBytes,
193
+ formatCappedCount,
194
+ formatClockDuration,
195
+ formatCompactNumber,
196
+ formatCurrency,
197
+ formatDateRange,
198
+ formatDuration,
199
+ formatList,
200
+ formatNumber,
201
+ formatPercent,
202
+ formatRelativeTime,
203
+ formatWait,
204
+ getDateTimeFormat,
205
+ getListFormat,
206
+ getNumberFormat,
207
+ getPluralRules,
208
+ getRelativeTimeFormat,
209
+ pluralize,
210
+ splitDuration
211
+ };
@@ -0,0 +1,36 @@
1
+ // src/i18n/index.ts
2
+ var PLACEHOLDER_PATTERN = /\{(\w+)\}/g;
3
+ function extractPlaceholders(template) {
4
+ const names = template.matchAll(PLACEHOLDER_PATTERN).map((match) => match[1]);
5
+ return [...new Set(names)];
6
+ }
7
+ function interpolate(template, ...args) {
8
+ const params = args[0] ?? {};
9
+ return template.replace(PLACEHOLDER_PATTERN, (match, name) => {
10
+ const value = Object.hasOwn(params, name) ? params[name] : undefined;
11
+ return value == null ? match : String(value);
12
+ });
13
+ }
14
+ function flattenMessages(messages) {
15
+ const out = {};
16
+ const visit = (node, prefix) => {
17
+ for (const [key, value] of Object.entries(node)) {
18
+ if (typeof value === "string")
19
+ out[prefix + key] = value;
20
+ else
21
+ visit(value, `${prefix + key}.`);
22
+ }
23
+ };
24
+ visit(messages, "");
25
+ return out;
26
+ }
27
+ function createTranslator(messages) {
28
+ const flat = flattenMessages(messages);
29
+ return (key, ...args) => interpolate(flat[key] ?? key, ...args);
30
+ }
31
+ export {
32
+ createTranslator,
33
+ extractPlaceholders,
34
+ flattenMessages,
35
+ interpolate
36
+ };