@larc-iu/plaid-client 0.2.0-alpha.26 → 0.2.0-alpha.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.js CHANGED
@@ -161,6 +161,22 @@ import {
161
161
  * @typedef {{document: string, constraint: string, deleted: number, joined: number}} ConstraintRepair
162
162
  */
163
163
 
164
+ /**
165
+ * A value-set rule as `refusedValues` takes it: the list, the characters a
166
+ * value is split on, and whether every part (`"all"`, the default) or only
167
+ * the first must be listed.
168
+ * @typedef {{values: string[], delimiters?: string, parts?: "all" | "first"}} ValueSetRule
169
+ */
170
+
171
+ /**
172
+ * What a value-set rule would refuse in a layer's stored values: how many,
173
+ * how many distinct, and the most frequent with the parts not listed.
174
+ * @typedef {object} RefusedValues
175
+ * @property {number} count
176
+ * @property {number} valueCount
177
+ * @property {Array<{value: any, parts: string[], count: number}>} values
178
+ */
179
+
164
180
  /**
165
181
  * Entities that can carry a comment.
166
182
  * @typedef {"document" | "text" | "token" | "span" | "relation" | "vocab-item"} CommentableType
@@ -256,6 +272,7 @@ import {
256
272
  * @property {number} [userDataValueBytes]
257
273
  * @property {number} [guidelineTitleLength]
258
274
  * @property {number} [guidelineBodyLength]
275
+ * @property {number} [commentBodyLength]
259
276
  * @property {number} [passwordMinLength]
260
277
  * @property {number} [lockExpirationMs]
261
278
  */
@@ -322,11 +339,362 @@ import {
322
339
  * and one the server ended is named "StreamClosed".
323
340
  * @typedef {object} SSEConnection
324
341
  * @property {() => void} close
325
- * @property {() => any} getStats
342
+ * @property {() => SSEStats} getStats
326
343
  * @property {number} readyState
327
344
  * @property {(Error & {status?: number}) | null} error
328
345
  */
329
346
 
347
+ /**
348
+ * How an SSE connection has fared so far. `events` counts what arrived by
349
+ * event type ("audit-log", "message", "heartbeat", "connected", "other").
350
+ * @typedef {object} SSEStats
351
+ * @property {number} durationSeconds
352
+ * @property {boolean} isConnected
353
+ * @property {boolean} isClosed
354
+ * @property {string | null} clientId
355
+ * @property {Record<string, number>} events
356
+ * @property {number} readyState
357
+ */
358
+
359
+ /**
360
+ * A value a span or relation holds: a JSON scalar.
361
+ * @typedef {string | number | boolean | null} AtomicValue
362
+ */
363
+
364
+ /**
365
+ * What a create answers: the new record's id.
366
+ * @typedef {{id: string}} Created
367
+ */
368
+
369
+ /**
370
+ * A project. `textLayers` nests its layers in their display order, each
371
+ * with the layers built on it, and `vocabs` lists the vocabularies linked to
372
+ * it.
373
+ * @typedef {object} Project
374
+ * @property {string} id
375
+ * @property {string} name
376
+ * @property {Record<string, any>} config
377
+ * @property {string[]} readers
378
+ * @property {string[]} writers
379
+ * @property {string[]} maintainers
380
+ * @property {ProjectTextLayer[]} textLayers
381
+ * @property {ProjectVocab[]} vocabs
382
+ */
383
+
384
+ /**
385
+ * @typedef {{id: string, name: string, config: Record<string, any>, tokenLayers: ProjectTokenLayer[]}} ProjectTextLayer
386
+ * @typedef {{id: string, name: string, config: Record<string, any>,
387
+ * constraints: Record<string, LayerConstraint[]>, spanLayers: ProjectSpanLayer[]}} ProjectTokenLayer
388
+ * @typedef {{id: string, name: string, config: Record<string, any>,
389
+ * constraints: Record<string, LayerConstraint[]>, relationLayers: ProjectRelationLayer[]}} ProjectSpanLayer
390
+ * @typedef {{id: string, name: string, config: Record<string, any>,
391
+ * constraints: Record<string, LayerConstraint[]>}} ProjectRelationLayer
392
+ * @typedef {{id: string, name: string, maintainers: string[], config: Record<string, any>}} ProjectVocab
393
+ */
394
+
395
+ /**
396
+ * A document. `version` rises by one on every write to it or its layers.
397
+ * A read with `includeBody` also carries `textLayers`, the document's
398
+ * annotation nested under each layer.
399
+ * @typedef {object} PlaidDocument
400
+ * @property {string} id
401
+ * @property {string} name
402
+ * @property {string} project
403
+ * @property {number} version
404
+ * @property {string} timeCreated
405
+ * @property {string} timeModified
406
+ * @property {Record<string, any>} [metadata]
407
+ * @property {DocumentTextLayer[]} [textLayers]
408
+ */
409
+
410
+ /**
411
+ * A text layer as a document read nests it: `text` is null where the
412
+ * document has no text in this layer.
413
+ * @typedef {object} DocumentTextLayer
414
+ * @property {string} id
415
+ * @property {string} name
416
+ * @property {Record<string, any>} config
417
+ * @property {{id: string, document: string, body: string, digest: string,
418
+ * metadata?: Record<string, any>} | null} text
419
+ * @property {DocumentTokenLayer[]} tokenLayers
420
+ */
421
+
422
+ /**
423
+ * @typedef {object} DocumentTokenLayer
424
+ * @property {string} id
425
+ * @property {string} name
426
+ * @property {Record<string, any>} config
427
+ * @property {Record<string, LayerConstraint[]>} constraints
428
+ * @property {string} overlapMode
429
+ * @property {string | null} parentTokenLayer
430
+ * @property {Array<{id: string, begin: number, end: number, precedence: number | null,
431
+ * metadata?: Record<string, any>}>} tokens
432
+ * @property {DocumentSpanLayer[]} spanLayers
433
+ * @property {Array<ProjectVocab & {vocabLinks: Array<{id: string, vocabItem: VocabItem,
434
+ * tokens: string[], metadata?: Record<string, any>}>}>} vocabs
435
+ */
436
+
437
+ /**
438
+ * @typedef {object} DocumentSpanLayer
439
+ * @property {string} id
440
+ * @property {string} name
441
+ * @property {Record<string, any>} config
442
+ * @property {Record<string, LayerConstraint[]>} constraints
443
+ * @property {Array<{id: string, value: AtomicValue, tokens: string[],
444
+ * metadata?: Record<string, any>}>} spans
445
+ * @property {Array<ProjectRelationLayer & {relations: Array<{id: string, source: string,
446
+ * target: string, value: AtomicValue, metadata?: Record<string, any>}>}>} relationLayers
447
+ */
448
+
449
+ /**
450
+ * A text: one document's body in one text layer. `digest` names the body,
451
+ * as an edit's `base`.
452
+ * @typedef {object} PlaidText
453
+ * @property {string} id
454
+ * @property {string} body
455
+ * @property {string} document
456
+ * @property {string} layer
457
+ * @property {string} digest
458
+ * @property {Record<string, any>} [metadata]
459
+ */
460
+
461
+ /**
462
+ * What a text write changed besides the body: the tokens whose extent
463
+ * moved (with `layer` and `text` on one the write made), the spans and
464
+ * vocabulary links whose tokens or value changed, and the ids of what it
465
+ * deleted.
466
+ * @typedef {object} TextReshape
467
+ * @property {Array<{id: string, begin: number, end: number, layer?: string, text?: string}>} tokens
468
+ * @property {Array<{id: string, tokens?: string[], value?: AtomicValue}>} spans
469
+ * @property {Array<{id: string, tokens: string[]}>} vocabLinks
470
+ * @property {{tokens: string[], spans: string[], relations: string[], vocabLinks: string[]}} deleted
471
+ */
472
+
473
+ /**
474
+ * A token. `value` is the text it covers.
475
+ * @typedef {object} Token
476
+ * @property {string} id
477
+ * @property {string} text
478
+ * @property {number} begin
479
+ * @property {number} end
480
+ * @property {string} layer
481
+ * @property {string} document
482
+ * @property {number | null} precedence
483
+ * @property {string} value
484
+ * @property {Record<string, any>} [metadata]
485
+ */
486
+
487
+ /**
488
+ * @typedef {object} Span
489
+ * @property {string} id
490
+ * @property {string} layer
491
+ * @property {string} document
492
+ * @property {AtomicValue} value
493
+ * @property {string[]} tokens
494
+ * @property {Record<string, any>} [metadata]
495
+ */
496
+
497
+ /**
498
+ * A relation, from the span `source` to the span `target`.
499
+ * @typedef {object} Relation
500
+ * @property {string} id
501
+ * @property {string} layer
502
+ * @property {string} document
503
+ * @property {string} source
504
+ * @property {string} target
505
+ * @property {AtomicValue} value
506
+ * @property {Record<string, any>} [metadata]
507
+ */
508
+
509
+ /**
510
+ * @typedef {object} VocabItem
511
+ * @property {string} id
512
+ * @property {string} layer
513
+ * @property {string} form
514
+ * @property {Record<string, any>} [metadata]
515
+ */
516
+
517
+ /**
518
+ * A link from a vocabulary entry to the tokens of one occurrence.
519
+ * @typedef {object} VocabLink
520
+ * @property {string} id
521
+ * @property {string} vocabItem
522
+ * @property {string} document
523
+ * @property {string[]} tokens
524
+ * @property {Record<string, any>} [metadata]
525
+ */
526
+
527
+ /**
528
+ * A vocabulary. `items` is there on a read made with `includeItems`.
529
+ * @typedef {object} VocabLayer
530
+ * @property {string} id
531
+ * @property {string} name
532
+ * @property {string} timeCreated
533
+ * @property {string} timeModified
534
+ * @property {Record<string, any>} config
535
+ * @property {string[]} maintainers
536
+ * @property {VocabItem[]} [items]
537
+ */
538
+
539
+ /**
540
+ * @typedef {{id: string, name: string, project: string, config: Record<string, any>}} TextLayer
541
+ * @typedef {{id: string, name: string, textLayer: string, project: string, overlapMode: string,
542
+ * parentTokenLayer: string | null, config: Record<string, any>,
543
+ * constraints: Record<string, LayerConstraint[]>}} TokenLayer
544
+ * @typedef {{id: string, name: string, tokenLayer: string, project: string, config: Record<string, any>,
545
+ * constraints: Record<string, LayerConstraint[]>}} SpanLayer
546
+ * @typedef {{id: string, name: string, spanLayer: string, project: string, config: Record<string, any>,
547
+ * constraints: Record<string, LayerConstraint[]>}} RelationLayer
548
+ */
549
+
550
+ /**
551
+ * The records with metadata, by the path segment of their routes.
552
+ * @typedef {{documents: PlaidDocument, texts: PlaidText, tokens: Token, spans: Span,
553
+ * relations: Relation, "vocab-items": VocabItem, "vocab-links": VocabLink}} MetadataRecords
554
+ */
555
+
556
+ /**
557
+ * The four layer kinds, by the path segment of their routes.
558
+ * @typedef {{"text-layers": TextLayer, "token-layers": TokenLayer, "span-layers": SpanLayer,
559
+ * "relation-layers": RelationLayer}} LayerRecords
560
+ */
561
+
562
+ /**
563
+ * @typedef {object} User
564
+ * @property {string} id
565
+ * @property {string} displayName
566
+ * @property {boolean} isAdmin
567
+ * @property {string | null} deactivatedAt
568
+ * @property {string | null} avatarHash
569
+ */
570
+
571
+ /**
572
+ * A project as the project list gives it: the project, with how many
573
+ * documents it has and when the newest of them last changed.
574
+ * @typedef {Project & {documentCount: number, lastModified: string | null}} ProjectListEntry
575
+ */
576
+
577
+ /**
578
+ * A document as a project's document list gives it.
579
+ * @typedef {object} DocumentListEntry
580
+ * @property {string} id
581
+ * @property {string} name
582
+ * @property {number} version
583
+ * @property {string} timeCreated
584
+ * @property {string} timeModified
585
+ */
586
+
587
+ /**
588
+ * A named API token, without the signed token itself, which only `create`
589
+ * returns.
590
+ * @typedef {object} ApiToken
591
+ * @property {string} id
592
+ * @property {string} userId
593
+ * @property {string} name
594
+ * @property {string} createdAt
595
+ * @property {string | null} lastUsedAt
596
+ * @property {string | null} revokedAt
597
+ */
598
+
599
+ /**
600
+ * Who made an operation, as the audit log names them.
601
+ * @typedef {{id: string, displayName: string, avatarHash: string | null}} AuditUser
602
+ */
603
+
604
+ /**
605
+ * A project or a document an audit entry names, while it exists.
606
+ * @typedef {{id: string, name: string}} AuditTarget
607
+ */
608
+
609
+ /**
610
+ * One operation of an audit entry.
611
+ * @typedef {object} AuditOp
612
+ * @property {string} id
613
+ * @property {string} type The kind of write, such as "document/update".
614
+ * @property {string} description
615
+ * @property {string} time
616
+ * @property {string} endTime The time to read at to see the operation done.
617
+ * @property {AuditUser | null} user
618
+ * @property {string} [credential]
619
+ * @property {AuditTarget} [project]
620
+ * @property {AuditTarget} [document]
621
+ * @property {string} [batchId]
622
+ */
623
+
624
+ /**
625
+ * One entry of an audit log: a logical operation, an atomic batch or a lone
626
+ * write, with its operations oldest first (`ops`, cut to `opsLimit` when one
627
+ * was asked for, while `opCount` counts them all).
628
+ * @typedef {object} AuditEntry
629
+ * @property {string} id
630
+ * @property {string} time
631
+ * @property {string} endTime
632
+ * @property {AuditUser | null} user
633
+ * @property {AuditTarget[]} projects
634
+ * @property {AuditTarget[]} documents
635
+ * @property {AuditOp[]} ops
636
+ * @property {number} opCount
637
+ * @property {string} [groupId]
638
+ * @property {string} [message]
639
+ * @property {OperationKind} [kind]
640
+ * @property {string} [ref]
641
+ * @property {string} [batchId]
642
+ * @property {{id: string, name: string}} [apiToken]
643
+ * @property {string} [credential]
644
+ */
645
+
646
+ /**
647
+ * A document lock as its holder sees it. `expiresAt` is in epoch
648
+ * milliseconds.
649
+ * @typedef {{lockId: string, userId: string, expiresAt: number}} LockGrant
650
+ */
651
+
652
+ /**
653
+ * What `documents.restore` did, or with `dryRun` would do: per kind of
654
+ * record, how many come back, are set back and are removed (per layer for
655
+ * the layered kinds), and what could not come back, with why.
656
+ * @typedef {object} DocumentRestoreSummary
657
+ * @property {boolean} name
658
+ * @property {boolean} documentMetadata
659
+ * @property {RestoreCounts} texts
660
+ * @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} tokens
661
+ * @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} spans
662
+ * @property {RestoreCounts & {byLayer: Array<RestoreCounts & {layerId: string}>}} relations
663
+ * @property {RestoreCounts} vocabLinks
664
+ * @property {Array<{kind: string, count: number, reason: string}>} skipped
665
+ * @property {{name: string, taken: string}} [nameKept] When the name the
666
+ * document had then belongs to another document now: the name it keeps,
667
+ * and that other document's name.
668
+ * @property {number} total 0 when nothing changes.
669
+ */
670
+
671
+ /** @typedef {{inserted: number, updated: number, deleted: number}} RestoreCounts */
672
+
673
+ /**
674
+ * A logical-operation group: its label, who made it and when, and the kind
675
+ * and reference of the operation when it has them.
676
+ * @typedef {object} OperationGroup
677
+ * @property {string} id
678
+ * @property {string | null} message
679
+ * @property {string} user
680
+ * @property {string} createdAt
681
+ * @property {string} [kind]
682
+ * @property {string} [ref]
683
+ */
684
+
685
+ /**
686
+ * A query's answer. For "ids" and "entities", one row of `results` per
687
+ * match, a cell per `columns` entry: an id, or the entity as its GET
688
+ * answers it. For "count", only the count. `truncated`: the rows or the
689
+ * count stopped at the server's cap.
690
+ * @typedef {object} QueryResult
691
+ * @property {string} return
692
+ * @property {number} count
693
+ * @property {boolean} truncated
694
+ * @property {string[]} [columns]
695
+ * @property {any[][]} [results]
696
+ */
697
+
330
698
  // Helper: normalize a document-read `layers` filter to the wire's
331
699
  // comma-separated form. Accepts an array of layer ids or a ready-made string;
332
700
  // anything empty becomes undefined so no `?layers=` is sent at all.
@@ -429,9 +797,12 @@ const AVATAR_TOKEN_FRESH_MS = 5 * 1000;
429
797
  * the prototype: the token and base URL, strict mode and the document versions
430
798
  * it tracks, the open logical operation.
431
799
  */
432
- function openBatch(client) {
800
+ function openBatch(client, { timeout } = {}) {
433
801
  const batch = Object.create(client);
434
802
  batch.client = client;
803
+ // This batch's own timeout per request, in place of the client's
804
+ // `batchTimeout` (undefined: the client's).
805
+ batch.submitTimeout = timeout;
435
806
  batch.operations = [];
436
807
  // The document each queued op's strict-mode stamp is for (null for none),
437
808
  // and the logical operation it joined (null for none), index for index with
@@ -494,7 +865,15 @@ async function submitBatch(batch) {
494
865
  // needs to know. The same holds for `committedResults`.
495
866
  const results = [];
496
867
  try {
497
- await sendChunks(client, url, ops, stamps, results, groups);
868
+ await sendChunks(
869
+ client,
870
+ url,
871
+ ops,
872
+ stamps,
873
+ results,
874
+ groups,
875
+ batch.submitTimeout,
876
+ );
498
877
  } catch (error) {
499
878
  if (error && typeof error === "object") {
500
879
  error.committed = results.length;
@@ -511,7 +890,15 @@ function claimedVersion(path) {
511
890
  return v == null ? null : Number(v);
512
891
  }
513
892
 
514
- async function sendChunks(client, url, ops, stamps, results, groups = []) {
893
+ async function sendChunks(
894
+ client,
895
+ url,
896
+ ops,
897
+ stamps,
898
+ results,
899
+ groups = [],
900
+ timeout = undefined,
901
+ ) {
515
902
  // Every chunk's refs are checked before the first request goes, so a batch
516
903
  // whose refs cannot resolve writes nothing.
517
904
  const chunks = [];
@@ -549,7 +936,7 @@ async function sendChunks(client, url, ops, stamps, results, groups = []) {
549
936
  // its path now carries it.
550
937
  const j = stamps.slice(i, i + chunk.length).findIndex(Boolean);
551
938
  record(j < 0 ? null : claimedVersion(chunk[j].path));
552
- const answered = await client._postBatch(url, chunk, { key });
939
+ const answered = await client._postBatch(url, chunk, { key, timeout });
553
940
  results.push(...answered);
554
941
  if (wasReplayed(answered)) markReplayed(results);
555
942
  // The server took the chunk, so each operation it joined exists.
@@ -746,6 +1133,7 @@ class PlaidClient {
746
1133
  * @param {string} [auditMessage] - Audit message for this write
747
1134
  * @param {object} [opts]
748
1135
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
1136
+ * @returns {Promise<Created>}
749
1137
  */
750
1138
  create: (vocabItem, tokens, metadata, auditMessage, { id } = {}) =>
751
1139
  this._request("POST", "/api/v1/vocab-links", {
@@ -780,6 +1168,7 @@ class PlaidClient {
780
1168
  /**
781
1169
  * Get a vocab link by ID
782
1170
  * @param {string} id - The resource ID
1171
+ * @returns {Promise<VocabLink>}
783
1172
  */
784
1173
  get: (id) =>
785
1174
  this._request("GET", `/api/v1/vocab-links/${id}`),
@@ -787,6 +1176,7 @@ class PlaidClient {
787
1176
  * Delete a vocab link
788
1177
  * @param {string} id - The resource ID
789
1178
  * @param {string} [auditMessage] - Audit message for this write
1179
+ * @returns {Promise<void>}
790
1180
  */
791
1181
  delete: (id, auditMessage) =>
792
1182
  this._request("DELETE", `/api/v1/vocab-links/${id}`, { auditMessage }),
@@ -801,6 +1191,7 @@ class PlaidClient {
801
1191
  * @param {string} id - The resource ID
802
1192
  * @param {boolean} [includeItems] - Include vocab items
803
1193
  * @param {string} [asOf] - The moment to read at (ISO-8601 instant)
1194
+ * @returns {Promise<VocabLayer>}
804
1195
  */
805
1196
  get: (id, includeItems, asOf) =>
806
1197
  this._request("GET", `/api/v1/vocab-layers/${id}`, {
@@ -813,6 +1204,7 @@ class PlaidClient {
813
1204
  * @param {string} id - The vocabulary ID
814
1205
  * @param {string} itemId - The entry ID
815
1206
  * @param {string} asOf - The moment to read at (ISO-8601 instant)
1207
+ * @returns {Promise<VocabItem>}
816
1208
  */
817
1209
  getItemAt: (id, itemId, asOf) =>
818
1210
  this._request("GET", `/api/v1/vocab-layers/${id}/items/${itemId}`, {
@@ -833,6 +1225,7 @@ class PlaidClient {
833
1225
  * each with only its operations that did
834
1226
  * @param {string[]|string} [kinds] - Only the entries of operations of
835
1227
  * these kinds (e.g. `['review', 'guess-adoption']`), each whole
1228
+ * @returns {Promise<AuditEntry[]>}
836
1229
  */
837
1230
  audit: (id, startTime, endTime, opTypes, itemId, kinds) =>
838
1231
  listAll(this, `/api/v1/vocab-layers/${id}/audit`, {
@@ -863,7 +1256,7 @@ class PlaidClient {
863
1256
  * @param {string} [opts.entryId] - Read only this entry, not a page
864
1257
  * @param {string} [opts.itemId] - Only the changes that wrote this one
865
1258
  * entry, each with only its operations that did
866
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
1259
+ * @returns {Promise<Page<AuditEntry>>}
867
1260
  */
868
1261
  auditPage: (
869
1262
  id,
@@ -920,6 +1313,7 @@ class PlaidClient {
920
1313
  * Delete a vocab layer.
921
1314
  * @param {string} id - The resource ID
922
1315
  * @param {string} [auditMessage] - Audit message for this write
1316
+ * @returns {Promise<void>}
923
1317
  */
924
1318
  delete: (id, auditMessage) =>
925
1319
  this._request("DELETE", `/api/v1/vocab-layers/${id}`, { auditMessage }),
@@ -928,6 +1322,7 @@ class PlaidClient {
928
1322
  * @param {string} id - The resource ID
929
1323
  * @param {string} name - The name
930
1324
  * @param {string} [auditMessage] - Audit message for this write
1325
+ * @returns {Promise<VocabLayer>}
931
1326
  */
932
1327
  update: (id, name, auditMessage) =>
933
1328
  this._request("PATCH", `/api/v1/vocab-layers/${id}`, {
@@ -938,6 +1333,7 @@ class PlaidClient {
938
1333
  /**
939
1334
  * List all vocab layers accessible to user. Transparently follows
940
1335
  * pagination cursors and returns the full flat array.
1336
+ * @returns {Promise<VocabLayer[]>}
941
1337
  */
942
1338
  list: () => listAll(this, "/api/v1/vocab-layers"),
943
1339
  /**
@@ -945,7 +1341,7 @@ class PlaidClient {
945
1341
  * @param {object} [opts]
946
1342
  * @param {number} [opts.limit] - Page size (1..1000; server default 100)
947
1343
  * @param {string} [opts.cursor] - Opaque cursor from a previous page
948
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
1344
+ * @returns {Promise<Page<VocabLayer>>}
949
1345
  */
950
1346
  listPage: ({ limit, cursor } = {}) =>
951
1347
  listPage(this, "/api/v1/vocab-layers", {
@@ -956,7 +1352,7 @@ class PlaidClient {
956
1352
  * Async-iterate vocab layers page by page; yields each page's entries array.
957
1353
  * @param {object} [opts]
958
1354
  * @param {number} [opts.pageSize] - Per-request page size
959
- * @returns {AsyncGenerator<Array>}
1355
+ * @returns {AsyncGenerator<VocabLayer[]>}
960
1356
  */
961
1357
  iterPages: ({ pageSize } = {}) =>
962
1358
  iterPages(this, "/api/v1/vocab-layers", {
@@ -968,6 +1364,7 @@ class PlaidClient {
968
1364
  * @param {string} [auditMessage] - Audit message for this write
969
1365
  * @param {object} [opts]
970
1366
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
1367
+ * @returns {Promise<Created>}
971
1368
  */
972
1369
  create: (name, auditMessage, { id } = {}) =>
973
1370
  this._request("POST", "/api/v1/vocab-layers", {
@@ -979,6 +1376,7 @@ class PlaidClient {
979
1376
  * @param {string} id - The resource ID
980
1377
  * @param {string} userId - The user ID
981
1378
  * @param {string} [auditMessage] - Audit message for this write
1379
+ * @returns {Promise<void>}
982
1380
  */
983
1381
  addMaintainer: (id, userId, auditMessage) =>
984
1382
  this._request(
@@ -991,6 +1389,7 @@ class PlaidClient {
991
1389
  * @param {string} id - The resource ID
992
1390
  * @param {string} userId - The user ID
993
1391
  * @param {string} [auditMessage] - Audit message for this write
1392
+ * @returns {Promise<void>}
994
1393
  */
995
1394
  removeMaintainer: (id, userId, auditMessage) =>
996
1395
  this._request(
@@ -1007,6 +1406,7 @@ class PlaidClient {
1007
1406
  * @param {string} relationId - The relation ID
1008
1407
  * @param {string} spanId - The span ID
1009
1408
  * @param {string} [auditMessage] - Audit message for this write
1409
+ * @returns {Promise<Relation>}
1010
1410
  */
1011
1411
  setTarget: (relationId, spanId, auditMessage) =>
1012
1412
  this._request("PUT", `/api/v1/relations/${relationId}/target`, {
@@ -1016,6 +1416,7 @@ class PlaidClient {
1016
1416
  /**
1017
1417
  * Get a relation by ID.
1018
1418
  * @param {string} relationId - The relation ID
1419
+ * @returns {Promise<Relation>}
1019
1420
  */
1020
1421
  get: (relationId) =>
1021
1422
  this._request("GET", `/api/v1/relations/${relationId}`),
@@ -1023,6 +1424,7 @@ class PlaidClient {
1023
1424
  * Delete a relation.
1024
1425
  * @param {string} relationId - The relation ID
1025
1426
  * @param {string} [auditMessage] - Audit message for this write
1427
+ * @returns {Promise<void>}
1026
1428
  */
1027
1429
  delete: (relationId, auditMessage) =>
1028
1430
  this._request("DELETE", `/api/v1/relations/${relationId}`, {
@@ -1033,6 +1435,7 @@ class PlaidClient {
1033
1435
  * @param {string} relationId - The relation ID
1034
1436
  * @param {any} value - The value
1035
1437
  * @param {string} [auditMessage] - Audit message for this write
1438
+ * @returns {Promise<Relation>}
1036
1439
  */
1037
1440
  update: (relationId, value, auditMessage) =>
1038
1441
  this._request("PATCH", `/api/v1/relations/${relationId}`, {
@@ -1044,6 +1447,7 @@ class PlaidClient {
1044
1447
  * @param {string} relationId - The relation ID
1045
1448
  * @param {string} spanId - The span ID
1046
1449
  * @param {string} [auditMessage] - Audit message for this write
1450
+ * @returns {Promise<Relation>}
1047
1451
  */
1048
1452
  setSource: (relationId, spanId, auditMessage) =>
1049
1453
  this._request("PUT", `/api/v1/relations/${relationId}/source`, {
@@ -1054,19 +1458,20 @@ class PlaidClient {
1054
1458
  * Create a new relation. A relation is a directed edge between two spans
1055
1459
  * with a value, useful for expressing phenomena such as syntactic or
1056
1460
  * semantic relations.
1057
- * @param {string} layerId - The relation layer ID
1058
- * @param {string} sourceId - The source span ID
1059
- * @param {string} targetId - The target span ID
1461
+ * @param {string} relationLayerId - The relation layer ID
1462
+ * @param {string} source - The source span ID
1463
+ * @param {string} target - The target span ID
1060
1464
  * @param {any} value - The value
1061
1465
  * @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
1062
1466
  * @param {string} [auditMessage] - Audit message for this write
1063
1467
  * @param {object} [opts]
1064
1468
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
1469
+ * @returns {Promise<Created>}
1065
1470
  */
1066
1471
  create: (
1067
- layerId,
1068
- sourceId,
1069
- targetId,
1472
+ relationLayerId,
1473
+ source,
1474
+ target,
1070
1475
  value,
1071
1476
  metadata,
1072
1477
  auditMessage,
@@ -1076,9 +1481,9 @@ class PlaidClient {
1076
1481
  auditMessage,
1077
1482
  body: bodyOf({
1078
1483
  id,
1079
- "layer-id": layerId,
1080
- "source-id": sourceId,
1081
- "target-id": targetId,
1484
+ "relation-layer-id": relationLayerId,
1485
+ source,
1486
+ target,
1082
1487
  value,
1083
1488
  metadata,
1084
1489
  }),
@@ -1118,6 +1523,7 @@ class PlaidClient {
1118
1523
  this.spanLayers = {
1119
1524
  ...configMethods(this, "span-layers"),
1120
1525
  ...constraintMethods(this, "span-layers"),
1526
+ ...valueSetMethods(this, "span-layers"),
1121
1527
  ...layerMethods(this, "span-layers"),
1122
1528
  /**
1123
1529
  * Create a new span layer.
@@ -1126,6 +1532,7 @@ class PlaidClient {
1126
1532
  * @param {string} [auditMessage] - Audit message for this write
1127
1533
  * @param {object} [opts]
1128
1534
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
1535
+ * @returns {Promise<Created>}
1129
1536
  */
1130
1537
  create: (tokenLayerId, name, auditMessage, { id } = {}) =>
1131
1538
  this._request("POST", "/api/v1/span-layers", {
@@ -1140,6 +1547,7 @@ class PlaidClient {
1140
1547
  * @param {string} spanId - The span ID
1141
1548
  * @param {Array} tokens - The tokens
1142
1549
  * @param {string} [auditMessage] - Audit message for this write
1550
+ * @returns {Promise<Span>}
1143
1551
  */
1144
1552
  setTokens: (spanId, tokens, auditMessage) =>
1145
1553
  this._request("PUT", `/api/v1/spans/${spanId}/tokens`, {
@@ -1156,6 +1564,7 @@ class PlaidClient {
1156
1564
  * @param {string} [auditMessage] - Audit message for this write
1157
1565
  * @param {object} [opts]
1158
1566
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
1567
+ * @returns {Promise<Created>}
1159
1568
  */
1160
1569
  create: (spanLayerId, tokens, value, metadata, auditMessage, { id } = {}) =>
1161
1570
  this._request("POST", "/api/v1/spans", {
@@ -1171,6 +1580,7 @@ class PlaidClient {
1171
1580
  /**
1172
1581
  * Get a span by ID.
1173
1582
  * @param {string} spanId - The span ID
1583
+ * @returns {Promise<Span>}
1174
1584
  */
1175
1585
  get: (spanId) =>
1176
1586
  this._request("GET", `/api/v1/spans/${spanId}`),
@@ -1178,6 +1588,7 @@ class PlaidClient {
1178
1588
  * Delete a span.
1179
1589
  * @param {string} spanId - The span ID
1180
1590
  * @param {string} [auditMessage] - Audit message for this write
1591
+ * @returns {Promise<void>}
1181
1592
  */
1182
1593
  delete: (spanId, auditMessage) =>
1183
1594
  this._request("DELETE", `/api/v1/spans/${spanId}`, { auditMessage }),
@@ -1186,6 +1597,7 @@ class PlaidClient {
1186
1597
  * @param {string} spanId - The span ID
1187
1598
  * @param {any} value - The value
1188
1599
  * @param {string} [auditMessage] - Audit message for this write
1600
+ * @returns {Promise<Span>}
1189
1601
  */
1190
1602
  update: (spanId, value, auditMessage) =>
1191
1603
  this._request("PATCH", `/api/v1/spans/${spanId}`, {
@@ -1316,10 +1728,13 @@ class PlaidClient {
1316
1728
  * @param {string} body - The request body
1317
1729
  * @param {any} [metadata] - Metadata map. Omit to leave unset; pass null to send JSON null.
1318
1730
  * @param {string} [auditMessage]
1319
- * @param {{id?: string, tokenEdges?: number[]}} [options] - `id`: the new text's id, a UUIDv7
1320
- * this client minted. `tokenEdges`: the code-point offsets where the tokens made next on
1321
- * this text begin and end. The body is stored composed, except a character one of them
1322
- * falls inside, which stays as sent (see composeText).
1731
+ * @param {{id?: string, tokenEdges?: number[], tokenizeLayer?: string}} [options] - `id`: the
1732
+ * new text's id, a UUIDv7 this client minted. `tokenEdges`: the code-point offsets where
1733
+ * the tokens made next on this text begin and end. The body is stored composed, except a
1734
+ * character one of them falls inside, which stays as sent (see composeText).
1735
+ * `tokenizeLayer`: a word layer of the text, which gets the body's words, cut by the
1736
+ * server ("Tokenize new text").
1737
+ * @returns {Promise<Created>}
1323
1738
  */
1324
1739
  create: (
1325
1740
  textLayerId,
@@ -1327,7 +1742,7 @@ class PlaidClient {
1327
1742
  body,
1328
1743
  metadata,
1329
1744
  auditMessage,
1330
- { id, tokenEdges } = {},
1745
+ { id, tokenEdges, tokenizeLayer } = {},
1331
1746
  ) =>
1332
1747
  this._request("POST", "/api/v1/texts", {
1333
1748
  auditMessage,
@@ -1337,12 +1752,14 @@ class PlaidClient {
1337
1752
  "document-id": documentId,
1338
1753
  body,
1339
1754
  "token-edges": tokenEdges,
1755
+ "tokenize-layer": tokenizeLayer,
1340
1756
  metadata,
1341
1757
  }),
1342
1758
  }),
1343
1759
  /**
1344
1760
  * Get a text.
1345
1761
  * @param {string} textId - The text ID
1762
+ * @returns {Promise<PlaidText>}
1346
1763
  */
1347
1764
  get: (textId) =>
1348
1765
  this._request("GET", `/api/v1/texts/${textId}`),
@@ -1350,6 +1767,7 @@ class PlaidClient {
1350
1767
  * Delete a text and all dependent data.
1351
1768
  * @param {string} textId - The text ID
1352
1769
  * @param {string} [auditMessage] - Audit message for this write
1770
+ * @returns {Promise<void>}
1353
1771
  */
1354
1772
  delete: (textId, auditMessage) =>
1355
1773
  this._request("DELETE", `/api/v1/texts/${textId}`, { auditMessage }),
@@ -1371,12 +1789,16 @@ class PlaidClient {
1371
1789
  * @param {string} textId - The text ID
1372
1790
  * @param {any} body - The request body
1373
1791
  * @param {string} [auditMessage]
1374
- * @param {{base?: string, versioned?: boolean}} [options]
1792
+ * `tokenizeLayer`: a word layer of the text, whose words the server makes for the text the
1793
+ * update types, after it places the old tokens ("Tokenize new text"). They come in the
1794
+ * answer's `reshape`.
1795
+ * @param {{base?: string, versioned?: boolean, tokenizeLayer?: string}} [options]
1796
+ * @returns {Promise<PlaidText & {reshape: TextReshape}>}
1375
1797
  */
1376
- update: (textId, body, auditMessage, { base, versioned } = {}) =>
1798
+ update: (textId, body, auditMessage, { base, versioned, tokenizeLayer } = {}) =>
1377
1799
  this._request("PATCH", `/api/v1/texts/${textId}`, {
1378
1800
  auditMessage,
1379
- body: bodyOf({ body, base }),
1801
+ body: bodyOf({ body, base, "tokenize-layer": tokenizeLayer }),
1380
1802
  versioned: versioned ?? (base === undefined || base === null),
1381
1803
  }),
1382
1804
  /**
@@ -1390,16 +1812,17 @@ class PlaidClient {
1390
1812
  * `base`, the digest of the body the edits were made on, the edit
1391
1813
  * applies only to that body and is refused with 409 and `text-changed`
1392
1814
  * otherwise, and strict mode does not stamp it unless `versioned` is
1393
- * true. See composeTextEdits and gapsToOps.
1815
+ * true. `tokenizeLayer` is as `update` takes it. See composeTextEdits and gapsToOps.
1394
1816
  * @param {string} textId - The text ID
1395
1817
  * @param {Array<object>} edits - The edit directives
1396
1818
  * @param {string} [auditMessage]
1397
- * @param {{base?: string, versioned?: boolean}} [options]
1819
+ * @param {{base?: string, versioned?: boolean, tokenizeLayer?: string}} [options]
1820
+ * @returns {Promise<PlaidText & {reshape: TextReshape}>}
1398
1821
  */
1399
- edit: (textId, edits, auditMessage, { base, versioned } = {}) =>
1822
+ edit: (textId, edits, auditMessage, { base, versioned, tokenizeLayer } = {}) =>
1400
1823
  this._request("PATCH", `/api/v1/texts/${textId}`, {
1401
1824
  auditMessage,
1402
- body: bodyOf({ edits, base }),
1825
+ body: bodyOf({ edits, base, "tokenize-layer": tokenizeLayer }),
1403
1826
  versioned: versioned ?? (base === undefined || base === null),
1404
1827
  }),
1405
1828
  };
@@ -1410,6 +1833,7 @@ class PlaidClient {
1410
1833
  * returns the full flat array. Admin-or-maintainer only.
1411
1834
  * @param {object} [opts]
1412
1835
  * @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
1836
+ * @returns {Promise<User[]>}
1413
1837
  */
1414
1838
  list: ({ q } = {}) =>
1415
1839
  listAll(this, "/api/v1/users", { query: { q } }),
@@ -1419,7 +1843,7 @@ class PlaidClient {
1419
1843
  * @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
1420
1844
  * @param {number} [opts.limit] - Page size (1..1000; server default 100)
1421
1845
  * @param {string} [opts.cursor] - Opaque cursor from a previous page
1422
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
1846
+ * @returns {Promise<Page<User>>}
1423
1847
  */
1424
1848
  listPage: ({ q, limit, cursor } = {}) =>
1425
1849
  listPage(this, "/api/v1/users", {
@@ -1432,7 +1856,7 @@ class PlaidClient {
1432
1856
  * @param {object} [opts]
1433
1857
  * @param {string} [opts.q] - Filter to users whose display name contains this text, or whose email starts with it (contains it, when the text holds an at sign), case-insensitive
1434
1858
  * @param {number} [opts.pageSize] - Per-request page size
1435
- * @returns {AsyncGenerator<Array>}
1859
+ * @returns {AsyncGenerator<User[]>}
1436
1860
  */
1437
1861
  iterPages: ({ q, pageSize } = {}) =>
1438
1862
  iterPages(this, "/api/v1/users", {
@@ -1449,6 +1873,7 @@ class PlaidClient {
1449
1873
  * @param {string} [displayName] - How the user is shown in the UI.
1450
1874
  * Defaults to the local part of the email.
1451
1875
  * @param {string} [auditMessage] - Audit message for this write
1876
+ * @returns {Promise<Created>}
1452
1877
  */
1453
1878
  create: (email, password, isAdmin, displayName, auditMessage) =>
1454
1879
  this._request("POST", "/api/v1/users", {
@@ -1474,6 +1899,7 @@ class PlaidClient {
1474
1899
  * one of its operations matches, carrying only the ones that did.
1475
1900
  * @param {string[]|string} [kinds] - Only the entries of operations of
1476
1901
  * these kinds (e.g. `['review', 'guess-adoption']`), each whole
1902
+ * @returns {Promise<AuditEntry[]>}
1477
1903
  */
1478
1904
  audit: (userId, startTime, endTime, opTypes, kinds) =>
1479
1905
  listAll(this, `/api/v1/users/${userId}/audit`, {
@@ -1501,7 +1927,7 @@ class PlaidClient {
1501
1927
  * operations in `ops` (1..1000). `opCount` on every entry says how
1502
1928
  * many it has
1503
1929
  * @param {string} [opts.entryId] - Read only this entry, not a page
1504
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
1930
+ * @returns {Promise<Page<AuditEntry>>}
1505
1931
  */
1506
1932
  auditPage: (
1507
1933
  userId,
@@ -1523,6 +1949,7 @@ class PlaidClient {
1523
1949
  /**
1524
1950
  * Get a user by ID
1525
1951
  * @param {string} id - The resource ID
1952
+ * @returns {Promise<User>}
1526
1953
  */
1527
1954
  get: (id) =>
1528
1955
  this._request("GET", `/api/v1/users/${id}`),
@@ -1534,6 +1961,7 @@ class PlaidClient {
1534
1961
  * Reversible via activate(), which restores login only.
1535
1962
  * @param {string} id - The resource ID
1536
1963
  * @param {string} [auditMessage] - Audit message for this write
1964
+ * @returns {Promise<void>}
1537
1965
  */
1538
1966
  delete: (id, auditMessage) =>
1539
1967
  this._request("DELETE", `/api/v1/users/${id}`, { auditMessage }),
@@ -1543,6 +1971,7 @@ class PlaidClient {
1543
1971
  * at deactivation are NOT restored — re-grant them deliberately.
1544
1972
  * @param {string} id - The resource ID
1545
1973
  * @param {string} [auditMessage] - Audit message for this write
1974
+ * @returns {Promise<User>}
1546
1975
  */
1547
1976
  activate: (id, auditMessage) =>
1548
1977
  this._request("POST", `/api/v1/users/${id}/activate`, { auditMessage }),
@@ -1559,6 +1988,7 @@ class PlaidClient {
1559
1988
  * @param {string} [displayName] - New display name
1560
1989
  * @param {boolean} [isAdmin] - New admin status
1561
1990
  * @param {string} [auditMessage] - Audit message for this write
1991
+ * @returns {Promise<User>}
1562
1992
  */
1563
1993
  update: (id, password, displayName, isAdmin, auditMessage) =>
1564
1994
  this._request("PATCH", `/api/v1/users/${id}`, {
@@ -1660,6 +2090,7 @@ class PlaidClient {
1660
2090
  * Fetch a user's profile picture as raw bytes. Most callers want
1661
2091
  * avatarUrl() instead. This one is for non-browser consumers.
1662
2092
  * @param {string} id - The user ID
2093
+ * @returns {Promise<ArrayBuffer>}
1663
2094
  */
1664
2095
  getAvatar: (id) =>
1665
2096
  this._request("GET", `/api/v1/users/${id}/avatar`, {
@@ -1673,6 +2104,7 @@ class PlaidClient {
1673
2104
  * @param {string} id - The user ID
1674
2105
  * @param {File} file - The image to upload
1675
2106
  * @param {string} [auditMessage] - Audit message for this write
2107
+ * @returns {Promise<User>}
1676
2108
  */
1677
2109
  setAvatar: (id, file, auditMessage) => {
1678
2110
  const fd = new FormData();
@@ -1688,6 +2120,7 @@ class PlaidClient {
1688
2120
  * Remove a profile picture. Your own, or anyone's if you are an admin.
1689
2121
  * @param {string} id - The user ID
1690
2122
  * @param {string} [auditMessage] - Audit message for this write
2123
+ * @returns {Promise<void>}
1691
2124
  */
1692
2125
  // No flag: the upload beside this one is multipart and cannot be
1693
2126
  // batched, but a DELETE carries no blob, so the batch transport takes
@@ -1827,6 +2260,7 @@ class PlaidClient {
1827
2260
  * Delete one private data entry; 404 if absent.
1828
2261
  * @param {string} userId
1829
2262
  * @param {string} key
2263
+ * @returns {Promise<void>}
1830
2264
  */
1831
2265
  delete: (userId, key) =>
1832
2266
  this._request(
@@ -1844,6 +2278,7 @@ class PlaidClient {
1844
2278
  * string itself — that is only returned once, by create().
1845
2279
  * Transparently follows pagination cursors and returns the full flat array.
1846
2280
  * @param {string} userId - The user ID who owns the tokens
2281
+ * @returns {Promise<ApiToken[]>}
1847
2282
  */
1848
2283
  list: (userId) => listAll(this, `/api/v1/users/${userId}/tokens`),
1849
2284
  /**
@@ -1852,7 +2287,7 @@ class PlaidClient {
1852
2287
  * @param {object} [opts]
1853
2288
  * @param {number} [opts.limit] - Page size (1..1000; server default 100)
1854
2289
  * @param {string} [opts.cursor] - Opaque cursor from a previous page
1855
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
2290
+ * @returns {Promise<Page<ApiToken>>}
1856
2291
  */
1857
2292
  listPage: (userId, { limit, cursor } = {}) =>
1858
2293
  listPage(this, `/api/v1/users/${userId}/tokens`, { limit, cursor }),
@@ -1862,7 +2297,7 @@ class PlaidClient {
1862
2297
  * @param {string} userId - The user ID who owns the tokens
1863
2298
  * @param {object} [opts]
1864
2299
  * @param {number} [opts.pageSize] - Per-request page size
1865
- * @returns {AsyncGenerator<Array>}
2300
+ * @returns {AsyncGenerator<ApiToken[]>}
1866
2301
  */
1867
2302
  iterPages: (userId, { pageSize } = {}) =>
1868
2303
  iterPages(this, `/api/v1/users/${userId}/tokens`, { pageSize }),
@@ -1892,6 +2327,7 @@ class PlaidClient {
1892
2327
  * @param {string} userId - The user ID who owns the token
1893
2328
  * @param {string} tokenId - The token ID to revoke
1894
2329
  * @param {string} [auditMessage] - Audit message for this write
2330
+ * @returns {Promise<void>}
1895
2331
  */
1896
2332
  revoke: (userId, tokenId, auditMessage) =>
1897
2333
  this._request("DELETE", `/api/v1/users/${userId}/tokens/${tokenId}`, {
@@ -2004,6 +2440,7 @@ class PlaidClient {
2004
2440
  * the creator, an admin, or a maintainer of the invite's project.
2005
2441
  * @param {string} id - The invite ID
2006
2442
  * @param {string} [auditMessage] - Audit message for this write
2443
+ * @returns {Promise<void>}
2007
2444
  */
2008
2445
  revoke: (id, auditMessage) =>
2009
2446
  this._request("DELETE", `/api/v1/invites/${id}`, { auditMessage }),
@@ -2175,6 +2612,7 @@ class PlaidClient {
2175
2612
  * matches, carrying only the ones that did.
2176
2613
  * @param {string[]|string} [opts.kinds] - Only the entries of operations
2177
2614
  * of these kinds (e.g. `['review']`), each whole
2615
+ * @returns {Promise<AuditEntry[]>}
2178
2616
  */
2179
2617
  list: ({ startTime, endTime, opTypes, kinds } = {}) =>
2180
2618
  listAll(this, "/api/v1/audit", {
@@ -2200,7 +2638,7 @@ class PlaidClient {
2200
2638
  * operations in `ops` (1..1000). `opCount` on every entry says how
2201
2639
  * many it has
2202
2640
  * @param {string} [opts.entryId] - Read only this entry, not a page
2203
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
2641
+ * @returns {Promise<Page<AuditEntry>>}
2204
2642
  */
2205
2643
  listPage: ({ startTime, endTime, opTypes, kinds, order, limit, cursor, opsLimit, entryId } = {}) =>
2206
2644
  listPage(this, "/api/v1/audit", {
@@ -2224,7 +2662,7 @@ class PlaidClient {
2224
2662
  * @param {string[]|string} [opts.opTypes] - Only these op types
2225
2663
  * @param {OperationKind[]|string} [opts.kinds] - Only the entries of operations of these kinds, each whole
2226
2664
  * @param {number} [opts.pageSize] - Per-request page size
2227
- * @returns {AsyncGenerator<Array>}
2665
+ * @returns {AsyncGenerator<AuditEntry[]>}
2228
2666
  */
2229
2667
  iterPages: ({ startTime, endTime, opTypes, kinds, pageSize } = {}) =>
2230
2668
  iterPages(this, "/api/v1/audit", {
@@ -2271,6 +2709,24 @@ class PlaidClient {
2271
2709
 
2272
2710
  this.tokenLayers = {
2273
2711
  ...layerMethods(this, "token-layers"),
2712
+ /**
2713
+ * Count, per document, the tokens of each layer whose surface matches a
2714
+ * regex, as a query's `value: {regex}` on a token decides. Each text is
2715
+ * read once, so it stays fast on a long document. Layers you cannot read
2716
+ * are left out.
2717
+ * @param {string[]} layerIds - The token layer ids
2718
+ * @param {{regex: string, flags?: string}} value - The regex spec
2719
+ * @returns {Promise<{results: Array<[string, string, number]>}>} [layerId,
2720
+ * documentId, count] rows, a document with tokens of the layer and no
2721
+ * match counting 0.
2722
+ */
2723
+ countMatching: (layerIds, value) =>
2724
+ this._request("POST", "/api/v1/query/token-counts", {
2725
+ body: { layers: layerIds, value },
2726
+ outOfBand: true,
2727
+ timeout: queryTimeout(this),
2728
+ noBusyRetry: true,
2729
+ }),
2274
2730
  /**
2275
2731
  * Create a new token layer.
2276
2732
  * @param {string} textLayerId - The text layer ID
@@ -2280,6 +2736,7 @@ class PlaidClient {
2280
2736
  * @param {string} [auditMessage] - Audit message for this write
2281
2737
  * @param {object} [opts]
2282
2738
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
2739
+ * @returns {Promise<Created>}
2283
2740
  */
2284
2741
  create: (
2285
2742
  textLayerId,
@@ -2307,6 +2764,7 @@ class PlaidClient {
2307
2764
  /**
2308
2765
  * Check the lock status of a document.
2309
2766
  * @param {string} documentId - The document ID
2767
+ * @returns {Promise<{userId: string, expiresAt: number} | void>}
2310
2768
  */
2311
2769
  checkLock: (documentId) =>
2312
2770
  this._request("GET", `/api/v1/documents/${documentId}/lock`),
@@ -2331,6 +2789,7 @@ class PlaidClient {
2331
2789
  * @param {string} documentId - The document ID
2332
2790
  * @param {string} [auditMessage]
2333
2791
  * @param {string} [newLockId] - A holder id this client minted
2792
+ * @returns {Promise<LockGrant>}
2334
2793
  */
2335
2794
  acquireLock: (documentId, auditMessage, newLockId) =>
2336
2795
  this._request("POST", `/api/v1/documents/${documentId}/lock`, {
@@ -2348,6 +2807,7 @@ class PlaidClient {
2348
2807
  * @param {string} documentId - The document ID
2349
2808
  * @param {string} lockId - The `lockId` acquireLock answered with
2350
2809
  * @param {string} [auditMessage] - Audit message for this write
2810
+ * @returns {Promise<LockGrant>}
2351
2811
  */
2352
2812
  renewLock: (documentId, lockId, auditMessage) =>
2353
2813
  this._request("POST", `/api/v1/documents/${documentId}/lock`, {
@@ -2363,6 +2823,7 @@ class PlaidClient {
2363
2823
  * @param {string} documentId - The document ID
2364
2824
  * @param {string} lockId - The `lockId` acquireLock answered with
2365
2825
  * @param {string} [auditMessage] - Audit message for this write
2826
+ * @returns {Promise<void>}
2366
2827
  */
2367
2828
  releaseLock: (documentId, lockId, auditMessage) =>
2368
2829
  this._request("DELETE", `/api/v1/documents/${documentId}/lock`, {
@@ -2406,6 +2867,12 @@ class PlaidClient {
2406
2867
  * lost lock is recorded on the CLIENT, like strict mode, so it stops
2407
2868
  * every write the client makes (on any batch of it too) and not only the
2408
2869
  * ones this block makes.
2870
+ *
2871
+ * A core restart drops every lock, and until the next renewal finds it
2872
+ * gone (up to half the lock window) this block's writes still pass, since
2873
+ * writes carry no lock id. When a write must not land past that point,
2874
+ * hold the document in strict mode too (`enterStrictMode`), so such a
2875
+ * write is refused with a 409.
2409
2876
  * @template T
2410
2877
  * @param {string} documentId - The document ID
2411
2878
  * @param {(lock: import("./documentLock.js").DocumentLock) => T | Promise<T>} fn - The work to run while holding it
@@ -2458,6 +2925,7 @@ class PlaidClient {
2458
2925
  * @param {File} file - The file to upload
2459
2926
  * @param {string} [auditMessage] - Custom audit-log message for this write
2460
2927
  * @param {{onProgress?: (progress: {loaded: number, total: number | null}) => void}} [options] - `{ onProgress }`: called with `{ loaded, total }` (bytes) as the file goes up. In a browser the upload then travels by XMLHttpRequest, and the request timeout only fires when the upload stalls, not on total time.
2928
+ * @returns {Promise<{message: string, extension: string, contentType: string}>}
2461
2929
  */
2462
2930
  uploadMedia: (documentId, file, auditMessage, { onProgress } = {}) => {
2463
2931
  const fd = new FormData();
@@ -2478,12 +2946,13 @@ class PlaidClient {
2478
2946
  * recording meant, the `?v=` of the document's `mediaUrl`. The delete
2479
2947
  * is refused with a 409 (`media-changed`, and the current `media-url`)
2480
2948
  * when the stored recording is another one.
2949
+ * @returns {Promise<void>}
2481
2950
  */
2482
2951
  // The upload above is multipart and cannot be batched, but a DELETE
2483
2952
  // carries no blob, so the batch transport takes it. It is a write
2484
- // of the document's own data and queues like any other. Note that the
2485
- // file removal happens outside the server's transaction, so a batch that
2486
- // aborts after this op does not bring the file back.
2953
+ // of the document's own data and queues like any other. The server
2954
+ // removes the file only after the write commits, so a batch that aborts
2955
+ // after this op leaves the recording in place.
2487
2956
  deleteMedia: (documentId, auditMessage, { mediaVersion } = {}) =>
2488
2957
  this._request("DELETE", `/api/v1/documents/${documentId}/media`, {
2489
2958
  auditMessage,
@@ -2506,6 +2975,7 @@ class PlaidClient {
2506
2975
  * one of its operations matches, carrying only the ones that did.
2507
2976
  * @param {string[]|string} [kinds] - Only the entries of operations of
2508
2977
  * these kinds (e.g. `['review', 'guess-adoption']`), each whole
2978
+ * @returns {Promise<AuditEntry[]>}
2509
2979
  */
2510
2980
  audit: (documentId, startTime, endTime, opTypes, kinds) =>
2511
2981
  listAll(this, `/api/v1/documents/${documentId}/audit`, {
@@ -2533,7 +3003,7 @@ class PlaidClient {
2533
3003
  * operations in `ops` (1..1000). `opCount` on every entry says how
2534
3004
  * many it has
2535
3005
  * @param {string} [opts.entryId] - Read only this entry, not a page
2536
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
3006
+ * @returns {Promise<Page<AuditEntry>>}
2537
3007
  */
2538
3008
  auditPage: (
2539
3009
  documentId,
@@ -2557,7 +3027,9 @@ class PlaidClient {
2557
3027
  * operation: what was deleted since then comes back under its original
2558
3028
  * id, what was added since is removed, and what changed is set back,
2559
3029
  * across every layer. A layer deleted since then, or a vocabulary entry
2560
- * that no longer exists, is skipped and reported under `skipped`.
3030
+ * that no longer exists, is skipped and reported under `skipped`. A name
3031
+ * another document holds now stays with it, and the document keeps its
3032
+ * current name, reported under `nameKept` (`{name, taken}`).
2561
3033
  * Resolves to a summary of the changes. Maintainers only.
2562
3034
  * @param {string} documentId - The document ID
2563
3035
  * @param {string} asOf - The moment to go back to (ISO-8601 instant),
@@ -2565,6 +3037,7 @@ class PlaidClient {
2565
3037
  * @param {{dryRun?: boolean}} [options] - `{ dryRun }`: with `dryRun` true nothing
2566
3038
  * is written and the summary says what would change
2567
3039
  * @param {string} [auditMessage] - Custom audit message for this operation
3040
+ * @returns {Promise<DocumentRestoreSummary>}
2568
3041
  */
2569
3042
  restore: (documentId, asOf, { dryRun } = {}, auditMessage) =>
2570
3043
  this._request("POST", `/api/v1/documents/${documentId}/restore`, {
@@ -2577,6 +3050,10 @@ class PlaidClient {
2577
3050
  /**
2578
3051
  * Get a document. Set `includeBody` to true to include all data.
2579
3052
  *
3053
+ * A document with a recording has `mediaUrl`, and `mediaDuration`, the
3054
+ * recording's length in seconds, when the server could read it from the
3055
+ * file at upload (WAV, MP4, M4A, MOV, FLAC, MP3).
3056
+ *
2580
3057
  * `layers` narrows a body read to the layers you name (ids of any kind:
2581
3058
  * text, token, span, or relation). A layer comes back when it is named
2582
3059
  * or is an ancestor of a named layer, and carries its own
@@ -2588,6 +3065,7 @@ class PlaidClient {
2588
3065
  * @param {boolean} [includeBody] - Include document body data
2589
3066
  * @param {string} [asOf] - Temporal query timestamp
2590
3067
  * @param {string[]|string} [layers] - Layer ids to restrict a body read to
3068
+ * @returns {Promise<PlaidDocument>}
2591
3069
  */
2592
3070
  get: (documentId, includeBody, asOf, layers) =>
2593
3071
  this._request("GET", `/api/v1/documents/${documentId}`, {
@@ -2601,16 +3079,20 @@ class PlaidClient {
2601
3079
  * Delete a document and all data contained.
2602
3080
  * @param {string} documentId - The document ID
2603
3081
  * @param {string} [auditMessage] - Audit message for this write
3082
+ * @returns {Promise<void>}
2604
3083
  */
2605
3084
  delete: (documentId, auditMessage) =>
2606
3085
  this._request("DELETE", `/api/v1/documents/${documentId}`, {
2607
3086
  auditMessage,
2608
3087
  }),
2609
3088
  /**
2610
- * Update a document's name.
3089
+ * Update a document's name. A name another document of the project holds,
3090
+ * ignoring case,
3091
+ * is refused with 422 and `name-taken`.
2611
3092
  * @param {string} documentId - The document ID
2612
3093
  * @param {string} name - The name
2613
3094
  * @param {string} [auditMessage] - Audit message for this write
3095
+ * @returns {Promise<PlaidDocument>}
2614
3096
  */
2615
3097
  update: (documentId, name, auditMessage) =>
2616
3098
  this._request("PATCH", `/api/v1/documents/${documentId}`, {
@@ -2625,11 +3107,21 @@ class PlaidClient {
2625
3107
  * @param {string} [auditMessage] - Audit message for this write
2626
3108
  * @param {object} [opts]
2627
3109
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3110
+ * @param {boolean} [opts.freeName] - A name another document of the project holds, ignoring case, becomes
3111
+ * the first free "name (2)", "name (3)" and on. Without it such a name is refused with 422
3112
+ * and `name-taken`.
3113
+ * @returns {Promise<{id: string, name: string}>} The new id and the name stored
2628
3114
  */
2629
- create: (projectId, name, metadata, auditMessage, { id } = {}) =>
3115
+ create: (projectId, name, metadata, auditMessage, { id, freeName } = {}) =>
2630
3116
  this._request("POST", "/api/v1/documents", {
2631
3117
  auditMessage,
2632
- body: bodyOf({ id, "project-id": projectId, name, metadata }),
3118
+ body: bodyOf({
3119
+ id,
3120
+ "project-id": projectId,
3121
+ name,
3122
+ metadata,
3123
+ "free-name": freeName || undefined,
3124
+ }),
2633
3125
  }),
2634
3126
  /**
2635
3127
  * Copy a document and everything in it into a new document of the same
@@ -2637,19 +3129,27 @@ class PlaidClient {
2637
3129
  * the vocabulary entries its links name, and holds the source's texts,
2638
3130
  * tokens, spans, relations and vocab links under fresh ids, with their
2639
3131
  * metadata. Comments do not travel. The media file does, unless
2640
- * `includeMedia` is false. Resolves to `{ id }`, plus `mediaError` when
2641
- * the source had media the copy could not take with it.
3132
+ * `includeMedia` is false. Resolves to `{ id, name }`, plus `mediaError` when
3133
+ * the source had media the copy could not take with it. A name another
3134
+ * document of the project holds, ignoring case, is refused with 422 and `name-taken`,
3135
+ * unless `freeName`, which takes the first free "name (2)", "name (3)"
3136
+ * and on.
2642
3137
  * @param {string} documentId - The document to copy
2643
3138
  * @param {string} name - The new document's name
2644
- * @param {{includeMedia?: boolean, id?: string}} [options] - `{ includeMedia }`: false leaves the media
3139
+ * @param {{includeMedia?: boolean, id?: string, freeName?: boolean}} [options] - `{ includeMedia }`: false leaves the media
2645
3140
  * file behind
2646
3141
  * @param {string} [auditMessage] - Custom audit message for this operation
2647
- * @returns {Promise<{id: string, mediaError?: string}>}
3142
+ * @returns {Promise<{id: string, name: string, mediaError?: string}>}
2648
3143
  */
2649
- copy: (documentId, name, { includeMedia, id } = {}, auditMessage) =>
3144
+ copy: (documentId, name, { includeMedia, id, freeName } = {}, auditMessage) =>
2650
3145
  this._request("POST", `/api/v1/documents/${documentId}/copy`, {
2651
3146
  auditMessage,
2652
- body: bodyOf({ id, name, "include-media": includeMedia }),
3147
+ body: bodyOf({
3148
+ id,
3149
+ name,
3150
+ "include-media": includeMedia,
3151
+ "free-name": freeName || undefined,
3152
+ }),
2653
3153
  }),
2654
3154
  };
2655
3155
 
@@ -2659,6 +3159,7 @@ class PlaidClient {
2659
3159
  * @param {string} id - The resource ID
2660
3160
  * @param {string} userId - The user ID
2661
3161
  * @param {string} [auditMessage] - Audit message for this write
3162
+ * @returns {Promise<void>}
2662
3163
  */
2663
3164
  addWriter: (id, userId, auditMessage) =>
2664
3165
  this._request("POST", `/api/v1/projects/${id}/writers/${userId}`, {
@@ -2669,6 +3170,7 @@ class PlaidClient {
2669
3170
  * @param {string} id - The resource ID
2670
3171
  * @param {string} userId - The user ID
2671
3172
  * @param {string} [auditMessage] - Audit message for this write
3173
+ * @returns {Promise<void>}
2672
3174
  */
2673
3175
  removeWriter: (id, userId, auditMessage) =>
2674
3176
  this._request("DELETE", `/api/v1/projects/${id}/writers/${userId}`, {
@@ -2679,6 +3181,7 @@ class PlaidClient {
2679
3181
  * @param {string} id - The resource ID
2680
3182
  * @param {string} userId - The user ID
2681
3183
  * @param {string} [auditMessage] - Audit message for this write
3184
+ * @returns {Promise<void>}
2682
3185
  */
2683
3186
  addReader: (id, userId, auditMessage) =>
2684
3187
  this._request("POST", `/api/v1/projects/${id}/readers/${userId}`, {
@@ -2689,6 +3192,7 @@ class PlaidClient {
2689
3192
  * @param {string} id - The resource ID
2690
3193
  * @param {string} userId - The user ID
2691
3194
  * @param {string} [auditMessage] - Audit message for this write
3195
+ * @returns {Promise<void>}
2692
3196
  */
2693
3197
  removeReader: (id, userId, auditMessage) =>
2694
3198
  this._request("DELETE", `/api/v1/projects/${id}/readers/${userId}`, {
@@ -2700,6 +3204,7 @@ class PlaidClient {
2700
3204
  * @param {string} id - The resource ID
2701
3205
  * @param {string} userId - The user ID
2702
3206
  * @param {string} [auditMessage] - Audit message for this write
3207
+ * @returns {Promise<void>}
2703
3208
  */
2704
3209
  addMaintainer: (id, userId, auditMessage) =>
2705
3210
  this._request("POST", `/api/v1/projects/${id}/maintainers/${userId}`, {
@@ -2710,6 +3215,7 @@ class PlaidClient {
2710
3215
  * @param {string} id - The resource ID
2711
3216
  * @param {string} userId - The user ID
2712
3217
  * @param {string} [auditMessage] - Audit message for this write
3218
+ * @returns {Promise<void>}
2713
3219
  */
2714
3220
  removeMaintainer: (id, userId, auditMessage) =>
2715
3221
  this._request(
@@ -2733,6 +3239,7 @@ class PlaidClient {
2733
3239
  * one of its operations matches, carrying only the ones that did.
2734
3240
  * @param {string[]|string} [kinds] - Only the entries of operations of
2735
3241
  * these kinds (e.g. `['review', 'guess-adoption']`), each whole
3242
+ * @returns {Promise<AuditEntry[]>}
2736
3243
  */
2737
3244
  audit: (projectId, startTime, endTime, opTypes, kinds) =>
2738
3245
  listAll(this, `/api/v1/projects/${projectId}/audit`, {
@@ -2760,7 +3267,7 @@ class PlaidClient {
2760
3267
  * operations in `ops` (1..1000). `opCount` on every entry says how
2761
3268
  * many it has
2762
3269
  * @param {string} [opts.entryId] - Read only this entry, not a page
2763
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
3270
+ * @returns {Promise<Page<AuditEntry>>}
2764
3271
  */
2765
3272
  auditPage: (
2766
3273
  projectId,
@@ -2798,6 +3305,7 @@ class PlaidClient {
2798
3305
  * @param {string} id - The resource ID
2799
3306
  * @param {string} vocabId - The vocab layer ID
2800
3307
  * @param {string} [auditMessage] - Audit message for this write
3308
+ * @returns {Promise<void>}
2801
3309
  */
2802
3310
  linkVocab: (id, vocabId, auditMessage) =>
2803
3311
  this._request("POST", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
@@ -2808,6 +3316,7 @@ class PlaidClient {
2808
3316
  * @param {string} id - The resource ID
2809
3317
  * @param {string} vocabId - The vocab layer ID
2810
3318
  * @param {string} [auditMessage] - Audit message for this write
3319
+ * @returns {Promise<void>}
2811
3320
  */
2812
3321
  unlinkVocab: (id, vocabId, auditMessage) =>
2813
3322
  this._request("DELETE", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
@@ -2817,6 +3326,7 @@ class PlaidClient {
2817
3326
  * Get a project by ID. To fetch the project's documents, use
2818
3327
  * listDocuments(id) — the include-documents flag has been removed.
2819
3328
  * @param {string} id - The resource ID
3329
+ * @returns {Promise<Project>}
2820
3330
  */
2821
3331
  get: (id) =>
2822
3332
  this._request("GET", `/api/v1/projects/${id}`),
@@ -2827,6 +3337,7 @@ class PlaidClient {
2827
3337
  * Note: this endpoint does not support temporal (`as-of`) queries; the
2828
3338
  * server rejects `?as-of=` on the documents-list route with a 400.
2829
3339
  * @param {string} id - The project ID
3340
+ * @returns {Promise<DocumentListEntry[]>}
2830
3341
  */
2831
3342
  listDocuments: (id) => listAll(this, `/api/v1/projects/${id}/documents`),
2832
3343
  /**
@@ -2838,7 +3349,7 @@ class PlaidClient {
2838
3349
  * @param {object} [opts]
2839
3350
  * @param {number} [opts.limit] - Page size (1..1000; server default 100)
2840
3351
  * @param {string} [opts.cursor] - Opaque cursor from a previous page
2841
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
3352
+ * @returns {Promise<Page<DocumentListEntry>>}
2842
3353
  */
2843
3354
  listDocumentsPage: (id, { limit, cursor } = {}) =>
2844
3355
  listPage(this, `/api/v1/projects/${id}/documents`, { limit, cursor }),
@@ -2851,7 +3362,7 @@ class PlaidClient {
2851
3362
  * @param {string} id - The project ID
2852
3363
  * @param {object} [opts]
2853
3364
  * @param {number} [opts.pageSize] - Per-request page size
2854
- * @returns {AsyncGenerator<Array>}
3365
+ * @returns {AsyncGenerator<DocumentListEntry[]>}
2855
3366
  */
2856
3367
  iterDocuments: (id, { pageSize } = {}) =>
2857
3368
  iterPages(this, `/api/v1/projects/${id}/documents`, { pageSize }),
@@ -2863,6 +3374,7 @@ class PlaidClient {
2863
3374
  * @param {string} [auditMessage] - Custom audit-log message
2864
3375
  * @param {object} [options]
2865
3376
  * @param {number} [options.timeout] - Per-request timeout in ms, the client's own by default (0/null disables)
3377
+ * @returns {Promise<void>}
2866
3378
  */
2867
3379
  delete: (id, auditMessage, { timeout } = {}) =>
2868
3380
  this._request("DELETE", `/api/v1/projects/${id}`, {
@@ -2874,6 +3386,7 @@ class PlaidClient {
2874
3386
  * @param {string} id - The resource ID
2875
3387
  * @param {string} name - The name
2876
3388
  * @param {string} [auditMessage] - Audit message for this write
3389
+ * @returns {Promise<Project>}
2877
3390
  */
2878
3391
  update: (id, name, auditMessage) =>
2879
3392
  this._request("PATCH", `/api/v1/projects/${id}`, {
@@ -2883,6 +3396,7 @@ class PlaidClient {
2883
3396
  /**
2884
3397
  * List all projects accessible to user. Transparently follows pagination
2885
3398
  * cursors and returns the full flat array.
3399
+ * @returns {Promise<ProjectListEntry[]>}
2886
3400
  */
2887
3401
  list: () => listAll(this, "/api/v1/projects"),
2888
3402
  /**
@@ -2890,7 +3404,7 @@ class PlaidClient {
2890
3404
  * @param {object} [opts]
2891
3405
  * @param {number} [opts.limit] - Page size (1..1000; server default 100)
2892
3406
  * @param {string} [opts.cursor] - Opaque cursor from a previous page
2893
- * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
3407
+ * @returns {Promise<Page<ProjectListEntry>>}
2894
3408
  */
2895
3409
  listPage: ({ limit, cursor } = {}) =>
2896
3410
  listPage(this, "/api/v1/projects", {
@@ -2901,7 +3415,7 @@ class PlaidClient {
2901
3415
  * Async-iterate projects page by page; yields each page's entries array.
2902
3416
  * @param {object} [opts]
2903
3417
  * @param {number} [opts.pageSize] - Per-request page size
2904
- * @returns {AsyncGenerator<Array>}
3418
+ * @returns {AsyncGenerator<ProjectListEntry[]>}
2905
3419
  */
2906
3420
  iterPages: ({ pageSize } = {}) =>
2907
3421
  iterPages(this, "/api/v1/projects", {
@@ -2913,6 +3427,7 @@ class PlaidClient {
2913
3427
  * @param {string} [auditMessage] - Audit message for this write
2914
3428
  * @param {object} [opts]
2915
3429
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3430
+ * @returns {Promise<Created>}
2916
3431
  */
2917
3432
  create: (name, auditMessage, { id } = {}) =>
2918
3433
  this._request("POST", "/api/v1/projects", {
@@ -2931,6 +3446,7 @@ class PlaidClient {
2931
3446
  * @param {string} [auditMessage] - Audit message for this write
2932
3447
  * @param {object} [opts]
2933
3448
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3449
+ * @returns {Promise<Created>}
2934
3450
  */
2935
3451
  create: (projectId, name, auditMessage, { id } = {}) =>
2936
3452
  this._request("POST", "/api/v1/text-layers", {
@@ -2949,6 +3465,7 @@ class PlaidClient {
2949
3465
  * @param {string} [auditMessage] - Audit message for this write
2950
3466
  * @param {object} [opts]
2951
3467
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3468
+ * @returns {Promise<Created>}
2952
3469
  */
2953
3470
  create: (vocabLayerId, form, metadata, auditMessage, { id } = {}) =>
2954
3471
  this._request("POST", "/api/v1/vocab-items", {
@@ -2997,6 +3514,7 @@ class PlaidClient {
2997
3514
  /**
2998
3515
  * Get a vocab item by ID
2999
3516
  * @param {string} id - The resource ID
3517
+ * @returns {Promise<VocabItem>}
3000
3518
  */
3001
3519
  get: (id) =>
3002
3520
  this._request("GET", `/api/v1/vocab-items/${id}`),
@@ -3009,6 +3527,7 @@ class PlaidClient {
3009
3527
  * @param {{expectedLinkCount?: number}} [options] - `expectedLinkCount`: the
3010
3528
  * number of links the caller showed. The delete is refused with a 409 when
3011
3529
  * the entry has any other number of links by then.
3530
+ * @returns {Promise<void>}
3012
3531
  */
3013
3532
  delete: (id, auditMessage, { expectedLinkCount } = {}) =>
3014
3533
  this._request("DELETE", `/api/v1/vocab-items/${id}`, {
@@ -3046,6 +3565,7 @@ class PlaidClient {
3046
3565
  * @param {string} id - The resource ID
3047
3566
  * @param {string} form - The vocab item form
3048
3567
  * @param {string} [auditMessage] - Audit message for this write
3568
+ * @returns {Promise<VocabItem>}
3049
3569
  */
3050
3570
  update: (id, form, auditMessage) =>
3051
3571
  this._request("PATCH", `/api/v1/vocab-items/${id}`, {
@@ -3063,6 +3583,7 @@ class PlaidClient {
3063
3583
  * @param {string} [auditMessage] - Audit message for this write
3064
3584
  * @param {object} [opts]
3065
3585
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3586
+ * @returns {Promise<Created>}
3066
3587
  */
3067
3588
  create: (spanLayerId, name, auditMessage, { id } = {}) =>
3068
3589
  this._request("POST", "/api/v1/relation-layers", {
@@ -3071,6 +3592,7 @@ class PlaidClient {
3071
3592
  }),
3072
3593
  ...configMethods(this, "relation-layers"),
3073
3594
  ...constraintMethods(this, "relation-layers"),
3595
+ ...valueSetMethods(this, "relation-layers"),
3074
3596
  };
3075
3597
 
3076
3598
  this.tokens = {
@@ -3094,6 +3616,7 @@ class PlaidClient {
3094
3616
  * @param {string} [auditMessage] - Audit message for this write
3095
3617
  * @param {object} [opts]
3096
3618
  * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
3619
+ * @returns {Promise<Created>}
3097
3620
  */
3098
3621
  create: (
3099
3622
  tokenLayerId,
@@ -3120,6 +3643,7 @@ class PlaidClient {
3120
3643
  /**
3121
3644
  * Get a token.
3122
3645
  * @param {string} tokenId - The token ID
3646
+ * @returns {Promise<Token>}
3123
3647
  */
3124
3648
  get: (tokenId) =>
3125
3649
  this._request("GET", `/api/v1/tokens/${tokenId}`),
@@ -3128,6 +3652,7 @@ class PlaidClient {
3128
3652
  * have no remaining tokens, the span will also be deleted.
3129
3653
  * @param {string} tokenId - The token ID
3130
3654
  * @param {string} [auditMessage] - Audit message for this write
3655
+ * @returns {Promise<void>}
3131
3656
  */
3132
3657
  delete: (tokenId, auditMessage) =>
3133
3658
  this._request("DELETE", `/api/v1/tokens/${tokenId}`, { auditMessage }),
@@ -3141,6 +3666,7 @@ class PlaidClient {
3141
3666
  * CLEAR it (revert to no explicit ordering). bodyOf keeps null but
3142
3667
  * drops undefined, so the three cases map correctly to the server.
3143
3668
  * @param {string} [auditMessage] - Audit message for this write
3669
+ * @returns {Promise<Token>}
3144
3670
  */
3145
3671
  update: (tokenId, begin, end, precedence, auditMessage) =>
3146
3672
  this._request("PATCH", `/api/v1/tokens/${tokenId}`, {
@@ -3175,8 +3701,9 @@ class PlaidClient {
3175
3701
  body,
3176
3702
  }),
3177
3703
  /**
3178
- * Split a token at a Unicode code-point offset. The original token becomes the
3179
- * left half (keeps its ID, spans, vocab-links); the new right token's ID is returned.
3704
+ * Split a token at a Unicode code-point offset. A new token is made for one
3705
+ * half and its ID is returned. The original token keeps its ID, spans and
3706
+ * vocab-links on the left half, or on the right half with `keep: "right"`.
3180
3707
  * @param {string} tokenId - The token ID
3181
3708
  * @param {number} position - Code-point offset to split at (strictly between begin and end)
3182
3709
  * @param {string} [auditMessage] - Audit message for this write
@@ -3187,6 +3714,7 @@ class PlaidClient {
3187
3714
  * half. A relation layer whose relations must stay inside one token
3188
3715
  * of this layer declares a same-ancestor constraint, and the server deletes
3189
3716
  * the relations the split leaves crossing in the same transaction.
3717
+ * @returns {Promise<Created>}
3190
3718
  */
3191
3719
  split: (
3192
3720
  tokenId,
@@ -3206,6 +3734,7 @@ class PlaidClient {
3206
3734
  * @param {string} tokenId - The anchor token ID
3207
3735
  * @param {string} otherTokenId - The other token to merge in
3208
3736
  * @param {string} [auditMessage] - Audit message for this write
3737
+ * @returns {Promise<Token>}
3209
3738
  */
3210
3739
  merge: (tokenId, otherTokenId, auditMessage) =>
3211
3740
  this._request("POST", `/api/v1/tokens/${tokenId}/merge`, {
@@ -3220,6 +3749,7 @@ class PlaidClient {
3220
3749
  * @param {number} [begin] - New start offset, inclusive (Unicode code points)
3221
3750
  * @param {number} [end] - New end offset, exclusive (Unicode code points)
3222
3751
  * @param {string} [auditMessage] - Audit message for this write
3752
+ * @returns {Promise<Token>}
3223
3753
  */
3224
3754
  shift: (tokenId, begin, end, auditMessage) =>
3225
3755
  this._request("POST", `/api/v1/tokens/${tokenId}/shift`, {
@@ -3255,7 +3785,7 @@ class PlaidClient {
3255
3785
  * queues, so a message queued after the writes goes out after them.
3256
3786
  * @param {string} projectId - The UUID of the project to send to
3257
3787
  * @param {any} data - The message data to send
3258
- * @returns {Promise<any>} Response from the send operation
3788
+ * @returns {Promise<{success: boolean, message: string}>}
3259
3789
  */
3260
3790
  sendMessage: (projectId, data) =>
3261
3791
  this._request("POST", `/api/v1/projects/${projectId}/message`, {
@@ -3382,6 +3912,11 @@ class PlaidClient {
3382
3912
  cancelServiceRequest(this, projectId, requestId),
3383
3913
  };
3384
3914
 
3915
+ // outOfBand: a query is a read that travels as a POST (see the note at
3916
+ // the top of http.js). Made on a batch it goes over the wire like any
3917
+ // read, and it never joins a logical operation. It waits past core's own
3918
+ // query limit, so a query too broad answers with core's 408, and a 503
3919
+ // from core's full queue of large queries is not retried.
3385
3920
  /**
3386
3921
  * Run a query over every project you can read.
3387
3922
  *
@@ -3413,16 +3948,11 @@ class PlaidClient {
3413
3948
  *
3414
3949
  * @param {Object} body - The query AST ({find, where, scope?, limit?, orderBy?,
3415
3950
  * return?, bindings?}).
3416
- * @returns {Promise<Object>} For 'ids'/'entities': {columns, results, count, truncated}.
3951
+ * @returns {Promise<QueryResult>} For 'ids'/'entities': {columns, results, count, truncated}.
3417
3952
  * For 'count': {return: 'count', count, truncated}, where `truncated` says
3418
3953
  * the count stopped at the server's cap. Entity cells are full entity
3419
3954
  * objects (same shape as the GET endpoints).
3420
3955
  */
3421
- // outOfBand: a query is a read that travels as a POST (see the note at
3422
- // the top of http.js). Made on a batch it goes over the wire like any
3423
- // read, and it never joins a logical operation. It waits past core's own
3424
- // query limit, so a query too broad answers with core's 408, and a 503
3425
- // from core's full queue of large queries is not retried.
3426
3956
  this.query = (body) =>
3427
3957
  this._request("POST", "/api/v1/query", {
3428
3958
  body,
@@ -3438,12 +3968,14 @@ class PlaidClient {
3438
3968
  /**
3439
3969
  * Get a logical-operation group (its label + creator).
3440
3970
  * @param {string} id - The group id
3971
+ * @returns {Promise<OperationGroup>}
3441
3972
  */
3442
3973
  get: (id) => this._request("GET", `/api/v1/operation-groups/${id}`),
3443
3974
  /**
3444
3975
  * Relabel a logical-operation group after the fact. Owner or admin only.
3445
3976
  * @param {string} id - The group id
3446
3977
  * @param {string|null} message - The new label
3978
+ * @returns {Promise<OperationGroup>}
3447
3979
  */
3448
3980
  update: (id, message) =>
3449
3981
  this._request("PATCH", `/api/v1/operation-groups/${id}`, {
@@ -3504,6 +4036,7 @@ class PlaidClient {
3504
4036
  * Delete a comment. The author may delete their own; a project
3505
4037
  * maintainer (or admin) may delete any.
3506
4038
  * @param {string} id - The comment id
4039
+ * @returns {Promise<void>}
3507
4040
  */
3508
4041
  delete: (id) =>
3509
4042
  this._request("DELETE", `/api/v1/comments/${id}`, { noOperation: true }),
@@ -3808,6 +4341,7 @@ class PlaidClient {
3808
4341
  * Delete a guideline.
3809
4342
  * @param {string} id - The guideline id
3810
4343
  * @param {string} [auditMessage] - Audit message for this write
4344
+ * @returns {Promise<void>}
3811
4345
  */
3812
4346
  delete: (id, auditMessage) =>
3813
4347
  this._request("DELETE", `/api/v1/guidelines/${id}`, { auditMessage }),
@@ -4087,10 +4621,15 @@ class PlaidClient {
4087
4621
  * parents created earlier in the same batch, and any op's failure rolls the
4088
4622
  * whole batch back. A batch is not nestable. Prefer `batched()`, which
4089
4623
  * submits or aborts for you.
4624
+ *
4625
+ * `timeout` (ms, or null for none) is how long each of this batch's
4626
+ * requests may wait for its answer, in place of the client's
4627
+ * `batchTimeout`, for this batch alone.
4628
+ * @param {{timeout?: number | null}} [options]
4090
4629
  * @returns {PlaidBatch} the batch view
4091
4630
  */
4092
- batch() {
4093
- return openBatch(this);
4631
+ batch({ timeout } = {}) {
4632
+ return openBatch(this, { timeout });
4094
4633
  }
4095
4634
 
4096
4635
  /**
@@ -4099,9 +4638,10 @@ class PlaidClient {
4099
4638
  * recased: the headers keep the server's spelling. `key` is the request's
4100
4639
  * Idempotency-Key: a batch whose answer is lost (no response, 502, 504) is
4101
4640
  * sent again under it, and answered from the first send if that landed.
4641
+ * `timeout` is this request's, in place of `batchTimeout` when given.
4102
4642
  * @private
4103
4643
  */
4104
- async _postBatch(url, body, { key = uuidv7() } = {}) {
4644
+ async _postBatch(url, body, { key = uuidv7(), timeout } = {}) {
4105
4645
  const fetchOptions = {
4106
4646
  method: "POST",
4107
4647
  headers: {
@@ -4117,7 +4657,9 @@ class PlaidClient {
4117
4657
  // repeating it is safe. A fresh timeout signal per attempt — an
4118
4658
  // AbortSignal.timeout stays aborted once it fires.
4119
4659
  const attempt = async () => {
4120
- const signal = timeoutSignal(this.batchTimeout);
4660
+ const signal = timeoutSignal(
4661
+ timeout !== undefined ? timeout : this.batchTimeout,
4662
+ );
4121
4663
  let res;
4122
4664
  try {
4123
4665
  res = await fetch(
@@ -4231,12 +4773,14 @@ class PlaidClient {
4231
4773
  * });
4232
4774
  *
4233
4775
  * A write made on `client` inside `fn` is not part of the batch: it goes
4234
- * over the wire at once, as it would anywhere else. See `batch()`.
4776
+ * over the wire at once, as it would anywhere else. See `batch()`, which
4777
+ * also says what `timeout` does.
4235
4778
  * @param {(batch: PlaidBatch) => (void | Promise<void>)} fn
4779
+ * @param {{timeout?: number | null}} [options]
4236
4780
  * @returns {Promise<any[]>}
4237
4781
  */
4238
- async batched(fn) {
4239
- const batch = this.batch();
4782
+ async batched(fn, { timeout } = {}) {
4783
+ const batch = this.batch({ timeout });
4240
4784
  try {
4241
4785
  await fn(batch);
4242
4786
  } catch (e) {
@@ -4381,25 +4925,25 @@ class PlaidClient {
4381
4925
  * neither body is re-cased.
4382
4926
  */
4383
4927
  function configRequest(auditMessage, value, options, isDelete = false) {
4928
+ // The answer is a 204 with no body, so there is nothing to keep un-recased.
4384
4929
  if (!options || !Object.hasOwn(options, "expected")) {
4385
- return isDelete
4386
- ? { auditMessage, skipResponseTransform: true }
4387
- : { auditMessage, rawBody: value, skipResponseTransform: true };
4930
+ return isDelete ? { auditMessage } : { auditMessage, rawBody: value };
4388
4931
  }
4389
4932
  const expected = options.expected === undefined ? null : options.expected;
4390
4933
  return {
4391
4934
  auditMessage,
4392
4935
  queryParams: { "if-unchanged": true },
4393
4936
  rawBody: isDelete ? { expected } : { expected, value },
4394
- skipResponseTransform: true,
4395
4937
  };
4396
4938
  }
4397
4939
 
4398
4940
  /**
4399
4941
  * The three metadata methods of a bundle whose entities carry metadata, on
4400
4942
  * `/api/v1/<kind>/<id>/metadata`. Metadata is opaque, so no body is re-cased.
4943
+ * Each answers the entity as its GET does.
4944
+ * @template {keyof MetadataRecords} K
4401
4945
  * @param {PlaidClient} client
4402
- * @param {string} kind
4946
+ * @param {K} kind
4403
4947
  */
4404
4948
  function metadataMethods(client, kind) {
4405
4949
  const path = (id) => `/api/v1/${kind}/${id}/metadata`;
@@ -4409,6 +4953,7 @@ function metadataMethods(client, kind) {
4409
4953
  * @param {string} id - The entity's ID
4410
4954
  * @param {object} body - The new metadata map
4411
4955
  * @param {string} [auditMessage] - Audit message for this write
4956
+ * @returns {Promise<MetadataRecords[K]>}
4412
4957
  */
4413
4958
  setMetadata: (id, body, auditMessage) =>
4414
4959
  client._request("PUT", path(id), { auditMessage, rawBody: body }),
@@ -4416,6 +4961,7 @@ function metadataMethods(client, kind) {
4416
4961
  * Remove all metadata from an entity.
4417
4962
  * @param {string} id - The entity's ID
4418
4963
  * @param {string} [auditMessage] - Audit message for this write
4964
+ * @returns {Promise<MetadataRecords[K]>}
4419
4965
  */
4420
4966
  deleteMetadata: (id, auditMessage) => client._request("DELETE", path(id), { auditMessage }),
4421
4967
  /**
@@ -4423,6 +4969,7 @@ function metadataMethods(client, kind) {
4423
4969
  * @param {string} id - The entity's ID
4424
4970
  * @param {MetadataOp[]} body - The metadata ops
4425
4971
  * @param {string} [auditMessage] - Audit message for this write
4972
+ * @returns {Promise<MetadataRecords[K]>}
4426
4973
  */
4427
4974
  patchMetadata: (id, body, auditMessage) =>
4428
4975
  client._request("PATCH", path(id), { auditMessage, rawBody: body }),
@@ -4446,7 +4993,10 @@ function configMethods(client, kind) {
4446
4993
  * @param {any} configValue - Configuration value to set
4447
4994
  * @param {string} [auditMessage] - Audit message for this write
4448
4995
  * @param {{expected?: any}} [options] - `expected`: the value read for this key (null or undefined when it
4449
- * was absent). The write is then refused with a 409 when the stored value differs.
4996
+ * was absent). The write is then refused with a 409 when the stored value differs. A value the key
4997
+ * already holds is not written and leaves no audit row, so the value read, expecting it, states in a
4998
+ * batch that the key is unchanged.
4999
+ * @returns {Promise<void>}
4450
5000
  */
4451
5001
  setConfig: (id, namespace, configKey, configValue, auditMessage, options) =>
4452
5002
  client._request(
@@ -4460,7 +5010,9 @@ function configMethods(client, kind) {
4460
5010
  * @param {string} namespace - The config namespace
4461
5011
  * @param {string} configKey - The config key
4462
5012
  * @param {string} [auditMessage] - Audit message for this write
4463
- * @param {{expected?: any}} [options] - `expected`, as on setConfig.
5013
+ * @param {{expected?: any}} [options] - `expected`, as on setConfig. A key that is absent is not
5014
+ * written, so expecting null states in a batch that it is still absent.
5015
+ * @returns {Promise<void>}
4464
5016
  */
4465
5017
  deleteConfig: (id, namespace, configKey, auditMessage, options) =>
4466
5018
  client._request(
@@ -4474,8 +5026,9 @@ function configMethods(client, kind) {
4474
5026
  /**
4475
5027
  * The get, delete, update and shift methods of the four layer bundles, on
4476
5028
  * `/api/v1/<kind>/<layerId>`.
5029
+ * @template {keyof LayerRecords} K
4477
5030
  * @param {PlaidClient} client
4478
- * @param {string} kind
5031
+ * @param {K} kind
4479
5032
  */
4480
5033
  function layerMethods(client, kind) {
4481
5034
  const path = (layerId) => `/api/v1/${kind}/${layerId}`;
@@ -4483,12 +5036,14 @@ function layerMethods(client, kind) {
4483
5036
  /**
4484
5037
  * Get a layer by ID.
4485
5038
  * @param {string} layerId - The layer ID
5039
+ * @returns {Promise<LayerRecords[K]>}
4486
5040
  */
4487
5041
  get: (layerId) => client._request("GET", path(layerId)),
4488
5042
  /**
4489
5043
  * Delete a layer.
4490
5044
  * @param {string} layerId - The layer ID
4491
5045
  * @param {string} [auditMessage] - Audit message for this write
5046
+ * @returns {Promise<void>}
4492
5047
  */
4493
5048
  delete: (layerId, auditMessage) => client._request("DELETE", path(layerId), { auditMessage }),
4494
5049
  /**
@@ -4496,6 +5051,7 @@ function layerMethods(client, kind) {
4496
5051
  * @param {string} layerId - The layer ID
4497
5052
  * @param {string} name - The new name
4498
5053
  * @param {string} [auditMessage] - Audit message for this write
5054
+ * @returns {Promise<LayerRecords[K]>}
4499
5055
  */
4500
5056
  update: (layerId, name, auditMessage) =>
4501
5057
  client._request("PATCH", path(layerId), { auditMessage, body: bodyOf({ name }) }),
@@ -4504,6 +5060,7 @@ function layerMethods(client, kind) {
4504
5060
  * @param {string} layerId - The layer ID
4505
5061
  * @param {string} direction - The direction ("up" or "down")
4506
5062
  * @param {string} [auditMessage] - Audit message for this write
5063
+ * @returns {Promise<void>}
4507
5064
  */
4508
5065
  shift: (layerId, direction, auditMessage) =>
4509
5066
  client._request("POST", `${path(layerId)}/shift`, {
@@ -4546,6 +5103,7 @@ function constraintMethods(client, kind) {
4546
5103
  * @param {string} namespace - The declaring app's namespace
4547
5104
  * @param {string} [auditMessage] - Audit message for this write
4548
5105
  * @param {{expected?: Array<object>|null}} [options] - `expected`, as on setConstraints.
5106
+ * @returns {Promise<void>}
4549
5107
  */
4550
5108
  deleteConstraints: (layerId, namespace, auditMessage, options) => {
4551
5109
  const body = constraintsBody(undefined, options);
@@ -4559,10 +5117,16 @@ function constraintMethods(client, kind) {
4559
5117
  * data. Writes nothing.
4560
5118
  * @param {string} layerId - The layer ID
4561
5119
  * @param {LayerConstraint[]} constraints - The list to check
5120
+ * @param {{limit?: number}} [options] - `limit`: how many violations to list (default 100)
4562
5121
  * @returns {Promise<{violations: ConstraintViolation[], violationCount: number}>}
4563
5122
  */
4564
- checkConstraints: (layerId, constraints) =>
4565
- client._request("POST", `${base(layerId)}/check`, { body: { constraints } }),
5123
+ checkConstraints: (layerId, constraints, options) =>
5124
+ client._request("POST", `${base(layerId)}/check`, {
5125
+ body: {
5126
+ constraints,
5127
+ ...(options?.limit != null ? { limit: options.limit } : {}),
5128
+ },
5129
+ }),
4566
5130
  /**
4567
5131
  * Apply the remedies of the given constraints' remediable types
4568
5132
  * (coextensive, single-span, single-link, same-ancestor) to every
@@ -4584,6 +5148,38 @@ function constraintMethods(client, kind) {
4584
5148
  };
4585
5149
  }
4586
5150
 
5151
+ /**
5152
+ * The value-set read of a span or relation layer bundle, on
5153
+ * `/api/v1/<kind>/<layerId>/constraints/refused-values`.
5154
+ * @param {PlaidClient} client
5155
+ * @param {string} kind
5156
+ */
5157
+ function valueSetMethods(client, kind) {
5158
+ return {
5159
+ /**
5160
+ * The stored values of this layer that a value-set rule would refuse,
5161
+ * before it is declared: exactly what a declaration of it would meet,
5162
+ * with the rule's exemptions (an unconfirmed machine value, a value an
5163
+ * import, a document copy or a restore set). Writes nothing, and a
5164
+ * reader may ask.
5165
+ * @param {string} layerId - The layer ID
5166
+ * @param {ValueSetRule} rule - The list as a value-set declaration takes it
5167
+ * @param {{limit?: number}} [options] - `limit`: how many values to list, the most frequent first
5168
+ * (default 20, at most 100)
5169
+ * @returns {Promise<RefusedValues>}
5170
+ */
5171
+ refusedValues: (layerId, rule, options) =>
5172
+ client._request("POST", `/api/v1/${kind}/${layerId}/constraints/refused-values`, {
5173
+ body: bodyOf({
5174
+ values: rule?.values,
5175
+ delimiters: rule?.delimiters,
5176
+ parts: rule?.parts,
5177
+ limit: options?.limit,
5178
+ }),
5179
+ }),
5180
+ };
5181
+ }
5182
+
4587
5183
  export default PlaidClient;
4588
5184
  export { PlaidClient };
4589
5185
  export { valueSetAllows, violationsOf } from "./constraints.js";
@@ -4625,6 +5221,9 @@ export {
4625
5221
  // request, and `critical()` holds that off around writes. See ./services.js
4626
5222
  // and the manual, "Stopping a request".
4627
5223
  export { ServiceCancelled, createCancelScope } from "./services.js";
5224
+ // The two messages a write that got no answer is reported with: one the
5225
+ // server may have saved, one never sent. See ./services.js.
5226
+ export { UNKNOWN_OUTCOME, UNREACHABLE } from "./services.js";
4628
5227
  // Who asked for a service run, named in its History label and in what it
4629
5228
  // stores. See ./requester.js.
4630
5229
  export { REQUESTED_BY, makeRequester, requesterOf } from "./requester.js";