@larc-iu/plaid-client 0.2.0-alpha.4 → 0.2.0-alpha.6

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/index.d.ts CHANGED
@@ -305,6 +305,54 @@ interface UserDataBundle {
305
305
  delete(userId: string, key: string): Promise<any>;
306
306
  }
307
307
 
308
+ interface CommentFilters {
309
+ documentId?: string;
310
+ entityType?: CommentableType;
311
+ entityId?: string;
312
+ }
313
+
314
+ /** Entities that can carry a comment. */
315
+ type CommentableType = "document" | "text" | "token" | "span" | "relation";
316
+
317
+ interface Comment {
318
+ id: string;
319
+ projectId: string;
320
+ documentId: string;
321
+ entityType: CommentableType;
322
+ entityId: string;
323
+ authorId: string;
324
+ body: string;
325
+ createdAt: string;
326
+ updatedAt: string;
327
+ /** True once the body has been rewritten since it was posted. */
328
+ edited: boolean;
329
+ }
330
+
331
+ interface CommentsBundle {
332
+ create(
333
+ entityType: CommentableType,
334
+ entityId: string,
335
+ body: string,
336
+ ): Promise<Comment>;
337
+ get(id: string): Promise<Comment>;
338
+ update(id: string, body: string): Promise<Comment>;
339
+ delete(id: string): Promise<any>;
340
+ list(projectId: string, filters?: CommentFilters): Promise<Comment[]>;
341
+ listPage(
342
+ projectId: string,
343
+ opts?: CommentFilters & { limit?: number; cursor?: string },
344
+ ): Promise<Page<Comment>>;
345
+ iterPages(
346
+ projectId: string,
347
+ opts?: CommentFilters & { pageSize?: number },
348
+ ): AsyncGenerator<Comment[]>;
349
+ /** `{entityId: count}`; keys are raw entity ids, never key-transformed. */
350
+ counts(
351
+ projectId: string,
352
+ filters?: CommentFilters,
353
+ ): Promise<Record<string, number>>;
354
+ }
355
+
308
356
  interface ApiTokensBundle {
309
357
  list(userId: string): Promise<any[]>;
310
358
  listPage(
@@ -769,6 +817,7 @@ export declare class PlaidClient {
769
817
  apiTokens: ApiTokensBundle;
770
818
  userData: UserDataBundle;
771
819
  invites: InvitesBundle;
820
+ comments: CommentsBundle;
772
821
  tokenLayers: TokenLayersBundle;
773
822
  documents: DocumentsBundle;
774
823
  messages: MessagesBundle;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@larc-iu/plaid-client",
3
- "version": "0.2.0-alpha.4",
3
+ "version": "0.2.0-alpha.6",
4
4
  "description": "JavaScript client for the Plaid annotation API",
5
5
  "type": "module",
6
6
  "repository": {
package/src/http.js CHANGED
@@ -132,6 +132,13 @@ export function timeoutSignal(timeout) {
132
132
  * formData - If true, body is FormData; skip Content-Type header
133
133
  * queryParams - Object of query param key/values to append
134
134
  * noBatch - If true, throw when in batch mode
135
+ * bypassBatch - If true, go over the wire even while a batch is open.
136
+ * For READS only: a batch is a write transaction, its
137
+ * ops return no value to their caller until submit, and
138
+ * the sub-request runs against the tx Connection rather
139
+ * than the pool. A read swallowed by an ambient batch is
140
+ * therefore useless to the caller AND can fail the whole
141
+ * batch (see `query`).
135
142
  * skipResponseTransform - Return raw parsed JSON (no transformResponse)
136
143
  * noAuth - Skip Authorization header
137
144
  * binaryResponse - Return arrayBuffer instead of JSON/text
@@ -146,6 +153,7 @@ export async function makeRequest(client, method, path, options = {}) {
146
153
  formData,
147
154
  queryParams,
148
155
  noBatch,
156
+ bypassBatch,
149
157
  skipResponseTransform,
150
158
  noAuth,
151
159
  binaryResponse,
@@ -219,8 +227,11 @@ export async function makeRequest(client, method, path, options = {}) {
219
227
  group.written = true;
220
228
  }
221
229
 
222
- // Batch mode
223
- if (client.isBatching) {
230
+ // Batch mode. A `bypassBatch` read is deliberately NOT queued: it belongs to
231
+ // whoever called it, not to whatever batch happens to be open on this shared
232
+ // client (a document reconcile, an import), and it reads the pre-batch state
233
+ // exactly as it would have if the batch were not running.
234
+ if (client.isBatching && !bypassBatch) {
224
235
  if (noBatch) {
225
236
  throw new Error(`This endpoint cannot be used in batch mode: ${path}`);
226
237
  }
package/src/index.js CHANGED
@@ -2149,8 +2149,14 @@ class PlaidClient {
2149
2149
  * For 'count': {return: 'count', count}. Entity cells are full entity objects
2150
2150
  * (same shape as the GET endpoints).
2151
2151
  */
2152
+ // bypassBatch: a query is a read. `client.batched()` flips one flag on the
2153
+ // whole client, so a query issued by unrelated code while a batch is open
2154
+ // (the IGT island fetching precedent while a document reconciles) used to
2155
+ // be queued into it: the caller got `{batched: true}` instead of results,
2156
+ // and the server ran the query against the batch's tx Connection, which
2157
+ // 500s and rolls back every write in that batch.
2152
2158
  this.query = (body, auditMessage) =>
2153
- this._request("POST", "/api/v1/query", { auditMessage, body });
2159
+ this._request("POST", "/api/v1/query", { auditMessage, body, bypassBatch: true });
2154
2160
 
2155
2161
  // Logical-operation groups (audit-log grouping). There is no create: a
2156
2162
  // group row is made lazily by the first write carrying `?group-id=`
@@ -2171,6 +2177,120 @@ class PlaidClient {
2171
2177
  body: { message },
2172
2178
  }),
2173
2179
  };
2180
+
2181
+ this.comments = {
2182
+ /**
2183
+ * Post a comment on an entity. Requires write access to the entity's
2184
+ * project; the author is the authenticated caller. Comments are not
2185
+ * audited and do not bump the document version.
2186
+ * @param {'document'|'text'|'token'|'span'|'relation'} entityType - What kind of thing is being commented on
2187
+ * @param {string} entityId - The commented entity's id
2188
+ * @param {string} body - The comment text (1..10000 characters)
2189
+ */
2190
+ create: (entityType, entityId, body) =>
2191
+ this._request("POST", "/api/v1/comments", {
2192
+ body: bodyOf({ "entity-type": entityType, "entity-id": entityId, body }),
2193
+ }),
2194
+ /**
2195
+ * Read one comment.
2196
+ * @param {string} id - The comment id
2197
+ */
2198
+ get: (id) => this._request("GET", `/api/v1/comments/${id}`),
2199
+ /**
2200
+ * Edit a comment's body. Only the comment's AUTHOR may do this - not
2201
+ * maintainers, not admins. Sets `edited` on the comment.
2202
+ * @param {string} id - The comment id
2203
+ * @param {string} body - The replacement text
2204
+ */
2205
+ update: (id, body) =>
2206
+ this._request("PATCH", `/api/v1/comments/${id}`, { body: { body } }),
2207
+ /**
2208
+ * Delete a comment. The author may delete their own; a project
2209
+ * maintainer (or admin) may delete any.
2210
+ * @param {string} id - The comment id
2211
+ */
2212
+ delete: (id) => this._request("DELETE", `/api/v1/comments/${id}`),
2213
+ /**
2214
+ * List comments in a project, oldest first. Transparently follows
2215
+ * pagination cursors and returns the full flat array.
2216
+ * Cannot be used inside a batch (auto-paginates across requests); throws if called while batching - use listPage() for a single page in a batch.
2217
+ * @param {string} projectId - The project to read
2218
+ * @param {object} [filters] - Narrow the scope
2219
+ * @param {string} [filters.documentId] - Every comment anywhere in one document
2220
+ * @param {string} [filters.entityType] - With entityId, one entity's thread
2221
+ * @param {string} [filters.entityId] - With entityType, one entity's thread
2222
+ */
2223
+ list: (projectId, { documentId, entityType, entityId } = {}) =>
2224
+ listAll(this, `/api/v1/projects/${projectId}/comments`, {
2225
+ query: {
2226
+ "document-id": documentId,
2227
+ "entity-type": entityType,
2228
+ "entity-id": entityId,
2229
+ },
2230
+ }),
2231
+ /**
2232
+ * Fetch a single page of a project's comments.
2233
+ * @param {string} projectId - The project to read
2234
+ * @param {object} [opts]
2235
+ * @param {number} [opts.limit] - Page size (1..1000; server default 100)
2236
+ * @param {string} [opts.cursor] - Opaque cursor from a previous page
2237
+ * @param {string} [opts.documentId] - Every comment anywhere in one document
2238
+ * @param {string} [opts.entityType] - With entityId, one entity's thread
2239
+ * @param {string} [opts.entityId] - With entityType, one entity's thread
2240
+ * @returns {Promise<{entries: Array, nextCursor: (string|null)}>}
2241
+ */
2242
+ listPage: (projectId, { limit, cursor, documentId, entityType, entityId } = {}) =>
2243
+ listPage(this, `/api/v1/projects/${projectId}/comments`, {
2244
+ limit,
2245
+ cursor,
2246
+ query: {
2247
+ "document-id": documentId,
2248
+ "entity-type": entityType,
2249
+ "entity-id": entityId,
2250
+ },
2251
+ }),
2252
+ /**
2253
+ * Async-iterate a project's comments page by page; yields each page's entries array.
2254
+ * Cannot be used inside a batch (auto-paginates across requests); throws on first iteration if called while batching - use listPage() for a single page in a batch.
2255
+ * @param {string} projectId - The project to read
2256
+ * @param {object} [opts]
2257
+ * @param {number} [opts.pageSize] - Per-request page size
2258
+ * @param {string} [opts.documentId] - Every comment anywhere in one document
2259
+ * @param {string} [opts.entityType] - With entityId, one entity's thread
2260
+ * @param {string} [opts.entityId] - With entityType, one entity's thread
2261
+ * @returns {AsyncGenerator<Array>}
2262
+ */
2263
+ iterPages: (projectId, { pageSize, documentId, entityType, entityId } = {}) =>
2264
+ iterPages(this, `/api/v1/projects/${projectId}/comments`, {
2265
+ pageSize,
2266
+ query: {
2267
+ "document-id": documentId,
2268
+ "entity-type": entityType,
2269
+ "entity-id": entityId,
2270
+ },
2271
+ }),
2272
+ /**
2273
+ * Comment counts per entity, as an `{entityId: n}` map, over the same
2274
+ * scope and filters as `list`. One cheap request paints a comment
2275
+ * indicator on every annotated item in a document without paging
2276
+ * through the bodies.
2277
+ *
2278
+ * The response is NOT key-transformed: its keys are entity ids, and
2279
+ * camelCasing would mangle the hyphens in a UUID.
2280
+ * @param {string} projectId - The project to read
2281
+ * @param {object} [filters] - Same filters as list()
2282
+ * @returns {Promise<Object<string, number>>}
2283
+ */
2284
+ counts: (projectId, { documentId, entityType, entityId } = {}) =>
2285
+ this._request("GET", `/api/v1/projects/${projectId}/comments/counts`, {
2286
+ queryParams: {
2287
+ "document-id": documentId,
2288
+ "entity-type": entityType,
2289
+ "entity-id": entityId,
2290
+ },
2291
+ skipResponseTransform: true,
2292
+ }),
2293
+ };
2174
2294
  }
2175
2295
 
2176
2296
  // --- Core methods ---