@hasna/contacts 0.6.33 → 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/dist/mcp/index.js CHANGED
@@ -6741,7 +6741,10 @@ function uuid2() {
6741
6741
  return crypto.randomUUID();
6742
6742
  }
6743
6743
  function now() {
6744
- return new Date().toISOString();
6744
+ const currentMs = Date.now();
6745
+ const nextMs = currentMs > lastNowMs ? currentMs : lastNowMs + 1;
6746
+ lastNowMs = nextMs;
6747
+ return new Date(nextMs).toISOString();
6745
6748
  }
6746
6749
  function quoteIdentifier(identifier) {
6747
6750
  return `"${identifier.replaceAll('"', '""')}"`;
@@ -6779,7 +6782,7 @@ function runMigrations(db) {
6779
6782
  db.exec(`INSERT OR REPLACE INTO _migrations(version) VALUES(${i})`);
6780
6783
  }
6781
6784
  }
6782
- var MIGRATIONS, _db = null;
6785
+ var MIGRATIONS, _db = null, lastNowMs = 0;
6783
6786
  var init_database = __esm(() => {
6784
6787
  init_sqlite_adapter();
6785
6788
  init_paths();
@@ -7351,6 +7354,81 @@ var init_database = __esm(() => {
7351
7354
  );
7352
7355
 
7353
7356
  CREATE INDEX IF NOT EXISTS idx_contacts_tombstones_deleted_at ON _contacts_tombstones(deleted_at);
7357
+ `,
7358
+ `
7359
+ CREATE TABLE IF NOT EXISTS contact_project_membership_states (
7360
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
7361
+ project_id TEXT NOT NULL,
7362
+ linked INTEGER NOT NULL CHECK(linked IN (0, 1)),
7363
+ revision INTEGER NOT NULL DEFAULT 0 CHECK(revision >= 0),
7364
+ updated_at TEXT NOT NULL,
7365
+ PRIMARY KEY (contact_id, project_id)
7366
+ );
7367
+
7368
+ INSERT OR IGNORE INTO contact_project_membership_states
7369
+ (contact_id, project_id, linked, revision, updated_at)
7370
+ SELECT contact_id, project_id, 1, 0, datetime('now')
7371
+ FROM contact_projects;
7372
+
7373
+ CREATE TABLE IF NOT EXISTS contact_project_membership_receipts (
7374
+ receipt_id TEXT PRIMARY KEY,
7375
+ direction TEXT NOT NULL CHECK(direction IN ('attach', 'detach')),
7376
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
7377
+ project_id TEXT NOT NULL,
7378
+ operation_id TEXT NOT NULL,
7379
+ step_id TEXT NOT NULL,
7380
+ expected_version TEXT NOT NULL,
7381
+ before_json TEXT NOT NULL,
7382
+ after_json TEXT NOT NULL,
7383
+ created_at TEXT NOT NULL,
7384
+ UNIQUE(operation_id, step_id)
7385
+ );
7386
+
7387
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_states_project
7388
+ ON contact_project_membership_states(project_id, linked, contact_id);
7389
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_receipts_target
7390
+ ON contact_project_membership_receipts(contact_id, project_id, created_at);
7391
+
7392
+ CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_insert
7393
+ AFTER INSERT ON contact_projects
7394
+ BEGIN
7395
+ INSERT INTO contact_project_membership_states
7396
+ (contact_id, project_id, linked, revision, updated_at)
7397
+ VALUES (NEW.contact_id, NEW.project_id, 1, 1, datetime('now'))
7398
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
7399
+ linked = 1,
7400
+ revision = CASE
7401
+ WHEN contact_project_membership_states.linked = 1
7402
+ THEN contact_project_membership_states.revision
7403
+ ELSE contact_project_membership_states.revision + 1
7404
+ END,
7405
+ updated_at = CASE
7406
+ WHEN contact_project_membership_states.linked = 1
7407
+ THEN contact_project_membership_states.updated_at
7408
+ ELSE datetime('now')
7409
+ END;
7410
+ END;
7411
+
7412
+ CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_delete
7413
+ AFTER DELETE ON contact_projects
7414
+ WHEN EXISTS (SELECT 1 FROM contacts WHERE id = OLD.contact_id)
7415
+ BEGIN
7416
+ INSERT INTO contact_project_membership_states
7417
+ (contact_id, project_id, linked, revision, updated_at)
7418
+ VALUES (OLD.contact_id, OLD.project_id, 0, 1, datetime('now'))
7419
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
7420
+ linked = 0,
7421
+ revision = CASE
7422
+ WHEN contact_project_membership_states.linked = 0
7423
+ THEN contact_project_membership_states.revision
7424
+ ELSE contact_project_membership_states.revision + 1
7425
+ END,
7426
+ updated_at = CASE
7427
+ WHEN contact_project_membership_states.linked = 0
7428
+ THEN contact_project_membership_states.updated_at
7429
+ ELSE datetime('now')
7430
+ END;
7431
+ END;
7354
7432
  `
7355
7433
  ];
7356
7434
  });
@@ -7448,6 +7526,219 @@ var init_tombstones = __esm(() => {
7448
7526
  init_database();
7449
7527
  });
7450
7528
 
7529
+ // src/types/project-memberships.ts
7530
+ var ContactProjectMembershipConflictError;
7531
+ var init_project_memberships = __esm(() => {
7532
+ ContactProjectMembershipConflictError = class ContactProjectMembershipConflictError extends Error {
7533
+ constructor(message) {
7534
+ super(message);
7535
+ this.name = "ContactProjectMembershipConflictError";
7536
+ }
7537
+ };
7538
+ });
7539
+
7540
+ // src/db/project-memberships.ts
7541
+ import { createHash } from "crypto";
7542
+ function digest(value) {
7543
+ return createHash("sha256").update(value).digest("hex");
7544
+ }
7545
+ function membershipVersion(contactId, projectId, linked, revision) {
7546
+ return `cpmv_${digest(JSON.stringify([contactId, projectId, linked, revision])).slice(0, 32)}`;
7547
+ }
7548
+ function receiptId(input) {
7549
+ return `cpmr_${digest(JSON.stringify([
7550
+ input.operation_id,
7551
+ input.step_id,
7552
+ input.contact_id,
7553
+ input.project_id
7554
+ ])).slice(0, 32)}`;
7555
+ }
7556
+ function stateRow(contactId, projectId, db) {
7557
+ const persisted = db.query(`SELECT contact_id, project_id, linked, revision
7558
+ FROM contact_project_membership_states
7559
+ WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId);
7560
+ if (persisted)
7561
+ return persisted;
7562
+ const linked = Boolean(db.query(`SELECT 1 AS present FROM contact_projects WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId));
7563
+ return { contact_id: contactId, project_id: projectId, linked: linked ? 1 : 0, revision: 0 };
7564
+ }
7565
+ function snapshot(row) {
7566
+ const linked = Boolean(row.linked);
7567
+ return {
7568
+ contact_id: row.contact_id,
7569
+ project_id: row.project_id,
7570
+ linked,
7571
+ version: membershipVersion(row.contact_id, row.project_id, linked, row.revision)
7572
+ };
7573
+ }
7574
+ function required2(value, name) {
7575
+ const normalized = value.trim();
7576
+ if (!normalized)
7577
+ throw new Error(`${name} is required`);
7578
+ return normalized;
7579
+ }
7580
+ function normalizeInput(input) {
7581
+ return {
7582
+ contact_id: required2(input.contact_id, "contact_id"),
7583
+ project_id: required2(input.project_id, "project_id"),
7584
+ operation_id: required2(input.operation_id, "operation_id"),
7585
+ step_id: required2(input.step_id, "step_id"),
7586
+ expected_version: required2(input.expected_version, "expected_version")
7587
+ };
7588
+ }
7589
+ function parseSnapshot(value) {
7590
+ return JSON.parse(value);
7591
+ }
7592
+ function replay(row, direction, input) {
7593
+ if (row.direction !== direction || row.contact_id !== input.contact_id || row.project_id !== input.project_id || row.expected_version !== input.expected_version) {
7594
+ throw new ContactProjectMembershipConflictError(`operation_id/step_id already accepted for a different contact-project membership mutation`);
7595
+ }
7596
+ return {
7597
+ outcome: "duplicate_of_accepted",
7598
+ operation_id: row.operation_id,
7599
+ step_id: row.step_id,
7600
+ before: parseSnapshot(row.before_json),
7601
+ after: parseSnapshot(row.after_json),
7602
+ receipt_id: row.receipt_id
7603
+ };
7604
+ }
7605
+ function transitionWithoutReceipt(contactId, projectId, linked, db) {
7606
+ const beforeRow = stateRow(contactId, projectId, db);
7607
+ const changed = Boolean(beforeRow.linked) !== linked;
7608
+ const afterRow = {
7609
+ ...beforeRow,
7610
+ linked: linked ? 1 : 0,
7611
+ revision: changed ? beforeRow.revision + 1 : beforeRow.revision
7612
+ };
7613
+ const changedAt = now();
7614
+ db.run(`INSERT INTO contact_project_membership_states
7615
+ (contact_id, project_id, linked, revision, updated_at)
7616
+ VALUES (?, ?, ?, ?, ?)
7617
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
7618
+ linked = excluded.linked,
7619
+ revision = excluded.revision,
7620
+ updated_at = excluded.updated_at`, [contactId, projectId, afterRow.linked, afterRow.revision, changedAt]);
7621
+ if (linked) {
7622
+ db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, projectId]);
7623
+ } else {
7624
+ db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [contactId, projectId]);
7625
+ }
7626
+ return changed;
7627
+ }
7628
+ function setContactProjectMembershipWithoutReceipt(contactId, projectId, linked, db = getDatabase()) {
7629
+ const normalizedContactId = required2(contactId, "contact_id");
7630
+ const normalizedProjectId = required2(projectId, "project_id");
7631
+ return db.transaction(() => transitionWithoutReceipt(normalizedContactId, normalizedProjectId, linked, db));
7632
+ }
7633
+ function replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, db = getDatabase()) {
7634
+ const normalizedContactId = required2(contactId, "contact_id");
7635
+ const normalizedProjectIds = [...new Set(projectIds.map((projectId) => required2(projectId, "project_id")))];
7636
+ return db.transaction(() => {
7637
+ const currentRows = db.query(`SELECT project_id FROM contact_projects WHERE contact_id = ?`).all(normalizedContactId);
7638
+ const desired = new Set(normalizedProjectIds);
7639
+ const population = new Set([...currentRows.map((row) => row.project_id), ...normalizedProjectIds]);
7640
+ for (const projectId of population) {
7641
+ transitionWithoutReceipt(normalizedContactId, projectId, desired.has(projectId), db);
7642
+ }
7643
+ return normalizedProjectIds;
7644
+ });
7645
+ }
7646
+ function readContactProjectMembership(contactId, projectId, db = getDatabase()) {
7647
+ return snapshot(stateRow(required2(contactId, "contact_id"), required2(projectId, "project_id"), db));
7648
+ }
7649
+ function listContactProjectMemberships(projectId, maxItems, db = getDatabase()) {
7650
+ const normalizedProjectId = required2(projectId, "project_id");
7651
+ if (!Number.isInteger(maxItems) || maxItems < 1)
7652
+ throw new Error("max_items must be a positive integer");
7653
+ const rows = db.query(`SELECT cp.contact_id, cp.project_id,
7654
+ COALESCE(state.linked, 1) AS linked,
7655
+ COALESCE(state.revision, 0) AS revision
7656
+ FROM contact_projects cp
7657
+ LEFT JOIN contact_project_membership_states state
7658
+ ON state.contact_id = cp.contact_id AND state.project_id = cp.project_id
7659
+ WHERE cp.project_id = ?
7660
+ ORDER BY cp.contact_id ASC
7661
+ LIMIT ?`).all(normalizedProjectId, maxItems + 1);
7662
+ if (rows.length > maxItems) {
7663
+ throw new Error(`contact project membership collection exceeds max_items=${maxItems}`);
7664
+ }
7665
+ const contactIds = rows.map((row) => row.contact_id);
7666
+ return {
7667
+ project_id: normalizedProjectId,
7668
+ contact_ids: contactIds,
7669
+ complete: true,
7670
+ membership_revision: `cpml_${digest(JSON.stringify(rows.map((row) => snapshot(row)))).slice(0, 32)}`
7671
+ };
7672
+ }
7673
+ function mutateContactProjectMembership(direction, rawInput, db = getDatabase()) {
7674
+ const input = normalizeInput(rawInput);
7675
+ return db.transaction(() => {
7676
+ const existingReceipt = db.query(`SELECT direction, contact_id, project_id, operation_id, step_id, expected_version,
7677
+ before_json, after_json, receipt_id
7678
+ FROM contact_project_membership_receipts
7679
+ WHERE operation_id = ? AND step_id = ?`).get(input.operation_id, input.step_id);
7680
+ if (existingReceipt)
7681
+ return replay(existingReceipt, direction, input);
7682
+ const contact = db.query(`SELECT id FROM contacts WHERE id = ?`).get(input.contact_id);
7683
+ if (!contact)
7684
+ throw new Error(`contact not found: ${input.contact_id}`);
7685
+ const beforeRow = stateRow(input.contact_id, input.project_id, db);
7686
+ const before = snapshot(beforeRow);
7687
+ if (before.version !== input.expected_version) {
7688
+ throw new ContactProjectMembershipConflictError(`contact project membership expected_version conflict: expected ${input.expected_version}, current ${before.version}`);
7689
+ }
7690
+ const desiredLinked = direction === "attach";
7691
+ const changed = before.linked !== desiredLinked;
7692
+ const afterRow = {
7693
+ ...beforeRow,
7694
+ linked: desiredLinked ? 1 : 0,
7695
+ revision: changed ? beforeRow.revision + 1 : beforeRow.revision
7696
+ };
7697
+ const changedAt = now();
7698
+ db.run(`INSERT INTO contact_project_membership_states
7699
+ (contact_id, project_id, linked, revision, updated_at)
7700
+ VALUES (?, ?, ?, ?, ?)
7701
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
7702
+ linked = excluded.linked,
7703
+ revision = excluded.revision,
7704
+ updated_at = excluded.updated_at`, [input.contact_id, input.project_id, afterRow.linked, afterRow.revision, changedAt]);
7705
+ if (desiredLinked) {
7706
+ db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [input.contact_id, input.project_id]);
7707
+ } else {
7708
+ db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [input.contact_id, input.project_id]);
7709
+ }
7710
+ const after = snapshot(afterRow);
7711
+ const id = receiptId(input);
7712
+ db.run(`INSERT INTO contact_project_membership_receipts (
7713
+ receipt_id, direction, contact_id, project_id, operation_id, step_id,
7714
+ expected_version, before_json, after_json, created_at
7715
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
7716
+ id,
7717
+ direction,
7718
+ input.contact_id,
7719
+ input.project_id,
7720
+ input.operation_id,
7721
+ input.step_id,
7722
+ input.expected_version,
7723
+ JSON.stringify(before),
7724
+ JSON.stringify(after),
7725
+ changedAt
7726
+ ]);
7727
+ return {
7728
+ outcome: changed ? "accepted" : "duplicate_of_accepted",
7729
+ operation_id: input.operation_id,
7730
+ step_id: input.step_id,
7731
+ before,
7732
+ after,
7733
+ receipt_id: id
7734
+ };
7735
+ });
7736
+ }
7737
+ var init_project_memberships2 = __esm(() => {
7738
+ init_database();
7739
+ init_project_memberships();
7740
+ });
7741
+
7451
7742
  // src/db/contacts.ts
