@hasna/contacts 0.6.34 → 0.6.35
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/dashboard/dist/assets/index-0l6aQb1t.css +1 -0
- package/dashboard/dist/assets/index-opnZdkVD.js +229 -0
- package/dashboard/dist/index.html +13 -0
- package/dist/cli/commands/advanced.d.ts +3 -0
- package/dist/cli/commands/advanced.d.ts.map +1 -0
- package/dist/cli/commands/audience.d.ts +18 -0
- package/dist/cli/commands/audience.d.ts.map +1 -0
- package/dist/cli/commands/core.d.ts +3 -0
- package/dist/cli/commands/core.d.ts.map +1 -0
- package/dist/cli/commands/crm.d.ts +3 -0
- package/dist/cli/commands/crm.d.ts.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +1060 -46
- package/dist/cli/storage.d.ts +3 -0
- package/dist/cli/storage.d.ts.map +1 -0
- package/dist/cli/utils.d.ts +10 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cloud/http-storage.d.ts +88 -0
- package/dist/cloud/http-storage.d.ts.map +1 -0
- package/dist/db/activity.d.ts +15 -0
- package/dist/db/activity.d.ts.map +1 -0
- package/dist/db/applications.d.ts +10 -0
- package/dist/db/applications.d.ts.map +1 -0
- package/dist/db/audiences.d.ts +51 -0
- package/dist/db/audiences.d.ts.map +1 -0
- package/dist/db/companies.d.ts +19 -0
- package/dist/db/companies.d.ts.map +1 -0
- package/dist/db/contact-tasks.d.ts +19 -0
- package/dist/db/contact-tasks.d.ts.map +1 -0
- package/dist/db/contacts.d.ts +30 -0
- package/dist/db/contacts.d.ts.map +1 -0
- package/dist/db/coordination.d.ts +30 -0
- package/dist/db/coordination.d.ts.map +1 -0
- package/dist/db/database.d.ts +8 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/deals.d.ts +14 -0
- package/dist/db/deals.d.ts.map +1 -0
- package/dist/db/documents.d.ts +38 -0
- package/dist/db/documents.d.ts.map +1 -0
- package/dist/db/events.d.ts +14 -0
- package/dist/db/events.d.ts.map +1 -0
- package/dist/db/field-history.d.ts +17 -0
- package/dist/db/field-history.d.ts.map +1 -0
- package/dist/db/freshness.d.ts +24 -0
- package/dist/db/freshness.d.ts.map +1 -0
- package/dist/db/graph.d.ts +21 -0
- package/dist/db/graph.d.ts.map +1 -0
- package/dist/db/groups.d.ts +22 -0
- package/dist/db/groups.d.ts.map +1 -0
- package/dist/db/health.d.ts +40 -0
- package/dist/db/health.d.ts.map +1 -0
- package/dist/db/identity.d.ts +32 -0
- package/dist/db/identity.d.ts.map +1 -0
- package/dist/db/job-history.d.ts +29 -0
- package/dist/db/job-history.d.ts.map +1 -0
- package/dist/db/learnings.d.ts +43 -0
- package/dist/db/learnings.d.ts.map +1 -0
- package/dist/db/notes.d.ts +8 -0
- package/dist/db/notes.d.ts.map +1 -0
- package/dist/db/org-chart.d.ts +37 -0
- package/dist/db/org-chart.d.ts.map +1 -0
- package/dist/db/org-members.d.ts +9 -0
- package/dist/db/org-members.d.ts.map +1 -0
- package/dist/db/paths.d.ts +3 -0
- package/dist/db/paths.d.ts.map +1 -0
- package/dist/db/pg-migrations.d.ts +8 -0
- package/dist/db/pg-migrations.d.ts.map +1 -0
- package/dist/db/project-memberships.d.ts +8 -0
- package/dist/db/project-memberships.d.ts.map +1 -0
- package/dist/db/relationships.d.ts +30 -0
- package/dist/db/relationships.d.ts.map +1 -0
- package/dist/db/signals.d.ts +18 -0
- package/dist/db/signals.d.ts.map +1 -0
- package/dist/db/sqlite-adapter.d.ts +15 -0
- package/dist/db/sqlite-adapter.d.ts.map +1 -0
- package/dist/db/storage.d.ts +15 -0
- package/dist/db/storage.d.ts.map +1 -0
- package/dist/db/tags.d.ts +14 -0
- package/dist/db/tags.d.ts.map +1 -0
- package/dist/db/tombstones.d.ts +19 -0
- package/dist/db/tombstones.d.ts.map +1 -0
- package/dist/db/vendor-comms.d.ts +15 -0
- package/dist/db/vendor-comms.d.ts.map +1 -0
- package/dist/generated/storage-kit/health.d.ts +20 -0
- package/dist/generated/storage-kit/health.d.ts.map +1 -0
- package/dist/generated/storage-kit/index.d.ts +8 -0
- package/dist/generated/storage-kit/index.d.ts.map +1 -0
- package/dist/generated/storage-kit/migrations.d.ts +48 -0
- package/dist/generated/storage-kit/migrations.d.ts.map +1 -0
- package/dist/generated/storage-kit/mode.d.ts +48 -0
- package/dist/generated/storage-kit/mode.d.ts.map +1 -0
- package/dist/generated/storage-kit/pool.d.ts +34 -0
- package/dist/generated/storage-kit/pool.d.ts.map +1 -0
- package/dist/generated/storage-kit/query.d.ts +36 -0
- package/dist/generated/storage-kit/query.d.ts.map +1 -0
- package/dist/generated/storage-kit/tls.d.ts +26 -0
- package/dist/generated/storage-kit/tls.d.ts.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +613 -23
- package/dist/lib/apple-contacts.d.ts +3 -0
- package/dist/lib/apple-contacts.d.ts.map +1 -0
- package/dist/lib/audience-contract.d.ts +215 -0
- package/dist/lib/audience-contract.d.ts.map +1 -0
- package/dist/lib/audit.d.ts +12 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/brief.d.ts +3 -0
- package/dist/lib/brief.d.ts.map +1 -0
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/connector.d.ts +44 -0
- package/dist/lib/connector.d.ts.map +1 -0
- package/dist/lib/context.d.ts +5 -0
- package/dist/lib/context.d.ts.map +1 -0
- package/dist/lib/dedup.d.ts +10 -0
- package/dist/lib/dedup.d.ts.map +1 -0
- package/dist/lib/document-scanner.d.ts +8 -0
- package/dist/lib/document-scanner.d.ts.map +1 -0
- package/dist/lib/embeddings.d.ts +9 -0
- package/dist/lib/embeddings.d.ts.map +1 -0
- package/dist/lib/export.d.ts +8 -0
- package/dist/lib/export.d.ts.map +1 -0
- package/dist/lib/freshness.d.ts +19 -0
- package/dist/lib/freshness.d.ts.map +1 -0
- package/dist/lib/gmail-import.d.ts +51 -0
- package/dist/lib/gmail-import.d.ts.map +1 -0
- package/dist/lib/google-contacts.d.ts +115 -0
- package/dist/lib/google-contacts.d.ts.map +1 -0
- package/dist/lib/images.d.ts +30 -0
- package/dist/lib/images.d.ts.map +1 -0
- package/dist/lib/import.d.ts +10 -0
- package/dist/lib/import.d.ts.map +1 -0
- package/dist/lib/learning-maintenance.d.ts +8 -0
- package/dist/lib/learning-maintenance.d.ts.map +1 -0
- package/dist/lib/mailery-sync.d.ts +46 -0
- package/dist/lib/mailery-sync.d.ts.map +1 -0
- package/dist/lib/meeting-capture.d.ts +15 -0
- package/dist/lib/meeting-capture.d.ts.map +1 -0
- package/dist/lib/package-version.d.ts +6 -0
- package/dist/lib/package-version.d.ts.map +1 -0
- package/dist/lib/signals.d.ts +9 -0
- package/dist/lib/signals.d.ts.map +1 -0
- package/dist/lib/signature-parser.d.ts +36 -0
- package/dist/lib/signature-parser.d.ts.map +1 -0
- package/dist/lib/stats.d.ts +24 -0
- package/dist/lib/stats.d.ts.map +1 -0
- package/dist/lib/timeline.d.ts +11 -0
- package/dist/lib/timeline.d.ts.map +1 -0
- package/dist/lib/upcoming.d.ts +14 -0
- package/dist/lib/upcoming.d.ts.map +1 -0
- package/dist/lib/vault.d.ts +24 -0
- package/dist/lib/vault.d.ts.map +1 -0
- package/dist/mcp/handlers/advanced.d.ts +14 -0
- package/dist/mcp/handlers/advanced.d.ts.map +1 -0
- package/dist/mcp/handlers/audience.d.ts +9 -0
- package/dist/mcp/handlers/audience.d.ts.map +1 -0
- package/dist/mcp/handlers/core.d.ts +10 -0
- package/dist/mcp/handlers/core.d.ts.map +1 -0
- package/dist/mcp/handlers/crm.d.ts +14 -0
- package/dist/mcp/handlers/crm.d.ts.map +1 -0
- package/dist/mcp/handlers/index.d.ts +4 -0
- package/dist/mcp/handlers/index.d.ts.map +1 -0
- package/dist/mcp/handlers/types.d.ts +10 -0
- package/dist/mcp/handlers/types.d.ts.map +1 -0
- package/dist/mcp/http.d.ts +17 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +339 -27
- package/dist/mcp/register-tools.d.ts +3 -0
- package/dist/mcp/register-tools.d.ts.map +1 -0
- package/dist/mcp/schema.d.ts +3 -0
- package/dist/mcp/schema.d.ts.map +1 -0
- package/dist/mcp/storage-tools.d.ts +3 -0
- package/dist/mcp/storage-tools.d.ts.map +1 -0
- package/dist/mcp/tools.d.ts +3799 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/sdk/index.d.ts +16 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +63 -0
- package/dist/sdk/v1.generated.d.ts +270 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -0
- package/dist/server/cloud-index.d.ts +3 -0
- package/dist/server/cloud-index.d.ts.map +1 -0
- package/dist/server/cloud-serve.d.ts +3 -0
- package/dist/server/cloud-serve.d.ts.map +1 -0
- package/dist/server/cloud.d.ts +53 -0
- package/dist/server/cloud.d.ts.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +1022 -46
- package/dist/server/openapi.d.ts +1134 -0
- package/dist/server/openapi.d.ts.map +1 -0
- package/dist/server/pg-store.d.ts +893 -0
- package/dist/server/pg-store.d.ts.map +1 -0
- package/dist/server/security.d.ts +24 -0
- package/dist/server/security.d.ts.map +1 -0
- package/dist/server/serve.d.ts +9 -0
- package/dist/server/serve.d.ts.map +1 -0
- package/dist/server/v1.d.ts +28 -0
- package/dist/server/v1.d.ts.map +1 -0
- package/dist/store/index.d.ts +387 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +856 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/project-memberships.d.ts +32 -0
- package/dist/types/project-memberships.d.ts.map +1 -0
- package/migrations/0015_contact_project_membership_authority.sql +88 -0
- package/package.json +1 -1
- package/vendor/fast-uri/README.md +12 -0
- package/dist/server/cloud-index.js +0 -9187
package/dist/index.js
CHANGED
|
@@ -144,7 +144,10 @@ function uuid() {
|
|
|
144
144
|
return crypto.randomUUID();
|
|
145
145
|
}
|
|
146
146
|
function now() {
|
|
147
|
-
|
|
147
|
+
const currentMs = Date.now();
|
|
148
|
+
const nextMs = currentMs > lastNowMs ? currentMs : lastNowMs + 1;
|
|
149
|
+
lastNowMs = nextMs;
|
|
150
|
+
return new Date(nextMs).toISOString();
|
|
148
151
|
}
|
|
149
152
|
function quoteIdentifier(identifier) {
|
|
150
153
|
return `"${identifier.replaceAll('"', '""')}"`;
|
|
@@ -182,7 +185,7 @@ function runMigrations(db) {
|
|
|
182
185
|
db.exec(`INSERT OR REPLACE INTO _migrations(version) VALUES(${i})`);
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
|
-
var MIGRATIONS, _db = null;
|
|
188
|
+
var MIGRATIONS, _db = null, lastNowMs = 0;
|
|
186
189
|
var init_database = __esm(() => {
|
|
187
190
|
init_sqlite_adapter();
|
|
188
191
|
init_paths();
|
|
@@ -754,6 +757,81 @@ var init_database = __esm(() => {
|
|
|
754
757
|
);
|
|
755
758
|
|
|
756
759
|
CREATE INDEX IF NOT EXISTS idx_contacts_tombstones_deleted_at ON _contacts_tombstones(deleted_at);
|
|
760
|
+
`,
|
|
761
|
+
`
|
|
762
|
+
CREATE TABLE IF NOT EXISTS contact_project_membership_states (
|
|
763
|
+
contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
764
|
+
project_id TEXT NOT NULL,
|
|
765
|
+
linked INTEGER NOT NULL CHECK(linked IN (0, 1)),
|
|
766
|
+
revision INTEGER NOT NULL DEFAULT 0 CHECK(revision >= 0),
|
|
767
|
+
updated_at TEXT NOT NULL,
|
|
768
|
+
PRIMARY KEY (contact_id, project_id)
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
INSERT OR IGNORE INTO contact_project_membership_states
|
|
772
|
+
(contact_id, project_id, linked, revision, updated_at)
|
|
773
|
+
SELECT contact_id, project_id, 1, 0, datetime('now')
|
|
774
|
+
FROM contact_projects;
|
|
775
|
+
|
|
776
|
+
CREATE TABLE IF NOT EXISTS contact_project_membership_receipts (
|
|
777
|
+
receipt_id TEXT PRIMARY KEY,
|
|
778
|
+
direction TEXT NOT NULL CHECK(direction IN ('attach', 'detach')),
|
|
779
|
+
contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
780
|
+
project_id TEXT NOT NULL,
|
|
781
|
+
operation_id TEXT NOT NULL,
|
|
782
|
+
step_id TEXT NOT NULL,
|
|
783
|
+
expected_version TEXT NOT NULL,
|
|
784
|
+
before_json TEXT NOT NULL,
|
|
785
|
+
after_json TEXT NOT NULL,
|
|
786
|
+
created_at TEXT NOT NULL,
|
|
787
|
+
UNIQUE(operation_id, step_id)
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
CREATE INDEX IF NOT EXISTS idx_contact_project_membership_states_project
|
|
791
|
+
ON contact_project_membership_states(project_id, linked, contact_id);
|
|
792
|
+
CREATE INDEX IF NOT EXISTS idx_contact_project_membership_receipts_target
|
|
793
|
+
ON contact_project_membership_receipts(contact_id, project_id, created_at);
|
|
794
|
+
|
|
795
|
+
CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_insert
|
|
796
|
+
AFTER INSERT ON contact_projects
|
|
797
|
+
BEGIN
|
|
798
|
+
INSERT INTO contact_project_membership_states
|
|
799
|
+
(contact_id, project_id, linked, revision, updated_at)
|
|
800
|
+
VALUES (NEW.contact_id, NEW.project_id, 1, 1, datetime('now'))
|
|
801
|
+
ON CONFLICT(contact_id, project_id) DO UPDATE SET
|
|
802
|
+
linked = 1,
|
|
803
|
+
revision = CASE
|
|
804
|
+
WHEN contact_project_membership_states.linked = 1
|
|
805
|
+
THEN contact_project_membership_states.revision
|
|
806
|
+
ELSE contact_project_membership_states.revision + 1
|
|
807
|
+
END,
|
|
808
|
+
updated_at = CASE
|
|
809
|
+
WHEN contact_project_membership_states.linked = 1
|
|
810
|
+
THEN contact_project_membership_states.updated_at
|
|
811
|
+
ELSE datetime('now')
|
|
812
|
+
END;
|
|
813
|
+
END;
|
|
814
|
+
|
|
815
|
+
CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_delete
|
|
816
|
+
AFTER DELETE ON contact_projects
|
|
817
|
+
WHEN EXISTS (SELECT 1 FROM contacts WHERE id = OLD.contact_id)
|
|
818
|
+
BEGIN
|
|
819
|
+
INSERT INTO contact_project_membership_states
|
|
820
|
+
(contact_id, project_id, linked, revision, updated_at)
|
|
821
|
+
VALUES (OLD.contact_id, OLD.project_id, 0, 1, datetime('now'))
|
|
822
|
+
ON CONFLICT(contact_id, project_id) DO UPDATE SET
|
|
823
|
+
linked = 0,
|
|
824
|
+
revision = CASE
|
|
825
|
+
WHEN contact_project_membership_states.linked = 0
|
|
826
|
+
THEN contact_project_membership_states.revision
|
|
827
|
+
ELSE contact_project_membership_states.revision + 1
|
|
828
|
+
END,
|
|
829
|
+
updated_at = CASE
|
|
830
|
+
WHEN contact_project_membership_states.linked = 0
|
|
831
|
+
THEN contact_project_membership_states.updated_at
|
|
832
|
+
ELSE datetime('now')
|
|
833
|
+
END;
|
|
834
|
+
END;
|
|
757
835
|
`
|
|
758
836
|
];
|
|
759
837
|
});
|
|
@@ -853,6 +931,219 @@ var init_tombstones = __esm(() => {
|
|
|
853
931
|
init_database();
|
|
854
932
|
});
|
|
855
933
|
|
|
934
|
+
// src/types/project-memberships.ts
|
|
935
|
+
var ContactProjectMembershipConflictError;
|
|
936
|
+
var init_project_memberships = __esm(() => {
|
|
937
|
+
ContactProjectMembershipConflictError = class ContactProjectMembershipConflictError extends Error {
|
|
938
|
+
constructor(message) {
|
|
939
|
+
super(message);
|
|
940
|
+
this.name = "ContactProjectMembershipConflictError";
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
// src/db/project-memberships.ts
|
|
946
|
+
import { createHash } from "crypto";
|
|
947
|
+
function digest(value) {
|
|
948
|
+
return createHash("sha256").update(value).digest("hex");
|
|
949
|
+
}
|
|
950
|
+
function membershipVersion(contactId, projectId, linked, revision) {
|
|
951
|
+
return `cpmv_${digest(JSON.stringify([contactId, projectId, linked, revision])).slice(0, 32)}`;
|
|
952
|
+
}
|
|
953
|
+
function receiptId(input) {
|
|
954
|
+
return `cpmr_${digest(JSON.stringify([
|
|
955
|
+
input.operation_id,
|
|
956
|
+
input.step_id,
|
|
957
|
+
input.contact_id,
|
|
958
|
+
input.project_id
|
|
959
|
+
])).slice(0, 32)}`;
|
|
960
|
+
}
|
|
961
|
+
function stateRow(contactId, projectId, db) {
|
|
962
|
+
const persisted = db.query(`SELECT contact_id, project_id, linked, revision
|
|
963
|
+
FROM contact_project_membership_states
|
|
964
|
+
WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId);
|
|
965
|
+
if (persisted)
|
|
966
|
+
return persisted;
|
|
967
|
+
const linked = Boolean(db.query(`SELECT 1 AS present FROM contact_projects WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId));
|
|
968
|
+
return { contact_id: contactId, project_id: projectId, linked: linked ? 1 : 0, revision: 0 };
|
|
969
|
+
}
|
|
970
|
+
function snapshot(row) {
|
|
971
|
+
const linked = Boolean(row.linked);
|
|
972
|
+
return {
|
|
973
|
+
contact_id: row.contact_id,
|
|
974
|
+
project_id: row.project_id,
|
|
975
|
+
linked,
|
|
976
|
+
version: membershipVersion(row.contact_id, row.project_id, linked, row.revision)
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function required(value, name) {
|
|
980
|
+
const normalized = value.trim();
|
|
981
|
+
if (!normalized)
|
|
982
|
+
throw new Error(`${name} is required`);
|
|
983
|
+
return normalized;
|
|
984
|
+
}
|
|
985
|
+
function normalizeInput(input) {
|
|
986
|
+
return {
|
|
987
|
+
contact_id: required(input.contact_id, "contact_id"),
|
|
988
|
+
project_id: required(input.project_id, "project_id"),
|
|
989
|
+
operation_id: required(input.operation_id, "operation_id"),
|
|
990
|
+
step_id: required(input.step_id, "step_id"),
|
|
991
|
+
expected_version: required(input.expected_version, "expected_version")
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
function parseSnapshot(value) {
|
|
995
|
+
return JSON.parse(value);
|
|
996
|
+
}
|
|
997
|
+
function replay(row, direction, input) {
|
|
998
|
+
if (row.direction !== direction || row.contact_id !== input.contact_id || row.project_id !== input.project_id || row.expected_version !== input.expected_version) {
|
|
999
|
+
throw new ContactProjectMembershipConflictError(`operation_id/step_id already accepted for a different contact-project membership mutation`);
|
|
1000
|
+
}
|
|
1001
|
+
return {
|
|
1002
|
+
outcome: "duplicate_of_accepted",
|
|
1003
|
+
operation_id: row.operation_id,
|
|
1004
|
+
step_id: row.step_id,
|
|
1005
|
+
before: parseSnapshot(row.before_json),
|
|
1006
|
+
after: parseSnapshot(row.after_json),
|
|
1007
|
+
receipt_id: row.receipt_id
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
function transitionWithoutReceipt(contactId, projectId, linked, db) {
|
|
1011
|
+
const beforeRow = stateRow(contactId, projectId, db);
|
|
1012
|
+
const changed = Boolean(beforeRow.linked) !== linked;
|
|
1013
|
+
const afterRow = {
|
|
1014
|
+
...beforeRow,
|
|
1015
|
+
linked: linked ? 1 : 0,
|
|
1016
|
+
revision: changed ? beforeRow.revision + 1 : beforeRow.revision
|
|
1017
|
+
};
|
|
1018
|
+
const changedAt = now();
|
|
1019
|
+
db.run(`INSERT INTO contact_project_membership_states
|
|
1020
|
+
(contact_id, project_id, linked, revision, updated_at)
|
|
1021
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1022
|
+
ON CONFLICT(contact_id, project_id) DO UPDATE SET
|
|
1023
|
+
linked = excluded.linked,
|
|
1024
|
+
revision = excluded.revision,
|
|
1025
|
+
updated_at = excluded.updated_at`, [contactId, projectId, afterRow.linked, afterRow.revision, changedAt]);
|
|
1026
|
+
if (linked) {
|
|
1027
|
+
db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, projectId]);
|
|
1028
|
+
} else {
|
|
1029
|
+
db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [contactId, projectId]);
|
|
1030
|
+
}
|
|
1031
|
+
return changed;
|
|
1032
|
+
}
|
|
1033
|
+
function setContactProjectMembershipWithoutReceipt(contactId, projectId, linked, db = getDatabase()) {
|
|
1034
|
+
const normalizedContactId = required(contactId, "contact_id");
|
|
1035
|
+
const normalizedProjectId = required(projectId, "project_id");
|
|
1036
|
+
return db.transaction(() => transitionWithoutReceipt(normalizedContactId, normalizedProjectId, linked, db));
|
|
1037
|
+
}
|
|
1038
|
+
function replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, db = getDatabase()) {
|
|
1039
|
+
const normalizedContactId = required(contactId, "contact_id");
|
|
1040
|
+
const normalizedProjectIds = [...new Set(projectIds.map((projectId) => required(projectId, "project_id")))];
|
|
1041
|
+
return db.transaction(() => {
|
|
1042
|
+
const currentRows = db.query(`SELECT project_id FROM contact_projects WHERE contact_id = ?`).all(normalizedContactId);
|
|
1043
|
+
const desired = new Set(normalizedProjectIds);
|
|
1044
|
+
const population = new Set([...currentRows.map((row) => row.project_id), ...normalizedProjectIds]);
|
|
1045
|
+
for (const projectId of population) {
|
|
1046
|
+
transitionWithoutReceipt(normalizedContactId, projectId, desired.has(projectId), db);
|
|
1047
|
+
}
|
|
1048
|
+
return normalizedProjectIds;
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
function readContactProjectMembership(contactId, projectId, db = getDatabase()) {
|
|
1052
|
+
return snapshot(stateRow(required(contactId, "contact_id"), required(projectId, "project_id"), db));
|
|
1053
|
+
}
|
|
1054
|
+
function listContactProjectMemberships(projectId, maxItems, db = getDatabase()) {
|
|
1055
|
+
const normalizedProjectId = required(projectId, "project_id");
|
|
1056
|
+
if (!Number.isInteger(maxItems) || maxItems < 1)
|
|
1057
|
+
throw new Error("max_items must be a positive integer");
|
|
1058
|
+
const rows = db.query(`SELECT cp.contact_id, cp.project_id,
|
|
1059
|
+
COALESCE(state.linked, 1) AS linked,
|
|
1060
|
+
COALESCE(state.revision, 0) AS revision
|
|
1061
|
+
FROM contact_projects cp
|
|
1062
|
+
LEFT JOIN contact_project_membership_states state
|
|
1063
|
+
ON state.contact_id = cp.contact_id AND state.project_id = cp.project_id
|
|
1064
|
+
WHERE cp.project_id = ?
|
|
1065
|
+
ORDER BY cp.contact_id ASC
|
|
1066
|
+
LIMIT ?`).all(normalizedProjectId, maxItems + 1);
|
|
1067
|
+
if (rows.length > maxItems) {
|
|
1068
|
+
throw new Error(`contact project membership collection exceeds max_items=${maxItems}`);
|
|
1069
|
+
}
|
|
1070
|
+
const contactIds = rows.map((row) => row.contact_id);
|
|
1071
|
+
return {
|
|
1072
|
+
project_id: normalizedProjectId,
|
|
1073
|
+
contact_ids: contactIds,
|
|
1074
|
+
complete: true,
|
|
1075
|
+
membership_revision: `cpml_${digest(JSON.stringify(rows.map((row) => snapshot(row)))).slice(0, 32)}`
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function mutateContactProjectMembership(direction, rawInput, db = getDatabase()) {
|
|
1079
|
+
const input = normalizeInput(rawInput);
|
|
1080
|
+
return db.transaction(() => {
|
|
1081
|
+
const existingReceipt = db.query(`SELECT direction, contact_id, project_id, operation_id, step_id, expected_version,
|
|
1082
|
+
before_json, after_json, receipt_id
|
|
1083
|
+
FROM contact_project_membership_receipts
|
|
1084
|
+
WHERE operation_id = ? AND step_id = ?`).get(input.operation_id, input.step_id);
|
|
1085
|
+
if (existingReceipt)
|
|
1086
|
+
return replay(existingReceipt, direction, input);
|
|
1087
|
+
const contact = db.query(`SELECT id FROM contacts WHERE id = ?`).get(input.contact_id);
|
|
1088
|
+
if (!contact)
|
|
1089
|
+
throw new Error(`contact not found: ${input.contact_id}`);
|
|
1090
|
+
const beforeRow = stateRow(input.contact_id, input.project_id, db);
|
|
1091
|
+
const before = snapshot(beforeRow);
|
|
1092
|
+
if (before.version !== input.expected_version) {
|
|
1093
|
+
throw new ContactProjectMembershipConflictError(`contact project membership expected_version conflict: expected ${input.expected_version}, current ${before.version}`);
|
|
1094
|
+
}
|
|
1095
|
+
const desiredLinked = direction === "attach";
|
|
1096
|
+
const changed = before.linked !== desiredLinked;
|
|
1097
|
+
const afterRow = {
|
|
1098
|
+
...beforeRow,
|
|
1099
|
+
linked: desiredLinked ? 1 : 0,
|
|
1100
|
+
revision: changed ? beforeRow.revision + 1 : beforeRow.revision
|
|
1101
|
+
};
|
|
1102
|
+
const changedAt = now();
|
|
1103
|
+
db.run(`INSERT INTO contact_project_membership_states
|
|
1104
|
+
(contact_id, project_id, linked, revision, updated_at)
|
|
1105
|
+
VALUES (?, ?, ?, ?, ?)
|
|
1106
|
+
ON CONFLICT(contact_id, project_id) DO UPDATE SET
|
|
1107
|
+
linked = excluded.linked,
|
|
1108
|
+
revision = excluded.revision,
|
|
1109
|
+
updated_at = excluded.updated_at`, [input.contact_id, input.project_id, afterRow.linked, afterRow.revision, changedAt]);
|
|
1110
|
+
if (desiredLinked) {
|
|
1111
|
+
db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [input.contact_id, input.project_id]);
|
|
1112
|
+
} else {
|
|
1113
|
+
db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [input.contact_id, input.project_id]);
|
|
1114
|
+
}
|
|
1115
|
+
const after = snapshot(afterRow);
|
|
1116
|
+
const id = receiptId(input);
|
|
1117
|
+
db.run(`INSERT INTO contact_project_membership_receipts (
|
|
1118
|
+
receipt_id, direction, contact_id, project_id, operation_id, step_id,
|
|
1119
|
+
expected_version, before_json, after_json, created_at
|
|
1120
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
1121
|
+
id,
|
|
1122
|
+
direction,
|
|
1123
|
+
input.contact_id,
|
|
1124
|
+
input.project_id,
|
|
1125
|
+
input.operation_id,
|
|
1126
|
+
input.step_id,
|
|
1127
|
+
input.expected_version,
|
|
1128
|
+
JSON.stringify(before),
|
|
1129
|
+
JSON.stringify(after),
|
|
1130
|
+
changedAt
|
|
1131
|
+
]);
|
|
1132
|
+
return {
|
|
1133
|
+
outcome: changed ? "accepted" : "duplicate_of_accepted",
|
|
1134
|
+
operation_id: input.operation_id,
|
|
1135
|
+
step_id: input.step_id,
|
|
1136
|
+
before,
|
|
1137
|
+
after,
|
|
1138
|
+
receipt_id: id
|
|
1139
|
+
};
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
var init_project_memberships2 = __esm(() => {
|
|
1143
|
+
init_database();
|
|
1144
|
+
init_project_memberships();
|
|
1145
|
+
});
|
|
1146
|
+
|
|
856
1147
|
// src/db/contacts.ts
|
|
857
1148
|
var exports_contacts = {};
|
|
858
1149
|
__export(exports_contacts, {
|
|
@@ -1451,34 +1742,32 @@ function autoLinkContactToCompany(contactId, db) {
|
|
|
1451
1742
|
}
|
|
1452
1743
|
function linkContactToProject(contactId, projectId, db) {
|
|
1453
1744
|
const d = db || getDatabase();
|
|
1454
|
-
|
|
1745
|
+
setContactProjectMembershipWithoutReceipt(contactId, projectId, true, d);
|
|
1455
1746
|
}
|
|
1456
1747
|
function unlinkContactFromProject(contactId, projectId, db) {
|
|
1457
1748
|
const d = db || getDatabase();
|
|
1458
|
-
|
|
1749
|
+
setContactProjectMembershipWithoutReceipt(contactId, projectId, false, d);
|
|
1459
1750
|
}
|
|
1460
1751
|
function getContactProjectIds(contactId, db) {
|
|
1461
1752
|
const d = db || getDatabase();
|
|
1462
|
-
const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id =
|
|
1753
|
+
const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id = ? ORDER BY project_id ASC`).all(contactId);
|
|
1463
1754
|
return rows.map((r) => r.project_id);
|
|
1464
1755
|
}
|
|
1465
1756
|
function listContactIdsByProject(projectId, db) {
|
|
1466
1757
|
const d = db || getDatabase();
|
|
1467
|
-
const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id =
|
|
1758
|
+
const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id = ? ORDER BY contact_id ASC`).all(projectId);
|
|
1468
1759
|
return rows.map((r) => r.contact_id);
|
|
1469
1760
|
}
|
|
1470
1761
|
function setContactProjects(contactId, projectIds, db) {
|
|
1471
1762
|
const d = db || getDatabase();
|
|
1472
|
-
|
|
1473
|
-
for (const pid of projectIds) {
|
|
1474
|
-
d.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, pid]);
|
|
1475
|
-
}
|
|
1763
|
+
replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, d);
|
|
1476
1764
|
}
|
|
1477
1765
|
var init_contacts = __esm(() => {
|
|
1478
1766
|
init_types();
|
|
1479
1767
|
init_database();
|
|
1480
1768
|
init_activity();
|
|
1481
1769
|
init_tombstones();
|
|
1770
|
+
init_project_memberships2();
|
|
1482
1771
|
});
|
|
1483
1772
|
|
|
1484
1773
|
// src/db/events.ts
|
|
@@ -1644,6 +1933,7 @@ function getStorageStatus(db = getDatabase()) {
|
|
|
1644
1933
|
|
|
1645
1934
|
// src/store/index.ts
|
|
1646
1935
|
init_contacts();
|
|
1936
|
+
init_project_memberships2();
|
|
1647
1937
|
|
|
1648
1938
|
// src/db/companies.ts
|
|
1649
1939
|
init_types();
|
|
@@ -3173,7 +3463,7 @@ init_database();
|
|
|
3173
3463
|
init_database();
|
|
3174
3464
|
import { existsSync as existsSync3, readFileSync, writeFileSync, mkdirSync as mkdirSync3, unlinkSync } from "fs";
|
|
3175
3465
|
import { join as join2 } from "path";
|
|
3176
|
-
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
|
|
3466
|
+
import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash as createHash2 } from "crypto";
|
|
3177
3467
|
var VAULT_DIR = getDataDir();
|
|
3178
3468
|
var VAULT_CONFIG = join2(VAULT_DIR, "vault.json");
|
|
3179
3469
|
var VAULT_SESSION = join2(VAULT_DIR, ".vault-session");
|
|
@@ -3219,7 +3509,7 @@ function initVault(passphrase) {
|
|
|
3219
3509
|
mkdirSync3(DOCUMENTS_DIR, { recursive: true });
|
|
3220
3510
|
const salt = randomBytes(32);
|
|
3221
3511
|
const key = deriveKey(passphrase, salt);
|
|
3222
|
-
const keyHash =
|
|
3512
|
+
const keyHash = createHash2("sha256").update(key).digest("hex");
|
|
3223
3513
|
const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
|
|
3224
3514
|
writeFileSync(VAULT_CONFIG, JSON.stringify(config, null, 2));
|
|
3225
3515
|
_derivedKey = key;
|
|
@@ -3234,7 +3524,7 @@ function unlockVault(passphrase) {
|
|
|
3234
3524
|
const config = JSON.parse(readFileSync(VAULT_CONFIG, "utf-8"));
|
|
3235
3525
|
const salt = Buffer.from(config.salt, "hex");
|
|
3236
3526
|
const key = deriveKey(passphrase, salt);
|
|
3237
|
-
const keyHash =
|
|
3527
|
+
const keyHash = createHash2("sha256").update(key).digest("hex");
|
|
3238
3528
|
if (keyHash !== config.key_hash)
|
|
3239
3529
|
return false;
|
|
3240
3530
|
_derivedKey = key;
|
|
@@ -4749,6 +5039,15 @@ class LocalStore {
|
|
|
4749
5039
|
async listContactIdsByProject(projectId) {
|
|
4750
5040
|
return listContactIdsByProject(projectId, this.db);
|
|
4751
5041
|
}
|
|
5042
|
+
async readContactProjectMembership(contactId, projectId) {
|
|
5043
|
+
return readContactProjectMembership(contactId, projectId, this.db);
|
|
5044
|
+
}
|
|
5045
|
+
async listContactProjectMemberships(projectId, maxItems) {
|
|
5046
|
+
return listContactProjectMemberships(projectId, maxItems, this.db);
|
|
5047
|
+
}
|
|
5048
|
+
async mutateContactProjectMembership(direction, input) {
|
|
5049
|
+
return mutateContactProjectMembership(direction, input, this.db);
|
|
5050
|
+
}
|
|
4752
5051
|
async createCompany(input) {
|
|
4753
5052
|
return createCompany(input, this.db);
|
|
4754
5053
|
}
|
|
@@ -5371,20 +5670,33 @@ class ApiStore {
|
|
|
5371
5670
|
async findContactByEmailAddress(address) {
|
|
5372
5671
|
return this.getContactByEmail(address);
|
|
5373
5672
|
}
|
|
5374
|
-
async linkContactToProject() {
|
|
5375
|
-
|
|
5673
|
+
async linkContactToProject(contactId, projectId) {
|
|
5674
|
+
await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
|
|
5675
|
+
}
|
|
5676
|
+
async unlinkContactFromProject(contactId, projectId) {
|
|
5677
|
+
await this.del(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
|
|
5678
|
+
}
|
|
5679
|
+
async getContactProjectIds(contactId) {
|
|
5680
|
+
return pick(await this.g(`/contacts/${this.enc(contactId)}/projects`), "project_ids") ?? [];
|
|
5376
5681
|
}
|
|
5377
|
-
async
|
|
5378
|
-
|
|
5682
|
+
async setContactProjects(contactId, projectIds) {
|
|
5683
|
+
await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects`, { project_ids: projectIds });
|
|
5379
5684
|
}
|
|
5380
|
-
async
|
|
5381
|
-
return
|
|
5685
|
+
async listContactIdsByProject(projectId) {
|
|
5686
|
+
return pick(await this.g(`/projects/${this.enc(projectId)}/contacts`), "contact_ids") ?? [];
|
|
5382
5687
|
}
|
|
5383
|
-
async
|
|
5384
|
-
return
|
|
5688
|
+
async readContactProjectMembership(contactId, projectId) {
|
|
5689
|
+
return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships/${this.enc(contactId)}`);
|
|
5385
5690
|
}
|
|
5386
|
-
async
|
|
5387
|
-
return
|
|
5691
|
+
async listContactProjectMemberships(projectId, maxItems) {
|
|
5692
|
+
return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships`, { query: { max_items: maxItems } });
|
|
5693
|
+
}
|
|
5694
|
+
async mutateContactProjectMembership(direction, input) {
|
|
5695
|
+
return this.client.transport.post(`/projects/${this.enc(input.project_id)}/contact-memberships/${this.enc(input.contact_id)}/${direction}`, {
|
|
5696
|
+
operation_id: input.operation_id,
|
|
5697
|
+
step_id: input.step_id,
|
|
5698
|
+
expected_version: input.expected_version
|
|
5699
|
+
});
|
|
5388
5700
|
}
|
|
5389
5701
|
async createCompany(input) {
|
|
5390
5702
|
const res = await this.client.create("companies", stripUndefined(input));
|
|
@@ -5907,6 +6219,7 @@ function resetStoreCache() {
|
|
|
5907
6219
|
}
|
|
5908
6220
|
|
|
5909
6221
|
// src/index.ts
|
|
6222
|
+
init_project_memberships();
|
|
5910
6223
|
init_types();
|
|
5911
6224
|
|
|
5912
6225
|
// src/sdk/v1.generated.ts
|
|
@@ -6013,6 +6326,34 @@ class ContactsV1Client {
|
|
|
6013
6326
|
init
|
|
6014
6327
|
});
|
|
6015
6328
|
}
|
|
6329
|
+
async getContactProjectIds(contactId, init) {
|
|
6330
|
+
return this.request("GET", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects`, {
|
|
6331
|
+
body: undefined,
|
|
6332
|
+
query: undefined,
|
|
6333
|
+
init
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
async setContactProjects(contactId, body, init) {
|
|
6337
|
+
return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects`, {
|
|
6338
|
+
body,
|
|
6339
|
+
query: undefined,
|
|
6340
|
+
init
|
|
6341
|
+
});
|
|
6342
|
+
}
|
|
6343
|
+
async linkContactToProject(contactId, projectId, init) {
|
|
6344
|
+
return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects/${encodeURIComponent(String(projectId))}`, {
|
|
6345
|
+
body: undefined,
|
|
6346
|
+
query: undefined,
|
|
6347
|
+
init
|
|
6348
|
+
});
|
|
6349
|
+
}
|
|
6350
|
+
async unlinkContactFromProject(contactId, projectId, init) {
|
|
6351
|
+
return this.request("DELETE", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects/${encodeURIComponent(String(projectId))}`, {
|
|
6352
|
+
body: undefined,
|
|
6353
|
+
query: undefined,
|
|
6354
|
+
init
|
|
6355
|
+
});
|
|
6356
|
+
}
|
|
6016
6357
|
async addTagToContact(contactId, tagId, init) {
|
|
6017
6358
|
return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/tags/${encodeURIComponent(String(tagId))}`, {
|
|
6018
6359
|
body: undefined,
|
|
@@ -6048,6 +6389,41 @@ class ContactsV1Client {
|
|
|
6048
6389
|
init
|
|
6049
6390
|
});
|
|
6050
6391
|
}
|
|
6392
|
+
async listContactProjectMemberships(projectId, query, init) {
|
|
6393
|
+
return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships`, {
|
|
6394
|
+
body: undefined,
|
|
6395
|
+
query,
|
|
6396
|
+
init
|
|
6397
|
+
});
|
|
6398
|
+
}
|
|
6399
|
+
async readContactProjectMembership(projectId, contactId, init) {
|
|
6400
|
+
return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}`, {
|
|
6401
|
+
body: undefined,
|
|
6402
|
+
query: undefined,
|
|
6403
|
+
init
|
|
6404
|
+
});
|
|
6405
|
+
}
|
|
6406
|
+
async attachContactProjectMembership(projectId, contactId, body, init) {
|
|
6407
|
+
return this.request("POST", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}/attach`, {
|
|
6408
|
+
body,
|
|
6409
|
+
query: undefined,
|
|
6410
|
+
init
|
|
6411
|
+
});
|
|
6412
|
+
}
|
|
6413
|
+
async detachContactProjectMembership(projectId, contactId, body, init) {
|
|
6414
|
+
return this.request("POST", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}/detach`, {
|
|
6415
|
+
body,
|
|
6416
|
+
query: undefined,
|
|
6417
|
+
init
|
|
6418
|
+
});
|
|
6419
|
+
}
|
|
6420
|
+
async listContactIdsByProject(projectId, init) {
|
|
6421
|
+
return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contacts`, {
|
|
6422
|
+
body: undefined,
|
|
6423
|
+
query: undefined,
|
|
6424
|
+
init
|
|
6425
|
+
});
|
|
6426
|
+
}
|
|
6051
6427
|
async getStats(init) {
|
|
6052
6428
|
return this.request("GET", `/v1/stats`, {
|
|
6053
6429
|
body: undefined,
|
|
@@ -6258,6 +6634,58 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
6258
6634
|
color: { type: "string" },
|
|
6259
6635
|
description: { type: "string" }
|
|
6260
6636
|
}
|
|
6637
|
+
},
|
|
6638
|
+
ProjectIdsInput: {
|
|
6639
|
+
type: "object",
|
|
6640
|
+
required: ["project_ids"],
|
|
6641
|
+
properties: {
|
|
6642
|
+
project_ids: {
|
|
6643
|
+
type: "array",
|
|
6644
|
+
items: { type: "string", minLength: 1 },
|
|
6645
|
+
uniqueItems: true
|
|
6646
|
+
}
|
|
6647
|
+
}
|
|
6648
|
+
},
|
|
6649
|
+
ContactProjectMembershipSnapshot: {
|
|
6650
|
+
type: "object",
|
|
6651
|
+
required: ["contact_id", "project_id", "linked", "version"],
|
|
6652
|
+
properties: {
|
|
6653
|
+
contact_id: { type: "string" },
|
|
6654
|
+
project_id: { type: "string" },
|
|
6655
|
+
linked: { type: "boolean" },
|
|
6656
|
+
version: { type: "string" }
|
|
6657
|
+
}
|
|
6658
|
+
},
|
|
6659
|
+
ContactProjectMembershipMutationInput: {
|
|
6660
|
+
type: "object",
|
|
6661
|
+
required: ["operation_id", "step_id", "expected_version"],
|
|
6662
|
+
properties: {
|
|
6663
|
+
operation_id: { type: "string", minLength: 1 },
|
|
6664
|
+
step_id: { type: "string", minLength: 1 },
|
|
6665
|
+
expected_version: { type: "string", minLength: 1 }
|
|
6666
|
+
}
|
|
6667
|
+
},
|
|
6668
|
+
ContactProjectMembershipMutationResult: {
|
|
6669
|
+
type: "object",
|
|
6670
|
+
required: ["outcome", "operation_id", "step_id", "before", "after", "receipt_id"],
|
|
6671
|
+
properties: {
|
|
6672
|
+
outcome: { type: "string", enum: ["accepted", "duplicate_of_accepted"] },
|
|
6673
|
+
operation_id: { type: "string" },
|
|
6674
|
+
step_id: { type: "string" },
|
|
6675
|
+
before: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" },
|
|
6676
|
+
after: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" },
|
|
6677
|
+
receipt_id: { type: "string" }
|
|
6678
|
+
}
|
|
6679
|
+
},
|
|
6680
|
+
ContactProjectMembershipListResult: {
|
|
6681
|
+
type: "object",
|
|
6682
|
+
required: ["project_id", "contact_ids", "complete", "membership_revision"],
|
|
6683
|
+
properties: {
|
|
6684
|
+
project_id: { type: "string" },
|
|
6685
|
+
contact_ids: { type: "array", items: { type: "string" } },
|
|
6686
|
+
complete: { type: "boolean", const: true },
|
|
6687
|
+
membership_revision: { type: "string" }
|
|
6688
|
+
}
|
|
6261
6689
|
}
|
|
6262
6690
|
}
|
|
6263
6691
|
},
|
|
@@ -6458,6 +6886,167 @@ function buildV1OpenApiDocument(version = getPackageVersion()) {
|
|
|
6458
6886
|
})
|
|
6459
6887
|
}
|
|
6460
6888
|
},
|
|
6889
|
+
"/v1/contacts/{contact_id}/projects": {
|
|
6890
|
+
get: {
|
|
6891
|
+
operationId: "getContactProjectIds",
|
|
6892
|
+
summary: "List project ids attached to a contact",
|
|
6893
|
+
parameters: [
|
|
6894
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } }
|
|
6895
|
+
],
|
|
6896
|
+
responses: objResponse({
|
|
6897
|
+
contact_id: { type: "string" },
|
|
6898
|
+
project_ids: { type: "array", items: { type: "string" } }
|
|
6899
|
+
})
|
|
6900
|
+
},
|
|
6901
|
+
put: {
|
|
6902
|
+
operationId: "setContactProjects",
|
|
6903
|
+
summary: "Atomically replace a contact's project memberships",
|
|
6904
|
+
parameters: [
|
|
6905
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } }
|
|
6906
|
+
],
|
|
6907
|
+
requestBody: {
|
|
6908
|
+
required: true,
|
|
6909
|
+
content: { "application/json": { schema: { $ref: "#/components/schemas/ProjectIdsInput" } } }
|
|
6910
|
+
},
|
|
6911
|
+
responses: objResponse({
|
|
6912
|
+
contact_id: { type: "string" },
|
|
6913
|
+
project_ids: { type: "array", items: { type: "string" } }
|
|
6914
|
+
})
|
|
6915
|
+
}
|
|
6916
|
+
},
|
|
6917
|
+
"/v1/contacts/{contact_id}/projects/{project_id}": {
|
|
6918
|
+
put: {
|
|
6919
|
+
operationId: "linkContactToProject",
|
|
6920
|
+
summary: "Attach a contact to a project idempotently",
|
|
6921
|
+
parameters: [
|
|
6922
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } },
|
|
6923
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } }
|
|
6924
|
+
],
|
|
6925
|
+
responses: objResponse({
|
|
6926
|
+
attached: { type: "boolean" },
|
|
6927
|
+
contact_id: { type: "string" },
|
|
6928
|
+
project_id: { type: "string" }
|
|
6929
|
+
})
|
|
6930
|
+
},
|
|
6931
|
+
delete: {
|
|
6932
|
+
operationId: "unlinkContactFromProject",
|
|
6933
|
+
summary: "Detach a contact from a project",
|
|
6934
|
+
parameters: [
|
|
6935
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } },
|
|
6936
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } }
|
|
6937
|
+
],
|
|
6938
|
+
responses: objResponse({
|
|
6939
|
+
removed: { type: "boolean" },
|
|
6940
|
+
contact_id: { type: "string" },
|
|
6941
|
+
project_id: { type: "string" }
|
|
6942
|
+
})
|
|
6943
|
+
}
|
|
6944
|
+
},
|
|
6945
|
+
"/v1/projects/{project_id}/contacts": {
|
|
6946
|
+
get: {
|
|
6947
|
+
operationId: "listContactIdsByProject",
|
|
6948
|
+
summary: "List contact ids attached to a project",
|
|
6949
|
+
parameters: [
|
|
6950
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } }
|
|
6951
|
+
],
|
|
6952
|
+
responses: objResponse({
|
|
6953
|
+
project_id: { type: "string" },
|
|
6954
|
+
contact_ids: { type: "array", items: { type: "string" } }
|
|
6955
|
+
})
|
|
6956
|
+
}
|
|
6957
|
+
},
|
|
6958
|
+
"/v1/projects/{project_id}/contact-memberships": {
|
|
6959
|
+
get: {
|
|
6960
|
+
operationId: "listContactProjectMemberships",
|
|
6961
|
+
summary: "List the complete authoritative contact membership collection for a project",
|
|
6962
|
+
parameters: [
|
|
6963
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } },
|
|
6964
|
+
{ name: "max_items", in: "query", required: true, schema: { type: "integer", minimum: 1 } }
|
|
6965
|
+
],
|
|
6966
|
+
responses: {
|
|
6967
|
+
"200": {
|
|
6968
|
+
content: {
|
|
6969
|
+
"application/json": {
|
|
6970
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipListResult" }
|
|
6971
|
+
}
|
|
6972
|
+
}
|
|
6973
|
+
}
|
|
6974
|
+
}
|
|
6975
|
+
}
|
|
6976
|
+
},
|
|
6977
|
+
"/v1/projects/{project_id}/contact-memberships/{contact_id}": {
|
|
6978
|
+
get: {
|
|
6979
|
+
operationId: "readContactProjectMembership",
|
|
6980
|
+
summary: "Read one authoritative contact-project membership snapshot",
|
|
6981
|
+
parameters: [
|
|
6982
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } },
|
|
6983
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } }
|
|
6984
|
+
],
|
|
6985
|
+
responses: {
|
|
6986
|
+
"200": {
|
|
6987
|
+
content: {
|
|
6988
|
+
"application/json": {
|
|
6989
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" }
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6993
|
+
}
|
|
6994
|
+
}
|
|
6995
|
+
},
|
|
6996
|
+
"/v1/projects/{project_id}/contact-memberships/{contact_id}/attach": {
|
|
6997
|
+
post: {
|
|
6998
|
+
operationId: "attachContactProjectMembership",
|
|
6999
|
+
summary: "Attach a contact to a project under expected-version CAS with a replay-safe receipt",
|
|
7000
|
+
parameters: [
|
|
7001
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } },
|
|
7002
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } }
|
|
7003
|
+
],
|
|
7004
|
+
requestBody: {
|
|
7005
|
+
required: true,
|
|
7006
|
+
content: {
|
|
7007
|
+
"application/json": {
|
|
7008
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationInput" }
|
|
7009
|
+
}
|
|
7010
|
+
}
|
|
7011
|
+
},
|
|
7012
|
+
responses: {
|
|
7013
|
+
"200": {
|
|
7014
|
+
content: {
|
|
7015
|
+
"application/json": {
|
|
7016
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationResult" }
|
|
7017
|
+
}
|
|
7018
|
+
}
|
|
7019
|
+
}
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
},
|
|
7023
|
+
"/v1/projects/{project_id}/contact-memberships/{contact_id}/detach": {
|
|
7024
|
+
post: {
|
|
7025
|
+
operationId: "detachContactProjectMembership",
|
|
7026
|
+
summary: "Detach a contact from a project under expected-version CAS with a replay-safe receipt",
|
|
7027
|
+
parameters: [
|
|
7028
|
+
{ name: "project_id", in: "path", required: true, schema: { type: "string" } },
|
|
7029
|
+
{ name: "contact_id", in: "path", required: true, schema: { type: "string" } }
|
|
7030
|
+
],
|
|
7031
|
+
requestBody: {
|
|
7032
|
+
required: true,
|
|
7033
|
+
content: {
|
|
7034
|
+
"application/json": {
|
|
7035
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationInput" }
|
|
7036
|
+
}
|
|
7037
|
+
}
|
|
7038
|
+
},
|
|
7039
|
+
responses: {
|
|
7040
|
+
"200": {
|
|
7041
|
+
content: {
|
|
7042
|
+
"application/json": {
|
|
7043
|
+
schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationResult" }
|
|
7044
|
+
}
|
|
7045
|
+
}
|
|
7046
|
+
}
|
|
7047
|
+
}
|
|
7048
|
+
}
|
|
7049
|
+
},
|
|
6461
7050
|
"/v1/stats": {
|
|
6462
7051
|
get: {
|
|
6463
7052
|
operationId: "getStats",
|
|
@@ -6482,6 +7071,7 @@ export {
|
|
|
6482
7071
|
DuplicateAudienceIdError,
|
|
6483
7072
|
ContactsV1Client,
|
|
6484
7073
|
ApiError as ContactsV1ApiError,
|
|
7074
|
+
ContactProjectMembershipConflictError,
|
|
6485
7075
|
ContactNotFoundError,
|
|
6486
7076
|
CompanyNotFoundError,
|
|
6487
7077
|
CONSENT_STATUSES,
|