@linxin666/dsh-pet 0.2.9 → 0.3.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/README.i18n.yaml +2 -2
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/assets/whale-refined/previews/idle.gif +0 -0
- package/lib/client.js +68 -29
- package/lib/client.js.map +1 -1
- package/lib/index.js +97 -51
- package/lib/types/client/PetSettingsCard.d.ts +5 -2
- package/lib/types/client/PetSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PetSettingsCard.js +29 -5
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +27 -19
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +7 -2
- package/lib/types/http.d.ts +43 -0
- package/lib/types/http.d.ts.map +1 -0
- package/lib/types/http.js +92 -0
- package/lib/types/registry.d.ts +8 -0
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +37 -9
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +16 -44
- package/package.json +1 -1
- package/src/client/PetSettingsCard.tsx +23 -5
- package/src/client/PetSprite.test.tsx +16 -0
- package/src/client/PetSprite.tsx +51 -27
- package/src/client/index.ts +7 -2
- package/src/client/pet.module.css +34 -2
- package/src/client/settings-section.module.css +3 -3
- package/src/http.ts +105 -0
- package/src/registry.test.ts +83 -1
- package/src/registry.ts +40 -8
- package/src/routes.ts +16 -45
package/lib/index.js
CHANGED
|
@@ -2774,9 +2774,14 @@ function resolveLive2dEntry(manifest, dir, options) {
|
|
|
2774
2774
|
record("error", "pet " + manifest.id + ": renderer live2d requires a live2d block");
|
|
2775
2775
|
return;
|
|
2776
2776
|
}
|
|
2777
|
+
const modelFile = join(dir, block.model);
|
|
2777
2778
|
let model3;
|
|
2778
2779
|
try {
|
|
2779
|
-
|
|
2780
|
+
if (guardedScannedJsonStat(modelFile, options, "live2d model " + block.model, 33554432) === void 0) {
|
|
2781
|
+
statSync(modelFile);
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
model3 = JSON.parse(readFileSync(modelFile, "utf8"));
|
|
2780
2785
|
} catch (error) {
|
|
2781
2786
|
record("error", "pet " + manifest.id + ": live2d model " + block.model + " is not readable: " + (error instanceof Error ? error.message : String(error)));
|
|
2782
2787
|
return;
|
|
@@ -2833,7 +2838,7 @@ function scanPetDir(dir, options) {
|
|
|
2833
2838
|
for (const name of names) {
|
|
2834
2839
|
const manifestFile = join(dir, name, "pet.json");
|
|
2835
2840
|
if (!existsSync(manifestFile)) continue;
|
|
2836
|
-
const parsed = readPetJson(manifestFile, options
|
|
2841
|
+
const parsed = readPetJson(manifestFile, options);
|
|
2837
2842
|
if (parsed === void 0) continue;
|
|
2838
2843
|
const entryDir = join(dir, name);
|
|
2839
2844
|
const verdict = parsePetManifest(parsed, entryDir);
|
|
@@ -2871,22 +2876,38 @@ function scanPetDir(dir, options) {
|
|
|
2871
2876
|
}
|
|
2872
2877
|
return entries;
|
|
2873
2878
|
}
|
|
2874
|
-
/**
|
|
2875
|
-
|
|
2879
|
+
/**
|
|
2880
|
+
* Read and parse one pet.json manifest; undefined (warning recorded) on
|
|
2881
|
+
* failure. The descriptor stat guard applies first: a pathological file —
|
|
2882
|
+
* huge, or a FIFO/device — is skipped with a warning instead of stalling
|
|
2883
|
+
* or OOM-ing the host at scan time (same discipline as voice/decoration).
|
|
2884
|
+
*/
|
|
2885
|
+
function readPetJson(file, options) {
|
|
2886
|
+
if (guardedScannedJsonStat(file, options, "pet manifest") === void 0) return void 0;
|
|
2876
2887
|
try {
|
|
2877
2888
|
return JSON.parse(readFileSync(file, "utf8"));
|
|
2878
2889
|
} catch (error) {
|
|
2879
|
-
warnings?.push("skipping " + file + ": " + (error instanceof Error ? error.message : String(error)));
|
|
2890
|
+
options.warnings?.push("skipping " + file + ": " + (error instanceof Error ? error.message : String(error)));
|
|
2880
2891
|
return;
|
|
2881
2892
|
}
|
|
2882
2893
|
}
|
|
2883
2894
|
/**
|
|
2895
|
+
* Scan-time read ceiling for user-authored JSON descriptors (voice.json,
|
|
2896
|
+
* .voice.json, decoration.json): the registry reads these synchronously at
|
|
2897
|
+
* plugin startup, and a pathological file — multi-GB, or a FIFO/device
|
|
2898
|
+
* symlink — must not hang or exhaust the host before the warn-and-drop
|
|
2899
|
+
* discipline can apply (review-spd follow-up, pet-center M4/M5).
|
|
2900
|
+
*/
|
|
2901
|
+
const PET_SCAN_JSON_CAP = 64 * 1024;
|
|
2902
|
+
/**
|
|
2884
2903
|
* Stat one scanned JSON descriptor with a regular-file + size guard, so a
|
|
2885
2904
|
* pathological user file is skipped with a warning instead of stalling or
|
|
2886
2905
|
* OOM-ing the host at startup. Returns the Stats, or undefined when the
|
|
2887
|
-
* caller must skip the file (a warning was recorded).
|
|
2906
|
+
* caller must skip the file (a warning was recorded). 'cap' defaults to
|
|
2907
|
+
* the descriptor ceiling (PET_SCAN_JSON_CAP); model descriptors pass the
|
|
2908
|
+
* larger live2d ceiling.
|
|
2888
2909
|
*/
|
|
2889
|
-
function guardedScannedJsonStat(file, options, what) {
|
|
2910
|
+
function guardedScannedJsonStat(file, options, what, cap = PET_SCAN_JSON_CAP) {
|
|
2890
2911
|
let st;
|
|
2891
2912
|
try {
|
|
2892
2913
|
st = statSync(file);
|
|
@@ -2905,8 +2926,8 @@ function guardedScannedJsonStat(file, options, what) {
|
|
|
2905
2926
|
warn(what + " is not a regular file; ignored");
|
|
2906
2927
|
return;
|
|
2907
2928
|
}
|
|
2908
|
-
if (st.size >
|
|
2909
|
-
warn(what + " exceeds the
|
|
2929
|
+
if (st.size > cap) {
|
|
2930
|
+
warn(what + " exceeds the " + cap + "-byte scan ceiling; ignored");
|
|
2910
2931
|
return;
|
|
2911
2932
|
}
|
|
2912
2933
|
return st;
|
|
@@ -3621,6 +3642,63 @@ function isPairingAccess(value) {
|
|
|
3621
3642
|
return value !== void 0 && value !== null && typeof value.isPairedDevice === "function";
|
|
3622
3643
|
}
|
|
3623
3644
|
//#endregion
|
|
3645
|
+
//#region src/http.ts
|
|
3646
|
+
/** Default body cap for readJsonBody: 64 KiB. */
|
|
3647
|
+
const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
|
|
3648
|
+
/** Family-default JSON response headers; callers may append or override. */
|
|
3649
|
+
const JSON_HEADERS = {
|
|
3650
|
+
"content-type": "application/json; charset=utf-8",
|
|
3651
|
+
"referrer-policy": "no-referrer"
|
|
3652
|
+
};
|
|
3653
|
+
/**
|
|
3654
|
+
* Lenient bounded body reader: parse a request body as JSON, or null on an
|
|
3655
|
+
* empty body, invalid JSON, or a body past maxBytes (default 64 KiB).
|
|
3656
|
+
* Overflow destroys the request instead of draining the remainder (no drain
|
|
3657
|
+
* call, matching the current repo-wide behavior); callers must not keep
|
|
3658
|
+
* reading the request afterwards. With objectOnly, non-JSON-object payloads
|
|
3659
|
+
* also yield null.
|
|
3660
|
+
*/
|
|
3661
|
+
async function readJsonBody(req, opts = {}) {
|
|
3662
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_JSON_BODY_MAX_BYTES;
|
|
3663
|
+
const chunks = [];
|
|
3664
|
+
let size = 0;
|
|
3665
|
+
for await (const chunk of req) {
|
|
3666
|
+
const buffer = chunk;
|
|
3667
|
+
size += buffer.length;
|
|
3668
|
+
if (size > maxBytes) {
|
|
3669
|
+
req.destroy();
|
|
3670
|
+
return null;
|
|
3671
|
+
}
|
|
3672
|
+
chunks.push(buffer);
|
|
3673
|
+
}
|
|
3674
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
3675
|
+
if (text === "") return null;
|
|
3676
|
+
try {
|
|
3677
|
+
const parsed = JSON.parse(text);
|
|
3678
|
+
if (opts.objectOnly && !isJsonObject(parsed)) return null;
|
|
3679
|
+
return parsed;
|
|
3680
|
+
} catch {
|
|
3681
|
+
return null;
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
/** Whether a value is a JSON object: typeof object, not null, not an array. */
|
|
3685
|
+
function isJsonObject(value) {
|
|
3686
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Write one JSON response. Default headers are the family defaults
|
|
3690
|
+
* (content-type and referrer-policy); caller headers are appended or
|
|
3691
|
+
* override them.
|
|
3692
|
+
*/
|
|
3693
|
+
function writeJson(res, status, body, headers = {}) {
|
|
3694
|
+
const payload = JSON.stringify(body);
|
|
3695
|
+
res.writeHead(status, {
|
|
3696
|
+
...JSON_HEADERS,
|
|
3697
|
+
...headers
|
|
3698
|
+
});
|
|
3699
|
+
res.end(payload);
|
|
3700
|
+
}
|
|
3701
|
+
//#endregion
|
|
3624
3702
|
//#region src/routes.ts
|
|
3625
3703
|
/**
|
|
3626
3704
|
* Pet HTTP routes — the browser half talks to the host through plain
|
|
@@ -3696,52 +3774,19 @@ function mimeFor(file) {
|
|
|
3696
3774
|
if (dot < 0) return "application/octet-stream";
|
|
3697
3775
|
return MIME_BY_EXT[file.slice(dot).toLowerCase()] ?? "application/octet-stream";
|
|
3698
3776
|
}
|
|
3699
|
-
/** Write one JSON response. */
|
|
3700
|
-
function json(res, status, body) {
|
|
3701
|
-
res.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
3702
|
-
res.end(JSON.stringify(body));
|
|
3703
|
-
}
|
|
3704
3777
|
/** Require the method or answer 405. */
|
|
3705
3778
|
function requireMethod(req, res, method) {
|
|
3706
3779
|
if (req.method === method) return true;
|
|
3707
|
-
|
|
3780
|
+
writeJson(res, 405, {
|
|
3708
3781
|
ok: false,
|
|
3709
3782
|
error: "method-not-allowed"
|
|
3710
3783
|
});
|
|
3711
3784
|
return false;
|
|
3712
3785
|
}
|
|
3713
|
-
/** Read a JSON request body (bounded). */
|
|
3714
|
-
function readJsonBody(req) {
|
|
3715
|
-
return new Promise((resolve, reject) => {
|
|
3716
|
-
let size = 0;
|
|
3717
|
-
const chunks = [];
|
|
3718
|
-
req.on("data", (chunk) => {
|
|
3719
|
-
size += chunk.length;
|
|
3720
|
-
if (size > 64 * 1024) {
|
|
3721
|
-
reject(/* @__PURE__ */ new Error("body-too-large"));
|
|
3722
|
-
queueMicrotask(() => req.destroy());
|
|
3723
|
-
return;
|
|
3724
|
-
}
|
|
3725
|
-
chunks.push(chunk);
|
|
3726
|
-
});
|
|
3727
|
-
req.on("end", () => {
|
|
3728
|
-
if (chunks.length === 0) {
|
|
3729
|
-
resolve({});
|
|
3730
|
-
return;
|
|
3731
|
-
}
|
|
3732
|
-
try {
|
|
3733
|
-
resolve(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
3734
|
-
} catch {
|
|
3735
|
-
reject(/* @__PURE__ */ new Error("invalid-json"));
|
|
3736
|
-
}
|
|
3737
|
-
});
|
|
3738
|
-
req.on("error", reject);
|
|
3739
|
-
});
|
|
3740
|
-
}
|
|
3741
3786
|
/** Shared route fence: loopback always passes; a live paired-device cookie is an extra allow path. */
|
|
3742
3787
|
function guard(ctx, req, res) {
|
|
3743
3788
|
if (isPetAllowed(ctx, req)) return true;
|
|
3744
|
-
|
|
3789
|
+
writeJson(res, 403, {
|
|
3745
3790
|
ok: false,
|
|
3746
3791
|
error: "forbidden: loopback-only"
|
|
3747
3792
|
});
|
|
@@ -3755,8 +3800,8 @@ function getRoute(ctx, path, run) {
|
|
|
3755
3800
|
handler: (req, res) => {
|
|
3756
3801
|
if (!guard(ctx, req, res)) return;
|
|
3757
3802
|
if (!requireMethod(req, res, "GET")) return;
|
|
3758
|
-
run().then((value) =>
|
|
3759
|
-
|
|
3803
|
+
run().then((value) => writeJson(res, 200, value), (error) => {
|
|
3804
|
+
writeJson(res, 500, {
|
|
3760
3805
|
ok: false,
|
|
3761
3806
|
error: error instanceof Error ? error.message : String(error)
|
|
3762
3807
|
});
|
|
@@ -3772,15 +3817,16 @@ function postRoute(ctx, path, run) {
|
|
|
3772
3817
|
handler: (req, res) => {
|
|
3773
3818
|
if (!guard(ctx, req, res)) return Promise.resolve();
|
|
3774
3819
|
if (!requireMethod(req, res, "POST")) return Promise.resolve();
|
|
3775
|
-
return readJsonBody(req).then((
|
|
3776
|
-
|
|
3777
|
-
|
|
3820
|
+
return readJsonBody(req, { maxBytes: 64 * 1024 }).then((parsed) => {
|
|
3821
|
+
const payload = parsed ?? {};
|
|
3822
|
+
return run(typeof payload === "object" && payload !== null ? payload : {}).then((value) => writeJson(res, 200, value), (error) => {
|
|
3823
|
+
writeJson(res, 400, {
|
|
3778
3824
|
ok: false,
|
|
3779
3825
|
error: error instanceof Error ? error.message : String(error)
|
|
3780
3826
|
});
|
|
3781
3827
|
});
|
|
3782
3828
|
}, (error) => {
|
|
3783
|
-
|
|
3829
|
+
writeJson(res, 400, {
|
|
3784
3830
|
ok: false,
|
|
3785
3831
|
error: error instanceof Error ? error.message : String(error)
|
|
3786
3832
|
});
|
|
@@ -3976,7 +4022,7 @@ function runtimeHandler(ctx, roots) {
|
|
|
3976
4022
|
const base = spec.root === "runtimeDir" ? roots.runtimeDir : roots.vendorDir;
|
|
3977
4023
|
const file = join(base, name);
|
|
3978
4024
|
if (!existsSync(file)) {
|
|
3979
|
-
|
|
4025
|
+
writeJson(res, 404, {
|
|
3980
4026
|
ok: false,
|
|
3981
4027
|
error: "runtime-file-missing",
|
|
3982
4028
|
file: name
|
|
@@ -72,6 +72,9 @@ export declare class PetSettingsCardController {
|
|
|
72
72
|
private diagnostics;
|
|
73
73
|
private loaded;
|
|
74
74
|
private attempts;
|
|
75
|
+
private disposed;
|
|
76
|
+
/** Pending deferred-load or retry timer; cancelled by dispose(). */
|
|
77
|
+
private pendingTimer;
|
|
75
78
|
/** @param scope - the bound settings scope for the 'pet' namespace. */
|
|
76
79
|
constructor(scope: SettingsScope<PetSettings>);
|
|
77
80
|
/** Fetch registry diagnostics once (soft-fail: an empty list on error). */
|
|
@@ -85,8 +88,8 @@ export declare class PetSettingsCardController {
|
|
|
85
88
|
*/
|
|
86
89
|
inject(): PetSettingsCardFace;
|
|
87
90
|
/**
|
|
88
|
-
* Release the card's scope subscription
|
|
89
|
-
* disposer calls this on teardown.
|
|
91
|
+
* Release the card's scope subscription, bound stores and pending load
|
|
92
|
+
* timers; the slot disposer calls this on teardown.
|
|
90
93
|
*/
|
|
91
94
|
dispose(): void;
|
|
92
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAI1F,OAAO,EAAoD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG1J,+EAA+E;AAC/E,MAAM,WAAW,WAAW;IAC1B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,0CAA0C;AAC1C,MAAM,WAAW,oBAAqB,SAAQ,SAAS;IACrD,4BAA4B;IAC5B,OAAO,EAAE,cAAc,CAAA;IACvB,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,iBAAiB;IACjB,IAAI,EAAE,cAAc,CAAA;IACpB,mBAAmB;IACnB,KAAK,EAAE,cAAc,CAAA;IACrB,oBAAoB;IACpB,MAAM,EAAE,cAAc,CAAA;IACtB,oBAAoB;IACpB,KAAK,EAAE,cAAc,CAAA;IACrB,uCAAuC;IACvC,iBAAiB,EAAE,cAAc,CAAA;IACjC,wEAAwE;IACxE,UAAU,EAAE,SAAS;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACvD,+EAA+E;IAC/E,cAAc,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAC7C;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,KAAK,EAAE;QACL,iEAAiE;QACjE,eAAe,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAA;KACrD,CAAA;CACF;AAQD,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB;AAiBD,2DAA2D;AAC3D,qBAAa,yBAAyB;IACpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAuB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAI3D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4B;IACtD,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,QAAQ,CAAI;
|
|
1
|
+
{"version":3,"file":"PetSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PetSettingsCard.tsx"],"names":[],"mappings":"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"}
|
|
@@ -29,6 +29,9 @@ export class PetSettingsCardController {
|
|
|
29
29
|
diagnostics = [];
|
|
30
30
|
loaded = false;
|
|
31
31
|
attempts = 0;
|
|
32
|
+
disposed = false;
|
|
33
|
+
/** Pending deferred-load or retry timer; cancelled by dispose(). */
|
|
34
|
+
pendingTimer;
|
|
32
35
|
/** @param scope - the bound settings scope for the 'pet' namespace. */
|
|
33
36
|
constructor(scope) {
|
|
34
37
|
this.form = new CardForm(scope, [
|
|
@@ -45,7 +48,10 @@ export class PetSettingsCardController {
|
|
|
45
48
|
// the first registry request until that pass completes so transport
|
|
46
49
|
// plugins (notably remote-web-ui on a paired non-loopback origin) can
|
|
47
50
|
// install their fetch channel before /api/pet/pets is issued.
|
|
48
|
-
window.setTimeout(() => {
|
|
51
|
+
this.pendingTimer = window.setTimeout(() => {
|
|
52
|
+
this.pendingTimer = undefined;
|
|
53
|
+
if (this.disposed)
|
|
54
|
+
return;
|
|
49
55
|
void this.loadPets();
|
|
50
56
|
void this.loadDiagnostics();
|
|
51
57
|
}, 0);
|
|
@@ -54,6 +60,8 @@ export class PetSettingsCardController {
|
|
|
54
60
|
async loadDiagnostics() {
|
|
55
61
|
try {
|
|
56
62
|
this.diagnostics = await fetchPetDiagnostics();
|
|
63
|
+
if (this.disposed)
|
|
64
|
+
return;
|
|
57
65
|
this.store.set(this.projection());
|
|
58
66
|
}
|
|
59
67
|
catch {
|
|
@@ -62,10 +70,12 @@ export class PetSettingsCardController {
|
|
|
62
70
|
}
|
|
63
71
|
/** Resolve the registry choices once (retried a few times on failure). */
|
|
64
72
|
async loadPets() {
|
|
65
|
-
if (this.loaded)
|
|
73
|
+
if (this.loaded || this.disposed)
|
|
66
74
|
return;
|
|
67
75
|
try {
|
|
68
76
|
const list = await fetchPetChoices();
|
|
77
|
+
if (this.disposed)
|
|
78
|
+
return;
|
|
69
79
|
this.petChoices.splice(0, this.petChoices.length, ...list.map(choice => choice.id));
|
|
70
80
|
for (const choice of list)
|
|
71
81
|
this.petLabels.set(choice.id, choice.displayName);
|
|
@@ -73,9 +83,16 @@ export class PetSettingsCardController {
|
|
|
73
83
|
this.store.set(this.projection());
|
|
74
84
|
}
|
|
75
85
|
catch {
|
|
86
|
+
if (this.disposed)
|
|
87
|
+
return;
|
|
76
88
|
this.attempts += 1;
|
|
77
89
|
if (this.attempts < 3) {
|
|
78
|
-
window.setTimeout(() => {
|
|
90
|
+
this.pendingTimer = window.setTimeout(() => {
|
|
91
|
+
this.pendingTimer = undefined;
|
|
92
|
+
if (this.disposed)
|
|
93
|
+
return;
|
|
94
|
+
void this.loadPets();
|
|
95
|
+
}, 3000);
|
|
79
96
|
}
|
|
80
97
|
}
|
|
81
98
|
}
|
|
@@ -101,10 +118,17 @@ export class PetSettingsCardController {
|
|
|
101
118
|
return { hooks: { petSettingsCard: this.store }, ...this.form.actions() };
|
|
102
119
|
}
|
|
103
120
|
/**
|
|
104
|
-
* Release the card's scope subscription
|
|
105
|
-
* disposer calls this on teardown.
|
|
121
|
+
* Release the card's scope subscription, bound stores and pending load
|
|
122
|
+
* timers; the slot disposer calls this on teardown.
|
|
106
123
|
*/
|
|
107
124
|
dispose() {
|
|
125
|
+
if (this.disposed)
|
|
126
|
+
return;
|
|
127
|
+
this.disposed = true;
|
|
128
|
+
if (this.pendingTimer !== undefined) {
|
|
129
|
+
window.clearTimeout(this.pendingTimer);
|
|
130
|
+
this.pendingTimer = undefined;
|
|
131
|
+
}
|
|
108
132
|
this.form.dispose();
|
|
109
133
|
}
|
|
110
134
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAiiB5D"}
|
|
@@ -407,25 +407,33 @@ export function PetSprite(props) {
|
|
|
407
407
|
return;
|
|
408
408
|
clearHideTimer();
|
|
409
409
|
hideTimerRef.current = window.setTimeout(() => setHovered(false), 300);
|
|
410
|
-
}, children: [_jsx("div", { ref: spriteRef, className: styles.sprite, style: {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
410
|
+
}, children: [_jsxs("div", { className: styles.spriteWrap, style: { width: spriteWidth, height: spriteHeight }, children: [_jsx("div", { ref: spriteRef, className: styles.sprite, style: {
|
|
411
|
+
width: spriteWidth,
|
|
412
|
+
height: spriteHeight,
|
|
413
|
+
...(props.visual === undefined
|
|
414
|
+
? {
|
|
415
|
+
backgroundImage: imageReady ? 'url(' + definition.atlasUrl + ')' : undefined,
|
|
416
|
+
backgroundSize: (cell.width * columns * spriteScale) + 'px ' + (cell.height * (definition.atlasRows ?? rows.length) * spriteScale) + 'px',
|
|
417
|
+
backgroundRepeat: 'no-repeat',
|
|
418
|
+
backgroundPosition: '0 0',
|
|
419
|
+
}
|
|
420
|
+
: {}),
|
|
421
|
+
cursor: dragRef.current === null ? 'grab' : 'grabbing',
|
|
422
|
+
}, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onClick: () => {
|
|
423
|
+
// A pointer sequence that moved (dragged) still fires a trailing
|
|
424
|
+
// click; skip the pet when that happened.
|
|
425
|
+
if (draggedRef.current)
|
|
426
|
+
return;
|
|
427
|
+
props.onPet();
|
|
428
|
+
}, role: "button", "aria-label": definition.displayName, children: props.visual }), _jsx("button", { type: "button", className: styles.closeButton, "aria-label": panelLabel('hide', props.t('pet.hide')), title: panelLabel('hide', props.t('pet.hide')), "data-testid": "pet-close", onPointerDown: (e) => {
|
|
429
|
+
// Keep the close control from starting a drag on the sprite.
|
|
430
|
+
e.stopPropagation();
|
|
431
|
+
}, onClick: (e) => {
|
|
432
|
+
// The close control sits beside the pet button; do not pet as a
|
|
433
|
+
// side effect of closing the overlay.
|
|
434
|
+
e.stopPropagation();
|
|
435
|
+
props.onHide();
|
|
436
|
+
}, children: "\u00D7" })] }), feedback !== null && (_jsx("div", { ref: bubbleRef, className: clsx(styles.bubble, feedback.kind === 'feed' ? styles.bubbleFeed : styles.bubblePet), children: feedback.text }, feedback.at)), feedback === null && (sessionBubbles.length > 0 || statusBubble !== undefined || whisper !== undefined) && (_jsxs("div", { ref: bubbleRef, className: styles.bubbleStack, onPointerEnter: () => setStackPeek(true), onPointerLeave: () => setStackPeek(false), children: [visibleSessions.map((session, index) => {
|
|
429
437
|
// The whisper rides the display session's bubble — the stack's
|
|
430
438
|
// primary entry (DOM-first, rendered bottom-most by the reversed
|
|
431
439
|
// column so it stays glued to the sprite when extras open above).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAgEnI,wEAAwE;AACxE,eAAO,MAAM,MAAM,UAA2E,CAAA;AAE9F,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAwB,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAgEnI,wEAAwE;AACxE,eAAO,MAAM,MAAM,UAA2E,CAAA;AAE9F,qEAAqE;AACrE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AACxE,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AACrD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACtF,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAA;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA0Q9C"}
|
|
@@ -70,8 +70,13 @@ export function apply(ctx) {
|
|
|
70
70
|
};
|
|
71
71
|
// First-level settings section: one staged form over the 'pet' settings
|
|
72
72
|
// namespace, registered as a top-level settings page. The controller loads
|
|
73
|
-
// the petId choices from the registry endpoint itself
|
|
73
|
+
// the petId choices from the registry endpoint itself — the registry lists
|
|
74
|
+
// the available pets (built-in assets plus user dirs), so the section only
|
|
75
|
+
// ever shows installed pets. Installing new pets happens in the Workshop
|
|
76
|
+
// store.
|
|
74
77
|
const petSettings = new PetSettingsCardController(settingsScope);
|
|
78
|
+
// The section entry owns the controller: unregistering it (fiber disposal,
|
|
79
|
+
// hot reload) releases the scope subscription through petSettings.dispose.
|
|
75
80
|
ctx.slots.inject('settings.section', () => {
|
|
76
81
|
const unregister = ctx.slots.register({
|
|
77
82
|
name: 'settings.section',
|
|
@@ -82,8 +87,8 @@ export function apply(ctx) {
|
|
|
82
87
|
inject: () => petSettings.inject(),
|
|
83
88
|
}, PetSettingsSection);
|
|
84
89
|
return () => {
|
|
85
|
-
petSettings.dispose();
|
|
86
90
|
unregister();
|
|
91
|
+
petSettings.dispose();
|
|
87
92
|
};
|
|
88
93
|
});
|
|
89
94
|
// The global pet entry, its store, and the poll loop live while the plugin
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared JSON body/response helpers for the host route families: one strict
|
|
3
|
+
* bounded body reader, one lenient bounded body reader, one JSON object
|
|
4
|
+
* narrow, and one JSON writer. Previously these were copy-pasted across the
|
|
5
|
+
* package route files (routes.ts, update-routes.ts, mobile-api.ts, and each
|
|
6
|
+
* family's route module) with drifting contracts: body caps ranging 4 KiB to
|
|
7
|
+
* 1 MiB and four distinct overflow behaviors (reject, undefined, null, throw).
|
|
8
|
+
*
|
|
9
|
+
* Packages receive this file as a generated copy via scripts/sync-shared.mjs;
|
|
10
|
+
* edit this shared source and re-run the sync instead of editing a copy.
|
|
11
|
+
* Consumer code is migrated onto it in follow-up waves; no call site changes
|
|
12
|
+
* belong in the same change as its introduction.
|
|
13
|
+
* @module dsh-web-ui-shared/host/http
|
|
14
|
+
*/
|
|
15
|
+
import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'node:http';
|
|
16
|
+
/**
|
|
17
|
+
* Strict bounded body reader: parse a request body of at most maxBytes as
|
|
18
|
+
* JSON.
|
|
19
|
+
* @throws 'body too large' past the cap, or the JSON.parse error for an
|
|
20
|
+
* invalid or empty payload.
|
|
21
|
+
*/
|
|
22
|
+
export declare function readBoundedJson(req: IncomingMessage, maxBytes: number): Promise<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Lenient bounded body reader: parse a request body as JSON, or null on an
|
|
25
|
+
* empty body, invalid JSON, or a body past maxBytes (default 64 KiB).
|
|
26
|
+
* Overflow destroys the request instead of draining the remainder (no drain
|
|
27
|
+
* call, matching the current repo-wide behavior); callers must not keep
|
|
28
|
+
* reading the request afterwards. With objectOnly, non-JSON-object payloads
|
|
29
|
+
* also yield null.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readJsonBody(req: IncomingMessage, opts?: {
|
|
32
|
+
maxBytes?: number;
|
|
33
|
+
objectOnly?: boolean;
|
|
34
|
+
}): Promise<unknown | null>;
|
|
35
|
+
/** Narrow a value to a JSON object, or undefined when it is not one. */
|
|
36
|
+
export declare function asJsonObject(value: unknown): Record<string, unknown> | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Write one JSON response. Default headers are the family defaults
|
|
39
|
+
* (content-type and referrer-policy); caller headers are appended or
|
|
40
|
+
* override them.
|
|
41
|
+
*/
|
|
42
|
+
export declare function writeJson(res: ServerResponse, status: number, body: unknown, headers?: OutgoingHttpHeaders): void;
|
|
43
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAWrF;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU9F;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACrD,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAsBzB;AAOD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,mBAAwB,GAChC,IAAI,CAIN"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Generated by scripts/sync-shared.mjs from shared/host/http.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
|
|
2
|
+
/**
|
|
3
|
+
* Shared JSON body/response helpers for the host route families: one strict
|
|
4
|
+
* bounded body reader, one lenient bounded body reader, one JSON object
|
|
5
|
+
* narrow, and one JSON writer. Previously these were copy-pasted across the
|
|
6
|
+
* package route files (routes.ts, update-routes.ts, mobile-api.ts, and each
|
|
7
|
+
* family's route module) with drifting contracts: body caps ranging 4 KiB to
|
|
8
|
+
* 1 MiB and four distinct overflow behaviors (reject, undefined, null, throw).
|
|
9
|
+
*
|
|
10
|
+
* Packages receive this file as a generated copy via scripts/sync-shared.mjs;
|
|
11
|
+
* edit this shared source and re-run the sync instead of editing a copy.
|
|
12
|
+
* Consumer code is migrated onto it in follow-up waves; no call site changes
|
|
13
|
+
* belong in the same change as its introduction.
|
|
14
|
+
* @module dsh-web-ui-shared/host/http
|
|
15
|
+
*/
|
|
16
|
+
/** Default body cap for readJsonBody: 64 KiB. */
|
|
17
|
+
const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
|
|
18
|
+
/** Family-default JSON response headers; callers may append or override. */
|
|
19
|
+
const JSON_HEADERS = {
|
|
20
|
+
'content-type': 'application/json; charset=utf-8',
|
|
21
|
+
'referrer-policy': 'no-referrer',
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Strict bounded body reader: parse a request body of at most maxBytes as
|
|
25
|
+
* JSON.
|
|
26
|
+
* @throws 'body too large' past the cap, or the JSON.parse error for an
|
|
27
|
+
* invalid or empty payload.
|
|
28
|
+
*/
|
|
29
|
+
export async function readBoundedJson(req, maxBytes) {
|
|
30
|
+
const chunks = [];
|
|
31
|
+
let size = 0;
|
|
32
|
+
for await (const chunk of req) {
|
|
33
|
+
const buffer = chunk;
|
|
34
|
+
size += buffer.length;
|
|
35
|
+
if (size > maxBytes)
|
|
36
|
+
throw new Error('body too large');
|
|
37
|
+
chunks.push(buffer);
|
|
38
|
+
}
|
|
39
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Lenient bounded body reader: parse a request body as JSON, or null on an
|
|
43
|
+
* empty body, invalid JSON, or a body past maxBytes (default 64 KiB).
|
|
44
|
+
* Overflow destroys the request instead of draining the remainder (no drain
|
|
45
|
+
* call, matching the current repo-wide behavior); callers must not keep
|
|
46
|
+
* reading the request afterwards. With objectOnly, non-JSON-object payloads
|
|
47
|
+
* also yield null.
|
|
48
|
+
*/
|
|
49
|
+
export async function readJsonBody(req, opts = {}) {
|
|
50
|
+
const maxBytes = opts.maxBytes ?? DEFAULT_JSON_BODY_MAX_BYTES;
|
|
51
|
+
const chunks = [];
|
|
52
|
+
let size = 0;
|
|
53
|
+
for await (const chunk of req) {
|
|
54
|
+
const buffer = chunk;
|
|
55
|
+
size += buffer.length;
|
|
56
|
+
if (size > maxBytes) {
|
|
57
|
+
req.destroy();
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
chunks.push(buffer);
|
|
61
|
+
}
|
|
62
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
63
|
+
if (text === '')
|
|
64
|
+
return null;
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(text);
|
|
67
|
+
if (opts.objectOnly && !isJsonObject(parsed))
|
|
68
|
+
return null;
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Whether a value is a JSON object: typeof object, not null, not an array. */
|
|
76
|
+
function isJsonObject(value) {
|
|
77
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
78
|
+
}
|
|
79
|
+
/** Narrow a value to a JSON object, or undefined when it is not one. */
|
|
80
|
+
export function asJsonObject(value) {
|
|
81
|
+
return isJsonObject(value) ? value : undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Write one JSON response. Default headers are the family defaults
|
|
85
|
+
* (content-type and referrer-policy); caller headers are appended or
|
|
86
|
+
* override them.
|
|
87
|
+
*/
|
|
88
|
+
export function writeJson(res, status, body, headers = {}) {
|
|
89
|
+
const payload = JSON.stringify(body);
|
|
90
|
+
res.writeHead(status, { ...JSON_HEADERS, ...headers });
|
|
91
|
+
res.end(payload);
|
|
92
|
+
}
|
package/lib/types/registry.d.ts
CHANGED
|
@@ -258,6 +258,14 @@ export declare function resolvePetManifest(raw: unknown, dir: string, options?:
|
|
|
258
258
|
* discipline can apply (review-spd follow-up, pet-center M4/M5).
|
|
259
259
|
*/
|
|
260
260
|
export declare const PET_SCAN_JSON_CAP: number;
|
|
261
|
+
/**
|
|
262
|
+
* Scan-time read ceiling for a live2d model3.json, matching the asset
|
|
263
|
+
* route's model cap (PET_ASSET_CAPS.model). Model descriptors are far
|
|
264
|
+
* larger than the other scanned JSON, but a pathological file — huge, or a
|
|
265
|
+
* FIFO/device — must still be skipped with a warning instead of stalling
|
|
266
|
+
* or OOM-ing the host at plugin startup (same review-spd follow-up).
|
|
267
|
+
*/
|
|
268
|
+
export declare const PET_SCAN_LIVE2D_MODEL_CAP: number;
|
|
261
269
|
/** Decoration asset URL prefix (served by the decoration route, M5). */
|
|
262
270
|
export declare const DECORATION_ASSET_PREFIX = "/api/pet/decoration";
|
|
263
271
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/registry.ts"],"names":[],"mappings":"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"}
|