@larc-iu/plaid-client 0.2.0-alpha.3 → 0.2.0-alpha.30
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 +39 -0
- package/package.json +13 -6
- package/src/apiAnnotations.d.ts +508 -0
- package/src/apiAnnotations.js +543 -0
- package/src/apiDocuments.d.ts +569 -0
- package/src/apiDocuments.js +551 -0
- package/src/apiLayers.d.ts +369 -0
- package/src/apiLayers.js +222 -0
- package/src/apiMessages.d.ts +293 -0
- package/src/apiMessages.js +265 -0
- package/src/apiPeople.d.ts +626 -0
- package/src/apiPeople.js +698 -0
- package/src/apiProject.d.ts +761 -0
- package/src/apiProject.js +774 -0
- package/src/apiServer.d.ts +455 -0
- package/src/apiServer.js +422 -0
- package/src/apiVocab.d.ts +456 -0
- package/src/apiVocab.js +425 -0
- package/src/batch.d.ts +54 -0
- package/src/batch.js +323 -0
- package/src/batchRef.d.ts +30 -0
- package/src/batchRef.js +128 -0
- package/src/client.d.ts +1321 -0
- package/src/client.js +743 -0
- package/src/codepoint.d.ts +94 -0
- package/src/codepoint.js +131 -1
- package/src/constraints.d.ts +85 -0
- package/src/constraints.js +112 -0
- package/src/created.d.ts +2 -0
- package/src/created.js +29 -0
- package/src/documentLock.d.ts +172 -0
- package/src/documentLock.js +384 -0
- package/src/entryForms.d.ts +63 -0
- package/src/entryForms.js +152 -0
- package/src/events.d.ts +60 -0
- package/src/events.js +271 -0
- package/src/families.d.ts +349 -0
- package/src/families.js +396 -0
- package/src/http.d.ts +233 -0
- package/src/http.js +809 -93
- package/src/ids.d.ts +26 -0
- package/src/ids.js +96 -0
- package/src/index.d.ts +101 -0
- package/src/index.js +183 -2105
- package/src/metadataOps.d.ts +15 -0
- package/src/metadataOps.js +124 -0
- package/src/operations.d.ts +36 -0
- package/src/operations.js +164 -0
- package/src/pagination.d.ts +49 -0
- package/src/pagination.js +20 -53
- package/src/provenance.d.ts +243 -0
- package/src/provenance.js +458 -36
- package/src/replayed.d.ts +3 -0
- package/src/replayed.js +31 -0
- package/src/requester.d.ts +60 -0
- package/src/requester.js +74 -0
- package/src/roles.d.ts +104 -0
- package/src/roles.js +64 -2
- package/src/serviceSchema.d.ts +337 -0
- package/src/serviceSchema.js +177 -60
- package/src/services.d.ts +319 -0
- package/src/services.js +742 -58
- package/src/sse.d.ts +30 -0
- package/src/sse.js +80 -3
- package/src/testing.d.ts +128 -0
- package/src/testing.js +379 -0
- package/src/textEdits.d.ts +59 -0
- package/src/textEdits.js +234 -0
- package/src/transforms.d.ts +25 -0
- package/src/transforms.js +11 -1
- package/src/types.d.ts +867 -0
- package/src/types.js +663 -0
- package/index.d.ts +0 -532
package/src/index.js
CHANGED
|
@@ -2,2112 +2,131 @@
|
|
|
2
2
|
* plaid-client - JavaScript client for the Plaid annotation API
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
makeNetworkError, timeoutSignal, DEFAULT_TIMEOUT_MS,
|
|
9
|
-
} from './http.js';
|
|
10
|
-
import { listAll, listPage, iterPages } from './pagination.js';
|
|
11
|
-
import { createSSEConnection } from './sse.js';
|
|
12
|
-
import {
|
|
13
|
-
discoverServices,
|
|
14
|
-
discardService,
|
|
15
|
-
serve,
|
|
16
|
-
requestService,
|
|
17
|
-
} from './services.js';
|
|
5
|
+
// ---- Types --------------------------------------------------------------
|
|
6
|
+
// The shapes the API answers with and takes are in types.js, named here so
|
|
7
|
+
// this module's JSDoc can use them and the package root exports them.
|
|
18
8
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Create a new vocab link between tokens and a vocab item.
|
|
104
|
-
* @param {string} vocabItem - The vocab item to link
|
|
105
|
-
* @param {Array} tokens - The tokens to link
|
|
106
|
-
* @param {any} [metadata] - Metadata for the link. Omit to leave unset; pass null to send JSON null.
|
|
107
|
-
*/
|
|
108
|
-
create: (vocabItem, tokens, metadata, auditMessage) =>
|
|
109
|
-
this._request('POST', '/api/v1/vocab-links', { auditMessage,
|
|
110
|
-
body: bodyOf({ 'vocab-item': vocabItem, tokens, metadata }),
|
|
111
|
-
}),
|
|
112
|
-
/**
|
|
113
|
-
* Create multiple vocab links in a single operation. Entries may
|
|
114
|
-
* reference different vocab items, but all tokens across the call must
|
|
115
|
-
* belong to one document.
|
|
116
|
-
* @param {Array<{vocabItem: string, tokens: string[], metadata?: any}>} body - The vocab links to create
|
|
117
|
-
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
118
|
-
*/
|
|
119
|
-
bulkCreate: (body, auditMessage) =>
|
|
120
|
-
this._request('POST', '/api/v1/vocab-links/bulk', { auditMessage, body }),
|
|
121
|
-
/**
|
|
122
|
-
* Delete multiple vocab links in a single operation. Provide an array of IDs.
|
|
123
|
-
* @param {string[]} body - The vocab link IDs to delete
|
|
124
|
-
*/
|
|
125
|
-
bulkDelete: (body, auditMessage) =>
|
|
126
|
-
this._request('DELETE', '/api/v1/vocab-links/bulk', { auditMessage, body }),
|
|
127
|
-
/**
|
|
128
|
-
* Replace all metadata for a vocab link. The entire metadata map is replaced - existing metadata keys not included in the request will be removed.
|
|
129
|
-
* @param {string} id - The resource ID
|
|
130
|
-
* @param {any} body - The request body
|
|
131
|
-
*/
|
|
132
|
-
setMetadata: (id, body, auditMessage) =>
|
|
133
|
-
this._request('PUT', `/api/v1/vocab-links/${id}/metadata`, { auditMessage,
|
|
134
|
-
rawBody: body, skipResponseTransform: true,
|
|
135
|
-
}),
|
|
136
|
-
/**
|
|
137
|
-
* Remove all metadata from a vocab link.
|
|
138
|
-
* @param {string} id - The resource ID
|
|
139
|
-
*/
|
|
140
|
-
deleteMetadata: (id, auditMessage) =>
|
|
141
|
-
this._request('DELETE', `/api/v1/vocab-links/${id}/metadata`, { auditMessage,
|
|
142
|
-
skipResponseTransform: true,
|
|
143
|
-
}),
|
|
144
|
-
/**
|
|
145
|
-
* Patch (shallow-merge) metadata for a vocab link. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
146
|
-
* @param {string} id - The resource ID
|
|
147
|
-
* @param {any} body - The metadata patch
|
|
148
|
-
*/
|
|
149
|
-
patchMetadata: (id, body, auditMessage) =>
|
|
150
|
-
this._request('PATCH', `/api/v1/vocab-links/${id}/metadata`, { auditMessage,
|
|
151
|
-
rawBody: body, skipResponseTransform: true,
|
|
152
|
-
}),
|
|
153
|
-
/**
|
|
154
|
-
* Get a vocab link by ID
|
|
155
|
-
* @param {string} id - The resource ID
|
|
156
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
157
|
-
*/
|
|
158
|
-
get: (id, asOf) =>
|
|
159
|
-
this._request('GET', `/api/v1/vocab-links/${id}`, {
|
|
160
|
-
queryParams: { 'as-of': asOf },
|
|
161
|
-
}),
|
|
162
|
-
/**
|
|
163
|
-
* Delete a vocab link
|
|
164
|
-
* @param {string} id - The resource ID
|
|
165
|
-
*/
|
|
166
|
-
delete: (id, auditMessage) =>
|
|
167
|
-
this._request('DELETE', `/api/v1/vocab-links/${id}`, { auditMessage }),
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
this.vocabLayers = {
|
|
171
|
-
/**
|
|
172
|
-
* Get a vocab layer by ID
|
|
173
|
-
* @param {string} id - The resource ID
|
|
174
|
-
* @param {boolean} [includeItems] - Include vocab items
|
|
175
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
176
|
-
*/
|
|
177
|
-
get: (id, includeItems, asOf) =>
|
|
178
|
-
this._request('GET', `/api/v1/vocab-layers/${id}`, {
|
|
179
|
-
queryParams: { 'include-items': includeItems, 'as-of': asOf },
|
|
180
|
-
}),
|
|
181
|
-
/**
|
|
182
|
-
* Delete a vocab layer.
|
|
183
|
-
* @param {string} id - The resource ID
|
|
184
|
-
*/
|
|
185
|
-
delete: (id, auditMessage) =>
|
|
186
|
-
this._request('DELETE', `/api/v1/vocab-layers/${id}`, { auditMessage }),
|
|
187
|
-
/**
|
|
188
|
-
* Update a vocab layer's name.
|
|
189
|
-
* @param {string} id - The resource ID
|
|
190
|
-
* @param {string} name - The name
|
|
191
|
-
*/
|
|
192
|
-
update: (id, name, auditMessage) =>
|
|
193
|
-
this._request('PATCH', `/api/v1/vocab-layers/${id}`, { auditMessage,
|
|
194
|
-
body: bodyOf({ name }),
|
|
195
|
-
}),
|
|
196
|
-
/**
|
|
197
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
198
|
-
* @param {string} id - The resource ID
|
|
199
|
-
* @param {string} namespace - The config namespace
|
|
200
|
-
* @param {string} configKey - The config key
|
|
201
|
-
* @param {any} configValue - Configuration value to set
|
|
202
|
-
*/
|
|
203
|
-
setConfig: (id, namespace, configKey, configValue, auditMessage) =>
|
|
204
|
-
this._request('PUT', `/api/v1/vocab-layers/${id}/config/${namespace}/${configKey}`, { auditMessage,
|
|
205
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
206
|
-
}),
|
|
207
|
-
/**
|
|
208
|
-
* Remove a configuration value for a layer.
|
|
209
|
-
* @param {string} id - The resource ID
|
|
210
|
-
* @param {string} namespace - The config namespace
|
|
211
|
-
* @param {string} configKey - The config key
|
|
212
|
-
*/
|
|
213
|
-
deleteConfig: (id, namespace, configKey, auditMessage) =>
|
|
214
|
-
this._request('DELETE', `/api/v1/vocab-layers/${id}/config/${namespace}/${configKey}`, { auditMessage,
|
|
215
|
-
skipResponseTransform: true,
|
|
216
|
-
}),
|
|
217
|
-
/**
|
|
218
|
-
* List all vocab layers accessible to user. Transparently follows
|
|
219
|
-
* pagination cursors and returns the full flat array.
|
|
220
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
221
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
222
|
-
*/
|
|
223
|
-
list: (asOf) =>
|
|
224
|
-
listAll(this, '/api/v1/vocab-layers', { query: { 'as-of': asOf } }),
|
|
225
|
-
/**
|
|
226
|
-
* Fetch a single page of vocab layers.
|
|
227
|
-
* @param {object} [opts]
|
|
228
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
229
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
230
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
231
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
232
|
-
*/
|
|
233
|
-
listPage: ({ limit, cursor, asOf } = {}) =>
|
|
234
|
-
listPage(this, '/api/v1/vocab-layers', { limit, cursor, query: { 'as-of': asOf } }),
|
|
235
|
-
/**
|
|
236
|
-
* Async-iterate vocab layers page by page; yields each page's entries array.
|
|
237
|
-
* @param {object} [opts]
|
|
238
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
239
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
240
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
241
|
-
* @returns {AsyncGenerator<Array>}
|
|
242
|
-
*/
|
|
243
|
-
iterPages: ({ pageSize, asOf } = {}) =>
|
|
244
|
-
iterPages(this, '/api/v1/vocab-layers', { pageSize, query: { 'as-of': asOf } }),
|
|
245
|
-
/**
|
|
246
|
-
* Create a new vocab layer. Note: this also registers the user as a maintainer.
|
|
247
|
-
* @param {string} name - The name
|
|
248
|
-
*/
|
|
249
|
-
create: (name, auditMessage) =>
|
|
250
|
-
this._request('POST', '/api/v1/vocab-layers', { auditMessage,
|
|
251
|
-
body: bodyOf({ name }),
|
|
252
|
-
}),
|
|
253
|
-
/**
|
|
254
|
-
* Assign a user as a maintainer for this vocab layer.
|
|
255
|
-
* @param {string} id - The resource ID
|
|
256
|
-
* @param {string} userId - The user ID
|
|
257
|
-
*/
|
|
258
|
-
addMaintainer: (id, userId, auditMessage) =>
|
|
259
|
-
this._request('POST', `/api/v1/vocab-layers/${id}/maintainers/${userId}`, { auditMessage }),
|
|
260
|
-
/**
|
|
261
|
-
* Remove a user's maintainer privileges for this vocab layer.
|
|
262
|
-
* @param {string} id - The resource ID
|
|
263
|
-
* @param {string} userId - The user ID
|
|
264
|
-
*/
|
|
265
|
-
removeMaintainer: (id, userId, auditMessage) =>
|
|
266
|
-
this._request('DELETE', `/api/v1/vocab-layers/${id}/maintainers/${userId}`, { auditMessage }),
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
this.relations = {
|
|
270
|
-
/**
|
|
271
|
-
* Replace all metadata for a relation.
|
|
272
|
-
* @param {string} relationId - The relation ID
|
|
273
|
-
* @param {any} body - The request body
|
|
274
|
-
*/
|
|
275
|
-
setMetadata: (relationId, body, auditMessage) =>
|
|
276
|
-
this._request('PUT', `/api/v1/relations/${relationId}/metadata`, { auditMessage,
|
|
277
|
-
rawBody: body, skipResponseTransform: true,
|
|
278
|
-
}),
|
|
279
|
-
/**
|
|
280
|
-
* Remove all metadata from a relation.
|
|
281
|
-
* @param {string} relationId - The relation ID
|
|
282
|
-
*/
|
|
283
|
-
deleteMetadata: (relationId, auditMessage) =>
|
|
284
|
-
this._request('DELETE', `/api/v1/relations/${relationId}/metadata`, { auditMessage,
|
|
285
|
-
skipResponseTransform: true,
|
|
286
|
-
}),
|
|
287
|
-
/**
|
|
288
|
-
* Patch (shallow-merge) metadata for a relation. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
289
|
-
* @param {string} relationId - The relation ID
|
|
290
|
-
* @param {any} body - The metadata patch
|
|
291
|
-
*/
|
|
292
|
-
patchMetadata: (relationId, body, auditMessage) =>
|
|
293
|
-
this._request('PATCH', `/api/v1/relations/${relationId}/metadata`, { auditMessage,
|
|
294
|
-
rawBody: body, skipResponseTransform: true,
|
|
295
|
-
}),
|
|
296
|
-
/**
|
|
297
|
-
* Update the target span of a relation.
|
|
298
|
-
* @param {string} relationId - The relation ID
|
|
299
|
-
* @param {string} spanId - The span ID
|
|
300
|
-
*/
|
|
301
|
-
setTarget: (relationId, spanId, auditMessage) =>
|
|
302
|
-
this._request('PUT', `/api/v1/relations/${relationId}/target`, { auditMessage,
|
|
303
|
-
body: bodyOf({ 'span-id': spanId }),
|
|
304
|
-
}),
|
|
305
|
-
/**
|
|
306
|
-
* Get a relation by ID.
|
|
307
|
-
* @param {string} relationId - The relation ID
|
|
308
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
309
|
-
*/
|
|
310
|
-
get: (relationId, asOf) =>
|
|
311
|
-
this._request('GET', `/api/v1/relations/${relationId}`, {
|
|
312
|
-
queryParams: { 'as-of': asOf },
|
|
313
|
-
}),
|
|
314
|
-
/**
|
|
315
|
-
* Delete a relation.
|
|
316
|
-
* @param {string} relationId - The relation ID
|
|
317
|
-
*/
|
|
318
|
-
delete: (relationId, auditMessage) =>
|
|
319
|
-
this._request('DELETE', `/api/v1/relations/${relationId}`, { auditMessage }),
|
|
320
|
-
/**
|
|
321
|
-
* Update a relation's value.
|
|
322
|
-
* @param {string} relationId - The relation ID
|
|
323
|
-
* @param {any} value - The value
|
|
324
|
-
*/
|
|
325
|
-
update: (relationId, value, auditMessage) =>
|
|
326
|
-
this._request('PATCH', `/api/v1/relations/${relationId}`, { auditMessage,
|
|
327
|
-
body: bodyOf({ value }),
|
|
328
|
-
}),
|
|
329
|
-
/**
|
|
330
|
-
* Update the source span of a relation.
|
|
331
|
-
* @param {string} relationId - The relation ID
|
|
332
|
-
* @param {string} spanId - The span ID
|
|
333
|
-
*/
|
|
334
|
-
setSource: (relationId, spanId, auditMessage) =>
|
|
335
|
-
this._request('PUT', `/api/v1/relations/${relationId}/source`, { auditMessage,
|
|
336
|
-
body: bodyOf({ 'span-id': spanId }),
|
|
337
|
-
}),
|
|
338
|
-
/**
|
|
339
|
-
* Create a new relation. A relation is a directed edge between two spans
|
|
340
|
-
* with a value, useful for expressing phenomena such as syntactic or
|
|
341
|
-
* semantic relations.
|
|
342
|
-
* @param {string} layerId - The relation layer ID
|
|
343
|
-
* @param {string} sourceId - The source span ID
|
|
344
|
-
* @param {string} targetId - The target span ID
|
|
345
|
-
* @param {any} value - The value
|
|
346
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
347
|
-
*/
|
|
348
|
-
create: (layerId, sourceId, targetId, value, metadata, auditMessage) =>
|
|
349
|
-
this._request('POST', '/api/v1/relations', { auditMessage,
|
|
350
|
-
body: bodyOf({ 'layer-id': layerId, 'source-id': sourceId, 'target-id': targetId, value, metadata }),
|
|
351
|
-
}),
|
|
352
|
-
/**
|
|
353
|
-
* Create multiple relations in a single operation.
|
|
354
|
-
* @param {Array} body - The request body
|
|
355
|
-
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
356
|
-
*/
|
|
357
|
-
bulkCreate: (body, auditMessage) =>
|
|
358
|
-
this._request('POST', '/api/v1/relations/bulk', { auditMessage, body }),
|
|
359
|
-
/**
|
|
360
|
-
* Delete multiple relations in a single operation. Provide an array of IDs.
|
|
361
|
-
* @param {Array} body - The request body
|
|
362
|
-
*/
|
|
363
|
-
bulkDelete: (body, auditMessage) =>
|
|
364
|
-
this._request('DELETE', '/api/v1/relations/bulk', { auditMessage, body }),
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
this.spanLayers = {
|
|
368
|
-
/**
|
|
369
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
370
|
-
* @param {string} spanLayerId - The span layer ID
|
|
371
|
-
* @param {string} namespace - The config namespace
|
|
372
|
-
* @param {string} configKey - The config key
|
|
373
|
-
* @param {any} configValue - Configuration value to set
|
|
374
|
-
*/
|
|
375
|
-
setConfig: (spanLayerId, namespace, configKey, configValue, auditMessage) =>
|
|
376
|
-
this._request('PUT', `/api/v1/span-layers/${spanLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
377
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
378
|
-
}),
|
|
379
|
-
/**
|
|
380
|
-
* Remove a configuration value for a layer.
|
|
381
|
-
* @param {string} spanLayerId - The span layer ID
|
|
382
|
-
* @param {string} namespace - The config namespace
|
|
383
|
-
* @param {string} configKey - The config key
|
|
384
|
-
*/
|
|
385
|
-
deleteConfig: (spanLayerId, namespace, configKey, auditMessage) =>
|
|
386
|
-
this._request('DELETE', `/api/v1/span-layers/${spanLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
387
|
-
skipResponseTransform: true,
|
|
388
|
-
}),
|
|
389
|
-
/**
|
|
390
|
-
* Get a span layer by ID.
|
|
391
|
-
* @param {string} spanLayerId - The span layer ID
|
|
392
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
393
|
-
*/
|
|
394
|
-
get: (spanLayerId, asOf) =>
|
|
395
|
-
this._request('GET', `/api/v1/span-layers/${spanLayerId}`, {
|
|
396
|
-
queryParams: { 'as-of': asOf },
|
|
397
|
-
}),
|
|
398
|
-
/**
|
|
399
|
-
* Delete a span layer.
|
|
400
|
-
* @param {string} spanLayerId - The span layer ID
|
|
401
|
-
*/
|
|
402
|
-
delete: (spanLayerId, auditMessage) =>
|
|
403
|
-
this._request('DELETE', `/api/v1/span-layers/${spanLayerId}`, { auditMessage }),
|
|
404
|
-
/**
|
|
405
|
-
* Update a span layer's name.
|
|
406
|
-
* @param {string} spanLayerId - The span layer ID
|
|
407
|
-
* @param {string} name - The name
|
|
408
|
-
*/
|
|
409
|
-
update: (spanLayerId, name, auditMessage) =>
|
|
410
|
-
this._request('PATCH', `/api/v1/span-layers/${spanLayerId}`, { auditMessage,
|
|
411
|
-
body: bodyOf({ name }),
|
|
412
|
-
}),
|
|
413
|
-
/**
|
|
414
|
-
* Create a new span layer.
|
|
415
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
416
|
-
* @param {string} name - The name
|
|
417
|
-
*/
|
|
418
|
-
create: (tokenLayerId, name, auditMessage) =>
|
|
419
|
-
this._request('POST', '/api/v1/span-layers', { auditMessage,
|
|
420
|
-
body: bodyOf({ 'token-layer-id': tokenLayerId, name }),
|
|
421
|
-
}),
|
|
422
|
-
/**
|
|
423
|
-
* Shift a span layer's display order.
|
|
424
|
-
* @param {string} spanLayerId - The span layer ID
|
|
425
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
426
|
-
*/
|
|
427
|
-
shift: (spanLayerId, direction, auditMessage) =>
|
|
428
|
-
this._request('POST', `/api/v1/span-layers/${spanLayerId}/shift`, { auditMessage,
|
|
429
|
-
body: bodyOf({ direction }),
|
|
430
|
-
}),
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
this.spans = {
|
|
434
|
-
/**
|
|
435
|
-
* Replace tokens for a span.
|
|
436
|
-
* @param {string} spanId - The span ID
|
|
437
|
-
* @param {Array} tokens - The tokens
|
|
438
|
-
*/
|
|
439
|
-
setTokens: (spanId, tokens, auditMessage) =>
|
|
440
|
-
this._request('PUT', `/api/v1/spans/${spanId}/tokens`, { auditMessage,
|
|
441
|
-
body: bodyOf({ tokens }),
|
|
442
|
-
}),
|
|
443
|
-
/**
|
|
444
|
-
* Create a new span. A span holds a primary atomic value and optional
|
|
445
|
-
* metadata, and must at all times be associated with one or more tokens.
|
|
446
|
-
* @param {string} spanLayerId - The span layer ID
|
|
447
|
-
* @param {Array} tokens - The tokens
|
|
448
|
-
* @param {any} value - The value
|
|
449
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
450
|
-
*/
|
|
451
|
-
create: (spanLayerId, tokens, value, metadata, auditMessage) =>
|
|
452
|
-
this._request('POST', '/api/v1/spans', { auditMessage,
|
|
453
|
-
body: bodyOf({ 'span-layer-id': spanLayerId, tokens, value, metadata }),
|
|
454
|
-
}),
|
|
455
|
-
/**
|
|
456
|
-
* Get a span by ID.
|
|
457
|
-
* @param {string} spanId - The span ID
|
|
458
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
459
|
-
*/
|
|
460
|
-
get: (spanId, asOf) =>
|
|
461
|
-
this._request('GET', `/api/v1/spans/${spanId}`, {
|
|
462
|
-
queryParams: { 'as-of': asOf },
|
|
463
|
-
}),
|
|
464
|
-
/**
|
|
465
|
-
* Delete a span.
|
|
466
|
-
* @param {string} spanId - The span ID
|
|
467
|
-
*/
|
|
468
|
-
delete: (spanId, auditMessage) =>
|
|
469
|
-
this._request('DELETE', `/api/v1/spans/${spanId}`, { auditMessage }),
|
|
470
|
-
/**
|
|
471
|
-
* Update a span's value.
|
|
472
|
-
* @param {string} spanId - The span ID
|
|
473
|
-
* @param {any} value - The value
|
|
474
|
-
*/
|
|
475
|
-
update: (spanId, value, auditMessage) =>
|
|
476
|
-
this._request('PATCH', `/api/v1/spans/${spanId}`, { auditMessage,
|
|
477
|
-
body: bodyOf({ value }),
|
|
478
|
-
}),
|
|
479
|
-
/**
|
|
480
|
-
* Create multiple spans in a single operation.
|
|
481
|
-
* @param {Array} body - The request body
|
|
482
|
-
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
483
|
-
*/
|
|
484
|
-
bulkCreate: (body, auditMessage) =>
|
|
485
|
-
this._request('POST', '/api/v1/spans/bulk', { auditMessage, body }),
|
|
486
|
-
/**
|
|
487
|
-
* Delete multiple spans in a single operation. Provide an array of IDs.
|
|
488
|
-
* @param {Array} body - The request body
|
|
489
|
-
*/
|
|
490
|
-
bulkDelete: (body, auditMessage) =>
|
|
491
|
-
this._request('DELETE', '/api/v1/spans/bulk', { auditMessage, body }),
|
|
492
|
-
/**
|
|
493
|
-
* Replace all metadata for a span.
|
|
494
|
-
* @param {string} spanId - The span ID
|
|
495
|
-
* @param {any} body - The request body
|
|
496
|
-
*/
|
|
497
|
-
setMetadata: (spanId, body, auditMessage) =>
|
|
498
|
-
this._request('PUT', `/api/v1/spans/${spanId}/metadata`, { auditMessage,
|
|
499
|
-
rawBody: body, skipResponseTransform: true,
|
|
500
|
-
}),
|
|
501
|
-
/**
|
|
502
|
-
* Remove all metadata from a span.
|
|
503
|
-
* @param {string} spanId - The span ID
|
|
504
|
-
*/
|
|
505
|
-
deleteMetadata: (spanId, auditMessage) =>
|
|
506
|
-
this._request('DELETE', `/api/v1/spans/${spanId}/metadata`, { auditMessage,
|
|
507
|
-
skipResponseTransform: true,
|
|
508
|
-
}),
|
|
509
|
-
/**
|
|
510
|
-
* Patch (shallow-merge) metadata for a span. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
511
|
-
* @param {string} spanId - The span ID
|
|
512
|
-
* @param {any} body - The metadata patch
|
|
513
|
-
*/
|
|
514
|
-
patchMetadata: (spanId, body, auditMessage) =>
|
|
515
|
-
this._request('PATCH', `/api/v1/spans/${spanId}/metadata`, { auditMessage,
|
|
516
|
-
rawBody: body, skipResponseTransform: true,
|
|
517
|
-
}),
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
this.batch = {
|
|
521
|
-
/**
|
|
522
|
-
* Execute multiple API operations atomically. If any operation fails, all
|
|
523
|
-
* changes are rolled back.
|
|
524
|
-
* @param {Array} body - The request body
|
|
525
|
-
*/
|
|
526
|
-
submit: (body, auditMessage) =>
|
|
527
|
-
this._request('POST', '/api/v1/batch', { auditMessage,
|
|
528
|
-
body, noBatch: true,
|
|
529
|
-
}),
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
this.texts = {
|
|
533
|
-
/**
|
|
534
|
-
* Replace all metadata for a text.
|
|
535
|
-
* @param {string} textId - The text ID
|
|
536
|
-
* @param {any} body - The request body
|
|
537
|
-
*/
|
|
538
|
-
setMetadata: (textId, body, auditMessage) =>
|
|
539
|
-
this._request('PUT', `/api/v1/texts/${textId}/metadata`, { auditMessage,
|
|
540
|
-
rawBody: body, skipResponseTransform: true,
|
|
541
|
-
}),
|
|
542
|
-
/**
|
|
543
|
-
* Remove all metadata from a text.
|
|
544
|
-
* @param {string} textId - The text ID
|
|
545
|
-
*/
|
|
546
|
-
deleteMetadata: (textId, auditMessage) =>
|
|
547
|
-
this._request('DELETE', `/api/v1/texts/${textId}/metadata`, { auditMessage,
|
|
548
|
-
skipResponseTransform: true,
|
|
549
|
-
}),
|
|
550
|
-
/**
|
|
551
|
-
* Patch (shallow-merge) metadata for a text. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
552
|
-
* @param {string} textId - The text ID
|
|
553
|
-
* @param {any} body - The metadata patch
|
|
554
|
-
*/
|
|
555
|
-
patchMetadata: (textId, body, auditMessage) =>
|
|
556
|
-
this._request('PATCH', `/api/v1/texts/${textId}/metadata`, { auditMessage,
|
|
557
|
-
rawBody: body, skipResponseTransform: true,
|
|
558
|
-
}),
|
|
559
|
-
/**
|
|
560
|
-
* Create a new text in a document's text layer. A text is a container for
|
|
561
|
-
* one long string in `body` for a given layer.
|
|
562
|
-
* @param {string} textLayerId - The text layer ID
|
|
563
|
-
* @param {string} documentId - The document ID
|
|
564
|
-
* @param {string} body - The request body
|
|
565
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
566
|
-
*/
|
|
567
|
-
create: (textLayerId, documentId, body, metadata, auditMessage) =>
|
|
568
|
-
this._request('POST', '/api/v1/texts', { auditMessage,
|
|
569
|
-
body: bodyOf({ 'text-layer-id': textLayerId, 'document-id': documentId, body, metadata }),
|
|
570
|
-
}),
|
|
571
|
-
/**
|
|
572
|
-
* Get a text.
|
|
573
|
-
* @param {string} textId - The text ID
|
|
574
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
575
|
-
*/
|
|
576
|
-
get: (textId, asOf) =>
|
|
577
|
-
this._request('GET', `/api/v1/texts/${textId}`, {
|
|
578
|
-
queryParams: { 'as-of': asOf },
|
|
579
|
-
}),
|
|
580
|
-
/**
|
|
581
|
-
* Delete a text and all dependent data.
|
|
582
|
-
* @param {string} textId - The text ID
|
|
583
|
-
*/
|
|
584
|
-
delete: (textId, auditMessage) =>
|
|
585
|
-
this._request('DELETE', `/api/v1/texts/${textId}`, { auditMessage }),
|
|
586
|
-
/**
|
|
587
|
-
* Update a text's body. A diff is computed and token indices are updated
|
|
588
|
-
* so that tokens remain intact. Alternatively, `body` can be a list of
|
|
589
|
-
* edit directives.
|
|
590
|
-
* @param {string} textId - The text ID
|
|
591
|
-
* @param {any} body - The request body
|
|
592
|
-
*/
|
|
593
|
-
update: (textId, body, auditMessage) =>
|
|
594
|
-
this._request('PATCH', `/api/v1/texts/${textId}`, { auditMessage,
|
|
595
|
-
body: bodyOf({ body }),
|
|
596
|
-
}),
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
this.users = {
|
|
600
|
-
/**
|
|
601
|
-
* List (or search) users. Transparently follows pagination cursors and
|
|
602
|
-
* returns the full flat array. Admin-or-maintainer only.
|
|
603
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
604
|
-
* @param {object} [opts]
|
|
605
|
-
* @param {string} [opts.q] - Filter to usernames containing this text (case-insensitive)
|
|
606
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
607
|
-
*/
|
|
608
|
-
list: ({ q, asOf } = {}) =>
|
|
609
|
-
listAll(this, '/api/v1/users', { query: { q, 'as-of': asOf } }),
|
|
610
|
-
/**
|
|
611
|
-
* Fetch a single page of users (optionally filtered by `q`).
|
|
612
|
-
* @param {object} [opts]
|
|
613
|
-
* @param {string} [opts.q] - Filter to usernames containing this text (case-insensitive)
|
|
614
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
615
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
616
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
617
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
618
|
-
*/
|
|
619
|
-
listPage: ({ q, limit, cursor, asOf } = {}) =>
|
|
620
|
-
listPage(this, '/api/v1/users', { limit, cursor, query: { q, 'as-of': asOf } }),
|
|
621
|
-
/**
|
|
622
|
-
* Async-iterate users page by page; yields each page's entries array.
|
|
623
|
-
* @param {object} [opts]
|
|
624
|
-
* @param {string} [opts.q] - Filter to usernames containing this text (case-insensitive)
|
|
625
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
626
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
627
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
628
|
-
* @returns {AsyncGenerator<Array>}
|
|
629
|
-
*/
|
|
630
|
-
iterPages: ({ q, pageSize, asOf } = {}) =>
|
|
631
|
-
iterPages(this, '/api/v1/users', { pageSize, query: { q, 'as-of': asOf } }),
|
|
632
|
-
/**
|
|
633
|
-
* Create a new user
|
|
634
|
-
* @param {string} username - The username
|
|
635
|
-
* @param {string} password - The password
|
|
636
|
-
* @param {boolean} isAdmin - Whether the user is an admin
|
|
637
|
-
*/
|
|
638
|
-
create: (username, password, isAdmin, auditMessage) =>
|
|
639
|
-
this._request('POST', '/api/v1/users', { auditMessage,
|
|
640
|
-
body: bodyOf({ username, password, 'is-admin': isAdmin }),
|
|
641
|
-
}),
|
|
642
|
-
/**
|
|
643
|
-
* Get audit log for a user's actions. Transparently follows pagination
|
|
644
|
-
* cursors and returns the full flat array.
|
|
645
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
646
|
-
* @param {string} userId - The user ID
|
|
647
|
-
* @param {string} [startTime] - Start of time range
|
|
648
|
-
* @param {string} [endTime] - End of time range
|
|
649
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
650
|
-
* @param {string[]|string} [opTypes] - Only return operations of these
|
|
651
|
-
* types, spelled as in an entry's `op/type` (e.g.
|
|
652
|
-
* `['span-layer/create', 'span-layer/delete']`). An entry appears when
|
|
653
|
-
* one of its operations matches, carrying only the ones that did.
|
|
654
|
-
*/
|
|
655
|
-
audit: (userId, startTime, endTime, asOf, opTypes) =>
|
|
656
|
-
listAll(this, `/api/v1/users/${userId}/audit`, {
|
|
657
|
-
query: {
|
|
658
|
-
'start-time': startTime,
|
|
659
|
-
'end-time': endTime,
|
|
660
|
-
'as-of': asOf,
|
|
661
|
-
'op-types': opTypesParam(opTypes),
|
|
662
|
-
},
|
|
663
|
-
}),
|
|
664
|
-
/**
|
|
665
|
-
* Get a user by ID
|
|
666
|
-
* @param {string} id - The resource ID
|
|
667
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
668
|
-
*/
|
|
669
|
-
get: (id, asOf) =>
|
|
670
|
-
this._request('GET', `/api/v1/users/${id}`, {
|
|
671
|
-
queryParams: { 'as-of': asOf },
|
|
672
|
-
}),
|
|
673
|
-
/**
|
|
674
|
-
* Deactivate a user. Users are never hard-deleted: deactivation
|
|
675
|
-
* rejects their logins and tokens, strips their project memberships
|
|
676
|
-
* and vocab maintainerships, and revokes their API tokens. The user
|
|
677
|
-
* stays visible in listings with a deactivated-at timestamp.
|
|
678
|
-
* Reversible via activate(), which restores login only.
|
|
679
|
-
* @param {string} id - The resource ID
|
|
680
|
-
*/
|
|
681
|
-
delete: (id, auditMessage) =>
|
|
682
|
-
this._request('DELETE', `/api/v1/users/${id}`, { auditMessage }),
|
|
683
|
-
/**
|
|
684
|
-
* Reactivate a deactivated user, restoring their ability to log in.
|
|
685
|
-
* Project memberships, vocab maintainerships, and API tokens removed
|
|
686
|
-
* at deactivation are NOT restored — re-grant them deliberately.
|
|
687
|
-
* @param {string} id - The resource ID
|
|
688
|
-
*/
|
|
689
|
-
activate: (id, auditMessage) =>
|
|
690
|
-
this._request('POST', `/api/v1/users/${id}/activate`, { auditMessage }),
|
|
691
|
-
/**
|
|
692
|
-
* Modify a user. Admins may change the username, password, and admin
|
|
693
|
-
* status of any user. All other users may only modify their own username
|
|
694
|
-
* or password.
|
|
695
|
-
* @param {string} id - The resource ID
|
|
696
|
-
* @param {string} [password] - New password
|
|
697
|
-
* @param {string} [username] - New username
|
|
698
|
-
* @param {boolean} [isAdmin] - New admin status
|
|
699
|
-
*/
|
|
700
|
-
update: (id, password, username, isAdmin, auditMessage) =>
|
|
701
|
-
this._request('PATCH', `/api/v1/users/${id}`, { auditMessage,
|
|
702
|
-
body: bodyOf({ password, username, 'is-admin': isAdmin }),
|
|
703
|
-
}),
|
|
704
|
-
/**
|
|
705
|
-
* Build a URL for a user's profile picture, suitable for use directly as
|
|
706
|
-
* an `<img>` src. An image element cannot send an Authorization header,
|
|
707
|
-
* so the session token rides in the query string, the same way document
|
|
708
|
-
* media does.
|
|
709
|
-
*
|
|
710
|
-
* Pass the user record's `avatarHash` as the second argument whenever you
|
|
711
|
-
* have it: the URL then addresses that exact picture, so the browser can
|
|
712
|
-
* cache it indefinitely and still pick up a replacement the moment the
|
|
713
|
-
* user changes it. Returns null when the user has no picture, so callers
|
|
714
|
-
* can fall back to initials without a wasted request.
|
|
715
|
-
* @param {string} id - The user ID
|
|
716
|
-
* @param {string} [avatarHash] - The user record's `avatarHash`
|
|
717
|
-
* @returns {string|null}
|
|
718
|
-
*/
|
|
719
|
-
avatarUrl: (id, avatarHash) => {
|
|
720
|
-
if (avatarHash === null) return null;
|
|
721
|
-
const params = new URLSearchParams({ token: this.token });
|
|
722
|
-
if (avatarHash) params.set('v', avatarHash);
|
|
723
|
-
return `${this.baseUrl}/api/v1/users/${id}/avatar?${params}`;
|
|
724
|
-
},
|
|
725
|
-
/**
|
|
726
|
-
* Fetch a user's profile picture as raw bytes. Most callers want
|
|
727
|
-
* avatarUrl() instead. This one is for non-browser consumers.
|
|
728
|
-
* @param {string} id - The user ID
|
|
729
|
-
*/
|
|
730
|
-
getAvatar: (id) =>
|
|
731
|
-
this._request('GET', `/api/v1/users/${id}/avatar`, {
|
|
732
|
-
noBatch: true,
|
|
733
|
-
binaryResponse: true,
|
|
734
|
-
}),
|
|
735
|
-
/**
|
|
736
|
-
* Upload a profile picture. Your own, or anyone's if you are an admin.
|
|
737
|
-
* The server center-crops to a square, scales to the configured edge
|
|
738
|
-
* length, and re-encodes, so no client-side resizing is needed. Accepts
|
|
739
|
-
* PNG, JPEG, WebP, and GIF. Resolves to the updated user record.
|
|
740
|
-
* @param {string} id - The user ID
|
|
741
|
-
* @param {File} file - The image to upload
|
|
742
|
-
*/
|
|
743
|
-
setAvatar: (id, file, auditMessage) => {
|
|
744
|
-
const fd = new FormData();
|
|
745
|
-
fd.append('file', file);
|
|
746
|
-
return this._request('PUT', `/api/v1/users/${id}/avatar`, { auditMessage,
|
|
747
|
-
body: fd, formData: true, noBatch: true,
|
|
748
|
-
});
|
|
749
|
-
},
|
|
750
|
-
/**
|
|
751
|
-
* Remove a profile picture. Your own, or anyone's if you are an admin.
|
|
752
|
-
* @param {string} id - The user ID
|
|
753
|
-
*/
|
|
754
|
-
deleteAvatar: (id, auditMessage) =>
|
|
755
|
-
this._request('DELETE', `/api/v1/users/${id}/avatar`, { auditMessage,
|
|
756
|
-
noBatch: true,
|
|
757
|
-
}),
|
|
758
|
-
};
|
|
759
|
-
|
|
760
|
-
this.apiTokens = {
|
|
761
|
-
/**
|
|
762
|
-
* List a user's named API tokens. Never includes the signed token
|
|
763
|
-
* string itself — that is only returned once, by create().
|
|
764
|
-
* Transparently follows pagination cursors and returns the full flat array.
|
|
765
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
766
|
-
* @param {string} userId - The user ID who owns the tokens
|
|
767
|
-
*/
|
|
768
|
-
list: (userId) =>
|
|
769
|
-
listAll(this, `/api/v1/users/${userId}/tokens`),
|
|
770
|
-
/**
|
|
771
|
-
* Fetch a single page of a user's named API tokens.
|
|
772
|
-
* @param {string} userId - The user ID who owns the tokens
|
|
773
|
-
* @param {object} [opts]
|
|
774
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
775
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
776
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
777
|
-
*/
|
|
778
|
-
listPage: (userId, { limit, cursor } = {}) =>
|
|
779
|
-
listPage(this, `/api/v1/users/${userId}/tokens`, { limit, cursor }),
|
|
780
|
-
/**
|
|
781
|
-
* Async-iterate a user's named API tokens page by page; yields each
|
|
782
|
-
* page's entries array.
|
|
783
|
-
* @param {string} userId - The user ID who owns the tokens
|
|
784
|
-
* @param {object} [opts]
|
|
785
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
786
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
787
|
-
* @returns {AsyncGenerator<Array>}
|
|
788
|
-
*/
|
|
789
|
-
iterPages: (userId, { pageSize } = {}) =>
|
|
790
|
-
iterPages(this, `/api/v1/users/${userId}/tokens`, { pageSize }),
|
|
791
|
-
/**
|
|
792
|
-
* Mint a named API token for a user. The returned `token` is the signed
|
|
793
|
-
* credential and is shown ONLY here — store it immediately. API tokens
|
|
794
|
-
* do not expire and survive password changes / logout; revoke to kill.
|
|
795
|
-
* @param {string} userId - The user ID who will own the token
|
|
796
|
-
* @param {string} name - A human label, e.g. "Stanza Parser"
|
|
797
|
-
* @returns {Promise<{id: string, name: string, token: string}>}
|
|
798
|
-
*/
|
|
799
|
-
create: (userId, name, auditMessage) =>
|
|
800
|
-
this._request('POST', `/api/v1/users/${userId}/tokens`, { auditMessage,
|
|
801
|
-
body: bodyOf({ name }),
|
|
802
|
-
}),
|
|
803
|
-
/**
|
|
804
|
-
* Revoke a named API token (soft-revoke; idempotent).
|
|
805
|
-
* @param {string} userId - The user ID who owns the token
|
|
806
|
-
* @param {string} tokenId - The token ID to revoke
|
|
807
|
-
*/
|
|
808
|
-
revoke: (userId, tokenId, auditMessage) =>
|
|
809
|
-
this._request('DELETE', `/api/v1/users/${userId}/tokens/${tokenId}`, { auditMessage }),
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
this.invites = {
|
|
813
|
-
/**
|
|
814
|
-
* List invites you minted, oldest first. With `projectId`, lists that
|
|
815
|
-
* project's invites instead (including co-maintainers'), which requires
|
|
816
|
-
* maintainer or admin on it. Never includes invite codes — the code is
|
|
817
|
-
* returned once, by create(), and is not recoverable afterward.
|
|
818
|
-
* Transparently follows pagination cursors and returns the full flat array.
|
|
819
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
820
|
-
* @param {object} [opts]
|
|
821
|
-
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
822
|
-
*/
|
|
823
|
-
list: ({ projectId } = {}) =>
|
|
824
|
-
listAll(this, '/api/v1/invites', { query: { 'project-id': projectId } }),
|
|
825
|
-
/**
|
|
826
|
-
* Fetch a single page of invites.
|
|
827
|
-
* @param {object} [opts]
|
|
828
|
-
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
829
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
830
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
831
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
832
|
-
*/
|
|
833
|
-
listPage: ({ projectId, limit, cursor } = {}) =>
|
|
834
|
-
listPage(this, '/api/v1/invites', { limit, cursor, query: { 'project-id': projectId } }),
|
|
835
|
-
/**
|
|
836
|
-
* Async-iterate invites page by page; yields each page's entries array.
|
|
837
|
-
* @param {object} [opts]
|
|
838
|
-
* @param {string} [opts.projectId] - List this project's invites instead of your own
|
|
839
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
840
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
841
|
-
* @returns {AsyncGenerator<Array>}
|
|
842
|
-
*/
|
|
843
|
-
iterPages: ({ projectId, pageSize } = {}) =>
|
|
844
|
-
iterPages(this, '/api/v1/invites', { pageSize, query: { 'project-id': projectId } }),
|
|
845
|
-
/**
|
|
846
|
-
* Mint an invite. The returned `code` is shown ONLY here — it is never
|
|
847
|
-
* stored and cannot be recovered, so build and hand off the link now.
|
|
848
|
-
* Use PlaidClient.inviteUrl() to turn it into one.
|
|
849
|
-
*
|
|
850
|
-
* Admins may mint anything. A project maintainer may mint role grants on
|
|
851
|
-
* projects they maintain, and nothing else: no admin grant, no grantless
|
|
852
|
-
* invite, no password resets.
|
|
853
|
-
* @param {object} opts
|
|
854
|
-
* @param {string} [opts.projectId] - Project the redeemer joins (with projectRole)
|
|
855
|
-
* @param {string} [opts.projectRole] - "reader" | "writer" | "maintainer"
|
|
856
|
-
* @param {boolean} [opts.grantAdmin] - Make the new account a global admin (admin only)
|
|
857
|
-
* @param {string} [opts.targetUserId] - Make this a password reset for that user (admin only)
|
|
858
|
-
* @param {number} [opts.maxUses] - How many accounts this link may create (default 1)
|
|
859
|
-
* @param {number} [opts.ttlDays] - Days until it expires (default 14, max 365)
|
|
860
|
-
* @param {string} [opts.note] - Human label shown in your invite list
|
|
861
|
-
* @returns {Promise<{id: string, code: string, kind: string, status: string}>}
|
|
862
|
-
*/
|
|
863
|
-
create: ({ projectId, projectRole, grantAdmin, targetUserId, maxUses, ttlDays, note } = {}, auditMessage) =>
|
|
864
|
-
this._request('POST', '/api/v1/invites', { auditMessage,
|
|
865
|
-
body: bodyOf({
|
|
866
|
-
'project-id': projectId, 'project-role': projectRole,
|
|
867
|
-
'grant-admin': grantAdmin, 'target-user-id': targetUserId,
|
|
868
|
-
'max-uses': maxUses, 'ttl-days': ttlDays, note,
|
|
869
|
-
}),
|
|
870
|
-
}),
|
|
871
|
-
/**
|
|
872
|
-
* Revoke an invite, killing the link immediately. Idempotent. Allowed for
|
|
873
|
-
* the creator, an admin, or a maintainer of the invite's project.
|
|
874
|
-
* @param {string} id - The invite ID
|
|
875
|
-
*/
|
|
876
|
-
revoke: (id, auditMessage) =>
|
|
877
|
-
this._request('DELETE', `/api/v1/invites/${id}`, { auditMessage }),
|
|
878
|
-
};
|
|
879
|
-
|
|
880
|
-
this.tokenLayers = {
|
|
881
|
-
/**
|
|
882
|
-
* Shift a token layer's display order.
|
|
883
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
884
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
885
|
-
*/
|
|
886
|
-
shift: (tokenLayerId, direction, auditMessage) =>
|
|
887
|
-
this._request('POST', `/api/v1/token-layers/${tokenLayerId}/shift`, { auditMessage,
|
|
888
|
-
body: bodyOf({ direction }),
|
|
889
|
-
}),
|
|
890
|
-
/**
|
|
891
|
-
* Create a new token layer.
|
|
892
|
-
* @param {string} textLayerId - The text layer ID
|
|
893
|
-
* @param {string} name - The name
|
|
894
|
-
* @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.
|
|
895
|
-
* @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).
|
|
896
|
-
*/
|
|
897
|
-
create: (textLayerId, name, overlapMode, parentTokenLayerId, auditMessage) =>
|
|
898
|
-
this._request('POST', '/api/v1/token-layers', { auditMessage,
|
|
899
|
-
body: bodyOf({ 'text-layer-id': textLayerId, name, 'overlap-mode': overlapMode, 'parent-token-layer-id': parentTokenLayerId }),
|
|
900
|
-
}),
|
|
901
|
-
/**
|
|
902
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
903
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
904
|
-
* @param {string} namespace - The config namespace
|
|
905
|
-
* @param {string} configKey - The config key
|
|
906
|
-
* @param {any} configValue - Configuration value to set
|
|
907
|
-
*/
|
|
908
|
-
setConfig: (tokenLayerId, namespace, configKey, configValue, auditMessage) =>
|
|
909
|
-
this._request('PUT', `/api/v1/token-layers/${tokenLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
910
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
911
|
-
}),
|
|
912
|
-
/**
|
|
913
|
-
* Remove a configuration value for a layer.
|
|
914
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
915
|
-
* @param {string} namespace - The config namespace
|
|
916
|
-
* @param {string} configKey - The config key
|
|
917
|
-
*/
|
|
918
|
-
deleteConfig: (tokenLayerId, namespace, configKey, auditMessage) =>
|
|
919
|
-
this._request('DELETE', `/api/v1/token-layers/${tokenLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
920
|
-
skipResponseTransform: true,
|
|
921
|
-
}),
|
|
922
|
-
/**
|
|
923
|
-
* Get a token layer by ID.
|
|
924
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
925
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
926
|
-
*/
|
|
927
|
-
get: (tokenLayerId, asOf) =>
|
|
928
|
-
this._request('GET', `/api/v1/token-layers/${tokenLayerId}`, {
|
|
929
|
-
queryParams: { 'as-of': asOf },
|
|
930
|
-
}),
|
|
931
|
-
/**
|
|
932
|
-
* Delete a token layer.
|
|
933
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
934
|
-
*/
|
|
935
|
-
delete: (tokenLayerId, auditMessage) =>
|
|
936
|
-
this._request('DELETE', `/api/v1/token-layers/${tokenLayerId}`, { auditMessage }),
|
|
937
|
-
/**
|
|
938
|
-
* Update a token layer's name.
|
|
939
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
940
|
-
* @param {string} name - The name
|
|
941
|
-
*/
|
|
942
|
-
update: (tokenLayerId, name, auditMessage) =>
|
|
943
|
-
this._request('PATCH', `/api/v1/token-layers/${tokenLayerId}`, { auditMessage,
|
|
944
|
-
body: bodyOf({ name }),
|
|
945
|
-
}),
|
|
946
|
-
};
|
|
947
|
-
|
|
948
|
-
this.documents = {
|
|
949
|
-
/**
|
|
950
|
-
* Check the lock status of a document.
|
|
951
|
-
* @param {string} documentId - The document ID
|
|
952
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
953
|
-
*/
|
|
954
|
-
checkLock: (documentId, asOf) =>
|
|
955
|
-
this._request('GET', `/api/v1/documents/${documentId}/lock`, {
|
|
956
|
-
queryParams: { 'as-of': asOf },
|
|
957
|
-
}),
|
|
958
|
-
/**
|
|
959
|
-
* Acquire or refresh a document lock
|
|
960
|
-
* @param {string} documentId - The document ID
|
|
961
|
-
*/
|
|
962
|
-
acquireLock: (documentId, auditMessage) =>
|
|
963
|
-
this._request('POST', `/api/v1/documents/${documentId}/lock`, { auditMessage }),
|
|
964
|
-
/**
|
|
965
|
-
* Release a document lock
|
|
966
|
-
* @param {string} documentId - The document ID
|
|
967
|
-
*/
|
|
968
|
-
releaseLock: (documentId, auditMessage) =>
|
|
969
|
-
this._request('DELETE', `/api/v1/documents/${documentId}/lock`, { auditMessage }),
|
|
970
|
-
/**
|
|
971
|
-
* Get media file for a document
|
|
972
|
-
* @param {string} documentId - The document ID
|
|
973
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
974
|
-
*/
|
|
975
|
-
getMedia: (documentId, asOf) =>
|
|
976
|
-
this._request('GET', `/api/v1/documents/${documentId}/media`, {
|
|
977
|
-
queryParams: { 'as-of': asOf },
|
|
978
|
-
noBatch: true,
|
|
979
|
-
binaryResponse: true,
|
|
980
|
-
}),
|
|
981
|
-
/**
|
|
982
|
-
* Upload a media file for a document. Uses Apache Tika for content validation.
|
|
983
|
-
* @param {string} documentId - The document ID
|
|
984
|
-
* @param {File} file - The file to upload
|
|
985
|
-
*/
|
|
986
|
-
uploadMedia: (documentId, file, auditMessage) => {
|
|
987
|
-
const fd = new FormData();
|
|
988
|
-
fd.append('file', file);
|
|
989
|
-
return this._request('PUT', `/api/v1/documents/${documentId}/media`, { auditMessage,
|
|
990
|
-
body: fd, formData: true, noBatch: true,
|
|
991
|
-
});
|
|
992
|
-
},
|
|
993
|
-
/**
|
|
994
|
-
* Delete media file for a document
|
|
995
|
-
* @param {string} documentId - The document ID
|
|
996
|
-
*/
|
|
997
|
-
deleteMedia: (documentId, auditMessage) =>
|
|
998
|
-
this._request('DELETE', `/api/v1/documents/${documentId}/media`, { auditMessage,
|
|
999
|
-
noBatch: true,
|
|
1000
|
-
}),
|
|
1001
|
-
/**
|
|
1002
|
-
* Replace all metadata for a document.
|
|
1003
|
-
* @param {string} documentId - The document ID
|
|
1004
|
-
* @param {any} body - The request body
|
|
1005
|
-
*/
|
|
1006
|
-
setMetadata: (documentId, body, auditMessage) =>
|
|
1007
|
-
this._request('PUT', `/api/v1/documents/${documentId}/metadata`, { auditMessage,
|
|
1008
|
-
rawBody: body, skipResponseTransform: true,
|
|
1009
|
-
}),
|
|
1010
|
-
/**
|
|
1011
|
-
* Remove all metadata from a document.
|
|
1012
|
-
* @param {string} documentId - The document ID
|
|
1013
|
-
*/
|
|
1014
|
-
deleteMetadata: (documentId, auditMessage) =>
|
|
1015
|
-
this._request('DELETE', `/api/v1/documents/${documentId}/metadata`, { auditMessage,
|
|
1016
|
-
skipResponseTransform: true,
|
|
1017
|
-
}),
|
|
1018
|
-
/**
|
|
1019
|
-
* Patch (shallow-merge) metadata for a document. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
1020
|
-
* @param {string} documentId - The document ID
|
|
1021
|
-
* @param {any} body - The metadata patch
|
|
1022
|
-
*/
|
|
1023
|
-
patchMetadata: (documentId, body, auditMessage) =>
|
|
1024
|
-
this._request('PATCH', `/api/v1/documents/${documentId}/metadata`, { auditMessage,
|
|
1025
|
-
rawBody: body, skipResponseTransform: true,
|
|
1026
|
-
}),
|
|
1027
|
-
/**
|
|
1028
|
-
* Get audit log for a document. Transparently follows pagination cursors
|
|
1029
|
-
* and returns the full flat array.
|
|
1030
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
1031
|
-
* @param {string} documentId - The document ID
|
|
1032
|
-
* @param {string} [startTime] - Start of time range
|
|
1033
|
-
* @param {string} [endTime] - End of time range
|
|
1034
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1035
|
-
* @param {string[]|string} [opTypes] - Only return operations of these
|
|
1036
|
-
* types, spelled as in an entry's `op/type` (e.g.
|
|
1037
|
-
* `['span-layer/create', 'span-layer/delete']`). An entry appears when
|
|
1038
|
-
* one of its operations matches, carrying only the ones that did.
|
|
1039
|
-
*/
|
|
1040
|
-
audit: (documentId, startTime, endTime, asOf, opTypes) =>
|
|
1041
|
-
listAll(this, `/api/v1/documents/${documentId}/audit`, {
|
|
1042
|
-
query: {
|
|
1043
|
-
'start-time': startTime,
|
|
1044
|
-
'end-time': endTime,
|
|
1045
|
-
'as-of': asOf,
|
|
1046
|
-
'op-types': opTypesParam(opTypes),
|
|
1047
|
-
},
|
|
1048
|
-
}),
|
|
1049
|
-
/**
|
|
1050
|
-
* Get a document. Set `includeBody` to true to include all data.
|
|
1051
|
-
*
|
|
1052
|
-
* `layers` narrows a body read to the layers you name (ids of any kind:
|
|
1053
|
-
* text, token, span, or relation). A layer comes back when it is named
|
|
1054
|
-
* or is an ancestor of a named layer, and carries its own
|
|
1055
|
-
* texts/tokens/spans/relations/vocabs only when it is itself named — so
|
|
1056
|
-
* name the text layer too if you also want the text body. An id that is
|
|
1057
|
-
* not a layer of this document's project is an error, not a quietly
|
|
1058
|
-
* smaller response. Requires `includeBody`.
|
|
1059
|
-
* @param {string} documentId - The document ID
|
|
1060
|
-
* @param {boolean} [includeBody] - Include document body data
|
|
1061
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1062
|
-
* @param {string[]|string} [layers] - Layer ids to restrict a body read to
|
|
1063
|
-
*/
|
|
1064
|
-
get: (documentId, includeBody, asOf, layers) =>
|
|
1065
|
-
this._request('GET', `/api/v1/documents/${documentId}`, {
|
|
1066
|
-
queryParams: {
|
|
1067
|
-
'include-body': includeBody,
|
|
1068
|
-
'as-of': asOf,
|
|
1069
|
-
layers: layersParam(layers),
|
|
1070
|
-
},
|
|
1071
|
-
}),
|
|
1072
|
-
/**
|
|
1073
|
-
* Delete a document and all data contained.
|
|
1074
|
-
* @param {string} documentId - The document ID
|
|
1075
|
-
*/
|
|
1076
|
-
delete: (documentId, auditMessage) =>
|
|
1077
|
-
this._request('DELETE', `/api/v1/documents/${documentId}`, { auditMessage }),
|
|
1078
|
-
/**
|
|
1079
|
-
* Update a document's name.
|
|
1080
|
-
* @param {string} documentId - The document ID
|
|
1081
|
-
* @param {string} name - The name
|
|
1082
|
-
*/
|
|
1083
|
-
update: (documentId, name, auditMessage) =>
|
|
1084
|
-
this._request('PATCH', `/api/v1/documents/${documentId}`, { auditMessage,
|
|
1085
|
-
body: bodyOf({ name }),
|
|
1086
|
-
}),
|
|
1087
|
-
/**
|
|
1088
|
-
* Create a new document in a project.
|
|
1089
|
-
* @param {string} projectId - The project ID
|
|
1090
|
-
* @param {string} name - The name
|
|
1091
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1092
|
-
*/
|
|
1093
|
-
create: (projectId, name, metadata, auditMessage) =>
|
|
1094
|
-
this._request('POST', '/api/v1/documents', { auditMessage,
|
|
1095
|
-
body: bodyOf({ 'project-id': projectId, name, metadata }),
|
|
1096
|
-
}),
|
|
1097
|
-
};
|
|
1098
|
-
|
|
1099
|
-
this.projects = {
|
|
1100
|
-
/**
|
|
1101
|
-
* Set a user's access level to read and write for this project.
|
|
1102
|
-
* @param {string} id - The resource ID
|
|
1103
|
-
* @param {string} userId - The user ID
|
|
1104
|
-
*/
|
|
1105
|
-
addWriter: (id, userId, auditMessage) =>
|
|
1106
|
-
this._request('POST', `/api/v1/projects/${id}/writers/${userId}`, { auditMessage }),
|
|
1107
|
-
/**
|
|
1108
|
-
* Remove a user's writer privileges for this project.
|
|
1109
|
-
* @param {string} id - The resource ID
|
|
1110
|
-
* @param {string} userId - The user ID
|
|
1111
|
-
*/
|
|
1112
|
-
removeWriter: (id, userId, auditMessage) =>
|
|
1113
|
-
this._request('DELETE', `/api/v1/projects/${id}/writers/${userId}`, { auditMessage }),
|
|
1114
|
-
/**
|
|
1115
|
-
* Set a user's access level to read-only for this project.
|
|
1116
|
-
* @param {string} id - The resource ID
|
|
1117
|
-
* @param {string} userId - The user ID
|
|
1118
|
-
*/
|
|
1119
|
-
addReader: (id, userId, auditMessage) =>
|
|
1120
|
-
this._request('POST', `/api/v1/projects/${id}/readers/${userId}`, { auditMessage }),
|
|
1121
|
-
/**
|
|
1122
|
-
* Remove a user's reader privileges for this project.
|
|
1123
|
-
* @param {string} id - The resource ID
|
|
1124
|
-
* @param {string} userId - The user ID
|
|
1125
|
-
*/
|
|
1126
|
-
removeReader: (id, userId, auditMessage) =>
|
|
1127
|
-
this._request('DELETE', `/api/v1/projects/${id}/readers/${userId}`, { auditMessage }),
|
|
1128
|
-
/**
|
|
1129
|
-
* Set a configuration value for a project in an editor namespace.
|
|
1130
|
-
* @param {string} id - The resource ID
|
|
1131
|
-
* @param {string} namespace - The config namespace
|
|
1132
|
-
* @param {string} configKey - The config key
|
|
1133
|
-
* @param {any} configValue - Configuration value to set
|
|
1134
|
-
*/
|
|
1135
|
-
setConfig: (id, namespace, configKey, configValue, auditMessage) =>
|
|
1136
|
-
this._request('PUT', `/api/v1/projects/${id}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1137
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
1138
|
-
}),
|
|
1139
|
-
/**
|
|
1140
|
-
* Remove a configuration value for a project.
|
|
1141
|
-
* @param {string} id - The resource ID
|
|
1142
|
-
* @param {string} namespace - The config namespace
|
|
1143
|
-
* @param {string} configKey - The config key
|
|
1144
|
-
*/
|
|
1145
|
-
deleteConfig: (id, namespace, configKey, auditMessage) =>
|
|
1146
|
-
this._request('DELETE', `/api/v1/projects/${id}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1147
|
-
skipResponseTransform: true,
|
|
1148
|
-
}),
|
|
1149
|
-
/**
|
|
1150
|
-
* Assign a user as a maintainer for this project.
|
|
1151
|
-
* @param {string} id - The resource ID
|
|
1152
|
-
* @param {string} userId - The user ID
|
|
1153
|
-
*/
|
|
1154
|
-
addMaintainer: (id, userId, auditMessage) =>
|
|
1155
|
-
this._request('POST', `/api/v1/projects/${id}/maintainers/${userId}`, { auditMessage }),
|
|
1156
|
-
/**
|
|
1157
|
-
* Remove a user's maintainer privileges for this project.
|
|
1158
|
-
* @param {string} id - The resource ID
|
|
1159
|
-
* @param {string} userId - The user ID
|
|
1160
|
-
*/
|
|
1161
|
-
removeMaintainer: (id, userId, auditMessage) =>
|
|
1162
|
-
this._request('DELETE', `/api/v1/projects/${id}/maintainers/${userId}`, { auditMessage }),
|
|
1163
|
-
/**
|
|
1164
|
-
* Get audit log for a project. Transparently follows pagination cursors
|
|
1165
|
-
* and returns the full flat array.
|
|
1166
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
1167
|
-
* @param {string} projectId - The project ID
|
|
1168
|
-
* @param {string} [startTime] - Start of time range
|
|
1169
|
-
* @param {string} [endTime] - End of time range
|
|
1170
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1171
|
-
* @param {string[]|string} [opTypes] - Only return operations of these
|
|
1172
|
-
* types, spelled as in an entry's `op/type` (e.g.
|
|
1173
|
-
* `['span-layer/create', 'span-layer/delete']`). An entry appears when
|
|
1174
|
-
* one of its operations matches, carrying only the ones that did.
|
|
1175
|
-
*/
|
|
1176
|
-
audit: (projectId, startTime, endTime, asOf, opTypes) =>
|
|
1177
|
-
listAll(this, `/api/v1/projects/${projectId}/audit`, {
|
|
1178
|
-
query: {
|
|
1179
|
-
'start-time': startTime,
|
|
1180
|
-
'end-time': endTime,
|
|
1181
|
-
'as-of': asOf,
|
|
1182
|
-
'op-types': opTypesParam(opTypes),
|
|
1183
|
-
},
|
|
1184
|
-
}),
|
|
1185
|
-
/**
|
|
1186
|
-
* Link a vocabulary to a project.
|
|
1187
|
-
* @param {string} id - The resource ID
|
|
1188
|
-
* @param {string} vocabId - The vocab layer ID
|
|
1189
|
-
*/
|
|
1190
|
-
linkVocab: (id, vocabId, auditMessage) =>
|
|
1191
|
-
this._request('POST', `/api/v1/projects/${id}/vocabs/${vocabId}`, { auditMessage }),
|
|
1192
|
-
/**
|
|
1193
|
-
* Unlink a vocabulary from a project.
|
|
1194
|
-
* @param {string} id - The resource ID
|
|
1195
|
-
* @param {string} vocabId - The vocab layer ID
|
|
1196
|
-
*/
|
|
1197
|
-
unlinkVocab: (id, vocabId, auditMessage) =>
|
|
1198
|
-
this._request('DELETE', `/api/v1/projects/${id}/vocabs/${vocabId}`, { auditMessage }),
|
|
1199
|
-
/**
|
|
1200
|
-
* Get a project by ID. To fetch the project's documents, use
|
|
1201
|
-
* listDocuments(id) — the include-documents flag has been removed.
|
|
1202
|
-
* @param {string} id - The resource ID
|
|
1203
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1204
|
-
*/
|
|
1205
|
-
get: (id, asOf) =>
|
|
1206
|
-
this._request('GET', `/api/v1/projects/${id}`, {
|
|
1207
|
-
queryParams: { 'as-of': asOf },
|
|
1208
|
-
}),
|
|
1209
|
-
/**
|
|
1210
|
-
* List all documents in a project. Transparently follows pagination
|
|
1211
|
-
* cursors and returns the full flat array.
|
|
1212
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
1213
|
-
*
|
|
1214
|
-
* Note: this endpoint does not support temporal (`as-of`) queries; the
|
|
1215
|
-
* server rejects `?as-of=` on the documents-list route with a 400.
|
|
1216
|
-
* @param {string} id - The project ID
|
|
1217
|
-
*/
|
|
1218
|
-
listDocuments: (id) =>
|
|
1219
|
-
listAll(this, `/api/v1/projects/${id}/documents`),
|
|
1220
|
-
/**
|
|
1221
|
-
* Fetch a single page of a project's documents.
|
|
1222
|
-
*
|
|
1223
|
-
* Note: this endpoint does not support temporal (`as-of`) queries; the
|
|
1224
|
-
* server rejects `?as-of=` on the documents-list route with a 400.
|
|
1225
|
-
* @param {string} id - The project ID
|
|
1226
|
-
* @param {object} [opts]
|
|
1227
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1228
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1229
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
1230
|
-
*/
|
|
1231
|
-
listDocumentsPage: (id, { limit, cursor } = {}) =>
|
|
1232
|
-
listPage(this, `/api/v1/projects/${id}/documents`, { limit, cursor }),
|
|
1233
|
-
/**
|
|
1234
|
-
* Async-iterate a project's documents page by page; yields each page's
|
|
1235
|
-
* entries array.
|
|
1236
|
-
*
|
|
1237
|
-
* Note: this endpoint does not support temporal (`as-of`) queries; the
|
|
1238
|
-
* server rejects `?as-of=` on the documents-list route with a 400.
|
|
1239
|
-
* @param {string} id - The project ID
|
|
1240
|
-
* @param {object} [opts]
|
|
1241
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
1242
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
1243
|
-
* @returns {AsyncGenerator<Array>}
|
|
1244
|
-
*/
|
|
1245
|
-
iterDocuments: (id, { pageSize } = {}) =>
|
|
1246
|
-
iterPages(this, `/api/v1/projects/${id}/documents`, { pageSize }),
|
|
1247
|
-
/**
|
|
1248
|
-
* Delete a project and everything in it. This is irrecoverable.
|
|
1249
|
-
*
|
|
1250
|
-
* For a large project this can take a long time, so the client's
|
|
1251
|
-
* per-request timeout is DISABLED by default. Pass `{ timeout }` to
|
|
1252
|
-
* impose a finite bound.
|
|
1253
|
-
* @param {string} id - The resource ID
|
|
1254
|
-
* @param {string} [auditMessage] - Custom audit-log message
|
|
1255
|
-
* @param {object} [options]
|
|
1256
|
-
* @param {number} [options.timeout=0] - Per-request timeout in ms (0/null disables)
|
|
1257
|
-
*/
|
|
1258
|
-
delete: (id, auditMessage, { timeout = 0 } = {}) =>
|
|
1259
|
-
this._request('DELETE', `/api/v1/projects/${id}`, { auditMessage, timeout }),
|
|
1260
|
-
/**
|
|
1261
|
-
* Update a project's name.
|
|
1262
|
-
* @param {string} id - The resource ID
|
|
1263
|
-
* @param {string} name - The name
|
|
1264
|
-
*/
|
|
1265
|
-
update: (id, name, auditMessage) =>
|
|
1266
|
-
this._request('PATCH', `/api/v1/projects/${id}`, { auditMessage,
|
|
1267
|
-
body: bodyOf({ name }),
|
|
1268
|
-
}),
|
|
1269
|
-
/**
|
|
1270
|
-
* List all projects accessible to user. Transparently follows pagination
|
|
1271
|
-
* cursors and returns the full flat array.
|
|
1272
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws if called while batching — use listPage() for a single page in a batch.
|
|
1273
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1274
|
-
*/
|
|
1275
|
-
list: (asOf) =>
|
|
1276
|
-
listAll(this, '/api/v1/projects', { query: { 'as-of': asOf } }),
|
|
1277
|
-
/**
|
|
1278
|
-
* Fetch a single page of projects.
|
|
1279
|
-
* @param {object} [opts]
|
|
1280
|
-
* @param {number} [opts.limit] - Page size (1..1000; server default 100)
|
|
1281
|
-
* @param {string} [opts.cursor] - Opaque cursor from a previous page
|
|
1282
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
1283
|
-
* @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
|
|
1284
|
-
*/
|
|
1285
|
-
listPage: ({ limit, cursor, asOf } = {}) =>
|
|
1286
|
-
listPage(this, '/api/v1/projects', { limit, cursor, query: { 'as-of': asOf } }),
|
|
1287
|
-
/**
|
|
1288
|
-
* Async-iterate projects page by page; yields each page's entries array.
|
|
1289
|
-
* @param {object} [opts]
|
|
1290
|
-
* @param {number} [opts.pageSize] - Per-request page size
|
|
1291
|
-
* @param {string} [opts.asOf] - Temporal query timestamp
|
|
1292
|
-
* Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching — use listPage() for a single page in a batch.
|
|
1293
|
-
* @returns {AsyncGenerator<Array>}
|
|
1294
|
-
*/
|
|
1295
|
-
iterPages: ({ pageSize, asOf } = {}) =>
|
|
1296
|
-
iterPages(this, '/api/v1/projects', { pageSize, query: { 'as-of': asOf } }),
|
|
1297
|
-
/**
|
|
1298
|
-
* Create a new project. Note: this also registers the user as a maintainer.
|
|
1299
|
-
* @param {string} name - The name
|
|
1300
|
-
*/
|
|
1301
|
-
create: (name, auditMessage) =>
|
|
1302
|
-
this._request('POST', '/api/v1/projects', { auditMessage,
|
|
1303
|
-
body: bodyOf({ name }),
|
|
1304
|
-
}),
|
|
1305
|
-
};
|
|
1306
|
-
|
|
1307
|
-
this.textLayers = {
|
|
1308
|
-
/**
|
|
1309
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
1310
|
-
* @param {string} textLayerId - The text layer ID
|
|
1311
|
-
* @param {string} namespace - The config namespace
|
|
1312
|
-
* @param {string} configKey - The config key
|
|
1313
|
-
* @param {any} configValue - Configuration value to set
|
|
1314
|
-
*/
|
|
1315
|
-
setConfig: (textLayerId, namespace, configKey, configValue, auditMessage) =>
|
|
1316
|
-
this._request('PUT', `/api/v1/text-layers/${textLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1317
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
1318
|
-
}),
|
|
1319
|
-
/**
|
|
1320
|
-
* Remove a configuration value for a layer.
|
|
1321
|
-
* @param {string} textLayerId - The text layer ID
|
|
1322
|
-
* @param {string} namespace - The config namespace
|
|
1323
|
-
* @param {string} configKey - The config key
|
|
1324
|
-
*/
|
|
1325
|
-
deleteConfig: (textLayerId, namespace, configKey, auditMessage) =>
|
|
1326
|
-
this._request('DELETE', `/api/v1/text-layers/${textLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1327
|
-
skipResponseTransform: true,
|
|
1328
|
-
}),
|
|
1329
|
-
/**
|
|
1330
|
-
* Get a text layer by ID.
|
|
1331
|
-
* @param {string} textLayerId - The text layer ID
|
|
1332
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1333
|
-
*/
|
|
1334
|
-
get: (textLayerId, asOf) =>
|
|
1335
|
-
this._request('GET', `/api/v1/text-layers/${textLayerId}`, {
|
|
1336
|
-
queryParams: { 'as-of': asOf },
|
|
1337
|
-
}),
|
|
1338
|
-
/**
|
|
1339
|
-
* Delete a text layer.
|
|
1340
|
-
* @param {string} textLayerId - The text layer ID
|
|
1341
|
-
*/
|
|
1342
|
-
delete: (textLayerId, auditMessage) =>
|
|
1343
|
-
this._request('DELETE', `/api/v1/text-layers/${textLayerId}`, { auditMessage }),
|
|
1344
|
-
/**
|
|
1345
|
-
* Update a text layer's name.
|
|
1346
|
-
* @param {string} textLayerId - The text layer ID
|
|
1347
|
-
* @param {string} name - The name
|
|
1348
|
-
*/
|
|
1349
|
-
update: (textLayerId, name, auditMessage) =>
|
|
1350
|
-
this._request('PATCH', `/api/v1/text-layers/${textLayerId}`, { auditMessage,
|
|
1351
|
-
body: bodyOf({ name }),
|
|
1352
|
-
}),
|
|
1353
|
-
/**
|
|
1354
|
-
* Shift a text layer's display order within the project.
|
|
1355
|
-
* @param {string} textLayerId - The text layer ID
|
|
1356
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
1357
|
-
*/
|
|
1358
|
-
shift: (textLayerId, direction, auditMessage) =>
|
|
1359
|
-
this._request('POST', `/api/v1/text-layers/${textLayerId}/shift`, { auditMessage,
|
|
1360
|
-
body: bodyOf({ direction }),
|
|
1361
|
-
}),
|
|
1362
|
-
/**
|
|
1363
|
-
* Create a new text layer for a project.
|
|
1364
|
-
* @param {string} projectId - The project ID
|
|
1365
|
-
* @param {string} name - The name
|
|
1366
|
-
*/
|
|
1367
|
-
create: (projectId, name, auditMessage) =>
|
|
1368
|
-
this._request('POST', '/api/v1/text-layers', { auditMessage,
|
|
1369
|
-
body: bodyOf({ 'project-id': projectId, name }),
|
|
1370
|
-
}),
|
|
1371
|
-
};
|
|
1372
|
-
|
|
1373
|
-
this.vocabItems = {
|
|
1374
|
-
/**
|
|
1375
|
-
* Replace all metadata for a vocab item.
|
|
1376
|
-
* @param {string} id - The resource ID
|
|
1377
|
-
* @param {any} body - The request body
|
|
1378
|
-
*/
|
|
1379
|
-
setMetadata: (id, body, auditMessage) =>
|
|
1380
|
-
this._request('PUT', `/api/v1/vocab-items/${id}/metadata`, { auditMessage,
|
|
1381
|
-
rawBody: body, skipResponseTransform: true,
|
|
1382
|
-
}),
|
|
1383
|
-
/**
|
|
1384
|
-
* Remove all metadata from a vocab item.
|
|
1385
|
-
* @param {string} id - The resource ID
|
|
1386
|
-
*/
|
|
1387
|
-
deleteMetadata: (id, auditMessage) =>
|
|
1388
|
-
this._request('DELETE', `/api/v1/vocab-items/${id}/metadata`, { auditMessage,
|
|
1389
|
-
skipResponseTransform: true,
|
|
1390
|
-
}),
|
|
1391
|
-
/**
|
|
1392
|
-
* Patch (shallow-merge) metadata for a vocab item. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
1393
|
-
* @param {string} id - The resource ID
|
|
1394
|
-
* @param {any} body - The metadata patch
|
|
1395
|
-
*/
|
|
1396
|
-
patchMetadata: (id, body, auditMessage) =>
|
|
1397
|
-
this._request('PATCH', `/api/v1/vocab-items/${id}/metadata`, { auditMessage,
|
|
1398
|
-
rawBody: body, skipResponseTransform: true,
|
|
1399
|
-
}),
|
|
1400
|
-
/**
|
|
1401
|
-
* Create a new vocab item
|
|
1402
|
-
* @param {string} vocabLayerId - The vocab layer ID
|
|
1403
|
-
* @param {string} form - The vocab item form
|
|
1404
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1405
|
-
*/
|
|
1406
|
-
create: (vocabLayerId, form, metadata, auditMessage) =>
|
|
1407
|
-
this._request('POST', '/api/v1/vocab-items', { auditMessage,
|
|
1408
|
-
body: bodyOf({ 'vocab-layer-id': vocabLayerId, form, metadata }),
|
|
1409
|
-
}),
|
|
1410
|
-
/**
|
|
1411
|
-
* Create multiple vocab items in a single operation. Entries may target
|
|
1412
|
-
* different vocab layers; the user must have write access to each.
|
|
1413
|
-
* @param {Array<{vocabLayerId: string, form: string, metadata?: any}>} body - The vocab items to create
|
|
1414
|
-
*/
|
|
1415
|
-
bulkCreate: (body, auditMessage) =>
|
|
1416
|
-
this._request('POST', '/api/v1/vocab-items/bulk', { auditMessage, body }),
|
|
1417
|
-
/**
|
|
1418
|
-
* Delete multiple vocab items in a single operation. Each item's
|
|
1419
|
-
* descendant vocab links are deleted too. Provide an array of IDs.
|
|
1420
|
-
* @param {string[]} body - The vocab item IDs to delete
|
|
1421
|
-
*/
|
|
1422
|
-
bulkDelete: (body, auditMessage) =>
|
|
1423
|
-
this._request('DELETE', '/api/v1/vocab-items/bulk', { auditMessage, body }),
|
|
1424
|
-
/**
|
|
1425
|
-
* Get a vocab item by ID
|
|
1426
|
-
* @param {string} id - The resource ID
|
|
1427
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1428
|
-
*/
|
|
1429
|
-
get: (id, asOf) =>
|
|
1430
|
-
this._request('GET', `/api/v1/vocab-items/${id}`, {
|
|
1431
|
-
queryParams: { 'as-of': asOf },
|
|
1432
|
-
}),
|
|
1433
|
-
/**
|
|
1434
|
-
* Delete a vocab item
|
|
1435
|
-
* @param {string} id - The resource ID
|
|
1436
|
-
*/
|
|
1437
|
-
delete: (id, auditMessage) =>
|
|
1438
|
-
this._request('DELETE', `/api/v1/vocab-items/${id}`, { auditMessage }),
|
|
1439
|
-
/**
|
|
1440
|
-
* Update a vocab item's form
|
|
1441
|
-
* @param {string} id - The resource ID
|
|
1442
|
-
* @param {string} form - The vocab item form
|
|
1443
|
-
*/
|
|
1444
|
-
update: (id, form, auditMessage) =>
|
|
1445
|
-
this._request('PATCH', `/api/v1/vocab-items/${id}`, { auditMessage,
|
|
1446
|
-
body: bodyOf({ form }),
|
|
1447
|
-
}),
|
|
1448
|
-
};
|
|
1449
|
-
|
|
1450
|
-
this.relationLayers = {
|
|
1451
|
-
/**
|
|
1452
|
-
* Shift a relation layer's display order.
|
|
1453
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1454
|
-
* @param {string} direction - The direction ("up" or "down")
|
|
1455
|
-
*/
|
|
1456
|
-
shift: (relationLayerId, direction, auditMessage) =>
|
|
1457
|
-
this._request('POST', `/api/v1/relation-layers/${relationLayerId}/shift`, { auditMessage,
|
|
1458
|
-
body: bodyOf({ direction }),
|
|
1459
|
-
}),
|
|
1460
|
-
/**
|
|
1461
|
-
* Create a new relation layer.
|
|
1462
|
-
* @param {string} spanLayerId - The span layer ID
|
|
1463
|
-
* @param {string} name - The name
|
|
1464
|
-
*/
|
|
1465
|
-
create: (spanLayerId, name, auditMessage) =>
|
|
1466
|
-
this._request('POST', '/api/v1/relation-layers', { auditMessage,
|
|
1467
|
-
body: bodyOf({ 'span-layer-id': spanLayerId, name }),
|
|
1468
|
-
}),
|
|
1469
|
-
/**
|
|
1470
|
-
* Set a configuration value for a layer in an editor namespace.
|
|
1471
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1472
|
-
* @param {string} namespace - The config namespace
|
|
1473
|
-
* @param {string} configKey - The config key
|
|
1474
|
-
* @param {any} configValue - Configuration value to set
|
|
1475
|
-
*/
|
|
1476
|
-
setConfig: (relationLayerId, namespace, configKey, configValue, auditMessage) =>
|
|
1477
|
-
this._request('PUT', `/api/v1/relation-layers/${relationLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1478
|
-
rawBody: configValue, skipResponseTransform: true,
|
|
1479
|
-
}),
|
|
1480
|
-
/**
|
|
1481
|
-
* Remove a configuration value for a layer.
|
|
1482
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1483
|
-
* @param {string} namespace - The config namespace
|
|
1484
|
-
* @param {string} configKey - The config key
|
|
1485
|
-
*/
|
|
1486
|
-
deleteConfig: (relationLayerId, namespace, configKey, auditMessage) =>
|
|
1487
|
-
this._request('DELETE', `/api/v1/relation-layers/${relationLayerId}/config/${namespace}/${configKey}`, { auditMessage,
|
|
1488
|
-
skipResponseTransform: true,
|
|
1489
|
-
}),
|
|
1490
|
-
/**
|
|
1491
|
-
* Get a relation layer by ID.
|
|
1492
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1493
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1494
|
-
*/
|
|
1495
|
-
get: (relationLayerId, asOf) =>
|
|
1496
|
-
this._request('GET', `/api/v1/relation-layers/${relationLayerId}`, {
|
|
1497
|
-
queryParams: { 'as-of': asOf },
|
|
1498
|
-
}),
|
|
1499
|
-
/**
|
|
1500
|
-
* Delete a relation layer.
|
|
1501
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1502
|
-
*/
|
|
1503
|
-
delete: (relationLayerId, auditMessage) =>
|
|
1504
|
-
this._request('DELETE', `/api/v1/relation-layers/${relationLayerId}`, { auditMessage }),
|
|
1505
|
-
/**
|
|
1506
|
-
* Update a relation layer's name.
|
|
1507
|
-
* @param {string} relationLayerId - The relation layer ID
|
|
1508
|
-
* @param {string} name - The name
|
|
1509
|
-
*/
|
|
1510
|
-
update: (relationLayerId, name, auditMessage) =>
|
|
1511
|
-
this._request('PATCH', `/api/v1/relation-layers/${relationLayerId}`, { auditMessage,
|
|
1512
|
-
body: bodyOf({ name }),
|
|
1513
|
-
}),
|
|
1514
|
-
};
|
|
1515
|
-
|
|
1516
|
-
this.tokens = {
|
|
1517
|
-
/**
|
|
1518
|
-
* Create a new token in a token layer. Tokens define text substrings
|
|
1519
|
-
* using begin and end offsets. Tokens may be zero-width and may overlap.
|
|
1520
|
-
* For tokens sharing the same begin, precedence controls the linear
|
|
1521
|
-
* ordering.
|
|
1522
|
-
*
|
|
1523
|
-
* Offsets are 0-based indices in Unicode CODE POINTS (not UTF-16 code
|
|
1524
|
-
* units): a supplementary-plane character (emoji, SMP script) is one
|
|
1525
|
-
* position. JS strings are UTF-16, so do NOT use `str.length` /
|
|
1526
|
-
* `str.substring` to compute offsets — count code points instead
|
|
1527
|
-
* (e.g. `[...str].length`, or iterate with `codePointAt`).
|
|
1528
|
-
* @param {string} tokenLayerId - The token layer ID
|
|
1529
|
-
* @param {string} text - The text ID
|
|
1530
|
-
* @param {number} begin - Start offset, inclusive (Unicode code points)
|
|
1531
|
-
* @param {number} end - End offset, exclusive (Unicode code points)
|
|
1532
|
-
* @param {number} [precedence] - Ordering precedence
|
|
1533
|
-
* @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
|
|
1534
|
-
*/
|
|
1535
|
-
create: (tokenLayerId, text, begin, end, precedence, metadata, auditMessage) =>
|
|
1536
|
-
this._request('POST', '/api/v1/tokens', { auditMessage,
|
|
1537
|
-
body: bodyOf({ 'token-layer-id': tokenLayerId, text, begin, end, precedence, metadata }),
|
|
1538
|
-
}),
|
|
1539
|
-
/**
|
|
1540
|
-
* Get a token.
|
|
1541
|
-
* @param {string} tokenId - The token ID
|
|
1542
|
-
* @param {string} [asOf] - Temporal query timestamp
|
|
1543
|
-
*/
|
|
1544
|
-
get: (tokenId, asOf) =>
|
|
1545
|
-
this._request('GET', `/api/v1/tokens/${tokenId}`, {
|
|
1546
|
-
queryParams: { 'as-of': asOf },
|
|
1547
|
-
}),
|
|
1548
|
-
/**
|
|
1549
|
-
* Delete a token and remove it from any spans. If this causes a span to
|
|
1550
|
-
* have no remaining tokens, the span will also be deleted.
|
|
1551
|
-
* @param {string} tokenId - The token ID
|
|
1552
|
-
*/
|
|
1553
|
-
delete: (tokenId, auditMessage) =>
|
|
1554
|
-
this._request('DELETE', `/api/v1/tokens/${tokenId}`, { auditMessage }),
|
|
1555
|
-
/**
|
|
1556
|
-
* Update a token.
|
|
1557
|
-
* @param {string} tokenId - The token ID
|
|
1558
|
-
* @param {number} [begin] - New start offset, inclusive (Unicode code points)
|
|
1559
|
-
* @param {number} [end] - New end offset, exclusive (Unicode code points)
|
|
1560
|
-
* @param {?number} [precedence] - Ordering precedence. Omit (undefined)
|
|
1561
|
-
* to leave unchanged; pass a number to set; pass null explicitly to
|
|
1562
|
-
* CLEAR it (revert to no explicit ordering). bodyOf keeps null but
|
|
1563
|
-
* drops undefined, so the three cases map correctly to the server.
|
|
1564
|
-
*/
|
|
1565
|
-
update: (tokenId, begin, end, precedence, auditMessage) =>
|
|
1566
|
-
this._request('PATCH', `/api/v1/tokens/${tokenId}`, { auditMessage,
|
|
1567
|
-
body: bodyOf({ begin, end, precedence }),
|
|
1568
|
-
}),
|
|
1569
|
-
/**
|
|
1570
|
-
* Create multiple tokens in a single operation.
|
|
1571
|
-
* @param {Array} body - The request body
|
|
1572
|
-
* @returns {Promise<{ids: string[]}>} The created entity IDs, in input order. (bulkDelete resolves to no value.)
|
|
1573
|
-
*/
|
|
1574
|
-
bulkCreate: (body, auditMessage) =>
|
|
1575
|
-
this._request('POST', '/api/v1/tokens/bulk', { auditMessage, body }),
|
|
1576
|
-
/**
|
|
1577
|
-
* Delete multiple tokens in a single operation. Provide an array of IDs.
|
|
1578
|
-
* @param {Array} body - The request body
|
|
1579
|
-
*/
|
|
1580
|
-
bulkDelete: (body, auditMessage) =>
|
|
1581
|
-
this._request('DELETE', '/api/v1/tokens/bulk', { auditMessage, body }),
|
|
1582
|
-
/**
|
|
1583
|
-
* Split a token at a Unicode code-point offset. The original token becomes the
|
|
1584
|
-
* left half (keeps its ID, spans, vocab-links); the new right token's ID is returned.
|
|
1585
|
-
* @param {string} tokenId - The token ID
|
|
1586
|
-
* @param {number} position - Code-point offset to split at (strictly between begin and end)
|
|
1587
|
-
*/
|
|
1588
|
-
split: (tokenId, position, auditMessage) =>
|
|
1589
|
-
this._request('POST', `/api/v1/tokens/${tokenId}/split`, { auditMessage,
|
|
1590
|
-
body: bodyOf({ position }),
|
|
1591
|
-
}),
|
|
1592
|
-
/**
|
|
1593
|
-
* Merge two tokens. The left token (smaller begin) survives with the combined
|
|
1594
|
-
* extent; the right is deleted and its spans/vocab-links are reparented to the left.
|
|
1595
|
-
* On partitioning layers the tokens must be adjacent; on non-overlapping layers the
|
|
1596
|
-
* merged extent must not engulf a third token.
|
|
1597
|
-
* @param {string} tokenId - The anchor token ID
|
|
1598
|
-
* @param {string} otherTokenId - The other token to merge in
|
|
1599
|
-
*/
|
|
1600
|
-
merge: (tokenId, otherTokenId, auditMessage) =>
|
|
1601
|
-
this._request('POST', `/api/v1/tokens/${tokenId}/merge`, { auditMessage,
|
|
1602
|
-
body: bodyOf({ 'other-token-id': otherTokenId }),
|
|
1603
|
-
}),
|
|
1604
|
-
/**
|
|
1605
|
-
* Shift a token's boundary. On partitioning layers the adjacent token is
|
|
1606
|
-
* auto-adjusted to preserve the partition; on non-overlapping layers a shift that
|
|
1607
|
-
* would create an overlap is rejected.
|
|
1608
|
-
* @param {string} tokenId - The token ID
|
|
1609
|
-
* @param {number} [begin] - New start offset, inclusive (Unicode code points)
|
|
1610
|
-
* @param {number} [end] - New end offset, exclusive (Unicode code points)
|
|
1611
|
-
*/
|
|
1612
|
-
shift: (tokenId, begin, end, auditMessage) =>
|
|
1613
|
-
this._request('POST', `/api/v1/tokens/${tokenId}/shift`, { auditMessage,
|
|
1614
|
-
body: bodyOf({ begin, end }),
|
|
1615
|
-
}),
|
|
1616
|
-
/**
|
|
1617
|
-
* Replace all metadata for a token.
|
|
1618
|
-
* @param {string} tokenId - The token ID
|
|
1619
|
-
* @param {any} body - The request body
|
|
1620
|
-
*/
|
|
1621
|
-
setMetadata: (tokenId, body, auditMessage) =>
|
|
1622
|
-
this._request('PUT', `/api/v1/tokens/${tokenId}/metadata`, { auditMessage,
|
|
1623
|
-
rawBody: body, skipResponseTransform: true,
|
|
1624
|
-
}),
|
|
1625
|
-
/**
|
|
1626
|
-
* Remove all metadata from a token.
|
|
1627
|
-
* @param {string} tokenId - The token ID
|
|
1628
|
-
*/
|
|
1629
|
-
deleteMetadata: (tokenId, auditMessage) =>
|
|
1630
|
-
this._request('DELETE', `/api/v1/tokens/${tokenId}/metadata`, { auditMessage,
|
|
1631
|
-
skipResponseTransform: true,
|
|
1632
|
-
}),
|
|
1633
|
-
/**
|
|
1634
|
-
* Patch (shallow-merge) metadata for a token. Keys present in the body are set or overwritten; keys not present are left untouched; a key whose value is null is deleted. Merging is top-level only (nested objects are replaced wholesale, not deep-merged), so a literal null cannot be stored as a value. An empty body changes no metadata.
|
|
1635
|
-
* @param {string} tokenId - The token ID
|
|
1636
|
-
* @param {any} body - The metadata patch
|
|
1637
|
-
*/
|
|
1638
|
-
patchMetadata: (tokenId, body, auditMessage) =>
|
|
1639
|
-
this._request('PATCH', `/api/v1/tokens/${tokenId}/metadata`, { auditMessage,
|
|
1640
|
-
rawBody: body, skipResponseTransform: true,
|
|
1641
|
-
}),
|
|
1642
|
-
};
|
|
1643
|
-
|
|
1644
|
-
this.messages = {
|
|
1645
|
-
/**
|
|
1646
|
-
* Open a Server-Sent Events stream for a project.
|
|
1647
|
-
* @param {string} projectId - The UUID of the project to listen to
|
|
1648
|
-
* @param {function} onEvent - Callback function that receives (eventType, data). If it returns true, listening will stop.
|
|
1649
|
-
* @param {string} [path] - Stream path under baseUrl (defaults to the project /listen bus; service channels pass their own).
|
|
1650
|
-
* @returns {Object} SSE connection object with .close() and .getStats() methods
|
|
1651
|
-
*/
|
|
1652
|
-
listen: (projectId, onEvent, path) =>
|
|
1653
|
-
createSSEConnection(this, projectId, onEvent, path),
|
|
1654
|
-
|
|
1655
|
-
/**
|
|
1656
|
-
* Send a message to project listeners.
|
|
1657
|
-
*
|
|
1658
|
-
* `data` may be any JSON value and is sent VERBATIM (`rawBody`): a
|
|
1659
|
-
* message payload is opaque application data, like `metadata` and
|
|
1660
|
-
* `config`, so its keys must not be re-cased on the way out. Without
|
|
1661
|
-
* this a key such as `case-marker` would reach listeners as
|
|
1662
|
-
* `caseMarker` in JS and `case_marker` in Python. `listen` restores it
|
|
1663
|
-
* verbatim on the way in.
|
|
1664
|
-
* @param {string} projectId - The UUID of the project to send to
|
|
1665
|
-
* @param {any} data - The message data to send
|
|
1666
|
-
* @returns {Promise<any>} Response from the send operation
|
|
1667
|
-
*/
|
|
1668
|
-
sendMessage: (projectId, data, auditMessage) =>
|
|
1669
|
-
this._request('POST', `/api/v1/projects/${projectId}/message`, { auditMessage,
|
|
1670
|
-
rawBody: { body: data },
|
|
1671
|
-
}),
|
|
1672
|
-
|
|
1673
|
-
/**
|
|
1674
|
-
* Discover the services seen on a project (synchronous GET). Currently
|
|
1675
|
-
* connected services carry `online: true`; previously-seen offline ones
|
|
1676
|
-
* carry `online: false` plus a `lastSeenAt` stamp.
|
|
1677
|
-
* @param {string} projectId - The UUID of the project to query
|
|
1678
|
-
* @returns {Promise<Array>} Array of discovered service information
|
|
1679
|
-
*/
|
|
1680
|
-
discoverServices: (projectId) =>
|
|
1681
|
-
discoverServices(this, projectId),
|
|
1682
|
-
|
|
1683
|
-
/**
|
|
1684
|
-
* Forget a previously-seen (offline) service. Maintainer-only; 409 if
|
|
1685
|
-
* the service is currently connected.
|
|
1686
|
-
* @param {string} projectId - The UUID of the project
|
|
1687
|
-
* @param {string} serviceId - The ID of the service to forget
|
|
1688
|
-
* @returns {Promise<void>}
|
|
1689
|
-
*/
|
|
1690
|
-
discardService: (projectId, serviceId) =>
|
|
1691
|
-
discardService(this, projectId, serviceId),
|
|
1692
|
-
|
|
1693
|
-
/**
|
|
1694
|
-
* Register as a service and handle incoming work requests.
|
|
1695
|
-
* @param {string} projectId - The UUID of the project to serve
|
|
1696
|
-
* @param {Object} serviceInfo - Service information {serviceId, serviceName, description}
|
|
1697
|
-
* @param {function} onServiceRequest - Callback (data, responseHelper)
|
|
1698
|
-
* @param {Object} [extras] - Optional additional service metadata
|
|
1699
|
-
* @returns {Object} Service registration object with .stop() method
|
|
1700
|
-
*/
|
|
1701
|
-
serve: (projectId, serviceInfo, onServiceRequest, extras) =>
|
|
1702
|
-
serve(this, projectId, serviceInfo, onServiceRequest, extras),
|
|
1703
|
-
|
|
1704
|
-
/**
|
|
1705
|
-
* Request a service to perform work and await its result.
|
|
1706
|
-
* @param {string} projectId - The UUID of the project
|
|
1707
|
-
* @param {string} serviceId - The ID of the service to request
|
|
1708
|
-
* @param {any} data - The request data
|
|
1709
|
-
* @param {number} [timeout] - Timeout in milliseconds (default: 10000)
|
|
1710
|
-
* @param {function} [onProgress] - Called with each progress payload {percent, message}
|
|
1711
|
-
* @returns {Promise<any>} Service response
|
|
1712
|
-
*/
|
|
1713
|
-
requestService: (projectId, serviceId, data, timeout, onProgress) =>
|
|
1714
|
-
requestService(this, projectId, serviceId, data, timeout, onProgress),
|
|
1715
|
-
};
|
|
1716
|
-
|
|
1717
|
-
/**
|
|
1718
|
-
* Run a query over every project you can read.
|
|
1719
|
-
*
|
|
1720
|
-
* `body` is the query AST. Its keys follow the usual client convention
|
|
1721
|
-
* (camelCase, e.g. `scope.projectIds`) and are converted to the wire
|
|
1722
|
-
* format automatically; clause heads and variables are plain strings you
|
|
1723
|
-
* write literally (e.g. `'span'`, `'?s1'`, `'vocab-link'`). Example:
|
|
1724
|
-
*
|
|
1725
|
-
* await client.query({
|
|
1726
|
-
* find: ['?s1', '?s2'],
|
|
1727
|
-
* where: [
|
|
1728
|
-
* ['span', '?s1', { layer: posLayerId, value: 'NOUN' }],
|
|
1729
|
-
* ['span', '?s2', { layer: posLayerId, value: 'VERB' }],
|
|
1730
|
-
* ['covers', '?s1', '?t1'], ['covers', '?s2', '?t2'],
|
|
1731
|
-
* ['precedes', '?t1', '?t2'],
|
|
1732
|
-
* ],
|
|
1733
|
-
* return: 'entities', // 'ids' (default) | 'entities' | 'count'
|
|
1734
|
-
* limit: 100,
|
|
1735
|
-
* });
|
|
1736
|
-
*
|
|
1737
|
-
* A `layer` is referenced by its id (its UUID) only — not by name or
|
|
1738
|
-
* path. To match a layer by name, bind it with a `*-layer` clause (e.g.
|
|
1739
|
-
* `['span-layer', '?sl', { name: 'pos' }]`) and use the variable.
|
|
1740
|
-
*
|
|
1741
|
-
* Optional keys: `scope` (restrict to projects by id, `{projectIds}`), `orderBy`
|
|
1742
|
-
* (sort rows), and `bindings` (substitute `?name` placeholders with literals).
|
|
1743
|
-
* `return` may also be an aggregate spec `{group, aggregates}`. See the query
|
|
1744
|
-
* language reference.
|
|
1745
|
-
*
|
|
1746
|
-
* @param {Object} body - The query AST ({find, where, scope?, limit?, orderBy?,
|
|
1747
|
-
* return?, bindings?}).
|
|
1748
|
-
* @returns {Promise<Object>} For 'ids'/'entities': {columns, results, count, truncated}.
|
|
1749
|
-
* For 'count': {return: 'count', count}. Entity cells are full entity objects
|
|
1750
|
-
* (same shape as the GET endpoints).
|
|
1751
|
-
*/
|
|
1752
|
-
this.query = (body, auditMessage) =>
|
|
1753
|
-
this._request('POST', '/api/v1/query', { auditMessage, body });
|
|
1754
|
-
|
|
1755
|
-
// Logical-operation groups (audit-log grouping). There is no create: a
|
|
1756
|
-
// group row is made lazily by the first write carrying `?group-id=`
|
|
1757
|
-
// (see beginOperation). Not an audited write, so no auditMessage arg.
|
|
1758
|
-
this.operationGroups = {
|
|
1759
|
-
/**
|
|
1760
|
-
* Get a logical-operation group (its label + creator).
|
|
1761
|
-
* @param {string} id - The group id
|
|
1762
|
-
*/
|
|
1763
|
-
get: (id) =>
|
|
1764
|
-
this._request('GET', `/api/v1/operation-groups/${id}`),
|
|
1765
|
-
/**
|
|
1766
|
-
* Relabel a logical-operation group after the fact. Owner or admin only.
|
|
1767
|
-
* @param {string} id - The group id
|
|
1768
|
-
* @param {string|null} message - The new label
|
|
1769
|
-
*/
|
|
1770
|
-
update: (id, message) =>
|
|
1771
|
-
this._request('PATCH', `/api/v1/operation-groups/${id}`, { body: { message } }),
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
// --- Core methods ---
|
|
1776
|
-
|
|
1777
|
-
async _request(method, path, options = {}) {
|
|
1778
|
-
return makeRequest(this, method, path, options);
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
/**
|
|
1782
|
-
* Enter strict mode for a specific document, requiring document version
|
|
1783
|
-
* headers so that conflicting concurrent writes are rejected.
|
|
1784
|
-
* @param {string} documentId - The ID of the document to track versions for
|
|
1785
|
-
*/
|
|
1786
|
-
enterStrictMode(documentId) {
|
|
1787
|
-
this.strictModeDocumentId = documentId;
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
|
-
/** Exit strict mode and stop tracking document versions for writes. */
|
|
1791
|
-
exitStrictMode() {
|
|
1792
|
-
this.strictModeDocumentId = null;
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
/**
|
|
1796
|
-
* Begin a LOGICAL OPERATION: a user-meaningful action ("Merge morphemes",
|
|
1797
|
-
* "Re-transcribe") implemented as many low-level writes, possibly across
|
|
1798
|
-
* several batches and even a service round-trip. Until `endOperation()`,
|
|
1799
|
-
* every write is stamped with a client-minted `?group-id=` (and the message)
|
|
1800
|
-
* so the audit log shows the whole run as ONE expandable entry labeled
|
|
1801
|
-
* `message`, with each write's own description underneath.
|
|
1802
|
-
*
|
|
1803
|
-
* Grouping is orthogonal to batches: a batch is a transaction boundary, an
|
|
1804
|
-
* operation is an intent boundary. An operation is NOT atomic — if write 3
|
|
1805
|
-
* of 5 fails, writes 1–2 stay committed (and logged under the group). Use a
|
|
1806
|
-
* batch inside the operation for any step that must be all-or-nothing.
|
|
1807
|
-
*
|
|
1808
|
-
* Nesting flattens: a `beginOperation` while one is open is a no-op that
|
|
1809
|
-
* joins the outer operation (the outer label wins), and the matching
|
|
1810
|
-
* `endOperation` is likewise a no-op. The label is recorded on the FIRST
|
|
1811
|
-
* write, so an operation that is never ended (crash, closed tab) is still
|
|
1812
|
-
* labeled in the log.
|
|
1813
|
-
*
|
|
1814
|
-
* @param {string} message - Human label for the operation.
|
|
1815
|
-
* @param {object} [opts] - Optional `{ id }`: adopt an existing group id instead of minting one (a service joining the requester's operation; `requestService` propagates an open operation to the service automatically).
|
|
1816
|
-
* @returns {string} The operation's group id.
|
|
1817
|
-
*/
|
|
1818
|
-
beginOperation(message, { id } = {}) {
|
|
1819
|
-
if (this.operationGroup) {
|
|
1820
|
-
this.operationGroup.depth += 1;
|
|
1821
|
-
return this.operationGroup.id;
|
|
1822
|
-
}
|
|
1823
|
-
this.operationGroup = {
|
|
1824
|
-
id: id || crypto.randomUUID(),
|
|
1825
|
-
message: message == null ? null : String(message),
|
|
1826
|
-
depth: 1,
|
|
1827
|
-
written: false,
|
|
1828
|
-
refined: undefined,
|
|
1829
|
-
};
|
|
1830
|
-
return this.operationGroup.id;
|
|
1831
|
-
}
|
|
1832
|
-
|
|
1833
|
-
/**
|
|
1834
|
-
* End the current logical operation. With no argument this is purely local
|
|
1835
|
-
* (no request). Pass a refined `message` to relabel the group now that the
|
|
1836
|
-
* outcome is known (e.g. `endOperation('Merged 3 morphemes')`) — that sends
|
|
1837
|
-
* one PATCH, skipped if the operation never wrote anything. A refine from a
|
|
1838
|
-
* nested (flattened) `endOperation` is ignored; the outer label wins.
|
|
1839
|
-
* @param {string} [message] - Optional refined label.
|
|
1840
|
-
* @returns {Promise<void>}
|
|
1841
|
-
*/
|
|
1842
|
-
async endOperation(message) {
|
|
1843
|
-
const group = this.operationGroup;
|
|
1844
|
-
if (!group) return;
|
|
1845
|
-
if (group.depth > 1) {
|
|
1846
|
-
group.depth -= 1;
|
|
1847
|
-
return;
|
|
1848
|
-
}
|
|
1849
|
-
this.operationGroup = null;
|
|
1850
|
-
const refined = message !== undefined ? message : group.refined;
|
|
1851
|
-
if (refined !== undefined && group.written) {
|
|
1852
|
-
try {
|
|
1853
|
-
await this.operationGroups.update(group.id, refined);
|
|
1854
|
-
} catch (e) {
|
|
1855
|
-
// 404: the group never materialized server-side (every tagged write
|
|
1856
|
-
// failed or a batch was aborted). Nothing to relabel — not an error.
|
|
1857
|
-
if (e.status !== 404) throw e;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
/**
|
|
1863
|
-
* Run `fn` as one logical operation (see beginOperation), ending it when
|
|
1864
|
-
* `fn` settles — including on throw. `fn` receives a `setMessage(msg)`
|
|
1865
|
-
* callback to refine the label once the outcome is known.
|
|
1866
|
-
*
|
|
1867
|
-
* await client.withOperation('Merge morphemes', async (setMessage) => {
|
|
1868
|
-
* await client.batched(() => { ... });
|
|
1869
|
-
* setMessage(`Merged ${n} morphemes`);
|
|
1870
|
-
* });
|
|
1871
|
-
*
|
|
1872
|
-
* @param {string} message - Human label for the operation.
|
|
1873
|
-
* @param {function} fn - The work to run; receives `setMessage(msg)` to refine the label once the outcome is known.
|
|
1874
|
-
* @returns {Promise<any>} Whatever `fn` resolves to.
|
|
1875
|
-
*/
|
|
1876
|
-
async withOperation(message, fn) {
|
|
1877
|
-
this.beginOperation(message);
|
|
1878
|
-
const group = this.operationGroup;
|
|
1879
|
-
const setMessage = (msg) => { if (group.depth === 1) group.refined = msg; };
|
|
1880
|
-
try {
|
|
1881
|
-
return await fn(setMessage);
|
|
1882
|
-
} finally {
|
|
1883
|
-
await this.endOperation();
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
/** Begin a batch of operations. Subsequent API calls will be queued. */
|
|
1888
|
-
beginBatch() {
|
|
1889
|
-
this.isBatching = true;
|
|
1890
|
-
this.batchOperations = [];
|
|
1891
|
-
// Strict mode stamps the expected document-version on the FIRST write of
|
|
1892
|
-
// the batch only (see _request) — reset the marker per batch.
|
|
1893
|
-
this.batchVersionStamped = false;
|
|
1894
|
-
}
|
|
1895
|
-
|
|
1896
|
-
/**
|
|
1897
|
-
* Submit all queued batch operations as a single batch request, executed
|
|
1898
|
-
* atomically. If any operation fails, all changes are rolled back.
|
|
1899
|
-
* @returns {Promise<Array>} Array of results corresponding to each operation
|
|
1900
|
-
*/
|
|
1901
|
-
async submitBatch() {
|
|
1902
|
-
if (!this.isBatching) {
|
|
1903
|
-
throw new Error('No active batch. Call beginBatch() first.');
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
if (this.batchOperations.length === 0) {
|
|
1907
|
-
this.isBatching = false;
|
|
1908
|
-
return [];
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
try {
|
|
1912
|
-
let url = `${this.baseUrl}/api/v1/batch`;
|
|
1913
|
-
const body = this.batchOperations.map(op => ({
|
|
1914
|
-
path: op.path,
|
|
1915
|
-
method: op.method.toUpperCase(),
|
|
1916
|
-
...(op.body && { body: op.body }),
|
|
1917
|
-
}));
|
|
1918
|
-
|
|
1919
|
-
const fetchOptions = {
|
|
1920
|
-
method: 'POST',
|
|
1921
|
-
headers: {
|
|
1922
|
-
'Authorization': `Bearer ${this.token}`,
|
|
1923
|
-
'Content-Type': 'application/json',
|
|
1924
|
-
},
|
|
1925
|
-
body: JSON.stringify(body),
|
|
1926
|
-
};
|
|
1927
|
-
const signal = timeoutSignal(this.timeout);
|
|
1928
|
-
if (signal) fetchOptions.signal = signal;
|
|
1929
|
-
|
|
1930
|
-
try {
|
|
1931
|
-
const response = await fetch(url, fetchOptions);
|
|
1932
|
-
if (!response.ok) {
|
|
1933
|
-
throw makeHttpError(response, await parseErrorBody(response), url, 'POST');
|
|
1934
|
-
}
|
|
1935
|
-
|
|
1936
|
-
const results = await response.json();
|
|
1937
|
-
|
|
1938
|
-
// Extract document versions from each batch response
|
|
1939
|
-
for (const result of results) {
|
|
1940
|
-
if (result.headers && result.headers['X-Document-Versions']) {
|
|
1941
|
-
try {
|
|
1942
|
-
const versionsMap = JSON.parse(result.headers['X-Document-Versions']);
|
|
1943
|
-
if (typeof versionsMap === 'object' && versionsMap !== null) {
|
|
1944
|
-
// Clone once per response, then merge — not once per entry.
|
|
1945
|
-
this.documentVersions = { ...this.documentVersions, ...versionsMap };
|
|
1946
|
-
}
|
|
1947
|
-
} catch (e) {
|
|
1948
|
-
console.warn('Failed to parse document versions header from batch response:', e);
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
|
-
return results.map(result => transformResponse(result));
|
|
1954
|
-
} catch (error) {
|
|
1955
|
-
if (error.status) throw error;
|
|
1956
|
-
throw makeNetworkError(error, url, 'POST');
|
|
1957
|
-
}
|
|
1958
|
-
} finally {
|
|
1959
|
-
this.isBatching = false;
|
|
1960
|
-
this.batchOperations = [];
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
/** Abort the current batch without executing any operations. */
|
|
1965
|
-
abortBatch() {
|
|
1966
|
-
this.isBatching = false;
|
|
1967
|
-
this.batchOperations = [];
|
|
1968
|
-
}
|
|
1969
|
-
|
|
1970
|
-
/**
|
|
1971
|
-
* Check if currently in batch mode.
|
|
1972
|
-
* @returns {boolean}
|
|
1973
|
-
*/
|
|
1974
|
-
isBatchMode() {
|
|
1975
|
-
return this.isBatching;
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
/**
|
|
1979
|
-
* Run `fn` with a batch open, then submit all queued ops as ONE atomic
|
|
1980
|
-
* request — or abort the batch if `fn` throws, so a half-open batch can never
|
|
1981
|
-
* silently swallow later non-batch calls. `fn` makes the (queued) client
|
|
1982
|
-
* calls; it must NOT call submitBatch itself. Resolves to the batch results
|
|
1983
|
-
* array (`[]` if `fn` queued nothing).
|
|
1984
|
-
*
|
|
1985
|
-
* const [sentRes, wordRes] = await client.batched(async () => {
|
|
1986
|
-
* client.tokens.bulkCreate(sentenceOps);
|
|
1987
|
-
* client.tokens.bulkCreate(wordOps);
|
|
1988
|
-
* });
|
|
1989
|
-
*
|
|
1990
|
-
* Server-side a batch runs sequentially in one transaction (a child op sees
|
|
1991
|
-
* parents created earlier in the same `fn`; any op's failure rolls the whole
|
|
1992
|
-
* batch back). Not nestable. Named `batched()` because `client.batch` is the
|
|
1993
|
-
* low-level batch resource.
|
|
1994
|
-
* @param {() => (void | Promise<void>)} fn
|
|
1995
|
-
* @returns {Promise<Array>}
|
|
1996
|
-
*/
|
|
1997
|
-
async batched(fn) {
|
|
1998
|
-
this.beginBatch();
|
|
1999
|
-
try {
|
|
2000
|
-
await fn();
|
|
2001
|
-
} catch (e) {
|
|
2002
|
-
// fn failed: drop the queued ops so the client leaves batch mode and
|
|
2003
|
-
// later plain calls don't queue into a never-submitted batch.
|
|
2004
|
-
if (this.isBatchMode()) this.abortBatch();
|
|
2005
|
-
throw e;
|
|
2006
|
-
}
|
|
2007
|
-
return this.submitBatch();
|
|
2008
|
-
}
|
|
2009
|
-
|
|
2010
|
-
/**
|
|
2011
|
-
* Authenticate and return a new client instance with token. This is the
|
|
2012
|
-
* single auth entry point — there is no `client.login` resource.
|
|
2013
|
-
* @param {string} baseUrl - The base URL for the API
|
|
2014
|
-
* @param {string} userId - User ID for authentication
|
|
2015
|
-
* @param {string} password - Password for authentication
|
|
2016
|
-
* @param {object} [options] - Client options forwarded to the constructor (e.g. { timeout })
|
|
2017
|
-
* @returns {Promise<PlaidClient>} - Authenticated client instance
|
|
2018
|
-
*/
|
|
2019
|
-
/**
|
|
2020
|
-
* Build the link to hand someone for an invite code. The server never sees
|
|
2021
|
-
* an app URL, so the app that minted the invite is the one that names it.
|
|
2022
|
-
* Both SPAs use HashRouter, so the code rides in the fragment — which also
|
|
2023
|
-
* keeps it out of server access logs.
|
|
2024
|
-
* @param {string} appUrl - Where the SPA lives, e.g. "https://plaid.example.org/igt/"
|
|
2025
|
-
* @param {string} code - The code returned by invites.create()
|
|
2026
|
-
* @returns {string}
|
|
2027
|
-
*/
|
|
2028
|
-
static inviteUrl(appUrl, code) {
|
|
2029
|
-
return `${appUrl.replace(/#.*$/, '').replace(/\/$/, '')}/#/invite/${encodeURIComponent(code)}`;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
/**
|
|
2033
|
-
* Describe an invite code, with NO authentication — this is what a signup
|
|
2034
|
-
* page calls before the redeemer has an account. Returns the kind of link
|
|
2035
|
-
* ("signup" or "password-reset"), its status ("active", "used", "expired",
|
|
2036
|
-
* "revoked"), and the project it grants access to, if any.
|
|
2037
|
-
*
|
|
2038
|
-
* Throws a 404-shaped error if the code is unknown. A known-but-dead code
|
|
2039
|
-
* resolves normally with a non-"active" status, so the page can say why.
|
|
2040
|
-
* @param {string} baseUrl - The API base URL
|
|
2041
|
-
* @param {string} code - The invite code
|
|
2042
|
-
* @param {object} [options]
|
|
2043
|
-
* @returns {Promise<{kind: string, status: string, expiresAt: string, projectName?: string, projectRole?: string, username?: string, grantAdmin: boolean}>}
|
|
2044
|
-
*/
|
|
2045
|
-
static async lookupInvite(baseUrl, code, options = {}) {
|
|
2046
|
-
return anonymousPost(baseUrl, '/api/v1/invite-codes/lookup', { code }, options);
|
|
2047
|
-
}
|
|
2048
|
-
|
|
2049
|
-
/**
|
|
2050
|
-
* Redeem an invite code, with NO authentication.
|
|
2051
|
-
*
|
|
2052
|
-
* For a signup invite, pass `username` and `password` to create the account;
|
|
2053
|
-
* the invite's grants are applied in the same transaction. For a password
|
|
2054
|
-
* reset link, pass `password` only. Resolves to a logged-in client, exactly
|
|
2055
|
-
* like login() — the redeemer just chose these credentials, so there is no
|
|
2056
|
-
* reason to send them to a login form to retype them.
|
|
2057
|
-
* @param {string} baseUrl - The API base URL
|
|
2058
|
-
* @param {string} code - The invite code
|
|
2059
|
-
* @param {object} credentials
|
|
2060
|
-
* @param {string} [credentials.username] - Desired username (signup only)
|
|
2061
|
-
* @param {string} credentials.password - Desired password (min 8 characters)
|
|
2062
|
-
* @param {object} [options]
|
|
2063
|
-
* @returns {Promise<{client: PlaidClient, userId: string, kind: string}>}
|
|
2064
|
-
*/
|
|
2065
|
-
static async redeemInvite(baseUrl, code, { username, password } = {}, options = {}) {
|
|
2066
|
-
const data = await anonymousPost(
|
|
2067
|
-
baseUrl, '/api/v1/invite-codes/redeem',
|
|
2068
|
-
bodyOf({ code, username, password }), options,
|
|
2069
|
-
);
|
|
2070
|
-
return {
|
|
2071
|
-
client: new PlaidClient(baseUrl.replace(/\/$/, ''), data.token || '', options),
|
|
2072
|
-
userId: data.userId,
|
|
2073
|
-
kind: data.kind,
|
|
2074
|
-
};
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
static async login(baseUrl, userId, password, options = {}) {
|
|
2078
|
-
baseUrl = baseUrl.replace(/\/$/, '');
|
|
2079
|
-
const url = `${baseUrl}/api/v1/login`;
|
|
2080
|
-
try {
|
|
2081
|
-
const fetchOptions = {
|
|
2082
|
-
method: 'POST',
|
|
2083
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2084
|
-
body: JSON.stringify({ 'user-id': userId, password }),
|
|
2085
|
-
};
|
|
2086
|
-
const signal = timeoutSignal(options.timeout !== undefined ? options.timeout : DEFAULT_TIMEOUT_MS);
|
|
2087
|
-
if (signal) fetchOptions.signal = signal;
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {import("./types.js").TextEditOp} TextEditOp
|
|
11
|
+
* @typedef {import("./types.js").TextGap} TextGap
|
|
12
|
+
* @typedef {import("./types.js").MetadataOp} MetadataOp
|
|
13
|
+
* @typedef {import("./types.js").LayerConstraint} LayerConstraint
|
|
14
|
+
* @typedef {import("./types.js").ConstraintWriteOptions} ConstraintWriteOptions
|
|
15
|
+
* @typedef {import("./types.js").ConstraintViolation} ConstraintViolation
|
|
16
|
+
* @typedef {import("./types.js").Requester} Requester
|
|
17
|
+
* @typedef {import("./types.js").BatchRef} BatchRef
|
|
18
|
+
* @typedef {import("./types.js").ServiceParam} ServiceParam
|
|
19
|
+
* @typedef {import("./types.js").ServiceInfo} ServiceInfo
|
|
20
|
+
* @typedef {import("./types.js").DiscoveredService} DiscoveredService
|
|
21
|
+
* @typedef {import("./types.js").ResponseHelper} ResponseHelper
|
|
22
|
+
* @typedef {import("./types.js").ServiceRegistration} ServiceRegistration
|
|
23
|
+
* @typedef {import("./types.js").ServiceStatusEvent} ServiceStatusEvent
|
|
24
|
+
* @typedef {import("./types.js").OperationGroupsBundle} OperationGroupsBundle
|
|
25
|
+
* @typedef {import("./types.js").PlaidClientOptions} PlaidClientOptions
|
|
26
|
+
* @typedef {import("./types.js").OperationKind} OperationKind
|
|
27
|
+
* @typedef {import("./types.js").KeySeed} KeySeed
|
|
28
|
+
* @typedef {import("./types.js").PlaidBatch} PlaidBatch
|
|
29
|
+
* @typedef {import("./types.js").AuditPageOptions} AuditPageOptions
|
|
30
|
+
* @typedef {import("./types.js").VocabItemRestoreSummary} VocabItemRestoreSummary
|
|
31
|
+
* @typedef {import("./types.js").UserDataEntry} UserDataEntry
|
|
32
|
+
* @typedef {import("./types.js").AdminUserDataEntry} AdminUserDataEntry
|
|
33
|
+
* @typedef {import("./types.js").ActivityTallyRow} ActivityTallyRow
|
|
34
|
+
* @typedef {import("./types.js").ConstraintRepair} ConstraintRepair
|
|
35
|
+
* @typedef {import("./types.js").ValueSetRule} ValueSetRule
|
|
36
|
+
* @typedef {import("./types.js").RefusedValues} RefusedValues
|
|
37
|
+
* @typedef {import("./types.js").CommentableType} CommentableType
|
|
38
|
+
* @typedef {import("./types.js").Comment} Comment
|
|
39
|
+
* @typedef {import("./types.js").Guideline} Guideline
|
|
40
|
+
* @typedef {import("./types.js").ClientEventType} ClientEventType
|
|
41
|
+
* @typedef {import("./types.js").ClientEvent} ClientEvent
|
|
42
|
+
* @typedef {import("./types.js").Invite} Invite
|
|
43
|
+
* @typedef {import("./types.js").ServerLimits} ServerLimits
|
|
44
|
+
* @typedef {import("./types.js").ServerHealth} ServerHealth
|
|
45
|
+
* @typedef {import("./types.js").RateLimitBucket} RateLimitBucket
|
|
46
|
+
* @typedef {import("./types.js").RateLimitSnapshot} RateLimitSnapshot
|
|
47
|
+
* @typedef {import("./types.js").BackupStatus} BackupStatus
|
|
48
|
+
* @typedef {import("./types.js").ServerReport} ServerReport
|
|
49
|
+
* @typedef {import("./types.js").LiveLog} LiveLog
|
|
50
|
+
* @typedef {import("./types.js").SSEConnection} SSEConnection
|
|
51
|
+
* @typedef {import("./types.js").SSEStats} SSEStats
|
|
52
|
+
* @typedef {import("./types.js").AtomicValue} AtomicValue
|
|
53
|
+
* @typedef {import("./types.js").Created} Created
|
|
54
|
+
* @typedef {import("./types.js").Project} Project
|
|
55
|
+
* @typedef {import("./types.js").ProjectTextLayer} ProjectTextLayer
|
|
56
|
+
* @typedef {import("./types.js").ProjectTokenLayer} ProjectTokenLayer
|
|
57
|
+
* @typedef {import("./types.js").ProjectSpanLayer} ProjectSpanLayer
|
|
58
|
+
* @typedef {import("./types.js").ProjectRelationLayer} ProjectRelationLayer
|
|
59
|
+
* @typedef {import("./types.js").ProjectVocab} ProjectVocab
|
|
60
|
+
* @typedef {import("./types.js").PlaidDocument} PlaidDocument
|
|
61
|
+
* @typedef {import("./types.js").DocumentTextLayer} DocumentTextLayer
|
|
62
|
+
* @typedef {import("./types.js").DocumentTokenLayer} DocumentTokenLayer
|
|
63
|
+
* @typedef {import("./types.js").DocumentSpanLayer} DocumentSpanLayer
|
|
64
|
+
* @typedef {import("./types.js").PlaidText} PlaidText
|
|
65
|
+
* @typedef {import("./types.js").TextReshape} TextReshape
|
|
66
|
+
* @typedef {import("./types.js").Token} Token
|
|
67
|
+
* @typedef {import("./types.js").Span} Span
|
|
68
|
+
* @typedef {import("./types.js").Relation} Relation
|
|
69
|
+
* @typedef {import("./types.js").VocabItem} VocabItem
|
|
70
|
+
* @typedef {import("./types.js").VocabLink} VocabLink
|
|
71
|
+
* @typedef {import("./types.js").VocabLayer} VocabLayer
|
|
72
|
+
* @typedef {import("./types.js").TextLayer} TextLayer
|
|
73
|
+
* @typedef {import("./types.js").TokenLayer} TokenLayer
|
|
74
|
+
* @typedef {import("./types.js").SpanLayer} SpanLayer
|
|
75
|
+
* @typedef {import("./types.js").RelationLayer} RelationLayer
|
|
76
|
+
* @typedef {import("./types.js").MetadataRecords} MetadataRecords
|
|
77
|
+
* @typedef {import("./types.js").LayerRecords} LayerRecords
|
|
78
|
+
* @typedef {import("./types.js").User} User
|
|
79
|
+
* @typedef {import("./types.js").ProjectListEntry} ProjectListEntry
|
|
80
|
+
* @typedef {import("./types.js").DocumentListEntry} DocumentListEntry
|
|
81
|
+
* @typedef {import("./types.js").ApiToken} ApiToken
|
|
82
|
+
* @typedef {import("./types.js").AuditUser} AuditUser
|
|
83
|
+
* @typedef {import("./types.js").AuditTarget} AuditTarget
|
|
84
|
+
* @typedef {import("./types.js").AuditOp} AuditOp
|
|
85
|
+
* @typedef {import("./types.js").AuditEntry} AuditEntry
|
|
86
|
+
* @typedef {import("./types.js").LockGrant} LockGrant
|
|
87
|
+
* @typedef {import("./types.js").DocumentRestoreSummary} DocumentRestoreSummary
|
|
88
|
+
* @typedef {import("./types.js").RestoreCounts} RestoreCounts
|
|
89
|
+
* @typedef {import("./types.js").OperationGroup} OperationGroup
|
|
90
|
+
* @typedef {import("./types.js").QueryResult} QueryResult
|
|
91
|
+
*/
|
|
2088
92
|
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
93
|
+
/**
|
|
94
|
+
* @template [T=any]
|
|
95
|
+
* @typedef {import("./types.js").Page<T>} Page
|
|
96
|
+
*/
|
|
2093
97
|
|
|
2094
|
-
|
|
2095
|
-
const token = data.token || '';
|
|
2096
|
-
return new PlaidClient(baseUrl, token, options);
|
|
2097
|
-
} catch (error) {
|
|
2098
|
-
if (error.status) throw error;
|
|
2099
|
-
throw makeNetworkError(error, url, 'POST');
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
98
|
+
import { PlaidClient } from "./client.js";
|
|
2103
99
|
|
|
2104
100
|
export default PlaidClient;
|
|
2105
101
|
export { PlaidClient };
|
|
102
|
+
export { BACKUP_TIMEOUT_MS } from "./apiServer.js";
|
|
103
|
+
export { valueSetAllows, violationCountOf, violationsOf } from "./constraints.js";
|
|
2106
104
|
|
|
2107
105
|
// Unicode code-point helpers for text offsets (token begin/end are code-point
|
|
2108
106
|
// indices). See ./codepoint.js.
|
|
2109
|
-
export {
|
|
2110
|
-
|
|
107
|
+
export {
|
|
108
|
+
cpLength,
|
|
109
|
+
cpSlice,
|
|
110
|
+
cpSlicer,
|
|
111
|
+
utf16ToCp,
|
|
112
|
+
cpIndexOf,
|
|
113
|
+
composeText,
|
|
114
|
+
} from "./codepoint.js";
|
|
115
|
+
export { composeTextEdits, gapsToOps, applyTextOps } from "./textEdits.js";
|
|
116
|
+
export {
|
|
117
|
+
PLAID_NAMESPACE,
|
|
118
|
+
ROLE_KEY,
|
|
119
|
+
PRESERVE_ON_SPLIT_KEY,
|
|
120
|
+
SPLIT_ON_SPACE_KEY,
|
|
121
|
+
IGNORED_TOKENS_KEY,
|
|
122
|
+
TARTAN_KEY,
|
|
123
|
+
RESEARCH_KEY,
|
|
124
|
+
readResearchTelemetry,
|
|
125
|
+
ROLES,
|
|
126
|
+
readRole,
|
|
127
|
+
readIgnoredTokens,
|
|
128
|
+
findByRole,
|
|
129
|
+
} from "./roles.js";
|
|
2111
130
|
// Service self-description helpers: filter discovered services by task, read a
|
|
2112
131
|
// service's parameter schema/summary, and build/coerce form values. See
|
|
2113
132
|
// ./serviceSchema.js.
|
|
@@ -2119,20 +138,79 @@ export {
|
|
|
2119
138
|
getServiceSummary,
|
|
2120
139
|
buildDefaultValues,
|
|
2121
140
|
coerceParamValues,
|
|
2122
|
-
} from
|
|
2123
|
-
//
|
|
2124
|
-
//
|
|
2125
|
-
//
|
|
2126
|
-
// "
|
|
141
|
+
} from "./serviceSchema.js";
|
|
142
|
+
// Cooperative cancellation for a service AUTHORED in JS: `progress()` is a
|
|
143
|
+
// checkpoint that throws ServiceCancelled once the requester stops the
|
|
144
|
+
// request, and `critical()` holds that off around writes. See ./services.js
|
|
145
|
+
// and the manual, "Stopping a request".
|
|
146
|
+
export { ServiceCancelled, createCancelScope } from "./services.js";
|
|
147
|
+
// The two messages a write that got no answer is reported with: one the
|
|
148
|
+
// server may have saved, one never sent. See ./services.js.
|
|
149
|
+
export { UNKNOWN_OUTCOME, UNREACHABLE } from "./services.js";
|
|
150
|
+
// Who asked for a service run, named in its History label and in what it
|
|
151
|
+
// stores. See ./requester.js.
|
|
152
|
+
export { REQUESTED_BY, makeRequester, requesterOf } from "./requester.js";
|
|
153
|
+
// Document locks: `client.documents.locked()` holds one for a block of work and
|
|
154
|
+
// renews it while the block runs. DocumentLockLost is what a write throws once
|
|
155
|
+
// a renewal has failed. See ./documentLock.js.
|
|
156
|
+
export { DocumentLock, DocumentLockLost } from "./documentLock.js";
|
|
157
|
+
// Provenance: the cross-app convention for who made an annotation (flat
|
|
158
|
+
// prov/provSource/provConfirmed metadata; absence = a verifier; 'inferred' =
|
|
159
|
+
// a machine; 'contributed' = a person whose work is reviewed), plus the
|
|
160
|
+
// machine-writer contract. See ./provenance.js and the manual, "Provenance".
|
|
2127
161
|
export {
|
|
2128
162
|
PROV,
|
|
2129
163
|
PROV_STATES,
|
|
2130
|
-
|
|
164
|
+
PROVENANCE_KEYS,
|
|
2131
165
|
stampInferred,
|
|
2132
166
|
confirmedInferred,
|
|
167
|
+
stampContributed,
|
|
2133
168
|
provState,
|
|
169
|
+
provOrigin,
|
|
2134
170
|
isMachine,
|
|
2135
171
|
isProtected,
|
|
2136
|
-
|
|
172
|
+
needsReview,
|
|
173
|
+
contributeOnEdit,
|
|
2137
174
|
serviceSource,
|
|
2138
|
-
|
|
175
|
+
REVIEW_KEY,
|
|
176
|
+
readReview,
|
|
177
|
+
reviewsAnyone,
|
|
178
|
+
projectRole,
|
|
179
|
+
isReviewed,
|
|
180
|
+
withReviewedUser,
|
|
181
|
+
CONFIDENCE_KEY,
|
|
182
|
+
DEFAULT_CONFIDENCE_THRESHOLD,
|
|
183
|
+
readConfidenceThreshold,
|
|
184
|
+
machineProb,
|
|
185
|
+
isDoubtful,
|
|
186
|
+
VALUE_PROBS_KEY,
|
|
187
|
+
FORM_PROBS_KEY,
|
|
188
|
+
readValueProbs,
|
|
189
|
+
writerPolicy,
|
|
190
|
+
} from "./provenance.js";
|
|
191
|
+
// Lexicon entry forms are stored without affix or clitic markers, the marker
|
|
192
|
+
// drawn from the entry's morph type. Every entry-form writer stores what
|
|
193
|
+
// entryForm returns. See ./entryForms.js.
|
|
194
|
+
export {
|
|
195
|
+
MORPH_TYPES,
|
|
196
|
+
entryForm,
|
|
197
|
+
readAffixMarkers,
|
|
198
|
+
decorateWithAffixMarkers,
|
|
199
|
+
} from "./entryForms.js";
|
|
200
|
+
// Metadata ops: the body of a metadata PATCH and of a bulk update entry's
|
|
201
|
+
// `metadata`. See ./metadataOps.js.
|
|
202
|
+
export {
|
|
203
|
+
metadataOps,
|
|
204
|
+
applyMetadataOps,
|
|
205
|
+
mergeMetadata,
|
|
206
|
+
isReservedMetadataKey,
|
|
207
|
+
} from "./metadataOps.js";
|
|
208
|
+
// The ids a create or bulk create answered with, off the call or its batch
|
|
209
|
+
// result. See ./created.js.
|
|
210
|
+
export { createdId, createdIds } from "./created.js";
|
|
211
|
+
// UUIDv7 ids for what a client creates. See ./ids.js and the manual,
|
|
212
|
+
// "Retrying a write". uuidv4 works where crypto.randomUUID does not (a page
|
|
213
|
+
// served over plain HTTP from any host but localhost).
|
|
214
|
+
export { uuidv4, uuidv7, normalizeUserId } from "./ids.js";
|
|
215
|
+
export { wasReplayed } from "./replayed.js";
|
|
216
|
+
export { MAX_BATCH_OPS } from "./batch.js";
|