@larc-iu/plaid-client 0.2.0-alpha.3 → 0.2.0-alpha.32

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.
Files changed (73) hide show
  1. package/README.md +39 -0
  2. package/package.json +13 -6
  3. package/src/apiAnnotations.d.ts +508 -0
  4. package/src/apiAnnotations.js +543 -0
  5. package/src/apiDocuments.d.ts +569 -0
  6. package/src/apiDocuments.js +551 -0
  7. package/src/apiLayers.d.ts +369 -0
  8. package/src/apiLayers.js +222 -0
  9. package/src/apiMessages.d.ts +293 -0
  10. package/src/apiMessages.js +265 -0
  11. package/src/apiPeople.d.ts +626 -0
  12. package/src/apiPeople.js +698 -0
  13. package/src/apiProject.d.ts +761 -0
  14. package/src/apiProject.js +774 -0
  15. package/src/apiServer.d.ts +457 -0
  16. package/src/apiServer.js +424 -0
  17. package/src/apiVocab.d.ts +456 -0
  18. package/src/apiVocab.js +425 -0
  19. package/src/batch.d.ts +54 -0
  20. package/src/batch.js +323 -0
  21. package/src/batchRef.d.ts +30 -0
  22. package/src/batchRef.js +128 -0
  23. package/src/client.d.ts +1321 -0
  24. package/src/client.js +743 -0
  25. package/src/codepoint.d.ts +94 -0
  26. package/src/codepoint.js +131 -1
  27. package/src/constraints.d.ts +85 -0
  28. package/src/constraints.js +112 -0
  29. package/src/created.d.ts +2 -0
  30. package/src/created.js +29 -0
  31. package/src/documentLock.d.ts +172 -0
  32. package/src/documentLock.js +384 -0
  33. package/src/entryForms.d.ts +68 -0
  34. package/src/entryForms.js +164 -0
  35. package/src/events.d.ts +60 -0
  36. package/src/events.js +271 -0
  37. package/src/families.d.ts +349 -0
  38. package/src/families.js +396 -0
  39. package/src/http.d.ts +233 -0
  40. package/src/http.js +809 -93
  41. package/src/ids.d.ts +26 -0
  42. package/src/ids.js +96 -0
  43. package/src/index.d.ts +101 -0
  44. package/src/index.js +183 -2105
  45. package/src/metadataOps.d.ts +15 -0
  46. package/src/metadataOps.js +124 -0
  47. package/src/operations.d.ts +36 -0
  48. package/src/operations.js +164 -0
  49. package/src/pagination.d.ts +49 -0
  50. package/src/pagination.js +20 -53
  51. package/src/provenance.d.ts +243 -0
  52. package/src/provenance.js +458 -36
  53. package/src/replayed.d.ts +3 -0
  54. package/src/replayed.js +31 -0
  55. package/src/requester.d.ts +60 -0
  56. package/src/requester.js +74 -0
  57. package/src/roles.d.ts +104 -0
  58. package/src/roles.js +64 -2
  59. package/src/serviceSchema.d.ts +337 -0
  60. package/src/serviceSchema.js +177 -60
  61. package/src/services.d.ts +319 -0
  62. package/src/services.js +742 -58
  63. package/src/sse.d.ts +30 -0
  64. package/src/sse.js +80 -3
  65. package/src/testing.d.ts +128 -0
  66. package/src/testing.js +379 -0
  67. package/src/textEdits.d.ts +59 -0
  68. package/src/textEdits.js +234 -0
  69. package/src/transforms.d.ts +25 -0
  70. package/src/transforms.js +11 -1
  71. package/src/types.d.ts +867 -0
  72. package/src/types.js +663 -0
  73. package/index.d.ts +0 -532
