@opengeni/documents 0.6.9-canary.0 → 0.7.0-canary.0

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/dist/index.d.ts CHANGED
@@ -117,6 +117,14 @@ export type RunDocumentDefaultCollectionBackfillInput = RunDocumentDefaultCollec
117
117
  actorSubjectId: string;
118
118
  accountAdminAuthorization: DocumentAccountAdminAuthorization;
119
119
  };
120
+ export type DocumentMigrationAuditInput = {
121
+ accountId: string;
122
+ workspaceId: string;
123
+ actorSubjectId: string;
124
+ accountAdminAuthorization: DocumentAccountAdminAuthorization;
125
+ limit?: number | undefined;
126
+ cursor?: string | undefined;
127
+ };
120
128
  export type AgentDocumentAuthorityContext = {
121
129
  sessionId: string;
122
130
  attemptId: string;
@@ -321,6 +329,87 @@ export declare function runDocumentDefaultCollectionBackfill(db: Database, input
321
329
  adoptedCount: number;
322
330
  completedAt: string | null;
323
331
  }>;
332
+ export declare function listDocumentDefaultCollectionBackfillRuns(db: Database, input: DocumentMigrationAuditInput): Promise<{
333
+ runs: {
334
+ runId: string;
335
+ status: "completed" | "running";
336
+ lastWorkspaceId: string | null;
337
+ processedCount: number;
338
+ createdCount: number;
339
+ adoptedCount: number;
340
+ completedAt: string | null;
341
+ actorSubjectId: string;
342
+ startedAt: string;
343
+ updatedAt: string;
344
+ }[];
345
+ hasMore: boolean;
346
+ nextCursor: string | null;
347
+ }>;
348
+ export declare function getDocumentDefaultCollectionBackfillAudit(db: Database, input: DocumentMigrationAuditInput & {
349
+ runId: string;
350
+ operationCursor?: string | undefined;
351
+ receiptCursor?: string | undefined;
352
+ }): Promise<{
353
+ run: {
354
+ runId: string;
355
+ status: "completed" | "running";
356
+ lastWorkspaceId: string | null;
357
+ processedCount: number;
358
+ createdCount: number;
359
+ adoptedCount: number;
360
+ completedAt: string | null;
361
+ actorSubjectId: string;
362
+ startedAt: string;
363
+ updatedAt: string;
364
+ };
365
+ operations: {
366
+ operationId: string;
367
+ result: {
368
+ runId: string;
369
+ operationId: string;
370
+ status: "completed" | "running";
371
+ lastWorkspaceId: string | null;
372
+ processedCount: number;
373
+ createdCount: number;
374
+ adoptedCount: number;
375
+ completedAt: string | null;
376
+ };
377
+ createdAt: string;
378
+ }[];
379
+ receipts: {
380
+ workspaceId: string;
381
+ baseId: string;
382
+ outcome: "adopted" | "created";
383
+ createdAt: string;
384
+ }[];
385
+ operationsHasMore: boolean;
386
+ operationsNextCursor: string | null;
387
+ receiptsHasMore: boolean;
388
+ receiptsNextCursor: string | null;
389
+ }>;
390
+ export declare function listOrganizationDocumentAuthorityReclassifications(db: Database, input: DocumentMigrationAuditInput): Promise<{
391
+ receipts: {
392
+ operationId: string;
393
+ documentId: string;
394
+ previousAuthority: {
395
+ kind: "organization" | "personal" | "workspace";
396
+ workspaceId: string | null;
397
+ subjectId: string | null;
398
+ authorityId: string | null;
399
+ };
400
+ authority: {
401
+ kind: "organization" | "personal" | "workspace";
402
+ workspaceId: string | null;
403
+ subjectId: string | null;
404
+ authorityId: string | null;
405
+ };
406
+ createdAt: string;
407
+ actorSubjectId: string;
408
+ requestWorkspaceId: string;
409
+ }[];
410
+ hasMore: boolean;
411
+ nextCursor: string | null;
412
+ }>;
324
413
  export declare function reclassifyDocumentAuthority(db: Database, input: ReclassifyDocumentAuthorityInput): Promise<{
325
414
  operationId: string;
326
415
  documentId: string;
package/dist/index.js CHANGED
@@ -3,7 +3,14 @@ import {
3
3
  KnowledgeProviderCitation,
4
4
  DocumentAuthorityReclassification,
5
5
  ListDocumentAuthorityReclassificationsResponse,
6
- DocumentDefaultCollectionBackfill
6
+ DocumentDefaultCollectionBackfill,
7
+ DocumentDefaultCollectionBackfillAudit,
8
+ DocumentDefaultCollectionBackfillOperationAudit,
9
+ DocumentDefaultCollectionBackfillReceiptAudit,
10
+ DocumentDefaultCollectionBackfillRunAudit,
11
+ ListDocumentDefaultCollectionBackfillRunsResponse,
12
+ ListOrganizationDocumentAuthorityReclassificationsResponse,
13
+ OrganizationDocumentAuthorityReclassification
7
14
  } from "@opengeni/contracts";
8
15
  import {
9
16
  createPersonalDocumentAuthority,
@@ -740,6 +747,195 @@ async function runDocumentDefaultCollectionBackfill(db, input) {
740
747
  }
741
748
  );
742
749
  }
750
+ async function listDocumentDefaultCollectionBackfillRuns(db, input) {
751
+ const limit = validateDocumentMigrationAuditLimit(input.limit);
752
+ const cursor = input.cursor ? decodeDocumentMigrationAuditCursor(input.cursor, ["backfill-runs", input.accountId], true) : null;
753
+ return await withDocumentAccountRls(
754
+ db,
755
+ input.accountId,
756
+ input.workspaceId,
757
+ { viewerSubjectId: input.actorSubjectId },
758
+ async (scopedDb) => {
759
+ const command = {
760
+ accountId: input.accountId,
761
+ workspaceId: input.workspaceId,
762
+ actorSubjectId: input.actorSubjectId,
763
+ accountAdminAuthorization: input.accountAdminAuthorization,
764
+ limit: limit + 1,
765
+ beforeStartedAt: cursor?.timestamp ?? null,
766
+ beforeRunId: cursor?.id ?? null
767
+ };
768
+ const rows = await scopedDb.execute(sql`
769
+ SELECT list_document_default_collection_backfill_runs(
770
+ ${JSON.stringify(command)}::jsonb
771
+ ) AS result
772
+ `);
773
+ const parsed = rows.map((row) => DocumentDefaultCollectionBackfillRunAudit.parse(row.result));
774
+ const hasMore = parsed.length > limit;
775
+ const runs = parsed.slice(0, limit);
776
+ const tail = hasMore ? runs.at(-1) : null;
777
+ return ListDocumentDefaultCollectionBackfillRunsResponse.parse({
778
+ runs,
779
+ hasMore,
780
+ nextCursor: tail ? encodeDocumentMigrationAuditCursor(["backfill-runs", input.accountId], {
781
+ timestamp: tail.startedAt,
782
+ id: tail.runId
783
+ }) : null
784
+ });
785
+ }
786
+ );
787
+ }
788
+ async function getDocumentDefaultCollectionBackfillAudit(db, input) {
789
+ const limit = validateDocumentMigrationAuditLimit(input.limit);
790
+ const operationCursor = input.operationCursor ? decodeDocumentMigrationAuditCursor(
791
+ input.operationCursor,
792
+ ["backfill-operations", input.accountId, input.runId],
793
+ true
794
+ ) : null;
795
+ const receiptCursor = input.receiptCursor ? decodeDocumentMigrationAuditCursor(
796
+ input.receiptCursor,
797
+ ["backfill-receipts", input.accountId, input.runId],
798
+ false
799
+ ) : null;
800
+ return await withDocumentAccountRls(
801
+ db,
802
+ input.accountId,
803
+ input.workspaceId,
804
+ { viewerSubjectId: input.actorSubjectId },
805
+ async (scopedDb) => {
806
+ const command = {
807
+ accountId: input.accountId,
808
+ workspaceId: input.workspaceId,
809
+ actorSubjectId: input.actorSubjectId,
810
+ accountAdminAuthorization: input.accountAdminAuthorization,
811
+ runId: input.runId,
812
+ limit,
813
+ operationBeforeCreatedAt: operationCursor?.timestamp ?? null,
814
+ operationBeforeId: operationCursor?.id ?? null,
815
+ receiptAfterWorkspaceId: receiptCursor?.id ?? null
816
+ };
817
+ const rows = await scopedDb.execute(sql`
818
+ SELECT get_document_default_collection_backfill_audit(
819
+ ${JSON.stringify(command)}::jsonb
820
+ ) AS result
821
+ `);
822
+ const raw = rows[0]?.result;
823
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
824
+ throw new Error("document Default collection backfill audit returned no result");
825
+ }
826
+ const value = raw;
827
+ const run = DocumentDefaultCollectionBackfillRunAudit.parse(value.run);
828
+ const operations = Array.isArray(value.operations) ? value.operations.map(
829
+ (operation) => DocumentDefaultCollectionBackfillOperationAudit.parse(operation)
830
+ ) : [];
831
+ const receipts = Array.isArray(value.receipts) ? value.receipts.map(
832
+ (receipt) => DocumentDefaultCollectionBackfillReceiptAudit.parse(receipt)
833
+ ) : [];
834
+ const operationsHasMore = operations.length > limit;
835
+ const receiptsHasMore = receipts.length > limit;
836
+ const operationPage = operations.slice(0, limit);
837
+ const receiptPage = receipts.slice(0, limit);
838
+ const operationTail = operationsHasMore ? operationPage.at(-1) : null;
839
+ const receiptTail = receiptsHasMore ? receiptPage.at(-1) : null;
840
+ return DocumentDefaultCollectionBackfillAudit.parse({
841
+ run,
842
+ operations: operationPage,
843
+ receipts: receiptPage,
844
+ operationsHasMore,
845
+ operationsNextCursor: operationTail ? encodeDocumentMigrationAuditCursor(
846
+ ["backfill-operations", input.accountId, input.runId],
847
+ { timestamp: operationTail.createdAt, id: operationTail.operationId }
848
+ ) : null,
849
+ receiptsHasMore,
850
+ receiptsNextCursor: receiptTail ? encodeDocumentMigrationAuditCursor(
851
+ ["backfill-receipts", input.accountId, input.runId],
852
+ { timestamp: null, id: receiptTail.workspaceId }
853
+ ) : null
854
+ });
855
+ }
856
+ );
857
+ }
858
+ async function listOrganizationDocumentAuthorityReclassifications(db, input) {
859
+ const limit = validateDocumentMigrationAuditLimit(input.limit);
860
+ const cursor = input.cursor ? decodeDocumentMigrationAuditCursor(
861
+ input.cursor,
862
+ ["organization-reclassifications", input.accountId],
863
+ true
864
+ ) : null;
865
+ return await withDocumentAccountRls(
866
+ db,
867
+ input.accountId,
868
+ input.workspaceId,
869
+ { viewerSubjectId: input.actorSubjectId },
870
+ async (scopedDb) => {
871
+ const command = {
872
+ accountId: input.accountId,
873
+ workspaceId: input.workspaceId,
874
+ actorSubjectId: input.actorSubjectId,
875
+ accountAdminAuthorization: input.accountAdminAuthorization,
876
+ limit: limit + 1,
877
+ beforeCreatedAt: cursor?.timestamp ?? null,
878
+ beforeOperationId: cursor?.id ?? null
879
+ };
880
+ const rows = await scopedDb.execute(sql`
881
+ SELECT list_organization_document_authority_reclassifications(
882
+ ${JSON.stringify(command)}::jsonb
883
+ ) AS result
884
+ `);
885
+ const parsed = rows.map(
886
+ (row) => OrganizationDocumentAuthorityReclassification.parse(row.result)
887
+ );
888
+ const hasMore = parsed.length > limit;
889
+ const receipts = parsed.slice(0, limit);
890
+ const tail = hasMore ? receipts.at(-1) : null;
891
+ return ListOrganizationDocumentAuthorityReclassificationsResponse.parse({
892
+ receipts,
893
+ hasMore,
894
+ nextCursor: tail ? encodeDocumentMigrationAuditCursor(
895
+ ["organization-reclassifications", input.accountId],
896
+ { timestamp: tail.createdAt, id: tail.operationId }
897
+ ) : null
898
+ });
899
+ }
900
+ );
901
+ }
902
+ function validateDocumentMigrationAuditLimit(value) {
903
+ const limit = value ?? 50;
904
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
905
+ throw new Error("document migration audit limit must be between 1 and 100");
906
+ }
907
+ return limit;
908
+ }
909
+ function documentMigrationAuditCursorScope(parts) {
910
+ return createHash("sha256").update(JSON.stringify(["document-migration-audit", 1, ...parts]), "utf8").digest("hex").slice(0, 32);
911
+ }
912
+ function encodeDocumentMigrationAuditCursor(parts, cursor) {
913
+ return Buffer.from(
914
+ JSON.stringify({
915
+ v: 1,
916
+ s: documentMigrationAuditCursorScope(parts),
917
+ t: cursor.timestamp,
918
+ i: cursor.id
919
+ }),
920
+ "utf8"
921
+ ).toString("base64url");
922
+ }
923
+ function decodeDocumentMigrationAuditCursor(value, parts, requireTimestamp) {
924
+ try {
925
+ if (!value || value.length > 1024) throw new Error("cursor length");
926
+ const bytes = Buffer.from(value, "base64url");
927
+ if (bytes.toString("base64url") !== value) throw new Error("cursor encoding");
928
+ const parsed = JSON.parse(bytes.toString("utf8"));
929
+ if (Object.keys(parsed).sort().join(",") !== "i,s,t,v" || parsed.v !== 1 || parsed.s !== documentMigrationAuditCursorScope(parts) || typeof parsed.i !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(
930
+ parsed.i
931
+ ) || (requireTimestamp ? typeof parsed.t !== "string" || !Number.isFinite(new Date(parsed.t).getTime()) : parsed.t !== null)) {
932
+ throw new Error("cursor payload");
933
+ }
934
+ return { timestamp: typeof parsed.t === "string" ? parsed.t : null, id: parsed.i };
935
+ } catch (error) {
936
+ throw new Error("invalid document migration audit cursor", { cause: error });
937
+ }
938
+ }
743
939
  async function reclassifyDocumentAuthority(db, input) {
744
940
  return await withDocumentAccountRls(
745
941
  db,
@@ -2909,6 +3105,7 @@ export {
2909
3105
  getDocument,
2910
3106
  getDocumentBase,
2911
3107
  getDocumentChunk,
3108
+ getDocumentDefaultCollectionBackfillAudit,
2912
3109
  getDocumentForIndexing,
2913
3110
  getDocumentInventory,
2914
3111
  getDocumentOriginalFile,
@@ -2919,8 +3116,10 @@ export {
2919
3116
  listDocumentAuthorityReclassifications,
2920
3117
  listDocumentBases,
2921
3118
  listDocumentBasesEnsuringDefault,
3119
+ listDocumentDefaultCollectionBackfillRuns,
2922
3120
  listDocuments,
2923
3121
  listEffectiveIndexedDocuments,
3122
+ listOrganizationDocumentAuthorityReclassifications,
2924
3123
  moveDocumentToBase,
2925
3124
  parseCurationOutcome,
2926
3125
  parseDocumentBytes,