@ethercorps/sveltekit-og 4.3.0-next.2 → 4.3.0-next.4

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.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { FONT_CACHE_MAP } from "./helpers/cache.js";
2
- import { logger } from "./helpers/logger.js";
3
2
  /** * Base font class defining the structure required by Satori.
4
3
  * All font types inherit from this class.
5
4
  */
@@ -64,26 +63,22 @@ export const loadGoogleFont = async (family, { text, weight = 400, style = "norm
64
63
  }
65
64
  const cssResponse = await fetch(cssUrl);
66
65
  if (!cssResponse.ok) {
67
- logger.error(`Failed to fetch Google Font CSS for ${family}. Status: ${cssResponse.status}`);
68
66
  throw new Error(`Failed to fetch Google Font CSS for ${family}. Status: ${cssResponse.status}`);
69
67
  }
70
68
  const css = await cssResponse.text();
71
69
  // 3. Extract the font file URL (the actual TTF/OTF file)
72
70
  const fontUrl = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/)?.[1];
73
71
  if (!fontUrl) {
74
- logger.error(`Could not find a compatible truetype font source in the CSS for ${family}.`);
75
72
  throw new Error(`Could not find a compatible truetype font source in the CSS for ${family}.`);
76
73
  }
77
74
  // 4. Fetch the font buffer
78
75
  const fontResponse = await fetch(fontUrl);
79
76
  if (!fontResponse.ok) {
80
- logger.error(`Failed to fetch font file from URL. Status: ${fontResponse.status}`);
81
77
  throw new Error(`Failed to fetch font file from URL. Status: ${fontResponse.status}`);
82
78
  }
83
79
  const buffer = await fontResponse.arrayBuffer();
84
80
  // 5. CACHE AND RETURN: Store the resolved ArrayBuffer in the Map, keyed by the CSS URL.
85
81
  FONT_CACHE_MAP.set(cssUrl, buffer);
86
- logger.debug(`Loaded Google Font: ${family}`);
87
82
  return buffer;
88
83
  };
