@character-foundry/character-foundry 0.4.3 → 0.5.0
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/app-framework.cjs +12 -3
- package/dist/app-framework.cjs.map +1 -1
- package/dist/app-framework.d.cts +9 -1
- package/dist/app-framework.d.ts +9 -1
- package/dist/app-framework.js +12 -3
- package/dist/app-framework.js.map +1 -1
- package/dist/charx.cjs +85 -52
- package/dist/charx.cjs.map +1 -1
- package/dist/charx.d.cts +22 -22
- package/dist/charx.d.ts +22 -22
- package/dist/charx.js +85 -52
- package/dist/charx.js.map +1 -1
- package/dist/exporter.cjs +104 -54
- package/dist/exporter.cjs.map +1 -1
- package/dist/exporter.d.cts +19 -19
- package/dist/exporter.d.ts +19 -19
- package/dist/exporter.js +104 -54
- package/dist/exporter.js.map +1 -1
- package/dist/federation.cjs +104 -36
- package/dist/federation.cjs.map +1 -1
- package/dist/federation.d.cts +54 -19
- package/dist/federation.d.ts +54 -19
- package/dist/federation.js +104 -36
- package/dist/federation.js.map +1 -1
- package/dist/index.cjs +104 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -29
- package/dist/index.d.ts +29 -29
- package/dist/index.js +104 -54
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +171 -31
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.d.cts +37 -23
- package/dist/loader.d.ts +37 -23
- package/dist/loader.js +171 -31
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.d.cts +23 -23
- package/dist/lorebook.d.ts +23 -23
- package/dist/normalizer.cjs +72 -18
- package/dist/normalizer.cjs.map +1 -1
- package/dist/normalizer.d.cts +37 -37
- package/dist/normalizer.d.ts +37 -37
- package/dist/normalizer.js +72 -18
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +72 -18
- package/dist/png.cjs.map +1 -1
- package/dist/png.d.cts +25 -25
- package/dist/png.d.ts +25 -25
- package/dist/png.js +72 -18
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs +80 -23
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +85 -67
- package/dist/schemas.d.ts +85 -67
- package/dist/schemas.js +80 -23
- package/dist/schemas.js.map +1 -1
- package/dist/voxta.cjs +91 -20
- package/dist/voxta.cjs.map +1 -1
- package/dist/voxta.d.cts +23 -23
- package/dist/voxta.d.ts +23 -23
- package/dist/voxta.js +91 -20
- package/dist/voxta.js.map +1 -1
- package/package.json +24 -24
package/dist/index.cjs
CHANGED
|
@@ -560,6 +560,58 @@ var import_zod = require("zod");
|
|
|
560
560
|
var import_zod2 = require("zod");
|
|
561
561
|
var import_zod3 = require("zod");
|
|
562
562
|
var import_zod4 = require("zod");
|
|
563
|
+
function preprocessTimestamp(val) {
|
|
564
|
+
if (val === null || val === void 0) return void 0;
|
|
565
|
+
let num;
|
|
566
|
+
if (typeof val === "number") {
|
|
567
|
+
num = val;
|
|
568
|
+
} else if (typeof val === "string") {
|
|
569
|
+
const trimmed = val.trim();
|
|
570
|
+
if (!trimmed) return void 0;
|
|
571
|
+
const parsed = Number(trimmed);
|
|
572
|
+
if (!isNaN(parsed)) {
|
|
573
|
+
num = parsed;
|
|
574
|
+
} else {
|
|
575
|
+
const date = new Date(trimmed);
|
|
576
|
+
if (isNaN(date.getTime())) return void 0;
|
|
577
|
+
num = Math.floor(date.getTime() / 1e3);
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
if (num > 1e10) {
|
|
583
|
+
num = Math.floor(num / 1e3);
|
|
584
|
+
}
|
|
585
|
+
if (num < 0) return void 0;
|
|
586
|
+
return num;
|
|
587
|
+
}
|
|
588
|
+
function preprocessNumeric(val) {
|
|
589
|
+
if (val === null || val === void 0) return void 0;
|
|
590
|
+
if (typeof val === "number") {
|
|
591
|
+
return isNaN(val) ? void 0 : val;
|
|
592
|
+
}
|
|
593
|
+
if (typeof val === "string") {
|
|
594
|
+
const trimmed = val.trim();
|
|
595
|
+
if (!trimmed) return void 0;
|
|
596
|
+
const parsed = Number(trimmed);
|
|
597
|
+
return isNaN(parsed) ? void 0 : parsed;
|
|
598
|
+
}
|
|
599
|
+
return void 0;
|
|
600
|
+
}
|
|
601
|
+
var KNOWN_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
602
|
+
"icon",
|
|
603
|
+
"background",
|
|
604
|
+
"emotion",
|
|
605
|
+
"user_icon",
|
|
606
|
+
"sound",
|
|
607
|
+
"video",
|
|
608
|
+
"custom",
|
|
609
|
+
"x-risu-asset"
|
|
610
|
+
]);
|
|
611
|
+
function preprocessAssetType(val) {
|
|
612
|
+
if (typeof val !== "string") return "custom";
|
|
613
|
+
return KNOWN_ASSET_TYPES.has(val) ? val : "custom";
|
|
614
|
+
}
|
|
563
615
|
var ISO8601Schema = import_zod.z.string().datetime();
|
|
564
616
|
var UUIDSchema = import_zod.z.string().uuid();
|
|
565
617
|
var SpecSchema = import_zod.z.enum(["v2", "v3"]);
|
|
@@ -582,16 +634,19 @@ var SourceFormatSchema = import_zod.z.enum([
|
|
|
582
634
|
// VoxPkg format
|
|
583
635
|
]);
|
|
584
636
|
var OriginalShapeSchema = import_zod.z.enum(["wrapped", "unwrapped", "legacy"]);
|
|
585
|
-
var AssetTypeSchema = import_zod.z.
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
637
|
+
var AssetTypeSchema = import_zod.z.preprocess(
|
|
638
|
+
preprocessAssetType,
|
|
639
|
+
import_zod.z.enum([
|
|
640
|
+
"icon",
|
|
641
|
+
"background",
|
|
642
|
+
"emotion",
|
|
643
|
+
"user_icon",
|
|
644
|
+
"sound",
|
|
645
|
+
"video",
|
|
646
|
+
"custom",
|
|
647
|
+
"x-risu-asset"
|
|
648
|
+
])
|
|
649
|
+
);
|
|
595
650
|
var AssetDescriptorSchema = import_zod.z.object({
|
|
596
651
|
type: AssetTypeSchema,
|
|
597
652
|
uri: import_zod.z.string(),
|
|
@@ -625,8 +680,8 @@ var CCv2LorebookEntrySchema = import_zod2.z.object({
|
|
|
625
680
|
var CCv2CharacterBookSchema = import_zod2.z.object({
|
|
626
681
|
name: import_zod2.z.string().optional(),
|
|
627
682
|
description: import_zod2.z.string().optional(),
|
|
628
|
-
scan_depth: import_zod2.z.number().int().nonnegative().optional(),
|
|
629
|
-
token_budget: import_zod2.z.number().int().nonnegative().optional(),
|
|
683
|
+
scan_depth: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
684
|
+
token_budget: import_zod2.z.preprocess(preprocessNumeric, import_zod2.z.number().int().nonnegative().optional()),
|
|
630
685
|
recursive_scanning: import_zod2.z.boolean().optional(),
|
|
631
686
|
extensions: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
632
687
|
entries: import_zod2.z.array(CCv2LorebookEntrySchema)
|
|
@@ -700,8 +755,8 @@ var CCv3LorebookEntrySchema = import_zod3.z.object({
|
|
|
700
755
|
var CCv3CharacterBookSchema = import_zod3.z.object({
|
|
701
756
|
name: import_zod3.z.string().optional(),
|
|
702
757
|
description: import_zod3.z.string().optional(),
|
|
703
|
-
scan_depth: import_zod3.z.number().int().nonnegative().optional(),
|
|
704
|
-
token_budget: import_zod3.z.number().int().nonnegative().optional(),
|
|
758
|
+
scan_depth: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
759
|
+
token_budget: import_zod3.z.preprocess(preprocessNumeric, import_zod3.z.number().int().nonnegative().optional()),
|
|
705
760
|
recursive_scanning: import_zod3.z.boolean().optional(),
|
|
706
761
|
extensions: import_zod3.z.record(import_zod3.z.unknown()).optional(),
|
|
707
762
|
entries: import_zod3.z.array(CCv3LorebookEntrySchema)
|
|
@@ -733,10 +788,9 @@ var CCv3DataInnerSchema = import_zod3.z.object({
|
|
|
733
788
|
nickname: import_zod3.z.string().optional(),
|
|
734
789
|
creator_notes_multilingual: import_zod3.z.record(import_zod3.z.string()).optional(),
|
|
735
790
|
source: import_zod3.z.array(import_zod3.z.string()).optional(),
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
modification_date: import_zod3.z.number().int().nonnegative().optional()
|
|
739
|
-
// Unix timestamp in seconds
|
|
791
|
+
// Unix timestamps - preprocess to handle ISO strings, numeric strings, milliseconds
|
|
792
|
+
creation_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional()),
|
|
793
|
+
modification_date: import_zod3.z.preprocess(preprocessTimestamp, import_zod3.z.number().int().nonnegative().optional())
|
|
740
794
|
});
|
|
741
795
|
var CCv3DataSchema = import_zod3.z.object({
|
|
742
796
|
spec: import_zod3.z.literal("chara_card_v3"),
|
|
@@ -1363,36 +1417,6 @@ var SAFE_ASSET_TYPES = /* @__PURE__ */ new Set([
|
|
|
1363
1417
|
"data",
|
|
1364
1418
|
"unknown"
|
|
1365
1419
|
]);
|
|
1366
|
-
var SAFE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1367
|
-
// Images
|
|
1368
|
-
"png",
|
|
1369
|
-
"jpg",
|
|
1370
|
-
"jpeg",
|
|
1371
|
-
"webp",
|
|
1372
|
-
"gif",
|
|
1373
|
-
"avif",
|
|
1374
|
-
"svg",
|
|
1375
|
-
"bmp",
|
|
1376
|
-
"ico",
|
|
1377
|
-
// Audio
|
|
1378
|
-
"mp3",
|
|
1379
|
-
"wav",
|
|
1380
|
-
"ogg",
|
|
1381
|
-
"flac",
|
|
1382
|
-
"m4a",
|
|
1383
|
-
"aac",
|
|
1384
|
-
"opus",
|
|
1385
|
-
// Video
|
|
1386
|
-
"mp4",
|
|
1387
|
-
"webm",
|
|
1388
|
-
"avi",
|
|
1389
|
-
"mov",
|
|
1390
|
-
"mkv",
|
|
1391
|
-
// Data
|
|
1392
|
-
"json",
|
|
1393
|
-
"txt",
|
|
1394
|
-
"bin"
|
|
1395
|
-
]);
|
|
1396
1420
|
function getCharxCategory(mimetype) {
|
|
1397
1421
|
if (mimetype.startsWith("image/")) return "images";
|
|
1398
1422
|
if (mimetype.startsWith("audio/")) return "audio";
|
|
@@ -1408,11 +1432,20 @@ function sanitizeAssetType(type) {
|
|
|
1408
1432
|
return sanitized || "custom";
|
|
1409
1433
|
}
|
|
1410
1434
|
function sanitizeExtension(ext) {
|
|
1411
|
-
const normalized = ext.replace(/^\./, "").toLowerCase()
|
|
1412
|
-
if (
|
|
1413
|
-
|
|
1435
|
+
const normalized = ext.trim().replace(/^\./, "").toLowerCase();
|
|
1436
|
+
if (!normalized) {
|
|
1437
|
+
throw new Error("Invalid asset extension: empty extension");
|
|
1438
|
+
}
|
|
1439
|
+
if (normalized.length > 64) {
|
|
1440
|
+
throw new Error(`Invalid asset extension: too long (${normalized.length} chars)`);
|
|
1441
|
+
}
|
|
1442
|
+
if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("\0")) {
|
|
1443
|
+
throw new Error("Invalid asset extension: path separators are not allowed");
|
|
1414
1444
|
}
|
|
1415
|
-
|
|
1445
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
1446
|
+
throw new Error(`Invalid asset extension: "${ext}"`);
|
|
1447
|
+
}
|
|
1448
|
+
return normalized;
|
|
1416
1449
|
}
|
|
1417
1450
|
function sanitizeName(name, ext) {
|
|
1418
1451
|
let safeName = name;
|
|
@@ -8194,6 +8227,22 @@ function sanitizeName2(name, ext) {
|
|
|
8194
8227
|
if (!safeName) safeName = "asset";
|
|
8195
8228
|
return safeName;
|
|
8196
8229
|
}
|
|
8230
|
+
function sanitizeExtension2(ext) {
|
|
8231
|
+
const normalized = ext.trim().replace(/^\./, "").toLowerCase();
|
|
8232
|
+
if (!normalized) {
|
|
8233
|
+
throw new Error("Invalid asset extension: empty extension");
|
|
8234
|
+
}
|
|
8235
|
+
if (normalized.length > 64) {
|
|
8236
|
+
throw new Error(`Invalid asset extension: too long (${normalized.length} chars)`);
|
|
8237
|
+
}
|
|
8238
|
+
if (normalized.includes("/") || normalized.includes("\\") || normalized.includes("\0")) {
|
|
8239
|
+
throw new Error("Invalid asset extension: path separators are not allowed");
|
|
8240
|
+
}
|
|
8241
|
+
if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
|
|
8242
|
+
throw new Error(`Invalid asset extension: "${ext}"`);
|
|
8243
|
+
}
|
|
8244
|
+
return normalized;
|
|
8245
|
+
}
|
|
8197
8246
|
function writeVoxta(card, assets, options = {}) {
|
|
8198
8247
|
const { compressionLevel = 6, includePackageJson = false } = options;
|
|
8199
8248
|
const cardData = card.data;
|
|
@@ -8282,8 +8331,9 @@ function writeVoxta(card, assets, options = {}) {
|
|
|
8282
8331
|
let assetCount = 0;
|
|
8283
8332
|
let mainThumbnail;
|
|
8284
8333
|
for (const asset of assets) {
|
|
8285
|
-
const
|
|
8286
|
-
const
|
|
8334
|
+
const safeExt = sanitizeExtension2(asset.ext);
|
|
8335
|
+
const safeName = sanitizeName2(asset.name, safeExt);
|
|
8336
|
+
const finalFilename = `${safeName}.${safeExt}`;
|
|
8287
8337
|
let voxtaPath = "";
|
|
8288
8338
|
const tags = asset.tags || [];
|
|
8289
8339
|
const isMainIcon = asset.type === "icon" && (tags.includes("portrait-override") || asset.name === "main" || asset.isMain);
|