fauna-api 1.0.686 → 1.0.693

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.
Files changed (39) hide show
  1. package/dist/src/authentification.d.ts +34 -0
  2. package/dist/src/authentification.js +135 -0
  3. package/dist/src/calendar.d.ts +5 -2
  4. package/dist/src/calendar.js +5 -2
  5. package/dist/src/cicada/inboxes.d.ts +7 -1
  6. package/dist/src/cicada/inboxes.js +2 -0
  7. package/dist/src/cicada/phone.d.ts +45 -0
  8. package/dist/src/cicada/phone.js +46 -0
  9. package/dist/src/extensions/web/session.d.ts +1 -3
  10. package/dist/src/extensions/web/session.js +0 -1
  11. package/dist/src/groups.d.ts +167 -20
  12. package/dist/src/groups.js +287 -31
  13. package/dist/src/marmot/web.d.ts +27 -9
  14. package/dist/src/marmot/web.js +118 -79
  15. package/dist/src/parrot/room.d.ts +6 -2
  16. package/dist/src/parrot/room.js +11 -0
  17. package/dist/src/parrot/share.d.ts +4 -1
  18. package/dist/src/parrot/share.js +4 -2
  19. package/dist/src/rooms.d.ts +37 -11
  20. package/dist/src/rooms.js +46 -14
  21. package/dist/src/shared/server.d.ts +23 -0
  22. package/dist/src/shared/server.js +41 -0
  23. package/dist/src/ui/authentification.d.ts +6 -2
  24. package/dist/src/ui/authentification.js +36 -10
  25. package/dist/src/ui/browser.js +1 -1
  26. package/dist/src/ui/chat.js +8 -6
  27. package/dist/src/ui/chatSequence.d.ts +3 -2
  28. package/dist/src/ui/chatSequence.js +7 -2
  29. package/dist/src/ui/drive.js +1 -1
  30. package/dist/src/ui/index.d.ts +2 -0
  31. package/dist/src/ui/index.js +2 -0
  32. package/dist/src/ui/maps.d.ts +1 -1
  33. package/dist/src/ui/maps.js +3 -1
  34. package/dist/src/ui/markdown.d.ts +5 -0
  35. package/dist/src/ui/markdown.js +33 -0
  36. package/dist/src/ui/toast.d.ts +2 -2
  37. package/dist/src/ui/toast.js +5 -5
  38. package/dist/src/webhooks.d.ts +2 -0
  39. package/package.json +2 -2
@@ -1,5 +1,95 @@
1
1
  import { Fauna } from "../index.js";
2
2
  import { LastMessage } from "./parrot/chat.js";
