@brftech/filex-app-ui 0.54.0 → 0.56.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/README.md +10 -5
- package/dist/filex-app-ui.iife.js +1 -1
- package/dist/filex-app-ui.iife.js.map +1 -1
- package/dist/filex-app-ui.js +154 -99
- package/dist/filex-app-ui.js.map +1 -1
- package/dist/index.d.ts +296 -4
- package/package.json +1 -1
- package/src/index.ts +83 -4
- package/src/protocol.ts +267 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,152 @@ export declare interface BridgeError {
|
|
|
6
6
|
message?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
/** `coedit.append` params: the host seals it and the relay places it. */
|
|
10
|
+
export declare interface CoEditAppendParams {
|
|
11
|
+
kind: CoEditKind;
|
|
12
|
+
body: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export declare interface CoEditAppendResult {
|
|
16
|
+
/** Where it landed; it comes back as a `coedit.entry` too. */
|
|
17
|
+
seq: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare interface CoEditBlobGetParams {
|
|
21
|
+
name: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export declare interface CoEditBlobGetResult {
|
|
25
|
+
name: string;
|
|
26
|
+
bytes: ArrayBuffer;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** `coedit.blob.put` / `coedit.blob.get`: the session's sealed blobs (the
|
|
30
|
+
* base document, images added while editing). A name, not a path. */
|
|
31
|
+
export declare interface CoEditBlobPutParams {
|
|
32
|
+
name: string;
|
|
33
|
+
data: ArrayBuffer;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** `coedit.cursor` event data. */
|
|
37
|
+
export declare interface CoEditCursor {
|
|
38
|
+
client: string;
|
|
39
|
+
cursor: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** `coedit.cursor` params: passed to the other members, not kept. The host
|
|
43
|
+
* sends a few a second at most (the last one wins). */
|
|
44
|
+
export declare interface CoEditCursorParams {
|
|
45
|
+
cursor: unknown;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** `coedit.dropped` event data (filex 0.56). */
|
|
49
|
+
export declare interface CoEditDropped {
|
|
50
|
+
/** The last entry the host handed over. */
|
|
51
|
+
from: number;
|
|
52
|
+
/** `gone`: the membership ended (the session closed, or the page was away
|
|
53
|
+
* too long) - join again. `broken`: an entry did not check out (missing,
|
|
54
|
+
* moved, altered, replayed) - stop editing together. */
|
|
55
|
+
reason: 'gone' | 'broken';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** One entry of the session's log, opened and checked by filex. */
|
|
59
|
+
export declare type CoEditEntry = (CoEditEntryBase & {
|
|
60
|
+
kind: CoEditKind;
|
|
61
|
+
body: unknown;
|
|
62
|
+
}) | (CoEditEntryBase & {
|
|
63
|
+
kind: 'join';
|
|
64
|
+
member: CoEditMember;
|
|
65
|
+
}) | (CoEditEntryBase & {
|
|
66
|
+
kind: 'leave';
|
|
67
|
+
}) | (CoEditEntryBase & {
|
|
68
|
+
kind: 'saved';
|
|
69
|
+
through: number;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
declare interface CoEditEntryBase {
|
|
73
|
+
seq: number;
|
|
74
|
+
/** The relay's time, Unix ms - the same for every member. */
|
|
75
|
+
at: number;
|
|
76
|
+
client: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* What `coedit.join` answers: who the app is in the session and how far
|
|
81
|
+
* the log has gone. The entries come as `coedit.entry` events once the app
|
|
82
|
+
* subscribes (`coedit.subscribe` from 0 for the whole log). `unavailable`
|
|
83
|
+
* when filex offers no editing together here (filex 0.55, editing together
|
|
84
|
+
* turned off, an encrypted file, a vault): the app edits alone - the message
|
|
85
|
+
* says why (`not_ready` after waiting for a session's creator, `encrypted`,
|
|
86
|
+
* `nothing_to_watch` for a person who may only read the file while nobody
|
|
87
|
+
* edits it, or the server's code: `coedit_off`, `coedit_build`,
|
|
88
|
+
* `coedit_file_changed`).
|
|
89
|
+
*/
|
|
90
|
+
export declare interface CoEditHello {
|
|
91
|
+
session: string;
|
|
92
|
+
me: CoEditMember;
|
|
93
|
+
/**
|
|
94
|
+
* The last entry's seq when filex handed the session over (0: an empty
|
|
95
|
+
* log). filex 0.56: the changes this browser kept from an earlier opening
|
|
96
|
+
* of the file (a lost connection, a closed tab), which filex sends before
|
|
97
|
+
* it answers, are in the log up to here - an editor that starts once it
|
|
98
|
+
* has read this far opens with them.
|
|
99
|
+
*/
|
|
100
|
+
head: number;
|
|
101
|
+
/** The last `changes` entry's seq, as far as `head`. */
|
|
102
|
+
changesHead: number;
|
|
103
|
+
/** The last entry the file on the storage holds. */
|
|
104
|
+
savedThrough: number;
|
|
105
|
+
/**
|
|
106
|
+
* filex 0.56: this member started the session. Before anybody else can
|
|
107
|
+
* join it puts the base document - EXACTLY the bytes it opened - with
|
|
108
|
+
* `coedit.blob.put('base', bytes)`. A member that did not start it opens
|
|
109
|
+
* `coedit.blob.get('base')`, never the file: the file may hold a save of
|
|
110
|
+
* the session already, and the log applies to the base.
|
|
111
|
+
*/
|
|
112
|
+
created: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** `coedit.join` params: join (or start) the session of an opened file. */
|
|
116
|
+
export declare interface CoEditJoinParams {
|
|
117
|
+
index?: number;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** What a member appends: the editor's changes, a lock request, a release. */
|
|
121
|
+
export declare type CoEditKind = 'changes' | 'lock' | 'release';
|
|
122
|
+
|
|
123
|
+
/** `coedit.lease`: the changes lease (one writer at a time, one that has
|
|
124
|
+
* seen every change). */
|
|
125
|
+
export declare interface CoEditLeaseParams {
|
|
126
|
+
op: 'acquire' | 'release';
|
|
127
|
+
/** The seq of the last `changes` entry the app applied. */
|
|
128
|
+
changesSeen: number;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export declare interface CoEditLeaseResult {
|
|
132
|
+
granted: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** A member of the session. */
|
|
136
|
+
export declare interface CoEditMember {
|
|
137
|
+
/** The relay's id for the connection. */
|
|
138
|
+
client: string;
|
|
139
|
+
/** An opaque id of the person, the same for each of their connections in
|
|
140
|
+
* this session; never an account id. */
|
|
141
|
+
user: string;
|
|
142
|
+
/** The person's display name. */
|
|
143
|
+
name: string;
|
|
144
|
+
/** The editor's per-session user index: given once, never twice. */
|
|
145
|
+
indexUser: number;
|
|
146
|
+
/** May append changes (an editor role and the right to change the file). */
|
|
147
|
+
canEdit: boolean;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** `coedit.subscribe` params: entries after `from` arrive as `coedit.entry`. */
|
|
151
|
+
export declare interface CoEditSubscribeParams {
|
|
152
|
+
from: number;
|
|
153
|
+
}
|
|
154
|
+
|
|
9
155
|
export declare interface ConfirmParams {
|
|
10
156
|
title?: string;
|
|
11
157
|
text: string;
|
|
@@ -92,16 +238,64 @@ export declare interface FileInfo {
|
|
|
92
238
|
/** It cannot be saved over: read-only storage, view-only opening, or the
|
|
93
239
|
* app holds no `files:write`. */
|
|
94
240
|
readOnly: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* 0.55.0: the file is end-to-end encrypted - `folder` in an encrypted
|
|
243
|
+
* folder, `vault` in a vault (where only one person edits at a time),
|
|
244
|
+
* `file` for a single encrypted file (`.fxe`). As the server says it;
|
|
245
|
+
* ABSENT for a file that is not encrypted.
|
|
246
|
+
*
|
|
247
|
+
* ⚠ The server holds such a file only as ciphertext and has no key. Unless
|
|
248
|
+
* `plaintext` is true, filex hands the app nothing of it: `file.read` is
|
|
249
|
+
* refused (`failed` with `encrypted`), a save is refused, and `readOnly`
|
|
250
|
+
* is true.
|
|
251
|
+
*/
|
|
252
|
+
encrypted?: 'folder' | 'vault' | 'file';
|
|
253
|
+
/**
|
|
254
|
+
* 0.56.0: filex hands this encrypted file to the app in the clear. The
|
|
255
|
+
* person's browser, which holds the folder key while the folder is
|
|
256
|
+
* unlocked, decrypts it for `file.read` and encrypts what `file.save`
|
|
257
|
+
* hands over before it leaves the browser; the server sees only
|
|
258
|
+
* ciphertext. Only with `encrypted: 'folder'` or `'vault'`, for an app
|
|
259
|
+
* whose manifest says `encrypted_folders: {"open": true}` (the grant
|
|
260
|
+
* `files:e2e-plaintext`) - never for a `.fxe`. ABSENT otherwise.
|
|
261
|
+
*
|
|
262
|
+
* In a vault (`encrypted: 'vault'`) the browser reads the file from the
|
|
263
|
+
* vault's packs and writes a save as the vault's next generation, under
|
|
264
|
+
* the vault's write lock (one writer at a time). A save is refused -
|
|
265
|
+
* `failed` with `vault_locked` - while somebody else writes the vault, and
|
|
266
|
+
* with `vault_lock_lost` when this session's write lock ended before the
|
|
267
|
+
* save was committed: nothing was written, save again. The vault locked
|
|
268
|
+
* meanwhile (its keys left the browser): `unavailable`, and filex closes
|
|
269
|
+
* the app.
|
|
270
|
+
*
|
|
271
|
+
* What changes for the app: a read and a save are held whole in the page
|
|
272
|
+
* (at most LIMITS.maxEncryptedBytes); a save is written only over the file
|
|
273
|
+
* as it was opened (`failed` with `changed` when it changed meanwhile);
|
|
274
|
+
* `file.saveAs` answers `unavailable` (a copy elsewhere would leave the
|
|
275
|
+
* folder decrypted); the app's module (`engine.call`, `job.submit`) is
|
|
276
|
+
* never handed the file; `state.get` / `state.set` use a store of their
|
|
277
|
+
* own for this folder, sealed in the person's browser (the server keeps it
|
|
278
|
+
* and cannot read it; apart from the app's ordinary store, a little smaller
|
|
279
|
+
* as it is measured sealed). Everything the app holds is plaintext: keep it
|
|
280
|
+
* in the frame. In a folder whose names are encrypted too, `name` and
|
|
281
|
+
* `ext` are the decrypted name's - the server never sees them.
|
|
282
|
+
*/
|
|
283
|
+
plaintext?: true;
|
|
95
284
|
}
|
|
96
285
|
|
|
97
286
|
export declare interface FilexApp {
|
|
98
287
|
readonly session: Session;
|
|
99
288
|
/** The file at `index` (default the first) the interface was opened with. */
|
|
100
289
|
open(index?: number): Promise<OpenedFile>;
|
|
101
|
-
/**
|
|
290
|
+
/**
|
|
291
|
+
* Save new content over an opened file. `through` (filex 0.56, editing
|
|
292
|
+
* together): the last log entry the saved document holds, so the session
|
|
293
|
+
* knows what is saved.
|
|
294
|
+
*/
|
|
102
295
|
save(data: SaveData, opts?: {
|
|
103
296
|
index?: number;
|
|
104
297
|
mime?: string;
|
|
298
|
+
through?: number;
|
|
105
299
|
}): Promise<SaveResult>;
|
|
106
300
|
/** Save a NEW file; filex asks the person where (its own folder picker). */
|
|
107
301
|
saveAs(name: string, data: SaveData, mime?: string): Promise<SaveAsResult>;
|
|
@@ -120,13 +314,46 @@ export declare interface FilexApp {
|
|
|
120
314
|
* filex asks the person first. `cancelled` when they say no.
|
|
121
315
|
*/
|
|
122
316
|
download(name: string, data: SaveData, mime?: string): Promise<DownloadResult>;
|
|
317
|
+
/**
|
|
318
|
+
* Print a PDF (filex 0.55; the app needs `ui.print` in its manifest): a
|
|
319
|
+
* sandboxed frame may not open the print dialog, so filex prints it from
|
|
320
|
+
* its own page. filex asks the person every time, and the dialog opens on
|
|
321
|
+
* their click on filex's Allow; at most LIMITS.maxPrintBytes. filex 0.54
|
|
322
|
+
* and older answer `unknown_method`, and a host whose print page may not
|
|
323
|
+
* be shown `unavailable`: hand the PDF over with `download` then.
|
|
324
|
+
*/
|
|
325
|
+
print(name: string, pdf: Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array>): Promise<PrintResult>;
|
|
326
|
+
/**
|
|
327
|
+
* Editing together (filex 0.56; the app's manifest says `"co_edit":
|
|
328
|
+
* {"open": true}`, granted as `files:co-edit`). filex seals what the app appends, orders it
|
|
329
|
+
* through its relay and hands every member the same entries in the same
|
|
330
|
+
* order (`coedit.entry` events after `subscribe`); the app never holds a
|
|
331
|
+
* key. `join` answering `unavailable` (filex 0.55, editing together off
|
|
332
|
+
* here, an encrypted file) means: edit alone. The member that started the
|
|
333
|
+
* session (`created`) puts the base document before anybody else may join.
|
|
334
|
+
*/
|
|
335
|
+
coedit: {
|
|
336
|
+
join(index?: number): Promise<CoEditHello>;
|
|
337
|
+
subscribe(from: number): Promise<void>;
|
|
338
|
+
append(kind: CoEditKind, body: unknown): Promise<CoEditAppendResult>;
|
|
339
|
+
lease(op: 'acquire' | 'release', changesSeen: number): Promise<boolean>;
|
|
340
|
+
cursor(cursor: unknown): void;
|
|
341
|
+
putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
342
|
+
getBlob(name: string): Promise<ArrayBuffer>;
|
|
343
|
+
leave(): Promise<void>;
|
|
344
|
+
};
|
|
123
345
|
/** Call the app's own module (`ui_call` export) with the opened files. */
|
|
124
346
|
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
125
347
|
/** Queue one of the app's actions on the opened files; answers the op. */
|
|
126
348
|
submit(action: string, params?: Record<string, unknown>): Promise<{
|
|
127
349
|
op: unknown;
|
|
128
350
|
}>;
|
|
129
|
-
/**
|
|
351
|
+
/**
|
|
352
|
+
* This person's small store for this app (JSON values, 8 KiB each). While
|
|
353
|
+
* the app holds a file of an encrypted folder in the clear
|
|
354
|
+
* (`FileInfo.plaintext`, filex 0.56) it is another store, sealed in the
|
|
355
|
+
* browser - apart from this one, and empty while the folder is locked.
|
|
356
|
+
*/
|
|
130
357
|
state: {
|
|
131
358
|
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
132
359
|
set(key: string, value: unknown): Promise<void>;
|
|
@@ -176,7 +403,18 @@ export declare type HostEvent =
|
|
|
176
403
|
/** The person closed the frame's surroundings; last chance to say so. */
|
|
177
404
|
| 'close.request'
|
|
178
405
|
/** An administrator installed a new version of this app: reload to use it. */
|
|
179
|
-
| 'app.updated'
|
|
406
|
+
| 'app.updated'
|
|
407
|
+
/** Editing together: the next entry of the session's log, opened. `data: CoEditEntry`. */
|
|
408
|
+
| 'coedit.entry'
|
|
409
|
+
/** Editing together: another member's cursor (not kept). `data: CoEditCursor`. */
|
|
410
|
+
| 'coedit.cursor'
|
|
411
|
+
/**
|
|
412
|
+
* Editing together: the host stopped handing entries over. `data:
|
|
413
|
+
* CoEditDropped` - `gone`: the membership ended (join again); `broken`: an
|
|
414
|
+
* entry did not check out (stop editing together). A connection that drops
|
|
415
|
+
* for a moment is the host's own: it reconnects and says nothing.
|
|
416
|
+
*/
|
|
417
|
+
| 'coedit.dropped';
|
|
180
418
|
|
|
181
419
|
/** What the host may ask the app (and wait for). */
|
|
182
420
|
export declare type HostRequest =
|
|
@@ -244,12 +482,35 @@ export declare const LIMITS: {
|
|
|
244
482
|
readonly connectTimeoutMs: 10000;
|
|
245
483
|
/** The largest file `ui.download` hands the person (streamed or not). */
|
|
246
484
|
readonly maxDownloadBytes: number;
|
|
485
|
+
/**
|
|
486
|
+
* The largest PDF `ui.print` prints (filex 0.55). Lower than a download:
|
|
487
|
+
* a download can stream to disk, a print is held whole in the page (one
|
|
488
|
+
* Blob) until the print dialog has read it, and a document worth printing
|
|
489
|
+
* is far smaller.
|
|
490
|
+
*/
|
|
491
|
+
readonly maxPrintBytes: number;
|
|
492
|
+
/**
|
|
493
|
+
* The largest file of an encrypted folder filex hands an app in the clear
|
|
494
|
+
* or takes back from it (filex 0.56, `FileInfo.plaintext`): it is
|
|
495
|
+
* decrypted and encrypted whole in the page, as filex's own explorer does
|
|
496
|
+
* for a file of this size.
|
|
497
|
+
*/
|
|
498
|
+
readonly maxEncryptedBytes: number;
|
|
247
499
|
};
|
|
248
500
|
|
|
249
501
|
/** What the app may ask the host. Anything else is `unknown_method`. */
|
|
250
502
|
export declare type Method = 'session.get' | 'file.read' | 'file.save' | 'file.saveAs' | 'ui.dirty' | 'ui.title' | 'ui.toast' | 'ui.confirm' | 'ui.close' | 'clipboard.write' | 'ui.download' | 'engine.call' | 'job.submit' | 'state.get' | 'state.set'
|
|
251
503
|
/** 0.52.0: what the app's license says (a paid app from a store), `LicenseInfo`. */
|
|
252
|
-
| 'license.get'
|
|
504
|
+
| 'license.get'
|
|
505
|
+
/** 0.55.0: filex prints a PDF the app hands it (`PrintParams`, grant `ui:print`). */
|
|
506
|
+
| 'ui.print'
|
|
507
|
+
/**
|
|
508
|
+
* Editing together (`coedit.*`, task #189): the session's log the host
|
|
509
|
+
* seals, orders through filex's relay and opens again. Defined in filex
|
|
510
|
+
* 0.55, which answers each `unavailable`; offered from 0.56 to an app
|
|
511
|
+
* granted `files:co-edit` (see `CoEditHello`). `unavailable` = edit alone.
|
|
512
|
+
*/
|
|
513
|
+
| 'coedit.join' | 'coedit.subscribe' | 'coedit.append' | 'coedit.lease' | 'coedit.cursor' | 'coedit.blob.put' | 'coedit.blob.get' | 'coedit.leave';
|
|
253
514
|
|
|
254
515
|
export declare const METHODS: readonly Method[];
|
|
255
516
|
|
|
@@ -270,6 +531,31 @@ export declare interface PortMessage {
|
|
|
270
531
|
v: number;
|
|
271
532
|
}
|
|
272
533
|
|
|
534
|
+
/**
|
|
535
|
+
* `ui.print` (filex 0.55.0): a PDF for the browser's print dialog. A
|
|
536
|
+
* sandboxed frame may not open that dialog (measured: Chromium ignores
|
|
537
|
+
* `print()` there and says so, Firefox ignores it), so filex prints the PDF
|
|
538
|
+
* from a page of its own, with the app's `ui:print` grant, never over
|
|
539
|
+
* LIMITS.maxPrintBytes. filex asks every time, above the frame, and the
|
|
540
|
+
* dialog opens only on the person's click on filex's Allow. The print dialog
|
|
541
|
+
* can save the PDF too, so `ui:print` is the same kind of grant as
|
|
542
|
+
* `ui:download`. filex 0.54 and older answer `unknown_method`.
|
|
543
|
+
*/
|
|
544
|
+
export declare interface PrintParams {
|
|
545
|
+
/** The document's name, for the question filex may ask - a name, not a path. */
|
|
546
|
+
name: string;
|
|
547
|
+
/** The PDF: a transferred ArrayBuffer or ReadableStream<Uint8Array>. */
|
|
548
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer;
|
|
549
|
+
/** `application/pdf` when given; nothing else is printed. */
|
|
550
|
+
mime?: string;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export declare interface PrintResult {
|
|
554
|
+
/** The browser's print dialog was opened with the PDF. */
|
|
555
|
+
printed: true;
|
|
556
|
+
size: number;
|
|
557
|
+
}
|
|
558
|
+
|
|
273
559
|
/** `file.read` params. */
|
|
274
560
|
export declare interface ReadParams {
|
|
275
561
|
index?: number;
|
|
@@ -326,6 +612,12 @@ export declare interface SaveParams {
|
|
|
326
612
|
/** A transferred ReadableStream<Uint8Array>, an ArrayBuffer or a string. */
|
|
327
613
|
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
328
614
|
mime?: string;
|
|
615
|
+
/**
|
|
616
|
+
* 0.55.0, editing together: the last log entry (`CoEditEntry.seq`) the
|
|
617
|
+
* saved document holds, so the session knows what is saved. Ignored
|
|
618
|
+
* outside a session.
|
|
619
|
+
*/
|
|
620
|
+
through?: number;
|
|
329
621
|
}
|
|
330
622
|
|
|
331
623
|
export declare interface SaveResult {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brftech/filex-app-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.56.0",
|
|
4
4
|
"description": "filex app interface SDK: the bridge between an app's own sandboxed interface and filex (open, save, dirty, call the app's module)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/filex-app-ui.js",
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,12 @@ import {
|
|
|
36
36
|
type ToastParams,
|
|
37
37
|
type DownloadResult,
|
|
38
38
|
type LicenseInfo,
|
|
39
|
+
type PrintResult,
|
|
40
|
+
type CoEditHello,
|
|
41
|
+
type CoEditKind,
|
|
42
|
+
type CoEditAppendResult,
|
|
43
|
+
type CoEditLeaseResult,
|
|
44
|
+
type CoEditBlobGetResult,
|
|
39
45
|
} from './protocol';
|
|
40
46
|
|
|
41
47
|
export * from './protocol';
|
|
@@ -78,8 +84,12 @@ export interface FilexApp {
|
|
|
78
84
|
readonly session: Session;
|
|
79
85
|
/** The file at `index` (default the first) the interface was opened with. */
|
|
80
86
|
open(index?: number): Promise<OpenedFile>;
|
|
81
|
-
/**
|
|
82
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Save new content over an opened file. `through` (filex 0.56, editing
|
|
89
|
+
* together): the last log entry the saved document holds, so the session
|
|
90
|
+
* knows what is saved.
|
|
91
|
+
*/
|
|
92
|
+
save(data: SaveData, opts?: { index?: number; mime?: string; through?: number }): Promise<SaveResult>;
|
|
83
93
|
/** Save a NEW file; filex asks the person where (its own folder picker). */
|
|
84
94
|
saveAs(name: string, data: SaveData, mime?: string): Promise<SaveAsResult>;
|
|
85
95
|
/** Unsaved changes, or not: filex asks before the person leaves them. */
|
|
@@ -97,11 +107,44 @@ export interface FilexApp {
|
|
|
97
107
|
* filex asks the person first. `cancelled` when they say no.
|
|
98
108
|
*/
|
|
99
109
|
download(name: string, data: SaveData, mime?: string): Promise<DownloadResult>;
|
|
110
|
+
/**
|
|
111
|
+
* Print a PDF (filex 0.55; the app needs `ui.print` in its manifest): a
|
|
112
|
+
* sandboxed frame may not open the print dialog, so filex prints it from
|
|
113
|
+
* its own page. filex asks the person every time, and the dialog opens on
|
|
114
|
+
* their click on filex's Allow; at most LIMITS.maxPrintBytes. filex 0.54
|
|
115
|
+
* and older answer `unknown_method`, and a host whose print page may not
|
|
116
|
+
* be shown `unavailable`: hand the PDF over with `download` then.
|
|
117
|
+
*/
|
|
118
|
+
print(name: string, pdf: Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array>): Promise<PrintResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Editing together (filex 0.56; the app's manifest says `"co_edit":
|
|
121
|
+
* {"open": true}`, granted as `files:co-edit`). filex seals what the app appends, orders it
|
|
122
|
+
* through its relay and hands every member the same entries in the same
|
|
123
|
+
* order (`coedit.entry` events after `subscribe`); the app never holds a
|
|
124
|
+
* key. `join` answering `unavailable` (filex 0.55, editing together off
|
|
125
|
+
* here, an encrypted file) means: edit alone. The member that started the
|
|
126
|
+
* session (`created`) puts the base document before anybody else may join.
|
|
127
|
+
*/
|
|
128
|
+
coedit: {
|
|
129
|
+
join(index?: number): Promise<CoEditHello>;
|
|
130
|
+
subscribe(from: number): Promise<void>;
|
|
131
|
+
append(kind: CoEditKind, body: unknown): Promise<CoEditAppendResult>;
|
|
132
|
+
lease(op: 'acquire' | 'release', changesSeen: number): Promise<boolean>;
|
|
133
|
+
cursor(cursor: unknown): void;
|
|
134
|
+
putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
135
|
+
getBlob(name: string): Promise<ArrayBuffer>;
|
|
136
|
+
leave(): Promise<void>;
|
|
137
|
+
};
|
|
100
138
|
/** Call the app's own module (`ui_call` export) with the opened files. */
|
|
101
139
|
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
102
140
|
/** Queue one of the app's actions on the opened files; answers the op. */
|
|
103
141
|
submit(action: string, params?: Record<string, unknown>): Promise<{ op: unknown }>;
|
|
104
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* This person's small store for this app (JSON values, 8 KiB each). While
|
|
144
|
+
* the app holds a file of an encrypted folder in the clear
|
|
145
|
+
* (`FileInfo.plaintext`, filex 0.56) it is another store, sealed in the
|
|
146
|
+
* browser - apart from this one, and empty while the folder is locked.
|
|
147
|
+
*/
|
|
105
148
|
state: {
|
|
106
149
|
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
107
150
|
set(key: string, value: unknown): Promise<void>;
|
|
@@ -301,7 +344,9 @@ function bridge(port: MessagePort, opts: ConnectOptions): FilexApp {
|
|
|
301
344
|
},
|
|
302
345
|
async save(data, o = {}) {
|
|
303
346
|
const p = await payload(data);
|
|
304
|
-
|
|
347
|
+
const params: Record<string, unknown> = { index: o.index ?? 0, data: p.data, mime: o.mime };
|
|
348
|
+
if (typeof o.through === 'number') params.through = o.through;
|
|
349
|
+
return request<SaveResult>('file.save', params, p.transfer);
|
|
305
350
|
},
|
|
306
351
|
async saveAs(name, data, mime) {
|
|
307
352
|
const p = await payload(data);
|
|
@@ -311,6 +356,40 @@ function bridge(port: MessagePort, opts: ConnectOptions): FilexApp {
|
|
|
311
356
|
const p = await payload(data);
|
|
312
357
|
return request<DownloadResult>('ui.download', { name, data: p.data, mime }, p.transfer);
|
|
313
358
|
},
|
|
359
|
+
async print(name, pdf) {
|
|
360
|
+
const p = await payload(pdf);
|
|
361
|
+
return request<PrintResult>('ui.print', { name, data: p.data, mime: 'application/pdf' }, p.transfer);
|
|
362
|
+
},
|
|
363
|
+
coedit: {
|
|
364
|
+
join(index = 0) {
|
|
365
|
+
return request<CoEditHello>('coedit.join', { index });
|
|
366
|
+
},
|
|
367
|
+
subscribe(from: number) {
|
|
368
|
+
return request<void>('coedit.subscribe', { from });
|
|
369
|
+
},
|
|
370
|
+
append(kind: CoEditKind, body: unknown) {
|
|
371
|
+
return request<CoEditAppendResult>('coedit.append', { kind, body });
|
|
372
|
+
},
|
|
373
|
+
async lease(op: 'acquire' | 'release', changesSeen: number) {
|
|
374
|
+
const r = await request<CoEditLeaseResult>('coedit.lease', { op, changesSeen });
|
|
375
|
+
return !!r?.granted;
|
|
376
|
+
},
|
|
377
|
+
cursor(cursor: unknown) {
|
|
378
|
+
notify('coedit.cursor', { cursor });
|
|
379
|
+
},
|
|
380
|
+
async putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView) {
|
|
381
|
+
const p = await payload(data);
|
|
382
|
+
if (!(p.data instanceof ArrayBuffer)) throw new FilexError({ code: 'invalid', message: 'a blob is bytes' });
|
|
383
|
+
await request<void>('coedit.blob.put', { name, data: p.data }, p.transfer);
|
|
384
|
+
},
|
|
385
|
+
async getBlob(name: string) {
|
|
386
|
+
const r = await request<CoEditBlobGetResult>('coedit.blob.get', { name });
|
|
387
|
+
return r.bytes;
|
|
388
|
+
},
|
|
389
|
+
async leave() {
|
|
390
|
+
await request<void>('coedit.leave');
|
|
391
|
+
},
|
|
392
|
+
},
|
|
314
393
|
dirty(on) {
|
|
315
394
|
notify('ui.dirty', { dirty: !!on });
|
|
316
395
|
},
|