@ogify/core 0.1.3 → 0.1.5
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 +165 -86
- package/dist/index.d.mts +77 -72
- package/dist/index.d.ts +77 -72
- package/dist/index.js +318 -51
- package/dist/index.mjs +295 -51
- package/package.json +15 -13
- package/LICENSE +0 -21
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import satori from 'satori';
|
|
2
2
|
import { html } from 'satori-html';
|
|
3
|
-
import {
|
|
3
|
+
import { renderAsync } from '@resvg/resvg-js';
|
|
4
|
+
import { LRUCache } from 'lru-cache';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as crypto from 'crypto';
|
|
8
|
+
import rtlCSSJS from 'rtl-css-js';
|
|
4
9
|
|
|
5
10
|
// src/template.ts
|
|
6
11
|
|
|
@@ -169,6 +174,216 @@ var GoogleFontDetector = class {
|
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
};
|
|
177
|
+
var DEFAULT_TTL = 1e3 * 60 * 60 * 24 * 7;
|
|
178
|
+
var DEFAULT_MAX = 100;
|
|
179
|
+
var DEFAULT_CACHE_DIR = ".ogify-cache";
|
|
180
|
+
var CacheManager = class {
|
|
181
|
+
cache;
|
|
182
|
+
config;
|
|
183
|
+
cacheDir = DEFAULT_CACHE_DIR;
|
|
184
|
+
/**
|
|
185
|
+
* Creates a new CacheManager instance.
|
|
186
|
+
*
|
|
187
|
+
* @param config - Cache configuration (memory or filesystem)
|
|
188
|
+
*/
|
|
189
|
+
constructor(config) {
|
|
190
|
+
this.config = config;
|
|
191
|
+
if (config.type === "memory") {
|
|
192
|
+
this.cache = new LRUCache({
|
|
193
|
+
max: config.max || DEFAULT_MAX,
|
|
194
|
+
ttl: config.ttl || DEFAULT_TTL
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (config.type === "filesystem") {
|
|
198
|
+
this.cacheDir = config.dir || DEFAULT_CACHE_DIR;
|
|
199
|
+
this.ensureCacheDirectory();
|
|
200
|
+
this.cleanExpiredFiles().catch((err) => {
|
|
201
|
+
console.warn("Failed to clean expired cache files:", err);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
// eslint-disable-next-line
|
|
206
|
+
generateKey(object) {
|
|
207
|
+
const sorted = Object.keys(object).sort().map((key) => `${key}=${object[key]}`);
|
|
208
|
+
return crypto.createHash("md5").update(sorted.join("|")).digest("hex");
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Retrieves a value from the cache.
|
|
212
|
+
*
|
|
213
|
+
* For filesystem cache, attempts to load from disk if not in memory.
|
|
214
|
+
*
|
|
215
|
+
* @param key - Cache key
|
|
216
|
+
* @returns Cached buffer or undefined if not found
|
|
217
|
+
*/
|
|
218
|
+
async get(key) {
|
|
219
|
+
if (this.config.type === "memory") {
|
|
220
|
+
return this.cache?.get(key);
|
|
221
|
+
}
|
|
222
|
+
if (this.config.type === "filesystem") {
|
|
223
|
+
return this.loadFromDisk(key);
|
|
224
|
+
}
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Stores a value in the cache.
|
|
229
|
+
*
|
|
230
|
+
* For filesystem cache, also persists to disk.
|
|
231
|
+
*
|
|
232
|
+
* @param key - Cache key
|
|
233
|
+
* @param value - Buffer to cache
|
|
234
|
+
*/
|
|
235
|
+
async set(key, value) {
|
|
236
|
+
if (this.config.type === "memory") {
|
|
237
|
+
this.cache?.set(key, value);
|
|
238
|
+
}
|
|
239
|
+
if (this.config.type === "filesystem") {
|
|
240
|
+
await this.saveToDisk(key, value);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Checks if a key exists in the cache.
|
|
245
|
+
*
|
|
246
|
+
* @param key - Cache key
|
|
247
|
+
* @returns true if the key exists and is not expired
|
|
248
|
+
*/
|
|
249
|
+
async has(key) {
|
|
250
|
+
if (this.config.type === "memory") {
|
|
251
|
+
return this.cache?.has(key) || false;
|
|
252
|
+
}
|
|
253
|
+
if (this.config.type === "filesystem") {
|
|
254
|
+
try {
|
|
255
|
+
await fs.promises.access(path.join(this.cacheDir, this.getFilename(key)));
|
|
256
|
+
return true;
|
|
257
|
+
} catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Clears all cached items.
|
|
265
|
+
*
|
|
266
|
+
* For filesystem cache, also removes all files from disk.
|
|
267
|
+
*/
|
|
268
|
+
async clear() {
|
|
269
|
+
if (this.config.type === "memory") {
|
|
270
|
+
this.cache?.clear();
|
|
271
|
+
}
|
|
272
|
+
if (this.config.type === "filesystem") {
|
|
273
|
+
await this.clearFilesystem();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Ensures the cache directory exists.
|
|
278
|
+
* Creates it if it doesn't exist.
|
|
279
|
+
*/
|
|
280
|
+
ensureCacheDirectory() {
|
|
281
|
+
if (!this.cacheDir) return;
|
|
282
|
+
if (!fs.existsSync(this.cacheDir)) {
|
|
283
|
+
fs.mkdirSync(this.cacheDir, { recursive: true });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Generates a safe filename from a cache key.
|
|
288
|
+
*
|
|
289
|
+
* Uses SHA-256 hash to avoid filesystem issues with special characters.
|
|
290
|
+
*
|
|
291
|
+
* @param key - Cache key
|
|
292
|
+
* @returns Safe filename
|
|
293
|
+
*/
|
|
294
|
+
getFilename(key) {
|
|
295
|
+
const hash = crypto.createHash("sha256").update(key).digest("hex");
|
|
296
|
+
return `${hash}.cache`;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Loads a cached item from disk.
|
|
300
|
+
*
|
|
301
|
+
* @param key - Cache key
|
|
302
|
+
* @returns Cached buffer or undefined if not found or expired
|
|
303
|
+
*/
|
|
304
|
+
async loadFromDisk(key) {
|
|
305
|
+
if (this.config.type !== "filesystem") return void 0;
|
|
306
|
+
const filename = this.getFilename(key);
|
|
307
|
+
const filepath = path.join(this.cacheDir, filename);
|
|
308
|
+
try {
|
|
309
|
+
try {
|
|
310
|
+
await fs.promises.access(filepath);
|
|
311
|
+
} catch {
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
const stats = await fs.promises.stat(filepath);
|
|
315
|
+
const age = Date.now() - stats.mtimeMs;
|
|
316
|
+
const ttl = this.config.ttl || DEFAULT_TTL;
|
|
317
|
+
if (age > ttl) {
|
|
318
|
+
await fs.promises.unlink(filepath).catch(() => {
|
|
319
|
+
});
|
|
320
|
+
return void 0;
|
|
321
|
+
}
|
|
322
|
+
const data = await fs.promises.readFile(filepath);
|
|
323
|
+
this.cache?.set(key, data);
|
|
324
|
+
return data;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
return void 0;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Saves a cached item to disk.
|
|
331
|
+
*
|
|
332
|
+
* @param key - Cache key
|
|
333
|
+
* @param value - Buffer to save
|
|
334
|
+
*/
|
|
335
|
+
async saveToDisk(key, value) {
|
|
336
|
+
if (this.config.type !== "filesystem") return;
|
|
337
|
+
const filename = this.getFilename(key);
|
|
338
|
+
const filepath = path.join(this.cacheDir, filename);
|
|
339
|
+
try {
|
|
340
|
+
await fs.promises.writeFile(filepath, value);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Loads all cached items from filesystem into memory on initialization.
|
|
346
|
+
*/
|
|
347
|
+
async cleanExpiredFiles() {
|
|
348
|
+
if (this.config.type !== "filesystem") return;
|
|
349
|
+
try {
|
|
350
|
+
const files = await fs.promises.readdir(this.cacheDir);
|
|
351
|
+
for (const file of files) {
|
|
352
|
+
if (!file.endsWith(".cache")) continue;
|
|
353
|
+
const filepath = path.join(this.cacheDir, file);
|
|
354
|
+
try {
|
|
355
|
+
const stats = await fs.promises.stat(filepath);
|
|
356
|
+
const age = Date.now() - stats.mtimeMs;
|
|
357
|
+
const ttl = this.config.ttl || DEFAULT_TTL;
|
|
358
|
+
if (age > ttl) {
|
|
359
|
+
await fs.promises.unlink(filepath).catch(() => {
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
} catch (error) {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
/**
|
|
369
|
+
* Clears all files from the filesystem cache directory.
|
|
370
|
+
*/
|
|
371
|
+
async clearFilesystem() {
|
|
372
|
+
if (this.config.type !== "filesystem") return;
|
|
373
|
+
const cacheDir = this.cacheDir;
|
|
374
|
+
try {
|
|
375
|
+
const files = await fs.promises.readdir(cacheDir);
|
|
376
|
+
const unlinkPromises = files.map(async (file) => {
|
|
377
|
+
if (!file.endsWith(".cache")) return;
|
|
378
|
+
const filepath = path.join(cacheDir, file);
|
|
379
|
+
await fs.promises.unlink(filepath).catch(() => {
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
await Promise.all(unlinkPromises);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
};
|
|
172
387
|
|
|
173
388
|
// src/utils/emoji-loader.ts
|
|
174
389
|
var ZERO_WIDTH_JOINER = String.fromCharCode(8205);
|
|
@@ -204,12 +419,15 @@ var apis = {
|
|
|
204
419
|
fluent: (code) => `https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/${code.toLowerCase()}_color.svg`,
|
|
205
420
|
fluentFlat: (code) => `https://cdn.jsdelivr.net/gh/shuding/fluentui-emoji-unicode/assets/${code.toLowerCase()}_flat.svg`
|
|
206
421
|
};
|
|
207
|
-
var cache = {
|
|
422
|
+
var cache = new CacheManager({
|
|
423
|
+
type: "memory"
|
|
424
|
+
});
|
|
208
425
|
async function loadEmoji(type, text) {
|
|
209
426
|
const code = getIconCode(text);
|
|
210
427
|
const cacheKey = `${type}:${code}`;
|
|
211
|
-
|
|
212
|
-
|
|
428
|
+
const cached = await cache.get(cacheKey);
|
|
429
|
+
if (cached) {
|
|
430
|
+
return cached.toString();
|
|
213
431
|
}
|
|
214
432
|
if (!type || !apis[type]) {
|
|
215
433
|
type = "noto";
|
|
@@ -218,19 +436,22 @@ async function loadEmoji(type, text) {
|
|
|
218
436
|
const baseUrl = typeof api === "function" ? api(code) : api;
|
|
219
437
|
const fullUrl = typeof api === "function" ? baseUrl : `${baseUrl}${code.toUpperCase()}.svg`;
|
|
220
438
|
const emojiPromise = fetch(fullUrl).then((response) => response.text()).then((svgContent) => `data:image/svg+xml;base64,${btoa(svgContent)}`);
|
|
221
|
-
cache
|
|
439
|
+
await cache.set(cacheKey, Buffer.from(await emojiPromise));
|
|
222
440
|
return emojiPromise;
|
|
223
441
|
}
|
|
224
442
|
|
|
225
|
-
// src/utils/fetcher.ts
|
|
226
|
-
var cache2 = {
|
|
443
|
+
// src/utils/font-fetcher.ts
|
|
444
|
+
var cache2 = new CacheManager({
|
|
445
|
+
type: "memory"
|
|
446
|
+
});
|
|
227
447
|
var loadFontFromUrl = async (url) => {
|
|
228
|
-
|
|
229
|
-
|
|
448
|
+
const cachedFont = await cache2.get(url);
|
|
449
|
+
if (cachedFont) {
|
|
450
|
+
return cachedFont;
|
|
230
451
|
}
|
|
231
|
-
const fontData = fetch(url).then((response) => response.arrayBuffer());
|
|
232
|
-
cache2
|
|
233
|
-
return fontData;
|
|
452
|
+
const fontData = await fetch(url).then((response) => response.arrayBuffer());
|
|
453
|
+
await cache2.set(url, Buffer.from(fontData));
|
|
454
|
+
return Buffer.from(fontData);
|
|
234
455
|
};
|
|
235
456
|
|
|
236
457
|
// src/utils/additional-asset-loader.ts
|
|
@@ -308,11 +529,12 @@ var DEFAULT_HEIGHT = 630;
|
|
|
308
529
|
async function renderTemplate(template, params, options) {
|
|
309
530
|
const width = options?.width || DEFAULT_WIDTH;
|
|
310
531
|
const height = options?.height || DEFAULT_HEIGHT;
|
|
311
|
-
const
|
|
532
|
+
const fonts = options?.fonts?.length ? options.fonts : template.fonts;
|
|
533
|
+
const emojiProvider = options?.emojiProvider || template.emojiProvider || "noto";
|
|
534
|
+
const satoriFonts = await loadFonts(fonts);
|
|
312
535
|
const htmlString = await template.renderer({
|
|
313
536
|
params: typeof params === "function" ? await params() : params,
|
|
314
|
-
|
|
315
|
-
height
|
|
537
|
+
...options
|
|
316
538
|
});
|
|
317
539
|
const element = html(htmlString);
|
|
318
540
|
const svg = await satori(element, {
|
|
@@ -331,32 +553,25 @@ async function renderTemplate(template, params, options) {
|
|
|
331
553
|
// Asset type ('emoji' or other)
|
|
332
554
|
segment,
|
|
333
555
|
// The character(s) to load
|
|
334
|
-
fonts
|
|
556
|
+
fonts,
|
|
335
557
|
// Available fonts for fallback detection
|
|
336
|
-
emojiProvider
|
|
558
|
+
emojiProvider
|
|
337
559
|
// Emoji provider (default: noto)
|
|
338
560
|
});
|
|
339
561
|
}
|
|
340
562
|
});
|
|
341
|
-
const
|
|
563
|
+
const pngData = await renderAsync(svg, {
|
|
342
564
|
fitTo: {
|
|
343
565
|
mode: "width",
|
|
344
566
|
// Scale based on width, maintain aspect ratio
|
|
345
567
|
value: width
|
|
346
568
|
}
|
|
347
569
|
});
|
|
348
|
-
const pngData = resvg.render();
|
|
349
570
|
return Buffer.from(pngData.asPng());
|
|
350
571
|
}
|
|
351
572
|
|
|
352
573
|
// src/renderer.ts
|
|
353
574
|
function validateTemplate(config) {
|
|
354
|
-
if (!config.id) {
|
|
355
|
-
throw new Error("Template must have an id");
|
|
356
|
-
}
|
|
357
|
-
if (!config.name) {
|
|
358
|
-
throw new Error("Template must have a name");
|
|
359
|
-
}
|
|
360
575
|
if (typeof config.renderer !== "function") {
|
|
361
576
|
throw new Error("Template must have a renderer function");
|
|
362
577
|
}
|
|
@@ -366,9 +581,14 @@ function defineTemplate(config) {
|
|
|
366
581
|
validateTemplate(config);
|
|
367
582
|
return config;
|
|
368
583
|
}
|
|
584
|
+
var DEFAULT_CACHE = {
|
|
585
|
+
type: "memory"
|
|
586
|
+
};
|
|
369
587
|
var TemplateRenderer = class {
|
|
370
588
|
/** Configuration including global settings and lifecycle hooks */
|
|
371
589
|
config;
|
|
590
|
+
/** Cache manager for fonts and icons */
|
|
591
|
+
cacheManager;
|
|
372
592
|
/**
|
|
373
593
|
* Internal registry mapping template IDs to template definitions.
|
|
374
594
|
*
|
|
@@ -377,7 +597,7 @@ var TemplateRenderer = class {
|
|
|
377
597
|
* - Guaranteed insertion order
|
|
378
598
|
* - Better memory efficiency than objects
|
|
379
599
|
*/
|
|
380
|
-
templates =
|
|
600
|
+
templates = {};
|
|
381
601
|
/**
|
|
382
602
|
* Creates a new TemplateRenderer instance.
|
|
383
603
|
*
|
|
@@ -385,6 +605,7 @@ var TemplateRenderer = class {
|
|
|
385
605
|
*/
|
|
386
606
|
constructor(config) {
|
|
387
607
|
this.config = config;
|
|
608
|
+
this.cacheManager = new CacheManager(config.cache || DEFAULT_CACHE);
|
|
388
609
|
this.registerTemplates(config.templates);
|
|
389
610
|
}
|
|
390
611
|
/**
|
|
@@ -393,12 +614,13 @@ var TemplateRenderer = class {
|
|
|
393
614
|
* Templates are indexed by their ID for fast lookup.
|
|
394
615
|
* If a template with the same ID already exists, it will be overwritten.
|
|
395
616
|
*
|
|
396
|
-
* @param templates -
|
|
617
|
+
* @param templates - Map of template definitions to register
|
|
397
618
|
*/
|
|
398
619
|
registerTemplates(templates) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
620
|
+
const keys = Object.keys(templates);
|
|
621
|
+
keys.forEach((key) => {
|
|
622
|
+
this.templates[key] = templates[key];
|
|
623
|
+
});
|
|
402
624
|
}
|
|
403
625
|
/**
|
|
404
626
|
* Retrieves a template by its unique ID.
|
|
@@ -412,20 +634,7 @@ var TemplateRenderer = class {
|
|
|
412
634
|
* @returns The template definition, or undefined if not found
|
|
413
635
|
*/
|
|
414
636
|
getTemplate(id) {
|
|
415
|
-
return this.templates
|
|
416
|
-
}
|
|
417
|
-
/**
|
|
418
|
-
* Gets all registered template IDs.
|
|
419
|
-
*
|
|
420
|
-
* Useful for:
|
|
421
|
-
* - Building template selection dropdowns
|
|
422
|
-
* - Listing available templates in documentation
|
|
423
|
-
* - Debugging template registration
|
|
424
|
-
*
|
|
425
|
-
* @returns Array of template IDs
|
|
426
|
-
*/
|
|
427
|
-
getTemplateIds() {
|
|
428
|
-
return Array.from(this.templates.keys());
|
|
637
|
+
return this.templates[id];
|
|
429
638
|
}
|
|
430
639
|
/**
|
|
431
640
|
* Renders a template to a PNG image buffer.
|
|
@@ -448,7 +657,7 @@ var TemplateRenderer = class {
|
|
|
448
657
|
* - Throws if rendering fails (font loading, HTML generation, etc.)
|
|
449
658
|
*
|
|
450
659
|
* @param templateId - ID of the template to render
|
|
451
|
-
* @param params - Parameters to pass to the template
|
|
660
|
+
* @param params - Parameters (or function returning params) to pass to the template
|
|
452
661
|
* @param options - Optional rendering options
|
|
453
662
|
* @param options.width - Custom image width in pixels (default: 1200)
|
|
454
663
|
* @param options.height - Custom image height in pixels (default: 630)
|
|
@@ -456,28 +665,63 @@ var TemplateRenderer = class {
|
|
|
456
665
|
* @throws Error if template is not found or rendering fails
|
|
457
666
|
*/
|
|
458
667
|
async renderToImage(templateId, params, options) {
|
|
459
|
-
const {
|
|
668
|
+
const { sharedParams } = this.config;
|
|
460
669
|
const template = this.getTemplate(templateId);
|
|
461
670
|
if (!template) {
|
|
462
671
|
throw new Error(`Template '${templateId}' not found`);
|
|
463
672
|
}
|
|
464
673
|
const mergedParams = {
|
|
465
|
-
...typeof
|
|
466
|
-
...typeof params === "function" ? await params() : params
|
|
674
|
+
...typeof sharedParams === "function" ? await sharedParams() : sharedParams || {},
|
|
675
|
+
...typeof params === "function" ? await params() : params || {}
|
|
467
676
|
};
|
|
677
|
+
const cacheKey = this.cacheManager.generateKey({
|
|
678
|
+
templateId,
|
|
679
|
+
...mergedParams,
|
|
680
|
+
...options
|
|
681
|
+
});
|
|
682
|
+
const cached = await this.cacheManager.get(cacheKey);
|
|
683
|
+
if (cached) {
|
|
684
|
+
return cached;
|
|
685
|
+
}
|
|
468
686
|
if (this.config.beforeRender) {
|
|
469
687
|
await this.config.beforeRender(templateId, mergedParams);
|
|
470
688
|
}
|
|
471
689
|
const imageBuffer = await renderTemplate(template, mergedParams, options);
|
|
690
|
+
await this.cacheManager.set(cacheKey, imageBuffer);
|
|
472
691
|
if (this.config.afterRender) {
|
|
473
692
|
await this.config.afterRender(templateId, mergedParams, imageBuffer);
|
|
474
693
|
}
|
|
475
694
|
return imageBuffer;
|
|
476
695
|
}
|
|
477
696
|
};
|
|
478
|
-
function
|
|
697
|
+
function createRenderer(config) {
|
|
479
698
|
return new TemplateRenderer(config);
|
|
480
699
|
}
|
|
481
|
-
/*! Copyright Twitter Inc. and other contributors. Licensed under MIT */
|
|
482
700
|
|
|
483
|
-
|
|
701
|
+
// src/utils/clsx.ts
|
|
702
|
+
function clsx(...inputs) {
|
|
703
|
+
var i = 0, tmp, str = "", len = inputs.length;
|
|
704
|
+
for (; i < len; i++) {
|
|
705
|
+
if (tmp = inputs[i]) {
|
|
706
|
+
if (typeof tmp === "string") {
|
|
707
|
+
str += (str && " ") + tmp;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
return str;
|
|
712
|
+
}
|
|
713
|
+
var objectToStyle = (style, options) => {
|
|
714
|
+
if (!style) {
|
|
715
|
+
return "";
|
|
716
|
+
}
|
|
717
|
+
const { isRTL = false } = options || {};
|
|
718
|
+
return Object.entries(isRTL ? rtlCSSJS(style) : style).filter(([_, value]) => value !== void 0 && value !== null && value !== "").map(([key, value]) => {
|
|
719
|
+
if (value || value === 0) {
|
|
720
|
+
const cssKey = key.startsWith("--") ? key : key.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
721
|
+
return `${cssKey}:${value}`;
|
|
722
|
+
}
|
|
723
|
+
return "";
|
|
724
|
+
}).filter(Boolean).join(";");
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
export { TemplateRenderer, clsx, createRenderer, defineTemplate, objectToStyle, renderTemplate, validateTemplate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ogify/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Core types and utilities for OGify Open Graph image generator",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"lint": "tsc --noEmit",
|
|
22
|
+
"clean": "rm -rf dist",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"test:ui": "vitest --ui",
|
|
26
|
+
"test:coverage": "vitest run --coverage"
|
|
27
|
+
},
|
|
18
28
|
"author": {
|
|
19
29
|
"name": "Hung Pham - @revolabs-io",
|
|
20
30
|
"email": "info@revolabs.io"
|
|
@@ -22,25 +32,17 @@
|
|
|
22
32
|
"license": "MIT",
|
|
23
33
|
"dependencies": {
|
|
24
34
|
"@resvg/resvg-js": "^2.6.2",
|
|
35
|
+
"lru-cache": "^11.2.4",
|
|
36
|
+
"rtl-css-js": "^1.16.1",
|
|
25
37
|
"satori": "^0.18.3",
|
|
26
38
|
"satori-html": "^0.3.2"
|
|
27
39
|
},
|
|
28
40
|
"devDependencies": {
|
|
29
|
-
"@types/node": "^
|
|
41
|
+
"@types/node": "^20",
|
|
30
42
|
"@vitest/ui": "^4.0.15",
|
|
31
43
|
"happy-dom": "^20.0.11",
|
|
32
44
|
"tsup": "^8.5.1",
|
|
33
45
|
"typescript": "^5.9.3",
|
|
34
46
|
"vitest": "^4.0.15"
|
|
35
|
-
},
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "tsup",
|
|
38
|
-
"dev": "tsup --watch",
|
|
39
|
-
"lint": "tsc --noEmit",
|
|
40
|
-
"clean": "rm -rf dist",
|
|
41
|
-
"test": "vitest run",
|
|
42
|
-
"test:watch": "vitest",
|
|
43
|
-
"test:ui": "vitest --ui",
|
|
44
|
-
"test:coverage": "vitest run --coverage"
|
|
45
47
|
}
|
|
46
|
-
}
|
|
48
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 RevoLabs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|