@takumi-rs/image-response 1.0.0-beta.2 → 1.0.0-beta.20
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/README.md +47 -35
- package/dist/response.cjs +291 -0
- package/dist/response.d.cts +62 -0
- package/dist/response.d.ts +62 -0
- package/dist/response.js +263 -0
- package/package.json +31 -31
- package/dist/node.cjs +0 -1
- package/dist/node.d.cts +0 -27
- package/dist/node.d.ts +0 -27
- package/dist/node.js +0 -1
- package/dist/wasm.cjs +0 -1
- package/dist/wasm.d.cts +0 -58
- package/dist/wasm.d.ts +0 -58
- package/dist/wasm.js +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Checkout the migration guide [From Next.js ImageResponse](https://takumi.kane.tw
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @takumi-rs/image-response
|
|
10
|
+
npm install @takumi-rs/image-response
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Usage
|
|
@@ -27,11 +27,31 @@ export function GET(request: Request) {
|
|
|
27
27
|
}
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
For shared configuration and cache isolation, create your own image response factory.
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { createImageResponse } from "@takumi-rs/image-response";
|
|
34
|
+
|
|
35
|
+
const ogImage = createImageResponse({
|
|
36
|
+
fonts: [
|
|
37
|
+
{
|
|
38
|
+
data: () => fetch("/fonts/Inter-Regular.ttf").then((res) => res.arrayBuffer()),
|
|
39
|
+
key: "inter-regular",
|
|
40
|
+
name: "Inter",
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export function GET() {
|
|
46
|
+
return ogImage(<OgImage />);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
30
50
|
### Fonts
|
|
31
51
|
|
|
32
|
-
Takumi comes with full
|
|
52
|
+
Takumi comes with full-axis [Geist](https://vercel.com/font) and Geist Mono by default.
|
|
33
53
|
|
|
34
|
-
We have global fonts cache to avoid loading the same fonts multiple times.
|
|
54
|
+
We have a global fonts cache to avoid loading the same fonts multiple times.
|
|
35
55
|
|
|
36
56
|
If your environment supports top-level await, you can load the fonts in global scope and reuse the fonts array.
|
|
37
57
|
|
|
@@ -48,30 +68,27 @@ const fonts = [
|
|
|
48
68
|
new ImageResponse(<OgImage />, { fonts });
|
|
49
69
|
```
|
|
50
70
|
|
|
51
|
-
If your environment doesn't support top-level await,
|
|
71
|
+
If your environment doesn't support top-level await, you can pass a lazy `data` loader instead.
|
|
52
72
|
|
|
53
73
|
```tsx
|
|
54
|
-
let isFontsLoaded = false;
|
|
55
|
-
|
|
56
74
|
export function GET(request: Request) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (!isFontsLoaded) {
|
|
60
|
-
isFontsLoaded = true;
|
|
61
|
-
fonts = [
|
|
75
|
+
return new ImageResponse(<OgImage />, {
|
|
76
|
+
fonts: [
|
|
62
77
|
{
|
|
63
78
|
name: "Inter",
|
|
64
|
-
data:
|
|
79
|
+
data: () => fetch("/fonts/Inter-Regular.ttf").then((res) => res.arrayBuffer()),
|
|
65
80
|
style: "normal",
|
|
66
81
|
weight: 400,
|
|
67
82
|
},
|
|
68
|
-
]
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return new ImageResponse(<OgImage />, { fonts });
|
|
83
|
+
],
|
|
84
|
+
});
|
|
72
85
|
}
|
|
73
86
|
```
|
|
74
87
|
|
|
88
|
+
The same pattern also works for `persistentImages`. Caches are scoped to each `createImageResponse()` instance.
|
|
89
|
+
|
|
90
|
+
`loadDefaultFonts` is only supported by the native `@takumi-rs/core` renderer. It has no effect when using the WASM renderer.
|
|
91
|
+
|
|
75
92
|
### Bring-Your-Own-Renderer (BYOR)
|
|
76
93
|
|
|
77
94
|
If you want to use your own renderer instance, you can pass it to the `ImageResponse` constructor.
|
|
@@ -89,33 +106,28 @@ new ImageResponse(<OgImage />, { renderer });
|
|
|
89
106
|
You can pass the JSX options to the `ImageResponse` constructor.
|
|
90
107
|
|
|
91
108
|
```tsx
|
|
92
|
-
new ImageResponse(<OgImage />, {
|
|
93
|
-
jsx: {
|
|
109
|
+
new ImageResponse(<OgImage />, {
|
|
110
|
+
jsx: {
|
|
94
111
|
defaultStyles: false,
|
|
95
|
-
}
|
|
112
|
+
},
|
|
96
113
|
});
|
|
97
114
|
```
|
|
98
115
|
|
|
99
|
-
|
|
116
|
+
### Error Handling
|
|
100
117
|
|
|
101
|
-
|
|
118
|
+
`ImageResponse` exposes a `ready` promise that resolves when rendering succeeds and rejects when rendering fails.
|
|
102
119
|
|
|
103
|
-
|
|
120
|
+
```tsx
|
|
121
|
+
const response = new ImageResponse(<OgImage />);
|
|
104
122
|
|
|
105
|
-
|
|
123
|
+
await response.ready;
|
|
124
|
+
return response;
|
|
125
|
+
```
|
|
106
126
|
|
|
107
|
-
|
|
127
|
+
You can also provide `onError` to render a fallback image instead of failing the response stream.
|
|
108
128
|
|
|
109
129
|
```tsx
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
export default {
|
|
115
|
-
fetch() {
|
|
116
|
-
return new ImageResponse(<div>Hello</div>, {
|
|
117
|
-
module,
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
}
|
|
130
|
+
const response = new ImageResponse(<OgImage />, {
|
|
131
|
+
onError: () => <div>Failed to generate image</div>,
|
|
132
|
+
});
|
|
121
133
|
```
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: !0 });
|
|
10
|
+
}, __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from == "object" || typeof from == "function")
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
!__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target,
|
|
22
|
+
mod
|
|
23
|
+
)), __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: !0 }), mod);
|
|
24
|
+
|
|
25
|
+
// src/response.tsx
|
|
26
|
+
var response_exports = {};
|
|
27
|
+
__export(response_exports, {
|
|
28
|
+
ImageResponse: () => ImageResponse,
|
|
29
|
+
createImageResponse: () => createImageResponse,
|
|
30
|
+
default: () => response_default
|
|
31
|
+
});
|
|
32
|
+
module.exports = __toCommonJS(response_exports);
|
|
33
|
+
var import_helpers = require("@takumi-rs/helpers"), import_emoji = require("@takumi-rs/helpers/emoji"), import_jsx = require("@takumi-rs/helpers/jsx");
|
|
34
|
+
|
|
35
|
+
// src/cache.ts
|
|
36
|
+
function createResourceCache() {
|
|
37
|
+
return {
|
|
38
|
+
functionDataCache: /* @__PURE__ */ new WeakMap(),
|
|
39
|
+
loadedFontKeys: /* @__PURE__ */ new Set(),
|
|
40
|
+
loadedFontObjects: /* @__PURE__ */ new WeakSet(),
|
|
41
|
+
loadedImageKeys: /* @__PURE__ */ new Set(),
|
|
42
|
+
loadedImageObjects: /* @__PURE__ */ new WeakSet(),
|
|
43
|
+
promiseDataCache: /* @__PURE__ */ new WeakMap(),
|
|
44
|
+
stringDataCache: /* @__PURE__ */ new Map()
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function getOrCreateStringDataCache(cache, key, load) {
|
|
48
|
+
let cached = cache.stringDataCache.get(key);
|
|
49
|
+
if (cached)
|
|
50
|
+
return cached;
|
|
51
|
+
let pending = Promise.resolve(load()).catch((error) => {
|
|
52
|
+
throw cache.stringDataCache.delete(key), error;
|
|
53
|
+
});
|
|
54
|
+
return cache.stringDataCache.set(key, pending), pending;
|
|
55
|
+
}
|
|
56
|
+
function getOrCreateFunctionDataCache(cache, loader) {
|
|
57
|
+
let cached = cache.functionDataCache.get(loader);
|
|
58
|
+
if (cached)
|
|
59
|
+
return cached;
|
|
60
|
+
let pending = Promise.resolve(loader()).catch((error) => {
|
|
61
|
+
throw cache.functionDataCache.delete(loader), error;
|
|
62
|
+
});
|
|
63
|
+
return cache.functionDataCache.set(loader, pending), pending;
|
|
64
|
+
}
|
|
65
|
+
function getOrCreatePromiseDataCache(cache, promise) {
|
|
66
|
+
let cached = cache.promiseDataCache.get(promise);
|
|
67
|
+
if (cached)
|
|
68
|
+
return cached;
|
|
69
|
+
let pending = promise.catch((error) => {
|
|
70
|
+
throw cache.promiseDataCache.delete(promise), error;
|
|
71
|
+
});
|
|
72
|
+
return cache.promiseDataCache.set(promise, pending), pending;
|
|
73
|
+
}
|
|
74
|
+
function hasBinaryData(value) {
|
|
75
|
+
return value instanceof Uint8Array || value instanceof ArrayBuffer;
|
|
76
|
+
}
|
|
77
|
+
function createFontCacheKey(font) {
|
|
78
|
+
return hasBinaryData(font) ? font : font.key ? `font:${font.key}` : font;
|
|
79
|
+
}
|
|
80
|
+
function createImageCacheKey(image) {
|
|
81
|
+
return image.key ? `image:${image.key}` : image;
|
|
82
|
+
}
|
|
83
|
+
async function resolveData(cache, data, cacheKey) {
|
|
84
|
+
return typeof data == "function" ? cacheKey ? getOrCreateStringDataCache(cache, cacheKey, data) : getOrCreateFunctionDataCache(cache, data) : data instanceof Promise ? cacheKey ? getOrCreateStringDataCache(cache, cacheKey, () => data) : getOrCreatePromiseDataCache(cache, data) : data;
|
|
85
|
+
}
|
|
86
|
+
async function resolveFont(cache, font) {
|
|
87
|
+
if (hasBinaryData(font))
|
|
88
|
+
return font;
|
|
89
|
+
let cacheKey = createFontCacheKey(font);
|
|
90
|
+
return {
|
|
91
|
+
...font,
|
|
92
|
+
data: await resolveData(cache, font.data, typeof cacheKey == "string" ? cacheKey : void 0)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function resolvePersistentImage(cache, image) {
|
|
96
|
+
let cacheKey = createImageCacheKey(image);
|
|
97
|
+
return {
|
|
98
|
+
...image,
|
|
99
|
+
data: await resolveData(cache, image.data, typeof cacheKey == "string" ? cacheKey : void 0)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/import.ts
|
|
104
|
+
var wasm = __toESM(require("@takumi-rs/wasm"), 1), importPromise = null;
|
|
105
|
+
function getImports(module2) {
|
|
106
|
+
return importPromise ??= getImportsImpl(module2), importPromise;
|
|
107
|
+
}
|
|
108
|
+
async function getImportsImpl(module2) {
|
|
109
|
+
if (module2)
|
|
110
|
+
return initializeWasm(module2);
|
|
111
|
+
let importedModule = await importBindings();
|
|
112
|
+
return importedModule && "Renderer" in importedModule ? importedModule : initializeWasm(importedModule);
|
|
113
|
+
}
|
|
114
|
+
async function initializeWasm(module2) {
|
|
115
|
+
let wasmModule = module2 !== void 0 && typeof module2 == "object" && "default" in module2 ? module2.default : module2;
|
|
116
|
+
try {
|
|
117
|
+
return await wasm.default(wasmModule ? { module_or_path: wasmModule } : void 0), wasm;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
"Couldn't automatically resolve Takumi native bindings. Please specify the module option with the WASM module.",
|
|
121
|
+
{
|
|
122
|
+
cause: error
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
async function importBindings() {
|
|
128
|
+
if (shouldSkipCoreImport())
|
|
129
|
+
return importWasmBindings();
|
|
130
|
+
try {
|
|
131
|
+
return await import("@takumi-rs/core");
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.warn(
|
|
134
|
+
"Unable to import @takumi-rs/core. Falling back to auto-detection of WASM bindings.",
|
|
135
|
+
{
|
|
136
|
+
cause: error
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
return importWasmBindings();
|
|
141
|
+
}
|
|
142
|
+
async function importWasmBindings() {
|
|
143
|
+
let nextPath = "@takumi-rs/wasm/next";
|
|
144
|
+
return typeof process < "u" && process.env.NEXT_RUNTIME ? import(
|
|
145
|
+
/* @vite-ignore */
|
|
146
|
+
nextPath
|
|
147
|
+
) : import(
|
|
148
|
+
/* turbopackIgnore: true */
|
|
149
|
+
/* webpackIgnore: true */
|
|
150
|
+
"@takumi-rs/wasm/auto"
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
function shouldSkipCoreImport() {
|
|
154
|
+
if (typeof process < "u" && process.env.NEXT_RUNTIME === "edge" || typeof window < "u" || typeof navigator < "u" && navigator.userAgent === "Cloudflare-Workers" || "WebSocketPair" in globalThis || "EdgeRuntime" in globalThis)
|
|
155
|
+
return !0;
|
|
156
|
+
let maybeWorkerGlobalScope = globalThis.WorkerGlobalScope;
|
|
157
|
+
return !!(maybeWorkerGlobalScope !== void 0 && maybeWorkerGlobalScope.prototype.isPrototypeOf(globalThis));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/response.tsx
|
|
161
|
+
var defaultFormat = "webp";
|
|
162
|
+
function hasLoadedResource(key, loadedKeys, loadedObjects) {
|
|
163
|
+
return typeof key == "string" ? loadedKeys.has(key) : loadedObjects.has(key);
|
|
164
|
+
}
|
|
165
|
+
function markLoadedResource(key, loadedKeys, loadedObjects) {
|
|
166
|
+
if (typeof key == "string") {
|
|
167
|
+
loadedKeys.add(key);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
loadedObjects.add(key);
|
|
171
|
+
}
|
|
172
|
+
function mergeOptions(defaultOptions, options) {
|
|
173
|
+
if (!defaultOptions)
|
|
174
|
+
return options;
|
|
175
|
+
if (!options)
|
|
176
|
+
return defaultOptions;
|
|
177
|
+
let headers = new Headers(defaultOptions.headers);
|
|
178
|
+
return options.headers && new Headers(options.headers).forEach((value, key) => {
|
|
179
|
+
headers.set(key, value);
|
|
180
|
+
}), "renderer" in options ? {
|
|
181
|
+
...defaultOptions,
|
|
182
|
+
...options,
|
|
183
|
+
headers
|
|
184
|
+
} : {
|
|
185
|
+
...defaultOptions,
|
|
186
|
+
...options,
|
|
187
|
+
fonts: options.fonts ?? defaultOptions.fonts,
|
|
188
|
+
headers,
|
|
189
|
+
persistentImages: options.persistentImages ?? defaultOptions.persistentImages,
|
|
190
|
+
stylesheets: [...defaultOptions.stylesheets ?? [], ...options.stylesheets ?? []]
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
var contentTypeMap = {
|
|
194
|
+
png: "image/png",
|
|
195
|
+
jpeg: "image/jpeg",
|
|
196
|
+
webp: "image/webp",
|
|
197
|
+
raw: "application/octet-stream"
|
|
198
|
+
};
|
|
199
|
+
async function renderComponent(component, options, imports, renderer) {
|
|
200
|
+
let { node: originalNode, stylesheets } = await (0, import_jsx.fromJsx)(component, options?.jsx), node = options?.emoji && options.emoji !== "from-font" ? (0, import_emoji.extractEmojis)(originalNode, options.emoji) : originalNode, fetchedResources = options?.fetchedResources !== void 0 ? options.fetchedResources : await (0, import_helpers.fetchResources)(imports.extractResourceUrls(node)), renderOptions = {
|
|
201
|
+
...options,
|
|
202
|
+
fetchedResources,
|
|
203
|
+
format: options?.format ?? defaultFormat,
|
|
204
|
+
stylesheets: [...options?.stylesheets ?? [], ...stylesheets]
|
|
205
|
+
};
|
|
206
|
+
return renderer.render(node, renderOptions, options?.signal);
|
|
207
|
+
}
|
|
208
|
+
function defaultErrorHandler(error) {
|
|
209
|
+
console.error("Takumi failed to render image."), console.error(error);
|
|
210
|
+
}
|
|
211
|
+
async function loadRendererResources(renderer, options, cache) {
|
|
212
|
+
let tasks = [];
|
|
213
|
+
if (options?.fonts && options.fonts.length > 0) {
|
|
214
|
+
let resolvedFonts = await Promise.all(
|
|
215
|
+
options.fonts.map(async (font) => ({
|
|
216
|
+
cacheKey: createFontCacheKey(font),
|
|
217
|
+
font: await resolveFont(cache, font)
|
|
218
|
+
}))
|
|
219
|
+
);
|
|
220
|
+
if ("loadFonts" in renderer) {
|
|
221
|
+
let filteredFonts = resolvedFonts.filter(({ cacheKey }) => hasLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects) ? !1 : (markLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects), !0));
|
|
222
|
+
filteredFonts.length > 0 && tasks.push(renderer.loadFonts(filteredFonts.map(({ font }) => font)));
|
|
223
|
+
} else
|
|
224
|
+
for (let { cacheKey, font } of resolvedFonts)
|
|
225
|
+
hasLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects) || (markLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects), renderer.loadFont(font));
|
|
226
|
+
}
|
|
227
|
+
if (options?.persistentImages && options.persistentImages.length > 0) {
|
|
228
|
+
let resolvedImages = await Promise.all(
|
|
229
|
+
options.persistentImages.map(async (image) => ({
|
|
230
|
+
cacheKey: createImageCacheKey(image),
|
|
231
|
+
image: await resolvePersistentImage(cache, image)
|
|
232
|
+
}))
|
|
233
|
+
);
|
|
234
|
+
for (let { cacheKey, image } of resolvedImages) {
|
|
235
|
+
if (hasLoadedResource(cacheKey, cache.loadedImageKeys, cache.loadedImageObjects))
|
|
236
|
+
continue;
|
|
237
|
+
markLoadedResource(cacheKey, cache.loadedImageKeys, cache.loadedImageObjects);
|
|
238
|
+
let maybePromise = renderer.putPersistentImage(image, options.signal);
|
|
239
|
+
maybePromise instanceof Promise && tasks.push(maybePromise);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
tasks.length > 0 && await Promise.all(tasks);
|
|
243
|
+
}
|
|
244
|
+
function createImageResponse(defaultOptions) {
|
|
245
|
+
let cache = createResourceCache();
|
|
246
|
+
return function(component, options) {
|
|
247
|
+
let mergedOptions = mergeOptions(defaultOptions, options), {
|
|
248
|
+
promise: ready,
|
|
249
|
+
reject: rejectReady,
|
|
250
|
+
resolve: resolveReady
|
|
251
|
+
} = Promise.withResolvers(), module2 = mergedOptions && "module" in mergedOptions ? mergedOptions.module : void 0, stream = new ReadableStream({
|
|
252
|
+
type: "bytes",
|
|
253
|
+
async start(controller) {
|
|
254
|
+
try {
|
|
255
|
+
let imports = await getImports(module2), isExternalRenderer = mergedOptions && "renderer" in mergedOptions, renderer = isExternalRenderer ? mergedOptions.renderer : cache.renderer ??= new imports.Renderer({
|
|
256
|
+
loadDefaultFonts: mergedOptions?.loadDefaultFonts
|
|
257
|
+
});
|
|
258
|
+
isExternalRenderer || await loadRendererResources(renderer, mergedOptions, cache);
|
|
259
|
+
let image = await renderComponent(component, mergedOptions, imports, renderer);
|
|
260
|
+
controller.enqueue(image), controller.close(), resolveReady();
|
|
261
|
+
} catch (error) {
|
|
262
|
+
controller.error(error), await (mergedOptions?.onError ?? defaultErrorHandler)(error), rejectReady(error);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}), headers = new Headers(mergedOptions?.headers);
|
|
266
|
+
headers.get("content-type") || headers.set("content-type", contentTypeMap[mergedOptions?.format ?? defaultFormat]);
|
|
267
|
+
let response = new Response(stream, {
|
|
268
|
+
headers,
|
|
269
|
+
status: mergedOptions?.status,
|
|
270
|
+
statusText: mergedOptions?.statusText
|
|
271
|
+
});
|
|
272
|
+
return Object.defineProperty(response, "ready", {
|
|
273
|
+
enumerable: !1,
|
|
274
|
+
value: ready,
|
|
275
|
+
writable: !1
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
var defaultImageResponse, ImageResponse = class extends Response {
|
|
280
|
+
ready;
|
|
281
|
+
constructor(component, options) {
|
|
282
|
+
defaultImageResponse ??= createImageResponse();
|
|
283
|
+
let response = defaultImageResponse(component, options);
|
|
284
|
+
super(response.body, response), this.ready = response.ready;
|
|
285
|
+
}
|
|
286
|
+
}, response_default = ImageResponse;
|
|
287
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
288
|
+
0 && (module.exports = {
|
|
289
|
+
ImageResponse,
|
|
290
|
+
createImageResponse
|
|
291
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
2
|
+
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
import * as napi from '@takumi-rs/core';
|
|
5
|
+
import * as wasm from '@takumi-rs/wasm';
|
|
6
|
+
|
|
7
|
+
type BinaryData = Uint8Array | ArrayBuffer;
|
|
8
|
+
type BinaryDataLoader = BinaryData | Promise<BinaryData> | (() => BinaryData | Promise<BinaryData>);
|
|
9
|
+
type ImageResponseFont = BinaryData | {
|
|
10
|
+
data: BinaryDataLoader;
|
|
11
|
+
key?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
style?: napi.FontDetails["style"] | wasm.FontDetails["style"];
|
|
14
|
+
weight?: number;
|
|
15
|
+
};
|
|
16
|
+
type ImageResponsePersistentImage = {
|
|
17
|
+
data: BinaryDataLoader;
|
|
18
|
+
key?: string;
|
|
19
|
+
src: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
declare module "react" {
|
|
23
|
+
interface DOMAttributes<T> {
|
|
24
|
+
tw?: string;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
type RenderOptions = napi.RenderOptions | wasm.RenderOptions;
|
|
28
|
+
type ManagedRendererOptions = {
|
|
29
|
+
fonts?: ImageResponseFont[];
|
|
30
|
+
/**
|
|
31
|
+
* Only supported by the native `@takumi-rs/core` renderer.
|
|
32
|
+
* This option is ignored when using the WASM renderer.
|
|
33
|
+
*/
|
|
34
|
+
loadDefaultFonts?: boolean;
|
|
35
|
+
persistentImages?: ImageResponsePersistentImage[];
|
|
36
|
+
/**
|
|
37
|
+
* @description The WebAssembly module to use for the renderer. If not provided, the default resolving strategy will be used.
|
|
38
|
+
*/
|
|
39
|
+
module?: wasm.InitInput | Promise<wasm.InitInput> | {
|
|
40
|
+
default: wasm.InitInput;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
44
|
+
renderer: napi.Renderer | wasm.Renderer;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
jsx?: FromJsxOptions;
|
|
47
|
+
emoji?: EmojiType | "from-font";
|
|
48
|
+
onError?: (error: unknown) => void | Promise<void>;
|
|
49
|
+
};
|
|
50
|
+
type ImageResponseOptionsWithoutRenderer = Omit<ImageResponseOptionsWithRenderer, "renderer"> & ManagedRendererOptions;
|
|
51
|
+
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
52
|
+
type ImageResponseResult = Response & {
|
|
53
|
+
readonly ready: Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
type ImageResponseFactory = (component: ReactNode, options?: ImageResponseOptions) => ImageResponseResult;
|
|
56
|
+
declare function createImageResponse(defaultOptions?: ImageResponseOptionsWithoutRenderer): ImageResponseFactory;
|
|
57
|
+
declare class ImageResponse extends Response {
|
|
58
|
+
readonly ready: Promise<void>;
|
|
59
|
+
constructor(component: ReactNode, options?: ImageResponseOptions);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { ImageResponse, type ImageResponseFactory, type ImageResponseOptions, type ImageResponseOptionsWithoutRenderer, type ImageResponseResult, createImageResponse, ImageResponse as default };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
2
|
+
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
import * as napi from '@takumi-rs/core';
|
|
5
|
+
import * as wasm from '@takumi-rs/wasm';
|
|
6
|
+
|
|
7
|
+
type BinaryData = Uint8Array | ArrayBuffer;
|
|
8
|
+
type BinaryDataLoader = BinaryData | Promise<BinaryData> | (() => BinaryData | Promise<BinaryData>);
|
|
9
|
+
type ImageResponseFont = BinaryData | {
|
|
10
|
+
data: BinaryDataLoader;
|
|
11
|
+
key?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
style?: napi.FontDetails["style"] | wasm.FontDetails["style"];
|
|
14
|
+
weight?: number;
|
|
15
|
+
};
|
|
16
|
+
type ImageResponsePersistentImage = {
|
|
17
|
+
data: BinaryDataLoader;
|
|
18
|
+
key?: string;
|
|
19
|
+
src: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
declare module "react" {
|
|
23
|
+
interface DOMAttributes<T> {
|
|
24
|
+
tw?: string;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
type RenderOptions = napi.RenderOptions | wasm.RenderOptions;
|
|
28
|
+
type ManagedRendererOptions = {
|
|
29
|
+
fonts?: ImageResponseFont[];
|
|
30
|
+
/**
|
|
31
|
+
* Only supported by the native `@takumi-rs/core` renderer.
|
|
32
|
+
* This option is ignored when using the WASM renderer.
|
|
33
|
+
*/
|
|
34
|
+
loadDefaultFonts?: boolean;
|
|
35
|
+
persistentImages?: ImageResponsePersistentImage[];
|
|
36
|
+
/**
|
|
37
|
+
* @description The WebAssembly module to use for the renderer. If not provided, the default resolving strategy will be used.
|
|
38
|
+
*/
|
|
39
|
+
module?: wasm.InitInput | Promise<wasm.InitInput> | {
|
|
40
|
+
default: wasm.InitInput;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
44
|
+
renderer: napi.Renderer | wasm.Renderer;
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
jsx?: FromJsxOptions;
|
|
47
|
+
emoji?: EmojiType | "from-font";
|
|
48
|
+
onError?: (error: unknown) => void | Promise<void>;
|
|
49
|
+
};
|
|
50
|
+
type ImageResponseOptionsWithoutRenderer = Omit<ImageResponseOptionsWithRenderer, "renderer"> & ManagedRendererOptions;
|
|
51
|
+
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
52
|
+
type ImageResponseResult = Response & {
|
|
53
|
+
readonly ready: Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
type ImageResponseFactory = (component: ReactNode, options?: ImageResponseOptions) => ImageResponseResult;
|
|
56
|
+
declare function createImageResponse(defaultOptions?: ImageResponseOptionsWithoutRenderer): ImageResponseFactory;
|
|
57
|
+
declare class ImageResponse extends Response {
|
|
58
|
+
readonly ready: Promise<void>;
|
|
59
|
+
constructor(component: ReactNode, options?: ImageResponseOptions);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { ImageResponse, type ImageResponseFactory, type ImageResponseOptions, type ImageResponseOptionsWithoutRenderer, type ImageResponseResult, createImageResponse, ImageResponse as default };
|
package/dist/response.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// src/response.tsx
|
|
2
|
+
import { fetchResources } from "@takumi-rs/helpers";
|
|
3
|
+
import { extractEmojis } from "@takumi-rs/helpers/emoji";
|
|
4
|
+
import { fromJsx } from "@takumi-rs/helpers/jsx";
|
|
5
|
+
|
|
6
|
+
// src/cache.ts
|
|
7
|
+
function createResourceCache() {
|
|
8
|
+
return {
|
|
9
|
+
functionDataCache: /* @__PURE__ */ new WeakMap(),
|
|
10
|
+
loadedFontKeys: /* @__PURE__ */ new Set(),
|
|
11
|
+
loadedFontObjects: /* @__PURE__ */ new WeakSet(),
|
|
12
|
+
loadedImageKeys: /* @__PURE__ */ new Set(),
|
|
13
|
+
loadedImageObjects: /* @__PURE__ */ new WeakSet(),
|
|
14
|
+
promiseDataCache: /* @__PURE__ */ new WeakMap(),
|
|
15
|
+
stringDataCache: /* @__PURE__ */ new Map()
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function getOrCreateStringDataCache(cache, key, load) {
|
|
19
|
+
let cached = cache.stringDataCache.get(key);
|
|
20
|
+
if (cached)
|
|
21
|
+
return cached;
|
|
22
|
+
let pending = Promise.resolve(load()).catch((error) => {
|
|
23
|
+
throw cache.stringDataCache.delete(key), error;
|
|
24
|
+
});
|
|
25
|
+
return cache.stringDataCache.set(key, pending), pending;
|
|
26
|
+
}
|
|
27
|
+
function getOrCreateFunctionDataCache(cache, loader) {
|
|
28
|
+
let cached = cache.functionDataCache.get(loader);
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached;
|
|
31
|
+
let pending = Promise.resolve(loader()).catch((error) => {
|
|
32
|
+
throw cache.functionDataCache.delete(loader), error;
|
|
33
|
+
});
|
|
34
|
+
return cache.functionDataCache.set(loader, pending), pending;
|
|
35
|
+
}
|
|
36
|
+
function getOrCreatePromiseDataCache(cache, promise) {
|
|
37
|
+
let cached = cache.promiseDataCache.get(promise);
|
|
38
|
+
if (cached)
|
|
39
|
+
return cached;
|
|
40
|
+
let pending = promise.catch((error) => {
|
|
41
|
+
throw cache.promiseDataCache.delete(promise), error;
|
|
42
|
+
});
|
|
43
|
+
return cache.promiseDataCache.set(promise, pending), pending;
|
|
44
|
+
}
|
|
45
|
+
function hasBinaryData(value) {
|
|
46
|
+
return value instanceof Uint8Array || value instanceof ArrayBuffer;
|
|
47
|
+
}
|
|
48
|
+
function createFontCacheKey(font) {
|
|
49
|
+
return hasBinaryData(font) ? font : font.key ? `font:${font.key}` : font;
|
|
50
|
+
}
|
|
51
|
+
function createImageCacheKey(image) {
|
|
52
|
+
return image.key ? `image:${image.key}` : image;
|
|
53
|
+
}
|
|
54
|
+
async function resolveData(cache, data, cacheKey) {
|
|
55
|
+
return typeof data == "function" ? cacheKey ? getOrCreateStringDataCache(cache, cacheKey, data) : getOrCreateFunctionDataCache(cache, data) : data instanceof Promise ? cacheKey ? getOrCreateStringDataCache(cache, cacheKey, () => data) : getOrCreatePromiseDataCache(cache, data) : data;
|
|
56
|
+
}
|
|
57
|
+
async function resolveFont(cache, font) {
|
|
58
|
+
if (hasBinaryData(font))
|
|
59
|
+
return font;
|
|
60
|
+
let cacheKey = createFontCacheKey(font);
|
|
61
|
+
return {
|
|
62
|
+
...font,
|
|
63
|
+
data: await resolveData(cache, font.data, typeof cacheKey == "string" ? cacheKey : void 0)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function resolvePersistentImage(cache, image) {
|
|
67
|
+
let cacheKey = createImageCacheKey(image);
|
|
68
|
+
return {
|
|
69
|
+
...image,
|
|
70
|
+
data: await resolveData(cache, image.data, typeof cacheKey == "string" ? cacheKey : void 0)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/import.ts
|
|
75
|
+
import * as wasm from "@takumi-rs/wasm";
|
|
76
|
+
var importPromise = null;
|
|
77
|
+
function getImports(module) {
|
|
78
|
+
return importPromise ??= getImportsImpl(module), importPromise;
|
|
79
|
+
}
|
|
80
|
+
async function getImportsImpl(module) {
|
|
81
|
+
if (module)
|
|
82
|
+
return initializeWasm(module);
|
|
83
|
+
let importedModule = await importBindings();
|
|
84
|
+
return importedModule && "Renderer" in importedModule ? importedModule : initializeWasm(importedModule);
|
|
85
|
+
}
|
|
86
|
+
async function initializeWasm(module) {
|
|
87
|
+
let wasmModule = module !== void 0 && typeof module == "object" && "default" in module ? module.default : module;
|
|
88
|
+
try {
|
|
89
|
+
return await wasm.default(wasmModule ? { module_or_path: wasmModule } : void 0), wasm;
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"Couldn't automatically resolve Takumi native bindings. Please specify the module option with the WASM module.",
|
|
93
|
+
{
|
|
94
|
+
cause: error
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function importBindings() {
|
|
100
|
+
if (shouldSkipCoreImport())
|
|
101
|
+
return importWasmBindings();
|
|
102
|
+
try {
|
|
103
|
+
return await import("@takumi-rs/core");
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.warn(
|
|
106
|
+
"Unable to import @takumi-rs/core. Falling back to auto-detection of WASM bindings.",
|
|
107
|
+
{
|
|
108
|
+
cause: error
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return importWasmBindings();
|
|
113
|
+
}
|
|
114
|
+
async function importWasmBindings() {
|
|
115
|
+
let nextPath = "@takumi-rs/wasm/next";
|
|
116
|
+
return typeof process < "u" && process.env.NEXT_RUNTIME ? import(
|
|
117
|
+
/* @vite-ignore */
|
|
118
|
+
nextPath
|
|
119
|
+
) : import(
|
|
120
|
+
/* turbopackIgnore: true */
|
|
121
|
+
/* webpackIgnore: true */
|
|
122
|
+
"@takumi-rs/wasm/auto"
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
function shouldSkipCoreImport() {
|
|
126
|
+
if (typeof process < "u" && process.env.NEXT_RUNTIME === "edge" || typeof window < "u" || typeof navigator < "u" && navigator.userAgent === "Cloudflare-Workers" || "WebSocketPair" in globalThis || "EdgeRuntime" in globalThis)
|
|
127
|
+
return !0;
|
|
128
|
+
let maybeWorkerGlobalScope = globalThis.WorkerGlobalScope;
|
|
129
|
+
return !!(maybeWorkerGlobalScope !== void 0 && maybeWorkerGlobalScope.prototype.isPrototypeOf(globalThis));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/response.tsx
|
|
133
|
+
var defaultFormat = "webp";
|
|
134
|
+
function hasLoadedResource(key, loadedKeys, loadedObjects) {
|
|
135
|
+
return typeof key == "string" ? loadedKeys.has(key) : loadedObjects.has(key);
|
|
136
|
+
}
|
|
137
|
+
function markLoadedResource(key, loadedKeys, loadedObjects) {
|
|
138
|
+
if (typeof key == "string") {
|
|
139
|
+
loadedKeys.add(key);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
loadedObjects.add(key);
|
|
143
|
+
}
|
|
144
|
+
function mergeOptions(defaultOptions, options) {
|
|
145
|
+
if (!defaultOptions)
|
|
146
|
+
return options;
|
|
147
|
+
if (!options)
|
|
148
|
+
return defaultOptions;
|
|
149
|
+
let headers = new Headers(defaultOptions.headers);
|
|
150
|
+
return options.headers && new Headers(options.headers).forEach((value, key) => {
|
|
151
|
+
headers.set(key, value);
|
|
152
|
+
}), "renderer" in options ? {
|
|
153
|
+
...defaultOptions,
|
|
154
|
+
...options,
|
|
155
|
+
headers
|
|
156
|
+
} : {
|
|
157
|
+
...defaultOptions,
|
|
158
|
+
...options,
|
|
159
|
+
fonts: options.fonts ?? defaultOptions.fonts,
|
|
160
|
+
headers,
|
|
161
|
+
persistentImages: options.persistentImages ?? defaultOptions.persistentImages,
|
|
162
|
+
stylesheets: [...defaultOptions.stylesheets ?? [], ...options.stylesheets ?? []]
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
var contentTypeMap = {
|
|
166
|
+
png: "image/png",
|
|
167
|
+
jpeg: "image/jpeg",
|
|
168
|
+
webp: "image/webp",
|
|
169
|
+
raw: "application/octet-stream"
|
|
170
|
+
};
|
|
171
|
+
async function renderComponent(component, options, imports, renderer) {
|
|
172
|
+
let { node: originalNode, stylesheets } = await fromJsx(component, options?.jsx), node = options?.emoji && options.emoji !== "from-font" ? extractEmojis(originalNode, options.emoji) : originalNode, fetchedResources = options?.fetchedResources !== void 0 ? options.fetchedResources : await fetchResources(imports.extractResourceUrls(node)), renderOptions = {
|
|
173
|
+
...options,
|
|
174
|
+
fetchedResources,
|
|
175
|
+
format: options?.format ?? defaultFormat,
|
|
176
|
+
stylesheets: [...options?.stylesheets ?? [], ...stylesheets]
|
|
177
|
+
};
|
|
178
|
+
return renderer.render(node, renderOptions, options?.signal);
|
|
179
|
+
}
|
|
180
|
+
function defaultErrorHandler(error) {
|
|
181
|
+
console.error("Takumi failed to render image."), console.error(error);
|
|
182
|
+
}
|
|
183
|
+
async function loadRendererResources(renderer, options, cache) {
|
|
184
|
+
let tasks = [];
|
|
185
|
+
if (options?.fonts && options.fonts.length > 0) {
|
|
186
|
+
let resolvedFonts = await Promise.all(
|
|
187
|
+
options.fonts.map(async (font) => ({
|
|
188
|
+
cacheKey: createFontCacheKey(font),
|
|
189
|
+
font: await resolveFont(cache, font)
|
|
190
|
+
}))
|
|
191
|
+
);
|
|
192
|
+
if ("loadFonts" in renderer) {
|
|
193
|
+
let filteredFonts = resolvedFonts.filter(({ cacheKey }) => hasLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects) ? !1 : (markLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects), !0));
|
|
194
|
+
filteredFonts.length > 0 && tasks.push(renderer.loadFonts(filteredFonts.map(({ font }) => font)));
|
|
195
|
+
} else
|
|
196
|
+
for (let { cacheKey, font } of resolvedFonts)
|
|
197
|
+
hasLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects) || (markLoadedResource(cacheKey, cache.loadedFontKeys, cache.loadedFontObjects), renderer.loadFont(font));
|
|
198
|
+
}
|
|
199
|
+
if (options?.persistentImages && options.persistentImages.length > 0) {
|
|
200
|
+
let resolvedImages = await Promise.all(
|
|
201
|
+
options.persistentImages.map(async (image) => ({
|
|
202
|
+
cacheKey: createImageCacheKey(image),
|
|
203
|
+
image: await resolvePersistentImage(cache, image)
|
|
204
|
+
}))
|
|
205
|
+
);
|
|
206
|
+
for (let { cacheKey, image } of resolvedImages) {
|
|
207
|
+
if (hasLoadedResource(cacheKey, cache.loadedImageKeys, cache.loadedImageObjects))
|
|
208
|
+
continue;
|
|
209
|
+
markLoadedResource(cacheKey, cache.loadedImageKeys, cache.loadedImageObjects);
|
|
210
|
+
let maybePromise = renderer.putPersistentImage(image, options.signal);
|
|
211
|
+
maybePromise instanceof Promise && tasks.push(maybePromise);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
tasks.length > 0 && await Promise.all(tasks);
|
|
215
|
+
}
|
|
216
|
+
function createImageResponse(defaultOptions) {
|
|
217
|
+
let cache = createResourceCache();
|
|
218
|
+
return function(component, options) {
|
|
219
|
+
let mergedOptions = mergeOptions(defaultOptions, options), {
|
|
220
|
+
promise: ready,
|
|
221
|
+
reject: rejectReady,
|
|
222
|
+
resolve: resolveReady
|
|
223
|
+
} = Promise.withResolvers(), module = mergedOptions && "module" in mergedOptions ? mergedOptions.module : void 0, stream = new ReadableStream({
|
|
224
|
+
type: "bytes",
|
|
225
|
+
async start(controller) {
|
|
226
|
+
try {
|
|
227
|
+
let imports = await getImports(module), isExternalRenderer = mergedOptions && "renderer" in mergedOptions, renderer = isExternalRenderer ? mergedOptions.renderer : cache.renderer ??= new imports.Renderer({
|
|
228
|
+
loadDefaultFonts: mergedOptions?.loadDefaultFonts
|
|
229
|
+
});
|
|
230
|
+
isExternalRenderer || await loadRendererResources(renderer, mergedOptions, cache);
|
|
231
|
+
let image = await renderComponent(component, mergedOptions, imports, renderer);
|
|
232
|
+
controller.enqueue(image), controller.close(), resolveReady();
|
|
233
|
+
} catch (error) {
|
|
234
|
+
controller.error(error), await (mergedOptions?.onError ?? defaultErrorHandler)(error), rejectReady(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}), headers = new Headers(mergedOptions?.headers);
|
|
238
|
+
headers.get("content-type") || headers.set("content-type", contentTypeMap[mergedOptions?.format ?? defaultFormat]);
|
|
239
|
+
let response = new Response(stream, {
|
|
240
|
+
headers,
|
|
241
|
+
status: mergedOptions?.status,
|
|
242
|
+
statusText: mergedOptions?.statusText
|
|
243
|
+
});
|
|
244
|
+
return Object.defineProperty(response, "ready", {
|
|
245
|
+
enumerable: !1,
|
|
246
|
+
value: ready,
|
|
247
|
+
writable: !1
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
var defaultImageResponse, ImageResponse = class extends Response {
|
|
252
|
+
ready;
|
|
253
|
+
constructor(component, options) {
|
|
254
|
+
defaultImageResponse ??= createImageResponse();
|
|
255
|
+
let response = defaultImageResponse(component, options);
|
|
256
|
+
super(response.body, response), this.ready = response.ready;
|
|
257
|
+
}
|
|
258
|
+
}, response_default = ImageResponse;
|
|
259
|
+
export {
|
|
260
|
+
ImageResponse,
|
|
261
|
+
createImageResponse,
|
|
262
|
+
response_default as default
|
|
263
|
+
};
|
package/package.json
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@takumi-rs/image-response",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
3
|
+
"version": "1.0.0-beta.20",
|
|
4
|
+
"license": "(MIT OR Apache-2.0)",
|
|
5
5
|
"author": {
|
|
6
|
-
"email": "me@kane.tw",
|
|
7
6
|
"name": "Kane Wang",
|
|
7
|
+
"email": "me@kane.tw",
|
|
8
8
|
"url": "https://kane.tw"
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"url": "git+https://github.com/kane50613/takumi.git"
|
|
12
12
|
},
|
|
13
|
-
"dependencies": {
|
|
14
|
-
"@takumi-rs/core": "1.0.0-beta.2",
|
|
15
|
-
"@takumi-rs/helpers": "1.0.0-beta.2",
|
|
16
|
-
"@takumi-rs/wasm": "1.0.0-beta.2"
|
|
17
|
-
},
|
|
18
|
-
"devDependencies": {
|
|
19
|
-
"@types/bun": "catalog:",
|
|
20
|
-
"@types/react": "catalog:",
|
|
21
|
-
"@types/react-dom": "catalog:",
|
|
22
|
-
"react": "catalog:",
|
|
23
|
-
"react-dom": "catalog:",
|
|
24
|
-
"tsup": "^8.5.1",
|
|
25
|
-
"type-fest": "^5.4.4",
|
|
26
|
-
"typescript": "catalog:"
|
|
27
|
-
},
|
|
28
|
-
"scripts": {
|
|
29
|
-
"build": "tsup-node src/backends/*.ts --clean --minify --dts --format esm,cjs --no-splitting",
|
|
30
|
-
"prepublishOnly": "jq '.dependencies[\"@takumi-rs/core\"] = .version | .dependencies[\"@takumi-rs/helpers\"] = .version | .dependencies[\"@takumi-rs/wasm\"] = .version' package.json > tmp.json && mv tmp.json package.json"
|
|
31
|
-
},
|
|
32
13
|
"files": [
|
|
33
14
|
"dist",
|
|
34
15
|
"package.json",
|
|
35
16
|
"README.md",
|
|
36
17
|
"tsconfig.json"
|
|
37
18
|
],
|
|
19
|
+
"type": "module",
|
|
38
20
|
"exports": {
|
|
39
21
|
".": {
|
|
40
|
-
"types": "./dist/
|
|
41
|
-
"import": "./dist/
|
|
42
|
-
"require": "./dist/
|
|
43
|
-
"default": "./dist/
|
|
22
|
+
"types": "./dist/response.d.ts",
|
|
23
|
+
"import": "./dist/response.js",
|
|
24
|
+
"require": "./dist/response.cjs",
|
|
25
|
+
"default": "./dist/response.js"
|
|
44
26
|
},
|
|
45
27
|
"./wasm": {
|
|
46
|
-
"types": "./dist/
|
|
47
|
-
"import": "./dist/
|
|
48
|
-
"require": "./dist/
|
|
49
|
-
"default": "./dist/
|
|
28
|
+
"types": "./dist/response.d.ts",
|
|
29
|
+
"import": "./dist/response.js",
|
|
30
|
+
"require": "./dist/response.cjs",
|
|
31
|
+
"default": "./dist/response.js"
|
|
50
32
|
}
|
|
51
33
|
},
|
|
52
|
-
"
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup-node src/response.tsx --clean --minify-syntax --dts --format esm,cjs --no-splitting",
|
|
36
|
+
"prepublishOnly": "jq '.dependencies[\"@takumi-rs/core\"] = .version | .dependencies[\"@takumi-rs/helpers\"] = .version | .dependencies[\"@takumi-rs/wasm\"] = .version' package.json > tmp.json && mv tmp.json package.json"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@takumi-rs/core": "1.0.0-beta.20",
|
|
40
|
+
"@takumi-rs/helpers": "1.0.0-beta.20",
|
|
41
|
+
"@takumi-rs/wasm": "1.0.0-beta.20"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/bun": "catalog:",
|
|
45
|
+
"@types/react": "catalog:",
|
|
46
|
+
"@types/react-dom": "catalog:",
|
|
47
|
+
"react": "catalog:",
|
|
48
|
+
"react-dom": "catalog:",
|
|
49
|
+
"tsup": "^8.5.1",
|
|
50
|
+
"type-fest": "^5.5.0",
|
|
51
|
+
"typescript": "catalog:"
|
|
52
|
+
}
|
|
53
53
|
}
|
package/dist/node.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";var m=Object.defineProperty;var w=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var I=(t,e)=>{for(var r in e)m(t,r,{get:e[r],enumerable:!0})},b=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of x(e))!j.call(t,n)&&n!==r&&m(t,n,{get:()=>e[n],enumerable:!(s=w(e,n))||s.enumerable});return t};var P=t=>b(m({},"__esModule",{value:!0}),t);var J={};I(J,{ImageResponse:()=>o,default:()=>E});module.exports=P(J);var c=require("@takumi-rs/core"),d=require("@takumi-rs/helpers"),p=require("@takumi-rs/helpers/emoji"),u=require("@takumi-rs/helpers/jsx"),a,T={format:"webp"};async function A(t){if(t&&"renderer"in t)return t.renderer;if(!a)return a=new c.Renderer(t),a;let e=[],r=a;return r?(t?.fonts&&t.fonts.length>0&&e.push(r.loadFonts(t.fonts)),t?.persistentImages&&e.push(...t.persistentImages.map(s=>r.putPersistentImage(s.src,s.data))),e.length>0&&await Promise.all(e),r):a}function F(t,e){if(e?.fetchedResources)return e.fetchedResources;let r=(0,c.extractResourceUrls)(t);return(0,d.fetchResources)(r)}function N(t,e){return new ReadableStream({type:"bytes",async start(r){try{let s=(0,u.fromJsx)(t,e?.jsx).then(async({node:i,stylesheets:l})=>{e?.emoji&&e.emoji!=="from-font"&&(i=(0,p.extractEmojis)(i,e.emoji));let O=await F(i,e);return{node:i,fetchedResources:O,stylesheets:l}}),[n,{node:f,fetchedResources:R,stylesheets:g}]=await Promise.all([A(e),s]),h={width:e?.width,height:e?.height,format:e?.format,quality:e?.quality,dithering:e?.dithering,drawDebugBorder:e?.drawDebugBorder,devicePixelRatio:e?.devicePixelRatio,fetchedResources:R,stylesheets:[...e?.stylesheets??[],...g]},y=await n.render(f,h,e?.signal);r.enqueue(y),r.close()}catch(s){r.error(s)}}})}var B={webp:"image/webp",png:"image/png",jpeg:"image/jpeg",raw:"application/octet-stream"},o=class extends Response{constructor(e,r){let s=N(e,r),n=new Headers(r?.headers);n.get("content-type")||n.set("content-type",B[r?.format??T.format]),super(s,{status:r?.status,statusText:r?.statusText,headers:n})}},E=o;0&&(module.exports={ImageResponse});
|
package/dist/node.d.cts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { RenderOptions, Renderer, ConstructRendererOptions } from '@takumi-rs/core';
|
|
2
|
-
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
3
|
-
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
4
|
-
import { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
declare module "react" {
|
|
7
|
-
interface DOMAttributes<T> {
|
|
8
|
-
tw?: string;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
12
|
-
renderer: Renderer;
|
|
13
|
-
signal?: AbortSignal;
|
|
14
|
-
jsx?: FromJsxOptions;
|
|
15
|
-
emoji?: EmojiType | "from-font";
|
|
16
|
-
};
|
|
17
|
-
type ImageResponseOptionsWithoutRenderer = ResponseInit & RenderOptions & ConstructRendererOptions & {
|
|
18
|
-
signal?: AbortSignal;
|
|
19
|
-
jsx?: FromJsxOptions;
|
|
20
|
-
emoji?: EmojiType | "from-font";
|
|
21
|
-
};
|
|
22
|
-
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
23
|
-
declare class ImageResponse extends Response {
|
|
24
|
-
constructor(component: ReactNode, options?: ImageResponseOptions);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export { ImageResponse, type ImageResponseOptions, ImageResponse as default };
|
package/dist/node.d.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { RenderOptions, Renderer, ConstructRendererOptions } from '@takumi-rs/core';
|
|
2
|
-
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
3
|
-
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
4
|
-
import { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
declare module "react" {
|
|
7
|
-
interface DOMAttributes<T> {
|
|
8
|
-
tw?: string;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
12
|
-
renderer: Renderer;
|
|
13
|
-
signal?: AbortSignal;
|
|
14
|
-
jsx?: FromJsxOptions;
|
|
15
|
-
emoji?: EmojiType | "from-font";
|
|
16
|
-
};
|
|
17
|
-
type ImageResponseOptionsWithoutRenderer = ResponseInit & RenderOptions & ConstructRendererOptions & {
|
|
18
|
-
signal?: AbortSignal;
|
|
19
|
-
jsx?: FromJsxOptions;
|
|
20
|
-
emoji?: EmojiType | "from-font";
|
|
21
|
-
};
|
|
22
|
-
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
23
|
-
declare class ImageResponse extends Response {
|
|
24
|
-
constructor(component: ReactNode, options?: ImageResponseOptions);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export { ImageResponse, type ImageResponseOptions, ImageResponse as default };
|
package/dist/node.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{extractResourceUrls as g,Renderer as h}from"@takumi-rs/core";import{fetchResources as y}from"@takumi-rs/helpers";import{extractEmojis as l}from"@takumi-rs/helpers/emoji";import{fromJsx as O}from"@takumi-rs/helpers/jsx";var a,w={format:"webp"};async function x(r){if(r&&"renderer"in r)return r.renderer;if(!a)return a=new h(r),a;let e=[],t=a;return t?(r?.fonts&&r.fonts.length>0&&e.push(t.loadFonts(r.fonts)),r?.persistentImages&&e.push(...r.persistentImages.map(s=>t.putPersistentImage(s.src,s.data))),e.length>0&&await Promise.all(e),t):a}function j(r,e){if(e?.fetchedResources)return e.fetchedResources;let t=g(r);return y(t)}function I(r,e){return new ReadableStream({type:"bytes",async start(t){try{let s=O(r,e?.jsx).then(async({node:i,stylesheets:f})=>{e?.emoji&&e.emoji!=="from-font"&&(i=l(i,e.emoji));let R=await j(i,e);return{node:i,fetchedResources:R,stylesheets:f}}),[n,{node:c,fetchedResources:m,stylesheets:d}]=await Promise.all([x(e),s]),p={width:e?.width,height:e?.height,format:e?.format,quality:e?.quality,dithering:e?.dithering,drawDebugBorder:e?.drawDebugBorder,devicePixelRatio:e?.devicePixelRatio,fetchedResources:m,stylesheets:[...e?.stylesheets??[],...d]},u=await n.render(c,p,e?.signal);t.enqueue(u),t.close()}catch(s){t.error(s)}}})}var b={webp:"image/webp",png:"image/png",jpeg:"image/jpeg",raw:"application/octet-stream"},o=class extends Response{constructor(e,t){let s=I(e,t),n=new Headers(t?.headers);n.get("content-type")||n.set("content-type",b[t?.format??w.format]),super(s,{status:t?.status,statusText:t?.statusText,headers:n})}},N=o;export{o as ImageResponse,N as default};
|
package/dist/wasm.cjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";var g=Object.create;var p=Object.defineProperty;var y=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,h=Object.prototype.hasOwnProperty;var j=(e,t)=>{for(var s in t)p(e,s,{get:t[s],enumerable:!0})},f=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of I(t))!h.call(e,r)&&r!==s&&p(e,r,{get:()=>t[r],enumerable:!(n=y(t,r))||n.enumerable});return e};var w=(e,t,s)=>(s=e!=null?g(O(e)):{},f(t||!e||!e.__esModule?p(s,"default",{value:e,enumerable:!0}):s,e)),x=e=>f(p({},"__esModule",{value:!0}),e);var E={};j(E,{ImageResponse:()=>m,default:()=>A});module.exports=x(E);var u=require("@takumi-rs/helpers"),d=require("@takumi-rs/helpers/emoji"),l=require("@takumi-rs/helpers/jsx"),a=w(require("@takumi-rs/wasm/no-bundler"),1),o;function b(e){if(e&&"renderer"in e)return e.renderer;if(!o)return o=new a.Renderer(e),o;if(e?.fonts)for(let t of e.fonts)o.loadFont(t);if(e?.persistentImages)for(let t of e.persistentImages)o.putPersistentImage(t);return o}var c=null;async function T(e){!("module"in e)||o||(c||(c=(async()=>{let t=await e.module;typeof t=="object"&&"default"in t&&(t=t.default),await(0,a.default)({module_or_path:t})})()),await c)}async function F(e,t){let s={...t},{node:n,stylesheets:r}=await(0,l.fromJsx)(e,s.jsx);if(s.stylesheets=[...s.stylesheets??[],...r],s.emoji&&s.emoji!=="from-font"&&(n=(0,d.extractEmojis)(n,s.emoji)),!s.fetchedResources){let i=(0,a.extractResourceUrls)(n);i.length>0&&(s.fetchedResources=await(0,u.fetchResources)(i))}return{node:n,options:s}}function N(e,t){return new ReadableStream({type:"bytes",async start(s){try{await T(t);let n=b(t),{node:r,options:i}=await F(e,t),R=n.render(r,i);s.enqueue(R),s.close()}catch(n){s.error(n)}}})}var P={png:"image/png",jpeg:"image/jpeg",webp:"image/webp",raw:"application/octet-stream"},W={format:"webp"},m=class extends Response{constructor(t,s){let n=N(t,s),r=new Headers(s.headers);r.get("content-type")||r.set("content-type",P[s.format??W.format]),super(n,{status:s.status,statusText:s.statusText,headers:r})}},A=m;0&&(module.exports={ImageResponse});
|
package/dist/wasm.d.cts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
2
|
-
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
3
|
-
import { RenderOptions, Renderer, InitInput, Font, ImageSource } from '@takumi-rs/wasm/no-bundler';
|
|
4
|
-
import { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
declare module "react" {
|
|
7
|
-
interface DOMAttributes<T> {
|
|
8
|
-
tw?: string;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
type ModuleOptions = {
|
|
12
|
-
/**
|
|
13
|
-
* @description The WebAssembly module to use for the renderer.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* For Cloudflare Workers, you can use the bundled WASM file.
|
|
17
|
-
* ```ts
|
|
18
|
-
* {
|
|
19
|
-
* module: import("@takumi-rs/wasm/takumi_wasm_bg.wasm"),
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* For Next.js Turbopack, you can use the nextjs helper.
|
|
24
|
-
* ```ts
|
|
25
|
-
* {
|
|
26
|
-
* module: import("@takumi-rs/wasm/next"),
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* For Vite, use `?url` suffix to get the URL of the WASM file.
|
|
31
|
-
*
|
|
32
|
-
* ```ts
|
|
33
|
-
* {
|
|
34
|
-
* module: import("@takumi-rs/wasm/takumi_wasm_bg.wasm?url"),
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
module: InitInput | Promise<InitInput> | {
|
|
39
|
-
default: InitInput;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
43
|
-
renderer: Renderer;
|
|
44
|
-
jsx?: FromJsxOptions;
|
|
45
|
-
emoji?: EmojiType | "from-font";
|
|
46
|
-
};
|
|
47
|
-
type ImageResponseOptionsWithoutRenderer = ResponseInit & RenderOptions & ModuleOptions & {
|
|
48
|
-
fonts?: Font[];
|
|
49
|
-
persistentImages?: ImageSource[];
|
|
50
|
-
jsx?: FromJsxOptions;
|
|
51
|
-
emoji?: EmojiType | "from-font";
|
|
52
|
-
};
|
|
53
|
-
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
54
|
-
declare class ImageResponse extends Response {
|
|
55
|
-
constructor(component: ReactNode, options: ImageResponseOptions);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export { ImageResponse, type ImageResponseOptions, ImageResponse as default };
|
package/dist/wasm.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { EmojiType } from '@takumi-rs/helpers/emoji';
|
|
2
|
-
import { FromJsxOptions } from '@takumi-rs/helpers/jsx';
|
|
3
|
-
import { RenderOptions, Renderer, InitInput, Font, ImageSource } from '@takumi-rs/wasm/no-bundler';
|
|
4
|
-
import { ReactNode } from 'react';
|
|
5
|
-
|
|
6
|
-
declare module "react" {
|
|
7
|
-
interface DOMAttributes<T> {
|
|
8
|
-
tw?: string;
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
type ModuleOptions = {
|
|
12
|
-
/**
|
|
13
|
-
* @description The WebAssembly module to use for the renderer.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* For Cloudflare Workers, you can use the bundled WASM file.
|
|
17
|
-
* ```ts
|
|
18
|
-
* {
|
|
19
|
-
* module: import("@takumi-rs/wasm/takumi_wasm_bg.wasm"),
|
|
20
|
-
* }
|
|
21
|
-
* ```
|
|
22
|
-
*
|
|
23
|
-
* For Next.js Turbopack, you can use the nextjs helper.
|
|
24
|
-
* ```ts
|
|
25
|
-
* {
|
|
26
|
-
* module: import("@takumi-rs/wasm/next"),
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*
|
|
30
|
-
* For Vite, use `?url` suffix to get the URL of the WASM file.
|
|
31
|
-
*
|
|
32
|
-
* ```ts
|
|
33
|
-
* {
|
|
34
|
-
* module: import("@takumi-rs/wasm/takumi_wasm_bg.wasm?url"),
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
module: InitInput | Promise<InitInput> | {
|
|
39
|
-
default: InitInput;
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
type ImageResponseOptionsWithRenderer = ResponseInit & RenderOptions & {
|
|
43
|
-
renderer: Renderer;
|
|
44
|
-
jsx?: FromJsxOptions;
|
|
45
|
-
emoji?: EmojiType | "from-font";
|
|
46
|
-
};
|
|
47
|
-
type ImageResponseOptionsWithoutRenderer = ResponseInit & RenderOptions & ModuleOptions & {
|
|
48
|
-
fonts?: Font[];
|
|
49
|
-
persistentImages?: ImageSource[];
|
|
50
|
-
jsx?: FromJsxOptions;
|
|
51
|
-
emoji?: EmojiType | "from-font";
|
|
52
|
-
};
|
|
53
|
-
type ImageResponseOptions = ImageResponseOptionsWithRenderer | ImageResponseOptionsWithoutRenderer;
|
|
54
|
-
declare class ImageResponse extends Response {
|
|
55
|
-
constructor(component: ReactNode, options: ImageResponseOptions);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export { ImageResponse, type ImageResponseOptions, ImageResponse as default };
|
package/dist/wasm.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{fetchResources as c}from"@takumi-rs/helpers";import{extractEmojis as f}from"@takumi-rs/helpers/emoji";import{fromJsx as u}from"@takumi-rs/helpers/jsx";import d,{extractResourceUrls as l,Renderer as R}from"@takumi-rs/wasm/no-bundler";var o;function g(s){if(s&&"renderer"in s)return s.renderer;if(!o)return o=new R(s),o;if(s?.fonts)for(let t of s.fonts)o.loadFont(t);if(s?.persistentImages)for(let t of s.persistentImages)o.putPersistentImage(t);return o}var i=null;async function y(s){!("module"in s)||o||(i||(i=(async()=>{let t=await s.module;typeof t=="object"&&"default"in t&&(t=t.default),await d({module_or_path:t})})()),await i)}async function I(s,t){let e={...t},{node:n,stylesheets:r}=await u(s,e.jsx);if(e.stylesheets=[...e.stylesheets??[],...r],e.emoji&&e.emoji!=="from-font"&&(n=f(n,e.emoji)),!e.fetchedResources){let a=l(n);a.length>0&&(e.fetchedResources=await c(a))}return{node:n,options:e}}function O(s,t){return new ReadableStream({type:"bytes",async start(e){try{await y(t);let n=g(t),{node:r,options:a}=await I(s,t),m=n.render(r,a);e.enqueue(m),e.close()}catch(n){e.error(n)}}})}var h={png:"image/png",jpeg:"image/jpeg",webp:"image/webp",raw:"application/octet-stream"},j={format:"webp"},p=class extends Response{constructor(t,e){let n=O(t,e),r=new Headers(e.headers);r.get("content-type")||r.set("content-type",h[e.format??j.format]),super(n,{status:e.status,statusText:e.statusText,headers:r})}},F=p;export{p as ImageResponse,F as default};
|