@artu-ai/compliance-sdk 0.4.0 → 0.4.2

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 (110) hide show
  1. package/package.json +4 -5
  2. package/src/errors/api.ts +0 -305
  3. package/src/errors/base.ts +0 -127
  4. package/src/errors/index.ts +0 -63
  5. package/src/errors/upload.ts +0 -204
  6. package/src/errors/validation.ts +0 -163
  7. package/src/exports/base.ts +0 -139
  8. package/src/exports/index.ts +0 -187
  9. package/src/exports/mexico/actividad-vulnerable/avi.ts +0 -207
  10. package/src/exports/mexico/actividad-vulnerable/jys.ts +0 -214
  11. package/src/exports/mexico/actividad-vulnerable/tsc.ts +0 -202
  12. package/src/exports/mexico/index.ts +0 -215
  13. package/src/models/Address.ts +0 -235
  14. package/src/models/BankAccount.ts +0 -235
  15. package/src/models/Client.ts +0 -363
  16. package/src/models/ContactMethod.ts +0 -197
  17. package/src/models/Document.ts +0 -355
  18. package/src/models/LinkedClient.ts +0 -84
  19. package/src/models/Report.ts +0 -193
  20. package/src/models/ReportItem.ts +0 -211
  21. package/src/models/Transaction.ts +0 -219
  22. package/src/models/base.ts +0 -56
  23. package/src/models/index.ts +0 -148
  24. package/src/models/mex/MexAddress.ts +0 -184
  25. package/src/models/mex/MexBankAccount.ts +0 -121
  26. package/src/models/mex/MexClient.ts +0 -177
  27. package/src/models/mex/MexDocument.ts +0 -890
  28. package/src/models/mex/MexReport.ts +0 -99
  29. package/src/models/mex/MexReportItem.ts +0 -79
  30. package/src/models/mex/MexTransaction.ts +0 -105
  31. package/src/models/mex/actividad-vulnerable/MexActividadVulnerableClient.ts +0 -66
  32. package/src/models/mex/actividad-vulnerable/MexActividadVulnerableReport.ts +0 -73
  33. package/src/models/mex/actividad-vulnerable/MexActividadVulnerableReportItem.ts +0 -74
  34. package/src/models/mex/actividad-vulnerable/MexActividadVulnerableTransaction.ts +0 -50
  35. package/src/models/mex/actividad-vulnerable/avi/MexAVIClient.ts +0 -80
  36. package/src/models/mex/actividad-vulnerable/avi/MexAVIReport.ts +0 -114
  37. package/src/models/mex/actividad-vulnerable/avi/MexAVIReportItem.ts +0 -105
  38. package/src/models/mex/actividad-vulnerable/avi/MexAVITransaction.ts +0 -157
  39. package/src/models/mex/actividad-vulnerable/index.ts +0 -78
  40. package/src/models/mex/actividad-vulnerable/jys/MexJYSClient.ts +0 -73
  41. package/src/models/mex/actividad-vulnerable/jys/MexJYSReport.ts +0 -114
  42. package/src/models/mex/actividad-vulnerable/jys/MexJYSReportItem.ts +0 -105
  43. package/src/models/mex/actividad-vulnerable/jys/MexJYSTransaction.ts +0 -178
  44. package/src/models/mex/actividad-vulnerable/tsc/MexTSCClient.ts +0 -73
  45. package/src/models/mex/actividad-vulnerable/tsc/MexTSCReport.ts +0 -114
  46. package/src/models/mex/actividad-vulnerable/tsc/MexTSCReportItem.ts +0 -105
  47. package/src/models/mex/actividad-vulnerable/tsc/MexTSCTransaction.ts +0 -104
  48. package/src/models/mex/index.ts +0 -57
  49. package/src/models/types.ts +0 -196
  50. package/src/namespaces/index.ts +0 -29
  51. package/src/namespaces/mexico/actividad-vulnerable/avi/index.ts +0 -62
  52. package/src/namespaces/mexico/actividad-vulnerable/index.ts +0 -79
  53. package/src/namespaces/mexico/actividad-vulnerable/jys/index.ts +0 -72
  54. package/src/namespaces/mexico/actividad-vulnerable/tsc/index.ts +0 -60
  55. package/src/namespaces/mexico/index.ts +0 -325
  56. package/src/resources/addresses.ts +0 -391
  57. package/src/resources/bank-accounts.ts +0 -417
  58. package/src/resources/base.ts +0 -327
  59. package/src/resources/clients.ts +0 -808
  60. package/src/resources/contact-methods.ts +0 -412
  61. package/src/resources/documents.ts +0 -688
  62. package/src/resources/index.ts +0 -40
  63. package/src/resources/mex/actividad-vulnerable/avi/clients.ts +0 -559
  64. package/src/resources/mex/actividad-vulnerable/avi/index.ts +0 -7
  65. package/src/resources/mex/actividad-vulnerable/avi/reports.ts +0 -609
  66. package/src/resources/mex/actividad-vulnerable/avi/transactions.ts +0 -404
  67. package/src/resources/mex/actividad-vulnerable/index.ts +0 -12
  68. package/src/resources/mex/actividad-vulnerable/jys/clients.ts +0 -445
  69. package/src/resources/mex/actividad-vulnerable/jys/index.ts +0 -7
  70. package/src/resources/mex/actividad-vulnerable/jys/reports.ts +0 -591
  71. package/src/resources/mex/actividad-vulnerable/jys/transactions.ts +0 -395
  72. package/src/resources/mex/actividad-vulnerable/tsc/clients.ts +0 -445
  73. package/src/resources/mex/actividad-vulnerable/tsc/index.ts +0 -7
  74. package/src/resources/mex/actividad-vulnerable/tsc/reports.ts +0 -591
  75. package/src/resources/mex/actividad-vulnerable/tsc/transactions.ts +0 -404
  76. package/src/resources/mex/addresses.ts +0 -493
  77. package/src/resources/mex/bank-accounts.ts +0 -283
  78. package/src/resources/mex/clients.ts +0 -758
  79. package/src/resources/mex/ebr.ts +0 -621
  80. package/src/resources/mex/index.ts +0 -23
  81. package/src/resources/mex/reports.ts +0 -643
  82. package/src/resources/mex/transactions.ts +0 -422
  83. package/src/resources/reports.ts +0 -515
  84. package/src/resources/transactions.ts +0 -358
  85. package/src/sdk/ComplianceSDK.ts +0 -812
  86. package/src/sdk/base.ts +0 -43
  87. package/src/sdk/index.ts +0 -98
  88. package/src/sdk/mex/ComplianceSDK.ts +0 -147
  89. package/src/sdk/mex/actividad-vulnerable/avi/ComplianceSDK.ts +0 -55
  90. package/src/sdk/mex/actividad-vulnerable/avi/index.ts +0 -16
  91. package/src/sdk/mex/actividad-vulnerable/avi/types.ts +0 -86
  92. package/src/sdk/mex/actividad-vulnerable/index.ts +0 -58
  93. package/src/sdk/mex/actividad-vulnerable/jys/ComplianceSDK.ts +0 -54
  94. package/src/sdk/mex/actividad-vulnerable/jys/index.ts +0 -16
  95. package/src/sdk/mex/actividad-vulnerable/jys/types.ts +0 -86
  96. package/src/sdk/mex/actividad-vulnerable/tsc/ComplianceSDK.ts +0 -54
  97. package/src/sdk/mex/actividad-vulnerable/tsc/index.ts +0 -16
  98. package/src/sdk/mex/actividad-vulnerable/tsc/types.ts +0 -86
  99. package/src/sdk/mex/index.ts +0 -66
  100. package/src/sdk/mex/types.ts +0 -88
  101. package/src/sdk/resource-registry.ts +0 -204
  102. package/src/sdk/sdk-registry.ts +0 -99
  103. package/src/sdk/types.ts +0 -304
  104. package/src/utils/environment.ts +0 -187
  105. package/src/utils/filters.ts +0 -412
  106. package/src/utils/index.ts +0 -134
  107. package/src/utils/pagination.ts +0 -143
  108. package/src/utils/session.ts +0 -303
  109. package/src/utils/trpc-client.ts +0 -242
  110. package/src/utils/upload.ts +0 -388