7452
7743
  var exports_contacts = {};
7453
7744
  __export(exports_contacts, {
@@ -8046,34 +8337,32 @@ function autoLinkContactToCompany(contactId, db) {
8046
8337
  }
8047
8338
  function linkContactToProject(contactId, projectId, db) {
8048
8339
  const d = db || getDatabase();
8049
- d.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, projectId]);
8340
+ setContactProjectMembershipWithoutReceipt(contactId, projectId, true, d);
8050
8341
  }
8051
8342
  function unlinkContactFromProject(contactId, projectId, db) {
8052
8343
  const d = db || getDatabase();
8053
- d.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [contactId, projectId]);
8344
+ setContactProjectMembershipWithoutReceipt(contactId, projectId, false, d);
8054
8345
  }
8055
8346
  function getContactProjectIds(contactId, db) {
8056
8347
  const d = db || getDatabase();
8057
- const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id = ?`).all(contactId);
8348
+ const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id = ? ORDER BY project_id ASC`).all(contactId);
8058
8349
  return rows.map((r) => r.project_id);
8059
8350
  }
8060
8351
  function listContactIdsByProject(projectId, db) {
8061
8352
  const d = db || getDatabase();
8062
- const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id = ?`).all(projectId);
8353
+ const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id = ? ORDER BY contact_id ASC`).all(projectId);
8063
8354
  return rows.map((r) => r.contact_id);
