@fgv/ts-web-extras 5.0.2 → 5.1.0-1

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.
Files changed (383) hide show
  1. package/.rush/temp/chunked-rush-logs/ts-web-extras.build.chunks.jsonl +58 -25
  2. package/.rush/temp/chunked-rush-logs/ts-web-extras.test.chunks.jsonl +70 -0
  3. package/.rush/temp/operation/build/all.log +58 -25
  4. package/.rush/temp/operation/build/error.log +18 -0
  5. package/.rush/temp/operation/build/log-chunks.jsonl +58 -25
  6. package/.rush/temp/operation/build/state.json +1 -1
  7. package/.rush/temp/operation/test/all.log +70 -0
  8. package/.rush/temp/operation/test/error.log +16 -0
  9. package/.rush/temp/operation/test/log-chunks.jsonl +70 -0
  10. package/.rush/temp/operation/test/state.json +3 -0
  11. package/.rush/temp/shrinkwrap-deps.json +175 -163
  12. package/config/jest.config.json +4 -1
  13. package/config/typedoc.json +6 -0
  14. package/dist/index.js +2 -2
  15. package/dist/index.js.map +1 -1
  16. package/dist/packlets/crypto-utils/browserCryptoProvider.js +254 -0
  17. package/dist/packlets/crypto-utils/browserCryptoProvider.js.map +1 -0
  18. package/dist/packlets/crypto-utils/browserHashProvider.js.map +1 -0
  19. package/dist/packlets/{crypto → crypto-utils}/index.js +1 -0
  20. package/dist/packlets/crypto-utils/index.js.map +1 -0
  21. package/dist/packlets/file-api-types/index.js +27 -3
  22. package/dist/packlets/file-api-types/index.js.map +1 -1
  23. package/dist/packlets/file-tree/directoryHandleStore.js +124 -0
  24. package/dist/packlets/file-tree/directoryHandleStore.js.map +1 -0
  25. package/dist/packlets/file-tree/fileApiTreeAccessors.js +91 -0
  26. package/dist/packlets/file-tree/fileApiTreeAccessors.js.map +1 -1
  27. package/dist/packlets/file-tree/fileSystemAccessTreeAccessors.js +414 -0
  28. package/dist/packlets/file-tree/fileSystemAccessTreeAccessors.js.map +1 -0
  29. package/dist/packlets/file-tree/httpTreeAccessors.js +279 -0
  30. package/dist/packlets/file-tree/httpTreeAccessors.js.map +1 -0
  31. package/dist/packlets/file-tree/index.js +4 -0
  32. package/dist/packlets/file-tree/index.js.map +1 -1
  33. package/dist/packlets/file-tree/localStorageTreeAccessors.js +359 -0
  34. package/dist/packlets/file-tree/localStorageTreeAccessors.js.map +1 -0
  35. package/dist/test/mocks/idb-keyval.js +6 -0
  36. package/dist/test/mocks/idb-keyval.js.map +1 -0
  37. package/dist/test/unit/browserHashProvider.test.js +1 -1
  38. package/dist/test/unit/browserHashProvider.test.js.map +1 -1
  39. package/dist/test/unit/directoryHandleStore.test.js +190 -0
  40. package/dist/test/unit/directoryHandleStore.test.js.map +1 -0
  41. package/dist/test/unit/fileApiTreeAccessors.test.js +51 -0
  42. package/dist/test/unit/fileApiTreeAccessors.test.js.map +1 -1
  43. package/dist/test/unit/fileApiTypes.test.js +30 -0
  44. package/dist/test/unit/fileApiTypes.test.js.map +1 -1
  45. package/dist/test/unit/fileSystemAccessTreeAccessors.test.js +622 -0
  46. package/dist/test/unit/fileSystemAccessTreeAccessors.test.js.map +1 -0
  47. package/dist/test/unit/httpTreeAccessors.test.js +1000 -0
  48. package/dist/test/unit/httpTreeAccessors.test.js.map +1 -0
  49. package/dist/test/unit/localStorageTreeAccessors.test.js +812 -0
  50. package/dist/test/unit/localStorageTreeAccessors.test.js.map +1 -0
  51. package/dist/test/utils/fileSystemAccessMocks.js +271 -0
  52. package/dist/test/utils/fileSystemAccessMocks.js.map +1 -0
  53. package/dist/ts-web-extras.d.ts +584 -1
  54. package/dist/tsdoc-metadata.json +1 -1
  55. package/docs/@fgv/namespaces/CryptoUtils/README.md +18 -0
  56. package/docs/@fgv/namespaces/CryptoUtils/classes/BrowserCryptoProvider.md +203 -0
  57. package/docs/@fgv/namespaces/CryptoUtils/classes/BrowserHashProvider.md +63 -0
  58. package/docs/@fgv/namespaces/CryptoUtils/functions/createBrowserCryptoProvider.md +18 -0
  59. package/docs/@fgv/namespaces/FileTreeHelpers/README.md +19 -0
  60. package/docs/@fgv/namespaces/FileTreeHelpers/functions/extractFileListMetadata.md +23 -0
  61. package/docs/@fgv/namespaces/FileTreeHelpers/functions/extractFileMetadata.md +23 -0
  62. package/docs/@fgv/namespaces/FileTreeHelpers/functions/fromDirectoryUpload.md +33 -0
  63. package/docs/@fgv/namespaces/FileTreeHelpers/functions/fromFileList.md +33 -0
  64. package/docs/@fgv/namespaces/FileTreeHelpers/functions/getOriginalFile.md +25 -0
  65. package/docs/@fgv/namespaces/FileTreeHelpers/variables/defaultFileApiTreeInitParams.md +11 -0
  66. package/docs/README.md +78 -0
  67. package/docs/classes/DirectoryHandleStore.md +116 -0
  68. package/docs/classes/FileApiTreeAccessors.md +286 -0
  69. package/docs/classes/FileSystemAccessTreeAccessors.md +557 -0
  70. package/docs/classes/HttpTreeAccessors.md +508 -0
  71. package/docs/classes/LocalStorageTreeAccessors.md +520 -0
  72. package/docs/functions/exportAsJson.md +23 -0
  73. package/docs/functions/exportUsingFileSystemAPI.md +26 -0
  74. package/docs/functions/extractDirectoryPath.md +23 -0
  75. package/docs/functions/isDirectoryHandle.md +23 -0
  76. package/docs/functions/isFileHandle.md +23 -0
  77. package/docs/functions/isFilePath.md +21 -0
  78. package/docs/functions/parseContextFilter.md +22 -0
  79. package/docs/functions/parseQualifierDefaults.md +22 -0
  80. package/docs/functions/parseResourceTypes.md +22 -0
  81. package/docs/functions/parseUrlParameters.md +15 -0
  82. package/docs/functions/safeShowDirectoryPicker.md +24 -0
  83. package/docs/functions/safeShowOpenFilePicker.md +24 -0
  84. package/docs/functions/safeShowSaveFilePicker.md +24 -0
  85. package/docs/functions/supportsFileSystemAccess.md +23 -0
  86. package/docs/interfaces/FilePickerAcceptType.md +16 -0
  87. package/docs/interfaces/FileSystemCreateWritableOptions.md +15 -0
  88. package/docs/interfaces/FileSystemDirectoryHandle.md +187 -0
  89. package/docs/interfaces/FileSystemFileHandle.md +106 -0
  90. package/docs/interfaces/FileSystemGetDirectoryOptions.md +15 -0
  91. package/docs/interfaces/FileSystemGetFileOptions.md +15 -0
  92. package/docs/interfaces/FileSystemHandle.md +69 -0
  93. package/docs/interfaces/FileSystemHandlePermissionDescriptor.md +15 -0
  94. package/docs/interfaces/FileSystemRemoveOptions.md +15 -0
  95. package/docs/interfaces/FileSystemWritableFileStream.md +127 -0
  96. package/docs/interfaces/IDirectoryHandleTreeInitializer.md +17 -0
  97. package/docs/interfaces/IFileHandleTreeInitializer.md +16 -0
  98. package/docs/interfaces/IFileListTreeInitializer.md +15 -0
  99. package/docs/interfaces/IFileMetadata.md +19 -0
  100. package/docs/interfaces/IFileSystemAccessTreeParams.md +30 -0
  101. package/docs/interfaces/IFsAccessApis.md +57 -0
  102. package/docs/interfaces/IHttpTreeParams.md +32 -0
  103. package/docs/interfaces/ILocalStorageTreeParams.md +30 -0
  104. package/docs/interfaces/IUrlConfigOptions.md +27 -0
  105. package/docs/interfaces/ShowDirectoryPickerOptions.md +17 -0
  106. package/docs/interfaces/ShowOpenFilePickerOptions.md +19 -0
  107. package/docs/interfaces/ShowSaveFilePickerOptions.md +19 -0
  108. package/docs/type-aliases/TreeInitializer.md +11 -0
  109. package/docs/type-aliases/WellKnownDirectory.md +11 -0
  110. package/docs/type-aliases/WindowWithFsAccess.md +11 -0
  111. package/docs/variables/DEFAULT_DIRECTORY_HANDLE_DB.md +11 -0
  112. package/docs/variables/DEFAULT_DIRECTORY_HANDLE_STORE.md +11 -0
  113. package/etc/ts-web-extras.api.md +124 -1
  114. package/lib/index.d.ts +2 -1
  115. package/lib/index.d.ts.map +1 -1
  116. package/lib/index.js +25 -2
  117. package/lib/index.js.map +1 -1
  118. package/lib/packlets/crypto-utils/browserCryptoProvider.d.ts +77 -0
  119. package/lib/packlets/crypto-utils/browserCryptoProvider.d.ts.map +1 -0
  120. package/lib/packlets/crypto-utils/browserCryptoProvider.js +259 -0
  121. package/lib/packlets/crypto-utils/browserCryptoProvider.js.map +1 -0
  122. package/lib/packlets/crypto-utils/browserHashProvider.d.ts.map +1 -0
  123. package/lib/packlets/crypto-utils/browserHashProvider.js.map +1 -0
  124. package/lib/packlets/{crypto → crypto-utils}/index.d.ts +1 -0
  125. package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
  126. package/lib/packlets/{crypto → crypto-utils}/index.js +1 -0
  127. package/lib/packlets/crypto-utils/index.js.map +1 -0
  128. package/lib/packlets/file-api-types/index.d.ts.map +1 -1
  129. package/lib/packlets/file-api-types/index.js +27 -3
  130. package/lib/packlets/file-api-types/index.js.map +1 -1
  131. package/lib/packlets/file-tree/directoryHandleStore.d.ts +59 -0
  132. package/lib/packlets/file-tree/directoryHandleStore.d.ts.map +1 -0
  133. package/lib/packlets/file-tree/directoryHandleStore.js +128 -0
  134. package/lib/packlets/file-tree/directoryHandleStore.js.map +1 -0
  135. package/lib/packlets/file-tree/fileApiTreeAccessors.d.ts +66 -0
  136. package/lib/packlets/file-tree/fileApiTreeAccessors.d.ts.map +1 -1
  137. package/lib/packlets/file-tree/fileApiTreeAccessors.js +91 -0
  138. package/lib/packlets/file-tree/fileApiTreeAccessors.js.map +1 -1
  139. package/lib/packlets/file-tree/fileSystemAccessTreeAccessors.d.ts +152 -0
  140. package/lib/packlets/file-tree/fileSystemAccessTreeAccessors.d.ts.map +1 -0
  141. package/lib/packlets/file-tree/fileSystemAccessTreeAccessors.js +418 -0
  142. package/lib/packlets/file-tree/fileSystemAccessTreeAccessors.js.map +1 -0
  143. package/lib/packlets/file-tree/httpTreeAccessors.d.ts +88 -0
  144. package/lib/packlets/file-tree/httpTreeAccessors.d.ts.map +1 -0
  145. package/lib/packlets/file-tree/httpTreeAccessors.js +283 -0
  146. package/lib/packlets/file-tree/httpTreeAccessors.js.map +1 -0
  147. package/lib/packlets/file-tree/index.d.ts +4 -0
  148. package/lib/packlets/file-tree/index.d.ts.map +1 -1
  149. package/lib/packlets/file-tree/index.js +4 -0
  150. package/lib/packlets/file-tree/index.js.map +1 -1
  151. package/lib/packlets/file-tree/localStorageTreeAccessors.d.ts +141 -0
  152. package/lib/packlets/file-tree/localStorageTreeAccessors.d.ts.map +1 -0
  153. package/lib/packlets/file-tree/localStorageTreeAccessors.js +363 -0
  154. package/lib/packlets/file-tree/localStorageTreeAccessors.js.map +1 -0
  155. package/lib/test/mocks/idb-keyval.d.ts +6 -0
  156. package/lib/test/mocks/idb-keyval.d.ts.map +1 -0
  157. package/lib/test/mocks/idb-keyval.js +9 -0
  158. package/lib/test/mocks/idb-keyval.js.map +1 -0
  159. package/lib/test/unit/browserHashProvider.test.js +21 -21
  160. package/lib/test/unit/browserHashProvider.test.js.map +1 -1
  161. package/lib/test/unit/directoryHandleStore.test.d.ts +2 -0
  162. package/lib/test/unit/directoryHandleStore.test.d.ts.map +1 -0
  163. package/lib/test/unit/directoryHandleStore.test.js +192 -0
  164. package/lib/test/unit/directoryHandleStore.test.js.map +1 -0
  165. package/lib/test/unit/fileApiTreeAccessors.test.js +51 -0
  166. package/lib/test/unit/fileApiTreeAccessors.test.js.map +1 -1
  167. package/lib/test/unit/fileApiTypes.test.js +30 -0
  168. package/lib/test/unit/fileApiTypes.test.js.map +1 -1
  169. package/lib/test/unit/fileSystemAccessTreeAccessors.test.d.ts +2 -0
  170. package/lib/test/unit/fileSystemAccessTreeAccessors.test.d.ts.map +1 -0
  171. package/lib/test/unit/fileSystemAccessTreeAccessors.test.js +624 -0
  172. package/lib/test/unit/fileSystemAccessTreeAccessors.test.js.map +1 -0
  173. package/lib/test/unit/httpTreeAccessors.test.d.ts +2 -0
  174. package/lib/test/unit/httpTreeAccessors.test.d.ts.map +1 -0
  175. package/lib/test/unit/httpTreeAccessors.test.js +1002 -0
  176. package/lib/test/unit/httpTreeAccessors.test.js.map +1 -0
  177. package/lib/test/unit/localStorageTreeAccessors.test.d.ts +2 -0
  178. package/lib/test/unit/localStorageTreeAccessors.test.d.ts.map +1 -0
  179. package/lib/test/unit/localStorageTreeAccessors.test.js +814 -0
  180. package/lib/test/unit/localStorageTreeAccessors.test.js.map +1 -0
  181. package/lib/test/utils/fileSystemAccessMocks.d.ts +53 -0
  182. package/lib/test/utils/fileSystemAccessMocks.d.ts.map +1 -0
  183. package/lib/test/utils/fileSystemAccessMocks.js +277 -0
  184. package/lib/test/utils/fileSystemAccessMocks.js.map +1 -0
  185. package/package.json +41 -34
  186. package/rush-logs/ts-web-extras.build.cache.log +0 -1
  187. package/rush-logs/ts-web-extras.build.error.log +18 -0
  188. package/rush-logs/ts-web-extras.build.log +58 -25
  189. package/rush-logs/ts-web-extras.test.cache.log +1 -0
  190. package/rush-logs/ts-web-extras.test.error.log +16 -0
  191. package/rush-logs/ts-web-extras.test.log +70 -0
  192. package/src/index.ts +2 -2
  193. package/src/packlets/crypto-utils/browserCryptoProvider.ts +311 -0
  194. package/src/packlets/{crypto → crypto-utils}/index.ts +1 -0
  195. package/src/packlets/file-api-types/index.ts +24 -3
  196. package/src/packlets/file-tree/directoryHandleStore.ts +136 -0
  197. package/src/packlets/file-tree/fileApiTreeAccessors.ts +108 -0
  198. package/src/packlets/file-tree/fileSystemAccessTreeAccessors.ts +519 -0
  199. package/src/packlets/file-tree/httpTreeAccessors.ts +381 -0
  200. package/src/packlets/file-tree/index.ts +4 -0
  201. package/src/packlets/file-tree/localStorageTreeAccessors.ts +430 -0
  202. package/src/test/mocks/idb-keyval.ts +5 -0
  203. package/src/test/unit/browserHashProvider.test.ts +1 -1
  204. package/src/test/unit/directoryHandleStore.test.ts +251 -0
  205. package/src/test/unit/fileApiTreeAccessors.test.ts +69 -0
  206. package/src/test/unit/fileApiTypes.test.ts +36 -0
  207. package/src/test/unit/fileSystemAccessTreeAccessors.test.ts +885 -0
  208. package/src/test/unit/httpTreeAccessors.test.ts +1278 -0
  209. package/src/test/unit/localStorageTreeAccessors.test.ts +1014 -0
  210. package/src/test/utils/fileSystemAccessMocks.ts +353 -0
  211. package/temp/build/typescript/ts_8nwakTlr.json +1 -0
  212. package/temp/coverage/crypto/browserHashProvider.ts.html +1 -1
  213. package/temp/coverage/crypto/index.html +1 -1
  214. package/temp/coverage/crypto-utils/browserCryptoProvider.ts.html +1018 -0
  215. package/temp/coverage/crypto-utils/browserHashProvider.ts.html +304 -0
  216. package/temp/coverage/crypto-utils/index.html +131 -0
  217. package/temp/coverage/file-tree/directoryHandleStore.ts.html +493 -0
  218. package/temp/coverage/file-tree/fileApiTreeAccessors.ts.html +330 -6
  219. package/temp/coverage/file-tree/fileSystemAccessTreeAccessors.ts.html +1642 -0
  220. package/temp/coverage/file-tree/httpTreeAccessors.ts.html +1228 -0
  221. package/temp/coverage/file-tree/index.html +69 -9
  222. package/temp/coverage/file-tree/localStorageTreeAccessors.ts.html +1375 -0
  223. package/temp/coverage/helpers/fileTreeHelpers.ts.html +1 -1
  224. package/temp/coverage/helpers/index.html +1 -1
  225. package/temp/coverage/index.html +13 -13
  226. package/temp/coverage/lcov-report/crypto/browserHashProvider.ts.html +1 -1
  227. package/temp/coverage/lcov-report/crypto/index.html +1 -1
  228. package/temp/coverage/lcov-report/crypto-utils/browserCryptoProvider.ts.html +1018 -0
  229. package/temp/coverage/lcov-report/crypto-utils/browserHashProvider.ts.html +304 -0
  230. package/temp/coverage/lcov-report/crypto-utils/index.html +131 -0
  231. package/temp/coverage/lcov-report/file-tree/directoryHandleStore.ts.html +493 -0
  232. package/temp/coverage/lcov-report/file-tree/fileApiTreeAccessors.ts.html +330 -6
  233. package/temp/coverage/lcov-report/file-tree/fileSystemAccessTreeAccessors.ts.html +1642 -0
  234. package/temp/coverage/lcov-report/file-tree/httpTreeAccessors.ts.html +1228 -0
  235. package/temp/coverage/lcov-report/file-tree/index.html +69 -9
  236. package/temp/coverage/lcov-report/file-tree/localStorageTreeAccessors.ts.html +1375 -0
  237. package/temp/coverage/lcov-report/helpers/fileTreeHelpers.ts.html +1 -1
  238. package/temp/coverage/lcov-report/helpers/index.html +1 -1
  239. package/temp/coverage/lcov-report/index.html +13 -13
  240. package/temp/coverage/lcov-report/url-utils/index.html +1 -1
  241. package/temp/coverage/lcov-report/url-utils/urlParams.ts.html +1 -1
  242. package/temp/coverage/lcov.info +2829 -428
  243. package/temp/coverage/url-utils/index.html +1 -1
  244. package/temp/coverage/url-utils/urlParams.ts.html +1 -1
  245. package/temp/test/jest/haste-map-b931e4e63102f86c5bd4949f7dced44f-9d713eb41149188b4e5c0ae3d86d0a57-2ad8e16b24e391b8cdbe50b55c137169 +0 -0
  246. package/temp/test/jest/perf-cache-b931e4e63102f86c5bd4949f7dced44f-da39a3ee5e6b4b0d3255bfef95601890 +1 -0
  247. package/temp/ts-web-extras.api.json +5282 -1472
  248. package/temp/ts-web-extras.api.md +124 -1
  249. package/dist/packlets/crypto/browserHashProvider.js.map +0 -1
  250. package/dist/packlets/crypto/index.js.map +0 -1
  251. package/docs/index.md +0 -34
  252. package/docs/ts-web-extras.browserhashprovider.hashparts.md +0 -88
  253. package/docs/ts-web-extras.browserhashprovider.hashstring.md +0 -72
  254. package/docs/ts-web-extras.browserhashprovider.md +0 -66
  255. package/docs/ts-web-extras.exportasjson.md +0 -70
  256. package/docs/ts-web-extras.exportusingfilesystemapi.md +0 -104
  257. package/docs/ts-web-extras.extractdirectorypath.md +0 -52
  258. package/docs/ts-web-extras.fileapitreeaccessors.create.md +0 -72
  259. package/docs/ts-web-extras.fileapitreeaccessors.extractfilemetadata.md +0 -54
  260. package/docs/ts-web-extras.fileapitreeaccessors.fromdirectoryupload.md +0 -72
  261. package/docs/ts-web-extras.fileapitreeaccessors.fromfilelist.md +0 -72
  262. package/docs/ts-web-extras.fileapitreeaccessors.getoriginalfile.md +0 -72
  263. package/docs/ts-web-extras.fileapitreeaccessors.md +0 -114
  264. package/docs/ts-web-extras.filepickeraccepttype.accept.md +0 -11
  265. package/docs/ts-web-extras.filepickeraccepttype.description.md +0 -11
  266. package/docs/ts-web-extras.filepickeraccepttype.md +0 -75
  267. package/docs/ts-web-extras.filesystemcreatewritableoptions_2.keepexistingdata.md +0 -11
  268. package/docs/ts-web-extras.filesystemcreatewritableoptions_2.md +0 -58
  269. package/docs/ts-web-extras.filesystemdirectoryhandle_2._symbol.asynciterator_.md +0 -15
  270. package/docs/ts-web-extras.filesystemdirectoryhandle_2.entries.md +0 -15
  271. package/docs/ts-web-extras.filesystemdirectoryhandle_2.getdirectoryhandle.md +0 -66
  272. package/docs/ts-web-extras.filesystemdirectoryhandle_2.getfilehandle.md +0 -66
  273. package/docs/ts-web-extras.filesystemdirectoryhandle_2.keys.md +0 -15
  274. package/docs/ts-web-extras.filesystemdirectoryhandle_2.kind.md +0 -11
  275. package/docs/ts-web-extras.filesystemdirectoryhandle_2.md +0 -146
  276. package/docs/ts-web-extras.filesystemdirectoryhandle_2.removeentry.md +0 -66
  277. package/docs/ts-web-extras.filesystemdirectoryhandle_2.resolve.md +0 -50
  278. package/docs/ts-web-extras.filesystemdirectoryhandle_2.values.md +0 -15
  279. package/docs/ts-web-extras.filesystemfilehandle_2.createwritable.md +0 -52
  280. package/docs/ts-web-extras.filesystemfilehandle_2.getfile.md +0 -15
  281. package/docs/ts-web-extras.filesystemfilehandle_2.kind.md +0 -11
  282. package/docs/ts-web-extras.filesystemfilehandle_2.md +0 -92
  283. package/docs/ts-web-extras.filesystemgetdirectoryoptions_2.create.md +0 -11
  284. package/docs/ts-web-extras.filesystemgetdirectoryoptions_2.md +0 -58
  285. package/docs/ts-web-extras.filesystemgetfileoptions_2.create.md +0 -11
  286. package/docs/ts-web-extras.filesystemgetfileoptions_2.md +0 -58
  287. package/docs/ts-web-extras.filesystemhandle_2.issameentry.md +0 -50
  288. package/docs/ts-web-extras.filesystemhandle_2.kind.md +0 -11
  289. package/docs/ts-web-extras.filesystemhandle_2.md +0 -119
  290. package/docs/ts-web-extras.filesystemhandle_2.name.md +0 -11
  291. package/docs/ts-web-extras.filesystemhandle_2.querypermission.md +0 -52
  292. package/docs/ts-web-extras.filesystemhandle_2.requestpermission.md +0 -52
  293. package/docs/ts-web-extras.filesystemhandlepermissiondescriptor.md +0 -58
  294. package/docs/ts-web-extras.filesystemhandlepermissiondescriptor.mode.md +0 -11
  295. package/docs/ts-web-extras.filesystemremoveoptions_2.md +0 -58
  296. package/docs/ts-web-extras.filesystemremoveoptions_2.recursive.md +0 -11
  297. package/docs/ts-web-extras.filesystemwritablefilestream_2.md +0 -57
  298. package/docs/ts-web-extras.filesystemwritablefilestream_2.seek.md +0 -50
  299. package/docs/ts-web-extras.filesystemwritablefilestream_2.truncate.md +0 -50
  300. package/docs/ts-web-extras.filesystemwritablefilestream_2.write.md +0 -50
  301. package/docs/ts-web-extras.filetreehelpers.defaultfileapitreeinitparams.md +0 -13
  302. package/docs/ts-web-extras.filetreehelpers.extractfilelistmetadata.md +0 -56
  303. package/docs/ts-web-extras.filetreehelpers.extractfilemetadata.md +0 -56
  304. package/docs/ts-web-extras.filetreehelpers.fromdirectoryupload.md +0 -76
  305. package/docs/ts-web-extras.filetreehelpers.fromfilelist.md +0 -76
  306. package/docs/ts-web-extras.filetreehelpers.getoriginalfile.md +0 -72
  307. package/docs/ts-web-extras.filetreehelpers.md +0 -102
  308. package/docs/ts-web-extras.idirectoryhandletreeinitializer.dirhandles.md +0 -11
  309. package/docs/ts-web-extras.idirectoryhandletreeinitializer.md +0 -100
  310. package/docs/ts-web-extras.idirectoryhandletreeinitializer.nonrecursive.md +0 -11
  311. package/docs/ts-web-extras.idirectoryhandletreeinitializer.prefix.md +0 -11
  312. package/docs/ts-web-extras.ifilehandletreeinitializer.filehandles.md +0 -11
  313. package/docs/ts-web-extras.ifilehandletreeinitializer.md +0 -79
  314. package/docs/ts-web-extras.ifilehandletreeinitializer.prefix.md +0 -11
  315. package/docs/ts-web-extras.ifilelisttreeinitializer.filelist.md +0 -11
  316. package/docs/ts-web-extras.ifilelisttreeinitializer.md +0 -58
  317. package/docs/ts-web-extras.ifilemetadata.lastmodified.md +0 -11
  318. package/docs/ts-web-extras.ifilemetadata.md +0 -124
  319. package/docs/ts-web-extras.ifilemetadata.name.md +0 -11
  320. package/docs/ts-web-extras.ifilemetadata.path.md +0 -11
  321. package/docs/ts-web-extras.ifilemetadata.size.md +0 -11
  322. package/docs/ts-web-extras.ifilemetadata.type.md +0 -11
  323. package/docs/ts-web-extras.ifsaccessapis.md +0 -56
  324. package/docs/ts-web-extras.ifsaccessapis.showdirectorypicker.md +0 -52
  325. package/docs/ts-web-extras.ifsaccessapis.showopenfilepicker.md +0 -52
  326. package/docs/ts-web-extras.ifsaccessapis.showsavefilepicker.md +0 -52
  327. package/docs/ts-web-extras.isdirectoryhandle.md +0 -56
  328. package/docs/ts-web-extras.isfilehandle.md +0 -56
  329. package/docs/ts-web-extras.isfilepath.md +0 -52
  330. package/docs/ts-web-extras.iurlconfigoptions.config.md +0 -13
  331. package/docs/ts-web-extras.iurlconfigoptions.configstartdir.md +0 -13
  332. package/docs/ts-web-extras.iurlconfigoptions.contextfilter.md +0 -13
  333. package/docs/ts-web-extras.iurlconfigoptions.input.md +0 -13
  334. package/docs/ts-web-extras.iurlconfigoptions.inputstartdir.md +0 -13
  335. package/docs/ts-web-extras.iurlconfigoptions.interactive.md +0 -13
  336. package/docs/ts-web-extras.iurlconfigoptions.loadzip.md +0 -13
  337. package/docs/ts-web-extras.iurlconfigoptions.maxdistance.md +0 -13
  338. package/docs/ts-web-extras.iurlconfigoptions.md +0 -286
  339. package/docs/ts-web-extras.iurlconfigoptions.qualifierdefaults.md +0 -13
  340. package/docs/ts-web-extras.iurlconfigoptions.reducequalifiers.md +0 -13
  341. package/docs/ts-web-extras.iurlconfigoptions.resourcetypes.md +0 -13
  342. package/docs/ts-web-extras.iurlconfigoptions.zipfile.md +0 -13
  343. package/docs/ts-web-extras.iurlconfigoptions.zippath.md +0 -13
  344. package/docs/ts-web-extras.md +0 -512
  345. package/docs/ts-web-extras.parsecontextfilter.md +0 -52
  346. package/docs/ts-web-extras.parsequalifierdefaults.md +0 -52
  347. package/docs/ts-web-extras.parseresourcetypes.md +0 -52
  348. package/docs/ts-web-extras.parseurlparameters.md +0 -17
  349. package/docs/ts-web-extras.safeshowdirectorypicker.md +0 -72
  350. package/docs/ts-web-extras.safeshowopenfilepicker.md +0 -72
  351. package/docs/ts-web-extras.safeshowsavefilepicker.md +0 -72
  352. package/docs/ts-web-extras.showdirectorypickeroptions.id.md +0 -11
  353. package/docs/ts-web-extras.showdirectorypickeroptions.md +0 -96
  354. package/docs/ts-web-extras.showdirectorypickeroptions.mode.md +0 -11
  355. package/docs/ts-web-extras.showdirectorypickeroptions.startin.md +0 -11
  356. package/docs/ts-web-extras.showopenfilepickeroptions.excludeacceptalloption.md +0 -11
  357. package/docs/ts-web-extras.showopenfilepickeroptions.id.md +0 -11
  358. package/docs/ts-web-extras.showopenfilepickeroptions.md +0 -134
  359. package/docs/ts-web-extras.showopenfilepickeroptions.multiple.md +0 -11
  360. package/docs/ts-web-extras.showopenfilepickeroptions.startin.md +0 -11
  361. package/docs/ts-web-extras.showopenfilepickeroptions.types.md +0 -11
  362. package/docs/ts-web-extras.showsavefilepickeroptions.excludeacceptalloption.md +0 -11
  363. package/docs/ts-web-extras.showsavefilepickeroptions.id.md +0 -11
  364. package/docs/ts-web-extras.showsavefilepickeroptions.md +0 -134
  365. package/docs/ts-web-extras.showsavefilepickeroptions.startin.md +0 -11
  366. package/docs/ts-web-extras.showsavefilepickeroptions.suggestedname.md +0 -11
  367. package/docs/ts-web-extras.showsavefilepickeroptions.types.md +0 -11
  368. package/docs/ts-web-extras.supportsfilesystemaccess.md +0 -56
  369. package/docs/ts-web-extras.treeinitializer.md +0 -15
  370. package/docs/ts-web-extras.wellknowndirectory.md +0 -13
  371. package/docs/ts-web-extras.windowwithfsaccess.md +0 -15
  372. package/lib/packlets/crypto/browserHashProvider.d.ts.map +0 -1
  373. package/lib/packlets/crypto/browserHashProvider.js.map +0 -1
  374. package/lib/packlets/crypto/index.d.ts.map +0 -1
  375. package/lib/packlets/crypto/index.js.map +0 -1
  376. package/temp/build/typescript/ts_vnCx6LlY.json +0 -1
  377. package/temp/test/jest/haste-map-7492f1b44480e0cdd1f220078fb3afd8-c8dd6c3430605adeb2f1cadf4f75e791-8c9336785555d572065b28c111982ba4 +0 -0
  378. package/temp/test/jest/perf-cache-7492f1b44480e0cdd1f220078fb3afd8-da39a3ee5e6b4b0d3255bfef95601890 +0 -1
  379. /package/dist/packlets/{crypto → crypto-utils}/browserHashProvider.js +0 -0
  380. /package/lib/packlets/{crypto → crypto-utils}/browserHashProvider.d.ts +0 -0
  381. /package/lib/packlets/{crypto → crypto-utils}/browserHashProvider.js +0 -0
  382. /package/src/packlets/{crypto → crypto-utils}/browserHashProvider.ts +0 -0
  383. /package/temp/test/jest/{jest-transform-cache-7492f1b44480e0cdd1f220078fb3afd8-79ef2876fae7ca75eedb2aa53dc48338/0e/package_0eb6535f5987849d93ea51ef33a14cf6 → jest-transform-cache-b931e4e63102f86c5bd4949f7dced44f-79ef2876fae7ca75eedb2aa53dc48338/b5/package_b5f57afc9ec2c011239b1608ee5bdfa5} +0 -0