@@ -0,0 +1,774 @@
1
+ /**
2
+ * The project bundles: projects, their guidelines, comments and events, and
3
+ * the logical-operation groups of the audit log, each a factory of the
4
+ * client (client.js installs them).
5
+ */
6
+
7
+ import { recorderFor } from "./events.js";
8
+ import { auditMethods, bodyOf, configMethods } from "./families.js";
9
+ import { iterPages, listAll, listPage } from "./pagination.js";
10
+
11
+ /** The query of an events read: `types` as the wire's comma-separated list. */
12
+ function eventQuery(types, startTime, endTime) {
13
+ return {
14
+ types: Array.isArray(types) ? types.join(",") || undefined : types,
15
+ "start-time": startTime,
16
+ "end-time": endTime,
17
+ };
18
+ }
19
+
20
+ /**
21
+ * @typedef {import("./types.js").TextEditOp} TextEditOp
22
+ * @typedef {import("./types.js").TextGap} TextGap
23
+ * @typedef {import("./types.js").MetadataOp} MetadataOp
24
+ * @typedef {import("./types.js").LayerConstraint} LayerConstraint
25
+ * @typedef {import("./types.js").ConstraintWriteOptions} ConstraintWriteOptions
26
+ * @typedef {import("./types.js").ConstraintViolation} ConstraintViolation
27
+ * @typedef {import("./types.js").Requester} Requester
28
+ * @typedef {import("./types.js").BatchRef} BatchRef
29
+ * @typedef {import("./types.js").ServiceParam} ServiceParam
30
+ * @typedef {import("./types.js").ServiceInfo} ServiceInfo
31
+ * @typedef {import("./types.js").DiscoveredService} DiscoveredService
32
+ * @typedef {import("./types.js").ResponseHelper} ResponseHelper
33
+ * @typedef {import("./types.js").ServiceRegistration} ServiceRegistration
34
+ * @typedef {import("./types.js").ServiceStatusEvent} ServiceStatusEvent
35
+ * @typedef {import("./types.js").OperationGroupsBundle} OperationGroupsBundle
36
+ * @typedef {import("./types.js").PlaidClientOptions} PlaidClientOptions
37
+ * @typedef {import("./types.js").OperationKind} OperationKind
38
+ * @typedef {import("./types.js").KeySeed} KeySeed
39
+ * @typedef {import("./types.js").PlaidBatch} PlaidBatch
40
+ * @typedef {import("./types.js").AuditPageOptions} AuditPageOptions
41
+ * @typedef {import("./types.js").VocabItemRestoreSummary} VocabItemRestoreSummary
42
+ * @typedef {import("./types.js").UserDataEntry} UserDataEntry
43
+ * @typedef {import("./types.js").AdminUserDataEntry} AdminUserDataEntry
44
+ * @typedef {import("./types.js").ActivityTallyRow} ActivityTallyRow
45
+ * @typedef {import("./types.js").ConstraintRepair} ConstraintRepair
46
+ * @typedef {import("./types.js").ValueSetRule} ValueSetRule
47
+ * @typedef {import("./types.js").RefusedValues} RefusedValues
48
+ * @typedef {import("./types.js").CommentableType} CommentableType
49
+ * @typedef {import("./types.js").Comment} Comment
50
+ * @typedef {import("./types.js").Guideline} Guideline
51
+ * @typedef {import("./types.js").ClientEventType} ClientEventType
52
+ * @typedef {import("./types.js").ClientEvent} ClientEvent
53
+ * @typedef {import("./types.js").Invite} Invite
54
+ * @typedef {import("./types.js").ServerLimits} ServerLimits
55
+ * @typedef {import("./types.js").ServerHealth} ServerHealth
56
+ * @typedef {import("./types.js").RateLimitBucket} RateLimitBucket
57
+ * @typedef {import("./types.js").RateLimitSnapshot} RateLimitSnapshot
58
+ * @typedef {import("./types.js").BackupStatus} BackupStatus
59
+ * @typedef {import("./types.js").ServerReport} ServerReport
60
+ * @typedef {import("./types.js").LiveLog} LiveLog
61
+ * @typedef {import("./types.js").SSEConnection} SSEConnection
62
+ * @typedef {import("./types.js").SSEStats} SSEStats
63
+ * @typedef {import("./types.js").AtomicValue} AtomicValue
64
+ * @typedef {import("./types.js").Created} Created
65
+ * @typedef {import("./types.js").Project} Project
66
+ * @typedef {import("./types.js").ProjectTextLayer} ProjectTextLayer
67
+ * @typedef {import("./types.js").ProjectTokenLayer} ProjectTokenLayer
68
+ * @typedef {import("./types.js").ProjectSpanLayer} ProjectSpanLayer
69
+ * @typedef {import("./types.js").ProjectRelationLayer} ProjectRelationLayer
70
+ * @typedef {import("./types.js").ProjectVocab} ProjectVocab
71
+ * @typedef {import("./types.js").PlaidDocument} PlaidDocument
72
+ * @typedef {import("./types.js").DocumentTextLayer} DocumentTextLayer
73
+ * @typedef {import("./types.js").DocumentTokenLayer} DocumentTokenLayer
74
+ * @typedef {import("./types.js").DocumentSpanLayer} DocumentSpanLayer
75
+ * @typedef {import("./types.js").PlaidText} PlaidText
76
+ * @typedef {import("./types.js").TextReshape} TextReshape
77
+ * @typedef {import("./types.js").Token} Token
78
+ * @typedef {import("./types.js").Span} Span
79
+ * @typedef {import("./types.js").Relation} Relation
80
+ * @typedef {import("./types.js").VocabItem} VocabItem
81
+ * @typedef {import("./types.js").VocabLink} VocabLink
82
+ * @typedef {import("./types.js").VocabLayer} VocabLayer
83
+ * @typedef {import("./types.js").TextLayer} TextLayer
84
+ * @typedef {import("./types.js").TokenLayer} TokenLayer
85
+ * @typedef {import("./types.js").SpanLayer} SpanLayer
86
+ * @typedef {import("./types.js").RelationLayer} RelationLayer
87
+ * @typedef {import("./types.js").MetadataRecords} MetadataRecords
88
+ * @typedef {import("./types.js").LayerRecords} LayerRecords
89
+ * @typedef {import("./types.js").User} User
90
+ * @typedef {import("./types.js").ProjectListEntry} ProjectListEntry
91
+ * @typedef {import("./types.js").DocumentListEntry} DocumentListEntry
92
+ * @typedef {import("./types.js").ApiToken} ApiToken
93
+ * @typedef {import("./types.js").AuditUser} AuditUser
94
+ * @typedef {import("./types.js").AuditTarget} AuditTarget
95
+ * @typedef {import("./types.js").AuditOp} AuditOp
96
+ * @typedef {import("./types.js").AuditEntry} AuditEntry
97
+ * @typedef {import("./types.js").LockGrant} LockGrant
98
+ * @typedef {import("./types.js").DocumentRestoreSummary} DocumentRestoreSummary
99
+ * @typedef {import("./types.js").RestoreCounts} RestoreCounts
100
+ * @typedef {import("./types.js").OperationGroup} OperationGroup
101
+ * @typedef {import("./types.js").QueryResult} QueryResult
102
+ */
103
+
104
+ /**
105
+ * @template [T=any]
106
+ * @typedef {import("./types.js").Page<T>} Page
107
+ */
108
+
109
+ /** @param {import("./client.js").PlaidClient} client */
110
+ export function projectsApi(client) {
111
+ return {
112
+ /**
113
+ * Get a project by ID. To fetch the project's documents, use
114
+ * listDocuments(id) — the include-documents flag has been removed.
115
+ * @param {string} id - The resource ID
116
+ * @returns {Promise<Project>}
117
+ */
118
+ get: (id) =>
119
+ client._request("GET", `/api/v1/projects/${id}`),
120
+ /**
121
+ * List all projects accessible to user. Transparently follows pagination
122
+ * cursors and returns the full flat array.
123
+ * @returns {Promise<ProjectListEntry[]>}
124
+ */
125
+ list: () => listAll(client, "/api/v1/projects"),
126
+ /**
127
+ * Fetch a single page of projects.
128
+ * @param {object} [opts]
129
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
130
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
131
+ * @returns {Promise<Page<ProjectListEntry>>}
132
+ */
133
+ listPage: ({ limit, cursor } = {}) =>
134
+ listPage(client, "/api/v1/projects", {
135
+ limit,
136
+ cursor,
137
+ }),
138
+ /**
139
+ * Async-iterate projects page by page; yields each page's entries array.
140
+ * @param {object} [opts]
141
+ * @param {number} [opts.pageSize] - Per-request page size
142
+ * @returns {AsyncGenerator<ProjectListEntry[]>}
143
+ */
144
+ iterPages: ({ pageSize } = {}) =>
145
+ iterPages(client, "/api/v1/projects", {
146
+ pageSize,
147
+ }),
148
+ /**
149
+ * List all documents in a project. Transparently follows pagination
150
+ * cursors and returns the full flat array.
151
+ *
152
+ * Note: this endpoint does not support temporal (`as-of`) queries; the
153
+ * server rejects `?as-of=` on the documents-list route with a 400.
154
+ * @param {string} id - The project ID
155
+ * @returns {Promise<DocumentListEntry[]>}
156
+ */
157
+ listDocuments: (id) => listAll(client, `/api/v1/projects/${id}/documents`),
158
+ /**
159
+ * Fetch a single page of a project's documents.
160
+ *
161
+ * Note: this endpoint does not support temporal (`as-of`) queries; the
162
+ * server rejects `?as-of=` on the documents-list route with a 400.
163
+ * @param {string} id - The project ID
164
+ * @param {object} [opts]
165
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
166
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
167
+ * @returns {Promise<Page<DocumentListEntry>>}
168
+ */
169
+ listDocumentsPage: (id, { limit, cursor } = {}) =>
170
+ listPage(client, `/api/v1/projects/${id}/documents`, { limit, cursor }),
171
+ /**
172
+ * Async-iterate a project's documents page by page; yields each page's
173
+ * entries array.
174
+ *
175
+ * Note: this endpoint does not support temporal (`as-of`) queries; the
176
+ * server rejects `?as-of=` on the documents-list route with a 400.
177
+ * @param {string} id - The project ID
178
+ * @param {object} [opts]
179
+ * @param {number} [opts.pageSize] - Per-request page size
180
+ * @returns {AsyncGenerator<DocumentListEntry[]>}
181
+ */
182
+ iterDocuments: (id, { pageSize } = {}) =>
183
+ iterPages(client, `/api/v1/projects/${id}/documents`, { pageSize }),
184
+ /**
185
+ * Create a new project. Note: this also registers the user as a maintainer.
186
+ * @param {string} name - The name
187
+ * @param {string} [auditMessage] - Audit message for this write
188
+ * @param {object} [opts]
189
+ * @param {string} [opts.id] - The new entity's id, minted by the caller (see uuidv7)
190
+ * @returns {Promise<Created>}
191
+ */
192
+ create: (name, auditMessage, { id } = {}) =>
193
+ client._request("POST", "/api/v1/projects", {
194
+ auditMessage,
195
+ body: bodyOf({ id, name }),
196
+ }),
197
+ /**
198
+ * Update a project's name.
199
+ * @param {string} id - The resource ID
200
+ * @param {string} name - The name
201
+ * @param {string} [auditMessage] - Audit message for this write
202
+ * @returns {Promise<Project>}
203
+ */
204
+ update: (id, name, auditMessage) =>
205
+ client._request("PATCH", `/api/v1/projects/${id}`, {
206
+ auditMessage,
207
+ body: bodyOf({ name }),
208
+ }),
209
+ /**
210
+ * Delete a project and everything in it. This is irrecoverable. The
211
+ * project is gone when this returns, and what it holds is removed on
212
+ * the server afterwards.
213
+ * @param {string} id - The resource ID
214
+ * @param {string} [auditMessage] - Custom audit-log message
215
+ * @param {object} [options]
216
+ * @param {number} [options.timeout] - Per-request timeout in ms, the client's own by default (0/null disables)
217
+ * @returns {Promise<void>}
218
+ */
219
+ delete: (id, auditMessage, { timeout } = {}) =>
220
+ client._request("DELETE", `/api/v1/projects/${id}`, {
221
+ auditMessage,
222
+ timeout,
223
+ }),
224
+ /**
225
+ * Set a user's access level to read and write for this project.
226
+ * @param {string} id - The resource ID
227
+ * @param {string} userId - The user ID
228
+ * @param {string} [auditMessage] - Audit message for this write
229
+ * @returns {Promise<void>}
230
+ */
231
+ addWriter: (id, userId, auditMessage) =>
232
+ client._request("POST", `/api/v1/projects/${id}/writers/${userId}`, {
233
+ auditMessage,
234
+ }),
235
+ /**
236
+ * Remove a user's writer privileges for this project.
237
+ * @param {string} id - The resource ID
238
+ * @param {string} userId - The user ID
239
+ * @param {string} [auditMessage] - Audit message for this write
240
+ * @returns {Promise<void>}
241
+ */
242
+ removeWriter: (id, userId, auditMessage) =>
243
+ client._request("DELETE", `/api/v1/projects/${id}/writers/${userId}`, {
244
+ auditMessage,
245
+ }),
246
+ /**
247
+ * Set a user's access level to read-only for this project.
248
+ * @param {string} id - The resource ID
249
+ * @param {string} userId - The user ID
250
+ * @param {string} [auditMessage] - Audit message for this write
251
+ * @returns {Promise<void>}
252
+ */
253
+ addReader: (id, userId, auditMessage) =>
254
+ client._request("POST", `/api/v1/projects/${id}/readers/${userId}`, {
255
+ auditMessage,
256
+ }),
257
+ /**
258
+ * Remove a user's reader privileges for this project.
259
+ * @param {string} id - The resource ID
260
+ * @param {string} userId - The user ID
261
+ * @param {string} [auditMessage] - Audit message for this write
262
+ * @returns {Promise<void>}
263
+ */
264
+ removeReader: (id, userId, auditMessage) =>
265
+ client._request("DELETE", `/api/v1/projects/${id}/readers/${userId}`, {
266
+ auditMessage,
267
+ }),
268
+ /**
269
+ * Assign a user as a maintainer for this project.
270
+ * @param {string} id - The resource ID
271
+ * @param {string} userId - The user ID
272
+ * @param {string} [auditMessage] - Audit message for this write
273
+ * @returns {Promise<void>}
274
+ */
275
+ addMaintainer: (id, userId, auditMessage) =>
276
+ client._request("POST", `/api/v1/projects/${id}/maintainers/${userId}`, {
277
+ auditMessage,
278
+ }),
279
+ /**
280
+ * Remove a user's maintainer privileges for this project.
281
+ * @param {string} id - The resource ID
282
+ * @param {string} userId - The user ID
283
+ * @param {string} [auditMessage] - Audit message for this write
284
+ * @returns {Promise<void>}
285
+ */
286
+ removeMaintainer: (id, userId, auditMessage) =>
287
+ client._request(
288
+ "DELETE",
289
+ `/api/v1/projects/${id}/maintainers/${userId}`,
290
+ { auditMessage },
291
+ ),
292
+ /**
293
+ * When the calling user last wrote to each document in a project, as a
294
+ * `{documentId: timestamp}` map. Documents they have never written to
295
+ * are absent. One request covers a whole document list.
296
+ *
297
+ * The response is NOT key-transformed: its keys are document ids, and
298
+ * camelCasing would mangle the hyphens in a UUID.
299
+ * @param {string} projectId - The project ID
300
+ * @returns {Promise<Object<string, string>>}
301
+ */
302
+ myLastEdits: (projectId) =>
303
+ client._request("GET", `/api/v1/projects/${projectId}/audit/last-edits`, {
304
+ skipResponseTransform: true,
305
+ }),
306
+ /**
307
+ * Link a vocabulary to a project.
308
+ * @param {string} id - The resource ID
309
+ * @param {string} vocabId - The vocab layer ID
310
+ * @param {string} [auditMessage] - Audit message for this write
311
+ * @returns {Promise<void>}
312
+ */
313
+ linkVocab: (id, vocabId, auditMessage) =>
314
+ client._request("POST", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
315
+ auditMessage,
316
+ }),
317
+ /**
318
+ * Unlink a vocabulary from a project.
319
+ * @param {string} id - The resource ID
320
+ * @param {string} vocabId - The vocab layer ID
321
+ * @param {string} [auditMessage] - Audit message for this write
322
+ * @returns {Promise<void>}
323
+ */
324
+ unlinkVocab: (id, vocabId, auditMessage) =>
325
+ client._request("DELETE", `/api/v1/projects/${id}/vocabs/${vocabId}`, {
326
+ auditMessage,
327
+ }),
328
+ ...configMethods(client, "projects"),
329
+ ...auditMethods(client, "projects"),
330
+ };
331
+ }
332
+
333
+ /** @param {import("./client.js").PlaidClient} client */
334
+ export function guidelinesApi(client) {
335
+ return {
336
+ /**
337
+ * Read one guideline, Markdown body included.
338
+ * @param {string} id - The guideline id
339
+ * @returns {Promise<Guideline>}
340
+ */
341
+ get: (id) => client._request("GET", `/api/v1/guidelines/${id}`),
342
+ /**
343
+ * List a project's guidelines, by title. Transparently follows
344
+ * pagination cursors and returns the full flat array.
345
+ *
346
+ * Without `includeBodies` each entry carries `bodyChars`, the length of
347
+ * its body, so a caller can budget before fetching any. Pinned
348
+ * guidelines are NOT sorted first: `pinned` is on every entry and
349
+ * grouping is the caller's.
350
+ * @param {string} projectId - The project to read
351
+ * @param {object} [opts]
352
+ * @param {boolean} [opts.includeBodies] - Return each body instead of its length
353
+ * @returns {Promise<Guideline[]>}
354
+ */
355
+ list: (projectId, { includeBodies } = {}) =>
356
+ listAll(client, `/api/v1/projects/${projectId}/guidelines`, {
357
+ query: { "include-bodies": includeBodies },
358
+ }),
359
+ /**
360
+ * Fetch a single page of a project's guidelines.
361
+ * @param {string} projectId - The project to read
362
+ * @param {object} [opts]
363
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
364
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
365
+ * @param {boolean} [opts.includeBodies] - Return each body instead of its length
366
+ * @returns {Promise<Page<Guideline>>}
367
+ */
368
+ listPage: (projectId, { limit, cursor, includeBodies } = {}) =>
369
+ listPage(client, `/api/v1/projects/${projectId}/guidelines`, {
370
+ limit,
371
+ cursor,
372
+ query: { "include-bodies": includeBodies },
373
+ }),
374
+ /**
375
+ * Async-iterate a project's guidelines page by page; yields each page's entries array.
376
+ * @param {string} projectId - The project to read
377
+ * @param {object} [opts]
378
+ * @param {number} [opts.pageSize] - Per-request page size
379
+ * @param {boolean} [opts.includeBodies] - Return each body instead of its length
380
+ * @returns {AsyncGenerator<Guideline[]>}
381
+ */
382
+ iterPages: (projectId, { pageSize, includeBodies } = {}) =>
383
+ iterPages(client, `/api/v1/projects/${projectId}/guidelines`, {
384
+ pageSize,
385
+ query: { "include-bodies": includeBodies },
386
+ }),
387
+ /**
388
+ * Create a guideline in a project. `title` is the handle an assistant
389
+ * asks for one by, and the only thing a person has to keep current. It is
390
+ * NOT required to be unique, so a title already in use is written like
391
+ * any other: warn about it, do not refuse it. A `pinned` guideline is one
392
+ * the assistant is given in full on every turn.
393
+ * @param {string} projectId - The project the guideline belongs to
394
+ * @param {string} title - The handle an assistant asks for one by (1..100 characters)
395
+ * @param {object} [opts]
396
+ * @param {string} [opts.body] - The Markdown text (up to 20000 characters; may be empty)
397
+ * @param {boolean} [opts.pinned] - Send this one to the assistant in full on every turn
398
+ * @param {string} [opts.id] - The new guideline's id, minted by the caller (see uuidv7)
399
+ * @param {string} [auditMessage] - Audit message for this write
400
+ * @returns {Promise<{id: string}>}
401
+ */
402
+ create: (projectId, title, { body, pinned, id } = {}, auditMessage) =>
403
+ client._request("POST", `/api/v1/projects/${projectId}/guidelines`, {
404
+ auditMessage,
405
+ body: bodyOf({ id, title, body, pinned }),
406
+ }),
407
+ /**
408
+ * Update a guideline. Every field is optional and an omitted one is
409
+ * left alone, so an edit to the body need not restate the title.
410
+ *
411
+ * Pass `expectedUpdatedAt` (the `updatedAt` you last read) when a person
412
+ * has been editing prose: the write then fails with 409 rather than
413
+ * overwriting somebody who saved in between. Leave it off for a pin
414
+ * toggle or a script, which have nothing of anyone's to lose.
415
+ * @param {string} id - The guideline id
416
+ * @param {object} [changes]
417
+ * @param {string} [changes.title] - The new handle
418
+ * @param {string} [changes.body] - The new Markdown text
419
+ * @param {boolean} [changes.pinned] - Whether the assistant always gets it in full
420
+ * @param {string} [changes.expectedUpdatedAt] - Write only if this is still the stored updatedAt
421
+ * @param {string} [auditMessage] - Audit message for this write
422
+ * @returns {Promise<Guideline>}
423
+ */
424
+ update: (
425
+ id,
426
+ { title, body, pinned, expectedUpdatedAt } = {},
427
+ auditMessage,
428
+ ) =>
429
+ client._request("PATCH", `/api/v1/guidelines/${id}`, {
430
+ auditMessage,
431
+ queryParams: { "updated-at": expectedUpdatedAt },
432
+ body: bodyOf({ title, body, pinned }),
433
+ }),
434
+ /**
435
+ * Delete a guideline.
436
+ * @param {string} id - The guideline id
437
+ * @param {string} [auditMessage] - Audit message for this write
438
+ * @returns {Promise<void>}
439
+ */
440
+ delete: (id, auditMessage) =>
441
+ client._request("DELETE", `/api/v1/guidelines/${id}`, { auditMessage }),
442
+ };
443
+ }
444
+
445
+ /** @param {import("./client.js").PlaidClient} client */
446
+ export function commentsApi(client) {
447
+ return {
448
+ /**
449
+ * Read one comment.
450
+ * @param {string} id - The comment id
451
+ * @returns {Promise<Comment>}
452
+ */
453
+ get: (id) => client._request("GET", `/api/v1/comments/${id}`),
454
+ /**
455
+ * List comments in a project, oldest first. Transparently follows
456
+ * pagination cursors and returns the full flat array.
457
+ * @param {string} projectId - The project to read
458
+ * @param {object} [filters] - Narrow the scope
459
+ * @param {string} [filters.documentId] - Every comment anywhere in one document
460
+ * @param {string} [filters.entityType] - With entityId, one entity's thread
461
+ * @param {string} [filters.entityId] - With entityType, one entity's thread
462
+ * @returns {Promise<Comment[]>}
463
+ */
464
+ list: (projectId, { documentId, entityType, entityId } = {}) =>
465
+ listAll(client, `/api/v1/projects/${projectId}/comments`, {
466
+ query: {
467
+ "document-id": documentId,
468
+ "entity-type": entityType,
469
+ "entity-id": entityId,
470
+ },
471
+ }),
472
+ /**
473
+ * Fetch a single page of a project's comments.
474
+ * @param {string} projectId - The project to read
475
+ * @param {object} [opts]
476
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
477
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
478
+ * @param {string} [opts.documentId] - Every comment anywhere in one document
479
+ * @param {string} [opts.entityType] - With entityId, one entity's thread
480
+ * @param {string} [opts.entityId] - With entityType, one entity's thread
481
+ * @returns {Promise<Page<Comment>>}
482
+ */
483
+ listPage: (
484
+ projectId,
485
+ { limit, cursor, documentId, entityType, entityId } = {},
486
+ ) =>
487
+ listPage(client, `/api/v1/projects/${projectId}/comments`, {
488
+ limit,
489
+ cursor,
490
+ query: {
491
+ "document-id": documentId,
492
+ "entity-type": entityType,
493
+ "entity-id": entityId,
494
+ },
495
+ }),
496
+ /**
497
+ * Async-iterate a project's comments page by page; yields each page's entries array.
498
+ * @param {string} projectId - The project to read
499
+ * @param {object} [opts]
500
+ * @param {number} [opts.pageSize] - Per-request page size
501
+ * @param {string} [opts.documentId] - Every comment anywhere in one document
502
+ * @param {string} [opts.entityType] - With entityId, one entity's thread
503
+ * @param {string} [opts.entityId] - With entityType, one entity's thread
504
+ * @returns {AsyncGenerator<Comment[]>}
505
+ */
506
+ iterPages: (
507
+ projectId,
508
+ { pageSize, documentId, entityType, entityId } = {},
509
+ ) =>
510
+ iterPages(client, `/api/v1/projects/${projectId}/comments`, {
511
+ pageSize,
512
+ query: {
513
+ "document-id": documentId,
514
+ "entity-type": entityType,
515
+ "entity-id": entityId,
516
+ },
517
+ }),
518
+ /**
519
+ * List the comments on a vocabulary's entries, oldest first. Requires
520
+ * read access to the vocabulary. Transparently follows pagination
521
+ * cursors and returns the full flat array.
522
+ * @param {string} vocabId - The vocab layer to read
523
+ * @param {object} [filters]
524
+ * @param {string} [filters.entityId] - One entry's thread
525
+ * @returns {Promise<Comment[]>}
526
+ */
527
+ listInVocab: (vocabId, { entityId } = {}) =>
528
+ listAll(client, `/api/v1/vocab-layers/${vocabId}/comments`, {
529
+ query: { "entity-id": entityId },
530
+ }),
531
+ /**
532
+ * Fetch a single page of a vocabulary's comments.
533
+ * @param {string} vocabId - The vocab layer to read
534
+ * @param {object} [opts]
535
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
536
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
537
+ * @param {string} [opts.entityId] - One entry's thread
538
+ * @returns {Promise<Page<Comment>>}
539
+ */
540
+ listInVocabPage: (vocabId, { limit, cursor, entityId } = {}) =>
541
+ listPage(client, `/api/v1/vocab-layers/${vocabId}/comments`, {
542
+ limit,
543
+ cursor,
544
+ query: { "entity-id": entityId },
545
+ }),
546
+ /**
547
+ * Async-iterate a vocabulary's comments page by page, oldest first;
548
+ * yields each page's entries array.
549
+ * @param {string} vocabId - The vocab layer to read
550
+ * @param {object} [opts]
551
+ * @param {string} [opts.entityId] - One entry's thread
552
+ * @param {number} [opts.pageSize] - Per-request page size
553
+ * @returns {AsyncGenerator<Comment[]>}
554
+ */
555
+ iterInVocabPages: (vocabId, { entityId, pageSize } = {}) =>
556
+ iterPages(client, `/api/v1/vocab-layers/${vocabId}/comments`, {
557
+ pageSize,
558
+ query: { "entity-id": entityId },
559
+ }),
560
+ /**
561
+ * Post a comment on an entity. Requires write access to the entity's
562
+ * project, or to the vocabulary for a vocab-item; the author is the
563
+ * authenticated caller. Comments are not audited and do not bump the
564
+ * document version.
565
+ *
566
+ * A comment outlives its anchor: deleting the entity does not delete
567
+ * the comment. `anchorLabel` is the caption shown once that happens
568
+ * ("Gloss of ktab, sentence 4"), so pass what the comment is about.
569
+ * @param {CommentableType} entityType - What kind of thing is being commented on
570
+ * @param {string} entityId - The commented entity's id
571
+ * @param {string} body - The comment text (1..10000 characters)
572
+ * @param {object} [opts]
573
+ * @param {string} [opts.anchorLabel] - What the comment is about, in words (at most 200 characters)
574
+ * @param {string} [opts.id] - The new comment's id, minted by the caller (see uuidv7)
575
+ * @returns {Promise<Comment>}
576
+ */
577
+ create: (entityType, entityId, body, { anchorLabel, id } = {}) =>
578
+ client._request("POST", "/api/v1/comments", {
579
+ // A comment is not audited, so it never joins an open operation,
580
+ // and never takes a number from an edit's Idempotency-Key seed.
581
+ noOperation: true,
582
+ body: bodyOf({
583
+ id,
584
+ "entity-type": entityType,
585
+ "entity-id": entityId,
586
+ body,
587
+ "anchor-label": anchorLabel,
588
+ }),
589
+ }),
590
+ /**
591
+ * Edit a comment's body. Only the comment's AUTHOR may do this - not
592
+ * maintainers, not admins. Sets `edited` on the comment.
593
+ * @param {string} id - The comment id
594
+ * @param {string} body - The replacement text
595
+ * @returns {Promise<Comment>}
596
+ */
597
+ update: (id, body) =>
598
+ client._request("PATCH", `/api/v1/comments/${id}`, {
599
+ body: { body },
600
+ noOperation: true,
601
+ }),
602
+ /**
603
+ * Delete a comment. The author may delete their own; a project
604
+ * maintainer (or admin) may delete any.
605
+ * @param {string} id - The comment id
606
+ * @returns {Promise<void>}
607
+ */
608
+ delete: (id) =>
609
+ client._request("DELETE", `/api/v1/comments/${id}`, { noOperation: true }),
610
+ /**
611
+ * Comment counts per entity, as an `{entityId: n}` map, over the same
612
+ * scope and filters as `list`. One cheap request paints a comment
613
+ * indicator on every annotated item in a document without paging
614
+ * through the bodies.
615
+ *
616
+ * The response is NOT key-transformed: its keys are entity ids, and
617
+ * camelCasing would mangle the hyphens in a UUID.
618
+ * @param {string} projectId - The project to read
619
+ * @param {{documentId?: string, entityType?: CommentableType, entityId?: string}} [filters] - Same filters as list()
620
+ * @returns {Promise<Object<string, number>>}
621
+ */
622
+ counts: (projectId, { documentId, entityType, entityId } = {}) =>
623
+ client._request("GET", `/api/v1/projects/${projectId}/comments/counts`, {
624
+ queryParams: {
625
+ "document-id": documentId,
626
+ "entity-type": entityType,
627
+ "entity-id": entityId,
628
+ },
629
+ skipResponseTransform: true,
630
+ }),
631
+ /**
632
+ * Comment counts per entry of a vocabulary, as an `{entityId: n}` map.
633
+ * The response is NOT key-transformed: its keys are entry ids.
634
+ * @param {string} vocabId - The vocab layer to read
635
+ * @param {object} [filters]
636
+ * @param {string} [filters.entityId] - One entry only
637
+ * @returns {Promise<Object<string, number>>}
638
+ */
639
+ countsInVocab: (vocabId, { entityId } = {}) =>
640
+ client._request(
641
+ "GET",
642
+ `/api/v1/vocab-layers/${vocabId}/comments/counts`,
643
+ {
644
+ queryParams: { "entity-id": entityId },
645
+ skipResponseTransform: true,
646
+ },
647
+ ),
648
+ };
649
+ }
650
+
651
+ /** @param {import("./client.js").PlaidClient} client */
652
+ export function eventsApi(client) {
653
+ return {
654
+ /**
655
+ * A project's events in arrival order. Maintainer or admin only.
656
+ * Transparently follows pagination cursors and returns the full flat
657
+ * array.
658
+ * @param {string} projectId - The project to read
659
+ * @param {object} [filters]
660
+ * @param {string|string[]} [filters.types] - Only these types
661
+ * @param {string} [filters.startTime] - Only events the server stamped at or after this instant
662
+ * @param {string} [filters.endTime] - Only events the server stamped at or before this instant
663
+ * @returns {Promise<ClientEvent[]>}
664
+ */
665
+ list: (projectId, { types, startTime, endTime } = {}) =>
666
+ listAll(client, `/api/v1/projects/${projectId}/events`, {
667
+ query: eventQuery(types, startTime, endTime),
668
+ }),
669
+ /**
670
+ * One page of a project's events. Maintainer or admin only.
671
+ * @param {string} projectId - The project to read
672
+ * @param {object} [opts]
673
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
674
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
675
+ * @param {string|string[]} [opts.types] - Only these types
676
+ * @param {string} [opts.startTime] - Only events at or after this instant
677
+ * @param {string} [opts.endTime] - Only events at or before this instant
678
+ * @returns {Promise<Page<ClientEvent>>}
679
+ */
680
+ listPage: (
681
+ projectId,
682
+ { limit, cursor, types, startTime, endTime } = {},
683
+ ) =>
684
+ listPage(client, `/api/v1/projects/${projectId}/events`, {
685
+ limit,
686
+ cursor,
687
+ query: eventQuery(types, startTime, endTime),
688
+ }),
689
+ /**
690
+ * Async-iterate a project's events page by page; yields each page's entries array.
691
+ * @param {string} projectId - The project to read
692
+ * @param {object} [opts]
693
+ * @param {number} [opts.pageSize] - Per-request page size
694
+ * @param {string|string[]} [opts.types] - Only these types
695
+ * @param {string} [opts.startTime] - Only events at or after this instant
696
+ * @param {string} [opts.endTime] - Only events at or before this instant
697
+ * @returns {AsyncGenerator<ClientEvent[]>}
698
+ */
699
+ iterPages: (projectId, { pageSize, types, startTime, endTime } = {}) =>
700
+ iterPages(client, `/api/v1/projects/${projectId}/events`, {
701
+ pageSize,
702
+ query: eventQuery(types, startTime, endTime),
703
+ }),
704
+ /**
705
+ * Record events directly, with no buffering. Requires write access, and
706
+ * the server refuses with a 403 while the project's switch is off. All
707
+ * or nothing: one bad event refuses the request with a 400 naming it.
708
+ * @param {string} projectId - The project the events happened in
709
+ * @param {Array<{type: string, documentId?: string, targetId?: string, data?: object, clientTs?: string}>} events - At most 500 events
710
+ * @returns {Promise<{count: number}>}
711
+ */
712
+ create: (projectId, events) =>
713
+ client._request("POST", `/api/v1/projects/${projectId}/events`, {
714
+ body: events,
715
+ outOfBand: true,
716
+ }),
717
+ /**
718
+ * Record one research-telemetry event (plaid-core sql/client_event.clj).
719
+ * Fire and forget: the event is buffered and sent with
720
+ * others every ten seconds, at fifty events, and when the page is
721
+ * hidden. A batch that fails is dropped, nothing is retried, and
722
+ * nothing is ever thrown. Sends nothing for a project whose switch
723
+ * (`config.plaid.research.telemetry`) is off, which it reads from the
724
+ * project itself. `suggestion.shown` is recorded once per target,
725
+ * field and value in a page session.
726
+ * @param {ClientEventType} type - The event type
727
+ * @param {object} fields
728
+ * @param {string} fields.projectId - The project it happened in
729
+ * @param {string} [fields.documentId] - The document it happened in
730
+ * @param {string} [fields.targetId] - What it is about: a token, a plan
731
+ * @param {object} [fields.data] - Its details, keyed by single lowercase words (`value`, `source`, `field`, `written`, `conversation`)
732
+ * @returns {boolean} Whether the event was buffered
733
+ */
734
+ record: (type, fields) => recorderFor(client).record(client, type, fields),
735
+ /**
736
+ * Send what the recorder holds now, rather than at its next interval.
737
+ * @returns {void}
738
+ */
739
+ flush: () => recorderFor(client).flush(),
740
+ /**
741
+ * Tell the recorder a project's switch at once, e.g. right after a
742
+ * maintainer changed it. Turning it off drops what is buffered.
743
+ * @param {string} projectId - The project
744
+ * @param {boolean} on - Whether telemetry is on
745
+ * @returns {void}
746
+ */
747
+ setEnabled: (projectId, on) => recorderFor(client).setEnabled(projectId, on),
748
+ };
749
+ }
750
+
751
+ // Logical-operation groups (audit-log grouping). There is no create: a
752
+ // group row is made lazily by the first write carrying `?group-id=`
753
+ // (see beginOperation). Not an audited write, so no auditMessage arg.
754
+ /** @param {import("./client.js").PlaidClient} client */
755
+ export function operationGroupsApi(client) {
756
+ return {
757
+ /**
758
+ * Get a logical-operation group (its label + creator).
759
+ * @param {string} id - The group id
760
+ * @returns {Promise<OperationGroup>}
761
+ */
762
+ get: (id) => client._request("GET", `/api/v1/operation-groups/${id}`),
763
+ /**
764
+ * Relabel a logical-operation group after the fact. Owner or admin only.
765
+ * @param {string} id - The group id
766
+ * @param {string|null} message - The new label
767
+ * @returns {Promise<OperationGroup>}
768
+ */
769
+ update: (id, message) =>
770
+ client._request("PATCH", `/api/v1/operation-groups/${id}`, {
771
+ body: { message },
772
+ }),
773
+ };
774
+ }