hanzio 1.0.8 → 1.0.10
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.
- package/README.md +8 -16
- package/dist/index.d.ts +0 -1
- package/dist/jwt/index.d.ts +3 -3
- package/dist/secrets/index.d.ts +1 -1
- package/dist/secrets/infisical.d.ts +2 -0
- package/dist/src/index.js +192 -76
- package/dist/src/jwt/index.js +98 -7
- package/dist/src/secrets/index.js +340 -55
- package/dist/src/secrets/infisical.js +339 -0
- package/package.json +6 -9
- package/src/index.ts +0 -1
- package/src/jwt/index.ts +158 -12
- package/src/secrets/index.ts +4 -73
- package/src/secrets/infisical.ts +108 -0
- package/src/gzip/index.ts +0 -29
package/README.md
CHANGED
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
A small TypeScript-first utility library for common application code: arrays,
|
|
4
4
|
strings, promises, caching, state machines, dates, URLs, Zod helpers, API clients,
|
|
5
|
-
JWTs,
|
|
5
|
+
JWTs, sitemap crawling, queues, secrets, and colorful terminal logging.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
bun add hanzio
|
|
10
|
+
bun add hanzio zod
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
The package is ESM and Bun-friendly. Bun consumers resolve the `bun` export to
|
|
14
14
|
the source TypeScript files, while Node/bundler consumers resolve built files
|
|
15
15
|
from `dist`.
|
|
16
|
+
`zod` is a peer dependency because the Zod helpers and API wrapper share the
|
|
17
|
+
consumer application's Zod instance.
|
|
16
18
|
|
|
17
19
|
## Imports
|
|
18
20
|
|
|
@@ -27,10 +29,10 @@ Heavier or dependency-specific modules are also available as subpath exports:
|
|
|
27
29
|
```ts
|
|
28
30
|
import { createZId } from 'hanzio/zod'
|
|
29
31
|
import { jwtSign } from 'hanzio/jwt'
|
|
30
|
-
import { compressString } from 'hanzio/gzip'
|
|
31
32
|
import { getDomainSitemap } from 'hanzio/sitemap'
|
|
32
33
|
import PQueue from 'hanzio/p-queue'
|
|
33
34
|
import { createCoolLogger } from 'hanzio/cool-console-log'
|
|
35
|
+
import { defineSecretSet } from 'hanzio/secrets'
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
The package currently publishes `src/**/*.ts` intentionally so Bun can consume
|
|
@@ -217,7 +219,7 @@ await promiseTimeout(250)
|
|
|
217
219
|
Infisical before the rest of the program starts.
|
|
218
220
|
|
|
219
221
|
```ts
|
|
220
|
-
import { defineSecretSet } from 'hanzio'
|
|
222
|
+
import { defineSecretSet } from 'hanzio/secrets'
|
|
221
223
|
|
|
222
224
|
export const backendSecrets = await defineSecretSet(
|
|
223
225
|
['DATABASE_URL', 'APP_SECRET'] as const,
|
|
@@ -229,7 +231,8 @@ const databaseUrl = backendSecrets.secret('DATABASE_URL')
|
|
|
229
231
|
|
|
230
232
|
For Vite config, `viteSecretSetPlugin(secretSet)` exposes the loaded values as
|
|
231
233
|
`import.meta.env.*` replacements. Use `getViteDefine(secretSet)` if you need the
|
|
232
|
-
raw `define` object.
|
|
234
|
+
raw `define` object. Import Vite helpers from `hanzio/secrets/vite` so browser
|
|
235
|
+
bundles do not pull in the Infisical SDK.
|
|
233
236
|
|
|
234
237
|
### Cache
|
|
235
238
|
|
|
@@ -470,17 +473,6 @@ const token = await jwtSign({ sub: 'user_1' }, 'secret')
|
|
|
470
473
|
const payload = await jwtVerify<{ sub: string }>(token, 'secret')
|
|
471
474
|
```
|
|
472
475
|
|
|
473
|
-
### Gzip
|
|
474
|
-
|
|
475
|
-
`compressString` and `decompressString` gzip roundtrip strings as base64.
|
|
476
|
-
|
|
477
|
-
```ts
|
|
478
|
-
import { compressString, decompressString } from 'hanzio/gzip'
|
|
479
|
-
|
|
480
|
-
const compressed = compressString('hello')
|
|
481
|
-
const text = decompressString(compressed)
|
|
482
|
-
```
|
|
483
|
-
|
|
484
476
|
### Sitemap
|
|
485
477
|
|
|
486
478
|
`getDomainSitemap` discovers and crawls sitemap URLs for a domain.
|
package/dist/index.d.ts
CHANGED
package/dist/jwt/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export declare function jwtSign<
|
|
3
|
-
export declare function jwtVerify<Payload extends
|
|
1
|
+
export type JWTPayload = Record<string, unknown>;
|
|
2
|
+
export declare function jwtSign<Payload extends JWTPayload>(payload: Payload, secret: string): Promise<string>;
|
|
3
|
+
export declare function jwtVerify<Payload extends JWTPayload>(token: string, secret: string): Promise<Payload | null>;
|
package/dist/secrets/index.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export type DefineSecretSetOptions<SecretKey extends string> = {
|
|
|
6
6
|
readonly clientIdEnvKey?: string;
|
|
7
7
|
readonly clientSecretEnvKey?: string;
|
|
8
8
|
readonly loader?: SecretSetLoader<SecretKey>;
|
|
9
|
-
readonly getLocalIp?: () => string;
|
|
10
9
|
};
|
|
11
10
|
export type SecretSetLoaderContext<SecretKey extends string> = {
|
|
12
11
|
readonly keys: readonly SecretKey[];
|
|
@@ -22,6 +21,7 @@ export type SecretSet<SecretKey extends string> = {
|
|
|
22
21
|
secrets: () => Record<SecretKey, string>;
|
|
23
22
|
reload: () => Promise<SecretSet<SecretKey>>;
|
|
24
23
|
};
|
|
24
|
+
export * from './infisical';
|
|
25
25
|
export * from './vite';
|
|
26
26
|
export declare function getSecretEnvironment(): SecretEnvironment;
|
|
27
27
|
export declare function defineSecretSet<const SecretKeys extends readonly string[]>(keys: SecretKeys, options: DefineSecretSetOptions<SecretKeys[number]>): Promise<SecretSet<SecretKeys[number]>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { SecretSetLoaderContext } from '.';
|
|
2
|
+
export declare function loadInfisicalSecrets<SecretKey extends string>({ keys, projectId, environment, siteUrl, clientIdEnvKey, clientSecretEnvKey }: SecretSetLoaderContext<SecretKey>): Promise<Partial<Record<SecretKey, string>>>;
|
package/dist/src/index.js
CHANGED
|
@@ -880,32 +880,110 @@ class PQueue {
|
|
|
880
880
|
}
|
|
881
881
|
|
|
882
882
|
// src/jwt/index.ts
|
|
883
|
-
|
|
883
|
+
var BYTE_CHUNK_SIZE = 32768;
|
|
884
|
+
var textEncoder = new TextEncoder;
|
|
885
|
+
var textDecoder = new TextDecoder;
|
|
884
886
|
async function jwtSign(payload, secret) {
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
+
const header = encodeJson({ alg: "HS256" });
|
|
888
|
+
const body = encodeJson({
|
|
889
|
+
...payload,
|
|
890
|
+
iat: Math.floor(Date.now() / 1000)
|
|
891
|
+
});
|
|
892
|
+
const data = `${header}.${body}`;
|
|
893
|
+
const signature = await sign(data, secret);
|
|
894
|
+
return `${data}.${encodeBase64Url(signature)}`;
|
|
887
895
|
}
|
|
888
|
-
async function
|
|
896
|
+
async function jwtVerify(token, secret) {
|
|
889
897
|
try {
|
|
890
|
-
const
|
|
891
|
-
|
|
898
|
+
const parts = token.split(".");
|
|
899
|
+
if (parts.length !== 3)
|
|
900
|
+
return null;
|
|
901
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
902
|
+
const header = decodeJson(encodedHeader);
|
|
903
|
+
if (!isJwtHeader(header))
|
|
904
|
+
return null;
|
|
905
|
+
const data = `${encodedHeader}.${encodedPayload}`;
|
|
906
|
+
const signature = decodeBase64Url(encodedSignature);
|
|
907
|
+
const isValid = await verify(data, signature, secret);
|
|
908
|
+
if (!isValid)
|
|
909
|
+
return null;
|
|
910
|
+
const payload = decodeJson(encodedPayload);
|
|
911
|
+
if (!isJwtPayload(payload) || !isTimeValid(payload))
|
|
912
|
+
return null;
|
|
892
913
|
return payload;
|
|
893
914
|
} catch (_error) {
|
|
894
915
|
return null;
|
|
895
916
|
}
|
|
896
917
|
}
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
const
|
|
903
|
-
return
|
|
904
|
-
}
|
|
905
|
-
function
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
918
|
+
async function sign(data, secret) {
|
|
919
|
+
const key = await getKey(secret, ["sign"]);
|
|
920
|
+
return await getSubtleCrypto().sign("HMAC", key, encodeText(data));
|
|
921
|
+
}
|
|
922
|
+
async function verify(data, signature, secret) {
|
|
923
|
+
const key = await getKey(secret, ["verify"]);
|
|
924
|
+
return await getSubtleCrypto().verify("HMAC", key, signature, encodeText(data));
|
|
925
|
+
}
|
|
926
|
+
async function getKey(secret, usages) {
|
|
927
|
+
return await getSubtleCrypto().importKey("raw", encodeText(secret), {
|
|
928
|
+
name: "HMAC",
|
|
929
|
+
hash: "SHA-256"
|
|
930
|
+
}, false, usages);
|
|
931
|
+
}
|
|
932
|
+
function encodeJson(value) {
|
|
933
|
+
return encodeBase64Url(encodeText(JSON.stringify(value)));
|
|
934
|
+
}
|
|
935
|
+
function decodeJson(value) {
|
|
936
|
+
return JSON.parse(textDecoder.decode(decodeBase64Url(value)));
|
|
937
|
+
}
|
|
938
|
+
function encodeBase64Url(value) {
|
|
939
|
+
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
940
|
+
return btoa(bytesToBinaryString(bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
941
|
+
}
|
|
942
|
+
function decodeBase64Url(value) {
|
|
943
|
+
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
944
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
945
|
+
return binaryStringToBytes(atob(padded)).buffer;
|
|
946
|
+
}
|
|
947
|
+
function encodeText(value) {
|
|
948
|
+
return toArrayBuffer(textEncoder.encode(value));
|
|
949
|
+
}
|
|
950
|
+
function toArrayBuffer(bytes) {
|
|
951
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
952
|
+
}
|
|
953
|
+
function bytesToBinaryString(bytes) {
|
|
954
|
+
let binary = "";
|
|
955
|
+
for (let offset = 0;offset < bytes.length; offset += BYTE_CHUNK_SIZE) {
|
|
956
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + BYTE_CHUNK_SIZE));
|
|
957
|
+
}
|
|
958
|
+
return binary;
|
|
959
|
+
}
|
|
960
|
+
function binaryStringToBytes(binary) {
|
|
961
|
+
const bytes = new Uint8Array(binary.length);
|
|
962
|
+
for (let index = 0;index < binary.length; index += 1) {
|
|
963
|
+
bytes[index] = binary.charCodeAt(index);
|
|
964
|
+
}
|
|
965
|
+
return bytes;
|
|
966
|
+
}
|
|
967
|
+
function getSubtleCrypto() {
|
|
968
|
+
const subtle = globalThis.crypto?.subtle;
|
|
969
|
+
if (!subtle) {
|
|
970
|
+
throw new Error("Web Crypto is not available in this runtime");
|
|
971
|
+
}
|
|
972
|
+
return subtle;
|
|
973
|
+
}
|
|
974
|
+
function isJwtHeader(value) {
|
|
975
|
+
return typeof value === "object" && value !== null && "alg" in value && value.alg === "HS256";
|
|
976
|
+
}
|
|
977
|
+
function isJwtPayload(value) {
|
|
978
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
979
|
+
}
|
|
980
|
+
function isTimeValid(payload) {
|
|
981
|
+
const now = Math.floor(Date.now() / 1000);
|
|
982
|
+
if (typeof payload.exp === "number" && now >= payload.exp)
|
|
983
|
+
return false;
|
|
984
|
+
if (typeof payload.nbf === "number" && now < payload.nbf)
|
|
985
|
+
return false;
|
|
986
|
+
return true;
|
|
909
987
|
}
|
|
910
988
|
|
|
911
989
|
// src/sitemap/index.ts
|
|
@@ -1023,6 +1101,98 @@ var getDomainSitemap = async (domain, options = {}) => {
|
|
|
1023
1101
|
return [...result.links];
|
|
1024
1102
|
};
|
|
1025
1103
|
|
|
1104
|
+
// src/secrets/infisical.ts
|
|
1105
|
+
import { z as z4 } from "zod";
|
|
1106
|
+
var defaultFetch = globalThis.fetch;
|
|
1107
|
+
var infisicalAuthResponseSchema = z4.object({
|
|
1108
|
+
accessToken: z4.string()
|
|
1109
|
+
});
|
|
1110
|
+
var infisicalSecretSchema = z4.object({
|
|
1111
|
+
secretKey: z4.string(),
|
|
1112
|
+
secretValue: z4.string()
|
|
1113
|
+
});
|
|
1114
|
+
var infisicalSecretsResponseSchema = z4.object({
|
|
1115
|
+
secrets: z4.array(infisicalSecretSchema)
|
|
1116
|
+
});
|
|
1117
|
+
async function loadInfisicalSecrets({
|
|
1118
|
+
keys,
|
|
1119
|
+
projectId,
|
|
1120
|
+
environment,
|
|
1121
|
+
siteUrl,
|
|
1122
|
+
clientIdEnvKey,
|
|
1123
|
+
clientSecretEnvKey
|
|
1124
|
+
}) {
|
|
1125
|
+
const clientId = process.env[clientIdEnvKey];
|
|
1126
|
+
const clientSecret = process.env[clientSecretEnvKey];
|
|
1127
|
+
if (!clientId || !clientSecret) {
|
|
1128
|
+
throw new Error("Missing Infisical credentials");
|
|
1129
|
+
}
|
|
1130
|
+
const infisical = createInfisicalApiClient(siteUrl);
|
|
1131
|
+
const { data: auth } = await infisical.request("login", {
|
|
1132
|
+
reqBody: {
|
|
1133
|
+
clientId,
|
|
1134
|
+
clientSecret
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
const { data } = await infisical.request("listSecrets", {
|
|
1138
|
+
reqHeaders: {
|
|
1139
|
+
Authorization: `Bearer ${auth.accessToken}`
|
|
1140
|
+
},
|
|
1141
|
+
reqQuery: {
|
|
1142
|
+
environment,
|
|
1143
|
+
projectId,
|
|
1144
|
+
secretPath: "/",
|
|
1145
|
+
viewSecretValue: true,
|
|
1146
|
+
expandSecretReferences: true,
|
|
1147
|
+
includeImports: true
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
const wantedKeys = new Set(keys);
|
|
1151
|
+
return Object.fromEntries(data.secrets.filter((secret) => wantedKeys.has(secret.secretKey)).map((secret) => [secret.secretKey, secret.secretValue]));
|
|
1152
|
+
}
|
|
1153
|
+
function createInfisicalApiClient(siteUrl) {
|
|
1154
|
+
return createApiClient({
|
|
1155
|
+
name: "infisical",
|
|
1156
|
+
baseApiUrls: {
|
|
1157
|
+
infisical: siteUrl
|
|
1158
|
+
},
|
|
1159
|
+
defaultBaseApiUrl: "infisical",
|
|
1160
|
+
fetch: getFetch(),
|
|
1161
|
+
retries: 1,
|
|
1162
|
+
endpoints: {
|
|
1163
|
+
login: {
|
|
1164
|
+
method: "POST",
|
|
1165
|
+
path: "/api/v1/auth/universal-auth/login",
|
|
1166
|
+
reqBodySchema: z4.object({
|
|
1167
|
+
clientId: z4.string(),
|
|
1168
|
+
clientSecret: z4.string()
|
|
1169
|
+
}),
|
|
1170
|
+
resSchema: infisicalAuthResponseSchema,
|
|
1171
|
+
reqBodyFormat: "json"
|
|
1172
|
+
},
|
|
1173
|
+
listSecrets: {
|
|
1174
|
+
method: "GET",
|
|
1175
|
+
path: "/api/v4/secrets",
|
|
1176
|
+
reqHeadersSchema: z4.object({
|
|
1177
|
+
Authorization: z4.string()
|
|
1178
|
+
}),
|
|
1179
|
+
reqQuerySchema: z4.object({
|
|
1180
|
+
projectId: z4.string(),
|
|
1181
|
+
environment: z4.string(),
|
|
1182
|
+
secretPath: z4.string(),
|
|
1183
|
+
viewSecretValue: z4.boolean(),
|
|
1184
|
+
expandSecretReferences: z4.boolean(),
|
|
1185
|
+
includeImports: z4.boolean()
|
|
1186
|
+
}),
|
|
1187
|
+
resSchema: infisicalSecretsResponseSchema
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
function getFetch() {
|
|
1193
|
+
return globalThis.fetch ?? defaultFetch;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1026
1196
|
// src/secrets/vite.ts
|
|
1027
1197
|
function getViteDefine(secretSet) {
|
|
1028
1198
|
return Object.fromEntries(Object.entries(secretSet.secrets()).map(([key, value]) => [
|
|
@@ -1039,10 +1209,6 @@ function viteSecretSetPlugin(secretSet) {
|
|
|
1039
1209
|
};
|
|
1040
1210
|
}
|
|
1041
1211
|
|
|
1042
|
-
// src/secrets/index.ts
|
|
1043
|
-
import { InfisicalSDK } from "@infisical/sdk";
|
|
1044
|
-
import { networkInterfaces } from "node:os";
|
|
1045
|
-
|
|
1046
1212
|
// src/cool-console-log/index.ts
|
|
1047
1213
|
var terminalColors = {
|
|
1048
1214
|
reset: "\x1B[0m",
|
|
@@ -1150,8 +1316,6 @@ function logBanner(text, color = "cyan", options = {}) {
|
|
|
1150
1316
|
|
|
1151
1317
|
// src/secrets/index.ts
|
|
1152
1318
|
var DEFAULT_INFISICAL_SITE_URL = "https://eu.infisical.com";
|
|
1153
|
-
var LOCAL_IP_VALUE = "LOCAL_IP";
|
|
1154
|
-
var VPN_INTERFACE_RE = /^(utun|tun|tailscale|wg|ipsec|ppp)/i;
|
|
1155
1319
|
var logger = createCoolLogger();
|
|
1156
1320
|
function getSecretEnvironment() {
|
|
1157
1321
|
const env = process.env.SECRETS_ENV || "development";
|
|
@@ -1164,13 +1328,7 @@ function getSecretEnvironment() {
|
|
|
1164
1328
|
async function defineSecretSet(keys, options) {
|
|
1165
1329
|
const keySet = [...new Set(keys)];
|
|
1166
1330
|
const loader = options.loader ?? loadInfisicalSecrets;
|
|
1167
|
-
const getLocalIp = options.getLocalIp ?? getLocalIpAddress;
|
|
1168
1331
|
let cachedSecrets = null;
|
|
1169
|
-
const resolveValue = (value) => {
|
|
1170
|
-
if (value === LOCAL_IP_VALUE)
|
|
1171
|
-
return getLocalIp();
|
|
1172
|
-
return value;
|
|
1173
|
-
};
|
|
1174
1332
|
const loadSecrets = async (force = false) => {
|
|
1175
1333
|
const envSecrets = readEnvSecrets(keySet, force ? cachedSecrets : null);
|
|
1176
1334
|
const keysMissingFromEnv = keySet.filter((key) => !envSecrets[key]);
|
|
@@ -1190,7 +1348,7 @@ async function defineSecretSet(keys, options) {
|
|
|
1190
1348
|
}
|
|
1191
1349
|
const nextSecrets = Object.fromEntries(keySet.map((key) => [
|
|
1192
1350
|
key,
|
|
1193
|
-
|
|
1351
|
+
envSecrets[key] ?? loadedSecrets[key]
|
|
1194
1352
|
]));
|
|
1195
1353
|
for (const [key, value] of Object.entries(nextSecrets)) {
|
|
1196
1354
|
process.env[key] = value;
|
|
@@ -1204,7 +1362,7 @@ async function defineSecretSet(keys, options) {
|
|
|
1204
1362
|
if (!value) {
|
|
1205
1363
|
throw new Error(`Secret ${key} not found`);
|
|
1206
1364
|
}
|
|
1207
|
-
return
|
|
1365
|
+
return value;
|
|
1208
1366
|
},
|
|
1209
1367
|
secrets() {
|
|
1210
1368
|
if (!cachedSecrets) {
|
|
@@ -1220,31 +1378,6 @@ async function defineSecretSet(keys, options) {
|
|
|
1220
1378
|
await loadSecrets();
|
|
1221
1379
|
return secretSet;
|
|
1222
1380
|
}
|
|
1223
|
-
async function loadInfisicalSecrets({
|
|
1224
|
-
keys,
|
|
1225
|
-
projectId,
|
|
1226
|
-
environment,
|
|
1227
|
-
siteUrl,
|
|
1228
|
-
clientIdEnvKey,
|
|
1229
|
-
clientSecretEnvKey
|
|
1230
|
-
}) {
|
|
1231
|
-
const clientId = process.env[clientIdEnvKey];
|
|
1232
|
-
const clientSecret = process.env[clientSecretEnvKey];
|
|
1233
|
-
if (!clientId || !clientSecret) {
|
|
1234
|
-
throw new Error("Missing Infisical credentials");
|
|
1235
|
-
}
|
|
1236
|
-
const client = new InfisicalSDK({ siteUrl });
|
|
1237
|
-
await client.auth().universalAuth.login({
|
|
1238
|
-
clientId,
|
|
1239
|
-
clientSecret
|
|
1240
|
-
});
|
|
1241
|
-
const { secrets } = await client.secrets().listSecrets({
|
|
1242
|
-
environment,
|
|
1243
|
-
projectId
|
|
1244
|
-
});
|
|
1245
|
-
const wantedKeys = new Set(keys);
|
|
1246
|
-
return Object.fromEntries(secrets.filter((secret) => wantedKeys.has(secret.secretKey)).map((secret) => [secret.secretKey, secret.secretValue]));
|
|
1247
|
-
}
|
|
1248
1381
|
function resolveEnvironment(environment) {
|
|
1249
1382
|
if (typeof environment === "function")
|
|
1250
1383
|
return environment();
|
|
@@ -1262,22 +1395,6 @@ function readEnvSecrets(keys, previousSecrets = null) {
|
|
|
1262
1395
|
return value && value !== previousSecrets?.[key];
|
|
1263
1396
|
}).map((key) => [key, process.env[key]]));
|
|
1264
1397
|
}
|
|
1265
|
-
function getLocalIpAddress() {
|
|
1266
|
-
const interfaces = networkInterfaces();
|
|
1267
|
-
for (const name of Object.keys(interfaces)) {
|
|
1268
|
-
if (VPN_INTERFACE_RE.test(name))
|
|
1269
|
-
continue;
|
|
1270
|
-
const nets = interfaces[name];
|
|
1271
|
-
if (!nets)
|
|
1272
|
-
continue;
|
|
1273
|
-
for (const net of nets) {
|
|
1274
|
-
if (net.family === "IPv4" && !net.internal) {
|
|
1275
|
-
return net.address;
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
return "127.0.0.1";
|
|
1280
|
-
}
|
|
1281
1398
|
|
|
1282
1399
|
// src/typedswitch/index.ts
|
|
1283
1400
|
function runSwitch(value, keyOrCases, casesOrDefault, defaultCase) {
|
|
@@ -1935,9 +2052,10 @@ export {
|
|
|
1935
2052
|
normalizeSingleOrArray,
|
|
1936
2053
|
logOperationSummary,
|
|
1937
2054
|
logBanner,
|
|
2055
|
+
loadInfisicalSecrets,
|
|
1938
2056
|
linearRegressionTrend,
|
|
1939
2057
|
keyBy,
|
|
1940
|
-
|
|
2058
|
+
jwtVerify,
|
|
1941
2059
|
jwtSign,
|
|
1942
2060
|
jsonToZodSchemaMapper,
|
|
1943
2061
|
isSuccess,
|
|
@@ -1957,13 +2075,11 @@ export {
|
|
|
1957
2075
|
findArrayDifferenceByKey,
|
|
1958
2076
|
extractNumber,
|
|
1959
2077
|
defineSecretSet,
|
|
1960
|
-
decompressString,
|
|
1961
2078
|
createZId,
|
|
1962
2079
|
createResolveColorsByKeys,
|
|
1963
2080
|
createCoolLogger,
|
|
1964
2081
|
createApiWrapper,
|
|
1965
2082
|
createApiClient,
|
|
1966
|
-
compressString,
|
|
1967
2083
|
compact,
|
|
1968
2084
|
colorize,
|
|
1969
2085
|
chunkArray,
|
package/dist/src/jwt/index.js
CHANGED
|
@@ -1,19 +1,110 @@
|
|
|
1
1
|
// src/jwt/index.ts
|
|
2
|
-
|
|
2
|
+
var BYTE_CHUNK_SIZE = 32768;
|
|
3
|
+
var textEncoder = new TextEncoder;
|
|
4
|
+
var textDecoder = new TextDecoder;
|
|
3
5
|
async function jwtSign(payload, secret) {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
+
const header = encodeJson({ alg: "HS256" });
|
|
7
|
+
const body = encodeJson({
|
|
8
|
+
...payload,
|
|
9
|
+
iat: Math.floor(Date.now() / 1000)
|
|
10
|
+
});
|
|
11
|
+
const data = `${header}.${body}`;
|
|
12
|
+
const signature = await sign(data, secret);
|
|
13
|
+
return `${data}.${encodeBase64Url(signature)}`;
|
|
6
14
|
}
|
|
7
|
-
async function
|
|
15
|
+
async function jwtVerify(token, secret) {
|
|
8
16
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
17
|
+
const parts = token.split(".");
|
|
18
|
+
if (parts.length !== 3)
|
|
19
|
+
return null;
|
|
20
|
+
const [encodedHeader, encodedPayload, encodedSignature] = parts;
|
|
21
|
+
const header = decodeJson(encodedHeader);
|
|
22
|
+
if (!isJwtHeader(header))
|
|
23
|
+
return null;
|
|
24
|
+
const data = `${encodedHeader}.${encodedPayload}`;
|
|
25
|
+
const signature = decodeBase64Url(encodedSignature);
|
|
26
|
+
const isValid = await verify(data, signature, secret);
|
|
27
|
+
if (!isValid)
|
|
28
|
+
return null;
|
|
29
|
+
const payload = decodeJson(encodedPayload);
|
|
30
|
+
if (!isJwtPayload(payload) || !isTimeValid(payload))
|
|
31
|
+
return null;
|
|
11
32
|
return payload;
|
|
12
33
|
} catch (_error) {
|
|
13
34
|
return null;
|
|
14
35
|
}
|
|
15
36
|
}
|
|
37
|
+
async function sign(data, secret) {
|
|
38
|
+
const key = await getKey(secret, ["sign"]);
|
|
39
|
+
return await getSubtleCrypto().sign("HMAC", key, encodeText(data));
|
|
40
|
+
}
|
|
41
|
+
async function verify(data, signature, secret) {
|
|
42
|
+
const key = await getKey(secret, ["verify"]);
|
|
43
|
+
return await getSubtleCrypto().verify("HMAC", key, signature, encodeText(data));
|
|
44
|
+
}
|
|
45
|
+
async function getKey(secret, usages) {
|
|
46
|
+
return await getSubtleCrypto().importKey("raw", encodeText(secret), {
|
|
47
|
+
name: "HMAC",
|
|
48
|
+
hash: "SHA-256"
|
|
49
|
+
}, false, usages);
|
|
50
|
+
}
|
|
51
|
+
function encodeJson(value) {
|
|
52
|
+
return encodeBase64Url(encodeText(JSON.stringify(value)));
|
|
53
|
+
}
|
|
54
|
+
function decodeJson(value) {
|
|
55
|
+
return JSON.parse(textDecoder.decode(decodeBase64Url(value)));
|
|
56
|
+
}
|
|
57
|
+
function encodeBase64Url(value) {
|
|
58
|
+
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
59
|
+
return btoa(bytesToBinaryString(bytes)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
|
|
60
|
+
}
|
|
61
|
+
function decodeBase64Url(value) {
|
|
62
|
+
const base64 = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
63
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
|
|
64
|
+
return binaryStringToBytes(atob(padded)).buffer;
|
|
65
|
+
}
|
|
66
|
+
function encodeText(value) {
|
|
67
|
+
return toArrayBuffer(textEncoder.encode(value));
|
|
68
|
+
}
|
|
69
|
+
function toArrayBuffer(bytes) {
|
|
70
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
71
|
+
}
|
|
72
|
+
function bytesToBinaryString(bytes) {
|
|
73
|
+
let binary = "";
|
|
74
|
+
for (let offset = 0;offset < bytes.length; offset += BYTE_CHUNK_SIZE) {
|
|
75
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + BYTE_CHUNK_SIZE));
|
|
76
|
+
}
|
|
77
|
+
return binary;
|
|
78
|
+
}
|
|
79
|
+
function binaryStringToBytes(binary) {
|
|
80
|
+
const bytes = new Uint8Array(binary.length);
|
|
81
|
+
for (let index = 0;index < binary.length; index += 1) {
|
|
82
|
+
bytes[index] = binary.charCodeAt(index);
|
|
83
|
+
}
|
|
84
|
+
return bytes;
|
|
85
|
+
}
|
|
86
|
+
function getSubtleCrypto() {
|
|
87
|
+
const subtle = globalThis.crypto?.subtle;
|
|
88
|
+
if (!subtle) {
|
|
89
|
+
throw new Error("Web Crypto is not available in this runtime");
|
|
90
|
+
}
|
|
91
|
+
return subtle;
|
|
92
|
+
}
|
|
93
|
+
function isJwtHeader(value) {
|
|
94
|
+
return typeof value === "object" && value !== null && "alg" in value && value.alg === "HS256";
|
|
95
|
+
}
|
|
96
|
+
function isJwtPayload(value) {
|
|
97
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
98
|
+
}
|
|
99
|
+
function isTimeValid(payload) {
|
|
100
|
+
const now = Math.floor(Date.now() / 1000);
|
|
101
|
+
if (typeof payload.exp === "number" && now >= payload.exp)
|
|
102
|
+
return false;
|
|
103
|
+
if (typeof payload.nbf === "number" && now < payload.nbf)
|
|
104
|
+
return false;
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
16
107
|
export {
|
|
17
|
-
|
|
108
|
+
jwtVerify,
|
|
18
109
|
jwtSign
|
|
19
110
|
};
|