tangled
alpha
login
or
join now
dunkirk.sh
/
traverse
1
fork
atom
snatching amp's walkthrough for my own purposes mwhahaha
traverse.dunkirk.sh/diagram/6121f05c-a5ef-4ecf-8ffc-02534c5e767c
1
fork
atom
overview
issues
pulls
pipelines
feat: add og images
dunkirk.sh
1 month ago
0e83eb16
b3d85b32
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+273
-3
5 changed files
expand all
collapse all
unified
split
bun.lock
package.json
src
index.ts
og.ts
template.ts
+49
bun.lock
···
5
5
"": {
6
6
"name": "traverse",
7
7
"dependencies": {
8
8
+
"@fontsource/inter": "^5.2.8",
8
9
"@modelcontextprotocol/sdk": "^1.26.0",
10
10
+
"@resvg/resvg-wasm": "^2.6.2",
11
11
+
"satori": "^0.19.1",
9
12
},
10
13
"devDependencies": {
11
14
"@types/bun": "latest",
···
16
19
},
17
20
},
18
21
"packages": {
22
22
+
"@fontsource/inter": ["@fontsource/inter@5.2.8", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
23
23
+
19
24
"@hono/node-server": ["@hono/node-server@1.19.9", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw=="],
20
25
21
26
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.26.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg=="],
22
27
28
28
+
"@resvg/resvg-wasm": ["@resvg/resvg-wasm@2.6.2", "", {}, "sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw=="],
29
29
+
30
30
+
"@shuding/opentype.js": ["@shuding/opentype.js@1.4.0-beta.0", "", { "dependencies": { "fflate": "^0.7.3", "string.prototype.codepointat": "^0.2.1" }, "bin": { "ot": "bin/ot" } }, "sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA=="],
31
31
+
23
32
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
24
33
25
34
"@types/node": ["@types/node@25.2.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ=="],
···
30
39
31
40
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
32
41
42
42
+
"base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="],
43
43
+
33
44
"body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
34
45
35
46
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
···
40
51
41
52
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
42
53
54
54
+
"camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="],
55
55
+
56
56
+
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
57
57
+
43
58
"content-disposition": ["content-disposition@1.0.1", "", {}, "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q=="],
44
59
45
60
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
···
51
66
"cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="],
52
67
53
68
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
69
69
+
70
70
+
"css-background-parser": ["css-background-parser@0.1.0", "", {}, "sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA=="],
71
71
+
72
72
+
"css-box-shadow": ["css-box-shadow@1.0.0-3", "", {}, "sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg=="],
73
73
+
74
74
+
"css-color-keywords": ["css-color-keywords@1.0.0", "", {}, "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="],
75
75
+
76
76
+
"css-gradient-parser": ["css-gradient-parser@0.0.17", "", {}, "sha512-w2Xy9UMMwlKtou0vlRnXvWglPAceXCTtcmVSo8ZBUvqCV5aXEFP/PC6d+I464810I9FT++UACwTD5511bmGPUg=="],
77
77
+
78
78
+
"css-to-react-native": ["css-to-react-native@3.2.0", "", { "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", "postcss-value-parser": "^4.0.2" } }, "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ=="],
54
79
55
80
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
56
81
···
60
85
61
86
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
62
87
88
88
+
"emoji-regex-xs": ["emoji-regex-xs@2.0.1", "", {}, "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g=="],
89
89
+
63
90
"encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="],
64
91
65
92
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
···
84
111
85
112
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
86
113
114
114
+
"fflate": ["fflate@0.7.4", "", {}, "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw=="],
115
115
+
87
116
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
88
117
89
118
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
···
101
130
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
102
131
103
132
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
133
133
+
134
134
+
"hex-rgb": ["hex-rgb@4.3.0", "", {}, "sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw=="],
104
135
105
136
"hono": ["hono@4.11.9", "", {}, "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ=="],
106
137
···
124
155
125
156
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
126
157
158
158
+
"linebreak": ["linebreak@1.1.0", "", { "dependencies": { "base64-js": "0.0.8", "unicode-trie": "^2.0.0" } }, "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ=="],
159
159
+
127
160
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
128
161
129
162
"media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
···
145
178
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
146
179
147
180
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
181
181
+
182
182
+
"pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
183
183
+
184
184
+
"parse-css-color": ["parse-css-color@0.2.1", "", { "dependencies": { "color-name": "^1.1.4", "hex-rgb": "^4.1.0" } }, "sha512-bwS/GGIFV3b6KS4uwpzCFj4w297Yl3uqnSgIPsoQkx7GMLROXfMnWvxfNkL0oh8HVhZA4hvJoEoEIqonfJ3BWg=="],
148
185
149
186
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
150
187
···
154
191
155
192
"pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="],
156
193
194
194
+
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
195
195
+
157
196
"proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="],
158
197
159
198
"qs": ["qs@6.14.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ=="],
···
167
206
"router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="],
168
207
169
208
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
209
209
+
210
210
+
"satori": ["satori@0.19.1", "", { "dependencies": { "@shuding/opentype.js": "1.4.0-beta.0", "css-background-parser": "^0.1.0", "css-box-shadow": "1.0.0-3", "css-gradient-parser": "^0.0.17", "css-to-react-native": "^3.0.0", "emoji-regex-xs": "^2.0.1", "escape-html": "^1.0.3", "linebreak": "^1.1.0", "parse-css-color": "^0.2.1", "postcss-value-parser": "^4.2.0", "yoga-layout": "^3.2.1" } }, "sha512-/XaT/JiWLfNlgjlQdde4wXB1/6F+FEze9c3OW2QIH0ywsfOrY57YOetgESWyOFHW3JfEQ6dJAo2U9Xwb7+DDAw=="],
170
211
171
212
"send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
172
213
···
188
229
189
230
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
190
231
232
232
+
"string.prototype.codepointat": ["string.prototype.codepointat@0.2.1", "", {}, "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="],
233
233
+
234
234
+
"tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
235
235
+
191
236
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
192
237
193
238
"type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
···
196
241
197
242
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
198
243
244
244
+
"unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="],
245
245
+
199
246
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
200
247
201
248
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
···
203
250
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
204
251
205
252
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
253
253
+
254
254
+
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
206
255
207
256
"zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="],
208
257
+4
-1
package.json
···
23
23
"typescript": "^5"
24
24
},
25
25
"dependencies": {
26
26
-
"@modelcontextprotocol/sdk": "^1.26.0"
26
26
+
"@fontsource/inter": "^5.2.8",
27
27
+
"@modelcontextprotocol/sdk": "^1.26.0",
28
28
+
"@resvg/resvg-wasm": "^2.6.2",
29
29
+
"satori": "^0.19.1"
27
30
}
28
31
}
+21
src/index.ts
···
4
4
import { generateViewerHTML } from "./template.ts";
5
5
import type { WalkthroughDiagram } from "./types.ts";
6
6
import { initDb, loadAllDiagrams, saveDiagram, deleteDiagramFromDb, generateId, getSharedUrl, saveSharedUrl } from "./storage.ts";
7
7
+
import { generateOgImage } from "./og.ts";
7
8
import { loadConfig } from "./config.ts";
8
9
9
10
const PORT = parseInt(process.env.TRAVERSE_PORT || "4173", 10);
···
25
26
port: PORT,
26
27
async fetch(req) {
27
28
const url = new URL(req.url);
29
29
+
30
30
+
// OG image route — must be matched before /diagram/:id
31
31
+
const ogMatch = url.pathname.match(/^\/diagram\/([\w-]+)\/og\.png$/);
32
32
+
if (ogMatch) {
33
33
+
const id = ogMatch[1]!;
34
34
+
const diagram = diagrams.get(id);
35
35
+
if (!diagram) {
36
36
+
return new Response("Not found", { status: 404 });
37
37
+
}
38
38
+
const nodeNames = Object.values(diagram.nodes).map(n => n.title);
39
39
+
const png = await generateOgImage(id, diagram.summary, nodeNames);
40
40
+
return new Response(png, {
41
41
+
headers: {
42
42
+
"Content-Type": "image/png",
43
43
+
"Cache-Control": "public, max-age=86400",
44
44
+
},
45
45
+
});
46
46
+
}
47
47
+
28
48
const diagramMatch = url.pathname.match(/^\/diagram\/([\w-]+)$/);
29
49
30
50
if (diagramMatch) {
···
42
62
shareServerUrl: config.shareServerUrl,
43
63
diagramId: id,
44
64
existingShareUrl: existingShareUrl ?? undefined,
65
65
+
baseUrl: url.origin,
45
66
}), {
46
67
headers: { "Content-Type": "text/html; charset=utf-8" },
47
68
});
+189
src/og.ts
···
1
1
+
import satori from "satori";
2
2
+
import { initWasm, Resvg } from "@resvg/resvg-wasm";
3
3
+
import { join } from "path";
4
4
+
5
5
+
// Load Inter font files (woff, not woff2 — satori doesn't support woff2)
6
6
+
const fontsDir = join(import.meta.dir, "../node_modules/@fontsource/inter/files");
7
7
+
const [interRegular, interBold] = await Promise.all([
8
8
+
Bun.file(join(fontsDir, "inter-latin-400-normal.woff")).arrayBuffer(),
9
9
+
Bun.file(join(fontsDir, "inter-latin-700-normal.woff")).arrayBuffer(),
10
10
+
]);
11
11
+
12
12
+
// Initialize resvg-wasm
13
13
+
const wasmPath = join(import.meta.dir, "../node_modules/@resvg/resvg-wasm/index_bg.wasm");
14
14
+
await initWasm(Bun.file(wasmPath).arrayBuffer());
15
15
+
16
16
+
// Cache generated images by diagram ID
17
17
+
const cache = new Map<string, Buffer>();
18
18
+
19
19
+
export async function generateOgImage(
20
20
+
id: string,
21
21
+
summary: string,
22
22
+
nodeNames: string[],
23
23
+
): Promise<Buffer> {
24
24
+
const cached = cache.get(id);
25
25
+
if (cached) return cached;
26
26
+
27
27
+
const nodeCount = nodeNames.length;
28
28
+
const displayNodes = nodeNames.slice(0, 8);
29
29
+
const extra = nodeCount > 8 ? nodeCount - 8 : 0;
30
30
+
31
31
+
const svg = await satori(
32
32
+
{
33
33
+
type: "div",
34
34
+
props: {
35
35
+
style: {
36
36
+
width: "100%",
37
37
+
height: "100%",
38
38
+
display: "flex",
39
39
+
flexDirection: "column",
40
40
+
justifyContent: "space-between",
41
41
+
padding: "60px",
42
42
+
backgroundColor: "#0a0a0a",
43
43
+
color: "#e5e5e5",
44
44
+
fontFamily: "Inter",
45
45
+
},
46
46
+
children: [
47
47
+
// Top: Traverse label
48
48
+
{
49
49
+
type: "div",
50
50
+
props: {
51
51
+
style: {
52
52
+
display: "flex",
53
53
+
alignItems: "center",
54
54
+
gap: "10px",
55
55
+
},
56
56
+
children: [
57
57
+
{
58
58
+
type: "div",
59
59
+
props: {
60
60
+
style: {
61
61
+
fontSize: "20px",
62
62
+
fontWeight: 700,
63
63
+
color: "#a3a3a3",
64
64
+
letterSpacing: "0.05em",
65
65
+
textTransform: "uppercase" as const,
66
66
+
},
67
67
+
children: "Traverse",
68
68
+
},
69
69
+
},
70
70
+
],
71
71
+
},
72
72
+
},
73
73
+
// Middle: Summary headline
74
74
+
{
75
75
+
type: "div",
76
76
+
props: {
77
77
+
style: {
78
78
+
display: "flex",
79
79
+
flexDirection: "column",
80
80
+
gap: "24px",
81
81
+
flex: 1,
82
82
+
justifyContent: "center",
83
83
+
},
84
84
+
children: [
85
85
+
{
86
86
+
type: "div",
87
87
+
props: {
88
88
+
style: {
89
89
+
fontSize: summary.length > 60 ? "36px" : "44px",
90
90
+
fontWeight: 700,
91
91
+
lineHeight: 1.2,
92
92
+
color: "#e5e5e5",
93
93
+
overflow: "hidden",
94
94
+
textOverflow: "ellipsis",
95
95
+
},
96
96
+
children: summary,
97
97
+
},
98
98
+
},
99
99
+
],
100
100
+
},
101
101
+
},
102
102
+
// Bottom: Node pills + count
103
103
+
{
104
104
+
type: "div",
105
105
+
props: {
106
106
+
style: {
107
107
+
display: "flex",
108
108
+
alignItems: "center",
109
109
+
justifyContent: "space-between",
110
110
+
gap: "16px",
111
111
+
},
112
112
+
children: [
113
113
+
{
114
114
+
type: "div",
115
115
+
props: {
116
116
+
style: {
117
117
+
display: "flex",
118
118
+
flexWrap: "wrap",
119
119
+
gap: "8px",
120
120
+
flex: 1,
121
121
+
},
122
122
+
children: [
123
123
+
...displayNodes.map((name) => ({
124
124
+
type: "div",
125
125
+
props: {
126
126
+
style: {
127
127
+
fontSize: "14px",
128
128
+
color: "#a3a3a3",
129
129
+
backgroundColor: "#1c1c1e",
130
130
+
padding: "4px 12px",
131
131
+
borderRadius: "6px",
132
132
+
border: "1px solid #262626",
133
133
+
},
134
134
+
children: name,
135
135
+
},
136
136
+
})),
137
137
+
...(extra > 0
138
138
+
? [
139
139
+
{
140
140
+
type: "div",
141
141
+
props: {
142
142
+
style: {
143
143
+
fontSize: "14px",
144
144
+
color: "#666",
145
145
+
padding: "4px 8px",
146
146
+
},
147
147
+
children: `+${extra} more`,
148
148
+
},
149
149
+
},
150
150
+
]
151
151
+
: []),
152
152
+
],
153
153
+
},
154
154
+
},
155
155
+
{
156
156
+
type: "div",
157
157
+
props: {
158
158
+
style: {
159
159
+
fontSize: "16px",
160
160
+
color: "#666",
161
161
+
flexShrink: 0,
162
162
+
},
163
163
+
children: `${nodeCount} node${nodeCount !== 1 ? "s" : ""}`,
164
164
+
},
165
165
+
},
166
166
+
],
167
167
+
},
168
168
+
},
169
169
+
],
170
170
+
},
171
171
+
},
172
172
+
{
173
173
+
width: 1200,
174
174
+
height: 630,
175
175
+
fonts: [
176
176
+
{ name: "Inter", data: interRegular, weight: 400, style: "normal" as const },
177
177
+
{ name: "Inter", data: interBold, weight: 700, style: "normal" as const },
178
178
+
],
179
179
+
},
180
180
+
);
181
181
+
182
182
+
const resvg = new Resvg(svg, {
183
183
+
fitTo: { mode: "width", value: 1200 },
184
184
+
});
185
185
+
const png = Buffer.from(resvg.render().asPng());
186
186
+
187
187
+
cache.set(id, png);
188
188
+
return png;
189
189
+
}
+10
-2
src/template.ts
···
5
5
shareServerUrl?: string;
6
6
diagramId?: string;
7
7
existingShareUrl?: string;
8
8
+
baseUrl?: string;
8
9
}
9
10
10
11
export function generateViewerHTML(diagram: WalkthroughDiagram, gitHash: string = "dev", projectRoot: string = "", options: ViewerOptions = {}): string {
11
12
const diagramJSON = JSON.stringify(diagram).replace(/<\//g, "<\\/");
12
12
-
const { mode = "local", shareServerUrl = "", diagramId = "", existingShareUrl = "" } = options;
13
13
+
const { mode = "local", shareServerUrl = "", diagramId = "", existingShareUrl = "", baseUrl = "" } = options;
13
14
14
15
return `<!DOCTYPE html>
15
16
<html lang="en">
···
17
18
<meta charset="UTF-8" />
18
19
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
19
20
<title>Traverse — ${escapeHTML(diagram.summary)}</title>
20
20
-
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
21
21
+
<link rel="icon" href="/icon.svg" type="image/svg+xml" />${baseUrl && diagramId ? `
22
22
+
<meta property="og:type" content="website" />
23
23
+
<meta property="og:title" content="${escapeHTML(diagram.summary)}" />
24
24
+
<meta property="og:description" content="Interactive code walkthrough with ${Object.keys(diagram.nodes).length} nodes" />
25
25
+
<meta property="og:image" content="${escapeHTML(baseUrl)}/diagram/${escapeHTML(diagramId)}/og.png" />
26
26
+
<meta name="twitter:card" content="summary_large_image" />
27
27
+
<meta name="twitter:title" content="${escapeHTML(diagram.summary)}" />
28
28
+
<meta name="twitter:image" content="${escapeHTML(baseUrl)}/diagram/${escapeHTML(diagramId)}/og.png" />` : ""}
21
29
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11/styles/github-dark.min.css" id="hljs-dark" disabled />
22
30
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11/styles/github.min.css" id="hljs-light" />
23
31
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/highlight.min.js"></script>