@larc-iu/plaid-client 0.2.0-alpha.23 → 0.2.0-alpha.29
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 +4 -0
- package/package.json +11 -5
- package/src/batchRef.d.ts +30 -0
- package/src/codepoint.d.ts +94 -0
- package/src/codepoint.js +131 -1
- package/src/constraints.d.ts +77 -0
- package/src/constraints.js +26 -1
- package/src/created.d.ts +2 -0
- package/src/documentLock.d.ts +158 -0
- package/src/documentLock.js +27 -3
- package/src/events.d.ts +60 -0
- package/src/events.js +7 -3
- package/src/http.d.ts +212 -0
- package/src/http.js +7 -0
- package/src/ids.d.ts +26 -0
- package/src/ids.js +22 -0
- package/src/index.d.ts +4568 -0
- package/src/index.js +1597 -758
- package/src/metadataOps.d.ts +15 -0
- package/src/metadataOps.js +13 -8
- package/src/pagination.d.ts +49 -0
- package/src/pagination.js +2 -19
- package/src/provenance.d.ts +200 -0
- package/src/provenance.js +34 -6
- package/src/replayed.d.ts +3 -0
- package/src/replayed.js +5 -1
- package/src/requester.d.ts +60 -0
- package/src/requester.js +21 -3
- package/src/roles.d.ts +65 -0
- package/src/roles.js +3 -2
- package/src/serviceSchema.d.ts +331 -0
- package/src/serviceSchema.js +75 -9
- package/src/services.d.ts +302 -0
- package/src/services.js +82 -11
- package/src/sse.d.ts +30 -0
- package/src/sse.js +11 -0
- package/src/textEdits.d.ts +59 -0
- package/src/textEdits.js +33 -6
- package/src/transforms.d.ts +25 -0
- package/index.d.ts +0 -2026
package/src/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
DEFAULT_BATCH_TIMEOUT_MS,
|
|
23
23
|
queryTimeout,
|
|
24
24
|
} from "./http.js";
|
|
25
|
-
import { uuidv4, uuidv7 } from "./ids.js";
|
|
25
|
+
import { uuidv4, uuidv7, normalizeUserId } from "./ids.js";
|
|
26
26
|
import { constraintsBody } from "./constraints.js";
|
|
27
27
|
import { listAll, listPage, iterPages } from "./pagination.js";
|
|
28
28
|
import { recorderFor } from "./events.js";
|
|
@@ -39,6 +39,662 @@ import {
|
|
|
39
39
|
cancelServiceRequest,
|
|
40
40
|
} from "./services.js";
|
|
41
41
|
|
|
42
|
+
// ---- Types --------------------------------------------------------------
|
|
43
|
+
// The shapes the API answers with and takes, for editors and for the
|
|
44
|
+
// declarations `npm run types` generates from this JSDoc (index.d.ts).
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {import("./textEdits.js").TextEditOp} TextEditOp
|
|
48
|
+
* @typedef {import("./textEdits.js").TextGap} TextGap
|
|
49
|
+
* @typedef {import("./metadataOps.js").MetadataOp} MetadataOp
|
|
50
|
+
* @typedef {import("./constraints.js").LayerConstraint} LayerConstraint
|
|
51
|
+
* @typedef {import("./constraints.js").ConstraintWriteOptions} ConstraintWriteOptions
|
|
52
|
+
* @typedef {import("./constraints.js").ConstraintViolation} ConstraintViolation
|
|
53
|
+
* @typedef {import("./requester.js").Requester} Requester
|
|
54
|
+
* @typedef {import("./batchRef.js").BatchRef} BatchRef
|
|
55
|
+
* @typedef {import("./serviceSchema.js").ServiceParam} ServiceParam
|
|
56
|
+
* @typedef {import("./serviceSchema.js").ServiceInfo} ServiceInfo
|
|
57
|
+
* @typedef {import("./serviceSchema.js").DiscoveredService} DiscoveredService
|
|
58
|
+
* @typedef {import("./services.js").ResponseHelper} ResponseHelper
|
|
59
|
+
* @typedef {import("./services.js").ServiceRegistration} ServiceRegistration
|
|
60
|
+
* @typedef {import("./services.js").ServiceStatusEvent} ServiceStatusEvent
|
|
61
|
+
* @typedef {PlaidClient["operationGroups"]} OperationGroupsBundle
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* A single page of a cursor-paginated collection.
|
|
66
|
+
* @template [T=any]
|
|
67
|
+
* @typedef {{entries: T[], nextCursor: string | null}} Page
|
|
68
|
+
*/
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Client options.
|
|
72
|
+
* @typedef {object} PlaidClientOptions
|
|
73
|
+
* @property {number | null} [timeout] Per-request timeout in ms (default
|
|
74
|
+
* 30000, 0 or null disables it).
|
|
75
|
+
* @property {number | null} [batchTimeout] Timeout for batch submissions in
|
|
76
|
+
* ms (default 180000, 0 or null disables it). Aborting a batch does NOT
|
|
77
|
+
* stop the server, which keeps running the transaction and holding the
|
|
78
|
+
* write lock. Defaults to `timeout` when that is given and this is not.
|
|
79
|
+
* @property {number[]} [retryDelaysMs] Delays in ms before sending a keyed
|
|
80
|
+
* write again when its answer was lost (no response, 502 or 504). Default
|
|
81
|
+
* [1000, 3000, 9000].
|
|
82
|
+
* @property {((error: Error) => void) | null} [onAuthError] Fired once when
|
|
83
|
+
* a request returns HTTP 401 (missing, expired or invalid token). 403 does
|
|
84
|
+
* NOT trigger it.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* What kind of operation a logical-operation group is (`?group-kind=`).
|
|
89
|
+
* @typedef {"assistant-plan" | "service-run" | "import" | "bulk-edit"
|
|
90
|
+
* | "guess-adoption" | "repair" | "review"} OperationKind
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Idempotency-Key seed for an operation run more than once (`client.keySeed()`).
|
|
95
|
+
* @typedef {{seed: string, stamps: Map<number, number | null>}} KeySeed
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* A batch: the client's bundles, on which every write of project data queues
|
|
100
|
+
* until `submit()`. See `PlaidClient.batch()`. `submit()` resolves to one
|
|
101
|
+
* result per operation, in order. `ref(opIndex, index)` stands for the id a
|
|
102
|
+
* queued operation will create, in the body of a later write on this batch.
|
|
103
|
+
* @typedef {PlaidClient & {
|
|
104
|
+
* readonly client: PlaidClient,
|
|
105
|
+
* readonly operations: Array<{path: string, method: string, body?: any}>,
|
|
106
|
+
* submit(): Promise<any[]>,
|
|
107
|
+
* abort(): void,
|
|
108
|
+
* ref(opIndex?: number, index?: number): BatchRef,
|
|
109
|
+
* }} PlaidBatch
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Options for a single page of any audit log. `kinds` keeps only the entries
|
|
114
|
+
* of operations of these kinds, each whole. `order: "desc"` pages newest
|
|
115
|
+
* first, and a cursor belongs to the direction that made it. `opsLimit` keeps
|
|
116
|
+
* only each entry's oldest N operations in `ops` (1..1000), and every entry
|
|
117
|
+
* has `opCount`. `entryId` reads only that entry rather than a page.
|
|
118
|
+
* @typedef {object} AuditPageOptions
|
|
119
|
+
* @property {string} [startTime]
|
|
120
|
+
* @property {string} [endTime]
|
|
121
|
+
* @property {string[] | string} [opTypes]
|
|
122
|
+
* @property {OperationKind[] | string} [kinds]
|
|
123
|
+
* @property {"asc" | "desc"} [order]
|
|
124
|
+
* @property {number} [limit]
|
|
125
|
+
* @property {string} [cursor]
|
|
126
|
+
* @property {number} [opsLimit]
|
|
127
|
+
* @property {string} [entryId]
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* What `vocabLayers.restoreItem` did, or with `dryRun` would do.
|
|
132
|
+
* @typedef {object} VocabItemRestoreSummary
|
|
133
|
+
* @property {boolean} inserted The entry was gone and came back under its old id.
|
|
134
|
+
* @property {boolean} form A living entry's form was set back.
|
|
135
|
+
* @property {boolean} metadata A living entry's fields were set back.
|
|
136
|
+
* @property {number} total 0 when nothing changes.
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* One private data entry. `version` is raised by one on every write of the
|
|
141
|
+
* entry, and a new entry is 1.
|
|
142
|
+
* @typedef {{key: string, updatedAt: string, version: number, value?: any}} UserDataEntry
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
/** @typedef {UserDataEntry & {userId: string}} AdminUserDataEntry */
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* One user's activity over a scope and window (`audit.tally`).
|
|
149
|
+
* @typedef {object} ActivityTallyRow
|
|
150
|
+
* @property {{id: string, displayName?: string}} user
|
|
151
|
+
* @property {number} operations
|
|
152
|
+
* @property {number} changes
|
|
153
|
+
* @property {number} documents
|
|
154
|
+
* @property {string} firstTs
|
|
155
|
+
* @property {string} lastTs
|
|
156
|
+
* @property {Array<{date: string, changes: number}>} [byDay]
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* What a constraint repair did in one document.
|
|
161
|
+
* @typedef {{document: string, constraint: string, deleted: number, joined: number}} ConstraintRepair
|
|
162
|
+
*/
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* A value-set rule as `refusedValues` takes it: the list, the characters a
|
|
166
|
+
* value is split on, and whether every part (`"all"`, the default) or only
|
|
167
|
+
* the first must be listed.
|
|
168
|
+
* @typedef {{values: string[], delimiters?: string, parts?: "all" | "first"}} ValueSetRule
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* What a value-set rule would refuse in a layer's stored values: how many,
|
|
173
|
+
* how many distinct, and the most frequent with the parts not listed.
|
|
174
|
+
* @typedef {object} RefusedValues
|
|
175
|
+
* @property {number} count
|
|
176
|
+
* @property {number} valueCount
|
|
177
|
+
* @property {Array<{value: any, parts: string[], count: number}>} values
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Entities that can carry a comment.
|
|
182
|
+
* @typedef {"document" | "text" | "token" | "span" | "relation" | "vocab-item"} CommentableType
|
|
183
|
+
*/
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* One comment. `projectId` and `documentId` are null for a comment on a
|
|
187
|
+
* vocabulary entry, and `vocabLayerId` is set only for one. `anchorLabel` is
|
|
188
|
+
* what the comment is about, in words, captured when it was posted: a
|
|
189
|
+
* comment outlives its anchor. `edited` is true once the body has been
|
|
190
|
+
* rewritten since it was posted.
|
|
191
|
+
* @typedef {object} Comment
|
|
192
|
+
* @property {string} id
|
|
193
|
+
* @property {string | null} projectId
|
|
194
|
+
* @property {string | null} documentId
|
|
195
|
+
* @property {string | null} vocabLayerId
|
|
196
|
+
* @property {CommentableType} entityType
|
|
197
|
+
* @property {string} entityId
|
|
198
|
+
* @property {string | null} anchorLabel
|
|
199
|
+
* @property {string} authorId
|
|
200
|
+
* @property {string} body
|
|
201
|
+
* @property {string} createdAt
|
|
202
|
+
* @property {string} updatedAt
|
|
203
|
+
* @property {boolean} edited
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* One entry in a project's annotation manual. `body` is present on a single
|
|
208
|
+
* read and on a list made with `includeBodies`, `bodyChars` on a list made
|
|
209
|
+
* without. `pinned`: the assistant is given this one in full on every turn.
|
|
210
|
+
* @typedef {object} Guideline
|
|
211
|
+
* @property {string} id
|
|
212
|
+
* @property {string} projectId
|
|
213
|
+
* @property {string} title
|
|
214
|
+
* @property {string} [body]
|
|
215
|
+
* @property {number} [bodyChars]
|
|
216
|
+
* @property {boolean} pinned
|
|
217
|
+
* @property {string} createdAt
|
|
218
|
+
* @property {string} updatedAt
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* A research-telemetry event type.
|
|
223
|
+
* @typedef {"suggestion.shown" | "suggestion.adopted" | "suggestion.dismissed" | "plan.opened"} ClientEventType
|
|
224
|
+
*/
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* One research-telemetry event as the server stores it.
|
|
228
|
+
* @typedef {object} ClientEvent
|
|
229
|
+
* @property {number} id
|
|
230
|
+
* @property {string} projectId
|
|
231
|
+
* @property {string | null} documentId
|
|
232
|
+
* @property {string} userId
|
|
233
|
+
* @property {ClientEventType} type
|
|
234
|
+
* @property {string | null} targetId
|
|
235
|
+
* @property {Record<string, any> | null} data
|
|
236
|
+
* @property {string | null} clientTs
|
|
237
|
+
* @property {string} ts
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* An invite. `kind` is "signup" or "password-reset", `status` "active",
|
|
242
|
+
* "used", "expired", "revoked" or "inactive".
|
|
243
|
+
* @typedef {object} Invite
|
|
244
|
+
* @property {string} id
|
|
245
|
+
* @property {string} kind
|
|
246
|
+
* @property {string} status
|
|
247
|
+
* @property {string} createdBy
|
|
248
|
+
* @property {string} createdAt
|
|
249
|
+
* @property {string} expiresAt
|
|
250
|
+
* @property {number} maxUses
|
|
251
|
+
* @property {number} uses
|
|
252
|
+
* @property {string | null} revokedAt
|
|
253
|
+
* @property {string | null} note
|
|
254
|
+
* @property {string | null} targetUserId
|
|
255
|
+
* @property {boolean} grantAdmin
|
|
256
|
+
* @property {string | null} projectId
|
|
257
|
+
* @property {string | null} projectRole
|
|
258
|
+
*/
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Server limits, as reported by /api/v1/info. An unset limit is absent.
|
|
262
|
+
* `passwordMinLength` is the shortest password the server accepts on any
|
|
263
|
+
* path, `lockExpirationMs` how long a document lock is held before it lapses.
|
|
264
|
+
* @typedef {object} ServerLimits
|
|
265
|
+
* @property {number} [mediaFileBytes]
|
|
266
|
+
* @property {number} [jsonBodyBytes]
|
|
267
|
+
* @property {number} [batchOperations]
|
|
268
|
+
* @property {number} [metadataDepth]
|
|
269
|
+
* @property {number} [metadataKeyCount]
|
|
270
|
+
* @property {number} [metadataStringLength]
|
|
271
|
+
* @property {number} [metadataTotalBytes]
|
|
272
|
+
* @property {number} [userDataValueBytes]
|
|
273
|
+
* @property {number} [guidelineTitleLength]
|
|
274
|
+
* @property {number} [guidelineBodyLength]
|
|
275
|
+
* @property {number} [commentBodyLength]
|
|
276
|
+
* @property {number} [passwordMinLength]
|
|
277
|
+
* @property {number} [lockExpirationMs]
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* @typedef {object} ServerHealth
|
|
282
|
+
* @property {boolean} ok
|
|
283
|
+
* @property {string} version
|
|
284
|
+
* @property {number} uptimeMs
|
|
285
|
+
* @property {{dbSizeMb?: number, auditRows?: number, error?: string}} audit
|
|
286
|
+
*/
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* @typedef {{ip: string, userId?: string, failures: number, limit: number, blocked: boolean}} RateLimitBucket
|
|
290
|
+
*/
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* @typedef {object} RateLimitSnapshot
|
|
294
|
+
* @property {number} windowMs
|
|
295
|
+
* @property {RateLimitBucket[]} logins
|
|
296
|
+
* @property {RateLimitBucket[]} ips
|
|
297
|
+
* @property {RateLimitBucket[]} invites
|
|
298
|
+
*/
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @typedef {object} BackupStatus
|
|
302
|
+
* @property {boolean} enabled
|
|
303
|
+
* @property {string} directory
|
|
304
|
+
* @property {number} retention
|
|
305
|
+
* @property {string} time
|
|
306
|
+
* @property {Array<{name: string, bytes: number, modified: string}>} backups
|
|
307
|
+
* @property {boolean} [ok]
|
|
308
|
+
*/
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @typedef {object} ServerReport
|
|
312
|
+
* @property {string} version
|
|
313
|
+
* @property {{uptimeMs: number, startedAt: string, java: string, heapUsed: number, heapMax: number}} jvm
|
|
314
|
+
* @property {{path: string | null, bytes: number | null, walBytes: number | null,
|
|
315
|
+
* slowQueryThresholdMs?: number, journalMode?: string, maxPoolSize?: number,
|
|
316
|
+
* tables: Record<string, number | null>}} database
|
|
317
|
+
* @property {{directory: string, files: number, bytes: number, orphans: number, orphanBytes: number}} media
|
|
318
|
+
* @property {BackupStatus} backup
|
|
319
|
+
* @property {Record<string, any>} settings
|
|
320
|
+
*/
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* The live log. Requests and events are buffered separately, so a burst of
|
|
324
|
+
* requests cannot evict an error. In each, `matched` is how many passed the
|
|
325
|
+
* filters (which may exceed what `entries` holds) and `held` how many are
|
|
326
|
+
* buffered in total. `file` is the configured log file's absolute path, or
|
|
327
|
+
* null when logging to stdout.
|
|
328
|
+
* @typedef {object} LiveLog
|
|
329
|
+
* @property {{entries: object[], matched: number, held: number, capacity: number,
|
|
330
|
+
* stats: Record<string, number | null>}} requests
|
|
331
|
+
* @property {{entries: object[], matched: number, held: number, capacity: number,
|
|
332
|
+
* byLevel: Record<string, number>}} events
|
|
333
|
+
* @property {string | null} file
|
|
334
|
+
*/
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* An open server-sent-events stream. Once CLOSED, `error` says why: null when
|
|
338
|
+
* this side closed it. A refused stream's error carries the HTTP `status`,
|
|
339
|
+
* and one the server ended is named "StreamClosed".
|
|
340
|
+
* @typedef {object} SSEConnection
|
|
341
|
+
* @property {() => void} close
|
|
342
|
+
* @property {() => SSEStats} getStats
|
|
343
|
+
* @property {number} readyState
|
|
344
|
+
* @property {(Error & {status?: number}) | null} error
|
|
345
|
+
*/
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* How an SSE connection has fared so far. `events` counts what arrived by
|
|
349
|
+
* event type ("audit-log", "message", "heartbeat", "connected", "other").
|
|
350
|
+
* @typedef {object} SSEStats
|
|
351
|
+
* @property {number} durationSeconds
|
|
352
|
+
* @property {boolean} isConnected
|
|
353
|
+
* @property {boolean} isClosed
|
|
354
|
+
* @property {string | null} clientId
|
|
355
|
+
* @property {Record<string, number>} events
|
|
356
|
+
* @property {number} readyState
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A value a span or relation holds: a JSON scalar.
|
|
361
|
+
* @typedef {string | number | boolean | null} AtomicValue
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* What a create answers: the new record's id.
|
|
366
|
+
* @typedef {{id: string}} Created
|
|
367
|
+
*/
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* A project. `textLayers` nests its layers in their display order, each
|
|
371
|
+
* with the layers built on it, and `vocabs` lists the vocabularies linked to
|
|
372
|
+
* it.
|
|
373
|
+
* @typedef {object} Project
|
|
374
|
+
* @property {string} id
|
|
375
|
+
* @property {string} name
|
|
376
|
+
* @property {Record<string, any>} config
|
|
377
|
+
* @property {string[]} readers
|
|
378
|
+
* @property {string[]} writers
|
|
379
|
+
* @property {string[]} maintainers
|
|
380
|
+
* @property {ProjectTextLayer[]} textLayers
|
|
381
|
+
* @property {ProjectVocab[]} vocabs
|
|
382
|
+
*/
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @typedef {{id: string, name: string, config: Record<string, any>, tokenLayers: ProjectTokenLayer[]}} ProjectTextLayer
|
|
386
|
+
* @typedef {{id: string, name: string, config: Record<string, any>,
|
|
387
|
+
* constraints: Record<string, LayerConstraint[]>, spanLayers: ProjectSpanLayer[]}} ProjectTokenLayer
|
|
388
|
+
* @typedef {{id: string, name: string, config: Record<string, any>,
|
|
389
|
+
* constraints: Record<string, LayerConstraint[]>, relationLayers: ProjectRelationLayer[]}} ProjectSpanLayer
|
|
390
|
+
* @typedef {{id: string, name: string, config: Record<string, any>,
|
|
391
|
+
* constraints: Record<string, LayerConstraint[]>}} ProjectRelationLayer
|
|
392
|
+
* @typedef {{id: string, name: string, maintainers: string[], config: Record<string, any>}} ProjectVocab
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* A document. `version` rises by one on every write to it or its layers.
|
|
397
|
+
* A read with `includeBody` also carries `textLayers`, the document's
|
|
398
|
+
* annotation nested under each layer.
|
|
399
|
+
* @typedef {object} PlaidDocument
|
|
400
|
+
* @property {string} id
|
|
401
|
+
* @property {string} name
|
|
402
|
+
* @property {string} project
|
|
403
|
+
* @property {number} version
|
|
404
|
+
* @property {string} timeCreated
|
|
405
|
+
* @property {string} timeModified
|
|
406
|
+
* @property {Record<string, any>} [metadata]
|
|
407
|
+
* @property {DocumentTextLayer[]} [textLayers]
|
|
408
|
+
*/
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* A text layer as a document read nests it: `text` is null where the
|
|
412
|
+
* document has no text in this layer.
|
|
413
|
+
* @typedef {object} DocumentTextLayer
|
|
414
|
+
* @property {string} id
|
|
415
|
+
* @property {string} name
|
|
416
|
+
* @property {Record<string, any>} config
|
|
417
|
+
* @property {{id: string, document: string, body: string, digest: string,
|
|
418
|
+
* metadata?: Record<string, any>} | null} text
|
|
419
|
+
* @property {DocumentTokenLayer[]} tokenLayers
|
|
420
|
+
*/
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* @typedef {object} DocumentTokenLayer
|
|
424
|
+
* @property {string} id
|
|
425
|
+
* @property {string} name
|
|
426
|
+
* @property {Record<string, any>} config
|
|
427
|
+
* @property {Record<string, LayerConstraint[]>} constraints
|
|
428
|
+
* @property {string} overlapMode
|
|
429
|
+
* @property {string | null} parentTokenLayer
|
|
430
|
+
* @property {Array<{id: string, begin: number, end: number, precedence: number | null,
|
|
431
|
+
* metadata?: Record<string, any>}>} tokens
|
|
432
|
+
* @property {DocumentSpanLayer[]} spanLayers
|
|
433
|
+
* @property {Array<ProjectVocab & {vocabLinks: Array<{id: string, vocabItem: VocabItem,
|
|
434
|
+
* tokens: string[], metadata?: Record<string, any>}>}>} vocabs
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* @typedef {object} DocumentSpanLayer
|
|
439
|
+
* @property {string} id
|
|
440
|
+
* @property {string} name
|
|
441
|
+
* @property {Record<string, any>} config
|
|
442
|
+
* @property {Record<string, LayerConstraint[]>} constraints
|
|
443
|
+
* @property {Array<{id: string, value: AtomicValue, tokens: string[],
|
|
444
|
+
* metadata?: Record<string, any>}>} spans
|
|
445
|
+
* @property {Array<ProjectRelationLayer & {relations: Array<{id: string, source: string,
|
|
446
|
+
* target: string, value: AtomicValue, metadata?: Record<string, any>}>}>} relationLayers
|
|
447
|
+
*/
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* A text: one document's body in one text layer. `digest` names the body,
|
|
451
|
+
* as an edit's `base`.
|
|
452
|
+
* @typedef {object} PlaidText
|
|
453
|
+
* @property {string} id
|
|
454
|
+
* @property {string} body
|
|
455
|
+
* @property {string} document
|
|
456
|
+
* @property {string} layer
|
|
457
|
+
* @property {string} digest
|
|
458
|
+
* @property {Record<string, any>} [metadata]
|
|
459
|
+
*/
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* What a text write changed besides the body: the tokens whose extent
|
|
463
|
+
* moved (with `layer` and `text` on one the write made), the spans and
|
|
464
|
+
* vocabulary links whose tokens or value changed, and the ids of what it
|
|
465
|
+
* deleted.
|
|
466
|
+
* @typedef {object} TextReshape
|
|
467
|
+
* @property {Array<{id: string, begin: number, end: number, layer?: string, text?: string}>} tokens
|
|
468
|
+
* @property {Array<{id: string, tokens?: string[], value?: AtomicValue}>} spans
|
|
469
|
+
* @property {Array<{id: string, tokens: string[]}>} vocabLinks
|
|
470
|
+
* @property {{tokens: string[], spans: string[], relations: string[], vocabLinks: string[]}} deleted
|
|
471
|
+
*/
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* A token. `value` is the text it covers.
|
|
475
|
+
* @typedef {object} Token
|
|
476
|
+
* @property {string} id
|
|
477
|
+
* @property {string} text
|
|
478
|
+
* @property {number} begin
|
|
479
|
+
* @property {number} end
|
|
480
|
+
* @property {string} layer
|
|
481
|
+
* @property {string} document
|
|
482
|
+
* @property {number | null} precedence
|
|
483
|
+
* @property {string} value
|
|
484
|
+
* @property {Record<string, any>} [metadata]
|
|
485
|
+
*/
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* @typedef {object} Span
|
|
489
|
+
* @property {string} id
|
|
490
|
+
* @property {string} layer
|
|
491
|
+
* @property {string} document
|
|
492
|
+
* @property {AtomicValue} value
|
|
493
|
+
* @property {string[]} tokens
|
|
494
|
+
* @property {Record<string, any>} [metadata]
|
|
495
|
+
*/
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* A relation, from the span `source` to the span `target`.
|
|
499
|
+
* @typedef {object} Relation
|
|
500
|
+
* @property {string} id
|
|
501
|
+
* @property {string} layer
|
|
502
|
+
* @property {string} document
|
|
503
|
+
* @property {string} source
|
|
504
|
+
* @property {string} target
|
|
505
|
+
* @property {AtomicValue} value
|
|
506
|
+
* @property {Record<string, any>} [metadata]
|
|
507
|
+
*/
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* @typedef {object} VocabItem
|
|
511
|
+
* @property {string} id
|
|
512
|
+
* @property {string} layer
|
|
513
|
+
* @property {string} form
|
|
514
|
+
* @property {Record<string, any>} [metadata]
|
|
515
|
+
*/
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* A link from a vocabulary entry to the tokens of one occurrence.
|
|
519
|
+
* @typedef {object} VocabLink
|
|
520
|
+
* @property {string} id
|
|
521
|
+
* @property {string} vocabItem
|
|
522
|
+
* @property {string} document
|
|
523
|
+
* @property {string[]} tokens
|
|
524
|
+
* @property {Record<string, any>} [metadata]
|
|
525
|
+
*/
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* A vocabulary. `items` is there on a read made with `includeItems`.
|
|
529
|
+
* @typedef {object} VocabLayer
|
|
530
|
+
* @property {string} id
|
|
531
|
+
* @property {string} name
|
|
532
|
+
* @property {string} timeCreated
|
|
533
|
+
* @property {string} timeModified
|
|
534
|
+
* @property {Record<string, any>} config
|
|
535
|
+
* @property {string[]} maintainers
|
|
536
|
+
* @property {VocabItem[]} [items]
|
|
537
|
+
*/
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* @typedef {{id: string, name: string, project: string, config: Record<string, any>}} TextLayer
|
|
541
|
+
* @typedef {{id: string, name: string, textLayer: string, project: string, overlapMode: string,
|
|
542
|
+
* parentTokenLayer: string | null, config: Record<string, any>,
|
|
543
|
+
* constraints: Record<string, LayerConstraint[]>}} TokenLayer
|
|
544
|
+
* @typedef {{id: string, name: string, tokenLayer: string, project: string, config: Record<string, any>,
|
|
545
|
+
* constraints: Record<string, LayerConstraint[]>}} SpanLayer
|
|
546
|
+
* @typedef {{id: string, name: string, spanLayer: string, project: string, config: Record<string, any>,
|
|
547
|
+
* constraints: Record<string, LayerConstraint[]>}} RelationLayer
|
|
548
|
+
*/
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* The records with metadata, by the path segment of their routes.
|
|
552
|
+
* @typedef {{documents: PlaidDocument, texts: PlaidText, tokens: Token, spans: Span,
|
|
553
|
+
* relations: Relation, "vocab-items": VocabItem, "vocab-links": VocabLink}} MetadataRecords
|
|
554
|
+
*/
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* The four layer kinds, by the path segment of their routes.
|
|
558
|
+
* @typedef {{"text-layers": TextLayer, "token-layers": TokenLayer, "span-layers": SpanLayer,
|
|
559
|
+
* "relation-layers": RelationLayer}} LayerRecords
|
|
560
|
+
*/
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* @typedef {object} User
|
|
564
|
+
* @property {string} id
|
|
565
|
+
* @property {string} displayName
|
|
566
|
+
* @property {boolean} isAdmin
|
|
567
|
+
* @property {string | null} deactivatedAt
|
|
568
|
+
* @property {string | null} avatarHash
|
|
569
|
+
*/
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* A project as the project list gives it: the project, with how many
|
|
573
|
+
* documents it has and when the newest of them last changed.
|
|
574
|
+
* @typedef {Project & {documentCount: number, lastModified: string | null}} ProjectListEntry
|
|
575
|
+
*/
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* A document as a project's document list gives it.
|
|
579
|
+
* @typedef {object} DocumentListEntry
|
|
580
|
+
* @property {string} id
|
|
581
|
+
* @property {string} name
|
|
582
|
+
* @property {number} version
|
|
583
|
+
* @property {string} timeCreated
|
|
584
|
+
* @property {string} timeModified
|
|
585
|
+
*/
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* A named API token, without the signed token itself, which only `create`
|
|
589
|
+
* returns.
|
|
590
|
+
* @typedef {object} ApiToken
|
|
591
|
+
* @property {string} id
|
|
592
|
+
* @property {string} userId
|
|
593
|
+
* @property {string} name
|
|
594
|
+
* @property {string} createdAt
|
|
595
|
+
* @property {string | null} lastUsedAt
|
|
596
|
+
* @property {string | null} revokedAt
|
|
597
|
+
*/
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Who made an operation, as the audit log names them.
|
|
601
|
+
* @typedef {{id: string, displayName: string, avatarHash: string | null}} AuditUser
|
|
602
|
+
*/
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* A project or a document an audit entry names, while it exists.
|
|
606
|
+
* @typedef {{id: string, name: string}} AuditTarget
|
|
607
|
+
*/
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* One operation of an audit entry.
|
|
611
|
+
* @typedef {object} AuditOp
|
|
612
|
+
* @property {string} id
|
|
613
|
+
* @property {string} type The kind of write, such as "document/update".
|
|
614
|
+
* @property {string} description
|
|
615
|
+
* @property {string} time
|
|
616
|
+
* @property {string} endTime The time to read at to see the operation done.
|
|
617
|
+
* @property {AuditUser | null} user
|
|
618
|
+
* @property {string} [credential]
|
|
619
|
+
* @property {AuditTarget} [project]
|
|
620
|
+
* @property {AuditTarget} [document]
|
|
621
|
+
* @property {string} [batchId]
|
|
622
|
+
*/
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* One entry of an audit log: a logical operation, an atomic batch or a lone
|
|
626
|
+
* write, with its operations oldest first (`ops`, cut to `opsLimit` when one
|
|
627
|
+
* was asked for, while `opCount` counts them all).
|
|
628
|
+
* @typedef {object} AuditEntry
|
|
629
|
+
* @property {string} id
|
|
630
|
+
* @property {string} time
|
|
631
|
+
* @property {string} endTime
|
|
632
|
+
* @property {AuditUser | null} user
|
|
633
|
+
* @property {AuditTarget[]} projects
|
|
634
|
+
* @property {AuditTarget[]} documents
|
|
635
|
+
* @property {AuditOp[]} ops
|
|
636
|
+
* @property {number} opCount
|
|
637
|
+
* @property {string} [groupId]
|
|
638
|
+
* @property {string} [message]
|
|
639
|
+
* @property {OperationKind} [kind]
|
|
640
|
+
* @property {string} [ref]
|
|
641
|
+
* @property {string} [batchId]
|
|
642
|
+
* @property {{id: string, name: string}} [apiToken]
|
|
643
|
+
* @property {string} [credential]
|
|
644
|
+
*/
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* A document lock as its holder sees it. `expiresAt` is in epoch
|
|
648
|
+
* milliseconds.
|
|
649
|
+
* @typedef {{lockId: string, userId: string, expiresAt: number}} LockGrant
|
|
650
|
+
*/
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* What `documents.restore` did, or with `dryRun` would do: per kind of
|
|
654
|
+
* record, how many come back, are set back and are removed (per layer for
|
|
655
|
+
* the layered kinds), and what could not come back, with why.
|
|
656
|
+
* @typedef {object} DocumentRestoreSummary
|
|
657
|
+
* @property {boolean} name
|
|
658
|
+
* @property {boolean} documentMetadata
|
|
659
|
+
* @property {RestoreCounts} texts
|
|
660
|
+
* @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} tokens
|
|
661
|
+
* @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} spans
|
|
662
|
+
* @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} relations
|
|
663
|
+
* @property {RestoreCounts} vocabLinks
|
|
664
|
+
* @property {Array<{kind: string, count: number, reason: string}>} skipped
|
|
665
|
+
* @property {{name: string, taken: string}} [nameKept] When the name the
|
|
666
|
+
* document had then belongs to another document now: the name it keeps,
|
|
667
|
+
* and that other document's name.
|
|
668
|
+
* @property {number} total 0 when nothing changes.
|
|
669
|
+
*/
|
|
670
|
+
|
|
671
|
+
/** @typedef {{inserted: number, updated: number, deleted: number}} RestoreCounts */
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* A logical-operation group: its label, who made it and when, and the kind
|
|
675
|
+
* and reference of the operation when it has them.
|
|
676
|
+
* @typedef {object} OperationGroup
|
|
677
|
+
* @property {string} id
|
|
678
|
+
* @property {string | null} message
|
|
679
|
+
* @property {string} user
|
|
680
|
+
* @property {string} createdAt
|
|
681
|
+
* @property {string} [kind]
|
|
682
|
+
* @property {string} [ref]
|
|
683
|
+
*/
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* A query's answer. For "ids" and "entities", one row of `results` per
|
|
687
|
+
* match, a cell per `columns` entry: an id, or the entity as its GET
|
|
688
|
+
* answers it. For "count", only the count. `truncated`: the rows or the
|
|
689
|
+
* count stopped at the server's cap.
|
|
690
|
+
* @typedef {object} QueryResult
|
|
691
|
+
* @property {string} return
|
|
692
|
+
* @property {number} count
|
|
693
|
+
* @property {boolean} truncated
|
|
694
|
+
* @property {string[]} [columns]
|
|
695
|
+
* @property {any[][]} [results]
|
|
696
|
+
*/
|
|
697
|
+
|
|
42
698
|
// Helper: normalize a document-read `layers` filter to the wire's
|
|
43
699
|
// comma-separated form. Accepts an array of layer ids or a ready-made string;
|
|
44
700
|
// anything empty becomes undefined so no `?layers=` is sent at all.
|
|
@@ -70,48 +726,22 @@ function bodyOf(obj) {
|
|
|
70
726
|
return result;
|
|
71
727
|
}
|
|
72
728
|
|
|
73
|
-
//
|
|
74
|
-
// and
|
|
75
|
-
// are called before any client exists,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const url = `${base}${path}`;
|
|
79
|
-
try {
|
|
80
|
-
const fetchOptions = {
|
|
81
|
-
method: "POST",
|
|
82
|
-
headers: { "Content-Type": "application/json" },
|
|
83
|
-
body: JSON.stringify(body),
|
|
84
|
-
};
|
|
85
|
-
const signal = timeoutSignal(
|
|
86
|
-
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS,
|
|
87
|
-
);
|
|
88
|
-
if (signal) fetchOptions.signal = signal;
|
|
89
|
-
|
|
90
|
-
const response = await fetch(url, fetchOptions);
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
throw makeHttpError(
|
|
93
|
-
response,
|
|
94
|
-
await parseErrorBody(response),
|
|
95
|
-
url,
|
|
96
|
-
"POST",
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
return transformResponse(await response.json());
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (error.status) throw error;
|
|
102
|
-
throw makeNetworkError(error, url, "POST");
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* GET a path with no authentication, for the two endpoints a client may need
|
|
108
|
-
* before it has a session: /health and /api/v1/info.
|
|
109
|
-
*/
|
|
110
|
-
async function anonymousGet(baseUrl, path, options = {}) {
|
|
729
|
+
// A request with no Authorization header: login, invite lookup and redemption
|
|
730
|
+
// (POST, with a body), /health and /api/v1/info (GET). Deliberately does not
|
|
731
|
+
// go through PlaidClient._request: these are called before any client exists,
|
|
732
|
+
// which is the whole point.
|
|
733
|
+
async function anonymousRequest(method, baseUrl, path, body, options = {}) {
|
|
111
734
|
const base = baseUrl.replace(/\/$/, "");
|
|
112
735
|
const url = `${base}${path}`;
|
|
113
736
|
try {
|
|
114
|
-
const fetchOptions =
|
|
737
|
+
const fetchOptions =
|
|
738
|
+
method === "GET"
|
|
739
|
+
? { method }
|
|
740
|
+
: {
|
|
741
|
+
method,
|
|
742
|
+
headers: { "Content-Type": "application/json" },
|
|
743
|
+
body: JSON.stringify(body),
|
|
744
|
+
};
|
|
115
745
|
const signal = timeoutSignal(
|
|
116
746
|
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS,
|
|
117
747
|
);
|
|
@@ -119,12 +749,12 @@ async function anonymousGet(baseUrl, path, options = {}) {
|
|
|
119
749
|
|
|
120
750
|
const response = await fetch(url, fetchOptions);
|
|
121
751
|
if (!response.ok) {
|
|
122
|
-
throw makeHttpError(response, await parseErrorBody(response), url,
|
|
752
|
+
throw makeHttpError(response, await parseErrorBody(response), url, method);
|
|
123
753
|
}
|
|
124
754
|
return transformResponse(await response.json());
|
|
125
755
|
} catch (error) {
|
|
126
756
|
if (error.status) throw error;
|
|
127
|
-
throw makeNetworkError(error, url,
|
|
757
|
+
throw makeNetworkError(error, url, method);
|
|
128
758
|
}
|
|
129
759
|
}
|
|
130
760
|
|
|
@@ -155,15 +785,24 @@ const OPERATION_KINDS = [
|
|
|
155
785
|
*/
|
|
156
786
|
export const BACKUP_TIMEOUT_MS = 30 * 60 * 1000;
|
|
157
787
|
|
|
788
|
+
/** An avatar token is minted again once less than this is left of it. */
|
|
789
|
+
const AVATAR_TOKEN_RENEW_MS = 10 * 60 * 1000;
|
|
790
|
+
// An avatar token minted this recently is the renewal a caller asking for one
|
|
791
|
+
// wanted.
|
|
792
|
+
const AVATAR_TOKEN_FRESH_MS = 5 * 1000;
|
|
793
|
+
|
|
158
794
|
/**
|
|
159
795
|
* A batch is the client with a different `_request` (see `queueRequest`) and
|
|
160
796
|
* its own copy of the bundles. Everything else resolves to the client through
|
|
161
797
|
* the prototype: the token and base URL, strict mode and the document versions
|
|
162
798
|
* it tracks, the open logical operation.
|
|
163
799
|
*/
|
|
164
|
-
function openBatch(client) {
|
|
800
|
+
function openBatch(client, { timeout } = {}) {
|
|
165
801
|
const batch = Object.create(client);
|
|
166
802
|
batch.client = client;
|
|
803
|
+
// This batch's own timeout per request, in place of the client's
|
|
804
|
+
// `batchTimeout` (undefined: the client's).
|
|
805
|
+
batch.submitTimeout = timeout;
|
|
167
806
|
batch.operations = [];
|
|
168
807
|
// The document each queued op's strict-mode stamp is for (null for none),
|
|
169
808
|
// and the logical operation it joined (null for none), index for index with
|
|
@@ -226,7 +865,15 @@ async function submitBatch(batch) {
|
|
|
226
865
|
// needs to know. The same holds for `committedResults`.
|
|
227
866
|
const results = [];
|
|
228
867
|
try {
|
|
229
|
-
await sendChunks(
|
|
868
|
+
await sendChunks(
|
|
869
|
+
client,
|
|
870
|
+
url,
|
|
871
|
+
ops,
|
|
872
|
+
stamps,
|
|
873
|
+
results,
|
|
874
|
+
groups,
|
|
875
|
+
batch.submitTimeout,
|
|
876
|
+
);
|
|
230
877
|
} catch (error) {
|
|
231
878
|
if (error && typeof error === "object") {
|
|
232
879
|
error.committed = results.length;
|
|
@@ -243,7 +890,15 @@ function claimedVersion(path) {
|
|
|
243
890
|
return v == null ? null : Number(v);
|
|
244
891
|
}
|
|
245
892
|
|
|
246
|
-
async function sendChunks(
|
|
893
|
+
async function sendChunks(
|
|
894
|
+
client,
|
|
895
|
+
url,
|
|
896
|
+
ops,
|
|
897
|
+
stamps,
|
|
898
|
+
results,
|
|
899
|
+
groups = [],
|
|
900
|
+
timeout = undefined,
|
|
901
|
+
) {
|
|
247
902
|
// Every chunk's refs are checked before the first request goes, so a batch
|
|
248
903
|
// whose refs cannot resolve writes nothing.
|
|
249
904
|
const chunks = [];
|
|
@@ -281,7 +936,7 @@ async function sendChunks(client, url, ops, stamps, results, groups = []) {
|
|
|
281
936
|
// its path now carries it.
|
|
282
937
|
const j = stamps.slice(i, i + chunk.length).findIndex(Boolean);
|
|
283
938
|
record(j < 0 ? null : claimedVersion(chunk[j].path));
|
|
284
|
-
const answered = await client._postBatch(url, chunk, { key });
|
|
939
|
+
const answered = await client._postBatch(url, chunk, { key, timeout });
|
|
285
940
|
results.push(...answered);
|
|
286
941
|
if (wasReplayed(answered)) markReplayed(results);
|
|
287
942
|
// The server took the chunk, so each operation it joined exists.
|
|
@@ -316,13 +971,12 @@ class PlaidClient {
|
|
|
316
971
|
* Create a new PlaidClient instance
|
|
317
972
|
* @param {string} baseUrl - The base URL for the API
|
|
318
973
|
* @param {string} token - The authentication token
|
|
319
|
-
* @param {
|
|
320
|
-
* @param {number} [options.timeout=30000] - Per-request timeout in ms (0 or null disables it)
|
|
321
|
-
* @param {number} [options.batchTimeout=180000] - Timeout for batch submissions in ms (0 or null disables it)
|
|
974
|
+
* @param {PlaidClientOptions} [options] - Client options
|
|
322
975
|
*/
|
|
323
976
|
constructor(baseUrl, token, options = {}) {
|
|
324
977
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
325
978
|
this.token = token;
|
|
979
|
+
/** @type {number | null} */
|
|
326
980
|
this.timeout =
|
|
327
981
|
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS;
|
|
328
982
|
// Batch submissions get a longer budget than a single request. Aborting a
|
|
@@ -330,6 +984,7 @@ class PlaidClient {
|
|
|
330
984
|
// holding the single SQLite write lock, so a client that gives up early
|
|
331
985
|
// just adds a retry on top of a write that is still in progress. Falls
|
|
332
986
|
// back to an explicit `timeout` when one was given.
|
|
987
|
+
/** @type {number | null} */
|
|
333
988
|
this.batchTimeout =
|
|
334
989
|
options.batchTimeout !== undefined
|
|
335
990
|
? options.batchTimeout
|
|
@@ -338,33 +993,90 @@ class PlaidClient {
|
|
|
338
993
|
: DEFAULT_BATCH_TIMEOUT_MS;
|
|
339
994
|
// Delays (ms) before sending a keyed write again when its answer was lost
|
|
340
995
|
// (no response, 502, 504). See the Idempotency-Key note in http.js.
|
|
996
|
+
/** @type {number[] | undefined} */
|
|
341
997
|
this.retryDelaysMs = options.retryDelaysMs;
|
|
998
|
+
/**
|
|
999
|
+
* The latest version seen for each document written through this client,
|
|
1000
|
+
* keyed by document id.
|
|
1001
|
+
* @type {Record<string, number>}
|
|
1002
|
+
*/
|
|
342
1003
|
this.documentVersions = {};
|
|
1004
|
+
/**
|
|
1005
|
+
* True once a response left its version list out
|
|
1006
|
+
* (X-Document-Versions-Omitted, past fifty documents). Every version held
|
|
1007
|
+
* was then forgotten, and the strict-mode document's is read again before
|
|
1008
|
+
* the next write.
|
|
1009
|
+
* @type {boolean | undefined}
|
|
1010
|
+
*/
|
|
1011
|
+
this.documentVersionsOmitted = undefined;
|
|
343
1012
|
// The server's clock minus this machine's, in ms, from the last response
|
|
344
1013
|
// with a Date header (null before one). See serverNow().
|
|
1014
|
+
/** @type {number | null} */
|
|
345
1015
|
this.serverClockOffsetMs = null;
|
|
1016
|
+
/**
|
|
1017
|
+
* The document strict mode is entered for, or null.
|
|
1018
|
+
* @type {string | null}
|
|
1019
|
+
*/
|
|
346
1020
|
this.strictModeDocumentId = null;
|
|
347
1021
|
// Set to a DocumentLockLost while a `documents.locked()` block's keep-alive
|
|
348
1022
|
// has failed. Every write throws it until the block exits; see
|
|
349
1023
|
// documentLock.js.
|
|
1024
|
+
/** @type {import("./documentLock.js").DocumentLockLost | null} */
|
|
350
1025
|
this.documentLockLost = null;
|
|
351
1026
|
// The open logical operation (audit-log group), or null. While set, every
|
|
352
1027
|
// write is stamped with `?group-id=` (+ `group-message`) so the audit log
|
|
353
1028
|
// folds them into ONE expandable entry. See beginOperation / withOperation.
|
|
354
1029
|
// Shape: { id, message, depth, written, refined, frames }, where each of
|
|
355
1030
|
// `frames` is { keys, count, minted, depth, owned } and the last numbers.
|
|
1031
|
+
/**
|
|
1032
|
+
* @type {{id: string, message: string | null, kind: OperationKind | null,
|
|
1033
|
+
* ref: string | null, depth: number, written: boolean, refined: any,
|
|
1034
|
+
* frames: object[]} | null}
|
|
1035
|
+
*/
|
|
356
1036
|
this.operationGroup = null;
|
|
357
1037
|
// Optional callback fired once (per client) when any request returns HTTP
|
|
358
1038
|
// 401 — i.e. the token is missing/expired/invalid. Apps use it to discard
|
|
359
1039
|
// the stored token and route back to login. See makeRequest in http.js.
|
|
1040
|
+
/** @type {((error: Error) => void) | null} */
|
|
360
1041
|
this.onAuthError = options.onAuthError || null;
|
|
1042
|
+
/** @private */
|
|
361
1043
|
this._authErrorFired = false;
|
|
362
1044
|
// Who hears that a request is being sent again (onRetry).
|
|
1045
|
+
/** @private */
|
|
363
1046
|
this._retryListeners = new Set();
|
|
1047
|
+
// GET /info as last read (`server.info()`), and whether a stream of this
|
|
1048
|
+
// client dropped since.
|
|
1049
|
+
/** @private */
|
|
1050
|
+
this._serverFacts = { info: null, stale: false };
|
|
1051
|
+
// The key frame the last beginOperation opened, or null (withOperation).
|
|
1052
|
+
/** @private @type {object | null} */
|
|
1053
|
+
this._openedFrame = null;
|
|
364
1054
|
|
|
365
1055
|
this._installResources();
|
|
366
1056
|
}
|
|
367
1057
|
|
|
1058
|
+
/**
|
|
1059
|
+
* A stream of this client ended without being closed here: the server may
|
|
1060
|
+
* have restarted, with other limits.
|
|
1061
|
+
* @private
|
|
1062
|
+
*/
|
|
1063
|
+
_noteStreamDropped() {
|
|
1064
|
+
this._serverFacts.stale = true;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* A stream of this client is open. After a drop the limits are read again,
|
|
1069
|
+
* once however many streams reopen.
|
|
1070
|
+
* @private
|
|
1071
|
+
*/
|
|
1072
|
+
_noteStreamOpened() {
|
|
1073
|
+
if (!this._serverFacts.stale) return;
|
|
1074
|
+
this.server.refresh().catch(() => {
|
|
1075
|
+
// The figures known before stand, and the next open asks again.
|
|
1076
|
+
this._serverFacts.stale = true;
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
368
1080
|
/**
|
|
369
1081
|
* Hear every time a request is sent again: after a 503 (the database was
|
|
370
1082
|
* busy), or for a keyed write after its answer was lost (no response, 502,
|
|
@@ -379,7 +1091,10 @@ class PlaidClient {
|
|
|
379
1091
|
return () => this._retryListeners.delete(listener);
|
|
380
1092
|
}
|
|
381
1093
|
|
|
382
|
-
/**
|
|
1094
|
+
/**
|
|
1095
|
+
* Tell every `onRetry` listener. A listener that throws is passed over.
|
|
1096
|
+
* @private
|
|
1097
|
+
*/
|
|
383
1098
|
_noteRetry(info) {
|
|
384
1099
|
for (const listener of this._retryListeners) {
|
|
385
1100
|
try {
|
|
@@ -406,6 +1121,7 @@ class PlaidClient {
|
|
|
406
1121
|
* the client, and again on every batch it opens (see `batch()`): a batch is
|
|
407
1122
|
* a view of the client with its own `_request`, so the same bundles built
|
|
408
1123
|
* on it queue instead of sending.
|
|
1124
|
+
* @private
|
|
409
1125
|
*/
|
|
410
1126
|
_installResources() {
|
|
411
1127
|
this.vocabLinks = {
|
|
@@ -414,6 +1130,10 @@ class PlaidClient {
|
|
|
414
1130
|
* @param {string} vocabItem - The vocab item to link
|
|
415
1131
|
* @param {Array} tokens - The tokens to link
|
|
416
1132
|
* @param {any} [metadata] - Metadata for the link. Omit to leave unset; pass null to send JSON null.
|
|
1133
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1134
|
+
* @param {object} [opts]
|
|
1135
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1136
|
+
* @returns {Promise<Created>}
|
|
417
1137
|
*/
|
|
418
1138
|
create: (vocabItem, tokens, metadata, auditMessage, { id } = {}) =>
|
|
419
1139
|
this._request("POST", "/api/v1/vocab-links", {
|
|
@@ -425,6 +1145,7 @@ class PlaidClient {
|
|
|
425
1145
|
* reference different vocab items, but all tokens across the call must
|
|
426
1146
|
* belong to one document.
|
|
427
1147
|
* @param {Array<{vocabItem: string, tokens: string[], metadata?: any}>} body - The vocab links to create
|
|
1148
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
428
1149
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
429
1150
|
*/
|
|
430
1151
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -435,49 +1156,27 @@ class PlaidClient {
|
|
|
435
1156
|
/**
|
|
436
1157
|
* Delete multiple vocab links in a single operation. Provide an array of IDs.
|
|
437
1158
|
* @param {string[]} body - The vocab link IDs to delete
|
|
1159
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1160
|
+
* @returns {Promise<void>}
|
|
438
1161
|
*/
|
|
439
1162
|
bulkDelete: (body, auditMessage) =>
|
|
440
1163
|
this._request("DELETE", "/api/v1/vocab-links/bulk", {
|
|
441
1164
|
auditMessage,
|
|
442
1165
|
body,
|
|
443
1166
|
}),
|
|
444
|
-
|
|
445
|
-
* Replace all metadata for a vocab link. The entire metadata map is replaced - existing metadata keys not included in the request will be removed.
|
|
446
|
-
* @param {string} id - The resource ID
|
|
447
|
-
* @param {any} body - The request body
|
|
448
|
-
*/
|
|
449
|
-
setMetadata: (id, body, auditMessage) =>
|
|
450
|
-
this._request("PUT", `/api/v1/vocab-links/${id}/metadata`, {
|
|
451
|
-
auditMessage,
|
|
452
|
-
rawBody: body,
|
|
453
|
-
}),
|
|
454
|
-
/**
|
|
455
|
-
* Remove all metadata from a vocab link.
|
|
456
|
-
* @param {string} id - The resource ID
|
|
457
|
-
*/
|
|
458
|
-
deleteMetadata: (id, auditMessage) =>
|
|
459
|
-
this._request("DELETE", `/api/v1/vocab-links/${id}/metadata`, {
|
|
460
|
-
auditMessage,
|
|
461
|
-
}),
|
|
462
|
-
/**
|
|
463
|
-
* Edit metadata for a vocab link with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
464
|
-
* @param {string} id - The resource ID
|
|
465
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
466
|
-
*/
|
|
467
|
-
patchMetadata: (id, body, auditMessage) =>
|
|
468
|
-
this._request("PATCH", `/api/v1/vocab-links/${id}/metadata`, {
|
|
469
|
-
auditMessage,
|
|
470
|
-
rawBody: body,
|
|
471
|
-
}),
|
|
1167
|
+
...metadataMethods(this, "vocab-links"),
|
|
472
1168
|
/**
|
|
473
1169
|
* Get a vocab link by ID
|
|
474
1170
|
* @param {string} id - The resource ID
|
|
1171
|
+
* @returns {Promise<VocabLink>}
|
|
475
1172
|
*/
|
|
476
1173
|
get: (id) =>
|
|
477
1174
|
this._request("GET", `/api/v1/vocab-links/${id}`),
|
|
478
1175
|
/**
|
|
479
1176
|
* Delete a vocab link
|
|
480
1177
|
* @param {string} id - The resource ID
|
|
1178
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1179
|
+
* @returns {Promise<void>}
|
|
481
1180
|
*/
|
|
482
1181
|
delete: (id, auditMessage) =>
|
|
483
1182
|
this._request("DELETE", `/api/v1/vocab-links/${id}`, { auditMessage }),
|
|
@@ -492,6 +1191,7 @@ class PlaidClient {
|
|
|
492
1191
|
* @param {string} id - The resource ID
|
|
493
1192
|
* @param {boolean} [includeItems] - Include vocab items
|
|
494
1193
|
* @param {string} [asOf] - The moment to read at (ISO-8601 instant)
|
|
1194
|
+
* @returns {Promise<VocabLayer>}
|
|
495
1195
|
*/
|
|
496
1196
|
get: (id, includeItems, asOf) =>
|
|
497
1197
|
this._request("GET", `/api/v1/vocab-layers/${id}`, {
|
|
@@ -504,6 +1204,7 @@ class PlaidClient {
|
|
|
504
1204
|
* @param {string} id - The vocabulary ID
|
|
505
1205
|
* @param {string} itemId - The entry ID
|
|
506
1206
|
* @param {string} asOf - The moment to read at (ISO-8601 instant)
|
|
1207
|
+
* @returns {Promise<VocabItem>}
|
|
507
1208
|
*/
|
|
508
1209
|
getItemAt: (id, itemId, asOf) =>
|
|
509
1210
|
this._request("GET", `/api/v1/vocab-layers/${id}/items/${itemId}`, {
|
|
@@ -524,6 +1225,7 @@ class PlaidClient {
|
|
|
524
1225
|
* each with only its operations that did
|
|
525
1226
|
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
526
1227
|
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
1228
|
+
* @returns {Promise<AuditEntry[]>}
|
|
527
1229
|
*/
|
|
528
1230
|
audit: (id, startTime, endTime, opTypes, itemId, kinds) =>
|
|
529
1231
|
listAll(this, `/api/v1/vocab-layers/${id}/audit`, {
|
|
@@ -541,6 +1243,10 @@ class PlaidClient {
|
|
|
541
1243
|
* that may be long: audit() walks every page before it resolves.
|
|
542
1244
|
* @param {string} id - The vocabulary ID
|
|
543
1245
|
* @param {object} [opts]
|
|
1246
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
1247
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
1248
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
1249
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
544
1250
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
545
1251
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
546
1252
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -550,7 +1256,7 @@ class PlaidClient {
|
|
|
550
1256
|
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
551
1257
|
* @param {string} [opts.itemId] - Only the changes that wrote this one
|
|
552
1258
|
* entry, each with only its operations that did
|
|
553
|
-
* @returns {Promise<
|
|
1259
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
554
1260
|
*/
|
|
555
1261
|
auditPage: (
|
|
556
1262
|
id,
|
|
@@ -586,9 +1292,10 @@ class PlaidClient {
|
|
|
586
1292
|
* @param {string} itemId - The entry ID
|
|
587
1293
|
* @param {string} asOf - The moment to go back to (ISO-8601 instant),
|
|
588
1294
|
* typically a history entry's `endTime`
|
|
589
|
-
* @param {
|
|
1295
|
+
* @param {{dryRun?: boolean}} [options] - `{ dryRun }`: with `dryRun` true nothing
|
|
590
1296
|
* is written and the summary says what would change
|
|
591
1297
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
1298
|
+
* @returns {Promise<VocabItemRestoreSummary>}
|
|
592
1299
|
*/
|
|
593
1300
|
restoreItem: (id, itemId, asOf, { dryRun } = {}, auditMessage) =>
|
|
594
1301
|
this._request(
|
|
@@ -605,6 +1312,8 @@ class PlaidClient {
|
|
|
605
1312
|
/**
|
|
606
1313
|
* Delete a vocab layer.
|
|
607
1314
|
* @param {string} id - The resource ID
|
|
1315
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1316
|
+
* @returns {Promise<void>}
|
|
608
1317
|
*/
|
|
609
1318
|
delete: (id, auditMessage) =>
|
|
610
1319
|
this._request("DELETE", `/api/v1/vocab-layers/${id}`, { auditMessage }),
|
|
@@ -612,45 +1321,19 @@ class PlaidClient {
|
|
|
612
1321
|
* Update a vocab layer's name.
|
|
613
1322
|
* @param {string} id - The resource ID
|
|
614
1323
|
* @param {string} name - The name
|
|
1324
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1325
|
+
* @returns {Promise<VocabLayer>}
|
|
615
1326
|
*/
|
|
616
1327
|
update: (id, name, auditMessage) =>
|
|
617
1328
|
this._request("PATCH", `/api/v1/vocab-layers/${id}`, {
|
|
618
1329
|
auditMessage,
|
|
619
1330
|
body: bodyOf({ name }),
|
|
620
1331
|
}),
|
|
621
|
-
|
|
622
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
623
|
-
* @param {string} id - The resource ID
|
|
624
|
-
* @param {string} namespace - The config namespace
|
|
625
|
-
* @param {string} configKey - The config key
|
|
626
|
-
* @param {any} configValue - Configuration value to set
|
|
627
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
628
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
629
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
630
|
-
*/
|
|
631
|
-
setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
|
|
632
|
-
this._request(
|
|
633
|
-
"PUT",
|
|
634
|
-
`/api/v1/vocab-layers/${id}/config/${namespace}/${configKey}`,
|
|
635
|
-
configRequest(auditMessage, configValue, options),
|
|
636
|
-
),
|
|
637
|
-
/**
|
|
638
|
-
* Remove a configuration value for a layer.
|
|
639
|
-
* @param {string} id - The resource ID
|
|
640
|
-
* @param {string} namespace - The config namespace
|
|
641
|
-
* @param {string} configKey - The config key
|
|
642
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
643
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
644
|
-
*/
|
|
645
|
-
deleteConfig: (id, namespace, configKey, auditMessage, options) =>
|
|
646
|
-
this._request(
|
|
647
|
-
"DELETE",
|
|
648
|
-
`/api/v1/vocab-layers/${id}/config/${namespace}/${configKey}`,
|
|
649
|
-
configRequest(auditMessage, undefined, options, true),
|
|
650
|
-
),
|
|
1332
|
+
...configMethods(this, "vocab-layers"),
|
|
651
1333
|
/**
|
|
652
1334
|
* List all vocab layers accessible to user. Transparently follows
|
|
653
1335
|
* pagination cursors and returns the full flat array.
|
|
1336
|
+
* @returns {Promise<VocabLayer[]>}
|
|
654
1337
|
*/
|
|
655
1338
|
list: () => listAll(this, "/api/v1/vocab-layers"),
|
|
656
1339
|
/**
|
|
@@ -658,7 +1341,7 @@ class PlaidClient {
|
|
|
658
1341
|
* @param {object} [opts]
|
|
659
1342
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
660
1343
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
661
|
-
* @returns {Promise<
|
|
1344
|
+
* @returns {Promise<Page<VocabLayer>>}
|
|
662
1345
|
*/
|
|
663
1346
|
listPage: ({ limit, cursor } = {}) =>
|
|
664
1347
|
listPage(this, "/api/v1/vocab-layers", {
|
|
@@ -669,7 +1352,7 @@ class PlaidClient {
|
|
|
669
1352
|
* Async-iterate vocab layers page by page; yields each page's entries array.
|
|
670
1353
|
* @param {object} [opts]
|
|
671
1354
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
672
|
-
* @returns {AsyncGenerator<
|
|
1355
|
+
* @returns {AsyncGenerator<VocabLayer[]>}
|
|
673
1356
|
*/
|
|
674
1357
|
iterPages: ({ pageSize } = {}) =>
|
|
675
1358
|
iterPages(this, "/api/v1/vocab-layers", {
|
|
@@ -678,6 +1361,10 @@ class PlaidClient {
|
|
|
678
1361
|
/**
|
|
679
1362
|
* Create a new vocab layer. Note: this also registers the user as a maintainer.
|
|
680
1363
|
* @param {string} name - The name
|
|
1364
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1365
|
+
* @param {object} [opts]
|
|
1366
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1367
|
+
* @returns {Promise<Created>}
|
|
681
1368
|
*/
|
|
682
1369
|
create: (name, auditMessage, { id } = {}) =>
|
|
683
1370
|
this._request("POST", "/api/v1/vocab-layers", {
|
|
@@ -688,6 +1375,8 @@ class PlaidClient {
|
|
|
688
1375
|
* Assign a user as a maintainer for this vocab layer.
|
|
689
1376
|
* @param {string} id - The resource ID
|
|
690
1377
|
* @param {string} userId - The user ID
|
|
1378
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1379
|
+
* @returns {Promise<void>}
|
|
691
1380
|
*/
|
|
692
1381
|
addMaintainer: (id, userId, auditMessage) =>
|
|
693
1382
|
this._request(
|
|
@@ -699,6 +1388,8 @@ class PlaidClient {
|
|
|
699
1388
|
* Remove a user's maintainer privileges for this vocab layer.
|
|
700
1389
|
* @param {string} id - The resource ID
|
|
701
1390
|
* @param {string} userId - The user ID
|
|
1391
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1392
|
+
* @returns {Promise<void>}
|
|
702
1393
|
*/
|
|
703
1394
|
removeMaintainer: (id, userId, auditMessage) =>
|
|
704
1395
|
this._request(
|
|
@@ -709,38 +1400,13 @@ class PlaidClient {
|
|
|
709
1400
|
};
|
|
710
1401
|
|
|
711
1402
|
this.relations = {
|
|
712
|
-
|
|
713
|
-
* Replace all metadata for a relation.
|
|
714
|
-
* @param {string} relationId - The relation ID
|
|
715
|
-
* @param {any} body - The request body
|
|
716
|
-
*/
|
|
717
|
-
setMetadata: (relationId, body, auditMessage) =>
|
|
718
|
-
this._request("PUT", `/api/v1/relations/${relationId}/metadata`, {
|
|
719
|
-
auditMessage,
|
|
720
|
-
rawBody: body,
|
|
721
|
-
}),
|
|
722
|
-
/**
|
|
723
|
-
* Remove all metadata from a relation.
|
|
724
|
-
* @param {string} relationId - The relation ID
|
|
725
|
-
*/
|
|
726
|
-
deleteMetadata: (relationId, auditMessage) =>
|
|
727
|
-
this._request("DELETE", `/api/v1/relations/${relationId}/metadata`, {
|
|
728
|
-
auditMessage,
|
|
729
|
-
}),
|
|
730
|
-
/**
|
|
731
|
-
* Edit metadata for a relation with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
732
|
-
* @param {string} relationId - The relation ID
|
|
733
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
734
|
-
*/
|
|
735
|
-
patchMetadata: (relationId, body, auditMessage) =>
|
|
736
|
-
this._request("PATCH", `/api/v1/relations/${relationId}/metadata`, {
|
|
737
|
-
auditMessage,
|
|
738
|
-
rawBody: body,
|
|
739
|
-
}),
|
|
1403
|
+
...metadataMethods(this, "relations"),
|
|
740
1404
|
/**
|
|
741
1405
|
* Update the target span of a relation.
|
|
742
1406
|
* @param {string} relationId - The relation ID
|
|
743
1407
|
* @param {string} spanId - The span ID
|
|
1408
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1409
|
+
* @returns {Promise<Relation>}
|
|
744
1410
|
*/
|
|
745
1411
|
setTarget: (relationId, spanId, auditMessage) =>
|
|
746
1412
|
this._request("PUT", `/api/v1/relations/${relationId}/target`, {
|
|
@@ -750,12 +1416,15 @@ class PlaidClient {
|
|
|
750
1416
|
/**
|
|
751
1417
|
* Get a relation by ID.
|
|
752
1418
|
* @param {string} relationId - The relation ID
|
|
1419
|
+
* @returns {Promise<Relation>}
|
|
753
1420
|
*/
|
|
754
1421
|
get: (relationId) =>
|
|
755
1422
|
this._request("GET", `/api/v1/relations/${relationId}`),
|
|
756
1423
|
/**
|
|
757
1424
|
* Delete a relation.
|
|
758
1425
|
* @param {string} relationId - The relation ID
|
|
1426
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1427
|
+
* @returns {Promise<void>}
|
|
759
1428
|
*/
|
|
760
1429
|
delete: (relationId, auditMessage) =>
|
|
761
1430
|
this._request("DELETE", `/api/v1/relations/${relationId}`, {
|
|
@@ -765,6 +1434,8 @@ class PlaidClient {
|
|
|
765
1434
|
* Update a relation's value.
|
|
766
1435
|
* @param {string} relationId - The relation ID
|
|
767
1436
|
* @param {any} value - The value
|
|
1437
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1438
|
+
* @returns {Promise<Relation>}
|
|
768
1439
|
*/
|
|
769
1440
|
update: (relationId, value, auditMessage) =>
|
|
770
1441
|
this._request("PATCH", `/api/v1/relations/${relationId}`, {
|
|
@@ -775,6 +1446,8 @@ class PlaidClient {
|
|
|
775
1446
|
* Update the source span of a relation.
|
|
776
1447
|
* @param {string} relationId - The relation ID
|
|
777
1448
|
* @param {string} spanId - The span ID
|
|
1449
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1450
|
+
* @returns {Promise<Relation>}
|
|
778
1451
|
*/
|
|
779
1452
|
setSource: (relationId, spanId, auditMessage) =>
|
|
780
1453
|
this._request("PUT", `/api/v1/relations/${relationId}/source`, {
|
|
@@ -785,16 +1458,20 @@ class PlaidClient {
|
|
|
785
1458
|
* Create a new relation. A relation is a directed edge between two spans
|
|
786
1459
|
* with a value, useful for expressing phenomena such as syntactic or
|
|
787
1460
|
* semantic relations.
|
|
788
|
-
* @param {string}
|
|
789
|
-
* @param {string}
|
|
790
|
-
* @param {string}
|
|
1461
|
+
* @param {string} relationLayerId - The relation layer ID
|
|
1462
|
+
* @param {string} source - The source span ID
|
|
1463
|
+
* @param {string} target - The target span ID
|
|
791
1464
|
* @param {any} value - The value
|
|
792
1465
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1466
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1467
|
+
* @param {object} [opts]
|
|
1468
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1469
|
+
* @returns {Promise<Created>}
|
|
793
1470
|
*/
|
|
794
1471
|
create: (
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1472
|
+
relationLayerId,
|
|
1473
|
+
source,
|
|
1474
|
+
target,
|
|
798
1475
|
value,
|
|
799
1476
|
metadata,
|
|
800
1477
|
auditMessage,
|
|
@@ -804,9 +1481,9 @@ class PlaidClient {
|
|
|
804
1481
|
auditMessage,
|
|
805
1482
|
body: bodyOf({
|
|
806
1483
|
id,
|
|
807
|
-
"layer-id":
|
|
808
|
-
|
|
809
|
-
|
|
1484
|
+
"relation-layer-id": relationLayerId,
|
|
1485
|
+
source,
|
|
1486
|
+
target,
|
|
810
1487
|
value,
|
|
811
1488
|
metadata,
|
|
812
1489
|
}),
|
|
@@ -814,6 +1491,7 @@ class PlaidClient {
|
|
|
814
1491
|
/**
|
|
815
1492
|
* Create multiple relations in a single operation.
|
|
816
1493
|
* @param {Array} body - The request body
|
|
1494
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
817
1495
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
818
1496
|
*/
|
|
819
1497
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -821,6 +1499,8 @@ class PlaidClient {
|
|
|
821
1499
|
/**
|
|
822
1500
|
* Delete multiple relations in a single operation. Provide an array of IDs.
|
|
823
1501
|
* @param {Array} body - The request body
|
|
1502
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1503
|
+
* @returns {Promise<void>}
|
|
824
1504
|
*/
|
|
825
1505
|
bulkDelete: (body, auditMessage) =>
|
|
826
1506
|
this._request("DELETE", "/api/v1/relations/bulk", {
|
|
@@ -830,6 +1510,7 @@ class PlaidClient {
|
|
|
830
1510
|
/**
|
|
831
1511
|
* Update many relations in a single operation: set values and/or patch metadata.
|
|
832
1512
|
* @param {Array} body - Objects of the shape `{id, value?, metadata?}`. `value` is set only when the key is present (null sends JSON null); `metadata` is a list of metadata ops, as for patchMetadata. The relations may lie in several documents of one project; every document touched has its version bumped and every new version comes back in `X-Document-Versions` (past fifty documents, only their number, in `X-Document-Versions-Omitted`). A `document-version` precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.
|
|
1513
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
833
1514
|
* @returns {Promise<{count: number}>} How many relations were updated.
|
|
834
1515
|
*/
|
|
835
1516
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -840,81 +1521,24 @@ class PlaidClient {
|
|
|
840
1521
|
};
|
|
841
1522
|
|
|
842
1523
|
this.spanLayers = {
|
|
843
|
-
|
|
844
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
845
|
-
* @param {string} spanLayerId - The span layer ID
|
|
846
|
-
* @param {string} namespace - The config namespace
|
|
847
|
-
* @param {string} configKey - The config key
|
|
848
|
-
* @param {any} configValue - Configuration value to set
|
|
849
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
850
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
851
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
852
|
-
*/
|
|
853
|
-
setConfig: (spanLayerId, namespace, configKey, configValue, auditMessage, options) =>
|
|
854
|
-
this._request(
|
|
855
|
-
"PUT",
|
|
856
|
-
`/api/v1/span-layers/${spanLayerId}/config/${namespace}/${configKey}`,
|
|
857
|
-
configRequest(auditMessage, configValue, options),
|
|
858
|
-
),
|
|
859
|
-
/**
|
|
860
|
-
* Remove a configuration value for a layer.
|
|
861
|
-
* @param {string} spanLayerId - The span layer ID
|
|
862
|
-
* @param {string} namespace - The config namespace
|
|
863
|
-
* @param {string} configKey - The config key
|
|
864
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
865
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
866
|
-
*/
|
|
867
|
-
deleteConfig: (spanLayerId, namespace, configKey, auditMessage, options) =>
|
|
868
|
-
this._request(
|
|
869
|
-
"DELETE",
|
|
870
|
-
`/api/v1/span-layers/${spanLayerId}/config/${namespace}/${configKey}`,
|
|
871
|
-
configRequest(auditMessage, undefined, options, true),
|
|
872
|
-
),
|
|
1524
|
+
...configMethods(this, "span-layers"),
|
|
873
1525
|
...constraintMethods(this, "span-layers"),
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
* @param {string} spanLayerId - The span layer ID
|
|
877
|
-
*/
|
|
878
|
-
get: (spanLayerId) =>
|
|
879
|
-
this._request("GET", `/api/v1/span-layers/${spanLayerId}`),
|
|
880
|
-
/**
|
|
881
|
-
* Delete a span layer.
|
|
882
|
-
* @param {string} spanLayerId - The span layer ID
|
|
883
|
-
*/
|
|
884
|
-
delete: (spanLayerId, auditMessage) =>
|
|
885
|
-
this._request("DELETE", `/api/v1/span-layers/${spanLayerId}`, {
|
|
886
|
-
auditMessage,
|
|
887
|
-
}),
|
|
888
|
-
/**
|
|
889
|
-
* Update a span layer's name.
|
|
890
|
-
* @param {string} spanLayerId - The span layer ID
|
|
891
|
-
* @param {string} name - The name
|
|
892
|
-
*/
|
|
893
|
-
update: (spanLayerId, name, auditMessage) =>
|
|
894
|
-
this._request("PATCH", `/api/v1/span-layers/${spanLayerId}`, {
|
|
895
|
-
auditMessage,
|
|
896
|
-
body: bodyOf({ name }),
|
|
897
|
-
}),
|
|
1526
|
+
...valueSetMethods(this, "span-layers"),
|
|
1527
|
+
...layerMethods(this, "span-layers"),
|
|
898
1528
|
/**
|
|
899
1529
|
* Create a new span layer.
|
|
900
1530
|
* @param {string} tokenLayerId - The token layer ID
|
|
901
1531
|
* @param {string} name - The name
|
|
1532
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1533
|
+
* @param {object} [opts]
|
|
1534
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1535
|
+
* @returns {Promise<Created>}
|
|
902
1536
|
*/
|
|
903
1537
|
create: (tokenLayerId, name, auditMessage, { id } = {}) =>
|
|
904
1538
|
this._request("POST", "/api/v1/span-layers", {
|
|
905
1539
|
auditMessage,
|
|
906
1540
|
body: bodyOf({ id, "token-layer-id": tokenLayerId, name }),
|
|
907
1541
|
}),
|
|
908
|
-
/**
|
|
909
|
-
* Shift a span layer's display order.
|
|
910
|
-
* @param {string} spanLayerId - The span layer ID
|
|
911
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
912
|
-
*/
|
|
913
|
-
shift: (spanLayerId, direction, auditMessage) =>
|
|
914
|
-
this._request("POST", `/api/v1/span-layers/${spanLayerId}/shift`, {
|
|
915
|
-
auditMessage,
|
|
916
|
-
body: bodyOf({ direction }),
|
|
917
|
-
}),
|
|
918
1542
|
};
|
|
919
1543
|
|
|
920
1544
|
this.spans = {
|
|
@@ -922,6 +1546,8 @@ class PlaidClient {
|
|
|
922
1546
|
* Replace tokens for a span.
|
|
923
1547
|
* @param {string} spanId - The span ID
|
|
924
1548
|
* @param {Array} tokens - The tokens
|
|
1549
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1550
|
+
* @returns {Promise<Span>}
|
|
925
1551
|
*/
|
|
926
1552
|
setTokens: (spanId, tokens, auditMessage) =>
|
|
927
1553
|
this._request("PUT", `/api/v1/spans/${spanId}/tokens`, {
|
|
@@ -935,6 +1561,10 @@ class PlaidClient {
|
|
|
935
1561
|
* @param {Array} tokens - The tokens
|
|
936
1562
|
* @param {any} value - The value
|
|
937
1563
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1564
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1565
|
+
* @param {object} [opts]
|
|
1566
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1567
|
+
* @returns {Promise<Created>}
|
|
938
1568
|
*/
|
|
939
1569
|
create: (spanLayerId, tokens, value, metadata, auditMessage, { id } = {}) =>
|
|
940
1570
|
this._request("POST", "/api/v1/spans", {
|
|
@@ -950,12 +1580,15 @@ class PlaidClient {
|
|
|
950
1580
|
/**
|
|
951
1581
|
* Get a span by ID.
|
|
952
1582
|
* @param {string} spanId - The span ID
|
|
1583
|
+
* @returns {Promise<Span>}
|
|
953
1584
|
*/
|
|
954
1585
|
get: (spanId) =>
|
|
955
1586
|
this._request("GET", `/api/v1/spans/${spanId}`),
|
|
956
1587
|
/**
|
|
957
1588
|
* Delete a span.
|
|
958
1589
|
* @param {string} spanId - The span ID
|
|
1590
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1591
|
+
* @returns {Promise<void>}
|
|
959
1592
|
*/
|
|
960
1593
|
delete: (spanId, auditMessage) =>
|
|
961
1594
|
this._request("DELETE", `/api/v1/spans/${spanId}`, { auditMessage }),
|
|
@@ -963,6 +1596,8 @@ class PlaidClient {
|
|
|
963
1596
|
* Update a span's value.
|
|
964
1597
|
* @param {string} spanId - The span ID
|
|
965
1598
|
* @param {any} value - The value
|
|
1599
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1600
|
+
* @returns {Promise<Span>}
|
|
966
1601
|
*/
|
|
967
1602
|
update: (spanId, value, auditMessage) =>
|
|
968
1603
|
this._request("PATCH", `/api/v1/spans/${spanId}`, {
|
|
@@ -972,6 +1607,7 @@ class PlaidClient {
|
|
|
972
1607
|
/**
|
|
973
1608
|
* Create multiple spans in a single operation.
|
|
974
1609
|
* @param {Array} body - The request body
|
|
1610
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
975
1611
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
976
1612
|
*/
|
|
977
1613
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -979,12 +1615,15 @@ class PlaidClient {
|
|
|
979
1615
|
/**
|
|
980
1616
|
* Delete multiple spans in a single operation. Provide an array of IDs.
|
|
981
1617
|
* @param {Array} body - The request body
|
|
1618
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1619
|
+
* @returns {Promise<void>}
|
|
982
1620
|
*/
|
|
983
1621
|
bulkDelete: (body, auditMessage) =>
|
|
984
1622
|
this._request("DELETE", "/api/v1/spans/bulk", { auditMessage, body }),
|
|
985
1623
|
/**
|
|
986
1624
|
* Update many spans in a single operation: set values and/or patch metadata.
|
|
987
1625
|
* @param {Array} body - Objects of the shape `{id, value?, metadata?}`. `value` is set only when the key is present (null sends JSON null); `metadata` is a list of metadata ops, as for patchMetadata. The spans may lie in several documents of one project; every document touched has its version bumped and every new version comes back in `X-Document-Versions` (past fifty documents, only their number, in `X-Document-Versions-Omitted`). A `document-version` precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.
|
|
1626
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
988
1627
|
* @returns {Promise<{count: number}>} How many spans were updated.
|
|
989
1628
|
*/
|
|
990
1629
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -992,64 +1631,71 @@ class PlaidClient {
|
|
|
992
1631
|
auditMessage,
|
|
993
1632
|
body,
|
|
994
1633
|
}),
|
|
995
|
-
|
|
996
|
-
* Replace all metadata for a span.
|
|
997
|
-
* @param {string} spanId - The span ID
|
|
998
|
-
* @param {any} body - The request body
|
|
999
|
-
*/
|
|
1000
|
-
setMetadata: (spanId, body, auditMessage) =>
|
|
1001
|
-
this._request("PUT", `/api/v1/spans/${spanId}/metadata`, {
|
|
1002
|
-
auditMessage,
|
|
1003
|
-
rawBody: body,
|
|
1004
|
-
}),
|
|
1005
|
-
/**
|
|
1006
|
-
* Remove all metadata from a span.
|
|
1007
|
-
* @param {string} spanId - The span ID
|
|
1008
|
-
*/
|
|
1009
|
-
deleteMetadata: (spanId, auditMessage) =>
|
|
1010
|
-
this._request("DELETE", `/api/v1/spans/${spanId}/metadata`, {
|
|
1011
|
-
auditMessage,
|
|
1012
|
-
}),
|
|
1013
|
-
/**
|
|
1014
|
-
* Edit metadata for a span with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
1015
|
-
* @param {string} spanId - The span ID
|
|
1016
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
1017
|
-
*/
|
|
1018
|
-
patchMetadata: (spanId, body, auditMessage) =>
|
|
1019
|
-
this._request("PATCH", `/api/v1/spans/${spanId}/metadata`, {
|
|
1020
|
-
auditMessage,
|
|
1021
|
-
rawBody: body,
|
|
1022
|
-
}),
|
|
1634
|
+
...metadataMethods(this, "spans"),
|
|
1023
1635
|
};
|
|
1024
1636
|
|
|
1025
|
-
// Server-level facts.
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1637
|
+
// Server-level facts. Read once, and again only when they may have
|
|
1638
|
+
// changed: after this client's connection to the server was lost and came
|
|
1639
|
+
// back (a restart, the only way the limits change, always drops it), or
|
|
1640
|
+
// after the server refused a private data write as too large. A caller
|
|
1641
|
+
// asking "will this file be accepted" does not pay a round trip to find
|
|
1642
|
+
// out. The facts live on the client (`_serverFacts`), so a batch opened
|
|
1643
|
+
// on it reads the same.
|
|
1029
1644
|
this.server = {
|
|
1030
1645
|
/**
|
|
1031
1646
|
* This server's version and the limits it enforces, e.g.
|
|
1032
1647
|
* `{ limits: { mediaFileBytes, jsonBodyBytes, batchOperations, … } }`.
|
|
1033
1648
|
* Sizes are in bytes. Unauthenticated.
|
|
1649
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
1034
1650
|
*/
|
|
1035
1651
|
info: () => {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1652
|
+
const facts = this._serverFacts;
|
|
1653
|
+
if (!facts.info) {
|
|
1654
|
+
const asked = this._request("GET", "/api/v1/info").catch((err) => {
|
|
1655
|
+
// A failure must not be kept: a client that starts before the
|
|
1039
1656
|
// server is up would never see the limits at all.
|
|
1040
|
-
|
|
1657
|
+
if (facts.info === asked) facts.info = null;
|
|
1041
1658
|
throw err;
|
|
1042
1659
|
});
|
|
1660
|
+
facts.info = asked;
|
|
1043
1661
|
}
|
|
1044
|
-
return
|
|
1662
|
+
return facts.info;
|
|
1663
|
+
},
|
|
1664
|
+
/**
|
|
1665
|
+
* Read `GET /info` again and resolve to it. Until it answers, a reader
|
|
1666
|
+
* waits for it. The facts known before are kept when the server does
|
|
1667
|
+
* not answer, and the error is thrown.
|
|
1668
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
1669
|
+
*/
|
|
1670
|
+
refresh: () => {
|
|
1671
|
+
const facts = this._serverFacts;
|
|
1672
|
+
const before = facts.info;
|
|
1673
|
+
facts.stale = false;
|
|
1674
|
+
const fresh = this._request("GET", "/api/v1/info");
|
|
1675
|
+
const held = fresh.catch((err) => {
|
|
1676
|
+
if (facts.info === held) facts.info = before;
|
|
1677
|
+
if (before) return before;
|
|
1678
|
+
throw err;
|
|
1679
|
+
});
|
|
1680
|
+
held.catch(() => {});
|
|
1681
|
+
facts.info = held;
|
|
1682
|
+
return fresh;
|
|
1683
|
+
},
|
|
1684
|
+
/**
|
|
1685
|
+
* Just the limits, which is what a caller almost always wants.
|
|
1686
|
+
* @returns {Promise<ServerLimits>}
|
|
1687
|
+
*/
|
|
1688
|
+
limits: async () => {
|
|
1689
|
+
// Through `self`: tsc types a bundle that names itself as
|
|
1690
|
+
// `this.server` in its own methods `any` in the declarations.
|
|
1691
|
+
const self = this;
|
|
1692
|
+
return (await self.server.info()).limits;
|
|
1045
1693
|
},
|
|
1046
|
-
/** Just the limits, which is what a caller almost always wants. */
|
|
1047
|
-
limits: async () => (await this.server.info()).limits,
|
|
1048
1694
|
/**
|
|
1049
1695
|
* Liveness, version and database size. Unauthenticated, and served
|
|
1050
1696
|
* outside the REST router at /health, so it answers even while the API
|
|
1051
1697
|
* is refusing requests. Never cached — the point is that it is current.
|
|
1052
|
-
* @returns {Promise<
|
|
1698
|
+
* @returns {Promise<ServerHealth>}
|
|
1053
1699
|
*/
|
|
1054
1700
|
health: () => PlaidClient.health(this.baseUrl, { timeout: this.timeout }),
|
|
1055
1701
|
};
|
|
@@ -1073,34 +1719,7 @@ class PlaidClient {
|
|
|
1073
1719
|
};
|
|
1074
1720
|
|
|
1075
1721
|
this.texts = {
|
|
1076
|
-
|
|
1077
|
-
* Replace all metadata for a text.
|
|
1078
|
-
* @param {string} textId - The text ID
|
|
1079
|
-
* @param {any} body - The request body
|
|
1080
|
-
*/
|
|
1081
|
-
setMetadata: (textId, body, auditMessage) =>
|
|
1082
|
-
this._request("PUT", `/api/v1/texts/${textId}/metadata`, {
|
|
1083
|
-
auditMessage,
|
|
1084
|
-
rawBody: body,
|
|
1085
|
-
}),
|
|
1086
|
-
/**
|
|
1087
|
-
* Remove all metadata from a text.
|
|
1088
|
-
* @param {string} textId - The text ID
|
|
1089
|
-
*/
|
|
1090
|
-
deleteMetadata: (textId, auditMessage) =>
|
|
1091
|
-
this._request("DELETE", `/api/v1/texts/${textId}/metadata`, {
|
|
1092
|
-
auditMessage,
|
|
1093
|
-
}),
|
|
1094
|
-
/**
|
|
1095
|
-
* Edit metadata for a text with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
1096
|
-
* @param {string} textId - The text ID
|
|
1097
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
1098
|
-
*/
|
|
1099
|
-
patchMetadata: (textId, body, auditMessage) =>
|
|
1100
|
-
this._request("PATCH", `/api/v1/texts/${textId}/metadata`, {
|
|
1101
|
-
auditMessage,
|
|
1102
|
-
rawBody: body,
|
|
1103
|
-
}),
|
|
1722
|
+
...metadataMethods(this, "texts"),
|
|
1104
1723
|
/**
|
|
1105
1724
|
* Create a new text in a document's text layer. A text is a container for
|
|
1106
1725
|
* one long string in `body` for a given layer.
|
|
@@ -1108,6 +1727,14 @@ class PlaidClient {
|
|
|
1108
1727
|
* @param {string} documentId - The document ID
|
|
1109
1728
|
* @param {string} body - The request body
|
|
1110
1729
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1730
|
+
* @param {string} [auditMessage]
|
|
1731
|
+
* @param {{id?: string, tokenEdges?: number[], tokenizeLayer?: string}} [options] - `id`: the
|
|
1732
|
+
* new text's id, a UUIDv7 this client minted. `tokenEdges`: the code-point offsets where
|
|
1733
|
+
* the tokens made next on this text begin and end. The body is stored composed, except a
|
|
1734
|
+
* character one of them falls inside, which stays as sent (see composeText).
|
|
1735
|
+
* `tokenizeLayer`: a word layer of the text, which gets the body's words, cut by the
|
|
1736
|
+
* server ("Tokenize new text").
|
|
1737
|
+
* @returns {Promise<Created>}
|
|
1111
1738
|
*/
|
|
1112
1739
|
create: (
|
|
1113
1740
|
textLayerId,
|
|
@@ -1115,7 +1742,7 @@ class PlaidClient {
|
|
|
1115
1742
|
body,
|
|
1116
1743
|
metadata,
|
|
1117
1744
|
auditMessage,
|
|
1118
|
-
{ id } = {},
|
|
1745
|
+
{ id, tokenEdges, tokenizeLayer } = {},
|
|
1119
1746
|
) =>
|
|
1120
1747
|
this._request("POST", "/api/v1/texts", {
|
|
1121
1748
|
auditMessage,
|
|
@@ -1124,18 +1751,23 @@ class PlaidClient {
|
|
|
1124
1751
|
"text-layer-id": textLayerId,
|
|
1125
1752
|
"document-id": documentId,
|
|
1126
1753
|
body,
|
|
1754
|
+
"token-edges": tokenEdges,
|
|
1755
|
+
"tokenize-layer": tokenizeLayer,
|
|
1127
1756
|
metadata,
|
|
1128
1757
|
}),
|
|
1129
1758
|
}),
|
|
1130
1759
|
/**
|
|
1131
1760
|
* Get a text.
|
|
1132
1761
|
* @param {string} textId - The text ID
|
|
1762
|
+
* @returns {Promise<PlaidText>}
|
|
1133
1763
|
*/
|
|
1134
1764
|
get: (textId) =>
|
|
1135
1765
|
this._request("GET", `/api/v1/texts/${textId}`),
|
|
1136
1766
|
/**
|
|
1137
1767
|
* Delete a text and all dependent data.
|
|
1138
1768
|
* @param {string} textId - The text ID
|
|
1769
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1770
|
+
* @returns {Promise<void>}
|
|
1139
1771
|
*/
|
|
1140
1772
|
delete: (textId, auditMessage) =>
|
|
1141
1773
|
this._request("DELETE", `/api/v1/texts/${textId}`, { auditMessage }),
|
|
@@ -1157,12 +1789,16 @@ class PlaidClient {
|
|
|
1157
1789
|
* @param {string} textId - The text ID
|
|
1158
1790
|
* @param {any} body - The request body
|
|
1159
1791
|
* @param {string} [auditMessage]
|
|
1160
|
-
*
|
|
1792
|
+
* `tokenizeLayer`: a word layer of the text, whose words the server makes for the text the
|
|
1793
|
+
* update types, after it places the old tokens ("Tokenize new text"). They come in the
|
|
1794
|
+
* answer's `reshape`.
|
|
1795
|
+
* @param {{base?: string, versioned?: boolean, tokenizeLayer?: string}} [options]
|
|
1796
|
+
* @returns {Promise<PlaidText & {reshape: TextReshape}>}
|
|
1161
1797
|
*/
|
|
1162
|
-
update: (textId, body, auditMessage, { base, versioned } = {}) =>
|
|
1798
|
+
update: (textId, body, auditMessage, { base, versioned, tokenizeLayer } = {}) =>
|
|
1163
1799
|
this._request("PATCH", `/api/v1/texts/${textId}`, {
|
|
1164
1800
|
auditMessage,
|
|
1165
|
-
body: bodyOf({ body, base }),
|
|
1801
|
+
body: bodyOf({ body, base, "tokenize-layer": tokenizeLayer }),
|
|
1166
1802
|
versioned: versioned ?? (base === undefined || base === null),
|
|
1167
1803
|
}),
|
|
1168
1804
|
/**
|
|
@@ -1176,16 +1812,17 @@ class PlaidClient {
|
|
|
1176
1812
|
* `base`, the digest of the body the edits were made on, the edit
|
|
1177
1813
|
* applies only to that body and is refused with 409 and `text-changed`
|
|
1178
1814
|
* otherwise, and strict mode does not stamp it unless `versioned` is
|
|
1179
|
-
* true. See composeTextEdits and gapsToOps.
|
|
1815
|
+
* true. `tokenizeLayer` is as `update` takes it. See composeTextEdits and gapsToOps.
|
|
1180
1816
|
* @param {string} textId - The text ID
|
|
1181
1817
|
* @param {Array<object>} edits - The edit directives
|
|
1182
1818
|
* @param {string} [auditMessage]
|
|
1183
|
-
* @param {{base?: string, versioned?: boolean}} [options]
|
|
1819
|
+
* @param {{base?: string, versioned?: boolean, tokenizeLayer?: string}} [options]
|
|
1820
|
+
* @returns {Promise<PlaidText & {reshape: TextReshape}>}
|
|
1184
1821
|
*/
|
|
1185
|
-
edit: (textId, edits, auditMessage, { base, versioned } = {}) =>
|
|
1822
|
+
edit: (textId, edits, auditMessage, { base, versioned, tokenizeLayer } = {}) =>
|
|
1186
1823
|
this._request("PATCH", `/api/v1/texts/${textId}`, {
|
|
1187
1824
|
auditMessage,
|
|
1188
|
-
body: bodyOf({ edits, base }),
|
|
1825
|
+
body: bodyOf({ edits, base, "tokenize-layer": tokenizeLayer }),
|
|
1189
1826
|
versioned: versioned ?? (base === undefined || base === null),
|
|
1190
1827
|
}),
|
|
1191
1828
|
};
|
|
@@ -1195,17 +1832,18 @@ class PlaidClient {
|
|
|
1195
1832
|
* List (or search) users. Transparently follows pagination cursors and
|
|
1196
1833
|
* returns the full flat array. Admin-or-maintainer only.
|
|
1197
1834
|
* @param {object} [opts]
|
|
1198
|
-
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text
|
|
1835
|
+
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
|
|
1836
|
+
* @returns {Promise<User[]>}
|
|
1199
1837
|
*/
|
|
1200
1838
|
list: ({ q } = {}) =>
|
|
1201
1839
|
listAll(this, "/api/v1/users", { query: { q } }),
|
|
1202
1840
|
/**
|
|
1203
1841
|
* Fetch a single page of users (optionally filtered by `q`).
|
|
1204
1842
|
* @param {object} [opts]
|
|
1205
|
-
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text
|
|
1843
|
+
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
|
|
1206
1844
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1207
1845
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1208
|
-
* @returns {Promise<
|
|
1846
|
+
* @returns {Promise<Page<User>>}
|
|
1209
1847
|
*/
|
|
1210
1848
|
listPage: ({ q, limit, cursor } = {}) =>
|
|
1211
1849
|
listPage(this, "/api/v1/users", {
|
|
@@ -1216,9 +1854,9 @@ class PlaidClient {
|
|
|
1216
1854
|
/**
|
|
1217
1855
|
* Async-iterate users page by page; yields each page's entries array.
|
|
1218
1856
|
* @param {object} [opts]
|
|
1219
|
-
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text
|
|
1857
|
+
* @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
|
|
1220
1858
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
1221
|
-
* @returns {AsyncGenerator<
|
|
1859
|
+
* @returns {AsyncGenerator<User[]>}
|
|
1222
1860
|
*/
|
|
1223
1861
|
iterPages: ({ q, pageSize } = {}) =>
|
|
1224
1862
|
iterPages(this, "/api/v1/users", {
|
|
@@ -1234,12 +1872,14 @@ class PlaidClient {
|
|
|
1234
1872
|
* @param {boolean} isAdmin - Whether the user is an admin
|
|
1235
1873
|
* @param {string} [displayName] - How the user is shown in the UI.
|
|
1236
1874
|
* Defaults to the local part of the email.
|
|
1875
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1876
|
+
* @returns {Promise<Created>}
|
|
1237
1877
|
*/
|
|
1238
1878
|
create: (email, password, isAdmin, displayName, auditMessage) =>
|
|
1239
1879
|
this._request("POST", "/api/v1/users", {
|
|
1240
1880
|
auditMessage,
|
|
1241
1881
|
body: bodyOf({
|
|
1242
|
-
email,
|
|
1882
|
+
email: normalizeUserId(email),
|
|
1243
1883
|
password,
|
|
1244
1884
|
"is-admin": isAdmin,
|
|
1245
1885
|
// Left out when not given (null too), so core gives the user the
|
|
@@ -1259,6 +1899,7 @@ class PlaidClient {
|
|
|
1259
1899
|
* one of its operations matches, carrying only the ones that did.
|
|
1260
1900
|
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
1261
1901
|
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
1902
|
+
* @returns {Promise<AuditEntry[]>}
|
|
1262
1903
|
*/
|
|
1263
1904
|
audit: (userId, startTime, endTime, opTypes, kinds) =>
|
|
1264
1905
|
listAll(this, `/api/v1/users/${userId}/audit`, {
|
|
@@ -1275,6 +1916,10 @@ class PlaidClient {
|
|
|
1275
1916
|
* that may be long: audit() walks every page before it resolves.
|
|
1276
1917
|
* @param {string} userId - The user ID
|
|
1277
1918
|
* @param {object} [opts]
|
|
1919
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
1920
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
1921
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
1922
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
1278
1923
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
1279
1924
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1280
1925
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -1282,7 +1927,7 @@ class PlaidClient {
|
|
|
1282
1927
|
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
1283
1928
|
* many it has
|
|
1284
1929
|
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
1285
|
-
* @returns {Promise<
|
|
1930
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
1286
1931
|
*/
|
|
1287
1932
|
auditPage: (
|
|
1288
1933
|
userId,
|
|
@@ -1304,6 +1949,7 @@ class PlaidClient {
|
|
|
1304
1949
|
/**
|
|
1305
1950
|
* Get a user by ID
|
|
1306
1951
|
* @param {string} id - The resource ID
|
|
1952
|
+
* @returns {Promise<User>}
|
|
1307
1953
|
*/
|
|
1308
1954
|
get: (id) =>
|
|
1309
1955
|
this._request("GET", `/api/v1/users/${id}`),
|
|
@@ -1314,6 +1960,8 @@ class PlaidClient {
|
|
|
1314
1960
|
* stays visible in listings with a deactivated-at timestamp.
|
|
1315
1961
|
* Reversible via activate(), which restores login only.
|
|
1316
1962
|
* @param {string} id - The resource ID
|
|
1963
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1964
|
+
* @returns {Promise<void>}
|
|
1317
1965
|
*/
|
|
1318
1966
|
delete: (id, auditMessage) =>
|
|
1319
1967
|
this._request("DELETE", `/api/v1/users/${id}`, { auditMessage }),
|
|
@@ -1322,6 +1970,8 @@ class PlaidClient {
|
|
|
1322
1970
|
* Project memberships, vocab maintainerships, and API tokens removed
|
|
1323
1971
|
* at deactivation are NOT restored — re-grant them deliberately.
|
|
1324
1972
|
* @param {string} id - The resource ID
|
|
1973
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1974
|
+
* @returns {Promise<User>}
|
|
1325
1975
|
*/
|
|
1326
1976
|
activate: (id, auditMessage) =>
|
|
1327
1977
|
this._request("POST", `/api/v1/users/${id}/activate`, { auditMessage }),
|
|
@@ -1337,6 +1987,8 @@ class PlaidClient {
|
|
|
1337
1987
|
* @param {string} [password] - New password
|
|
1338
1988
|
* @param {string} [displayName] - New display name
|
|
1339
1989
|
* @param {boolean} [isAdmin] - New admin status
|
|
1990
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1991
|
+
* @returns {Promise<User>}
|
|
1340
1992
|
*/
|
|
1341
1993
|
update: (id, password, displayName, isAdmin, auditMessage) =>
|
|
1342
1994
|
this._request("PATCH", `/api/v1/users/${id}`, {
|
|
@@ -1348,23 +2000,89 @@ class PlaidClient {
|
|
|
1348
2000
|
}),
|
|
1349
2001
|
}),
|
|
1350
2002
|
/**
|
|
1351
|
-
*
|
|
1352
|
-
* an `<img
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
2003
|
+
* A token that shows any user's profile picture without an
|
|
2004
|
+
* Authorization header, for an `<img>`, which cannot send one. Resolves
|
|
2005
|
+
* to `{ token, expiresAt }` (`expiresAt` an ISO-8601 instant, a day on by
|
|
2006
|
+
* default). The server refuses it on every route but the picture, and
|
|
2007
|
+
* it stops working when the user signs out or changes password.
|
|
2008
|
+
*
|
|
2009
|
+
* One token serves the whole client: it is kept and handed out again
|
|
2010
|
+
* until under ten minutes of it remain (half its life, for one minted
|
|
2011
|
+
* near the end of a login), calls made while one is being minted share
|
|
2012
|
+
* that request, and it is dropped when the client's token changes.
|
|
2013
|
+
* avatarUrl() uses it.
|
|
2014
|
+
* @param {{renew?: boolean}} [options] - `renew`: mint a new one
|
|
2015
|
+
* whatever is kept, unless one was minted in the last few seconds
|
|
2016
|
+
* (or is being minted), which is shared
|
|
2017
|
+
* @returns {Promise<{token: string, expiresAt: string}>}
|
|
2018
|
+
*/
|
|
2019
|
+
avatarToken: ({ renew = false } = {}) => {
|
|
2020
|
+
// A batch is a view of its client and shares the client's token.
|
|
2021
|
+
const owner = this.client ?? this;
|
|
2022
|
+
const cached = owner._avatarToken;
|
|
2023
|
+
if (cached && cached.forToken === owner.token) {
|
|
2024
|
+
if (cached.pending) return cached.pending;
|
|
2025
|
+
const now = owner.serverNow().getTime();
|
|
2026
|
+
// A renewal shares one minted a moment ago: every picture on a page
|
|
2027
|
+
// fails together when a token dies, and each asks.
|
|
2028
|
+
if (renew && now - cached.mintedAt < AVATAR_TOKEN_FRESH_MS) {
|
|
2029
|
+
return Promise.resolve(cached.link);
|
|
2030
|
+
}
|
|
2031
|
+
const expires = Date.parse(cached.link.expiresAt);
|
|
2032
|
+
// Renewed ten minutes before it runs out, or at half its life when
|
|
2033
|
+
// it was minted for less than twenty: the server caps it at the
|
|
2034
|
+
// login's own expiry, and near the end of a login every token it
|
|
2035
|
+
// mints runs out at that same moment, so a fixed ten minutes asked
|
|
2036
|
+
// for a new one on every call.
|
|
2037
|
+
const renewAt = Math.min(AVATAR_TOKEN_RENEW_MS, (expires - cached.mintedAt) / 2);
|
|
2038
|
+
if (!renew && expires - now > renewAt) return Promise.resolve(cached.link);
|
|
2039
|
+
}
|
|
2040
|
+
const entry = {
|
|
2041
|
+
forToken: owner.token,
|
|
2042
|
+
pending: null,
|
|
2043
|
+
link: null,
|
|
2044
|
+
mintedAt: owner.serverNow().getTime(),
|
|
2045
|
+
};
|
|
2046
|
+
entry.pending = owner
|
|
2047
|
+
._request("POST", "/api/v1/avatar-link", { outOfBand: true })
|
|
2048
|
+
.then(
|
|
2049
|
+
(link) => {
|
|
2050
|
+
entry.pending = null;
|
|
2051
|
+
entry.link = link;
|
|
2052
|
+
return link;
|
|
2053
|
+
},
|
|
2054
|
+
(e) => {
|
|
2055
|
+
if (owner._avatarToken === entry) owner._avatarToken = null;
|
|
2056
|
+
throw e;
|
|
2057
|
+
},
|
|
2058
|
+
);
|
|
2059
|
+
owner._avatarToken = entry;
|
|
2060
|
+
return entry.pending;
|
|
2061
|
+
},
|
|
2062
|
+
/**
|
|
2063
|
+
* Build a URL for a user's profile picture, for an `<img>` src. The
|
|
2064
|
+
* URL carries the client's avatar token (see avatarToken()), never the
|
|
2065
|
+
* login token.
|
|
1355
2066
|
*
|
|
1356
2067
|
* Pass the user record's `avatarHash` as the second argument whenever you
|
|
1357
2068
|
* have it: the URL then addresses that exact picture, so the browser can
|
|
1358
2069
|
* cache it indefinitely and still pick up a replacement the moment the
|
|
1359
|
-
* user changes it.
|
|
1360
|
-
* can fall back to initials without a
|
|
2070
|
+
* user changes it. Resolves to null when `avatarHash` is null (the user
|
|
2071
|
+
* has no picture), so callers can fall back to initials without a
|
|
2072
|
+
* wasted request.
|
|
1361
2073
|
* @param {string} id - The user ID
|
|
1362
|
-
* @param {string} [avatarHash] - The user record's `avatarHash`
|
|
1363
|
-
* @
|
|
2074
|
+
* @param {string|null} [avatarHash] - The user record's `avatarHash`
|
|
2075
|
+
* @param {{renew?: boolean}} [options] - `renew`: mint a new avatar
|
|
2076
|
+
* token first, for a picture its URL failed to load (the token
|
|
2077
|
+
* stops working when the login it was minted under ends)
|
|
2078
|
+
* @returns {Promise<string|null>}
|
|
1364
2079
|
*/
|
|
1365
|
-
avatarUrl: (id, avatarHash) => {
|
|
2080
|
+
avatarUrl: async (id, avatarHash, { renew = false } = {}) => {
|
|
1366
2081
|
if (avatarHash === null) return null;
|
|
1367
|
-
|
|
2082
|
+
// Through `self`, as in server.limits.
|
|
2083
|
+
const self = this;
|
|
2084
|
+
const { token } = await self.users.avatarToken({ renew });
|
|
2085
|
+
const params = new URLSearchParams({ "avatar-token": token });
|
|
1368
2086
|
if (avatarHash) params.set("v", avatarHash);
|
|
1369
2087
|
return `${this.baseUrl}/api/v1/users/${id}/avatar?${params}`;
|
|
1370
2088
|
},
|
|
@@ -1372,6 +2090,7 @@ class PlaidClient {
|
|
|
1372
2090
|
* Fetch a user's profile picture as raw bytes. Most callers want
|
|
1373
2091
|
* avatarUrl() instead. This one is for non-browser consumers.
|
|
1374
2092
|
* @param {string} id - The user ID
|
|
2093
|
+
* @returns {Promise<ArrayBuffer>}
|
|
1375
2094
|
*/
|
|
1376
2095
|
getAvatar: (id) =>
|
|
1377
2096
|
this._request("GET", `/api/v1/users/${id}/avatar`, {
|
|
@@ -1384,6 +2103,8 @@ class PlaidClient {
|
|
|
1384
2103
|
* PNG, JPEG, WebP, and GIF. Resolves to the updated user record.
|
|
1385
2104
|
* @param {string} id - The user ID
|
|
1386
2105
|
* @param {File} file - The image to upload
|
|
2106
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2107
|
+
* @returns {Promise<User>}
|
|
1387
2108
|
*/
|
|
1388
2109
|
setAvatar: (id, file, auditMessage) => {
|
|
1389
2110
|
const fd = new FormData();
|
|
@@ -1398,6 +2119,8 @@ class PlaidClient {
|
|
|
1398
2119
|
/**
|
|
1399
2120
|
* Remove a profile picture. Your own, or anyone's if you are an admin.
|
|
1400
2121
|
* @param {string} id - The user ID
|
|
2122
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2123
|
+
* @returns {Promise<void>}
|
|
1401
2124
|
*/
|
|
1402
2125
|
// No flag: the upload beside this one is multipart and cannot be
|
|
1403
2126
|
// batched, but a DELETE carries no blob, so the batch transport takes
|
|
@@ -1421,7 +2144,8 @@ class PlaidClient {
|
|
|
1421
2144
|
* every project without dragging down the transcripts beside them.
|
|
1422
2145
|
*
|
|
1423
2146
|
* `pageSize` is exposed here, and defaults lower than elsewhere, because
|
|
1424
|
-
* one value runs to
|
|
2147
|
+
* one value runs to megabytes (`userDataValueBytes` in
|
|
2148
|
+
* `server.limits()`): a page of them with `includeValues` is the
|
|
1425
2149
|
* largest response this API can be asked for. Raise it when the listing
|
|
1426
2150
|
* is keys, or the values are known to be small.
|
|
1427
2151
|
* @param {string} userId
|
|
@@ -1430,6 +2154,7 @@ class PlaidClient {
|
|
|
1430
2154
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1431
2155
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1432
2156
|
* @param {number} [opts.pageSize=100] - Entries per request (1..1000)
|
|
2157
|
+
* @returns {Promise<UserDataEntry[]>}
|
|
1433
2158
|
*/
|
|
1434
2159
|
list: (userId, { prefix, pattern, includeValues, pageSize = 100 } = {}) =>
|
|
1435
2160
|
listAll(this, `/api/v1/users/${userId}/data`, {
|
|
@@ -1445,7 +2170,7 @@ class PlaidClient {
|
|
|
1445
2170
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1446
2171
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1447
2172
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1448
|
-
* @returns {Promise<
|
|
2173
|
+
* @returns {Promise<Page<UserDataEntry>>}
|
|
1449
2174
|
*/
|
|
1450
2175
|
listPage: (
|
|
1451
2176
|
userId,
|
|
@@ -1464,6 +2189,7 @@ class PlaidClient {
|
|
|
1464
2189
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1465
2190
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1466
2191
|
* @param {number} [opts.pageSize=100] - Entries per request (1..1000)
|
|
2192
|
+
* @returns {AsyncGenerator<UserDataEntry[]>}
|
|
1467
2193
|
*/
|
|
1468
2194
|
iterPages: (
|
|
1469
2195
|
userId,
|
|
@@ -1477,6 +2203,7 @@ class PlaidClient {
|
|
|
1477
2203
|
* Read one private data entry ({key, updatedAt, version, value}); 404 if absent.
|
|
1478
2204
|
* @param {string} userId
|
|
1479
2205
|
* @param {string} key
|
|
2206
|
+
* @returns {Promise<UserDataEntry & {value: any}>}
|
|
1480
2207
|
*/
|
|
1481
2208
|
get: (userId, key) =>
|
|
1482
2209
|
this._request(
|
|
@@ -1484,8 +2211,11 @@ class PlaidClient {
|
|
|
1484
2211
|
`/api/v1/users/${userId}/data/${encodeURIComponent(key)}`,
|
|
1485
2212
|
),
|
|
1486
2213
|
/**
|
|
1487
|
-
* Create or replace one private data entry. `value` is any JSON
|
|
1488
|
-
*
|
|
2214
|
+
* Create or replace one private data entry. `value` is any JSON, up to
|
|
2215
|
+
* the server's `userDataValueBytes` (`server.limits()`, 5 MB by
|
|
2216
|
+
* default), and is refused with 413 over it, after which
|
|
2217
|
+
* `server.limits()` answers the figure the server has now. Not audited,
|
|
2218
|
+
* not batchable.
|
|
1489
2219
|
*
|
|
1490
2220
|
* The server stores it verbatim, but this client recases object keys on
|
|
1491
2221
|
* the way out and back like any other body (`myKey` <-> `my-key`), so a
|
|
@@ -1506,20 +2236,31 @@ class PlaidClient {
|
|
|
1506
2236
|
* @param {number} [opts.version] - The version this write was made from
|
|
1507
2237
|
* @returns {Promise<{key: string, updatedAt: string, version: number}>}
|
|
1508
2238
|
*/
|
|
1509
|
-
put: (userId, key, value, { version } = {}) =>
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
2239
|
+
put: async (userId, key, value, { version } = {}) => {
|
|
2240
|
+
try {
|
|
2241
|
+
return await this._request(
|
|
2242
|
+
"PUT",
|
|
2243
|
+
`/api/v1/users/${userId}/data/${encodeURIComponent(key)}`,
|
|
2244
|
+
{
|
|
2245
|
+
body: value,
|
|
2246
|
+
noBatch: true,
|
|
2247
|
+
queryParams: { version },
|
|
2248
|
+
},
|
|
2249
|
+
);
|
|
2250
|
+
} catch (err) {
|
|
2251
|
+
if (err?.status === 413) {
|
|
2252
|
+
// The cap may have changed since it was read: the caller deciding
|
|
2253
|
+
// what to do next reads the server's figure now.
|
|
2254
|
+
await this.server.refresh().catch(() => {});
|
|
2255
|
+
}
|
|
2256
|
+
throw err;
|
|
2257
|
+
}
|
|
2258
|
+
},
|
|
1519
2259
|
/**
|
|
1520
2260
|
* Delete one private data entry; 404 if absent.
|
|
1521
2261
|
* @param {string} userId
|
|
1522
2262
|
* @param {string} key
|
|
2263
|
+
* @returns {Promise<void>}
|
|
1523
2264
|
*/
|
|
1524
2265
|
delete: (userId, key) =>
|
|
1525
2266
|
this._request(
|
|
@@ -1537,6 +2278,7 @@ class PlaidClient {
|
|
|
1537
2278
|
* string itself — that is only returned once, by create().
|
|
1538
2279
|
* Transparently follows pagination cursors and returns the full flat array.
|
|
1539
2280
|
* @param {string} userId - The user ID who owns the tokens
|
|
2281
|
+
* @returns {Promise<ApiToken[]>}
|
|
1540
2282
|
*/
|
|
1541
2283
|
list: (userId) => listAll(this, `/api/v1/users/${userId}/tokens`),
|
|
1542
2284
|
/**
|
|
@@ -1545,7 +2287,7 @@ class PlaidClient {
|
|
|
1545
2287
|
* @param {object} [opts]
|
|
1546
2288
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1547
2289
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1548
|
-
* @returns {Promise<
|
|
2290
|
+
* @returns {Promise<Page<ApiToken>>}
|
|
1549
2291
|
*/
|
|
1550
2292
|
listPage: (userId, { limit, cursor } = {}) =>
|
|
1551
2293
|
listPage(this, `/api/v1/users/${userId}/tokens`, { limit, cursor }),
|
|
@@ -1555,7 +2297,7 @@ class PlaidClient {
|
|
|
1555
2297
|
* @param {string} userId - The user ID who owns the tokens
|
|
1556
2298
|
* @param {object} [opts]
|
|
1557
2299
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
1558
|
-
* @returns {AsyncGenerator<
|
|
2300
|
+
* @returns {AsyncGenerator<ApiToken[]>}
|
|
1559
2301
|
*/
|
|
1560
2302
|
iterPages: (userId, { pageSize } = {}) =>
|
|
1561
2303
|
iterPages(this, `/api/v1/users/${userId}/tokens`, { pageSize }),
|
|
@@ -1567,6 +2309,7 @@ class PlaidClient {
|
|
|
1567
2309
|
* needs a sign-in token.
|
|
1568
2310
|
* @param {string} userId - The user ID who will own the token
|
|
1569
2311
|
* @param {string} name - A human label, e.g. "Stanza parser"
|
|
2312
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1570
2313
|
* @returns {Promise<{id: string, name: string, token: string}>}
|
|
1571
2314
|
*/
|
|
1572
2315
|
create: (userId, name, auditMessage) =>
|
|
@@ -1583,6 +2326,8 @@ class PlaidClient {
|
|
|
1583
2326
|
* needs a sign-in token.
|
|
1584
2327
|
* @param {string} userId - The user ID who owns the token
|
|
1585
2328
|
* @param {string} tokenId - The token ID to revoke
|
|
2329
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2330
|
+
* @returns {Promise<void>}
|
|
1586
2331
|
*/
|
|
1587
2332
|
revoke: (userId, tokenId, auditMessage) =>
|
|
1588
2333
|
this._request("DELETE", `/api/v1/users/${userId}/tokens/${tokenId}`, {
|
|
@@ -1600,6 +2345,7 @@ class PlaidClient {
|
|
|
1600
2345
|
* @param {object} [opts]
|
|
1601
2346
|
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
1602
2347
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
2348
|
+
* @returns {Promise<Invite[]>}
|
|
1603
2349
|
*/
|
|
1604
2350
|
list: ({ projectId, all } = {}) =>
|
|
1605
2351
|
listAll(this, "/api/v1/invites", {
|
|
@@ -1612,7 +2358,7 @@ class PlaidClient {
|
|
|
1612
2358
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
1613
2359
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1614
2360
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1615
|
-
* @returns {Promise<
|
|
2361
|
+
* @returns {Promise<Page<Invite>>}
|
|
1616
2362
|
*/
|
|
1617
2363
|
listPage: ({ projectId, all, limit, cursor } = {}) =>
|
|
1618
2364
|
listPage(this, "/api/v1/invites", {
|
|
@@ -1626,7 +2372,7 @@ class PlaidClient {
|
|
|
1626
2372
|
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
1627
2373
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
1628
2374
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
1629
|
-
* @returns {AsyncGenerator<
|
|
2375
|
+
* @returns {AsyncGenerator<Invite[]>}
|
|
1630
2376
|
*/
|
|
1631
2377
|
iterPages: ({ projectId, all, pageSize } = {}) =>
|
|
1632
2378
|
iterPages(this, "/api/v1/invites", {
|
|
@@ -1659,7 +2405,8 @@ class PlaidClient {
|
|
|
1659
2405
|
* @param {number} [opts.maxUses] - How many accounts this link may create (default 1)
|
|
1660
2406
|
* @param {number} [opts.ttlDays] - Days until it expires (default 14, max 365)
|
|
1661
2407
|
* @param {string} [opts.note] - Human label shown in your invite list
|
|
1662
|
-
* @
|
|
2408
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2409
|
+
* @returns {Promise<Invite & {code: string}>}
|
|
1663
2410
|
*/
|
|
1664
2411
|
create: (
|
|
1665
2412
|
{
|
|
@@ -1692,6 +2439,8 @@ class PlaidClient {
|
|
|
1692
2439
|
* Revoke an invite, killing the link immediately. Idempotent. Allowed for
|
|
1693
2440
|
* the creator, an admin, or a maintainer of the invite's project.
|
|
1694
2441
|
* @param {string} id - The invite ID
|
|
2442
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2443
|
+
* @returns {Promise<void>}
|
|
1695
2444
|
*/
|
|
1696
2445
|
revoke: (id, auditMessage) =>
|
|
1697
2446
|
this._request("DELETE", `/api/v1/invites/${id}`, { auditMessage }),
|
|
@@ -1704,7 +2453,7 @@ class PlaidClient {
|
|
|
1704
2453
|
* configuration and the backups on disk, and the settings an operator
|
|
1705
2454
|
* gets asked to confirm. Carries no secrets. Runs a count per table and
|
|
1706
2455
|
* walks the media directory, so open it, do not poll it. Admin only.
|
|
1707
|
-
* @returns {Promise<
|
|
2456
|
+
* @returns {Promise<ServerReport>}
|
|
1708
2457
|
*/
|
|
1709
2458
|
server: () => this._request("GET", "/api/v1/admin/server"),
|
|
1710
2459
|
/**
|
|
@@ -1718,7 +2467,7 @@ class PlaidClient {
|
|
|
1718
2467
|
* rather than the client's usual timeout. A proxy in front of the server
|
|
1719
2468
|
* may still give up first (a 504): the backup goes on, and its file
|
|
1720
2469
|
* shows in `server().backup.backups` once it is written.
|
|
1721
|
-
* @returns {Promise<
|
|
2470
|
+
* @returns {Promise<BackupStatus>}
|
|
1722
2471
|
*
|
|
1723
2472
|
* outOfBand, as every admin action on the server itself is: none of them
|
|
1724
2473
|
* writes project data (see the note at the top of http.js).
|
|
@@ -1738,6 +2487,7 @@ class PlaidClient {
|
|
|
1738
2487
|
* Drop the lock on a document whoever holds it. Idempotent. For a client
|
|
1739
2488
|
* that went away without releasing one. Admin only.
|
|
1740
2489
|
* @param {string} documentId - The document ID
|
|
2490
|
+
* @returns {Promise<{result: string}>}
|
|
1741
2491
|
*/
|
|
1742
2492
|
releaseLock: (documentId) =>
|
|
1743
2493
|
this._request("DELETE", `/api/v1/admin/locks/${documentId}`, {
|
|
@@ -1747,7 +2497,7 @@ class PlaidClient {
|
|
|
1747
2497
|
* Live login and invite rate-limit buckets: the address, the account
|
|
1748
2498
|
* where there is one, failures inside the window, the limit, and whether
|
|
1749
2499
|
* it is currently blocking. Admin only.
|
|
1750
|
-
* @returns {Promise<
|
|
2500
|
+
* @returns {Promise<RateLimitSnapshot>}
|
|
1751
2501
|
*/
|
|
1752
2502
|
rateLimits: () => this._request("GET", "/api/v1/admin/rate-limits"),
|
|
1753
2503
|
/**
|
|
@@ -1757,6 +2507,7 @@ class PlaidClient {
|
|
|
1757
2507
|
* @param {object} [opts]
|
|
1758
2508
|
* @param {string} [opts.ip] - Clear only this address
|
|
1759
2509
|
* @param {string} [opts.userId] - Narrow to one account on that address
|
|
2510
|
+
* @returns {Promise<{result: string}>}
|
|
1760
2511
|
*/
|
|
1761
2512
|
clearRateLimits: ({ ip, userId } = {}) =>
|
|
1762
2513
|
this._request("DELETE", "/api/v1/admin/rate-limits", {
|
|
@@ -1780,7 +2531,7 @@ class PlaidClient {
|
|
|
1780
2531
|
* @param {string} [opts.status] - "2xx".."5xx", an exact code, or "failures"
|
|
1781
2532
|
* @param {string} [opts.user] - Only requests made by this account
|
|
1782
2533
|
* @param {string} [opts.method] - Only requests with this HTTP method
|
|
1783
|
-
* @returns {Promise<
|
|
2534
|
+
* @returns {Promise<LiveLog>}
|
|
1784
2535
|
*/
|
|
1785
2536
|
logs: ({ limit, q, level, status, user, method } = {}) =>
|
|
1786
2537
|
this._request("GET", "/api/v1/admin/logs", {
|
|
@@ -1814,7 +2565,7 @@ class PlaidClient {
|
|
|
1814
2565
|
* @param {string} [opts.prefix] - Only keys starting with this
|
|
1815
2566
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1816
2567
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1817
|
-
* @returns {Promise<
|
|
2568
|
+
* @returns {Promise<AdminUserDataEntry[]>}
|
|
1818
2569
|
*/
|
|
1819
2570
|
userData: ({ prefix, pattern, includeValues } = {}) =>
|
|
1820
2571
|
listAll(this, "/api/v1/admin/user-data", {
|
|
@@ -1833,7 +2584,7 @@ class PlaidClient {
|
|
|
1833
2584
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1834
2585
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1835
2586
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1836
|
-
* @returns {Promise<
|
|
2587
|
+
* @returns {Promise<Page<AdminUserDataEntry>>}
|
|
1837
2588
|
*/
|
|
1838
2589
|
userDataPage: ({ prefix, pattern, includeValues, limit, cursor } = {}) =>
|
|
1839
2590
|
listPage(this, "/api/v1/admin/user-data", {
|
|
@@ -1861,6 +2612,7 @@ class PlaidClient {
|
|
|
1861
2612
|
* matches, carrying only the ones that did.
|
|
1862
2613
|
* @param {string[]|string} [opts.kinds] - Only the entries of operations
|
|
1863
2614
|
* of these kinds (e.g. `['review']`), each whole
|
|
2615
|
+
* @returns {Promise<AuditEntry[]>}
|
|
1864
2616
|
*/
|
|
1865
2617
|
list: ({ startTime, endTime, opTypes, kinds } = {}) =>
|
|
1866
2618
|
listAll(this, "/api/v1/audit", {
|
|
@@ -1874,6 +2626,10 @@ class PlaidClient {
|
|
|
1874
2626
|
/**
|
|
1875
2627
|
* Fetch a single page of the instance-wide audit log. Admin only.
|
|
1876
2628
|
* @param {object} [opts]
|
|
2629
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2630
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2631
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2632
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
1877
2633
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first, which
|
|
1878
2634
|
* is what a feed wants. A cursor belongs to the direction that made it.
|
|
1879
2635
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
@@ -1882,7 +2638,7 @@ class PlaidClient {
|
|
|
1882
2638
|
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
1883
2639
|
* many it has
|
|
1884
2640
|
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
1885
|
-
* @returns {Promise<
|
|
2641
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
1886
2642
|
*/
|
|
1887
2643
|
listPage: ({ startTime, endTime, opTypes, kinds, order, limit, cursor, opsLimit, entryId } = {}) =>
|
|
1888
2644
|
listPage(this, "/api/v1/audit", {
|
|
@@ -1900,7 +2656,13 @@ class PlaidClient {
|
|
|
1900
2656
|
}),
|
|
1901
2657
|
/**
|
|
1902
2658
|
* Async-iterate the instance-wide audit log page by page. Admin only.
|
|
1903
|
-
* @
|
|
2659
|
+
* @param {object} [opts]
|
|
2660
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2661
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2662
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2663
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2664
|
+
* @param {number} [opts.pageSize] - Per-request page size
|
|
2665
|
+
* @returns {AsyncGenerator<AuditEntry[]>}
|
|
1904
2666
|
*/
|
|
1905
2667
|
iterPages: ({ startTime, endTime, opTypes, kinds, pageSize } = {}) =>
|
|
1906
2668
|
iterPages(this, "/api/v1/audit", {
|
|
@@ -1928,7 +2690,7 @@ class PlaidClient {
|
|
|
1928
2690
|
* @param {boolean} [opts.daily] - Also return `byDay`, a list of
|
|
1929
2691
|
* `{date, changes}` per user, oldest first, at the cost of a second
|
|
1930
2692
|
* grouped scan
|
|
1931
|
-
* @returns {Promise<
|
|
2693
|
+
* @returns {Promise<ActivityTallyRow[]>}
|
|
1932
2694
|
*/
|
|
1933
2695
|
tally: async ({ projectId, startTime, endTime, daily } = {}) => {
|
|
1934
2696
|
const path = projectId
|
|
@@ -1946,15 +2708,24 @@ class PlaidClient {
|
|
|
1946
2708
|
};
|
|
1947
2709
|
|
|
1948
2710
|
this.tokenLayers = {
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
*
|
|
1952
|
-
*
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2711
|
+
...layerMethods(this, "token-layers"),
|
|
2712
|
+
/**
|
|
2713
|
+
* Count, per document, the tokens of each layer whose surface matches a
|
|
2714
|
+
* regex, as a query's `value: {regex}` on a token decides. Each text is
|
|
2715
|
+
* read once, so it stays fast on a long document. Layers you cannot read
|
|
2716
|
+
* are left out.
|
|
2717
|
+
* @param {string[]} layerIds - The token layer ids
|
|
2718
|
+
* @param {{regex: string, flags?: string}} value - The regex spec
|
|
2719
|
+
* @returns {Promise<{results: Array<[string, string, number]>}>} [layerId,
|
|
2720
|
+
* documentId, count] rows, a document with tokens of the layer and no
|
|
2721
|
+
* match counting 0.
|
|
2722
|
+
*/
|
|
2723
|
+
countMatching: (layerIds, value) =>
|
|
2724
|
+
this._request("POST", "/api/v1/query/token-counts", {
|
|
2725
|
+
body: { layers: layerIds, value },
|
|
2726
|
+
outOfBand: true,
|
|
2727
|
+
timeout: queryTimeout(this),
|
|
2728
|
+
noBusyRetry: true,
|
|
1958
2729
|
}),
|
|
1959
2730
|
/**
|
|
1960
2731
|
* Create a new token layer.
|
|
@@ -1962,6 +2733,10 @@ class PlaidClient {
|
|
|
1962
2733
|
* @param {string} name - The name
|
|
1963
2734
|
* @param {string} [overlapMode] - Per-layer, immutable token invariant: "any" (default), "non-overlapping", or "partitioning". On partitioning layers, single token create/update/delete are rejected; use bulkCreate plus split/merge/shift.
|
|
1964
2735
|
* @param {string} [parentTokenLayerId] - Optional immutable parent token layer. Tokens in this layer must nest within a parent-layer token; the parent layer must be in the same text layer and be "non-overlapping" or "partitioning" (an "any" parent is rejected). A nested layer may be "any" or "non-overlapping" but not "partitioning" (partitioning is only for root layers), e.g. words (non-overlapping, parent=sentences) within sentences (partitioning).
|
|
2736
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2737
|
+
* @param {object} [opts]
|
|
2738
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2739
|
+
* @returns {Promise<Created>}
|
|
1965
2740
|
*/
|
|
1966
2741
|
create: (
|
|
1967
2742
|
textLayerId,
|
|
@@ -1981,67 +2756,15 @@ class PlaidClient {
|
|
|
1981
2756
|
"parent-token-layer-id": parentTokenLayerId,
|
|
1982
2757
|
}),
|
|
1983
2758
|
}),
|
|
1984
|
-
|
|
1985
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
1986
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
1987
|
-
* @param {string} namespace - The config namespace
|
|
1988
|
-
* @param {string} configKey - The config key
|
|
1989
|
-
* @param {any} configValue - Configuration value to set
|
|
1990
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
1991
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
1992
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
1993
|
-
*/
|
|
1994
|
-
setConfig: (tokenLayerId, namespace, configKey, configValue, auditMessage, options) =>
|
|
1995
|
-
this._request(
|
|
1996
|
-
"PUT",
|
|
1997
|
-
`/api/v1/token-layers/${tokenLayerId}/config/${namespace}/${configKey}`,
|
|
1998
|
-
configRequest(auditMessage, configValue, options),
|
|
1999
|
-
),
|
|
2000
|
-
/**
|
|
2001
|
-
* Remove a configuration value for a layer.
|
|
2002
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
2003
|
-
* @param {string} namespace - The config namespace
|
|
2004
|
-
* @param {string} configKey - The config key
|
|
2005
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2006
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
2007
|
-
*/
|
|
2008
|
-
deleteConfig: (tokenLayerId, namespace, configKey, auditMessage, options) =>
|
|
2009
|
-
this._request(
|
|
2010
|
-
"DELETE",
|
|
2011
|
-
`/api/v1/token-layers/${tokenLayerId}/config/${namespace}/${configKey}`,
|
|
2012
|
-
configRequest(auditMessage, undefined, options, true),
|
|
2013
|
-
),
|
|
2759
|
+
...configMethods(this, "token-layers"),
|
|
2014
2760
|
...constraintMethods(this, "token-layers"),
|
|
2015
|
-
/**
|
|
2016
|
-
* Get a token layer by ID.
|
|
2017
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
2018
|
-
*/
|
|
2019
|
-
get: (tokenLayerId) =>
|
|
2020
|
-
this._request("GET", `/api/v1/token-layers/${tokenLayerId}`),
|
|
2021
|
-
/**
|
|
2022
|
-
* Delete a token layer.
|
|
2023
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
2024
|
-
*/
|
|
2025
|
-
delete: (tokenLayerId, auditMessage) =>
|
|
2026
|
-
this._request("DELETE", `/api/v1/token-layers/${tokenLayerId}`, {
|
|
2027
|
-
auditMessage,
|
|
2028
|
-
}),
|
|
2029
|
-
/**
|
|
2030
|
-
* Update a token layer's name.
|
|
2031
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
2032
|
-
* @param {string} name - The name
|
|
2033
|
-
*/
|
|
2034
|
-
update: (tokenLayerId, name, auditMessage) =>
|
|
2035
|
-
this._request("PATCH", `/api/v1/token-layers/${tokenLayerId}`, {
|
|
2036
|
-
auditMessage,
|
|
2037
|
-
body: bodyOf({ name }),
|
|
2038
|
-
}),
|
|
2039
2761
|
};
|
|
2040
2762
|
|
|
2041
2763
|
this.documents = {
|
|
2042
2764
|
/**
|
|
2043
2765
|
* Check the lock status of a document.
|
|
2044
2766
|
* @param {string} documentId - The document ID
|
|
2767
|
+
* @returns {Promise<{userId: string, expiresAt: number} | void>}
|
|
2045
2768
|
*/
|
|
2046
2769
|
checkLock: (documentId) =>
|
|
2047
2770
|
this._request("GET", `/api/v1/documents/${documentId}/lock`),
|
|
@@ -2066,6 +2789,7 @@ class PlaidClient {
|
|
|
2066
2789
|
* @param {string} documentId - The document ID
|
|
2067
2790
|
* @param {string} [auditMessage]
|
|
2068
2791
|
* @param {string} [newLockId] - A holder id this client minted
|
|
2792
|
+
* @returns {Promise<LockGrant>}
|
|
2069
2793
|
*/
|
|
2070
2794
|
acquireLock: (documentId, auditMessage, newLockId) =>
|
|
2071
2795
|
this._request("POST", `/api/v1/documents/${documentId}/lock`, {
|
|
@@ -2082,6 +2806,8 @@ class PlaidClient {
|
|
|
2082
2806
|
* outOfBand, for the same reason as acquireLock.
|
|
2083
2807
|
* @param {string} documentId - The document ID
|
|
2084
2808
|
* @param {string} lockId - The `lockId` acquireLock answered with
|
|
2809
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2810
|
+
* @returns {Promise<LockGrant>}
|
|
2085
2811
|
*/
|
|
2086
2812
|
renewLock: (documentId, lockId, auditMessage) =>
|
|
2087
2813
|
this._request("POST", `/api/v1/documents/${documentId}/lock`, {
|
|
@@ -2096,6 +2822,8 @@ class PlaidClient {
|
|
|
2096
2822
|
* and not released at all if it aborts.
|
|
2097
2823
|
* @param {string} documentId - The document ID
|
|
2098
2824
|
* @param {string} lockId - The `lockId` acquireLock answered with
|
|
2825
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2826
|
+
* @returns {Promise<void>}
|
|
2099
2827
|
*/
|
|
2100
2828
|
releaseLock: (documentId, lockId, auditMessage) =>
|
|
2101
2829
|
this._request("DELETE", `/api/v1/documents/${documentId}/lock`, {
|
|
@@ -2139,9 +2867,17 @@ class PlaidClient {
|
|
|
2139
2867
|
* lost lock is recorded on the CLIENT, like strict mode, so it stops
|
|
2140
2868
|
* every write the client makes (on any batch of it too) and not only the
|
|
2141
2869
|
* ones this block makes.
|
|
2870
|
+
*
|
|
2871
|
+
* A core restart drops every lock, and until the next renewal finds it
|
|
2872
|
+
* gone (up to half the lock window) this block's writes still pass, since
|
|
2873
|
+
* writes carry no lock id. When a write must not land past that point,
|
|
2874
|
+
* hold the document in strict mode too (`enterStrictMode`), so such a
|
|
2875
|
+
* write is refused with a 409.
|
|
2876
|
+
* @template T
|
|
2142
2877
|
* @param {string} documentId - The document ID
|
|
2143
|
-
* @param {(lock: DocumentLock) =>
|
|
2144
|
-
* @param {
|
|
2878
|
+
* @param {(lock: import("./documentLock.js").DocumentLock) => T | Promise<T>} fn - The work to run while holding it
|
|
2879
|
+
* @param {{keepAlive?: boolean}} [options] - `{ keepAlive }`: renew on a timer (default true)
|
|
2880
|
+
* @returns {Promise<T>}
|
|
2145
2881
|
*/
|
|
2146
2882
|
locked: (documentId, fn, options) =>
|
|
2147
2883
|
withDocumentLock(this, documentId, fn, options),
|
|
@@ -2150,11 +2886,34 @@ class PlaidClient {
|
|
|
2150
2886
|
* no as-of form: the route refuses the parameter. Prefer the document's
|
|
2151
2887
|
* own `mediaUrl`, which carries the file's version for caching.
|
|
2152
2888
|
* @param {string} documentId - The document ID
|
|
2889
|
+
* @returns {Promise<ArrayBuffer>}
|
|
2153
2890
|
*/
|
|
2154
2891
|
getMedia: (documentId) =>
|
|
2155
2892
|
this._request("GET", `/api/v1/documents/${documentId}/media`, {
|
|
2156
2893
|
binaryResponse: true,
|
|
2157
2894
|
}),
|
|
2895
|
+
/**
|
|
2896
|
+
* Get a link that plays the document's recording without an
|
|
2897
|
+
* Authorization header, for an audio or video element's `src`, which
|
|
2898
|
+
* cannot send one. `url` is the document's media URL with a
|
|
2899
|
+
* `media-token` added, resolved against the client's base URL. The token
|
|
2900
|
+
* opens only this recording, for this user, and the server refuses it on
|
|
2901
|
+
* every other route. It expires at `expiresAt` (an ISO-8601 instant, six
|
|
2902
|
+
* hours on by default), and sooner when the user signs out, changes
|
|
2903
|
+
* password or loses access to the project: ask for a new link then. A
|
|
2904
|
+
* document with no recording is a 404. Writes nothing, so it takes no
|
|
2905
|
+
* Idempotency-Key and goes over the wire when made on a batch.
|
|
2906
|
+
* @param {string} documentId - The document ID
|
|
2907
|
+
* @returns {Promise<{url: string, expiresAt: string}>}
|
|
2908
|
+
*/
|
|
2909
|
+
mediaLink: async (documentId) => {
|
|
2910
|
+
const link = await this._request(
|
|
2911
|
+
"POST",
|
|
2912
|
+
`/api/v1/documents/${documentId}/media/link`,
|
|
2913
|
+
{ outOfBand: true },
|
|
2914
|
+
);
|
|
2915
|
+
return { ...link, url: `${this.baseUrl}${link.url}` };
|
|
2916
|
+
},
|
|
2158
2917
|
/**
|
|
2159
2918
|
* Upload a media file for a document. Uses Apache Tika for content validation.
|
|
2160
2919
|
* A document that already has a recording refuses it with a 409
|
|
@@ -2165,7 +2924,8 @@ class PlaidClient {
|
|
|
2165
2924
|
* @param {string} documentId - The document ID
|
|
2166
2925
|
* @param {File} file - The file to upload
|
|
2167
2926
|
* @param {string} [auditMessage] - Custom audit-log message for this write
|
|
2168
|
-
* @param {
|
|
2927
|
+
* @param {{onProgress?: (progress: {loaded: number, total: number | null}) => void}} [options] - `{ onProgress }`: called with `{ loaded, total }` (bytes) as the file goes up. In a browser the upload then travels by XMLHttpRequest, and the request timeout only fires when the upload stalls, not on total time.
|
|
2928
|
+
* @returns {Promise<{message: string, extension: string, contentType: string}>}
|
|
2169
2929
|
*/
|
|
2170
2930
|
uploadMedia: (documentId, file, auditMessage, { onProgress } = {}) => {
|
|
2171
2931
|
const fd = new FormData();
|
|
@@ -2186,50 +2946,25 @@ class PlaidClient {
|
|
|
2186
2946
|
* recording meant, the `?v=` of the document's `mediaUrl`. The delete
|
|
2187
2947
|
* is refused with a 409 (`media-changed`, and the current `media-url`)
|
|
2188
2948
|
* when the stored recording is another one.
|
|
2949
|
+
* @returns {Promise<void>}
|
|
2189
2950
|
*/
|
|
2190
2951
|
// The upload above is multipart and cannot be batched, but a DELETE
|
|
2191
2952
|
// carries no blob, so the batch transport takes it. It is a write
|
|
2192
|
-
// of the document's own data and queues like any other.
|
|
2193
|
-
// file
|
|
2194
|
-
//
|
|
2953
|
+
// of the document's own data and queues like any other. The server
|
|
2954
|
+
// removes the file only after the write commits, so a batch that aborts
|
|
2955
|
+
// after this op leaves the recording in place.
|
|
2195
2956
|
deleteMedia: (documentId, auditMessage, { mediaVersion } = {}) =>
|
|
2196
2957
|
this._request("DELETE", `/api/v1/documents/${documentId}/media`, {
|
|
2197
2958
|
auditMessage,
|
|
2198
2959
|
queryParams: { "media-version": mediaVersion },
|
|
2199
2960
|
}),
|
|
2200
|
-
|
|
2201
|
-
* Replace all metadata for a document.
|
|
2202
|
-
* @param {string} documentId - The document ID
|
|
2203
|
-
* @param {any} body - The request body
|
|
2204
|
-
*/
|
|
2205
|
-
setMetadata: (documentId, body, auditMessage) =>
|
|
2206
|
-
this._request("PUT", `/api/v1/documents/${documentId}/metadata`, {
|
|
2207
|
-
auditMessage,
|
|
2208
|
-
rawBody: body,
|
|
2209
|
-
}),
|
|
2210
|
-
/**
|
|
2211
|
-
* Remove all metadata from a document.
|
|
2212
|
-
* @param {string} documentId - The document ID
|
|
2213
|
-
*/
|
|
2214
|
-
deleteMetadata: (documentId, auditMessage) =>
|
|
2215
|
-
this._request("DELETE", `/api/v1/documents/${documentId}/metadata`, {
|
|
2216
|
-
auditMessage,
|
|
2217
|
-
}),
|
|
2218
|
-
/**
|
|
2219
|
-
* Edit metadata for a document with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
2220
|
-
* @param {string} documentId - The document ID
|
|
2221
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
2222
|
-
*/
|
|
2223
|
-
patchMetadata: (documentId, body, auditMessage) =>
|
|
2224
|
-
this._request("PATCH", `/api/v1/documents/${documentId}/metadata`, {
|
|
2225
|
-
auditMessage,
|
|
2226
|
-
rawBody: body,
|
|
2227
|
-
}),
|
|
2961
|
+
...metadataMethods(this, "documents"),
|
|
2228
2962
|
/**
|
|
2229
2963
|
* Get audit log for a document. Transparently follows pagination cursors
|
|
2230
2964
|
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2231
2965
|
* says what kind of credential made it as `credential`: `login`,
|
|
2232
|
-
* `named-token
|
|
2966
|
+
* `named-token` or `delegated` (absent on older operations, `service`
|
|
2967
|
+
* on some from 2026-10-06 to 2026-10-08),
|
|
2233
2968
|
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2234
2969
|
* @param {string} documentId - The document ID
|
|
2235
2970
|
* @param {string} [startTime] - Start of time range
|
|
@@ -2240,6 +2975,7 @@ class PlaidClient {
|
|
|
2240
2975
|
* one of its operations matches, carrying only the ones that did.
|
|
2241
2976
|
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
2242
2977
|
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
2978
|
+
* @returns {Promise<AuditEntry[]>}
|
|
2243
2979
|
*/
|
|
2244
2980
|
audit: (documentId, startTime, endTime, opTypes, kinds) =>
|
|
2245
2981
|
listAll(this, `/api/v1/documents/${documentId}/audit`, {
|
|
@@ -2256,6 +2992,10 @@ class PlaidClient {
|
|
|
2256
2992
|
* that may be long: audit() walks every page before it resolves.
|
|
2257
2993
|
* @param {string} documentId - The document ID
|
|
2258
2994
|
* @param {object} [opts]
|
|
2995
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2996
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2997
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2998
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2259
2999
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
2260
3000
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2261
3001
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -2263,7 +3003,7 @@ class PlaidClient {
|
|
|
2263
3003
|
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
2264
3004
|
* many it has
|
|
2265
3005
|
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
2266
|
-
* @returns {Promise<
|
|
3006
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
2267
3007
|
*/
|
|
2268
3008
|
auditPage: (
|
|
2269
3009
|
documentId,
|
|
@@ -2287,14 +3027,17 @@ class PlaidClient {
|
|
|
2287
3027
|
* operation: what was deleted since then comes back under its original
|
|
2288
3028
|
* id, what was added since is removed, and what changed is set back,
|
|
2289
3029
|
* across every layer. A layer deleted since then, or a vocabulary entry
|
|
2290
|
-
* that no longer exists, is skipped and reported under `skipped`.
|
|
3030
|
+
* that no longer exists, is skipped and reported under `skipped`. A name
|
|
3031
|
+
* another document holds now stays with it, and the document keeps its
|
|
3032
|
+
* current name, reported under `nameKept` (`{name, taken}`).
|
|
2291
3033
|
* Resolves to a summary of the changes. Maintainers only.
|
|
2292
3034
|
* @param {string} documentId - The document ID
|
|
2293
3035
|
* @param {string} asOf - The moment to go back to (ISO-8601 instant),
|
|
2294
3036
|
* typically a history entry's `endTime`
|
|
2295
|
-
* @param {
|
|
3037
|
+
* @param {{dryRun?: boolean}} [options] - `{ dryRun }`: with `dryRun` true nothing
|
|
2296
3038
|
* is written and the summary says what would change
|
|
2297
3039
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
3040
|
+
* @returns {Promise<DocumentRestoreSummary>}
|
|
2298
3041
|
*/
|
|
2299
3042
|
restore: (documentId, asOf, { dryRun } = {}, auditMessage) =>
|
|
2300
3043
|
this._request("POST", `/api/v1/documents/${documentId}/restore`, {
|
|
@@ -2307,6 +3050,10 @@ class PlaidClient {
|
|
|
2307
3050
|
/**
|
|
2308
3051
|
* Get a document. Set `includeBody` to true to include all data.
|
|
2309
3052
|
*
|
|
3053
|
+
* A document with a recording has `mediaUrl`, and `mediaDuration`, the
|
|
3054
|
+
* recording's length in seconds, when the server could read it from the
|
|
3055
|
+
* file at upload (WAV, MP4, M4A, MOV, FLAC, MP3).
|
|
3056
|
+
*
|
|
2310
3057
|
* `layers` narrows a body read to the layers you name (ids of any kind:
|
|
2311
3058
|
* text, token, span, or relation). A layer comes back when it is named
|
|
2312
3059
|
* or is an ancestor of a named layer, and carries its own
|
|
@@ -2318,6 +3065,7 @@ class PlaidClient {
|
|
|
2318
3065
|
* @param {boolean} [includeBody] - Include document body data
|
|
2319
3066
|
* @param {string} [asOf] - Temporal query timestamp
|
|
2320
3067
|
* @param {string[]|string} [layers] - Layer ids to restrict a body read to
|
|
3068
|
+
* @returns {Promise<PlaidDocument>}
|
|
2321
3069
|
*/
|
|
2322
3070
|
get: (documentId, includeBody, asOf, layers) =>
|
|
2323
3071
|
this._request("GET", `/api/v1/documents/${documentId}`, {
|
|
@@ -2330,15 +3078,21 @@ class PlaidClient {
|
|
|
2330
3078
|
/**
|
|
2331
3079
|
* Delete a document and all data contained.
|
|
2332
3080
|
* @param {string} documentId - The document ID
|
|
3081
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3082
|
+
* @returns {Promise<void>}
|
|
2333
3083
|
*/
|
|
2334
3084
|
delete: (documentId, auditMessage) =>
|
|
2335
3085
|
this._request("DELETE", `/api/v1/documents/${documentId}`, {
|
|
2336
3086
|
auditMessage,
|
|
2337
3087
|
}),
|
|
2338
3088
|
/**
|
|
2339
|
-
* Update a document's name.
|
|
3089
|
+
* Update a document's name. A name another document of the project holds,
|
|
3090
|
+
* ignoring case,
|
|
3091
|
+
* is refused with 422 and `name-taken`.
|
|
2340
3092
|
* @param {string} documentId - The document ID
|
|
2341
3093
|
* @param {string} name - The name
|
|
3094
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3095
|
+
* @returns {Promise<PlaidDocument>}
|
|
2342
3096
|
*/
|
|
2343
3097
|
update: (documentId, name, auditMessage) =>
|
|
2344
3098
|
this._request("PATCH", `/api/v1/documents/${documentId}`, {
|
|
@@ -2350,11 +3104,24 @@ class PlaidClient {
|
|
|
2350
3104
|
* @param {string} projectId - The project ID
|
|
2351
3105
|
* @param {string} name - The name
|
|
2352
3106
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
3107
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3108
|
+
* @param {object} [opts]
|
|
3109
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3110
|
+
* @param {boolean} [opts.freeName] - A name another document of the project holds, ignoring case, becomes
|
|
3111
|
+
* the first free "name (2)", "name (3)" and on. Without it such a name is refused with 422
|
|
3112
|
+
* and `name-taken`.
|
|
3113
|
+
* @returns {Promise<{id: string, name: string}>} The new id and the name stored
|
|
2353
3114
|
*/
|
|
2354
|
-
create: (projectId, name, metadata, auditMessage, { id } = {}) =>
|
|
3115
|
+
create: (projectId, name, metadata, auditMessage, { id, freeName } = {}) =>
|
|
2355
3116
|
this._request("POST", "/api/v1/documents", {
|
|
2356
3117
|
auditMessage,
|
|
2357
|
-
body: bodyOf({
|
|
3118
|
+
body: bodyOf({
|
|
3119
|
+
id,
|
|
3120
|
+
"project-id": projectId,
|
|
3121
|
+
name,
|
|
3122
|
+
metadata,
|
|
3123
|
+
"free-name": freeName || undefined,
|
|
3124
|
+
}),
|
|
2358
3125
|
}),
|
|
2359
3126
|
/**
|
|
2360
3127
|
* Copy a document and everything in it into a new document of the same
|
|
@@ -2362,18 +3129,27 @@ class PlaidClient {
|
|
|
2362
3129
|
* the vocabulary entries its links name, and holds the source's texts,
|
|
2363
3130
|
* tokens, spans, relations and vocab links under fresh ids, with their
|
|
2364
3131
|
* metadata. Comments do not travel. The media file does, unless
|
|
2365
|
-
* `includeMedia` is false. Resolves to `{ id }`, plus `mediaError` when
|
|
2366
|
-
* the source had media the copy could not take with it.
|
|
3132
|
+
* `includeMedia` is false. Resolves to `{ id, name }`, plus `mediaError` when
|
|
3133
|
+
* the source had media the copy could not take with it. A name another
|
|
3134
|
+
* document of the project holds, ignoring case, is refused with 422 and `name-taken`,
|
|
3135
|
+
* unless `freeName`, which takes the first free "name (2)", "name (3)"
|
|
3136
|
+
* and on.
|
|
2367
3137
|
* @param {string} documentId - The document to copy
|
|
2368
3138
|
* @param {string} name - The new document's name
|
|
2369
|
-
* @param {
|
|
3139
|
+
* @param {{includeMedia?: boolean, id?: string, freeName?: boolean}} [options] - `{ includeMedia }`: false leaves the media
|
|
2370
3140
|
* file behind
|
|
2371
3141
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
3142
|
+
* @returns {Promise<{id: string, name: string, mediaError?: string}>}
|
|
2372
3143
|
*/
|
|
2373
|
-
copy: (documentId, name, { includeMedia, id } = {}, auditMessage) =>
|
|
3144
|
+
copy: (documentId, name, { includeMedia, id, freeName } = {}, auditMessage) =>
|
|
2374
3145
|
this._request("POST", `/api/v1/documents/${documentId}/copy`, {
|
|
2375
3146
|
auditMessage,
|
|
2376
|
-
body: bodyOf({
|
|
3147
|
+
body: bodyOf({
|
|
3148
|
+
id,
|
|
3149
|
+
name,
|
|
3150
|
+
"include-media": includeMedia,
|
|
3151
|
+
"free-name": freeName || undefined,
|
|
3152
|
+
}),
|
|
2377
3153
|
}),
|
|
2378
3154
|
};
|
|
2379
3155
|
|
|
@@ -2382,6 +3158,8 @@ class PlaidClient {
|
|
|
2382
3158
|
* Set a user's access level to read and write for this project.
|
|
2383
3159
|
* @param {string} id - The resource ID
|
|
2384
3160
|
* @param {string} userId - The user ID
|
|
3161
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3162
|
+
* @returns {Promise<void>}
|
|
2385
3163
|
*/
|
|
2386
3164
|
addWriter: (id, userId, auditMessage) =>
|
|
2387
3165
|
this._request("POST", `/api/v1/projects/${id}/writers/${userId}`, {
|
|
@@ -2391,6 +3169,8 @@ class PlaidClient {
|
|
|
2391
3169
|
* Remove a user's writer privileges for this project.
|
|
2392
3170
|
* @param {string} id - The resource ID
|
|
2393
3171
|
* @param {string} userId - The user ID
|
|
3172
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3173
|
+
* @returns {Promise<void>}
|
|
2394
3174
|
*/
|
|
2395
3175
|
removeWriter: (id, userId, auditMessage) =>
|
|
2396
3176
|
this._request("DELETE", `/api/v1/projects/${id}/writers/${userId}`, {
|
|
@@ -2400,6 +3180,8 @@ class PlaidClient {
|
|
|
2400
3180
|
* Set a user's access level to read-only for this project.
|
|
2401
3181
|
* @param {string} id - The resource ID
|
|
2402
3182
|
* @param {string} userId - The user ID
|
|
3183
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3184
|
+
* @returns {Promise<void>}
|
|
2403
3185
|
*/
|
|
2404
3186
|
addReader: (id, userId, auditMessage) =>
|
|
2405
3187
|
this._request("POST", `/api/v1/projects/${id}/readers/${userId}`, {
|
|
@@ -2409,45 +3191,20 @@ class PlaidClient {
|
|
|
2409
3191
|
* Remove a user's reader privileges for this project.
|
|
2410
3192
|
* @param {string} id - The resource ID
|
|
2411
3193
|
* @param {string} userId - The user ID
|
|
3194
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3195
|
+
* @returns {Promise<void>}
|
|
2412
3196
|
*/
|
|
2413
3197
|
removeReader: (id, userId, auditMessage) =>
|
|
2414
3198
|
this._request("DELETE", `/api/v1/projects/${id}/readers/${userId}`, {
|
|
2415
3199
|
auditMessage,
|
|
2416
3200
|
}),
|
|
2417
|
-
|
|
2418
|
-
* Set a configuration value for a project in an editor namespace.
|
|
2419
|
-
* @param {string} id - The resource ID
|
|
2420
|
-
* @param {string} namespace - The config namespace
|
|
2421
|
-
* @param {string} configKey - The config key
|
|
2422
|
-
* @param {any} configValue - Configuration value to set
|
|
2423
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2424
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
2425
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
2426
|
-
*/
|
|
2427
|
-
setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
|
|
2428
|
-
this._request(
|
|
2429
|
-
"PUT",
|
|
2430
|
-
`/api/v1/projects/${id}/config/${namespace}/${configKey}`,
|
|
2431
|
-
configRequest(auditMessage, configValue, options),
|
|
2432
|
-
),
|
|
2433
|
-
/**
|
|
2434
|
-
* Remove a configuration value for a project.
|
|
2435
|
-
* @param {string} id - The resource ID
|
|
2436
|
-
* @param {string} namespace - The config namespace
|
|
2437
|
-
* @param {string} configKey - The config key
|
|
2438
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2439
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
2440
|
-
*/
|
|
2441
|
-
deleteConfig: (id, namespace, configKey, auditMessage, options) =>
|
|
2442
|
-
this._request(
|
|
2443
|
-
"DELETE",
|
|
2444
|
-
`/api/v1/projects/${id}/config/${namespace}/${configKey}`,
|
|
2445
|
-
configRequest(auditMessage, undefined, options, true),
|
|
2446
|
-
),
|
|
3201
|
+
...configMethods(this, "projects"),
|
|
2447
3202
|
/**
|
|
2448
3203
|
* Assign a user as a maintainer for this project.
|
|
2449
3204
|
* @param {string} id - The resource ID
|
|
2450
3205
|
* @param {string} userId - The user ID
|
|
3206
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3207
|
+
* @returns {Promise<void>}
|
|
2451
3208
|
*/
|
|
2452
3209
|
addMaintainer: (id, userId, auditMessage) =>
|
|
2453
3210
|
this._request("POST", `/api/v1/projects/${id}/maintainers/${userId}`, {
|
|
@@ -2457,6 +3214,8 @@ class PlaidClient {
|
|
|
2457
3214
|
* Remove a user's maintainer privileges for this project.
|
|
2458
3215
|
* @param {string} id - The resource ID
|
|
2459
3216
|
* @param {string} userId - The user ID
|
|
3217
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3218
|
+
* @returns {Promise<void>}
|
|
2460
3219
|
*/
|
|
2461
3220
|
removeMaintainer: (id, userId, auditMessage) =>
|
|
2462
3221
|
this._request(
|
|
@@ -2468,7 +3227,8 @@ class PlaidClient {
|
|
|
2468
3227
|
* Get audit log for a project. Transparently follows pagination cursors
|
|
2469
3228
|
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2470
3229
|
* says what kind of credential made it as `credential`: `login`,
|
|
2471
|
-
* `named-token
|
|
3230
|
+
* `named-token` or `delegated` (absent on older operations, `service`
|
|
3231
|
+
* on some from 2026-10-06 to 2026-10-08),
|
|
2472
3232
|
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2473
3233
|
* @param {string} projectId - The project ID
|
|
2474
3234
|
* @param {string} [startTime] - Start of time range
|
|
@@ -2479,6 +3239,7 @@ class PlaidClient {
|
|
|
2479
3239
|
* one of its operations matches, carrying only the ones that did.
|
|
2480
3240
|
* @param {string[]|string} [kinds] - Only the entries of operations of
|
|
2481
3241
|
* these kinds (e.g. `['review', 'guess-adoption']`), each whole
|
|
3242
|
+
* @returns {Promise<AuditEntry[]>}
|
|
2482
3243
|
*/
|
|
2483
3244
|
audit: (projectId, startTime, endTime, opTypes, kinds) =>
|
|
2484
3245
|
listAll(this, `/api/v1/projects/${projectId}/audit`, {
|
|
@@ -2495,6 +3256,10 @@ class PlaidClient {
|
|
|
2495
3256
|
* that may be long: audit() walks every page before it resolves.
|
|
2496
3257
|
* @param {string} projectId - The project ID
|
|
2497
3258
|
* @param {object} [opts]
|
|
3259
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
3260
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
3261
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
3262
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2498
3263
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
2499
3264
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2500
3265
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -2502,7 +3267,7 @@ class PlaidClient {
|
|
|
2502
3267
|
* operations in `ops` (1..1000). `opCount` on every entry says how
|
|
2503
3268
|
* many it has
|
|
2504
3269
|
* @param {string} [opts.entryId] - Read only this entry, not a page
|
|
2505
|
-
* @returns {Promise<
|
|
3270
|
+
* @returns {Promise<Page<AuditEntry>>}
|
|
2506
3271
|
*/
|
|
2507
3272
|
auditPage: (
|
|
2508
3273
|
projectId,
|
|
@@ -2539,6 +3304,8 @@ class PlaidClient {
|
|
|
2539
3304
|
* Link a vocabulary to a project.
|
|
2540
3305
|
* @param {string} id - The resource ID
|
|
2541
3306
|
* @param {string} vocabId - The vocab layer ID
|
|
3307
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3308
|
+
* @returns {Promise<void>}
|
|
2542
3309
|
*/
|
|
2543
3310
|
linkVocab: (id, vocabId, auditMessage) =>
|
|
2544
3311
|
this._request("POST", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
|
|
@@ -2548,6 +3315,8 @@ class PlaidClient {
|
|
|
2548
3315
|
* Unlink a vocabulary from a project.
|
|
2549
3316
|
* @param {string} id - The resource ID
|
|
2550
3317
|
* @param {string} vocabId - The vocab layer ID
|
|
3318
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3319
|
+
* @returns {Promise<void>}
|
|
2551
3320
|
*/
|
|
2552
3321
|
unlinkVocab: (id, vocabId, auditMessage) =>
|
|
2553
3322
|
this._request("DELETE", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
|
|
@@ -2557,6 +3326,7 @@ class PlaidClient {
|
|
|
2557
3326
|
* Get a project by ID. To fetch the project's documents, use
|
|
2558
3327
|
* listDocuments(id) — the include-documents flag has been removed.
|
|
2559
3328
|
* @param {string} id - The resource ID
|
|
3329
|
+
* @returns {Promise<Project>}
|
|
2560
3330
|
*/
|
|
2561
3331
|
get: (id) =>
|
|
2562
3332
|
this._request("GET", `/api/v1/projects/${id}`),
|
|
@@ -2567,6 +3337,7 @@ class PlaidClient {
|
|
|
2567
3337
|
* Note: this endpoint does not support temporal (`as-of`) queries; the
|
|
2568
3338
|
* server rejects `?as-of=` on the documents-list route with a 400.
|
|
2569
3339
|
* @param {string} id - The project ID
|
|
3340
|
+
* @returns {Promise<DocumentListEntry[]>}
|
|
2570
3341
|
*/
|
|
2571
3342
|
listDocuments: (id) => listAll(this, `/api/v1/projects/${id}/documents`),
|
|
2572
3343
|
/**
|
|
@@ -2578,7 +3349,7 @@ class PlaidClient {
|
|
|
2578
3349
|
* @param {object} [opts]
|
|
2579
3350
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2580
3351
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
2581
|
-
* @returns {Promise<
|
|
3352
|
+
* @returns {Promise<Page<DocumentListEntry>>}
|
|
2582
3353
|
*/
|
|
2583
3354
|
listDocumentsPage: (id, { limit, cursor } = {}) =>
|
|
2584
3355
|
listPage(this, `/api/v1/projects/${id}/documents`, { limit, cursor }),
|
|
@@ -2591,7 +3362,7 @@ class PlaidClient {
|
|
|
2591
3362
|
* @param {string} id - The project ID
|
|
2592
3363
|
* @param {object} [opts]
|
|
2593
3364
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
2594
|
-
* @returns {AsyncGenerator<
|
|
3365
|
+
* @returns {AsyncGenerator<DocumentListEntry[]>}
|
|
2595
3366
|
*/
|
|
2596
3367
|
iterDocuments: (id, { pageSize } = {}) =>
|
|
2597
3368
|
iterPages(this, `/api/v1/projects/${id}/documents`, { pageSize }),
|
|
@@ -2603,6 +3374,7 @@ class PlaidClient {
|
|
|
2603
3374
|
* @param {string} [auditMessage] - Custom audit-log message
|
|
2604
3375
|
* @param {object} [options]
|
|
2605
3376
|
* @param {number} [options.timeout] - Per-request timeout in ms, the client's own by default (0/null disables)
|
|
3377
|
+
* @returns {Promise<void>}
|
|
2606
3378
|
*/
|
|
2607
3379
|
delete: (id, auditMessage, { timeout } = {}) =>
|
|
2608
3380
|
this._request("DELETE", `/api/v1/projects/${id}`, {
|
|
@@ -2613,6 +3385,8 @@ class PlaidClient {
|
|
|
2613
3385
|
* Update a project's name.
|
|
2614
3386
|
* @param {string} id - The resource ID
|
|
2615
3387
|
* @param {string} name - The name
|
|
3388
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3389
|
+
* @returns {Promise<Project>}
|
|
2616
3390
|
*/
|
|
2617
3391
|
update: (id, name, auditMessage) =>
|
|
2618
3392
|
this._request("PATCH", `/api/v1/projects/${id}`, {
|
|
@@ -2622,6 +3396,7 @@ class PlaidClient {
|
|
|
2622
3396
|
/**
|
|
2623
3397
|
* List all projects accessible to user. Transparently follows pagination
|
|
2624
3398
|
* cursors and returns the full flat array.
|
|
3399
|
+
* @returns {Promise<ProjectListEntry[]>}
|
|
2625
3400
|
*/
|
|
2626
3401
|
list: () => listAll(this, "/api/v1/projects"),
|
|
2627
3402
|
/**
|
|
@@ -2629,7 +3404,7 @@ class PlaidClient {
|
|
|
2629
3404
|
* @param {object} [opts]
|
|
2630
3405
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2631
3406
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
2632
|
-
* @returns {Promise<
|
|
3407
|
+
* @returns {Promise<Page<ProjectListEntry>>}
|
|
2633
3408
|
*/
|
|
2634
3409
|
listPage: ({ limit, cursor } = {}) =>
|
|
2635
3410
|
listPage(this, "/api/v1/projects", {
|
|
@@ -2640,7 +3415,7 @@ class PlaidClient {
|
|
|
2640
3415
|
* Async-iterate projects page by page; yields each page's entries array.
|
|
2641
3416
|
* @param {object} [opts]
|
|
2642
3417
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
2643
|
-
* @returns {AsyncGenerator<
|
|
3418
|
+
* @returns {AsyncGenerator<ProjectListEntry[]>}
|
|
2644
3419
|
*/
|
|
2645
3420
|
iterPages: ({ pageSize } = {}) =>
|
|
2646
3421
|
iterPages(this, "/api/v1/projects", {
|
|
@@ -2649,6 +3424,10 @@ class PlaidClient {
|
|
|
2649
3424
|
/**
|
|
2650
3425
|
* Create a new project. Note: this also registers the user as a maintainer.
|
|
2651
3426
|
* @param {string} name - The name
|
|
3427
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3428
|
+
* @param {object} [opts]
|
|
3429
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3430
|
+
* @returns {Promise<Created>}
|
|
2652
3431
|
*/
|
|
2653
3432
|
create: (name, auditMessage, { id } = {}) =>
|
|
2654
3433
|
this._request("POST", "/api/v1/projects", {
|
|
@@ -2658,74 +3437,16 @@ class PlaidClient {
|
|
|
2658
3437
|
};
|
|
2659
3438
|
|
|
2660
3439
|
this.textLayers = {
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
* @param {string} textLayerId - The text layer ID
|
|
2664
|
-
* @param {string} namespace - The config namespace
|
|
2665
|
-
* @param {string} configKey - The config key
|
|
2666
|
-
* @param {any} configValue - Configuration value to set
|
|
2667
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2668
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
2669
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
2670
|
-
*/
|
|
2671
|
-
setConfig: (textLayerId, namespace, configKey, configValue, auditMessage, options) =>
|
|
2672
|
-
this._request(
|
|
2673
|
-
"PUT",
|
|
2674
|
-
`/api/v1/text-layers/${textLayerId}/config/${namespace}/${configKey}`,
|
|
2675
|
-
configRequest(auditMessage, configValue, options),
|
|
2676
|
-
),
|
|
2677
|
-
/**
|
|
2678
|
-
* Remove a configuration value for a layer.
|
|
2679
|
-
* @param {string} textLayerId - The text layer ID
|
|
2680
|
-
* @param {string} namespace - The config namespace
|
|
2681
|
-
* @param {string} configKey - The config key
|
|
2682
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2683
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
2684
|
-
*/
|
|
2685
|
-
deleteConfig: (textLayerId, namespace, configKey, auditMessage, options) =>
|
|
2686
|
-
this._request(
|
|
2687
|
-
"DELETE",
|
|
2688
|
-
`/api/v1/text-layers/${textLayerId}/config/${namespace}/${configKey}`,
|
|
2689
|
-
configRequest(auditMessage, undefined, options, true),
|
|
2690
|
-
),
|
|
2691
|
-
/**
|
|
2692
|
-
* Get a text layer by ID.
|
|
2693
|
-
* @param {string} textLayerId - The text layer ID
|
|
2694
|
-
*/
|
|
2695
|
-
get: (textLayerId) =>
|
|
2696
|
-
this._request("GET", `/api/v1/text-layers/${textLayerId}`),
|
|
2697
|
-
/**
|
|
2698
|
-
* Delete a text layer.
|
|
2699
|
-
* @param {string} textLayerId - The text layer ID
|
|
2700
|
-
*/
|
|
2701
|
-
delete: (textLayerId, auditMessage) =>
|
|
2702
|
-
this._request("DELETE", `/api/v1/text-layers/${textLayerId}`, {
|
|
2703
|
-
auditMessage,
|
|
2704
|
-
}),
|
|
2705
|
-
/**
|
|
2706
|
-
* Update a text layer's name.
|
|
2707
|
-
* @param {string} textLayerId - The text layer ID
|
|
2708
|
-
* @param {string} name - The name
|
|
2709
|
-
*/
|
|
2710
|
-
update: (textLayerId, name, auditMessage) =>
|
|
2711
|
-
this._request("PATCH", `/api/v1/text-layers/${textLayerId}`, {
|
|
2712
|
-
auditMessage,
|
|
2713
|
-
body: bodyOf({ name }),
|
|
2714
|
-
}),
|
|
2715
|
-
/**
|
|
2716
|
-
* Shift a text layer's display order within the project.
|
|
2717
|
-
* @param {string} textLayerId - The text layer ID
|
|
2718
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
2719
|
-
*/
|
|
2720
|
-
shift: (textLayerId, direction, auditMessage) =>
|
|
2721
|
-
this._request("POST", `/api/v1/text-layers/${textLayerId}/shift`, {
|
|
2722
|
-
auditMessage,
|
|
2723
|
-
body: bodyOf({ direction }),
|
|
2724
|
-
}),
|
|
3440
|
+
...configMethods(this, "text-layers"),
|
|
3441
|
+
...layerMethods(this, "text-layers"),
|
|
2725
3442
|
/**
|
|
2726
3443
|
* Create a new text layer for a project.
|
|
2727
3444
|
* @param {string} projectId - The project ID
|
|
2728
3445
|
* @param {string} name - The name
|
|
3446
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3447
|
+
* @param {object} [opts]
|
|
3448
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3449
|
+
* @returns {Promise<Created>}
|
|
2729
3450
|
*/
|
|
2730
3451
|
create: (projectId, name, auditMessage, { id } = {}) =>
|
|
2731
3452
|
this._request("POST", "/api/v1/text-layers", {
|
|
@@ -2735,39 +3456,16 @@ class PlaidClient {
|
|
|
2735
3456
|
};
|
|
2736
3457
|
|
|
2737
3458
|
this.vocabItems = {
|
|
2738
|
-
|
|
2739
|
-
* Replace all metadata for a vocab item.
|
|
2740
|
-
* @param {string} id - The resource ID
|
|
2741
|
-
* @param {any} body - The request body
|
|
2742
|
-
*/
|
|
2743
|
-
setMetadata: (id, body, auditMessage) =>
|
|
2744
|
-
this._request("PUT", `/api/v1/vocab-items/${id}/metadata`, {
|
|
2745
|
-
auditMessage,
|
|
2746
|
-
rawBody: body,
|
|
2747
|
-
}),
|
|
2748
|
-
/**
|
|
2749
|
-
* Remove all metadata from a vocab item.
|
|
2750
|
-
* @param {string} id - The resource ID
|
|
2751
|
-
*/
|
|
2752
|
-
deleteMetadata: (id, auditMessage) =>
|
|
2753
|
-
this._request("DELETE", `/api/v1/vocab-items/${id}/metadata`, {
|
|
2754
|
-
auditMessage,
|
|
2755
|
-
}),
|
|
2756
|
-
/**
|
|
2757
|
-
* Edit metadata for a vocab item with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
2758
|
-
* @param {string} id - The resource ID
|
|
2759
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
2760
|
-
*/
|
|
2761
|
-
patchMetadata: (id, body, auditMessage) =>
|
|
2762
|
-
this._request("PATCH", `/api/v1/vocab-items/${id}/metadata`, {
|
|
2763
|
-
auditMessage,
|
|
2764
|
-
rawBody: body,
|
|
2765
|
-
}),
|
|
3459
|
+
...metadataMethods(this, "vocab-items"),
|
|
2766
3460
|
/**
|
|
2767
3461
|
* Create a new vocab item
|
|
2768
3462
|
* @param {string} vocabLayerId - The vocab layer ID
|
|
2769
3463
|
* @param {string} form - The vocab item form
|
|
2770
3464
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
3465
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3466
|
+
* @param {object} [opts]
|
|
3467
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3468
|
+
* @returns {Promise<Created>}
|
|
2771
3469
|
*/
|
|
2772
3470
|
create: (vocabLayerId, form, metadata, auditMessage, { id } = {}) =>
|
|
2773
3471
|
this._request("POST", "/api/v1/vocab-items", {
|
|
@@ -2778,6 +3476,8 @@ class PlaidClient {
|
|
|
2778
3476
|
* Create multiple vocab items in a single operation. Entries may target
|
|
2779
3477
|
* different vocab layers; the user must have write access to each.
|
|
2780
3478
|
* @param {Array<{vocabLayerId: string, form: string, metadata?: any}>} body - The vocab items to create
|
|
3479
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3480
|
+
* @returns {Promise<{ids: string[]}>}
|
|
2781
3481
|
*/
|
|
2782
3482
|
bulkCreate: (body, auditMessage) =>
|
|
2783
3483
|
this._request("POST", "/api/v1/vocab-items/bulk", {
|
|
@@ -2788,6 +3488,7 @@ class PlaidClient {
|
|
|
2788
3488
|
* Update many vocab items in a single operation: set forms and/or patch
|
|
2789
3489
|
* metadata.
|
|
2790
3490
|
* @param {Array<{id: string, form?: string, metadata?: Array<object>}>} body - `form` is set only when the key is present; `metadata` is a list of metadata ops, as for patchMetadata. The entries may lie in several vocab layers; the user must have write access to each. An unknown id refuses the whole update, and an id may appear only once. Only an entry whose form really changes restates the documents linking it, and a strict-mode client picks up their new versions from the response.
|
|
3491
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2791
3492
|
* @returns {Promise<{count: number}>} How many vocab items were updated.
|
|
2792
3493
|
*/
|
|
2793
3494
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -2802,6 +3503,8 @@ class PlaidClient {
|
|
|
2802
3503
|
* Every document holding one of those links has its version bumped, and
|
|
2803
3504
|
* a strict-mode client picks up their new versions from the response.
|
|
2804
3505
|
* @param {string[]} body - The vocab item IDs to delete
|
|
3506
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3507
|
+
* @returns {Promise<void>}
|
|
2805
3508
|
*/
|
|
2806
3509
|
bulkDelete: (body, auditMessage) =>
|
|
2807
3510
|
this._request("DELETE", "/api/v1/vocab-items/bulk", {
|
|
@@ -2811,6 +3514,7 @@ class PlaidClient {
|
|
|
2811
3514
|
/**
|
|
2812
3515
|
* Get a vocab item by ID
|
|
2813
3516
|
* @param {string} id - The resource ID
|
|
3517
|
+
* @returns {Promise<VocabItem>}
|
|
2814
3518
|
*/
|
|
2815
3519
|
get: (id) =>
|
|
2816
3520
|
this._request("GET", `/api/v1/vocab-items/${id}`),
|
|
@@ -2823,6 +3527,7 @@ class PlaidClient {
|
|
|
2823
3527
|
* @param {{expectedLinkCount?: number}} [options] - `expectedLinkCount`: the
|
|
2824
3528
|
* number of links the caller showed. The delete is refused with a 409 when
|
|
2825
3529
|
* the entry has any other number of links by then.
|
|
3530
|
+
* @returns {Promise<void>}
|
|
2826
3531
|
*/
|
|
2827
3532
|
delete: (id, auditMessage, { expectedLinkCount } = {}) =>
|
|
2828
3533
|
this._request("DELETE", `/api/v1/vocab-items/${id}`, {
|
|
@@ -2844,6 +3549,7 @@ class PlaidClient {
|
|
|
2844
3549
|
* Needs maintainer rights on the vocabulary.
|
|
2845
3550
|
* @param {string} survivorId - The entry that stays
|
|
2846
3551
|
* @param {string[]} loserIds - The entries merged into it
|
|
3552
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2847
3553
|
* @returns {Promise<{moved: number, duplicates: number, removed: string[]}>}
|
|
2848
3554
|
*/
|
|
2849
3555
|
merge: (survivorId, loserIds, auditMessage) =>
|
|
@@ -2858,6 +3564,8 @@ class PlaidClient {
|
|
|
2858
3564
|
* from the response.
|
|
2859
3565
|
* @param {string} id - The resource ID
|
|
2860
3566
|
* @param {string} form - The vocab item form
|
|
3567
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3568
|
+
* @returns {Promise<VocabItem>}
|
|
2861
3569
|
*/
|
|
2862
3570
|
update: (id, form, auditMessage) =>
|
|
2863
3571
|
this._request("PATCH", `/api/v1/vocab-items/${id}`, {
|
|
@@ -2867,82 +3575,24 @@ class PlaidClient {
|
|
|
2867
3575
|
};
|
|
2868
3576
|
|
|
2869
3577
|
this.relationLayers = {
|
|
2870
|
-
|
|
2871
|
-
* Shift a relation layer's display order.
|
|
2872
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2873
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
2874
|
-
*/
|
|
2875
|
-
shift: (relationLayerId, direction, auditMessage) =>
|
|
2876
|
-
this._request(
|
|
2877
|
-
"POST",
|
|
2878
|
-
`/api/v1/relation-layers/${relationLayerId}/shift`,
|
|
2879
|
-
{ auditMessage, body: bodyOf({ direction }) },
|
|
2880
|
-
),
|
|
3578
|
+
...layerMethods(this, "relation-layers"),
|
|
2881
3579
|
/**
|
|
2882
3580
|
* Create a new relation layer.
|
|
2883
3581
|
* @param {string} spanLayerId - The span layer ID
|
|
2884
3582
|
* @param {string} name - The name
|
|
3583
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3584
|
+
* @param {object} [opts]
|
|
3585
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3586
|
+
* @returns {Promise<Created>}
|
|
2885
3587
|
*/
|
|
2886
3588
|
create: (spanLayerId, name, auditMessage, { id } = {}) =>
|
|
2887
3589
|
this._request("POST", "/api/v1/relation-layers", {
|
|
2888
3590
|
auditMessage,
|
|
2889
3591
|
body: bodyOf({ id, "span-layer-id": spanLayerId, name }),
|
|
2890
3592
|
}),
|
|
2891
|
-
|
|
2892
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
2893
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2894
|
-
* @param {string} namespace - The config namespace
|
|
2895
|
-
* @param {string} configKey - The config key
|
|
2896
|
-
* @param {any} configValue - Configuration value to set
|
|
2897
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2898
|
-
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
2899
|
-
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
2900
|
-
*/
|
|
2901
|
-
setConfig: (relationLayerId, namespace, configKey, configValue, auditMessage, options) =>
|
|
2902
|
-
this._request(
|
|
2903
|
-
"PUT",
|
|
2904
|
-
`/api/v1/relation-layers/${relationLayerId}/config/${namespace}/${configKey}`,
|
|
2905
|
-
configRequest(auditMessage, configValue, options),
|
|
2906
|
-
),
|
|
2907
|
-
/**
|
|
2908
|
-
* Remove a configuration value for a layer.
|
|
2909
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2910
|
-
* @param {string} namespace - The config namespace
|
|
2911
|
-
* @param {string} configKey - The config key
|
|
2912
|
-
* @param {string} [auditMessage] - Audit message for this write
|
|
2913
|
-
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
2914
|
-
*/
|
|
2915
|
-
deleteConfig: (relationLayerId, namespace, configKey, auditMessage, options) =>
|
|
2916
|
-
this._request(
|
|
2917
|
-
"DELETE",
|
|
2918
|
-
`/api/v1/relation-layers/${relationLayerId}/config/${namespace}/${configKey}`,
|
|
2919
|
-
configRequest(auditMessage, undefined, options, true),
|
|
2920
|
-
),
|
|
3593
|
+
...configMethods(this, "relation-layers"),
|
|
2921
3594
|
...constraintMethods(this, "relation-layers"),
|
|
2922
|
-
|
|
2923
|
-
* Get a relation layer by ID.
|
|
2924
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2925
|
-
*/
|
|
2926
|
-
get: (relationLayerId) =>
|
|
2927
|
-
this._request("GET", `/api/v1/relation-layers/${relationLayerId}`),
|
|
2928
|
-
/**
|
|
2929
|
-
* Delete a relation layer.
|
|
2930
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2931
|
-
*/
|
|
2932
|
-
delete: (relationLayerId, auditMessage) =>
|
|
2933
|
-
this._request("DELETE", `/api/v1/relation-layers/${relationLayerId}`, {
|
|
2934
|
-
auditMessage,
|
|
2935
|
-
}),
|
|
2936
|
-
/**
|
|
2937
|
-
* Update a relation layer's name.
|
|
2938
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
2939
|
-
* @param {string} name - The name
|
|
2940
|
-
*/
|
|
2941
|
-
update: (relationLayerId, name, auditMessage) =>
|
|
2942
|
-
this._request("PATCH", `/api/v1/relation-layers/${relationLayerId}`, {
|
|
2943
|
-
auditMessage,
|
|
2944
|
-
body: bodyOf({ name }),
|
|
2945
|
-
}),
|
|
3595
|
+
...valueSetMethods(this, "relation-layers"),
|
|
2946
3596
|
};
|
|
2947
3597
|
|
|
2948
3598
|
this.tokens = {
|
|
@@ -2963,6 +3613,10 @@ class PlaidClient {
|
|
|
2963
3613
|
* @param {number} end - End offset, exclusive (Unicode code points)
|
|
2964
3614
|
* @param {number} [precedence] - Ordering precedence
|
|
2965
3615
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
3616
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3617
|
+
* @param {object} [opts]
|
|
3618
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
3619
|
+
* @returns {Promise<Created>}
|
|
2966
3620
|
*/
|
|
2967
3621
|
create: (
|
|
2968
3622
|
tokenLayerId,
|
|
@@ -2989,6 +3643,7 @@ class PlaidClient {
|
|
|
2989
3643
|
/**
|
|
2990
3644
|
* Get a token.
|
|
2991
3645
|
* @param {string} tokenId - The token ID
|
|
3646
|
+
* @returns {Promise<Token>}
|
|
2992
3647
|
*/
|
|
2993
3648
|
get: (tokenId) =>
|
|
2994
3649
|
this._request("GET", `/api/v1/tokens/${tokenId}`),
|
|
@@ -2996,6 +3651,8 @@ class PlaidClient {
|
|
|
2996
3651
|
* Delete a token and remove it from any spans. If this causes a span to
|
|
2997
3652
|
* have no remaining tokens, the span will also be deleted.
|
|
2998
3653
|
* @param {string} tokenId - The token ID
|
|
3654
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3655
|
+
* @returns {Promise<void>}
|
|
2999
3656
|
*/
|
|
3000
3657
|
delete: (tokenId, auditMessage) =>
|
|
3001
3658
|
this._request("DELETE", `/api/v1/tokens/${tokenId}`, { auditMessage }),
|
|
@@ -3008,6 +3665,8 @@ class PlaidClient {
|
|
|
3008
3665
|
* to leave unchanged; pass a number to set; pass null explicitly to
|
|
3009
3666
|
* CLEAR it (revert to no explicit ordering). bodyOf keeps null but
|
|
3010
3667
|
* drops undefined, so the three cases map correctly to the server.
|
|
3668
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3669
|
+
* @returns {Promise<Token>}
|
|
3011
3670
|
*/
|
|
3012
3671
|
update: (tokenId, begin, end, precedence, auditMessage) =>
|
|
3013
3672
|
this._request("PATCH", `/api/v1/tokens/${tokenId}`, {
|
|
@@ -3017,6 +3676,7 @@ class PlaidClient {
|
|
|
3017
3676
|
/**
|
|
3018
3677
|
* Create multiple tokens in a single operation.
|
|
3019
3678
|
* @param {Array} body - The request body
|
|
3679
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3020
3680
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
3021
3681
|
*/
|
|
3022
3682
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -3024,12 +3684,15 @@ class PlaidClient {
|
|
|
3024
3684
|
/**
|
|
3025
3685
|
* Delete multiple tokens in a single operation. Provide an array of IDs.
|
|
3026
3686
|
* @param {Array} body - The request body
|
|
3687
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3688
|
+
* @returns {Promise<void>}
|
|
3027
3689
|
*/
|
|
3028
3690
|
bulkDelete: (body, auditMessage) =>
|
|
3029
3691
|
this._request("DELETE", "/api/v1/tokens/bulk", { auditMessage, body }),
|
|
3030
3692
|
/**
|
|
3031
3693
|
* Update many tokens in a single operation: patch metadata.
|
|
3032
3694
|
* @param {Array} body - Objects of the shape `{id, metadata}`. `metadata` is a list of metadata ops, as for patchMetadata. The tokens may lie in several documents of one project; every document touched has its version bumped and every new version comes back in `X-Document-Versions` (past fifty documents, only their number, in `X-Document-Versions-Omitted`). A `document-version` precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.
|
|
3695
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3033
3696
|
* @returns {Promise<{count: number}>} How many tokens were updated.
|
|
3034
3697
|
*/
|
|
3035
3698
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -3038,8 +3701,9 @@ class PlaidClient {
|
|
|
3038
3701
|
body,
|
|
3039
3702
|
}),
|
|
3040
3703
|
/**
|
|
3041
|
-
* Split a token at a Unicode code-point offset.
|
|
3042
|
-
*
|
|
3704
|
+
* Split a token at a Unicode code-point offset. A new token is made for one
|
|
3705
|
+
* half and its ID is returned. The original token keeps its ID, spans and
|
|
3706
|
+
* vocab-links on the left half, or on the right half with `keep: "right"`.
|
|
3043
3707
|
* @param {string} tokenId - The token ID
|
|
3044
3708
|
* @param {number} position - Code-point offset to split at (strictly between begin and end)
|
|
3045
3709
|
* @param {string} [auditMessage] - Audit message for this write
|
|
@@ -3050,6 +3714,7 @@ class PlaidClient {
|
|
|
3050
3714
|
* half. A relation layer whose relations must stay inside one token
|
|
3051
3715
|
* of this layer declares a same-ancestor constraint, and the server deletes
|
|
3052
3716
|
* the relations the split leaves crossing in the same transaction.
|
|
3717
|
+
* @returns {Promise<Created>}
|
|
3053
3718
|
*/
|
|
3054
3719
|
split: (
|
|
3055
3720
|
tokenId,
|
|
@@ -3068,6 +3733,8 @@ class PlaidClient {
|
|
|
3068
3733
|
* merged extent must not engulf a third token.
|
|
3069
3734
|
* @param {string} tokenId - The anchor token ID
|
|
3070
3735
|
* @param {string} otherTokenId - The other token to merge in
|
|
3736
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3737
|
+
* @returns {Promise<Token>}
|
|
3071
3738
|
*/
|
|
3072
3739
|
merge: (tokenId, otherTokenId, auditMessage) =>
|
|
3073
3740
|
this._request("POST", `/api/v1/tokens/${tokenId}/merge`, {
|
|
@@ -3081,49 +3748,24 @@ class PlaidClient {
|
|
|
3081
3748
|
* @param {string} tokenId - The token ID
|
|
3082
3749
|
* @param {number} [begin] - New start offset, inclusive (Unicode code points)
|
|
3083
3750
|
* @param {number} [end] - New end offset, exclusive (Unicode code points)
|
|
3751
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3752
|
+
* @returns {Promise<Token>}
|
|
3084
3753
|
*/
|
|
3085
3754
|
shift: (tokenId, begin, end, auditMessage) =>
|
|
3086
3755
|
this._request("POST", `/api/v1/tokens/${tokenId}/shift`, {
|
|
3087
3756
|
auditMessage,
|
|
3088
3757
|
body: bodyOf({ begin, end }),
|
|
3089
3758
|
}),
|
|
3090
|
-
|
|
3091
|
-
* Replace all metadata for a token.
|
|
3092
|
-
* @param {string} tokenId - The token ID
|
|
3093
|
-
* @param {any} body - The request body
|
|
3094
|
-
*/
|
|
3095
|
-
setMetadata: (tokenId, body, auditMessage) =>
|
|
3096
|
-
this._request("PUT", `/api/v1/tokens/${tokenId}/metadata`, {
|
|
3097
|
-
auditMessage,
|
|
3098
|
-
rawBody: body,
|
|
3099
|
-
}),
|
|
3100
|
-
/**
|
|
3101
|
-
* Remove all metadata from a token.
|
|
3102
|
-
* @param {string} tokenId - The token ID
|
|
3103
|
-
*/
|
|
3104
|
-
deleteMetadata: (tokenId, auditMessage) =>
|
|
3105
|
-
this._request("DELETE", `/api/v1/tokens/${tokenId}/metadata`, {
|
|
3106
|
-
auditMessage,
|
|
3107
|
-
}),
|
|
3108
|
-
/**
|
|
3109
|
-
* Edit metadata for a token with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
3110
|
-
* @param {string} tokenId - The token ID
|
|
3111
|
-
* @param {Array<{op: string, path: string[], value?: any}>} body - The metadata ops
|
|
3112
|
-
*/
|
|
3113
|
-
patchMetadata: (tokenId, body, auditMessage) =>
|
|
3114
|
-
this._request("PATCH", `/api/v1/tokens/${tokenId}/metadata`, {
|
|
3115
|
-
auditMessage,
|
|
3116
|
-
rawBody: body,
|
|
3117
|
-
}),
|
|
3759
|
+
...metadataMethods(this, "tokens"),
|
|
3118
3760
|
};
|
|
3119
3761
|
|
|
3120
3762
|
this.messages = {
|
|
3121
3763
|
/**
|
|
3122
3764
|
* Open a Server-Sent Events stream for a project.
|
|
3123
3765
|
* @param {string} projectId - The UUID of the project to listen to
|
|
3124
|
-
* @param {
|
|
3766
|
+
* @param {(eventType: string, data: any) => void | boolean} onEvent - Callback function that receives (eventType, data). If it returns true, listening will stop.
|
|
3125
3767
|
* @param {string} [path] - Stream path under baseUrl (defaults to the project /listen bus; service channels pass their own).
|
|
3126
|
-
* @returns {
|
|
3768
|
+
* @returns {SSEConnection} SSE connection object with .close() and .getStats() methods
|
|
3127
3769
|
*/
|
|
3128
3770
|
listen: (projectId, onEvent, path) =>
|
|
3129
3771
|
createSSEConnection(this, projectId, onEvent, path),
|
|
@@ -3143,7 +3785,7 @@ class PlaidClient {
|
|
|
3143
3785
|
* queues, so a message queued after the writes goes out after them.
|
|
3144
3786
|
* @param {string} projectId - The UUID of the project to send to
|
|
3145
3787
|
* @param {any} data - The message data to send
|
|
3146
|
-
* @returns {Promise<
|
|
3788
|
+
* @returns {Promise<{success: boolean, message: string}>}
|
|
3147
3789
|
*/
|
|
3148
3790
|
sendMessage: (projectId, data) =>
|
|
3149
3791
|
this._request("POST", `/api/v1/projects/${projectId}/message`, {
|
|
@@ -3156,7 +3798,7 @@ class PlaidClient {
|
|
|
3156
3798
|
* connected services carry `online: true`; previously-seen offline ones
|
|
3157
3799
|
* carry `online: false` plus a `lastSeenAt` stamp.
|
|
3158
3800
|
* @param {string} projectId - The UUID of the project to query
|
|
3159
|
-
* @returns {Promise<
|
|
3801
|
+
* @returns {Promise<DiscoveredService[]>} Array of discovered service information
|
|
3160
3802
|
*/
|
|
3161
3803
|
discoverServices: (projectId) => discoverServices(this, projectId),
|
|
3162
3804
|
|
|
@@ -3176,14 +3818,14 @@ class PlaidClient {
|
|
|
3176
3818
|
* The registration reopens its channel whenever it drops, so a server
|
|
3177
3819
|
* restart needs no service restart.
|
|
3178
3820
|
* @param {string} projectId - The UUID of the project to serve
|
|
3179
|
-
* @param {
|
|
3180
|
-
* @param {
|
|
3821
|
+
* @param {ServiceInfo} serviceInfo - Service information {serviceId, serviceName, description}
|
|
3822
|
+
* @param {(data: any, responseHelper: ResponseHelper) => void} onServiceRequest - Callback (data, responseHelper)
|
|
3181
3823
|
* @param {Object} [extras] - Optional additional service metadata
|
|
3182
|
-
* @param {
|
|
3824
|
+
* @param {(event: ServiceStatusEvent, projectId: string, detail?: string) => void} [onStatus] - Optional callback (event, projectId, detail) for
|
|
3183
3825
|
* connection transitions: 'registered', 'reconnected', 'disconnected',
|
|
3184
3826
|
* and 'stopped' when the server refuses the channel for good, which
|
|
3185
3827
|
* ends the registration
|
|
3186
|
-
* @returns {
|
|
3828
|
+
* @returns {ServiceRegistration} Service registration with .stop(), .isRunning(), .isConnected()
|
|
3187
3829
|
*/
|
|
3188
3830
|
serve: (projectId, serviceInfo, onServiceRequest, extras, onStatus) =>
|
|
3189
3831
|
serve(this, projectId, serviceInfo, onServiceRequest, extras, onStatus),
|
|
@@ -3204,7 +3846,7 @@ class PlaidClient {
|
|
|
3204
3846
|
* @param {number} [timeout=10000] - Timeout in ms
|
|
3205
3847
|
* @param {function} [onProgress] - Called with each progress payload {percent, message}
|
|
3206
3848
|
* @param {AbortSignal} [signal] - Abort to stop waiting; rejects with an AbortError
|
|
3207
|
-
* @param {
|
|
3849
|
+
* @param {{requestId?: string, onAccepted?: (requestId: string) => void, projectIds?: string[], noOperation?: boolean}} [opts] - {requestId, onAccepted, projectIds, noOperation}
|
|
3208
3850
|
* @returns {Promise<any>} The service's result
|
|
3209
3851
|
*/
|
|
3210
3852
|
requestService: (
|
|
@@ -3270,6 +3912,11 @@ class PlaidClient {
|
|
|
3270
3912
|
cancelServiceRequest(this, projectId, requestId),
|
|
3271
3913
|
};
|
|
3272
3914
|
|
|
3915
|
+
// outOfBand: a query is a read that travels as a POST (see the note at
|
|
3916
|
+
// the top of http.js). Made on a batch it goes over the wire like any
|
|
3917
|
+
// read, and it never joins a logical operation. It waits past core's own
|
|
3918
|
+
// query limit, so a query too broad answers with core's 408, and a 503
|
|
3919
|
+
// from core's full queue of large queries is not retried.
|
|
3273
3920
|
/**
|
|
3274
3921
|
* Run a query over every project you can read.
|
|
3275
3922
|
*
|
|
@@ -3301,16 +3948,11 @@ class PlaidClient {
|
|
|
3301
3948
|
*
|
|
3302
3949
|
* @param {Object} body - The query AST ({find, where, scope?, limit?, orderBy?,
|
|
3303
3950
|
* return?, bindings?}).
|
|
3304
|
-
* @returns {Promise<
|
|
3951
|
+
* @returns {Promise<QueryResult>} For 'ids'/'entities': {columns, results, count, truncated}.
|
|
3305
3952
|
* For 'count': {return: 'count', count, truncated}, where `truncated` says
|
|
3306
3953
|
* the count stopped at the server's cap. Entity cells are full entity
|
|
3307
3954
|
* objects (same shape as the GET endpoints).
|
|
3308
3955
|
*/
|
|
3309
|
-
// outOfBand: a query is a read that travels as a POST (see the note at
|
|
3310
|
-
// the top of http.js). Made on a batch it goes over the wire like any
|
|
3311
|
-
// read, and it never joins a logical operation. It waits past core's own
|
|
3312
|
-
// query limit, so a query too broad answers with core's 408, and a 503
|
|
3313
|
-
// from core's full queue of large queries is not retried.
|
|
3314
3956
|
this.query = (body) =>
|
|
3315
3957
|
this._request("POST", "/api/v1/query", {
|
|
3316
3958
|
body,
|
|
@@ -3326,12 +3968,14 @@ class PlaidClient {
|
|
|
3326
3968
|
/**
|
|
3327
3969
|
* Get a logical-operation group (its label + creator).
|
|
3328
3970
|
* @param {string} id - The group id
|
|
3971
|
+
* @returns {Promise<OperationGroup>}
|
|
3329
3972
|
*/
|
|
3330
3973
|
get: (id) => this._request("GET", `/api/v1/operation-groups/${id}`),
|
|
3331
3974
|
/**
|
|
3332
3975
|
* Relabel a logical-operation group after the fact. Owner or admin only.
|
|
3333
3976
|
* @param {string} id - The group id
|
|
3334
3977
|
* @param {string|null} message - The new label
|
|
3978
|
+
* @returns {Promise<OperationGroup>}
|
|
3335
3979
|
*/
|
|
3336
3980
|
update: (id, message) =>
|
|
3337
3981
|
this._request("PATCH", `/api/v1/operation-groups/${id}`, {
|
|
@@ -3349,11 +3993,13 @@ class PlaidClient {
|
|
|
3349
3993
|
* A comment outlives its anchor: deleting the entity does not delete
|
|
3350
3994
|
* the comment. `anchorLabel` is the caption shown once that happens
|
|
3351
3995
|
* ("Gloss of ktab, sentence 4"), so pass what the comment is about.
|
|
3352
|
-
* @param {
|
|
3996
|
+
* @param {CommentableType} entityType - What kind of thing is being commented on
|
|
3353
3997
|
* @param {string} entityId - The commented entity's id
|
|
3354
3998
|
* @param {string} body - The comment text (1..10000 characters)
|
|
3355
3999
|
* @param {object} [opts]
|
|
3356
4000
|
* @param {string} [opts.anchorLabel] - What the comment is about, in words (at most 200 characters)
|
|
4001
|
+
* @param {string} [opts.id] - The new comment's id, minted by the caller (see uuidv7)
|
|
4002
|
+
* @returns {Promise<Comment>}
|
|
3357
4003
|
*/
|
|
3358
4004
|
create: (entityType, entityId, body, { anchorLabel, id } = {}) =>
|
|
3359
4005
|
this._request("POST", "/api/v1/comments", {
|
|
@@ -3371,6 +4017,7 @@ class PlaidClient {
|
|
|
3371
4017
|
/**
|
|
3372
4018
|
* Read one comment.
|
|
3373
4019
|
* @param {string} id - The comment id
|
|
4020
|
+
* @returns {Promise<Comment>}
|
|
3374
4021
|
*/
|
|
3375
4022
|
get: (id) => this._request("GET", `/api/v1/comments/${id}`),
|
|
3376
4023
|
/**
|
|
@@ -3378,6 +4025,7 @@ class PlaidClient {
|
|
|
3378
4025
|
* maintainers, not admins. Sets `edited` on the comment.
|
|
3379
4026
|
* @param {string} id - The comment id
|
|
3380
4027
|
* @param {string} body - The replacement text
|
|
4028
|
+
* @returns {Promise<Comment>}
|
|
3381
4029
|
*/
|
|
3382
4030
|
update: (id, body) =>
|
|
3383
4031
|
this._request("PATCH", `/api/v1/comments/${id}`, {
|
|
@@ -3388,6 +4036,7 @@ class PlaidClient {
|
|
|
3388
4036
|
* Delete a comment. The author may delete their own; a project
|
|
3389
4037
|
* maintainer (or admin) may delete any.
|
|
3390
4038
|
* @param {string} id - The comment id
|
|
4039
|
+
* @returns {Promise<void>}
|
|
3391
4040
|
*/
|
|
3392
4041
|
delete: (id) =>
|
|
3393
4042
|
this._request("DELETE", `/api/v1/comments/${id}`, { noOperation: true }),
|
|
@@ -3399,6 +4048,7 @@ class PlaidClient {
|
|
|
3399
4048
|
* @param {string} [filters.documentId] - Every comment anywhere in one document
|
|
3400
4049
|
* @param {string} [filters.entityType] - With entityId, one entity's thread
|
|
3401
4050
|
* @param {string} [filters.entityId] - With entityType, one entity's thread
|
|
4051
|
+
* @returns {Promise<Comment[]>}
|
|
3402
4052
|
*/
|
|
3403
4053
|
list: (projectId, { documentId, entityType, entityId } = {}) =>
|
|
3404
4054
|
listAll(this, `/api/v1/projects/${projectId}/comments`, {
|
|
@@ -3417,7 +4067,7 @@ class PlaidClient {
|
|
|
3417
4067
|
* @param {string} [opts.documentId] - Every comment anywhere in one document
|
|
3418
4068
|
* @param {string} [opts.entityType] - With entityId, one entity's thread
|
|
3419
4069
|
* @param {string} [opts.entityId] - With entityType, one entity's thread
|
|
3420
|
-
* @returns {Promise<
|
|
4070
|
+
* @returns {Promise<Page<Comment>>}
|
|
3421
4071
|
*/
|
|
3422
4072
|
listPage: (
|
|
3423
4073
|
projectId,
|
|
@@ -3440,7 +4090,7 @@ class PlaidClient {
|
|
|
3440
4090
|
* @param {string} [opts.documentId] - Every comment anywhere in one document
|
|
3441
4091
|
* @param {string} [opts.entityType] - With entityId, one entity's thread
|
|
3442
4092
|
* @param {string} [opts.entityId] - With entityType, one entity's thread
|
|
3443
|
-
* @returns {AsyncGenerator<
|
|
4093
|
+
* @returns {AsyncGenerator<Comment[]>}
|
|
3444
4094
|
*/
|
|
3445
4095
|
iterPages: (
|
|
3446
4096
|
projectId,
|
|
@@ -3463,7 +4113,7 @@ class PlaidClient {
|
|
|
3463
4113
|
* The response is NOT key-transformed: its keys are entity ids, and
|
|
3464
4114
|
* camelCasing would mangle the hyphens in a UUID.
|
|
3465
4115
|
* @param {string} projectId - The project to read
|
|
3466
|
-
* @param {
|
|
4116
|
+
* @param {{documentId?: string, entityType?: CommentableType, entityId?: string}} [filters] - Same filters as list()
|
|
3467
4117
|
* @returns {Promise<Object<string, number>>}
|
|
3468
4118
|
*/
|
|
3469
4119
|
counts: (projectId, { documentId, entityType, entityId } = {}) =>
|
|
@@ -3482,6 +4132,7 @@ class PlaidClient {
|
|
|
3482
4132
|
* @param {string} vocabId - The vocab layer to read
|
|
3483
4133
|
* @param {object} [filters]
|
|
3484
4134
|
* @param {string} [filters.entityId] - One entry's thread
|
|
4135
|
+
* @returns {Promise<Comment[]>}
|
|
3485
4136
|
*/
|
|
3486
4137
|
listInVocab: (vocabId, { entityId } = {}) =>
|
|
3487
4138
|
listAll(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3494,7 +4145,7 @@ class PlaidClient {
|
|
|
3494
4145
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
3495
4146
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
3496
4147
|
* @param {string} [opts.entityId] - One entry's thread
|
|
3497
|
-
* @returns {Promise<
|
|
4148
|
+
* @returns {Promise<Page<Comment>>}
|
|
3498
4149
|
*/
|
|
3499
4150
|
listInVocabPage: (vocabId, { limit, cursor, entityId } = {}) =>
|
|
3500
4151
|
listPage(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3509,7 +4160,7 @@ class PlaidClient {
|
|
|
3509
4160
|
* @param {object} [opts]
|
|
3510
4161
|
* @param {string} [opts.entityId] - One entry's thread
|
|
3511
4162
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
3512
|
-
* @returns {AsyncGenerator<
|
|
4163
|
+
* @returns {AsyncGenerator<Comment[]>}
|
|
3513
4164
|
*/
|
|
3514
4165
|
iterInVocabPages: (vocabId, { entityId, pageSize } = {}) =>
|
|
3515
4166
|
iterPages(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3537,15 +4188,15 @@ class PlaidClient {
|
|
|
3537
4188
|
|
|
3538
4189
|
this.events = {
|
|
3539
4190
|
/**
|
|
3540
|
-
* Record one research-telemetry event (core
|
|
3541
|
-
*
|
|
4191
|
+
* Record one research-telemetry event (plaid-core sql/client_event.clj).
|
|
4192
|
+
* Fire and forget: the event is buffered and sent with
|
|
3542
4193
|
* others every ten seconds, at fifty events, and when the page is
|
|
3543
4194
|
* hidden. A batch that fails is dropped, nothing is retried, and
|
|
3544
4195
|
* nothing is ever thrown. Sends nothing for a project whose switch
|
|
3545
4196
|
* (`config.plaid.research.telemetry`) is off, which it reads from the
|
|
3546
4197
|
* project itself. `suggestion.shown` is recorded once per target,
|
|
3547
4198
|
* field and value in a page session.
|
|
3548
|
-
* @param {
|
|
4199
|
+
* @param {ClientEventType} type - The event type
|
|
3549
4200
|
* @param {object} fields
|
|
3550
4201
|
* @param {string} fields.projectId - The project it happened in
|
|
3551
4202
|
* @param {string} [fields.documentId] - The document it happened in
|
|
@@ -3556,6 +4207,7 @@ class PlaidClient {
|
|
|
3556
4207
|
record: (type, fields) => recorderFor(this).record(this, type, fields),
|
|
3557
4208
|
/**
|
|
3558
4209
|
* Send what the recorder holds now, rather than at its next interval.
|
|
4210
|
+
* @returns {void}
|
|
3559
4211
|
*/
|
|
3560
4212
|
flush: () => recorderFor(this).flush(),
|
|
3561
4213
|
/**
|
|
@@ -3563,6 +4215,7 @@ class PlaidClient {
|
|
|
3563
4215
|
* maintainer changed it. Turning it off drops what is buffered.
|
|
3564
4216
|
* @param {string} projectId - The project
|
|
3565
4217
|
* @param {boolean} on - Whether telemetry is on
|
|
4218
|
+
* @returns {void}
|
|
3566
4219
|
*/
|
|
3567
4220
|
setEnabled: (projectId, on) => recorderFor(this).setEnabled(projectId, on),
|
|
3568
4221
|
/**
|
|
@@ -3587,6 +4240,7 @@ class PlaidClient {
|
|
|
3587
4240
|
* @param {string|string[]} [filters.types] - Only these types
|
|
3588
4241
|
* @param {string} [filters.startTime] - Only events the server stamped at or after this instant
|
|
3589
4242
|
* @param {string} [filters.endTime] - Only events the server stamped at or before this instant
|
|
4243
|
+
* @returns {Promise<ClientEvent[]>}
|
|
3590
4244
|
*/
|
|
3591
4245
|
list: (projectId, { types, startTime, endTime } = {}) =>
|
|
3592
4246
|
listAll(this, `/api/v1/projects/${projectId}/events`, {
|
|
@@ -3601,7 +4255,7 @@ class PlaidClient {
|
|
|
3601
4255
|
* @param {string|string[]} [opts.types] - Only these types
|
|
3602
4256
|
* @param {string} [opts.startTime] - Only events at or after this instant
|
|
3603
4257
|
* @param {string} [opts.endTime] - Only events at or before this instant
|
|
3604
|
-
* @returns {Promise<
|
|
4258
|
+
* @returns {Promise<Page<ClientEvent>>}
|
|
3605
4259
|
*/
|
|
3606
4260
|
listPage: (
|
|
3607
4261
|
projectId,
|
|
@@ -3620,7 +4274,7 @@ class PlaidClient {
|
|
|
3620
4274
|
* @param {string|string[]} [opts.types] - Only these types
|
|
3621
4275
|
* @param {string} [opts.startTime] - Only events at or after this instant
|
|
3622
4276
|
* @param {string} [opts.endTime] - Only events at or before this instant
|
|
3623
|
-
* @returns {AsyncGenerator<
|
|
4277
|
+
* @returns {AsyncGenerator<ClientEvent[]>}
|
|
3624
4278
|
*/
|
|
3625
4279
|
iterPages: (projectId, { pageSize, types, startTime, endTime } = {}) =>
|
|
3626
4280
|
iterPages(this, `/api/v1/projects/${projectId}/events`, {
|
|
@@ -3641,6 +4295,9 @@ class PlaidClient {
|
|
|
3641
4295
|
* @param {object} [opts]
|
|
3642
4296
|
* @param {string} [opts.body] - The Markdown text (up to 20000 characters; may be empty)
|
|
3643
4297
|
* @param {boolean} [opts.pinned] - Send this one to the assistant in full on every turn
|
|
4298
|
+
* @param {string} [opts.id] - The new guideline's id, minted by the caller (see uuidv7)
|
|
4299
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4300
|
+
* @returns {Promise<{id: string}>}
|
|
3644
4301
|
*/
|
|
3645
4302
|
create: (projectId, title, { body, pinned, id } = {}, auditMessage) =>
|
|
3646
4303
|
this._request("POST", `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3650,6 +4307,7 @@ class PlaidClient {
|
|
|
3650
4307
|
/**
|
|
3651
4308
|
* Read one guideline, Markdown body included.
|
|
3652
4309
|
* @param {string} id - The guideline id
|
|
4310
|
+
* @returns {Promise<Guideline>}
|
|
3653
4311
|
*/
|
|
3654
4312
|
get: (id) => this._request("GET", `/api/v1/guidelines/${id}`),
|
|
3655
4313
|
/**
|
|
@@ -3666,6 +4324,8 @@ class PlaidClient {
|
|
|
3666
4324
|
* @param {string} [changes.body] - The new Markdown text
|
|
3667
4325
|
* @param {boolean} [changes.pinned] - Whether the assistant always gets it in full
|
|
3668
4326
|
* @param {string} [changes.expectedUpdatedAt] - Write only if this is still the stored updatedAt
|
|
4327
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4328
|
+
* @returns {Promise<Guideline>}
|
|
3669
4329
|
*/
|
|
3670
4330
|
update: (
|
|
3671
4331
|
id,
|
|
@@ -3680,6 +4340,8 @@ class PlaidClient {
|
|
|
3680
4340
|
/**
|
|
3681
4341
|
* Delete a guideline.
|
|
3682
4342
|
* @param {string} id - The guideline id
|
|
4343
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4344
|
+
* @returns {Promise<void>}
|
|
3683
4345
|
*/
|
|
3684
4346
|
delete: (id, auditMessage) =>
|
|
3685
4347
|
this._request("DELETE", `/api/v1/guidelines/${id}`, { auditMessage }),
|
|
@@ -3694,6 +4356,7 @@ class PlaidClient {
|
|
|
3694
4356
|
* @param {string} projectId - The project to read
|
|
3695
4357
|
* @param {object} [opts]
|
|
3696
4358
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
4359
|
+
* @returns {Promise<Guideline[]>}
|
|
3697
4360
|
*/
|
|
3698
4361
|
list: (projectId, { includeBodies } = {}) =>
|
|
3699
4362
|
listAll(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3706,7 +4369,7 @@ class PlaidClient {
|
|
|
3706
4369
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
3707
4370
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
3708
4371
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
3709
|
-
* @returns {Promise<
|
|
4372
|
+
* @returns {Promise<Page<Guideline>>}
|
|
3710
4373
|
*/
|
|
3711
4374
|
listPage: (projectId, { limit, cursor, includeBodies } = {}) =>
|
|
3712
4375
|
listPage(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3720,7 +4383,7 @@ class PlaidClient {
|
|
|
3720
4383
|
* @param {object} [opts]
|
|
3721
4384
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
3722
4385
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
3723
|
-
* @returns {AsyncGenerator<
|
|
4386
|
+
* @returns {AsyncGenerator<Guideline[]>}
|
|
3724
4387
|
*/
|
|
3725
4388
|
iterPages: (projectId, { pageSize, includeBodies } = {}) =>
|
|
3726
4389
|
iterPages(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3732,6 +4395,7 @@ class PlaidClient {
|
|
|
3732
4395
|
|
|
3733
4396
|
// --- Core methods ---
|
|
3734
4397
|
|
|
4398
|
+
/** @private */
|
|
3735
4399
|
async _request(method, path, options = {}) {
|
|
3736
4400
|
return makeRequest(this, method, path, options);
|
|
3737
4401
|
}
|
|
@@ -3794,7 +4458,7 @@ class PlaidClient {
|
|
|
3794
4458
|
* matching end, and the outer numbering resumes after it.
|
|
3795
4459
|
*
|
|
3796
4460
|
* @param {string} message - Human label for the operation.
|
|
3797
|
-
* @param {
|
|
4461
|
+
* @param {{groupId?: string, kind?: OperationKind, ref?: string, keys?: KeySeed, minted?: Iterable<string>}} [opts] - Optional `{ groupId, kind, ref, keys, minted }`. `groupId` adopts an existing group id instead of minting one (a service joining the requester's operation; `requestService` propagates an open operation to the service automatically). Python's is `group_id`. `kind`, `ref` and `keys` are described above. `minted` is the ids the operation mints for what it creates: a create refused 409 id-taken for one of them was made by an earlier send of the operation, and answers as made.
|
|
3798
4462
|
* @returns {string} The operation's group id.
|
|
3799
4463
|
*/
|
|
3800
4464
|
beginOperation(message, { groupId, kind, ref, keys, minted } = {}) {
|
|
@@ -3843,7 +4507,7 @@ class PlaidClient {
|
|
|
3843
4507
|
* A seed for the Idempotency-Keys of a logical operation that may be run
|
|
3844
4508
|
* again from the top (see beginOperation's `keys`). Keep it with the work
|
|
3845
4509
|
* it belongs to and pass it to every run.
|
|
3846
|
-
* @returns {
|
|
4510
|
+
* @returns {KeySeed}
|
|
3847
4511
|
*/
|
|
3848
4512
|
keySeed() {
|
|
3849
4513
|
return { seed: uuidv7(), stamps: new Map() };
|
|
@@ -3898,10 +4562,11 @@ class PlaidClient {
|
|
|
3898
4562
|
*
|
|
3899
4563
|
* await client.withOperation('Import ELAN corpus', run, { kind: 'import', ref: 'format:elan' });
|
|
3900
4564
|
*
|
|
4565
|
+
* @template T
|
|
3901
4566
|
* @param {string} message - Human label for the operation.
|
|
3902
|
-
* @param {
|
|
3903
|
-
* @param {
|
|
3904
|
-
* @returns {Promise<
|
|
4567
|
+
* @param {(setMessage: (msg: string) => void) => T | Promise<T>} fn - The work to run; receives `setMessage(msg)` to refine the label once the outcome is known.
|
|
4568
|
+
* @param {{kind?: OperationKind, ref?: string, groupId?: string, keys?: KeySeed, minted?: Iterable<string>}} [opts] - Optional `{ kind, ref, groupId, keys, minted }`, as for beginOperation. `kind` is one of `assistant-plan`, `service-run`, `import`, `bulk-edit`, `guess-adoption`, `repair` or `review`, and any other throws before `fn` runs.
|
|
4569
|
+
* @returns {Promise<T>} Whatever `fn` resolves to.
|
|
3905
4570
|
*/
|
|
3906
4571
|
async withOperation(message, fn, { kind, ref, groupId, keys, minted } = {}) {
|
|
3907
4572
|
this.beginOperation(message, { kind, ref, groupId, keys, minted });
|
|
@@ -3956,10 +4621,15 @@ class PlaidClient {
|
|
|
3956
4621
|
* parents created earlier in the same batch, and any op's failure rolls the
|
|
3957
4622
|
* whole batch back. A batch is not nestable. Prefer `batched()`, which
|
|
3958
4623
|
* submits or aborts for you.
|
|
3959
|
-
*
|
|
4624
|
+
*
|
|
4625
|
+
* `timeout` (ms, or null for none) is how long each of this batch's
|
|
4626
|
+
* requests may wait for its answer, in place of the client's
|
|
4627
|
+
* `batchTimeout`, for this batch alone.
|
|
4628
|
+
* @param {{timeout?: number | null}} [options]
|
|
4629
|
+
* @returns {PlaidBatch} the batch view
|
|
3960
4630
|
*/
|
|
3961
|
-
batch() {
|
|
3962
|
-
return openBatch(this);
|
|
4631
|
+
batch({ timeout } = {}) {
|
|
4632
|
+
return openBatch(this, { timeout });
|
|
3963
4633
|
}
|
|
3964
4634
|
|
|
3965
4635
|
/**
|
|
@@ -3968,8 +4638,10 @@ class PlaidClient {
|
|
|
3968
4638
|
* recased: the headers keep the server's spelling. `key` is the request's
|
|
3969
4639
|
* Idempotency-Key: a batch whose answer is lost (no response, 502, 504) is
|
|
3970
4640
|
* sent again under it, and answered from the first send if that landed.
|
|
4641
|
+
* `timeout` is this request's, in place of `batchTimeout` when given.
|
|
4642
|
+
* @private
|
|
3971
4643
|
*/
|
|
3972
|
-
async _postBatch(url, body, { key = uuidv7() } = {}) {
|
|
4644
|
+
async _postBatch(url, body, { key = uuidv7(), timeout } = {}) {
|
|
3973
4645
|
const fetchOptions = {
|
|
3974
4646
|
method: "POST",
|
|
3975
4647
|
headers: {
|
|
@@ -3985,7 +4657,9 @@ class PlaidClient {
|
|
|
3985
4657
|
// repeating it is safe. A fresh timeout signal per attempt — an
|
|
3986
4658
|
// AbortSignal.timeout stays aborted once it fires.
|
|
3987
4659
|
const attempt = async () => {
|
|
3988
|
-
const signal = timeoutSignal(
|
|
4660
|
+
const signal = timeoutSignal(
|
|
4661
|
+
timeout !== undefined ? timeout : this.batchTimeout,
|
|
4662
|
+
);
|
|
3989
4663
|
let res;
|
|
3990
4664
|
try {
|
|
3991
4665
|
res = await fetch(
|
|
@@ -4099,12 +4773,14 @@ class PlaidClient {
|
|
|
4099
4773
|
* });
|
|
4100
4774
|
*
|
|
4101
4775
|
* A write made on `client` inside `fn` is not part of the batch: it goes
|
|
4102
|
-
* over the wire at once, as it would anywhere else. See `batch()
|
|
4103
|
-
*
|
|
4104
|
-
* @
|
|
4776
|
+
* over the wire at once, as it would anywhere else. See `batch()`, which
|
|
4777
|
+
* also says what `timeout` does.
|
|
4778
|
+
* @param {(batch: PlaidBatch) => (void | Promise<void>)} fn
|
|
4779
|
+
* @param {{timeout?: number | null}} [options]
|
|
4780
|
+
* @returns {Promise<any[]>}
|
|
4105
4781
|
*/
|
|
4106
|
-
async batched(fn) {
|
|
4107
|
-
const batch = this.batch();
|
|
4782
|
+
async batched(fn, { timeout } = {}) {
|
|
4783
|
+
const batch = this.batch({ timeout });
|
|
4108
4784
|
try {
|
|
4109
4785
|
await fn(batch);
|
|
4110
4786
|
} catch (e) {
|
|
@@ -4119,20 +4795,22 @@ class PlaidClient {
|
|
|
4119
4795
|
* instance — for a launcher or status page checking whether a server is up
|
|
4120
4796
|
* before anyone logs in.
|
|
4121
4797
|
* @param {string} baseUrl - The API base URL
|
|
4122
|
-
* @param {
|
|
4798
|
+
* @param {{timeout?: number | null}} [options]
|
|
4799
|
+
* @returns {Promise<ServerHealth>}
|
|
4123
4800
|
*/
|
|
4124
4801
|
static async health(baseUrl, options = {}) {
|
|
4125
|
-
return
|
|
4802
|
+
return anonymousRequest("GET", baseUrl, "/health", undefined, options);
|
|
4126
4803
|
}
|
|
4127
4804
|
|
|
4128
4805
|
/**
|
|
4129
4806
|
* The limits this server enforces, with NO authentication and no client
|
|
4130
4807
|
* instance. See the instance method for the shape.
|
|
4131
4808
|
* @param {string} baseUrl - The API base URL
|
|
4132
|
-
* @param {
|
|
4809
|
+
* @param {{timeout?: number | null}} [options]
|
|
4810
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
4133
4811
|
*/
|
|
4134
4812
|
static async info(baseUrl, options = {}) {
|
|
4135
|
-
return
|
|
4813
|
+
return anonymousRequest("GET", baseUrl, "/api/v1/info", undefined, options);
|
|
4136
4814
|
}
|
|
4137
4815
|
|
|
4138
4816
|
/**
|
|
@@ -4163,7 +4841,7 @@ class PlaidClient {
|
|
|
4163
4841
|
* @returns {Promise<{kind: string, status: string, expiresAt: string, projectName?: string, projectRole?: string, email?: string, grantAdmin: boolean}>}
|
|
4164
4842
|
*/
|
|
4165
4843
|
static async lookupInvite(baseUrl, code, options = {}) {
|
|
4166
|
-
return
|
|
4844
|
+
return anonymousRequest("POST", baseUrl, "/api/v1/invites/lookup", { code }, options);
|
|
4167
4845
|
}
|
|
4168
4846
|
|
|
4169
4847
|
/**
|
|
@@ -4192,10 +4870,16 @@ class PlaidClient {
|
|
|
4192
4870
|
{ email, password, displayName } = {},
|
|
4193
4871
|
options = {},
|
|
4194
4872
|
) {
|
|
4195
|
-
const data = await
|
|
4873
|
+
const data = await anonymousRequest(
|
|
4874
|
+
"POST",
|
|
4196
4875
|
baseUrl,
|
|
4197
4876
|
"/api/v1/invites/redeem",
|
|
4198
|
-
bodyOf({
|
|
4877
|
+
bodyOf({
|
|
4878
|
+
code,
|
|
4879
|
+
email: normalizeUserId(email),
|
|
4880
|
+
password,
|
|
4881
|
+
"display-name": displayName,
|
|
4882
|
+
}),
|
|
4199
4883
|
options,
|
|
4200
4884
|
);
|
|
4201
4885
|
return {
|
|
@@ -4220,35 +4904,14 @@ class PlaidClient {
|
|
|
4220
4904
|
*/
|
|
4221
4905
|
static async login(baseUrl, userId, password, options = {}) {
|
|
4222
4906
|
baseUrl = baseUrl.replace(/\/$/, "");
|
|
4223
|
-
const
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS,
|
|
4232
|
-
);
|
|
4233
|
-
if (signal) fetchOptions.signal = signal;
|
|
4234
|
-
|
|
4235
|
-
const response = await fetch(url, fetchOptions);
|
|
4236
|
-
if (!response.ok) {
|
|
4237
|
-
throw makeHttpError(
|
|
4238
|
-
response,
|
|
4239
|
-
await parseErrorBody(response),
|
|
4240
|
-
url,
|
|
4241
|
-
"POST",
|
|
4242
|
-
);
|
|
4243
|
-
}
|
|
4244
|
-
|
|
4245
|
-
const data = await response.json();
|
|
4246
|
-
const token = data.token || "";
|
|
4247
|
-
return new PlaidClient(baseUrl, token, options);
|
|
4248
|
-
} catch (error) {
|
|
4249
|
-
if (error.status) throw error;
|
|
4250
|
-
throw makeNetworkError(error, url, "POST");
|
|
4251
|
-
}
|
|
4907
|
+
const data = await anonymousRequest(
|
|
4908
|
+
"POST",
|
|
4909
|
+
baseUrl,
|
|
4910
|
+
"/api/v1/login",
|
|
4911
|
+
{ "user-id": normalizeUserId(userId), password },
|
|
4912
|
+
options,
|
|
4913
|
+
);
|
|
4914
|
+
return new PlaidClient(baseUrl, data.token || "", options);
|
|
4252
4915
|
}
|
|
4253
4916
|
}
|
|
4254
4917
|
|
|
@@ -4262,23 +4925,156 @@ class PlaidClient {
|
|
|
4262
4925
|
* neither body is re-cased.
|
|
4263
4926
|
*/
|
|
4264
4927
|
function configRequest(auditMessage, value, options, isDelete = false) {
|
|
4928
|
+
// The answer is a 204 with no body, so there is nothing to keep un-recased.
|
|
4265
4929
|
if (!options || !Object.hasOwn(options, "expected")) {
|
|
4266
|
-
return isDelete
|
|
4267
|
-
? { auditMessage, skipResponseTransform: true }
|
|
4268
|
-
: { auditMessage, rawBody: value, skipResponseTransform: true };
|
|
4930
|
+
return isDelete ? { auditMessage } : { auditMessage, rawBody: value };
|
|
4269
4931
|
}
|
|
4270
4932
|
const expected = options.expected === undefined ? null : options.expected;
|
|
4271
4933
|
return {
|
|
4272
4934
|
auditMessage,
|
|
4273
4935
|
queryParams: { "if-unchanged": true },
|
|
4274
4936
|
rawBody: isDelete ? { expected } : { expected, value },
|
|
4275
|
-
|
|
4937
|
+
};
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
/**
|
|
4941
|
+
* The three metadata methods of a bundle whose entities carry metadata, on
|
|
4942
|
+
* `/api/v1/<kind>/<id>/metadata`. Metadata is opaque, so no body is re-cased.
|
|
4943
|
+
* Each answers the entity as its GET does.
|
|
4944
|
+
* @template {keyof MetadataRecords} K
|
|
4945
|
+
* @param {PlaidClient} client
|
|
4946
|
+
* @param {K} kind
|
|
4947
|
+
*/
|
|
4948
|
+
function metadataMethods(client, kind) {
|
|
4949
|
+
const path = (id) => `/api/v1/${kind}/${id}/metadata`;
|
|
4950
|
+
return {
|
|
4951
|
+
/**
|
|
4952
|
+
* Replace all metadata of an entity. Keys left out of the new map are removed.
|
|
4953
|
+
* @param {string} id - The entity's ID
|
|
4954
|
+
* @param {object} body - The new metadata map
|
|
4955
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4956
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
4957
|
+
*/
|
|
4958
|
+
setMetadata: (id, body, auditMessage) =>
|
|
4959
|
+
client._request("PUT", path(id), { auditMessage, rawBody: body }),
|
|
4960
|
+
/**
|
|
4961
|
+
* Remove all metadata from an entity.
|
|
4962
|
+
* @param {string} id - The entity's ID
|
|
4963
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4964
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
4965
|
+
*/
|
|
4966
|
+
deleteMetadata: (id, auditMessage) => client._request("DELETE", path(id), { auditMessage }),
|
|
4967
|
+
/**
|
|
4968
|
+
* Edit an entity's metadata with a list of ops applied in order, in one operation. `{op: 'set', path, value}` writes value at path, creating missing objects along it; `{op: 'delete', path}` removes the key at path (a no-op when absent). A path is a non-empty array of keys, the first a top-level key. A path through a non-object is refused (400). See metadataOps and applyMetadataOps.
|
|
4969
|
+
* @param {string} id - The entity's ID
|
|
4970
|
+
* @param {MetadataOp[]} body - The metadata ops
|
|
4971
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4972
|
+
* @returns {Promise<MetadataRecords[K]>}
|
|
4973
|
+
*/
|
|
4974
|
+
patchMetadata: (id, body, auditMessage) =>
|
|
4975
|
+
client._request("PATCH", path(id), { auditMessage, rawBody: body }),
|
|
4976
|
+
};
|
|
4977
|
+
}
|
|
4978
|
+
|
|
4979
|
+
/**
|
|
4980
|
+
* The two config methods of a bundle whose records carry editor config, on
|
|
4981
|
+
* `/api/v1/<kind>/<id>/config/<namespace>/<key>`.
|
|
4982
|
+
* @param {PlaidClient} client
|
|
4983
|
+
* @param {string} kind
|
|
4984
|
+
*/
|
|
4985
|
+
function configMethods(client, kind) {
|
|
4986
|
+
const path = (id, namespace, configKey) => `/api/v1/${kind}/${id}/config/${namespace}/${configKey}`;
|
|
4987
|
+
return {
|
|
4988
|
+
/**
|
|
4989
|
+
* Set a configuration value in an editor namespace.
|
|
4990
|
+
* @param {string} id - The record's ID
|
|
4991
|
+
* @param {string} namespace - The config namespace
|
|
4992
|
+
* @param {string} configKey - The config key
|
|
4993
|
+
* @param {any} configValue - Configuration value to set
|
|
4994
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4995
|
+
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
4996
|
+
* was absent). The write is then refused with a 409 when the stored value differs. A value the key
|
|
4997
|
+
* already holds is not written and leaves no audit row, so the value read, expecting it, states in a
|
|
4998
|
+
* batch that the key is unchanged.
|
|
4999
|
+
* @returns {Promise<void>}
|
|
5000
|
+
*/
|
|
5001
|
+
setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
|
|
5002
|
+
client._request(
|
|
5003
|
+
"PUT",
|
|
5004
|
+
path(id, namespace, configKey),
|
|
5005
|
+
configRequest(auditMessage, configValue, options),
|
|
5006
|
+
),
|
|
5007
|
+
/**
|
|
5008
|
+
* Remove a configuration value.
|
|
5009
|
+
* @param {string} id - The record's ID
|
|
5010
|
+
* @param {string} namespace - The config namespace
|
|
5011
|
+
* @param {string} configKey - The config key
|
|
5012
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
5013
|
+
* @param {{expected?: any}} [options] - `expected`, as on setConfig. A key that is absent is not
|
|
5014
|
+
* written, so expecting null states in a batch that it is still absent.
|
|
5015
|
+
* @returns {Promise<void>}
|
|
5016
|
+
*/
|
|
5017
|
+
deleteConfig: (id, namespace, configKey, auditMessage, options) =>
|
|
5018
|
+
client._request(
|
|
5019
|
+
"DELETE",
|
|
5020
|
+
path(id, namespace, configKey),
|
|
5021
|
+
configRequest(auditMessage, undefined, options, true),
|
|
5022
|
+
),
|
|
5023
|
+
};
|
|
5024
|
+
}
|
|
5025
|
+
|
|
5026
|
+
/**
|
|
5027
|
+
* The get, delete, update and shift methods of the four layer bundles, on
|
|
5028
|
+
* `/api/v1/<kind>/<layerId>`.
|
|
5029
|
+
* @template {keyof LayerRecords} K
|
|
5030
|
+
* @param {PlaidClient} client
|
|
5031
|
+
* @param {K} kind
|
|
5032
|
+
*/
|
|
5033
|
+
function layerMethods(client, kind) {
|
|
5034
|
+
const path = (layerId) => `/api/v1/${kind}/${layerId}`;
|
|
5035
|
+
return {
|
|
5036
|
+
/**
|
|
5037
|
+
* Get a layer by ID.
|
|
5038
|
+
* @param {string} layerId - The layer ID
|
|
5039
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
5040
|
+
*/
|
|
5041
|
+
get: (layerId) => client._request("GET", path(layerId)),
|
|
5042
|
+
/**
|
|
5043
|
+
* Delete a layer.
|
|
5044
|
+
* @param {string} layerId - The layer ID
|
|
5045
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
5046
|
+
* @returns {Promise<void>}
|
|
5047
|
+
*/
|
|
5048
|
+
delete: (layerId, auditMessage) => client._request("DELETE", path(layerId), { auditMessage }),
|
|
5049
|
+
/**
|
|
5050
|
+
* Rename a layer.
|
|
5051
|
+
* @param {string} layerId - The layer ID
|
|
5052
|
+
* @param {string} name - The new name
|
|
5053
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
5054
|
+
* @returns {Promise<LayerRecords[K]>}
|
|
5055
|
+
*/
|
|
5056
|
+
update: (layerId, name, auditMessage) =>
|
|
5057
|
+
client._request("PATCH", path(layerId), { auditMessage, body: bodyOf({ name }) }),
|
|
5058
|
+
/**
|
|
5059
|
+
* Move a layer one place in its display order.
|
|
5060
|
+
* @param {string} layerId - The layer ID
|
|
5061
|
+
* @param {string} direction - The direction ("up" or "down")
|
|
5062
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
5063
|
+
* @returns {Promise<void>}
|
|
5064
|
+
*/
|
|
5065
|
+
shift: (layerId, direction, auditMessage) =>
|
|
5066
|
+
client._request("POST", `${path(layerId)}/shift`, {
|
|
5067
|
+
auditMessage,
|
|
5068
|
+
body: bodyOf({ direction }),
|
|
5069
|
+
}),
|
|
4276
5070
|
};
|
|
4277
5071
|
}
|
|
4278
5072
|
|
|
4279
5073
|
/**
|
|
4280
5074
|
* The four layer-constraint methods of a layer bundle, on `/api/v1/<kind>`.
|
|
4281
5075
|
* See constraints.js and the core manual, "Layer constraints".
|
|
5076
|
+
* @param {PlaidClient} client
|
|
5077
|
+
* @param {string} kind
|
|
4282
5078
|
*/
|
|
4283
5079
|
function constraintMethods(client, kind) {
|
|
4284
5080
|
const base = (layerId) => `/api/v1/${kind}/${layerId}/constraints`;
|
|
@@ -4289,12 +5085,12 @@ function constraintMethods(client, kind) {
|
|
|
4289
5085
|
* stored data breaks them (see violationsOf).
|
|
4290
5086
|
* @param {string} layerId - The layer ID
|
|
4291
5087
|
* @param {string} namespace - The declaring app's namespace, e.g. "igt"
|
|
4292
|
-
* @param {
|
|
5088
|
+
* @param {LayerConstraint[]} constraints - e.g. [{type: "single-span"}, {type: "value-set", values: ["N"]}]
|
|
4293
5089
|
* @param {string} [auditMessage] - Audit message for this write
|
|
4294
5090
|
* @param {{expected?: Array<object>|null}} [options] - `expected`: the list read for this
|
|
4295
5091
|
* namespace (null when it was absent). The write is then refused with a 409 when the stored
|
|
4296
5092
|
* list differs.
|
|
4297
|
-
* @returns {Promise<{constraints:
|
|
5093
|
+
* @returns {Promise<{constraints: Record<string, LayerConstraint[]>}>} the layer's whole constraint map
|
|
4298
5094
|
*/
|
|
4299
5095
|
setConstraints: (layerId, namespace, constraints, auditMessage, options) =>
|
|
4300
5096
|
client._request("PUT", `${base(layerId)}/${namespace}`, {
|
|
@@ -4307,6 +5103,7 @@ function constraintMethods(client, kind) {
|
|
|
4307
5103
|
* @param {string} namespace - The declaring app's namespace
|
|
4308
5104
|
* @param {string} [auditMessage] - Audit message for this write
|
|
4309
5105
|
* @param {{expected?: Array<object>|null}} [options] - `expected`, as on setConstraints.
|
|
5106
|
+
* @returns {Promise<void>}
|
|
4310
5107
|
*/
|
|
4311
5108
|
deleteConstraints: (layerId, namespace, auditMessage, options) => {
|
|
4312
5109
|
const body = constraintsBody(undefined, options);
|
|
@@ -4319,11 +5116,17 @@ function constraintMethods(client, kind) {
|
|
|
4319
5116
|
* The violations the given constraints would meet in this layer's stored
|
|
4320
5117
|
* data. Writes nothing.
|
|
4321
5118
|
* @param {string} layerId - The layer ID
|
|
4322
|
-
* @param {
|
|
4323
|
-
* @
|
|
5119
|
+
* @param {LayerConstraint[]} constraints - The list to check
|
|
5120
|
+
* @param {{limit?: number}} [options] - `limit`: how many violations to list (default 100)
|
|
5121
|
+
* @returns {Promise<{violations: ConstraintViolation[], violationCount: number}>}
|
|
4324
5122
|
*/
|
|
4325
|
-
checkConstraints: (layerId, constraints) =>
|
|
4326
|
-
client._request("POST", `${base(layerId)}/check`, {
|
|
5123
|
+
checkConstraints: (layerId, constraints, options) =>
|
|
5124
|
+
client._request("POST", `${base(layerId)}/check`, {
|
|
5125
|
+
body: {
|
|
5126
|
+
constraints,
|
|
5127
|
+
...(options?.limit != null ? { limit: options.limit } : {}),
|
|
5128
|
+
},
|
|
5129
|
+
}),
|
|
4327
5130
|
/**
|
|
4328
5131
|
* Apply the remedies of the given constraints' remediable types
|
|
4329
5132
|
* (coextensive, single-span, single-link, same-ancestor) to every
|
|
@@ -4332,10 +5135,10 @@ function constraintMethods(client, kind) {
|
|
|
4332
5135
|
* ask. A document another user holds the lock on is left as it is and
|
|
4333
5136
|
* listed under `locked`.
|
|
4334
5137
|
* @param {string} layerId - The layer ID
|
|
4335
|
-
* @param {
|
|
5138
|
+
* @param {LayerConstraint[]} constraints - The list to repair for
|
|
4336
5139
|
* @param {string} [auditMessage] - Audit message for this write
|
|
4337
5140
|
* @param {{document?: string}} [options]
|
|
4338
|
-
* @returns {Promise<{repaired:
|
|
5141
|
+
* @returns {Promise<{repaired: ConstraintRepair[], locked: Array<{document: string, lockedBy: string}>, violations: ConstraintViolation[], violationCount: number}>}
|
|
4339
5142
|
*/
|
|
4340
5143
|
repairConstraints: (layerId, constraints, auditMessage, options) =>
|
|
4341
5144
|
client._request("POST", `${base(layerId)}/repair`, {
|
|
@@ -4345,6 +5148,38 @@ function constraintMethods(client, kind) {
|
|
|
4345
5148
|
};
|
|
4346
5149
|
}
|
|
4347
5150
|
|
|
5151
|
+
/**
|
|
5152
|
+
* The value-set read of a span or relation layer bundle, on
|
|
5153
|
+
* `/api/v1/<kind>/<layerId>/constraints/refused-values`.
|
|
5154
|
+
* @param {PlaidClient} client
|
|
5155
|
+
* @param {string} kind
|
|
5156
|
+
*/
|
|
5157
|
+
function valueSetMethods(client, kind) {
|
|
5158
|
+
return {
|
|
5159
|
+
/**
|
|
5160
|
+
* The stored values of this layer that a value-set rule would refuse,
|
|
5161
|
+
* before it is declared: exactly what a declaration of it would meet,
|
|
5162
|
+
* with the rule's exemptions (an unconfirmed machine value, a value an
|
|
5163
|
+
* import, a document copy or a restore set). Writes nothing, and a
|
|
5164
|
+
* reader may ask.
|
|
5165
|
+
* @param {string} layerId - The layer ID
|
|
5166
|
+
* @param {ValueSetRule} rule - The list as a value-set declaration takes it
|
|
5167
|
+
* @param {{limit?: number}} [options] - `limit`: how many values to list, the most frequent first
|
|
5168
|
+
* (default 20, at most 100)
|
|
5169
|
+
* @returns {Promise<RefusedValues>}
|
|
5170
|
+
*/
|
|
5171
|
+
refusedValues: (layerId, rule, options) =>
|
|
5172
|
+
client._request("POST", `/api/v1/${kind}/${layerId}/constraints/refused-values`, {
|
|
5173
|
+
body: bodyOf({
|
|
5174
|
+
values: rule?.values,
|
|
5175
|
+
delimiters: rule?.delimiters,
|
|
5176
|
+
parts: rule?.parts,
|
|
5177
|
+
limit: options?.limit,
|
|
5178
|
+
}),
|
|
5179
|
+
}),
|
|
5180
|
+
};
|
|
5181
|
+
}
|
|
5182
|
+
|
|
4348
5183
|
export default PlaidClient;
|
|
4349
5184
|
export { PlaidClient };
|
|
4350
5185
|
export { valueSetAllows, violationsOf } from "./constraints.js";
|
|
@@ -4357,6 +5192,7 @@ export {
|
|
|
4357
5192
|
cpSlicer,
|
|
4358
5193
|
utf16ToCp,
|
|
4359
5194
|
cpIndexOf,
|
|
5195
|
+
composeText,
|
|
4360
5196
|
} from "./codepoint.js";
|
|
4361
5197
|
export { composeTextEdits, gapsToOps, applyTextOps } from "./textEdits.js";
|
|
4362
5198
|
export {
|
|
@@ -4385,6 +5221,9 @@ export {
|
|
|
4385
5221
|
// request, and `critical()` holds that off around writes. See ./services.js
|
|
4386
5222
|
// and the manual, "Stopping a request".
|
|
4387
5223
|
export { ServiceCancelled, createCancelScope } from "./services.js";
|
|
5224
|
+
// The two messages a write that got no answer is reported with: one the
|
|
5225
|
+
// server may have saved, one never sent. See ./services.js.
|
|
5226
|
+
export { UNKNOWN_OUTCOME, UNREACHABLE } from "./services.js";
|
|
4388
5227
|
// Who asked for a service run, named in its History label and in what it
|
|
4389
5228
|
// stores. See ./requester.js.
|
|
4390
5229
|
export { REQUESTED_BY, makeRequester, requesterOf } from "./requester.js";
|
|
@@ -4431,6 +5270,6 @@ export { createdId, createdIds } from "./created.js";
|
|
|
4431
5270
|
// UUIDv7 ids for what a client creates. See ./ids.js and the manual,
|
|
4432
5271
|
// "Retrying a write". uuidv4 works where crypto.randomUUID does not (a page
|
|
4433
5272
|
// served over plain HTTP from any host but localhost).
|
|
4434
|
-
export { uuidv4, uuidv7 } from "./ids.js";
|
|
5273
|
+
export { uuidv4, uuidv7, normalizeUserId } from "./ids.js";
|
|
4435
5274
|
export { wasReplayed } from "./replayed.js";
|
|
4436
5275
|
export { MAX_BATCH_OPS };
|