@@ -1,688 +0,0 @@
1
- /**
2
- * Documents resource
3
- *
4
- * CRUD operations for documents. File upload/download operations are handled separately.
5
- * All documents are created with pending_upload status.
6
- */
7
-
8
- import type {
9
- PaginatedResponse,
10
- DocumentFilter,
11
- DocumentSort,
12
- CreateDocumentInput,
13
- UpdateDocumentInput,
14
- ListOptions,
15
- ConfirmUploadInput,
16
- PresignedUploadUrl,
17
- PresignedDownloadUrl,
18
- } from "@artu-ai/shared";
19
- import {
20
- createDocumentInputSchema,
21
- updateDocumentInputSchema,
22
- confirmUploadInputSchema,
23
- } from "@artu-ai/shared";
24
- import { z } from "zod";
25
- import { Document, type DocumentData } from "../models/Document";
26
- import { BaseResource } from "./base";
27
- import {
28
- uploadToS3,
29
- normalizeFile,
30
- validateFileSize,
31
- type UploadableFile,
32
- type ProgressCallback,
33
- } from "../utils/upload";
34
- import { UploadError } from "../errors/upload";
35
-
36
- // ─────────────────────────────────────────────────────────────────
37
- // Types
38
- // ─────────────────────────────────────────────────────────────────
39
-
40
- export interface BatchOptions {
41
- atomic?: boolean;
42
- }
43
-
44
- export interface AtomicBatchResult<T> {
45
- atomic: true;
46
- data: T[];
47
- }
48
-
49
- export interface PartialBatchResult<T> {
50
- atomic: false;
51
- succeeded: { index: number; data: T }[];
52
- failed: { index: number; error: { code: string; message: string } }[];
53
- }
54
-
55
- /**
56
- * Result of creating a document.
57
- *
58
- * - With contentType: Returns document + presigned upload URL
59
- * - Without contentType: Returns document only (no-file document)
60
- */
61
- export interface CreateDocumentResult {
62
- /** The created document */
63
- document: Document;
64
- /** Presigned URL for uploading the file (only for file uploads) */
65
- upload?: PresignedUploadUrl;
66
- }
67
-
68
- /**
69
- * Input for uploading a document file.
70
- * Combines document metadata with upload options.
71
- */
72
- export type UploadDocumentInput = Omit<CreateDocumentInput, "contentType"> & {
73
- /** Optional content type override. If not provided, will be inferred from file. */
74
- contentType?: string;
75
- };
76
-
77
- /**
78
- * Options for the upload operation.
79
- */
80
- export interface UploadOptions {
81
- /**
82
- * Progress callback, called with a value from 0 to 1.
83
- * Only works in browser environments.
84
- */
85
- onProgress?: ProgressCallback;
86
-
87
- /**
88
- * AbortSignal for cancelling the upload.
89
- * @example
90
- * const controller = new AbortController();
91
- * sdk.documents.upload(file, data, { signal: controller.signal });
92
- * // Later: controller.abort();
93
- */
94
- signal?: AbortSignal;
95
-
96
- /**
97
- * Upload timeout in milliseconds.
98
- * @default 300000 (5 minutes)
99
- */
100
- timeout?: number;
101
- }
102
-
103
- // ─────────────────────────────────────────────────────────────────
104
- // Resource
105
- // ─────────────────────────────────────────────────────────────────
106
-
107
- /**
108
- * Documents resource for managing document records.
109
- *
110
- * @example
111
- * ```typescript
112
- * // Create a document record
113
- * const doc = await sdk.documents.create({
114
- * clientId: "client_123",
115
- * jurisdiction: "MX",
116
- * type: "ine_front",
117
- * category: "identity",
118
- * });
119
- *
120
- * // List documents
121
- * const docs = await sdk.documents.list({ filter: { clientId: "client_123" } });
122
- *
123
- * // Update document metadata
124
- * await sdk.documents.update(doc.id, { metadata: { verified: true } });
125
- * ```
126
- */
127
- export class DocumentsResource extends BaseResource<
128
- Document,
129
- CreateDocumentInput,
130
- UpdateDocumentInput,
131
- DocumentFilter,
132
- DocumentSort,
133
- CreateDocumentResult
134
- > {
135
- // ─────────────────────────────────────────────────────────────────
136
- // Create
137
- // ─────────────────────────────────────────────────────────────────
138
-
139
- /**
140
- * Creates a new document record and returns a presigned POST upload URL.
141
- *
142
- * Documents are created with status "pending_upload".
143
- * Use the returned presigned URL to upload the file directly to storage.
144
- * After upload, call `confirmUpload()` to transition status to "uploaded".
145
- *
146
- * **Important:** The upload uses POST with FormData (not PUT). This allows
147
- * enforcing a 25 MB file size limit at the storage level.
148
- *
149
- * @param data - Document data
150
- * @returns The created document and presigned upload URL with form fields
151
- *
152
- * @example
153
- * ```typescript
154
- * // Create document and get upload URL
155
- * const { document, upload } = await compliance.documents.create({
156
- * clientId: "client-123",
157
- * jurisdiction: "MX",
158
- * type: "ine_front",
159
- * });
160
- *
161
- * // Build FormData with fields + file (file must be last!)
162
- * const formData = new FormData();
163
- * Object.entries(upload.fields).forEach(([key, value]) => {
164
- * formData.append(key, value);
165
- * });
166
- * formData.append("Content-Type", file.type); // Add content-type
167
- * formData.append("file", file); // File MUST be last
168
- *
169
- * // Upload via POST
170
- * const uploadResponse = await fetch(upload.url, {
171
- * method: "POST",
172
- * body: formData,
173
- * });
174
- *
175
- * if (!uploadResponse.ok) {
176
- * throw new Error(`Upload failed: ${uploadResponse.status}`);
177
- * }
178
- *
179
- * // Confirm upload with file metadata
180
- * await compliance.documents.confirmUpload({
181
- * id: document.id,
182
- * filename: file.name,
183
- * contentType: file.type,
184
- * size: file.size,
185
- * });
186
- * ```
187
- */
188
- async create(data: CreateDocumentInput): Promise<CreateDocumentResult> {
189
- const validated = this.validate(createDocumentInputSchema, data);
190
-
191
- const response = await this.execute(() =>
192
- this.trpc.documents.create.mutate(validated)
193
- );
194
-
195
- return {
196
- document: this.instantiate(response.document as DocumentData),
197
- upload: response.upload,
198
- };
199
- }
200
-
201
- /**
202
- * Confirms that a file has been uploaded to storage.
203
- *
204
- * Call this after uploading a file via the presigned URL returned by `create()`.
205
- * This transitions the document status from "pending_upload" to "uploaded".
206
- *
207
- * @param data - Upload confirmation data
208
- * @returns The updated document
209
- */
210
- async confirmUpload(data: ConfirmUploadInput): Promise<Document> {
211
- const validated = this.validate(confirmUploadInputSchema, data);
212
-
213
- const response = await this.execute(() =>
214
- this.trpc.documents.confirmUpload.mutate(validated)
215
- );
216
-
217
- return this.instantiate(response as DocumentData);
218
- }
219
-
220
- // ─────────────────────────────────────────────────────────────────
221
- // Upload (High-Level Helper)
222
- // ─────────────────────────────────────────────────────────────────
223
-
224
- /**
225
- * Uploads a file and creates a document in one operation.
226
- *
227
- * This is a convenience method that combines:
228
- * 1. Creating a document record with presigned URL
229
- * 2. Uploading the file directly to storage
230
- * 3. Confirming the upload
231
- *
232
- * For more control over the upload process, use `create()`, manual upload,
233
- * and `confirmUpload()` separately.
234
- *
235
- * @param file - The file to upload (File, Blob, or UploadableFile)
236
- * @param data - Document metadata (clientId, jurisdiction, type, etc.)
237
- * @param options - Upload options (progress callback, abort signal, timeout)
238
- * @returns The created and uploaded document
239
- *
240
- * @example
241
- * ```typescript
242
- * // Simple upload
243
- * const document = await sdk.documents.upload(file, {
244
- * clientId: "client-123",
245
- * jurisdiction: "MX",
246
- * type: "ine_front",
247
- * });
248
- *
249
- * // With progress tracking
250
- * const document = await sdk.documents.upload(file, {
251
- * clientId: "client-123",
252
- * jurisdiction: "MX",
253
- * type: "ine_front",
254
- * }, {
255
- * onProgress: (progress) => {
256
- * console.log(`Upload: ${Math.round(progress * 100)}%`);
257
- * },
258
- * });
259
- *
260
- * // With abort support
261
- * const controller = new AbortController();
262
- * const uploadPromise = sdk.documents.upload(file, data, {
263
- * signal: controller.signal,
264
- * });
265
- *
266
- * // Cancel the upload
267
- * controller.abort();
268
- * ```
269
- *
270
- * @throws {FileTooLargeError} If file exceeds 25 MB limit
271
- * @throws {UploadError} If upload fails at any stage
272
- * @throws {UploadAbortedError} If upload is cancelled via AbortSignal
273
- * @throws {UploadTimeoutError} If upload times out
274
- */
275
- async upload(
276
- file: File | Blob | UploadableFile,
277
- data: UploadDocumentInput,
278
- options: UploadOptions = {}
279
- ): Promise<Document> {
280
- const { onProgress, signal, timeout } = options;
281
-
282
- // Normalize file input
283
- const normalizedFile = normalizeFile(file);
284
-
285
- // Validate file size before starting
286
- validateFileSize(normalizedFile.size);
287
-
288
- // Check if already aborted
289
- if (signal?.aborted) {
290
- throw new UploadError("Upload was aborted before starting", "request");
291
- }
292
-
293
- // Determine content type (use provided or infer from file)
294
- const contentType = data.contentType || normalizedFile.type;
295
- if (!contentType || contentType === "application/octet-stream") {
296
- throw new UploadError(
297
- "Could not determine file content type. Please provide contentType explicitly.",
298
- "request"
299
- );
300
- }
301
-
302
- // Validate content type is allowed
303
- const allowedTypes = [
304
- "image/jpeg",
305
- "image/png",
306
- "image/webp",
307
- "application/pdf",
308
- ] as const;
309
- type AllowedContentType = (typeof allowedTypes)[number];
310
-
311
- if (!allowedTypes.includes(contentType as AllowedContentType)) {
312
- throw new UploadError(
313
- `Invalid file type: ${contentType}. Allowed types: ${allowedTypes.join(", ")}`,
314
- "request"
315
- );
316
- }
317
-
318
- // Step 1: Create document and get presigned URL
319
- const createInput: CreateDocumentInput = {
320
- ...data,
321
- contentType: contentType as AllowedContentType,
322
- };
323
-
324
- const { document, upload } = await this.create(createInput);
325
-
326
- // upload should always be present when contentType is provided
327
- if (!upload) {
328
- throw new UploadError(
329
- "Server did not return upload URL. This should not happen.",
330
- "request"
331
- );
332
- }
333
-
334
- // Step 2: Upload file to S3
335
- // Note: If upload fails, the document remains in pending_upload status
336
- // and will be cleaned up by the orphan document cleanup job
337
- await uploadToS3(normalizedFile, upload, {
338
- onProgress,
339
- signal,
340
- timeout,
341
- });
342
-
343
- // Step 3: Confirm upload
344
- return this.confirmUpload({
345
- id: document.id,
346
- filename: normalizedFile.name,
347
- contentType,
348
- size: normalizedFile.size,
349
- });
350
- }
351
-
352
- /**
353
- * Gets a presigned URL for downloading/viewing a document.
354
- *
355
- * The URL allows direct access to the file in storage without going through the backend.
356
- * Use this for displaying documents in the UI or downloading them.
357
- *
358
- * @param id - Document ID
359
- * @returns Presigned download URL with expiration
360
- * @throws If document doesn't exist, is in pending_upload status, or has no storage path
361
- *
362
- * @example
363
- * ```typescript
364
- * // Get download URL for viewing
365
- * const { url, expiresAt } = await compliance.documents.getDownloadUrl(docId);
366
- *
367
- * // Display in browser
368
- * window.open(url, '_blank');
369
- *
370
- * // Or use in an img tag
371
- * <img src={url} alt="Document" />
372
- * ```
373
- */
374
- async getDownloadUrl(id: string): Promise<PresignedDownloadUrl> {
375
- const response = await this.execute(() =>
376
- this.trpc.documents.getDownloadUrl.query({ id })
377
- );
378
-
379
- return response;
380
- }
381
-
382
- // ─────────────────────────────────────────────────────────────────
383
- // Retrieve
384
- // ─────────────────────────────────────────────────────────────────
385
-
386
- /**
387
- * Retrieves a document by ID
388
- *
389
- * @param id - Document ID
390
- * @returns The document
391
- */
392
- async retrieve(id: string): Promise<Document> {
393
- const response = await this.execute(() =>
394
- this.trpc.documents.retrieve.query({ id })
395
- );
396
- return this.instantiate(response as DocumentData);
397
- }
398
-
399
- /**
400
- * Retrieves a document by metadata key/value.
401
- */
402
- async retrieveByMetadata(key: string, value: string): Promise<Document> {
403
- const response = await this.execute(() =>
404
- this.trpc.documents.retrieveByMetadata.query({ key, value })
405
- );
406
- return this.instantiate(response as DocumentData);
407
- }
408
-
409
- /**
410
- * Retrieves a document by externalId
411
- */
412
- async retrieveByExternalId(externalId: string): Promise<Document> {
413
- const response = await this.execute(() =>
414
- this.trpc.documents.retrieveByExternalId.query({ externalId })
415
- );
416
- return this.instantiate(response as DocumentData);
417
- }
418
-
419
- // ─────────────────────────────────────────────────────────────────
420
- // Update
421
- // ─────────────────────────────────────────────────────────────────
422
-
423
- /**
424
- * Updates a document's metadata, expiration, or fields.
425
- *
426
- * Note: Status cannot be updated directly - it's managed by the system.
427
- *
428
- * @param id - Document ID
429
- * @param data - Fields to update
430
- * @returns The updated document
431
- */
432
- async update(id: string, data: UpdateDocumentInput): Promise<Document> {
433
- const validated = this.validate(updateDocumentInputSchema, data);
434
-
435
- const response = await this.execute(() =>
436
- this.trpc.documents.update.mutate({
437
- id,
438
- data: validated,
439
- })
440
- );
441
- return this.instantiate(response as DocumentData);
442
- }
443
-
444
- // ─────────────────────────────────────────────────────────────────
445
- // Delete
446
- // ─────────────────────────────────────────────────────────────────
447
-
448
- /**
449
- * Soft-deletes a document
450
- *
451
- * @param id - Document ID
452
- */
453
- async delete(id: string): Promise<void> {
454
- await this.execute(() => this.trpc.documents.delete.mutate({ id }));
455
- }
456
-
457
- // ─────────────────────────────────────────────────────────────────
458
- // List
459
- // ─────────────────────────────────────────────────────────────────
460
-
461
- /**
462
- * Lists documents with optional filtering and pagination.
463
- *
464
- * @param options - List options with filter, cursor, and limit
465
- * @returns Paginated list of documents
466
- */
467
- async list(
468
- options?: ListOptions<DocumentFilter, DocumentSort>
469
- ): Promise<PaginatedResponse<Document>> {
470
- const response = await this.execute(() =>
471
- this.trpc.documents.list.query(options)
472
- );
473
-
474
- const typedResponse = response as {
475
- data: DocumentData[];
476
- pagination: { nextCursor?: string; hasMore: boolean };
477
- };
478
-
479
- return {
480
- data: typedResponse.data.map((d) => this.instantiate(d)),
481
- pagination: typedResponse.pagination,
482
- };
483
- }
484
-
485
- /**
486
- * Lists all documents for a specific client with pagination.
487
- *
488
- * @param clientId - Client ID
489
- * @param options - Pagination options
490
- * @returns Paginated list of documents
491
- */
492
- async listByClient(
493
- clientId: string,
494
- options?: { cursor?: string; limit?: number }
495
- ): Promise<PaginatedResponse<Document>> {
496
- const response = await this.execute(() =>
497
- this.trpc.documents.listByClient.query({ clientId, ...options })
498
- );
499
-
500
- const typedResponse = response as {
501
- data: DocumentData[];
502
- pagination: { nextCursor?: string; hasMore: boolean };
503
- };
504
-
505
- return {
506
- data: typedResponse.data.map((d) => this.instantiate(d)),
507
- pagination: typedResponse.pagination,
508
- };
509
- }
510
-
511
- /**
512
- * Async iterator for documents with optional filtering.
513
- *
514
- * @param options - Filter options
515
- * @yields Documents one at a time
516
- */
517
- async *iterate(
518
- options?: ListOptions<DocumentFilter, DocumentSort>
519
- ): AsyncGenerator<Document, void, unknown> {
520
- let cursor: string | undefined;
521
- let hasMore = true;
522
-
523
- while (hasMore) {
524
- const response = await this.list({
525
- ...options,
526
- cursor,
527
- limit: options?.limit ?? 100,
528
- });
529
-
530
- for (const doc of response.data) {
531
- yield doc;
532
- }
533
-
534
- cursor = response.pagination.nextCursor;
535
- hasMore = response.pagination.hasMore;
536
- }
537
- }
538
-
539
- // ─────────────────────────────────────────────────────────────────
540
- // Batch Operations
541
- // ─────────────────────────────────────────────────────────────────
542
-
543
- /**
544
- * Creates multiple document records in a batch.
545
- *
546
- * @param documents - Array of document creation data
547
- * @param options - Batch options (atomic: true by default)
548
- * @returns Atomic result with data array, or partial result with succeeded/failed
549
- */
550
- async createMany(
551
- documents: CreateDocumentInput[],
552
- options?: { atomic?: true }
553
- ): Promise<AtomicBatchResult<Document>>;
554
- async createMany(
555
- documents: CreateDocumentInput[],
556
- options: { atomic: false }
557
- ): Promise<PartialBatchResult<Document>>;
558
- async createMany(
559
- documents: CreateDocumentInput[],
560
- options: BatchOptions = {}
561
- ): Promise<AtomicBatchResult<Document> | PartialBatchResult<Document>> {
562
- const atomic = options.atomic ?? true;
563
- const validated = this.validate(
564
- z.array(createDocumentInputSchema),
565
- documents
566
- );
567
-
568
- const response = await this.execute(() =>
569
- this.trpc.documents.createMany.mutate({ items: validated, atomic })
570
- );
571
-
572
- if (response.atomic) {
573
- return {
574
- atomic: true,
575
- data: response.data.map((d) => this.instantiate(d as DocumentData)),
576
- };
577
- } else {
578
- return {
579
- atomic: false,
580
- succeeded: response.succeeded.map((item) => ({
581
- index: item.index,
582
- data: this.instantiate(item.data as DocumentData),
583
- })),
584
- failed: response.failed,
585
- };
586
- }
587
- }
588
-
589
- /**
590
- * Updates multiple documents in a batch.
591
- *
592
- * @param updates - Array of { id, data } objects
593
- * @param options - Batch options (atomic: true by default)
594
- * @returns Atomic result with data array, or partial result with succeeded/failed
595
- */
596
- async updateMany(
597
- updates: { id: string; data: UpdateDocumentInput }[],
598
- options?: { atomic?: true }
599
- ): Promise<AtomicBatchResult<Document>>;
600
- async updateMany(
601
- updates: { id: string; data: UpdateDocumentInput }[],
602
- options: { atomic: false }
603
- ): Promise<PartialBatchResult<Document>>;
604
- async updateMany(
605
- updates: { id: string; data: UpdateDocumentInput }[],
606
- options: BatchOptions = {}
607
- ): Promise<AtomicBatchResult<Document> | PartialBatchResult<Document>> {
608
- const atomic = options.atomic ?? true;
609
- const validated = updates.map((u) => ({
610
- id: u.id,
611
- data: this.validate(updateDocumentInputSchema, u.data),
612
- }));
613
-
614
- const response = await this.execute(() =>
615
- this.trpc.documents.updateMany.mutate({ items: validated, atomic })
616
- );
617
-
618
- if (response.atomic) {
619
- return {
620
- atomic: true,
621
- data: response.data.map((d) => this.instantiate(d as DocumentData)),
622
- };
623
- } else {
624
- return {
625
- atomic: false,
626
- succeeded: response.succeeded.map((item) => ({
627
- index: item.index,
628
- data: this.instantiate(item.data as DocumentData),
629
- })),
630
- failed: response.failed,
631
- };
632
- }
633
- }
634
-
635
- // ─────────────────────────────────────────────────────────────────
636
- // Helpers
637
- // ─────────────────────────────────────────────────────────────────
638
-
639
- /**
640
- * Gets document count for a client
641
- *
642
- * @param clientId - Client ID
643
- * @returns Count of documents
644
- */
645
- async countByClient(clientId: string): Promise<number> {
646
- const response = await this.listByClient(clientId, { limit: 100 });
647
- // For accurate count, we'd need a dedicated endpoint
648
- // This is an approximation
649
- let count = response.data.length;
650
- let cursor = response.pagination.nextCursor;
651
-
652
- while (response.pagination.hasMore && cursor) {
653
- const next = await this.listByClient(clientId, { cursor, limit: 100 });
654
- count += next.data.length;
655
- cursor = next.pagination.nextCursor;
656
- if (!next.pagination.hasMore) break;
657
- }
658
-
659
- return count;
660
- }
661
-
662
- /**
663
- * Checks if a document with the given type exists for a client
664
- *
665
- * @param clientId - Client ID
666
- * @param type - Document type
667
- * @returns True if document exists and is available
668
- */
669
- async hasDocumentType(clientId: string, type: string): Promise<boolean> {
670
- const response = await this.list({
671
- filter: { clientId, type },
672
- limit: 1,
673
- });
674
- return response.data.some((d) => d.type === type && d.isAvailable);
675
- }
676
-
677
- // ─────────────────────────────────────────────────────────────────
678
- // Protected Methods
679
- // ─────────────────────────────────────────────────────────────────
680
-
681
- /**
682
- * Instantiates a Document model from raw data.
683
- * Override in subclasses to return jurisdiction-specific models.
684
- */
685
- protected instantiate(data: DocumentData): Document {
686
- return new Document(data);
687
- }
688
- }