@brftech/filex-core 0.31.0 → 0.32.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.d.ts CHANGED
@@ -54,6 +54,13 @@ declare type __VLS_Props_11 = {
54
54
  /** ui-fix — mirror the main panel's virtual `.trash` row at storage root
55
55
  * so both split panes list identical rows (no row-offset). Defaults on. */
56
56
  trashVisible?: boolean;
57
+ /**
58
+ * The navigation panel is already offering Trash, so neither pane draws the
59
+ * virtual row. Passed down rather than worked out here: the panel belongs to
60
+ * the host explorer, and a pane that guessed at it is how the two halves of a
61
+ * split end up listing different rows.
62
+ */
63
+ navOffersTrash?: boolean;
57
64
  };
58
65
 
59
66
  declare type __VLS_Props_12 = {
@@ -85,9 +92,15 @@ declare type __VLS_Props_14 = {
85
92
  locale: LocaleCode;
86
93
  /** The folder has a user recovery key slot (v2 markers created since 0.31). */
87
94
  hasRecovery: boolean;
88
- /** The folder has an escrow slot AND this installation has escrow enabled. */
89
- hasEscrow: boolean;
90
- /** Short id of the escrow key the folder was sealed to. */
95
+ /**
96
+ * Whether the escrow door applies to THIS folder — see escrowAvailability.
97
+ * 'predates' and 'other-key' are not "no escrow tab"; they are two
98
+ * different facts the dialog has to state, because an admin who knows the
99
+ * installation has escrow reads a missing tab as a bug and tries the key
100
+ * anyway.
101
+ */
102
+ escrowState: EscrowAvailability;
103
+ /** Short id of the escrow key THIS FOLDER was sealed to, when it has one. */
91
104
  escrowKid?: string | null;
92
105
  busy?: boolean;
93
106
  /** Set by the parent after a failed attempt. */
@@ -287,6 +300,40 @@ declare type __VLS_Props_9 = {
287
300
  narrow?: boolean;
288
301
  };
289
302
 
303
+ /**
304
+ * Give an EXISTING v2 folder an escrow slot, in place, using the folder
305
+ * password its owner has just typed.
306
+ *
307
+ * ── Why this exists ─────────────────────────────────────────────────
308
+ *
309
+ * Escrow used to be all-or-nothing at install time, and then adoptable but
310
+ * never retroactive: on any installation that had been running for a while,
311
+ * escrow covered only folders nobody had created yet. On a real deployment
312
+ * the folders that matter already exist, so "new folders only" means escrow
313
+ * covers nothing anyone cares about.
314
+ *
315
+ * The server still cannot do this, and that has not changed: adding a slot
316
+ * needs the folder master key, which needs a credential the server has never
317
+ * held. What CAN do it is the browser, at the one moment the password is in
318
+ * memory — exactly where `upgradeMarkerV1` already lives. Same moment, same
319
+ * shape, different slot.
320
+ *
321
+ * ⚠⚠ Accepting hands the operator of this installation a second, permanent
322
+ * way into this folder. It is the folder's owner who decides, from inside,
323
+ * with the password; no configuration change and no admin action can do it
324
+ * for them. The caller MUST say that in those words before calling this —
325
+ * see `e2e.escrowoffer.*` in the locales.
326
+ *
327
+ * ⚠ v2 only. A v1 marker has no slots at all; the path for those is
328
+ * `upgradeMarkerV1`, which already seals an escrow slot when the
329
+ * installation has a key and already discloses it. Two doors into the same
330
+ * room would be two chances to get the disclosure wrong.
331
+ *
332
+ * ⚠ No file is re-encrypted, moved or rewritten. Only `.filex-e2e.json`
333
+ * changes, and only by gaining `esc` (and losing `esc_declined`).
334
+ */
335
+ export declare function addEscrowSlot(marker: E2eMarker, password: string, escrowPublicKey: string): Promise<E2eMarker>;
336
+
290
337
  /**
291
338
  * The self-service API-token surface (`/api/tokens`).
292
339
  *
@@ -589,6 +636,19 @@ export declare function createMarker(password: string, iterations?: number): Pro
589
636
  kek: CryptoKey;
590
637
  }>;
591
638
 
639
+ /**
640
+ * Record that this folder's owner was offered an escrow slot and declined.
641
+ *
642
+ * A refusal is a decision, not a delay: without this the offer would come
643
+ * back on every single unlock, which is how people learn to click past
644
+ * security dialogs without reading them. Nothing about the folder's keys
645
+ * changes — the only effect is that filex stops asking.
646
+ *
647
+ * Reversible by `addEscrowSlot`, which is the way back for somebody who
648
+ * says no today and changes their mind next month.
649
+ */
650
+ export declare function declineEscrowSlot(marker: E2eMarker, when: string): E2eMarker;
651
+
592
652
  /**
593
653
  * Decrypt a 'filexe2e' blob with the folder master key. Throws
594
654
  * E2eDecryptError on a wrong key / tampered data, and a plain Error when the
@@ -661,10 +721,19 @@ export declare const E2E_MAGIC = "filexe2e";
661
721
  * - No key, password or recovery key is ever stored, logged or sent to a
662
722
  * server. The FMK lives in an in-memory key ring and dies with the tab.
663
723
  * - `deriveKek` imports non-extractable. Raw KEK bytes are produced ONLY
664
- * by `deriveKekBits`, only while upgrading a v1 marker, and only long
665
- * enough to wrap them into the new slots.
724
+ * by `deriveKekBits`, only for a marker whose FMK *is* the KEK
725
+ * (`upgradeMarkerV1`, and `addEscrowSlot` on a folder it produced), and
726
+ * only long enough to wrap them into a slot.
666
727
  * - A folder created while escrow was off carries no escrow slot, so the
667
- * escrow key cannot open it. That is arithmetic, not policy.
728
+ * escrow key cannot open it, and nothing the OPERATOR does changes
729
+ * that — not enabling escrow, not adopting it, not any admin action or
730
+ * future version. That is arithmetic, not policy: adding a slot needs
731
+ * the folder master key, and the server has never held a credential
732
+ * that produces one.
733
+ * - The folder's OWNER can, from inside, with the password:
734
+ * `addEscrowSlot`. That is the only door, it opens from one side only,
735
+ * and it is the reason `escrowAvailability` says "not as things stand"
736
+ * rather than "never".
668
737
  *
669
738
  * File layout ('filexe2e' magic, fixed 97-byte header) — UNCHANGED in v2:
670
739
  * [0..8) magic "filexe2e"
@@ -722,6 +791,23 @@ export declare interface E2eMarker {
722
791
  rk?: E2eRecoverySlot;
723
792
  /** v2 only, optional: the operator escrow slot. */
724
793
  esc?: E2eEscrowSlot;
794
+ /**
795
+ * v2 only, optional: an ISO timestamp recording that this folder's owner
796
+ * was OFFERED an escrow slot and said no.
797
+ *
798
+ * It lives in the marker rather than in browser storage because the unit
799
+ * of the decision is the FOLDER, not the device: the same person opening
800
+ * the folder from their phone must not be asked again, and a decision
801
+ * that vanished when someone cleared their site data would be no decision
802
+ * at all. It travels with the folder through a move, a backup and a
803
+ * restore, for the same reason the key slots do.
804
+ *
805
+ * It holds no key material and hides nothing from the operator — it is a
806
+ * record of an answer, and its only effect is that filex stops asking.
807
+ * `addEscrowSlot` clears it, so a decline is reversible by the one person
808
+ * who can reverse it.
809
+ */
810
+ esc_declined?: string;
725
811
  }
726
812
 
727
813
  /** User-recovery-key slot: HKDF salt + the FMK wrapped under the derived key. */
@@ -823,6 +909,41 @@ export declare function ensureHighlight(): Promise<unknown | null>;
823
909
  */
824
910
  export declare function ensureMonaco(): Promise<unknown | null>;
825
911
 
912
+ /**
913
+ * Why the escrow door is, or is not, on offer for this folder.
914
+ *
915
+ * 'off' this installation has no escrow key at all.
916
+ * 'available' the folder is sealed to THIS installation's escrow key.
917
+ * 'predates' the installation has an escrow key, and this folder has no
918
+ * escrow slot: it was created before escrow existed here.
919
+ * 'other-key' the folder carries an escrow slot sealed to a DIFFERENT
920
+ * key id — it came from another installation, via a restore
921
+ * or a copied data directory.
922
+ *
923
+ * ⚠ 'predates' exists because escrow can be ADOPTED by an installation
924
+ * that already has folders (FILEX_INSTALLATION_E2E_ESCROW_ADOPT), and
925
+ * adoption is not retroactive: the folder's master key was wrapped to its
926
+ * recovery paths when the folder was created. Before this distinction
927
+ * existed the dialog simply showed no Escrow tab, which is true but says
928
+ * nothing — an admin who knows escrow is on reads a missing tab as a bug,
929
+ * tries the key anyway, and learns the real answer from a failure. The UI
930
+ * has to say it instead.
931
+ *
932
+ * ⚠⚠ 'predates' means "not as things stand", NOT "never". The folder's
933
+ * owner can add a slot from inside with the password (`addEscrowSlot`,
934
+ * offered at unlock). Any wording built on this state has to leave that
935
+ * door visible, or it tells an operator their escrow key can never reach a
936
+ * folder whose owner could hand it over this afternoon.
937
+ *
938
+ * ⚠ 'other-key' was a quieter lie: the dialog labelled the escrow field
939
+ * with the INSTALLATION's key id whatever the folder's slot said, so a
940
+ * folder restored from another install looked openable by the key the
941
+ * operator has, and was not.
942
+ */
943
+ export declare type EscrowAvailability = 'off' | 'available' | 'predates' | 'other-key';
944
+
945
+ export declare function escrowAvailability(m: E2eMarker | null, installationKid: string | null | undefined): EscrowAvailability;
946
+
826
947
  /**
827
948
  * Stable short name for an escrow key: first 8 bytes of SHA-256(SPKI), hex.
828
949
  * Written into every escrow slot so a marker says WHICH key opens it, and so
@@ -830,6 +951,27 @@ export declare function ensureMonaco(): Promise<unknown | null>;
830
951
  */
831
952
  export declare function escrowKeyId(spkiB64: string): Promise<string>;
832
953
 
954
+ /**
955
+ * Whether this folder's owner should be offered an escrow slot, and whether
956
+ * they have already answered.
957
+ *
958
+ * 'n/a' nothing to offer: the installation has no escrow key, the
959
+ * folder already has a slot, or the marker is v1 (whose path
960
+ * is `upgradeMarkerV1`).
961
+ * 'offer' the offer applies and no answer has been recorded.
962
+ * 'declined' the offer applies and the owner said no. Do not ask again;
963
+ * leave a way back.
964
+ *
965
+ * ⚠ This deliberately does NOT look at whether the folder is unlocked. That
966
+ * is the caller's business, and it matters: the offer may only be shown
967
+ * after an unlock actually succeeded, because accepting needs the password
968
+ * and because asking someone who cannot open the folder to give away a key
969
+ * to it is asking the wrong person.
970
+ */
971
+ export declare type EscrowOfferState = 'n/a' | 'offer' | 'declined';
972
+
973
+ export declare function escrowOfferState(m: E2eMarker | null, installationKid: string | null | undefined): EscrowOfferState;
974
+
833
975
  export declare interface ExplorerConfig {
834
976
  /**
835
977
  * Modern shorthand: URL prefix for the standard /api/files/* layout.
@@ -988,6 +1130,22 @@ export declare interface ExplorerConfig {
988
1130
  * navigation panel starts expanded, the tab strip
989
1131
  * and split pane are off, the gallery view mode and
990
1132
  * the host's "How to connect" surface are hidden.
1133
+ * 'drive' — everything `simple` does, plus the shell an end
1134
+ * user already knows: one primary "New" menu, one
1135
+ * search field in the header (with its ⌘K/Ctrl+K
1136
+ * escalation into the command palette), a filter row
1137
+ * under the breadcrumb, Folders and Files as
1138
+ * labelled sections in grid view, the details panel
1139
+ * split into Details / Activity, and a storage line
1140
+ * under the navigation.
1141
+ *
1142
+ * ⚠ `drive` is a SUPERSET of `simple`, not a sibling: everything `simple`
1143
+ * turns off stays off, and the code asks `simpleUi` for those questions so a
1144
+ * later change to `simple` cannot silently miss `drive`. It is a third value
1145
+ * rather than a second boolean because "which chrome" is ONE question with
1146
+ * three answers — a `driveShell: true` next to `uiProfile: 'standard'` would
1147
+ * be a combination nobody can describe, and keeping that question single is
1148
+ * why `uiProfile` was a preset to begin with.
991
1149
  *
992
1150
  * Why it exists (GitHub #14): the reporter's users are not in IT and read
993
1151
  * split panes, tabs and mount instructions as a file manager they would have
@@ -999,7 +1157,7 @@ export declare interface ExplorerConfig {
999
1157
  * rest of the chrome differ. A viewer's own collapse choice, once made,
1000
1158
  * outranks the profile — it is a per-viewer preference, not a policy.
1001
1159
  */
1002
- uiProfile?: 'standard' | 'simple';
1160
+ uiProfile?: 'standard' | 'simple' | 'drive';
1003
1161
  /**
1004
1162
  * Render the navigation panel (Upload · Recent / Starred / Shared with me /
1005
1163
  * Trash · the storage list). Collapsible to an icon rail by the viewer, whose
@@ -1324,6 +1482,8 @@ inspectorOpen?: boolean;
1324
1482
  navOpen?: boolean;
1325
1483
  navEnabled?: boolean;
1326
1484
  viewModes?: ViewMode[];
1485
+ shell?: "classic" | "drive";
1486
+ scopeLabel?: string;
1327
1487
  }> & Readonly<{
1328
1488
  onAction?: ((key: string) => any) | undefined;
1329
1489
  onUpload?: (() => any) | undefined;
@@ -1338,6 +1498,7 @@ onRefresh?: (() => any) | undefined;
1338
1498
  "onToggle-nav"?: (() => any) | undefined;
1339
1499
  "onOpen-theme"?: (() => any) | undefined;
1340
1500
  "onOpen-shortcut-settings"?: (() => any) | undefined;
1501
+ "onOpen-palette"?: ((query: string) => any) | undefined;
1341
1502
  }>, {
1342
1503
  focusSearch: () => Promise<void>;
1343
1504
  }, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
@@ -1354,6 +1515,7 @@ refresh: () => any;
1354
1515
  "toggle-nav": () => any;
1355
1516
  "open-theme": () => any;
1356
1517
  "open-shortcut-settings": () => any;
1518
+ "open-palette": (query: string) => any;
1357
1519
  }, PublicProps, {}, false, {}, {}, GlobalComponents, GlobalDirectives, string, {
1358
1520
  primaryEl: HTMLDivElement;
1359
1521
  wideMoreBtnEl: HTMLButtonElement;
@@ -1463,6 +1625,8 @@ inspectorOpen?: boolean;
1463
1625
  navOpen?: boolean;
1464
1626
  navEnabled?: boolean;
1465
1627
  viewModes?: ViewMode[];
1628
+ shell?: "classic" | "drive";
1629
+ scopeLabel?: string;
1466
1630
  }> & Readonly<{
1467
1631
  onAction?: ((key: string) => any) | undefined;
1468
1632
  onUpload?: (() => any) | undefined;
@@ -1477,6 +1641,7 @@ onRefresh?: (() => any) | undefined;
1477
1641
  "onToggle-nav"?: (() => any) | undefined;
1478
1642
  "onOpen-theme"?: (() => any) | undefined;
1479
1643
  "onOpen-shortcut-settings"?: (() => any) | undefined;
1644
+ "onOpen-palette"?: ((query: string) => any) | undefined;
1480
1645
  }>, {
1481
1646
  focusSearch: () => Promise<void>;
1482
1647
  }, {}, {}, {}, {}> | null;
@@ -1496,6 +1661,7 @@ viewMode?: ViewMode;
1496
1661
  thumbSrc?: (n: FileNode) => string | null;
1497
1662
  keepBadgeFor?: (n: FileNode) => "kept" | "syncing" | "cloud" | "partial" | null;
1498
1663
  trashVisible?: boolean;
1664
+ navOffersTrash?: boolean;
1499
1665
  }> & Readonly<{
1500
1666
  onTransfer?: ((p: {
1501
1667
  sources: string[];
@@ -1550,6 +1716,7 @@ viewMode?: ViewMode;
1550
1716
  thumbSrc?: (n: FileNode) => string | null;
1551
1717
  keepBadgeFor?: (n: FileNode) => "kept" | "syncing" | "cloud" | "partial" | null;
1552
1718
  trashVisible?: boolean;
1719
+ navOffersTrash?: boolean;
1553
1720
  }> & Readonly<{
1554
1721
  onTransfer?: ((p: {
1555
1722
  sources: string[];
@@ -2213,6 +2380,14 @@ onNav?: ((delta: number) => any) | undefined;
2213
2380
  "onOpen-full"?: (() => any) | undefined;
2214
2381
  }>, {}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
2215
2382
 
2383
+ /** surucu:d1 — `GET /api/files/quota/me` (quota.Snapshot). */
2384
+ declare interface QuotaSnapshot {
2385
+ used_bytes: number;
2386
+ quota_bytes: number;
2387
+ percent_used: number;
2388
+ unlimited: boolean;
2389
+ }
2390
+
2216
2391
  export declare const RecentlyOpened: DefineComponent<__VLS_Props_5, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, {} & {
2217
2392
  error: (message: string) => any;
2218
2393
  open: (node: RecentNode) => any;
@@ -3008,6 +3183,7 @@ export declare function useFileApi(config: ExplorerConfig): {
3008
3183
  limit?: number;
3009
3184
  scope?: GlobalSearchScope;
3010
3185
  }) => Promise<GlobalSearchHit[]>;
3186
+ quotaMe: () => Promise<QuotaSnapshot | null>;
3011
3187
  subfolders: (path: string) => Promise<{
3012
3188
  folders: FileNode[];
3013
3189
  }>;
@@ -3038,7 +3214,9 @@ export declare function useFileApi(config: ExplorerConfig): {
3038
3214
  uploadMultipart: (path: string, files: File[], onProgress?: (p: number) => void) => Promise<ManagerResponse>;
3039
3215
  downloadUrl: (path: string) => string;
3040
3216
  previewUrl: (path: string) => string;
3041
- fetchBlob: (path: string) => Promise<{
3217
+ fetchBlob: (path: string, opts?: {
3218
+ fresh?: boolean;
3219
+ }) => Promise<{
3042
3220
  url: string;
3043
3221
  blob: Blob;
3044
3222
  mime: string;