3
+ export const GroupRef = {
4
+ fromJSON(json) {
5
+ return {
6
+ id: String(json.id),
7
+ name: String(json.name),
8
+ ...(json.icon ? { icon: String(json.icon) } : {}),
9
+ };
10
+ },
11
+ };
12
+ export const GroupMember = {
13
+ fromJSON(json) {
14
+ return {
15
+ email: String(json.email),
16
+ ...(json.displayName ? { displayName: String(json.displayName) } : {}),
17
+ ...(json.icon ? { icon: String(json.icon) } : {}),
18
+ through: String(json.through),
19
+ };
20
+ },
21
+ };
22
+ export const Group = {
23
+ fromJSON(json) {
24
+ return {
25
+ id: String(json.id),
26
+ name: String(json.name),
27
+ ...(json.icon ? { icon: String(json.icon) } : {}),
28
+ balance: Number(json.balance),
29
+ createdAt: new Date(json.createdAt),
30
+ admins: json.admins.map(String),
31
+ people: json.people.map(String),
32
+ groups: json.groups.map(GroupRef.fromJSON),
33
+ members: json.members.map(GroupMember.fromJSON),
34
+ through: String(json.through),
35
+ };
36
+ },
37
+ };
38
+ export const JoinLink = {
39
+ fromJSON(json) {
40
+ return {
41
+ code: String(json.code),
42
+ uses: Number(json.uses),
43
+ ...(typeof json.maxUses === 'number' ? { maxUses: json.maxUses } : {}),
44
+ expires: new Date(json.expires),
45
+ };
46
+ },
47
+ };
48
+ export const GroupSummary = {
49
+ fromJSON(json) {
50
+ return {
51
+ id: String(json.id),
52
+ name: String(json.name),
53
+ ...(json.icon ? { icon: String(json.icon) } : {}),
54
+ memberCount: Number(json.memberCount),
55
+ };
56
+ },
57
+ };
58
+ export const MatchedGroup = {
59
+ fromJSON(json) {
60
+ return { ...GroupSummary.fromJSON(json), through: String(json.through) };
61
+ },
62
+ };
63
+ export const GroupMatch = {
64
+ fromJSON(json) {
65
+ return {
66
+ profile: {
67
+ email: String(json.profile.email),
68
+ ...(json.profile.displayName ? { displayName: String(json.profile.displayName) } : {}),
69
+ ...(json.profile.icon ? { icon: String(json.profile.icon) } : {}),
70
+ },
71
+ groups: json.groups.map(MatchedGroup.fromJSON),
72
+ };
73
+ },
74
+ };
75
+ export const GroupNest = {
76
+ fromJSON(json) {
77
+ return {
78
+ token: String(json.token),
79
+ parent: GroupRef.fromJSON(json.parent),
80
+ child: GroupRef.fromJSON(json.child),
81
+ from: json.from === 'child' ? 'child' : 'parent',
82
+ ...(json.by ? { by: String(json.by) } : {}),
83
+ expires: new Date(json.expires),
84
+ };
85
+ },
86
+ };
87
+ export const NestOffer = {
88
+ fromJSON(json) {
89
+ return json.nested === true ? { nested: true } : { nested: false, token: String(json.token) };
90
+ },
91
+ };
92
+ /** Start a group. You are its first admin. Needs a subscription. */
3
93
  export async function create(credentials, name) {
4
94
  const response = await fetch(`${Fauna.getURL()}/groups`, {
5
95
  method: "POST",
@@ -12,9 +102,10 @@ export async function create(credentials, name) {
12
102
  if (!response.ok) {
13
103
  throw new Error(await response.text());
14
104
  }
15
- const raw = await response.json();
16
- return { ...raw, createdAt: new Date(raw.createdAt) };
105
+ return Group.fromJSON(await response.json());
17
106
  }
107
+ /** Every group you are in, directly or through a nested group, oldest first.
108
+ * Each says how you are in it (`through`). */
18
109
  export async function list(authorization) {
19
110
  const response = await fetch(`${Fauna.getURL()}/groups`, {
20
111
  method: "GET",
@@ -25,11 +116,10 @@ export async function list(authorization) {
25
116
  if (!response.ok) {
26
117
  throw new Error(await response.text());
27
118
  }
28
- const raw = await response.json();
29
- return raw.map((group) => ({ ...group, createdAt: new Date(group.createdAt) }));
119
+ return (await response.json()).map(Group.fromJSON);
30
120
  }
31
- /** People matching a partial email or name, best first, each with their groups.
32
- * Only people in at least one group come back. */
121
+ /** People matching a partial email or name, best first, each with every group
122
+ * they are in and how. Only people in at least one group come back. */
33
123
  export async function query(authorization, profile) {
34
124
  const response = await fetch(`${Fauna.getURL()}/groups/query`, {
35
125
  method: "POST",
@@ -42,7 +132,7 @@ export async function query(authorization, profile) {
42
132
  if (!response.ok) {
43
133
  throw new Error(await response.text());
44
134
  }
45
- return await response.json();
135
+ return (await response.json()).map(GroupMatch.fromJSON);
46
136
  }
47
137
  /** Name, icon and member count of groups by id, including groups you are not in.
48
138
  * Unknown ids are left out. */
@@ -58,8 +148,9 @@ export async function summaries(authorization, ids) {
58
148
  if (!response.ok) {
59
149
  throw new Error(await response.text());
60
150
  }
61
- return await response.json();
151
+ return (await response.json()).map(GroupSummary.fromJSON);
62
152
  }
153
+ /** A group you are in, directly or through a nested group. */
63
154
  export async function get(authorization, id) {
64
155
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}`, {
65
156
  method: "GET",
@@ -70,9 +161,9 @@ export async function get(authorization, id) {
70
161
  if (!response.ok) {
71
162
  throw new Error(await response.text());
72
163
  }
73
- const raw = await response.json();
74
- return { ...raw, createdAt: new Date(raw.createdAt) };
164
+ return Group.fromJSON(await response.json());
75
165
  }
166
+ /** Change the group's name or icon. Admins only. */
76
167
  export async function edit(credentials, id, update) {
77
168
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}`, {
78
169
  method: "PATCH",
@@ -85,14 +176,27 @@ export async function edit(credentials, id, update) {
85
176
  if (!response.ok) {
86
177
  throw new Error(await response.text());
87
178
  }
88
- const raw = await response.json();
89
- return { ...raw, createdAt: new Date(raw.createdAt) };
179
+ return Group.fromJSON(await response.json());
90
180
  }
91
- /** Add a user to a group by email. Resolves `{ status: 'added' }` if they were
92
- * added directly (they have a Fauna account), or `{ status: 'invited', token }`
93
- * if a pending invite was stored instead (no Fauna account for that email yet).
94
- * Fauna emails the invite unless `notify` is false, so an app can send its own
95
- * email carrying the token. */
181
+ /** Delete a group with its chats, files, calendar, rooms, webhooks and
182
+ * partnerships. It leaves every group it was in; the groups inside it stay.
183
+ * Its direct admins may, and so may anyone holding `fauna/groups:delete:global`. */
184
+ export async function remove(credentials, id) {
185
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}`, {
186
+ method: "DELETE",
187
+ headers: {
188
+ "Authorization": `Token ${credentials.token}`
189
+ }
190
+ });
191
+ if (!response.ok) {
192
+ throw new Error(await response.text());
193
+ }
194
+ }
195
+ /** Invite an email to a group. Admins only. Nobody joins until they accept the
196
+ * invite with its token (`acceptInvite`), whether or not the email already has
197
+ * a Fauna account, and the answer is the same either way. Fauna emails the
198
+ * invite unless `notify` is false, so an app can send its own email carrying
199
+ * the token. Resolves to the token. */
96
200
  export async function invite(credentials, id, email, options = {}) {
97
201
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/invite`, {
98
202
  method: "POST",
@@ -105,9 +209,10 @@ export async function invite(credentials, id, email, options = {}) {
105
209
  if (!response.ok) {
106
210
  throw new Error(await response.text());
107
211
  }
108
- return await response.json();
212
+ return (await response.json()).token;
109
213
  }
110
- /** Join the group an email invite points at, using the token from the invite. */
214
+ /** Join the group an email invite points at, using the token from the invite.
215
+ * `alreadyMember` says you were directly in it already. */
111
216
  export async function acceptInvite(credentials, token) {
112
217
  const response = await fetch(`${Fauna.getURL()}/groups/invites/accept`, {
113
218
  method: "POST",
@@ -121,17 +226,19 @@ export async function acceptInvite(credentials, token) {
121
226
  throw new Error(await response.text());
122
227
  }
123
228
  const raw = await response.json();
124
- return {
125
- group: { ...raw.group, createdAt: new Date(raw.group.createdAt) },
126
- alreadyMember: raw.alreadyMember
127
- };
229
+ return { group: Group.fromJSON(raw.group), alreadyMember: raw.alreadyMember === true };
128
230
  }
129
- export async function generateLink(credentials, id) {
231
+ /** A join code good for seven days. Admins only. People join with it, and admins
232
+ * of other groups put their group inside this one with it. `maxUses` caps how
233
+ * many it lets in; without it, any number can join until it expires. */
234
+ export async function generateLink(credentials, id, options = {}) {
130
235
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/link`, {
131
236
  method: "POST",
132
237
  headers: {
238
+ "Content-Type": "application/json",
133
239
  "Authorization": `Token ${credentials.token}`
134
- }
240
+ },
241
+ body: JSON.stringify({ maxUses: options.maxUses })
135
242
  });
136
243
  if (!response.ok) {
137
244
  throw new Error(await response.text());
@@ -139,24 +246,52 @@ export async function generateLink(credentials, id) {
139
246
  const json = await response.json();
140
247
  return json.code;
141
248
  }
142
- export async function join(credentials, code) {
249
+ /** The group's join links that still work, newest first. Admins only. */
250
+ export async function links(credentials, id) {
251
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/links`, {
252
+ method: "GET",
253
+ headers: {
254
+ "Authorization": `Token ${credentials.token}`
255
+ }
256
+ });
257
+ if (!response.ok) {
258
+ throw new Error(await response.text());
259
+ }
260
+ return (await response.json()).map(JoinLink.fromJSON);
261
+ }
262
+ /** A join link stops working. Admins only. */
263
+ export async function revokeLink(credentials, id, code) {
264
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/links/${encodeURIComponent(code)}`, {
265
+ method: "DELETE",
266
+ headers: {
267
+ "Authorization": `Token ${credentials.token}`
268
+ }
269
+ });
270
+ if (!response.ok) {
271
+ throw new Error(await response.text());
272
+ }
273
+ }
274
+ /** Join a group with a join code. With `group`, a group you administer goes
275
+ * inside it instead, and you are in it through that group. `alreadyMember` says
276
+ * you (or `group`) were in it already. */
277
+ export async function join(credentials, code, group) {
143
278
  const response = await fetch(`${Fauna.getURL()}/groups/join`, {
144
279
  method: "POST",
145
280
  headers: {
146
281
  "Content-Type": "application/json",
147
282
  "Authorization": `Token ${credentials.token}`
148
283
  },
149
- body: JSON.stringify({ code })
284
+ body: JSON.stringify({ code, group })
150
285
  });
151
286
  if (!response.ok) {
152
287
  throw new Error(await response.text());
153
288
  }
154
289
  const raw = await response.json();
155
- return {
156
- group: { ...raw.group, createdAt: new Date(raw.group.createdAt) },
157
- alreadyMember: raw.alreadyMember
158
- };
290
+ return { group: Group.fromJSON(raw.group), alreadyMember: raw.alreadyMember === true };
159
291
  }
292
+ /** Leave a group you are directly in. The last admin can't while anyone else is
293
+ * in it, directly or through a nested group: make someone else an admin, or
294
+ * delete the group. When nobody else is in it, leaving deletes it. */
160
295
  export async function leave(credentials, id) {
161
296
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/membership`, {
162
297
  method: "DELETE",
@@ -168,6 +303,7 @@ export async function leave(credentials, id) {
168
303
  throw new Error(await response.text());
169
304
  }
170
305
  }
306
+ /** Remove a person who is directly in the group. Admins only. */
171
307
  export async function kick(credentials, id, email) {
172
308
  const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/members/${encodeURIComponent(email)}`, {
173
309
  method: "DELETE",
@@ -179,6 +315,126 @@ export async function kick(credentials, id, email) {
179
315
  throw new Error(await response.text());
180
316
  }
181
317
  }
318
+ /** Make a person who is directly in the group an admin. Admins only. Someone in
319
+ * it only through a nested group is invited directly first. */
320
+ export async function promote(credentials, id, email) {
321
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/admins/${encodeURIComponent(email)}`, {
322
+ method: "POST",
323
+ headers: {
324
+ "Authorization": `Token ${credentials.token}`
325
+ }
326
+ });
327
+ if (!response.ok) {
328
+ throw new Error(await response.text());
329
+ }
330
+ return Group.fromJSON(await response.json());
331
+ }
332
+ /** Make an admin a member again. Admins only; a group keeps at least one admin. */
333
+ export async function demote(credentials, id, email) {
334
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/admins/${encodeURIComponent(email)}`, {
335
+ method: "DELETE",
336
+ headers: {
337
+ "Authorization": `Token ${credentials.token}`
338
+ }
339
+ });
340
+ if (!response.ok) {
341
+ throw new Error(await response.text());
342
+ }
343
+ return Group.fromJSON(await response.json());
344
+ }
345
+ /** Invite group `child` inside group `id`; the child's admins accept with the
346
+ * token (`acceptNest`). Admins of `id` only. Nests at once when you administer
347
+ * both. Unless `notify` is false, the child's admins hear about it in their
348
+ * Fauna inbox. Nesting is at most 5 groups deep and never makes a loop. */
349
+ export async function inviteGroup(credentials, id, child, options = {}) {
350
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/groups`, {
351
+ method: "POST",
352
+ headers: {
353
+ "Content-Type": "application/json",
354
+ "Authorization": `Token ${credentials.token}`
355
+ },
356
+ body: JSON.stringify({ group: child, notify: options.notify })
357
+ });
358
+ if (!response.ok) {
359
+ throw new Error(await response.text());
360
+ }
361
+ return NestOffer.fromJSON(await response.json());
362
+ }
363
+ /** Ask to put group `id` inside group `parent`; the parent's admins approve with
364
+ * the token (`acceptNest`). Admins of `id` only. Nests at once when you
365
+ * administer both. Unless `notify` is false, the parent's admins hear about it
366
+ * in their Fauna inbox. */
367
+ export async function requestGroup(credentials, id, parent, options = {}) {
368
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/parents`, {
369
+ method: "POST",
370
+ headers: {
371
+ "Content-Type": "application/json",
372
+ "Authorization": `Token ${credentials.token}`
373
+ },
374
+ body: JSON.stringify({ group: parent, notify: options.notify })
375
+ });
376
+ if (!response.ok) {
377
+ throw new Error(await response.text());
378
+ }
379
+ return NestOffer.fromJSON(await response.json());
380
+ }
381
+ /** The invites and requests waiting on or from a group, newest first. Admins only. */
382
+ export async function nests(credentials, id, query) {
383
+ const params = new URLSearchParams({ cursor: String(query.cursor), max: String(query.max) });
384
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/nests?${params}`, {
385
+ method: "GET",
386
+ headers: {
387
+ "Authorization": `Token ${credentials.token}`
388
+ }
389
+ });
390
+ if (!response.ok) {
391
+ throw new Error(await response.text());
392
+ }
393
+ return (await response.json()).map(GroupNest.fromJSON);
394
+ }
395
+ /** Accept an invite into a group, or approve a request to join one, by its
396
+ * token. An admin of the side it waits on only. Resolves to the parent group. */
397
+ export async function acceptNest(credentials, token) {
398
+ const response = await fetch(`${Fauna.getURL()}/groups/nests/accept`, {
399
+ method: "POST",
400
+ headers: {
401
+ "Content-Type": "application/json",
402
+ "Authorization": `Token ${credentials.token}`
403
+ },
404
+ body: JSON.stringify({ token })
405
+ });
406
+ if (!response.ok) {
407
+ throw new Error(await response.text());
408
+ }
409
+ return Group.fromJSON(await response.json());
410
+ }
411
+ /** Drop a waiting invite or request by its token. An admin of either group. */
412
+ export async function revokeNest(credentials, token) {
413
+ const response = await fetch(`${Fauna.getURL()}/groups/nests/revoke`, {
414
+ method: "POST",
415
+ headers: {
416
+ "Content-Type": "application/json",
417
+ "Authorization": `Token ${credentials.token}`
418
+ },
419
+ body: JSON.stringify({ token })
420
+ });
421
+ if (!response.ok) {
422
+ throw new Error(await response.text());
423
+ }
424
+ }
425
+ /** Take group `child` out of group `id`. An admin of either group. Everyone who
426
+ * was in `id` only through it loses it. */
427
+ export async function unnest(credentials, id, child) {
428
+ const response = await fetch(`${Fauna.getURL()}/groups/${encodeURIComponent(id)}/groups/${encodeURIComponent(child)}`, {
429
+ method: "DELETE",
430
+ headers: {
431
+ "Authorization": `Token ${credentials.token}`
432
+ }
433
+ });
434
+ if (!response.ok) {
435
+ throw new Error(await response.text());
436
+ }
437
+ }
182
438
  function hydratePartnership(raw) {
183
439
  return {
184
440
  ...raw,
@@ -162,8 +162,9 @@ export declare class DomRecorder {
162
162
  stop(): void;
163
163
  }
164
164
  /**
165
- * Re-render a `WebSnapshot` into a target document (typically an isolated iframe
166
- * or shadow root on the viewer) and apply incoming `WebMutation`s. The replayer
165
+ * Re-render a shared page into a frame inside `mount` and apply incoming `WebMutation`s.
166
+ * The page is someone else's, so the frame is sandboxed without scripts: the replayer
167
+ * writes into it from this page, and nothing the page carries ever runs. The replayer
167
168
  * keeps its own id->Node map mirroring the recorder's synthetic ids.
168
169
  */
169
170
  export declare class DomReplayer {
@@ -185,9 +186,15 @@ export declare class DomReplayer {
185
186
  } | null;
186
187
  private docRoot;
187
188
  /**
188
- * Apply a full cleaned HTML document (or fragment). Uses an iframe when the
189
- * payload looks like a full document so `<html>/<head>/<body>` structure and
190
- * DFS ids match the host.
189
+ * A fresh frame for a replay, loaded with `srcdoc`. Same origin without scripts: this
190
+ * page can write into it, and the shared page can't run anything there. Null when a
191
+ * newer replay started while it loaded.
192
+ */
193
+ private openFrame;
194
+ private flushQueued;
195
+ /**
196
+ * Apply a cleaned HTML document, or a fragment, which the frame parses into one.
197
+ * The `<html>/<head>/<body>` structure and DFS ids match the host's.
191
198
  */
192
199
  applyHtml(html: string, viewport?: {
193
200
  width: number;
@@ -229,10 +236,11 @@ export declare class DomReplayer {
229
236
  */
230
237
  private mirrorStates;
231
238
  private ownerDoc;
239
+ /** `seen` keeps a subtree that names a node twice (or itself) from building forever. */
232
240
  private build;
233
241
  private buildFromList;
234
242
  /** Render a fresh snapshot, replacing whatever the mount currently holds. */
235
- apply(snapshot: WebSnapshot): void;
243
+ apply(snapshot: WebSnapshot): Promise<void>;
236
244
  /** Apply one incremental mutation to the rendered tree. */
237
245
  mutate(mutation: WebMutation): void;
238
246
  }
@@ -249,10 +257,20 @@ export declare function pageToHtml(root?: Node): {
249
257
  clone: Element;
250
258
  cssUrls: string[];
251
259
  };
252
- /** Install a MutationObserver that posts WebMutations to `window[pushName]`.
260
+ /** Install a MutationObserver that posts WebMutations to `window[pushName]` as
261
+ * `{ token, mutation }`, so the host knows which recorder sent each one.
253
262
  * Framed documents are walked, observed, and re-inlined on navigation, with
254
- * their html, head, and body addressed as the divs `pageToHtml` emits. */
255
- export declare function pageInstallRecorder(pushName: string): void;
263
+ * their html, head, and body addressed as the divs `pageToHtml` emits.
264
+ * With `remoteFrames`, a frame whose document this page can't read is left to
265
+ * the host, which streams it from the frame itself: its wrapper says
266
+ * `data-fauna-iframe="remote"` and holds nothing. With `framed`, this document
267
+ * is such a frame, recorded the way a parent would inline it, and
268
+ * `__faunaRecorder.snapshot()` serializes it. */
269
+ export declare function pageInstallRecorder(pushName: string, options: {
270
+ token: string;
271
+ framed?: boolean;
272
+ remoteFrames?: boolean;
273
+ }): void;
256
274
  export declare function pageStopRecorder(): void;
257
275
  /** Runs in the page: the pointer at (x, y) with the CSS cursor of whatever is under it,
258
276
  * looking inside frames the recorder inlines. */