Signed-off-by: Anirudh Oppiliappan anirudh@tangled.org
+70
-94
avatar/package-lock.json
+70
-94
avatar/package-lock.json
···
8
"name": "avatar",
9
"version": "0.0.0",
10
"dependencies": {
11
-
"@atproto/identity": "^0.4.1"
12
},
13
"devDependencies": {
14
"@cloudflare/vitest-pool-workers": "^0.8.19",
···
16
"wrangler": "^4.14.1"
17
}
18
},
19
-
"node_modules/@atproto/common-web": {
20
-
"version": "0.4.7",
21
-
"resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.7.tgz",
22
-
"integrity": "sha512-vjw2+81KPo2/SAbbARGn64Ln+6JTI0FTI4xk8if0ebBfDxFRmHb2oSN1y77hzNq/ybGHqA2mecfhS03pxC5+lg==",
23
-
"license": "MIT",
24
"dependencies": {
25
-
"@atproto/lex-data": "0.0.3",
26
-
"@atproto/lex-json": "0.0.3",
27
-
"zod": "^3.23.8"
28
}
29
},
30
-
"node_modules/@atproto/crypto": {
31
-
"version": "0.4.5",
32
-
"resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz",
33
-
"integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==",
34
-
"license": "MIT",
35
"dependencies": {
36
-
"@noble/curves": "^1.7.0",
37
-
"@noble/hashes": "^1.6.1",
38
-
"uint8arrays": "3.0.0"
39
},
40
-
"engines": {
41
-
"node": ">=18.7.0"
42
}
43
},
44
-
"node_modules/@atproto/identity": {
45
-
"version": "0.4.10",
46
-
"resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.10.tgz",
47
-
"integrity": "sha512-nQbzDLXOhM8p/wo0cTh5DfMSOSHzj6jizpodX37LJ4S1TZzumSxAjHEZa5Rev3JaoD5uSWMVE0MmKEGWkPPvfQ==",
48
-
"license": "MIT",
49
"dependencies": {
50
-
"@atproto/common-web": "^0.4.4",
51
-
"@atproto/crypto": "^0.4.4"
52
-
},
53
-
"engines": {
54
-
"node": ">=18.7.0"
55
}
56
},
57
-
"node_modules/@atproto/lex-data": {
58
-
"version": "0.0.3",
59
-
"resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.3.tgz",
60
-
"integrity": "sha512-ivo1IpY/EX+RIpxPgCf4cPhQo5bfu4nrpa1vJCt8hCm9SfoonJkDFGa0n4SMw4JnXZoUcGcrJ46L+D8bH6GI2g==",
61
-
"license": "MIT",
62
"dependencies": {
63
-
"@atproto/syntax": "0.4.2",
64
-
"multiformats": "^9.9.0",
65
-
"tslib": "^2.8.1",
66
-
"uint8arrays": "3.0.0",
67
-
"unicode-segmenter": "^0.14.0"
68
}
69
},
70
-
"node_modules/@atproto/lex-json": {
71
-
"version": "0.0.3",
72
-
"resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.3.tgz",
73
-
"integrity": "sha512-ZVcY7XlRfdPYvQQ2WroKUepee0+NCovrSXgXURM3Xv+n5jflJCoczguROeRr8sN0xvT0ZbzMrDNHCUYKNnxcjw==",
74
-
"license": "MIT",
75
"dependencies": {
76
-
"@atproto/lex-data": "0.0.3",
77
-
"tslib": "^2.8.1"
78
}
79
},
80
-
"node_modules/@atproto/syntax": {
81
-
"version": "0.4.2",
82
-
"resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz",
83
-
"integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==",
84
-
"license": "MIT"
85
},
86
"node_modules/@cloudflare/kv-asset-handler": {
87
"version": "0.4.0",
···
1085
"@jridgewell/sourcemap-codec": "^1.4.10"
1086
}
1087
},
1088
-
"node_modules/@noble/curves": {
1089
-
"version": "1.9.7",
1090
-
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz",
1091
-
"integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==",
1092
-
"license": "MIT",
1093
-
"dependencies": {
1094
-
"@noble/hashes": "1.8.0"
1095
-
},
1096
-
"engines": {
1097
-
"node": "^14.21.3 || >=16"
1098
-
},
1099
-
"funding": {
1100
-
"url": "https://paulmillr.com/funding/"
1101
-
}
1102
-
},
1103
-
"node_modules/@noble/hashes": {
1104
-
"version": "1.8.0",
1105
-
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
1106
-
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
1107
-
"license": "MIT",
1108
-
"engines": {
1109
-
"node": "^14.21.3 || >=16"
1110
-
},
1111
-
"funding": {
1112
-
"url": "https://paulmillr.com/funding/"
1113
-
}
1114
-
},
1115
"node_modules/@rollup/rollup-android-arm-eabi": {
1116
"version": "4.40.1",
1117
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
···
1392
"win32"
1393
]
1394
},
1395
"node_modules/@types/estree": {
1396
"version": "1.0.7",
1397
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
···
1809
"@esbuild/win32-x64": "0.25.3"
1810
}
1811
},
1812
"node_modules/estree-walker": {
1813
"version": "3.0.3",
1814
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
···
1978
"dev": true,
1979
"license": "MIT"
1980
},
1981
-
"node_modules/multiformats": {
1982
-
"version": "9.9.0",
1983
-
"resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz",
1984
-
"integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==",
1985
-
"license": "(Apache-2.0 AND MIT)"
1986
-
},
1987
"node_modules/mustache": {
1988
"version": "4.2.0",
1989
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
···
2333
"version": "2.8.1",
2334
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
2335
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
2336
-
"license": "0BSD"
2337
},
2338
"node_modules/ufo": {
2339
"version": "1.6.1",
···
2342
"dev": true,
2343
"license": "MIT"
2344
},
2345
-
"node_modules/uint8arrays": {
2346
-
"version": "3.0.0",
2347
-
"resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz",
2348
-
"integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==",
2349
-
"license": "MIT",
2350
-
"dependencies": {
2351
-
"multiformats": "^9.4.2"
2352
-
}
2353
-
},
2354
"node_modules/undici": {
2355
"version": "5.29.0",
2356
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
···
3130
"version": "3.24.3",
3131
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz",
3132
"integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==",
3133
"license": "MIT",
3134
"funding": {
3135
"url": "https://github.com/sponsors/colinhacks"
···
8
"name": "avatar",
9
"version": "0.0.0",
10
"dependencies": {
11
+
"@atcute/identity-resolver": "^1.2.2"
12
},
13
"devDependencies": {
14
"@cloudflare/vitest-pool-workers": "^0.8.19",
···
16
"wrangler": "^4.14.1"
17
}
18
},
19
+
"node_modules/@atcute/identity": {
20
+
"version": "1.1.3",
21
+
"resolved": "https://registry.npmjs.org/@atcute/identity/-/identity-1.1.3.tgz",
22
+
"integrity": "sha512-oIqPoI8TwWeQxvcLmFEZLdN2XdWcaLVtlm8pNk0E72As9HNzzD9pwKPrLr3rmTLRIoULPPFmq9iFNsTeCIU9ng==",
23
+
"license": "0BSD",
24
+
"peer": true,
25
"dependencies": {
26
+
"@atcute/lexicons": "^1.2.4",
27
+
"@badrap/valita": "^0.4.6"
28
}
29
},
30
+
"node_modules/@atcute/identity-resolver": {
31
+
"version": "1.2.2",
32
+
"resolved": "https://registry.npmjs.org/@atcute/identity-resolver/-/identity-resolver-1.2.2.tgz",
33
+
"integrity": "sha512-eUh/UH4bFvuXS0X7epYCeJC/kj4rbBXfSRumLEH4smMVwNOgTo7cL/0Srty+P/qVPoZEyXdfEbS0PHJyzoXmHw==",
34
+
"license": "0BSD",
35
"dependencies": {
36
+
"@atcute/lexicons": "^1.2.6",
37
+
"@atcute/util-fetch": "^1.0.5",
38
+
"@badrap/valita": "^0.4.6"
39
},
40
+
"peerDependencies": {
41
+
"@atcute/identity": "^1.0.0"
42
}
43
},
44
+
"node_modules/@atcute/lexicons": {
45
+
"version": "1.2.6",
46
+
"resolved": "https://registry.npmjs.org/@atcute/lexicons/-/lexicons-1.2.6.tgz",
47
+
"integrity": "sha512-s76UQd8D+XmHIzrjD9CJ9SOOeeLPHc+sMmcj7UFakAW/dDFXc579fcRdRfuUKvXBL5v1Gs2VgDdlh/IvvQZAwA==",
48
+
"license": "0BSD",
49
"dependencies": {
50
+
"@atcute/uint8array": "^1.0.6",
51
+
"@atcute/util-text": "^0.0.1",
52
+
"@standard-schema/spec": "^1.1.0",
53
+
"esm-env": "^1.2.2"
54
}
55
},
56
+
"node_modules/@atcute/uint8array": {
57
+
"version": "1.0.6",
58
+
"resolved": "https://registry.npmjs.org/@atcute/uint8array/-/uint8array-1.0.6.tgz",
59
+
"integrity": "sha512-ucfRBQc7BFT8n9eCyGOzDHEMKF/nZwhS2pPao4Xtab1ML3HdFYcX2DM1tadCzas85QTGxHe5urnUAAcNKGRi9A==",
60
+
"license": "0BSD"
61
+
},
62
+
"node_modules/@atcute/util-fetch": {
63
+
"version": "1.0.5",
64
+
"resolved": "https://registry.npmjs.org/@atcute/util-fetch/-/util-fetch-1.0.5.tgz",
65
+
"integrity": "sha512-qjHj01BGxjSjIFdPiAjSARnodJIIyKxnCMMEcXMESo9TAyND6XZQqrie5fia+LlYWVXdpsTds8uFQwc9jdKTig==",
66
+
"license": "0BSD",
67
"dependencies": {
68
+
"@badrap/valita": "^0.4.6"
69
}
70
},
71
+
"node_modules/@atcute/util-text": {
72
+
"version": "0.0.1",
73
+
"resolved": "https://registry.npmjs.org/@atcute/util-text/-/util-text-0.0.1.tgz",
74
+
"integrity": "sha512-t1KZqvn0AYy+h2KcJyHnKF9aEqfRfMUmyY8j1ELtAEIgqN9CxINAjxnoRCJIFUlvWzb+oY3uElQL/Vyk3yss0g==",
75
+
"license": "0BSD",
76
"dependencies": {
77
+
"unicode-segmenter": "^0.14.4"
78
}
79
},
80
+
"node_modules/@badrap/valita": {
81
+
"version": "0.4.6",
82
+
"resolved": "https://registry.npmjs.org/@badrap/valita/-/valita-0.4.6.tgz",
83
+
"integrity": "sha512-4kdqcjyxo/8RQ8ayjms47HCWZIF5981oE5nIenbfThKDxWXtEHKipAOWlflpPJzZx9y/JWYQkp18Awr7VuepFg==",
84
+
"license": "MIT",
85
+
"engines": {
86
+
"node": ">= 18"
87
+
}
88
},
89
"node_modules/@cloudflare/kv-asset-handler": {
90
"version": "0.4.0",
···
1088
"@jridgewell/sourcemap-codec": "^1.4.10"
1089
}
1090
},
1091
"node_modules/@rollup/rollup-android-arm-eabi": {
1092
"version": "4.40.1",
1093
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz",
···
1368
"win32"
1369
]
1370
},
1371
+
"node_modules/@standard-schema/spec": {
1372
+
"version": "1.1.0",
1373
+
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
1374
+
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
1375
+
"license": "MIT"
1376
+
},
1377
"node_modules/@types/estree": {
1378
"version": "1.0.7",
1379
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
···
1791
"@esbuild/win32-x64": "0.25.3"
1792
}
1793
},
1794
+
"node_modules/esm-env": {
1795
+
"version": "1.2.2",
1796
+
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
1797
+
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
1798
+
"license": "MIT"
1799
+
},
1800
"node_modules/estree-walker": {
1801
"version": "3.0.3",
1802
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
···
1966
"dev": true,
1967
"license": "MIT"
1968
},
1969
"node_modules/mustache": {
1970
"version": "4.2.0",
1971
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
···
2315
"version": "2.8.1",
2316
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
2317
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
2318
+
"dev": true,
2319
+
"license": "0BSD",
2320
+
"optional": true
2321
},
2322
"node_modules/ufo": {
2323
"version": "1.6.1",
···
2326
"dev": true,
2327
"license": "MIT"
2328
},
2329
"node_modules/undici": {
2330
"version": "5.29.0",
2331
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
···
3105
"version": "3.24.3",
3106
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz",
3107
"integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==",
3108
+
"dev": true,
3109
"license": "MIT",
3110
"funding": {
3111
"url": "https://github.com/sponsors/colinhacks"
+2
-1
avatar/package.json
+2
-1
avatar/package.json
···
2
"name": "avatar",
3
"version": "0.0.0",
4
"private": true,
5
+
"type": "module",
6
"scripts": {
7
"deploy": "wrangler deploy",
8
"dev": "wrangler dev",
···
10
"test": "vitest"
11
},
12
"dependencies": {
13
+
"@atcute/identity-resolver": "^1.2.2"
14
},
15
"devDependencies": {
16
"@cloudflare/vitest-pool-workers": "^0.8.19",
+64
-29
avatar/src/index.js
+64
-29
avatar/src/index.js
···
1
-
import { IdResolver } from "@atproto/identity";
2
3
export default {
4
async fetch(request, env) {
5
// Helper function to generate a color from a string
···
17
};
18
19
// Helper function to fetch Tangled profile from PDS
20
-
const getTangledAvatarFromPDS = async (actor, resolver) => {
21
try {
22
// Resolve the identity
23
-
const identity = await resolver.resolve(actor);
24
if (!identity) {
25
console.log({
26
level: "debug",
···
31
}
32
33
const did = identity.did;
34
-
const pdsEndpoint = identity.pdsUrl;
35
36
if (!pdsEndpoint) {
37
console.log({
···
43
return null;
44
}
45
46
-
console.log({
47
-
level: "debug",
48
-
message: "fetching Tangled profile from PDS",
49
-
actor: actor,
50
-
did: did,
51
-
pdsEndpoint: pdsEndpoint,
52
-
});
53
54
// Fetch the Tangled profile record from PDS
55
-
const profileResponse = await fetch(
56
-
`${pdsEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=org.tangled.actor.profile&rkey=self`,
57
-
);
58
59
if (!profileResponse.ok) {
60
console.log({
···
67
}
68
69
const profileData = await profileResponse.json();
70
-
const avatarCID = profileData?.value?.avatar;
71
72
-
if (!avatarCID) {
73
console.log({
74
level: "debug",
75
-
message: "Tangled profile has no avatar CID",
76
actor: actor,
77
});
78
return null;
79
}
80
81
-
// Construct blob URL
82
-
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${avatarCID}`;
83
84
-
console.log({
85
-
level: "debug",
86
-
message: "found Tangled avatar",
87
-
actor: actor,
88
-
avatarCID: avatarCID,
89
-
});
90
91
return blobUrl;
92
} catch (e) {
93
console.log({
···
158
try {
159
let avatarUrl = null;
160
161
-
// Create identity resolver
162
-
const resolver = new IdResolver();
163
-
164
// Try to get Tangled avatar from user's PDS first
165
-
avatarUrl = await getTangledAvatarFromPDS(actor, resolver);
166
167
// If no Tangled avatar, fall back to Bluesky
168
if (!avatarUrl) {
···
1
+
import {
2
+
LocalActorResolver,
3
+
CompositeHandleResolver,
4
+
DohJsonHandleResolver,
5
+
WellKnownHandleResolver,
6
+
CompositeDidDocumentResolver,
7
+
PlcDidDocumentResolver,
8
+
WebDidDocumentResolver,
9
+
} from "@atcute/identity-resolver";
10
11
+
// Initialize resolvers for Cloudflare Workers
12
+
const handleResolver = new CompositeHandleResolver({
13
+
strategy: "race",
14
+
methods: {
15
+
dns: new DohJsonHandleResolver({
16
+
dohUrl: "https://cloudflare-dns.com/dns-query",
17
+
}),
18
+
http: new WellKnownHandleResolver(),
19
+
},
20
+
});
21
+
22
+
const didDocumentResolver = new CompositeDidDocumentResolver({
23
+
methods: {
24
+
plc: new PlcDidDocumentResolver(),
25
+
web: new WebDidDocumentResolver(),
26
+
},
27
+
});
28
+
29
+
const actorResolver = new LocalActorResolver({
30
+
handleResolver,
31
+
didDocumentResolver,
32
+
});
33
+
34
export default {
35
async fetch(request, env) {
36
// Helper function to generate a color from a string
···
48
};
49
50
// Helper function to fetch Tangled profile from PDS
51
+
const getTangledAvatarFromPDS = async (actor) => {
52
try {
53
// Resolve the identity
54
+
const identity = await actorResolver.resolve(actor);
55
if (!identity) {
56
console.log({
57
level: "debug",
···
62
}
63
64
const did = identity.did;
65
+
const pdsEndpoint = identity.pds.replace(/\/$/, ""); // Remove trailing slash
66
67
if (!pdsEndpoint) {
68
console.log({
···
74
return null;
75
}
76
77
+
const profileUrl = `${pdsEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=sh.tangled.actor.profile&rkey=self`;
78
79
// Fetch the Tangled profile record from PDS
80
+
const profileResponse = await fetch(profileUrl);
81
82
if (!profileResponse.ok) {
83
console.log({
···
90
}
91
92
const profileData = await profileResponse.json();
93
+
const avatarBlob = profileData?.value?.avatar;
94
95
+
if (!avatarBlob) {
96
console.log({
97
level: "debug",
98
+
message: "Tangled profile has no avatar",
99
actor: actor,
100
});
101
return null;
102
}
103
104
+
// Extract CID from blob reference object
105
+
// The ref might be an object with $link property or a string
106
+
let avatarCID;
107
+
if (typeof avatarBlob.ref === "string") {
108
+
avatarCID = avatarBlob.ref;
109
+
} else if (avatarBlob.ref?.$link) {
110
+
avatarCID = avatarBlob.ref.$link;
111
+
} else if (typeof avatarBlob === "string") {
112
+
avatarCID = avatarBlob;
113
+
}
114
115
+
if (!avatarCID || typeof avatarCID !== "string") {
116
+
console.log({
117
+
level: "warn",
118
+
message: "could not extract valid CID from avatar blob",
119
+
actor: actor,
120
+
avatarBlob: avatarBlob,
121
+
avatarBlobRef: avatarBlob.ref,
122
+
});
123
+
return null;
124
+
}
125
126
+
// Construct blob URL (pdsEndpoint already has trailing slash removed)
127
+
const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${avatarCID}`;
128
+
129
return blobUrl;
130
} catch (e) {
131
console.log({
···
196
try {
197
let avatarUrl = null;
198
199
// Try to get Tangled avatar from user's PDS first
200
+
avatarUrl = await getTangledAvatarFromPDS(actor);
201
202
// If no Tangled avatar, fall back to Bluesky
203
if (!avatarUrl) {
+13
-13
avatar/wrangler.jsonc
+13
-13
avatar/wrangler.jsonc
···
1
{
2
-
"$schema": "node_modules/wrangler/config-schema.json",
3
-
"name": "avatar",
4
-
"main": "src/index.js",
5
-
"compatibility_date": "2025-05-03",
6
-
"observability": {
7
-
"enabled": true,
8
-
},
9
-
"routes": [
10
-
{
11
-
"pattern": "avatar.tangled.sh",
12
-
"custom_domain": true,
13
-
},
14
-
],
15
}
···
1
{
2
+
"$schema": "node_modules/wrangler/config-schema.json",
3
+
"name": "avatar",
4
+
"main": "src/index.js",
5
+
"compatibility_date": "2025-05-03",
6
+
"observability": {
7
+
"enabled": true,
8
+
},
9
+
"routes": [
10
+
{
11
+
"pattern": "avatar.tangled.sh",
12
+
"custom_domain": true,
13
+
},
14
+
],
15
}
History
7 rounds
2 comments
anirudh.fi
submitted
#6
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
expand 0 comments
pull request successfully merged
anirudh.fi
submitted
#5
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
expand 0 comments
anirudh.fi
submitted
#4
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
expand 0 comments
anirudh.fi
submitted
#3
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
expand 0 comments
anirudh.fi
submitted
#2
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
expand 0 comments
anirudh.fi
submitted
#1
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
anirudh.fi
submitted
#0
1 commit
expand
collapse
avatar: fetch tangled avatar before falling back to bsky & random color
Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.org>
why do we do this (just out of curiosity):
!!blob:Boolean(blobUrl)blobUrl != null && blobUrl != ""