8064
8355
  }
8065
8356
  function setContactProjects(contactId, projectIds, db) {
8066
8357
  const d = db || getDatabase();
8067
- d.run(`DELETE FROM contact_projects WHERE contact_id = ?`, [contactId]);
8068
- for (const pid of projectIds) {
8069
- d.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, pid]);
8070
- }
8358
+ replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, d);
8071
8359
  }
8072
8360
  var init_contacts = __esm(() => {
8073
8361
  init_types();
8074
8362
  init_database();
8075
8363
  init_activity();
8076
8364
  init_tombstones();
8365
+ init_project_memberships2();
8077
8366
  });
8078
8367
 
8079
8368
  // src/db/events.ts
@@ -21293,6 +21582,7 @@ function getStorageStatus(db = getDatabase()) {
21293
21582
 
21294
21583
  // src/store/index.ts
21295
21584
  init_contacts();
21585
+ init_project_memberships2();
21296
21586
 
21297
21587
  // src/db/companies.ts
21298
21588
  init_types();
@@ -22822,7 +23112,7 @@ init_database();
22822
23112
  init_database();
22823
23113
  import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync3, unlinkSync } from "fs";
22824
23114
  import { join as join3 } from "path";
22825
- import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
23115
+ import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash as createHash2 } from "crypto";
22826
23116
  var VAULT_DIR = getDataDir();
