@trycompai/db 1.3.18 → 1.3.19

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.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,eAAO,MAAM,EAAE,gIAA+C,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,eAAO,MAAM,EAAE,gIAIX,CAAC"}
package/dist/client.js CHANGED
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.db = void 0;
4
4
  const client_1 = require("@prisma/client");
5
5
  const globalForPrisma = global;
6
- exports.db = globalForPrisma.prisma || new client_1.PrismaClient();
6
+ exports.db = globalForPrisma.prisma ||
7
+ new client_1.PrismaClient({
8
+ datasourceUrl: process.env.DATABASE_URL,
9
+ });
7
10
  if (process.env.NODE_ENV !== 'production')
8
11
  globalForPrisma.prisma = exports.db;
@@ -7,7 +7,6 @@ generator client {
7
7
  datasource db {
8
8
  provider = "postgresql"
9
9
  url = env("DATABASE_URL")
10
- directUrl = env("DATABASE_URL")
11
10
  extensions = [pgcrypto]
12
11
  }
13
12
 
@@ -53,14 +52,17 @@ enum AttachmentType {
53
52
 
54
53
  // ===== auth.prisma =====
55
54
  model User {
56
- id String @id @default(dbgenerated("generate_prefixed_cuid('usr'::text)"))
57
- name String
58
- email String
59
- emailVerified Boolean
60
- image String?
61
- createdAt DateTime @default(now())
62
- updatedAt DateTime @updatedAt
63
- lastLogin DateTime?
55
+ id String @id @default(dbgenerated("generate_prefixed_cuid('usr'::text)"))
56
+ name String
57
+ email String
58
+ emailVerified Boolean
59
+ image String?
60
+ createdAt DateTime @default(now())
61
+ updatedAt DateTime @updatedAt
62
+ lastLogin DateTime?
63
+ emailNotificationsUnsubscribed Boolean @default(false)
64
+ emailPreferences Json? @default("{\"policyNotifications\":true,\"taskReminders\":true,\"weeklyTaskDigest\":true,\"unassignedItemsNotifications\":true}")
65
+ isPlatformAdmin Boolean @default(false)
64
66
 
65
67
  accounts Account[]
66
68
  auditLog AuditLog[]
@@ -147,12 +149,13 @@ model Member {
147
149
 
148
150
  department Departments @default(none)
149
151
  isActive Boolean @default(true)
150
- deactivated Boolean @default(false)
152
+ deactivated Boolean @default(false)
151
153
  employeeTrainingVideoCompletion EmployeeTrainingVideoCompletion[]
152
154
  fleetDmLabelId Int?
153
155
 
154
156
  assignedPolicies Policy[] @relation("PolicyAssignee") // Policies where this member is an assignee
155
157
  approvedPolicies Policy[] @relation("PolicyApprover") // Policies where this member is an approver
158
+ approvedSOADocuments SOADocument[] @relation("SOADocumentApprover") // SOA documents where this member is an approver
156
159
  risks Risk[]
157
160
  tasks Task[]
158
161
  vendors Vendor[]
@@ -173,16 +176,17 @@ model Invitation {
173
176
  expiresAt DateTime
174
177
  inviterId String
175
178
  user User @relation(fields: [inviterId], references: [id], onDelete: Cascade)
179
+ createdAt DateTime @default(now())
176
180
  }
177
181
 
178
182
  // This is only for the app to consume, shouldn't be enforced by DB
179
183
  // Otherwise it won't work with Better Auth, as per https://www.better-auth.com/docs/plugins/organization#access-control
180
184
  enum Role {
181
- owner
182
- admin
183
- auditor
184
- employee
185
- contractor
185
+ owner
186
+ admin
187
+ auditor
188
+ employee
189
+ contractor
186
190
  }
187
191
 
188
192
  enum PolicyStatus {
@@ -392,6 +396,8 @@ model FrameworkEditorFramework {
392
396
 
393
397
  requirements FrameworkEditorRequirement[]
394
398
  frameworkInstances FrameworkInstance[]
399
+ soaConfigurations SOAFrameworkConfiguration[] // Multiple SOA config versions per framework
400
+ soaDocuments SOADocument[] // SOA documents from organizations
395
401
 
396
402
  // Dates
397
403
  createdAt DateTime @default(now())
@@ -485,6 +491,424 @@ model FrameworkInstance {
485
491
  }
486
492
 
487
493
 
494
+ // ===== integration-platform.prisma =====
495
+ // ===== Integration Platform =====
496
+ // New integration platform models for scalable, config-driven integrations
497
+
498
+ /// Stores metadata about available integration providers (synced from code manifests)
499
+ model IntegrationProvider {
500
+ id String @id @default(dbgenerated("generate_prefixed_cuid('prv'::text)"))
501
+ /// Unique slug matching manifest ID (e.g., "github", "slack")
502
+ slug String @unique
503
+ /// Display name
504
+ name String
505
+ /// Category for grouping
506
+ category String
507
+ /// Hash of manifest for detecting changes
508
+ manifestHash String?
509
+ /// Capabilities JSON array
510
+ capabilities Json @default("[]")
511
+ /// Whether provider is active
512
+ isActive Boolean @default(true)
513
+
514
+ createdAt DateTime @default(now())
515
+ updatedAt DateTime @updatedAt
516
+
517
+ connections IntegrationConnection[]
518
+
519
+ @@index([slug])
520
+ @@index([category])
521
+ }
522
+
523
+ /// Represents an organization's connection to an integration provider
524
+ model IntegrationConnection {
525
+ id String @id @default(dbgenerated("generate_prefixed_cuid('icn'::text)"))
526
+
527
+ /// Reference to the provider
528
+ providerId String
529
+ provider IntegrationProvider @relation(fields: [providerId], references: [id], onDelete: Cascade)
530
+
531
+ /// Organization that owns this connection
532
+ organizationId String
533
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
534
+
535
+ /// Connection status
536
+ status IntegrationConnectionStatus @default(pending)
537
+
538
+ /// Auth strategy used (oauth2, api_key, basic, jwt, custom)
539
+ authStrategy String
540
+
541
+ /// Reference to active credential version
542
+ activeCredentialVersionId String?
543
+
544
+ /// Last successful sync timestamp
545
+ lastSyncAt DateTime?
546
+
547
+ /// Next scheduled sync timestamp
548
+ nextSyncAt DateTime?
549
+
550
+ /// Custom sync cadence (cron expression), null = use default
551
+ syncCadence String?
552
+
553
+ /// Additional metadata (e.g., connected account info)
554
+ metadata Json?
555
+
556
+ /// User-configured variables for checks (collected after OAuth)
557
+ variables Json?
558
+
559
+ /// Error message if status is error
560
+ errorMessage String?
561
+
562
+ createdAt DateTime @default(now())
563
+ updatedAt DateTime @updatedAt
564
+
565
+ credentialVersions IntegrationCredentialVersion[]
566
+ runs IntegrationRun[]
567
+ findings IntegrationPlatformFinding[]
568
+ checkRuns IntegrationCheckRun[]
569
+
570
+ @@unique([providerId, organizationId])
571
+ @@index([organizationId])
572
+ @@index([providerId])
573
+ @@index([status])
574
+ }
575
+
576
+ enum IntegrationConnectionStatus {
577
+ pending // Awaiting credential setup
578
+ active // Connected and operational
579
+ error // Connection has errors
580
+ paused // Manually paused by user
581
+ disconnected // User disconnected
582
+ }
583
+
584
+ /// Stores encrypted credentials with versioning for audit trail
585
+ model IntegrationCredentialVersion {
586
+ id String @id @default(dbgenerated("generate_prefixed_cuid('icv'::text)"))
587
+
588
+ /// Parent connection
589
+ connectionId String
590
+ connection IntegrationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
591
+
592
+ /// Encrypted credential payload (JSON with encrypted fields)
593
+ encryptedPayload Json
594
+
595
+ /// Version number (auto-increment per connection)
596
+ version Int
597
+
598
+ /// Token expiration (for OAuth tokens)
599
+ expiresAt DateTime?
600
+
601
+ /// When this version was rotated/replaced
602
+ rotatedAt DateTime?
603
+
604
+ createdAt DateTime @default(now())
605
+
606
+ @@unique([connectionId, version])
607
+ @@index([connectionId])
608
+ }
609
+
610
+ /// Records each sync/job execution for audit and debugging
611
+ model IntegrationRun {
612
+ id String @id @default(dbgenerated("generate_prefixed_cuid('irn'::text)"))
613
+
614
+ /// Parent connection
615
+ connectionId String
616
+ connection IntegrationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
617
+
618
+ /// Type of job
619
+ jobType IntegrationRunJobType
620
+
621
+ /// Execution status
622
+ status IntegrationRunStatus @default(pending)
623
+
624
+ /// Timestamps
625
+ startedAt DateTime?
626
+ completedAt DateTime?
627
+
628
+ /// Duration in milliseconds
629
+ durationMs Int?
630
+
631
+ /// Number of findings from this run
632
+ findingsCount Int @default(0)
633
+
634
+ /// Error details if failed
635
+ error Json?
636
+
637
+ /// Additional metadata (trigger source, cursor, etc.)
638
+ metadata Json?
639
+
640
+ createdAt DateTime @default(now())
641
+
642
+ findings IntegrationPlatformFinding[]
643
+
644
+ @@index([connectionId])
645
+ @@index([status])
646
+ @@index([createdAt])
647
+ }
648
+
649
+ enum IntegrationRunJobType {
650
+ full_sync
651
+ delta_sync
652
+ webhook
653
+ manual
654
+ test_connection
655
+ }
656
+
657
+ enum IntegrationRunStatus {
658
+ pending
659
+ running
660
+ success
661
+ failed
662
+ cancelled
663
+ }
664
+
665
+ /// Stores findings/results from integration syncs
666
+ model IntegrationPlatformFinding {
667
+ id String @id @default(dbgenerated("generate_prefixed_cuid('ipf'::text)"))
668
+
669
+ /// Parent run (optional - webhooks may not have runs)
670
+ runId String?
671
+ run IntegrationRun? @relation(fields: [runId], references: [id], onDelete: SetNull)
672
+
673
+ /// Parent connection
674
+ connectionId String
675
+ connection IntegrationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
676
+
677
+ /// Resource classification
678
+ resourceType String
679
+ resourceId String
680
+
681
+ /// Finding details
682
+ title String
683
+ description String?
684
+
685
+ /// Severity level
686
+ severity IntegrationFindingSeverity @default(info)
687
+
688
+ /// Finding status
689
+ status IntegrationFindingStatus @default(open)
690
+
691
+ /// Remediation guidance
692
+ remediation String?
693
+
694
+ /// Raw payload from provider
695
+ rawPayload Json?
696
+
697
+ createdAt DateTime @default(now())
698
+ updatedAt DateTime @updatedAt
699
+
700
+ @@index([connectionId])
701
+ @@index([runId])
702
+ @@index([resourceType, resourceId])
703
+ @@index([severity])
704
+ @@index([status])
705
+ }
706
+
707
+ enum IntegrationFindingSeverity {
708
+ info
709
+ low
710
+ medium
711
+ high
712
+ critical
713
+ }
714
+
715
+ enum IntegrationFindingStatus {
716
+ open
717
+ resolved
718
+ ignored
719
+ }
720
+
721
+ /// Stores OAuth state for CSRF protection during OAuth flow
722
+ model IntegrationOAuthState {
723
+ id String @id @default(dbgenerated("generate_prefixed_cuid('ios'::text)"))
724
+
725
+ /// Random state parameter
726
+ state String @unique
727
+
728
+ /// Provider slug
729
+ providerSlug String
730
+
731
+ /// Organization initiating the OAuth
732
+ organizationId String
733
+
734
+ /// User initiating the OAuth
735
+ userId String
736
+
737
+ /// PKCE code verifier (if using PKCE)
738
+ codeVerifier String?
739
+
740
+ /// Redirect URL after OAuth completes
741
+ redirectUrl String?
742
+
743
+ /// Expiration timestamp
744
+ expiresAt DateTime
745
+
746
+ createdAt DateTime @default(now())
747
+
748
+ @@index([state])
749
+ @@index([expiresAt])
750
+ }
751
+
752
+ /// Stores organization-level OAuth app credentials
753
+ /// Allows orgs (especially self-hosters) to use their own OAuth apps
754
+ model IntegrationOAuthApp {
755
+ id String @id @default(dbgenerated("generate_prefixed_cuid('ioa'::text)"))
756
+
757
+ /// Provider slug (e.g., "github", "slack")
758
+ providerSlug String
759
+
760
+ /// Organization that owns this OAuth app config
761
+ organizationId String
762
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
763
+
764
+ /// Encrypted client ID
765
+ encryptedClientId Json
766
+
767
+ /// Encrypted client secret
768
+ encryptedClientSecret Json
769
+
770
+ /// Optional: custom scopes (overrides manifest defaults)
771
+ customScopes String[]
772
+
773
+ /// Provider-specific settings (e.g., Rippling app name for authorize URL)
774
+ /// Stored as JSON: { "appName": "compai533c" }
775
+ customSettings Json?
776
+
777
+ /// Whether this config is active
778
+ isActive Boolean @default(true)
779
+
780
+ createdAt DateTime @default(now())
781
+ updatedAt DateTime @updatedAt
782
+
783
+ @@unique([providerSlug, organizationId])
784
+ @@index([organizationId])
785
+ @@index([providerSlug])
786
+ }
787
+
788
+ /// Records check runs linked to tasks for compliance verification
789
+ model IntegrationCheckRun {
790
+ id String @id @default(dbgenerated("generate_prefixed_cuid('icr'::text)"))
791
+
792
+ /// Parent connection
793
+ connectionId String
794
+ connection IntegrationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
795
+
796
+ /// Task being verified (optional - checks can run without a task)
797
+ taskId String?
798
+ task Task? @relation(fields: [taskId], references: [id], onDelete: SetNull)
799
+
800
+ /// Check ID from the manifest
801
+ checkId String
802
+
803
+ /// Check name (denormalized for display)
804
+ checkName String
805
+
806
+ /// Execution status
807
+ status IntegrationRunStatus @default(pending)
808
+
809
+ /// Timestamps
810
+ startedAt DateTime?
811
+ completedAt DateTime?
812
+
813
+ /// Duration in milliseconds
814
+ durationMs Int?
815
+
816
+ /// Summary counts
817
+ totalChecked Int @default(0)
818
+ passedCount Int @default(0)
819
+ failedCount Int @default(0)
820
+
821
+ /// Error message if failed
822
+ errorMessage String?
823
+
824
+ /// Full execution logs (JSON array)
825
+ logs Json?
826
+
827
+ createdAt DateTime @default(now())
828
+
829
+ /// Results from this check run
830
+ results IntegrationCheckResult[]
831
+
832
+ @@index([connectionId])
833
+ @@index([taskId])
834
+ @@index([checkId])
835
+ @@index([status])
836
+ @@index([createdAt])
837
+ }
838
+
839
+ /// Stores individual results (pass/fail) from check runs
840
+ model IntegrationCheckResult {
841
+ id String @id @default(dbgenerated("generate_prefixed_cuid('icx'::text)"))
842
+
843
+ /// Parent check run
844
+ checkRunId String
845
+ checkRun IntegrationCheckRun @relation(fields: [checkRunId], references: [id], onDelete: Cascade)
846
+
847
+ /// Whether this result is a pass or fail
848
+ passed Boolean
849
+
850
+ /// Resource classification
851
+ resourceType String
852
+ resourceId String
853
+
854
+ /// Result details
855
+ title String
856
+ description String?
857
+
858
+ /// Severity (for failures)
859
+ severity IntegrationFindingSeverity?
860
+
861
+ /// Remediation guidance (for failures)
862
+ remediation String?
863
+
864
+ /// Evidence/proof (JSON - API response data)
865
+ evidence Json?
866
+
867
+ /// When this evidence was collected
868
+ collectedAt DateTime @default(now())
869
+
870
+ @@index([checkRunId])
871
+ @@index([passed])
872
+ @@index([resourceType, resourceId])
873
+ }
874
+
875
+ /// Stores platform-wide OAuth app credentials
876
+ /// Used by platform operators to provide default OAuth apps for all users
877
+ model IntegrationPlatformCredential {
878
+ id String @id @default(dbgenerated("generate_prefixed_cuid('ipc'::text)"))
879
+
880
+ /// Provider slug (e.g., "github", "slack") - unique per platform
881
+ providerSlug String @unique
882
+
883
+ /// Encrypted client ID
884
+ encryptedClientId Json
885
+
886
+ /// Encrypted client secret
887
+ encryptedClientSecret Json
888
+
889
+ /// Optional: custom scopes (overrides manifest defaults)
890
+ customScopes String[]
891
+
892
+ /// Provider-specific settings (e.g., Rippling app name for authorize URL)
893
+ /// Stored as JSON: { "appName": "compai533c" }
894
+ customSettings Json?
895
+
896
+ /// Whether this credential is active
897
+ isActive Boolean @default(true)
898
+
899
+ /// Who created this credential
900
+ createdById String?
901
+
902
+ /// Who last updated this credential
903
+ updatedById String?
904
+
905
+ createdAt DateTime @default(now())
906
+ updatedAt DateTime @updatedAt
907
+
908
+ @@index([providerSlug])
909
+ }
910
+
911
+
488
912
  // ===== integration.prisma =====
489
913
  model Integration {
490
914
  id String @id @default(dbgenerated("generate_prefixed_cuid('int'::text)"))
@@ -522,15 +946,15 @@ model IntegrationResult {
522
946
 
523
947
  // ===== knowledge-base-document.prisma =====
524
948
  model KnowledgeBaseDocument {
525
- id String @id @default(dbgenerated("generate_prefixed_cuid('kbd'::text)"))
526
- name String // Original filename
527
- description String? // Optional user description/notes
528
- s3Key String // S3 storage key (e.g., "org123/knowledge-base-documents/timestamp-file.pdf")
529
- fileType String // MIME type (e.g., "application/pdf")
530
- fileSize Int // File size in bytes
949
+ id String @id @default(dbgenerated("generate_prefixed_cuid('kbd'::text)"))
950
+ name String // Original filename
951
+ description String? // Optional user description/notes
952
+ s3Key String // S3 storage key (e.g., "org123/knowledge-base-documents/timestamp-file.pdf")
953
+ fileType String // MIME type (e.g., "application/pdf")
954
+ fileSize Int // File size in bytes
531
955
  processingStatus KnowledgeBaseDocumentProcessingStatus @default(pending) // Track indexing status
532
- processedAt DateTime? // When indexing completed
533
- triggerRunId String? // Trigger.dev run ID for tracking processing progress
956
+ processedAt DateTime? // When indexing completed
957
+ triggerRunId String? // Trigger.dev run ID for tracking processing progress
534
958
 
535
959
  // Dates
536
960
  createdAt DateTime @default(now())
@@ -547,14 +971,13 @@ model KnowledgeBaseDocument {
547
971
  }
548
972
 
549
973
  enum KnowledgeBaseDocumentProcessingStatus {
550
- pending // Uploaded but not yet processed/indexed
551
- processing // Currently being processed/indexed
552
- completed // Successfully indexed in vector database
553
- failed // Processing failed
974
+ pending // Uploaded but not yet processed/indexed
975
+ processing // Currently being processed/indexed
976
+ completed // Successfully indexed in vector database
977
+ failed // Processing failed
554
978
  }
555
979
 
556
980
 
557
-
558
981
  // ===== onboarding.prisma =====
559
982
  model Onboarding {
560
983
  organizationId String @id
@@ -594,28 +1017,39 @@ model Organization {
594
1017
  fleetDmLabelId Int?
595
1018
  isFleetSetupCompleted Boolean @default(false)
596
1019
 
597
- apiKeys ApiKey[]
598
- auditLog AuditLog[]
599
- controls Control[]
600
- frameworkInstances FrameworkInstance[]
601
- integrations Integration[]
602
- invitations Invitation[]
603
- members Member[]
604
- policy Policy[]
605
- risk Risk[]
606
- vendors Vendor[]
607
- tasks Task[]
608
- comments Comment[]
609
- attachments Attachment[]
610
- trust Trust[]
611
- context Context[]
612
- secrets Secret[]
613
- trustAccessRequests TrustAccessRequest[]
614
- trustNdaAgreements TrustNDAAgreement[]
615
- trustDocuments TrustDocument[]
616
- knowledgeBaseDocuments KnowledgeBaseDocument[]
617
- questionnaires Questionnaire[]
1020
+ // Employee sync provider (e.g., 'google-workspace', 'rippling')
1021
+ // When set, the scheduled sync will only use this provider
1022
+ employeeSyncProvider String?
1023
+
1024
+ apiKeys ApiKey[]
1025
+ auditLog AuditLog[]
1026
+ controls Control[]
1027
+ frameworkInstances FrameworkInstance[]
1028
+ integrations Integration[]
1029
+ invitations Invitation[]
1030
+ members Member[]
1031
+ policy Policy[]
1032
+ risk Risk[]
1033
+ vendors Vendor[]
1034
+ tasks Task[]
1035
+ comments Comment[]
1036
+ attachments Attachment[]
1037
+ trust Trust[]
1038
+ context Context[]
1039
+ secrets Secret[]
1040
+ trustAccessRequests TrustAccessRequest[]
1041
+ trustNdaAgreements TrustNDAAgreement[]
1042
+ trustDocuments TrustDocument[]
1043
+ trustResources TrustResource[] @relation("OrganizationTrustResources")
1044
+ knowledgeBaseDocuments KnowledgeBaseDocument[]
1045
+ questionnaires Questionnaire[]
618
1046
  securityQuestionnaireManualAnswers SecurityQuestionnaireManualAnswer[]
1047
+ soaDocuments SOADocument[]
1048
+ primaryColor String?
1049
+
1050
+ // Integration Platform
1051
+ integrationConnections IntegrationConnection[]
1052
+ integrationOAuthApps IntegrationOAuthApp[]
619
1053
 
620
1054
  @@index([slug])
621
1055
  }
@@ -665,15 +1099,16 @@ model Policy {
665
1099
 
666
1100
  // ===== questionnaire.prisma =====
667
1101
  model Questionnaire {
668
- id String @id @default(dbgenerated("generate_prefixed_cuid('qst'::text)"))
669
- filename String // Original filename
670
- s3Key String // S3 storage key for the uploaded file
671
- fileType String // MIME type (e.g., "application/pdf")
672
- fileSize Int // File size in bytes
673
- status QuestionnaireStatus @default(parsing) // Parsing status
674
- parsedAt DateTime? // When parsing completed
675
- totalQuestions Int @default(0) // Total number of questions parsed
676
- answeredQuestions Int @default(0) // Number of questions with answers
1102
+ id String @id @default(dbgenerated("generate_prefixed_cuid('qst'::text)"))
1103
+ filename String // Original filename
1104
+ s3Key String // S3 storage key for the uploaded file
1105
+ fileType String // MIME type (e.g., "application/pdf")
1106
+ fileSize Int // File size in bytes
1107
+ status QuestionnaireStatus @default(parsing) // Parsing status
1108
+ parsedAt DateTime? // When parsing completed
1109
+ totalQuestions Int @default(0) // Total number of questions parsed
1110
+ answeredQuestions Int @default(0) // Number of questions with answers
1111
+ source String @default("internal") // Source of the questionnaire: 'internal' (from app) or 'external' (from trust portal)
677
1112
 
678
1113
  // Dates
679
1114
  createdAt DateTime @default(now())
@@ -681,24 +1116,25 @@ model Questionnaire {
681
1116
 
682
1117
  // Relationships
683
1118
  organizationId String
684
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1119
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
685
1120
  questions QuestionnaireQuestionAnswer[]
686
1121
  manualAnswers SecurityQuestionnaireManualAnswer[] // Manual answers saved from this questionnaire
687
1122
 
688
1123
  @@index([organizationId])
689
1124
  @@index([organizationId, createdAt])
690
1125
  @@index([status])
1126
+ @@index([source])
691
1127
  }
692
1128
 
693
1129
  model QuestionnaireQuestionAnswer {
694
- id String @id @default(dbgenerated("generate_prefixed_cuid('qqa'::text)"))
695
- question String // The question text
696
- answer String? // The answer (nullable if not provided in file or not generated yet)
697
- status QuestionnaireAnswerStatus @default(untouched) // Answer status
698
- questionIndex Int // Order/index of the question in the questionnaire
699
- sources Json? // Sources used for generated answers (array of source objects)
700
- generatedAt DateTime? // When answer was generated (if status is generated)
701
- updatedBy String? // User ID who last updated the answer (if manual)
1130
+ id String @id @default(dbgenerated("generate_prefixed_cuid('qqa'::text)"))
1131
+ question String // The question text
1132
+ answer String? // The answer (nullable if not provided in file or not generated yet)
1133
+ status QuestionnaireAnswerStatus @default(untouched) // Answer status
1134
+ questionIndex Int // Order/index of the question in the questionnaire
1135
+ sources Json? // Sources used for generated answers (array of source objects)
1136
+ generatedAt DateTime? // When answer was generated (if status is generated)
1137
+ updatedBy String? // User ID who last updated the answer (if manual)
702
1138
 
703
1139
  // Dates
704
1140
  createdAt DateTime @default(now())
@@ -714,19 +1150,18 @@ model QuestionnaireQuestionAnswer {
714
1150
  }
715
1151
 
716
1152
  enum QuestionnaireStatus {
717
- parsing // Currently being parsed
718
- completed // Successfully parsed
719
- failed // Parsing failed
1153
+ parsing // Currently being parsed
1154
+ completed // Successfully parsed
1155
+ failed // Parsing failed
720
1156
  }
721
1157
 
722
1158
  enum QuestionnaireAnswerStatus {
723
- untouched // No answer yet (empty or not generated)
724
- generated // AI generated answer
725
- manual // Manually written/edited by user
1159
+ untouched // No answer yet (empty or not generated)
1160
+ generated // AI generated answer
1161
+ manual // Manually written/edited by user
726
1162
  }
727
1163
 
728
1164
 
729
-
730
1165
  // ===== requirement.prisma =====
731
1166
  model RequirementMap {
732
1167
  id String @id @default(dbgenerated("generate_prefixed_cuid('req'::text)"))
@@ -827,36 +1262,35 @@ model Secret {
827
1262
 
828
1263
  // ===== security-questionnaire-manual-answer.prisma =====
829
1264
  model SecurityQuestionnaireManualAnswer {
830
- id String @id @default(dbgenerated("generate_prefixed_cuid('sqma'::text)"))
831
- question String // The question text
832
- answer String // The answer text (required for saved answers)
833
- tags String[] @default([]) // Optional tags for categorization
834
-
1265
+ id String @id @default(dbgenerated("generate_prefixed_cuid('sqma'::text)"))
1266
+ question String // The question text
1267
+ answer String // The answer text (required for saved answers)
1268
+ tags String[] @default([]) // Optional tags for categorization
1269
+
835
1270
  // Optional reference to original questionnaire (for tracking)
836
1271
  sourceQuestionnaireId String?
837
1272
  sourceQuestionnaire Questionnaire? @relation(fields: [sourceQuestionnaireId], references: [id], onDelete: SetNull)
838
-
1273
+
839
1274
  // User who created/updated this answer
840
- createdBy String? // User ID
841
- updatedBy String? // User ID
842
-
1275
+ createdBy String? // User ID
1276
+ updatedBy String? // User ID
1277
+
843
1278
  // Dates
844
1279
  createdAt DateTime @default(now())
845
1280
  updatedAt DateTime @updatedAt
846
-
1281
+
847
1282
  // Relationships
848
1283
  organizationId String
849
1284
  organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
850
-
1285
+
1286
+ @@unique([organizationId, question]) // Prevent duplicate questions per organization
851
1287
  @@index([organizationId])
852
1288
  @@index([organizationId, question])
853
1289
  @@index([tags])
854
1290
  @@index([createdAt])
855
- @@unique([organizationId, question]) // Prevent duplicate questions per organization
856
1291
  }
857
1292
 
858
1293
 
859
-
860
1294
  // ===== shared.prisma =====
861
1295
  model ApiKey {
862
1296
  id String @id @default(dbgenerated("generate_prefixed_cuid('apk'::text)"))
@@ -965,6 +1399,143 @@ enum Impact {
965
1399
  }
966
1400
 
967
1401
 
1402
+ // ===== soa.prisma =====
1403
+ // Statement of Applicability (SOA) Auto-complete Configuration and Answers
1404
+
1405
+ model SOAFrameworkConfiguration {
1406
+ id String @id @default(dbgenerated("generate_prefixed_cuid('soa_cfg'::text)"))
1407
+ frameworkId String
1408
+ framework FrameworkEditorFramework @relation(fields: [frameworkId], references: [id], onDelete: Cascade)
1409
+
1410
+ // Configuration versioning - allows multiple configurations per framework
1411
+ version Int @default(1) // Version number for this configuration (increments when config changes)
1412
+ isLatest Boolean @default(true) // Whether this is the latest configuration version
1413
+
1414
+ // Column definitions for SOA structure (template used when creating new documents)
1415
+ columns Json // Array of { name: string, type: string } objects
1416
+ // Example: [{ name: "Control ID", type: "string" }, { name: "Control Name", type: "string" }, { name: "Applicable", type: "boolean" }, { name: "Justification", type: "text" }]
1417
+
1418
+ // Predefined questions for this framework
1419
+ // Documents reference a specific configuration version via SOADocument.configurationId
1420
+ // Old documents keep their old config version, new documents use new config version
1421
+ questions Json // Array of question objects with unique IDs
1422
+ // Example: [{ id: "A.5.1.1", text: "Is this control applicable?", columnMapping: "Applicable", controlId: "A.5.1.1" }, ...]
1423
+ // IMPORTANT: question.id must be unique and stable - this is what SOAAnswer.questionId references
1424
+
1425
+ // Dates
1426
+ createdAt DateTime @default(now())
1427
+ updatedAt DateTime @updatedAt
1428
+
1429
+ // Relationships
1430
+ documents SOADocument[]
1431
+
1432
+ @@unique([frameworkId, version]) // Prevent duplicate configuration versions
1433
+ @@index([frameworkId])
1434
+ @@index([frameworkId, version])
1435
+ @@index([frameworkId, isLatest])
1436
+ }
1437
+
1438
+ model SOADocument {
1439
+ id String @id @default(dbgenerated("generate_prefixed_cuid('soa_doc'::text)"))
1440
+
1441
+ // Framework and organization context
1442
+ frameworkId String
1443
+ framework FrameworkEditorFramework @relation(fields: [frameworkId], references: [id], onDelete: Cascade)
1444
+ organizationId String
1445
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1446
+
1447
+ // Configuration reference - references a specific SOAFrameworkConfiguration version
1448
+ // Each document version can use a different configuration version
1449
+ // Old documents keep their old config, new documents use new config
1450
+ configurationId String
1451
+ configuration SOAFrameworkConfiguration @relation(fields: [configurationId], references: [id], onDelete: Cascade)
1452
+
1453
+ // Document versioning
1454
+ version Int @default(1) // Version number for this document (increments yearly)
1455
+ isLatest Boolean @default(true) // Whether this is the latest version
1456
+
1457
+ // Document status
1458
+ status SOADocumentStatus @default(draft) // draft, in_progress, completed
1459
+
1460
+ // Document metadata
1461
+ totalQuestions Int @default(0) // Total number of questions in this document
1462
+ answeredQuestions Int @default(0) // Number of questions with answers
1463
+
1464
+ // Approval tracking
1465
+ preparedBy String @default("Comp AI") // Always "Comp AI"
1466
+ approverId String? // Member ID who will approve this document (set when submitted for approval)
1467
+ approver Member? @relation("SOADocumentApprover", fields: [approverId], references: [id], onDelete: SetNull, onUpdate: Cascade)
1468
+ approvedAt DateTime? // When document was approved
1469
+
1470
+ // Dates
1471
+ completedAt DateTime? // When document was completed
1472
+ createdAt DateTime @default(now())
1473
+ updatedAt DateTime @updatedAt
1474
+
1475
+ // Relationships
1476
+ answers SOAAnswer[]
1477
+
1478
+ @@unique([frameworkId, organizationId, version]) // Prevent duplicate versions
1479
+ @@index([frameworkId, organizationId])
1480
+ @@index([frameworkId, organizationId, version])
1481
+ @@index([frameworkId, organizationId, isLatest])
1482
+ @@index([configurationId])
1483
+ @@index([status])
1484
+ }
1485
+
1486
+ model SOAAnswer {
1487
+ id String @id @default(dbgenerated("generate_prefixed_cuid('soa_ans'::text)"))
1488
+
1489
+ // Document context (replaces direct framework/organization link)
1490
+ documentId String
1491
+ document SOADocument @relation(fields: [documentId], references: [id], onDelete: Cascade)
1492
+
1493
+ // Question reference - references question.id from SOADocument.configuration.questions
1494
+ // References the specific configuration version that the document uses
1495
+ // If config changes, old documents still reference their old config version
1496
+ questionId String // Must match a question.id from SOADocument.configuration.questions
1497
+
1498
+ // Answer data - simple text answer
1499
+ answer String? // Text answer (nullable if not generated yet)
1500
+
1501
+ // Answer metadata
1502
+ status SOAAnswerStatus @default(untouched) // untouched, generated, manual
1503
+ sources Json? // Sources used for generated answers (similar to questionnaire)
1504
+ generatedAt DateTime? // When answer was generated
1505
+
1506
+ // Answer versioning (within the document)
1507
+ answerVersion Int @default(1) // Version number for this specific answer
1508
+ isLatestAnswer Boolean @default(true) // Whether this is the latest version of this answer
1509
+
1510
+ // User tracking
1511
+ createdBy String? // User ID who created this answer
1512
+ updatedBy String? // User ID who last updated this answer
1513
+
1514
+ // Dates
1515
+ createdAt DateTime @default(now())
1516
+ updatedAt DateTime @updatedAt
1517
+
1518
+ @@unique([documentId, questionId, answerVersion]) // Prevent duplicate answer versions
1519
+ @@index([documentId])
1520
+ @@index([documentId, questionId])
1521
+ @@index([documentId, questionId, isLatestAnswer])
1522
+ @@index([status])
1523
+ }
1524
+
1525
+ enum SOADocumentStatus {
1526
+ draft // Document is being created/edited
1527
+ in_progress // Document is being generated
1528
+ needs_review // Document is submitted for approval
1529
+ completed // Document is complete and approved
1530
+ }
1531
+
1532
+ enum SOAAnswerStatus {
1533
+ untouched // No answer yet (not generated)
1534
+ generated // AI generated answer
1535
+ manual // Manually written/edited by user
1536
+ }
1537
+
1538
+
968
1539
  // ===== task.prisma =====
969
1540
  model Task {
970
1541
  // Metadata
@@ -994,7 +1565,8 @@ model Task {
994
1565
  risks Risk[]
995
1566
  evidenceAutomations EvidenceAutomation[]
996
1567
 
997
- EvidenceAutomationRun EvidenceAutomationRun[]
1568
+ EvidenceAutomationRun EvidenceAutomationRun[]
1569
+ integrationCheckRuns IntegrationCheckRun[]
998
1570
  }
999
1571
 
1000
1572
  enum TaskStatus {
@@ -1067,6 +1639,33 @@ enum FrameworkStatus {
1067
1639
  compliant
1068
1640
  }
1069
1641
 
1642
+ enum TrustFramework {
1643
+ iso_27001
1644
+ iso_42001
1645
+ gdpr
1646
+ hipaa
1647
+ soc2_type1
1648
+ soc2_type2
1649
+ pci_dss
1650
+ nen_7510
1651
+ iso_9001
1652
+ }
1653
+
1654
+ model TrustResource {
1655
+ id String @id @default(dbgenerated("generate_prefixed_cuid('tcr'::text)"))
1656
+ organizationId String
1657
+ organization Organization @relation("OrganizationTrustResources", fields: [organizationId], references: [id], onDelete: Cascade)
1658
+ framework TrustFramework
1659
+ s3Key String
1660
+ fileName String
1661
+ fileSize Int
1662
+ createdAt DateTime @default(now())
1663
+ updatedAt DateTime @updatedAt
1664
+
1665
+ @@unique([organizationId, framework])
1666
+ @@index([organizationId])
1667
+ }
1668
+
1070
1669
  model TrustAccessRequest {
1071
1670
  id String @id @default(dbgenerated("generate_prefixed_cuid('tar'::text)"))
1072
1671
  organizationId String
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@trycompai/db",
3
3
  "description": "Database package with Prisma client and schema for Comp AI",
4
- "version": "1.3.18",
4
+ "version": "1.3.19",
5
5
  "dependencies": {
6
6
  "@prisma/client": "^6.13.0",
7
7
  "dotenv": "^16.4.5",
@@ -38,6 +38,7 @@
38
38
  "check-types": "tsc --noEmit",
39
39
  "db:generate": "prisma generate",
40
40
  "db:migrate": "prisma migrate dev",
41
+ "db:migrate:reset": "prisma migrate reset",
41
42
  "db:push": "prisma db push",
42
43
  "db:seed": "bun prisma/seed/seed.ts",
43
44
  "db:studio": "prisma studio",