@dimina-kit/devtools 0.4.0-dev.20260729062524 → 0.4.0-dev.20260801163345
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/main/app/app.js +7 -0
- package/dist/main/app/bootstrap.d.ts +8 -5
- package/dist/main/app/bootstrap.js +15 -15
- package/dist/main/index.bundle.js +2207 -5152
- package/dist/main/ipc/app-lifecycle.d.ts +1 -31
- package/dist/main/ipc/app-lifecycle.js +1 -53
- package/dist/main/ipc/bridge-router.d.ts +19 -152
- package/dist/main/ipc/bridge-router.js +28 -2186
- package/dist/main/ipc/page-scroll.d.ts +1 -13
- package/dist/main/ipc/page-scroll.js +1 -16
- package/dist/main/ipc/session-listener-bag.d.ts +1 -40
- package/dist/main/ipc/session-listener-bag.js +1 -39
- package/dist/main/services/console-forward/index.d.ts +1 -60
- package/dist/main/services/console-forward/index.js +1 -225
- package/dist/main/services/console-forward/internal-log.d.ts +1 -10
- package/dist/main/services/console-forward/internal-log.js +1 -22
- package/dist/main/services/diagnostics/index.d.ts +1 -58
- package/dist/main/services/diagnostics/index.js +1 -60
- package/dist/main/services/dimina-resource-server.d.ts +1 -6
- package/dist/main/services/dimina-resource-server.js +1 -98
- package/dist/main/services/mcp/target-manager.js +5 -1
- package/dist/main/services/network-forward/index.d.ts +15 -3
- package/dist/main/services/network-forward/index.js +30 -4
- package/dist/main/services/network-forward/websocket.d.ts +36 -0
- package/dist/main/services/network-forward/websocket.js +120 -0
- package/dist/main/services/service-host-pool/pool.d.ts +1 -196
- package/dist/main/services/service-host-pool/pool.js +1 -363
- package/dist/main/services/simulator/custom-apis.d.ts +1 -42
- package/dist/main/services/simulator/custom-apis.js +1 -46
- package/dist/main/services/simulator/referer.d.ts +1 -9
- package/dist/main/services/simulator/referer.js +1 -19
- package/dist/main/services/simulator-storage/service-storage-ops.d.ts +1 -39
- package/dist/main/services/simulator-storage/service-storage-ops.js +1 -66
- package/dist/main/services/simulator-temp-files/disk.d.ts +1 -54
- package/dist/main/services/simulator-temp-files/disk.js +1 -237
- package/dist/main/services/simulator-temp-files/fs-channels.d.ts +1 -68
- package/dist/main/services/simulator-temp-files/fs-channels.js +1 -107
- package/dist/main/services/simulator-temp-files/index.d.ts +1 -32
- package/dist/main/services/simulator-temp-files/index.js +1 -181
- package/dist/main/services/simulator-temp-files/request-handler.d.ts +1 -29
- package/dist/main/services/simulator-temp-files/request-handler.js +1 -177
- package/dist/main/services/simulator-temp-files/resolver.d.ts +1 -21
- package/dist/main/services/simulator-temp-files/resolver.js +1 -21
- package/dist/main/services/simulator-temp-files/store.d.ts +1 -13
- package/dist/main/services/simulator-temp-files/store.js +1 -23
- package/dist/main/services/views/miniapp-partition.d.ts +1 -89
- package/dist/main/services/views/miniapp-partition.js +1 -158
- package/dist/main/services/views/simulator-session-policy.d.ts +1 -27
- package/dist/main/services/views/simulator-session-policy.js +1 -98
- package/dist/main/services/workbench-context.d.ts +4 -1
- package/dist/main/services/workbench-context.js +17 -0
- package/dist/main/utils/ipc-registry.d.ts +21 -3
- package/dist/main/utils/ipc-registry.js +26 -23
- package/dist/main/utils/paths.d.ts +14 -0
- package/dist/main/utils/paths.js +14 -0
- package/dist/main/windows/service-host-window/create.d.ts +1 -92
- package/dist/main/windows/service-host-window/create.js +1 -154
- package/dist/native-host/common/common.js +154 -58
- package/dist/native-host/container/pageFrame.css +1 -1
- package/dist/native-host/render/render.js +6611 -3312
- package/dist/native-host/service/service.js +2 -2
- package/dist/preload/index.d.ts +2 -0
- package/dist/preload/index.js +1 -0
- package/dist/preload/runtime/clipboard.d.ts +6 -0
- package/dist/preload/runtime/clipboard.js +12 -0
- package/dist/preload/runtime/native-host.d.ts +2 -0
- package/dist/preload/runtime/native-host.js +5 -10
- package/dist/preload/runtime/node-bindings.d.ts +18 -0
- package/dist/preload/runtime/node-bindings.js +44 -0
- package/dist/preload/windows/host-toolbar-runtime.cjs.map +2 -2
- package/dist/preload/windows/main.cjs +1195 -897
- package/dist/preload/windows/main.cjs.map +4 -4
- package/dist/preload/windows/simulator.cjs +149 -60
- package/dist/preload/windows/simulator.cjs.map +4 -4
- package/dist/preload/windows/simulator.js +149 -60
- package/dist/render-host/pageFrame.html +3 -3
- package/dist/shared/bridge-channels.d.ts +1 -419
- package/dist/shared/bridge-channels.js +1 -89
- package/dist/shared/dmb-resource-url.d.ts +2 -0
- package/dist/shared/dmb-resource-url.js +2 -0
- package/dist/shared/ipc-channels.d.ts +2 -32
- package/dist/shared/ipc-channels.js +2 -5
- package/dist/shared/node-bindings.d.ts +2 -0
- package/dist/shared/node-bindings.js +2 -0
- package/dist/shared/request-core.d.ts +1 -83
- package/dist/shared/request-core.js +1 -167
- package/dist/simulator/assets/device-shell-Cw7c-IkG.js +2 -0
- package/dist/simulator/assets/{device-shell-BHz1v3Yo.css → device-shell-DbC5ccs8.css} +1 -1
- package/dist/simulator/assets/{jsx-runtime-hBv-rHqQ.js → jsx-runtime-DkKmJyBb.js} +2 -2
- package/dist/simulator/assets/simulator-COIE6R49.js +10 -0
- package/dist/simulator/assets/simulator-mini-app-BLn7XV8e.js +2 -0
- package/dist/simulator/simulator.html +2 -2
- package/dist/simulator/temp-files.d.ts +29 -0
- package/dist/simulator/temp-files.js +79 -0
- package/dist/simulator/types.d.ts +43 -0
- package/dist/simulator/types.js +2 -0
- package/package.json +6 -5
- package/dist/shared/simulator-api-metadata.d.ts +0 -26
- package/dist/shared/simulator-api-metadata.js +0 -59
- package/dist/shared/vpath.d.ts +0 -42
- package/dist/shared/vpath.js +0 -105
- package/dist/simulator/assets/device-shell-CH_DMYaz.js +0 -2
- package/dist/simulator/assets/simulator-DXraYerh.js +0 -10
- package/dist/simulator/assets/simulator-mini-app-w1Bhwkld.js +0 -2
|
@@ -28,7 +28,7 @@ var main_exports = {};
|
|
|
28
28
|
module.exports = __toCommonJS(main_exports);
|
|
29
29
|
var import_electron = require("electron");
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// ../dimina-electron-runtime/dist/shared/bridge-channels.js
|
|
32
32
|
var BRIDGE_CHANNELS = {
|
|
33
33
|
SPAWN: "dmb:spawn",
|
|
34
34
|
DISPOSE: "dmb:dispose",
|
|
@@ -108,8 +108,8 @@ function makeBaseContext() {
|
|
|
108
108
|
}
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
-
function runApiAsync(
|
|
112
|
-
const handler =
|
|
111
|
+
function runApiAsync(handlers2, name, params) {
|
|
112
|
+
const handler = handlers2[name];
|
|
113
113
|
if (!handler) {
|
|
114
114
|
return Promise.resolve({ ok: false, errMsg: `${name}:fail no handler` });
|
|
115
115
|
}
|
|
@@ -322,36 +322,45 @@ function vibrateLong({ success, complete } = {}) {
|
|
|
322
322
|
onComplete?.();
|
|
323
323
|
}
|
|
324
324
|
var scanCode = notSupportedApi("scanCode");
|
|
325
|
+
function getClipboardBridge() {
|
|
326
|
+
return window.__diminaClipboard;
|
|
327
|
+
}
|
|
325
328
|
function getClipboardData({ success, fail, complete } = {}) {
|
|
326
329
|
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
)
|
|
330
|
+
const bridge = getClipboardBridge();
|
|
331
|
+
if (!bridge) {
|
|
332
|
+
onFail?.({ errMsg: "getClipboardData:fail clipboard bridge unavailable" });
|
|
333
|
+
onComplete?.();
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
try {
|
|
337
|
+
const data = bridge.readText();
|
|
338
|
+
onSuccess?.({ data, errMsg: "getClipboardData:ok" });
|
|
339
|
+
} catch (error) {
|
|
340
|
+
onFail?.({ errMsg: `getClipboardData:fail ${error?.message ?? error}` });
|
|
341
|
+
}
|
|
342
|
+
onComplete?.();
|
|
337
343
|
}
|
|
338
344
|
function setClipboardData({ data, success, fail, complete } = {}) {
|
|
339
345
|
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
340
346
|
if (typeof data !== "string") {
|
|
341
347
|
onFail?.({ errMsg: "setClipboardData:fail data is required" });
|
|
342
348
|
onComplete?.();
|
|
343
|
-
return
|
|
349
|
+
return;
|
|
344
350
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}
|
|
354
|
-
)
|
|
351
|
+
const bridge = getClipboardBridge();
|
|
352
|
+
if (!bridge) {
|
|
353
|
+
onFail?.({ errMsg: "setClipboardData:fail clipboard bridge unavailable" });
|
|
354
|
+
onComplete?.();
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
try {
|
|
358
|
+
bridge.writeText(data);
|
|
359
|
+
onSuccess?.({ errMsg: "setClipboardData:ok" });
|
|
360
|
+
} catch (error) {
|
|
361
|
+
onFail?.({ errMsg: `setClipboardData:fail ${error?.message ?? error}` });
|
|
362
|
+
}
|
|
363
|
+
onComplete?.();
|
|
355
364
|
}
|
|
356
365
|
function mapEffectiveType(effectiveType) {
|
|
357
366
|
switch (effectiveType) {
|
|
@@ -403,6 +412,71 @@ function bindDomEvents(target, eventMap, fire, buildPayload) {
|
|
|
403
412
|
};
|
|
404
413
|
}
|
|
405
414
|
|
|
415
|
+
// ../dimina-electron-runtime/dist/shared/node-bindings.js
|
|
416
|
+
var NODE_BINDINGS_GLOBAL = "__diminaNodeBindings";
|
|
417
|
+
function getNodeBindings() {
|
|
418
|
+
const bag = globalThis[NODE_BINDINGS_GLOBAL];
|
|
419
|
+
return bag && typeof bag === "object" ? bag : {};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ../dimina-electron-runtime/dist/shared/vpath.js
|
|
423
|
+
var import_node_os = __toESM(require("node:os"), 1);
|
|
424
|
+
var import_node_path = __toESM(require("node:path"), 1);
|
|
425
|
+
var os = typeof import_node_os.default?.homedir === "function" ? import_node_os.default : getNodeBindings().os ?? import_node_os.default;
|
|
426
|
+
var path = typeof import_node_path.default?.join === "function" ? import_node_path.default : getNodeBindings().path ?? import_node_path.default;
|
|
427
|
+
var DIFILE_PREFIX = "difile://";
|
|
428
|
+
function sandboxBase() {
|
|
429
|
+
const env = typeof process !== "undefined" && process.env && process.env.DIMINA_HOME || "";
|
|
430
|
+
if (env) {
|
|
431
|
+
return path.join(env, "files");
|
|
432
|
+
}
|
|
433
|
+
return path.join(os.homedir(), ".dimina", "files");
|
|
434
|
+
}
|
|
435
|
+
function resolveVPath(url) {
|
|
436
|
+
if (typeof url !== "string")
|
|
437
|
+
return null;
|
|
438
|
+
if (!url.startsWith(DIFILE_PREFIX))
|
|
439
|
+
return null;
|
|
440
|
+
const raw = url.slice(DIFILE_PREFIX.length);
|
|
441
|
+
if (raw.length === 0)
|
|
442
|
+
return null;
|
|
443
|
+
let decoded;
|
|
444
|
+
try {
|
|
445
|
+
decoded = decodeURIComponent(raw);
|
|
446
|
+
} catch {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
if (decoded.includes("\0"))
|
|
450
|
+
return null;
|
|
451
|
+
decoded = decoded.replace(/^[/\\]+/, "");
|
|
452
|
+
if (decoded.length === 0)
|
|
453
|
+
return null;
|
|
454
|
+
const segments = decoded.split(/[/\\]+/);
|
|
455
|
+
if (segments.some((s) => s === ".." || s === "."))
|
|
456
|
+
return null;
|
|
457
|
+
let kind;
|
|
458
|
+
let writable;
|
|
459
|
+
if (decoded.startsWith("_tmp/")) {
|
|
460
|
+
kind = "tmp";
|
|
461
|
+
writable = false;
|
|
462
|
+
} else if (decoded.startsWith("_store/")) {
|
|
463
|
+
kind = "store";
|
|
464
|
+
writable = false;
|
|
465
|
+
} else {
|
|
466
|
+
kind = "usr";
|
|
467
|
+
writable = true;
|
|
468
|
+
}
|
|
469
|
+
if (kind === "tmp") {
|
|
470
|
+
return { kind, writable };
|
|
471
|
+
}
|
|
472
|
+
const base = sandboxBase();
|
|
473
|
+
const joined = path.join(base, decoded);
|
|
474
|
+
const normalized = path.normalize(joined);
|
|
475
|
+
if (normalized !== base && !normalized.startsWith(base + path.sep))
|
|
476
|
+
return null;
|
|
477
|
+
return { kind, writable, realPath: normalized };
|
|
478
|
+
}
|
|
479
|
+
|
|
406
480
|
// src/simulator/temp-files.ts
|
|
407
481
|
var tempFiles = /* @__PURE__ */ new Map();
|
|
408
482
|
var activeSink = null;
|
|
@@ -442,988 +516,1227 @@ function getTempFileName(path2, blob, fallback = "file") {
|
|
|
442
516
|
return fallback;
|
|
443
517
|
}
|
|
444
518
|
|
|
445
|
-
// src/simulator/simulator-api-
|
|
446
|
-
function
|
|
447
|
-
|
|
448
|
-
|
|
519
|
+
// src/simulator/simulator-api-fs.ts
|
|
520
|
+
async function _tmpBytes(url) {
|
|
521
|
+
try {
|
|
522
|
+
const blob = await resolveTempFilePath(url);
|
|
523
|
+
return await blobToBuffer(blob);
|
|
524
|
+
} catch (err) {
|
|
525
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
526
|
+
throw new Error(`ENOENT: no such file (${url}): ${msg}`, { cause: err });
|
|
527
|
+
}
|
|
449
528
|
}
|
|
450
|
-
function
|
|
451
|
-
|
|
452
|
-
input.type = "file";
|
|
453
|
-
input.accept = config.accept;
|
|
454
|
-
input.multiple = config.multiple;
|
|
455
|
-
if (config.capture) input.setAttribute("capture", config.capture);
|
|
456
|
-
input.style.display = "none";
|
|
457
|
-
document.body.appendChild(input);
|
|
458
|
-
const removeInput = () => input.remove();
|
|
459
|
-
input.addEventListener("change", () => {
|
|
460
|
-
const files = Array.from(input.files || []);
|
|
461
|
-
if (files.length === 0) {
|
|
462
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail cancel` });
|
|
463
|
-
cbs.onComplete?.();
|
|
464
|
-
removeInput();
|
|
465
|
-
return;
|
|
466
|
-
}
|
|
467
|
-
void onPick(files, removeInput);
|
|
468
|
-
});
|
|
469
|
-
input.click();
|
|
529
|
+
function isArrayBuffer(value) {
|
|
530
|
+
return Object.prototype.toString.call(value) === "[object ArrayBuffer]";
|
|
470
531
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
532
|
+
var BufferImpl = getNodeBindings().buffer?.Buffer ?? (typeof Buffer !== "undefined" ? Buffer : void 0);
|
|
533
|
+
async function blobToBuffer(blob) {
|
|
534
|
+
if (!BufferImpl) {
|
|
535
|
+
throw new Error("Buffer binding unavailable");
|
|
536
|
+
}
|
|
537
|
+
const readable = blob;
|
|
538
|
+
if (typeof readable.arrayBuffer === "function") {
|
|
539
|
+
const ab = await readable.arrayBuffer();
|
|
540
|
+
return BufferImpl.from(new Uint8Array(ab));
|
|
541
|
+
}
|
|
542
|
+
if (typeof FileReader !== "function") {
|
|
543
|
+
throw new Error("Blob cannot be read as ArrayBuffer");
|
|
544
|
+
}
|
|
545
|
+
return new Promise((resolve, reject) => {
|
|
546
|
+
const reader = new FileReader();
|
|
547
|
+
reader.onerror = () => reject(reader.error ?? new Error("Blob read failed"));
|
|
548
|
+
reader.onload = () => {
|
|
549
|
+
const result = reader.result;
|
|
550
|
+
if (!isArrayBuffer(result)) {
|
|
551
|
+
reject(new Error("Blob read did not return an ArrayBuffer"));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
resolve(BufferImpl.from(new Uint8Array(result)));
|
|
555
|
+
};
|
|
556
|
+
reader.readAsArrayBuffer(blob);
|
|
557
|
+
});
|
|
489
558
|
}
|
|
490
|
-
function
|
|
491
|
-
|
|
492
|
-
if (
|
|
493
|
-
|
|
494
|
-
return;
|
|
559
|
+
function resolveNodeModule(fromBindings, name, probe) {
|
|
560
|
+
if (fromBindings && probe(fromBindings)) return fromBindings;
|
|
561
|
+
if (typeof require !== "undefined") {
|
|
562
|
+
const mod = require(name);
|
|
563
|
+
if (mod && probe(mod)) return mod;
|
|
495
564
|
}
|
|
496
|
-
|
|
497
|
-
overlay.style.cssText = "position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;cursor:pointer;";
|
|
498
|
-
const img = document.createElement("img");
|
|
499
|
-
img.src = current || urls[0] || "";
|
|
500
|
-
img.style.cssText = "max-width:90%;max-height:90%;object-fit:contain;";
|
|
501
|
-
overlay.appendChild(img);
|
|
502
|
-
overlay.addEventListener("click", () => overlay.remove());
|
|
503
|
-
document.body.appendChild(overlay);
|
|
504
|
-
onSuccess?.({ errMsg: "previewImage:ok" });
|
|
505
|
-
onComplete?.();
|
|
565
|
+
return null;
|
|
506
566
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
567
|
+
var _fs = resolveNodeModule(getNodeBindings().fs, "fs", (m) => typeof m.readFile === "function");
|
|
568
|
+
var _path = resolveNodeModule(getNodeBindings().path, "path", (m) => typeof m.join === "function");
|
|
569
|
+
var _crypto = resolveNodeModule(getNodeBindings().crypto, "crypto", (m) => typeof m.createHash === "function");
|
|
570
|
+
function guardFsAvailable(apiName, cbs) {
|
|
571
|
+
if (_fs) return false;
|
|
572
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail not available in browser context` });
|
|
573
|
+
cbs.onComplete?.();
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
function containsSymlink(realPath) {
|
|
577
|
+
const rel = _path.relative(sandboxBase(), realPath);
|
|
578
|
+
if (!rel || rel.startsWith("..")) return false;
|
|
579
|
+
let cur = sandboxBase();
|
|
580
|
+
for (const seg of rel.split(_path.sep)) {
|
|
581
|
+
cur = _path.join(cur, seg);
|
|
582
|
+
let entry;
|
|
512
583
|
try {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const ctx = canvas.getContext("2d");
|
|
517
|
-
ctx.drawImage(img, 0, 0);
|
|
518
|
-
canvas.toBlob(
|
|
519
|
-
(blob) => {
|
|
520
|
-
if (blob) {
|
|
521
|
-
const tempFilePath = createTempFilePath(blob);
|
|
522
|
-
onSuccess?.({ tempFilePath, errMsg: "compressImage:ok" });
|
|
523
|
-
} else {
|
|
524
|
-
onFail?.({ errMsg: "compressImage:fail compression error" });
|
|
525
|
-
}
|
|
526
|
-
onComplete?.();
|
|
527
|
-
},
|
|
528
|
-
"image/jpeg",
|
|
529
|
-
quality / 100
|
|
530
|
-
);
|
|
531
|
-
} catch (error) {
|
|
532
|
-
onFail?.({ errMsg: `compressImage:fail ${error.message}` });
|
|
533
|
-
onComplete?.();
|
|
584
|
+
entry = _fs.lstatSync(cur);
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
534
587
|
}
|
|
535
|
-
|
|
536
|
-
img.onerror = () => {
|
|
537
|
-
onFail?.({ errMsg: "compressImage:fail image load error" });
|
|
538
|
-
onComplete?.();
|
|
539
|
-
};
|
|
540
|
-
img.src = src;
|
|
541
|
-
}
|
|
542
|
-
function saveImageToPhotosAlbum({ filePath, success, fail, complete }) {
|
|
543
|
-
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
544
|
-
try {
|
|
545
|
-
const a = document.createElement("a");
|
|
546
|
-
a.href = filePath;
|
|
547
|
-
a.download = "image";
|
|
548
|
-
a.click();
|
|
549
|
-
onSuccess?.({ errMsg: "saveImageToPhotosAlbum:ok" });
|
|
550
|
-
} catch (error) {
|
|
551
|
-
onFail?.({ errMsg: `saveImageToPhotosAlbum:fail ${error.message}` });
|
|
588
|
+
if (entry.isSymbolicLink()) return true;
|
|
552
589
|
}
|
|
553
|
-
|
|
590
|
+
return false;
|
|
554
591
|
}
|
|
555
|
-
function
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
img.onerror = () => {
|
|
571
|
-
onFail?.({ errMsg: "getImageInfo:fail image load error" });
|
|
572
|
-
onComplete?.();
|
|
592
|
+
function resolveOrBail(p, apiName, cbs) {
|
|
593
|
+
const v = resolveVPath(p);
|
|
594
|
+
if (v && (!v.realPath || !_fs || !containsSymlink(v.realPath))) return v;
|
|
595
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail invalid or unsafe path` });
|
|
596
|
+
cbs.onComplete?.();
|
|
597
|
+
return void 0;
|
|
598
|
+
}
|
|
599
|
+
function toWxStats(s) {
|
|
600
|
+
return {
|
|
601
|
+
size: s.size,
|
|
602
|
+
mode: s.mode,
|
|
603
|
+
lastAccessedTime: Math.floor(s.atimeMs / 1e3),
|
|
604
|
+
lastModifiedTime: Math.floor(s.mtimeMs / 1e3),
|
|
605
|
+
isFile: s.isFile(),
|
|
606
|
+
isDirectory: s.isDirectory()
|
|
573
607
|
};
|
|
574
|
-
img.src = src;
|
|
575
608
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
609
|
+
function ensureWritable(v, apiName, cbs) {
|
|
610
|
+
if (v.writable && v.realPath) return true;
|
|
611
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail permission denied` });
|
|
612
|
+
cbs.onComplete?.();
|
|
613
|
+
return false;
|
|
580
614
|
}
|
|
581
|
-
function
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
615
|
+
function tmpFailHandler(apiName, cbs) {
|
|
616
|
+
return (err) => {
|
|
617
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` });
|
|
618
|
+
cbs.onComplete?.();
|
|
619
|
+
};
|
|
585
620
|
}
|
|
586
|
-
function
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
if (types.size > 1) return "mix";
|
|
596
|
-
return files[0]?.fileType ?? "image";
|
|
621
|
+
function nodeComplete(apiName, cbs, buildOk) {
|
|
622
|
+
return (err, ...args) => {
|
|
623
|
+
if (err) {
|
|
624
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` });
|
|
625
|
+
} else {
|
|
626
|
+
cbs.onSuccess?.({ ...buildOk ? buildOk(...args) : void 0, errMsg: `${apiName}:ok` });
|
|
627
|
+
}
|
|
628
|
+
cbs.onComplete?.();
|
|
629
|
+
};
|
|
597
630
|
}
|
|
598
|
-
function
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
631
|
+
function mkdirpThenWrite(destReal, apiName, cbs, writeFn, okExtra) {
|
|
632
|
+
_fs.mkdir(_path.dirname(destReal), { recursive: true }, (mkdirErr) => {
|
|
633
|
+
if (mkdirErr) {
|
|
634
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail ${mkdirErr.message}` });
|
|
635
|
+
cbs.onComplete?.();
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
try {
|
|
639
|
+
writeFn(nodeComplete(apiName, cbs, () => okExtra));
|
|
640
|
+
} catch (err) {
|
|
641
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail ${err instanceof Error ? err.message : String(err)}` });
|
|
642
|
+
cbs.onComplete?.();
|
|
643
|
+
}
|
|
604
644
|
});
|
|
605
645
|
}
|
|
606
|
-
function
|
|
607
|
-
|
|
608
|
-
|
|
646
|
+
function fsAccess({ path: path2, success, fail, complete }) {
|
|
647
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
648
|
+
const { onSuccess, onFail, onComplete } = cbs;
|
|
649
|
+
if (guardFsAvailable("fsAccess", cbs)) return;
|
|
650
|
+
const v = resolveOrBail(path2, "fsAccess", cbs);
|
|
651
|
+
if (!v) return;
|
|
652
|
+
if (v.kind === "tmp") {
|
|
653
|
+
_tmpBytes(path2).then(
|
|
654
|
+
() => {
|
|
655
|
+
onSuccess?.({ errMsg: "fsAccess:ok" });
|
|
656
|
+
onComplete?.();
|
|
657
|
+
},
|
|
658
|
+
tmpFailHandler("fsAccess", cbs)
|
|
659
|
+
);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
if (!v.realPath) {
|
|
663
|
+
onFail?.({ errMsg: "fsAccess:fail invalid path" });
|
|
664
|
+
onComplete?.();
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
_fs.access(v.realPath, _fs.constants.F_OK, nodeComplete("fsAccess", cbs));
|
|
668
|
+
}
|
|
669
|
+
function fsStat({ path: path2, recursive = false, success, fail, complete }) {
|
|
670
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
671
|
+
const { onSuccess, onFail, onComplete } = cbs;
|
|
672
|
+
if (guardFsAvailable("fsStat", cbs)) return;
|
|
673
|
+
const v = resolveOrBail(path2, "fsStat", cbs);
|
|
674
|
+
if (!v) return;
|
|
675
|
+
if (v.kind === "tmp") {
|
|
676
|
+
_tmpBytes(path2).then(
|
|
677
|
+
(buf) => {
|
|
678
|
+
onSuccess?.({
|
|
679
|
+
stats: {
|
|
680
|
+
size: buf.length,
|
|
681
|
+
mode: 0,
|
|
682
|
+
lastAccessedTime: 0,
|
|
683
|
+
lastModifiedTime: 0,
|
|
684
|
+
isFile: true,
|
|
685
|
+
isDirectory: false
|
|
686
|
+
},
|
|
687
|
+
errMsg: "fsStat:ok"
|
|
688
|
+
});
|
|
689
|
+
onComplete?.();
|
|
690
|
+
},
|
|
691
|
+
tmpFailHandler("fsStat", cbs)
|
|
692
|
+
);
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
if (!v.realPath) {
|
|
696
|
+
onFail?.({ errMsg: "fsStat:fail invalid path" });
|
|
697
|
+
onComplete?.();
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const resolved = v.realPath;
|
|
701
|
+
if (recursive) {
|
|
702
|
+
const statsMap = {};
|
|
609
703
|
let settled = false;
|
|
610
|
-
|
|
611
|
-
const finish = (metadata) => {
|
|
704
|
+
const failOnce = (err) => {
|
|
612
705
|
if (settled) return;
|
|
613
706
|
settled = true;
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
video.onloadedmetadata = null;
|
|
617
|
-
video.onseeked = null;
|
|
618
|
-
video.onerror = null;
|
|
619
|
-
video.removeAttribute("src");
|
|
620
|
-
video.load();
|
|
621
|
-
resolve(metadata);
|
|
707
|
+
onFail?.({ errMsg: `fsStat:fail ${err.message}` });
|
|
708
|
+
onComplete?.();
|
|
622
709
|
};
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
638
|
-
} catch {
|
|
639
|
-
done("");
|
|
710
|
+
const okOnce = () => {
|
|
711
|
+
if (settled) return;
|
|
712
|
+
settled = true;
|
|
713
|
+
onSuccess?.({ stats: statsMap, errMsg: "fsStat:ok" });
|
|
714
|
+
onComplete?.();
|
|
715
|
+
};
|
|
716
|
+
const relKey = (full) => {
|
|
717
|
+
const rel = _path.relative(resolved, full);
|
|
718
|
+
return rel === "" ? "." : rel.split(_path.sep).join("/");
|
|
719
|
+
};
|
|
720
|
+
const statInto = (full, cb) => {
|
|
721
|
+
_fs.lstat(full, (err, s) => {
|
|
722
|
+
if (err) {
|
|
723
|
+
cb(err);
|
|
640
724
|
return;
|
|
641
725
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
726
|
+
if (s.isSymbolicLink()) {
|
|
727
|
+
cb(new Error("invalid or unsafe path"));
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
statsMap[relKey(full)] = toWxStats(s);
|
|
731
|
+
cb(null, s.isDirectory());
|
|
732
|
+
});
|
|
733
|
+
};
|
|
734
|
+
const walkDir = (dir, cb) => {
|
|
735
|
+
_fs.readdir(dir, { withFileTypes: true }, (err, entries) => {
|
|
736
|
+
if (err) {
|
|
737
|
+
cb(err);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
let pending = entries.length;
|
|
741
|
+
if (pending === 0) {
|
|
742
|
+
cb(null);
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
let erred = false;
|
|
746
|
+
const done = (e) => {
|
|
747
|
+
if (erred) return;
|
|
748
|
+
if (e) {
|
|
749
|
+
erred = true;
|
|
750
|
+
cb(e);
|
|
751
|
+
return;
|
|
647
752
|
}
|
|
753
|
+
if (--pending === 0) cb(null);
|
|
648
754
|
};
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
(
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
},
|
|
661
|
-
"image/jpeg",
|
|
662
|
-
0.8
|
|
663
|
-
);
|
|
664
|
-
} catch {
|
|
665
|
-
clearTimeout(fallbackTimer);
|
|
666
|
-
toDataUrlFallback();
|
|
755
|
+
for (const entry of entries) {
|
|
756
|
+
const full = _path.join(dir, entry.name);
|
|
757
|
+
statInto(full, (statErr, isDirectory) => {
|
|
758
|
+
if (statErr) {
|
|
759
|
+
done(statErr);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (isDirectory) walkDir(full, done);
|
|
763
|
+
else done(null);
|
|
764
|
+
});
|
|
667
765
|
}
|
|
668
766
|
});
|
|
669
767
|
};
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
video.onloadedmetadata = () => {
|
|
674
|
-
const width = video.videoWidth || 0;
|
|
675
|
-
const height = video.videoHeight || 0;
|
|
676
|
-
const duration = Number.isFinite(video.duration) ? video.duration : 0;
|
|
677
|
-
const metadata = { width, height, duration, thumbTempFilePath: "" };
|
|
678
|
-
if (!width || !height || duration <= 0) {
|
|
679
|
-
finish(metadata);
|
|
768
|
+
statInto(resolved, (rootErr) => {
|
|
769
|
+
if (rootErr) {
|
|
770
|
+
failOnce(rootErr);
|
|
680
771
|
return;
|
|
681
772
|
}
|
|
682
|
-
|
|
683
|
-
if (
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
try {
|
|
692
|
-
video.currentTime = Math.min(0.1, Math.max(0, duration - 0.01));
|
|
693
|
-
} catch {
|
|
694
|
-
finish(metadata);
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
video.onerror = () => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: "" });
|
|
698
|
-
video.src = src;
|
|
699
|
-
});
|
|
700
|
-
}
|
|
701
|
-
async function buildChooseMediaTempFile(file) {
|
|
702
|
-
const tempFilePath = createTempFilePath(file);
|
|
703
|
-
const fileType = file.type.startsWith("video") ? "video" : "image";
|
|
704
|
-
if (fileType === "video") {
|
|
705
|
-
const metadata2 = await readVideoMetadata(tempFilePath);
|
|
706
|
-
return {
|
|
707
|
-
tempFilePath,
|
|
708
|
-
size: file.size,
|
|
709
|
-
duration: metadata2.duration,
|
|
710
|
-
height: metadata2.height,
|
|
711
|
-
width: metadata2.width,
|
|
712
|
-
thumbTempFilePath: metadata2.thumbTempFilePath,
|
|
713
|
-
fileType,
|
|
714
|
-
originalFileObj: file
|
|
715
|
-
};
|
|
773
|
+
walkDir(resolved, (err) => {
|
|
774
|
+
if (err) failOnce(err);
|
|
775
|
+
else okOnce();
|
|
776
|
+
});
|
|
777
|
+
});
|
|
778
|
+
} else {
|
|
779
|
+
_fs.stat(resolved, nodeComplete("fsStat", cbs, (s) => ({
|
|
780
|
+
stats: toWxStats(s)
|
|
781
|
+
})));
|
|
716
782
|
}
|
|
717
|
-
const metadata = await readImageMetadata(tempFilePath);
|
|
718
|
-
return {
|
|
719
|
-
tempFilePath,
|
|
720
|
-
size: file.size,
|
|
721
|
-
duration: 0,
|
|
722
|
-
height: metadata.height,
|
|
723
|
-
width: metadata.width,
|
|
724
|
-
thumbTempFilePath: "",
|
|
725
|
-
fileType,
|
|
726
|
-
originalFileObj: file
|
|
727
|
-
};
|
|
728
783
|
}
|
|
729
|
-
function
|
|
784
|
+
function fsReadFile({ filePath, encoding, success, fail, complete }) {
|
|
730
785
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
731
786
|
const { onSuccess, onFail, onComplete } = cbs;
|
|
732
|
-
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
capture: captureAttrFor(normalizedSourceType, camera)
|
|
741
|
-
},
|
|
742
|
-
cbs,
|
|
743
|
-
async (rawFiles, removeInput) => {
|
|
744
|
-
const files = rawFiles.slice(0, normalizedCount);
|
|
745
|
-
try {
|
|
746
|
-
const tempFiles2 = await Promise.all(files.map(buildChooseMediaTempFile));
|
|
747
|
-
onSuccess?.({
|
|
748
|
-
tempFiles: tempFiles2,
|
|
749
|
-
type: getChooseMediaResultType(tempFiles2),
|
|
750
|
-
failedCount: 0,
|
|
751
|
-
errMsg: "chooseMedia:ok"
|
|
752
|
-
});
|
|
753
|
-
} catch (error) {
|
|
754
|
-
onFail?.({ errMsg: `chooseMedia:fail ${error.message}` });
|
|
755
|
-
} finally {
|
|
787
|
+
if (guardFsAvailable("fsReadFile", cbs)) return;
|
|
788
|
+
const v = resolveOrBail(filePath, "fsReadFile", cbs);
|
|
789
|
+
if (!v) return;
|
|
790
|
+
if (v.kind === "tmp") {
|
|
791
|
+
_tmpBytes(filePath).then(
|
|
792
|
+
(buf) => {
|
|
793
|
+
const data = encoding ? buf.toString(encoding) : buf;
|
|
794
|
+
onSuccess?.({ data, errMsg: "fsReadFile:ok" });
|
|
756
795
|
onComplete?.();
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
796
|
+
},
|
|
797
|
+
tmpFailHandler("fsReadFile", cbs)
|
|
798
|
+
);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
if (!v.realPath) {
|
|
802
|
+
onFail?.({ errMsg: "fsReadFile:fail invalid path" });
|
|
803
|
+
onComplete?.();
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
_fs.readFile(v.realPath, encoding || null, nodeComplete("fsReadFile", cbs, (data) => ({ data })));
|
|
761
807
|
}
|
|
762
|
-
function
|
|
808
|
+
function fsWriteFile({ filePath, data, encoding = "utf8", success, fail, complete }) {
|
|
763
809
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
764
|
-
|
|
765
|
-
const
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
cbs,
|
|
770
|
-
async (files, removeInput) => {
|
|
771
|
-
const file = files[0];
|
|
772
|
-
const tempFilePath = createTempFilePath(file);
|
|
773
|
-
const metadata = await readVideoMetadata(tempFilePath);
|
|
774
|
-
onSuccess?.({
|
|
775
|
-
tempFilePath,
|
|
776
|
-
duration: metadata.duration,
|
|
777
|
-
size: file.size,
|
|
778
|
-
width: metadata.width,
|
|
779
|
-
height: metadata.height,
|
|
780
|
-
errMsg: "chooseVideo:ok"
|
|
781
|
-
});
|
|
782
|
-
onComplete?.();
|
|
783
|
-
removeInput();
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
}
|
|
787
|
-
var AUDIO_EVENT_MAP = {
|
|
788
|
-
play: "play",
|
|
789
|
-
pause: "pause",
|
|
790
|
-
ended: "ended",
|
|
791
|
-
error: "error",
|
|
792
|
-
timeupdate: "timeUpdate",
|
|
793
|
-
waiting: "waiting",
|
|
794
|
-
seeking: "seeking",
|
|
795
|
-
seeked: "seeked",
|
|
796
|
-
canplay: "canplay"
|
|
797
|
-
};
|
|
798
|
-
var _newAudioInstances = /* @__PURE__ */ new Map();
|
|
799
|
-
var _audioEventDisposers = /* @__PURE__ */ new Map();
|
|
800
|
-
var _audioFire = /* @__PURE__ */ new Map();
|
|
801
|
-
function audioSnapshot(audio, event) {
|
|
802
|
-
return {
|
|
803
|
-
event,
|
|
804
|
-
currentTime: audio.currentTime || 0,
|
|
805
|
-
duration: Number.isFinite(audio.duration) ? audio.duration : 0,
|
|
806
|
-
buffered: audio.buffered.length ? audio.buffered.end(audio.buffered.length - 1) : 0,
|
|
807
|
-
paused: audio.paused
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
function audioCreate({ audioId }) {
|
|
811
|
-
_newAudioInstances.set(audioId, new Audio());
|
|
810
|
+
if (guardFsAvailable("fsWriteFile", cbs)) return;
|
|
811
|
+
const v = resolveOrBail(filePath, "fsWriteFile", cbs);
|
|
812
|
+
if (!v) return;
|
|
813
|
+
if (!ensureWritable(v, "fsWriteFile", cbs)) return;
|
|
814
|
+
mkdirpThenWrite(v.realPath, "fsWriteFile", cbs, (done) => _fs.writeFile(v.realPath, data, { encoding }, done));
|
|
812
815
|
}
|
|
813
|
-
function
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
audio,
|
|
821
|
-
AUDIO_EVENT_MAP,
|
|
822
|
-
fire,
|
|
823
|
-
(event) => audioSnapshot(audio, event)
|
|
824
|
-
);
|
|
825
|
-
_audioEventDisposers.set(audioId, dispose);
|
|
816
|
+
function fsAppendFile({ filePath, data, encoding = "utf8", success, fail, complete }) {
|
|
817
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
818
|
+
if (guardFsAvailable("fsAppendFile", cbs)) return;
|
|
819
|
+
const v = resolveOrBail(filePath, "fsAppendFile", cbs);
|
|
820
|
+
if (!v) return;
|
|
821
|
+
if (!ensureWritable(v, "fsAppendFile", cbs)) return;
|
|
822
|
+
_fs.appendFile(v.realPath, data, { encoding }, nodeComplete("fsAppendFile", cbs));
|
|
826
823
|
}
|
|
827
|
-
function
|
|
828
|
-
const
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
break;
|
|
843
|
-
case "autoplay":
|
|
844
|
-
audio.autoplay = !!value;
|
|
845
|
-
break;
|
|
846
|
-
case "loop":
|
|
847
|
-
audio.loop = !!value;
|
|
848
|
-
break;
|
|
849
|
-
case "volume":
|
|
850
|
-
audio.volume = Math.max(0, Math.min(1, Number(value) || 0));
|
|
851
|
-
break;
|
|
852
|
-
case "playbackRate":
|
|
853
|
-
audio.playbackRate = Number(value) || 1;
|
|
854
|
-
break;
|
|
824
|
+
function fsCopyFile({ srcPath, destPath, success, fail, complete }) {
|
|
825
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
826
|
+
const { onFail, onComplete } = cbs;
|
|
827
|
+
if (guardFsAvailable("fsCopyFile", cbs)) return;
|
|
828
|
+
const vSrc = resolveOrBail(srcPath, "fsCopyFile", cbs);
|
|
829
|
+
if (!vSrc) return;
|
|
830
|
+
const vDest = resolveOrBail(destPath, "fsCopyFile", cbs);
|
|
831
|
+
if (!vDest) return;
|
|
832
|
+
if (!ensureWritable(vDest, "fsCopyFile", cbs)) return;
|
|
833
|
+
if (vSrc.kind === "tmp") {
|
|
834
|
+
_tmpBytes(srcPath).then(
|
|
835
|
+
(buf) => mkdirpThenWrite(vDest.realPath, "fsCopyFile", cbs, (done) => _fs.writeFile(vDest.realPath, buf, done)),
|
|
836
|
+
tmpFailHandler("fsCopyFile", cbs)
|
|
837
|
+
);
|
|
838
|
+
return;
|
|
855
839
|
}
|
|
840
|
+
if (!vSrc.realPath) {
|
|
841
|
+
onFail?.({ errMsg: "fsCopyFile:fail invalid src path" });
|
|
842
|
+
onComplete?.();
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
_fs.copyFile(vSrc.realPath, vDest.realPath, nodeComplete("fsCopyFile", cbs));
|
|
856
846
|
}
|
|
857
|
-
function
|
|
858
|
-
const
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
function audioStop({ audioId }) {
|
|
868
|
-
const audio = _newAudioInstances.get(audioId);
|
|
869
|
-
if (!audio) return;
|
|
870
|
-
audio.pause();
|
|
871
|
-
audio.currentTime = 0;
|
|
872
|
-
_audioFire.get(audioId)?.(audioSnapshot(audio, "stop"));
|
|
873
|
-
}
|
|
874
|
-
function audioSeek({ audioId, position }) {
|
|
875
|
-
const audio = _newAudioInstances.get(audioId);
|
|
876
|
-
if (!audio) return;
|
|
877
|
-
audio.currentTime = position;
|
|
847
|
+
function fsRename({ oldPath, newPath, success, fail, complete }) {
|
|
848
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
849
|
+
if (guardFsAvailable("fsRename", cbs)) return;
|
|
850
|
+
const vOld = resolveOrBail(oldPath, "fsRename", cbs);
|
|
851
|
+
if (!vOld) return;
|
|
852
|
+
const vNew = resolveOrBail(newPath, "fsRename", cbs);
|
|
853
|
+
if (!vNew) return;
|
|
854
|
+
if (!ensureWritable(vOld, "fsRename", cbs)) return;
|
|
855
|
+
if (!ensureWritable(vNew, "fsRename", cbs)) return;
|
|
856
|
+
_fs.rename(vOld.realPath, vNew.realPath, nodeComplete("fsRename", cbs));
|
|
878
857
|
}
|
|
879
|
-
function
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
if (!
|
|
885
|
-
|
|
886
|
-
audio.removeAttribute("src");
|
|
887
|
-
audio.load();
|
|
888
|
-
_newAudioInstances.delete(audioId);
|
|
858
|
+
function fsUnlink({ filePath, success, fail, complete }) {
|
|
859
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
860
|
+
if (guardFsAvailable("fsUnlink", cbs)) return;
|
|
861
|
+
const v = resolveOrBail(filePath, "fsUnlink", cbs);
|
|
862
|
+
if (!v) return;
|
|
863
|
+
if (!ensureWritable(v, "fsUnlink", cbs)) return;
|
|
864
|
+
_fs.unlink(v.realPath, nodeComplete("fsUnlink", cbs));
|
|
889
865
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
if (env) {
|
|
898
|
-
return import_node_path.default.join(env, "files");
|
|
899
|
-
}
|
|
900
|
-
return import_node_path.default.join(import_node_os.default.homedir(), ".dimina", "files");
|
|
866
|
+
function fsMkdir({ dirPath, recursive = false, success, fail, complete }) {
|
|
867
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
868
|
+
if (guardFsAvailable("fsMkdir", cbs)) return;
|
|
869
|
+
const v = resolveOrBail(dirPath, "fsMkdir", cbs);
|
|
870
|
+
if (!v) return;
|
|
871
|
+
if (!ensureWritable(v, "fsMkdir", cbs)) return;
|
|
872
|
+
_fs.mkdir(v.realPath, { recursive }, nodeComplete("fsMkdir", cbs));
|
|
901
873
|
}
|
|
902
|
-
function
|
|
903
|
-
|
|
904
|
-
if (
|
|
905
|
-
const
|
|
906
|
-
if (
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
if (decoded.includes("\0")) return null;
|
|
914
|
-
decoded = decoded.replace(/^[/\\]+/, "");
|
|
915
|
-
if (decoded.length === 0) return null;
|
|
916
|
-
const segments = decoded.split(/[/\\]+/);
|
|
917
|
-
if (segments.some((s) => s === ".." || s === ".")) return null;
|
|
918
|
-
let kind;
|
|
919
|
-
let writable;
|
|
920
|
-
if (decoded.startsWith("_tmp/")) {
|
|
921
|
-
kind = "tmp";
|
|
922
|
-
writable = false;
|
|
923
|
-
} else if (decoded.startsWith("_store/")) {
|
|
924
|
-
kind = "store";
|
|
925
|
-
writable = false;
|
|
874
|
+
function fsRmdir({ dirPath, recursive = false, success, fail, complete }) {
|
|
875
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
876
|
+
if (guardFsAvailable("fsRmdir", cbs)) return;
|
|
877
|
+
const v = resolveOrBail(dirPath, "fsRmdir", cbs);
|
|
878
|
+
if (!v) return;
|
|
879
|
+
if (!ensureWritable(v, "fsRmdir", cbs)) return;
|
|
880
|
+
if (recursive) {
|
|
881
|
+
const rmFn = _fs.rm ?? _fs.rmdir;
|
|
882
|
+
rmFn(v.realPath, { recursive: true }, nodeComplete("fsRmdir", cbs));
|
|
926
883
|
} else {
|
|
927
|
-
|
|
928
|
-
writable = true;
|
|
929
|
-
}
|
|
930
|
-
if (kind === "tmp") {
|
|
931
|
-
return { kind, writable };
|
|
884
|
+
_fs.rmdir(v.realPath, nodeComplete("fsRmdir", cbs));
|
|
932
885
|
}
|
|
933
|
-
const base = sandboxBase();
|
|
934
|
-
const joined = import_node_path.default.join(base, decoded);
|
|
935
|
-
const normalized = import_node_path.default.normalize(joined);
|
|
936
|
-
if (normalized !== base && !normalized.startsWith(base + import_node_path.default.sep)) return null;
|
|
937
|
-
return { kind, writable, realPath: normalized };
|
|
938
886
|
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
}
|
|
950
|
-
function isArrayBuffer(value) {
|
|
951
|
-
return Object.prototype.toString.call(value) === "[object ArrayBuffer]";
|
|
952
|
-
}
|
|
953
|
-
async function blobToBuffer(blob) {
|
|
954
|
-
const readable = blob;
|
|
955
|
-
if (typeof readable.arrayBuffer === "function") {
|
|
956
|
-
const ab = await readable.arrayBuffer();
|
|
957
|
-
return Buffer.from(new Uint8Array(ab));
|
|
887
|
+
function fsReaddir({ dirPath, success, fail, complete }) {
|
|
888
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
889
|
+
const { onFail, onComplete } = cbs;
|
|
890
|
+
if (guardFsAvailable("fsReaddir", cbs)) return;
|
|
891
|
+
const v = resolveOrBail(dirPath, "fsReaddir", cbs);
|
|
892
|
+
if (!v) return;
|
|
893
|
+
if (v.kind === "tmp" || v.kind === "store") {
|
|
894
|
+
onFail?.({ errMsg: `fsReaddir:fail ${v.kind} is a flat namespace (no dir tree)` });
|
|
895
|
+
onComplete?.();
|
|
896
|
+
return;
|
|
958
897
|
}
|
|
959
|
-
if (
|
|
960
|
-
|
|
898
|
+
if (!v.realPath) {
|
|
899
|
+
onFail?.({ errMsg: "fsReaddir:fail invalid path" });
|
|
900
|
+
onComplete?.();
|
|
901
|
+
return;
|
|
961
902
|
}
|
|
962
|
-
|
|
963
|
-
const reader = new FileReader();
|
|
964
|
-
reader.onerror = () => reject(reader.error ?? new Error("Blob read failed"));
|
|
965
|
-
reader.onload = () => {
|
|
966
|
-
const result = reader.result;
|
|
967
|
-
if (!isArrayBuffer(result)) {
|
|
968
|
-
reject(new Error("Blob read did not return an ArrayBuffer"));
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
resolve(Buffer.from(new Uint8Array(result)));
|
|
972
|
-
};
|
|
973
|
-
reader.readAsArrayBuffer(blob);
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
var _fs = typeof require !== "undefined" ? require("fs") : null;
|
|
977
|
-
var _path = typeof require !== "undefined" ? require("path") : null;
|
|
978
|
-
var _crypto = typeof require !== "undefined" ? require("crypto") : null;
|
|
979
|
-
function guardFsAvailable(apiName, cbs) {
|
|
980
|
-
if (_fs) return false;
|
|
981
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail not available in browser context` });
|
|
982
|
-
cbs.onComplete?.();
|
|
983
|
-
return true;
|
|
984
|
-
}
|
|
985
|
-
function resolveOrBail(p, apiName, cbs) {
|
|
986
|
-
const v = resolveVPath(p);
|
|
987
|
-
if (v) return v;
|
|
988
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail invalid or unsafe path` });
|
|
989
|
-
cbs.onComplete?.();
|
|
990
|
-
return void 0;
|
|
991
|
-
}
|
|
992
|
-
function ensureWritable(v, apiName, cbs) {
|
|
993
|
-
if (v.writable && v.realPath) return true;
|
|
994
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail permission denied` });
|
|
995
|
-
cbs.onComplete?.();
|
|
996
|
-
return false;
|
|
997
|
-
}
|
|
998
|
-
function tmpFailHandler(apiName, cbs) {
|
|
999
|
-
return (err) => {
|
|
1000
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` });
|
|
1001
|
-
cbs.onComplete?.();
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
function nodeComplete(apiName, cbs, buildOk) {
|
|
1005
|
-
return (err, ...args) => {
|
|
1006
|
-
if (err) {
|
|
1007
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail ${err.message}` });
|
|
1008
|
-
} else {
|
|
1009
|
-
cbs.onSuccess?.({ ...buildOk ? buildOk(...args) : void 0, errMsg: `${apiName}:ok` });
|
|
1010
|
-
}
|
|
1011
|
-
cbs.onComplete?.();
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
function mkdirpThenWrite(destReal, apiName, cbs, writeFn, okExtra) {
|
|
1015
|
-
_fs.mkdir(_path.dirname(destReal), { recursive: true }, (mkdirErr) => {
|
|
1016
|
-
if (mkdirErr) {
|
|
1017
|
-
cbs.onFail?.({ errMsg: `${apiName}:fail ${mkdirErr.message}` });
|
|
1018
|
-
cbs.onComplete?.();
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
writeFn(nodeComplete(apiName, cbs, () => okExtra));
|
|
1022
|
-
});
|
|
903
|
+
_fs.readdir(v.realPath, nodeComplete("fsReaddir", cbs, (files) => ({ files })));
|
|
1023
904
|
}
|
|
1024
|
-
function
|
|
905
|
+
function fsGetFileInfo({ filePath, digestAlgorithm, success, fail, complete }) {
|
|
1025
906
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1026
907
|
const { onSuccess, onFail, onComplete } = cbs;
|
|
1027
|
-
if (guardFsAvailable("
|
|
1028
|
-
const v = resolveOrBail(
|
|
908
|
+
if (guardFsAvailable("fsGetFileInfo", cbs)) return;
|
|
909
|
+
const v = resolveOrBail(filePath, "fsGetFileInfo", cbs);
|
|
1029
910
|
if (!v) return;
|
|
911
|
+
const algorithm = digestAlgorithm === "sha1" ? "sha1" : "md5";
|
|
1030
912
|
if (v.kind === "tmp") {
|
|
1031
|
-
_tmpBytes(
|
|
1032
|
-
() => {
|
|
1033
|
-
|
|
1034
|
-
|
|
913
|
+
_tmpBytes(filePath).then(
|
|
914
|
+
(buf) => {
|
|
915
|
+
try {
|
|
916
|
+
const hash = _crypto.createHash(algorithm);
|
|
917
|
+
hash.update(buf);
|
|
918
|
+
onSuccess?.({ size: buf.length, digest: hash.digest("hex"), errMsg: "fsGetFileInfo:ok" });
|
|
919
|
+
onComplete?.();
|
|
920
|
+
} catch (hashErr) {
|
|
921
|
+
onFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` });
|
|
922
|
+
onComplete?.();
|
|
923
|
+
}
|
|
1035
924
|
},
|
|
1036
|
-
tmpFailHandler("
|
|
925
|
+
tmpFailHandler("fsGetFileInfo", cbs)
|
|
1037
926
|
);
|
|
1038
927
|
return;
|
|
1039
928
|
}
|
|
1040
929
|
if (!v.realPath) {
|
|
1041
|
-
onFail?.({ errMsg: "
|
|
930
|
+
onFail?.({ errMsg: "fsGetFileInfo:fail invalid path" });
|
|
1042
931
|
onComplete?.();
|
|
1043
932
|
return;
|
|
1044
933
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
(
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
size: buf.length,
|
|
1059
|
-
mode: 0,
|
|
1060
|
-
lastAccessedTime: 0,
|
|
1061
|
-
lastModifiedTime: 0,
|
|
1062
|
-
isFile: true,
|
|
1063
|
-
isDirectory: false
|
|
1064
|
-
},
|
|
1065
|
-
errMsg: "fsStat:ok"
|
|
1066
|
-
});
|
|
934
|
+
const resolved = v.realPath;
|
|
935
|
+
_fs.stat(resolved, (err, s) => {
|
|
936
|
+
if (err) {
|
|
937
|
+
onFail?.({ errMsg: `fsGetFileInfo:fail ${err.message}` });
|
|
938
|
+
onComplete?.();
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
try {
|
|
942
|
+
const hash = _crypto.createHash(algorithm);
|
|
943
|
+
const stream = _fs.createReadStream(resolved);
|
|
944
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
945
|
+
stream.on("end", () => {
|
|
946
|
+
onSuccess?.({ size: s.size, digest: hash.digest("hex"), errMsg: "fsGetFileInfo:ok" });
|
|
1067
947
|
onComplete?.();
|
|
1068
|
-
}
|
|
1069
|
-
|
|
948
|
+
});
|
|
949
|
+
stream.on("error", (hashErr) => {
|
|
950
|
+
onFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` });
|
|
951
|
+
onComplete?.();
|
|
952
|
+
});
|
|
953
|
+
} catch (hashErr) {
|
|
954
|
+
onFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` });
|
|
955
|
+
onComplete?.();
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
function resolveSaveFileDestination(tempFilePath, filePath, cbs) {
|
|
960
|
+
if (typeof filePath === "string" && filePath) {
|
|
961
|
+
const dest = resolveOrBail(filePath, "fsSaveFile", cbs);
|
|
962
|
+
if (!dest) return void 0;
|
|
963
|
+
if (!ensureWritable(dest, "fsSaveFile", cbs)) return void 0;
|
|
964
|
+
return { savedFilePath: filePath, destReal: dest.realPath };
|
|
965
|
+
}
|
|
966
|
+
const ext = _path.extname(tempFilePath) || "";
|
|
967
|
+
const id = _crypto.randomUUID() + ext;
|
|
968
|
+
const savedFilePath = `difile://_store/${id}`;
|
|
969
|
+
const destResolved = resolveVPath(savedFilePath);
|
|
970
|
+
if (!destResolved || !destResolved.realPath) {
|
|
971
|
+
cbs.onFail?.({ errMsg: "fsSaveFile:fail unable to allocate destination" });
|
|
972
|
+
cbs.onComplete?.();
|
|
973
|
+
return void 0;
|
|
974
|
+
}
|
|
975
|
+
if (containsSymlink(destResolved.realPath)) {
|
|
976
|
+
cbs.onFail?.({ errMsg: "fsSaveFile:fail invalid or unsafe path" });
|
|
977
|
+
cbs.onComplete?.();
|
|
978
|
+
return void 0;
|
|
979
|
+
}
|
|
980
|
+
return { savedFilePath, destReal: destResolved.realPath };
|
|
981
|
+
}
|
|
982
|
+
function fsSaveFile({ tempFilePath, filePath, success, fail, complete }) {
|
|
983
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
984
|
+
const { onFail, onComplete } = cbs;
|
|
985
|
+
if (guardFsAvailable("fsSaveFile", cbs)) return;
|
|
986
|
+
const src = resolveOrBail(tempFilePath, "fsSaveFile", cbs);
|
|
987
|
+
if (!src) return;
|
|
988
|
+
const destination = resolveSaveFileDestination(tempFilePath, filePath, cbs);
|
|
989
|
+
if (!destination) return;
|
|
990
|
+
const { savedFilePath, destReal } = destination;
|
|
991
|
+
if (src.kind === "tmp") {
|
|
992
|
+
_tmpBytes(tempFilePath).then(
|
|
993
|
+
(bytes) => mkdirpThenWrite(destReal, "fsSaveFile", cbs, (done) => _fs.writeFile(destReal, bytes, done), { savedFilePath }),
|
|
994
|
+
tmpFailHandler("fsSaveFile", cbs)
|
|
1070
995
|
);
|
|
1071
996
|
return;
|
|
1072
997
|
}
|
|
1073
|
-
if (!
|
|
1074
|
-
onFail?.({ errMsg: "
|
|
998
|
+
if (!src.realPath) {
|
|
999
|
+
onFail?.({ errMsg: "fsSaveFile:fail invalid src path" });
|
|
1075
1000
|
onComplete?.();
|
|
1076
1001
|
return;
|
|
1077
1002
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
cb(null);
|
|
1090
|
-
return;
|
|
1091
|
-
}
|
|
1092
|
-
for (const entry of entries) {
|
|
1093
|
-
const full = _path.join(dir, entry.name);
|
|
1094
|
-
_fs.stat(full, (statErr, s) => {
|
|
1095
|
-
if (!statErr) {
|
|
1096
|
-
statsMap[full] = {
|
|
1097
|
-
size: s.size,
|
|
1098
|
-
mode: s.mode,
|
|
1099
|
-
lastAccessedTime: s.atimeMs,
|
|
1100
|
-
lastModifiedTime: s.mtimeMs,
|
|
1101
|
-
isFile: s.isFile(),
|
|
1102
|
-
isDirectory: s.isDirectory()
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
if (entry.isDirectory()) {
|
|
1106
|
-
walkDir(full, () => {
|
|
1107
|
-
if (--pending === 0) cb(null);
|
|
1108
|
-
});
|
|
1109
|
-
} else {
|
|
1110
|
-
if (--pending === 0) cb(null);
|
|
1111
|
-
}
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
});
|
|
1115
|
-
};
|
|
1116
|
-
walkDir(resolved, (err) => {
|
|
1117
|
-
if (err) {
|
|
1118
|
-
onFail?.({ errMsg: `fsStat:fail ${err.message}` });
|
|
1119
|
-
} else {
|
|
1120
|
-
onSuccess?.({ stats: statsMap, errMsg: "fsStat:ok" });
|
|
1003
|
+
if (src.realPath === destReal) {
|
|
1004
|
+
_fs.stat(src.realPath, (statErr, st) => {
|
|
1005
|
+
if (statErr) {
|
|
1006
|
+
onFail?.({ errMsg: `fsSaveFile:fail ${statErr.message}` });
|
|
1007
|
+
onComplete?.();
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
if (!st.isFile()) {
|
|
1011
|
+
onFail?.({ errMsg: `fsSaveFile:fail illegal operation on a directory, copyfile '${tempFilePath}'` });
|
|
1012
|
+
onComplete?.();
|
|
1013
|
+
return;
|
|
1121
1014
|
}
|
|
1015
|
+
cbs.onSuccess?.({ savedFilePath, errMsg: "fsSaveFile:ok" });
|
|
1122
1016
|
onComplete?.();
|
|
1123
1017
|
});
|
|
1124
|
-
|
|
1125
|
-
_fs.stat(resolved, nodeComplete("fsStat", cbs, (s) => ({
|
|
1126
|
-
stats: {
|
|
1127
|
-
size: s.size,
|
|
1128
|
-
mode: s.mode,
|
|
1129
|
-
lastAccessedTime: s.atimeMs,
|
|
1130
|
-
lastModifiedTime: s.mtimeMs,
|
|
1131
|
-
isFile: s.isFile(),
|
|
1132
|
-
isDirectory: s.isDirectory()
|
|
1133
|
-
}
|
|
1134
|
-
})));
|
|
1018
|
+
return;
|
|
1135
1019
|
}
|
|
1020
|
+
mkdirpThenWrite(destReal, "fsSaveFile", cbs, (done) => _fs.copyFile(src.realPath, destReal, done), { savedFilePath });
|
|
1136
1021
|
}
|
|
1137
|
-
function
|
|
1022
|
+
function fsGetSavedFileList({ success, fail, complete } = {}) {
|
|
1138
1023
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1139
1024
|
const { onSuccess, onFail, onComplete } = cbs;
|
|
1140
|
-
if (guardFsAvailable("
|
|
1141
|
-
const
|
|
1142
|
-
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1145
|
-
(buf) => {
|
|
1146
|
-
const data = encoding ? buf.toString(encoding) : buf;
|
|
1147
|
-
onSuccess?.({ data, errMsg: "fsReadFile:ok" });
|
|
1148
|
-
onComplete?.();
|
|
1149
|
-
},
|
|
1150
|
-
tmpFailHandler("fsReadFile", cbs)
|
|
1151
|
-
);
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
if (!v.realPath) {
|
|
1155
|
-
onFail?.({ errMsg: "fsReadFile:fail invalid path" });
|
|
1025
|
+
if (guardFsAvailable("fsGetSavedFileList", cbs)) return;
|
|
1026
|
+
const storeVpath = resolveVPath("difile://_store/");
|
|
1027
|
+
const storeDir = storeVpath?.realPath;
|
|
1028
|
+
if (!storeDir) {
|
|
1029
|
+
onSuccess?.({ fileList: [], errMsg: "fsGetSavedFileList:ok" });
|
|
1156
1030
|
onComplete?.();
|
|
1157
1031
|
return;
|
|
1158
1032
|
}
|
|
1159
|
-
_fs.
|
|
1033
|
+
_fs.readdir(storeDir, (err, files) => {
|
|
1034
|
+
if (err) {
|
|
1035
|
+
if (err.code === "ENOENT") {
|
|
1036
|
+
onSuccess?.({ fileList: [], errMsg: "fsGetSavedFileList:ok" });
|
|
1037
|
+
} else {
|
|
1038
|
+
onFail?.({ errMsg: `fsGetSavedFileList:fail ${err.message}` });
|
|
1039
|
+
}
|
|
1040
|
+
onComplete?.();
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
let pending = files.length;
|
|
1044
|
+
if (pending === 0) {
|
|
1045
|
+
onSuccess?.({ fileList: [], errMsg: "fsGetSavedFileList:ok" });
|
|
1046
|
+
onComplete?.();
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const fileList = [];
|
|
1050
|
+
let settled = false;
|
|
1051
|
+
for (const name of files) {
|
|
1052
|
+
const full = _path.join(storeDir, name);
|
|
1053
|
+
_fs.stat(full, (statErr, s) => {
|
|
1054
|
+
if (settled) return;
|
|
1055
|
+
if (statErr) {
|
|
1056
|
+
settled = true;
|
|
1057
|
+
onFail?.({ errMsg: `fsGetSavedFileList:fail ${statErr.message}` });
|
|
1058
|
+
onComplete?.();
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
fileList.push({
|
|
1062
|
+
filePath: `difile://_store/${name}`,
|
|
1063
|
+
size: s.size,
|
|
1064
|
+
// wx createTime is a seconds-unit epoch.
|
|
1065
|
+
createTime: Math.floor(s.birthtimeMs / 1e3)
|
|
1066
|
+
});
|
|
1067
|
+
if (--pending === 0) {
|
|
1068
|
+
settled = true;
|
|
1069
|
+
onSuccess?.({ fileList, errMsg: "fsGetSavedFileList:ok" });
|
|
1070
|
+
onComplete?.();
|
|
1071
|
+
}
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1160
1075
|
}
|
|
1161
|
-
function
|
|
1076
|
+
function fsRemoveSavedFile({ filePath, success, fail, complete }) {
|
|
1162
1077
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1163
|
-
|
|
1164
|
-
|
|
1078
|
+
const { onFail, onComplete } = cbs;
|
|
1079
|
+
if (guardFsAvailable("fsRemoveSavedFile", cbs)) return;
|
|
1080
|
+
const v = resolveOrBail(filePath, "fsRemoveSavedFile", cbs);
|
|
1165
1081
|
if (!v) return;
|
|
1166
|
-
if (
|
|
1167
|
-
|
|
1082
|
+
if (v.kind !== "store" || !v.realPath) {
|
|
1083
|
+
onFail?.({ errMsg: "fsRemoveSavedFile:fail only _store/ entries may be removed" });
|
|
1084
|
+
onComplete?.();
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
_fs.unlink(v.realPath, nodeComplete("fsRemoveSavedFile", cbs));
|
|
1168
1088
|
}
|
|
1169
|
-
function
|
|
1089
|
+
function fsTruncate({ filePath, length = 0, success, fail, complete }) {
|
|
1170
1090
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1171
|
-
if (guardFsAvailable("
|
|
1172
|
-
const v = resolveOrBail(filePath, "
|
|
1091
|
+
if (guardFsAvailable("fsTruncate", cbs)) return;
|
|
1092
|
+
const v = resolveOrBail(filePath, "fsTruncate", cbs);
|
|
1173
1093
|
if (!v) return;
|
|
1174
|
-
if (!ensureWritable(v, "
|
|
1175
|
-
_fs.
|
|
1094
|
+
if (!ensureWritable(v, "fsTruncate", cbs)) return;
|
|
1095
|
+
_fs.truncate(v.realPath, length, nodeComplete("fsTruncate", cbs));
|
|
1176
1096
|
}
|
|
1177
|
-
function
|
|
1097
|
+
function readVPathBytesSync(p) {
|
|
1098
|
+
if (!_fs) return null;
|
|
1099
|
+
const v = resolveVPath(p);
|
|
1100
|
+
if (!v?.realPath) return null;
|
|
1101
|
+
if (containsSymlink(v.realPath)) return null;
|
|
1102
|
+
try {
|
|
1103
|
+
return _fs.readFileSync(v.realPath);
|
|
1104
|
+
} catch {
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// src/simulator/simulator-api-media.ts
|
|
1110
|
+
function captureAttrFor(sourceType, camera) {
|
|
1111
|
+
if (sourceType.length !== 1 || sourceType[0] !== "camera") return void 0;
|
|
1112
|
+
return camera === "front" ? "user" : "environment";
|
|
1113
|
+
}
|
|
1114
|
+
function createFilePicker(apiName, config, cbs, onPick) {
|
|
1115
|
+
const input = document.createElement("input");
|
|
1116
|
+
input.type = "file";
|
|
1117
|
+
input.accept = config.accept;
|
|
1118
|
+
input.multiple = config.multiple;
|
|
1119
|
+
if (config.capture) input.setAttribute("capture", config.capture);
|
|
1120
|
+
input.style.display = "none";
|
|
1121
|
+
document.body.appendChild(input);
|
|
1122
|
+
const removeInput = () => input.remove();
|
|
1123
|
+
input.addEventListener("change", () => {
|
|
1124
|
+
const files = Array.from(input.files || []);
|
|
1125
|
+
if (files.length === 0) {
|
|
1126
|
+
cbs.onFail?.({ errMsg: `${apiName}:fail cancel` });
|
|
1127
|
+
cbs.onComplete?.();
|
|
1128
|
+
removeInput();
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
void onPick(files, removeInput);
|
|
1132
|
+
});
|
|
1133
|
+
input.click();
|
|
1134
|
+
}
|
|
1135
|
+
function chooseImage({ count = 9, sourceType, camera, success, fail, complete }) {
|
|
1178
1136
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1179
|
-
const {
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1137
|
+
const { onSuccess, onComplete } = cbs;
|
|
1138
|
+
const normalizedCount = normalizeChooseMediaCount(count);
|
|
1139
|
+
const normalizedSourceType = normalizeStringArray(sourceType, ["album", "camera"]);
|
|
1140
|
+
createFilePicker(
|
|
1141
|
+
"chooseImage",
|
|
1142
|
+
{ accept: "image/*", multiple: normalizedCount > 1, capture: captureAttrFor(normalizedSourceType, camera) },
|
|
1143
|
+
cbs,
|
|
1144
|
+
(rawFiles, removeInput) => {
|
|
1145
|
+
const files = rawFiles.slice(0, normalizedCount);
|
|
1146
|
+
const tempFilePaths = files.map((f) => createTempFilePath(f));
|
|
1147
|
+
const tempFiles2 = files.map((f, i) => ({ path: tempFilePaths[i], size: f.size }));
|
|
1148
|
+
onSuccess?.({ tempFilePaths, tempFiles: tempFiles2, errMsg: "chooseImage:ok" });
|
|
1149
|
+
onComplete?.();
|
|
1150
|
+
removeInput();
|
|
1151
|
+
}
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
function previewImage({ urls, current, success, complete }) {
|
|
1155
|
+
const { onSuccess, onComplete } = bindCallbacks(this, { success, complete });
|
|
1156
|
+
if (!urls || urls.length === 0) {
|
|
1157
|
+
onComplete?.();
|
|
1158
|
+
return;
|
|
1159
|
+
}
|
|
1160
|
+
const overlay = document.createElement("div");
|
|
1161
|
+
overlay.style.cssText = "position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.85);display:flex;align-items:center;justify-content:center;cursor:pointer;";
|
|
1162
|
+
const img = document.createElement("img");
|
|
1163
|
+
img.src = current || urls[0] || "";
|
|
1164
|
+
img.style.cssText = "max-width:90%;max-height:90%;object-fit:contain;";
|
|
1165
|
+
overlay.appendChild(img);
|
|
1166
|
+
overlay.addEventListener("click", () => overlay.remove());
|
|
1167
|
+
document.body.appendChild(overlay);
|
|
1168
|
+
onSuccess?.({ errMsg: "previewImage:ok" });
|
|
1169
|
+
onComplete?.();
|
|
1170
|
+
}
|
|
1171
|
+
function compressImage({ src, quality = 80, success, fail, complete }) {
|
|
1172
|
+
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
1173
|
+
const img = new Image();
|
|
1174
|
+
img.crossOrigin = "anonymous";
|
|
1175
|
+
img.onload = () => {
|
|
1176
|
+
try {
|
|
1177
|
+
const canvas = document.createElement("canvas");
|
|
1178
|
+
canvas.width = img.naturalWidth;
|
|
1179
|
+
canvas.height = img.naturalHeight;
|
|
1180
|
+
const ctx = canvas.getContext("2d");
|
|
1181
|
+
ctx.drawImage(img, 0, 0);
|
|
1182
|
+
canvas.toBlob(
|
|
1183
|
+
(blob) => {
|
|
1184
|
+
if (blob) {
|
|
1185
|
+
const tempFilePath = createTempFilePath(blob);
|
|
1186
|
+
onSuccess?.({ tempFilePath, errMsg: "compressImage:ok" });
|
|
1187
|
+
} else {
|
|
1188
|
+
onFail?.({ errMsg: "compressImage:fail compression error" });
|
|
1189
|
+
}
|
|
1190
|
+
onComplete?.();
|
|
1191
|
+
},
|
|
1192
|
+
"image/jpeg",
|
|
1193
|
+
quality / 100
|
|
1194
|
+
);
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
onFail?.({ errMsg: `compressImage:fail ${error.message}` });
|
|
1197
|
+
onComplete?.();
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
img.onerror = () => {
|
|
1201
|
+
onFail?.({ errMsg: "compressImage:fail image load error" });
|
|
1202
|
+
onComplete?.();
|
|
1203
|
+
};
|
|
1204
|
+
img.src = src;
|
|
1205
|
+
}
|
|
1206
|
+
function saveImageToPhotosAlbum({ filePath, success, fail, complete }) {
|
|
1207
|
+
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
1208
|
+
const triggerDownload = (href) => {
|
|
1209
|
+
const a = document.createElement("a");
|
|
1210
|
+
a.href = href;
|
|
1211
|
+
a.download = "image";
|
|
1212
|
+
a.click();
|
|
1213
|
+
};
|
|
1214
|
+
if (typeof filePath === "string" && filePath.startsWith("difile://_tmp/")) {
|
|
1215
|
+
resolveTempFilePath(filePath).then(
|
|
1216
|
+
(blob) => new Promise((resolve, reject) => {
|
|
1217
|
+
const reader = new FileReader();
|
|
1218
|
+
reader.onerror = () => reject(reader.error ?? new Error("blob read failed"));
|
|
1219
|
+
reader.onload = () => resolve(String(reader.result));
|
|
1220
|
+
reader.readAsDataURL(blob);
|
|
1221
|
+
})
|
|
1222
|
+
).then(
|
|
1223
|
+
(href) => {
|
|
1224
|
+
triggerDownload(href);
|
|
1225
|
+
onSuccess?.({ errMsg: "saveImageToPhotosAlbum:ok" });
|
|
1226
|
+
onComplete?.();
|
|
1227
|
+
},
|
|
1228
|
+
(err) => {
|
|
1229
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1230
|
+
onFail?.({ errMsg: `saveImageToPhotosAlbum:fail ${msg}` });
|
|
1231
|
+
onComplete?.();
|
|
1232
|
+
}
|
|
1190
1233
|
);
|
|
1191
1234
|
return;
|
|
1192
1235
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1236
|
+
try {
|
|
1237
|
+
if (typeof filePath === "string" && filePath.startsWith("difile://")) {
|
|
1238
|
+
const bytes = readVPathBytesSync(filePath);
|
|
1239
|
+
if (bytes) {
|
|
1240
|
+
triggerDownload(`data:application/octet-stream;base64,${bytes.toString("base64")}`);
|
|
1241
|
+
onSuccess?.({ errMsg: "saveImageToPhotosAlbum:ok" });
|
|
1242
|
+
} else {
|
|
1243
|
+
onFail?.({ errMsg: "saveImageToPhotosAlbum:fail no such file" });
|
|
1244
|
+
}
|
|
1245
|
+
} else {
|
|
1246
|
+
onFail?.({ errMsg: "saveImageToPhotosAlbum:fail invalid file path" });
|
|
1247
|
+
}
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
onFail?.({ errMsg: `saveImageToPhotosAlbum:fail ${error.message}` });
|
|
1197
1250
|
}
|
|
1198
|
-
|
|
1251
|
+
onComplete?.();
|
|
1199
1252
|
}
|
|
1200
|
-
function
|
|
1201
|
-
const
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1253
|
+
function getImageInfo({ src, success, fail, complete }) {
|
|
1254
|
+
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
1255
|
+
const img = new Image();
|
|
1256
|
+
img.crossOrigin = "anonymous";
|
|
1257
|
+
img.onload = () => {
|
|
1258
|
+
onSuccess?.({
|
|
1259
|
+
width: img.naturalWidth,
|
|
1260
|
+
height: img.naturalHeight,
|
|
1261
|
+
path: src,
|
|
1262
|
+
orientation: "up",
|
|
1263
|
+
type: "unknown",
|
|
1264
|
+
errMsg: "getImageInfo:ok"
|
|
1265
|
+
});
|
|
1266
|
+
onComplete?.();
|
|
1267
|
+
};
|
|
1268
|
+
img.onerror = () => {
|
|
1269
|
+
onFail?.({ errMsg: "getImageInfo:fail image load error" });
|
|
1270
|
+
onComplete?.();
|
|
1271
|
+
};
|
|
1272
|
+
img.src = src;
|
|
1210
1273
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
if (!v) return;
|
|
1216
|
-
if (!ensureWritable(v, "fsUnlink", cbs)) return;
|
|
1217
|
-
_fs.unlink(v.realPath, nodeComplete("fsUnlink", cbs));
|
|
1274
|
+
var VIDEO_METADATA_TIMEOUT_MS = 5e3;
|
|
1275
|
+
var VIDEO_THUMBNAIL_TIMEOUT_MS = 500;
|
|
1276
|
+
function normalizeStringArray(value, fallback) {
|
|
1277
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : fallback;
|
|
1218
1278
|
}
|
|
1219
|
-
function
|
|
1220
|
-
const
|
|
1221
|
-
if (
|
|
1222
|
-
|
|
1223
|
-
if (!v) return;
|
|
1224
|
-
if (!ensureWritable(v, "fsMkdir", cbs)) return;
|
|
1225
|
-
_fs.mkdir(v.realPath, { recursive }, nodeComplete("fsMkdir", cbs));
|
|
1279
|
+
function normalizeChooseMediaCount(value) {
|
|
1280
|
+
const n = Number(value);
|
|
1281
|
+
if (!Number.isFinite(n)) return 9;
|
|
1282
|
+
return Math.max(1, Math.min(20, Math.floor(n)));
|
|
1226
1283
|
}
|
|
1227
|
-
function
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
const
|
|
1231
|
-
if (
|
|
1232
|
-
|
|
1233
|
-
const rmFn = _fs.rm ?? _fs.rmdir;
|
|
1234
|
-
rmFn(v.realPath, { recursive }, nodeComplete("fsRmdir", cbs));
|
|
1284
|
+
function getChooseMediaAccept(mediaType) {
|
|
1285
|
+
const wantsMix = mediaType.includes("mix");
|
|
1286
|
+
const wantsImage = wantsMix || mediaType.includes("image");
|
|
1287
|
+
const wantsVideo = wantsMix || mediaType.includes("video");
|
|
1288
|
+
if (wantsImage && wantsVideo) return "image/*,video/*";
|
|
1289
|
+
return wantsVideo ? "video/*" : "image/*";
|
|
1235
1290
|
}
|
|
1236
|
-
function
|
|
1237
|
-
const
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
const v = resolveOrBail(dirPath, "fsReaddir", cbs);
|
|
1241
|
-
if (!v) return;
|
|
1242
|
-
if (v.kind === "tmp" || v.kind === "store") {
|
|
1243
|
-
onFail?.({ errMsg: `fsReaddir:fail ${v.kind} is a flat namespace (no dir tree)` });
|
|
1244
|
-
onComplete?.();
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
if (!v.realPath) {
|
|
1248
|
-
onFail?.({ errMsg: "fsReaddir:fail invalid path" });
|
|
1249
|
-
onComplete?.();
|
|
1250
|
-
return;
|
|
1251
|
-
}
|
|
1252
|
-
_fs.readdir(v.realPath, nodeComplete("fsReaddir", cbs, (files) => ({ files })));
|
|
1291
|
+
function getChooseMediaResultType(files) {
|
|
1292
|
+
const types = new Set(files.map((file) => file.fileType));
|
|
1293
|
+
if (types.size > 1) return "mix";
|
|
1294
|
+
return files[0]?.fileType ?? "image";
|
|
1253
1295
|
}
|
|
1254
|
-
function
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1296
|
+
function readImageMetadata(src) {
|
|
1297
|
+
return new Promise((resolve) => {
|
|
1298
|
+
const img = new Image();
|
|
1299
|
+
img.onload = () => resolve({ width: img.naturalWidth || 0, height: img.naturalHeight || 0 });
|
|
1300
|
+
img.onerror = () => resolve({ width: 0, height: 0 });
|
|
1301
|
+
img.src = src;
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
function readVideoMetadata(src) {
|
|
1305
|
+
return new Promise((resolve) => {
|
|
1306
|
+
const video = document.createElement("video");
|
|
1307
|
+
let settled = false;
|
|
1308
|
+
let seekTimer;
|
|
1309
|
+
const finish = (metadata) => {
|
|
1310
|
+
if (settled) return;
|
|
1311
|
+
settled = true;
|
|
1312
|
+
clearTimeout(timer);
|
|
1313
|
+
if (seekTimer) clearTimeout(seekTimer);
|
|
1314
|
+
video.onloadedmetadata = null;
|
|
1315
|
+
video.onseeked = null;
|
|
1316
|
+
video.onerror = null;
|
|
1317
|
+
video.removeAttribute("src");
|
|
1318
|
+
video.load();
|
|
1319
|
+
resolve(metadata);
|
|
1320
|
+
};
|
|
1321
|
+
const drawThumbnail = (width, height) => {
|
|
1322
|
+
return new Promise((resolveThumb) => {
|
|
1323
|
+
let resolved = false;
|
|
1324
|
+
const done = (value) => {
|
|
1325
|
+
if (resolved) return;
|
|
1326
|
+
resolved = true;
|
|
1327
|
+
resolveThumb(value);
|
|
1328
|
+
};
|
|
1329
|
+
let canvas;
|
|
1330
|
+
try {
|
|
1331
|
+
canvas = document.createElement("canvas");
|
|
1332
|
+
canvas.width = width || 1;
|
|
1333
|
+
canvas.height = height || 1;
|
|
1334
|
+
const ctx = canvas.getContext("2d");
|
|
1335
|
+
ctx?.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
1336
|
+
} catch {
|
|
1337
|
+
done("");
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
const toDataUrlFallback = () => {
|
|
1265
1341
|
try {
|
|
1266
|
-
|
|
1267
|
-
hash.update(buf);
|
|
1268
|
-
result.digest = hash.digest("hex");
|
|
1342
|
+
done(canvas.toDataURL("image/jpeg", 0.8));
|
|
1269
1343
|
} catch {
|
|
1344
|
+
done("");
|
|
1270
1345
|
}
|
|
1346
|
+
};
|
|
1347
|
+
const fallbackTimer = setTimeout(toDataUrlFallback, VIDEO_THUMBNAIL_TIMEOUT_MS);
|
|
1348
|
+
try {
|
|
1349
|
+
canvas.toBlob(
|
|
1350
|
+
(blob) => {
|
|
1351
|
+
clearTimeout(fallbackTimer);
|
|
1352
|
+
if (settled || resolved) return;
|
|
1353
|
+
if (blob) {
|
|
1354
|
+
done(createTempFilePath(blob));
|
|
1355
|
+
} else {
|
|
1356
|
+
toDataUrlFallback();
|
|
1357
|
+
}
|
|
1358
|
+
},
|
|
1359
|
+
"image/jpeg",
|
|
1360
|
+
0.8
|
|
1361
|
+
);
|
|
1362
|
+
} catch {
|
|
1363
|
+
clearTimeout(fallbackTimer);
|
|
1364
|
+
toDataUrlFallback();
|
|
1271
1365
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1366
|
+
});
|
|
1367
|
+
};
|
|
1368
|
+
const timer = setTimeout(() => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: "" }), VIDEO_METADATA_TIMEOUT_MS);
|
|
1369
|
+
video.preload = "metadata";
|
|
1370
|
+
video.muted = true;
|
|
1371
|
+
video.onloadedmetadata = () => {
|
|
1372
|
+
const width = video.videoWidth || 0;
|
|
1373
|
+
const height = video.videoHeight || 0;
|
|
1374
|
+
const duration = Number.isFinite(video.duration) ? video.duration : 0;
|
|
1375
|
+
const metadata = { width, height, duration, thumbTempFilePath: "" };
|
|
1376
|
+
if (!width || !height || duration <= 0) {
|
|
1377
|
+
finish(metadata);
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
video.onseeked = async () => {
|
|
1381
|
+
if (seekTimer) {
|
|
1382
|
+
clearTimeout(seekTimer);
|
|
1383
|
+
seekTimer = void 0;
|
|
1384
|
+
}
|
|
1385
|
+
const thumbTempFilePath = await drawThumbnail(width, height);
|
|
1386
|
+
finish({ ...metadata, thumbTempFilePath });
|
|
1387
|
+
};
|
|
1388
|
+
seekTimer = setTimeout(() => finish(metadata), VIDEO_THUMBNAIL_TIMEOUT_MS);
|
|
1389
|
+
try {
|
|
1390
|
+
video.currentTime = Math.min(0.1, Math.max(0, duration - 0.01));
|
|
1391
|
+
} catch {
|
|
1392
|
+
finish(metadata);
|
|
1393
|
+
}
|
|
1394
|
+
};
|
|
1395
|
+
video.onerror = () => finish({ width: 0, height: 0, duration: 0, thumbTempFilePath: "" });
|
|
1396
|
+
video.src = src;
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
async function buildChooseMediaTempFile(file) {
|
|
1400
|
+
const tempFilePath = createTempFilePath(file);
|
|
1401
|
+
const fileType = file.type.startsWith("video") ? "video" : "image";
|
|
1402
|
+
if (fileType === "video") {
|
|
1403
|
+
const metadata2 = await readVideoMetadata(tempFilePath);
|
|
1404
|
+
return {
|
|
1405
|
+
tempFilePath,
|
|
1406
|
+
size: file.size,
|
|
1407
|
+
duration: metadata2.duration,
|
|
1408
|
+
height: metadata2.height,
|
|
1409
|
+
width: metadata2.width,
|
|
1410
|
+
thumbTempFilePath: metadata2.thumbTempFilePath,
|
|
1411
|
+
fileType,
|
|
1412
|
+
originalFileObj: file
|
|
1413
|
+
};
|
|
1283
1414
|
}
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1415
|
+
const metadata = await readImageMetadata(tempFilePath);
|
|
1416
|
+
return {
|
|
1417
|
+
tempFilePath,
|
|
1418
|
+
size: file.size,
|
|
1419
|
+
duration: 0,
|
|
1420
|
+
height: metadata.height,
|
|
1421
|
+
width: metadata.width,
|
|
1422
|
+
thumbTempFilePath: "",
|
|
1423
|
+
fileType,
|
|
1424
|
+
originalFileObj: file
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
function chooseMedia({ count = 9, mediaType = ["image", "video"], sourceType = ["album", "camera"], camera = "back", success, fail, complete }) {
|
|
1428
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1429
|
+
const { onSuccess, onFail, onComplete } = cbs;
|
|
1430
|
+
const normalizedCount = normalizeChooseMediaCount(count);
|
|
1431
|
+
const normalizedMediaType = normalizeStringArray(mediaType, ["image", "video"]);
|
|
1432
|
+
const normalizedSourceType = normalizeStringArray(sourceType, ["album", "camera"]);
|
|
1433
|
+
createFilePicker(
|
|
1434
|
+
"chooseMedia",
|
|
1435
|
+
{
|
|
1436
|
+
accept: getChooseMediaAccept(normalizedMediaType),
|
|
1437
|
+
multiple: normalizedCount > 1,
|
|
1438
|
+
capture: captureAttrFor(normalizedSourceType, camera)
|
|
1439
|
+
},
|
|
1440
|
+
cbs,
|
|
1441
|
+
async (rawFiles, removeInput) => {
|
|
1442
|
+
const files = rawFiles.slice(0, normalizedCount);
|
|
1293
1443
|
try {
|
|
1294
|
-
const
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
onComplete?.();
|
|
1301
|
-
});
|
|
1302
|
-
stream.on("error", (hashErr) => {
|
|
1303
|
-
onFail?.({ errMsg: `fsGetFileInfo:fail ${hashErr.message}` });
|
|
1304
|
-
onComplete?.();
|
|
1444
|
+
const tempFiles2 = await Promise.all(files.map(buildChooseMediaTempFile));
|
|
1445
|
+
onSuccess?.({
|
|
1446
|
+
tempFiles: tempFiles2,
|
|
1447
|
+
type: getChooseMediaResultType(tempFiles2),
|
|
1448
|
+
failedCount: 0,
|
|
1449
|
+
errMsg: "chooseMedia:ok"
|
|
1305
1450
|
});
|
|
1306
|
-
|
|
1307
|
-
|
|
1451
|
+
} catch (error) {
|
|
1452
|
+
onFail?.({ errMsg: `chooseMedia:fail ${error.message}` });
|
|
1453
|
+
} finally {
|
|
1454
|
+
onComplete?.();
|
|
1455
|
+
removeInput();
|
|
1308
1456
|
}
|
|
1309
1457
|
}
|
|
1310
|
-
|
|
1311
|
-
onComplete?.();
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
function fsSaveFile({ tempFilePath, filePath: _filePath, success, fail, complete }) {
|
|
1315
|
-
void _filePath;
|
|
1316
|
-
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1317
|
-
const { onFail, onComplete } = cbs;
|
|
1318
|
-
if (guardFsAvailable("fsSaveFile", cbs)) return;
|
|
1319
|
-
const src = resolveOrBail(tempFilePath, "fsSaveFile", cbs);
|
|
1320
|
-
if (!src) return;
|
|
1321
|
-
const ext = _path.extname(tempFilePath) || "";
|
|
1322
|
-
const id = _crypto.randomUUID() + ext;
|
|
1323
|
-
const savedFilePath = `difile://_store/${id}`;
|
|
1324
|
-
const destResolved = resolveVPath(savedFilePath);
|
|
1325
|
-
if (!destResolved || !destResolved.realPath) {
|
|
1326
|
-
onFail?.({ errMsg: "fsSaveFile:fail unable to allocate destination" });
|
|
1327
|
-
onComplete?.();
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
const destReal = destResolved.realPath;
|
|
1331
|
-
if (src.kind === "tmp") {
|
|
1332
|
-
_tmpBytes(tempFilePath).then(
|
|
1333
|
-
(bytes) => mkdirpThenWrite(destReal, "fsSaveFile", cbs, (done) => _fs.writeFile(destReal, bytes, done), { savedFilePath }),
|
|
1334
|
-
tmpFailHandler("fsSaveFile", cbs)
|
|
1335
|
-
);
|
|
1336
|
-
return;
|
|
1337
|
-
}
|
|
1338
|
-
if (!src.realPath) {
|
|
1339
|
-
onFail?.({ errMsg: "fsSaveFile:fail invalid src path" });
|
|
1340
|
-
onComplete?.();
|
|
1341
|
-
return;
|
|
1342
|
-
}
|
|
1343
|
-
mkdirpThenWrite(destReal, "fsSaveFile", cbs, (done) => _fs.copyFile(src.realPath, destReal, done), { savedFilePath });
|
|
1458
|
+
);
|
|
1344
1459
|
}
|
|
1345
|
-
function
|
|
1460
|
+
function chooseVideo({ sourceType, camera, success, fail, complete }) {
|
|
1346
1461
|
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1347
1462
|
const { onSuccess, onComplete } = cbs;
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1463
|
+
const normalizedSourceType = normalizeStringArray(sourceType, ["album", "camera"]);
|
|
1464
|
+
createFilePicker(
|
|
1465
|
+
"chooseVideo",
|
|
1466
|
+
{ accept: "video/*", multiple: false, capture: captureAttrFor(normalizedSourceType, camera) },
|
|
1467
|
+
cbs,
|
|
1468
|
+
async (files, removeInput) => {
|
|
1469
|
+
const file = files[0];
|
|
1470
|
+
const tempFilePath = createTempFilePath(file);
|
|
1471
|
+
const metadata = await readVideoMetadata(tempFilePath);
|
|
1472
|
+
onSuccess?.({
|
|
1473
|
+
tempFilePath,
|
|
1474
|
+
duration: metadata.duration,
|
|
1475
|
+
size: file.size,
|
|
1476
|
+
width: metadata.width,
|
|
1477
|
+
height: metadata.height,
|
|
1478
|
+
errMsg: "chooseVideo:ok"
|
|
1479
|
+
});
|
|
1365
1480
|
onComplete?.();
|
|
1366
|
-
|
|
1481
|
+
removeInput();
|
|
1367
1482
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1483
|
+
);
|
|
1484
|
+
}
|
|
1485
|
+
var AUDIO_EVENT_MAP = {
|
|
1486
|
+
play: "play",
|
|
1487
|
+
pause: "pause",
|
|
1488
|
+
ended: "ended",
|
|
1489
|
+
error: "error",
|
|
1490
|
+
timeupdate: "timeUpdate",
|
|
1491
|
+
waiting: "waiting",
|
|
1492
|
+
seeking: "seeking",
|
|
1493
|
+
seeked: "seeked",
|
|
1494
|
+
canplay: "canplay"
|
|
1495
|
+
};
|
|
1496
|
+
var _newAudioInstances = /* @__PURE__ */ new Map();
|
|
1497
|
+
var _audioEventDisposers = /* @__PURE__ */ new Map();
|
|
1498
|
+
var _audioFire = /* @__PURE__ */ new Map();
|
|
1499
|
+
function audioSnapshot(audio, event) {
|
|
1500
|
+
return {
|
|
1501
|
+
event,
|
|
1502
|
+
currentTime: audio.currentTime || 0,
|
|
1503
|
+
duration: Number.isFinite(audio.duration) ? audio.duration : 0,
|
|
1504
|
+
buffered: audio.buffered.length ? audio.buffered.end(audio.buffered.length - 1) : 0,
|
|
1505
|
+
paused: audio.paused
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
function audioCreate({ audioId }) {
|
|
1509
|
+
_newAudioInstances.set(audioId, new Audio());
|
|
1510
|
+
}
|
|
1511
|
+
function audioListen({ audioId, success }) {
|
|
1512
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1513
|
+
const fire = this.createCallbackFunction(success);
|
|
1514
|
+
if (!audio || !fire) return;
|
|
1515
|
+
_audioFire.set(audioId, fire);
|
|
1516
|
+
_audioEventDisposers.get(audioId)?.();
|
|
1517
|
+
const dispose = bindDomEvents(
|
|
1518
|
+
audio,
|
|
1519
|
+
AUDIO_EVENT_MAP,
|
|
1520
|
+
fire,
|
|
1521
|
+
(event) => audioSnapshot(audio, event)
|
|
1522
|
+
);
|
|
1523
|
+
_audioEventDisposers.set(audioId, dispose);
|
|
1524
|
+
}
|
|
1525
|
+
function audioSetProp({ audioId, prop, value, startTime, loop, volume, playbackRate, autoplay }) {
|
|
1526
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1527
|
+
if (!audio) return;
|
|
1528
|
+
switch (prop) {
|
|
1529
|
+
case "src":
|
|
1530
|
+
audio.src = value;
|
|
1531
|
+
if (startTime != null) audio.currentTime = startTime;
|
|
1532
|
+
if (loop != null) audio.loop = loop;
|
|
1533
|
+
if (volume != null) audio.volume = Math.max(0, Math.min(1, volume));
|
|
1534
|
+
if (playbackRate != null) audio.playbackRate = playbackRate;
|
|
1535
|
+
if (autoplay) audio.play().catch(() => {
|
|
1383
1536
|
});
|
|
1384
|
-
|
|
1537
|
+
break;
|
|
1538
|
+
case "startTime":
|
|
1539
|
+
audio.currentTime = Number(value) || 0;
|
|
1540
|
+
break;
|
|
1541
|
+
case "autoplay":
|
|
1542
|
+
audio.autoplay = !!value;
|
|
1543
|
+
break;
|
|
1544
|
+
case "loop":
|
|
1545
|
+
audio.loop = !!value;
|
|
1546
|
+
break;
|
|
1547
|
+
case "volume":
|
|
1548
|
+
audio.volume = Math.max(0, Math.min(1, Number(value) || 0));
|
|
1549
|
+
break;
|
|
1550
|
+
case "playbackRate":
|
|
1551
|
+
audio.playbackRate = Number(value) || 1;
|
|
1552
|
+
break;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
function audioPlay({ audioId, src }) {
|
|
1556
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1557
|
+
if (!audio) return;
|
|
1558
|
+
if (src && audio.src !== src) audio.src = src;
|
|
1559
|
+
audio.play().catch(() => {
|
|
1385
1560
|
});
|
|
1386
1561
|
}
|
|
1387
|
-
function
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
const
|
|
1392
|
-
if (!
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1562
|
+
function audioPause({ audioId }) {
|
|
1563
|
+
_newAudioInstances.get(audioId)?.pause();
|
|
1564
|
+
}
|
|
1565
|
+
function audioStop({ audioId }) {
|
|
1566
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1567
|
+
if (!audio) return;
|
|
1568
|
+
audio.pause();
|
|
1569
|
+
audio.currentTime = 0;
|
|
1570
|
+
_audioFire.get(audioId)?.(audioSnapshot(audio, "stop"));
|
|
1571
|
+
}
|
|
1572
|
+
function audioSeek({ audioId, position }) {
|
|
1573
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1574
|
+
if (!audio) return;
|
|
1575
|
+
audio.currentTime = position;
|
|
1576
|
+
}
|
|
1577
|
+
function audioDestroy({ audioId }) {
|
|
1578
|
+
_audioEventDisposers.get(audioId)?.();
|
|
1579
|
+
_audioEventDisposers.delete(audioId);
|
|
1580
|
+
_audioFire.delete(audioId);
|
|
1581
|
+
const audio = _newAudioInstances.get(audioId);
|
|
1582
|
+
if (!audio) return;
|
|
1583
|
+
audio.pause();
|
|
1584
|
+
audio.removeAttribute("src");
|
|
1585
|
+
audio.load();
|
|
1586
|
+
_newAudioInstances.delete(audioId);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/simulator/simulator-api-fsm.ts
|
|
1590
|
+
var ARRAY_BUFFER_BASE64_KEY = "__diminaArrayBufferBase64";
|
|
1591
|
+
var FILE_DATA_TYPE_KEY = "__diminaFileDataType";
|
|
1592
|
+
var FILE_DATA_BASE64_KEY = "__diminaFileDataBase64";
|
|
1593
|
+
var FSM_ADAPTERS = [
|
|
1594
|
+
{ short: "access", impl: fsAccess },
|
|
1595
|
+
{ short: "stat", impl: fsStat },
|
|
1596
|
+
{ short: "readFile", impl: fsReadFile, encodeReadResult: true },
|
|
1597
|
+
{ short: "writeFile", impl: fsWriteFile, decodeWriteData: true },
|
|
1598
|
+
{ short: "appendFile", impl: fsAppendFile, decodeWriteData: true },
|
|
1599
|
+
{ short: "copyFile", impl: fsCopyFile },
|
|
1600
|
+
{ short: "rename", impl: fsRename },
|
|
1601
|
+
{ short: "unlink", impl: fsUnlink },
|
|
1602
|
+
{ short: "mkdir", impl: fsMkdir },
|
|
1603
|
+
{ short: "rmdir", impl: fsRmdir },
|
|
1604
|
+
{ short: "readdir", impl: fsReaddir },
|
|
1605
|
+
{ short: "getFileInfo", impl: fsGetFileInfo },
|
|
1606
|
+
{ short: "saveFile", impl: fsSaveFile },
|
|
1607
|
+
{ short: "getSavedFileList", impl: fsGetSavedFileList },
|
|
1608
|
+
{ short: "removeSavedFile", impl: fsRemoveSavedFile },
|
|
1609
|
+
{ short: "truncate", impl: fsTruncate }
|
|
1610
|
+
];
|
|
1611
|
+
function legacyPrefix(short) {
|
|
1612
|
+
return `fs${short[0].toUpperCase()}${short.slice(1)}`;
|
|
1613
|
+
}
|
|
1614
|
+
function renameErrMsg(payload, legacy, short) {
|
|
1615
|
+
if (!payload || typeof payload !== "object") return payload;
|
|
1616
|
+
const rec = payload;
|
|
1617
|
+
if (typeof rec.errMsg === "string" && rec.errMsg.startsWith(`${legacy}:`)) {
|
|
1618
|
+
return { ...rec, errMsg: `${short}:${rec.errMsg.slice(legacy.length + 1)}` };
|
|
1397
1619
|
}
|
|
1398
|
-
|
|
1620
|
+
return payload;
|
|
1621
|
+
}
|
|
1622
|
+
var BufferImpl2 = getNodeBindings().buffer?.Buffer ?? (typeof Buffer !== "undefined" ? Buffer : void 0);
|
|
1623
|
+
function decodeFileData(data) {
|
|
1624
|
+
if (!BufferImpl2) return data;
|
|
1625
|
+
if (!data || typeof data !== "object") return data;
|
|
1626
|
+
const rec = data;
|
|
1627
|
+
if (rec[FILE_DATA_TYPE_KEY] !== "base64" || typeof rec[FILE_DATA_BASE64_KEY] !== "string") return data;
|
|
1628
|
+
return BufferImpl2.from(rec[FILE_DATA_BASE64_KEY], "base64");
|
|
1629
|
+
}
|
|
1630
|
+
function encodeReadResult(payload) {
|
|
1631
|
+
if (!BufferImpl2) return payload;
|
|
1632
|
+
if (!payload || typeof payload !== "object") return payload;
|
|
1633
|
+
const rec = payload;
|
|
1634
|
+
const data = rec.data;
|
|
1635
|
+
if (typeof data === "string" || !ArrayBuffer.isView(data)) return payload;
|
|
1636
|
+
const bytes = BufferImpl2.from(data.buffer, data.byteOffset, data.byteLength);
|
|
1637
|
+
return { ...rec, data: { [ARRAY_BUFFER_BASE64_KEY]: bytes.toString("base64") } };
|
|
1638
|
+
}
|
|
1639
|
+
function makeFsmHandler(spec) {
|
|
1640
|
+
const legacy = legacyPrefix(spec.short);
|
|
1641
|
+
return function(params) {
|
|
1642
|
+
const raw = params && typeof params === "object" && !Array.isArray(params) ? { ...params } : {};
|
|
1643
|
+
const { success, fail, complete, ...rest } = raw;
|
|
1644
|
+
const cbs = bindCallbacks(this, { success, fail, complete });
|
|
1645
|
+
const opts = rest;
|
|
1646
|
+
if (spec.decodeWriteData && "data" in opts) {
|
|
1647
|
+
opts.data = decodeFileData(opts.data);
|
|
1648
|
+
}
|
|
1649
|
+
let verdictFired = false;
|
|
1650
|
+
let completeFired = false;
|
|
1651
|
+
const fireSuccess = (out) => {
|
|
1652
|
+
if (verdictFired) return;
|
|
1653
|
+
verdictFired = true;
|
|
1654
|
+
cbs.onSuccess?.(out);
|
|
1655
|
+
};
|
|
1656
|
+
const fireFail = (out) => {
|
|
1657
|
+
if (verdictFired) return;
|
|
1658
|
+
verdictFired = true;
|
|
1659
|
+
cbs.onFail?.(out);
|
|
1660
|
+
};
|
|
1661
|
+
const fireComplete = () => {
|
|
1662
|
+
if (completeFired) return;
|
|
1663
|
+
completeFired = true;
|
|
1664
|
+
cbs.onComplete?.();
|
|
1665
|
+
};
|
|
1666
|
+
opts.success = (res) => {
|
|
1667
|
+
let out = renameErrMsg(res, legacy, spec.short);
|
|
1668
|
+
if (spec.encodeReadResult) out = encodeReadResult(out);
|
|
1669
|
+
fireSuccess(out);
|
|
1670
|
+
};
|
|
1671
|
+
opts.fail = (res) => {
|
|
1672
|
+
fireFail(renameErrMsg(res, legacy, spec.short));
|
|
1673
|
+
};
|
|
1674
|
+
opts.complete = fireComplete;
|
|
1675
|
+
const innerCtx = Object.create(this);
|
|
1676
|
+
innerCtx.createCallbackFunction = (fn) => typeof fn === "function" ? fn : void 0;
|
|
1677
|
+
try {
|
|
1678
|
+
spec.impl.call(innerCtx, opts);
|
|
1679
|
+
} catch (err) {
|
|
1680
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1681
|
+
fireFail({ errMsg: `${spec.short}:fail ${msg}` });
|
|
1682
|
+
fireComplete();
|
|
1683
|
+
}
|
|
1684
|
+
};
|
|
1399
1685
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1686
|
+
var handlers = {};
|
|
1687
|
+
for (const spec of FSM_ADAPTERS) {
|
|
1688
|
+
handlers[`FileSystemManager.${spec.short}`] = makeFsmHandler(spec);
|
|
1689
|
+
}
|
|
1690
|
+
var fileSystemManagerApis = handlers;
|
|
1691
|
+
|
|
1692
|
+
// ../dimina-electron-runtime/dist/shared/request-core.js
|
|
1693
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
1694
|
+
var MAX_TIMEOUT_MS = 2147483647;
|
|
1695
|
+
function resolveTimeoutBudgetMs(timeout) {
|
|
1696
|
+
const t = Number(timeout);
|
|
1697
|
+
return Number.isFinite(t) && t > 0 && t <= MAX_TIMEOUT_MS ? t : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
1407
1698
|
}
|
|
1408
|
-
var fsUnzip = notSupportedApi("fsUnzip");
|
|
1409
1699
|
|
|
1410
1700
|
// src/simulator/simulator-api-network.ts
|
|
1411
|
-
function downloadFile({ url, header = {}, filePath, success, fail, complete }) {
|
|
1701
|
+
function downloadFile({ url, header = {}, filePath, timeout, success, fail, complete }) {
|
|
1412
1702
|
const { onSuccess, onFail, onComplete } = bindCallbacks(this, { success, fail, complete });
|
|
1413
|
-
|
|
1414
|
-
|
|
1703
|
+
let settled = false;
|
|
1704
|
+
const settleFail = (errMsg) => {
|
|
1705
|
+
if (settled) return;
|
|
1706
|
+
settled = true;
|
|
1707
|
+
clearTimeout(timer);
|
|
1708
|
+
onFail?.({ errMsg });
|
|
1709
|
+
onComplete?.();
|
|
1710
|
+
};
|
|
1711
|
+
const settleOk = (result) => {
|
|
1712
|
+
if (settled) return;
|
|
1713
|
+
settled = true;
|
|
1714
|
+
clearTimeout(timer);
|
|
1715
|
+
onSuccess?.(result);
|
|
1716
|
+
onComplete?.();
|
|
1717
|
+
};
|
|
1718
|
+
const controller = new AbortController();
|
|
1719
|
+
const timer = setTimeout(() => {
|
|
1720
|
+
settleFail("downloadFile:fail timeout");
|
|
1721
|
+
controller.abort();
|
|
1722
|
+
}, resolveTimeoutBudgetMs(timeout));
|
|
1723
|
+
fetch(url, { headers: header, signal: controller.signal }).then(async (response) => {
|
|
1724
|
+
if (!response.ok) {
|
|
1725
|
+
settleFail(
|
|
1726
|
+
`downloadFile:fail HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}`
|
|
1727
|
+
);
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1415
1730
|
const blob = await response.blob();
|
|
1416
1731
|
const tempFilePath = createTempFilePath(blob);
|
|
1417
|
-
|
|
1732
|
+
settleOk({
|
|
1418
1733
|
tempFilePath,
|
|
1419
1734
|
filePath: filePath || tempFilePath,
|
|
1420
1735
|
statusCode: response.status,
|
|
1421
1736
|
errMsg: "downloadFile:ok"
|
|
1422
1737
|
});
|
|
1423
1738
|
}).catch((error) => {
|
|
1424
|
-
|
|
1425
|
-
}).finally(() => {
|
|
1426
|
-
onComplete?.();
|
|
1739
|
+
settleFail(`downloadFile:fail ${error.message}`);
|
|
1427
1740
|
});
|
|
1428
1741
|
}
|
|
1429
1742
|
var uploadRequests = /* @__PURE__ */ new Map();
|
|
@@ -1870,24 +2183,9 @@ var simulatorApis = {
|
|
|
1870
2183
|
audioStop,
|
|
1871
2184
|
audioSeek,
|
|
1872
2185
|
audioDestroy,
|
|
1873
|
-
// Filesystem
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
fsReadFile,
|
|
1877
|
-
fsWriteFile,
|
|
1878
|
-
fsAppendFile,
|
|
1879
|
-
fsCopyFile,
|
|
1880
|
-
fsRename,
|
|
1881
|
-
fsUnlink,
|
|
1882
|
-
fsMkdir,
|
|
1883
|
-
fsRmdir,
|
|
1884
|
-
fsReaddir,
|
|
1885
|
-
fsGetFileInfo,
|
|
1886
|
-
fsSaveFile,
|
|
1887
|
-
fsGetSavedFileList,
|
|
1888
|
-
fsRemoveSavedFile,
|
|
1889
|
-
fsTruncate,
|
|
1890
|
-
fsUnzip
|
|
2186
|
+
// Filesystem: the service thread invokes the dotted FileSystemManager.*
|
|
2187
|
+
// wire names (see simulator-api-fsm.ts).
|
|
2188
|
+
...fileSystemManagerApis
|
|
1891
2189
|
};
|
|
1892
2190
|
|
|
1893
2191
|
// src/preload/windows/main.ts
|