89
84
  export class GoogleFont extends BaseFont {
@@ -2,10 +2,11 @@ import { loadDynamicAsset } from "./emoji.js";
2
2
  import { default_fonts, DEFAULT_WIDTH } from "../helpers/defaults.js";
3
3
  import { useResvg, useSatori } from "../providers/instances.js";
4
4
  import { createVNode } from "./toJSX.js";
5
- import { logger } from "./logger.js";
5
+ import { createLogger } from "./logger.js";
6
6
  import { handleAsyncAll, handleAsync, ErrorCodes } from "./error-handler.js";
7
7
  export async function createSvg(element, imageOptions, componentOptions) {
8
- const [satori, vnodes] = await handleAsyncAll([() => useSatori(), () => Promise.resolve(createVNode(element, componentOptions))], ErrorCodes.SATORI_RENDER_FAILED, "Failed to initialize Satori or create VNode");
8
+ const log = createLogger(imageOptions.debug ?? false);
9
+ const [satori, vnodes] = await handleAsyncAll([() => useSatori(imageOptions.debug), () => Promise.resolve(createVNode(element, componentOptions))], ErrorCodes.SATORI_RENDER_FAILED, "Failed to initialize Satori or create VNode");
9
10
  const satoriOptions = structuredClone(imageOptions);
10
11
  if (!Object.hasOwn(satoriOptions, "fonts")) {
11
12
  satoriOptions["fonts"] = await handleAsync(() => default_fonts(), ErrorCodes.FONT_LOAD_FAILED, "Failed to load default fonts for Satori");
@@ -13,23 +14,24 @@ export async function createSvg(element, imageOptions, componentOptions) {
13
14
  satoriOptions["loadAdditionalAsset"] = loadDynamicAsset({
14
15
  emoji: imageOptions.emoji,
15
16
  });
16
- logger.debug("Generating SVG with Satori");
17
- logger.info("VNode provided to satori:", JSON.stringify(vnodes, null, 2), "\n");
18
- logger.info("Options provided to satori:", imageOptions);
17
+ log.debug("Generating SVG with Satori");
18
+ log.info("VNode provided to satori:", JSON.stringify(vnodes, null, 2), "\n");
19
+ log.info("Options provided to satori:", imageOptions);
19
20
  return handleAsync(() => satori(vnodes, satoriOptions), ErrorCodes.SATORI_RENDER_FAILED, "Failed to render SVG with Satori");
20
21
  }
21
22
  export async function createPng(element, imageOptions, componentOptions) {
23
+ const log = createLogger(imageOptions.debug ?? false);
22
24
  const svg = await handleAsync(() => createSvg(element, imageOptions, componentOptions), ErrorCodes.SATORI_RENDER_FAILED, "Failed to create SVG for PNG rendering");
23
- logger.debug("SVG generated by satori for ReSVG: \n", svg, "\n");
24
- const resvg_instance = await handleAsync(() => useResvg(), ErrorCodes.RESVG_INIT_FAILED, "Failed to initialize ReSVG");
25
+ log.debug("SVG generated by satori for ReSVG: \n", svg, "\n");
26
+ const resvg_instance = await handleAsync(() => useResvg(imageOptions.debug), ErrorCodes.RESVG_INIT_FAILED, "Failed to initialize ReSVG");
25
27
  const resvg_options = {
26
28
  fitTo: {
27
29
  mode: "width",
28
30
  value: imageOptions.width || DEFAULT_WIDTH,
29
31
  },
30
32
  };
31
- logger.debug("Rendering PNG with ReSVG");
32
- logger.info("Options provided to ReSVG:", resvg_options, "\n");
33
+ log.debug("Rendering PNG with ReSVG");
34
+ log.info("Options provided to ReSVG:", resvg_options, "\n");
33
35
  return handleAsync(async () => {
34
36
  const resvg = new resvg_instance(svg, resvg_options);
35
37
  const png_data = resvg.render();
@@ -1,4 +1,3 @@
1
- import { logger } from "./logger.js";
2
1
  export class ImageResponseError extends Error {
3
2
  code;
4
3
  originalError;
@@ -28,7 +27,6 @@ export async function handleAsync(operation, errorCode, errorMessage) {
28
27
  }
29
28
  catch (error) {
30
29
  const err = error instanceof Error ? error : new Error(String(error));
31
- logger.error(`${errorMessage}:`, err.message);
32
30
  throw new ImageResponseError(errorMessage, errorCode, err);
33
31
  }
34
32
  }
@@ -41,7 +39,6 @@ export function handleSync(operation, errorCode, errorMessage) {
41
39
  }
42
40
  catch (error) {
43
41
  const err = error instanceof Error ? error : new Error(String(error));
44
- logger.error(`${errorMessage}:`, err.message);
45
42
  throw new ImageResponseError(errorMessage, errorCode, err);
46
43
  }
47
44
  }
@@ -54,7 +51,6 @@ export async function handleAsyncAll(operations, errorCode, errorMessage) {
54
51
  }
55
52
  catch (error) {
56
53
  const err = error instanceof Error ? error : new Error(String(error));
57
- logger.error(`${errorMessage}:`, err.message);
58
54
  throw new ImageResponseError(errorMessage, errorCode, err);
59
55
  }
60
56
  }
@@ -63,7 +59,6 @@ export async function handleAsyncAll(operations, errorCode, errorMessage) {
63
59
  */
64
60
  export async function validateResponse(response, errorCode, errorMessage) {
65
61
  if (!response.ok) {
66
- logger.error(`${errorMessage}: HTTP ${response.status} ${response.statusText}`);
67
62
  throw new ImageResponseError(`${errorMessage} (HTTP ${response.status})`, errorCode);
68
63
  }
69
64
  const buffer = await response.arrayBuffer();
@@ -1,13 +1,5 @@
1
- /**
2
- * Set the debug flag for the current request context
3
- * Call this once at the beginning of your request handler
4
- */
5
- export declare function setDebug(enabled: boolean): void;
6
- /**
7
- * Get the current debug flag from the request context
8
- */
9
- export declare function isDebugEnabled(): boolean;
10
- export declare const logger: {
1
+ export type Logger = ReturnType<typeof createLogger>;
2
+ export declare function createLogger(debug: boolean): {
11
3
  debug: (message: string, ...args: unknown[]) => void;
12
4
  info: (message: string, ...args: unknown[]) => void;
13
5
  warn: (message: string, ...args: unknown[]) => void;
@@ -1,39 +1,21 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
1
  const PREFIX = '[SvelteKit-OG]';
3
- // Store for request-scoped debug flag
4
- const debugStorage = new AsyncLocalStorage();
5
- /**
6
- * Set the debug flag for the current request context
7
- * Call this once at the beginning of your request handler
8
- */
9
- export function setDebug(enabled) {
10
- debugStorage.enterWith(enabled);
11
- }
12
- /**
13
- * Get the current debug flag from the request context
14
- */
15
- export function isDebugEnabled() {
16
- return debugStorage.getStore() ?? false;
17
- }
18
- export const logger = {
19
- debug: (message, ...args) => {
20
- if (isDebugEnabled()) {
21
- console.log(`${PREFIX} 🔍 ${message}`, ...args);
22
- }
23
- },
24
- info: (message, ...args) => {
25
- if (isDebugEnabled()) {
26
- console.info(`${PREFIX} â„šī¸ ${message}`, ...args);
2
+ export function createLogger(debug) {
3
+ return {
4
+ debug: (message, ...args) => {
5
+ if (debug)
6
+ console.log(`${PREFIX} 🔍 ${message}`, ...args);
7
+ },
8
+ info: (message, ...args) => {
9
+ if (debug)
10
+ console.info(`${PREFIX} â„šī¸ ${message}`, ...args);
11
+ },
12
+ warn: (message, ...args) => {
13
+ if (debug)
14
+ console.warn(`${PREFIX} âš ī¸ ${message}`, ...args);
15
+ },
16
+ error: (message, ...args) => {
17
+ if (debug)
18
+ console.error(`${PREFIX} ❌ ${message}`, ...args);
27
19
  }
28
- },
29
- warn: (message, ...args) => {
30
- if (isDebugEnabled()) {
31
- console.warn(`${PREFIX} âš ī¸ ${message}`, ...args);
32
- }
33
- },
34
- error: (message, ...args) => {
35
- if (isDebugEnabled()) {
36
- console.error(`${PREFIX} ❌ ${message}`, ...args);
37
- }
38
- }
39
- };
20
+ };
21
+ }
@@ -1,13 +1,14 @@
1
1
  import { DEFAULT_OPTIONS, DEFAULT_STATUS_CODE, DEFAULT_STATUS_TEXT } from "./helpers/defaults.js";
2
2
  import { createPng, createSvg } from "./helpers/create.js";
3
- import { isDebugEnabled, logger, setDebug } from "./helpers/logger.js";
3
+ import { createLogger } from "./helpers/logger.js";
4
4
  import { handleAsync, ImageResponseError, ErrorCodes } from "./helpers/error-handler.js";
5
5
  import { formatBytes } from "./helpers/utils.js";
6
6
  export class ImageResponse extends Response {
7
7
  constructor(element, options, props) {
8
8
  const extended_options = Object.assign({ ...DEFAULT_OPTIONS }, options);
9
- setDebug(extended_options.debug ?? false);
10
- logger.debug("Debug mode", isDebugEnabled());
9
+ const isDebug = extended_options.debug ?? false;
10
+ const log = createLogger(isDebug);
11
+ log.debug("ImageResponse created");
11
12
  const create_image_function = extended_options.format === "png" ? createPng : createSvg;
12
13
  const body = new ReadableStream({
13
14
  async start(controller) {
@@ -15,8 +16,8 @@ export class ImageResponse extends Response {
15
16
  const buffer = (await handleAsync(() => create_image_function(element, extended_options, {
16
17
  props,
17
18
  }), ErrorCodes.UNKNOWN_ERROR, `Failed to generate ${extended_options.format?.toUpperCase()}`));
18
- logger.debug(buffer.length.toLocaleString());
19
- logger.info(`Generated ${extended_options.format.toUpperCase()}: ${formatBytes(buffer.length)}`);
19
+ log.debug(buffer.length.toLocaleString());
20
+ log.info(`Generated ${extended_options.format.toUpperCase()}: ${formatBytes(buffer.length)}`);
20
21
  controller.enqueue(buffer);
21
22
  controller.close();
22
23
  }
@@ -24,7 +25,7 @@ export class ImageResponse extends Response {
24
25
  const err = error instanceof ImageResponseError
25
26
  ? error
26
27
  : new ImageResponseError(error instanceof Error ? error.message : String(error), ErrorCodes.UNKNOWN_ERROR, error instanceof Error ? error : new Error(String(error)));
27
- logger.error("Failed to create image response:", err.message);
28
+ log.error("Failed to create image response:", err.message);
28
29
  controller.error(err);
29
30
  }
30
31
  },
@@ -1,5 +1,5 @@
1
1
  import type _satori from "satori";
2
- export declare function useResvg(): Promise<new (svg: Uint8Array | string, options?: import("@resvg/resvg-wasm").ResvgRenderOptions) => {
2
+ export declare function useResvg(debug?: boolean): Promise<new (svg: Uint8Array | string, options?: import("@resvg/resvg-wasm").ResvgRenderOptions) => {
3
3
  free(): void;
4
4
  render(): {
5
5
  free(): void;
@@ -35,4 +35,4 @@ export declare function useResvg(): Promise<new (svg: Uint8Array | string, optio
35
35
  readonly height: number;
36
36
  readonly width: number;
37
37
  }>;
38
- export declare function useSatori(): Promise<typeof _satori>;
38
+ export declare function useSatori(debug?: boolean): Promise<typeof _satori>;
@@ -1,5 +1,5 @@
1
1
  import { isEdgeLight, isWorkerd } from "std-env";
2
- import { logger } from "../helpers/logger.js";
2
+ import { createLogger } from "../helpers/logger.js";
3
3
  import { handleAsync, ErrorCodes } from "../helpers/error-handler.js";
4
4
  // we keep instances alive to avoid re-importing them on every request, maybe not needed but
5
5
  // also helps with type inference
@@ -10,13 +10,14 @@ const resvgInstance = {
10
10
  const satoriInstance = {
11
11
  instance: undefined,
12
12
  };
13
- export async function useResvg() {
13
+ export async function useResvg(debug = false) {
14
+ const log = createLogger(debug);
14
15
  if (resvgInstance.instance) {
15
16
  return resvgInstance.instance.Resvg;
16
17
  }
17
- logger.debug("Initializing Resvg WASM");
18
+ log.debug("Initializing Resvg WASM");
18
19
  const isWorkerLikeRuntime = isEdgeLight || isWorkerd;
19
- logger.info(`Detected runtime: ${isWorkerLikeRuntime ? "Edge Light or Workerd" : "Node.js"}`);
20
+ log.info(`Detected runtime: ${isWorkerLikeRuntime ? "Edge Light or Workerd" : "Node.js"}`);
20
21
  const moduleImport = await handleAsync(async () => {
21
22
  if (isWorkerLikeRuntime) {
22
23
  return import("./resvg/edge.js");
@@ -30,11 +31,12 @@ export async function useResvg() {
30
31
  await handleAsync(async () => resvgInstance.instance.initWasmPromise, ErrorCodes.RESVG_INIT_FAILED, "Failed to initialize ReSVG WASM");
31
32
  return resvgInstance.instance.Resvg;
32
33
  }
33
- export async function useSatori() {
34
+ export async function useSatori(debug = false) {
35
+ const log = createLogger(debug);
34
36
  if (satoriInstance.instance) {
35
37
  return satoriInstance.instance.satori;
36
38
  }
37
- logger.debug("Initializing Satori WASM");
39
+ log.debug("Initializing Satori WASM");
38
40
  satoriInstance.instance = await handleAsync(async () => {
39
41
  const mod = await import("./satori/node.js");
40
42
  return mod.default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethercorps/sveltekit-og",
3
- "version": "4.3.0-next.2",
3
+ "version": "4.3.0-next.4",
4
4
  "license": "MIT",
5
5
  "homepage": "https://sveltekit-og.dev",
6
6
  "repository": "github:ethercorps/sveltekit-og",