@banou/ponyfill 0.0.5 → 0.0.6
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/build/file-system.cjs +28 -0
- package/build/file-system.d.ts +46 -0
- package/build/file-system.js +27 -0
- package/build/index.cjs +4 -0
- package/build/index.d.ts +11 -3
- package/build/index.js +3 -1
- package/build/permissions.cjs +23 -0
- package/build/permissions.d.ts +54 -0
- package/build/permissions.js +22 -0
- package/build/storage.cjs +31 -1
- package/build/storage.d.ts +23 -0
- package/build/storage.js +31 -1
- package/package.json +11 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/file-system.ts
|
|
3
|
+
/**
|
|
4
|
+
* Whether this document is framed by another origin.
|
|
5
|
+
*
|
|
6
|
+
* Chromium exposes the picker either way and refuses it at call time, so a property probe says
|
|
7
|
+
* nothing. A same origin ancestor answers `location.origin`; a cross origin one throws, and so does
|
|
8
|
+
* an opaque origin, which is the case a sandboxed frame presents.
|
|
9
|
+
*/
|
|
10
|
+
var framedByAnotherOrigin = () => {
|
|
11
|
+
if (typeof window === "undefined") return false;
|
|
12
|
+
const top = window.top;
|
|
13
|
+
if (!top || top === window.self) return false;
|
|
14
|
+
try {
|
|
15
|
+
top.location.origin;
|
|
16
|
+
return false;
|
|
17
|
+
} catch {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var showSaveFilePicker = async (options) => {
|
|
22
|
+
const picker = globalThis.showSaveFilePicker;
|
|
23
|
+
if (!picker) throw new DOMException("this browser has no file save picker", "NotAllowedError");
|
|
24
|
+
if (framedByAnotherOrigin()) throw new DOMException("a cross origin frame cannot show a file save picker", "NotAllowedError");
|
|
25
|
+
return picker(options);
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
exports.showSaveFilePicker = showSaveFilePicker;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The File System Access pickers, with the same names and one behaviour picked.
|
|
3
|
+
*
|
|
4
|
+
* ## The divergence
|
|
5
|
+
*
|
|
6
|
+
* `showSaveFilePicker` exists on the window in Chromium whether or not it can actually be used, and
|
|
7
|
+
* refuses at CALL time in two cases that a property probe cannot see:
|
|
8
|
+
*
|
|
9
|
+
* - a cross origin ancestor frame, which fails with "Cross origin sub frames aren't allowed to show
|
|
10
|
+
* a file picker"
|
|
11
|
+
* - no transient activation, which fails with "Must be handling a user gesture to show a file picker"
|
|
12
|
+
*
|
|
13
|
+
* Firefox does not expose it at all, so `'showSaveFilePicker' in window` answers a different question
|
|
14
|
+
* on each engine: on one it means "might work", on the other "will never work".
|
|
15
|
+
*
|
|
16
|
+
* ## The pick
|
|
17
|
+
*
|
|
18
|
+
* REJECT BEFORE THE ACTIVATION IS SPENT, with a rejection a caller can tell apart from a cancel.
|
|
19
|
+
*
|
|
20
|
+
* That ordering is the whole of it. A caller with a fallback chain has one transient activation to
|
|
21
|
+
* spend, and a picker that rejects at call time has already consumed part of it, so the fallback the
|
|
22
|
+
* caller reaches for next can fail too. Rejecting before the call keeps the gesture intact.
|
|
23
|
+
*
|
|
24
|
+
* `NotAllowedError` for both refusals rather than `AbortError`, because `AbortError` is what the
|
|
25
|
+
* platform throws when the PERSON cancels, and a caller that cannot tell those apart shows an error
|
|
26
|
+
* for something the person chose to do. Ripple's `isSaveCancelled` matches on `AbortError`, so this
|
|
27
|
+
* distinction is load bearing rather than tidy.
|
|
28
|
+
*
|
|
29
|
+
* WHAT IS NOT ABSORBED, deliberately. The fallback chain itself stays with the caller: an anchor
|
|
30
|
+
* download, a service worker sink, or holding bytes in memory are not platform names and choosing
|
|
31
|
+
* between them is a product decision about what a page does when it cannot save. A ponyfill that
|
|
32
|
+
* silently downloaded something instead of opening a picker would be lying about which API it is.
|
|
33
|
+
*/
|
|
34
|
+
type SaveFilePickerOptions = {
|
|
35
|
+
suggestedName?: string;
|
|
36
|
+
types?: {
|
|
37
|
+
description?: string;
|
|
38
|
+
accept: Record<string, string[]>;
|
|
39
|
+
}[];
|
|
40
|
+
excludeAcceptAllOption?: boolean;
|
|
41
|
+
id?: string;
|
|
42
|
+
startIn?: unknown;
|
|
43
|
+
};
|
|
44
|
+
type SaveFilePicker = (options?: SaveFilePickerOptions) => Promise<FileSystemFileHandle>;
|
|
45
|
+
export declare const showSaveFilePicker: SaveFilePicker;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/file-system.ts
|
|
2
|
+
/**
|
|
3
|
+
* Whether this document is framed by another origin.
|
|
4
|
+
*
|
|
5
|
+
* Chromium exposes the picker either way and refuses it at call time, so a property probe says
|
|
6
|
+
* nothing. A same origin ancestor answers `location.origin`; a cross origin one throws, and so does
|
|
7
|
+
* an opaque origin, which is the case a sandboxed frame presents.
|
|
8
|
+
*/
|
|
9
|
+
var framedByAnotherOrigin = () => {
|
|
10
|
+
if (typeof window === "undefined") return false;
|
|
11
|
+
const top = window.top;
|
|
12
|
+
if (!top || top === window.self) return false;
|
|
13
|
+
try {
|
|
14
|
+
top.location.origin;
|
|
15
|
+
return false;
|
|
16
|
+
} catch {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
var showSaveFilePicker = async (options) => {
|
|
21
|
+
const picker = globalThis.showSaveFilePicker;
|
|
22
|
+
if (!picker) throw new DOMException("this browser has no file save picker", "NotAllowedError");
|
|
23
|
+
if (framedByAnotherOrigin()) throw new DOMException("a cross origin frame cannot show a file save picker", "NotAllowedError");
|
|
24
|
+
return picker(options);
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { showSaveFilePicker };
|
package/build/index.cjs
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_storage = require("./storage.cjs");
|
|
3
|
+
const require_permissions = require("./permissions.cjs");
|
|
4
|
+
const require_file_system = require("./file-system.cjs");
|
|
5
|
+
exports.permissions = require_permissions.permissions;
|
|
6
|
+
exports.showSaveFilePicker = require_file_system.showSaveFilePicker;
|
|
3
7
|
exports.storage = require_storage.storage;
|
package/build/index.d.ts
CHANGED
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
* anything: it is a utility library wearing the word. So the surface here is exactly the platform's
|
|
14
14
|
* surface, and a helper only becomes public if the platform has one by that name.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* WHERE ENGINES DIVERGE, ONE BEHAVIOUR IS PICKED and made consistent, so application code never
|
|
17
|
+
* branches. That is the point of the package rather than a liberty it takes. Each pick is argued
|
|
18
|
+
* where it lives, and each says what it gives up.
|
|
19
|
+
*
|
|
20
|
+
* Everything else stays behind the surface. The walk that corrects `estimate().usage`, the bounds on
|
|
21
|
+
* that walk, and the reconciliation between measured and reported figures are all internal, because
|
|
18
22
|
* `navigator.storage` has no such members and neither should this.
|
|
19
23
|
*
|
|
20
24
|
* ## What belongs in here
|
|
@@ -25,7 +29,11 @@
|
|
|
25
29
|
* next person is not made to repeat it.
|
|
26
30
|
*
|
|
27
31
|
* Every module here states what was measured, on what, and when. A workaround with no measurement
|
|
28
|
-
* behind it is a guess that outlives the bug it was written for
|
|
32
|
+
* behind it is a guess that outlives the bug it was written for, and where a measurement is missing
|
|
33
|
+
* the module says so rather than implying one.
|
|
29
34
|
*/
|
|
30
35
|
export { storage } from './storage';
|
|
36
|
+
export { permissions } from './permissions';
|
|
37
|
+
export { showSaveFilePicker } from './file-system';
|
|
31
38
|
export type { StorageEstimate } from './storage';
|
|
39
|
+
export type { PermissionStatus } from './permissions';
|
package/build/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region src/permissions.ts
|
|
3
|
+
var isState = (value) => value === "granted" || value === "denied" || value === "prompt";
|
|
4
|
+
var permissions = {
|
|
5
|
+
/**
|
|
6
|
+
* Same name and same signature, answering `'prompt'` wherever the platform would not answer.
|
|
7
|
+
*
|
|
8
|
+
* The `await` is INSIDE the `try` on purpose, and it is the whole reason this is a wrapper rather
|
|
9
|
+
* than a one-line `.catch`. `query` can throw synchronously for a name the engine does not know
|
|
10
|
+
* and reject asynchronously for one it does, and a `.catch` on the returned promise sees only the
|
|
11
|
+
* second. Getting that wrong turns a missing permission name into an uncaught exception at
|
|
12
|
+
* whatever moment the feature is first used.
|
|
13
|
+
*/
|
|
14
|
+
query: async (descriptor) => {
|
|
15
|
+
try {
|
|
16
|
+
const status = await (globalThis.navigator?.permissions)?.query(descriptor);
|
|
17
|
+
return { state: isState(status?.state) ? status.state : "prompt" };
|
|
18
|
+
} catch {
|
|
19
|
+
return { state: "prompt" };
|
|
20
|
+
}
|
|
21
|
+
} };
|
|
22
|
+
//#endregion
|
|
23
|
+
exports.permissions = permissions;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `navigator.permissions`, with the same member name and one behaviour picked.
|
|
3
|
+
*
|
|
4
|
+
* ## The divergence
|
|
5
|
+
*
|
|
6
|
+
* `query()` has FOUR ways of not answering, and an app that wants a permission state has to handle
|
|
7
|
+
* all of them separately even though every one means the same thing to it:
|
|
8
|
+
*
|
|
9
|
+
* 1. the engine has no Permissions API at all, so `navigator.permissions` is undefined
|
|
10
|
+
* 2. the engine has it but rejects the name, because `PermissionName` is a per-engine list
|
|
11
|
+
* 3. the engine has it and THROWS SYNCHRONOUSLY for a name it does not know, which a `.catch` on
|
|
12
|
+
* the returned promise does not see at all
|
|
13
|
+
* 4. it answers, with a real `PermissionState`
|
|
14
|
+
*
|
|
15
|
+
* Only the fourth is the platform's documented behaviour. The other three are the same question
|
|
16
|
+
* coming back unanswered in three different shapes, and the shapes are not interchangeable: case 3
|
|
17
|
+
* needs a `try` around the CALL rather than a `.catch` on its result, which is the kind of detail
|
|
18
|
+
* that is correct in the one file someone thought about it in and wrong everywhere else.
|
|
19
|
+
*
|
|
20
|
+
* ## The pick
|
|
21
|
+
*
|
|
22
|
+
* All three collapse to `'prompt'`.
|
|
23
|
+
*
|
|
24
|
+
* `'prompt'` rather than `'denied'` because an engine that cannot be asked has not refused anything,
|
|
25
|
+
* and treating silence as refusal costs the person a control that might have worked. `'prompt'` is
|
|
26
|
+
* also the honest description of what happens next: press the button and the browser will decide.
|
|
27
|
+
*
|
|
28
|
+
* WHAT THAT LOSES, stated rather than buried: after the collapse, `'prompt'` means either "the
|
|
29
|
+
* browser will ask" or "the browser cannot be asked at all", and nothing distinguishes them any
|
|
30
|
+
* more. That is deliberate. An app that genuinely needs to tell those apart is asking a question the
|
|
31
|
+
* Permissions API does not answer either, since case 2 and case 4 are indistinguishable from a
|
|
32
|
+
* rejected promise.
|
|
33
|
+
*
|
|
34
|
+
* NOT MEASURED, and this is the one entry in the package without a dated number behind it. What is
|
|
35
|
+
* recorded upstream is the SHAPE of the failure rather than an engine and a version: a query for a
|
|
36
|
+
* name an engine does not implement rejects, or throws outright. Both are handled here, and the
|
|
37
|
+
* absence of a measurement is why this file says so rather than inventing one.
|
|
38
|
+
*/
|
|
39
|
+
/** The platform's own `PermissionStatus`, narrowed to the member anything actually reads. */
|
|
40
|
+
export type PermissionStatus = {
|
|
41
|
+
state: PermissionState;
|
|
42
|
+
};
|
|
43
|
+
export declare const permissions: {
|
|
44
|
+
/**
|
|
45
|
+
* Same name and same signature, answering `'prompt'` wherever the platform would not answer.
|
|
46
|
+
*
|
|
47
|
+
* The `await` is INSIDE the `try` on purpose, and it is the whole reason this is a wrapper rather
|
|
48
|
+
* than a one-line `.catch`. `query` can throw synchronously for a name the engine does not know
|
|
49
|
+
* and reject asynchronously for one it does, and a `.catch` on the returned promise sees only the
|
|
50
|
+
* second. Getting that wrong turns a missing permission name into an uncaught exception at
|
|
51
|
+
* whatever moment the feature is first used.
|
|
52
|
+
*/
|
|
53
|
+
query: (descriptor: PermissionDescriptor) => Promise<PermissionStatus>;
|
|
54
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/permissions.ts
|
|
2
|
+
var isState = (value) => value === "granted" || value === "denied" || value === "prompt";
|
|
3
|
+
var permissions = {
|
|
4
|
+
/**
|
|
5
|
+
* Same name and same signature, answering `'prompt'` wherever the platform would not answer.
|
|
6
|
+
*
|
|
7
|
+
* The `await` is INSIDE the `try` on purpose, and it is the whole reason this is a wrapper rather
|
|
8
|
+
* than a one-line `.catch`. `query` can throw synchronously for a name the engine does not know
|
|
9
|
+
* and reject asynchronously for one it does, and a `.catch` on the returned promise sees only the
|
|
10
|
+
* second. Getting that wrong turns a missing permission name into an uncaught exception at
|
|
11
|
+
* whatever moment the feature is first used.
|
|
12
|
+
*/
|
|
13
|
+
query: async (descriptor) => {
|
|
14
|
+
try {
|
|
15
|
+
const status = await (globalThis.navigator?.permissions)?.query(descriptor);
|
|
16
|
+
return { state: isState(status?.state) ? status.state : "prompt" };
|
|
17
|
+
} catch {
|
|
18
|
+
return { state: "prompt" };
|
|
19
|
+
}
|
|
20
|
+
} };
|
|
21
|
+
//#endregion
|
|
22
|
+
export { permissions };
|
package/build/storage.cjs
CHANGED
|
@@ -119,7 +119,37 @@ var storage = {
|
|
|
119
119
|
quota: ceiling(estimate.quota, usage)
|
|
120
120
|
};
|
|
121
121
|
},
|
|
122
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Same name and signature, resolving what the origin IS rather than what the call said it did.
|
|
124
|
+
*
|
|
125
|
+
* TWO PICKS, both inside one call.
|
|
126
|
+
*
|
|
127
|
+
* FIRST, the answer. `persist()` resolves its own claim, and `persisted()` resolves the state the
|
|
128
|
+
* app then lives in. Those are different questions and only the second decides anything: whether
|
|
129
|
+
* the origin can still be evicted. A call can resolve false on an engine where the origin is
|
|
130
|
+
* already persistent, and an app reading the first answer then offers a button that has nothing
|
|
131
|
+
* left to do. So this resolves `persisted()`, falling back to the call's own answer only where the
|
|
132
|
+
* platform will not state the state.
|
|
133
|
+
*
|
|
134
|
+
* SECOND, the ceiling. A granted persist can move the quota by orders of magnitude: measured
|
|
135
|
+
* 2026-09-01 on Firefox, granting the "Store data in persistent storage" doorhanger moved the
|
|
136
|
+
* reported quota from 12 GB to 3.97 TB on an 8.03 TB device, about 330 times. `estimate()` latches
|
|
137
|
+
* the narrowest quota it has seen, which is right while nothing changes the ceiling and wrong the
|
|
138
|
+
* moment something does, so a successful grant forgets it. The next `estimate()` re-anchors on
|
|
139
|
+
* whatever the platform now says.
|
|
140
|
+
*
|
|
141
|
+
* Chromium, measured 2026-08-30 on Chrome 151, refuses this on every attempt with no prompt shown
|
|
142
|
+
* at any point, and the quota stays flat. That is not a failure to handle: it is the engine
|
|
143
|
+
* answering, and `persisted()` reports the same false afterwards.
|
|
144
|
+
*/
|
|
145
|
+
persist: async () => {
|
|
146
|
+
const native = globalThis.navigator?.storage;
|
|
147
|
+
if (!native?.persist) return false;
|
|
148
|
+
const answered = await native.persist().catch(() => false);
|
|
149
|
+
const persisted = await native.persisted?.().catch(() => null) ?? null ?? answered;
|
|
150
|
+
if (persisted) narrowest = void 0;
|
|
151
|
+
return persisted;
|
|
152
|
+
},
|
|
123
153
|
persisted: () => globalThis.navigator?.storage?.persisted?.() ?? Promise.resolve(false),
|
|
124
154
|
getDirectory: () => {
|
|
125
155
|
const native = globalThis.navigator?.storage;
|
package/build/storage.d.ts
CHANGED
|
@@ -81,6 +81,29 @@ export declare const storage: {
|
|
|
81
81
|
* downgrade, because the platform's figure is a floor rather than a guess.
|
|
82
82
|
*/
|
|
83
83
|
estimate: () => Promise<StorageEstimate>;
|
|
84
|
+
/**
|
|
85
|
+
* Same name and signature, resolving what the origin IS rather than what the call said it did.
|
|
86
|
+
*
|
|
87
|
+
* TWO PICKS, both inside one call.
|
|
88
|
+
*
|
|
89
|
+
* FIRST, the answer. `persist()` resolves its own claim, and `persisted()` resolves the state the
|
|
90
|
+
* app then lives in. Those are different questions and only the second decides anything: whether
|
|
91
|
+
* the origin can still be evicted. A call can resolve false on an engine where the origin is
|
|
92
|
+
* already persistent, and an app reading the first answer then offers a button that has nothing
|
|
93
|
+
* left to do. So this resolves `persisted()`, falling back to the call's own answer only where the
|
|
94
|
+
* platform will not state the state.
|
|
95
|
+
*
|
|
96
|
+
* SECOND, the ceiling. A granted persist can move the quota by orders of magnitude: measured
|
|
97
|
+
* 2026-09-01 on Firefox, granting the "Store data in persistent storage" doorhanger moved the
|
|
98
|
+
* reported quota from 12 GB to 3.97 TB on an 8.03 TB device, about 330 times. `estimate()` latches
|
|
99
|
+
* the narrowest quota it has seen, which is right while nothing changes the ceiling and wrong the
|
|
100
|
+
* moment something does, so a successful grant forgets it. The next `estimate()` re-anchors on
|
|
101
|
+
* whatever the platform now says.
|
|
102
|
+
*
|
|
103
|
+
* Chromium, measured 2026-08-30 on Chrome 151, refuses this on every attempt with no prompt shown
|
|
104
|
+
* at any point, and the quota stays flat. That is not a failure to handle: it is the engine
|
|
105
|
+
* answering, and `persisted()` reports the same false afterwards.
|
|
106
|
+
*/
|
|
84
107
|
persist: () => Promise<boolean>;
|
|
85
108
|
persisted: () => Promise<boolean>;
|
|
86
109
|
getDirectory: () => Promise<FileSystemDirectoryHandle>;
|
package/build/storage.js
CHANGED
|
@@ -118,7 +118,37 @@ var storage = {
|
|
|
118
118
|
quota: ceiling(estimate.quota, usage)
|
|
119
119
|
};
|
|
120
120
|
},
|
|
121
|
-
|
|
121
|
+
/**
|
|
122
|
+
* Same name and signature, resolving what the origin IS rather than what the call said it did.
|
|
123
|
+
*
|
|
124
|
+
* TWO PICKS, both inside one call.
|
|
125
|
+
*
|
|
126
|
+
* FIRST, the answer. `persist()` resolves its own claim, and `persisted()` resolves the state the
|
|
127
|
+
* app then lives in. Those are different questions and only the second decides anything: whether
|
|
128
|
+
* the origin can still be evicted. A call can resolve false on an engine where the origin is
|
|
129
|
+
* already persistent, and an app reading the first answer then offers a button that has nothing
|
|
130
|
+
* left to do. So this resolves `persisted()`, falling back to the call's own answer only where the
|
|
131
|
+
* platform will not state the state.
|
|
132
|
+
*
|
|
133
|
+
* SECOND, the ceiling. A granted persist can move the quota by orders of magnitude: measured
|
|
134
|
+
* 2026-09-01 on Firefox, granting the "Store data in persistent storage" doorhanger moved the
|
|
135
|
+
* reported quota from 12 GB to 3.97 TB on an 8.03 TB device, about 330 times. `estimate()` latches
|
|
136
|
+
* the narrowest quota it has seen, which is right while nothing changes the ceiling and wrong the
|
|
137
|
+
* moment something does, so a successful grant forgets it. The next `estimate()` re-anchors on
|
|
138
|
+
* whatever the platform now says.
|
|
139
|
+
*
|
|
140
|
+
* Chromium, measured 2026-08-30 on Chrome 151, refuses this on every attempt with no prompt shown
|
|
141
|
+
* at any point, and the quota stays flat. That is not a failure to handle: it is the engine
|
|
142
|
+
* answering, and `persisted()` reports the same false afterwards.
|
|
143
|
+
*/
|
|
144
|
+
persist: async () => {
|
|
145
|
+
const native = globalThis.navigator?.storage;
|
|
146
|
+
if (!native?.persist) return false;
|
|
147
|
+
const answered = await native.persist().catch(() => false);
|
|
148
|
+
const persisted = await native.persisted?.().catch(() => null) ?? null ?? answered;
|
|
149
|
+
if (persisted) narrowest = void 0;
|
|
150
|
+
return persisted;
|
|
151
|
+
},
|
|
122
152
|
persisted: () => globalThis.navigator?.storage?.persisted?.() ?? Promise.resolve(false),
|
|
123
153
|
getDirectory: () => {
|
|
124
154
|
const native = globalThis.navigator?.storage;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@banou/ponyfill",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "build/index.cjs",
|
|
6
6
|
"module": "build/index.js",
|
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
"types": "./build/storage.d.ts",
|
|
16
16
|
"import": "./build/storage.js",
|
|
17
17
|
"require": "./build/storage.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./permissions": {
|
|
20
|
+
"types": "./build/permissions.d.ts",
|
|
21
|
+
"import": "./build/permissions.js",
|
|
22
|
+
"require": "./build/permissions.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./file-system": {
|
|
25
|
+
"types": "./build/file-system.d.ts",
|
|
26
|
+
"import": "./build/file-system.js",
|
|
27
|
+
"require": "./build/file-system.cjs"
|
|
18
28
|
}
|
|
19
29
|
},
|
|
20
30
|
"scripts": {
|