@timber-js/app 0.2.0-alpha.170 → 0.2.0-alpha.171
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/dist/fonts/google.d.ts.map +1 -1
- package/dist/index.js +38 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/fonts/google.ts +63 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timber-js/app",
|
|
3
|
-
"version": "0.2.0-alpha.
|
|
3
|
+
"version": "0.2.0-alpha.171",
|
|
4
4
|
"description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"nuqs": "^2.0.0",
|
|
171
171
|
"react": "19.2.7",
|
|
172
172
|
"react-dom": "19.2.7",
|
|
173
|
-
"vite": "8.1.0",
|
|
173
|
+
"vite": "^8.1.0",
|
|
174
174
|
"wrangler": "^4.0.0",
|
|
175
175
|
"zod": "^3.22.0 || ^4.0.0"
|
|
176
176
|
},
|
package/src/fonts/google.ts
CHANGED
|
@@ -43,6 +43,28 @@ function extractNetworkErrorCode(err: unknown): string | undefined {
|
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const MAX_RETRIES = 3;
|
|
47
|
+
const MIN_TIMEOUT_MS = 100;
|
|
48
|
+
|
|
49
|
+
async function retry<T>(fn: () => Promise<T>): Promise<T> {
|
|
50
|
+
let lastError: unknown;
|
|
51
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
52
|
+
try {
|
|
53
|
+
return await fn();
|
|
54
|
+
} catch (err) {
|
|
55
|
+
lastError = err;
|
|
56
|
+
if (attempt < MAX_RETRIES) {
|
|
57
|
+
const delay = MIN_TIMEOUT_MS * 2 ** attempt;
|
|
58
|
+
console.error(
|
|
59
|
+
`${err instanceof Error ? err.message : String(err)}\n\nRetrying ${attempt + 1}/${MAX_RETRIES}...`
|
|
60
|
+
);
|
|
61
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
throw lastError;
|
|
66
|
+
}
|
|
67
|
+
|
|
46
68
|
/** Google Fonts CSS API v2 base URL. */
|
|
47
69
|
const GOOGLE_FONTS_API = 'https://fonts.googleapis.com/css2';
|
|
48
70
|
|
|
@@ -134,28 +156,31 @@ export function buildGoogleFontsUrl(font: ExtractedFont): string {
|
|
|
134
156
|
* We parse each block to extract the font URL, unicode-range, and subset label.
|
|
135
157
|
*/
|
|
136
158
|
export async function fetchGoogleFontsCss(url: string): Promise<GoogleFontFace[]> {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
const css = await retry(async () => {
|
|
160
|
+
let response: Response;
|
|
161
|
+
try {
|
|
162
|
+
response = await fetch(url, {
|
|
163
|
+
headers: { 'User-Agent': WOFF2_USER_AGENT },
|
|
164
|
+
});
|
|
165
|
+
} catch (err) {
|
|
166
|
+
const code = extractNetworkErrorCode(err);
|
|
167
|
+
throw new Error(
|
|
168
|
+
`[timber] Failed to fetch Google Fonts CSS from ${url}` +
|
|
169
|
+
(code ? ` (${code})` : '') +
|
|
170
|
+
`. Build requires internet access for font optimization.` +
|
|
171
|
+
` If building offline, set TIMBER_SKIP_FONTS=1 to skip font downloading.`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
151
174
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
if (!response.ok) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Google Fonts API returned ${response.status}: ${response.statusText} for ${url}`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return response.text();
|
|
182
|
+
});
|
|
157
183
|
|
|
158
|
-
const css = await response.text();
|
|
159
184
|
return parseGoogleFontsCss(css);
|
|
160
185
|
}
|
|
161
186
|
|
|
@@ -238,22 +263,24 @@ export function cacheKey(face: GoogleFontFace): string {
|
|
|
238
263
|
* Download a single font file from its URL.
|
|
239
264
|
*/
|
|
240
265
|
export async function downloadFontFile(url: string): Promise<Buffer> {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
266
|
+
return retry(async () => {
|
|
267
|
+
let response: Response;
|
|
268
|
+
try {
|
|
269
|
+
response = await fetch(url);
|
|
270
|
+
} catch (err) {
|
|
271
|
+
const code = extractNetworkErrorCode(err);
|
|
272
|
+
throw new Error(
|
|
273
|
+
`[timber] Failed to download font file from ${url}` +
|
|
274
|
+
(code ? ` (${code})` : '') +
|
|
275
|
+
`. Build requires internet access for font optimization.` +
|
|
276
|
+
` If building offline, set TIMBER_SKIP_FONTS=1 to skip font downloading.`
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
if (!response.ok) {
|
|
280
|
+
throw new Error(`Failed to download font from ${url}: ${response.status}`);
|
|
281
|
+
}
|
|
282
|
+
return Buffer.from(await response.arrayBuffer());
|
|
283
|
+
});
|
|
257
284
|
}
|
|
258
285
|
|
|
259
286
|
/**
|