@brftech/filex-app-ui 0.53.0 → 0.55.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 +9 -4
- 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 +147 -99
- package/dist/filex-app-ui.js.map +1 -1
- package/dist/index.d.ts +215 -2
- package/package.json +1 -1
- package/src/index.ts +66 -0
- package/src/protocol.ts +200 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,123 @@ 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. */
|
|
43
|
+
export declare interface CoEditCursorParams {
|
|
44
|
+
cursor: unknown;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** One entry of the session's log, opened and checked by filex. */
|
|
48
|
+
export declare type CoEditEntry = (CoEditEntryBase & {
|
|
49
|
+
kind: CoEditKind;
|
|
50
|
+
body: unknown;
|
|
51
|
+
}) | (CoEditEntryBase & {
|
|
52
|
+
kind: 'join';
|
|
53
|
+
member: CoEditMember;
|
|
54
|
+
}) | (CoEditEntryBase & {
|
|
55
|
+
kind: 'leave';
|
|
56
|
+
}) | (CoEditEntryBase & {
|
|
57
|
+
kind: 'saved';
|
|
58
|
+
through: number;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
declare interface CoEditEntryBase {
|
|
62
|
+
seq: number;
|
|
63
|
+
/** The relay's time, Unix ms - the same for every member. */
|
|
64
|
+
at: number;
|
|
65
|
+
client: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* What `coedit.join` answers: who the app is in the session and how far
|
|
70
|
+
* the log has gone. The entries come as `coedit.entry` events once the app
|
|
71
|
+
* subscribes (`coedit.subscribe` from 0 for the whole log). `unavailable`
|
|
72
|
+
* when filex offers no editing together here (filex 0.55, a vault, a file
|
|
73
|
+
* that cannot be shared): the app edits alone.
|
|
74
|
+
*/
|
|
75
|
+
export declare interface CoEditHello {
|
|
76
|
+
session: string;
|
|
77
|
+
me: CoEditMember;
|
|
78
|
+
/** The last entry's seq (0: an empty log). */
|
|
79
|
+
head: number;
|
|
80
|
+
/** The last `changes` entry's seq. */
|
|
81
|
+
changesHead: number;
|
|
82
|
+
/** The last entry the file on the storage holds. */
|
|
83
|
+
savedThrough: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** `coedit.join` params: join (or start) the session of an opened file. */
|
|
87
|
+
export declare interface CoEditJoinParams {
|
|
88
|
+
index?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** What a member appends: the editor's changes, a lock request, a release. */
|
|
92
|
+
export declare type CoEditKind = 'changes' | 'lock' | 'release';
|
|
93
|
+
|
|
94
|
+
/** `coedit.lease`: the changes lease (one writer at a time, one that has
|
|
95
|
+
* seen every change). */
|
|
96
|
+
export declare interface CoEditLeaseParams {
|
|
97
|
+
op: 'acquire' | 'release';
|
|
98
|
+
/** The seq of the last `changes` entry the app applied. */
|
|
99
|
+
changesSeen: number;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export declare interface CoEditLeaseResult {
|
|
103
|
+
granted: boolean;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** A member of the session. */
|
|
107
|
+
export declare interface CoEditMember {
|
|
108
|
+
/** The relay's id for the connection. */
|
|
109
|
+
client: string;
|
|
110
|
+
/** An opaque id of the person, the same for each of their connections in
|
|
111
|
+
* this session; never an account id. */
|
|
112
|
+
user: string;
|
|
113
|
+
/** The person's display name. */
|
|
114
|
+
name: string;
|
|
115
|
+
/** The editor's per-session user index: given once, never twice. */
|
|
116
|
+
indexUser: number;
|
|
117
|
+
/** May append changes (an editor role and the right to change the file). */
|
|
118
|
+
canEdit: boolean;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** `coedit.subscribe` params: entries after `from` arrive as `coedit.entry`. */
|
|
122
|
+
export declare interface CoEditSubscribeParams {
|
|
123
|
+
from: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
9
126
|
export declare interface ConfirmParams {
|
|
10
127
|
title?: string;
|
|
11
128
|
text: string;
|
|
@@ -92,6 +209,19 @@ export declare interface FileInfo {
|
|
|
92
209
|
/** It cannot be saved over: read-only storage, view-only opening, or the
|
|
93
210
|
* app holds no `files:write`. */
|
|
94
211
|
readOnly: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* 0.55.0: the file is end-to-end encrypted - `folder` in an encrypted
|
|
214
|
+
* folder, `vault` in a vault (where only one person edits at a time),
|
|
215
|
+
* `file` for a single encrypted file (`.fxe`). As the server says it;
|
|
216
|
+
* ABSENT for a file that is not encrypted.
|
|
217
|
+
*
|
|
218
|
+
* ⚠ filex 0.55 decrypts nothing for an app: the server holds such a file
|
|
219
|
+
* only as ciphertext and has no key, so `file.read` is refused
|
|
220
|
+
* (`failed` with `encrypted`), a save is refused, and `readOnly` is true.
|
|
221
|
+
* The explorer offers no app an encrypted file in the first place; the
|
|
222
|
+
* field is information, not a promise of plaintext.
|
|
223
|
+
*/
|
|
224
|
+
encrypted?: 'folder' | 'vault' | 'file';
|
|
95
225
|
}
|
|
96
226
|
|
|
97
227
|
export declare interface FilexApp {
|
|
@@ -120,6 +250,32 @@ export declare interface FilexApp {
|
|
|
120
250
|
* filex asks the person first. `cancelled` when they say no.
|
|
121
251
|
*/
|
|
122
252
|
download(name: string, data: SaveData, mime?: string): Promise<DownloadResult>;
|
|
253
|
+
/**
|
|
254
|
+
* Print a PDF (filex 0.55; the app needs `ui.print` in its manifest): a
|
|
255
|
+
* sandboxed frame may not open the print dialog, so filex prints it from
|
|
256
|
+
* its own page. filex asks the person every time, and the dialog opens on
|
|
257
|
+
* their click on filex's Allow; at most LIMITS.maxPrintBytes. filex 0.54
|
|
258
|
+
* and older answer `unknown_method`, and a host whose print page may not
|
|
259
|
+
* be shown `unavailable`: hand the PDF over with `download` then.
|
|
260
|
+
*/
|
|
261
|
+
print(name: string, pdf: Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array>): Promise<PrintResult>;
|
|
262
|
+
/**
|
|
263
|
+
* Editing together (filex 0.55: defined, not offered - every call answers
|
|
264
|
+
* `unavailable`, and the app edits alone). filex seals what the app
|
|
265
|
+
* appends, orders it through its relay and hands every member the same
|
|
266
|
+
* entries in the same order (`coedit.entry` events after `subscribe`);
|
|
267
|
+
* the app never holds a key.
|
|
268
|
+
*/
|
|
269
|
+
coedit: {
|
|
270
|
+
join(index?: number): Promise<CoEditHello>;
|
|
271
|
+
subscribe(from: number): Promise<void>;
|
|
272
|
+
append(kind: CoEditKind, body: unknown): Promise<CoEditAppendResult>;
|
|
273
|
+
lease(op: 'acquire' | 'release', changesSeen: number): Promise<boolean>;
|
|
274
|
+
cursor(cursor: unknown): void;
|
|
275
|
+
putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
276
|
+
getBlob(name: string): Promise<ArrayBuffer>;
|
|
277
|
+
leave(): Promise<void>;
|
|
278
|
+
};
|
|
123
279
|
/** Call the app's own module (`ui_call` export) with the opened files. */
|
|
124
280
|
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
125
281
|
/** Queue one of the app's actions on the opened files; answers the op. */
|
|
@@ -176,7 +332,17 @@ export declare type HostEvent =
|
|
|
176
332
|
/** The person closed the frame's surroundings; last chance to say so. */
|
|
177
333
|
| 'close.request'
|
|
178
334
|
/** An administrator installed a new version of this app: reload to use it. */
|
|
179
|
-
| 'app.updated'
|
|
335
|
+
| 'app.updated'
|
|
336
|
+
/** Editing together: the next entry of the session's log, opened. `data: CoEditEntry`. */
|
|
337
|
+
| 'coedit.entry'
|
|
338
|
+
/** Editing together: another member's cursor (not kept). `data: CoEditCursor`. */
|
|
339
|
+
| 'coedit.cursor'
|
|
340
|
+
/**
|
|
341
|
+
* Editing together: the host stopped handing entries over (the app fell
|
|
342
|
+
* behind, or the connection dropped). `data: {from}` - subscribe again
|
|
343
|
+
* from the last entry the app holds.
|
|
344
|
+
*/
|
|
345
|
+
| 'coedit.dropped';
|
|
180
346
|
|
|
181
347
|
/** What the host may ask the app (and wait for). */
|
|
182
348
|
export declare type HostRequest =
|
|
@@ -244,12 +410,28 @@ export declare const LIMITS: {
|
|
|
244
410
|
readonly connectTimeoutMs: 10000;
|
|
245
411
|
/** The largest file `ui.download` hands the person (streamed or not). */
|
|
246
412
|
readonly maxDownloadBytes: number;
|
|
413
|
+
/**
|
|
414
|
+
* The largest PDF `ui.print` prints (filex 0.55). Lower than a download:
|
|
415
|
+
* a download can stream to disk, a print is held whole in the page (one
|
|
416
|
+
* Blob) until the print dialog has read it, and a document worth printing
|
|
417
|
+
* is far smaller.
|
|
418
|
+
*/
|
|
419
|
+
readonly maxPrintBytes: number;
|
|
247
420
|
};
|
|
248
421
|
|
|
249
422
|
/** What the app may ask the host. Anything else is `unknown_method`. */
|
|
250
423
|
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
424
|
/** 0.52.0: what the app's license says (a paid app from a store), `LicenseInfo`. */
|
|
252
|
-
| 'license.get'
|
|
425
|
+
| 'license.get'
|
|
426
|
+
/** 0.55.0: filex prints a PDF the app hands it (`PrintParams`, grant `ui:print`). */
|
|
427
|
+
| 'ui.print'
|
|
428
|
+
/**
|
|
429
|
+
* 0.55.0, editing together (`coedit.*`, task #189): the session's log the
|
|
430
|
+
* host seals, orders through filex's relay and opens again. Defined so an
|
|
431
|
+
* app can be written against them; filex 0.55 offers no relay yet, so each
|
|
432
|
+
* answers `unavailable` - edit alone then (see `CoEditHello`).
|
|
433
|
+
*/
|
|
434
|
+
| 'coedit.join' | 'coedit.subscribe' | 'coedit.append' | 'coedit.lease' | 'coedit.cursor' | 'coedit.blob.put' | 'coedit.blob.get' | 'coedit.leave';
|
|
253
435
|
|
|
254
436
|
export declare const METHODS: readonly Method[];
|
|
255
437
|
|
|
@@ -270,6 +452,31 @@ export declare interface PortMessage {
|
|
|
270
452
|
v: number;
|
|
271
453
|
}
|
|
272
454
|
|
|
455
|
+
/**
|
|
456
|
+
* `ui.print` (filex 0.55.0): a PDF for the browser's print dialog. A
|
|
457
|
+
* sandboxed frame may not open that dialog (measured: Chromium ignores
|
|
458
|
+
* `print()` there and says so, Firefox ignores it), so filex prints the PDF
|
|
459
|
+
* from a page of its own, with the app's `ui:print` grant, never over
|
|
460
|
+
* LIMITS.maxPrintBytes. filex asks every time, above the frame, and the
|
|
461
|
+
* dialog opens only on the person's click on filex's Allow. The print dialog
|
|
462
|
+
* can save the PDF too, so `ui:print` is the same kind of grant as
|
|
463
|
+
* `ui:download`. filex 0.54 and older answer `unknown_method`.
|
|
464
|
+
*/
|
|
465
|
+
export declare interface PrintParams {
|
|
466
|
+
/** The document's name, for the question filex may ask - a name, not a path. */
|
|
467
|
+
name: string;
|
|
468
|
+
/** The PDF: a transferred ArrayBuffer or ReadableStream<Uint8Array>. */
|
|
469
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer;
|
|
470
|
+
/** `application/pdf` when given; nothing else is printed. */
|
|
471
|
+
mime?: string;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export declare interface PrintResult {
|
|
475
|
+
/** The browser's print dialog was opened with the PDF. */
|
|
476
|
+
printed: true;
|
|
477
|
+
size: number;
|
|
478
|
+
}
|
|
479
|
+
|
|
273
480
|
/** `file.read` params. */
|
|
274
481
|
export declare interface ReadParams {
|
|
275
482
|
index?: number;
|
|
@@ -326,6 +533,12 @@ export declare interface SaveParams {
|
|
|
326
533
|
/** A transferred ReadableStream<Uint8Array>, an ArrayBuffer or a string. */
|
|
327
534
|
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
328
535
|
mime?: string;
|
|
536
|
+
/**
|
|
537
|
+
* 0.55.0, editing together: the last log entry (`CoEditEntry.seq`) the
|
|
538
|
+
* saved document holds, so the session knows what is saved. Ignored
|
|
539
|
+
* outside a session.
|
|
540
|
+
*/
|
|
541
|
+
through?: number;
|
|
329
542
|
}
|
|
330
543
|
|
|
331
544
|
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.55.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';
|
|
@@ -97,6 +103,32 @@ export interface FilexApp {
|
|
|
97
103
|
* filex asks the person first. `cancelled` when they say no.
|
|
98
104
|
*/
|
|
99
105
|
download(name: string, data: SaveData, mime?: string): Promise<DownloadResult>;
|
|
106
|
+
/**
|
|
107
|
+
* Print a PDF (filex 0.55; the app needs `ui.print` in its manifest): a
|
|
108
|
+
* sandboxed frame may not open the print dialog, so filex prints it from
|
|
109
|
+
* its own page. filex asks the person every time, and the dialog opens on
|
|
110
|
+
* their click on filex's Allow; at most LIMITS.maxPrintBytes. filex 0.54
|
|
111
|
+
* and older answer `unknown_method`, and a host whose print page may not
|
|
112
|
+
* be shown `unavailable`: hand the PDF over with `download` then.
|
|
113
|
+
*/
|
|
114
|
+
print(name: string, pdf: Blob | ArrayBuffer | ArrayBufferView | ReadableStream<Uint8Array>): Promise<PrintResult>;
|
|
115
|
+
/**
|
|
116
|
+
* Editing together (filex 0.55: defined, not offered - every call answers
|
|
117
|
+
* `unavailable`, and the app edits alone). filex seals what the app
|
|
118
|
+
* appends, orders it through its relay and hands every member the same
|
|
119
|
+
* entries in the same order (`coedit.entry` events after `subscribe`);
|
|
120
|
+
* the app never holds a key.
|
|
121
|
+
*/
|
|
122
|
+
coedit: {
|
|
123
|
+
join(index?: number): Promise<CoEditHello>;
|
|
124
|
+
subscribe(from: number): Promise<void>;
|
|
125
|
+
append(kind: CoEditKind, body: unknown): Promise<CoEditAppendResult>;
|
|
126
|
+
lease(op: 'acquire' | 'release', changesSeen: number): Promise<boolean>;
|
|
127
|
+
cursor(cursor: unknown): void;
|
|
128
|
+
putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView): Promise<void>;
|
|
129
|
+
getBlob(name: string): Promise<ArrayBuffer>;
|
|
130
|
+
leave(): Promise<void>;
|
|
131
|
+
};
|
|
100
132
|
/** Call the app's own module (`ui_call` export) with the opened files. */
|
|
101
133
|
call<T = unknown>(method: string, params?: unknown): Promise<T>;
|
|
102
134
|
/** Queue one of the app's actions on the opened files; answers the op. */
|
|
@@ -311,6 +343,40 @@ function bridge(port: MessagePort, opts: ConnectOptions): FilexApp {
|
|
|
311
343
|
const p = await payload(data);
|
|
312
344
|
return request<DownloadResult>('ui.download', { name, data: p.data, mime }, p.transfer);
|
|
313
345
|
},
|
|
346
|
+
async print(name, pdf) {
|
|
347
|
+
const p = await payload(pdf);
|
|
348
|
+
return request<PrintResult>('ui.print', { name, data: p.data, mime: 'application/pdf' }, p.transfer);
|
|
349
|
+
},
|
|
350
|
+
coedit: {
|
|
351
|
+
join(index = 0) {
|
|
352
|
+
return request<CoEditHello>('coedit.join', { index });
|
|
353
|
+
},
|
|
354
|
+
subscribe(from: number) {
|
|
355
|
+
return request<void>('coedit.subscribe', { from });
|
|
356
|
+
},
|
|
357
|
+
append(kind: CoEditKind, body: unknown) {
|
|
358
|
+
return request<CoEditAppendResult>('coedit.append', { kind, body });
|
|
359
|
+
},
|
|
360
|
+
async lease(op: 'acquire' | 'release', changesSeen: number) {
|
|
361
|
+
const r = await request<CoEditLeaseResult>('coedit.lease', { op, changesSeen });
|
|
362
|
+
return !!r?.granted;
|
|
363
|
+
},
|
|
364
|
+
cursor(cursor: unknown) {
|
|
365
|
+
notify('coedit.cursor', { cursor });
|
|
366
|
+
},
|
|
367
|
+
async putBlob(name: string, data: Blob | ArrayBuffer | ArrayBufferView) {
|
|
368
|
+
const p = await payload(data);
|
|
369
|
+
if (!(p.data instanceof ArrayBuffer)) throw new FilexError({ code: 'invalid', message: 'a blob is bytes' });
|
|
370
|
+
await request<void>('coedit.blob.put', { name, data: p.data }, p.transfer);
|
|
371
|
+
},
|
|
372
|
+
async getBlob(name: string) {
|
|
373
|
+
const r = await request<CoEditBlobGetResult>('coedit.blob.get', { name });
|
|
374
|
+
return r.bytes;
|
|
375
|
+
},
|
|
376
|
+
async leave() {
|
|
377
|
+
await request<void>('coedit.leave');
|
|
378
|
+
},
|
|
379
|
+
},
|
|
314
380
|
dirty(on) {
|
|
315
381
|
notify('ui.dirty', { dirty: !!on });
|
|
316
382
|
},
|
package/src/protocol.ts
CHANGED
|
@@ -52,7 +52,23 @@ export type Method =
|
|
|
52
52
|
| 'state.get'
|
|
53
53
|
| 'state.set'
|
|
54
54
|
/** 0.52.0: what the app's license says (a paid app from a store), `LicenseInfo`. */
|
|
55
|
-
| 'license.get'
|
|
55
|
+
| 'license.get'
|
|
56
|
+
/** 0.55.0: filex prints a PDF the app hands it (`PrintParams`, grant `ui:print`). */
|
|
57
|
+
| 'ui.print'
|
|
58
|
+
/**
|
|
59
|
+
* 0.55.0, editing together (`coedit.*`, task #189): the session's log the
|
|
60
|
+
* host seals, orders through filex's relay and opens again. Defined so an
|
|
61
|
+
* app can be written against them; filex 0.55 offers no relay yet, so each
|
|
62
|
+
* answers `unavailable` - edit alone then (see `CoEditHello`).
|
|
63
|
+
*/
|
|
64
|
+
| 'coedit.join'
|
|
65
|
+
| 'coedit.subscribe'
|
|
66
|
+
| 'coedit.append'
|
|
67
|
+
| 'coedit.lease'
|
|
68
|
+
| 'coedit.cursor'
|
|
69
|
+
| 'coedit.blob.put'
|
|
70
|
+
| 'coedit.blob.get'
|
|
71
|
+
| 'coedit.leave';
|
|
56
72
|
|
|
57
73
|
export const METHODS: readonly Method[] = [
|
|
58
74
|
'session.get',
|
|
@@ -71,6 +87,15 @@ export const METHODS: readonly Method[] = [
|
|
|
71
87
|
'state.get',
|
|
72
88
|
'state.set',
|
|
73
89
|
'license.get',
|
|
90
|
+
'ui.print',
|
|
91
|
+
'coedit.join',
|
|
92
|
+
'coedit.subscribe',
|
|
93
|
+
'coedit.append',
|
|
94
|
+
'coedit.lease',
|
|
95
|
+
'coedit.cursor',
|
|
96
|
+
'coedit.blob.put',
|
|
97
|
+
'coedit.blob.get',
|
|
98
|
+
'coedit.leave',
|
|
74
99
|
];
|
|
75
100
|
|
|
76
101
|
/** What the host tells the app, unasked. */
|
|
@@ -84,7 +109,17 @@ export type HostEvent =
|
|
|
84
109
|
/** The person closed the frame's surroundings; last chance to say so. */
|
|
85
110
|
| 'close.request'
|
|
86
111
|
/** An administrator installed a new version of this app: reload to use it. */
|
|
87
|
-
| 'app.updated'
|
|
112
|
+
| 'app.updated'
|
|
113
|
+
/** Editing together: the next entry of the session's log, opened. `data: CoEditEntry`. */
|
|
114
|
+
| 'coedit.entry'
|
|
115
|
+
/** Editing together: another member's cursor (not kept). `data: CoEditCursor`. */
|
|
116
|
+
| 'coedit.cursor'
|
|
117
|
+
/**
|
|
118
|
+
* Editing together: the host stopped handing entries over (the app fell
|
|
119
|
+
* behind, or the connection dropped). `data: {from}` - subscribe again
|
|
120
|
+
* from the last entry the app holds.
|
|
121
|
+
*/
|
|
122
|
+
| 'coedit.dropped';
|
|
88
123
|
|
|
89
124
|
/** What the host may ask the app (and wait for). */
|
|
90
125
|
export type HostRequest =
|
|
@@ -178,6 +213,19 @@ export interface FileInfo {
|
|
|
178
213
|
/** It cannot be saved over: read-only storage, view-only opening, or the
|
|
179
214
|
* app holds no `files:write`. */
|
|
180
215
|
readOnly: boolean;
|
|
216
|
+
/**
|
|
217
|
+
* 0.55.0: the file is end-to-end encrypted - `folder` in an encrypted
|
|
218
|
+
* folder, `vault` in a vault (where only one person edits at a time),
|
|
219
|
+
* `file` for a single encrypted file (`.fxe`). As the server says it;
|
|
220
|
+
* ABSENT for a file that is not encrypted.
|
|
221
|
+
*
|
|
222
|
+
* ⚠ filex 0.55 decrypts nothing for an app: the server holds such a file
|
|
223
|
+
* only as ciphertext and has no key, so `file.read` is refused
|
|
224
|
+
* (`failed` with `encrypted`), a save is refused, and `readOnly` is true.
|
|
225
|
+
* The explorer offers no app an encrypted file in the first place; the
|
|
226
|
+
* field is information, not a promise of plaintext.
|
|
227
|
+
*/
|
|
228
|
+
encrypted?: 'folder' | 'vault' | 'file';
|
|
181
229
|
}
|
|
182
230
|
|
|
183
231
|
/** What `session.get` answers — what the app knows about where it runs. */
|
|
@@ -221,6 +269,12 @@ export interface SaveParams {
|
|
|
221
269
|
/** A transferred ReadableStream<Uint8Array>, an ArrayBuffer or a string. */
|
|
222
270
|
data: ReadableStream<Uint8Array> | ArrayBuffer | string;
|
|
223
271
|
mime?: string;
|
|
272
|
+
/**
|
|
273
|
+
* 0.55.0, editing together: the last log entry (`CoEditEntry.seq`) the
|
|
274
|
+
* saved document holds, so the session knows what is saved. Ignored
|
|
275
|
+
* outside a session.
|
|
276
|
+
*/
|
|
277
|
+
through?: number;
|
|
224
278
|
}
|
|
225
279
|
|
|
226
280
|
export interface SaveResult {
|
|
@@ -246,6 +300,143 @@ export interface DownloadResult {
|
|
|
246
300
|
size: number;
|
|
247
301
|
}
|
|
248
302
|
|
|
303
|
+
/**
|
|
304
|
+
* `ui.print` (filex 0.55.0): a PDF for the browser's print dialog. A
|
|
305
|
+
* sandboxed frame may not open that dialog (measured: Chromium ignores
|
|
306
|
+
* `print()` there and says so, Firefox ignores it), so filex prints the PDF
|
|
307
|
+
* from a page of its own, with the app's `ui:print` grant, never over
|
|
308
|
+
* LIMITS.maxPrintBytes. filex asks every time, above the frame, and the
|
|
309
|
+
* dialog opens only on the person's click on filex's Allow. The print dialog
|
|
310
|
+
* can save the PDF too, so `ui:print` is the same kind of grant as
|
|
311
|
+
* `ui:download`. filex 0.54 and older answer `unknown_method`.
|
|
312
|
+
*/
|
|
313
|
+
export interface PrintParams {
|
|
314
|
+
/** The document's name, for the question filex may ask - a name, not a path. */
|
|
315
|
+
name: string;
|
|
316
|
+
/** The PDF: a transferred ArrayBuffer or ReadableStream<Uint8Array>. */
|
|
317
|
+
data: ReadableStream<Uint8Array> | ArrayBuffer;
|
|
318
|
+
/** `application/pdf` when given; nothing else is printed. */
|
|
319
|
+
mime?: string;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export interface PrintResult {
|
|
323
|
+
/** The browser's print dialog was opened with the PDF. */
|
|
324
|
+
printed: true;
|
|
325
|
+
size: number;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* ── editing together (coedit.*, filex 0.55.0: defined, not offered) ────── */
|
|
329
|
+
|
|
330
|
+
/** What a member appends: the editor's changes, a lock request, a release. */
|
|
331
|
+
export type CoEditKind = 'changes' | 'lock' | 'release';
|
|
332
|
+
|
|
333
|
+
/** A member of the session. */
|
|
334
|
+
export interface CoEditMember {
|
|
335
|
+
/** The relay's id for the connection. */
|
|
336
|
+
client: string;
|
|
337
|
+
/** An opaque id of the person, the same for each of their connections in
|
|
338
|
+
* this session; never an account id. */
|
|
339
|
+
user: string;
|
|
340
|
+
/** The person's display name. */
|
|
341
|
+
name: string;
|
|
342
|
+
/** The editor's per-session user index: given once, never twice. */
|
|
343
|
+
indexUser: number;
|
|
344
|
+
/** May append changes (an editor role and the right to change the file). */
|
|
345
|
+
canEdit: boolean;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** `coedit.join` params: join (or start) the session of an opened file. */
|
|
349
|
+
export interface CoEditJoinParams {
|
|
350
|
+
index?: number;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* What `coedit.join` answers: who the app is in the session and how far
|
|
355
|
+
* the log has gone. The entries come as `coedit.entry` events once the app
|
|
356
|
+
* subscribes (`coedit.subscribe` from 0 for the whole log). `unavailable`
|
|
357
|
+
* when filex offers no editing together here (filex 0.55, a vault, a file
|
|
358
|
+
* that cannot be shared): the app edits alone.
|
|
359
|
+
*/
|
|
360
|
+
export interface CoEditHello {
|
|
361
|
+
session: string;
|
|
362
|
+
me: CoEditMember;
|
|
363
|
+
/** The last entry's seq (0: an empty log). */
|
|
364
|
+
head: number;
|
|
365
|
+
/** The last `changes` entry's seq. */
|
|
366
|
+
changesHead: number;
|
|
367
|
+
/** The last entry the file on the storage holds. */
|
|
368
|
+
savedThrough: number;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** `coedit.subscribe` params: entries after `from` arrive as `coedit.entry`. */
|
|
372
|
+
export interface CoEditSubscribeParams {
|
|
373
|
+
from: number;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** `coedit.append` params: the host seals it and the relay places it. */
|
|
377
|
+
export interface CoEditAppendParams {
|
|
378
|
+
kind: CoEditKind;
|
|
379
|
+
body: unknown;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export interface CoEditAppendResult {
|
|
383
|
+
/** Where it landed; it comes back as a `coedit.entry` too. */
|
|
384
|
+
seq: number;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** `coedit.lease`: the changes lease (one writer at a time, one that has
|
|
388
|
+
* seen every change). */
|
|
389
|
+
export interface CoEditLeaseParams {
|
|
390
|
+
op: 'acquire' | 'release';
|
|
391
|
+
/** The seq of the last `changes` entry the app applied. */
|
|
392
|
+
changesSeen: number;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface CoEditLeaseResult {
|
|
396
|
+
granted: boolean;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/** `coedit.cursor` params: passed to the other members, not kept. */
|
|
400
|
+
export interface CoEditCursorParams {
|
|
401
|
+
cursor: unknown;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/** `coedit.blob.put` / `coedit.blob.get`: the session's sealed blobs (the
|
|
405
|
+
* base document, images added while editing). A name, not a path. */
|
|
406
|
+
export interface CoEditBlobPutParams {
|
|
407
|
+
name: string;
|
|
408
|
+
data: ArrayBuffer;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export interface CoEditBlobGetParams {
|
|
412
|
+
name: string;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export interface CoEditBlobGetResult {
|
|
416
|
+
name: string;
|
|
417
|
+
bytes: ArrayBuffer;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
interface CoEditEntryBase {
|
|
421
|
+
seq: number;
|
|
422
|
+
/** The relay's time, Unix ms - the same for every member. */
|
|
423
|
+
at: number;
|
|
424
|
+
client: string;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** One entry of the session's log, opened and checked by filex. */
|
|
428
|
+
export type CoEditEntry =
|
|
429
|
+
| (CoEditEntryBase & { kind: CoEditKind; body: unknown })
|
|
430
|
+
| (CoEditEntryBase & { kind: 'join'; member: CoEditMember })
|
|
431
|
+
| (CoEditEntryBase & { kind: 'leave' })
|
|
432
|
+
| (CoEditEntryBase & { kind: 'saved'; through: number });
|
|
433
|
+
|
|
434
|
+
/** `coedit.cursor` event data. */
|
|
435
|
+
export interface CoEditCursor {
|
|
436
|
+
client: string;
|
|
437
|
+
cursor: unknown;
|
|
438
|
+
}
|
|
439
|
+
|
|
249
440
|
/**
|
|
250
441
|
* `file.saveAs`: a NEW file, in a folder the person picks in filex's own
|
|
251
442
|
* folder dialog (the one its Move to… uses), which opens in the opened
|
|
@@ -325,6 +516,13 @@ export const LIMITS = {
|
|
|
325
516
|
connectTimeoutMs: 10_000,
|
|
326
517
|
/** The largest file `ui.download` hands the person (streamed or not). */
|
|
327
518
|
maxDownloadBytes: 256 << 20,
|
|
519
|
+
/**
|
|
520
|
+
* The largest PDF `ui.print` prints (filex 0.55). Lower than a download:
|
|
521
|
+
* a download can stream to disk, a print is held whole in the page (one
|
|
522
|
+
* Blob) until the print dialog has read it, and a document worth printing
|
|
523
|
+
* is far smaller.
|
|
524
|
+
*/
|
|
525
|
+
maxPrintBytes: 64 << 20,
|
|
328
526
|
} as const;
|
|
329
527
|
|
|
330
528
|
/* ── guards ────────────────────────────────────────────────────────────── */
|