@cocreate/file-server 1.18.9 → 1.18.10

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.18.10](https://github.com/CoCreate-app/CoCreate-file-server/compare/v1.18.9...v1.18.10) (2025-11-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * enhance font and data URI handling in file processing ([25b7e8c](https://github.com/CoCreate-app/CoCreate-file-server/commit/25b7e8c1976c12ea99f1806025c248e0fa865ef0))
7
+
1
8
  ## [1.18.9](https://github.com/CoCreate-app/CoCreate-file-server/compare/v1.18.8...v1.18.9) (2025-10-11)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/file-server",
3
- "version": "1.18.9",
3
+ "version": "1.18.10",
4
4
  "description": "A simple file-server component in vanilla javascript. Easily configured using HTML5 data-attributes and/or JavaScript API.",
5
5
  "keywords": [
6
6
  "file-server",
package/src/index.js CHANGED
@@ -217,48 +217,74 @@ class CoCreateFileSystem {
217
217
 
218
218
  let contentType = file["content-type"] || "text/html";
219
219
 
220
- // Remove redundant handling for `src.src` in font file processing
221
- if (contentType.startsWith("font/") || /\.(woff2?|ttf|otf)$/i.test(pathname)) {
222
- try {
223
- if (typeof src === "string") {
224
- if (src.startsWith("data:font/")) {
225
- src = src.substring(src.indexOf(",") + 1);
220
+ // Normalize and decode src based on content-type and pathname.
221
+ async function normalizeSrc(src, contentType, pathname) {
222
+ const isBase64Only = (s) =>
223
+ typeof s === "string" && /^[A-Za-z0-9+/]+={0,2}$/.test(s) && s.length % 4 === 0;
224
+
225
+ const parseDataUri = (s) => {
226
+ // returns { mime, isBase64, data } or null
227
+ const m = /^data:([^;]+)(;base64)?,(.*)$/is.exec(s);
228
+ if (!m) return null;
229
+ return { mime: m[1].toLowerCase(), isBase64: !!m[2], data: m[3] };
230
+ };
231
+
232
+ // Fonts: data:font/...;base64 or plain base64 -> Buffer
233
+ if (contentType.startsWith("font/") || /\.(woff2?|ttf|otf)$/i.test(pathname)) {
234
+ if (Buffer.isBuffer(src)) return src;
235
+ if (typeof src !== "string") throw new Error("Invalid font src");
236
+ const d = parseDataUri(src);
237
+ if (d && d.isBase64) return Buffer.from(d.data, "base64");
238
+ // maybe stored as bare base64
239
+ if (isBase64Only(src)) return Buffer.from(src, "base64");
240
+ throw new Error("Font data is not valid base64 or data URI");
241
+ }
242
+
243
+ // Data URIs
244
+ if (typeof src === "string") {
245
+ const d = parseDataUri(src);
246
+ if (d) {
247
+ // SVG: decode to utf8 string so browsers render SVG correctly
248
+ if (d.mime === "image/svg+xml") {
249
+ if (d.isBase64) return Buffer.from(d.data, "base64").toString("utf8");
250
+ // URI-encoded SVG payload
251
+ try {
252
+ return decodeURIComponent(d.data);
253
+ } catch (_) {
254
+ return d.data;
255
+ }
226
256
  }
227
- if (/^([A-Za-z0-9+/]+={0,2})$/.test(src)) {
228
- // Decode base64-encoded font data
229
- src = Buffer.from(src, "base64");
230
- } else {
231
- throw new Error("Font data is not valid base64");
257
+ // Raster images -> Buffer
258
+ if (/^image\/(png|jpe?g|webp|bmp|gif)$/i.test(d.mime)) {
259
+ if (d.isBase64) return Buffer.from(d.data, "base64");
260
+ }
261
+ // If it's a text data URI (e.g., xml) and not base64, return decoded text
262
+ if (!d.isBase64) {
263
+ try {
264
+ return decodeURIComponent(d.data);
265
+ } catch (_) {
266
+ return d.data;
267
+ }
232
268
  }
233
- } else {
234
- throw new Error("Font data is not a valid base64 string");
235
269
  }
236
- } catch (err) {
237
- console.error("Error processing font file:", {
238
- message: err.message,
239
- contentType,
240
- srcType: typeof src
241
- });
242
- let pageNotFound = await getDefaultFile("/404.html");
243
- return sendResponse(pageNotFound.object[0].src, 404, {
244
- "Content-Type": "text/html"
245
- });
246
270
  }
247
- } else if (
248
- /^data:image\/[a-zA-Z0-9+.-]+;base64,([A-Za-z0-9+/]+={0,2})$/.test(
249
- src
250
- )
251
- ) {
252
- src = src.replace(/^data:image\/(png|jpeg|jpg);base64,/, "");
253
- src = Buffer.from(src, "base64");
254
- } else if (/^([A-Za-z0-9+/]+={0,2})$/.test(src)) {
255
- src = Buffer.from(src, "base64");
256
- } else if (contentType === "text/html") {
257
- try {
258
- let data = {};
259
271
 
260
- // pass detected theme (may be undefined) into server-side renderer
261
- src = await this.render.HTML(
272
+ // Plain base64-only string: decode to Buffer only for binary content types
273
+ if (isBase64Only(src)) {
274
+ if (
275
+ contentType.startsWith("image/") ||
276
+ contentType.startsWith("font/") ||
277
+ contentType === "application/octet-stream"
278
+ ) {
279
+ return Buffer.from(src, "base64");
280
+ }
281
+ // textual content kept as-is
282
+ return src;
283
+ }
284
+
285
+ // HTML rendering
286
+ if (contentType === "text/html") {
287
+ return await this.render.HTML(
262
288
  file,
263
289
  organization,
264
290
  urlObject,
@@ -266,20 +292,28 @@ class CoCreateFileSystem {
266
292
  lang,
267
293
  theme
268
294
  );
269
- } catch (err) {
270
- console.warn("server-side-render: " + err.message);
271
295
  }
272
- } else if (
273
- contentType === "text/xml" ||
274
- contentType === "application/xml"
275
- ) {
276
- const protocol = "https://";
277
- src = src.replaceAll("{{$host}}", `${protocol}${hostname}`);
278
- } else if (contentType !== "text/javascript" || contentType === "text/css") {
279
-
280
- console.warn(`Unknown content type: ${contentType}`);
296
+
297
+ // XML host replacement if src is string
298
+ if ((contentType === "text/xml" || contentType === "application/xml") && typeof src === "string") {
299
+ const protocol = "https://";
300
+ return src.replaceAll("{{$host}}", `${protocol}${hostname}`);
301
+ }
302
+
303
+ // Otherwise return as-is (string, Buffer, object)
304
+ return src;
281
305
  }
282
306
 
307
+ try {
308
+ src = await normalizeSrc.call(this, src, contentType, pathname);
309
+ } catch (err) {
310
+ console.error("Error processing file src:", err && err.message);
311
+ let pageNotFound = await getDefaultFile("/404.html");
312
+ return sendResponse(pageNotFound.object[0].src, 404, {
313
+ "Content-Type": "text/html"
314
+ });
315
+ }
316
+
283
317
  sendResponse(src, 200, { "Content-Type": contentType });
284
318
  this.sitemap.check(file, hostname);
285
319