@statewalker/webrun-files 0.2.0 → 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/dist/index-umd.js +239 -69
- package/dist/index-umd.min.js +2 -2
- package/dist/index.js +239 -69
- package/dist/index.min.js +2 -2
- package/package.json +2 -2
- package/src/BrowserFilesApi.js +238 -67
package/dist/index-umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// @statewalker/webrun-files v0.
|
|
1
|
+
// @statewalker/webrun-files v0.3.0 https://github.com/statewalker/webrun-files Copyright 2023 Mikhail Kotelnikov
|
|
2
2
|
(function (global, factory) {
|
|
3
3
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
4
4
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
@@ -246,7 +246,6 @@ const mimeTypeToExtensions = mimeTypes.reduce((index, t) => {
|
|
|
246
246
|
}, {});
|
|
247
247
|
|
|
248
248
|
// import mimeDb from 'mime-db';
|
|
249
|
-
// import mimeTypes from 'mime-types';
|
|
250
249
|
|
|
251
250
|
function getMimeType(url) {
|
|
252
251
|
url = url || '';
|
|
@@ -611,30 +610,165 @@ async function* list(fs, filePath, recursive) {
|
|
|
611
610
|
}
|
|
612
611
|
}
|
|
613
612
|
|
|
613
|
+
/**
|
|
614
|
+
* Stateless adapter for the "File and Directory Entries API".
|
|
615
|
+
* See https://developer.mozilla.org/en-US/docs/Web/API/File_and_Directory_Entries_API
|
|
616
|
+
*
|
|
617
|
+
* ## File and Directory Entries API
|
|
618
|
+
* The File and Directory Entries API simulates a local file system
|
|
619
|
+
* this web apps can navigate within and access files in. You can
|
|
620
|
+
* develop apps which read, write, and create files and/or directories
|
|
621
|
+
* in a virtual, sandboxed file system.
|
|
622
|
+
*/
|
|
623
|
+
class AdapterForFilesAndDirectoryEntriesApi {
|
|
624
|
+
static canApply(handle) {
|
|
625
|
+
return ((handle?.isDirectory || handle?.isFile) &&
|
|
626
|
+
(typeof handle?.name === "string"));
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async *listDirectoryChildren(handle) {
|
|
630
|
+
let dirReader = handle.createReader();
|
|
631
|
+
while (true) {
|
|
632
|
+
const list = await new Promise((resolve, reject) =>
|
|
633
|
+
dirReader.readEntries(resolve, reject)
|
|
634
|
+
);
|
|
635
|
+
if (!list.length) break;
|
|
636
|
+
yield* list;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
isDirectoryHandle(handle) {
|
|
641
|
+
return handle?.isDirectory;
|
|
642
|
+
}
|
|
643
|
+
isFileHandle(handle) {
|
|
644
|
+
return handle?.isFile;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
async getSubdirectoryHandle(dirHandle, directoryName, create) {
|
|
648
|
+
return new Promise((resolve, reject) =>
|
|
649
|
+
dirHandle.getDirectory(
|
|
650
|
+
directoryName,
|
|
651
|
+
{ create },
|
|
652
|
+
resolve,
|
|
653
|
+
reject,
|
|
654
|
+
)
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async getFileHandle(dirHandle, fileName, create = true) {
|
|
659
|
+
return new Promise((resolve, reject) =>
|
|
660
|
+
dirHandle.getFile(
|
|
661
|
+
fileName,
|
|
662
|
+
{ create },
|
|
663
|
+
resolve,
|
|
664
|
+
reject,
|
|
665
|
+
)
|
|
666
|
+
);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
async removeDirectoryEntry(dirHandle, handle) {
|
|
670
|
+
return new Promise((resolve, reject) => handle.remove(resolve, reject));
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async getFile(handle) {
|
|
674
|
+
const file = await new Promise((resolve, reject) =>
|
|
675
|
+
handle.file(resolve, reject)
|
|
676
|
+
);
|
|
677
|
+
return file;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
async getFileWriteStream(handle) {
|
|
681
|
+
throw new Error("Not implemented");
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Stateless adapter for the "File System API" and "File System Access API".
|
|
687
|
+
* See:
|
|
688
|
+
* - https://developer.mozilla.org/en-US/docs/Web/API/File_System_API
|
|
689
|
+
* - https://wicg.github.io/file-system-access/
|
|
690
|
+
*/
|
|
691
|
+
class AdapterForFileSystemApi {
|
|
692
|
+
static canApply(handle) {
|
|
693
|
+
return (handle?.kind === "file" || handle?.kind === "directory");
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async *listDirectoryChildren(handle) {
|
|
697
|
+
yield* handle.values();
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
isDirectoryHandle(handle) {
|
|
701
|
+
return handle?.kind === "directory";
|
|
702
|
+
}
|
|
703
|
+
isFileHandle(handle) {
|
|
704
|
+
return handle?.kind === "file";
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
async getSubdirectoryHandle(dirHandle, directoryName, create) {
|
|
708
|
+
return await dirHandle.getDirectoryHandle(directoryName, { create });
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async getFileHandle(dirHandle, fileName, create = true) {
|
|
712
|
+
return await dirHandle.getFileHandle(fileName, { create });
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
async removeDirectoryEntry(dirHandle, handle) {
|
|
716
|
+
return await dirHandle.removeEntry(handle.name, { recursive: true });
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
async getFile(handle) {
|
|
720
|
+
return await handle.getFile();
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
async getFileWriteStream(handle) {
|
|
724
|
+
return await handle.createWritable(); // Write the contents of the file to the stream.
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
614
728
|
class BrowserFilesApi extends FilesApi {
|
|
615
729
|
constructor(options) {
|
|
616
730
|
super(options);
|
|
617
|
-
if (!this.
|
|
618
|
-
throw new Error("Root directory
|
|
731
|
+
if (!this.rootHandle) {
|
|
732
|
+
throw new Error("Root directory handle is not defined");
|
|
733
|
+
}
|
|
734
|
+
if (!this.adapter) {
|
|
735
|
+
throw new Error(
|
|
736
|
+
"Can not define a browser adapter for the specified root handle",
|
|
737
|
+
);
|
|
619
738
|
}
|
|
620
739
|
}
|
|
621
740
|
|
|
622
|
-
get
|
|
623
|
-
return this.options.rootHandler;
|
|
741
|
+
get rootHandle() {
|
|
742
|
+
return this.options.rootHandle || this.options.rootHandler;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
get adapter() {
|
|
746
|
+
if (!this._adapter) {
|
|
747
|
+
const handle = this.rootHandle;
|
|
748
|
+
let adapter = AdapterForFilesAndDirectoryEntriesApi.canApply(handle)
|
|
749
|
+
? new AdapterForFilesAndDirectoryEntriesApi()
|
|
750
|
+
: AdapterForFileSystemApi.canApply(handle)
|
|
751
|
+
? new AdapterForFileSystemApi()
|
|
752
|
+
: null;
|
|
753
|
+
this._adapter = adapter;
|
|
754
|
+
}
|
|
755
|
+
return this._adapter;
|
|
624
756
|
}
|
|
625
757
|
|
|
626
758
|
async *list(file, { recursive = false } = {}) {
|
|
627
759
|
const segments = this._getPathSegments(file);
|
|
628
|
-
const handle = await this.
|
|
760
|
+
const handle = await this._getHandle(segments);
|
|
761
|
+
if (!handle) return;
|
|
629
762
|
const that = this;
|
|
630
|
-
if (handle
|
|
763
|
+
if (this.adapter.isDirectoryHandle(handle)) {
|
|
764
|
+
// yield await this._getFileInfo(segments, handle);
|
|
631
765
|
yield* listChildren(segments, handle, recursive);
|
|
632
766
|
}
|
|
633
767
|
|
|
634
768
|
async function* listChildren(segments, dirHandle, recursive) {
|
|
635
|
-
for await (const handle of
|
|
769
|
+
for await (const handle of that._listDirectoryContent(dirHandle)) {
|
|
636
770
|
yield await that._getFileInfo([...segments, handle.name], handle);
|
|
637
|
-
if (recursive && handle
|
|
771
|
+
if (recursive && that.adapter.isDirectoryHandle(handle)) {
|
|
638
772
|
yield* listChildren(
|
|
639
773
|
[...segments, handle.name],
|
|
640
774
|
handle,
|
|
@@ -647,32 +781,45 @@ class BrowserFilesApi extends FilesApi {
|
|
|
647
781
|
|
|
648
782
|
async stats(file) {
|
|
649
783
|
const segments = this._getPathSegments(file);
|
|
650
|
-
const handle = await this.
|
|
784
|
+
const handle = await this._getHandle(segments);
|
|
651
785
|
return handle ? await this._getFileInfo(segments, handle) : null;
|
|
652
786
|
}
|
|
653
787
|
|
|
654
788
|
async remove(file) {
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
789
|
+
const doRemove = async (dirHandle, handle) => {
|
|
790
|
+
let result = true;
|
|
791
|
+
if (this.adapter.isDirectoryHandle(handle)) {
|
|
792
|
+
const children = [];
|
|
793
|
+
for await (let child of this.adapter.listDirectoryChildren(handle)) {
|
|
794
|
+
children.push(child);
|
|
795
|
+
}
|
|
796
|
+
for (let child of children) {
|
|
797
|
+
result = result && await doRemove(handle, child);
|
|
798
|
+
}
|
|
664
799
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
800
|
+
if (dirHandle) {
|
|
801
|
+
result &= await this.adapter.removeDirectoryEntry(dirHandle, handle);
|
|
802
|
+
}
|
|
803
|
+
return result;
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
const segments = this._getPathSegments(file);
|
|
807
|
+
const handle = await this._getHandle(segments);
|
|
808
|
+
segments.pop();
|
|
809
|
+
const dirHandle = await this._getHandle(segments);
|
|
810
|
+
return doRemove(dirHandle !== handle ? dirHandle : null, handle);
|
|
670
811
|
}
|
|
671
812
|
|
|
672
813
|
async write(file, content) {
|
|
673
814
|
const segments = this._getPathSegments(file);
|
|
674
|
-
const
|
|
675
|
-
const
|
|
815
|
+
const fileName = segments.pop();
|
|
816
|
+
const dirHandle = await this._getDirectoryHandle(segments, true);
|
|
817
|
+
const fileHandle = await this.adapter.getFileHandle(
|
|
818
|
+
dirHandle,
|
|
819
|
+
fileName,
|
|
820
|
+
true,
|
|
821
|
+
);
|
|
822
|
+
const writable = await this.adapter.getFileWriteStream(fileHandle);
|
|
676
823
|
try {
|
|
677
824
|
if (typeof content === "function") content = content();
|
|
678
825
|
for await (let block of content) {
|
|
@@ -685,18 +832,26 @@ class BrowserFilesApi extends FilesApi {
|
|
|
685
832
|
|
|
686
833
|
async *read(file /* { start = 0, bufferSize = 1024 * 8 } = {} */) {
|
|
687
834
|
const segments = this._getPathSegments(file);
|
|
688
|
-
|
|
835
|
+
|
|
836
|
+
const fileName = segments.pop();
|
|
837
|
+
const dirHandle = await this._getDirectoryHandle(segments, false);
|
|
838
|
+
const fileHandle = await this.adapter.getFileHandle(
|
|
839
|
+
dirHandle,
|
|
840
|
+
fileName,
|
|
841
|
+
false,
|
|
842
|
+
);
|
|
689
843
|
if (!fileHandle) return;
|
|
690
|
-
const fileData = await
|
|
844
|
+
const fileData = await this.adapter.getFile(fileHandle);
|
|
691
845
|
const stream = fileData.stream();
|
|
692
846
|
const reader = stream.getReader();
|
|
693
847
|
try {
|
|
694
848
|
let chunk;
|
|
695
|
-
while (
|
|
696
|
-
|
|
849
|
+
while (chunk = await reader.read()) {
|
|
850
|
+
if (chunk.done) break;
|
|
851
|
+
yield await chunk.value;
|
|
697
852
|
}
|
|
698
853
|
} finally {
|
|
699
|
-
reader.cancel && reader.cancel();
|
|
854
|
+
await reader.cancel && reader.cancel();
|
|
700
855
|
// stream.cancel && stream.cancel();
|
|
701
856
|
}
|
|
702
857
|
}
|
|
@@ -707,41 +862,8 @@ class BrowserFilesApi extends FilesApi {
|
|
|
707
862
|
// TODO:
|
|
708
863
|
// async move(fromPath, toPath, options = {}) { ... }
|
|
709
864
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
try {
|
|
713
|
-
handle = await this._getFileHandler(segments, create);
|
|
714
|
-
} catch (e) {
|
|
715
|
-
try {
|
|
716
|
-
handle = await this._getDirectoryHandler(segments, create);
|
|
717
|
-
} catch (e) {
|
|
718
|
-
// Just ignore it
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
return handle;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
async _getDirectoryHandler(segments, create = true) {
|
|
725
|
-
segments = [...segments];
|
|
726
|
-
let dirHandle = this.rootHandler;
|
|
727
|
-
for (let directoryName of segments) {
|
|
728
|
-
dirHandle = await dirHandle.getDirectoryHandle(directoryName, { create });
|
|
729
|
-
}
|
|
730
|
-
return dirHandle;
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
async _getFileHandler(segments, create = true) {
|
|
734
|
-
segments = [...segments];
|
|
735
|
-
const filename = segments.pop();
|
|
736
|
-
let dirHandle = this.rootHandler;
|
|
737
|
-
for (let directoryName of segments) {
|
|
738
|
-
dirHandle = await dirHandle.getDirectoryHandle(directoryName, {
|
|
739
|
-
create: true,
|
|
740
|
-
});
|
|
741
|
-
}
|
|
742
|
-
const fileHandler = await dirHandle.getFileHandle(filename, { create });
|
|
743
|
-
return fileHandler;
|
|
744
|
-
}
|
|
865
|
+
// ---------------------------------------------------------
|
|
866
|
+
// Private methods
|
|
745
867
|
|
|
746
868
|
async _getFileInfo(segments, handle) {
|
|
747
869
|
segments = [...segments];
|
|
@@ -753,11 +875,12 @@ class BrowserFilesApi extends FilesApi {
|
|
|
753
875
|
path,
|
|
754
876
|
name,
|
|
755
877
|
};
|
|
756
|
-
if (handle
|
|
878
|
+
if (this.adapter.isDirectoryHandle(handle)) {
|
|
757
879
|
info.kind = "directory";
|
|
758
880
|
} else {
|
|
759
881
|
info.kind = "file";
|
|
760
|
-
|
|
882
|
+
|
|
883
|
+
const file = await this.adapter.getFile(handle);
|
|
761
884
|
info.size = file.size;
|
|
762
885
|
info.type = getMimeType(path);
|
|
763
886
|
info.lastModified = file.lastModified;
|
|
@@ -771,6 +894,53 @@ class BrowserFilesApi extends FilesApi {
|
|
|
771
894
|
const segments = filePath.split("/").filter((s) => !!s);
|
|
772
895
|
return segments;
|
|
773
896
|
}
|
|
897
|
+
|
|
898
|
+
async _getHandle(segments) {
|
|
899
|
+
const parentDirSegments = [...segments];
|
|
900
|
+
const name = parentDirSegments.pop();
|
|
901
|
+
const create = false;
|
|
902
|
+
try {
|
|
903
|
+
const dirHandle = await this._getDirectoryHandle(
|
|
904
|
+
parentDirSegments,
|
|
905
|
+
create,
|
|
906
|
+
);
|
|
907
|
+
try {
|
|
908
|
+
if (!name) return dirHandle;
|
|
909
|
+
return await this.adapter.getSubdirectoryHandle(
|
|
910
|
+
dirHandle,
|
|
911
|
+
name,
|
|
912
|
+
create,
|
|
913
|
+
);
|
|
914
|
+
} catch (error) {
|
|
915
|
+
return await this.adapter.getFileHandle(dirHandle, name, create);
|
|
916
|
+
}
|
|
917
|
+
} catch (error) {
|
|
918
|
+
return undefined;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
async _getDirectoryHandle(segments, create = true) {
|
|
923
|
+
segments = [...segments];
|
|
924
|
+
let dirHandle = this.rootHandle;
|
|
925
|
+
for (let directoryName of segments) {
|
|
926
|
+
dirHandle = await this.adapter.getSubdirectoryHandle(
|
|
927
|
+
dirHandle,
|
|
928
|
+
directoryName,
|
|
929
|
+
create,
|
|
930
|
+
);
|
|
931
|
+
if (!dirHandle) break;
|
|
932
|
+
}
|
|
933
|
+
return dirHandle;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
async *_listDirectoryContent(handle) {
|
|
937
|
+
let entries = [];
|
|
938
|
+
for await (const entry of this.adapter.listDirectoryChildren(handle)) {
|
|
939
|
+
entries.push(entry);
|
|
940
|
+
}
|
|
941
|
+
entries = entries.sort((a, b) => b.name > a.name ? 1 : -1);
|
|
942
|
+
yield* entries;
|
|
943
|
+
}
|
|
774
944
|
}
|
|
775
945
|
|
|
776
946
|
function addFilesApiLogger({
|
package/dist/index-umd.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// @statewalker/webrun-files v0.
|
|
2
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).statewalker=e.statewalker||{},e.statewalker["webrun-files"]=e.statewalker["webrun-files"]||{}))}(this,(function(e){"use strict";function t(e){const t=e.split("/").filter((e=>!!e&&"."!==e));return 0===t.length&&t.push(""),t.unshift(""),t.join("/")}function i(){throw new Error("Not implemented.")}class n{constructor(e){this.options=e||{}}async*list(e,t={}){yield i()}async stats(e){return i()}async remove(e){return i()}async write(e,t){return i()}async*read(e,t){yield i()}async copy(e,t,i={}){const n=async(e,t,i)=>{t=`${t}${i.path.substring(e.length)}`;const n=this.read(i);return await this.write(t,n)},s=void 0===i.recursive||!!i.recursive,o=await this.stats(e);if("directory"===o.kind)for await(const o of this.list(e,{...i,recursive:s}))"directory"!==o.kind&&await n(e,t,o);else await n(e,t,o);return!0}async move(e,t,i={}){return!!await this.copy(e,t,i)&&(await this.remove(e),!0)}normalizePath(e){return t("object"==typeof e?e.path:e+"")}}const s=[{extensions:["aac"],mime:"audio/aac",description:"AAC audio"},{extensions:["abw"],mime:"application/x-abiword",description:"AbiWord document"},{extensions:["arc"],mime:"application/x-freearc",description:"Archive document (multiple files embedded)"},{extensions:["avi"],mime:"video/x-msvideo",description:"AVI: Audio Video Interleave"},{extensions:["azw"],mime:"application/vnd.amazon.ebook",description:"Amazon Kindle eBook format"},{extensions:["bin"],mime:"application/octet-stream",description:"Any kind of binary data"},{extensions:["bmp"],mime:"image/bmp",description:"Windows OS/2 Bitmap Graphics"},{extensions:["bz"],mime:"application/x-bzip",description:"BZip archive"},{extensions:["bz2"],mime:"application/x-bzip2",description:"BZip2 archive"},{extensions:["csh"],mime:"application/x-csh",description:"C-Shell script"},{extensions:["css"],mime:"text/css",description:"Cascading Style Sheets (CSS)"},{extensions:["csv"],mime:"text/csv",description:"Comma-separated values (CSV)"},{extensions:["doc"],mime:"application/msword",description:"Microsoft Word"},{extensions:["docx"],mime:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",description:"Microsoft Word (OpenXML)"},{extensions:["eot"],mime:"application/vnd.ms-fontobject",description:"MS Embedded OpenType fonts"},{extensions:["epub"],mime:"application/epub+zip",description:"Electronic publication (EPUB)"},{extensions:["gz"],mime:"application/gzip",description:"GZip Compressed Archive"},{extensions:["gif"],mime:"image/gif",description:"Graphics Interchange Format (GIF)"},{extensions:["htm","html"],mime:"text/html",description:"HyperText Markup Language (HTML)"},{extensions:["ico"],mime:"image/vnd.microsoft.icon",description:"Icon format"},{extensions:["ics"],mime:"text/calendar",description:"iCalendar format"},{extensions:["jar"],mime:"application/java-archive",description:"Java Archive (JAR)"},{extensions:["jpg","jpeg"],mime:"image/jpeg",description:"JPEG images"},{extensions:["js"],mime:"text/javascript",description:"JavaScript per the following specifications: https://html.spec.whatwg.org/multipage/#scriptingLanguages, https://html.spec.whatwg.org/multipage/#dependencies:willful-violation, https://datatracker.ietf.org/doc/draft-ietf-dispatch-javascript-mjs/"},{extensions:["json"],mime:"application/json",description:"JSON format"},{extensions:["jsonld"],mime:"application/ld+json",description:"JSON-LD format"},{extensions:["mid","midi"],mime:"application/ld+json",description:"Musical Instrument Digital Interface (MIDI)\taudio/midi audio/x-midi"},{extensions:["mjs"],mime:"text/javascript",description:"JavaScript module"},{extensions:["mp3"],mime:"audio/mpeg",description:"MP3 audio"},{extensions:["md"],mime:"text/markdown",description:"Markdown"},{extensions:["mpeg"],mime:"video/mpeg",description:"MPEG Video"},{extensions:["mpkg"],mime:"application/vnd.apple.installer+xml",description:"Apple Installer Package"},{extensions:["odp"],mime:"application/vnd.oasis.opendocument.presentation",description:"OpenDocument presentation document"},{extensions:["ods"],mime:"application/vnd.oasis.opendocument.spreadsheet",description:"OpenDocument spreadsheet document"},{extensions:["odt"],mime:"application/vnd.oasis.opendocument.text",description:"OpenDocument text document"},{extensions:["oga"],mime:"audio/ogg",description:"OGG audio"},{extensions:["ogv"],mime:"video/ogg",description:"OGG video"},{extensions:["ogx"],mime:"application/ogg",description:"OGG"},{extensions:["opus"],mime:"audio/opus",description:"Opus audio"},{extensions:["otf"],mime:"font/otf",description:"OpenType font"},{extensions:["png"],mime:"image/png",description:"Portable Network Graphics"},{extensions:["pdf"],mime:"application/pdf",description:"Adobe Portable Document Format (PDF)"},{extensions:["php"],mime:"application/x-httpd-php",description:"Hypertext Preprocessor (Personal Home Page)"},{extensions:["ppt"],mime:"application/vnd.ms-powerpoint",description:"Microsoft PowerPoint"},{extensions:["pptx"],mime:"application/vnd.openxmlformats-officedocument.presentationml.presentation",description:"Microsoft PowerPoint (OpenXML)"},{extensions:["rar"],mime:"application/vnd.rar",description:"RAR archive"},{extensions:["rtf"],mime:"application/rtf",description:"Rich Text Format (RTF)"},{extensions:["sh"],mime:"application/x-sh",description:"Bourne shell script"},{extensions:["svg"],mime:"image/svg+xml",description:"Scalable Vector Graphics (SVG)"},{extensions:["swf"],mime:"application/x-shockwave-flash",description:"Small web format (SWF) or Adobe Flash document"},{extensions:["tar"],mime:"application/x-tar",description:"Tape Archive (TAR)"},{extensions:["tif","tiff"],mime:"image/tiff",description:"Tagged Image File Format (TIFF)"},{extensions:["ttf"],mime:"font/ttf",description:"TrueType Font"},{extensions:["txt"],mime:"text/plain",description:"Text, (generally ASCII or ISO 8859-n)"},{extensions:["vsd"],mime:"application/vnd.visio",description:"Microsoft Visio"},{extensions:["wav"],mime:"audio/wav",description:"Waveform Audio Format"},{extensions:["weba"],mime:"audio/webm",description:"WEBM audio"},{extensions:["webm"],mime:"video/webm",description:"WEBM video"},{extensions:["webp"],mime:"image/webp",description:"WEBP image"},{extensions:["woff"],mime:"font/woff",description:"Web Open Font Format (WOFF)"},{extensions:["woff2"],mime:"font/woff2",description:"Web Open Font Format (WOFF)"},{extensions:["xhtml"],mime:"application/xhtml+xml",description:"XHTML"},{extensions:["xls"],mime:"application/vnd.ms-excel",description:"Microsoft Excel"},{extensions:["xlsx"],mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",description:"Microsoft Excel (OpenXML)"},{extensions:["xml"],mime:"application/xml",description:"XML; application/xml - if not readable from casual users (RFC 3023, section 3); text/xml - if readable from casual users (RFC 3023, section 3)"},{extensions:["xul"],mime:"application/vnd.mozilla.xul+xml",description:"XUL"},{extensions:["zip"],mime:"application/zip",description:"ZIP archive"},{extensions:["3gp"],mime:"video/3gpp",description:"3GPP audio/video container"},{extensions:["3g2"],mime:"video/3gpp2",description:"3GPP2 audio/video container; audio/3gpp if it doesn't contain video; audio/3gpp2 if it doesn't contain video"},{extensions:["7z"],mime:"application/x-7z-compressed",description:"7-zip archive"},{extensions:["jsx"],mime:"text/jsx",description:"JavaScript Syntax Extension / JavaScript XML format"},{extensions:["ts"],mime:"text/ts",description:"TypeScript format"},{extensions:["tsx"],mime:"text/tsx",description:"TypeScript Syntax Extension / TypeScript XML format"}],o=s.reduce(((e,t)=>(e[t.mime]=t.description,e)),{}),a=s.reduce(((e,t)=>{for(const i of t.extensions)e[i]=t.mime;return e}),{}),r=s.reduce(((e,t)=>(e[t.mime]=t.extensions,e)),{});function c(e){const t=e=e||"",i=(e=(e=e.split("?").shift()).split("/").pop()).split(".").pop();let n=a[i];return n||(n=t.match(/api\.observablehq\.com/)||t.match(/\.ojs$/)?"application/javascript-observable":"application/binary"),n}function p(e){const t=e.split("/");return t.pop(),t.join("/")}function d(...e){let t=e.map((e=>e.split("/"))).reduce(((e,t)=>(e.push(...t),e)),[]);const i=t.shift();return t=t.filter((e=>!!e&&"."!==e)),t.unshift(i),t.join("/")}async function m(e,t){try{const i=await e.stat(t);if(i&&i.isDirectory())return!0}catch(e){}const i=p(t);return t!==i&&(await m(e,i),await e.mkdir(t,{recursive:!0}),!0)}async function l(e,t){try{if((await e.stat(t)).isDirectory()){for await(let i of f(e,t,!1))await l(e,i.path);await e.rmdir(t)}else await e.unlink(t)}catch(e){if("ENOENT"!==e.code)throw e}}async function*f(e,t,i){try{if((await e.stat(t)).isDirectory()){const n=await e.readdir(t);for(const s of n.sort()){const n=d(t,s);let o=await e.stat(n);yield{path:n,stat:o},i&&o.isDirectory()&&(yield*f(e,n,!0))}}}catch(e){if("ENOENT"!==e.code)throw e}}class h extends n{constructor(e){if(super(e),!this.rootHandler)throw new Error("Root directory handler is not defined")}get rootHandler(){return this.options.rootHandler}async*list(e,{recursive:t=!1}={}){const i=this._getPathSegments(e),n=await this._getHandler(i,!1),s=this;"directory"===n.kind&&(yield*async function*e(t,i,n){for await(const o of i.values())yield await s._getFileInfo([...t,o.name],o),n&&"directory"==o.kind&&(yield*e([...t,o.name],o,n))}(i,n,t))}async stats(e){const t=this._getPathSegments(e),i=await this._getHandler(t,!1);return i?await this._getFileInfo(t,i):null}async remove(e){let t=this._getPathSegments(e);const i=t.pop(),n=await this._getHandler(t,!1),s=[];if(i)s.push(i);else for await(const e of n.values())s.push(e.name);for(const e of s)await n.removeEntry(e,{recursive:!0});return!0}async write(e,t){const i=this._getPathSegments(e),n=await this._getFileHandler(i),s=await n.createWritable();try{"function"==typeof t&&(t=t());for await(let e of t)await s.write(e.buffer?e.buffer:e)}finally{await s.close()}}async*read(e){const t=this._getPathSegments(e),i=await this._getFileHandler(t,!1);if(!i)return;const n=(await i.getFile()).stream().getReader();try{let e;for(;(e=await n.read())&&!e.done;)yield e.value}finally{n.cancel&&n.cancel()}}async _getHandler(e,t=!1){let i;try{i=await this._getFileHandler(e,t)}catch(n){try{i=await this._getDirectoryHandler(e,t)}catch(e){}}return i}async _getDirectoryHandler(e,t=!0){e=[...e];let i=this.rootHandler;for(let n of e)i=await i.getDirectoryHandle(n,{create:t});return i}async _getFileHandler(e,t=!0){const i=(e=[...e]).pop();let n=this.rootHandler;for(let t of e)n=await n.getDirectoryHandle(t,{create:!0});return await n.getFileHandle(i,{create:t})}async _getFileInfo(e,t){const i=(e=[...e])[e.length-1];e.unshift("");const n=e.join("/"),s={kind:"",path:n,name:i};if("directory"===t.kind)s.kind="directory";else{s.kind="file";const e=await t.getFile();s.size=e.size,s.type=c(n),s.lastModified=e.lastModified}return s}_getPathSegments(e){return("object"==typeof e?e.path:e+"").split("/").filter((e=>!!e))}}async function u(e){let t=!1;try{if("file"===e.kind)await e.getFile();else for await(let t of e.values())break;t=!0}catch(i){console.log(i,e),t=!1}return t}async function x(e,t){const i={};t&&(i.mode="readwrite");let n=!1;return"granted"===await e.queryPermission(i)&&(n=!0),n||"granted"!==await e.requestPermission(i)||(n=!0),n}e.BrowserFilesApi=h,e.FilesApi=n,e.MemFilesApi=class extends n{constructor(e){super(e),this.index={"/":{kind:"directory",path:"/",name:""}}}async*list(e,{recursive:t=!1}={}){await this._init();const i=this.normalizePath(e),n=this._getPaths();for(const e of n){if(0!==e.indexOf(i)||e===i)continue;const n=e.substring(i.length+1).split("/");(t||1===n.length)&&(yield this._getFileInfo(e))}}async stats(e){await this._init();const t=this.normalizePath(e);return this._getFileInfo(t)}async remove(e){await this._init();const t=await this.stats(e);if(!t)return!1;for await(const{path:e}of this.list(t,{recursive:!0}))"/"!==e&&delete this.index[e];delete this.index[t.path]}async write(e,t){await this._init(),await this._doWrite(e,t)}async*read(e){await this._init();const t=this.normalizePath(e),i=this.index[t];if(!i||"file"!==i.kind)return;const n=i.content;yield*n}async _init(){return this._initPromise=this._initPromise||Promise.resolve().then((()=>this._addEntries(this.options.files)))}async _doWrite(e,t){const i=this.normalizePath(e).split("/").filter((e=>!!e)),n=[""];let s;for(let e=0;e<i.length;e++){const t=i[e];n.push(t);const o=n.join("/");s=this.index[o]=this.index[o]||{path:o,name:t,kind:"directory"}}s.kind="file",s.content=[],s.type=c(s.path),s.size=0,s.lastModified=Date.now(),"function"==typeof t&&(t=t());for await(let e of t)e=new Uint8Array(e),s.content.push(e),s.size+=e.length}async _addEntries(e={}){for(let[t,i]of Object.entries(e))"string"==typeof i?i=[(new TextEncoder).encode(i)]:i instanceof Uint8Array&&(i=[i]),await this._doWrite(t,i)}_getPaths(){return this._getIndexPaths(this.index)}_getIndexPaths(e){return Object.keys(e).sort((function(e,t){return e>t?1:e<t?-1:0}))}_getFileInfo(e){const t=this.index[e];if(!t)return null;const i={...t};return delete i.content,i}},e.NodeFilesApi=class extends n{constructor(e){if(super(e),!this.fs)throw new Error("File System is not defined.");if(!this.rootDir)throw new Error("Root directory is not defined.")}get rootDirSegments(){return this._rootDirSegments||(this._rootDirSegments=this._toPathSegments(this.options.rootDir||"./")),this._rootDirSegments}get rootDir(){return this._rootDir||(this._rootDir=this.rootDirSegments.join("/")),this._rootDir}get fs(){return this.options.fs}async _init(){return this._initialized=this._initialized||(async()=>{const e=this.rootDir;return await m(this.fs,e)})()}async*list(e,{recursive:t=!1}={}){await this._init();const i=this._expandPath(e),n=await this._stats(i);if(n&&"directory"===n.kind)for await(let e of f(this.fs,i,t))yield this._getFileInfo(e.path,e.stat)}async stats(e){await this._init();const t=this._expandPath(e);return this._stats(t)}async remove(e){await this._init();const t=this._expandPath(e);return await l(this.fs,t)}async write(e,t){await this._init();const i=this._expandPath(e),n=p(i);let s;await this.fs.mkdir(n,{recursive:!0});try{s=await this.fs.open(i,"w"),"function"==typeof t&&(t=t()),await s.writeFile(t)}finally{s&&await s.close()}}async*read(e,{start:t=0,bufferSize:i=8192}={}){await this._init();const n=this._expandPath(e);let s;try{s=await this.fs.open(n,"r");const e=0;for(let n=t,o=0;;n+=o){const t=new Uint8ClampedArray(i),a=await s.read(t,e,i,n);if(o=a.bytesRead,0===o)break;yield o<i?a.buffer.slice(0,o):a.buffer}}finally{s&&await s.close()}}async _stats(e){try{const t=await this.fs.stat(e);return this._getFileInfo(e,t)}catch(e){if("ENOENT"!==e.code)throw e;return null}}_getFileInfo(e,t){const i=this._toPathSegments(e).slice(this.rootDirSegments.length);i.unshift("");const n=i[i.length-1];let s={path:e=i.join("/"),name:n,kind:""};return t.isFile()?(s.size=t.size||0,s.type=c(e),s.kind="file",s.lastModified=new Date(t.mtime).getTime()):s.kind="directory",s}_toPathSegments(e){return(e=this.normalizePath(e)).split("/")}_expandPath(e){const t="object"==typeof e?e.path:e+"",i=this._toPathSegments(t).filter((e=>!!e));return i.unshift(...this.rootDirSegments),i.join("/")}},e.addFilesApiLogger=function({filesApi:e,log:t=console.log,wrapper:i={}}){return["list","stats","remove","write","read","copy","move"].reduce(((i,n)=>{const s=e[n].bind(e);return i[n]=(...i)=>{let o={method:n,stage:"enter",args:i};try{return t(o),o.result=s.call(e,...i)}catch(e){o.error=e}finally{o.stage="exit",t(o)}},i}),i)},e.extensionToMimeType=a,e.getMimeType=c,e.isHandlerAccessible=u,e.mimeTypeToExtensions=r,e.mimeTypes=s,e.mimeTypesDescriptions=o,e.normalizePath=t,e.openBrowserFilesApi=async function({handlerKey:e="root-dir",readwrite:t=!0,index:i={},get:n=(async e=>i[e]),set:s=(async(e,t)=>i[e]=t),del:o=(async e=>delete i[e])}={}){let a=await n(e);if(a||(a=await window.showDirectoryPicker(),await s(e,a)),!await x(a,t))throw new Error("Access was not granted");if(!await u(a))throw await o(e),new Error("Can not access to the folder. Please try again.");return new h({rootHandler:a})},e.verifyPermission=x}));
|
|
1
|
+
// @statewalker/webrun-files v0.3.0 https://github.com/statewalker/webrun-files Copyright 2023 Mikhail Kotelnikov
|
|
2
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t(((e="undefined"!=typeof globalThis?globalThis:e||self).statewalker=e.statewalker||{},e.statewalker["webrun-files"]=e.statewalker["webrun-files"]||{}))}(this,(function(e){"use strict";function t(e){const t=e.split("/").filter((e=>!!e&&"."!==e));return 0===t.length&&t.push(""),t.unshift(""),t.join("/")}function i(){throw new Error("Not implemented.")}class n{constructor(e){this.options=e||{}}async*list(e,t={}){yield i()}async stats(e){return i()}async remove(e){return i()}async write(e,t){return i()}async*read(e,t){yield i()}async copy(e,t,i={}){const n=async(e,t,i)=>{t=`${t}${i.path.substring(e.length)}`;const n=this.read(i);return await this.write(t,n)},s=void 0===i.recursive||!!i.recursive,o=await this.stats(e);if("directory"===o.kind)for await(const o of this.list(e,{...i,recursive:s}))"directory"!==o.kind&&await n(e,t,o);else await n(e,t,o);return!0}async move(e,t,i={}){return!!await this.copy(e,t,i)&&(await this.remove(e),!0)}normalizePath(e){return t("object"==typeof e?e.path:e+"")}}const s=[{extensions:["aac"],mime:"audio/aac",description:"AAC audio"},{extensions:["abw"],mime:"application/x-abiword",description:"AbiWord document"},{extensions:["arc"],mime:"application/x-freearc",description:"Archive document (multiple files embedded)"},{extensions:["avi"],mime:"video/x-msvideo",description:"AVI: Audio Video Interleave"},{extensions:["azw"],mime:"application/vnd.amazon.ebook",description:"Amazon Kindle eBook format"},{extensions:["bin"],mime:"application/octet-stream",description:"Any kind of binary data"},{extensions:["bmp"],mime:"image/bmp",description:"Windows OS/2 Bitmap Graphics"},{extensions:["bz"],mime:"application/x-bzip",description:"BZip archive"},{extensions:["bz2"],mime:"application/x-bzip2",description:"BZip2 archive"},{extensions:["csh"],mime:"application/x-csh",description:"C-Shell script"},{extensions:["css"],mime:"text/css",description:"Cascading Style Sheets (CSS)"},{extensions:["csv"],mime:"text/csv",description:"Comma-separated values (CSV)"},{extensions:["doc"],mime:"application/msword",description:"Microsoft Word"},{extensions:["docx"],mime:"application/vnd.openxmlformats-officedocument.wordprocessingml.document",description:"Microsoft Word (OpenXML)"},{extensions:["eot"],mime:"application/vnd.ms-fontobject",description:"MS Embedded OpenType fonts"},{extensions:["epub"],mime:"application/epub+zip",description:"Electronic publication (EPUB)"},{extensions:["gz"],mime:"application/gzip",description:"GZip Compressed Archive"},{extensions:["gif"],mime:"image/gif",description:"Graphics Interchange Format (GIF)"},{extensions:["htm","html"],mime:"text/html",description:"HyperText Markup Language (HTML)"},{extensions:["ico"],mime:"image/vnd.microsoft.icon",description:"Icon format"},{extensions:["ics"],mime:"text/calendar",description:"iCalendar format"},{extensions:["jar"],mime:"application/java-archive",description:"Java Archive (JAR)"},{extensions:["jpg","jpeg"],mime:"image/jpeg",description:"JPEG images"},{extensions:["js"],mime:"text/javascript",description:"JavaScript per the following specifications: https://html.spec.whatwg.org/multipage/#scriptingLanguages, https://html.spec.whatwg.org/multipage/#dependencies:willful-violation, https://datatracker.ietf.org/doc/draft-ietf-dispatch-javascript-mjs/"},{extensions:["json"],mime:"application/json",description:"JSON format"},{extensions:["jsonld"],mime:"application/ld+json",description:"JSON-LD format"},{extensions:["mid","midi"],mime:"application/ld+json",description:"Musical Instrument Digital Interface (MIDI)\taudio/midi audio/x-midi"},{extensions:["mjs"],mime:"text/javascript",description:"JavaScript module"},{extensions:["mp3"],mime:"audio/mpeg",description:"MP3 audio"},{extensions:["md"],mime:"text/markdown",description:"Markdown"},{extensions:["mpeg"],mime:"video/mpeg",description:"MPEG Video"},{extensions:["mpkg"],mime:"application/vnd.apple.installer+xml",description:"Apple Installer Package"},{extensions:["odp"],mime:"application/vnd.oasis.opendocument.presentation",description:"OpenDocument presentation document"},{extensions:["ods"],mime:"application/vnd.oasis.opendocument.spreadsheet",description:"OpenDocument spreadsheet document"},{extensions:["odt"],mime:"application/vnd.oasis.opendocument.text",description:"OpenDocument text document"},{extensions:["oga"],mime:"audio/ogg",description:"OGG audio"},{extensions:["ogv"],mime:"video/ogg",description:"OGG video"},{extensions:["ogx"],mime:"application/ogg",description:"OGG"},{extensions:["opus"],mime:"audio/opus",description:"Opus audio"},{extensions:["otf"],mime:"font/otf",description:"OpenType font"},{extensions:["png"],mime:"image/png",description:"Portable Network Graphics"},{extensions:["pdf"],mime:"application/pdf",description:"Adobe Portable Document Format (PDF)"},{extensions:["php"],mime:"application/x-httpd-php",description:"Hypertext Preprocessor (Personal Home Page)"},{extensions:["ppt"],mime:"application/vnd.ms-powerpoint",description:"Microsoft PowerPoint"},{extensions:["pptx"],mime:"application/vnd.openxmlformats-officedocument.presentationml.presentation",description:"Microsoft PowerPoint (OpenXML)"},{extensions:["rar"],mime:"application/vnd.rar",description:"RAR archive"},{extensions:["rtf"],mime:"application/rtf",description:"Rich Text Format (RTF)"},{extensions:["sh"],mime:"application/x-sh",description:"Bourne shell script"},{extensions:["svg"],mime:"image/svg+xml",description:"Scalable Vector Graphics (SVG)"},{extensions:["swf"],mime:"application/x-shockwave-flash",description:"Small web format (SWF) or Adobe Flash document"},{extensions:["tar"],mime:"application/x-tar",description:"Tape Archive (TAR)"},{extensions:["tif","tiff"],mime:"image/tiff",description:"Tagged Image File Format (TIFF)"},{extensions:["ttf"],mime:"font/ttf",description:"TrueType Font"},{extensions:["txt"],mime:"text/plain",description:"Text, (generally ASCII or ISO 8859-n)"},{extensions:["vsd"],mime:"application/vnd.visio",description:"Microsoft Visio"},{extensions:["wav"],mime:"audio/wav",description:"Waveform Audio Format"},{extensions:["weba"],mime:"audio/webm",description:"WEBM audio"},{extensions:["webm"],mime:"video/webm",description:"WEBM video"},{extensions:["webp"],mime:"image/webp",description:"WEBP image"},{extensions:["woff"],mime:"font/woff",description:"Web Open Font Format (WOFF)"},{extensions:["woff2"],mime:"font/woff2",description:"Web Open Font Format (WOFF)"},{extensions:["xhtml"],mime:"application/xhtml+xml",description:"XHTML"},{extensions:["xls"],mime:"application/vnd.ms-excel",description:"Microsoft Excel"},{extensions:["xlsx"],mime:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",description:"Microsoft Excel (OpenXML)"},{extensions:["xml"],mime:"application/xml",description:"XML; application/xml - if not readable from casual users (RFC 3023, section 3); text/xml - if readable from casual users (RFC 3023, section 3)"},{extensions:["xul"],mime:"application/vnd.mozilla.xul+xml",description:"XUL"},{extensions:["zip"],mime:"application/zip",description:"ZIP archive"},{extensions:["3gp"],mime:"video/3gpp",description:"3GPP audio/video container"},{extensions:["3g2"],mime:"video/3gpp2",description:"3GPP2 audio/video container; audio/3gpp if it doesn't contain video; audio/3gpp2 if it doesn't contain video"},{extensions:["7z"],mime:"application/x-7z-compressed",description:"7-zip archive"},{extensions:["jsx"],mime:"text/jsx",description:"JavaScript Syntax Extension / JavaScript XML format"},{extensions:["ts"],mime:"text/ts",description:"TypeScript format"},{extensions:["tsx"],mime:"text/tsx",description:"TypeScript Syntax Extension / TypeScript XML format"}],o=s.reduce(((e,t)=>(e[t.mime]=t.description,e)),{}),a=s.reduce(((e,t)=>{for(const i of t.extensions)e[i]=t.mime;return e}),{}),r=s.reduce(((e,t)=>(e[t.mime]=t.extensions,e)),{});function c(e){const t=e=e||"",i=(e=(e=e.split("?").shift()).split("/").pop()).split(".").pop();let n=a[i];return n||(n=t.match(/api\.observablehq\.com/)||t.match(/\.ojs$/)?"application/javascript-observable":"application/binary"),n}function p(e){const t=e.split("/");return t.pop(),t.join("/")}function d(...e){let t=e.map((e=>e.split("/"))).reduce(((e,t)=>(e.push(...t),e)),[]);const i=t.shift();return t=t.filter((e=>!!e&&"."!==e)),t.unshift(i),t.join("/")}async function l(e,t){try{const i=await e.stat(t);if(i&&i.isDirectory())return!0}catch(e){}const i=p(t);return t!==i&&(await l(e,i),await e.mkdir(t,{recursive:!0}),!0)}async function m(e,t){try{if((await e.stat(t)).isDirectory()){for await(let i of h(e,t,!1))await m(e,i.path);await e.rmdir(t)}else await e.unlink(t)}catch(e){if("ENOENT"!==e.code)throw e}}async function*h(e,t,i){try{if((await e.stat(t)).isDirectory()){const n=await e.readdir(t);for(const s of n.sort()){const n=d(t,s);let o=await e.stat(n);yield{path:n,stat:o},i&&o.isDirectory()&&(yield*h(e,n,!0))}}}catch(e){if("ENOENT"!==e.code)throw e}}class f{static canApply(e){return(e?.isDirectory||e?.isFile)&&"string"==typeof e?.name}async*listDirectoryChildren(e){let t=e.createReader();for(;;){const e=await new Promise(((e,i)=>t.readEntries(e,i)));if(!e.length)break;yield*e}}isDirectoryHandle(e){return e?.isDirectory}isFileHandle(e){return e?.isFile}async getSubdirectoryHandle(e,t,i){return new Promise(((n,s)=>e.getDirectory(t,{create:i},n,s)))}async getFileHandle(e,t,i=!0){return new Promise(((n,s)=>e.getFile(t,{create:i},n,s)))}async removeDirectoryEntry(e,t){return new Promise(((e,i)=>t.remove(e,i)))}async getFile(e){return await new Promise(((t,i)=>e.file(t,i)))}async getFileWriteStream(e){throw new Error("Not implemented")}}class u{static canApply(e){return"file"===e?.kind||"directory"===e?.kind}async*listDirectoryChildren(e){yield*e.values()}isDirectoryHandle(e){return"directory"===e?.kind}isFileHandle(e){return"file"===e?.kind}async getSubdirectoryHandle(e,t,i){return await e.getDirectoryHandle(t,{create:i})}async getFileHandle(e,t,i=!0){return await e.getFileHandle(t,{create:i})}async removeDirectoryEntry(e,t){return await e.removeEntry(t.name,{recursive:!0})}async getFile(e){return await e.getFile()}async getFileWriteStream(e){return await e.createWritable()}}class y extends n{constructor(e){if(super(e),!this.rootHandle)throw new Error("Root directory handle is not defined");if(!this.adapter)throw new Error("Can not define a browser adapter for the specified root handle")}get rootHandle(){return this.options.rootHandle||this.options.rootHandler}get adapter(){if(!this._adapter){const e=this.rootHandle;let t=f.canApply(e)?new f:u.canApply(e)?new u:null;this._adapter=t}return this._adapter}async*list(e,{recursive:t=!1}={}){const i=this._getPathSegments(e),n=await this._getHandle(i);if(!n)return;const s=this;this.adapter.isDirectoryHandle(n)&&(yield*async function*e(t,i,n){for await(const o of s._listDirectoryContent(i))yield await s._getFileInfo([...t,o.name],o),n&&s.adapter.isDirectoryHandle(o)&&(yield*e([...t,o.name],o,n))}(i,n,t))}async stats(e){const t=this._getPathSegments(e),i=await this._getHandle(t);return i?await this._getFileInfo(t,i):null}async remove(e){const t=async(e,i)=>{let n=!0;if(this.adapter.isDirectoryHandle(i)){const e=[];for await(let t of this.adapter.listDirectoryChildren(i))e.push(t);for(let s of e)n=n&&await t(i,s)}return e&&(n&=await this.adapter.removeDirectoryEntry(e,i)),n},i=this._getPathSegments(e),n=await this._getHandle(i);i.pop();const s=await this._getHandle(i);return t(s!==n?s:null,n)}async write(e,t){const i=this._getPathSegments(e),n=i.pop(),s=await this._getDirectoryHandle(i,!0),o=await this.adapter.getFileHandle(s,n,!0),a=await this.adapter.getFileWriteStream(o);try{"function"==typeof t&&(t=t());for await(let e of t)await a.write(e.buffer?e.buffer:e)}finally{await a.close()}}async*read(e){const t=this._getPathSegments(e),i=t.pop(),n=await this._getDirectoryHandle(t,!1),s=await this.adapter.getFileHandle(n,i,!1);if(!s)return;const o=(await this.adapter.getFile(s)).stream().getReader();try{let e;for(;(e=await o.read())&&!e.done;)yield await e.value}finally{await o.cancel&&o.cancel()}}async _getFileInfo(e,t){const i=(e=[...e])[e.length-1];e.unshift("");const n=e.join("/"),s={kind:"",path:n,name:i};if(this.adapter.isDirectoryHandle(t))s.kind="directory";else{s.kind="file";const e=await this.adapter.getFile(t);s.size=e.size,s.type=c(n),s.lastModified=e.lastModified}return s}_getPathSegments(e){return("object"==typeof e?e.path:e+"").split("/").filter((e=>!!e))}async _getHandle(e){const t=[...e],i=t.pop(),n=!1;try{const e=await this._getDirectoryHandle(t,n);try{return i?await this.adapter.getSubdirectoryHandle(e,i,n):e}catch(t){return await this.adapter.getFileHandle(e,i,n)}}catch(e){return}}async _getDirectoryHandle(e,t=!0){e=[...e];let i=this.rootHandle;for(let n of e)if(i=await this.adapter.getSubdirectoryHandle(i,n,t),!i)break;return i}async*_listDirectoryContent(e){let t=[];for await(const i of this.adapter.listDirectoryChildren(e))t.push(i);t=t.sort(((e,t)=>t.name>e.name?1:-1)),yield*t}}async function w(e){let t=!1;try{if("file"===e.kind)await e.getFile();else for await(let t of e.values())break;t=!0}catch(i){console.log(i,e),t=!1}return t}async function g(e,t){const i={};t&&(i.mode="readwrite");let n=!1;return"granted"===await e.queryPermission(i)&&(n=!0),n||"granted"!==await e.requestPermission(i)||(n=!0),n}e.BrowserFilesApi=y,e.FilesApi=n,e.MemFilesApi=class extends n{constructor(e){super(e),this.index={"/":{kind:"directory",path:"/",name:""}}}async*list(e,{recursive:t=!1}={}){await this._init();const i=this.normalizePath(e),n=this._getPaths();for(const e of n){if(0!==e.indexOf(i)||e===i)continue;const n=e.substring(i.length+1).split("/");(t||1===n.length)&&(yield this._getFileInfo(e))}}async stats(e){await this._init();const t=this.normalizePath(e);return this._getFileInfo(t)}async remove(e){await this._init();const t=await this.stats(e);if(!t)return!1;for await(const{path:e}of this.list(t,{recursive:!0}))"/"!==e&&delete this.index[e];delete this.index[t.path]}async write(e,t){await this._init(),await this._doWrite(e,t)}async*read(e){await this._init();const t=this.normalizePath(e),i=this.index[t];if(!i||"file"!==i.kind)return;const n=i.content;yield*n}async _init(){return this._initPromise=this._initPromise||Promise.resolve().then((()=>this._addEntries(this.options.files)))}async _doWrite(e,t){const i=this.normalizePath(e).split("/").filter((e=>!!e)),n=[""];let s;for(let e=0;e<i.length;e++){const t=i[e];n.push(t);const o=n.join("/");s=this.index[o]=this.index[o]||{path:o,name:t,kind:"directory"}}s.kind="file",s.content=[],s.type=c(s.path),s.size=0,s.lastModified=Date.now(),"function"==typeof t&&(t=t());for await(let e of t)e=new Uint8Array(e),s.content.push(e),s.size+=e.length}async _addEntries(e={}){for(let[t,i]of Object.entries(e))"string"==typeof i?i=[(new TextEncoder).encode(i)]:i instanceof Uint8Array&&(i=[i]),await this._doWrite(t,i)}_getPaths(){return this._getIndexPaths(this.index)}_getIndexPaths(e){return Object.keys(e).sort((function(e,t){return e>t?1:e<t?-1:0}))}_getFileInfo(e){const t=this.index[e];if(!t)return null;const i={...t};return delete i.content,i}},e.NodeFilesApi=class extends n{constructor(e){if(super(e),!this.fs)throw new Error("File System is not defined.");if(!this.rootDir)throw new Error("Root directory is not defined.")}get rootDirSegments(){return this._rootDirSegments||(this._rootDirSegments=this._toPathSegments(this.options.rootDir||"./")),this._rootDirSegments}get rootDir(){return this._rootDir||(this._rootDir=this.rootDirSegments.join("/")),this._rootDir}get fs(){return this.options.fs}async _init(){return this._initialized=this._initialized||(async()=>{const e=this.rootDir;return await l(this.fs,e)})()}async*list(e,{recursive:t=!1}={}){await this._init();const i=this._expandPath(e),n=await this._stats(i);if(n&&"directory"===n.kind)for await(let e of h(this.fs,i,t))yield this._getFileInfo(e.path,e.stat)}async stats(e){await this._init();const t=this._expandPath(e);return this._stats(t)}async remove(e){await this._init();const t=this._expandPath(e);return await m(this.fs,t)}async write(e,t){await this._init();const i=this._expandPath(e),n=p(i);let s;await this.fs.mkdir(n,{recursive:!0});try{s=await this.fs.open(i,"w"),"function"==typeof t&&(t=t()),await s.writeFile(t)}finally{s&&await s.close()}}async*read(e,{start:t=0,bufferSize:i=8192}={}){await this._init();const n=this._expandPath(e);let s;try{s=await this.fs.open(n,"r");const e=0;for(let n=t,o=0;;n+=o){const t=new Uint8ClampedArray(i),a=await s.read(t,e,i,n);if(o=a.bytesRead,0===o)break;yield o<i?a.buffer.slice(0,o):a.buffer}}finally{s&&await s.close()}}async _stats(e){try{const t=await this.fs.stat(e);return this._getFileInfo(e,t)}catch(e){if("ENOENT"!==e.code)throw e;return null}}_getFileInfo(e,t){const i=this._toPathSegments(e).slice(this.rootDirSegments.length);i.unshift("");const n=i[i.length-1];let s={path:e=i.join("/"),name:n,kind:""};return t.isFile()?(s.size=t.size||0,s.type=c(e),s.kind="file",s.lastModified=new Date(t.mtime).getTime()):s.kind="directory",s}_toPathSegments(e){return(e=this.normalizePath(e)).split("/")}_expandPath(e){const t="object"==typeof e?e.path:e+"",i=this._toPathSegments(t).filter((e=>!!e));return i.unshift(...this.rootDirSegments),i.join("/")}},e.addFilesApiLogger=function({filesApi:e,log:t=console.log,wrapper:i={}}){return["list","stats","remove","write","read","copy","move"].reduce(((i,n)=>{const s=e[n].bind(e);return i[n]=(...i)=>{let o={method:n,stage:"enter",args:i};try{return t(o),o.result=s.call(e,...i)}catch(e){o.error=e}finally{o.stage="exit",t(o)}},i}),i)},e.extensionToMimeType=a,e.getMimeType=c,e.isHandlerAccessible=w,e.mimeTypeToExtensions=r,e.mimeTypes=s,e.mimeTypesDescriptions=o,e.normalizePath=t,e.openBrowserFilesApi=async function({handlerKey:e="root-dir",readwrite:t=!0,index:i={},get:n=(async e=>i[e]),set:s=(async(e,t)=>i[e]=t),del:o=(async e=>delete i[e])}={}){let a=await n(e);if(a||(a=await window.showDirectoryPicker(),await s(e,a)),!await g(a,t))throw new Error("Access was not granted");if(!await w(a))throw await o(e),new Error("Can not access to the folder. Please try again.");return new y({rootHandler:a})},e.verifyPermission=g}));
|