@arcblock/did-connect-service 4.0.5 → 4.0.7
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/assets/fonts/noto-sans-sc-regular.otf +0 -0
- package/dist/_generated/asset-bytes.d.ts +3 -0
- package/dist/_generated/asset-bytes.d.ts.map +1 -0
- package/dist/_generated/asset-bytes.js +2 -0
- package/dist/_generated/asset-bytes.js.map +1 -0
- package/dist/_generated/asset-manifest.d.ts +3 -0
- package/dist/_generated/asset-manifest.d.ts.map +1 -0
- package/dist/_generated/asset-manifest.js +12 -0
- package/dist/_generated/asset-manifest.js.map +1 -0
- package/dist/asset-registry.d.ts +38 -0
- package/dist/asset-registry.d.ts.map +1 -0
- package/dist/asset-registry.js +73 -0
- package/dist/asset-registry.js.map +1 -0
- package/dist/assets/admin-core.c0b5af61.js +1393 -0
- package/dist/assets/admin-extra.7ca9c16b.js +2529 -0
- package/dist/assets/admin.c26bb17a.css +2219 -0
- package/dist/assets/design.99dc4ddc.css +97 -0
- package/dist/assets/did-address.7df30f28.js +51 -0
- package/dist/assets/header.94d9e46b.js +136 -0
- package/dist/assets/login.7b12c6dc.css +662 -0
- package/dist/assets/login.d3f05790.js +720 -0
- package/dist/assets/qr.c0d203ca.js +3 -0
- package/dist/embedded.d.ts +32 -0
- package/dist/embedded.d.ts.map +1 -1
- package/dist/embedded.js +3 -0
- package/dist/embedded.js.map +1 -1
- package/dist/handlers/auth-handler.d.ts +5 -0
- package/dist/handlers/auth-handler.d.ts.map +1 -1
- package/dist/handlers/auth-handler.js +11 -32
- package/dist/handlers/auth-handler.js.map +1 -1
- package/dist/handlers/branding-handler.d.ts +17 -0
- package/dist/handlers/branding-handler.d.ts.map +1 -1
- package/dist/handlers/branding-handler.js +107 -5
- package/dist/handlers/branding-handler.js.map +1 -1
- package/dist/identity/gravatar.d.ts +0 -2
- package/dist/identity/gravatar.d.ts.map +1 -1
- package/dist/identity/gravatar.js +0 -9
- package/dist/identity/gravatar.js.map +1 -1
- package/dist/og/emoji.d.ts +12 -0
- package/dist/og/emoji.d.ts.map +1 -0
- package/dist/og/emoji.js +71 -0
- package/dist/og/emoji.js.map +1 -0
- package/dist/og/generator.d.ts +3 -0
- package/dist/og/generator.d.ts.map +1 -0
- package/dist/og/generator.js +338 -0
- package/dist/og/generator.js.map +1 -0
- package/dist/og/index.d.ts +6 -0
- package/dist/og/index.d.ts.map +1 -0
- package/dist/og/index.js +4 -0
- package/dist/og/index.js.map +1 -0
- package/dist/og/passport-svg.d.ts +52 -0
- package/dist/og/passport-svg.d.ts.map +1 -0
- package/dist/og/passport-svg.js +157 -0
- package/dist/og/passport-svg.js.map +1 -0
- package/dist/og/ssrf-guard.d.ts +38 -0
- package/dist/og/ssrf-guard.d.ts.map +1 -0
- package/dist/og/ssrf-guard.js +188 -0
- package/dist/og/ssrf-guard.js.map +1 -0
- package/dist/og/templates.d.ts +26 -0
- package/dist/og/templates.d.ts.map +1 -0
- package/dist/og/templates.js +302 -0
- package/dist/og/templates.js.map +1 -0
- package/dist/og/types.d.ts +74 -0
- package/dist/og/types.d.ts.map +1 -0
- package/dist/og/types.js +14 -0
- package/dist/og/types.js.map +1 -0
- package/dist/pages/admin/index.d.ts.map +1 -1
- package/dist/pages/admin/index.js +25 -41
- package/dist/pages/admin/index.js.map +1 -1
- package/dist/pages/admin/tab-access.d.ts +1 -1
- package/dist/pages/admin/tab-access.d.ts.map +1 -1
- package/dist/pages/admin/tab-access.js +5 -2
- package/dist/pages/admin/tab-access.js.map +1 -1
- package/dist/pages/admin/tab-appearance.d.ts +1 -1
- package/dist/pages/admin/tab-appearance.d.ts.map +1 -1
- package/dist/pages/admin/tab-appearance.js +4 -2
- package/dist/pages/admin/tab-appearance.js.map +1 -1
- package/dist/pages/admin/tab-branding.d.ts.map +1 -1
- package/dist/pages/admin/tab-branding.js +4 -2
- package/dist/pages/admin/tab-branding.js.map +1 -1
- package/dist/pages/admin/tab-profile-accounts.d.ts.map +1 -1
- package/dist/pages/admin/tab-profile-accounts.js +4 -2
- package/dist/pages/admin/tab-profile-accounts.js.map +1 -1
- package/dist/pages/admin/tab-settings.d.ts.map +1 -1
- package/dist/pages/admin/tab-settings.js +4 -2
- package/dist/pages/admin/tab-settings.js.map +1 -1
- package/dist/pages/admin-instances-page.d.ts.map +1 -1
- package/dist/pages/admin-instances-page.js +4 -6
- package/dist/pages/admin-instances-page.js.map +1 -1
- package/dist/pages/error-page.d.ts.map +1 -1
- package/dist/pages/error-page.js +3 -2
- package/dist/pages/error-page.js.map +1 -1
- package/dist/pages/gen-access-key-page.d.ts.map +1 -1
- package/dist/pages/gen-access-key-page.js +3 -4
- package/dist/pages/gen-access-key-page.js.map +1 -1
- package/dist/pages/homepage.d.ts.map +1 -1
- package/dist/pages/homepage.js +4 -3
- package/dist/pages/homepage.js.map +1 -1
- package/dist/pages/invite-page.d.ts.map +1 -1
- package/dist/pages/invite-page.js +4 -4
- package/dist/pages/invite-page.js.map +1 -1
- package/dist/pages/login-page.d.ts.map +1 -1
- package/dist/pages/login-page.js +3 -4
- package/dist/pages/login-page.js.map +1 -1
- package/package.json +21 -5
- package/dist/access-key-handler.d.ts +0 -37
- package/dist/access-key-handler.d.ts.map +0 -1
- package/dist/access-key-handler.js +0 -316
- package/dist/access-key-handler.js.map +0 -1
- package/dist/access-key-util.d.ts +0 -19
- package/dist/access-key-util.d.ts.map +0 -1
- package/dist/access-key-util.js +0 -45
- package/dist/access-key-util.js.map +0 -1
- package/dist/access-policy.d.ts +0 -53
- package/dist/access-policy.d.ts.map +0 -1
- package/dist/access-policy.js +0 -153
- package/dist/access-policy.js.map +0 -1
- package/dist/auth-client.d.ts +0 -20
- package/dist/auth-client.d.ts.map +0 -1
- package/dist/auth-client.js +0 -42
- package/dist/auth-client.js.map +0 -1
- package/dist/auth-entrypoint.d.ts +0 -45
- package/dist/auth-entrypoint.d.ts.map +0 -1
- package/dist/auth-entrypoint.js +0 -31
- package/dist/auth-entrypoint.js.map +0 -1
- package/dist/auth-handler.d.ts +0 -136
- package/dist/auth-handler.d.ts.map +0 -1
- package/dist/auth-handler.js +0 -408
- package/dist/auth-handler.js.map +0 -1
- package/dist/auth-rpc-types.d.ts +0 -139
- package/dist/auth-rpc-types.d.ts.map +0 -1
- package/dist/auth-rpc-types.js +0 -11
- package/dist/auth-rpc-types.js.map +0 -1
- package/dist/auth-rpc.d.ts +0 -80
- package/dist/auth-rpc.d.ts.map +0 -1
- package/dist/auth-rpc.js +0 -257
- package/dist/auth-rpc.js.map +0 -1
- package/dist/auth-worker.d.ts +0 -42
- package/dist/auth-worker.d.ts.map +0 -1
- package/dist/auth-worker.js +0 -120
- package/dist/auth-worker.js.map +0 -1
- package/dist/blocklet-js-handler.d.ts +0 -22
- package/dist/blocklet-js-handler.d.ts.map +0 -1
- package/dist/blocklet-js-handler.js +0 -205
- package/dist/blocklet-js-handler.js.map +0 -1
- package/dist/branding-handler.d.ts +0 -42
- package/dist/branding-handler.d.ts.map +0 -1
- package/dist/branding-handler.js +0 -326
- package/dist/branding-handler.js.map +0 -1
- package/dist/d1-token-storage.d.ts +0 -31
- package/dist/d1-token-storage.d.ts.map +0 -1
- package/dist/d1-token-storage.js +0 -83
- package/dist/d1-token-storage.js.map +0 -1
- package/dist/did-connect-handler.d.ts +0 -57
- package/dist/did-connect-handler.d.ts.map +0 -1
- package/dist/did-connect-handler.js +0 -182
- package/dist/did-connect-handler.js.map +0 -1
- package/dist/did.d.ts +0 -14
- package/dist/did.d.ts.map +0 -1
- package/dist/did.js +0 -17
- package/dist/did.js.map +0 -1
- package/dist/email-login-handler.d.ts +0 -50
- package/dist/email-login-handler.d.ts.map +0 -1
- package/dist/email-login-handler.js +0 -238
- package/dist/email-login-handler.js.map +0 -1
- package/dist/federation-utils.d.ts +0 -23
- package/dist/federation-utils.d.ts.map +0 -1
- package/dist/federation-utils.js +0 -25
- package/dist/federation-utils.js.map +0 -1
- package/dist/handler.d.ts +0 -90
- package/dist/handler.d.ts.map +0 -1
- package/dist/handler.js +0 -591
- package/dist/handler.js.map +0 -1
- package/dist/identity/csrf.d.ts +0 -17
- package/dist/identity/csrf.d.ts.map +0 -1
- package/dist/identity/csrf.js +0 -56
- package/dist/identity/csrf.js.map +0 -1
- package/dist/identity/invitation-util.d.ts +0 -7
- package/dist/identity/invitation-util.d.ts.map +0 -1
- package/dist/identity/invitation-util.js +0 -66
- package/dist/identity/invitation-util.js.map +0 -1
- package/dist/instance-role.d.ts +0 -10
- package/dist/instance-role.d.ts.map +0 -1
- package/dist/instance-role.js +0 -20
- package/dist/instance-role.js.map +0 -1
- package/dist/jwt.d.ts +0 -7
- package/dist/jwt.d.ts.map +0 -1
- package/dist/jwt.js +0 -72
- package/dist/jwt.js.map +0 -1
- package/dist/login-entry.d.ts +0 -9
- package/dist/login-entry.d.ts.map +0 -1
- package/dist/login-entry.js +0 -9
- package/dist/login-entry.js.map +0 -1
- package/dist/membership-handler.d.ts +0 -27
- package/dist/membership-handler.d.ts.map +0 -1
- package/dist/membership-handler.js +0 -111
- package/dist/membership-handler.js.map +0 -1
- package/dist/oauth-callback-page.d.ts +0 -9
- package/dist/oauth-callback-page.d.ts.map +0 -1
- package/dist/oauth-callback-page.js +0 -31
- package/dist/oauth-callback-page.js.map +0 -1
- package/dist/oauth-handler.d.ts +0 -72
- package/dist/oauth-handler.d.ts.map +0 -1
- package/dist/oauth-handler.js +0 -423
- package/dist/oauth-handler.js.map +0 -1
- package/dist/page.d.ts +0 -33
- package/dist/page.d.ts.map +0 -1
- package/dist/page.js +0 -59
- package/dist/page.js.map +0 -1
- package/dist/pages/auth-script.d.ts +0 -18
- package/dist/pages/auth-script.d.ts.map +0 -1
- package/dist/pages/auth-script.js +0 -185
- package/dist/pages/auth-script.js.map +0 -1
- package/dist/pages/design-tokens.d.ts +0 -86
- package/dist/pages/design-tokens.d.ts.map +0 -1
- package/dist/pages/design-tokens.js +0 -159
- package/dist/pages/design-tokens.js.map +0 -1
- package/dist/pages/did-connect-script.d.ts +0 -16
- package/dist/pages/did-connect-script.d.ts.map +0 -1
- package/dist/pages/did-connect-script.js +0 -105
- package/dist/pages/did-connect-script.js.map +0 -1
- package/dist/pages/shared-styles.d.ts +0 -6
- package/dist/pages/shared-styles.d.ts.map +0 -1
- package/dist/pages/shared-styles.js +0 -109
- package/dist/pages/shared-styles.js.map +0 -1
- package/dist/rbac.d.ts +0 -19
- package/dist/rbac.d.ts.map +0 -1
- package/dist/rbac.js +0 -76
- package/dist/rbac.js.map +0 -1
- package/dist/session-context.d.ts +0 -35
- package/dist/session-context.d.ts.map +0 -1
- package/dist/session-context.js +0 -39
- package/dist/session-context.js.map +0 -1
- package/dist/store.d.ts +0 -222
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -1366
- package/dist/store.js.map +0 -1
- package/dist/team-handler.d.ts +0 -90
- package/dist/team-handler.d.ts.map +0 -1
- package/dist/team-handler.js +0 -1225
- package/dist/team-handler.js.map +0 -1
- package/dist/ticket-handler.d.ts +0 -28
- package/dist/ticket-handler.d.ts.map +0 -1
- package/dist/ticket-handler.js +0 -74
- package/dist/ticket-handler.js.map +0 -1
- package/dist/wallet-identity.d.ts +0 -32
- package/dist/wallet-identity.d.ts.map +0 -1
- package/dist/wallet-identity.js +0 -43
- package/dist/wallet-identity.js.map +0 -1
- package/dist/webauthn.d.ts +0 -65
- package/dist/webauthn.d.ts.map +0 -1
- package/dist/webauthn.js +0 -112
- package/dist/webauthn.js.map +0 -1
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface NFTColor {
|
|
2
|
+
start: string;
|
|
3
|
+
end: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const DEFAULT_COLORS: Record<string, NFTColor>;
|
|
6
|
+
/**
|
|
7
|
+
* Convert RGB color to HSL color.
|
|
8
|
+
* @param rgbString - The RGB color value (e.g. "#FF0000").
|
|
9
|
+
* @returns A [h, s, l] tuple with values in [0, 1].
|
|
10
|
+
*/
|
|
11
|
+
export declare function rgbToHsl(rgbString: string): [number, number, number];
|
|
12
|
+
/**
|
|
13
|
+
* Convert HSL color value to an RGB hex string.
|
|
14
|
+
* @param h - Hue in [0, 1].
|
|
15
|
+
* @param s - Saturation in [0, 1].
|
|
16
|
+
* @param l - Lightness in [0, 1].
|
|
17
|
+
* @returns A hex color string like "#rrggbb".
|
|
18
|
+
*/
|
|
19
|
+
export declare function hslToRgb(h: number, s: number, l: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* Convert a single color to usable NFT gradient background colors.
|
|
22
|
+
* Clamps saturation to ≤0.7 and lightness to [0.3, 0.6].
|
|
23
|
+
* @param rgbString - The background color in RGB hex format.
|
|
24
|
+
* @returns An NFTColor with clamped start and end gradient colors.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getNftBGColor(rgbString: string): NFTColor;
|
|
27
|
+
/**
|
|
28
|
+
* Derive a deterministic passport color hex string from a DID.
|
|
29
|
+
* @param did - The DID string.
|
|
30
|
+
* @returns A 7-character hex color string like "#rrggbb".
|
|
31
|
+
*/
|
|
32
|
+
export declare function getPassportColorFromDid(did: string): string;
|
|
33
|
+
/**
|
|
34
|
+
* Get NFT background color derived from a DID.
|
|
35
|
+
* @param did - The DID string.
|
|
36
|
+
* @returns An NFTColor with start and end gradient colors.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getNftBGColorFromDid(did: string): NFTColor;
|
|
39
|
+
/**
|
|
40
|
+
* Get the passport gradient color for display.
|
|
41
|
+
* @param preferredColor - "default" | "auto" | a hex color string | undefined
|
|
42
|
+
* @param did - The DID string (used when preferredColor is "auto").
|
|
43
|
+
* @returns An NFTColor with start and end gradient colors.
|
|
44
|
+
*/
|
|
45
|
+
export declare function getPassportColor(preferredColor: string | undefined, did: string): NFTColor;
|
|
46
|
+
/**
|
|
47
|
+
* Choose readable text color based on background luminance.
|
|
48
|
+
* @param background - A hex color string like "#rrggbb".
|
|
49
|
+
* @returns "#111" for light backgrounds, "#EEE" for dark backgrounds.
|
|
50
|
+
*/
|
|
51
|
+
export declare function getTextColor(background: string): "#111" | "#EEE";
|
|
52
|
+
//# sourceMappingURL=passport-svg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passport-svg.d.ts","sourceRoot":"","sources":["../../src/og/passport-svg.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAMnD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAkCpE;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CA8BhE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAqBzD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAQ1F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAMhE"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { Hasher } from "@ocap/mcrypto";
|
|
2
|
+
import { toHex } from "@ocap/util";
|
|
3
|
+
export const DEFAULT_COLORS = {
|
|
4
|
+
"did-space": { start: "#7D3AC5", end: "#9761D1" },
|
|
5
|
+
"app-space": { start: "#6CC68D", end: "#47B871" },
|
|
6
|
+
"app-purchase": { start: "#DE7021", end: "#E48D4E" },
|
|
7
|
+
"app-passport": { start: "#3882C7", end: "#7AB2F6" },
|
|
8
|
+
"app-kyc": { start: "#3882C7", end: "#7AB2F6" },
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Convert RGB color to HSL color.
|
|
12
|
+
* @param rgbString - The RGB color value (e.g. "#FF0000").
|
|
13
|
+
* @returns A [h, s, l] tuple with values in [0, 1].
|
|
14
|
+
*/
|
|
15
|
+
export function rgbToHsl(rgbString) {
|
|
16
|
+
const color = rgbString.replace("#", "");
|
|
17
|
+
const r = parseInt(color.slice(0, 2), 16) / 255;
|
|
18
|
+
const g = parseInt(color.slice(2, 4), 16) / 255;
|
|
19
|
+
const b = parseInt(color.slice(4, 6), 16) / 255;
|
|
20
|
+
const max = Math.max(r, g, b);
|
|
21
|
+
const min = Math.min(r, g, b);
|
|
22
|
+
let h = 0;
|
|
23
|
+
let s = 0;
|
|
24
|
+
const l = (max + min) / 2;
|
|
25
|
+
if (max === min) {
|
|
26
|
+
h = 0;
|
|
27
|
+
s = 0;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
const d = max - min;
|
|
31
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
32
|
+
switch (max) {
|
|
33
|
+
case r:
|
|
34
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
35
|
+
break;
|
|
36
|
+
case g:
|
|
37
|
+
h = (b - r) / d + 2;
|
|
38
|
+
break;
|
|
39
|
+
case b:
|
|
40
|
+
h = (r - g) / d + 4;
|
|
41
|
+
break;
|
|
42
|
+
default:
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
h /= 6;
|
|
46
|
+
}
|
|
47
|
+
return [h, s, l];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Convert HSL color value to an RGB hex string.
|
|
51
|
+
* @param h - Hue in [0, 1].
|
|
52
|
+
* @param s - Saturation in [0, 1].
|
|
53
|
+
* @param l - Lightness in [0, 1].
|
|
54
|
+
* @returns A hex color string like "#rrggbb".
|
|
55
|
+
*/
|
|
56
|
+
export function hslToRgb(h, s, l) {
|
|
57
|
+
let r;
|
|
58
|
+
let g;
|
|
59
|
+
let b;
|
|
60
|
+
if (s === 0) {
|
|
61
|
+
r = l;
|
|
62
|
+
g = l;
|
|
63
|
+
b = l;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const hue2rgb = (p, q, t2) => {
|
|
67
|
+
let t = t2;
|
|
68
|
+
if (t < 0)
|
|
69
|
+
t += 1;
|
|
70
|
+
if (t > 1)
|
|
71
|
+
t -= 1;
|
|
72
|
+
if (t < 1 / 6)
|
|
73
|
+
return p + (q - p) * 6 * t;
|
|
74
|
+
if (t < 1 / 2)
|
|
75
|
+
return q;
|
|
76
|
+
if (t < 2 / 3)
|
|
77
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
78
|
+
return p;
|
|
79
|
+
};
|
|
80
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
81
|
+
const p = 2 * l - q;
|
|
82
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
83
|
+
g = hue2rgb(p, q, h);
|
|
84
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
85
|
+
}
|
|
86
|
+
return `#${[Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]
|
|
87
|
+
.map((e) => e.toString(16).padStart(2, "0"))
|
|
88
|
+
.join("")}`;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Convert a single color to usable NFT gradient background colors.
|
|
92
|
+
* Clamps saturation to ≤0.7 and lightness to [0.3, 0.6].
|
|
93
|
+
* @param rgbString - The background color in RGB hex format.
|
|
94
|
+
* @returns An NFTColor with clamped start and end gradient colors.
|
|
95
|
+
*/
|
|
96
|
+
export function getNftBGColor(rgbString) {
|
|
97
|
+
if (rgbString) {
|
|
98
|
+
const data = rgbToHsl(rgbString);
|
|
99
|
+
// Hue h can be arbitrary
|
|
100
|
+
// Saturation s should not exceed 0.7
|
|
101
|
+
// Lightness l should not exceed 0.6 and should be above 0.3
|
|
102
|
+
const h = data[0];
|
|
103
|
+
const s = data[1] > 0.7 ? 0.7 : data[1];
|
|
104
|
+
const l = data[2] > 0.6 ? 0.6 : data[2] < 0.3 ? 0.3 : data[2];
|
|
105
|
+
const newColor = hslToRgb(h, s, l);
|
|
106
|
+
const newRepeatColor = hslToRgb(h, s, l - 0.1);
|
|
107
|
+
return {
|
|
108
|
+
start: newRepeatColor,
|
|
109
|
+
end: newColor,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
return DEFAULT_COLORS["app-passport"];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Derive a deterministic passport color hex string from a DID.
|
|
116
|
+
* @param did - The DID string.
|
|
117
|
+
* @returns A 7-character hex color string like "#rrggbb".
|
|
118
|
+
*/
|
|
119
|
+
export function getPassportColorFromDid(did) {
|
|
120
|
+
return `#${toHex(Hasher.SHA3.hash224(did)).slice(-6)}`;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get NFT background color derived from a DID.
|
|
124
|
+
* @param did - The DID string.
|
|
125
|
+
* @returns An NFTColor with start and end gradient colors.
|
|
126
|
+
*/
|
|
127
|
+
export function getNftBGColorFromDid(did) {
|
|
128
|
+
return getNftBGColor(getPassportColorFromDid(did));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Get the passport gradient color for display.
|
|
132
|
+
* @param preferredColor - "default" | "auto" | a hex color string | undefined
|
|
133
|
+
* @param did - The DID string (used when preferredColor is "auto").
|
|
134
|
+
* @returns An NFTColor with start and end gradient colors.
|
|
135
|
+
*/
|
|
136
|
+
export function getPassportColor(preferredColor, did) {
|
|
137
|
+
if (preferredColor === "default" || !preferredColor) {
|
|
138
|
+
return DEFAULT_COLORS["app-passport"];
|
|
139
|
+
}
|
|
140
|
+
if (preferredColor === "auto") {
|
|
141
|
+
return getNftBGColorFromDid(did);
|
|
142
|
+
}
|
|
143
|
+
return getNftBGColor(preferredColor);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Choose readable text color based on background luminance.
|
|
147
|
+
* @param background - A hex color string like "#rrggbb".
|
|
148
|
+
* @returns "#111" for light backgrounds, "#EEE" for dark backgrounds.
|
|
149
|
+
*/
|
|
150
|
+
export function getTextColor(background) {
|
|
151
|
+
const r = parseInt(background.slice(1, 3), 16);
|
|
152
|
+
const g = parseInt(background.slice(3, 5), 16);
|
|
153
|
+
const b = parseInt(background.slice(5, 7), 16);
|
|
154
|
+
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
155
|
+
return luminance > 0.5 ? "#111" : "#EEE";
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=passport-svg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passport-svg.js","sourceRoot":"","sources":["../../src/og/passport-svg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guard for server-side image fetches.
|
|
3
|
+
*
|
|
4
|
+
* The OG generator fetches caller-supplied `cover`/`logo` URLs from a public
|
|
5
|
+
* endpoint. Without a guard, an attacker could probe internal HTTPS hosts
|
|
6
|
+
* (cloud metadata, private services). This module:
|
|
7
|
+
* - enforces https
|
|
8
|
+
* - rejects private / loopback / link-local / unique-local / metadata
|
|
9
|
+
* targets, checked against the resolved IPs (not just the hostname) so a
|
|
10
|
+
* public domain pointing at a private address is still blocked
|
|
11
|
+
* - exposes a streaming reader that hard-caps the byte count instead of
|
|
12
|
+
* trusting the (forgeable) Content-Length header
|
|
13
|
+
*
|
|
14
|
+
* Residual risk: a narrow DNS-rebinding window between the resolve check and
|
|
15
|
+
* the fetch. Mitigated in practice by the short-lived fetch and the literal-IP
|
|
16
|
+
* checks; full pinning would require a custom connect-time hook.
|
|
17
|
+
*/
|
|
18
|
+
/** DNS resolver shape — overridable in tests to avoid real network lookups. */
|
|
19
|
+
export type LookupImpl = (host: string) => Promise<Array<{
|
|
20
|
+
address: string;
|
|
21
|
+
}>>;
|
|
22
|
+
export declare class SsrfBlockedError extends Error {
|
|
23
|
+
statusCode: number;
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
26
|
+
/** True if an IPv4/IPv6 literal is private, loopback, link-local, etc. */
|
|
27
|
+
export declare function isPrivateAddress(ip: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Reject a URL that isn't safe to fetch server-side. Throws SsrfBlockedError
|
|
30
|
+
* (400) on https violation or a private/blocked resolved address.
|
|
31
|
+
*/
|
|
32
|
+
export declare function assertFetchableUrl(rawUrl: string, lookupImpl?: LookupImpl): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Read a fetch Response body into a Buffer, aborting once `maxBytes` is
|
|
35
|
+
* exceeded. Does not trust Content-Length.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readBodyCapped(res: Response, maxBytes: number): Promise<Buffer>;
|
|
38
|
+
//# sourceMappingURL=ssrf-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf-guard.d.ts","sourceRoot":"","sources":["../../src/og/ssrf-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,CAAC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC;AAI/E,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,UAAU,SAAO;gBACL,OAAO,EAAE,MAAM;CAI5B;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAKpD;AA4ED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,GAAE,UAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA+B9G;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBrF"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF guard for server-side image fetches.
|
|
3
|
+
*
|
|
4
|
+
* The OG generator fetches caller-supplied `cover`/`logo` URLs from a public
|
|
5
|
+
* endpoint. Without a guard, an attacker could probe internal HTTPS hosts
|
|
6
|
+
* (cloud metadata, private services). This module:
|
|
7
|
+
* - enforces https
|
|
8
|
+
* - rejects private / loopback / link-local / unique-local / metadata
|
|
9
|
+
* targets, checked against the resolved IPs (not just the hostname) so a
|
|
10
|
+
* public domain pointing at a private address is still blocked
|
|
11
|
+
* - exposes a streaming reader that hard-caps the byte count instead of
|
|
12
|
+
* trusting the (forgeable) Content-Length header
|
|
13
|
+
*
|
|
14
|
+
* Residual risk: a narrow DNS-rebinding window between the resolve check and
|
|
15
|
+
* the fetch. Mitigated in practice by the short-lived fetch and the literal-IP
|
|
16
|
+
* checks; full pinning would require a custom connect-time hook.
|
|
17
|
+
*/
|
|
18
|
+
import { lookup as dnsLookup } from "node:dns/promises";
|
|
19
|
+
import { isIP } from "node:net";
|
|
20
|
+
const defaultLookup = (host) => dnsLookup(host, { all: true });
|
|
21
|
+
export class SsrfBlockedError extends Error {
|
|
22
|
+
statusCode = 400;
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message);
|
|
25
|
+
this.name = "SsrfBlockedError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** True if an IPv4/IPv6 literal is private, loopback, link-local, etc. */
|
|
29
|
+
export function isPrivateAddress(ip) {
|
|
30
|
+
const kind = isIP(ip);
|
|
31
|
+
if (kind === 4)
|
|
32
|
+
return isPrivateIPv4(ip);
|
|
33
|
+
if (kind === 6)
|
|
34
|
+
return isPrivateIPv6(ip.toLowerCase());
|
|
35
|
+
return true; // not a parseable IP → treat as unsafe
|
|
36
|
+
}
|
|
37
|
+
function isPrivateIPv4(ip) {
|
|
38
|
+
const o = ip.split(".").map(Number);
|
|
39
|
+
if (o.length !== 4 || o.some((n) => Number.isNaN(n) || n < 0 || n > 255))
|
|
40
|
+
return true;
|
|
41
|
+
const [a, b] = o;
|
|
42
|
+
if (a === 0)
|
|
43
|
+
return true; // 0.0.0.0/8
|
|
44
|
+
if (a === 10)
|
|
45
|
+
return true; // 10/8 private
|
|
46
|
+
if (a === 127)
|
|
47
|
+
return true; // loopback
|
|
48
|
+
if (a === 169 && b === 254)
|
|
49
|
+
return true; // link-local + cloud metadata 169.254.169.254
|
|
50
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
51
|
+
return true; // 172.16/12 private
|
|
52
|
+
if (a === 192 && b === 168)
|
|
53
|
+
return true; // 192.168/16 private
|
|
54
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
55
|
+
return true; // 100.64/10 CGNAT
|
|
56
|
+
if (a >= 224)
|
|
57
|
+
return true; // multicast + reserved
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Expand an IPv6 string (with `::` compression and/or a trailing dotted IPv4)
|
|
62
|
+
* into its 8 16-bit hextet values. Returns null if it can't be parsed.
|
|
63
|
+
*/
|
|
64
|
+
function expandIPv6(ip) {
|
|
65
|
+
const parts = ip.split("::");
|
|
66
|
+
if (parts.length > 2)
|
|
67
|
+
return null;
|
|
68
|
+
const toHextets = (segment) => {
|
|
69
|
+
if (segment === "")
|
|
70
|
+
return [];
|
|
71
|
+
const out = [];
|
|
72
|
+
for (const piece of segment.split(":")) {
|
|
73
|
+
if (piece.includes(".")) {
|
|
74
|
+
// Embedded dotted IPv4 → two hextets.
|
|
75
|
+
const o = piece.split(".").map(Number);
|
|
76
|
+
if (o.length !== 4 || o.some((n) => Number.isNaN(n) || n < 0 || n > 255))
|
|
77
|
+
return null;
|
|
78
|
+
out.push((o[0] << 8) | o[1], (o[2] << 8) | o[3]);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(piece))
|
|
82
|
+
return null;
|
|
83
|
+
out.push(parseInt(piece, 16));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
};
|
|
88
|
+
const head = toHextets(parts[0]);
|
|
89
|
+
const tail = parts.length === 2 ? toHextets(parts[1]) : [];
|
|
90
|
+
if (head === null || tail === null)
|
|
91
|
+
return null;
|
|
92
|
+
if (parts.length === 2) {
|
|
93
|
+
const gap = 8 - head.length - tail.length;
|
|
94
|
+
if (gap < 0)
|
|
95
|
+
return null;
|
|
96
|
+
return [...head, ...Array(gap).fill(0), ...tail];
|
|
97
|
+
}
|
|
98
|
+
return head.length === 8 ? head : null;
|
|
99
|
+
}
|
|
100
|
+
function isPrivateIPv6(ip) {
|
|
101
|
+
const h = expandIPv6(ip);
|
|
102
|
+
if (!h)
|
|
103
|
+
return true; // unparseable → unsafe
|
|
104
|
+
// Loopback (::1) and unspecified (::)
|
|
105
|
+
if (h.every((x, i) => (i < 7 ? x === 0 : x === 0 || x === 1)))
|
|
106
|
+
return true;
|
|
107
|
+
// IPv4-mapped (::ffff:a.b.c.d, any notation) and deprecated IPv4-compatible
|
|
108
|
+
// (::a.b.c.d): first 5 hextets zero, optional ffff marker → check embedded v4.
|
|
109
|
+
const firstFiveZero = h.slice(0, 5).every((x) => x === 0);
|
|
110
|
+
if (firstFiveZero && (h[5] === 0xffff || h[5] === 0)) {
|
|
111
|
+
const v4 = `${h[6] >> 8}.${h[6] & 0xff}.${h[7] >> 8}.${h[7] & 0xff}`;
|
|
112
|
+
return isPrivateIPv4(v4);
|
|
113
|
+
}
|
|
114
|
+
const first = h[0];
|
|
115
|
+
if ((first & 0xff00) === 0xff00)
|
|
116
|
+
return true; // multicast ff00::/8
|
|
117
|
+
if ((first & 0xffc0) === 0xfe80)
|
|
118
|
+
return true; // link-local fe80::/10
|
|
119
|
+
if ((first & 0xfe00) === 0xfc00)
|
|
120
|
+
return true; // unique local fc00::/7
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Reject a URL that isn't safe to fetch server-side. Throws SsrfBlockedError
|
|
125
|
+
* (400) on https violation or a private/blocked resolved address.
|
|
126
|
+
*/
|
|
127
|
+
export async function assertFetchableUrl(rawUrl, lookupImpl = defaultLookup) {
|
|
128
|
+
let url;
|
|
129
|
+
try {
|
|
130
|
+
url = new URL(rawUrl);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
throw new SsrfBlockedError(`invalid url: ${rawUrl}`);
|
|
134
|
+
}
|
|
135
|
+
if (url.protocol !== "https:") {
|
|
136
|
+
throw new SsrfBlockedError(`only https image URLs are allowed: ${rawUrl}`);
|
|
137
|
+
}
|
|
138
|
+
const host = url.hostname;
|
|
139
|
+
// Literal IP in the host → check directly (no DNS).
|
|
140
|
+
if (isIP(host)) {
|
|
141
|
+
if (isPrivateAddress(host))
|
|
142
|
+
throw new SsrfBlockedError(`blocked private address: ${host}`);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Hostname → resolve and ensure EVERY address is public.
|
|
146
|
+
let addrs;
|
|
147
|
+
try {
|
|
148
|
+
addrs = await lookupImpl(host);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
throw new SsrfBlockedError(`dns resolution failed: ${host}`);
|
|
152
|
+
}
|
|
153
|
+
if (addrs.length === 0)
|
|
154
|
+
throw new SsrfBlockedError(`no addresses for host: ${host}`);
|
|
155
|
+
for (const { address } of addrs) {
|
|
156
|
+
if (isPrivateAddress(address)) {
|
|
157
|
+
throw new SsrfBlockedError(`host ${host} resolves to blocked address ${address}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Read a fetch Response body into a Buffer, aborting once `maxBytes` is
|
|
163
|
+
* exceeded. Does not trust Content-Length.
|
|
164
|
+
*/
|
|
165
|
+
export async function readBodyCapped(res, maxBytes) {
|
|
166
|
+
if (!res.body) {
|
|
167
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
168
|
+
if (buf.byteLength > maxBytes)
|
|
169
|
+
throw new SsrfBlockedError(`response too large (> ${maxBytes} bytes)`);
|
|
170
|
+
return buf;
|
|
171
|
+
}
|
|
172
|
+
const chunks = [];
|
|
173
|
+
let total = 0;
|
|
174
|
+
const reader = res.body.getReader();
|
|
175
|
+
for (;;) {
|
|
176
|
+
const { done, value } = await reader.read();
|
|
177
|
+
if (done)
|
|
178
|
+
break;
|
|
179
|
+
total += value.byteLength;
|
|
180
|
+
if (total > maxBytes) {
|
|
181
|
+
await reader.cancel().catch(() => { });
|
|
182
|
+
throw new SsrfBlockedError(`response too large (> ${maxBytes} bytes)`);
|
|
183
|
+
}
|
|
184
|
+
chunks.push(Buffer.from(value));
|
|
185
|
+
}
|
|
186
|
+
return Buffer.concat(chunks);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=ssrf-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrf-guard.js","sourceRoot":"","sources":["../../src/og/ssrf-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NFTColor } from "./passport-svg.js";
|
|
2
|
+
export type OgTemplate = "default" | "section" | "cover" | "banner";
|
|
3
|
+
export type OgFormat = "png" | "svg" | "html";
|
|
4
|
+
export interface OgTemplateParams {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
logo: string;
|
|
8
|
+
background: string;
|
|
9
|
+
logoRounded: boolean;
|
|
10
|
+
color: NFTColor;
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
section: string;
|
|
14
|
+
cover: string;
|
|
15
|
+
template: OgTemplate;
|
|
16
|
+
format: OgFormat;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Public brand-logo SVG helper. Returns an inline SVG string with the given
|
|
20
|
+
* stroke color. Not used by any of the 4 OG templates in this file — it's
|
|
21
|
+
* exported for external callers (e.g., admin UI, embed previews) that want
|
|
22
|
+
* the same brand mark with a custom stroke.
|
|
23
|
+
*/
|
|
24
|
+
export declare const getLogoSvg: (color: string) => string;
|
|
25
|
+
export declare const getTemplate: (params: OgTemplateParams) => Promise<unknown>;
|
|
26
|
+
//# sourceMappingURL=templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/og/templates.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlD,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AACpE,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,QAAQ,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,UAAU,CAAC;IACrB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAWD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,KAAG,MAe1C,CAAC;AA+TF,eAAO,MAAM,WAAW,GAAU,QAAQ,gBAAgB,KAAG,OAAO,CAAC,OAAO,CAiB3E,CAAC"}
|