backd-js 0.3.0 → 0.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backd-js",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "JavaScript client for backd: auth, sessions and data for browsers, Node and edge runtimes.",
5
5
  "keywords": [
6
6
  "backd",
package/src/admin.js CHANGED
@@ -27,6 +27,7 @@ import { Job } from './functions.js'
27
27
  * @property {number} limit
28
28
  * @property {number} skip
29
29
  * @property {boolean} has_more
30
+ * @property {string} [next_cursor] With `has_more`: pass it as `after` for the next page.
30
31
  */
31
32
 
32
33
  /**
@@ -65,6 +66,7 @@ import { Job } from './functions.js'
65
66
  * @property {'data' | 'admin'} role
66
67
  * @property {string} prefix First characters of the key.
67
68
  * @property {string[]} networks Where it may be used from; empty: anywhere.
69
+ * @property {string[]} scopes What it reaches (`read:blog/posts`, `call:main/export`…); empty: everything.
68
70
  * @property {string} created_at
69
71
  * @property {string | null} last_used_at
70
72
  * @property {string | null} expires_at
@@ -225,13 +227,14 @@ class AdminUsers {
225
227
  }
226
228
 
227
229
  /**
228
- * A page of users, sorted by email.
229
- * @param {{ limit?: number, skip?: number }} [params]
230
+ * A page of users, sorted by email. `after` is the `next_cursor` of the
231
+ * previous page (not combinable with `skip`).
232
+ * @param {{ limit?: number, skip?: number, after?: string }} [params]
230
233
  * @param {RequestOptions} [opts]
231
234
  * @returns {Promise<UserPage>}
232
235
  */
233
236
  async list(params = {}, opts) {
234
- return (await this.admin._request({ method: 'GET', path: ['users'], query: { limit: params.limit, skip: params.skip }, ...opts })).data
237
+ return (await this.admin._request({ method: 'GET', path: ['users'], query: { limit: params.limit, skip: params.skip, after: params.after }, ...opts })).data
235
238
  }
236
239
 
237
240
  /**
@@ -388,17 +391,20 @@ class AdminAPIKeys {
388
391
 
389
392
  /**
390
393
  * Creates a key; store its `key` now, it is never shown again.
391
- * @param {{ name: string, role?: 'data' | 'admin', expiresIn?: string, networks?: string[] }} input
392
- * `role` defaults to data; `expiresIn`: days (`90d`) or Go durations (`12h`).
394
+ * @param {{ name: string, role?: 'data' | 'admin', expiresIn?: string, networks?: string[], scopes?: string[] }} input
395
+ * `role` defaults to data; `expiresIn`: days (`90d`) or Go durations (`12h`). `scopes` limit what a
396
+ * data key reaches: `read`, `write` or `call`, optionally followed by `:<database>` or
397
+ * `:<database>/<collection or function>`; none means everything.
393
398
  * @param {RequestOptions} [opts]
394
399
  * @returns {Promise<NewAPIKey>}
395
400
  */
396
- async create({ name, role, expiresIn, networks }, opts) {
401
+ async create({ name, role, expiresIn, networks, scopes }, opts) {
397
402
  /** @type {Record<string, unknown>} */
398
403
  const body = { name }
399
404
  if (role !== undefined) body.role = role
400
405
  if (expiresIn !== undefined) body.expires_in = expiresIn
401
406
  if (networks !== undefined) body.networks = networks
407
+ if (scopes !== undefined) body.scopes = scopes
402
408
  return (await this.admin._request({ method: 'POST', path: ['apikeys'], body, ...opts })).data
403
409
  }
404
410
 
package/src/auth.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { AuthenticationError, VerificationRequiredError } from './errors.js'
2
+ import { COOKIE_SESSION } from './storage.js'
2
3
 
3
4
  /**
4
5
  * @typedef {import('./client.js').Client} Client
@@ -19,8 +20,9 @@ import { AuthenticationError, VerificationRequiredError } from './errors.js'
19
20
  /**
20
21
  * A new session.
21
22
  * @typedef {object} Session
22
- * @property {string} token Session token (`bds_…`); already stored by the client.
23
- * @property {'Bearer'} token_type
23
+ * @property {string} [token] Session token (`bds_…`); already stored by the client. Absent with `cookies: true`:
24
+ * the session is in an HttpOnly cookie, out of reach of scripts.
25
+ * @property {'Bearer'} [token_type]
24
26
  * @property {string} session_id
25
27
  * @property {string} expires_at RFC3339; pushed forward as the session is used.
26
28
  * @property {User} user
@@ -81,8 +83,9 @@ export class Auth {
81
83
  * @returns {Promise<Session>}
82
84
  */
83
85
  async signup({ email, password, invitation, locale, redirectTo }, opts) {
84
- /** @type {Record<string, string>} */
86
+ /** @type {Record<string, string | boolean>} */
85
87
  const body = { email, password }
88
+ if (this.client.cookies) body.cookie = true
86
89
  if (invitation) body.invitation = invitation
87
90
  if (locale) body.locale = locale
88
91
  if (redirectTo) body.redirect_to = redirectTo
@@ -100,7 +103,8 @@ export class Auth {
100
103
  * @returns {Promise<Session>}
101
104
  */
102
105
  async login({ email, password }, opts) {
103
- const { data } = await this.client.request({ method: 'POST', path: ['_auth', 'login'], body: { email, password }, auth: false, ...opts })
106
+ const body = this.client.cookies ? { email, password, cookie: true } : { email, password }
107
+ const { data } = await this.client.request({ method: 'POST', path: ['_auth', 'login'], body, auth: false, ...opts })
104
108
  return this.signedIn(data)
105
109
  }
106
110
 
@@ -311,11 +315,23 @@ export class Auth {
311
315
  }
312
316
 
313
317
  /**
314
- * The stored session token, if any.
318
+ * The stored session token, if any. Always null with `cookies: true`: the
319
+ * token is in a cookie that scripts can't read.
315
320
  * @returns {Promise<string | null>}
316
321
  */
317
322
  async token() {
318
- return (await this.client.storage.get()) ?? null
323
+ const token = (await this.client.storage.get()) ?? null
324
+ return token === COOKIE_SESSION ? null : token
325
+ }
326
+
327
+ /**
328
+ * Whether this client believes it is signed in: it holds a token, or (with
329
+ * `cookies: true`) it signed in and hasn't signed out or been refused since.
330
+ * A page that has just loaded knows nothing: ask the server with {@link Auth#me}.
331
+ * @returns {Promise<boolean>}
332
+ */
333
+ async hasSession() {
334
+ return Boolean(await this.client.storage.get())
319
335
  }
320
336
 
321
337
  /**
@@ -334,7 +350,7 @@ export class Auth {
334
350
  * @returns {Promise<Session>}
335
351
  */
336
352
  async signedIn(session) {
337
- await this.client.storage.set(session.token)
353
+ await this.client.storage.set(session.token ?? COOKIE_SESSION)
338
354
  this.emit('SIGNED_IN', session)
339
355
  return session
340
356
  }
package/src/client.js CHANGED
@@ -2,7 +2,7 @@ import { Admin } from './admin.js'
2
2
  import { Auth } from './auth.js'
3
3
  import { Database } from './data.js'
4
4
  import { NetworkError, RetryableError, errorFromResponse } from './errors.js'
5
- import { memoryStorage } from './storage.js'
5
+ import { COOKIE_SESSION, memoryStorage } from './storage.js'
6
6
 
7
7
  /**
8
8
  * @typedef {import('./storage.js').TokenStorage} TokenStorage
@@ -23,6 +23,9 @@ import { memoryStorage } from './storage.js'
23
23
  * @property {string} [apiKey] Server-side only: an API key (`bdk_…`). Full access to the realm.
24
24
  * @property {boolean} [dangerouslyAllowBrowser] Allow `apiKey` in a browser. Anyone who loads the page gets the key.
25
25
  * @property {TokenStorage} [storage] Where the session token lives; memory by default.
26
+ * @property {boolean} [cookies] Browser apps: keep the session in an HttpOnly cookie that scripts can't read,
27
+ * instead of a token. The realm must enable `sessions.cookie` and list the app's origin in `cors.origins`; the
28
+ * app's page (or the API) must be on a site the cookie's SameSite setting allows. The admin API doesn't take cookies.
26
29
  * @property {RetryOptions} [retry] Retries for 429/503; off by default.
27
30
  * @property {typeof fetch} [fetch] A fetch implementation; the global one by default.
28
31
  * @property {Record<string, string>} [headers] Extra headers for every request.
@@ -81,11 +84,16 @@ export class Client {
81
84
  '`dangerouslyAllowBrowser: true` if you really mean it.',
82
85
  )
83
86
  }
87
+ if (options.cookies && options.apiKey) {
88
+ throw new TypeError('createClient: `cookies` is for sessions in a browser; an API key is sent in a header')
89
+ }
84
90
  /** @readonly */
85
91
  this.url = options.url.replace(/\/+$/, '')
86
92
  /** @readonly */
87
93
  this.realm = options.realm
88
94
  /** @internal */
95
+ this.cookies = Boolean(options.cookies)
96
+ /** @internal */
89
97
  this.apiKey = options.apiKey
90
98
  /** @internal */
91
99
  this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis)
@@ -138,14 +146,15 @@ export class Client {
138
146
  } else {
139
147
  const token = await this.storage.get()
140
148
  if (token) {
141
- headers.Authorization = 'Bearer ' + token
149
+ if (token !== COOKIE_SESSION) headers.Authorization = 'Bearer ' + token // a cookie session is sent by the browser
142
150
  sentSession = true
143
151
  }
144
152
  }
145
153
  }
146
154
 
147
155
  const retry = req.retry ?? this.retry
148
- const safe = req.method === 'GET' || Object.keys(headers).some((h) => h.toLowerCase() === 'if-match')
156
+ // Safe to repeat: reads, writes conditional on a version, and requests the server deduplicates by key.
157
+ const safe = req.method === 'GET' || Object.keys(headers).some((h) => ['if-match', 'idempotency-key'].includes(h.toLowerCase()))
149
158
  const attempts = safe ? Math.max(0, retry.attempts) : 0
150
159
  for (let attempt = 0; ; attempt++) {
151
160
  /** @type {Response} */
@@ -156,6 +165,7 @@ export class Client {
156
165
  headers,
157
166
  body: req.body === undefined ? undefined : JSON.stringify(req.body),
158
167
  signal: req.signal,
168
+ credentials: this.cookies ? 'include' : undefined,
159
169
  })
160
170
  } catch (cause) {
161
171
  throw new NetworkError({
package/src/data.js CHANGED
@@ -54,6 +54,15 @@ import { Job } from './functions.js'
54
54
  * @typedef {RequestOptions & { ifMatch?: number | string }} WriteOptions
55
55
  */
56
56
 
57
+ /**
58
+ * Options for creates and batches. `idempotencyKey` makes a retry safe: the
59
+ * server remembers the answer of the first request with a key for 24 hours and
60
+ * returns it again for the same key and body, instead of creating a second
61
+ * document. The client also retries such a request itself after a network
62
+ * error, `429` or `503` (see `retry`).
63
+ * @typedef {RequestOptions & { idempotencyKey?: string }} CreateOptions
64
+ */
65
+
57
66
  /**
58
67
  * One write for `db.batch(...)`, applied atomically with the others.
59
68
  * `create` and `replace` need `document`; `patch` needs `patch` (a JSON
@@ -99,14 +108,14 @@ export class Database {
99
108
  * mismatch (`ifMatch`) is a `VersionMismatchError`, same as a single
100
109
  * write.
101
110
  * @param {BatchOperation[]} operations
102
- * @param {RequestOptions} [opts]
111
+ * @param {CreateOptions} [opts]
103
112
  * @returns {Promise<(Doc | { id: string })[]>} one entry per operation, in order
104
113
  */
105
114
  async batch(operations, opts) {
106
115
  const body = {
107
116
  operations: operations.map(({ ifMatch, ...op }) => (ifMatch === undefined ? op : { ...op, if_match: ifMatchValue(ifMatch) })),
108
117
  }
109
- const { data } = await this.client.request({ method: 'POST', path: [this.name, '_batch'], body, ...opts })
118
+ const { data } = await this.client.request({ method: 'POST', path: [this.name, '_batch'], body, ...keyed(opts) })
110
119
  return data.results
111
120
  }
112
121
 
@@ -208,13 +217,15 @@ export class Collection {
208
217
  }
209
218
 
210
219
  /**
211
- * Creates a document. `id` and `_meta` are set by the server.
220
+ * Creates a document. `id` and `_meta` are set by the server. With an
221
+ * `idempotencyKey`, a repeated request returns the document created by the
222
+ * first one.
212
223
  * @param {T} doc
213
- * @param {RequestOptions} [opts]
224
+ * @param {CreateOptions} [opts]
214
225
  * @returns {Promise<Doc<T>>}
215
226
  */
216
227
  async create(doc, opts) {
217
- return (await this.client.request({ method: 'POST', path: this.path, body: doc, ...opts })).data
228
+ return (await this.client.request({ method: 'POST', path: this.path, body: doc, ...keyed(opts) })).data
218
229
  }
219
230
 
220
231
  /**
@@ -274,6 +285,16 @@ function listQuery(p) {
274
285
  }
275
286
  }
276
287
 
288
+ /**
289
+ * Turns `idempotencyKey` into an Idempotency-Key header.
290
+ * @param {CreateOptions} [opts]
291
+ * @returns {RequestOptions}
292
+ */
293
+ function keyed(opts = {}) {
294
+ const { idempotencyKey, ...rest } = opts
295
+ return idempotencyKey === undefined ? rest : { ...rest, headers: { ...rest.headers, 'Idempotency-Key': idempotencyKey } }
296
+ }
297
+
277
298
  /**
278
299
  * Turns `ifMatch` into an If-Match header.
279
300
  * @param {WriteOptions} opts
package/src/index.js CHANGED
@@ -30,6 +30,7 @@ export { memoryStorage, localStorageStorage } from './storage.js'
30
30
  * @typedef {import('./data.js').Meta} Meta
31
31
  * @typedef {import('./data.js').ListParams} ListParams
32
32
  * @typedef {import('./data.js').WriteOptions} WriteOptions
33
+ * @typedef {import('./data.js').CreateOptions} CreateOptions
33
34
  * @typedef {import('./admin.js').AdminUser} AdminUser
34
35
  * @typedef {import('./admin.js').UserPage} UserPage
35
36
  * @typedef {import('./admin.js').Invitation} Invitation
package/src/storage.js CHANGED
@@ -6,6 +6,13 @@
6
6
  * @property {() => void | Promise<void>} remove
7
7
  */
8
8
 
9
+ /**
10
+ * What the client stores, instead of a token, when the session lives in an
11
+ * HttpOnly cookie (`cookies: true`): a hint that there is a session, with
12
+ * nothing secret in it.
13
+ */
14
+ export const COOKIE_SESSION = 'cookie-session'
15
+
9
16
  /**
10
17
  * Keeps the token in memory: it is lost on reload, and scripts on the page
11
18
  * can't read it from storage. The default.
package/types/admin.d.ts CHANGED
@@ -28,6 +28,10 @@ export type UserPage = {
28
28
  limit: number;
29
29
  skip: number;
30
30
  has_more: boolean;
31
+ /**
32
+ * With `has_more`: pass it as `after` for the next page.
33
+ */
34
+ next_cursor?: string;
31
35
  };
32
36
  export type Invitation = {
33
37
  id: string;
@@ -79,6 +83,10 @@ export type APIKeyInfo = {
79
83
  * Where it may be used from; empty: anywhere.
80
84
  */
81
85
  networks: string[];
86
+ /**
87
+ * What it reaches (`read:blog/posts`, `call:main/export`…); empty: everything.
88
+ */
89
+ scopes: string[];
82
90
  created_at: string;
83
91
  last_used_at: string | null;
84
92
  expires_at: string | null;
@@ -241,6 +249,7 @@ export type JobsPage = {
241
249
  * @property {number} limit
242
250
  * @property {number} skip
243
251
  * @property {boolean} has_more
252
+ * @property {string} [next_cursor] With `has_more`: pass it as `after` for the next page.
244
253
  */
245
254
  /**
246
255
  * @typedef {object} Invitation
@@ -274,6 +283,7 @@ export type JobsPage = {
274
283
  * @property {'data' | 'admin'} role
275
284
  * @property {string} prefix First characters of the key.
276
285
  * @property {string[]} networks Where it may be used from; empty: anywhere.
286
+ * @property {string[]} scopes What it reaches (`read:blog/posts`, `call:main/export`…); empty: everything.
277
287
  * @property {string} created_at
278
288
  * @property {string | null} last_used_at
279
289
  * @property {string | null} expires_at
@@ -408,14 +418,16 @@ declare class AdminUsers {
408
418
  /** @param {Admin} admin */
409
419
  constructor(admin: Admin);
410
420
  /**
411
- * A page of users, sorted by email.
412
- * @param {{ limit?: number, skip?: number }} [params]
421
+ * A page of users, sorted by email. `after` is the `next_cursor` of the
422
+ * previous page (not combinable with `skip`).
423
+ * @param {{ limit?: number, skip?: number, after?: string }} [params]
413
424
  * @param {RequestOptions} [opts]
414
425
  * @returns {Promise<UserPage>}
415
426
  */
416
427
  list(params?: {
417
428
  limit?: number;
418
429
  skip?: number;
430
+ after?: string;
419
431
  }, opts?: RequestOptions): Promise<UserPage>;
420
432
  /**
421
433
  * The user with this email (case-insensitive), or null.
@@ -536,16 +548,19 @@ declare class AdminAPIKeys {
536
548
  list(opts?: RequestOptions): Promise<APIKeyInfo[]>;
537
549
  /**
538
550
  * Creates a key; store its `key` now, it is never shown again.
539
- * @param {{ name: string, role?: 'data' | 'admin', expiresIn?: string, networks?: string[] }} input
540
- * `role` defaults to data; `expiresIn`: days (`90d`) or Go durations (`12h`).
551
+ * @param {{ name: string, role?: 'data' | 'admin', expiresIn?: string, networks?: string[], scopes?: string[] }} input
552
+ * `role` defaults to data; `expiresIn`: days (`90d`) or Go durations (`12h`). `scopes` limit what a
553
+ * data key reaches: `read`, `write` or `call`, optionally followed by `:<database>` or
554
+ * `:<database>/<collection or function>`; none means everything.
541
555
  * @param {RequestOptions} [opts]
542
556
  * @returns {Promise<NewAPIKey>}
543
557
  */
544
- create({ name, role, expiresIn, networks }: {
558
+ create({ name, role, expiresIn, networks, scopes }: {
545
559
  name: string;
546
560
  role?: 'data' | 'admin';
547
561
  expiresIn?: string;
548
562
  networks?: string[];
563
+ scopes?: string[];
549
564
  }, opts?: RequestOptions): Promise<NewAPIKey>;
550
565
  /**
551
566
  * Revokes a key: it stops working at once.
package/types/auth.d.ts CHANGED
@@ -16,10 +16,11 @@ export type User = {
16
16
  };
17
17
  export type Session = {
18
18
  /**
19
- * Session token (`bds_…`); already stored by the client.
19
+ * Session token (`bds_…`); already stored by the client. Absent with `cookies: true`:
20
+ * the session is in an HttpOnly cookie, out of reach of scripts.
20
21
  */
21
- token: string;
22
- token_type: 'Bearer';
22
+ token?: string;
23
+ token_type?: 'Bearer';
23
24
  session_id: string;
24
25
  /**
25
26
  * RFC3339; pushed forward as the session is used.
@@ -246,10 +247,18 @@ export declare class Auth {
246
247
  */
247
248
  revokeSession(id: string, opts?: RequestOptions): Promise<void>;
248
249
  /**
249
- * The stored session token, if any.
250
+ * The stored session token, if any. Always null with `cookies: true`: the
251
+ * token is in a cookie that scripts can't read.
250
252
  * @returns {Promise<string | null>}
251
253
  */
252
254
  token(): Promise<string | null>;
255
+ /**
256
+ * Whether this client believes it is signed in: it holds a token, or (with
257
+ * `cookies: true`) it signed in and hasn't signed out or been refused since.
258
+ * A page that has just loaded knows nothing: ask the server with {@link Auth#me}.
259
+ * @returns {Promise<boolean>}
260
+ */
261
+ hasSession(): Promise<boolean>;
253
262
  /**
254
263
  * Calls listener on sign-in, sign-out and session expiry.
255
264
  * @param {AuthListener} listener
package/types/client.d.ts CHANGED
@@ -33,6 +33,12 @@ export type ClientOptions = {
33
33
  * Where the session token lives; memory by default.
34
34
  */
35
35
  storage?: TokenStorage;
36
+ /**
37
+ * Browser apps: keep the session in an HttpOnly cookie that scripts can't read,
38
+ * instead of a token. The realm must enable `sessions.cookie` and list the app's origin in `cors.origins`; the
39
+ * app's page (or the API) must be on a site the cookie's SameSite setting allows. The admin API doesn't take cookies.
40
+ */
41
+ cookies?: boolean;
36
42
  /**
37
43
  * Retries for 429/503; off by default.
38
44
  */
@@ -98,6 +104,8 @@ export declare class Client {
98
104
  /** @readonly */
99
105
  realm: string;
100
106
  /** @internal */
107
+ cookies: boolean;
108
+ /** @internal */
101
109
  apiKey: string | undefined;
102
110
  /** @internal */
103
111
  fetchImpl: typeof fetch;
package/types/data.d.ts CHANGED
@@ -75,6 +75,9 @@ export type Page<T extends object = Record<string, any>> = {
75
75
  export type WriteOptions = RequestOptions & {
76
76
  ifMatch?: number | string;
77
77
  };
78
+ export type CreateOptions = RequestOptions & {
79
+ idempotencyKey?: string;
80
+ };
78
81
  export type BatchOperation = {
79
82
  op: 'create' | 'replace' | 'patch' | 'delete';
80
83
  collection: string;
@@ -131,6 +134,14 @@ export type BatchOperation = {
131
134
  * VersionMismatchError unless the document is still at that version.
132
135
  * @typedef {RequestOptions & { ifMatch?: number | string }} WriteOptions
133
136
  */
137
+ /**
138
+ * Options for creates and batches. `idempotencyKey` makes a retry safe: the
139
+ * server remembers the answer of the first request with a key for 24 hours and
140
+ * returns it again for the same key and body, instead of creating a second
141
+ * document. The client also retries such a request itself after a network
142
+ * error, `429` or `503` (see `retry`).
143
+ * @typedef {RequestOptions & { idempotencyKey?: string }} CreateOptions
144
+ */
134
145
  /**
135
146
  * One write for `db.batch(...)`, applied atomically with the others.
136
147
  * `create` and `replace` need `document`; `patch` needs `patch` (a JSON
@@ -170,10 +181,10 @@ export declare class Database {
170
181
  * mismatch (`ifMatch`) is a `VersionMismatchError`, same as a single
171
182
  * write.
172
183
  * @param {BatchOperation[]} operations
173
- * @param {RequestOptions} [opts]
184
+ * @param {CreateOptions} [opts]
174
185
  * @returns {Promise<(Doc | { id: string })[]>} one entry per operation, in order
175
186
  */
176
- batch(operations: BatchOperation[], opts?: RequestOptions): Promise<(Doc | {
187
+ batch(operations: BatchOperation[], opts?: CreateOptions): Promise<(Doc | {
177
188
  id: string;
178
189
  })[]>;
179
190
  /**
@@ -239,12 +250,14 @@ export declare class Collection<T extends object = Record<string, any>> {
239
250
  */
240
251
  get(id: string, opts?: RequestOptions): Promise<Doc<T>>;
241
252
  /**
242
- * Creates a document. `id` and `_meta` are set by the server.
253
+ * Creates a document. `id` and `_meta` are set by the server. With an
254
+ * `idempotencyKey`, a repeated request returns the document created by the
255
+ * first one.
243
256
  * @param {T} doc
244
- * @param {RequestOptions} [opts]
257
+ * @param {CreateOptions} [opts]
245
258
  * @returns {Promise<Doc<T>>}
246
259
  */
247
- create(doc: T, opts?: RequestOptions): Promise<Doc<T>>;
260
+ create(doc: T, opts?: CreateOptions): Promise<Doc<T>>;
248
261
  /**
249
262
  * Replaces the whole document: fields not in `doc` are removed.
250
263
  * @param {string} id
package/types/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export type ErrorDetail = import('./errors.js').ErrorDetail;
17
17
  export type Meta = import('./data.js').Meta;
18
18
  export type ListParams = import('./data.js').ListParams;
19
19
  export type WriteOptions = import('./data.js').WriteOptions;
20
+ export type CreateOptions = import('./data.js').CreateOptions;
20
21
  export type AdminUser = import('./admin.js').AdminUser;
21
22
  export type UserPage = import('./admin.js').UserPage;
22
23
  export type Invitation = import('./admin.js').Invitation;
@@ -40,6 +41,7 @@ export type Page<T extends object = Record<string, any>> = import('./data.js').P
40
41
  * @typedef {import('./data.js').Meta} Meta
41
42
  * @typedef {import('./data.js').ListParams} ListParams
42
43
  * @typedef {import('./data.js').WriteOptions} WriteOptions
44
+ * @typedef {import('./data.js').CreateOptions} CreateOptions
43
45
  * @typedef {import('./admin.js').AdminUser} AdminUser
44
46
  * @typedef {import('./admin.js').UserPage} UserPage
45
47
  * @typedef {import('./admin.js').Invitation} Invitation
@@ -10,6 +10,12 @@ export type TokenStorage = {
10
10
  set: (token: string) => void | Promise<void>;
11
11
  remove: () => void | Promise<void>;
12
12
  };
13
+ /**
14
+ * What the client stores, instead of a token, when the session lives in an
15
+ * HttpOnly cookie (`cookies: true`): a hint that there is a session, with
16
+ * nothing secret in it.
17
+ */
18
+ export declare const COOKIE_SESSION = "cookie-session";
13
19
  /**
14
20
  * Keeps the token in memory: it is lost on reload, and scripts on the page
15
21
  * can't read it from storage. The default.