22827
23117
  var VAULT_CONFIG = join3(VAULT_DIR, "vault.json");
22828
23118
  var VAULT_SESSION = join3(VAULT_DIR, ".vault-session");
@@ -22868,7 +23158,7 @@ function initVault(passphrase) {
22868
23158
  mkdirSync3(DOCUMENTS_DIR, { recursive: true });
22869
23159
  const salt = randomBytes(32);
22870
23160
  const key = deriveKey(passphrase, salt);
22871
- const keyHash = createHash("sha256").update(key).digest("hex");
23161
+ const keyHash = createHash2("sha256").update(key).digest("hex");
22872
23162
  const config2 = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
22873
23163
  writeFileSync(VAULT_CONFIG, JSON.stringify(config2, null, 2));
22874
23164
  _derivedKey = key;
@@ -22883,7 +23173,7 @@ function unlockVault(passphrase) {
22883
23173
  const config2 = JSON.parse(readFileSync2(VAULT_CONFIG, "utf-8"));
22884
23174
  const salt = Buffer.from(config2.salt, "hex");
22885
23175
  const key = deriveKey(passphrase, salt);
22886
- const keyHash = createHash("sha256").update(key).digest("hex");
23176
+ const keyHash = createHash2("sha256").update(key).digest("hex");
22887
23177
  if (keyHash !== config2.key_hash)
22888
23178
  return false;
22889
23179
  _derivedKey = key;
@@ -24419,6 +24709,15 @@ class LocalStore {
24419
24709
  async listContactIdsByProject(projectId) {
24420
24710
  return listContactIdsByProject(projectId, this.db);
24421
24711
  }
24712
+ async readContactProjectMembership(contactId, projectId) {
24713
+ return readContactProjectMembership(contactId, projectId, this.db);
24714
+ }
24715
+ async listContactProjectMemberships(projectId, maxItems) {
24716
+ return listContactProjectMemberships(projectId, maxItems, this.db);
24717
+ }
24718
+ async mutateContactProjectMembership(direction, input) {
24719
+ return mutateContactProjectMembership(direction, input, this.db);
24720
+ }
24422
24721
  async createCompany(input) {
24423
24722
  return createCompany(input, this.db);
24424
24723
  }
@@ -25041,20 +25340,33 @@ class ApiStore {
25041
25340
  async findContactByEmailAddress(address) {
25042
25341
  return this.getContactByEmail(address);
25043
25342
  }
25044
- async linkContactToProject() {
25045
- return unavailable("linkContactToProject");
25343
+ async linkContactToProject(contactId, projectId) {
25344
+ await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
25046
25345
  }
25047
- async unlinkContactFromProject() {
25048
- return unavailable("unlinkContactFromProject");
25346
+ async unlinkContactFromProject(contactId, projectId) {
25347
+ await this.del(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
25348
+ }
25349
+ async getContactProjectIds(contactId) {
25350
+ return pick2(await this.g(`/contacts/${this.enc(contactId)}/projects`), "project_ids") ?? [];
25351
+ }
25352
+ async setContactProjects(contactId, projectIds) {
25353
+ await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects`, { project_ids: projectIds });
25354
+ }
25355
+ async listContactIdsByProject(projectId) {
25356
+ return pick2(await this.g(`/projects/${this.enc(projectId)}/contacts`), "contact_ids") ?? [];
25049
25357
  }
25050
- async getContactProjectIds() {
25051
- return unavailable("getContactProjectIds");
25358
+ async readContactProjectMembership(contactId, projectId) {
25359
+ return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships/${this.enc(contactId)}`);
25052
25360
  }
25053
- async setContactProjects() {
25054
- return unavailable("setContactProjects");
25361
+ async listContactProjectMemberships(projectId, maxItems) {
25362
+ return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships`, { query: { max_items: maxItems } });
25055
25363
  }
25056
- async listContactIdsByProject() {
25057
- return unavailable("listContactIdsByProject");
25364
+ async mutateContactProjectMembership(direction, input) {
25365
+ return this.client.transport.post(`/projects/${this.enc(input.project_id)}/contact-memberships/${this.enc(input.contact_id)}/${direction}`, {
25366
+ operation_id: input.operation_id,
25367
+ step_id: input.step_id,
25368
+ expected_version: input.expected_version
25369
+ });
25058
25370
  }
25059
25371
  async createCompany(input) {
25060
25372
  const res = await this.client.create("companies", stripUndefined(input));
@@ -27448,8 +27760,8 @@ var _contactsAgents = new Map;
27448
27760
  var advancedHandlers = {
27449
27761
  get_field_history: async (a) => json3({ history: await getStore().getFieldHistory(a.contact_id, a.field_name) }),
27450
27762
  get_contact_at: async (a) => {
27451
- const snapshot = await getStore().getContactAt(a.contact_id, a.timestamp);
27452
- return json3({ contact_id: a.contact_id, timestamp: a.timestamp, snapshot });
27763
+ const snapshot2 = await getStore().getContactAt(a.contact_id, a.timestamp);
27764
+ return json3({ contact_id: a.contact_id, timestamp: a.timestamp, snapshot: snapshot2 });
27453
27765
  },
27454
27766
  get_job_history: async (a) => json3({ history: await getStore().getJobHistory(a.contact_id) }),
27455
27767
  add_job_entry: async (a) => json3(await getStore().addJobEntry(a.contact_id, {
@@ -27925,11 +28237,11 @@ function jsonSchemaToZodType(schema) {
27925
28237
  }
27926
28238
  function jsonSchemaToZodObject(schema) {
27927
28239
  const objectSchema = schema && typeof schema === "object" ? schema : {};
27928
- const required2 = new Set(objectSchema.required ?? []);
28240
+ const required3 = new Set(objectSchema.required ?? []);
27929
28241
  const shape = {};
27930
28242
  for (const [key, value] of Object.entries(objectSchema.properties ?? {})) {
27931
28243
  const propertySchema = jsonSchemaToZodType(value);
27932
- shape[key] = required2.has(key) ? propertySchema : propertySchema.optional();
28244
+ shape[key] = required3.has(key) ? propertySchema : propertySchema.optional();
27933
28245
  }
27934
28246
  return describeSchema(exports_external.object(shape).passthrough(), objectSchema.description);
27935
28247
  }
@@ -12,5 +12,5 @@
12
12
  * const { contacts } = await client.listContacts({ limit: 20 });
13
13
  */
14
14
  export { ContactsV1Client, ApiError as ContactsV1ApiError } from "./v1.generated.js";
15
- export type { ContactsV1ClientOptions, Contact as ContactsV1Contact, Company as ContactsV1Company, Tag as ContactsV1Tag, CreateContactInput as ContactsV1CreateContactInput, UpdateContactInput as ContactsV1UpdateContactInput, CreateCompanyInput as ContactsV1CreateCompanyInput, UpdateCompanyInput as ContactsV1UpdateCompanyInput, CreateTagInput as ContactsV1CreateTagInput, UpdateTagInput as ContactsV1UpdateTagInput, } from "./v1.generated.js";
15
+ export type { ContactsV1ClientOptions, Contact as ContactsV1Contact, Company as ContactsV1Company, Tag as ContactsV1Tag, CreateContactInput as ContactsV1CreateContactInput, UpdateContactInput as ContactsV1UpdateContactInput, CreateCompanyInput as ContactsV1CreateCompanyInput, UpdateCompanyInput as ContactsV1UpdateCompanyInput, CreateTagInput as ContactsV1CreateTagInput, UpdateTagInput as ContactsV1UpdateTagInput, ProjectIdsInput as ContactsV1ProjectIdsInput, ContactProjectMembershipSnapshot as ContactsV1ProjectMembershipSnapshot, ContactProjectMembershipMutationInput as ContactsV1ProjectMembershipMutationInput, ContactProjectMembershipMutationResult as ContactsV1ProjectMembershipMutationResult, ContactProjectMembershipListResult as ContactsV1ProjectMembershipListResult, } from "./v1.generated.js";
16
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACrF,YAAY,EACV,uBAAuB,EACvB,OAAO,IAAI,iBAAiB,EAC5B,OAAO,IAAI,iBAAiB,EAC5B,GAAG,IAAI,aAAa,EACpB,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,cAAc,IAAI,wBAAwB,EAC1C,cAAc,IAAI,wBAAwB,GAC3C,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sdk/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,gBAAgB,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACrF,YAAY,EACV,uBAAuB,EACvB,OAAO,IAAI,iBAAiB,EAC5B,OAAO,IAAI,iBAAiB,EAC5B,GAAG,IAAI,aAAa,EACpB,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,kBAAkB,IAAI,4BAA4B,EAClD,cAAc,IAAI,wBAAwB,EAC1C,cAAc,IAAI,wBAAwB,EAC1C,eAAe,IAAI,yBAAyB,EAC5C,gCAAgC,IAAI,mCAAmC,EACvE,qCAAqC,IAAI,wCAAwC,EACjF,sCAAsC,IAAI,yCAAyC,EACnF,kCAAkC,IAAI,qCAAqC,GAC5E,MAAM,mBAAmB,CAAC"}
package/dist/sdk/index.js CHANGED
@@ -103,6 +103,34 @@ class ContactsV1Client {
103
103
  init
104
104
  });
105
105
  }
106
+ async getContactProjectIds(contactId, init) {
107
+ return this.request("GET", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects`, {
108
+ body: undefined,
109
+ query: undefined,
110
+ init
111
+ });
112
+ }
113
+ async setContactProjects(contactId, body, init) {
114
+ return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects`, {
115
+ body,
116
+ query: undefined,
117
+ init
118
+ });
119
+ }
120
+ async linkContactToProject(contactId, projectId, init) {
121
+ return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects/${encodeURIComponent(String(projectId))}`, {
122
+ body: undefined,
123
+ query: undefined,
124
+ init
125
+ });
126
+ }
127
+ async unlinkContactFromProject(contactId, projectId, init) {
128
+ return this.request("DELETE", `/v1/contacts/${encodeURIComponent(String(contactId))}/projects/${encodeURIComponent(String(projectId))}`, {
129
+ body: undefined,
130
+ query: undefined,
131
+ init
132
+ });
133
+ }
106
134
  async addTagToContact(contactId, tagId, init) {
107
135
  return this.request("PUT", `/v1/contacts/${encodeURIComponent(String(contactId))}/tags/${encodeURIComponent(String(tagId))}`, {
108
136
  body: undefined,
@@ -138,6 +166,41 @@ class ContactsV1Client {
138
166
  init
139
167
  });
140
168
  }
169
+ async listContactProjectMemberships(projectId, query, init) {
170
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships`, {
171
+ body: undefined,
172
+ query,
173
+ init
174
+ });
175
+ }
176
+ async readContactProjectMembership(projectId, contactId, init) {
177
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}`, {
178
+ body: undefined,
179
+ query: undefined,
180
+ init
181
+ });
182
+ }
183
+ async attachContactProjectMembership(projectId, contactId, body, init) {
184
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}/attach`, {
185
+ body,
186
+ query: undefined,
187
+ init
188
+ });
189
+ }
190
+ async detachContactProjectMembership(projectId, contactId, body, init) {
191
+ return this.request("POST", `/v1/projects/${encodeURIComponent(String(projectId))}/contact-memberships/${encodeURIComponent(String(contactId))}/detach`, {
192
+ body,
193
+ query: undefined,
194
+ init
195
+ });
196
+ }
197
+ async listContactIdsByProject(projectId, init) {
198
+ return this.request("GET", `/v1/projects/${encodeURIComponent(String(projectId))}/contacts`, {
199
+ body: undefined,
200
+ query: undefined,
201
+ init
202
+ });
203
+ }
141
204
  async getStats(init) {
142
205
  return this.request("GET", `/v1/stats`, {
143
206
  body: undefined,
@@ -12,6 +12,7 @@ export interface Contact {
12
12
  "sensitivity"?: string;
13
13
  "archived"?: boolean;
14
14
  "priority"?: number;
15
+ "tags": Array<Tag>;
15
16
  "created_at"?: string;
16
17
  "updated_at"?: string;
17
18
  }
@@ -81,6 +82,34 @@ export interface UpdateTagInput {
81
82
  "color"?: string;
82
83
  "description"?: string;
83
84
  }
85
+ export interface ProjectIdsInput {
86
+ "project_ids": Array<string>;
87
+ }
88
+ export interface ContactProjectMembershipSnapshot {
89
+ "contact_id": string;
90
+ "project_id": string;
91
+ "linked": boolean;
92
+ "version": string;
93
+ }
94
+ export interface ContactProjectMembershipMutationInput {
95
+ "operation_id": string;
96
+ "step_id": string;
97
+ "expected_version": string;
98
+ }
99
+ export interface ContactProjectMembershipMutationResult {
100
+ "outcome": "accepted" | "duplicate_of_accepted";
101
+ "operation_id": string;
102
+ "step_id": string;
103
+ "before": ContactProjectMembershipSnapshot;
104
+ "after": ContactProjectMembershipSnapshot;
105
+ "receipt_id": string;
106
+ }
107
+ export interface ContactProjectMembershipListResult {
108
+ "project_id": string;
109
+ "contact_ids": Array<string>;
110
+ "complete": boolean;
111
+ "membership_revision": string;
112
+ }
84
113
  export interface ContactsV1ClientOptions {
85
114
  /** Base URL, e.g. process.env.APP_API_URL. */
86
115
  baseUrl: string;
@@ -134,6 +163,7 @@ export declare class ContactsV1Client {
134
163
  "q"?: string;
135
164
  "company_id"?: string;
136
165
  "status"?: string;
166
+ "tag_id"?: string;
137
167
  "limit"?: number;
138
168
  "offset"?: number;
139
169
  }, init?: RequestInit): Promise<{
@@ -144,6 +174,28 @@ export declare class ContactsV1Client {
144
174
  createContact(body: CreateContactInput, init?: RequestInit): Promise<{
145
175
  "contact"?: Contact;
146
176
  }>;
177
+ /** List project ids attached to a contact */
178
+ getContactProjectIds(contactId: string, init?: RequestInit): Promise<{
179
+ "contact_id"?: string;
180
+ "project_ids"?: Array<string>;
181
+ }>;
182
+ /** Atomically replace a contact's project memberships */
183
+ setContactProjects(contactId: string, body: ProjectIdsInput, init?: RequestInit): Promise<{
184
+ "contact_id"?: string;
185
+ "project_ids"?: Array<string>;
186
+ }>;
187
+ /** Attach a contact to a project idempotently */
188
+ linkContactToProject(contactId: string, projectId: string, init?: RequestInit): Promise<{
189
+ "attached"?: boolean;
190
+ "contact_id"?: string;
191
+ "project_id"?: string;
192
+ }>;
193
+ /** Detach a contact from a project */
194
+ unlinkContactFromProject(contactId: string, projectId: string, init?: RequestInit): Promise<{
195
+ "removed"?: boolean;
196
+ "contact_id"?: string;
197
+ "project_id"?: string;
198
+ }>;
147
199
  /** Attach a tag to a contact idempotently */
148
200
  addTagToContact(contactId: string, tagId: string, init?: RequestInit): Promise<{
149
201
  "attached"?: boolean;
@@ -169,6 +221,21 @@ export declare class ContactsV1Client {
169
221
  updateContact(id: string, body: UpdateContactInput, init?: RequestInit): Promise<{
170
222
  "contact"?: Contact;
171
223
  }>;
224
+ /** List the complete authoritative contact membership collection for a project */
225
+ listContactProjectMemberships(projectId: string, query?: {
226
+ "max_items": number;
227
+ }, init?: RequestInit): Promise<ContactProjectMembershipListResult>;
228
+ /** Read one authoritative contact-project membership snapshot */
229
+ readContactProjectMembership(projectId: string, contactId: string, init?: RequestInit): Promise<ContactProjectMembershipSnapshot>;
230
+ /** Attach a contact to a project under expected-version CAS with a replay-safe receipt */
231
+ attachContactProjectMembership(projectId: string, contactId: string, body: ContactProjectMembershipMutationInput, init?: RequestInit): Promise<ContactProjectMembershipMutationResult>;
232
+ /** Detach a contact from a project under expected-version CAS with a replay-safe receipt */
233
+ detachContactProjectMembership(projectId: string, contactId: string, body: ContactProjectMembershipMutationInput, init?: RequestInit): Promise<ContactProjectMembershipMutationResult>;
234
+ /** List contact ids attached to a project */
235
+ listContactIdsByProject(projectId: string, init?: RequestInit): Promise<{
236
+ "project_id"?: string;
237
+ "contact_ids"?: Array<string>;
238
+ }>;
172
239
  /** Aggregate counts */
173
240
  getStats(init?: RequestInit): Promise<{
174
241
  "contacts"?: number;