@larc-iu/plaid-client 0.2.0-alpha.23 → 0.2.0-alpha.26
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 +3814 -0
- package/src/index.js +939 -699
- 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 +67 -10
- package/src/sse.d.ts +30 -0
- package/src/sse.js +11 -0
- package/src/textEdits.d.ts +59 -0
- package/src/textEdits.js +26 -1
- 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,294 @@ 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
|
+
* Entities that can carry a comment.
|
|
166
|
+
* @typedef {"document" | "text" | "token" | "span" | "relation" | "vocab-item"} CommentableType
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* One comment. `projectId` and `documentId` are null for a comment on a
|
|
171
|
+
* vocabulary entry, and `vocabLayerId` is set only for one. `anchorLabel` is
|
|
172
|
+
* what the comment is about, in words, captured when it was posted: a
|
|
173
|
+
* comment outlives its anchor. `edited` is true once the body has been
|
|
174
|
+
* rewritten since it was posted.
|
|
175
|
+
* @typedef {object} Comment
|
|
176
|
+
* @property {string} id
|
|
177
|
+
* @property {string | null} projectId
|
|
178
|
+
* @property {string | null} documentId
|
|
179
|
+
* @property {string | null} vocabLayerId
|
|
180
|
+
* @property {CommentableType} entityType
|
|
181
|
+
* @property {string} entityId
|
|
182
|
+
* @property {string | null} anchorLabel
|
|
183
|
+
* @property {string} authorId
|
|
184
|
+
* @property {string} body
|
|
185
|
+
* @property {string} createdAt
|
|
186
|
+
* @property {string} updatedAt
|
|
187
|
+
* @property {boolean} edited
|
|
188
|
+
*/
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* One entry in a project's annotation manual. `body` is present on a single
|
|
192
|
+
* read and on a list made with `includeBodies`, `bodyChars` on a list made
|
|
193
|
+
* without. `pinned`: the assistant is given this one in full on every turn.
|
|
194
|
+
* @typedef {object} Guideline
|
|
195
|
+
* @property {string} id
|
|
196
|
+
* @property {string} projectId
|
|
197
|
+
* @property {string} title
|
|
198
|
+
* @property {string} [body]
|
|
199
|
+
* @property {number} [bodyChars]
|
|
200
|
+
* @property {boolean} pinned
|
|
201
|
+
* @property {string} createdAt
|
|
202
|
+
* @property {string} updatedAt
|
|
203
|
+
*/
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* A research-telemetry event type.
|
|
207
|
+
* @typedef {"suggestion.shown" | "suggestion.adopted" | "suggestion.dismissed" | "plan.opened"} ClientEventType
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* One research-telemetry event as the server stores it.
|
|
212
|
+
* @typedef {object} ClientEvent
|
|
213
|
+
* @property {number} id
|
|
214
|
+
* @property {string} projectId
|
|
215
|
+
* @property {string | null} documentId
|
|
216
|
+
* @property {string} userId
|
|
217
|
+
* @property {ClientEventType} type
|
|
218
|
+
* @property {string | null} targetId
|
|
219
|
+
* @property {Record<string, any> | null} data
|
|
220
|
+
* @property {string | null} clientTs
|
|
221
|
+
* @property {string} ts
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* An invite. `kind` is "signup" or "password-reset", `status` "active",
|
|
226
|
+
* "used", "expired", "revoked" or "inactive".
|
|
227
|
+
* @typedef {object} Invite
|
|
228
|
+
* @property {string} id
|
|
229
|
+
* @property {string} kind
|
|
230
|
+
* @property {string} status
|
|
231
|
+
* @property {string} createdBy
|
|
232
|
+
* @property {string} createdAt
|
|
233
|
+
* @property {string} expiresAt
|
|
234
|
+
* @property {number} maxUses
|
|
235
|
+
* @property {number} uses
|
|
236
|
+
* @property {string | null} revokedAt
|
|
237
|
+
* @property {string | null} note
|
|
238
|
+
* @property {string | null} targetUserId
|
|
239
|
+
* @property {boolean} grantAdmin
|
|
240
|
+
* @property {string | null} projectId
|
|
241
|
+
* @property {string | null} projectRole
|
|
242
|
+
*/
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Server limits, as reported by /api/v1/info. An unset limit is absent.
|
|
246
|
+
* `passwordMinLength` is the shortest password the server accepts on any
|
|
247
|
+
* path, `lockExpirationMs` how long a document lock is held before it lapses.
|
|
248
|
+
* @typedef {object} ServerLimits
|
|
249
|
+
* @property {number} [mediaFileBytes]
|
|
250
|
+
* @property {number} [jsonBodyBytes]
|
|
251
|
+
* @property {number} [batchOperations]
|
|
252
|
+
* @property {number} [metadataDepth]
|
|
253
|
+
* @property {number} [metadataKeyCount]
|
|
254
|
+
* @property {number} [metadataStringLength]
|
|
255
|
+
* @property {number} [metadataTotalBytes]
|
|
256
|
+
* @property {number} [userDataValueBytes]
|
|
257
|
+
* @property {number} [guidelineTitleLength]
|
|
258
|
+
* @property {number} [guidelineBodyLength]
|
|
259
|
+
* @property {number} [passwordMinLength]
|
|
260
|
+
* @property {number} [lockExpirationMs]
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* @typedef {object} ServerHealth
|
|
265
|
+
* @property {boolean} ok
|
|
266
|
+
* @property {string} version
|
|
267
|
+
* @property {number} uptimeMs
|
|
268
|
+
* @property {{dbSizeMb?: number, auditRows?: number, error?: string}} audit
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* @typedef {{ip: string, userId?: string, failures: number, limit: number, blocked: boolean}} RateLimitBucket
|
|
273
|
+
*/
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @typedef {object} RateLimitSnapshot
|
|
277
|
+
* @property {number} windowMs
|
|
278
|
+
* @property {RateLimitBucket[]} logins
|
|
279
|
+
* @property {RateLimitBucket[]} ips
|
|
280
|
+
* @property {RateLimitBucket[]} invites
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @typedef {object} BackupStatus
|
|
285
|
+
* @property {boolean} enabled
|
|
286
|
+
* @property {string} directory
|
|
287
|
+
* @property {number} retention
|
|
288
|
+
* @property {string} time
|
|
289
|
+
* @property {Array<{name: string, bytes: number, modified: string}>} backups
|
|
290
|
+
* @property {boolean} [ok]
|
|
291
|
+
*/
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @typedef {object} ServerReport
|
|
295
|
+
* @property {string} version
|
|
296
|
+
* @property {{uptimeMs: number, startedAt: string, java: string, heapUsed: number, heapMax: number}} jvm
|
|
297
|
+
* @property {{path: string | null, bytes: number | null, walBytes: number | null,
|
|
298
|
+
* slowQueryThresholdMs?: number, journalMode?: string, maxPoolSize?: number,
|
|
299
|
+
* tables: Record<string, number | null>}} database
|
|
300
|
+
* @property {{directory: string, files: number, bytes: number, orphans: number, orphanBytes: number}} media
|
|
301
|
+
* @property {BackupStatus} backup
|
|
302
|
+
* @property {Record<string, any>} settings
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The live log. Requests and events are buffered separately, so a burst of
|
|
307
|
+
* requests cannot evict an error. In each, `matched` is how many passed the
|
|
308
|
+
* filters (which may exceed what `entries` holds) and `held` how many are
|
|
309
|
+
* buffered in total. `file` is the configured log file's absolute path, or
|
|
310
|
+
* null when logging to stdout.
|
|
311
|
+
* @typedef {object} LiveLog
|
|
312
|
+
* @property {{entries: object[], matched: number, held: number, capacity: number,
|
|
313
|
+
* stats: Record<string, number | null>}} requests
|
|
314
|
+
* @property {{entries: object[], matched: number, held: number, capacity: number,
|
|
315
|
+
* byLevel: Record<string, number>}} events
|
|
316
|
+
* @property {string | null} file
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* An open server-sent-events stream. Once CLOSED, `error` says why: null when
|
|
321
|
+
* this side closed it. A refused stream's error carries the HTTP `status`,
|
|
322
|
+
* and one the server ended is named "StreamClosed".
|
|
323
|
+
* @typedef {object} SSEConnection
|
|
324
|
+
* @property {() => void} close
|
|
325
|
+
* @property {() => any} getStats
|
|
326
|
+
* @property {number} readyState
|
|
327
|
+
* @property {(Error & {status?: number}) | null} error
|
|
328
|
+
*/
|
|
329
|
+
|
|
42
330
|
// Helper: normalize a document-read `layers` filter to the wire's
|
|
43
331
|
// comma-separated form. Accepts an array of layer ids or a ready-made string;
|
|
44
332
|
// anything empty becomes undefined so no `?layers=` is sent at all.
|
|
@@ -70,48 +358,22 @@ function bodyOf(obj) {
|
|
|
70
358
|
return result;
|
|
71
359
|
}
|
|
72
360
|
|
|
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 = {}) {
|
|
361
|
+
// A request with no Authorization header: login, invite lookup and redemption
|
|
362
|
+
// (POST, with a body), /health and /api/v1/info (GET). Deliberately does not
|
|
363
|
+
// go through PlaidClient._request: these are called before any client exists,
|
|
364
|
+
// which is the whole point.
|
|
365
|
+
async function anonymousRequest(method, baseUrl, path, body, options = {}) {
|
|
111
366
|
const base = baseUrl.replace(/\/$/, "");
|
|
112
367
|
const url = `${base}${path}`;
|
|
113
368
|
try {
|
|
114
|
-
const fetchOptions =
|
|
369
|
+
const fetchOptions =
|
|
370
|
+
method === "GET"
|
|
371
|
+
? { method }
|
|
372
|
+
: {
|
|
373
|
+
method,
|
|
374
|
+
headers: { "Content-Type": "application/json" },
|
|
375
|
+
body: JSON.stringify(body),
|
|
376
|
+
};
|
|
115
377
|
const signal = timeoutSignal(
|
|
116
378
|
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS,
|
|
117
379
|
);
|
|
@@ -119,12 +381,12 @@ async function anonymousGet(baseUrl, path, options = {}) {
|
|
|
119
381
|
|
|
120
382
|
const response = await fetch(url, fetchOptions);
|
|
121
383
|
if (!response.ok) {
|
|
122
|
-
throw makeHttpError(response, await parseErrorBody(response), url,
|
|
384
|
+
throw makeHttpError(response, await parseErrorBody(response), url, method);
|
|
123
385
|
}
|
|
124
386
|
return transformResponse(await response.json());
|
|
125
387
|
} catch (error) {
|
|
126
388
|
if (error.status) throw error;
|
|
127
|
-
throw makeNetworkError(error, url,
|
|
389
|
+
throw makeNetworkError(error, url, method);
|
|
128
390
|
}
|
|
129
391
|
}
|
|
130
392
|
|
|
@@ -155,6 +417,12 @@ const OPERATION_KINDS = [
|
|
|
155
417
|
*/
|
|
156
418
|
export const BACKUP_TIMEOUT_MS = 30 * 60 * 1000;
|
|
157
419
|
|
|
420
|
+
/** An avatar token is minted again once less than this is left of it. */
|
|
421
|
+
const AVATAR_TOKEN_RENEW_MS = 10 * 60 * 1000;
|
|
422
|
+
// An avatar token minted this recently is the renewal a caller asking for one
|
|
423
|
+
// wanted.
|
|
424
|
+
const AVATAR_TOKEN_FRESH_MS = 5 * 1000;
|
|
425
|
+
|
|
158
426
|
/**
|
|
159
427
|
* A batch is the client with a different `_request` (see `queueRequest`) and
|
|
160
428
|
* its own copy of the bundles. Everything else resolves to the client through
|
|
@@ -316,13 +584,12 @@ class PlaidClient {
|
|
|
316
584
|
* Create a new PlaidClient instance
|
|
317
585
|
* @param {string} baseUrl - The base URL for the API
|
|
318
586
|
* @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)
|
|
587
|
+
* @param {PlaidClientOptions} [options] - Client options
|
|
322
588
|
*/
|
|
323
589
|
constructor(baseUrl, token, options = {}) {
|
|
324
590
|
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
325
591
|
this.token = token;
|
|
592
|
+
/** @type {number | null} */
|
|
326
593
|
this.timeout =
|
|
327
594
|
options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS;
|
|
328
595
|
// Batch submissions get a longer budget than a single request. Aborting a
|
|
@@ -330,6 +597,7 @@ class PlaidClient {
|
|
|
330
597
|
// holding the single SQLite write lock, so a client that gives up early
|
|
331
598
|
// just adds a retry on top of a write that is still in progress. Falls
|
|
332
599
|
// back to an explicit `timeout` when one was given.
|
|
600
|
+
/** @type {number | null} */
|
|
333
601
|
this.batchTimeout =
|
|
334
602
|
options.batchTimeout !== undefined
|
|
335
603
|
? options.batchTimeout
|
|
@@ -338,33 +606,90 @@ class PlaidClient {
|
|
|
338
606
|
: DEFAULT_BATCH_TIMEOUT_MS;
|
|
339
607
|
// Delays (ms) before sending a keyed write again when its answer was lost
|
|
340
608
|
// (no response, 502, 504). See the Idempotency-Key note in http.js.
|
|
609
|
+
/** @type {number[] | undefined} */
|
|
341
610
|
this.retryDelaysMs = options.retryDelaysMs;
|
|
611
|
+
/**
|
|
612
|
+
* The latest version seen for each document written through this client,
|
|
613
|
+
* keyed by document id.
|
|
614
|
+
* @type {Record<string, number>}
|
|
615
|
+
*/
|
|
342
616
|
this.documentVersions = {};
|
|
617
|
+
/**
|
|
618
|
+
* True once a response left its version list out
|
|
619
|
+
* (X-Document-Versions-Omitted, past fifty documents). Every version held
|
|
620
|
+
* was then forgotten, and the strict-mode document's is read again before
|
|
621
|
+
* the next write.
|
|
622
|
+
* @type {boolean | undefined}
|
|
623
|
+
*/
|
|
624
|
+
this.documentVersionsOmitted = undefined;
|
|
343
625
|
// The server's clock minus this machine's, in ms, from the last response
|
|
344
626
|
// with a Date header (null before one). See serverNow().
|
|
627
|
+
/** @type {number | null} */
|
|
345
628
|
this.serverClockOffsetMs = null;
|
|
629
|
+
/**
|
|
630
|
+
* The document strict mode is entered for, or null.
|
|
631
|
+
* @type {string | null}
|
|
632
|
+
*/
|
|
346
633
|
this.strictModeDocumentId = null;
|
|
347
634
|
// Set to a DocumentLockLost while a `documents.locked()` block's keep-alive
|
|
348
635
|
// has failed. Every write throws it until the block exits; see
|
|
349
636
|
// documentLock.js.
|
|
637
|
+
/** @type {import("./documentLock.js").DocumentLockLost | null} */
|
|
350
638
|
this.documentLockLost = null;
|
|
351
639
|
// The open logical operation (audit-log group), or null. While set, every
|
|
352
640
|
// write is stamped with `?group-id=` (+ `group-message`) so the audit log
|
|
353
641
|
// folds them into ONE expandable entry. See beginOperation / withOperation.
|
|
354
642
|
// Shape: { id, message, depth, written, refined, frames }, where each of
|
|
355
643
|
// `frames` is { keys, count, minted, depth, owned } and the last numbers.
|
|
644
|
+
/**
|
|
645
|
+
* @type {{id: string, message: string | null, kind: OperationKind | null,
|
|
646
|
+
* ref: string | null, depth: number, written: boolean, refined: any,
|
|
647
|
+
* frames: object[]} | null}
|
|
648
|
+
*/
|
|
356
649
|
this.operationGroup = null;
|
|
357
650
|
// Optional callback fired once (per client) when any request returns HTTP
|
|
358
651
|
// 401 — i.e. the token is missing/expired/invalid. Apps use it to discard
|
|
359
652
|
// the stored token and route back to login. See makeRequest in http.js.
|
|
653
|
+
/** @type {((error: Error) => void) | null} */
|
|
360
654
|
this.onAuthError = options.onAuthError || null;
|
|
655
|
+
/** @private */
|
|
361
656
|
this._authErrorFired = false;
|
|
362
657
|
// Who hears that a request is being sent again (onRetry).
|
|
658
|
+
/** @private */
|
|
363
659
|
this._retryListeners = new Set();
|
|
660
|
+
// GET /info as last read (`server.info()`), and whether a stream of this
|
|
661
|
+
// client dropped since.
|
|
662
|
+
/** @private */
|
|
663
|
+
this._serverFacts = { info: null, stale: false };
|
|
664
|
+
// The key frame the last beginOperation opened, or null (withOperation).
|
|
665
|
+
/** @private @type {object | null} */
|
|
666
|
+
this._openedFrame = null;
|
|
364
667
|
|
|
365
668
|
this._installResources();
|
|
366
669
|
}
|
|
367
670
|
|
|
671
|
+
/**
|
|
672
|
+
* A stream of this client ended without being closed here: the server may
|
|
673
|
+
* have restarted, with other limits.
|
|
674
|
+
* @private
|
|
675
|
+
*/
|
|
676
|
+
_noteStreamDropped() {
|
|
677
|
+
this._serverFacts.stale = true;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* A stream of this client is open. After a drop the limits are read again,
|
|
682
|
+
* once however many streams reopen.
|
|
683
|
+
* @private
|
|
684
|
+
*/
|
|
685
|
+
_noteStreamOpened() {
|
|
686
|
+
if (!this._serverFacts.stale) return;
|
|
687
|
+
this.server.refresh().catch(() => {
|
|
688
|
+
// The figures known before stand, and the next open asks again.
|
|
689
|
+
this._serverFacts.stale = true;
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
368
693
|
/**
|
|
369
694
|
* Hear every time a request is sent again: after a 503 (the database was
|
|
370
695
|
* busy), or for a keyed write after its answer was lost (no response, 502,
|
|
@@ -379,7 +704,10 @@ class PlaidClient {
|
|
|
379
704
|
return () => this._retryListeners.delete(listener);
|
|
380
705
|
}
|
|
381
706
|
|
|
382
|
-
/**
|
|
707
|
+
/**
|
|
708
|
+
* Tell every `onRetry` listener. A listener that throws is passed over.
|
|
709
|
+
* @private
|
|
710
|
+
*/
|
|
383
711
|
_noteRetry(info) {
|
|
384
712
|
for (const listener of this._retryListeners) {
|
|
385
713
|
try {
|
|
@@ -406,6 +734,7 @@ class PlaidClient {
|
|
|
406
734
|
* the client, and again on every batch it opens (see `batch()`): a batch is
|
|
407
735
|
* a view of the client with its own `_request`, so the same bundles built
|
|
408
736
|
* on it queue instead of sending.
|
|
737
|
+
* @private
|
|
409
738
|
*/
|
|
410
739
|
_installResources() {
|
|
411
740
|
this.vocabLinks = {
|
|
@@ -414,6 +743,9 @@ class PlaidClient {
|
|
|
414
743
|
* @param {string} vocabItem - The vocab item to link
|
|
415
744
|
* @param {Array} tokens - The tokens to link
|
|
416
745
|
* @param {any} [metadata] - Metadata for the link. Omit to leave unset; pass null to send JSON null.
|
|
746
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
747
|
+
* @param {object} [opts]
|
|
748
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
417
749
|
*/
|
|
418
750
|
create: (vocabItem, tokens, metadata, auditMessage, { id } = {}) =>
|
|
419
751
|
this._request("POST", "/api/v1/vocab-links", {
|
|
@@ -425,6 +757,7 @@ class PlaidClient {
|
|
|
425
757
|
* reference different vocab items, but all tokens across the call must
|
|
426
758
|
* belong to one document.
|
|
427
759
|
* @param {Array<{vocabItem: string, tokens: string[], metadata?: any}>} body - The vocab links to create
|
|
760
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
428
761
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
429
762
|
*/
|
|
430
763
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -435,40 +768,15 @@ class PlaidClient {
|
|
|
435
768
|
/**
|
|
436
769
|
* Delete multiple vocab links in a single operation. Provide an array of IDs.
|
|
437
770
|
* @param {string[]} body - The vocab link IDs to delete
|
|
771
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
772
|
+
* @returns {Promise<void>}
|
|
438
773
|
*/
|
|
439
774
|
bulkDelete: (body, auditMessage) =>
|
|
440
775
|
this._request("DELETE", "/api/v1/vocab-links/bulk", {
|
|
441
776
|
auditMessage,
|
|
442
777
|
body,
|
|
443
778
|
}),
|
|
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
|
-
}),
|
|
779
|
+
...metadataMethods(this, "vocab-links"),
|
|
472
780
|
/**
|
|
473
781
|
* Get a vocab link by ID
|
|
474
782
|
* @param {string} id - The resource ID
|
|
@@ -478,6 +786,7 @@ class PlaidClient {
|
|
|
478
786
|
/**
|
|
479
787
|
* Delete a vocab link
|
|
480
788
|
* @param {string} id - The resource ID
|
|
789
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
481
790
|
*/
|
|
482
791
|
delete: (id, auditMessage) =>
|
|
483
792
|
this._request("DELETE", `/api/v1/vocab-links/${id}`, { auditMessage }),
|
|
@@ -541,6 +850,10 @@ class PlaidClient {
|
|
|
541
850
|
* that may be long: audit() walks every page before it resolves.
|
|
542
851
|
* @param {string} id - The vocabulary ID
|
|
543
852
|
* @param {object} [opts]
|
|
853
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
854
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
855
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
856
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
544
857
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
545
858
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
546
859
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -586,9 +899,10 @@ class PlaidClient {
|
|
|
586
899
|
* @param {string} itemId - The entry ID
|
|
587
900
|
* @param {string} asOf - The moment to go back to (ISO-8601 instant),
|
|
588
901
|
* typically a history entry's `endTime`
|
|
589
|
-
* @param {
|
|
902
|
+
* @param {{dryRun?: boolean}} [options] - `{ dryRun }`: with `dryRun` true nothing
|
|
590
903
|
* is written and the summary says what would change
|
|
591
904
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
905
|
+
* @returns {Promise<VocabItemRestoreSummary>}
|
|
592
906
|
*/
|
|
593
907
|
restoreItem: (id, itemId, asOf, { dryRun } = {}, auditMessage) =>
|
|
594
908
|
this._request(
|
|
@@ -605,6 +919,7 @@ class PlaidClient {
|
|
|
605
919
|
/**
|
|
606
920
|
* Delete a vocab layer.
|
|
607
921
|
* @param {string} id - The resource ID
|
|
922
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
608
923
|
*/
|
|
609
924
|
delete: (id, auditMessage) =>
|
|
610
925
|
this._request("DELETE", `/api/v1/vocab-layers/${id}`, { auditMessage }),
|
|
@@ -612,42 +927,14 @@ class PlaidClient {
|
|
|
612
927
|
* Update a vocab layer's name.
|
|
613
928
|
* @param {string} id - The resource ID
|
|
614
929
|
* @param {string} name - The name
|
|
930
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
615
931
|
*/
|
|
616
932
|
update: (id, name, auditMessage) =>
|
|
617
933
|
this._request("PATCH", `/api/v1/vocab-layers/${id}`, {
|
|
618
934
|
auditMessage,
|
|
619
935
|
body: bodyOf({ name }),
|
|
620
936
|
}),
|
|
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
|
-
),
|
|
937
|
+
...configMethods(this, "vocab-layers"),
|
|
651
938
|
/**
|
|
652
939
|
* List all vocab layers accessible to user. Transparently follows
|
|
653
940
|
* pagination cursors and returns the full flat array.
|
|
@@ -678,6 +965,9 @@ class PlaidClient {
|
|
|
678
965
|
/**
|
|
679
966
|
* Create a new vocab layer. Note: this also registers the user as a maintainer.
|
|
680
967
|
* @param {string} name - The name
|
|
968
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
969
|
+
* @param {object} [opts]
|
|
970
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
681
971
|
*/
|
|
682
972
|
create: (name, auditMessage, { id } = {}) =>
|
|
683
973
|
this._request("POST", "/api/v1/vocab-layers", {
|
|
@@ -688,6 +978,7 @@ class PlaidClient {
|
|
|
688
978
|
* Assign a user as a maintainer for this vocab layer.
|
|
689
979
|
* @param {string} id - The resource ID
|
|
690
980
|
* @param {string} userId - The user ID
|
|
981
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
691
982
|
*/
|
|
692
983
|
addMaintainer: (id, userId, auditMessage) =>
|
|
693
984
|
this._request(
|
|
@@ -699,6 +990,7 @@ class PlaidClient {
|
|
|
699
990
|
* Remove a user's maintainer privileges for this vocab layer.
|
|
700
991
|
* @param {string} id - The resource ID
|
|
701
992
|
* @param {string} userId - The user ID
|
|
993
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
702
994
|
*/
|
|
703
995
|
removeMaintainer: (id, userId, auditMessage) =>
|
|
704
996
|
this._request(
|
|
@@ -709,38 +1001,12 @@ class PlaidClient {
|
|
|
709
1001
|
};
|
|
710
1002
|
|
|
711
1003
|
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
|
-
}),
|
|
1004
|
+
...metadataMethods(this, "relations"),
|
|
740
1005
|
/**
|
|
741
1006
|
* Update the target span of a relation.
|
|
742
1007
|
* @param {string} relationId - The relation ID
|
|
743
1008
|
* @param {string} spanId - The span ID
|
|
1009
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
744
1010
|
*/
|
|
745
1011
|
setTarget: (relationId, spanId, auditMessage) =>
|
|
746
1012
|
this._request("PUT", `/api/v1/relations/${relationId}/target`, {
|
|
@@ -756,6 +1022,7 @@ class PlaidClient {
|
|
|
756
1022
|
/**
|
|
757
1023
|
* Delete a relation.
|
|
758
1024
|
* @param {string} relationId - The relation ID
|
|
1025
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
759
1026
|
*/
|
|
760
1027
|
delete: (relationId, auditMessage) =>
|
|
761
1028
|
this._request("DELETE", `/api/v1/relations/${relationId}`, {
|
|
@@ -765,6 +1032,7 @@ class PlaidClient {
|
|
|
765
1032
|
* Update a relation's value.
|
|
766
1033
|
* @param {string} relationId - The relation ID
|
|
767
1034
|
* @param {any} value - The value
|
|
1035
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
768
1036
|
*/
|
|
769
1037
|
update: (relationId, value, auditMessage) =>
|
|
770
1038
|
this._request("PATCH", `/api/v1/relations/${relationId}`, {
|
|
@@ -775,6 +1043,7 @@ class PlaidClient {
|
|
|
775
1043
|
* Update the source span of a relation.
|
|
776
1044
|
* @param {string} relationId - The relation ID
|
|
777
1045
|
* @param {string} spanId - The span ID
|
|
1046
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
778
1047
|
*/
|
|
779
1048
|
setSource: (relationId, spanId, auditMessage) =>
|
|
780
1049
|
this._request("PUT", `/api/v1/relations/${relationId}/source`, {
|
|
@@ -790,6 +1059,9 @@ class PlaidClient {
|
|
|
790
1059
|
* @param {string} targetId - The target span ID
|
|
791
1060
|
* @param {any} value - The value
|
|
792
1061
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1062
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1063
|
+
* @param {object} [opts]
|
|
1064
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
793
1065
|
*/
|
|
794
1066
|
create: (
|
|
795
1067
|
layerId,
|
|
@@ -814,6 +1086,7 @@ class PlaidClient {
|
|
|
814
1086
|
/**
|
|
815
1087
|
* Create multiple relations in a single operation.
|
|
816
1088
|
* @param {Array} body - The request body
|
|
1089
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
817
1090
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
818
1091
|
*/
|
|
819
1092
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -821,6 +1094,8 @@ class PlaidClient {
|
|
|
821
1094
|
/**
|
|
822
1095
|
* Delete multiple relations in a single operation. Provide an array of IDs.
|
|
823
1096
|
* @param {Array} body - The request body
|
|
1097
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1098
|
+
* @returns {Promise<void>}
|
|
824
1099
|
*/
|
|
825
1100
|
bulkDelete: (body, auditMessage) =>
|
|
826
1101
|
this._request("DELETE", "/api/v1/relations/bulk", {
|
|
@@ -830,6 +1105,7 @@ class PlaidClient {
|
|
|
830
1105
|
/**
|
|
831
1106
|
* Update many relations in a single operation: set values and/or patch metadata.
|
|
832
1107
|
* @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.
|
|
1108
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
833
1109
|
* @returns {Promise<{count: number}>} How many relations were updated.
|
|
834
1110
|
*/
|
|
835
1111
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -840,81 +1116,22 @@ class PlaidClient {
|
|
|
840
1116
|
};
|
|
841
1117
|
|
|
842
1118
|
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
|
-
),
|
|
1119
|
+
...configMethods(this, "span-layers"),
|
|
873
1120
|
...constraintMethods(this, "span-layers"),
|
|
874
|
-
|
|
875
|
-
* Get a span layer by ID.
|
|
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
|
-
}),
|
|
1121
|
+
...layerMethods(this, "span-layers"),
|
|
898
1122
|
/**
|
|
899
1123
|
* Create a new span layer.
|
|
900
1124
|
* @param {string} tokenLayerId - The token layer ID
|
|
901
1125
|
* @param {string} name - The name
|
|
1126
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1127
|
+
* @param {object} [opts]
|
|
1128
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
902
1129
|
*/
|
|
903
1130
|
create: (tokenLayerId, name, auditMessage, { id } = {}) =>
|
|
904
1131
|
this._request("POST", "/api/v1/span-layers", {
|
|
905
1132
|
auditMessage,
|
|
906
1133
|
body: bodyOf({ id, "token-layer-id": tokenLayerId, name }),
|
|
907
1134
|
}),
|
|
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
1135
|
};
|
|
919
1136
|
|
|
920
1137
|
this.spans = {
|
|
@@ -922,6 +1139,7 @@ class PlaidClient {
|
|
|
922
1139
|
* Replace tokens for a span.
|
|
923
1140
|
* @param {string} spanId - The span ID
|
|
924
1141
|
* @param {Array} tokens - The tokens
|
|
1142
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
925
1143
|
*/
|
|
926
1144
|
setTokens: (spanId, tokens, auditMessage) =>
|
|
927
1145
|
this._request("PUT", `/api/v1/spans/${spanId}/tokens`, {
|
|
@@ -935,6 +1153,9 @@ class PlaidClient {
|
|
|
935
1153
|
* @param {Array} tokens - The tokens
|
|
936
1154
|
* @param {any} value - The value
|
|
937
1155
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1156
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1157
|
+
* @param {object} [opts]
|
|
1158
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
938
1159
|
*/
|
|
939
1160
|
create: (spanLayerId, tokens, value, metadata, auditMessage, { id } = {}) =>
|
|
940
1161
|
this._request("POST", "/api/v1/spans", {
|
|
@@ -956,6 +1177,7 @@ class PlaidClient {
|
|
|
956
1177
|
/**
|
|
957
1178
|
* Delete a span.
|
|
958
1179
|
* @param {string} spanId - The span ID
|
|
1180
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
959
1181
|
*/
|
|
960
1182
|
delete: (spanId, auditMessage) =>
|
|
961
1183
|
this._request("DELETE", `/api/v1/spans/${spanId}`, { auditMessage }),
|
|
@@ -963,6 +1185,7 @@ class PlaidClient {
|
|
|
963
1185
|
* Update a span's value.
|
|
964
1186
|
* @param {string} spanId - The span ID
|
|
965
1187
|
* @param {any} value - The value
|
|
1188
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
966
1189
|
*/
|
|
967
1190
|
update: (spanId, value, auditMessage) =>
|
|
968
1191
|
this._request("PATCH", `/api/v1/spans/${spanId}`, {
|
|
@@ -972,6 +1195,7 @@ class PlaidClient {
|
|
|
972
1195
|
/**
|
|
973
1196
|
* Create multiple spans in a single operation.
|
|
974
1197
|
* @param {Array} body - The request body
|
|
1198
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
975
1199
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
976
1200
|
*/
|
|
977
1201
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -979,12 +1203,15 @@ class PlaidClient {
|
|
|
979
1203
|
/**
|
|
980
1204
|
* Delete multiple spans in a single operation. Provide an array of IDs.
|
|
981
1205
|
* @param {Array} body - The request body
|
|
1206
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1207
|
+
* @returns {Promise<void>}
|
|
982
1208
|
*/
|
|
983
1209
|
bulkDelete: (body, auditMessage) =>
|
|
984
1210
|
this._request("DELETE", "/api/v1/spans/bulk", { auditMessage, body }),
|
|
985
1211
|
/**
|
|
986
1212
|
* Update many spans in a single operation: set values and/or patch metadata.
|
|
987
1213
|
* @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.
|
|
1214
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
988
1215
|
* @returns {Promise<{count: number}>} How many spans were updated.
|
|
989
1216
|
*/
|
|
990
1217
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -992,64 +1219,71 @@ class PlaidClient {
|
|
|
992
1219
|
auditMessage,
|
|
993
1220
|
body,
|
|
994
1221
|
}),
|
|
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
|
-
}),
|
|
1222
|
+
...metadataMethods(this, "spans"),
|
|
1023
1223
|
};
|
|
1024
1224
|
|
|
1025
|
-
// Server-level facts.
|
|
1026
|
-
//
|
|
1027
|
-
//
|
|
1028
|
-
|
|
1225
|
+
// Server-level facts. Read once, and again only when they may have
|
|
1226
|
+
// changed: after this client's connection to the server was lost and came
|
|
1227
|
+
// back (a restart, the only way the limits change, always drops it), or
|
|
1228
|
+
// after the server refused a private data write as too large. A caller
|
|
1229
|
+
// asking "will this file be accepted" does not pay a round trip to find
|
|
1230
|
+
// out. The facts live on the client (`_serverFacts`), so a batch opened
|
|
1231
|
+
// on it reads the same.
|
|
1029
1232
|
this.server = {
|
|
1030
1233
|
/**
|
|
1031
1234
|
* This server's version and the limits it enforces, e.g.
|
|
1032
1235
|
* `{ limits: { mediaFileBytes, jsonBodyBytes, batchOperations, … } }`.
|
|
1033
1236
|
* Sizes are in bytes. Unauthenticated.
|
|
1237
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
1034
1238
|
*/
|
|
1035
1239
|
info: () => {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1240
|
+
const facts = this._serverFacts;
|
|
1241
|
+
if (!facts.info) {
|
|
1242
|
+
const asked = this._request("GET", "/api/v1/info").catch((err) => {
|
|
1243
|
+
// A failure must not be kept: a client that starts before the
|
|
1039
1244
|
// server is up would never see the limits at all.
|
|
1040
|
-
|
|
1245
|
+
if (facts.info === asked) facts.info = null;
|
|
1041
1246
|
throw err;
|
|
1042
1247
|
});
|
|
1248
|
+
facts.info = asked;
|
|
1043
1249
|
}
|
|
1044
|
-
return
|
|
1250
|
+
return facts.info;
|
|
1251
|
+
},
|
|
1252
|
+
/**
|
|
1253
|
+
* Read `GET /info` again and resolve to it. Until it answers, a reader
|
|
1254
|
+
* waits for it. The facts known before are kept when the server does
|
|
1255
|
+
* not answer, and the error is thrown.
|
|
1256
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
1257
|
+
*/
|
|
1258
|
+
refresh: () => {
|
|
1259
|
+
const facts = this._serverFacts;
|
|
1260
|
+
const before = facts.info;
|
|
1261
|
+
facts.stale = false;
|
|
1262
|
+
const fresh = this._request("GET", "/api/v1/info");
|
|
1263
|
+
const held = fresh.catch((err) => {
|
|
1264
|
+
if (facts.info === held) facts.info = before;
|
|
1265
|
+
if (before) return before;
|
|
1266
|
+
throw err;
|
|
1267
|
+
});
|
|
1268
|
+
held.catch(() => {});
|
|
1269
|
+
facts.info = held;
|
|
1270
|
+
return fresh;
|
|
1271
|
+
},
|
|
1272
|
+
/**
|
|
1273
|
+
* Just the limits, which is what a caller almost always wants.
|
|
1274
|
+
* @returns {Promise<ServerLimits>}
|
|
1275
|
+
*/
|
|
1276
|
+
limits: async () => {
|
|
1277
|
+
// Through `self`: tsc types a bundle that names itself as
|
|
1278
|
+
// `this.server` in its own methods `any` in the declarations.
|
|
1279
|
+
const self = this;
|
|
1280
|
+
return (await self.server.info()).limits;
|
|
1045
1281
|
},
|
|
1046
|
-
/** Just the limits, which is what a caller almost always wants. */
|
|
1047
|
-
limits: async () => (await this.server.info()).limits,
|
|
1048
1282
|
/**
|
|
1049
1283
|
* Liveness, version and database size. Unauthenticated, and served
|
|
1050
1284
|
* outside the REST router at /health, so it answers even while the API
|
|
1051
1285
|
* is refusing requests. Never cached — the point is that it is current.
|
|
1052
|
-
* @returns {Promise<
|
|
1286
|
+
* @returns {Promise<ServerHealth>}
|
|
1053
1287
|
*/
|
|
1054
1288
|
health: () => PlaidClient.health(this.baseUrl, { timeout: this.timeout }),
|
|
1055
1289
|
};
|
|
@@ -1073,34 +1307,7 @@ class PlaidClient {
|
|
|
1073
1307
|
};
|
|
1074
1308
|
|
|
1075
1309
|
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
|
-
}),
|
|
1310
|
+
...metadataMethods(this, "texts"),
|
|
1104
1311
|
/**
|
|
1105
1312
|
* Create a new text in a document's text layer. A text is a container for
|
|
1106
1313
|
* one long string in `body` for a given layer.
|
|
@@ -1108,6 +1315,11 @@ class PlaidClient {
|
|
|
1108
1315
|
* @param {string} documentId - The document ID
|
|
1109
1316
|
* @param {string} body - The request body
|
|
1110
1317
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1318
|
+
* @param {string} [auditMessage]
|
|
1319
|
+
* @param {{id?: string, tokenEdges?: number[]}} [options] - `id`: the new text's id, a UUIDv7
|
|
1320
|
+
* this client minted. `tokenEdges`: the code-point offsets where the tokens made next on
|
|
1321
|
+
* this text begin and end. The body is stored composed, except a character one of them
|
|
1322
|
+
* falls inside, which stays as sent (see composeText).
|
|
1111
1323
|
*/
|
|
1112
1324
|
create: (
|
|
1113
1325
|
textLayerId,
|
|
@@ -1115,7 +1327,7 @@ class PlaidClient {
|
|
|
1115
1327
|
body,
|
|
1116
1328
|
metadata,
|
|
1117
1329
|
auditMessage,
|
|
1118
|
-
{ id } = {},
|
|
1330
|
+
{ id, tokenEdges } = {},
|
|
1119
1331
|
) =>
|
|
1120
1332
|
this._request("POST", "/api/v1/texts", {
|
|
1121
1333
|
auditMessage,
|
|
@@ -1124,6 +1336,7 @@ class PlaidClient {
|
|
|
1124
1336
|
"text-layer-id": textLayerId,
|
|
1125
1337
|
"document-id": documentId,
|
|
1126
1338
|
body,
|
|
1339
|
+
"token-edges": tokenEdges,
|
|
1127
1340
|
metadata,
|
|
1128
1341
|
}),
|
|
1129
1342
|
}),
|
|
@@ -1136,6 +1349,7 @@ class PlaidClient {
|
|
|
1136
1349
|
/**
|
|
1137
1350
|
* Delete a text and all dependent data.
|
|
1138
1351
|
* @param {string} textId - The text ID
|
|
1352
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1139
1353
|
*/
|
|
1140
1354
|
delete: (textId, auditMessage) =>
|
|
1141
1355
|
this._request("DELETE", `/api/v1/texts/${textId}`, { auditMessage }),
|
|
@@ -1195,14 +1409,14 @@ class PlaidClient {
|
|
|
1195
1409
|
* List (or search) users. Transparently follows pagination cursors and
|
|
1196
1410
|
* returns the full flat array. Admin-or-maintainer only.
|
|
1197
1411
|
* @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
|
|
1412
|
+
* @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
|
|
1199
1413
|
*/
|
|
1200
1414
|
list: ({ q } = {}) =>
|
|
1201
1415
|
listAll(this, "/api/v1/users", { query: { q } }),
|
|
1202
1416
|
/**
|
|
1203
1417
|
* Fetch a single page of users (optionally filtered by `q`).
|
|
1204
1418
|
* @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
|
|
1419
|
+
* @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
1420
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1207
1421
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1208
1422
|
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
@@ -1216,7 +1430,7 @@ class PlaidClient {
|
|
|
1216
1430
|
/**
|
|
1217
1431
|
* Async-iterate users page by page; yields each page's entries array.
|
|
1218
1432
|
* @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
|
|
1433
|
+
* @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
1434
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
1221
1435
|
* @returns {AsyncGenerator<Array>}
|
|
1222
1436
|
*/
|
|
@@ -1234,12 +1448,13 @@ class PlaidClient {
|
|
|
1234
1448
|
* @param {boolean} isAdmin - Whether the user is an admin
|
|
1235
1449
|
* @param {string} [displayName] - How the user is shown in the UI.
|
|
1236
1450
|
* Defaults to the local part of the email.
|
|
1451
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1237
1452
|
*/
|
|
1238
1453
|
create: (email, password, isAdmin, displayName, auditMessage) =>
|
|
1239
1454
|
this._request("POST", "/api/v1/users", {
|
|
1240
1455
|
auditMessage,
|
|
1241
1456
|
body: bodyOf({
|
|
1242
|
-
email,
|
|
1457
|
+
email: normalizeUserId(email),
|
|
1243
1458
|
password,
|
|
1244
1459
|
"is-admin": isAdmin,
|
|
1245
1460
|
// Left out when not given (null too), so core gives the user the
|
|
@@ -1275,6 +1490,10 @@ class PlaidClient {
|
|
|
1275
1490
|
* that may be long: audit() walks every page before it resolves.
|
|
1276
1491
|
* @param {string} userId - The user ID
|
|
1277
1492
|
* @param {object} [opts]
|
|
1493
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
1494
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
1495
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
1496
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
1278
1497
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
1279
1498
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1280
1499
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -1314,6 +1533,7 @@ class PlaidClient {
|
|
|
1314
1533
|
* stays visible in listings with a deactivated-at timestamp.
|
|
1315
1534
|
* Reversible via activate(), which restores login only.
|
|
1316
1535
|
* @param {string} id - The resource ID
|
|
1536
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1317
1537
|
*/
|
|
1318
1538
|
delete: (id, auditMessage) =>
|
|
1319
1539
|
this._request("DELETE", `/api/v1/users/${id}`, { auditMessage }),
|
|
@@ -1322,6 +1542,7 @@ class PlaidClient {
|
|
|
1322
1542
|
* Project memberships, vocab maintainerships, and API tokens removed
|
|
1323
1543
|
* at deactivation are NOT restored — re-grant them deliberately.
|
|
1324
1544
|
* @param {string} id - The resource ID
|
|
1545
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1325
1546
|
*/
|
|
1326
1547
|
activate: (id, auditMessage) =>
|
|
1327
1548
|
this._request("POST", `/api/v1/users/${id}/activate`, { auditMessage }),
|
|
@@ -1337,6 +1558,7 @@ class PlaidClient {
|
|
|
1337
1558
|
* @param {string} [password] - New password
|
|
1338
1559
|
* @param {string} [displayName] - New display name
|
|
1339
1560
|
* @param {boolean} [isAdmin] - New admin status
|
|
1561
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1340
1562
|
*/
|
|
1341
1563
|
update: (id, password, displayName, isAdmin, auditMessage) =>
|
|
1342
1564
|
this._request("PATCH", `/api/v1/users/${id}`, {
|
|
@@ -1348,23 +1570,89 @@ class PlaidClient {
|
|
|
1348
1570
|
}),
|
|
1349
1571
|
}),
|
|
1350
1572
|
/**
|
|
1351
|
-
*
|
|
1352
|
-
* an `<img
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
1573
|
+
* A token that shows any user's profile picture without an
|
|
1574
|
+
* Authorization header, for an `<img>`, which cannot send one. Resolves
|
|
1575
|
+
* to `{ token, expiresAt }` (`expiresAt` an ISO-8601 instant, a day on by
|
|
1576
|
+
* default). The server refuses it on every route but the picture, and
|
|
1577
|
+
* it stops working when the user signs out or changes password.
|
|
1578
|
+
*
|
|
1579
|
+
* One token serves the whole client: it is kept and handed out again
|
|
1580
|
+
* until under ten minutes of it remain (half its life, for one minted
|
|
1581
|
+
* near the end of a login), calls made while one is being minted share
|
|
1582
|
+
* that request, and it is dropped when the client's token changes.
|
|
1583
|
+
* avatarUrl() uses it.
|
|
1584
|
+
* @param {{renew?: boolean}} [options] - `renew`: mint a new one
|
|
1585
|
+
* whatever is kept, unless one was minted in the last few seconds
|
|
1586
|
+
* (or is being minted), which is shared
|
|
1587
|
+
* @returns {Promise<{token: string, expiresAt: string}>}
|
|
1588
|
+
*/
|
|
1589
|
+
avatarToken: ({ renew = false } = {}) => {
|
|
1590
|
+
// A batch is a view of its client and shares the client's token.
|
|
1591
|
+
const owner = this.client ?? this;
|
|
1592
|
+
const cached = owner._avatarToken;
|
|
1593
|
+
if (cached && cached.forToken === owner.token) {
|
|
1594
|
+
if (cached.pending) return cached.pending;
|
|
1595
|
+
const now = owner.serverNow().getTime();
|
|
1596
|
+
// A renewal shares one minted a moment ago: every picture on a page
|
|
1597
|
+
// fails together when a token dies, and each asks.
|
|
1598
|
+
if (renew && now - cached.mintedAt < AVATAR_TOKEN_FRESH_MS) {
|
|
1599
|
+
return Promise.resolve(cached.link);
|
|
1600
|
+
}
|
|
1601
|
+
const expires = Date.parse(cached.link.expiresAt);
|
|
1602
|
+
// Renewed ten minutes before it runs out, or at half its life when
|
|
1603
|
+
// it was minted for less than twenty: the server caps it at the
|
|
1604
|
+
// login's own expiry, and near the end of a login every token it
|
|
1605
|
+
// mints runs out at that same moment, so a fixed ten minutes asked
|
|
1606
|
+
// for a new one on every call.
|
|
1607
|
+
const renewAt = Math.min(AVATAR_TOKEN_RENEW_MS, (expires - cached.mintedAt) / 2);
|
|
1608
|
+
if (!renew && expires - now > renewAt) return Promise.resolve(cached.link);
|
|
1609
|
+
}
|
|
1610
|
+
const entry = {
|
|
1611
|
+
forToken: owner.token,
|
|
1612
|
+
pending: null,
|
|
1613
|
+
link: null,
|
|
1614
|
+
mintedAt: owner.serverNow().getTime(),
|
|
1615
|
+
};
|
|
1616
|
+
entry.pending = owner
|
|
1617
|
+
._request("POST", "/api/v1/avatar-link", { outOfBand: true })
|
|
1618
|
+
.then(
|
|
1619
|
+
(link) => {
|
|
1620
|
+
entry.pending = null;
|
|
1621
|
+
entry.link = link;
|
|
1622
|
+
return link;
|
|
1623
|
+
},
|
|
1624
|
+
(e) => {
|
|
1625
|
+
if (owner._avatarToken === entry) owner._avatarToken = null;
|
|
1626
|
+
throw e;
|
|
1627
|
+
},
|
|
1628
|
+
);
|
|
1629
|
+
owner._avatarToken = entry;
|
|
1630
|
+
return entry.pending;
|
|
1631
|
+
},
|
|
1632
|
+
/**
|
|
1633
|
+
* Build a URL for a user's profile picture, for an `<img>` src. The
|
|
1634
|
+
* URL carries the client's avatar token (see avatarToken()), never the
|
|
1635
|
+
* login token.
|
|
1355
1636
|
*
|
|
1356
1637
|
* Pass the user record's `avatarHash` as the second argument whenever you
|
|
1357
1638
|
* have it: the URL then addresses that exact picture, so the browser can
|
|
1358
1639
|
* cache it indefinitely and still pick up a replacement the moment the
|
|
1359
|
-
* user changes it.
|
|
1360
|
-
* can fall back to initials without a
|
|
1640
|
+
* user changes it. Resolves to null when `avatarHash` is null (the user
|
|
1641
|
+
* has no picture), so callers can fall back to initials without a
|
|
1642
|
+
* wasted request.
|
|
1361
1643
|
* @param {string} id - The user ID
|
|
1362
|
-
* @param {string} [avatarHash] - The user record's `avatarHash`
|
|
1363
|
-
* @
|
|
1644
|
+
* @param {string|null} [avatarHash] - The user record's `avatarHash`
|
|
1645
|
+
* @param {{renew?: boolean}} [options] - `renew`: mint a new avatar
|
|
1646
|
+
* token first, for a picture its URL failed to load (the token
|
|
1647
|
+
* stops working when the login it was minted under ends)
|
|
1648
|
+
* @returns {Promise<string|null>}
|
|
1364
1649
|
*/
|
|
1365
|
-
avatarUrl: (id, avatarHash) => {
|
|
1650
|
+
avatarUrl: async (id, avatarHash, { renew = false } = {}) => {
|
|
1366
1651
|
if (avatarHash === null) return null;
|
|
1367
|
-
|
|
1652
|
+
// Through `self`, as in server.limits.
|
|
1653
|
+
const self = this;
|
|
1654
|
+
const { token } = await self.users.avatarToken({ renew });
|
|
1655
|
+
const params = new URLSearchParams({ "avatar-token": token });
|
|
1368
1656
|
if (avatarHash) params.set("v", avatarHash);
|
|
1369
1657
|
return `${this.baseUrl}/api/v1/users/${id}/avatar?${params}`;
|
|
1370
1658
|
},
|
|
@@ -1384,6 +1672,7 @@ class PlaidClient {
|
|
|
1384
1672
|
* PNG, JPEG, WebP, and GIF. Resolves to the updated user record.
|
|
1385
1673
|
* @param {string} id - The user ID
|
|
1386
1674
|
* @param {File} file - The image to upload
|
|
1675
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1387
1676
|
*/
|
|
1388
1677
|
setAvatar: (id, file, auditMessage) => {
|
|
1389
1678
|
const fd = new FormData();
|
|
@@ -1398,6 +1687,7 @@ class PlaidClient {
|
|
|
1398
1687
|
/**
|
|
1399
1688
|
* Remove a profile picture. Your own, or anyone's if you are an admin.
|
|
1400
1689
|
* @param {string} id - The user ID
|
|
1690
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1401
1691
|
*/
|
|
1402
1692
|
// No flag: the upload beside this one is multipart and cannot be
|
|
1403
1693
|
// batched, but a DELETE carries no blob, so the batch transport takes
|
|
@@ -1421,7 +1711,8 @@ class PlaidClient {
|
|
|
1421
1711
|
* every project without dragging down the transcripts beside them.
|
|
1422
1712
|
*
|
|
1423
1713
|
* `pageSize` is exposed here, and defaults lower than elsewhere, because
|
|
1424
|
-
* one value runs to
|
|
1714
|
+
* one value runs to megabytes (`userDataValueBytes` in
|
|
1715
|
+
* `server.limits()`): a page of them with `includeValues` is the
|
|
1425
1716
|
* largest response this API can be asked for. Raise it when the listing
|
|
1426
1717
|
* is keys, or the values are known to be small.
|
|
1427
1718
|
* @param {string} userId
|
|
@@ -1430,6 +1721,7 @@ class PlaidClient {
|
|
|
1430
1721
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1431
1722
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1432
1723
|
* @param {number} [opts.pageSize=100] - Entries per request (1..1000)
|
|
1724
|
+
* @returns {Promise<UserDataEntry[]>}
|
|
1433
1725
|
*/
|
|
1434
1726
|
list: (userId, { prefix, pattern, includeValues, pageSize = 100 } = {}) =>
|
|
1435
1727
|
listAll(this, `/api/v1/users/${userId}/data`, {
|
|
@@ -1445,7 +1737,7 @@ class PlaidClient {
|
|
|
1445
1737
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1446
1738
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1447
1739
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1448
|
-
* @returns {Promise<
|
|
1740
|
+
* @returns {Promise<Page<UserDataEntry>>}
|
|
1449
1741
|
*/
|
|
1450
1742
|
listPage: (
|
|
1451
1743
|
userId,
|
|
@@ -1464,6 +1756,7 @@ class PlaidClient {
|
|
|
1464
1756
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1465
1757
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1466
1758
|
* @param {number} [opts.pageSize=100] - Entries per request (1..1000)
|
|
1759
|
+
* @returns {AsyncGenerator<UserDataEntry[]>}
|
|
1467
1760
|
*/
|
|
1468
1761
|
iterPages: (
|
|
1469
1762
|
userId,
|
|
@@ -1477,6 +1770,7 @@ class PlaidClient {
|
|
|
1477
1770
|
* Read one private data entry ({key, updatedAt, version, value}); 404 if absent.
|
|
1478
1771
|
* @param {string} userId
|
|
1479
1772
|
* @param {string} key
|
|
1773
|
+
* @returns {Promise<UserDataEntry & {value: any}>}
|
|
1480
1774
|
*/
|
|
1481
1775
|
get: (userId, key) =>
|
|
1482
1776
|
this._request(
|
|
@@ -1484,8 +1778,11 @@ class PlaidClient {
|
|
|
1484
1778
|
`/api/v1/users/${userId}/data/${encodeURIComponent(key)}`,
|
|
1485
1779
|
),
|
|
1486
1780
|
/**
|
|
1487
|
-
* Create or replace one private data entry. `value` is any JSON
|
|
1488
|
-
*
|
|
1781
|
+
* Create or replace one private data entry. `value` is any JSON, up to
|
|
1782
|
+
* the server's `userDataValueBytes` (`server.limits()`, 5 MB by
|
|
1783
|
+
* default), and is refused with 413 over it, after which
|
|
1784
|
+
* `server.limits()` answers the figure the server has now. Not audited,
|
|
1785
|
+
* not batchable.
|
|
1489
1786
|
*
|
|
1490
1787
|
* The server stores it verbatim, but this client recases object keys on
|
|
1491
1788
|
* the way out and back like any other body (`myKey` <-> `my-key`), so a
|
|
@@ -1506,16 +1803,26 @@ class PlaidClient {
|
|
|
1506
1803
|
* @param {number} [opts.version] - The version this write was made from
|
|
1507
1804
|
* @returns {Promise<{key: string, updatedAt: string, version: number}>}
|
|
1508
1805
|
*/
|
|
1509
|
-
put: (userId, key, value, { version } = {}) =>
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1806
|
+
put: async (userId, key, value, { version } = {}) => {
|
|
1807
|
+
try {
|
|
1808
|
+
return await this._request(
|
|
1809
|
+
"PUT",
|
|
1810
|
+
`/api/v1/users/${userId}/data/${encodeURIComponent(key)}`,
|
|
1811
|
+
{
|
|
1812
|
+
body: value,
|
|
1813
|
+
noBatch: true,
|
|
1814
|
+
queryParams: { version },
|
|
1815
|
+
},
|
|
1816
|
+
);
|
|
1817
|
+
} catch (err) {
|
|
1818
|
+
if (err?.status === 413) {
|
|
1819
|
+
// The cap may have changed since it was read: the caller deciding
|
|
1820
|
+
// what to do next reads the server's figure now.
|
|
1821
|
+
await this.server.refresh().catch(() => {});
|
|
1822
|
+
}
|
|
1823
|
+
throw err;
|
|
1824
|
+
}
|
|
1825
|
+
},
|
|
1519
1826
|
/**
|
|
1520
1827
|
* Delete one private data entry; 404 if absent.
|
|
1521
1828
|
* @param {string} userId
|
|
@@ -1567,6 +1874,7 @@ class PlaidClient {
|
|
|
1567
1874
|
* needs a sign-in token.
|
|
1568
1875
|
* @param {string} userId - The user ID who will own the token
|
|
1569
1876
|
* @param {string} name - A human label, e.g. "Stanza parser"
|
|
1877
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1570
1878
|
* @returns {Promise<{id: string, name: string, token: string}>}
|
|
1571
1879
|
*/
|
|
1572
1880
|
create: (userId, name, auditMessage) =>
|
|
@@ -1583,6 +1891,7 @@ class PlaidClient {
|
|
|
1583
1891
|
* needs a sign-in token.
|
|
1584
1892
|
* @param {string} userId - The user ID who owns the token
|
|
1585
1893
|
* @param {string} tokenId - The token ID to revoke
|
|
1894
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1586
1895
|
*/
|
|
1587
1896
|
revoke: (userId, tokenId, auditMessage) =>
|
|
1588
1897
|
this._request("DELETE", `/api/v1/users/${userId}/tokens/${tokenId}`, {
|
|
@@ -1600,6 +1909,7 @@ class PlaidClient {
|
|
|
1600
1909
|
* @param {object} [opts]
|
|
1601
1910
|
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
1602
1911
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
1912
|
+
* @returns {Promise<Invite[]>}
|
|
1603
1913
|
*/
|
|
1604
1914
|
list: ({ projectId, all } = {}) =>
|
|
1605
1915
|
listAll(this, "/api/v1/invites", {
|
|
@@ -1612,7 +1922,7 @@ class PlaidClient {
|
|
|
1612
1922
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
1613
1923
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1614
1924
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1615
|
-
* @returns {Promise<
|
|
1925
|
+
* @returns {Promise<Page<Invite>>}
|
|
1616
1926
|
*/
|
|
1617
1927
|
listPage: ({ projectId, all, limit, cursor } = {}) =>
|
|
1618
1928
|
listPage(this, "/api/v1/invites", {
|
|
@@ -1626,7 +1936,7 @@ class PlaidClient {
|
|
|
1626
1936
|
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
1627
1937
|
* @param {boolean} [opts.all] - List every invite on the server (admin only)
|
|
1628
1938
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
1629
|
-
* @returns {AsyncGenerator<
|
|
1939
|
+
* @returns {AsyncGenerator<Invite[]>}
|
|
1630
1940
|
*/
|
|
1631
1941
|
iterPages: ({ projectId, all, pageSize } = {}) =>
|
|
1632
1942
|
iterPages(this, "/api/v1/invites", {
|
|
@@ -1659,7 +1969,8 @@ class PlaidClient {
|
|
|
1659
1969
|
* @param {number} [opts.maxUses] - How many accounts this link may create (default 1)
|
|
1660
1970
|
* @param {number} [opts.ttlDays] - Days until it expires (default 14, max 365)
|
|
1661
1971
|
* @param {string} [opts.note] - Human label shown in your invite list
|
|
1662
|
-
* @
|
|
1972
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1973
|
+
* @returns {Promise<Invite & {code: string}>}
|
|
1663
1974
|
*/
|
|
1664
1975
|
create: (
|
|
1665
1976
|
{
|
|
@@ -1692,6 +2003,7 @@ class PlaidClient {
|
|
|
1692
2003
|
* Revoke an invite, killing the link immediately. Idempotent. Allowed for
|
|
1693
2004
|
* the creator, an admin, or a maintainer of the invite's project.
|
|
1694
2005
|
* @param {string} id - The invite ID
|
|
2006
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
1695
2007
|
*/
|
|
1696
2008
|
revoke: (id, auditMessage) =>
|
|
1697
2009
|
this._request("DELETE", `/api/v1/invites/${id}`, { auditMessage }),
|
|
@@ -1704,7 +2016,7 @@ class PlaidClient {
|
|
|
1704
2016
|
* configuration and the backups on disk, and the settings an operator
|
|
1705
2017
|
* gets asked to confirm. Carries no secrets. Runs a count per table and
|
|
1706
2018
|
* walks the media directory, so open it, do not poll it. Admin only.
|
|
1707
|
-
* @returns {Promise<
|
|
2019
|
+
* @returns {Promise<ServerReport>}
|
|
1708
2020
|
*/
|
|
1709
2021
|
server: () => this._request("GET", "/api/v1/admin/server"),
|
|
1710
2022
|
/**
|
|
@@ -1718,7 +2030,7 @@ class PlaidClient {
|
|
|
1718
2030
|
* rather than the client's usual timeout. A proxy in front of the server
|
|
1719
2031
|
* may still give up first (a 504): the backup goes on, and its file
|
|
1720
2032
|
* shows in `server().backup.backups` once it is written.
|
|
1721
|
-
* @returns {Promise<
|
|
2033
|
+
* @returns {Promise<BackupStatus>}
|
|
1722
2034
|
*
|
|
1723
2035
|
* outOfBand, as every admin action on the server itself is: none of them
|
|
1724
2036
|
* writes project data (see the note at the top of http.js).
|
|
@@ -1738,6 +2050,7 @@ class PlaidClient {
|
|
|
1738
2050
|
* Drop the lock on a document whoever holds it. Idempotent. For a client
|
|
1739
2051
|
* that went away without releasing one. Admin only.
|
|
1740
2052
|
* @param {string} documentId - The document ID
|
|
2053
|
+
* @returns {Promise<{result: string}>}
|
|
1741
2054
|
*/
|
|
1742
2055
|
releaseLock: (documentId) =>
|
|
1743
2056
|
this._request("DELETE", `/api/v1/admin/locks/${documentId}`, {
|
|
@@ -1747,7 +2060,7 @@ class PlaidClient {
|
|
|
1747
2060
|
* Live login and invite rate-limit buckets: the address, the account
|
|
1748
2061
|
* where there is one, failures inside the window, the limit, and whether
|
|
1749
2062
|
* it is currently blocking. Admin only.
|
|
1750
|
-
* @returns {Promise<
|
|
2063
|
+
* @returns {Promise<RateLimitSnapshot>}
|
|
1751
2064
|
*/
|
|
1752
2065
|
rateLimits: () => this._request("GET", "/api/v1/admin/rate-limits"),
|
|
1753
2066
|
/**
|
|
@@ -1757,6 +2070,7 @@ class PlaidClient {
|
|
|
1757
2070
|
* @param {object} [opts]
|
|
1758
2071
|
* @param {string} [opts.ip] - Clear only this address
|
|
1759
2072
|
* @param {string} [opts.userId] - Narrow to one account on that address
|
|
2073
|
+
* @returns {Promise<{result: string}>}
|
|
1760
2074
|
*/
|
|
1761
2075
|
clearRateLimits: ({ ip, userId } = {}) =>
|
|
1762
2076
|
this._request("DELETE", "/api/v1/admin/rate-limits", {
|
|
@@ -1780,7 +2094,7 @@ class PlaidClient {
|
|
|
1780
2094
|
* @param {string} [opts.status] - "2xx".."5xx", an exact code, or "failures"
|
|
1781
2095
|
* @param {string} [opts.user] - Only requests made by this account
|
|
1782
2096
|
* @param {string} [opts.method] - Only requests with this HTTP method
|
|
1783
|
-
* @returns {Promise<
|
|
2097
|
+
* @returns {Promise<LiveLog>}
|
|
1784
2098
|
*/
|
|
1785
2099
|
logs: ({ limit, q, level, status, user, method } = {}) =>
|
|
1786
2100
|
this._request("GET", "/api/v1/admin/logs", {
|
|
@@ -1814,7 +2128,7 @@ class PlaidClient {
|
|
|
1814
2128
|
* @param {string} [opts.prefix] - Only keys starting with this
|
|
1815
2129
|
* @param {string} [opts.pattern] - Only keys matching this GLOB
|
|
1816
2130
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1817
|
-
* @returns {Promise<
|
|
2131
|
+
* @returns {Promise<AdminUserDataEntry[]>}
|
|
1818
2132
|
*/
|
|
1819
2133
|
userData: ({ prefix, pattern, includeValues } = {}) =>
|
|
1820
2134
|
listAll(this, "/api/v1/admin/user-data", {
|
|
@@ -1833,7 +2147,7 @@ class PlaidClient {
|
|
|
1833
2147
|
* @param {boolean} [opts.includeValues] - Also return each entry's value
|
|
1834
2148
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1835
2149
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1836
|
-
* @returns {Promise<
|
|
2150
|
+
* @returns {Promise<Page<AdminUserDataEntry>>}
|
|
1837
2151
|
*/
|
|
1838
2152
|
userDataPage: ({ prefix, pattern, includeValues, limit, cursor } = {}) =>
|
|
1839
2153
|
listPage(this, "/api/v1/admin/user-data", {
|
|
@@ -1874,6 +2188,10 @@ class PlaidClient {
|
|
|
1874
2188
|
/**
|
|
1875
2189
|
* Fetch a single page of the instance-wide audit log. Admin only.
|
|
1876
2190
|
* @param {object} [opts]
|
|
2191
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2192
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2193
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2194
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
1877
2195
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first, which
|
|
1878
2196
|
* is what a feed wants. A cursor belongs to the direction that made it.
|
|
1879
2197
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
@@ -1900,6 +2218,12 @@ class PlaidClient {
|
|
|
1900
2218
|
}),
|
|
1901
2219
|
/**
|
|
1902
2220
|
* Async-iterate the instance-wide audit log page by page. Admin only.
|
|
2221
|
+
* @param {object} [opts]
|
|
2222
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2223
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2224
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2225
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2226
|
+
* @param {number} [opts.pageSize] - Per-request page size
|
|
1903
2227
|
* @returns {AsyncGenerator<Array>}
|
|
1904
2228
|
*/
|
|
1905
2229
|
iterPages: ({ startTime, endTime, opTypes, kinds, pageSize } = {}) =>
|
|
@@ -1928,7 +2252,7 @@ class PlaidClient {
|
|
|
1928
2252
|
* @param {boolean} [opts.daily] - Also return `byDay`, a list of
|
|
1929
2253
|
* `{date, changes}` per user, oldest first, at the cost of a second
|
|
1930
2254
|
* grouped scan
|
|
1931
|
-
* @returns {Promise<
|
|
2255
|
+
* @returns {Promise<ActivityTallyRow[]>}
|
|
1932
2256
|
*/
|
|
1933
2257
|
tally: async ({ projectId, startTime, endTime, daily } = {}) => {
|
|
1934
2258
|
const path = projectId
|
|
@@ -1946,22 +2270,16 @@ class PlaidClient {
|
|
|
1946
2270
|
};
|
|
1947
2271
|
|
|
1948
2272
|
this.tokenLayers = {
|
|
1949
|
-
|
|
1950
|
-
* Shift a token layer's display order.
|
|
1951
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
1952
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
1953
|
-
*/
|
|
1954
|
-
shift: (tokenLayerId, direction, auditMessage) =>
|
|
1955
|
-
this._request("POST", `/api/v1/token-layers/${tokenLayerId}/shift`, {
|
|
1956
|
-
auditMessage,
|
|
1957
|
-
body: bodyOf({ direction }),
|
|
1958
|
-
}),
|
|
2273
|
+
...layerMethods(this, "token-layers"),
|
|
1959
2274
|
/**
|
|
1960
2275
|
* Create a new token layer.
|
|
1961
2276
|
* @param {string} textLayerId - The text layer ID
|
|
1962
2277
|
* @param {string} name - The name
|
|
1963
2278
|
* @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
2279
|
* @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).
|
|
2280
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2281
|
+
* @param {object} [opts]
|
|
2282
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
1965
2283
|
*/
|
|
1966
2284
|
create: (
|
|
1967
2285
|
textLayerId,
|
|
@@ -1971,71 +2289,18 @@ class PlaidClient {
|
|
|
1971
2289
|
auditMessage,
|
|
1972
2290
|
{ id } = {},
|
|
1973
2291
|
) =>
|
|
1974
|
-
this._request("POST", "/api/v1/token-layers", {
|
|
1975
|
-
auditMessage,
|
|
1976
|
-
body: bodyOf({
|
|
1977
|
-
id,
|
|
1978
|
-
"text-layer-id": textLayerId,
|
|
1979
|
-
name,
|
|
1980
|
-
"overlap-mode": overlapMode,
|
|
1981
|
-
"parent-token-layer-id": parentTokenLayerId,
|
|
1982
|
-
}),
|
|
1983
|
-
}),
|
|
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
|
-
),
|
|
2014
|
-
...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}`, {
|
|
2292
|
+
this._request("POST", "/api/v1/token-layers", {
|
|
2036
2293
|
auditMessage,
|
|
2037
|
-
body: bodyOf({
|
|
2294
|
+
body: bodyOf({
|
|
2295
|
+
id,
|
|
2296
|
+
"text-layer-id": textLayerId,
|
|
2297
|
+
name,
|
|
2298
|
+
"overlap-mode": overlapMode,
|
|
2299
|
+
"parent-token-layer-id": parentTokenLayerId,
|
|
2300
|
+
}),
|
|
2038
2301
|
}),
|
|
2302
|
+
...configMethods(this, "token-layers"),
|
|
2303
|
+
...constraintMethods(this, "token-layers"),
|
|
2039
2304
|
};
|
|
2040
2305
|
|
|
2041
2306
|
this.documents = {
|
|
@@ -2082,6 +2347,7 @@ class PlaidClient {
|
|
|
2082
2347
|
* outOfBand, for the same reason as acquireLock.
|
|
2083
2348
|
* @param {string} documentId - The document ID
|
|
2084
2349
|
* @param {string} lockId - The `lockId` acquireLock answered with
|
|
2350
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2085
2351
|
*/
|
|
2086
2352
|
renewLock: (documentId, lockId, auditMessage) =>
|
|
2087
2353
|
this._request("POST", `/api/v1/documents/${documentId}/lock`, {
|
|
@@ -2096,6 +2362,7 @@ class PlaidClient {
|
|
|
2096
2362
|
* and not released at all if it aborts.
|
|
2097
2363
|
* @param {string} documentId - The document ID
|
|
2098
2364
|
* @param {string} lockId - The `lockId` acquireLock answered with
|
|
2365
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2099
2366
|
*/
|
|
2100
2367
|
releaseLock: (documentId, lockId, auditMessage) =>
|
|
2101
2368
|
this._request("DELETE", `/api/v1/documents/${documentId}/lock`, {
|
|
@@ -2139,9 +2406,11 @@ class PlaidClient {
|
|
|
2139
2406
|
* lost lock is recorded on the CLIENT, like strict mode, so it stops
|
|
2140
2407
|
* every write the client makes (on any batch of it too) and not only the
|
|
2141
2408
|
* ones this block makes.
|
|
2409
|
+
* @template T
|
|
2142
2410
|
* @param {string} documentId - The document ID
|
|
2143
|
-
* @param {(lock: DocumentLock) =>
|
|
2144
|
-
* @param {
|
|
2411
|
+
* @param {(lock: import("./documentLock.js").DocumentLock) => T | Promise<T>} fn - The work to run while holding it
|
|
2412
|
+
* @param {{keepAlive?: boolean}} [options] - `{ keepAlive }`: renew on a timer (default true)
|
|
2413
|
+
* @returns {Promise<T>}
|
|
2145
2414
|
*/
|
|
2146
2415
|
locked: (documentId, fn, options) =>
|
|
2147
2416
|
withDocumentLock(this, documentId, fn, options),
|
|
@@ -2150,11 +2419,34 @@ class PlaidClient {
|
|
|
2150
2419
|
* no as-of form: the route refuses the parameter. Prefer the document's
|
|
2151
2420
|
* own `mediaUrl`, which carries the file's version for caching.
|
|
2152
2421
|
* @param {string} documentId - The document ID
|
|
2422
|
+
* @returns {Promise<ArrayBuffer>}
|
|
2153
2423
|
*/
|
|
2154
2424
|
getMedia: (documentId) =>
|
|
2155
2425
|
this._request("GET", `/api/v1/documents/${documentId}/media`, {
|
|
2156
2426
|
binaryResponse: true,
|
|
2157
2427
|
}),
|
|
2428
|
+
/**
|
|
2429
|
+
* Get a link that plays the document's recording without an
|
|
2430
|
+
* Authorization header, for an audio or video element's `src`, which
|
|
2431
|
+
* cannot send one. `url` is the document's media URL with a
|
|
2432
|
+
* `media-token` added, resolved against the client's base URL. The token
|
|
2433
|
+
* opens only this recording, for this user, and the server refuses it on
|
|
2434
|
+
* every other route. It expires at `expiresAt` (an ISO-8601 instant, six
|
|
2435
|
+
* hours on by default), and sooner when the user signs out, changes
|
|
2436
|
+
* password or loses access to the project: ask for a new link then. A
|
|
2437
|
+
* document with no recording is a 404. Writes nothing, so it takes no
|
|
2438
|
+
* Idempotency-Key and goes over the wire when made on a batch.
|
|
2439
|
+
* @param {string} documentId - The document ID
|
|
2440
|
+
* @returns {Promise<{url: string, expiresAt: string}>}
|
|
2441
|
+
*/
|
|
2442
|
+
mediaLink: async (documentId) => {
|
|
2443
|
+
const link = await this._request(
|
|
2444
|
+
"POST",
|
|
2445
|
+
`/api/v1/documents/${documentId}/media/link`,
|
|
2446
|
+
{ outOfBand: true },
|
|
2447
|
+
);
|
|
2448
|
+
return { ...link, url: `${this.baseUrl}${link.url}` };
|
|
2449
|
+
},
|
|
2158
2450
|
/**
|
|
2159
2451
|
* Upload a media file for a document. Uses Apache Tika for content validation.
|
|
2160
2452
|
* A document that already has a recording refuses it with a 409
|
|
@@ -2165,7 +2457,7 @@ class PlaidClient {
|
|
|
2165
2457
|
* @param {string} documentId - The document ID
|
|
2166
2458
|
* @param {File} file - The file to upload
|
|
2167
2459
|
* @param {string} [auditMessage] - Custom audit-log message for this write
|
|
2168
|
-
* @param {
|
|
2460
|
+
* @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.
|
|
2169
2461
|
*/
|
|
2170
2462
|
uploadMedia: (documentId, file, auditMessage, { onProgress } = {}) => {
|
|
2171
2463
|
const fd = new FormData();
|
|
@@ -2197,39 +2489,13 @@ class PlaidClient {
|
|
|
2197
2489
|
auditMessage,
|
|
2198
2490
|
queryParams: { "media-version": mediaVersion },
|
|
2199
2491
|
}),
|
|
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
|
-
}),
|
|
2492
|
+
...metadataMethods(this, "documents"),
|
|
2228
2493
|
/**
|
|
2229
2494
|
* Get audit log for a document. Transparently follows pagination cursors
|
|
2230
2495
|
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2231
2496
|
* says what kind of credential made it as `credential`: `login`,
|
|
2232
|
-
* `named-token
|
|
2497
|
+
* `named-token` or `delegated` (absent on older operations, `service`
|
|
2498
|
+
* on some from 2026-10-06 to 2026-10-08),
|
|
2233
2499
|
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2234
2500
|
* @param {string} documentId - The document ID
|
|
2235
2501
|
* @param {string} [startTime] - Start of time range
|
|
@@ -2256,6 +2522,10 @@ class PlaidClient {
|
|
|
2256
2522
|
* that may be long: audit() walks every page before it resolves.
|
|
2257
2523
|
* @param {string} documentId - The document ID
|
|
2258
2524
|
* @param {object} [opts]
|
|
2525
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2526
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2527
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2528
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2259
2529
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
2260
2530
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2261
2531
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -2292,7 +2562,7 @@ class PlaidClient {
|
|
|
2292
2562
|
* @param {string} documentId - The document ID
|
|
2293
2563
|
* @param {string} asOf - The moment to go back to (ISO-8601 instant),
|
|
2294
2564
|
* typically a history entry's `endTime`
|
|
2295
|
-
* @param {
|
|
2565
|
+
* @param {{dryRun?: boolean}} [options] - `{ dryRun }`: with `dryRun` true nothing
|
|
2296
2566
|
* is written and the summary says what would change
|
|
2297
2567
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
2298
2568
|
*/
|
|
@@ -2330,6 +2600,7 @@ class PlaidClient {
|
|
|
2330
2600
|
/**
|
|
2331
2601
|
* Delete a document and all data contained.
|
|
2332
2602
|
* @param {string} documentId - The document ID
|
|
2603
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2333
2604
|
*/
|
|
2334
2605
|
delete: (documentId, auditMessage) =>
|
|
2335
2606
|
this._request("DELETE", `/api/v1/documents/${documentId}`, {
|
|
@@ -2339,6 +2610,7 @@ class PlaidClient {
|
|
|
2339
2610
|
* Update a document's name.
|
|
2340
2611
|
* @param {string} documentId - The document ID
|
|
2341
2612
|
* @param {string} name - The name
|
|
2613
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2342
2614
|
*/
|
|
2343
2615
|
update: (documentId, name, auditMessage) =>
|
|
2344
2616
|
this._request("PATCH", `/api/v1/documents/${documentId}`, {
|
|
@@ -2350,6 +2622,9 @@ class PlaidClient {
|
|
|
2350
2622
|
* @param {string} projectId - The project ID
|
|
2351
2623
|
* @param {string} name - The name
|
|
2352
2624
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
2625
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2626
|
+
* @param {object} [opts]
|
|
2627
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2353
2628
|
*/
|
|
2354
2629
|
create: (projectId, name, metadata, auditMessage, { id } = {}) =>
|
|
2355
2630
|
this._request("POST", "/api/v1/documents", {
|
|
@@ -2366,9 +2641,10 @@ class PlaidClient {
|
|
|
2366
2641
|
* the source had media the copy could not take with it.
|
|
2367
2642
|
* @param {string} documentId - The document to copy
|
|
2368
2643
|
* @param {string} name - The new document's name
|
|
2369
|
-
* @param {
|
|
2644
|
+
* @param {{includeMedia?: boolean, id?: string}} [options] - `{ includeMedia }`: false leaves the media
|
|
2370
2645
|
* file behind
|
|
2371
2646
|
* @param {string} [auditMessage] - Custom audit message for this operation
|
|
2647
|
+
* @returns {Promise<{id: string, mediaError?: string}>}
|
|
2372
2648
|
*/
|
|
2373
2649
|
copy: (documentId, name, { includeMedia, id } = {}, auditMessage) =>
|
|
2374
2650
|
this._request("POST", `/api/v1/documents/${documentId}/copy`, {
|
|
@@ -2382,6 +2658,7 @@ class PlaidClient {
|
|
|
2382
2658
|
* Set a user's access level to read and write for this project.
|
|
2383
2659
|
* @param {string} id - The resource ID
|
|
2384
2660
|
* @param {string} userId - The user ID
|
|
2661
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2385
2662
|
*/
|
|
2386
2663
|
addWriter: (id, userId, auditMessage) =>
|
|
2387
2664
|
this._request("POST", `/api/v1/projects/${id}/writers/${userId}`, {
|
|
@@ -2391,6 +2668,7 @@ class PlaidClient {
|
|
|
2391
2668
|
* Remove a user's writer privileges for this project.
|
|
2392
2669
|
* @param {string} id - The resource ID
|
|
2393
2670
|
* @param {string} userId - The user ID
|
|
2671
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2394
2672
|
*/
|
|
2395
2673
|
removeWriter: (id, userId, auditMessage) =>
|
|
2396
2674
|
this._request("DELETE", `/api/v1/projects/${id}/writers/${userId}`, {
|
|
@@ -2400,6 +2678,7 @@ class PlaidClient {
|
|
|
2400
2678
|
* Set a user's access level to read-only for this project.
|
|
2401
2679
|
* @param {string} id - The resource ID
|
|
2402
2680
|
* @param {string} userId - The user ID
|
|
2681
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2403
2682
|
*/
|
|
2404
2683
|
addReader: (id, userId, auditMessage) =>
|
|
2405
2684
|
this._request("POST", `/api/v1/projects/${id}/readers/${userId}`, {
|
|
@@ -2409,45 +2688,18 @@ class PlaidClient {
|
|
|
2409
2688
|
* Remove a user's reader privileges for this project.
|
|
2410
2689
|
* @param {string} id - The resource ID
|
|
2411
2690
|
* @param {string} userId - The user ID
|
|
2691
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2412
2692
|
*/
|
|
2413
2693
|
removeReader: (id, userId, auditMessage) =>
|
|
2414
2694
|
this._request("DELETE", `/api/v1/projects/${id}/readers/${userId}`, {
|
|
2415
2695
|
auditMessage,
|
|
2416
2696
|
}),
|
|
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
|
-
),
|
|
2697
|
+
...configMethods(this, "projects"),
|
|
2447
2698
|
/**
|
|
2448
2699
|
* Assign a user as a maintainer for this project.
|
|
2449
2700
|
* @param {string} id - The resource ID
|
|
2450
2701
|
* @param {string} userId - The user ID
|
|
2702
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2451
2703
|
*/
|
|
2452
2704
|
addMaintainer: (id, userId, auditMessage) =>
|
|
2453
2705
|
this._request("POST", `/api/v1/projects/${id}/maintainers/${userId}`, {
|
|
@@ -2457,6 +2709,7 @@ class PlaidClient {
|
|
|
2457
2709
|
* Remove a user's maintainer privileges for this project.
|
|
2458
2710
|
* @param {string} id - The resource ID
|
|
2459
2711
|
* @param {string} userId - The user ID
|
|
2712
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2460
2713
|
*/
|
|
2461
2714
|
removeMaintainer: (id, userId, auditMessage) =>
|
|
2462
2715
|
this._request(
|
|
@@ -2468,7 +2721,8 @@ class PlaidClient {
|
|
|
2468
2721
|
* Get audit log for a project. Transparently follows pagination cursors
|
|
2469
2722
|
* and returns the full flat array. Each entry, and each of its `ops`,
|
|
2470
2723
|
* says what kind of credential made it as `credential`: `login`,
|
|
2471
|
-
* `named-token
|
|
2724
|
+
* `named-token` or `delegated` (absent on older operations, `service`
|
|
2725
|
+
* on some from 2026-10-06 to 2026-10-08),
|
|
2472
2726
|
* beside `apiToken` (the named token's id and name) when there was one.
|
|
2473
2727
|
* @param {string} projectId - The project ID
|
|
2474
2728
|
* @param {string} [startTime] - Start of time range
|
|
@@ -2495,6 +2749,10 @@ class PlaidClient {
|
|
|
2495
2749
|
* that may be long: audit() walks every page before it resolves.
|
|
2496
2750
|
* @param {string} projectId - The project ID
|
|
2497
2751
|
* @param {object} [opts]
|
|
2752
|
+
* @param {string} [opts.startTime] - Only entries at or after this instant
|
|
2753
|
+
* @param {string} [opts.endTime] - Only entries at or before this instant
|
|
2754
|
+
* @param {string[]|string} [opts.opTypes] - Only these op types
|
|
2755
|
+
* @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
|
|
2498
2756
|
* @param {"asc"|"desc"} [opts.order] - "desc" pages newest-first
|
|
2499
2757
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
2500
2758
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
@@ -2539,6 +2797,7 @@ class PlaidClient {
|
|
|
2539
2797
|
* Link a vocabulary to a project.
|
|
2540
2798
|
* @param {string} id - The resource ID
|
|
2541
2799
|
* @param {string} vocabId - The vocab layer ID
|
|
2800
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2542
2801
|
*/
|
|
2543
2802
|
linkVocab: (id, vocabId, auditMessage) =>
|
|
2544
2803
|
this._request("POST", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
|
|
@@ -2548,6 +2807,7 @@ class PlaidClient {
|
|
|
2548
2807
|
* Unlink a vocabulary from a project.
|
|
2549
2808
|
* @param {string} id - The resource ID
|
|
2550
2809
|
* @param {string} vocabId - The vocab layer ID
|
|
2810
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2551
2811
|
*/
|
|
2552
2812
|
unlinkVocab: (id, vocabId, auditMessage) =>
|
|
2553
2813
|
this._request("DELETE", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
|
|
@@ -2613,6 +2873,7 @@ class PlaidClient {
|
|
|
2613
2873
|
* Update a project's name.
|
|
2614
2874
|
* @param {string} id - The resource ID
|
|
2615
2875
|
* @param {string} name - The name
|
|
2876
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2616
2877
|
*/
|
|
2617
2878
|
update: (id, name, auditMessage) =>
|
|
2618
2879
|
this._request("PATCH", `/api/v1/projects/${id}`, {
|
|
@@ -2649,6 +2910,9 @@ class PlaidClient {
|
|
|
2649
2910
|
/**
|
|
2650
2911
|
* Create a new project. Note: this also registers the user as a maintainer.
|
|
2651
2912
|
* @param {string} name - The name
|
|
2913
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2914
|
+
* @param {object} [opts]
|
|
2915
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2652
2916
|
*/
|
|
2653
2917
|
create: (name, auditMessage, { id } = {}) =>
|
|
2654
2918
|
this._request("POST", "/api/v1/projects", {
|
|
@@ -2658,74 +2922,15 @@ class PlaidClient {
|
|
|
2658
2922
|
};
|
|
2659
2923
|
|
|
2660
2924
|
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
|
-
}),
|
|
2925
|
+
...configMethods(this, "text-layers"),
|
|
2926
|
+
...layerMethods(this, "text-layers"),
|
|
2725
2927
|
/**
|
|
2726
2928
|
* Create a new text layer for a project.
|
|
2727
2929
|
* @param {string} projectId - The project ID
|
|
2728
2930
|
* @param {string} name - The name
|
|
2931
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2932
|
+
* @param {object} [opts]
|
|
2933
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2729
2934
|
*/
|
|
2730
2935
|
create: (projectId, name, auditMessage, { id } = {}) =>
|
|
2731
2936
|
this._request("POST", "/api/v1/text-layers", {
|
|
@@ -2735,39 +2940,15 @@ class PlaidClient {
|
|
|
2735
2940
|
};
|
|
2736
2941
|
|
|
2737
2942
|
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
|
-
}),
|
|
2943
|
+
...metadataMethods(this, "vocab-items"),
|
|
2766
2944
|
/**
|
|
2767
2945
|
* Create a new vocab item
|
|
2768
2946
|
* @param {string} vocabLayerId - The vocab layer ID
|
|
2769
2947
|
* @param {string} form - The vocab item form
|
|
2770
2948
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
2949
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2950
|
+
* @param {object} [opts]
|
|
2951
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2771
2952
|
*/
|
|
2772
2953
|
create: (vocabLayerId, form, metadata, auditMessage, { id } = {}) =>
|
|
2773
2954
|
this._request("POST", "/api/v1/vocab-items", {
|
|
@@ -2778,6 +2959,8 @@ class PlaidClient {
|
|
|
2778
2959
|
* Create multiple vocab items in a single operation. Entries may target
|
|
2779
2960
|
* different vocab layers; the user must have write access to each.
|
|
2780
2961
|
* @param {Array<{vocabLayerId: string, form: string, metadata?: any}>} body - The vocab items to create
|
|
2962
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2963
|
+
* @returns {Promise<{ids: string[]}>}
|
|
2781
2964
|
*/
|
|
2782
2965
|
bulkCreate: (body, auditMessage) =>
|
|
2783
2966
|
this._request("POST", "/api/v1/vocab-items/bulk", {
|
|
@@ -2788,6 +2971,7 @@ class PlaidClient {
|
|
|
2788
2971
|
* Update many vocab items in a single operation: set forms and/or patch
|
|
2789
2972
|
* metadata.
|
|
2790
2973
|
* @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.
|
|
2974
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2791
2975
|
* @returns {Promise<{count: number}>} How many vocab items were updated.
|
|
2792
2976
|
*/
|
|
2793
2977
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -2802,6 +2986,8 @@ class PlaidClient {
|
|
|
2802
2986
|
* Every document holding one of those links has its version bumped, and
|
|
2803
2987
|
* a strict-mode client picks up their new versions from the response.
|
|
2804
2988
|
* @param {string[]} body - The vocab item IDs to delete
|
|
2989
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2990
|
+
* @returns {Promise<void>}
|
|
2805
2991
|
*/
|
|
2806
2992
|
bulkDelete: (body, auditMessage) =>
|
|
2807
2993
|
this._request("DELETE", "/api/v1/vocab-items/bulk", {
|
|
@@ -2844,6 +3030,7 @@ class PlaidClient {
|
|
|
2844
3030
|
* Needs maintainer rights on the vocabulary.
|
|
2845
3031
|
* @param {string} survivorId - The entry that stays
|
|
2846
3032
|
* @param {string[]} loserIds - The entries merged into it
|
|
3033
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2847
3034
|
* @returns {Promise<{moved: number, duplicates: number, removed: string[]}>}
|
|
2848
3035
|
*/
|
|
2849
3036
|
merge: (survivorId, loserIds, auditMessage) =>
|
|
@@ -2858,6 +3045,7 @@ class PlaidClient {
|
|
|
2858
3045
|
* from the response.
|
|
2859
3046
|
* @param {string} id - The resource ID
|
|
2860
3047
|
* @param {string} form - The vocab item form
|
|
3048
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2861
3049
|
*/
|
|
2862
3050
|
update: (id, form, auditMessage) =>
|
|
2863
3051
|
this._request("PATCH", `/api/v1/vocab-items/${id}`, {
|
|
@@ -2867,82 +3055,22 @@ class PlaidClient {
|
|
|
2867
3055
|
};
|
|
2868
3056
|
|
|
2869
3057
|
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
|
-
),
|
|
3058
|
+
...layerMethods(this, "relation-layers"),
|
|
2881
3059
|
/**
|
|
2882
3060
|
* Create a new relation layer.
|
|
2883
3061
|
* @param {string} spanLayerId - The span layer ID
|
|
2884
3062
|
* @param {string} name - The name
|
|
3063
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3064
|
+
* @param {object} [opts]
|
|
3065
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2885
3066
|
*/
|
|
2886
3067
|
create: (spanLayerId, name, auditMessage, { id } = {}) =>
|
|
2887
3068
|
this._request("POST", "/api/v1/relation-layers", {
|
|
2888
3069
|
auditMessage,
|
|
2889
3070
|
body: bodyOf({ id, "span-layer-id": spanLayerId, name }),
|
|
2890
3071
|
}),
|
|
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
|
-
),
|
|
3072
|
+
...configMethods(this, "relation-layers"),
|
|
2921
3073
|
...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
|
-
}),
|
|
2946
3074
|
};
|
|
2947
3075
|
|
|
2948
3076
|
this.tokens = {
|
|
@@ -2963,6 +3091,9 @@ class PlaidClient {
|
|
|
2963
3091
|
* @param {number} end - End offset, exclusive (Unicode code points)
|
|
2964
3092
|
* @param {number} [precedence] - Ordering precedence
|
|
2965
3093
|
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
3094
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3095
|
+
* @param {object} [opts]
|
|
3096
|
+
* @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
|
|
2966
3097
|
*/
|
|
2967
3098
|
create: (
|
|
2968
3099
|
tokenLayerId,
|
|
@@ -2996,6 +3127,7 @@ class PlaidClient {
|
|
|
2996
3127
|
* Delete a token and remove it from any spans. If this causes a span to
|
|
2997
3128
|
* have no remaining tokens, the span will also be deleted.
|
|
2998
3129
|
* @param {string} tokenId - The token ID
|
|
3130
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
2999
3131
|
*/
|
|
3000
3132
|
delete: (tokenId, auditMessage) =>
|
|
3001
3133
|
this._request("DELETE", `/api/v1/tokens/${tokenId}`, { auditMessage }),
|
|
@@ -3008,6 +3140,7 @@ class PlaidClient {
|
|
|
3008
3140
|
* to leave unchanged; pass a number to set; pass null explicitly to
|
|
3009
3141
|
* CLEAR it (revert to no explicit ordering). bodyOf keeps null but
|
|
3010
3142
|
* drops undefined, so the three cases map correctly to the server.
|
|
3143
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3011
3144
|
*/
|
|
3012
3145
|
update: (tokenId, begin, end, precedence, auditMessage) =>
|
|
3013
3146
|
this._request("PATCH", `/api/v1/tokens/${tokenId}`, {
|
|
@@ -3017,6 +3150,7 @@ class PlaidClient {
|
|
|
3017
3150
|
/**
|
|
3018
3151
|
* Create multiple tokens in a single operation.
|
|
3019
3152
|
* @param {Array} body - The request body
|
|
3153
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3020
3154
|
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
3021
3155
|
*/
|
|
3022
3156
|
bulkCreate: (body, auditMessage) =>
|
|
@@ -3024,12 +3158,15 @@ class PlaidClient {
|
|
|
3024
3158
|
/**
|
|
3025
3159
|
* Delete multiple tokens in a single operation. Provide an array of IDs.
|
|
3026
3160
|
* @param {Array} body - The request body
|
|
3161
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3162
|
+
* @returns {Promise<void>}
|
|
3027
3163
|
*/
|
|
3028
3164
|
bulkDelete: (body, auditMessage) =>
|
|
3029
3165
|
this._request("DELETE", "/api/v1/tokens/bulk", { auditMessage, body }),
|
|
3030
3166
|
/**
|
|
3031
3167
|
* Update many tokens in a single operation: patch metadata.
|
|
3032
3168
|
* @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.
|
|
3169
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3033
3170
|
* @returns {Promise<{count: number}>} How many tokens were updated.
|
|
3034
3171
|
*/
|
|
3035
3172
|
bulkUpdate: (body, auditMessage) =>
|
|
@@ -3068,6 +3205,7 @@ class PlaidClient {
|
|
|
3068
3205
|
* merged extent must not engulf a third token.
|
|
3069
3206
|
* @param {string} tokenId - The anchor token ID
|
|
3070
3207
|
* @param {string} otherTokenId - The other token to merge in
|
|
3208
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3071
3209
|
*/
|
|
3072
3210
|
merge: (tokenId, otherTokenId, auditMessage) =>
|
|
3073
3211
|
this._request("POST", `/api/v1/tokens/${tokenId}/merge`, {
|
|
@@ -3081,49 +3219,23 @@ class PlaidClient {
|
|
|
3081
3219
|
* @param {string} tokenId - The token ID
|
|
3082
3220
|
* @param {number} [begin] - New start offset, inclusive (Unicode code points)
|
|
3083
3221
|
* @param {number} [end] - New end offset, exclusive (Unicode code points)
|
|
3222
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3084
3223
|
*/
|
|
3085
3224
|
shift: (tokenId, begin, end, auditMessage) =>
|
|
3086
3225
|
this._request("POST", `/api/v1/tokens/${tokenId}/shift`, {
|
|
3087
3226
|
auditMessage,
|
|
3088
3227
|
body: bodyOf({ begin, end }),
|
|
3089
3228
|
}),
|
|
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
|
-
}),
|
|
3229
|
+
...metadataMethods(this, "tokens"),
|
|
3118
3230
|
};
|
|
3119
3231
|
|
|
3120
3232
|
this.messages = {
|
|
3121
3233
|
/**
|
|
3122
3234
|
* Open a Server-Sent Events stream for a project.
|
|
3123
3235
|
* @param {string} projectId - The UUID of the project to listen to
|
|
3124
|
-
* @param {
|
|
3236
|
+
* @param {(eventType: string, data: any) => void | boolean} onEvent - Callback function that receives (eventType, data). If it returns true, listening will stop.
|
|
3125
3237
|
* @param {string} [path] - Stream path under baseUrl (defaults to the project /listen bus; service channels pass their own).
|
|
3126
|
-
* @returns {
|
|
3238
|
+
* @returns {SSEConnection} SSE connection object with .close() and .getStats() methods
|
|
3127
3239
|
*/
|
|
3128
3240
|
listen: (projectId, onEvent, path) =>
|
|
3129
3241
|
createSSEConnection(this, projectId, onEvent, path),
|
|
@@ -3156,7 +3268,7 @@ class PlaidClient {
|
|
|
3156
3268
|
* connected services carry `online: true`; previously-seen offline ones
|
|
3157
3269
|
* carry `online: false` plus a `lastSeenAt` stamp.
|
|
3158
3270
|
* @param {string} projectId - The UUID of the project to query
|
|
3159
|
-
* @returns {Promise<
|
|
3271
|
+
* @returns {Promise<DiscoveredService[]>} Array of discovered service information
|
|
3160
3272
|
*/
|
|
3161
3273
|
discoverServices: (projectId) => discoverServices(this, projectId),
|
|
3162
3274
|
|
|
@@ -3176,14 +3288,14 @@ class PlaidClient {
|
|
|
3176
3288
|
* The registration reopens its channel whenever it drops, so a server
|
|
3177
3289
|
* restart needs no service restart.
|
|
3178
3290
|
* @param {string} projectId - The UUID of the project to serve
|
|
3179
|
-
* @param {
|
|
3180
|
-
* @param {
|
|
3291
|
+
* @param {ServiceInfo} serviceInfo - Service information {serviceId, serviceName, description}
|
|
3292
|
+
* @param {(data: any, responseHelper: ResponseHelper) => void} onServiceRequest - Callback (data, responseHelper)
|
|
3181
3293
|
* @param {Object} [extras] - Optional additional service metadata
|
|
3182
|
-
* @param {
|
|
3294
|
+
* @param {(event: ServiceStatusEvent, projectId: string, detail?: string) => void} [onStatus] - Optional callback (event, projectId, detail) for
|
|
3183
3295
|
* connection transitions: 'registered', 'reconnected', 'disconnected',
|
|
3184
3296
|
* and 'stopped' when the server refuses the channel for good, which
|
|
3185
3297
|
* ends the registration
|
|
3186
|
-
* @returns {
|
|
3298
|
+
* @returns {ServiceRegistration} Service registration with .stop(), .isRunning(), .isConnected()
|
|
3187
3299
|
*/
|
|
3188
3300
|
serve: (projectId, serviceInfo, onServiceRequest, extras, onStatus) =>
|
|
3189
3301
|
serve(this, projectId, serviceInfo, onServiceRequest, extras, onStatus),
|
|
@@ -3204,7 +3316,7 @@ class PlaidClient {
|
|
|
3204
3316
|
* @param {number} [timeout=10000] - Timeout in ms
|
|
3205
3317
|
* @param {function} [onProgress] - Called with each progress payload {percent, message}
|
|
3206
3318
|
* @param {AbortSignal} [signal] - Abort to stop waiting; rejects with an AbortError
|
|
3207
|
-
* @param {
|
|
3319
|
+
* @param {{requestId?: string, onAccepted?: (requestId: string) => void, projectIds?: string[], noOperation?: boolean}} [opts] - {requestId, onAccepted, projectIds, noOperation}
|
|
3208
3320
|
* @returns {Promise<any>} The service's result
|
|
3209
3321
|
*/
|
|
3210
3322
|
requestService: (
|
|
@@ -3349,11 +3461,13 @@ class PlaidClient {
|
|
|
3349
3461
|
* A comment outlives its anchor: deleting the entity does not delete
|
|
3350
3462
|
* the comment. `anchorLabel` is the caption shown once that happens
|
|
3351
3463
|
* ("Gloss of ktab, sentence 4"), so pass what the comment is about.
|
|
3352
|
-
* @param {
|
|
3464
|
+
* @param {CommentableType} entityType - What kind of thing is being commented on
|
|
3353
3465
|
* @param {string} entityId - The commented entity's id
|
|
3354
3466
|
* @param {string} body - The comment text (1..10000 characters)
|
|
3355
3467
|
* @param {object} [opts]
|
|
3356
3468
|
* @param {string} [opts.anchorLabel] - What the comment is about, in words (at most 200 characters)
|
|
3469
|
+
* @param {string} [opts.id] - The new comment's id, minted by the caller (see uuidv7)
|
|
3470
|
+
* @returns {Promise<Comment>}
|
|
3357
3471
|
*/
|
|
3358
3472
|
create: (entityType, entityId, body, { anchorLabel, id } = {}) =>
|
|
3359
3473
|
this._request("POST", "/api/v1/comments", {
|
|
@@ -3371,6 +3485,7 @@ class PlaidClient {
|
|
|
3371
3485
|
/**
|
|
3372
3486
|
* Read one comment.
|
|
3373
3487
|
* @param {string} id - The comment id
|
|
3488
|
+
* @returns {Promise<Comment>}
|
|
3374
3489
|
*/
|
|
3375
3490
|
get: (id) => this._request("GET", `/api/v1/comments/${id}`),
|
|
3376
3491
|
/**
|
|
@@ -3378,6 +3493,7 @@ class PlaidClient {
|
|
|
3378
3493
|
* maintainers, not admins. Sets `edited` on the comment.
|
|
3379
3494
|
* @param {string} id - The comment id
|
|
3380
3495
|
* @param {string} body - The replacement text
|
|
3496
|
+
* @returns {Promise<Comment>}
|
|
3381
3497
|
*/
|
|
3382
3498
|
update: (id, body) =>
|
|
3383
3499
|
this._request("PATCH", `/api/v1/comments/${id}`, {
|
|
@@ -3399,6 +3515,7 @@ class PlaidClient {
|
|
|
3399
3515
|
* @param {string} [filters.documentId] - Every comment anywhere in one document
|
|
3400
3516
|
* @param {string} [filters.entityType] - With entityId, one entity's thread
|
|
3401
3517
|
* @param {string} [filters.entityId] - With entityType, one entity's thread
|
|
3518
|
+
* @returns {Promise<Comment[]>}
|
|
3402
3519
|
*/
|
|
3403
3520
|
list: (projectId, { documentId, entityType, entityId } = {}) =>
|
|
3404
3521
|
listAll(this, `/api/v1/projects/${projectId}/comments`, {
|
|
@@ -3417,7 +3534,7 @@ class PlaidClient {
|
|
|
3417
3534
|
* @param {string} [opts.documentId] - Every comment anywhere in one document
|
|
3418
3535
|
* @param {string} [opts.entityType] - With entityId, one entity's thread
|
|
3419
3536
|
* @param {string} [opts.entityId] - With entityType, one entity's thread
|
|
3420
|
-
* @returns {Promise<
|
|
3537
|
+
* @returns {Promise<Page<Comment>>}
|
|
3421
3538
|
*/
|
|
3422
3539
|
listPage: (
|
|
3423
3540
|
projectId,
|
|
@@ -3440,7 +3557,7 @@ class PlaidClient {
|
|
|
3440
3557
|
* @param {string} [opts.documentId] - Every comment anywhere in one document
|
|
3441
3558
|
* @param {string} [opts.entityType] - With entityId, one entity's thread
|
|
3442
3559
|
* @param {string} [opts.entityId] - With entityType, one entity's thread
|
|
3443
|
-
* @returns {AsyncGenerator<
|
|
3560
|
+
* @returns {AsyncGenerator<Comment[]>}
|
|
3444
3561
|
*/
|
|
3445
3562
|
iterPages: (
|
|
3446
3563
|
projectId,
|
|
@@ -3463,7 +3580,7 @@ class PlaidClient {
|
|
|
3463
3580
|
* The response is NOT key-transformed: its keys are entity ids, and
|
|
3464
3581
|
* camelCasing would mangle the hyphens in a UUID.
|
|
3465
3582
|
* @param {string} projectId - The project to read
|
|
3466
|
-
* @param {
|
|
3583
|
+
* @param {{documentId?: string, entityType?: CommentableType, entityId?: string}} [filters] - Same filters as list()
|
|
3467
3584
|
* @returns {Promise<Object<string, number>>}
|
|
3468
3585
|
*/
|
|
3469
3586
|
counts: (projectId, { documentId, entityType, entityId } = {}) =>
|
|
@@ -3482,6 +3599,7 @@ class PlaidClient {
|
|
|
3482
3599
|
* @param {string} vocabId - The vocab layer to read
|
|
3483
3600
|
* @param {object} [filters]
|
|
3484
3601
|
* @param {string} [filters.entityId] - One entry's thread
|
|
3602
|
+
* @returns {Promise<Comment[]>}
|
|
3485
3603
|
*/
|
|
3486
3604
|
listInVocab: (vocabId, { entityId } = {}) =>
|
|
3487
3605
|
listAll(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3494,7 +3612,7 @@ class PlaidClient {
|
|
|
3494
3612
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
3495
3613
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
3496
3614
|
* @param {string} [opts.entityId] - One entry's thread
|
|
3497
|
-
* @returns {Promise<
|
|
3615
|
+
* @returns {Promise<Page<Comment>>}
|
|
3498
3616
|
*/
|
|
3499
3617
|
listInVocabPage: (vocabId, { limit, cursor, entityId } = {}) =>
|
|
3500
3618
|
listPage(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3509,7 +3627,7 @@ class PlaidClient {
|
|
|
3509
3627
|
* @param {object} [opts]
|
|
3510
3628
|
* @param {string} [opts.entityId] - One entry's thread
|
|
3511
3629
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
3512
|
-
* @returns {AsyncGenerator<
|
|
3630
|
+
* @returns {AsyncGenerator<Comment[]>}
|
|
3513
3631
|
*/
|
|
3514
3632
|
iterInVocabPages: (vocabId, { entityId, pageSize } = {}) =>
|
|
3515
3633
|
iterPages(this, `/api/v1/vocab-layers/${vocabId}/comments`, {
|
|
@@ -3537,15 +3655,15 @@ class PlaidClient {
|
|
|
3537
3655
|
|
|
3538
3656
|
this.events = {
|
|
3539
3657
|
/**
|
|
3540
|
-
* Record one research-telemetry event (core
|
|
3541
|
-
*
|
|
3658
|
+
* Record one research-telemetry event (plaid-core sql/client_event.clj).
|
|
3659
|
+
* Fire and forget: the event is buffered and sent with
|
|
3542
3660
|
* others every ten seconds, at fifty events, and when the page is
|
|
3543
3661
|
* hidden. A batch that fails is dropped, nothing is retried, and
|
|
3544
3662
|
* nothing is ever thrown. Sends nothing for a project whose switch
|
|
3545
3663
|
* (`config.plaid.research.telemetry`) is off, which it reads from the
|
|
3546
3664
|
* project itself. `suggestion.shown` is recorded once per target,
|
|
3547
3665
|
* field and value in a page session.
|
|
3548
|
-
* @param {
|
|
3666
|
+
* @param {ClientEventType} type - The event type
|
|
3549
3667
|
* @param {object} fields
|
|
3550
3668
|
* @param {string} fields.projectId - The project it happened in
|
|
3551
3669
|
* @param {string} [fields.documentId] - The document it happened in
|
|
@@ -3556,6 +3674,7 @@ class PlaidClient {
|
|
|
3556
3674
|
record: (type, fields) => recorderFor(this).record(this, type, fields),
|
|
3557
3675
|
/**
|
|
3558
3676
|
* Send what the recorder holds now, rather than at its next interval.
|
|
3677
|
+
* @returns {void}
|
|
3559
3678
|
*/
|
|
3560
3679
|
flush: () => recorderFor(this).flush(),
|
|
3561
3680
|
/**
|
|
@@ -3563,6 +3682,7 @@ class PlaidClient {
|
|
|
3563
3682
|
* maintainer changed it. Turning it off drops what is buffered.
|
|
3564
3683
|
* @param {string} projectId - The project
|
|
3565
3684
|
* @param {boolean} on - Whether telemetry is on
|
|
3685
|
+
* @returns {void}
|
|
3566
3686
|
*/
|
|
3567
3687
|
setEnabled: (projectId, on) => recorderFor(this).setEnabled(projectId, on),
|
|
3568
3688
|
/**
|
|
@@ -3587,6 +3707,7 @@ class PlaidClient {
|
|
|
3587
3707
|
* @param {string|string[]} [filters.types] - Only these types
|
|
3588
3708
|
* @param {string} [filters.startTime] - Only events the server stamped at or after this instant
|
|
3589
3709
|
* @param {string} [filters.endTime] - Only events the server stamped at or before this instant
|
|
3710
|
+
* @returns {Promise<ClientEvent[]>}
|
|
3590
3711
|
*/
|
|
3591
3712
|
list: (projectId, { types, startTime, endTime } = {}) =>
|
|
3592
3713
|
listAll(this, `/api/v1/projects/${projectId}/events`, {
|
|
@@ -3601,7 +3722,7 @@ class PlaidClient {
|
|
|
3601
3722
|
* @param {string|string[]} [opts.types] - Only these types
|
|
3602
3723
|
* @param {string} [opts.startTime] - Only events at or after this instant
|
|
3603
3724
|
* @param {string} [opts.endTime] - Only events at or before this instant
|
|
3604
|
-
* @returns {Promise<
|
|
3725
|
+
* @returns {Promise<Page<ClientEvent>>}
|
|
3605
3726
|
*/
|
|
3606
3727
|
listPage: (
|
|
3607
3728
|
projectId,
|
|
@@ -3620,7 +3741,7 @@ class PlaidClient {
|
|
|
3620
3741
|
* @param {string|string[]} [opts.types] - Only these types
|
|
3621
3742
|
* @param {string} [opts.startTime] - Only events at or after this instant
|
|
3622
3743
|
* @param {string} [opts.endTime] - Only events at or before this instant
|
|
3623
|
-
* @returns {AsyncGenerator<
|
|
3744
|
+
* @returns {AsyncGenerator<ClientEvent[]>}
|
|
3624
3745
|
*/
|
|
3625
3746
|
iterPages: (projectId, { pageSize, types, startTime, endTime } = {}) =>
|
|
3626
3747
|
iterPages(this, `/api/v1/projects/${projectId}/events`, {
|
|
@@ -3641,6 +3762,9 @@ class PlaidClient {
|
|
|
3641
3762
|
* @param {object} [opts]
|
|
3642
3763
|
* @param {string} [opts.body] - The Markdown text (up to 20000 characters; may be empty)
|
|
3643
3764
|
* @param {boolean} [opts.pinned] - Send this one to the assistant in full on every turn
|
|
3765
|
+
* @param {string} [opts.id] - The new guideline's id, minted by the caller (see uuidv7)
|
|
3766
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3767
|
+
* @returns {Promise<{id: string}>}
|
|
3644
3768
|
*/
|
|
3645
3769
|
create: (projectId, title, { body, pinned, id } = {}, auditMessage) =>
|
|
3646
3770
|
this._request("POST", `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3650,6 +3774,7 @@ class PlaidClient {
|
|
|
3650
3774
|
/**
|
|
3651
3775
|
* Read one guideline, Markdown body included.
|
|
3652
3776
|
* @param {string} id - The guideline id
|
|
3777
|
+
* @returns {Promise<Guideline>}
|
|
3653
3778
|
*/
|
|
3654
3779
|
get: (id) => this._request("GET", `/api/v1/guidelines/${id}`),
|
|
3655
3780
|
/**
|
|
@@ -3666,6 +3791,8 @@ class PlaidClient {
|
|
|
3666
3791
|
* @param {string} [changes.body] - The new Markdown text
|
|
3667
3792
|
* @param {boolean} [changes.pinned] - Whether the assistant always gets it in full
|
|
3668
3793
|
* @param {string} [changes.expectedUpdatedAt] - Write only if this is still the stored updatedAt
|
|
3794
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3795
|
+
* @returns {Promise<Guideline>}
|
|
3669
3796
|
*/
|
|
3670
3797
|
update: (
|
|
3671
3798
|
id,
|
|
@@ -3680,6 +3807,7 @@ class PlaidClient {
|
|
|
3680
3807
|
/**
|
|
3681
3808
|
* Delete a guideline.
|
|
3682
3809
|
* @param {string} id - The guideline id
|
|
3810
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
3683
3811
|
*/
|
|
3684
3812
|
delete: (id, auditMessage) =>
|
|
3685
3813
|
this._request("DELETE", `/api/v1/guidelines/${id}`, { auditMessage }),
|
|
@@ -3694,6 +3822,7 @@ class PlaidClient {
|
|
|
3694
3822
|
* @param {string} projectId - The project to read
|
|
3695
3823
|
* @param {object} [opts]
|
|
3696
3824
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
3825
|
+
* @returns {Promise<Guideline[]>}
|
|
3697
3826
|
*/
|
|
3698
3827
|
list: (projectId, { includeBodies } = {}) =>
|
|
3699
3828
|
listAll(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3706,7 +3835,7 @@ class PlaidClient {
|
|
|
3706
3835
|
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
3707
3836
|
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
3708
3837
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
3709
|
-
* @returns {Promise<
|
|
3838
|
+
* @returns {Promise<Page<Guideline>>}
|
|
3710
3839
|
*/
|
|
3711
3840
|
listPage: (projectId, { limit, cursor, includeBodies } = {}) =>
|
|
3712
3841
|
listPage(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3720,7 +3849,7 @@ class PlaidClient {
|
|
|
3720
3849
|
* @param {object} [opts]
|
|
3721
3850
|
* @param {number} [opts.pageSize] - Per-request page size
|
|
3722
3851
|
* @param {boolean} [opts.includeBodies] - Return each body instead of its length
|
|
3723
|
-
* @returns {AsyncGenerator<
|
|
3852
|
+
* @returns {AsyncGenerator<Guideline[]>}
|
|
3724
3853
|
*/
|
|
3725
3854
|
iterPages: (projectId, { pageSize, includeBodies } = {}) =>
|
|
3726
3855
|
iterPages(this, `/api/v1/projects/${projectId}/guidelines`, {
|
|
@@ -3732,6 +3861,7 @@ class PlaidClient {
|
|
|
3732
3861
|
|
|
3733
3862
|
// --- Core methods ---
|
|
3734
3863
|
|
|
3864
|
+
/** @private */
|
|
3735
3865
|
async _request(method, path, options = {}) {
|
|
3736
3866
|
return makeRequest(this, method, path, options);
|
|
3737
3867
|
}
|
|
@@ -3794,7 +3924,7 @@ class PlaidClient {
|
|
|
3794
3924
|
* matching end, and the outer numbering resumes after it.
|
|
3795
3925
|
*
|
|
3796
3926
|
* @param {string} message - Human label for the operation.
|
|
3797
|
-
* @param {
|
|
3927
|
+
* @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
3928
|
* @returns {string} The operation's group id.
|
|
3799
3929
|
*/
|
|
3800
3930
|
beginOperation(message, { groupId, kind, ref, keys, minted } = {}) {
|
|
@@ -3843,7 +3973,7 @@ class PlaidClient {
|
|
|
3843
3973
|
* A seed for the Idempotency-Keys of a logical operation that may be run
|
|
3844
3974
|
* again from the top (see beginOperation's `keys`). Keep it with the work
|
|
3845
3975
|
* it belongs to and pass it to every run.
|
|
3846
|
-
* @returns {
|
|
3976
|
+
* @returns {KeySeed}
|
|
3847
3977
|
*/
|
|
3848
3978
|
keySeed() {
|
|
3849
3979
|
return { seed: uuidv7(), stamps: new Map() };
|
|
@@ -3898,10 +4028,11 @@ class PlaidClient {
|
|
|
3898
4028
|
*
|
|
3899
4029
|
* await client.withOperation('Import ELAN corpus', run, { kind: 'import', ref: 'format:elan' });
|
|
3900
4030
|
*
|
|
4031
|
+
* @template T
|
|
3901
4032
|
* @param {string} message - Human label for the operation.
|
|
3902
|
-
* @param {
|
|
3903
|
-
* @param {
|
|
3904
|
-
* @returns {Promise<
|
|
4033
|
+
* @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.
|
|
4034
|
+
* @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.
|
|
4035
|
+
* @returns {Promise<T>} Whatever `fn` resolves to.
|
|
3905
4036
|
*/
|
|
3906
4037
|
async withOperation(message, fn, { kind, ref, groupId, keys, minted } = {}) {
|
|
3907
4038
|
this.beginOperation(message, { kind, ref, groupId, keys, minted });
|
|
@@ -3956,7 +4087,7 @@ class PlaidClient {
|
|
|
3956
4087
|
* parents created earlier in the same batch, and any op's failure rolls the
|
|
3957
4088
|
* whole batch back. A batch is not nestable. Prefer `batched()`, which
|
|
3958
4089
|
* submits or aborts for you.
|
|
3959
|
-
* @returns {
|
|
4090
|
+
* @returns {PlaidBatch} the batch view
|
|
3960
4091
|
*/
|
|
3961
4092
|
batch() {
|
|
3962
4093
|
return openBatch(this);
|
|
@@ -3968,6 +4099,7 @@ class PlaidClient {
|
|
|
3968
4099
|
* recased: the headers keep the server's spelling. `key` is the request's
|
|
3969
4100
|
* Idempotency-Key: a batch whose answer is lost (no response, 502, 504) is
|
|
3970
4101
|
* sent again under it, and answered from the first send if that landed.
|
|
4102
|
+
* @private
|
|
3971
4103
|
*/
|
|
3972
4104
|
async _postBatch(url, body, { key = uuidv7() } = {}) {
|
|
3973
4105
|
const fetchOptions = {
|
|
@@ -4100,8 +4232,8 @@ class PlaidClient {
|
|
|
4100
4232
|
*
|
|
4101
4233
|
* A write made on `client` inside `fn` is not part of the batch: it goes
|
|
4102
4234
|
* over the wire at once, as it would anywhere else. See `batch()`.
|
|
4103
|
-
* @param {(batch:
|
|
4104
|
-
* @returns {Promise<
|
|
4235
|
+
* @param {(batch: PlaidBatch) => (void | Promise<void>)} fn
|
|
4236
|
+
* @returns {Promise<any[]>}
|
|
4105
4237
|
*/
|
|
4106
4238
|
async batched(fn) {
|
|
4107
4239
|
const batch = this.batch();
|
|
@@ -4119,20 +4251,22 @@ class PlaidClient {
|
|
|
4119
4251
|
* instance — for a launcher or status page checking whether a server is up
|
|
4120
4252
|
* before anyone logs in.
|
|
4121
4253
|
* @param {string} baseUrl - The API base URL
|
|
4122
|
-
* @param {
|
|
4254
|
+
* @param {{timeout?: number | null}} [options]
|
|
4255
|
+
* @returns {Promise<ServerHealth>}
|
|
4123
4256
|
*/
|
|
4124
4257
|
static async health(baseUrl, options = {}) {
|
|
4125
|
-
return
|
|
4258
|
+
return anonymousRequest("GET", baseUrl, "/health", undefined, options);
|
|
4126
4259
|
}
|
|
4127
4260
|
|
|
4128
4261
|
/**
|
|
4129
4262
|
* The limits this server enforces, with NO authentication and no client
|
|
4130
4263
|
* instance. See the instance method for the shape.
|
|
4131
4264
|
* @param {string} baseUrl - The API base URL
|
|
4132
|
-
* @param {
|
|
4265
|
+
* @param {{timeout?: number | null}} [options]
|
|
4266
|
+
* @returns {Promise<{limits: ServerLimits}>}
|
|
4133
4267
|
*/
|
|
4134
4268
|
static async info(baseUrl, options = {}) {
|
|
4135
|
-
return
|
|
4269
|
+
return anonymousRequest("GET", baseUrl, "/api/v1/info", undefined, options);
|
|
4136
4270
|
}
|
|
4137
4271
|
|
|
4138
4272
|
/**
|
|
@@ -4163,7 +4297,7 @@ class PlaidClient {
|
|
|
4163
4297
|
* @returns {Promise<{kind: string, status: string, expiresAt: string, projectName?: string, projectRole?: string, email?: string, grantAdmin: boolean}>}
|
|
4164
4298
|
*/
|
|
4165
4299
|
static async lookupInvite(baseUrl, code, options = {}) {
|
|
4166
|
-
return
|
|
4300
|
+
return anonymousRequest("POST", baseUrl, "/api/v1/invites/lookup", { code }, options);
|
|
4167
4301
|
}
|
|
4168
4302
|
|
|
4169
4303
|
/**
|
|
@@ -4192,10 +4326,16 @@ class PlaidClient {
|
|
|
4192
4326
|
{ email, password, displayName } = {},
|
|
4193
4327
|
options = {},
|
|
4194
4328
|
) {
|
|
4195
|
-
const data = await
|
|
4329
|
+
const data = await anonymousRequest(
|
|
4330
|
+
"POST",
|
|
4196
4331
|
baseUrl,
|
|
4197
4332
|
"/api/v1/invites/redeem",
|
|
4198
|
-
bodyOf({
|
|
4333
|
+
bodyOf({
|
|
4334
|
+
code,
|
|
4335
|
+
email: normalizeUserId(email),
|
|
4336
|
+
password,
|
|
4337
|
+
"display-name": displayName,
|
|
4338
|
+
}),
|
|
4199
4339
|
options,
|
|
4200
4340
|
);
|
|
4201
4341
|
return {
|
|
@@ -4220,35 +4360,14 @@ class PlaidClient {
|
|
|
4220
4360
|
*/
|
|
4221
4361
|
static async login(baseUrl, userId, password, options = {}) {
|
|
4222
4362
|
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
|
-
}
|
|
4363
|
+
const data = await anonymousRequest(
|
|
4364
|
+
"POST",
|
|
4365
|
+
baseUrl,
|
|
4366
|
+
"/api/v1/login",
|
|
4367
|
+
{ "user-id": normalizeUserId(userId), password },
|
|
4368
|
+
options,
|
|
4369
|
+
);
|
|
4370
|
+
return new PlaidClient(baseUrl, data.token || "", options);
|
|
4252
4371
|
}
|
|
4253
4372
|
}
|
|
4254
4373
|
|
|
@@ -4276,9 +4395,129 @@ function configRequest(auditMessage, value, options, isDelete = false) {
|
|
|
4276
4395
|
};
|
|
4277
4396
|
}
|
|
4278
4397
|
|
|
4398
|
+
/**
|
|
4399
|
+
* The three metadata methods of a bundle whose entities carry metadata, on
|
|
4400
|
+
* `/api/v1/<kind>/<id>/metadata`. Metadata is opaque, so no body is re-cased.
|
|
4401
|
+
* @param {PlaidClient} client
|
|
4402
|
+
* @param {string} kind
|
|
4403
|
+
*/
|
|
4404
|
+
function metadataMethods(client, kind) {
|
|
4405
|
+
const path = (id) => `/api/v1/${kind}/${id}/metadata`;
|
|
4406
|
+
return {
|
|
4407
|
+
/**
|
|
4408
|
+
* Replace all metadata of an entity. Keys left out of the new map are removed.
|
|
4409
|
+
* @param {string} id - The entity's ID
|
|
4410
|
+
* @param {object} body - The new metadata map
|
|
4411
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4412
|
+
*/
|
|
4413
|
+
setMetadata: (id, body, auditMessage) =>
|
|
4414
|
+
client._request("PUT", path(id), { auditMessage, rawBody: body }),
|
|
4415
|
+
/**
|
|
4416
|
+
* Remove all metadata from an entity.
|
|
4417
|
+
* @param {string} id - The entity's ID
|
|
4418
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4419
|
+
*/
|
|
4420
|
+
deleteMetadata: (id, auditMessage) => client._request("DELETE", path(id), { auditMessage }),
|
|
4421
|
+
/**
|
|
4422
|
+
* 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.
|
|
4423
|
+
* @param {string} id - The entity's ID
|
|
4424
|
+
* @param {MetadataOp[]} body - The metadata ops
|
|
4425
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4426
|
+
*/
|
|
4427
|
+
patchMetadata: (id, body, auditMessage) =>
|
|
4428
|
+
client._request("PATCH", path(id), { auditMessage, rawBody: body }),
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4431
|
+
|
|
4432
|
+
/**
|
|
4433
|
+
* The two config methods of a bundle whose records carry editor config, on
|
|
4434
|
+
* `/api/v1/<kind>/<id>/config/<namespace>/<key>`.
|
|
4435
|
+
* @param {PlaidClient} client
|
|
4436
|
+
* @param {string} kind
|
|
4437
|
+
*/
|
|
4438
|
+
function configMethods(client, kind) {
|
|
4439
|
+
const path = (id, namespace, configKey) => `/api/v1/${kind}/${id}/config/${namespace}/${configKey}`;
|
|
4440
|
+
return {
|
|
4441
|
+
/**
|
|
4442
|
+
* Set a configuration value in an editor namespace.
|
|
4443
|
+
* @param {string} id - The record's ID
|
|
4444
|
+
* @param {string} namespace - The config namespace
|
|
4445
|
+
* @param {string} configKey - The config key
|
|
4446
|
+
* @param {any} configValue - Configuration value to set
|
|
4447
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4448
|
+
* @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
|
|
4449
|
+
* was absent). The write is then refused with a 409 when the stored value differs.
|
|
4450
|
+
*/
|
|
4451
|
+
setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
|
|
4452
|
+
client._request(
|
|
4453
|
+
"PUT",
|
|
4454
|
+
path(id, namespace, configKey),
|
|
4455
|
+
configRequest(auditMessage, configValue, options),
|
|
4456
|
+
),
|
|
4457
|
+
/**
|
|
4458
|
+
* Remove a configuration value.
|
|
4459
|
+
* @param {string} id - The record's ID
|
|
4460
|
+
* @param {string} namespace - The config namespace
|
|
4461
|
+
* @param {string} configKey - The config key
|
|
4462
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4463
|
+
* @param {{expected?: any}} [options] - `expected`, as on setConfig.
|
|
4464
|
+
*/
|
|
4465
|
+
deleteConfig: (id, namespace, configKey, auditMessage, options) =>
|
|
4466
|
+
client._request(
|
|
4467
|
+
"DELETE",
|
|
4468
|
+
path(id, namespace, configKey),
|
|
4469
|
+
configRequest(auditMessage, undefined, options, true),
|
|
4470
|
+
),
|
|
4471
|
+
};
|
|
4472
|
+
}
|
|
4473
|
+
|
|
4474
|
+
/**
|
|
4475
|
+
* The get, delete, update and shift methods of the four layer bundles, on
|
|
4476
|
+
* `/api/v1/<kind>/<layerId>`.
|
|
4477
|
+
* @param {PlaidClient} client
|
|
4478
|
+
* @param {string} kind
|
|
4479
|
+
*/
|
|
4480
|
+
function layerMethods(client, kind) {
|
|
4481
|
+
const path = (layerId) => `/api/v1/${kind}/${layerId}`;
|
|
4482
|
+
return {
|
|
4483
|
+
/**
|
|
4484
|
+
* Get a layer by ID.
|
|
4485
|
+
* @param {string} layerId - The layer ID
|
|
4486
|
+
*/
|
|
4487
|
+
get: (layerId) => client._request("GET", path(layerId)),
|
|
4488
|
+
/**
|
|
4489
|
+
* Delete a layer.
|
|
4490
|
+
* @param {string} layerId - The layer ID
|
|
4491
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4492
|
+
*/
|
|
4493
|
+
delete: (layerId, auditMessage) => client._request("DELETE", path(layerId), { auditMessage }),
|
|
4494
|
+
/**
|
|
4495
|
+
* Rename a layer.
|
|
4496
|
+
* @param {string} layerId - The layer ID
|
|
4497
|
+
* @param {string} name - The new name
|
|
4498
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4499
|
+
*/
|
|
4500
|
+
update: (layerId, name, auditMessage) =>
|
|
4501
|
+
client._request("PATCH", path(layerId), { auditMessage, body: bodyOf({ name }) }),
|
|
4502
|
+
/**
|
|
4503
|
+
* Move a layer one place in its display order.
|
|
4504
|
+
* @param {string} layerId - The layer ID
|
|
4505
|
+
* @param {string} direction - The direction ("up" or "down")
|
|
4506
|
+
* @param {string} [auditMessage] - Audit message for this write
|
|
4507
|
+
*/
|
|
4508
|
+
shift: (layerId, direction, auditMessage) =>
|
|
4509
|
+
client._request("POST", `${path(layerId)}/shift`, {
|
|
4510
|
+
auditMessage,
|
|
4511
|
+
body: bodyOf({ direction }),
|
|
4512
|
+
}),
|
|
4513
|
+
};
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4279
4516
|
/**
|
|
4280
4517
|
* The four layer-constraint methods of a layer bundle, on `/api/v1/<kind>`.
|
|
4281
4518
|
* See constraints.js and the core manual, "Layer constraints".
|
|
4519
|
+
* @param {PlaidClient} client
|
|
4520
|
+
* @param {string} kind
|
|
4282
4521
|
*/
|
|
4283
4522
|
function constraintMethods(client, kind) {
|
|
4284
4523
|
const base = (layerId) => `/api/v1/${kind}/${layerId}/constraints`;
|
|
@@ -4289,12 +4528,12 @@ function constraintMethods(client, kind) {
|
|
|
4289
4528
|
* stored data breaks them (see violationsOf).
|
|
4290
4529
|
* @param {string} layerId - The layer ID
|
|
4291
4530
|
* @param {string} namespace - The declaring app's namespace, e.g. "igt"
|
|
4292
|
-
* @param {
|
|
4531
|
+
* @param {LayerConstraint[]} constraints - e.g. [{type: "single-span"}, {type: "value-set", values: ["N"]}]
|
|
4293
4532
|
* @param {string} [auditMessage] - Audit message for this write
|
|
4294
4533
|
* @param {{expected?: Array<object>|null}} [options] - `expected`: the list read for this
|
|
4295
4534
|
* namespace (null when it was absent). The write is then refused with a 409 when the stored
|
|
4296
4535
|
* list differs.
|
|
4297
|
-
* @returns {Promise<{constraints:
|
|
4536
|
+
* @returns {Promise<{constraints: Record<string, LayerConstraint[]>}>} the layer's whole constraint map
|
|
4298
4537
|
*/
|
|
4299
4538
|
setConstraints: (layerId, namespace, constraints, auditMessage, options) =>
|
|
4300
4539
|
client._request("PUT", `${base(layerId)}/${namespace}`, {
|
|
@@ -4319,8 +4558,8 @@ function constraintMethods(client, kind) {
|
|
|
4319
4558
|
* The violations the given constraints would meet in this layer's stored
|
|
4320
4559
|
* data. Writes nothing.
|
|
4321
4560
|
* @param {string} layerId - The layer ID
|
|
4322
|
-
* @param {
|
|
4323
|
-
* @returns {Promise<{violations:
|
|
4561
|
+
* @param {LayerConstraint[]} constraints - The list to check
|
|
4562
|
+
* @returns {Promise<{violations: ConstraintViolation[], violationCount: number}>}
|
|
4324
4563
|
*/
|
|
4325
4564
|
checkConstraints: (layerId, constraints) =>
|
|
4326
4565
|
client._request("POST", `${base(layerId)}/check`, { body: { constraints } }),
|
|
@@ -4332,10 +4571,10 @@ function constraintMethods(client, kind) {
|
|
|
4332
4571
|
* ask. A document another user holds the lock on is left as it is and
|
|
4333
4572
|
* listed under `locked`.
|
|
4334
4573
|
* @param {string} layerId - The layer ID
|
|
4335
|
-
* @param {
|
|
4574
|
+
* @param {LayerConstraint[]} constraints - The list to repair for
|
|
4336
4575
|
* @param {string} [auditMessage] - Audit message for this write
|
|
4337
4576
|
* @param {{document?: string}} [options]
|
|
4338
|
-
* @returns {Promise<{repaired:
|
|
4577
|
+
* @returns {Promise<{repaired: ConstraintRepair[], locked: Array<{document: string, lockedBy: string}>, violations: ConstraintViolation[], violationCount: number}>}
|
|
4339
4578
|
*/
|
|
4340
4579
|
repairConstraints: (layerId, constraints, auditMessage, options) =>
|
|
4341
4580
|
client._request("POST", `${base(layerId)}/repair`, {
|
|
@@ -4357,6 +4596,7 @@ export {
|
|
|
4357
4596
|
cpSlicer,
|
|
4358
4597
|
utf16ToCp,
|
|
4359
4598
|
cpIndexOf,
|
|
4599
|
+
composeText,
|
|
4360
4600
|
} from "./codepoint.js";
|
|
4361
4601
|
export { composeTextEdits, gapsToOps, applyTextOps } from "./textEdits.js";
|
|
4362
4602
|
export {
|
|
@@ -4431,6 +4671,6 @@ export { createdId, createdIds } from "./created.js";
|
|
|
4431
4671
|
// UUIDv7 ids for what a client creates. See ./ids.js and the manual,
|
|
4432
4672
|
// "Retrying a write". uuidv4 works where crypto.randomUUID does not (a page
|
|
4433
4673
|
// served over plain HTTP from any host but localhost).
|
|
4434
|
-
export { uuidv4, uuidv7 } from "./ids.js";
|
|
4674
|
+
export { uuidv4, uuidv7, normalizeUserId } from "./ids.js";
|
|
4435
4675
|
export { wasReplayed } from "./replayed.js";
|
|
4436
4676
|
export { MAX_BATCH_OPS };
|