@assemble-dev/shared-utils 0.1.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.
- package/LICENSE +93 -0
- package/dist/api-keys/index.cjs +90 -0
- package/dist/api-keys/index.d.cts +15 -0
- package/dist/api-keys/index.d.ts +15 -0
- package/dist/api-keys/index.js +46 -0
- package/dist/app-error-D7pnDUHg.d.cts +11 -0
- package/dist/app-error-D7pnDUHg.d.ts +11 -0
- package/dist/auth/index.cjs +202 -0
- package/dist/auth/index.d.cts +66 -0
- package/dist/auth/index.d.ts +66 -0
- package/dist/auth/index.js +155 -0
- package/dist/chunk-7TK7K2QQ.js +59 -0
- package/dist/chunk-CP7QDN2K.js +19 -0
- package/dist/chunk-HOZLAE6A.js +22 -0
- package/dist/chunk-OW6VCN32.js +17 -0
- package/dist/chunk-XDYQIW3Q.js +191 -0
- package/dist/errors/index.cjs +66 -0
- package/dist/errors/index.d.cts +6 -0
- package/dist/errors/index.d.ts +6 -0
- package/dist/errors/index.js +10 -0
- package/dist/ids/index.cjs +93 -0
- package/dist/ids/index.d.cts +13 -0
- package/dist/ids/index.d.ts +13 -0
- package/dist/ids/index.js +22 -0
- package/dist/index.cjs +388 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +102 -0
- package/dist/provider-keys/index.cjs +46 -0
- package/dist/provider-keys/index.d.cts +6 -0
- package/dist/provider-keys/index.d.ts +6 -0
- package/dist/provider-keys/index.js +12 -0
- package/dist/redaction/index.cjs +230 -0
- package/dist/redaction/index.d.cts +55 -0
- package/dist/redaction/index.d.ts +55 -0
- package/dist/redaction/index.js +32 -0
- package/package.json +78 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AppError
|
|
3
|
+
} from "../chunk-CP7QDN2K.js";
|
|
4
|
+
|
|
5
|
+
// src/auth/create-auth-middleware.ts
|
|
6
|
+
import { AppErrorCode as AppErrorCode2 } from "@assemble-dev/shared-types";
|
|
7
|
+
|
|
8
|
+
// src/auth/verify-supabase-jwt.ts
|
|
9
|
+
import { AppErrorCode } from "@assemble-dev/shared-types";
|
|
10
|
+
import { createLocalJWKSet, createRemoteJWKSet, jwtVerify } from "jose";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
var SUPABASE_AUTHENTICATED_AUDIENCE = "authenticated";
|
|
13
|
+
var SupabaseJwtUserMetadataSchema = z.object({
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
full_name: z.string().optional()
|
|
16
|
+
});
|
|
17
|
+
var SupabaseJwtClaimsSchema = z.object({
|
|
18
|
+
sub: z.string().min(1),
|
|
19
|
+
exp: z.number().int().positive(),
|
|
20
|
+
iss: z.string().min(1),
|
|
21
|
+
aud: z.union([z.string(), z.array(z.string())]),
|
|
22
|
+
email: z.string().email().optional(),
|
|
23
|
+
user_metadata: SupabaseJwtUserMetadataSchema.optional()
|
|
24
|
+
});
|
|
25
|
+
function createSupabaseRemoteKeySet(jwksUrl) {
|
|
26
|
+
return createRemoteJWKSet(new URL(jwksUrl));
|
|
27
|
+
}
|
|
28
|
+
function createSupabaseLocalKeySet(jwks) {
|
|
29
|
+
return createLocalJWKSet(jwks);
|
|
30
|
+
}
|
|
31
|
+
function createUnauthorizedError(message) {
|
|
32
|
+
return new AppError({
|
|
33
|
+
code: AppErrorCode.Unauthorized,
|
|
34
|
+
message,
|
|
35
|
+
statusCode: 401
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
async function verifySignedTokenPayload(input) {
|
|
39
|
+
try {
|
|
40
|
+
const verification = await jwtVerify(input.token, input.keySet, {
|
|
41
|
+
issuer: input.issuer,
|
|
42
|
+
audience: input.audience ?? SUPABASE_AUTHENTICATED_AUDIENCE,
|
|
43
|
+
algorithms: ["ES256", "RS256"],
|
|
44
|
+
currentDate: input.now === void 0 ? void 0 : new Date(input.now)
|
|
45
|
+
});
|
|
46
|
+
const parsedClaims = SupabaseJwtClaimsSchema.safeParse(verification.payload);
|
|
47
|
+
if (!parsedClaims.success) {
|
|
48
|
+
throw createUnauthorizedError("Invalid authentication token claims");
|
|
49
|
+
}
|
|
50
|
+
return parsedClaims.data;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof AppError) {
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
throw createUnauthorizedError("Invalid authentication token");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function ensureAudienceIsAuthenticated(claims, expectedAudience) {
|
|
59
|
+
const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
|
|
60
|
+
if (!audiences.includes(expectedAudience)) {
|
|
61
|
+
throw createUnauthorizedError("Authentication token audience is not trusted");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function verifySupabaseJwt(input) {
|
|
65
|
+
const claims = await verifySignedTokenPayload(input);
|
|
66
|
+
ensureAudienceIsAuthenticated(
|
|
67
|
+
claims,
|
|
68
|
+
input.audience ?? SUPABASE_AUTHENTICATED_AUDIENCE
|
|
69
|
+
);
|
|
70
|
+
return claims;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/auth/create-auth-middleware.ts
|
|
74
|
+
function createUnauthorizedError2(message) {
|
|
75
|
+
return new AppError({
|
|
76
|
+
code: AppErrorCode2.Unauthorized,
|
|
77
|
+
message,
|
|
78
|
+
statusCode: 401
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function extractBearerToken(headerValue) {
|
|
82
|
+
if (headerValue === void 0) {
|
|
83
|
+
throw createUnauthorizedError2("Missing Authorization header");
|
|
84
|
+
}
|
|
85
|
+
const bearerPrefix = "Bearer ";
|
|
86
|
+
if (!headerValue.startsWith(bearerPrefix)) {
|
|
87
|
+
throw createUnauthorizedError2("Invalid Authorization header");
|
|
88
|
+
}
|
|
89
|
+
const token = headerValue.slice(bearerPrefix.length).trim();
|
|
90
|
+
if (token.length === 0) {
|
|
91
|
+
throw createUnauthorizedError2("Missing bearer token");
|
|
92
|
+
}
|
|
93
|
+
return token;
|
|
94
|
+
}
|
|
95
|
+
function buildAuthSubjectFromClaims(claims) {
|
|
96
|
+
if (claims.email === void 0) {
|
|
97
|
+
throw createUnauthorizedError2("Authentication token is missing an email");
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
id: claims.sub,
|
|
101
|
+
email: claims.email,
|
|
102
|
+
name: claims.user_metadata?.name ?? claims.user_metadata?.full_name ?? null
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function createAuthMiddleware(config) {
|
|
106
|
+
return async (c, next) => {
|
|
107
|
+
const token = extractBearerToken(c.req.header("authorization"));
|
|
108
|
+
const claims = await verifySupabaseJwt({
|
|
109
|
+
token,
|
|
110
|
+
keySet: config.keySet,
|
|
111
|
+
issuer: config.issuer,
|
|
112
|
+
audience: config.audience
|
|
113
|
+
});
|
|
114
|
+
const subject = buildAuthSubjectFromClaims(claims);
|
|
115
|
+
const user = await config.resolveAuthUser(subject);
|
|
116
|
+
c.set("auth", { userId: user.id, authSubjectId: claims.sub });
|
|
117
|
+
await next();
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/auth/known-test-credentials.ts
|
|
122
|
+
import { createHash } from "node:crypto";
|
|
123
|
+
import { AppErrorCode as AppErrorCode3 } from "@assemble-dev/shared-types";
|
|
124
|
+
var KNOWN_TEST_CREDENTIAL_FINGERPRINTS = [
|
|
125
|
+
"56e17db07a352344ec8f8d91b98d65ee78213181872b8a66e41087442bb33f5b",
|
|
126
|
+
"39377069fcfbfbe509b540b9005998930d1d847a86776b560998a5cf958ba4d6"
|
|
127
|
+
];
|
|
128
|
+
function computeCredentialFingerprint(value) {
|
|
129
|
+
return createHash("sha256").update(value.trim()).digest("hex");
|
|
130
|
+
}
|
|
131
|
+
function assertNotTestCredentialInProduction(input) {
|
|
132
|
+
if (input.nodeEnv !== "production") {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const fingerprint = computeCredentialFingerprint(input.value);
|
|
136
|
+
if (KNOWN_TEST_CREDENTIAL_FINGERPRINTS.includes(fingerprint)) {
|
|
137
|
+
throw new AppError({
|
|
138
|
+
code: AppErrorCode3.InternalServerError,
|
|
139
|
+
message: `${input.label} is set to a committed test credential and must be replaced with a real production value`,
|
|
140
|
+
statusCode: 500,
|
|
141
|
+
details: { label: input.label }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
SUPABASE_AUTHENTICATED_AUDIENCE,
|
|
147
|
+
SupabaseJwtClaimsSchema,
|
|
148
|
+
assertNotTestCredentialInProduction,
|
|
149
|
+
buildAuthSubjectFromClaims,
|
|
150
|
+
computeCredentialFingerprint,
|
|
151
|
+
createAuthMiddleware,
|
|
152
|
+
createSupabaseLocalKeySet,
|
|
153
|
+
createSupabaseRemoteKeySet,
|
|
154
|
+
verifySupabaseJwt
|
|
155
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/ids/generate-id.ts
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
var idPrefixes = [
|
|
4
|
+
"run_",
|
|
5
|
+
"evt_",
|
|
6
|
+
"key_",
|
|
7
|
+
"eval_",
|
|
8
|
+
"apr_"
|
|
9
|
+
];
|
|
10
|
+
var suffixByteLength = 16;
|
|
11
|
+
var suffixHexLength = suffixByteLength * 2;
|
|
12
|
+
var suffixPattern = /^[0-9a-f]+$/;
|
|
13
|
+
function generateRandomSuffix() {
|
|
14
|
+
return randomBytes(suffixByteLength).toString("hex");
|
|
15
|
+
}
|
|
16
|
+
function generatePrefixedId(prefix) {
|
|
17
|
+
return `${prefix}${generateRandomSuffix()}`;
|
|
18
|
+
}
|
|
19
|
+
function generateRunId() {
|
|
20
|
+
return generatePrefixedId("run_");
|
|
21
|
+
}
|
|
22
|
+
function generateTraceEventId() {
|
|
23
|
+
return generatePrefixedId("evt_");
|
|
24
|
+
}
|
|
25
|
+
function generateApiKeyId() {
|
|
26
|
+
return generatePrefixedId("key_");
|
|
27
|
+
}
|
|
28
|
+
function generateEvalRunId() {
|
|
29
|
+
return generatePrefixedId("eval_");
|
|
30
|
+
}
|
|
31
|
+
function generateApprovalId() {
|
|
32
|
+
return generatePrefixedId("apr_");
|
|
33
|
+
}
|
|
34
|
+
function parseIdPrefix(id) {
|
|
35
|
+
const matchedPrefix = idPrefixes.find((prefix) => id.startsWith(prefix));
|
|
36
|
+
return matchedPrefix ?? null;
|
|
37
|
+
}
|
|
38
|
+
function hasIdPrefix(id, prefix) {
|
|
39
|
+
return id.startsWith(prefix);
|
|
40
|
+
}
|
|
41
|
+
function isValidPrefixedId(id, prefix) {
|
|
42
|
+
if (!id.startsWith(prefix)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
const suffix = id.slice(prefix.length);
|
|
46
|
+
return suffix.length === suffixHexLength && suffixPattern.test(suffix);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
idPrefixes,
|
|
51
|
+
generateRunId,
|
|
52
|
+
generateTraceEventId,
|
|
53
|
+
generateApiKeyId,
|
|
54
|
+
generateEvalRunId,
|
|
55
|
+
generateApprovalId,
|
|
56
|
+
parseIdPrefix,
|
|
57
|
+
hasIdPrefix,
|
|
58
|
+
isValidPrefixedId
|
|
59
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/errors/app-error.ts
|
|
2
|
+
var AppError = class extends Error {
|
|
3
|
+
code;
|
|
4
|
+
statusCode;
|
|
5
|
+
details;
|
|
6
|
+
cause;
|
|
7
|
+
constructor(input) {
|
|
8
|
+
super(input.message);
|
|
9
|
+
this.name = "AppError";
|
|
10
|
+
this.code = input.code;
|
|
11
|
+
this.statusCode = input.statusCode;
|
|
12
|
+
this.details = input.details;
|
|
13
|
+
this.cause = input.cause;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
AppError
|
|
19
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/errors/handle-app-error.ts
|
|
2
|
+
function createAppErrorResponse(error) {
|
|
3
|
+
if (error.details === void 0) {
|
|
4
|
+
return {
|
|
5
|
+
error: {
|
|
6
|
+
code: error.code,
|
|
7
|
+
message: error.message
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
error: {
|
|
13
|
+
code: error.code,
|
|
14
|
+
message: error.message,
|
|
15
|
+
details: error.details
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
createAppErrorResponse
|
|
22
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// src/provider-keys/named-api-key-env.ts
|
|
2
|
+
var providerApiKeyNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
3
|
+
var expectedProviderApiKeyNameFormat = "a letter followed by letters, numbers, hyphens, or underscores";
|
|
4
|
+
function validateProviderApiKeyName(apiKeyName) {
|
|
5
|
+
return providerApiKeyNamePattern.test(apiKeyName);
|
|
6
|
+
}
|
|
7
|
+
function buildNamedApiKeyEnvVarName(baseEnvVarName, apiKeyName) {
|
|
8
|
+
const normalizedName = apiKeyName.toUpperCase().replaceAll("-", "_");
|
|
9
|
+
return `${baseEnvVarName}_${normalizedName}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
providerApiKeyNamePattern,
|
|
14
|
+
expectedProviderApiKeyNameFormat,
|
|
15
|
+
validateProviderApiKeyName,
|
|
16
|
+
buildNamedApiKeyEnvVarName
|
|
17
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// src/redaction/redaction-mode.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var RedactionModeSchema = z.enum([
|
|
4
|
+
"metadata-only",
|
|
5
|
+
"redacted",
|
|
6
|
+
"full"
|
|
7
|
+
]);
|
|
8
|
+
var defaultRedactionMode = "redacted";
|
|
9
|
+
function resolveRedactionMode(mode) {
|
|
10
|
+
return mode ?? defaultRedactionMode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/redaction/secret-detection.ts
|
|
14
|
+
var redactedValue = "[REDACTED]";
|
|
15
|
+
var providerApiKeyPrefixes = [
|
|
16
|
+
"sk-",
|
|
17
|
+
"sk_live_",
|
|
18
|
+
"sk_test_",
|
|
19
|
+
"asm_",
|
|
20
|
+
"ghp_",
|
|
21
|
+
"gho_",
|
|
22
|
+
"ghs_",
|
|
23
|
+
"ghu_",
|
|
24
|
+
"github_pat_",
|
|
25
|
+
"xoxb-",
|
|
26
|
+
"xoxp-",
|
|
27
|
+
"xoxa-",
|
|
28
|
+
"xoxs-",
|
|
29
|
+
"pk_live_",
|
|
30
|
+
"rk_live_",
|
|
31
|
+
"whsec_",
|
|
32
|
+
"AKIA"
|
|
33
|
+
];
|
|
34
|
+
var secretBodyPattern = /^[A-Za-z0-9+/=_.-]{12,}$/;
|
|
35
|
+
var bearerTokenPattern = /^bearer\s+\S+/i;
|
|
36
|
+
var jwtShapedPattern = /^eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/;
|
|
37
|
+
var secretFieldNameSuffixes = [
|
|
38
|
+
"api_key",
|
|
39
|
+
"apikey",
|
|
40
|
+
"secret",
|
|
41
|
+
"token",
|
|
42
|
+
"password"
|
|
43
|
+
];
|
|
44
|
+
function escapeRegExpLiteral(literal) {
|
|
45
|
+
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
46
|
+
}
|
|
47
|
+
function buildProviderApiKeySubstringPattern() {
|
|
48
|
+
const escapedPrefixes = providerApiKeyPrefixes.map(escapeRegExpLiteral).join("|");
|
|
49
|
+
return new RegExp(`(?:${escapedPrefixes})[A-Za-z0-9+/=_.-]{12,}`, "g");
|
|
50
|
+
}
|
|
51
|
+
var providerApiKeySubstringPattern = buildProviderApiKeySubstringPattern();
|
|
52
|
+
var bearerTokenSubstringPattern = /bearer\s+\S+/gi;
|
|
53
|
+
var jwtShapedSubstringPattern = /eyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}[A-Za-z0-9_.-]*/g;
|
|
54
|
+
function replaceProviderApiKeySubstrings(value) {
|
|
55
|
+
return value.replace(providerApiKeySubstringPattern, redactedValue);
|
|
56
|
+
}
|
|
57
|
+
function replaceSecretShapedSubstrings(value) {
|
|
58
|
+
return replaceProviderApiKeySubstrings(value).replace(bearerTokenSubstringPattern, redactedValue).replace(jwtShapedSubstringPattern, redactedValue);
|
|
59
|
+
}
|
|
60
|
+
function isProviderApiKeyShapedString(value) {
|
|
61
|
+
return providerApiKeyPrefixes.some(
|
|
62
|
+
(prefix) => value.startsWith(prefix) && secretBodyPattern.test(value.slice(prefix.length))
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function isBearerTokenShapedString(value) {
|
|
66
|
+
return bearerTokenPattern.test(value);
|
|
67
|
+
}
|
|
68
|
+
function isJwtShapedString(value) {
|
|
69
|
+
return jwtShapedPattern.test(value);
|
|
70
|
+
}
|
|
71
|
+
function isSecretShapedString(value) {
|
|
72
|
+
return isProviderApiKeyShapedString(value) || isBearerTokenShapedString(value) || isJwtShapedString(value);
|
|
73
|
+
}
|
|
74
|
+
function isSecretNamedField(fieldName) {
|
|
75
|
+
const normalizedFieldName = fieldName.toLowerCase();
|
|
76
|
+
if (normalizedFieldName === "authorization") {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return secretFieldNameSuffixes.some(
|
|
80
|
+
(suffix) => normalizedFieldName.endsWith(suffix)
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/redaction/summarize-json-value.ts
|
|
85
|
+
function summarizeJsonValue(value) {
|
|
86
|
+
if (value === null) {
|
|
87
|
+
return { kind: "null" };
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === "string") {
|
|
90
|
+
return { kind: "string", length: value.length };
|
|
91
|
+
}
|
|
92
|
+
if (typeof value === "number") {
|
|
93
|
+
return { kind: "number" };
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === "boolean") {
|
|
96
|
+
return { kind: "boolean" };
|
|
97
|
+
}
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return { kind: "array", length: value.length };
|
|
100
|
+
}
|
|
101
|
+
return { kind: "object", keys: Object.keys(value) };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/redaction/redact-trace-payload.ts
|
|
105
|
+
import { z as z2 } from "zod";
|
|
106
|
+
var RedactTracePayloadOptionsSchema = z2.object({
|
|
107
|
+
mode: RedactionModeSchema.optional(),
|
|
108
|
+
sensitiveFieldNames: z2.array(z2.string()).optional()
|
|
109
|
+
});
|
|
110
|
+
function redactTracePayload(payload, options) {
|
|
111
|
+
const mode = resolveRedactionMode(options?.mode);
|
|
112
|
+
if (mode === "metadata-only") {
|
|
113
|
+
return { payload: summarizeJsonValue(payload), redacted: true };
|
|
114
|
+
}
|
|
115
|
+
const context = {
|
|
116
|
+
mode,
|
|
117
|
+
sensitiveFieldNames: buildSensitiveFieldNameSet(
|
|
118
|
+
options?.sensitiveFieldNames
|
|
119
|
+
),
|
|
120
|
+
redactedValueCount: 0
|
|
121
|
+
};
|
|
122
|
+
const redactedPayload = redactJsonValue(payload, context);
|
|
123
|
+
return {
|
|
124
|
+
payload: redactedPayload,
|
|
125
|
+
redacted: mode !== "full" || context.redactedValueCount > 0
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function buildSensitiveFieldNameSet(fieldNames) {
|
|
129
|
+
return new Set(
|
|
130
|
+
(fieldNames ?? []).map((fieldName) => fieldName.toLowerCase())
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
function redactJsonValue(value, context) {
|
|
134
|
+
if (typeof value === "string") {
|
|
135
|
+
return redactStringValue(value, context);
|
|
136
|
+
}
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
return value.map((item) => redactJsonValue(item, context));
|
|
139
|
+
}
|
|
140
|
+
if (value !== null && typeof value === "object") {
|
|
141
|
+
return redactObjectValue(value, context);
|
|
142
|
+
}
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
function redactStringValue(value, context) {
|
|
146
|
+
const shouldRedactWholeString = context.mode === "full" ? isProviderApiKeyShapedString(value) : isSecretShapedString(value);
|
|
147
|
+
if (shouldRedactWholeString) {
|
|
148
|
+
context.redactedValueCount += 1;
|
|
149
|
+
return redactedValue;
|
|
150
|
+
}
|
|
151
|
+
const valueWithSecretsReplaced = context.mode === "full" ? replaceProviderApiKeySubstrings(value) : replaceSecretShapedSubstrings(value);
|
|
152
|
+
if (valueWithSecretsReplaced !== value) {
|
|
153
|
+
context.redactedValueCount += 1;
|
|
154
|
+
}
|
|
155
|
+
return valueWithSecretsReplaced;
|
|
156
|
+
}
|
|
157
|
+
function redactObjectValue(value, context) {
|
|
158
|
+
const redactedEntries = Object.entries(value).map(
|
|
159
|
+
([fieldName, fieldValue]) => {
|
|
160
|
+
if (shouldRedactFieldValue(fieldName, context)) {
|
|
161
|
+
context.redactedValueCount += 1;
|
|
162
|
+
return [fieldName, redactedValue];
|
|
163
|
+
}
|
|
164
|
+
return [fieldName, redactJsonValue(fieldValue, context)];
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
return Object.fromEntries(redactedEntries);
|
|
168
|
+
}
|
|
169
|
+
function shouldRedactFieldValue(fieldName, context) {
|
|
170
|
+
if (context.sensitiveFieldNames.has(fieldName.toLowerCase())) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return context.mode === "redacted" && isSecretNamedField(fieldName);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export {
|
|
177
|
+
RedactionModeSchema,
|
|
178
|
+
defaultRedactionMode,
|
|
179
|
+
resolveRedactionMode,
|
|
180
|
+
redactedValue,
|
|
181
|
+
replaceProviderApiKeySubstrings,
|
|
182
|
+
replaceSecretShapedSubstrings,
|
|
183
|
+
isProviderApiKeyShapedString,
|
|
184
|
+
isBearerTokenShapedString,
|
|
185
|
+
isJwtShapedString,
|
|
186
|
+
isSecretShapedString,
|
|
187
|
+
isSecretNamedField,
|
|
188
|
+
summarizeJsonValue,
|
|
189
|
+
RedactTracePayloadOptionsSchema,
|
|
190
|
+
redactTracePayload
|
|
191
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/errors/index.ts
|
|
21
|
+
var errors_exports = {};
|
|
22
|
+
__export(errors_exports, {
|
|
23
|
+
AppError: () => AppError,
|
|
24
|
+
createAppErrorResponse: () => createAppErrorResponse
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(errors_exports);
|
|
27
|
+
|
|
28
|
+
// src/errors/app-error.ts
|
|
29
|
+
var AppError = class extends Error {
|
|
30
|
+
code;
|
|
31
|
+
statusCode;
|
|
32
|
+
details;
|
|
33
|
+
cause;
|
|
34
|
+
constructor(input) {
|
|
35
|
+
super(input.message);
|
|
36
|
+
this.name = "AppError";
|
|
37
|
+
this.code = input.code;
|
|
38
|
+
this.statusCode = input.statusCode;
|
|
39
|
+
this.details = input.details;
|
|
40
|
+
this.cause = input.cause;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/errors/handle-app-error.ts
|
|
45
|
+
function createAppErrorResponse(error) {
|
|
46
|
+
if (error.details === void 0) {
|
|
47
|
+
return {
|
|
48
|
+
error: {
|
|
49
|
+
code: error.code,
|
|
50
|
+
message: error.message
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
error: {
|
|
56
|
+
code: error.code,
|
|
57
|
+
message: error.message,
|
|
58
|
+
details: error.details
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
63
|
+
0 && (module.exports = {
|
|
64
|
+
AppError,
|
|
65
|
+
createAppErrorResponse
|
|
66
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ids/index.ts
|
|
21
|
+
var ids_exports = {};
|
|
22
|
+
__export(ids_exports, {
|
|
23
|
+
generateApiKeyId: () => generateApiKeyId,
|
|
24
|
+
generateApprovalId: () => generateApprovalId,
|
|
25
|
+
generateEvalRunId: () => generateEvalRunId,
|
|
26
|
+
generateRunId: () => generateRunId,
|
|
27
|
+
generateTraceEventId: () => generateTraceEventId,
|
|
28
|
+
hasIdPrefix: () => hasIdPrefix,
|
|
29
|
+
idPrefixes: () => idPrefixes,
|
|
30
|
+
isValidPrefixedId: () => isValidPrefixedId,
|
|
31
|
+
parseIdPrefix: () => parseIdPrefix
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(ids_exports);
|
|
34
|
+
|
|
35
|
+
// src/ids/generate-id.ts
|
|
36
|
+
var import_node_crypto = require("node:crypto");
|
|
37
|
+
var idPrefixes = [
|
|
38
|
+
"run_",
|
|
39
|
+
"evt_",
|
|
40
|
+
"key_",
|
|
41
|
+
"eval_",
|
|
42
|
+
"apr_"
|
|
43
|
+
];
|
|
44
|
+
var suffixByteLength = 16;
|
|
45
|
+
var suffixHexLength = suffixByteLength * 2;
|
|
46
|
+
var suffixPattern = /^[0-9a-f]+$/;
|
|
47
|
+
function generateRandomSuffix() {
|
|
48
|
+
return (0, import_node_crypto.randomBytes)(suffixByteLength).toString("hex");
|
|
49
|
+
}
|
|
50
|
+
function generatePrefixedId(prefix) {
|
|
51
|
+
return `${prefix}${generateRandomSuffix()}`;
|
|
52
|
+
}
|
|
53
|
+
function generateRunId() {
|
|
54
|
+
return generatePrefixedId("run_");
|
|
55
|
+
}
|
|
56
|
+
function generateTraceEventId() {
|
|
57
|
+
return generatePrefixedId("evt_");
|
|
58
|
+
}
|
|
59
|
+
function generateApiKeyId() {
|
|
60
|
+
return generatePrefixedId("key_");
|
|
61
|
+
}
|
|
62
|
+
function generateEvalRunId() {
|
|
63
|
+
return generatePrefixedId("eval_");
|
|
64
|
+
}
|
|
65
|
+
function generateApprovalId() {
|
|
66
|
+
return generatePrefixedId("apr_");
|
|
67
|
+
}
|
|
68
|
+
function parseIdPrefix(id) {
|
|
69
|
+
const matchedPrefix = idPrefixes.find((prefix) => id.startsWith(prefix));
|
|
70
|
+
return matchedPrefix ?? null;
|
|
71
|
+
}
|
|
72
|
+
function hasIdPrefix(id, prefix) {
|
|
73
|
+
return id.startsWith(prefix);
|
|
74
|
+
}
|
|
75
|
+
function isValidPrefixedId(id, prefix) {
|
|
76
|
+
if (!id.startsWith(prefix)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const suffix = id.slice(prefix.length);
|
|
80
|
+
return suffix.length === suffixHexLength && suffixPattern.test(suffix);
|
|
81
|
+
}
|
|
82
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
83
|
+
0 && (module.exports = {
|
|
84
|
+
generateApiKeyId,
|
|
85
|
+
generateApprovalId,
|
|
86
|
+
generateEvalRunId,
|
|
87
|
+
generateRunId,
|
|
88
|
+
generateTraceEventId,
|
|
89
|
+
hasIdPrefix,
|
|
90
|
+
idPrefixes,
|
|
91
|
+
isValidPrefixedId,
|
|
92
|
+
parseIdPrefix
|
|
93
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type IdPrefix = "run_" | "evt_" | "key_" | "eval_" | "apr_";
|
|
2
|
+
type PrefixedId<TPrefix extends IdPrefix> = `${TPrefix}${string}`;
|
|
3
|
+
declare const idPrefixes: readonly IdPrefix[];
|
|
4
|
+
declare function generateRunId(): PrefixedId<"run_">;
|
|
5
|
+
declare function generateTraceEventId(): PrefixedId<"evt_">;
|
|
6
|
+
declare function generateApiKeyId(): PrefixedId<"key_">;
|
|
7
|
+
declare function generateEvalRunId(): PrefixedId<"eval_">;
|
|
8
|
+
declare function generateApprovalId(): PrefixedId<"apr_">;
|
|
9
|
+
declare function parseIdPrefix(id: string): IdPrefix | null;
|
|
10
|
+
declare function hasIdPrefix<TPrefix extends IdPrefix>(id: string, prefix: TPrefix): id is PrefixedId<TPrefix>;
|
|
11
|
+
declare function isValidPrefixedId<TPrefix extends IdPrefix>(id: string, prefix: TPrefix): id is PrefixedId<TPrefix>;
|
|
12
|
+
|
|
13
|
+
export { type IdPrefix, type PrefixedId, generateApiKeyId, generateApprovalId, generateEvalRunId, generateRunId, generateTraceEventId, hasIdPrefix, idPrefixes, isValidPrefixedId, parseIdPrefix };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type IdPrefix = "run_" | "evt_" | "key_" | "eval_" | "apr_";
|
|
2
|
+
type PrefixedId<TPrefix extends IdPrefix> = `${TPrefix}${string}`;
|
|
3
|
+
declare const idPrefixes: readonly IdPrefix[];
|
|
4
|
+
declare function generateRunId(): PrefixedId<"run_">;
|
|
5
|
+
declare function generateTraceEventId(): PrefixedId<"evt_">;
|
|
6
|
+
declare function generateApiKeyId(): PrefixedId<"key_">;
|
|
7
|
+
declare function generateEvalRunId(): PrefixedId<"eval_">;
|
|
8
|
+
declare function generateApprovalId(): PrefixedId<"apr_">;
|
|
9
|
+
declare function parseIdPrefix(id: string): IdPrefix | null;
|
|
10
|
+
declare function hasIdPrefix<TPrefix extends IdPrefix>(id: string, prefix: TPrefix): id is PrefixedId<TPrefix>;
|
|
11
|
+
declare function isValidPrefixedId<TPrefix extends IdPrefix>(id: string, prefix: TPrefix): id is PrefixedId<TPrefix>;
|
|
12
|
+
|
|
13
|
+
export { type IdPrefix, type PrefixedId, generateApiKeyId, generateApprovalId, generateEvalRunId, generateRunId, generateTraceEventId, hasIdPrefix, idPrefixes, isValidPrefixedId, parseIdPrefix };
|