sliftutils 1.7.6 → 1.7.7

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/index.d.ts CHANGED
@@ -2423,6 +2423,7 @@ declare module "sliftutils/storage/fileSystemPointer" {
2423
2423
  handle: FileSystemFileHandle | FileSystemDirectoryHandle;
2424
2424
  }): Promise<FileSystemPointer>;
2425
2425
  export declare function deleteFileSystemPointer(pointer: FileSystemPointer): Promise<void>;
2426
+ export declare function findGrantedPointerHandle(mode: "read" | "readwrite"): Promise<FileSystemDirectoryHandle | undefined>;
2426
2427
  export declare function getFileSystemPointer(config: {
2427
2428
  pointer: FileSystemPointer;
2428
2429
  }): Promise<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -1,5 +1,5 @@
1
1
  import preact from "preact";
2
- import { getFileSystemPointer, storeFileSystemPointer } from "./fileSystemPointer";
2
+ import { findGrantedPointerHandle, getFileSystemPointer, storeFileSystemPointer } from "./fileSystemPointer";
3
3
  import { observable } from "mobx";
4
4
  import { observer } from "../render-utils/observer";
5
5
  import { cache, lazy } from "socket-function/src/caching";
@@ -36,6 +36,9 @@ declare global {
36
36
  // DO NOT enable this is isNode
37
37
  const USE_INDEXED_DB = false;
38
38
 
39
+ // How often a worker rechecks the pointer IndexedDB for a granted handle when none is available yet.
40
+ const WORKER_POLL_INTERVAL_MS = 60 * 1000;
41
+
39
42
  // These mirror the subset of the native FileSystemFileHandle / FileSystemDirectoryHandle API we use, so
40
43
  // the native browser handles, the Node handles, and the remote handles are all interchangeable — and
41
44
  // code written against the native handle (e.g. a recursive walk over `handle.entries()`) works on any of
@@ -460,6 +463,20 @@ export const getDirectoryHandle = lazy(async function getDirectoryHandle(): Prom
460
463
  if (storageRootOverride) {
461
464
  return storageRootOverride as unknown as DirectoryWrapper;
462
465
  }
466
+ // A worker can't show the picker (no DOM / no user activation) and can't read localStorage, but
467
+ // it CAN open the pointer IndexedDB the main-thread picker persists to. Poll it for a handle
468
+ // whose permission is already granted; the owning window may also postMessage a handle in
469
+ // mid-poll (checked each iteration). This has to come before the isNode branch: typesafecss's
470
+ // isNode() returns true in workers (window is undefined), so a worker would otherwise fall into
471
+ // the Node branch and try to use fs/path.
472
+ if ("WorkerGlobalScope" in globalThis) {
473
+ while (true) {
474
+ if (storageRootOverride) return storageRootOverride as unknown as DirectoryWrapper;
475
+ let handle = await findGrantedPointerHandle("readwrite");
476
+ if (handle) return handle as unknown as DirectoryWrapper;
477
+ await new Promise(resolve => setTimeout(resolve, WORKER_POLL_INTERVAL_MS));
478
+ }
479
+ }
463
480
  if (isNode()) {
464
481
  return new NodeJSDirectoryHandleWrapper(path.resolve("./data/"));
465
482
  }
@@ -4,6 +4,7 @@ export declare function storeFileSystemPointer(config: {
4
4
  handle: FileSystemFileHandle | FileSystemDirectoryHandle;
5
5
  }): Promise<FileSystemPointer>;
6
6
  export declare function deleteFileSystemPointer(pointer: FileSystemPointer): Promise<void>;
7
+ export declare function findGrantedPointerHandle(mode: "read" | "readwrite"): Promise<FileSystemDirectoryHandle | undefined>;
7
8
  export declare function getFileSystemPointer(config: {
8
9
  pointer: FileSystemPointer;
9
10
  }): Promise<{
@@ -55,6 +55,36 @@ export async function deleteFileSystemPointer(pointer: FileSystemPointer) {
55
55
  });
56
56
  }
57
57
 
58
+ // Enumerates every stored pointer handle whose permission is already granted for `mode`, newest first
59
+ // (pointer keys start with Date.now(), which sorts lexicographically). Callable from a Web Worker —
60
+ // unlike getFileSystemPointer's onUserActivation flow, this only uses queryPermission, which does
61
+ // NOT require user activation.
62
+ export async function findGrantedPointerHandle(mode: "read" | "readwrite"): Promise<FileSystemDirectoryHandle | undefined> {
63
+ let database = await db();
64
+ if (!database) return undefined;
65
+ let store = database.transaction(objectStoreName, "readonly").objectStore(objectStoreName);
66
+ let keysReq = store.getAllKeys();
67
+ let valuesReq = store.getAll();
68
+ await new Promise((resolve, reject) => {
69
+ keysReq.addEventListener("success", resolve);
70
+ keysReq.addEventListener("error", reject);
71
+ });
72
+ await new Promise((resolve, reject) => {
73
+ valuesReq.addEventListener("success", resolve);
74
+ valuesReq.addEventListener("error", reject);
75
+ });
76
+ let keys = keysReq.result as string[];
77
+ let values = valuesReq.result as (FileSystemFileHandle | FileSystemDirectoryHandle)[];
78
+ let entries = keys.map((key, i) => ({ key, value: values[i], time: +key.split("_")[0] || 0 }))
79
+ .sort((a, b) => b.time - a.time);
80
+ for (let entry of entries) {
81
+ if (entry.value.kind !== "directory") continue;
82
+ let state = await (entry.value as any).queryPermission({ mode });
83
+ if (state === "granted") return entry.value;
84
+ }
85
+ return undefined;
86
+ }
87
+
58
88
  export async function getFileSystemPointer(config: {
59
89
  pointer: FileSystemPointer;
60
90
  }): Promise<{