@@ -0,0 +1,624 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ *
5
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ * of this software and associated documentation files (the "Software"), to deal
7
+ * in the Software without restriction, including without limitation the rights
8
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ * copies of the Software, and to permit persons to whom the Software is
10
+ * furnished to do so, subject to the following conditions:
11
+ *
12
+ * The above copyright notice and this permission notice shall be included in all
13
+ * copies or substantial portions of the Software.
14
+ *
15
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ * SOFTWARE.
22
+ */
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ require("@fgv/ts-utils-jest");
25
+ const ts_utils_1 = require("@fgv/ts-utils");
26
+ const file_tree_1 = require("../../packlets/file-tree");
27
+ const file_tree_2 = require("../../packlets/file-tree");
28
+ const ts_json_base_1 = require("@fgv/ts-json-base");
29
+ const fileSystemAccessMocks_1 = require("../utils/fileSystemAccessMocks");
30
+ describe('FileSystemAccessTreeAccessors', () => {
31
+ describe('fromDirectoryHandle', () => {
32
+ test('creates accessors from directory handle with write permission', async () => {
33
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
34
+ 'test.txt': { content: 'test content', type: 'text/plain' },
35
+ 'config.json': { content: '{"name":"test"}', type: 'application/json' }
36
+ });
37
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle);
38
+ expect(result).toSucceedAndSatisfy((accessors) => {
39
+ expect(accessors).toBeInstanceOf(file_tree_1.FileSystemAccessTreeAccessors);
40
+ expect(accessors.getFileContents('/test.txt')).toSucceedWith('test content');
41
+ expect(accessors.getFileContents('/config.json')).toSucceedWith('{"name":"test"}');
42
+ });
43
+ });
44
+ test('creates accessors with nested directory structure', async () => {
45
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
46
+ 'src/index.js': { content: 'console.log("hello");', type: 'text/javascript' },
47
+ 'src/utils/helper.js': { content: 'export const help = () => {};', type: 'text/javascript' },
48
+ 'config/app.json': { content: '{"version":"1.0"}', type: 'application/json' }
49
+ });
50
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle);
51
+ expect(result).toSucceed();
52
+ const accessors = result.orThrow();
53
+ expect(accessors.getFileContents('/src/index.js')).toSucceed();
54
+ expect(accessors.getFileContents('/src/utils/helper.js')).toSucceed();
55
+ expect(accessors.getFileContents('/config/app.json')).toSucceed();
56
+ });
57
+ test('fails when write permission required but not granted', async () => {
58
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { hasWritePermission: false });
59
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
60
+ requireWritePermission: true
61
+ });
62
+ expect(result).toFailWith(/write permission required/i);
63
+ });
64
+ test('succeeds with read-only when write permission not required', async () => {
65
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { hasWritePermission: false });
66
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
67
+ requireWritePermission: false
68
+ });
69
+ expect(result).toSucceedAndSatisfy((accessors) => {
70
+ expect(accessors.getFileContents('/test.txt')).toSucceedWith('test');
71
+ });
72
+ });
73
+ test('applies custom prefix to file paths', async () => {
74
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
75
+ 'test.txt': { content: 'test', type: 'text/plain' }
76
+ });
77
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
78
+ prefix: '/myapp'
79
+ });
80
+ expect(result).toSucceed();
81
+ const accessors = result.orThrow();
82
+ // With prefix, files are accessed with the prefix prepended
83
+ expect(accessors.getFileContents('/myapp/test.txt')).toSucceed();
84
+ });
85
+ });
86
+ describe('fromFileHandle', () => {
87
+ test('creates single-file accessors from a file handle', async () => {
88
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', {
89
+ content: 'metadata:\n name: Test\nitems: {}',
90
+ type: 'text/plain'
91
+ });
92
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle);
93
+ expect(result).toSucceedAndSatisfy((accessors) => {
94
+ expect(accessors).toBeInstanceOf(file_tree_1.FileSystemAccessTreeAccessors);
95
+ expect(accessors.getFileContents('/collection.yaml')).toSucceedWith('metadata:\n name: Test\nitems: {}');
96
+ });
97
+ });
98
+ test('fails when write permission required but not granted', async () => {
99
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', { content: 'content', type: 'text/plain' }, undefined, { hasWritePermission: false });
100
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle, {
101
+ requireWritePermission: true
102
+ });
103
+ expect(result).toFailWith(/write permission required/i);
104
+ });
105
+ test('succeeds read-only when write permission not required', async () => {
106
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', { content: 'content', type: 'text/plain' }, undefined, { hasWritePermission: false });
107
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle, {
108
+ requireWritePermission: false
109
+ });
110
+ expect(result).toSucceedAndSatisfy((accessors) => {
111
+ expect(accessors.getFileContents('/collection.yaml')).toSucceedWith('content');
112
+ });
113
+ });
114
+ test('grants permission via prompt', async () => {
115
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', { content: 'content', type: 'text/plain' }, undefined, { permissionStatus: 'prompt', requestGranted: true });
116
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle);
117
+ expect(result).toSucceed();
118
+ });
119
+ test('fails gracefully when queryPermission throws', async () => {
120
+ const fileHandle = {
121
+ kind: 'file',
122
+ name: 'collection.yaml',
123
+ async getFile() {
124
+ return (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', {
125
+ content: 'content',
126
+ type: 'text/plain'
127
+ }).getFile();
128
+ },
129
+ async createWritable() {
130
+ throw new Error('not writable');
131
+ },
132
+ async queryPermission() {
133
+ throw new Error('Permission API unavailable');
134
+ },
135
+ async requestPermission() {
136
+ throw new Error('Permission API unavailable');
137
+ },
138
+ isSameEntry: async () => false
139
+ };
140
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle, {
141
+ requireWritePermission: true
142
+ });
143
+ expect(result).toFailWith(/write permission required/i);
144
+ });
145
+ test('places file at specified filePath when filePath is provided', async () => {
146
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', {
147
+ content: 'items: {}',
148
+ type: 'text/plain'
149
+ });
150
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle, {
151
+ filePath: '/data/confections/collection.yaml'
152
+ });
153
+ expect(result).toSucceedAndSatisfy((accessors) => {
154
+ expect(accessors.getFileContents('/data/confections/collection.yaml')).toSucceedWith('items: {}');
155
+ });
156
+ });
157
+ test('write-back syncs to original file handle', async () => {
158
+ let writtenContent = '';
159
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', { content: 'original', type: 'text/plain' }, (content) => {
160
+ writtenContent = content;
161
+ });
162
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromFileHandle(fileHandle)).orThrow();
163
+ accessors.saveFileContents('/collection.yaml', 'modified').orThrow();
164
+ expect(accessors.isDirty()).toBe(true);
165
+ const syncResult = await accessors.syncToDisk();
166
+ expect(syncResult).toSucceed();
167
+ expect(accessors.isDirty()).toBe(false);
168
+ expect(writtenContent).toBe('modified');
169
+ });
170
+ });
171
+ describe('FileApiTreeAccessors.createPersistentFromFile', () => {
172
+ test('creates a FileTree from a single file handle', async () => {
173
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', {
174
+ content: 'metadata:\n name: Test\nitems: {}',
175
+ type: 'text/plain'
176
+ });
177
+ const result = await file_tree_2.FileApiTreeAccessors.createPersistentFromFile(fileHandle);
178
+ expect(result).toSucceedAndSatisfy((tree) => {
179
+ expect(tree).toBeInstanceOf(ts_json_base_1.FileTree.FileTree);
180
+ expect(tree.getFile('/collection.yaml')).toSucceed();
181
+ });
182
+ });
183
+ test('fails when file handle cannot be opened', async () => {
184
+ const fileHandle = (0, fileSystemAccessMocks_1.createMockFileHandle)('collection.yaml', {
185
+ content: 'content',
186
+ type: 'text/plain',
187
+ failOnRead: true
188
+ });
189
+ const result = await file_tree_2.FileApiTreeAccessors.createPersistentFromFile(fileHandle);
190
+ expect(result).toFail();
191
+ });
192
+ });
193
+ describe('persistence operations', () => {
194
+ describe('isDirty and getDirtyPaths', () => {
195
+ test('returns false when no changes made', async () => {
196
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
197
+ 'test.txt': { content: 'original', type: 'text/plain' }
198
+ });
199
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle)).orThrow();
200
+ expect(accessors.isDirty()).toBe(false);
201
+ expect(accessors.getDirtyPaths()).toEqual([]);
202
+ });
203
+ test('returns true after saving file contents', async () => {
204
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
205
+ 'test.txt': { content: 'original', type: 'text/plain' }
206
+ });
207
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
208
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
209
+ expect(accessors.isDirty()).toBe(true);
210
+ expect(accessors.getDirtyPaths()).toEqual(['/test.txt']);
211
+ });
212
+ test('tracks multiple dirty files', async () => {
213
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
214
+ 'file1.txt': { content: 'content1', type: 'text/plain' },
215
+ 'file2.txt': { content: 'content2', type: 'text/plain' }
216
+ });
217
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
218
+ accessors.saveFileContents('/file1.txt', 'modified1').orThrow();
219
+ accessors.saveFileContents('/file2.txt', 'modified2').orThrow();
220
+ expect(accessors.isDirty()).toBe(true);
221
+ expect(accessors.getDirtyPaths()).toContain('/file1.txt');
222
+ expect(accessors.getDirtyPaths()).toContain('/file2.txt');
223
+ });
224
+ });
225
+ describe('syncToDisk', () => {
226
+ test('syncs modified files to disk', async () => {
227
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
228
+ 'test.txt': { content: 'original', type: 'text/plain' }
229
+ });
230
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
231
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
232
+ expect(accessors.isDirty()).toBe(true);
233
+ const syncResult = await accessors.syncToDisk();
234
+ expect(syncResult).toSucceed();
235
+ expect(accessors.isDirty()).toBe(false);
236
+ // Verify the file was actually written
237
+ const fileHandle = await dirHandle.getFileHandle('test.txt');
238
+ const file = await fileHandle.getFile();
239
+ const content = await file.text();
240
+ expect(content).toBe('modified');
241
+ });
242
+ test('syncs multiple files in one operation', async () => {
243
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
244
+ 'file1.txt': { content: 'content1', type: 'text/plain' },
245
+ 'file2.txt': { content: 'content2', type: 'text/plain' }
246
+ });
247
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
248
+ accessors.saveFileContents('/file1.txt', 'modified1').orThrow();
249
+ accessors.saveFileContents('/file2.txt', 'modified2').orThrow();
250
+ const syncResult = await accessors.syncToDisk();
251
+ expect(syncResult).toSucceed();
252
+ expect(accessors.isDirty()).toBe(false);
253
+ });
254
+ test('creates new files when syncing', async () => {
255
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
256
+ 'existing.txt': { content: 'existing', type: 'text/plain' }
257
+ });
258
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
259
+ // Add a new file to the in-memory tree
260
+ accessors.saveFileContents('/newfile.txt', 'new content').orThrow();
261
+ const syncResult = await accessors.syncToDisk();
262
+ expect(syncResult).toSucceed();
263
+ // Verify the new file was created
264
+ const fileHandle = await dirHandle.getFileHandle('newfile.txt');
265
+ expect(fileHandle).toBeDefined();
266
+ const file = await fileHandle.getFile();
267
+ const content = await file.text();
268
+ expect(content).toBe('new content');
269
+ });
270
+ test('creates nested directories when syncing new files', async () => {
271
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
272
+ 'existing.txt': { content: 'existing', type: 'text/plain' }
273
+ });
274
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
275
+ accessors.saveFileContents('/src/utils/helper.js', 'export const help = () => {};').orThrow();
276
+ const syncResult = await accessors.syncToDisk();
277
+ expect(syncResult).toSucceed();
278
+ // Verify nested directories were created
279
+ const srcDir = await dirHandle.getDirectoryHandle('src');
280
+ const utilsDir = await srcDir.getDirectoryHandle('utils');
281
+ const fileHandle = await utilsDir.getFileHandle('helper.js');
282
+ expect(fileHandle).toBeDefined();
283
+ });
284
+ test('fails when write permission not granted', async () => {
285
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'original', type: 'text/plain' } }, { hasWritePermission: false });
286
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
287
+ mutable: true,
288
+ requireWritePermission: false
289
+ })).orThrow();
290
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
291
+ const syncResult = await accessors.syncToDisk();
292
+ expect(syncResult).toFailWith(/write permission not granted/i);
293
+ });
294
+ test('clears dirty state after successful sync', async () => {
295
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
296
+ 'file1.txt': { content: 'content1', type: 'text/plain' },
297
+ 'file2.txt': { content: 'content2', type: 'text/plain' }
298
+ });
299
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
300
+ accessors.saveFileContents('/file1.txt', 'modified1').orThrow();
301
+ accessors.saveFileContents('/file2.txt', 'modified2').orThrow();
302
+ expect(accessors.isDirty()).toBe(true);
303
+ const syncResult = await accessors.syncToDisk();
304
+ expect(syncResult).toSucceed();
305
+ expect(accessors.isDirty()).toBe(false);
306
+ expect(accessors.getDirtyPaths()).toEqual([]);
307
+ });
308
+ });
309
+ describe('autoSync mode', () => {
310
+ test('automatically syncs changes when autoSync is enabled', async () => {
311
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
312
+ 'test.txt': { content: 'original', type: 'text/plain' }
313
+ });
314
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
315
+ mutable: true,
316
+ autoSync: true
317
+ })).orThrow();
318
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
319
+ // Give auto-sync a moment to complete (it's fire-and-forget)
320
+ await new Promise((resolve) => setTimeout(resolve, 10));
321
+ // Verify the file was written
322
+ const fileHandle = await dirHandle.getFileHandle('test.txt');
323
+ const file = await fileHandle.getFile();
324
+ const content = await file.text();
325
+ expect(content).toBe('modified');
326
+ });
327
+ test('does not auto-sync when autoSync is disabled', async () => {
328
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
329
+ 'test.txt': { content: 'original', type: 'text/plain' }
330
+ });
331
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
332
+ mutable: true,
333
+ autoSync: false
334
+ })).orThrow();
335
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
336
+ // Verify file is marked dirty but not synced
337
+ expect(accessors.getDirtyPaths()).toContain('/test.txt');
338
+ // In-memory content should be modified
339
+ expect(accessors.getFileContents('/test.txt')).toSucceedWith('modified');
340
+ });
341
+ test('logs when save auto-sync returns a failure Result', async () => {
342
+ const logger = {
343
+ detail: jest.fn(),
344
+ info: jest.fn(),
345
+ warn: jest.fn(),
346
+ error: jest.fn()
347
+ };
348
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
349
+ 'test.txt': { content: 'original', type: 'text/plain' }
350
+ });
351
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
352
+ mutable: true,
353
+ autoSync: true,
354
+ logger
355
+ })).orThrow();
356
+ accessors._syncFile = async () => (0, ts_utils_1.fail)('simulated save sync failure');
357
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
358
+ await new Promise((resolve) => setTimeout(resolve, 0));
359
+ expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('simulated save sync failure'));
360
+ });
361
+ test('logs when delete auto-sync throws unexpectedly', async () => {
362
+ const logger = {
363
+ detail: jest.fn(),
364
+ info: jest.fn(),
365
+ warn: jest.fn(),
366
+ error: jest.fn()
367
+ };
368
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
369
+ 'test.txt': { content: 'original', type: 'text/plain' }
370
+ });
371
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
372
+ mutable: true,
373
+ autoSync: true,
374
+ logger
375
+ })).orThrow();
376
+ accessors._deleteFileFromDisk = async () => {
377
+ throw new Error('simulated delete throw');
378
+ };
379
+ accessors.deleteFile('/test.txt').orThrow();
380
+ await new Promise((resolve) => setTimeout(resolve, 0));
381
+ expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('simulated delete throw'));
382
+ });
383
+ });
384
+ describe('fileIsMutable', () => {
385
+ test('returns persistent detail when write permission granted', async () => {
386
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
387
+ 'test.txt': { content: 'test', type: 'text/plain' }
388
+ });
389
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
390
+ const result = accessors.fileIsMutable('/test.txt');
391
+ expect(result).toSucceedWithDetail(true, 'persistent');
392
+ });
393
+ test('returns transient detail when write permission not granted', async () => {
394
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { hasWritePermission: false });
395
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
396
+ mutable: true,
397
+ requireWritePermission: false
398
+ })).orThrow();
399
+ const result = accessors.fileIsMutable('/test.txt');
400
+ expect(result).toSucceedWithDetail(true, 'transient');
401
+ });
402
+ test('returns not-mutable when mutability disabled', async () => {
403
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
404
+ 'test.txt': { content: 'test', type: 'text/plain' }
405
+ });
406
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: false })).orThrow();
407
+ const result = accessors.fileIsMutable('/test.txt');
408
+ expect(result).toFailWithDetail(/mutability is disabled/i, 'not-mutable');
409
+ });
410
+ });
411
+ describe('integration with FileApiTreeAccessors', () => {
412
+ test('createPersistent creates FileTree with persistent accessors', async () => {
413
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
414
+ 'test.txt': { content: 'test content', type: 'text/plain' }
415
+ });
416
+ const result = await file_tree_2.FileApiTreeAccessors.createPersistent(dirHandle, { mutable: true });
417
+ expect(result).toSucceed();
418
+ const tree = result.orThrow();
419
+ expect(tree).toBeInstanceOf(ts_json_base_1.FileTree.FileTree);
420
+ expect(ts_json_base_1.FileTree.isPersistentAccessors(tree.hal)).toBe(true);
421
+ const file = tree.getFile('/test.txt').orThrow();
422
+ expect(ts_json_base_1.FileTree.isMutableFileItem(file)).toBe(true);
423
+ if (ts_json_base_1.FileTree.isMutableFileItem(file)) {
424
+ expect(file.getIsMutable()).toSucceedWithDetail(true, 'persistent');
425
+ }
426
+ });
427
+ test('createPersistent with autoSync option', async () => {
428
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
429
+ 'test.txt': { content: 'original', type: 'text/plain' }
430
+ });
431
+ const result = await file_tree_2.FileApiTreeAccessors.createPersistent(dirHandle, {
432
+ mutable: true,
433
+ autoSync: true
434
+ });
435
+ expect(result).toSucceedAndSatisfy((tree) => {
436
+ expect(tree).toBeInstanceOf(ts_json_base_1.FileTree.FileTree);
437
+ });
438
+ const tree = result.orThrow();
439
+ const file = tree.getFile('/test.txt').orThrow();
440
+ expect(ts_json_base_1.FileTree.isMutableFileItem(file)).toBe(true);
441
+ if (ts_json_base_1.FileTree.isMutableFileItem(file)) {
442
+ file.setRawContents('modified').orThrow();
443
+ }
444
+ // Give auto-sync time to complete
445
+ await new Promise((resolve) => setTimeout(resolve, 10));
446
+ // Verify file was written
447
+ const fileHandle = await dirHandle.getFileHandle('test.txt');
448
+ const diskFile = await fileHandle.getFile();
449
+ const content = await diskFile.text();
450
+ expect(content).toBe('modified');
451
+ });
452
+ test('createPersistent fails with appropriate error', async () => {
453
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { hasWritePermission: false });
454
+ const result = await file_tree_2.FileApiTreeAccessors.createPersistent(dirHandle, {
455
+ requireWritePermission: true
456
+ });
457
+ expect(result).toFailWith(/write permission required/i);
458
+ });
459
+ });
460
+ describe('error handling', () => {
461
+ test('handles permission query errors gracefully', async () => {
462
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { permissionError: true });
463
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
464
+ requireWritePermission: false
465
+ });
466
+ // Should succeed but without write permission due to error
467
+ expect(result).toSucceed();
468
+ });
469
+ test('handles permission status "prompt" by requesting permission', async () => {
470
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', { 'test.txt': { content: 'test', type: 'text/plain' } }, { permissionStatus: 'prompt', requestGranted: true });
471
+ const result = await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, {
472
+ requireWritePermission: true
473
+ });
474
+ expect(result).toSucceedAndSatisfy((accessors) => {
475
+ expect(accessors).toBeInstanceOf(file_tree_1.FileSystemAccessTreeAccessors);
476
+ });
477
+ });
478
+ test('handles sync failures with aggregated errors', async () => {
479
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
480
+ 'file1.txt': { content: 'content1', type: 'text/plain' },
481
+ 'file2.txt': { content: 'content2', type: 'text/plain' }
482
+ });
483
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
484
+ // Modify files
485
+ accessors.saveFileContents('/file1.txt', 'modified1').orThrow();
486
+ accessors.saveFileContents('/file2.txt', 'modified2').orThrow();
487
+ // Replace the file handles with ones that fail to write
488
+ const handles = accessors._handles;
489
+ for (const [, handle] of handles) {
490
+ handle.createWritable = jest.fn().mockRejectedValue(new Error('Write permission denied'));
491
+ }
492
+ const syncResult = await accessors.syncToDisk();
493
+ expect(syncResult).toFailWith(/Failed to sync 2 file\(s\)/i);
494
+ });
495
+ test('handles getFileContents failure during sync', async () => {
496
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
497
+ 'test.txt': { content: 'original', type: 'text/plain' }
498
+ });
499
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
500
+ // Add a new file
501
+ accessors.saveFileContents('/newfile.txt', 'new content').orThrow();
502
+ // Override getFileContents to fail
503
+ const originalGetFileContents = accessors.getFileContents.bind(accessors);
504
+ accessors.getFileContents = jest.fn((path) => {
505
+ if (path === '/newfile.txt') {
506
+ return { isSuccess: () => false, isFailure: () => true, message: 'File read error' };
507
+ }
508
+ return originalGetFileContents(path);
509
+ });
510
+ const syncResult = await accessors.syncToDisk();
511
+ expect(syncResult).toFailWith(/File read error/i);
512
+ });
513
+ test('handles file write failure during sync', async () => {
514
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
515
+ 'test.txt': { content: 'original', type: 'text/plain' }
516
+ });
517
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
518
+ accessors.saveFileContents('/test.txt', 'modified').orThrow();
519
+ // Replace the handle with one that fails to write
520
+ const handles = accessors._handles;
521
+ const handle = handles.get('/test.txt');
522
+ handle.createWritable = jest.fn().mockRejectedValue(new Error('Disk full'));
523
+ const syncResult = await accessors.syncToDisk();
524
+ expect(syncResult).toFailWith(/Failed to write file.*Disk full/i);
525
+ });
526
+ test('handles invalid file path with no filename', async () => {
527
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
528
+ 'existing.txt': { content: 'existing', type: 'text/plain' }
529
+ });
530
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
531
+ // Override resolveAbsolutePath to return an empty path to trigger the error
532
+ const originalResolveAbsolutePath = accessors.resolveAbsolutePath.bind(accessors);
533
+ accessors.resolveAbsolutePath = jest.fn((path) => {
534
+ if (path === '/badpath') {
535
+ return ''; // This will result in no parts after split
536
+ }
537
+ return originalResolveAbsolutePath(path);
538
+ });
539
+ // Create a new file with the bad path - don't throw if it fails
540
+ const saveResult = accessors.saveFileContents('/badpath', 'content');
541
+ if (saveResult.isFailure()) {
542
+ // saveFileContents itself might reject the path
543
+ expect(saveResult).toFailWith(/invalid file path/i);
544
+ }
545
+ else {
546
+ // If save succeeded, sync should fail
547
+ const syncResult = await accessors.syncToDisk();
548
+ expect(syncResult).toFailWith(/Invalid file path/i);
549
+ }
550
+ });
551
+ test('handles file creation failure in _createAndWriteFile', async () => {
552
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
553
+ 'existing.txt': { content: 'existing', type: 'text/plain' }
554
+ });
555
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
556
+ // Add a new file that will need to be created
557
+ accessors.saveFileContents('/newdir/newfile.txt', 'new content').orThrow();
558
+ // Mock getDirectoryHandle to fail
559
+ const rootDir = accessors._rootDir;
560
+ rootDir.getDirectoryHandle = jest.fn().mockRejectedValue(new Error('Permission denied'));
561
+ const syncResult = await accessors.syncToDisk();
562
+ expect(syncResult).toFailWith(/Failed to create file.*Permission denied/i);
563
+ });
564
+ test('successfully deletes a root-level file from disk via syncToDisk pending deletions', async () => {
565
+ // Covers lines 329-333: the pending deletions loop in syncToDisk
566
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
567
+ 'toDelete.txt': { content: 'will be deleted', type: 'text/plain' },
568
+ 'keep.txt': { content: 'stays', type: 'text/plain' }
569
+ });
570
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
571
+ // Delete the file from in-memory tree
572
+ accessors.deleteFile('/toDelete.txt').orThrow();
573
+ expect(accessors.isDirty()).toBe(true);
574
+ expect(accessors.getDirtyPaths()).toContain('/toDelete.txt');
575
+ // syncToDisk should process the pending deletion
576
+ const syncResult = await accessors.syncToDisk();
577
+ expect(syncResult).toSucceed();
578
+ expect(accessors.isDirty()).toBe(false);
579
+ });
580
+ test('successfully deletes a nested file from disk via syncToDisk (navigates parent dirs)', async () => {
581
+ // Covers lines 468-469: getDirectoryHandle navigation loop in _deleteFileFromDisk
582
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
583
+ 'src/utils/helper.txt': { content: 'to be deleted', type: 'text/plain' },
584
+ 'src/index.txt': { content: 'stays', type: 'text/plain' }
585
+ });
586
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
587
+ // Delete the nested file
588
+ accessors.deleteFile('/src/utils/helper.txt').orThrow();
589
+ expect(accessors.isDirty()).toBe(true);
590
+ const syncResult = await accessors.syncToDisk();
591
+ expect(syncResult).toSucceed();
592
+ expect(accessors.isDirty()).toBe(false);
593
+ });
594
+ test('aggregates deletion errors when _deleteFileFromDisk fails during syncToDisk', async () => {
595
+ // Covers lines 330-332: the error aggregation when _deleteFileFromDisk returns failure
596
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
597
+ 'toDelete.txt': { content: 'content', type: 'text/plain' }
598
+ });
599
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
600
+ accessors.deleteFile('/toDelete.txt').orThrow();
601
+ // Make removeEntry fail to trigger the error path in _deleteFileFromDisk
602
+ const rootDir = accessors._rootDir;
603
+ rootDir.removeEntry = jest.fn().mockRejectedValue(new Error('Cannot delete: file locked'));
604
+ const syncResult = await accessors.syncToDisk();
605
+ expect(syncResult).toFailWith(/Failed to sync 1 file/i);
606
+ expect(syncResult).toFailWith(/delete.*toDelete\.txt/i);
607
+ });
608
+ test('catch block in _deleteFileFromDisk when removeEntry throws', async () => {
609
+ // Covers lines 473-476: the catch block in _deleteFileFromDisk
610
+ const dirHandle = (0, fileSystemAccessMocks_1.createMockDirectoryHandle)('/', {
611
+ 'target.txt': { content: 'content', type: 'text/plain' }
612
+ });
613
+ const accessors = (await file_tree_1.FileSystemAccessTreeAccessors.fromDirectoryHandle(dirHandle, { mutable: true })).orThrow();
614
+ accessors.deleteFile('/target.txt').orThrow();
615
+ // Directly make _rootDir.removeEntry throw a non-Error value to test the catch branch
616
+ const rootDir = accessors._rootDir;
617
+ rootDir.removeEntry = jest.fn().mockRejectedValue('non-error string');
618
+ const syncResult = await accessors.syncToDisk();
619
+ expect(syncResult).toFailWith(/Failed to delete file.*non-error string/i);
620
+ });
621
+ });
622
+ });
623
+ });
624
+ //# sourceMappingURL=fileSystemAccessTreeAccessors.test.js.map