fauna-api 1.0.759 → 1.0.771

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.
@@ -75,5 +75,7 @@ export declare function allocateBalance(credentials: Credentials, target: ApiKey
75
75
  export declare function generateApiKey(credentials: Credentials): Promise<ApiKey>;
76
76
  export declare function removeApiKey(credentials: Credentials, apiKey: ApiKey): Promise<boolean>;
77
77
  export declare function profile(credentials: Credentials, email: string): Promise<Profile>;
78
+ /** Profiles matching a partial email or name, best first, at most 20. An admin's
79
+ * answer carries more; this keeps the profiles. */
78
80
  export declare function query(credentials: Credentials, profile: Partial<Profile>): Promise<Profile[]>;
79
81
  export declare function editApiKey(credentials: Credentials, apiKey: ApiKey, update: Partial<ApiKey>): Promise<boolean>;
@@ -299,8 +299,10 @@ export async function profile(credentials, email) {
299
299
  }
300
300
  return await response.json();
301
301
  }
302
+ /** Profiles matching a partial email or name, best first, at most 20. An admin's
303
+ * answer carries more; this keeps the profiles. */
302
304
  export async function query(credentials, profile) {
303
- const response = await fetch(`${Fauna.getURL()}/authentification/query`, {
305
+ const response = await fetch(`${Fauna.getURL()}/credentials/query`, {
304
306
  method: "POST",
305
307
  headers: {
306
308
  "Content-Type": "application/json",
@@ -311,7 +313,8 @@ export async function query(credentials, profile) {
311
313
  if (!response.ok) {
312
314
  throw new Error(await response.text());
313
315
  }
314
- return await response.json();
316
+ const json = await response.json();
317
+ return Array.isArray(json) ? json : json.accounts;
315
318
  }
316
319
  export async function editApiKey(credentials, apiKey, update) {
317
320
  const response = await fetch(`${Fauna.getURL()}/authentification/api-keys/${encodeURIComponent(apiKey.id)}`, {
@@ -39,6 +39,10 @@ export type Event = {
39
39
  editable: boolean;
40
40
  /** Your answer, when you're invited. */
41
41
  response?: Answer;
42
+ /** Where `calendar.event.started` is POSTed as each occurrence starts. Only its calendar's owners see it. */
43
+ webhook: string | null;
44
+ /** Signs those deliveries (verify them with `Webhooks.verify`). Only on the `create` or `update` that set `webhook`. */
45
+ webhookSecret?: string;
42
46
  created: Date;
43
47
  updated: Date;
44
48
  };
@@ -57,6 +61,8 @@ export type Draft = {
57
61
  reminder?: number;
58
62
  /** Emails to invite. Each gets an invitation. */
59
63
  attendees?: string[];
64
+ /** A URL to POST `calendar.event.started` to as each occurrence starts. A new one gets a new secret; `update` with `''` removes it. */
65
+ webhook?: string;
60
66
  };
61
67
  /**
62
68
  * Events between `from` and `to` (at most 400 days apart), one per occurrence,
@@ -9,6 +9,7 @@ const Event = {
9
9
  updated: new Date(json.updated),
10
10
  source: json.source ?? null,
11
11
  attendees: json.attendees ?? [],
12
+ webhook: json.webhook ?? null,
12
13
  };
13
14
  },
14
15
  };
@@ -31,7 +31,9 @@ export declare function assign(authorization: Credentials | ApiKey, inboxId: str
31
31
  export declare function listAccounts(authorization: Credentials | ApiKey, inboxId: string): Promise<InboxAccount[]>;
32
32
  /** `dailyLimit` and `spentToday` are dollars per UTC day; at the limit the agent pauses until midnight UTC. */
33
33
  /** `reasoning` is one of the text model's catalog tiers, empty for its default; `voiceModel`
34
- * is the realtime voice the agent talks with on calls, empty for the catalog's first. */
34
+ * is the realtime voice the agent talks with on calls, empty for the catalog's first. With
35
+ * `approveOutbound`, the agent's replies to anyone but the owners wait: Fauna texts the draft
36
+ * to the owners, and an owner's SEND sends it while any other answer drops it. */
35
37
  export type AgentSettings = {
36
38
  model: Text;
37
39
  reasoning: string;
@@ -39,6 +41,7 @@ export type AgentSettings = {
39
41
  instructions: string;
40
42
  dailyLimit: number;
41
43
  spentToday: number;
44
+ approveOutbound: boolean;
42
45
  enabledAt: Date;
43
46
  };
44
47
  export declare const AgentSettings: {
@@ -52,6 +55,7 @@ export declare function setAgent(authorization: Credentials | ApiKey, inbox: str
52
55
  voiceModel?: Voice;
53
56
  instructions: string;
54
57
  dailyLimit: number;
58
+ approveOutbound: boolean;
55
59
  }): Promise<AgentSettings>;
56
60
  export declare function clearAgent(authorization: Credentials | ApiKey, inbox: string): Promise<void>;
57
61
  /**
@@ -66,6 +66,7 @@ export const AgentSettings = {
66
66
  instructions: String(json.instructions ?? ''),
67
67
  dailyLimit: Number(json.dailyLimit ?? 0),
68
68
  spentToday: Number(json.spentToday ?? 0),
69
+ approveOutbound: Boolean(json.approveOutbound),
69
70
  enabledAt: new Date(json.enabledAt),
70
71
  };
71
72
  },
@@ -87,6 +87,34 @@ export type GroupMatch = {
87
87
  export declare const GroupMatch: {
88
88
  fromJSON(json: any): GroupMatch;
89
89
  };
90
+ /** A group `query` found by its name. A signed in admin also sees its admins'
91
+ * emails, how many people are directly in it, and its paid balance, credit and
92
+ * debt in dollars; everyone else gets none of those. */
93
+ export type NamedGroup = {
94
+ id: string;
95
+ name: string;
96
+ icon?: string;
97
+ /** Everyone in it, directly or through a nested group. */
98
+ memberCount: number;
99
+ admins?: string[];
100
+ directCount?: number;
101
+ balance?: number;
102
+ credit?: number;
103
+ debt?: number;
104
+ };
105
+ export declare const NamedGroup: {
106
+ fromJSON(json: any): NamedGroup;
107
+ };
108
+ /** What `query` found: people with every group they are in, and groups by name.
109
+ * Only a signed in admin gets `total`, how many groups there are. */
110
+ export type GroupQuery = {
111
+ people: GroupMatch[];
112
+ groups: NamedGroup[];
113
+ total?: number;
114
+ };
115
+ export declare const GroupQuery: {
116
+ fromJSON(json: any): GroupQuery;
117
+ };
90
118
  /** Which side sent a nesting: `parent` invited the child, whose admins accept;
91
119
  * `child` asked to go inside the parent, whose admins approve. */
92
120
  export type NestSide = 'parent' | 'child';
@@ -132,8 +160,11 @@ export declare function create(credentials: Credentials, name: string): Promise<
132
160
  * Each says how you are in it (`through`). */
133
161
  export declare function list(authorization: Credentials | ApiKey): Promise<Group[]>;
134
162
  /** People matching a partial email or name, best first, each with every group
135
- * they are in and how. Only people in at least one group come back. */
136
- export declare function query(authorization: Credentials | ApiKey, profile: Partial<Profile>): Promise<GroupMatch[]>;
163
+ * they are in and how; only people in at least one group come back. And groups
164
+ * whose name matches either text, best first: only groups you are in, unless a
165
+ * signed in admin asks. An admin finds every group, with its admins and
166
+ * balances, and an admin's empty search lists the first 20 groups by name. */
167
+ export declare function query(authorization: Credentials | ApiKey, profile: Partial<Profile>): Promise<GroupQuery>;
137
168
  /** Name, icon and member count of groups by id, including groups you are not in.
138
169
  * Unknown ids are left out. */
139
170
  export declare function summaries(authorization: Credentials | ApiKey, ids: string[]): Promise<GroupSummary[]>;
@@ -72,6 +72,27 @@ export const GroupMatch = {
72
72
  };
73
73
  },
74
74
  };
75
+ export const NamedGroup = {
76
+ fromJSON(json) {
77
+ return {
78
+ ...GroupSummary.fromJSON(json),
79
+ ...(Array.isArray(json.admins) ? { admins: json.admins.map(String) } : {}),
80
+ ...(typeof json.directCount === 'number' ? { directCount: json.directCount } : {}),
81
+ ...(typeof json.balance === 'number' ? { balance: json.balance } : {}),
82
+ ...(typeof json.credit === 'number' ? { credit: json.credit } : {}),
83
+ ...(typeof json.debt === 'number' ? { debt: json.debt } : {}),
84
+ };
85
+ },
86
+ };
87
+ export const GroupQuery = {
88
+ fromJSON(json) {
89
+ return {
90
+ people: json.people.map(GroupMatch.fromJSON),
91
+ groups: json.groups.map(NamedGroup.fromJSON),
92
+ ...(typeof json.total === 'number' ? { total: json.total } : {}),
93
+ };
94
+ },
95
+ };
75
96
  export const GroupNest = {
76
97
  fromJSON(json) {
77
98
  return {
@@ -130,7 +151,10 @@ export async function list(authorization) {
130
151
  return (await response.json()).map(Group.fromJSON);
131
152
  }
132
153
  /** People matching a partial email or name, best first, each with every group
133
- * they are in and how. Only people in at least one group come back. */
154
+ * they are in and how; only people in at least one group come back. And groups
155
+ * whose name matches either text, best first: only groups you are in, unless a
156
+ * signed in admin asks. An admin finds every group, with its admins and
157
+ * balances, and an admin's empty search lists the first 20 groups by name. */
134
158
  export async function query(authorization, profile) {
135
159
  const response = await fetch(`${Fauna.getURL()}/groups/query`, {
136
160
  method: "POST",
@@ -143,7 +167,7 @@ export async function query(authorization, profile) {
143
167
  if (!response.ok) {
144
168
  throw new Error(await response.text());
145
169
  }
146
- return (await response.json()).map(GroupMatch.fromJSON);
170
+ return GroupQuery.fromJSON(await response.json());
147
171
  }
148
172
  /** Name, icon and member count of groups by id, including groups you are not in.
149
173
  * Unknown ids are left out. */
@@ -166,18 +166,29 @@ export declare class DomRecorder {
166
166
  * The page is someone else's, so the frame is sandboxed without scripts: the replayer
167
167
  * writes into it from this page, and nothing the page carries ever runs. The replayer
168
168
  * keeps its own id->Node map mirroring the recorder's synthetic ids.
169
+ *
170
+ * Every frame the page holds replays as a nested iframe of that frame, which inherits the
171
+ * sandbox, so a framed page lays out against its own viewport: fixed positions, viewport
172
+ * units and media queries resolve against the frame, as they did on the host. Nothing
173
+ * inside the copy can run, so no event there ever reaches this page; the replayer places
174
+ * each frame's document right after the frame joins the copy.
169
175
  */
170
176
  export declare class DomReplayer {
171
177
  private readonly mount;
172
178
  private nodes;
179
+ private ids;
173
180
  private _viewport;
174
181
  private iframe;
175
182
  private queued;
176
183
  private loads;
177
- private frameScopes;
178
- private scopedSheets;
179
- private boxSheet;
180
184
  private boxes;
185
+ /** The box each framed document's node sits in. */
186
+ private boxOf;
187
+ /** Boxes built before they joined the copy, so without a document yet. */
188
+ private unplaced;
189
+ /** Where the host has each scrolled element, and since when. */
190
+ private scrolls;
191
+ private scrollFrame;
181
192
  constructor(mount: Element);
182
193
  /** Host viewport from the last snapshot / viewport mutation (advisory). */
183
194
  getViewport(): {
@@ -207,36 +218,59 @@ export declare class DomReplayer {
207
218
  * `data-fauna-text`, and every other text node follows a `fauna:N` comment. A marker
208
219
  * with no text after it was an empty text node on the host, so one is made here. */
209
220
  private bindMarked;
210
- /** The document the replay renders into: its iframe's for full documents, else the mount's. */
211
- getDocument(): Document;
212
- /** Scope inlined frames' styles, mirror hover and focus, and size frame boxes, for a full document's frame. */
213
- private restyle;
221
+ private bind;
222
+ /** `next` takes over the id `old` was bound to. */
223
+ private rebind;
224
+ private reset;
214
225
  /**
215
- * Inlined frames are divs, so they get an iframe's box: inline block, 300 by 150 and
216
- * clipped unless the page says otherwise, their `width`, `height` and `frameborder`
217
- * attributes as sizes and border, and the framed document filling them. All at zero
218
- * specificity in a sheet of its own, so page CSS wins, like a real iframe's
219
- * presentational hints. No nodes join the replay, so ids stay aligned.
226
+ * A replay's HTML can't hold one document inside another, so an inlined frame's html,
227
+ * head and body arrive as divs marked `data-fauna-frame`. Every wrapper becomes a frame
228
+ * box, holding them as the html, head and body they stand for, under the ids they were
229
+ * bound to. Wrappers come in document order, so a frame's box is in the copy, with its
230
+ * document, before the frames inside it are unfolded.
220
231
  */
221
- private frameBoxes;
222
- /** Scroll a replayed element, or a framed document's html, to the host's `data-fauna-scroll`. */
223
- private applyScroll;
232
+ private unfold;
233
+ /** An iframe standing for a frame's wrapper, which never loads anything of its own. */
234
+ private createBox;
235
+ /** Insert into `parent`, or, for a frame box, into the framed document it shows. */
236
+ private insertInto;
224
237
  /**
225
- * A framed document is inlined into the replay, so its stylesheets would style the
226
- * whole copy: a site's `body { background }` repaints the page around its frame. Each
227
- * of its sheets goes inside `@scope` at the frame's html div instead. Sheets from other
228
- * origins can't be read, so they stay as they are.
238
+ * Put a box's framed document into the box's own document: its html as the document's
239
+ * root, and anything else (a placeholder) into a body of its own. A box gets its
240
+ * document the moment it joins the copy; until then what it shows waits in `content`,
241
+ * where changes apply all the same.
229
242
  */
230
- private scopeFrames;
243
+ private place;
244
+ /** Give every box that has joined the copy its document, the boxes inside them included. */
245
+ private settle;
246
+ /** The copy's document and every framed document in it. */
247
+ private documents;
248
+ /** Take a removed node out of the copy, and every id under it, framed documents included. */
249
+ private drop;
250
+ /** Keep `el` (a framed document's html for the frame's own scroll) where the host has it. */
251
+ private scrollTo;
252
+ /** Whether `el` is where the host has it, after moving it there. */
253
+ private scrollOnce;
254
+ /**
255
+ * A scroll lands only once its document is long enough, which can be after the changes
256
+ * that grow it, or after its images and fonts load. Once a frame after any change, and
257
+ * every frame while a recent scroll hasn't landed, scrolls go back where the host has them.
258
+ */
259
+ private rescroll;
260
+ /** The document the replay renders into: its iframe's for full documents, else the mount's. */
261
+ getDocument(): Document;
231
262
  /**
232
263
  * `:hover` and `:focus` never apply in a replay. The host marks what they would match
233
264
  * with `data-fauna-hover` and the focus attributes, so point those selectors at the
234
- * attributes instead; both weigh the same in the cascade. Only a full document's own
235
- * frame is rewritten.
265
+ * attributes instead; both weigh the same in the cascade. Only the copy's own documents
266
+ * are rewritten: its frame's, and each framed document's.
236
267
  */
237
268
  private mirrorStates;
238
269
  private ownerDoc;
239
- /** `seen` keeps a subtree that names a node twice (or itself) from building forever. */
270
+ /**
271
+ * `seen` keeps a subtree that names a node twice (or itself) from building forever. SVG
272
+ * and MathML take their namespace from where they sit, as the HTML parser gives it.
273
+ */
240
274
  private build;
241
275
  private buildFromList;
242
276
  /** Render a fresh snapshot, replacing whatever the mount currently holds. */
@@ -259,8 +293,8 @@ export declare function pageToHtml(root?: Node): {
259
293
  };
260
294
  /** Install a MutationObserver that posts WebMutations to `window[pushName]` as
261
295
  * `{ token, mutation }`, so the host knows which recorder sent each one.
262
- * Framed documents are walked, observed, and re-inlined on navigation, with
263
- * their html, head, and body addressed as the divs `pageToHtml` emits.
296
+ * Framed documents are walked, observed, and re-inlined on navigation, each
297
+ * as its own html, head, and body, which viewers replay as a nested frame.
264
298
  * With `remoteFrames`, a frame whose document this page can't read is left to
265
299
  * the host, which streams it from the frame itself: its wrapper says
266
300
  * `data-fauna-iframe="remote"` and holds nothing. With `framed`, this document
@@ -344,7 +378,7 @@ export declare function weightedProgress(stages: {
344
378
  export type WebRoomHooks = Partial<{
345
379
  /** Host published bootstrap meta — guests should HTTP GET the HTML. */
346
380
  onBootstrap(room: WebRoom, bootstrap: BootstrapMeta): void;
347
- /** The host published a fresh full-page snapshot (legacy / fallback). */
381
+ /** The host published a fresh full-page snapshot (fallback). */
348
382
  onSnapshot(room: WebRoom, snapshot: WebSnapshot): void;
349
383
  /** The host emitted one incremental DOM mutation. */
350
384
  onMutation(room: WebRoom, mutation: WebMutation): void;