@hasna/contacts 0.6.34 → 0.6.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/README.md +10 -1
  2. package/dashboard/dist/assets/index-0l6aQb1t.css +1 -0
  3. package/dashboard/dist/assets/index-opnZdkVD.js +229 -0
  4. package/dashboard/dist/index.html +13 -0
  5. package/dist/cli/commands/advanced.d.ts +3 -0
  6. package/dist/cli/commands/advanced.d.ts.map +1 -0
  7. package/dist/cli/commands/audience.d.ts +18 -0
  8. package/dist/cli/commands/audience.d.ts.map +1 -0
  9. package/dist/cli/commands/core.d.ts +3 -0
  10. package/dist/cli/commands/core.d.ts.map +1 -0
  11. package/dist/cli/commands/crm.d.ts +3 -0
  12. package/dist/cli/commands/crm.d.ts.map +1 -0
  13. package/dist/cli/index.d.ts +3 -0
  14. package/dist/cli/index.d.ts.map +1 -0
  15. package/dist/cli/index.js +2994 -579
  16. package/dist/cli/storage.d.ts +3 -0
  17. package/dist/cli/storage.d.ts.map +1 -0
  18. package/dist/cli/utils.d.ts +10 -0
  19. package/dist/cli/utils.d.ts.map +1 -0
  20. package/dist/cloud/http-storage.d.ts +88 -0
  21. package/dist/cloud/http-storage.d.ts.map +1 -0
  22. package/dist/db/activity.d.ts +15 -0
  23. package/dist/db/activity.d.ts.map +1 -0
  24. package/dist/db/applications.d.ts +10 -0
  25. package/dist/db/applications.d.ts.map +1 -0
  26. package/dist/db/audiences.d.ts +51 -0
  27. package/dist/db/audiences.d.ts.map +1 -0
  28. package/dist/db/companies.d.ts +19 -0
  29. package/dist/db/companies.d.ts.map +1 -0
  30. package/dist/db/contact-tasks.d.ts +19 -0
  31. package/dist/db/contact-tasks.d.ts.map +1 -0
  32. package/dist/db/contacts.d.ts +30 -0
  33. package/dist/db/contacts.d.ts.map +1 -0
  34. package/dist/db/coordination.d.ts +30 -0
  35. package/dist/db/coordination.d.ts.map +1 -0
  36. package/dist/db/database.d.ts +8 -0
  37. package/dist/db/database.d.ts.map +1 -0
  38. package/dist/db/deals.d.ts +14 -0
  39. package/dist/db/deals.d.ts.map +1 -0
  40. package/dist/db/documents.d.ts +38 -0
  41. package/dist/db/documents.d.ts.map +1 -0
  42. package/dist/db/events.d.ts +14 -0
  43. package/dist/db/events.d.ts.map +1 -0
  44. package/dist/db/field-history.d.ts +17 -0
  45. package/dist/db/field-history.d.ts.map +1 -0
  46. package/dist/db/freshness.d.ts +24 -0
  47. package/dist/db/freshness.d.ts.map +1 -0
  48. package/dist/db/graph.d.ts +21 -0
  49. package/dist/db/graph.d.ts.map +1 -0
  50. package/dist/db/groups.d.ts +22 -0
  51. package/dist/db/groups.d.ts.map +1 -0
  52. package/dist/db/health.d.ts +40 -0
  53. package/dist/db/health.d.ts.map +1 -0
  54. package/dist/db/identity.d.ts +32 -0
  55. package/dist/db/identity.d.ts.map +1 -0
  56. package/dist/db/job-history.d.ts +29 -0
  57. package/dist/db/job-history.d.ts.map +1 -0
  58. package/dist/db/learnings.d.ts +43 -0
  59. package/dist/db/learnings.d.ts.map +1 -0
  60. package/dist/db/notes.d.ts +8 -0
  61. package/dist/db/notes.d.ts.map +1 -0
  62. package/dist/db/org-chart.d.ts +37 -0
  63. package/dist/db/org-chart.d.ts.map +1 -0
  64. package/dist/db/org-members.d.ts +9 -0
  65. package/dist/db/org-members.d.ts.map +1 -0
  66. package/dist/db/paths.d.ts +15 -0
  67. package/dist/db/paths.d.ts.map +1 -0
  68. package/dist/db/pg-migrations.d.ts +8 -0
  69. package/dist/db/pg-migrations.d.ts.map +1 -0
  70. package/dist/db/project-memberships.d.ts +8 -0
  71. package/dist/db/project-memberships.d.ts.map +1 -0
  72. package/dist/db/relationships.d.ts +30 -0
  73. package/dist/db/relationships.d.ts.map +1 -0
  74. package/dist/db/signals.d.ts +18 -0
  75. package/dist/db/signals.d.ts.map +1 -0
  76. package/dist/db/sqlite-adapter.d.ts +15 -0
  77. package/dist/db/sqlite-adapter.d.ts.map +1 -0
  78. package/dist/db/storage.d.ts +15 -0
  79. package/dist/db/storage.d.ts.map +1 -0
  80. package/dist/db/tags.d.ts +14 -0
  81. package/dist/db/tags.d.ts.map +1 -0
  82. package/dist/db/tombstones.d.ts +19 -0
  83. package/dist/db/tombstones.d.ts.map +1 -0
  84. package/dist/db/vendor-comms.d.ts +15 -0
  85. package/dist/db/vendor-comms.d.ts.map +1 -0
  86. package/dist/generated/storage-kit/health.d.ts +20 -0
  87. package/dist/generated/storage-kit/health.d.ts.map +1 -0
  88. package/dist/generated/storage-kit/index.d.ts +8 -0
  89. package/dist/generated/storage-kit/index.d.ts.map +1 -0
  90. package/dist/generated/storage-kit/migrations.d.ts +48 -0
  91. package/dist/generated/storage-kit/migrations.d.ts.map +1 -0
  92. package/dist/generated/storage-kit/mode.d.ts +48 -0
  93. package/dist/generated/storage-kit/mode.d.ts.map +1 -0
  94. package/dist/generated/storage-kit/pool.d.ts +34 -0
  95. package/dist/generated/storage-kit/pool.d.ts.map +1 -0
  96. package/dist/generated/storage-kit/query.d.ts +36 -0
  97. package/dist/generated/storage-kit/query.d.ts.map +1 -0
  98. package/dist/generated/storage-kit/tls.d.ts +26 -0
  99. package/dist/generated/storage-kit/tls.d.ts.map +1 -0
  100. package/dist/index.d.ts +9 -0
  101. package/dist/index.d.ts.map +1 -0
  102. package/dist/index.js +805 -75
  103. package/dist/lib/apple-contacts.d.ts +3 -0
  104. package/dist/lib/apple-contacts.d.ts.map +1 -0
  105. package/dist/lib/audience-contract.d.ts +215 -0
  106. package/dist/lib/audience-contract.d.ts.map +1 -0
  107. package/dist/lib/audit.d.ts +12 -0
  108. package/dist/lib/audit.d.ts.map +1 -0
  109. package/dist/lib/brief.d.ts +3 -0
  110. package/dist/lib/brief.d.ts.map +1 -0
  111. package/dist/lib/config.d.ts +7 -0
  112. package/dist/lib/config.d.ts.map +1 -0
  113. package/dist/lib/connector.d.ts +44 -0
  114. package/dist/lib/connector.d.ts.map +1 -0
  115. package/dist/lib/context.d.ts +5 -0
  116. package/dist/lib/context.d.ts.map +1 -0
  117. package/dist/lib/dedup.d.ts +10 -0
  118. package/dist/lib/dedup.d.ts.map +1 -0
  119. package/dist/lib/document-scanner.d.ts +8 -0
  120. package/dist/lib/document-scanner.d.ts.map +1 -0
  121. package/dist/lib/embeddings.d.ts +9 -0
  122. package/dist/lib/embeddings.d.ts.map +1 -0
  123. package/dist/lib/export.d.ts +8 -0
  124. package/dist/lib/export.d.ts.map +1 -0
  125. package/dist/lib/freshness.d.ts +19 -0
  126. package/dist/lib/freshness.d.ts.map +1 -0
  127. package/dist/lib/gmail-import.d.ts +51 -0
  128. package/dist/lib/gmail-import.d.ts.map +1 -0
  129. package/dist/lib/google-contacts.d.ts +115 -0
  130. package/dist/lib/google-contacts.d.ts.map +1 -0
  131. package/dist/lib/images.d.ts +30 -0
  132. package/dist/lib/images.d.ts.map +1 -0
  133. package/dist/lib/import.d.ts +10 -0
  134. package/dist/lib/import.d.ts.map +1 -0
  135. package/dist/lib/learning-maintenance.d.ts +8 -0
  136. package/dist/lib/learning-maintenance.d.ts.map +1 -0
  137. package/dist/lib/mailery-sync.d.ts +46 -0
  138. package/dist/lib/mailery-sync.d.ts.map +1 -0
  139. package/dist/lib/meeting-capture.d.ts +15 -0
  140. package/dist/lib/meeting-capture.d.ts.map +1 -0
  141. package/dist/lib/package-version.d.ts +6 -0
  142. package/dist/lib/package-version.d.ts.map +1 -0
  143. package/dist/lib/signals.d.ts +9 -0
  144. package/dist/lib/signals.d.ts.map +1 -0
  145. package/dist/lib/signature-parser.d.ts +36 -0
  146. package/dist/lib/signature-parser.d.ts.map +1 -0
  147. package/dist/lib/stats.d.ts +24 -0
  148. package/dist/lib/stats.d.ts.map +1 -0
  149. package/dist/lib/timeline.d.ts +11 -0
  150. package/dist/lib/timeline.d.ts.map +1 -0
  151. package/dist/lib/upcoming.d.ts +14 -0
  152. package/dist/lib/upcoming.d.ts.map +1 -0
  153. package/dist/lib/vault.d.ts +28 -0
  154. package/dist/lib/vault.d.ts.map +1 -0
  155. package/dist/mcp/handlers/advanced.d.ts +14 -0
  156. package/dist/mcp/handlers/advanced.d.ts.map +1 -0
  157. package/dist/mcp/handlers/audience.d.ts +9 -0
  158. package/dist/mcp/handlers/audience.d.ts.map +1 -0
  159. package/dist/mcp/handlers/core.d.ts +10 -0
  160. package/dist/mcp/handlers/core.d.ts.map +1 -0
  161. package/dist/mcp/handlers/crm.d.ts +14 -0
  162. package/dist/mcp/handlers/crm.d.ts.map +1 -0
  163. package/dist/mcp/handlers/index.d.ts +4 -0
  164. package/dist/mcp/handlers/index.d.ts.map +1 -0
  165. package/dist/mcp/handlers/types.d.ts +10 -0
  166. package/dist/mcp/handlers/types.d.ts.map +1 -0
  167. package/dist/mcp/http.d.ts +17 -0
  168. package/dist/mcp/http.d.ts.map +1 -0
  169. package/dist/mcp/index.d.ts +5 -0
  170. package/dist/mcp/index.d.ts.map +1 -0
  171. package/dist/mcp/index.js +1054 -291
  172. package/dist/mcp/register-tools.d.ts +3 -0
  173. package/dist/mcp/register-tools.d.ts.map +1 -0
  174. package/dist/mcp/schema.d.ts +3 -0
  175. package/dist/mcp/schema.d.ts.map +1 -0
  176. package/dist/mcp/storage-tools.d.ts +3 -0
  177. package/dist/mcp/storage-tools.d.ts.map +1 -0
  178. package/dist/mcp/tools.d.ts +3799 -0
  179. package/dist/mcp/tools.d.ts.map +1 -0
  180. package/dist/sdk/index.d.ts +16 -0
  181. package/dist/sdk/index.d.ts.map +1 -0
  182. package/dist/sdk/index.js +63 -0
  183. package/dist/sdk/v1.generated.d.ts +270 -0
  184. package/dist/sdk/v1.generated.d.ts.map +1 -0
  185. package/dist/server/cloud-index.d.ts +3 -0
  186. package/dist/server/cloud-index.d.ts.map +1 -0
  187. package/dist/server/cloud-serve.d.ts +3 -0
  188. package/dist/server/cloud-serve.d.ts.map +1 -0
  189. package/dist/server/cloud.d.ts +53 -0
  190. package/dist/server/cloud.d.ts.map +1 -0
  191. package/dist/server/index.d.ts +3 -0
  192. package/dist/server/index.d.ts.map +1 -0
  193. package/dist/server/index.js +1750 -332
  194. package/dist/server/openapi.d.ts +1134 -0
  195. package/dist/server/openapi.d.ts.map +1 -0
  196. package/dist/server/pg-store.d.ts +893 -0
  197. package/dist/server/pg-store.d.ts.map +1 -0
  198. package/dist/server/security.d.ts +24 -0
  199. package/dist/server/security.d.ts.map +1 -0
  200. package/dist/server/serve.d.ts +9 -0
  201. package/dist/server/serve.d.ts.map +1 -0
  202. package/dist/server/v1.d.ts +28 -0
  203. package/dist/server/v1.d.ts.map +1 -0
  204. package/dist/store/index.d.ts +387 -0
  205. package/dist/store/index.d.ts.map +1 -0
  206. package/dist/types/index.d.ts +856 -0
  207. package/dist/types/index.d.ts.map +1 -0
  208. package/dist/types/project-memberships.d.ts +32 -0
  209. package/dist/types/project-memberships.d.ts.map +1 -0
  210. package/hasna.contract.json +1 -1
  211. package/migrations/0015_contact_project_membership_authority.sql +88 -0
  212. package/package.json +4 -2
  213. package/vendor/fast-uri/README.md +12 -0
  214. package/dist/server/cloud-index.js +0 -9187
@@ -87,43 +87,169 @@ class SqliteAdapter {
87
87
  }
88
88
  var init_sqlite_adapter = () => {};
89
89
 
90
- // src/db/paths.ts
91
- import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
90
+ // node_modules/.pnpm/@hasna+paths@0.2.2/node_modules/@hasna/paths/dist/index.js
91
+ import { homedir } from "os";
92
92
  import { join } from "path";
93
+ function assertApp(app) {
94
+ if (typeof app !== "string" || app.length === 0) {
95
+ throw new TypeError("paths: app must be a non-empty string");
96
+ }
97
+ if (!APP_SLUG_RE.test(app)) {
98
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
99
+ }
100
+ }
101
+ function assertKind(kind) {
102
+ if (!PATH_KINDS.includes(kind)) {
103
+ throw new TypeError(`paths: invalid path kind "${kind}" \u2014 expected one of ${PATH_KINDS.join(", ")}`);
104
+ }
105
+ }
106
+ function envOf(options) {
107
+ return options.env ?? process.env;
108
+ }
109
+ function envValue(options, kind) {
110
+ const value = envOf(options)[KIND_ENV[kind]];
111
+ return typeof value === "string" && value.length > 0 ? value : undefined;
112
+ }
113
+ function isMacOS(platform) {
114
+ return platform === "darwin";
115
+ }
116
+ function baseDir(kind, options) {
117
+ assertKind(kind);
118
+ const override = envValue(options, kind);
119
+ if (override)
120
+ return override;
121
+ const home = options.home ?? homedir();
122
+ const platform = options.platform ?? process.platform;
123
+ if (isMacOS(platform)) {
124
+ switch (kind) {
125
+ case "config":
126
+ case "data":
127
+ return join(home, "Library", "Application Support", "Hasna");
128
+ case "cache":
129
+ return join(home, "Library", "Caches", "Hasna");
130
+ case "state":
131
+ return join(home, "Library", "Logs", "Hasna");
132
+ }
133
+ }
134
+ switch (kind) {
135
+ case "config":
136
+ return join(home, ".config", "hasna");
137
+ case "data":
138
+ return join(home, ".local", "share", "hasna");
139
+ case "state":
140
+ return join(home, ".local", "state", "hasna");
141
+ case "cache":
142
+ return join(home, ".cache", "hasna");
143
+ }
144
+ }
145
+ function resolvePath(kind, options) {
146
+ assertKind(kind);
147
+ assertApp(options.app);
148
+ const appSegment = options.internal === true ? join("internal", options.app) : options.app;
149
+ return join(baseDir(kind, options), appSegment);
150
+ }
151
+ function dataDir(options) {
152
+ return resolvePath("data", options);
153
+ }
154
+ function stateDir(options) {
155
+ return resolvePath("state", options);
156
+ }
157
+ var PATH_KINDS, KIND_ENV, APP_SLUG_RE;
158
+ var init_dist = __esm(() => {
159
+ PATH_KINDS = ["config", "data", "state", "cache"];
160
+ KIND_ENV = {
161
+ config: "HASNA_CONFIG_HOME",
162
+ data: "HASNA_DATA_HOME",
163
+ state: "HASNA_STATE_HOME",
164
+ cache: "HASNA_CACHE_HOME"
165
+ };
166
+ APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
167
+ });
168
+
169
+ // src/db/paths.ts
170
+ import { chmodSync, copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
171
+ import { homedir as homedir2 } from "os";
172
+ import { join as join2 } from "path";
93
173
  function ensurePrivateDir(dir) {
94
174
  if (!existsSync(dir))
95
175
  mkdirSync(dir, { recursive: true, mode: 448 });
96
176
  chmodSync(dir, 448);
97
177
  }
178
+ function home() {
179
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir2();
180
+ }
181
+ function hasContent(dir) {
182
+ if (!existsSync(dir))
183
+ return false;
184
+ try {
185
+ return readdirSync(dir).length > 0;
186
+ } catch {
187
+ return false;
188
+ }
189
+ }
190
+ function adoptLegacy(source, target) {
191
+ if (!hasContent(source) || hasContent(target))
192
+ return;
193
+ ensurePrivateDir(target);
194
+ for (const entry of readdirSync(source)) {
195
+ if (entry === ".vault-session")
196
+ continue;
197
+ if (entry === "{backups,images,documents}")
198
+ continue;
199
+ const oldPath = join2(source, entry);
200
+ const newPath = join2(target, entry);
201
+ const st = statSync(oldPath);
202
+ if (st.isDirectory()) {
203
+ cpSync(oldPath, newPath, { recursive: true });
204
+ chmodSync(newPath, 448);
205
+ } else if (st.isFile()) {
206
+ copyFileSync(oldPath, newPath);
207
+ chmodSync(newPath, 384);
208
+ }
209
+ }
210
+ }
98
211
  function getDataDir() {
99
- const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
100
- const hasnaDir = join(home, ".hasna");
101
- const newDir = join(home, ".hasna", "contacts");
102
- const oldDir = join(home, ".contacts");
103
- if (existsSync(oldDir) && !existsSync(newDir)) {
104
- ensurePrivateDir(hasnaDir);
105
- ensurePrivateDir(newDir);
106
- for (const file of readdirSync(oldDir)) {
107
- const oldPath = join(oldDir, file);
108
- if (statSync(oldPath).isFile()) {
109
- const newPath = join(newDir, file);
110
- copyFileSync(oldPath, newPath);
111
- chmodSync(newPath, 384);
112
- }
113
- }
114
- }
115
- ensurePrivateDir(hasnaDir);
116
- ensurePrivateDir(newDir);
117
- return newDir;
212
+ const base = home();
213
+ const target = dataDir({ app: "contacts", home: base });
214
+ if (!checkedDataTargets.has(target)) {
215
+ adoptLegacy(join2(base, ".hasna", "contacts"), target);
216
+ adoptLegacy(join2(base, ".contacts"), target);
217
+ checkedDataTargets.add(target);
218
+ }
219
+ ensurePrivateDir(target);
220
+ return target;
221
+ }
222
+ function getStateDir() {
223
+ const base = home();
224
+ const target = stateDir({ app: "contacts", home: base });
225
+ if (!checkedStateTargets.has(target)) {
226
+ if (!hasContent(target)) {
227
+ const legacySession = join2(base, ".hasna", "contacts", ".vault-session");
228
+ if (existsSync(legacySession)) {
229
+ ensurePrivateDir(target);
230
+ const targetSession = join2(target, ".vault-session");
231
+ copyFileSync(legacySession, targetSession);
232
+ chmodSync(targetSession, 384);
233
+ }
234
+ }
235
+ checkedStateTargets.add(target);
236
+ }
237
+ ensurePrivateDir(target);
238
+ return target;
118
239
  }
119
240
  function getDbPath() {
120
241
  if (process.env["HASNA_CONTACTS_DB_PATH"])
121
242
  return process.env["HASNA_CONTACTS_DB_PATH"];
122
243
  if (process.env["CONTACTS_DB_PATH"])
123
244
  return process.env["CONTACTS_DB_PATH"];
124
- return join(getDataDir(), "contacts.db");
245
+ return join2(getDataDir(), "contacts.db");
125
246
  }
126
- var init_paths = () => {};
247
+ var checkedDataTargets, checkedStateTargets;
248
+ var init_paths = __esm(() => {
249
+ init_dist();
250
+ checkedDataTargets = new Set;
251
+ checkedStateTargets = new Set;
252
+ });
127
253
 
128
254
  // src/db/database.ts
129
255
  var exports_database = {};
@@ -131,6 +257,7 @@ __export(exports_database, {
131
257
  uuid: () => uuid,
132
258
  resetDatabase: () => resetDatabase,
133
259
  now: () => now,
260
+ getStateDir: () => getStateDir,
134
261
  getDbPath: () => getDbPath,
135
262
  getDatabase: () => getDatabase,
136
263
  getDataDir: () => getDataDir
@@ -176,7 +303,10 @@ function uuid() {
176
303
  return crypto.randomUUID();
177
304
  }
178
305
  function now() {
179
- return new Date().toISOString();
306
+ const currentMs = Date.now();
307
+ const nextMs = currentMs > lastNowMs ? currentMs : lastNowMs + 1;
308
+ lastNowMs = nextMs;
309
+ return new Date(nextMs).toISOString();
180
310
  }
181
311
  function quoteIdentifier(identifier) {
182
312
  return `"${identifier.replaceAll('"', '""')}"`;
@@ -214,7 +344,7 @@ function runMigrations(db) {
214
344
  db.exec(`INSERT OR REPLACE INTO _migrations(version) VALUES(${i})`);
215
345
  }
216
346
  }
217
- var MIGRATIONS, _db = null;
347
+ var MIGRATIONS, _db = null, lastNowMs = 0;
218
348
  var init_database = __esm(() => {
219
349
  init_sqlite_adapter();
220
350
  init_paths();
@@ -786,6 +916,81 @@ var init_database = __esm(() => {
786
916
  );
787
917
 
788
918
  CREATE INDEX IF NOT EXISTS idx_contacts_tombstones_deleted_at ON _contacts_tombstones(deleted_at);
919
+ `,
920
+ `
921
+ CREATE TABLE IF NOT EXISTS contact_project_membership_states (
922
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
923
+ project_id TEXT NOT NULL,
924
+ linked INTEGER NOT NULL CHECK(linked IN (0, 1)),
925
+ revision INTEGER NOT NULL DEFAULT 0 CHECK(revision >= 0),
926
+ updated_at TEXT NOT NULL,
927
+ PRIMARY KEY (contact_id, project_id)
928
+ );
929
+
930
+ INSERT OR IGNORE INTO contact_project_membership_states
931
+ (contact_id, project_id, linked, revision, updated_at)
932
+ SELECT contact_id, project_id, 1, 0, datetime('now')
933
+ FROM contact_projects;
934
+
935
+ CREATE TABLE IF NOT EXISTS contact_project_membership_receipts (
936
+ receipt_id TEXT PRIMARY KEY,
937
+ direction TEXT NOT NULL CHECK(direction IN ('attach', 'detach')),
938
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
939
+ project_id TEXT NOT NULL,
940
+ operation_id TEXT NOT NULL,
941
+ step_id TEXT NOT NULL,
942
+ expected_version TEXT NOT NULL,
943
+ before_json TEXT NOT NULL,
944
+ after_json TEXT NOT NULL,
945
+ created_at TEXT NOT NULL,
946
+ UNIQUE(operation_id, step_id)
947
+ );
948
+
949
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_states_project
950
+ ON contact_project_membership_states(project_id, linked, contact_id);
951
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_receipts_target
952
+ ON contact_project_membership_receipts(contact_id, project_id, created_at);
953
+
954
+ CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_insert
955
+ AFTER INSERT ON contact_projects
956
+ BEGIN
957
+ INSERT INTO contact_project_membership_states
958
+ (contact_id, project_id, linked, revision, updated_at)
959
+ VALUES (NEW.contact_id, NEW.project_id, 1, 1, datetime('now'))
960
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
961
+ linked = 1,
962
+ revision = CASE
963
+ WHEN contact_project_membership_states.linked = 1
964
+ THEN contact_project_membership_states.revision
965
+ ELSE contact_project_membership_states.revision + 1
966
+ END,
967
+ updated_at = CASE
968
+ WHEN contact_project_membership_states.linked = 1
969
+ THEN contact_project_membership_states.updated_at
970
+ ELSE datetime('now')
971
+ END;
972
+ END;
973
+
974
+ CREATE TRIGGER IF NOT EXISTS sync_contact_project_membership_state_after_delete
975
+ AFTER DELETE ON contact_projects
976
+ WHEN EXISTS (SELECT 1 FROM contacts WHERE id = OLD.contact_id)
977
+ BEGIN
978
+ INSERT INTO contact_project_membership_states
979
+ (contact_id, project_id, linked, revision, updated_at)
980
+ VALUES (OLD.contact_id, OLD.project_id, 0, 1, datetime('now'))
981
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
982
+ linked = 0,
983
+ revision = CASE
984
+ WHEN contact_project_membership_states.linked = 0
985
+ THEN contact_project_membership_states.revision
986
+ ELSE contact_project_membership_states.revision + 1
987
+ END,
988
+ updated_at = CASE
989
+ WHEN contact_project_membership_states.linked = 0
990
+ THEN contact_project_membership_states.updated_at
991
+ ELSE datetime('now')
992
+ END;
993
+ END;
789
994
  `
790
995
  ];
791
996
  });
@@ -883,6 +1088,219 @@ var init_tombstones = __esm(() => {
883
1088
  init_database();
884
1089
  });
885
1090
 
1091
+ // src/types/project-memberships.ts
1092
+ var ContactProjectMembershipConflictError;
1093
+ var init_project_memberships = __esm(() => {
1094
+ ContactProjectMembershipConflictError = class ContactProjectMembershipConflictError extends Error {
1095
+ constructor(message) {
1096
+ super(message);
1097
+ this.name = "ContactProjectMembershipConflictError";
1098
+ }
1099
+ };
1100
+ });
1101
+
1102
+ // src/db/project-memberships.ts
1103
+ import { createHash } from "crypto";
1104
+ function digest(value) {
1105
+ return createHash("sha256").update(value).digest("hex");
1106
+ }
1107
+ function membershipVersion(contactId, projectId, linked, revision) {
1108
+ return `cpmv_${digest(JSON.stringify([contactId, projectId, linked, revision])).slice(0, 32)}`;
1109
+ }
1110
+ function receiptId(input) {
1111
+ return `cpmr_${digest(JSON.stringify([
1112
+ input.operation_id,
1113
+ input.step_id,
1114
+ input.contact_id,
1115
+ input.project_id
1116
+ ])).slice(0, 32)}`;
1117
+ }
1118
+ function stateRow(contactId, projectId, db) {
1119
+ const persisted = db.query(`SELECT contact_id, project_id, linked, revision
1120
+ FROM contact_project_membership_states
1121
+ WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId);
1122
+ if (persisted)
1123
+ return persisted;
1124
+ const linked = Boolean(db.query(`SELECT 1 AS present FROM contact_projects WHERE contact_id = ? AND project_id = ?`).get(contactId, projectId));
1125
+ return { contact_id: contactId, project_id: projectId, linked: linked ? 1 : 0, revision: 0 };
1126
+ }
1127
+ function snapshot(row) {
1128
+ const linked = Boolean(row.linked);
1129
+ return {
1130
+ contact_id: row.contact_id,
1131
+ project_id: row.project_id,
1132
+ linked,
1133
+ version: membershipVersion(row.contact_id, row.project_id, linked, row.revision)
1134
+ };
1135
+ }
1136
+ function required(value, name) {
1137
+ const normalized = value.trim();
1138
+ if (!normalized)
1139
+ throw new Error(`${name} is required`);
1140
+ return normalized;
1141
+ }
1142
+ function normalizeInput(input) {
1143
+ return {
1144
+ contact_id: required(input.contact_id, "contact_id"),
1145
+ project_id: required(input.project_id, "project_id"),
1146
+ operation_id: required(input.operation_id, "operation_id"),
1147
+ step_id: required(input.step_id, "step_id"),
1148
+ expected_version: required(input.expected_version, "expected_version")
1149
+ };
1150
+ }
1151
+ function parseSnapshot(value) {
1152
+ return JSON.parse(value);
1153
+ }
1154
+ function replay(row, direction, input) {
1155
+ if (row.direction !== direction || row.contact_id !== input.contact_id || row.project_id !== input.project_id || row.expected_version !== input.expected_version) {
1156
+ throw new ContactProjectMembershipConflictError(`operation_id/step_id already accepted for a different contact-project membership mutation`);
1157
+ }
1158
+ return {
1159
+ outcome: "duplicate_of_accepted",
1160
+ operation_id: row.operation_id,
1161
+ step_id: row.step_id,
1162
+ before: parseSnapshot(row.before_json),
1163
+ after: parseSnapshot(row.after_json),
1164
+ receipt_id: row.receipt_id
1165
+ };
1166
+ }
1167
+ function transitionWithoutReceipt(contactId, projectId, linked, db) {
1168
+ const beforeRow = stateRow(contactId, projectId, db);
1169
+ const changed = Boolean(beforeRow.linked) !== linked;
1170
+ const afterRow = {
1171
+ ...beforeRow,
1172
+ linked: linked ? 1 : 0,
1173
+ revision: changed ? beforeRow.revision + 1 : beforeRow.revision
1174
+ };
1175
+ const changedAt = now();
1176
+ db.run(`INSERT INTO contact_project_membership_states
1177
+ (contact_id, project_id, linked, revision, updated_at)
1178
+ VALUES (?, ?, ?, ?, ?)
1179
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
1180
+ linked = excluded.linked,
1181
+ revision = excluded.revision,
1182
+ updated_at = excluded.updated_at`, [contactId, projectId, afterRow.linked, afterRow.revision, changedAt]);
1183
+ if (linked) {
1184
+ db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, projectId]);
1185
+ } else {
1186
+ db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [contactId, projectId]);
1187
+ }
1188
+ return changed;
1189
+ }
1190
+ function setContactProjectMembershipWithoutReceipt(contactId, projectId, linked, db = getDatabase()) {
1191
+ const normalizedContactId = required(contactId, "contact_id");
1192
+ const normalizedProjectId = required(projectId, "project_id");
1193
+ return db.transaction(() => transitionWithoutReceipt(normalizedContactId, normalizedProjectId, linked, db));
1194
+ }
1195
+ function replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, db = getDatabase()) {
1196
+ const normalizedContactId = required(contactId, "contact_id");
1197
+ const normalizedProjectIds = [...new Set(projectIds.map((projectId) => required(projectId, "project_id")))];
1198
+ return db.transaction(() => {
1199
+ const currentRows = db.query(`SELECT project_id FROM contact_projects WHERE contact_id = ?`).all(normalizedContactId);
1200
+ const desired = new Set(normalizedProjectIds);
1201
+ const population = new Set([...currentRows.map((row) => row.project_id), ...normalizedProjectIds]);
1202
+ for (const projectId of population) {
1203
+ transitionWithoutReceipt(normalizedContactId, projectId, desired.has(projectId), db);
1204
+ }
1205
+ return normalizedProjectIds;
1206
+ });
1207
+ }
1208
+ function readContactProjectMembership(contactId, projectId, db = getDatabase()) {
1209
+ return snapshot(stateRow(required(contactId, "contact_id"), required(projectId, "project_id"), db));
1210
+ }
1211
+ function listContactProjectMemberships(projectId, maxItems, db = getDatabase()) {
1212
+ const normalizedProjectId = required(projectId, "project_id");
1213
+ if (!Number.isInteger(maxItems) || maxItems < 1)
1214
+ throw new Error("max_items must be a positive integer");
1215
+ const rows = db.query(`SELECT cp.contact_id, cp.project_id,
1216
+ COALESCE(state.linked, 1) AS linked,
1217
+ COALESCE(state.revision, 0) AS revision
1218
+ FROM contact_projects cp
1219
+ LEFT JOIN contact_project_membership_states state
1220
+ ON state.contact_id = cp.contact_id AND state.project_id = cp.project_id
1221
+ WHERE cp.project_id = ?
1222
+ ORDER BY cp.contact_id ASC
1223
+ LIMIT ?`).all(normalizedProjectId, maxItems + 1);
1224
+ if (rows.length > maxItems) {
1225
+ throw new Error(`contact project membership collection exceeds max_items=${maxItems}`);
1226
+ }
1227
+ const contactIds = rows.map((row) => row.contact_id);
1228
+ return {
1229
+ project_id: normalizedProjectId,
1230
+ contact_ids: contactIds,
1231
+ complete: true,
1232
+ membership_revision: `cpml_${digest(JSON.stringify(rows.map((row) => snapshot(row)))).slice(0, 32)}`
1233
+ };
1234
+ }
1235
+ function mutateContactProjectMembership(direction, rawInput, db = getDatabase()) {
1236
+ const input = normalizeInput(rawInput);
1237
+ return db.transaction(() => {
1238
+ const existingReceipt = db.query(`SELECT direction, contact_id, project_id, operation_id, step_id, expected_version,
1239
+ before_json, after_json, receipt_id
1240
+ FROM contact_project_membership_receipts
1241
+ WHERE operation_id = ? AND step_id = ?`).get(input.operation_id, input.step_id);
1242
+ if (existingReceipt)
1243
+ return replay(existingReceipt, direction, input);
1244
+ const contact = db.query(`SELECT id FROM contacts WHERE id = ?`).get(input.contact_id);
1245
+ if (!contact)
1246
+ throw new Error(`contact not found: ${input.contact_id}`);
1247
+ const beforeRow = stateRow(input.contact_id, input.project_id, db);
1248
+ const before = snapshot(beforeRow);
1249
+ if (before.version !== input.expected_version) {
1250
+ throw new ContactProjectMembershipConflictError(`contact project membership expected_version conflict: expected ${input.expected_version}, current ${before.version}`);
1251
+ }
1252
+ const desiredLinked = direction === "attach";
1253
+ const changed = before.linked !== desiredLinked;
1254
+ const afterRow = {
1255
+ ...beforeRow,
1256
+ linked: desiredLinked ? 1 : 0,
1257
+ revision: changed ? beforeRow.revision + 1 : beforeRow.revision
1258
+ };
1259
+ const changedAt = now();
1260
+ db.run(`INSERT INTO contact_project_membership_states
1261
+ (contact_id, project_id, linked, revision, updated_at)
1262
+ VALUES (?, ?, ?, ?, ?)
1263
+ ON CONFLICT(contact_id, project_id) DO UPDATE SET
1264
+ linked = excluded.linked,
1265
+ revision = excluded.revision,
1266
+ updated_at = excluded.updated_at`, [input.contact_id, input.project_id, afterRow.linked, afterRow.revision, changedAt]);
1267
+ if (desiredLinked) {
1268
+ db.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [input.contact_id, input.project_id]);
1269
+ } else {
1270
+ db.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [input.contact_id, input.project_id]);
1271
+ }
1272
+ const after = snapshot(afterRow);
1273
+ const id = receiptId(input);
1274
+ db.run(`INSERT INTO contact_project_membership_receipts (
1275
+ receipt_id, direction, contact_id, project_id, operation_id, step_id,
1276
+ expected_version, before_json, after_json, created_at
1277
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
1278
+ id,
1279
+ direction,
1280
+ input.contact_id,
1281
+ input.project_id,
1282
+ input.operation_id,
1283
+ input.step_id,
1284
+ input.expected_version,
1285
+ JSON.stringify(before),
1286
+ JSON.stringify(after),
1287
+ changedAt
1288
+ ]);
1289
+ return {
1290
+ outcome: changed ? "accepted" : "duplicate_of_accepted",
1291
+ operation_id: input.operation_id,
1292
+ step_id: input.step_id,
1293
+ before,
1294
+ after,
1295
+ receipt_id: id
1296
+ };
1297
+ });
1298
+ }
1299
+ var init_project_memberships2 = __esm(() => {
1300
+ init_database();
1301
+ init_project_memberships();
1302
+ });
1303
+
886
1304
  // src/db/contacts.ts
887
1305
  var exports_contacts = {};
888
1306
  __export(exports_contacts, {
@@ -1481,37 +1899,141 @@ function autoLinkContactToCompany(contactId, db) {
1481
1899
  }
1482
1900
  function linkContactToProject(contactId, projectId, db) {
1483
1901
  const d = db || getDatabase();
1484
- d.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, projectId]);
1902
+ setContactProjectMembershipWithoutReceipt(contactId, projectId, true, d);
1485
1903
  }
1486
1904
  function unlinkContactFromProject(contactId, projectId, db) {
1487
1905
  const d = db || getDatabase();
1488
- d.run(`DELETE FROM contact_projects WHERE contact_id = ? AND project_id = ?`, [contactId, projectId]);
1906
+ setContactProjectMembershipWithoutReceipt(contactId, projectId, false, d);
1489
1907
  }
1490
1908
  function getContactProjectIds(contactId, db) {
1491
1909
  const d = db || getDatabase();
1492
- const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id = ?`).all(contactId);
1910
+ const rows = d.query(`SELECT project_id FROM contact_projects WHERE contact_id = ? ORDER BY project_id ASC`).all(contactId);
1493
1911
  return rows.map((r) => r.project_id);
1494
1912
  }
1495
1913
  function listContactIdsByProject(projectId, db) {
1496
1914
  const d = db || getDatabase();
1497
- const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id = ?`).all(projectId);
1915
+ const rows = d.query(`SELECT contact_id FROM contact_projects WHERE project_id = ? ORDER BY contact_id ASC`).all(projectId);
1498
1916
  return rows.map((r) => r.contact_id);
1499
1917
  }
1500
1918
  function setContactProjects(contactId, projectIds, db) {
1501
1919
  const d = db || getDatabase();
1502
- d.run(`DELETE FROM contact_projects WHERE contact_id = ?`, [contactId]);
1503
- for (const pid of projectIds) {
1504
- d.run(`INSERT OR IGNORE INTO contact_projects (contact_id, project_id) VALUES (?, ?)`, [contactId, pid]);
1505
- }
1920
+ replaceContactProjectMembershipsWithoutReceipts(contactId, projectIds, d);
1506
1921
  }
1507
1922
  var init_contacts = __esm(() => {
1508
1923
  init_types();
1509
1924
  init_database();
1510
1925
  init_activity();
1511
1926
  init_tombstones();
1927
+ init_project_memberships2();
1928
+ });
1929
+
1930
+ // node_modules/.pnpm/content-type@1.0.5/node_modules/content-type/index.js
1931
+ var require_content_type = __commonJS((exports) => {
1932
+ /*!
1933
+ * content-type
1934
+ * Copyright(c) 2015 Douglas Christopher Wilson
1935
+ * MIT Licensed
1936
+ */
1937
+ var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g;
1938
+ var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/;
1939
+ var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
1940
+ var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g;
1941
+ var QUOTE_REGEXP = /([\\"])/g;
1942
+ var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/;
1943
+ exports.format = format;
1944
+ exports.parse = parse;
1945
+ function format(obj) {
1946
+ if (!obj || typeof obj !== "object") {
1947
+ throw new TypeError("argument obj is required");
1948
+ }
1949
+ var parameters = obj.parameters;
1950
+ var type = obj.type;
1951
+ if (!type || !TYPE_REGEXP.test(type)) {
1952
+ throw new TypeError("invalid type");
1953
+ }
1954
+ var string = type;
1955
+ if (parameters && typeof parameters === "object") {
1956
+ var param;
1957
+ var params = Object.keys(parameters).sort();
1958
+ for (var i = 0;i < params.length; i++) {
1959
+ param = params[i];
1960
+ if (!TOKEN_REGEXP.test(param)) {
1961
+ throw new TypeError("invalid parameter name");
1962
+ }
1963
+ string += "; " + param + "=" + qstring(parameters[param]);
1964
+ }
1965
+ }
1966
+ return string;
1967
+ }
1968
+ function parse(string) {
1969
+ if (!string) {
1970
+ throw new TypeError("argument string is required");
1971
+ }
1972
+ var header = typeof string === "object" ? getcontenttype(string) : string;
1973
+ if (typeof header !== "string") {
1974
+ throw new TypeError("argument string is required to be a string");
1975
+ }
1976
+ var index = header.indexOf(";");
1977
+ var type = index !== -1 ? header.slice(0, index).trim() : header.trim();
1978
+ if (!TYPE_REGEXP.test(type)) {
1979
+ throw new TypeError("invalid media type");
1980
+ }
1981
+ var obj = new ContentType(type.toLowerCase());
1982
+ if (index !== -1) {
1983
+ var key;
1984
+ var match;
1985
+ var value;
1986
+ PARAM_REGEXP.lastIndex = index;
1987
+ while (match = PARAM_REGEXP.exec(header)) {
1988
+ if (match.index !== index) {
1989
+ throw new TypeError("invalid parameter format");
1990
+ }
1991
+ index += match[0].length;
1992
+ key = match[1].toLowerCase();
1993
+ value = match[2];
1994
+ if (value.charCodeAt(0) === 34) {
1995
+ value = value.slice(1, -1);
1996
+ if (value.indexOf("\\") !== -1) {
1997
+ value = value.replace(QESC_REGEXP, "$1");
1998
+ }
1999
+ }
2000
+ obj.parameters[key] = value;
2001
+ }
2002
+ if (index !== header.length) {
2003
+ throw new TypeError("invalid parameter format");
2004
+ }
2005
+ }
2006
+ return obj;
2007
+ }
2008
+ function getcontenttype(obj) {
2009
+ var header;
2010
+ if (typeof obj.getHeader === "function") {
2011
+ header = obj.getHeader("content-type");
2012
+ } else if (typeof obj.headers === "object") {
2013
+ header = obj.headers && obj.headers["content-type"];
2014
+ }
2015
+ if (typeof header !== "string") {
2016
+ throw new TypeError("content-type header is missing from object");
2017
+ }
2018
+ return header;
2019
+ }
2020
+ function qstring(val) {
2021
+ var str = String(val);
2022
+ if (TOKEN_REGEXP.test(str)) {
2023
+ return str;
2024
+ }
2025
+ if (str.length > 0 && !TEXT_REGEXP.test(str)) {
2026
+ throw new TypeError("invalid parameter value");
2027
+ }
2028
+ return '"' + str.replace(QUOTE_REGEXP, "\\$1") + '"';
2029
+ }
2030
+ function ContentType(type) {
2031
+ this.parameters = Object.create(null);
2032
+ this.type = type;
2033
+ }
1512
2034
  });
1513
2035
 
1514
- // node_modules/ajv/dist/compile/codegen/code.js
2036
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/code.js
1515
2037
  var require_code = __commonJS((exports) => {
1516
2038
  Object.defineProperty(exports, "__esModule", { value: true });
1517
2039
  exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = undefined;
@@ -1665,7 +2187,7 @@ var require_code = __commonJS((exports) => {
1665
2187
  exports.regexpCode = regexpCode;
1666
2188
  });
1667
2189
 
1668
- // node_modules/ajv/dist/compile/codegen/scope.js
2190
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/scope.js
1669
2191
  var require_scope = __commonJS((exports) => {
1670
2192
  Object.defineProperty(exports, "__esModule", { value: true });
1671
2193
  exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = undefined;
@@ -1811,7 +2333,7 @@ var require_scope = __commonJS((exports) => {
1811
2333
  exports.ValueScope = ValueScope;
1812
2334
  });
1813
2335
 
1814
- // node_modules/ajv/dist/compile/codegen/index.js
2336
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/codegen/index.js
1815
2337
  var require_codegen = __commonJS((exports) => {
1816
2338
  Object.defineProperty(exports, "__esModule", { value: true });
1817
2339
  exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.regexpCode = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = undefined;
@@ -2521,7 +3043,7 @@ var require_codegen = __commonJS((exports) => {
2521
3043
  }
2522
3044
  });
2523
3045
 
2524
- // node_modules/ajv/dist/compile/util.js
3046
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/util.js
2525
3047
  var require_util = __commonJS((exports) => {
2526
3048
  Object.defineProperty(exports, "__esModule", { value: true });
2527
3049
  exports.checkStrictMode = exports.getErrorPath = exports.Type = exports.useFunc = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = undefined;
@@ -2685,7 +3207,7 @@ var require_util = __commonJS((exports) => {
2685
3207
  exports.checkStrictMode = checkStrictMode;
2686
3208
  });
2687
3209
 
2688
- // node_modules/ajv/dist/compile/names.js
3210
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/names.js
2689
3211
  var require_names = __commonJS((exports) => {
2690
3212
  Object.defineProperty(exports, "__esModule", { value: true });
2691
3213
  var codegen_1 = require_codegen();
@@ -2710,7 +3232,7 @@ var require_names = __commonJS((exports) => {
2710
3232
  exports.default = names;
2711
3233
  });
2712
3234
 
2713
- // node_modules/ajv/dist/compile/errors.js
3235
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/errors.js
2714
3236
  var require_errors = __commonJS((exports) => {
2715
3237
  Object.defineProperty(exports, "__esModule", { value: true });
2716
3238
  exports.extendErrors = exports.resetErrorsCount = exports.reportExtraError = exports.reportError = exports.keyword$DataError = exports.keywordError = undefined;
@@ -2828,7 +3350,7 @@ var require_errors = __commonJS((exports) => {
2828
3350
  }
2829
3351
  });
2830
3352
 
2831
- // node_modules/ajv/dist/compile/validate/boolSchema.js
3353
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/boolSchema.js
2832
3354
  var require_boolSchema = __commonJS((exports) => {
2833
3355
  Object.defineProperty(exports, "__esModule", { value: true });
2834
3356
  exports.boolOrEmptySchema = exports.topBoolOrEmptySchema = undefined;
@@ -2876,7 +3398,7 @@ var require_boolSchema = __commonJS((exports) => {
2876
3398
  }
2877
3399
  });
2878
3400
 
2879
- // node_modules/ajv/dist/compile/rules.js
3401
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/rules.js
2880
3402
  var require_rules = __commonJS((exports) => {
2881
3403
  Object.defineProperty(exports, "__esModule", { value: true });
2882
3404
  exports.getRules = exports.isJSONType = undefined;
@@ -2904,7 +3426,7 @@ var require_rules = __commonJS((exports) => {
2904
3426
  exports.getRules = getRules;
2905
3427
  });
2906
3428
 
2907
- // node_modules/ajv/dist/compile/validate/applicability.js
3429
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/applicability.js
2908
3430
  var require_applicability = __commonJS((exports) => {
2909
3431
  Object.defineProperty(exports, "__esModule", { value: true });
2910
3432
  exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = undefined;
@@ -2924,7 +3446,7 @@ var require_applicability = __commonJS((exports) => {
2924
3446
  exports.shouldUseRule = shouldUseRule;
2925
3447
  });
2926
3448
 
2927
- // node_modules/ajv/dist/compile/validate/dataType.js
3449
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/dataType.js
2928
3450
  var require_dataType = __commonJS((exports) => {
2929
3451
  Object.defineProperty(exports, "__esModule", { value: true });
2930
3452
  exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = undefined;
@@ -3105,7 +3627,7 @@ var require_dataType = __commonJS((exports) => {
3105
3627
  }
3106
3628
  });
3107
3629
 
3108
- // node_modules/ajv/dist/compile/validate/defaults.js
3630
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/defaults.js
3109
3631
  var require_defaults = __commonJS((exports) => {
3110
3632
  Object.defineProperty(exports, "__esModule", { value: true });
3111
3633
  exports.assignDefaults = undefined;
@@ -3139,7 +3661,7 @@ var require_defaults = __commonJS((exports) => {
3139
3661
  }
3140
3662
  });
3141
3663
 
3142
- // node_modules/ajv/dist/vocabularies/code.js
3664
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/code.js
3143
3665
  var require_code2 = __commonJS((exports) => {
3144
3666
  Object.defineProperty(exports, "__esModule", { value: true });
3145
3667
  exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = undefined;
@@ -3268,7 +3790,7 @@ var require_code2 = __commonJS((exports) => {
3268
3790
  exports.validateUnion = validateUnion;
3269
3791
  });
3270
3792
 
3271
- // node_modules/ajv/dist/compile/validate/keyword.js
3793
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/keyword.js
3272
3794
  var require_keyword = __commonJS((exports) => {
3273
3795
  Object.defineProperty(exports, "__esModule", { value: true });
3274
3796
  exports.validateKeywordUsage = exports.validSchemaType = exports.funcKeywordCode = exports.macroKeywordCode = undefined;
@@ -3383,7 +3905,7 @@ var require_keyword = __commonJS((exports) => {
3383
3905
  exports.validateKeywordUsage = validateKeywordUsage;
3384
3906
  });
3385
3907
 
3386
- // node_modules/ajv/dist/compile/validate/subschema.js
3908
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/subschema.js
3387
3909
  var require_subschema = __commonJS((exports) => {
3388
3910
  Object.defineProperty(exports, "__esModule", { value: true });
3389
3911
  exports.extendSubschemaMode = exports.extendSubschemaData = exports.getSubschema = undefined;
@@ -3463,7 +3985,7 @@ var require_subschema = __commonJS((exports) => {
3463
3985
  exports.extendSubschemaMode = extendSubschemaMode;
3464
3986
  });
3465
3987
 
3466
- // node_modules/fast-deep-equal/index.js
3988
+ // node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js
3467
3989
  var require_fast_deep_equal = __commonJS((exports, module) => {
3468
3990
  module.exports = function equal(a, b) {
3469
3991
  if (a === b)
@@ -3505,7 +4027,7 @@ var require_fast_deep_equal = __commonJS((exports, module) => {
3505
4027
  };
3506
4028
  });
3507
4029
 
3508
- // node_modules/json-schema-traverse/index.js
4030
+ // node_modules/.pnpm/json-schema-traverse@1.0.0/node_modules/json-schema-traverse/index.js
3509
4031
  var require_json_schema_traverse = __commonJS((exports, module) => {
3510
4032
  var traverse = module.exports = function(schema, opts, cb) {
3511
4033
  if (typeof opts == "function") {
@@ -3588,7 +4110,7 @@ var require_json_schema_traverse = __commonJS((exports, module) => {
3588
4110
  }
3589
4111
  });
3590
4112
 
3591
- // node_modules/ajv/dist/compile/resolve.js
4113
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/resolve.js
3592
4114
  var require_resolve = __commonJS((exports) => {
3593
4115
  Object.defineProperty(exports, "__esModule", { value: true });
3594
4116
  exports.getSchemaRefs = exports.resolveUrl = exports.normalizeId = exports._getFullPath = exports.getFullPath = exports.inlineRef = undefined;
@@ -3741,7 +4263,7 @@ var require_resolve = __commonJS((exports) => {
3741
4263
  exports.getSchemaRefs = getSchemaRefs;
3742
4264
  });
3743
4265
 
3744
- // node_modules/ajv/dist/compile/validate/index.js
4266
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/validate/index.js
3745
4267
  var require_validate = __commonJS((exports) => {
3746
4268
  Object.defineProperty(exports, "__esModule", { value: true });
3747
4269
  exports.getData = exports.KeywordCxt = exports.validateFunctionCode = undefined;
@@ -4246,7 +4768,7 @@ var require_validate = __commonJS((exports) => {
4246
4768
  exports.getData = getData;
4247
4769
  });
4248
4770
 
4249
- // node_modules/ajv/dist/runtime/validation_error.js
4771
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/runtime/validation_error.js
4250
4772
  var require_validation_error = __commonJS((exports) => {
4251
4773
  Object.defineProperty(exports, "__esModule", { value: true });
4252
4774
 
@@ -4260,7 +4782,7 @@ var require_validation_error = __commonJS((exports) => {
4260
4782
  exports.default = ValidationError;
4261
4783
  });
4262
4784
 
4263
- // node_modules/ajv/dist/compile/ref_error.js
4785
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/ref_error.js
4264
4786
  var require_ref_error = __commonJS((exports) => {
4265
4787
  Object.defineProperty(exports, "__esModule", { value: true });
4266
4788
  var resolve_1 = require_resolve();
@@ -4275,7 +4797,7 @@ var require_ref_error = __commonJS((exports) => {
4275
4797
  exports.default = MissingRefError;
4276
4798
  });
4277
4799
 
4278
- // node_modules/ajv/dist/compile/index.js
4800
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/compile/index.js
4279
4801
  var require_compile = __commonJS((exports) => {
4280
4802
  Object.defineProperty(exports, "__esModule", { value: true });
4281
4803
  exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = undefined;
@@ -4496,7 +5018,7 @@ var require_compile = __commonJS((exports) => {
4496
5018
  }
4497
5019
  });
4498
5020
 
4499
- // node_modules/ajv/dist/refs/data.json
5021
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/refs/data.json
4500
5022
  var require_data = __commonJS((exports, module) => {
4501
5023
  module.exports = {
4502
5024
  $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#",
@@ -5286,7 +5808,7 @@ var require_fast_uri = __commonJS((exports, module) => {
5286
5808
  module.exports.fastUri = fastUri;
5287
5809
  });
5288
5810
 
5289
- // node_modules/ajv/dist/runtime/uri.js
5811
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/runtime/uri.js
5290
5812
  var require_uri = __commonJS((exports) => {
5291
5813
  Object.defineProperty(exports, "__esModule", { value: true });
5292
5814
  var uri = require_fast_uri();
@@ -5294,7 +5816,7 @@ var require_uri = __commonJS((exports) => {
5294
5816
  exports.default = uri;
5295
5817
  });
5296
5818
 
5297
- // node_modules/ajv/dist/core.js
5819
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/core.js
5298
5820
  var require_core = __commonJS((exports) => {
5299
5821
  Object.defineProperty(exports, "__esModule", { value: true });
5300
5822
  exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = undefined;
@@ -5405,7 +5927,7 @@ var require_core = __commonJS((exports) => {
5405
5927
  constructor(opts = {}) {
5406
5928
  this.schemas = {};
5407
5929
  this.refs = {};
5408
- this.formats = {};
5930
+ this.formats = Object.create(null);
5409
5931
  this._compilations = new Set;
5410
5932
  this._loading = {};
5411
5933
  this._cache = new Map;
@@ -5887,7 +6409,7 @@ var require_core = __commonJS((exports) => {
5887
6409
  }
5888
6410
  });
5889
6411
 
5890
- // node_modules/ajv/dist/vocabularies/core/id.js
6412
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/core/id.js
5891
6413
  var require_id = __commonJS((exports) => {
5892
6414
  Object.defineProperty(exports, "__esModule", { value: true });
5893
6415
  var def = {
@@ -5899,7 +6421,7 @@ var require_id = __commonJS((exports) => {
5899
6421
  exports.default = def;
5900
6422
  });
5901
6423
 
5902
- // node_modules/ajv/dist/vocabularies/core/ref.js
6424
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/core/ref.js
5903
6425
  var require_ref = __commonJS((exports) => {
5904
6426
  Object.defineProperty(exports, "__esModule", { value: true });
5905
6427
  exports.callRef = exports.getValidate = undefined;
@@ -6018,7 +6540,7 @@ var require_ref = __commonJS((exports) => {
6018
6540
  exports.default = def;
6019
6541
  });
6020
6542
 
6021
- // node_modules/ajv/dist/vocabularies/core/index.js
6543
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/core/index.js
6022
6544
  var require_core2 = __commonJS((exports) => {
6023
6545
  Object.defineProperty(exports, "__esModule", { value: true });
6024
6546
  var id_1 = require_id();
@@ -6036,7 +6558,7 @@ var require_core2 = __commonJS((exports) => {
6036
6558
  exports.default = core2;
6037
6559
  });
6038
6560
 
6039
- // node_modules/ajv/dist/vocabularies/validation/limitNumber.js
6561
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/limitNumber.js
6040
6562
  var require_limitNumber = __commonJS((exports) => {
6041
6563
  Object.defineProperty(exports, "__esModule", { value: true });
6042
6564
  var codegen_1 = require_codegen();
@@ -6065,7 +6587,7 @@ var require_limitNumber = __commonJS((exports) => {
6065
6587
  exports.default = def;
6066
6588
  });
6067
6589
 
6068
- // node_modules/ajv/dist/vocabularies/validation/multipleOf.js
6590
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/multipleOf.js
6069
6591
  var require_multipleOf = __commonJS((exports) => {
6070
6592
  Object.defineProperty(exports, "__esModule", { value: true });
6071
6593
  var codegen_1 = require_codegen();
@@ -6090,7 +6612,7 @@ var require_multipleOf = __commonJS((exports) => {
6090
6612
  exports.default = def;
6091
6613
  });
6092
6614
 
6093
- // node_modules/ajv/dist/runtime/ucs2length.js
6615
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/runtime/ucs2length.js
6094
6616
  var require_ucs2length = __commonJS((exports) => {
6095
6617
  Object.defineProperty(exports, "__esModule", { value: true });
6096
6618
  function ucs2length(str) {
@@ -6113,7 +6635,7 @@ var require_ucs2length = __commonJS((exports) => {
6113
6635
  ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default';
6114
6636
  });
6115
6637
 
6116
- // node_modules/ajv/dist/vocabularies/validation/limitLength.js
6638
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/limitLength.js
6117
6639
  var require_limitLength = __commonJS((exports) => {
6118
6640
  Object.defineProperty(exports, "__esModule", { value: true });
6119
6641
  var codegen_1 = require_codegen();
@@ -6142,7 +6664,7 @@ var require_limitLength = __commonJS((exports) => {
6142
6664
  exports.default = def;
6143
6665
  });
6144
6666
 
6145
- // node_modules/ajv/dist/vocabularies/validation/pattern.js
6667
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/pattern.js
6146
6668
  var require_pattern = __commonJS((exports) => {
6147
6669
  Object.defineProperty(exports, "__esModule", { value: true });
6148
6670
  var code_1 = require_code2();
@@ -6176,7 +6698,7 @@ var require_pattern = __commonJS((exports) => {
6176
6698
  exports.default = def;
6177
6699
  });
6178
6700
 
6179
- // node_modules/ajv/dist/vocabularies/validation/limitProperties.js
6701
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/limitProperties.js
6180
6702
  var require_limitProperties = __commonJS((exports) => {
6181
6703
  Object.defineProperty(exports, "__esModule", { value: true });
6182
6704
  var codegen_1 = require_codegen();
@@ -6202,7 +6724,7 @@ var require_limitProperties = __commonJS((exports) => {
6202
6724
  exports.default = def;
6203
6725
  });
6204
6726
 
6205
- // node_modules/ajv/dist/vocabularies/validation/required.js
6727
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/required.js
6206
6728
  var require_required = __commonJS((exports) => {
6207
6729
  Object.defineProperty(exports, "__esModule", { value: true });
6208
6730
  var code_1 = require_code2();
@@ -6281,7 +6803,7 @@ var require_required = __commonJS((exports) => {
6281
6803
  exports.default = def;
6282
6804
  });
6283
6805
 
6284
- // node_modules/ajv/dist/vocabularies/validation/limitItems.js
6806
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/limitItems.js
6285
6807
  var require_limitItems = __commonJS((exports) => {
6286
6808
  Object.defineProperty(exports, "__esModule", { value: true });
6287
6809
  var codegen_1 = require_codegen();
@@ -6307,7 +6829,7 @@ var require_limitItems = __commonJS((exports) => {
6307
6829
  exports.default = def;
6308
6830
  });
6309
6831
 
6310
- // node_modules/ajv/dist/runtime/equal.js
6832
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/runtime/equal.js
6311
6833
  var require_equal = __commonJS((exports) => {
6312
6834
  Object.defineProperty(exports, "__esModule", { value: true });
6313
6835
  var equal = require_fast_deep_equal();
@@ -6315,7 +6837,7 @@ var require_equal = __commonJS((exports) => {
6315
6837
  exports.default = equal;
6316
6838
  });
6317
6839
 
6318
- // node_modules/ajv/dist/vocabularies/validation/uniqueItems.js
6840
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js
6319
6841
  var require_uniqueItems = __commonJS((exports) => {
6320
6842
  Object.defineProperty(exports, "__esModule", { value: true });
6321
6843
  var dataType_1 = require_dataType();
@@ -6379,7 +6901,7 @@ var require_uniqueItems = __commonJS((exports) => {
6379
6901
  exports.default = def;
6380
6902
  });
6381
6903
 
6382
- // node_modules/ajv/dist/vocabularies/validation/const.js
6904
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/const.js
6383
6905
  var require_const = __commonJS((exports) => {
6384
6906
  Object.defineProperty(exports, "__esModule", { value: true });
6385
6907
  var codegen_1 = require_codegen();
@@ -6405,7 +6927,7 @@ var require_const = __commonJS((exports) => {
6405
6927
  exports.default = def;
6406
6928
  });
6407
6929
 
6408
- // node_modules/ajv/dist/vocabularies/validation/enum.js
6930
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/enum.js
6409
6931
  var require_enum = __commonJS((exports) => {
6410
6932
  Object.defineProperty(exports, "__esModule", { value: true });
6411
6933
  var codegen_1 = require_codegen();
@@ -6451,7 +6973,7 @@ var require_enum = __commonJS((exports) => {
6451
6973
  exports.default = def;
6452
6974
  });
6453
6975
 
6454
- // node_modules/ajv/dist/vocabularies/validation/index.js
6976
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/validation/index.js
6455
6977
  var require_validation = __commonJS((exports) => {
6456
6978
  Object.defineProperty(exports, "__esModule", { value: true });
6457
6979
  var limitNumber_1 = require_limitNumber();
@@ -6481,7 +7003,7 @@ var require_validation = __commonJS((exports) => {
6481
7003
  exports.default = validation;
6482
7004
  });
6483
7005
 
6484
- // node_modules/ajv/dist/vocabularies/applicator/additionalItems.js
7006
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js
6485
7007
  var require_additionalItems = __commonJS((exports) => {
6486
7008
  Object.defineProperty(exports, "__esModule", { value: true });
6487
7009
  exports.validateAdditionalItems = undefined;
@@ -6531,7 +7053,7 @@ var require_additionalItems = __commonJS((exports) => {
6531
7053
  exports.default = def;
6532
7054
  });
6533
7055
 
6534
- // node_modules/ajv/dist/vocabularies/applicator/items.js
7056
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/items.js
6535
7057
  var require_items = __commonJS((exports) => {
6536
7058
  Object.defineProperty(exports, "__esModule", { value: true });
6537
7059
  exports.validateTuple = undefined;
@@ -6585,7 +7107,7 @@ var require_items = __commonJS((exports) => {
6585
7107
  exports.default = def;
6586
7108
  });
6587
7109
 
6588
- // node_modules/ajv/dist/vocabularies/applicator/prefixItems.js
7110
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js
6589
7111
  var require_prefixItems = __commonJS((exports) => {
6590
7112
  Object.defineProperty(exports, "__esModule", { value: true });
6591
7113
  var items_1 = require_items();
@@ -6599,7 +7121,7 @@ var require_prefixItems = __commonJS((exports) => {
6599
7121
  exports.default = def;
6600
7122
  });
6601
7123
 
6602
- // node_modules/ajv/dist/vocabularies/applicator/items2020.js
7124
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/items2020.js
6603
7125
  var require_items2020 = __commonJS((exports) => {
6604
7126
  Object.defineProperty(exports, "__esModule", { value: true });
6605
7127
  var codegen_1 = require_codegen();
@@ -6631,7 +7153,7 @@ var require_items2020 = __commonJS((exports) => {
6631
7153
  exports.default = def;
6632
7154
  });
6633
7155
 
6634
- // node_modules/ajv/dist/vocabularies/applicator/contains.js
7156
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/contains.js
6635
7157
  var require_contains = __commonJS((exports) => {
6636
7158
  Object.defineProperty(exports, "__esModule", { value: true });
6637
7159
  var codegen_1 = require_codegen();
@@ -6722,7 +7244,7 @@ var require_contains = __commonJS((exports) => {
6722
7244
  exports.default = def;
6723
7245
  });
6724
7246
 
6725
- // node_modules/ajv/dist/vocabularies/applicator/dependencies.js
7247
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/dependencies.js
6726
7248
  var require_dependencies = __commonJS((exports) => {
6727
7249
  Object.defineProperty(exports, "__esModule", { value: true });
6728
7250
  exports.validateSchemaDeps = exports.validatePropertyDeps = exports.error = undefined;
@@ -6807,7 +7329,7 @@ var require_dependencies = __commonJS((exports) => {
6807
7329
  exports.default = def;
6808
7330
  });
6809
7331
 
6810
- // node_modules/ajv/dist/vocabularies/applicator/propertyNames.js
7332
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js
6811
7333
  var require_propertyNames = __commonJS((exports) => {
6812
7334
  Object.defineProperty(exports, "__esModule", { value: true });
6813
7335
  var codegen_1 = require_codegen();
@@ -6847,7 +7369,7 @@ var require_propertyNames = __commonJS((exports) => {
6847
7369
  exports.default = def;
6848
7370
  });
6849
7371
 
6850
- // node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js
7372
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js
6851
7373
  var require_additionalProperties = __commonJS((exports) => {
6852
7374
  Object.defineProperty(exports, "__esModule", { value: true });
6853
7375
  var code_1 = require_code2();
@@ -6950,7 +7472,7 @@ var require_additionalProperties = __commonJS((exports) => {
6950
7472
  exports.default = def;
6951
7473
  });
6952
7474
 
6953
- // node_modules/ajv/dist/vocabularies/applicator/properties.js
7475
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/properties.js
6954
7476
  var require_properties = __commonJS((exports) => {
6955
7477
  Object.defineProperty(exports, "__esModule", { value: true });
6956
7478
  var validate_1 = require_validate();
@@ -7005,7 +7527,7 @@ var require_properties = __commonJS((exports) => {
7005
7527
  exports.default = def;
7006
7528
  });
7007
7529
 
7008
- // node_modules/ajv/dist/vocabularies/applicator/patternProperties.js
7530
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js
7009
7531
  var require_patternProperties = __commonJS((exports) => {
7010
7532
  Object.defineProperty(exports, "__esModule", { value: true });
7011
7533
  var code_1 = require_code2();
@@ -7076,7 +7598,7 @@ var require_patternProperties = __commonJS((exports) => {
7076
7598
  exports.default = def;
7077
7599
  });
7078
7600
 
7079
- // node_modules/ajv/dist/vocabularies/applicator/not.js
7601
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/not.js
7080
7602
  var require_not = __commonJS((exports) => {
7081
7603
  Object.defineProperty(exports, "__esModule", { value: true });
7082
7604
  var util_1 = require_util();
@@ -7104,7 +7626,7 @@ var require_not = __commonJS((exports) => {
7104
7626
  exports.default = def;
7105
7627
  });
7106
7628
 
7107
- // node_modules/ajv/dist/vocabularies/applicator/anyOf.js
7629
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/anyOf.js
7108
7630
  var require_anyOf = __commonJS((exports) => {
7109
7631
  Object.defineProperty(exports, "__esModule", { value: true });
7110
7632
  var code_1 = require_code2();
@@ -7118,7 +7640,7 @@ var require_anyOf = __commonJS((exports) => {
7118
7640
  exports.default = def;
7119
7641
  });
7120
7642
 
7121
- // node_modules/ajv/dist/vocabularies/applicator/oneOf.js
7643
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/oneOf.js
7122
7644
  var require_oneOf = __commonJS((exports) => {
7123
7645
  Object.defineProperty(exports, "__esModule", { value: true });
7124
7646
  var codegen_1 = require_codegen();
@@ -7173,7 +7695,7 @@ var require_oneOf = __commonJS((exports) => {
7173
7695
  exports.default = def;
7174
7696
  });
7175
7697
 
7176
- // node_modules/ajv/dist/vocabularies/applicator/allOf.js
7698
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/allOf.js
7177
7699
  var require_allOf = __commonJS((exports) => {
7178
7700
  Object.defineProperty(exports, "__esModule", { value: true });
7179
7701
  var util_1 = require_util();
@@ -7197,7 +7719,7 @@ var require_allOf = __commonJS((exports) => {
7197
7719
  exports.default = def;
7198
7720
  });
7199
7721
 
7200
- // node_modules/ajv/dist/vocabularies/applicator/if.js
7722
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/if.js
7201
7723
  var require_if = __commonJS((exports) => {
7202
7724
  Object.defineProperty(exports, "__esModule", { value: true });
7203
7725
  var codegen_1 = require_codegen();
@@ -7263,7 +7785,7 @@ var require_if = __commonJS((exports) => {
7263
7785
  exports.default = def;
7264
7786
  });
7265
7787
 
7266
- // node_modules/ajv/dist/vocabularies/applicator/thenElse.js
7788
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/thenElse.js
7267
7789
  var require_thenElse = __commonJS((exports) => {
7268
7790
  Object.defineProperty(exports, "__esModule", { value: true });
7269
7791
  var util_1 = require_util();
@@ -7278,7 +7800,7 @@ var require_thenElse = __commonJS((exports) => {
7278
7800
  exports.default = def;
7279
7801
  });
7280
7802
 
7281
- // node_modules/ajv/dist/vocabularies/applicator/index.js
7803
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/applicator/index.js
7282
7804
  var require_applicator = __commonJS((exports) => {
7283
7805
  Object.defineProperty(exports, "__esModule", { value: true });
7284
7806
  var additionalItems_1 = require_additionalItems();
@@ -7321,7 +7843,7 @@ var require_applicator = __commonJS((exports) => {
7321
7843
  exports.default = getApplicator;
7322
7844
  });
7323
7845
 
7324
- // node_modules/ajv/dist/vocabularies/format/format.js
7846
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/format/format.js
7325
7847
  var require_format = __commonJS((exports) => {
7326
7848
  Object.defineProperty(exports, "__esModule", { value: true });
7327
7849
  var codegen_1 = require_codegen();
@@ -7408,7 +7930,7 @@ var require_format = __commonJS((exports) => {
7408
7930
  exports.default = def;
7409
7931
  });
7410
7932
 
7411
- // node_modules/ajv/dist/vocabularies/format/index.js
7933
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/format/index.js
7412
7934
  var require_format2 = __commonJS((exports) => {
7413
7935
  Object.defineProperty(exports, "__esModule", { value: true });
7414
7936
  var format_1 = require_format();
@@ -7416,7 +7938,7 @@ var require_format2 = __commonJS((exports) => {
7416
7938
  exports.default = format;
7417
7939
  });
7418
7940
 
7419
- // node_modules/ajv/dist/vocabularies/metadata.js
7941
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/metadata.js
7420
7942
  var require_metadata = __commonJS((exports) => {
7421
7943
  Object.defineProperty(exports, "__esModule", { value: true });
7422
7944
  exports.contentVocabulary = exports.metadataVocabulary = undefined;
@@ -7436,7 +7958,7 @@ var require_metadata = __commonJS((exports) => {
7436
7958
  ];
7437
7959
  });
7438
7960
 
7439
- // node_modules/ajv/dist/vocabularies/draft7.js
7961
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/draft7.js
7440
7962
  var require_draft7 = __commonJS((exports) => {
7441
7963
  Object.defineProperty(exports, "__esModule", { value: true });
7442
7964
  var core_1 = require_core2();
@@ -7455,7 +7977,7 @@ var require_draft7 = __commonJS((exports) => {
7455
7977
  exports.default = draft7Vocabularies;
7456
7978
  });
7457
7979
 
7458
- // node_modules/ajv/dist/vocabularies/discriminator/types.js
7980
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/discriminator/types.js
7459
7981
  var require_types = __commonJS((exports) => {
7460
7982
  Object.defineProperty(exports, "__esModule", { value: true });
7461
7983
  exports.DiscrError = undefined;
@@ -7466,7 +7988,7 @@ var require_types = __commonJS((exports) => {
7466
7988
  })(DiscrError || (exports.DiscrError = DiscrError = {}));
7467
7989
  });
7468
7990
 
7469
- // node_modules/ajv/dist/vocabularies/discriminator/index.js
7991
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/vocabularies/discriminator/index.js
7470
7992
  var require_discriminator = __commonJS((exports) => {
7471
7993
  Object.defineProperty(exports, "__esModule", { value: true });
7472
7994
  var codegen_1 = require_codegen();
@@ -7542,8 +8064,8 @@ var require_discriminator = __commonJS((exports) => {
7542
8064
  if (!tagRequired)
7543
8065
  throw new Error(`discriminator: "${tagName}" must be required`);
7544
8066
  return oneOfMapping;
7545
- function hasRequired({ required: required2 }) {
7546
- return Array.isArray(required2) && required2.includes(tagName);
8067
+ function hasRequired({ required: required3 }) {
8068
+ return Array.isArray(required3) && required3.includes(tagName);
7547
8069
  }
7548
8070
  function addMappings(sch, i) {
7549
8071
  if (sch.const) {
@@ -7568,7 +8090,7 @@ var require_discriminator = __commonJS((exports) => {
7568
8090
  exports.default = def;
7569
8091
  });
7570
8092
 
7571
- // node_modules/ajv/dist/refs/json-schema-draft-07.json
8093
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/refs/json-schema-draft-07.json
7572
8094
  var require_json_schema_draft_07 = __commonJS((exports, module) => {
7573
8095
  module.exports = {
7574
8096
  $schema: "http://json-schema.org/draft-07/schema#",
@@ -7723,7 +8245,7 @@ var require_json_schema_draft_07 = __commonJS((exports, module) => {
7723
8245
  };
7724
8246
  });
7725
8247
 
7726
- // node_modules/ajv/dist/ajv.js
8248
+ // node_modules/.pnpm/ajv@8.20.0/node_modules/ajv/dist/ajv.js
7727
8249
  var require_ajv = __commonJS((exports, module) => {
7728
8250
  Object.defineProperty(exports, "__esModule", { value: true });
7729
8251
  exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = exports.Ajv = undefined;
@@ -7791,7 +8313,7 @@ var require_ajv = __commonJS((exports, module) => {
7791
8313
  } });
7792
8314
  });
7793
8315
 
7794
- // node_modules/ajv-formats/dist/formats.js
8316
+ // node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.20.0/node_modules/ajv-formats/dist/formats.js
7795
8317
  var require_formats = __commonJS((exports) => {
7796
8318
  Object.defineProperty(exports, "__esModule", { value: true });
7797
8319
  exports.formatNames = exports.fastFormats = exports.fullFormats = undefined;
@@ -7968,7 +8490,7 @@ var require_formats = __commonJS((exports) => {
7968
8490
  }
7969
8491
  });
7970
8492
 
7971
- // node_modules/ajv-formats/dist/limit.js
8493
+ // node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.20.0/node_modules/ajv-formats/dist/limit.js
7972
8494
  var require_limit = __commonJS((exports) => {
7973
8495
  Object.defineProperty(exports, "__esModule", { value: true });
7974
8496
  exports.formatLimitDefinition = undefined;
@@ -8037,7 +8559,7 @@ var require_limit = __commonJS((exports) => {
8037
8559
  exports.default = formatLimitPlugin;
8038
8560
  });
8039
8561
 
8040
- // node_modules/ajv-formats/dist/index.js
8562
+ // node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.20.0/node_modules/ajv-formats/dist/index.js
8041
8563
  var require_dist = __commonJS((exports, module) => {
8042
8564
  Object.defineProperty(exports, "__esModule", { value: true });
8043
8565
  var formats_1 = require_formats();
@@ -9024,6 +9546,94 @@ var init_pg_migrations = __esm(() => {
9024
9546
  CREATE INDEX IF NOT EXISTS idx_contact_tags_tag_contact ON contact_tags(tag_id, contact_id);
9025
9547
 
9026
9548
  INSERT INTO _migrations (version) VALUES (13) ON CONFLICT DO NOTHING;
9549
+ `,
9550
+ `
9551
+ CREATE TABLE IF NOT EXISTS contact_project_membership_states (
9552
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
9553
+ project_id TEXT NOT NULL,
9554
+ linked BOOLEAN NOT NULL,
9555
+ revision BIGINT NOT NULL DEFAULT 0 CHECK(revision >= 0),
9556
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
9557
+ PRIMARY KEY (contact_id, project_id)
9558
+ );
9559
+
9560
+ INSERT INTO contact_project_membership_states
9561
+ (contact_id, project_id, linked, revision, updated_at)
9562
+ SELECT contact_id, project_id, TRUE, 0, NOW()
9563
+ FROM contact_projects
9564
+ ON CONFLICT DO NOTHING;
9565
+
9566
+ CREATE TABLE IF NOT EXISTS contact_project_membership_receipts (
9567
+ receipt_id TEXT PRIMARY KEY,
9568
+ direction TEXT NOT NULL CHECK(direction IN ('attach', 'detach')),
9569
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
9570
+ project_id TEXT NOT NULL,
9571
+ operation_id TEXT NOT NULL,
9572
+ step_id TEXT NOT NULL,
9573
+ expected_version TEXT NOT NULL,
9574
+ before_json JSONB NOT NULL,
9575
+ after_json JSONB NOT NULL,
9576
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
9577
+ UNIQUE(operation_id, step_id)
9578
+ );
9579
+
9580
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_states_project
9581
+ ON contact_project_membership_states(project_id, linked, contact_id);
9582
+ CREATE INDEX IF NOT EXISTS idx_contact_project_membership_receipts_target
9583
+ ON contact_project_membership_receipts(contact_id, project_id, created_at);
9584
+
9585
+ CREATE OR REPLACE FUNCTION sync_contact_project_membership_state_from_legacy()
9586
+ RETURNS TRIGGER
9587
+ LANGUAGE plpgsql
9588
+ AS $$
9589
+ DECLARE
9590
+ target_contact_id TEXT;
9591
+ target_project_id TEXT;
9592
+ target_linked BOOLEAN;
9593
+ BEGIN
9594
+ IF TG_OP = 'INSERT' THEN
9595
+ target_contact_id := NEW.contact_id;
9596
+ target_project_id := NEW.project_id;
9597
+ target_linked := TRUE;
9598
+ ELSE
9599
+ target_contact_id := OLD.contact_id;
9600
+ target_project_id := OLD.project_id;
9601
+ target_linked := FALSE;
9602
+ IF NOT EXISTS (SELECT 1 FROM contacts WHERE id = target_contact_id) THEN
9603
+ RETURN OLD;
9604
+ END IF;
9605
+ END IF;
9606
+
9607
+ INSERT INTO contact_project_membership_states
9608
+ (contact_id, project_id, linked, revision, updated_at)
9609
+ VALUES (target_contact_id, target_project_id, target_linked, 1, NOW())
9610
+ ON CONFLICT (contact_id, project_id) DO UPDATE SET
9611
+ linked = EXCLUDED.linked,
9612
+ revision = CASE
9613
+ WHEN contact_project_membership_states.linked IS DISTINCT FROM EXCLUDED.linked
9614
+ THEN contact_project_membership_states.revision + 1
9615
+ ELSE contact_project_membership_states.revision
9616
+ END,
9617
+ updated_at = CASE
9618
+ WHEN contact_project_membership_states.linked IS DISTINCT FROM EXCLUDED.linked
9619
+ THEN NOW()
9620
+ ELSE contact_project_membership_states.updated_at
9621
+ END;
9622
+
9623
+ IF TG_OP = 'INSERT' THEN
9624
+ RETURN NEW;
9625
+ END IF;
9626
+ RETURN OLD;
9627
+ END;
9628
+ $$;
9629
+
9630
+ DROP TRIGGER IF EXISTS sync_contact_project_membership_state_from_legacy
9631
+ ON contact_projects;
9632
+ CREATE TRIGGER sync_contact_project_membership_state_from_legacy
9633
+ AFTER INSERT OR DELETE ON contact_projects
9634
+ FOR EACH ROW EXECUTE FUNCTION sync_contact_project_membership_state_from_legacy();
9635
+
9636
+ INSERT INTO _migrations (version) VALUES (14) ON CONFLICT DO NOTHING;
9027
9637
  `
9028
9638
  ];
9029
9639
  });
@@ -9152,7 +9762,7 @@ var init_cloud = __esm(() => {
9152
9762
  init_database();
9153
9763
  init_contacts();
9154
9764
  import { existsSync as existsSync9 } from "fs";
9155
- import { join as join7, resolve as resolve2, relative } from "path";
9765
+ import { join as join9, resolve as resolve2, relative } from "path";
9156
9766
 
9157
9767
  // src/db/companies.ts
9158
9768
  init_types();
@@ -9978,8 +10588,8 @@ async function exportContacts(format, contacts) {
9978
10588
  // src/lib/images.ts
9979
10589
  init_database();
9980
10590
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, unlinkSync, readdirSync as readdirSync2, readFileSync, writeFileSync } from "fs";
9981
- import { join as join2, extname, basename } from "path";
9982
- var IMAGES_DIR = join2(getDataDir(), "images");
10591
+ import { join as join3, extname, basename } from "path";
10592
+ var IMAGES_DIR = join3(getDataDir(), "images");
9983
10593
  function ensureImagesDir() {
9984
10594
  if (!existsSync3(IMAGES_DIR))
9985
10595
  mkdirSync3(IMAGES_DIR, { recursive: true });
@@ -9996,14 +10606,14 @@ function saveImage(entityId, source, options) {
9996
10606
  const ext2 = base64Match[1] === "jpeg" ? "jpg" : base64Match[1];
9997
10607
  const data = Buffer.from(base64Match[2], "base64");
9998
10608
  const filename2 = `${entityId}.${ext2}`;
9999
- writeFileSync(join2(IMAGES_DIR, filename2), data);
10609
+ writeFileSync(join3(IMAGES_DIR, filename2), data);
10000
10610
  return filename2;
10001
10611
  }
10002
10612
  if (!existsSync3(source) && /^[A-Za-z0-9+/=\n\r]+$/.test(source.trim()) && source.length > 100) {
10003
10613
  const ext2 = options?.format || "jpg";
10004
10614
  const data = Buffer.from(source.trim(), "base64");
10005
10615
  const filename2 = `${entityId}.${ext2}`;
10006
- writeFileSync(join2(IMAGES_DIR, filename2), data);
10616
+ writeFileSync(join3(IMAGES_DIR, filename2), data);
10007
10617
  return filename2;
10008
10618
  }
10009
10619
  if (!existsSync3(source)) {
@@ -10015,14 +10625,14 @@ function saveImage(entityId, source, options) {
10015
10625
  throw new Error(`Unsupported image format: ${ext}. Supported: ${validExts.join(", ")}`);
10016
10626
  }
10017
10627
  const filename = `${entityId}.${ext === "jpeg" ? "jpg" : ext}`;
10018
- copyFileSync2(source, join2(IMAGES_DIR, filename));
10628
+ copyFileSync2(source, join3(IMAGES_DIR, filename));
10019
10629
  return filename;
10020
10630
  }
10021
10631
  function getImagePath(entityId) {
10022
10632
  ensureImagesDir();
10023
10633
  const files = readdirSync2(IMAGES_DIR);
10024
10634
  const match = files.find((f) => f.startsWith(`${entityId}.`));
10025
- return match ? join2(IMAGES_DIR, match) : null;
10635
+ return match ? join3(IMAGES_DIR, match) : null;
10026
10636
  }
10027
10637
  function getImageAsBase64(entityId) {
10028
10638
  const path = getImagePath(entityId);
@@ -10039,7 +10649,7 @@ function deleteImage(entityId) {
10039
10649
  let deleted = false;
10040
10650
  for (const f of files) {
10041
10651
  if (f.startsWith(`${entityId}.`)) {
10042
- unlinkSync(join2(IMAGES_DIR, f));
10652
+ unlinkSync(join3(IMAGES_DIR, f));
10043
10653
  deleted = true;
10044
10654
  }
10045
10655
  }
@@ -10051,20 +10661,31 @@ function listImages() {
10051
10661
  return files.map((f) => ({
10052
10662
  entity_id: basename(f, extname(f)),
10053
10663
  filename: f,
10054
- path: join2(IMAGES_DIR, f)
10664
+ path: join3(IMAGES_DIR, f)
10055
10665
  }));
10056
10666
  }
10057
10667
 
10058
10668
  // src/lib/vault.ts
10059
10669
  init_database();
10060
10670
  import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync4, unlinkSync as unlinkSync2 } from "fs";
10061
- import { join as join3 } from "path";
10062
- import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash } from "crypto";
10063
- var VAULT_DIR = getDataDir();
10064
- var VAULT_CONFIG = join3(VAULT_DIR, "vault.json");
10065
- var VAULT_SESSION = join3(VAULT_DIR, ".vault-session");
10066
- var DOCUMENTS_DIR = join3(VAULT_DIR, "documents");
10671
+ import { join as join4 } from "path";
10672
+ import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync, createHash as createHash2 } from "crypto";
10067
10673
  var SESSION_TTL_MS = 30 * 60 * 1000;
10674
+ function getVaultDir() {
10675
+ return getDataDir();
10676
+ }
10677
+ function getVaultConfigPath() {
10678
+ return join4(getDataDir(), "vault.json");
10679
+ }
10680
+ function getVaultSessionPath() {
10681
+ return join4(getStateDir(), ".vault-session");
10682
+ }
10683
+ function getDocumentsDir() {
10684
+ const dir = join4(getDataDir(), "documents");
10685
+ if (!existsSync4(dir))
10686
+ mkdirSync4(dir, { recursive: true });
10687
+ return dir;
10688
+ }
10068
10689
  var _derivedKey = null;
10069
10690
  function deriveKey(passphrase, salt) {
10070
10691
  return pbkdf2Sync(passphrase, salt, 1e5, 32, "sha512");
@@ -10074,16 +10695,17 @@ function saveSession(key) {
10074
10695
  key: key.toString("hex"),
10075
10696
  expires_at: new Date(Date.now() + SESSION_TTL_MS).toISOString()
10076
10697
  };
10077
- writeFileSync2(VAULT_SESSION, JSON.stringify(session), { mode: 384 });
10698
+ writeFileSync2(getVaultSessionPath(), JSON.stringify(session), { mode: 384 });
10078
10699
  }
10079
10700
  function loadSession() {
10080
- if (!existsSync4(VAULT_SESSION))
10701
+ const sessionPath = getVaultSessionPath();
10702
+ if (!existsSync4(sessionPath))
10081
10703
  return null;
10082
10704
  try {
10083
- const session = JSON.parse(readFileSync2(VAULT_SESSION, "utf-8"));
10705
+ const session = JSON.parse(readFileSync2(sessionPath, "utf-8"));
10084
10706
  if (new Date(session.expires_at).getTime() < Date.now()) {
10085
10707
  try {
10086
- unlinkSync2(VAULT_SESSION);
10708
+ unlinkSync2(sessionPath);
10087
10709
  } catch {}
10088
10710
  return null;
10089
10711
  }
@@ -10094,33 +10716,33 @@ function loadSession() {
10094
10716
  }
10095
10717
  function clearSession() {
10096
10718
  try {
10097
- if (existsSync4(VAULT_SESSION))
10098
- unlinkSync2(VAULT_SESSION);
10719
+ if (existsSync4(getVaultSessionPath()))
10720
+ unlinkSync2(getVaultSessionPath());
10099
10721
  } catch {}
10100
10722
  }
10101
10723
  function initVault(passphrase) {
10102
- if (!existsSync4(VAULT_DIR))
10103
- mkdirSync4(VAULT_DIR, { recursive: true });
10104
- if (!existsSync4(DOCUMENTS_DIR))
10105
- mkdirSync4(DOCUMENTS_DIR, { recursive: true });
10724
+ const vaultDir = getVaultDir();
10725
+ if (!existsSync4(vaultDir))
10726
+ mkdirSync4(vaultDir, { recursive: true });
10727
+ mkdirSync4(getDocumentsDir(), { recursive: true });
10106
10728
  const salt = randomBytes(32);
10107
10729
  const key = deriveKey(passphrase, salt);
10108
- const keyHash = createHash("sha256").update(key).digest("hex");
10730
+ const keyHash = createHash2("sha256").update(key).digest("hex");
10109
10731
  const config = { salt: salt.toString("hex"), key_hash: keyHash, created_at: new Date().toISOString() };
10110
- writeFileSync2(VAULT_CONFIG, JSON.stringify(config, null, 2));
10732
+ writeFileSync2(getVaultConfigPath(), JSON.stringify(config, null, 2));
10111
10733
  _derivedKey = key;
10112
10734
  saveSession(key);
10113
10735
  }
10114
10736
  function isVaultInitialized() {
10115
- return existsSync4(VAULT_CONFIG);
10737
+ return existsSync4(getVaultConfigPath());
10116
10738
  }
10117
10739
  function unlockVault(passphrase) {
10118
- if (!existsSync4(VAULT_CONFIG))
10740
+ if (!existsSync4(getVaultConfigPath()))
10119
10741
  throw new Error("Vault not initialized. Run 'contacts vault init' first.");
10120
- const config = JSON.parse(readFileSync2(VAULT_CONFIG, "utf-8"));
10742
+ const config = JSON.parse(readFileSync2(getVaultConfigPath(), "utf-8"));
10121
10743
  const salt = Buffer.from(config.salt, "hex");
10122
10744
  const key = deriveKey(passphrase, salt);
10123
- const keyHash = createHash("sha256").update(key).digest("hex");
10745
+ const keyHash = createHash2("sha256").update(key).digest("hex");
10124
10746
  if (keyHash !== config.key_hash)
10125
10747
  return false;
10126
10748
  _derivedKey = key;
@@ -10172,21 +10794,52 @@ function decrypt(ciphertext, iv) {
10172
10794
  return decrypted;
10173
10795
  }
10174
10796
  function storeFile(sourcePath, entityId) {
10175
- if (!existsSync4(DOCUMENTS_DIR))
10176
- mkdirSync4(DOCUMENTS_DIR, { recursive: true });
10797
+ const documentsDir = getDocumentsDir();
10798
+ if (!existsSync4(documentsDir))
10799
+ mkdirSync4(documentsDir, { recursive: true });
10177
10800
  const ext = sourcePath.split(".").pop() || "bin";
10178
- const destPath = join3(DOCUMENTS_DIR, `${entityId}.${ext}`);
10801
+ const destPath = join4(documentsDir, `${entityId}.${ext}`);
10179
10802
  const data = readFileSync2(sourcePath);
10180
10803
  writeFileSync2(destPath, data);
10181
10804
  return destPath;
10182
10805
  }
10183
- function getDocumentsDir() {
10184
- if (!existsSync4(DOCUMENTS_DIR))
10185
- mkdirSync4(DOCUMENTS_DIR, { recursive: true });
10186
- return DOCUMENTS_DIR;
10806
+
10807
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/mediaType.js
10808
+ var import_content_type = __toESM(require_content_type(), 1);
10809
+ function mediaTypeEssence(header) {
10810
+ if (!header) {
10811
+ return;
10812
+ }
10813
+ try {
10814
+ return import_content_type.default.parse(header).type;
10815
+ } catch {
10816
+ const essence = (header.split(";", 1)[0] ?? "").trim().toLowerCase();
10817
+ if (essence === "" || header.slice(essence.length).includes(",")) {
10818
+ return;
10819
+ }
10820
+ return essence;
10821
+ }
10822
+ }
10823
+ function isJsonContentType(header) {
10824
+ if (header === "application/json") {
10825
+ return true;
10826
+ }
10827
+ return mediaTypeEssence(header) === "application/json";
10187
10828
  }
10188
10829
 
10189
- // node_modules/zod/v4/core/core.js
10830
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/sseKeepAlive.js
10831
+ var DEFAULT_SSE_KEEP_ALIVE_MS = 15000;
10832
+ var MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
10833
+ function armSseKeepAlive(intervalMs, onTick) {
10834
+ if (!Number.isFinite(intervalMs) || intervalMs < 1) {
10835
+ return;
10836
+ }
10837
+ const timer = setInterval(onTick, Math.min(intervalMs, MAX_TIMER_DELAY_MS));
10838
+ timer.unref?.();
10839
+ return timer;
10840
+ }
10841
+
10842
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js
10190
10843
  var NEVER = Object.freeze({
10191
10844
  status: "aborted"
10192
10845
  });
@@ -10246,12 +10899,12 @@ function config(newConfig) {
10246
10899
  Object.assign(globalConfig, newConfig);
10247
10900
  return globalConfig;
10248
10901
  }
10249
- // node_modules/zod/v4/core/util.js
10902
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/util.js
10250
10903
  var exports_util = {};
10251
10904
  __export(exports_util, {
10252
10905
  unwrapMessage: () => unwrapMessage,
10253
10906
  stringifyPrimitive: () => stringifyPrimitive,
10254
- required: () => required,
10907
+ required: () => required2,
10255
10908
  randomString: () => randomString,
10256
10909
  propertyKeyTypes: () => propertyKeyTypes,
10257
10910
  promiseAllObject: () => promiseAllObject,
@@ -10662,7 +11315,7 @@ function partial(Class, schema, mask) {
10662
11315
  checks: []
10663
11316
  });
10664
11317
  }
10665
- function required(Class, schema, mask) {
11318
+ function required2(Class, schema, mask) {
10666
11319
  const oldShape = schema._zod.def.shape;
10667
11320
  const shape = { ...oldShape };
10668
11321
  if (mask) {
@@ -10760,7 +11413,7 @@ class Class {
10760
11413
  constructor(..._args) {}
10761
11414
  }
10762
11415
 
10763
- // node_modules/zod/v4/core/errors.js
11416
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/errors.js
10764
11417
  var initializer = (inst, def) => {
10765
11418
  inst.name = "$ZodError";
10766
11419
  Object.defineProperty(inst, "_zod", {
@@ -10834,7 +11487,7 @@ function formatError(error, _mapper) {
10834
11487
  return fieldErrors;
10835
11488
  }
10836
11489
 
10837
- // node_modules/zod/v4/core/parse.js
11490
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/parse.js
10838
11491
  var _parse = (_Err) => (schema, value, _ctx, _params) => {
10839
11492
  const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false };
10840
11493
  const result = schema._zod.run({ value, issues: [] }, ctx);
@@ -10885,7 +11538,7 @@ var _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
10885
11538
  } : { success: true, data: result.value };
10886
11539
  };
10887
11540
  var safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError);
10888
- // node_modules/zod/v4/core/regexes.js
11541
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/regexes.js
10889
11542
  var cuid = /^[cC][^\s-]{8,}$/;
10890
11543
  var cuid2 = /^[0-9a-z]+$/;
10891
11544
  var ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;
@@ -10943,7 +11596,7 @@ var _null = /null/i;
10943
11596
  var lowercase = /^[^A-Z]*$/;
10944
11597
  var uppercase = /^[^a-z]*$/;
10945
11598
 
10946
- // node_modules/zod/v4/core/checks.js
11599
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/checks.js
10947
11600
  var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => {
10948
11601
  var _a;
10949
11602
  inst._zod ?? (inst._zod = {});
@@ -11327,7 +11980,7 @@ var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (ins
11327
11980
  };
11328
11981
  });
11329
11982
 
11330
- // node_modules/zod/v4/core/doc.js
11983
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/doc.js
11331
11984
  class Doc {
11332
11985
  constructor(args = []) {
11333
11986
  this.content = [];
@@ -11365,14 +12018,14 @@ class Doc {
11365
12018
  }
11366
12019
  }
11367
12020
 
11368
- // node_modules/zod/v4/core/versions.js
12021
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/versions.js
11369
12022
  var version = {
11370
12023
  major: 4,
11371
12024
  minor: 0,
11372
12025
  patch: 0
11373
12026
  };
11374
12027
 
11375
- // node_modules/zod/v4/core/schemas.js
12028
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/schemas.js
11376
12029
  var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => {
11377
12030
  var _a;
11378
12031
  inst ?? (inst = {});
@@ -12603,7 +13256,7 @@ function handleRefineResult(result, payload, input, inst) {
12603
13256
  payload.issues.push(issue(_iss));
12604
13257
  }
12605
13258
  }
12606
- // node_modules/zod/v4/locales/en.js
13259
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/locales/en.js
12607
13260
  var parsedType = (data) => {
12608
13261
  const t = typeof data;
12609
13262
  switch (t) {
@@ -12720,7 +13373,7 @@ function en_default() {
12720
13373
  localeError: error()
12721
13374
  };
12722
13375
  }
12723
- // node_modules/zod/v4/core/registries.js
13376
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/registries.js
12724
13377
  var $output = Symbol("ZodOutput");
12725
13378
  var $input = Symbol("ZodInput");
12726
13379
 
@@ -12770,7 +13423,7 @@ function registry() {
12770
13423
  return new $ZodRegistry;
12771
13424
  }
12772
13425
  var globalRegistry = /* @__PURE__ */ registry();
12773
- // node_modules/zod/v4/core/api.js
13426
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/api.js
12774
13427
  function _string(Class2, params) {
12775
13428
  return new Class2({
12776
13429
  type: "string",
@@ -13205,7 +13858,7 @@ function _refine(Class2, fn, _params) {
13205
13858
  });
13206
13859
  return schema;
13207
13860
  }
13208
- // node_modules/zod/v4/core/to-json-schema.js
13861
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/to-json-schema.js
13209
13862
  class JSONSchemaGenerator {
13210
13863
  constructor(params) {
13211
13864
  this.counter = 0;
@@ -13957,7 +14610,7 @@ function isTransforming(_schema, _ctx) {
13957
14610
  }
13958
14611
  throw new Error(`Unknown schema type: ${def.type}`);
13959
14612
  }
13960
- // node_modules/zod/v4/classic/iso.js
14613
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/iso.js
13961
14614
  var exports_iso = {};
13962
14615
  __export(exports_iso, {
13963
14616
  time: () => time2,
@@ -13998,7 +14651,7 @@ function duration2(params) {
13998
14651
  return _isoDuration(ZodISODuration, params);
13999
14652
  }
14000
14653
 
14001
- // node_modules/zod/v4/classic/errors.js
14654
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/errors.js
14002
14655
  var initializer2 = (inst, issues) => {
14003
14656
  $ZodError.init(inst, issues);
14004
14657
  inst.name = "ZodError";
@@ -14027,13 +14680,13 @@ var ZodRealError = $constructor("ZodError", initializer2, {
14027
14680
  Parent: Error
14028
14681
  });
14029
14682
 
14030
- // node_modules/zod/v4/classic/parse.js
14683
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/parse.js
14031
14684
  var parse3 = /* @__PURE__ */ _parse(ZodRealError);
14032
14685
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
14033
14686
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
14034
14687
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
14035
14688
 
14036
- // node_modules/zod/v4/classic/schemas.js
14689
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/schemas.js
14037
14690
  var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
14038
14691
  $ZodType.init(inst, def);
14039
14692
  inst.def = def;
@@ -14638,10 +15291,10 @@ function superRefine(fn) {
14638
15291
  function preprocess(fn, schema) {
14639
15292
  return pipe(transform(fn), schema);
14640
15293
  }
14641
- // node_modules/zod/v4/classic/external.js
15294
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/external.js
14642
15295
  config(en_default());
14643
15296
 
14644
- // node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
15297
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
14645
15298
  var LATEST_PROTOCOL_VERSION = "2025-11-25";
14646
15299
  var DEFAULT_NEGOTIATED_PROTOCOL_VERSION = "2025-03-26";
14647
15300
  var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
@@ -14651,7 +15304,7 @@ var AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" || t
14651
15304
  var ProgressTokenSchema = union([string2(), number2().int()]);
14652
15305
  var CursorSchema = string2();
14653
15306
  var TaskCreationParamsSchema = looseObject({
14654
- ttl: union([number2(), _null3()]).optional(),
15307
+ ttl: number2().optional(),
14655
15308
  pollInterval: number2().optional()
14656
15309
  });
14657
15310
  var TaskMetadataSchema = object({
@@ -14805,7 +15458,8 @@ var ClientCapabilitiesSchema = object({
14805
15458
  roots: object({
14806
15459
  listChanged: boolean2().optional()
14807
15460
  }).optional(),
14808
- tasks: ClientTasksCapabilitySchema.optional()
15461
+ tasks: ClientTasksCapabilitySchema.optional(),
15462
+ extensions: record(string2(), AssertObjectSchema).optional()
14809
15463
  });
14810
15464
  var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
14811
15465
  protocolVersion: string2(),
@@ -14831,7 +15485,8 @@ var ServerCapabilitiesSchema = object({
14831
15485
  tools: object({
14832
15486
  listChanged: boolean2().optional()
14833
15487
  }).optional(),
14834
- tasks: ServerTasksCapabilitySchema.optional()
15488
+ tasks: ServerTasksCapabilitySchema.optional(),
15489
+ extensions: record(string2(), AssertObjectSchema).optional()
14835
15490
  });
14836
15491
  var InitializeResultSchema = ResultSchema.extend({
14837
15492
  protocolVersion: string2(),
@@ -14946,6 +15601,7 @@ var ResourceSchema = object({
14946
15601
  uri: string2(),
14947
15602
  description: optional(string2()),
14948
15603
  mimeType: optional(string2()),
15604
+ size: optional(number2()),
14949
15605
  annotations: AnnotationsSchema.optional(),
14950
15606
  _meta: optional(looseObject({}))
14951
15607
  });
@@ -15485,17 +16141,19 @@ class UrlElicitationRequiredError extends McpError {
15485
16141
  }
15486
16142
  }
15487
16143
 
15488
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
16144
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/webStandardStreamableHttp.js
15489
16145
  class WebStandardStreamableHTTPServerTransport {
15490
16146
  constructor(options = {}) {
15491
16147
  this._started = false;
15492
16148
  this._hasHandledRequest = false;
15493
16149
  this._streamMapping = new Map;
15494
16150
  this._requestToStreamMapping = new Map;
16151
+ this._resumableStreams = new Set;
15495
16152
  this._requestResponseMap = new Map;
15496
16153
  this._initialized = false;
15497
16154
  this._enableJsonResponse = false;
15498
16155
  this._standaloneSseStreamId = "_GET_stream";
16156
+ this._closed = false;
15499
16157
  this.sessionIdGenerator = options.sessionIdGenerator;
15500
16158
  this._enableJsonResponse = options.enableJsonResponse ?? false;
15501
16159
  this._eventStore = options.eventStore;
@@ -15505,6 +16163,22 @@ class WebStandardStreamableHTTPServerTransport {
15505
16163
  this._allowedOrigins = options.allowedOrigins;
15506
16164
  this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
15507
16165
  this._retryInterval = options.retryInterval;
16166
+ this._keepAliveMs = options.keepAliveMs ?? DEFAULT_SSE_KEEP_ALIVE_MS;
16167
+ }
16168
+ startKeepAlive(controller, encoder) {
16169
+ if (this._closed)
16170
+ return;
16171
+ const timer = armSseKeepAlive(this._keepAliveMs, () => {
16172
+ try {
16173
+ controller.enqueue(encoder.encode(`: keepalive
16174
+
16175
+ `));
16176
+ } catch {
16177
+ if (timer !== undefined)
16178
+ clearInterval(timer);
16179
+ }
16180
+ });
16181
+ return timer;
15508
16182
  }
15509
16183
  async start() {
15510
16184
  if (this._started) {
@@ -15552,6 +16226,9 @@ class WebStandardStreamableHTTPServerTransport {
15552
16226
  return;
15553
16227
  }
15554
16228
  async handleRequest(req, options) {
16229
+ if (this._closed) {
16230
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
16231
+ }
15555
16232
  if (!this.sessionIdGenerator && this._hasHandledRequest) {
15556
16233
  throw new Error("Stateless transport cannot be reused across requests. Create a new transport per request.");
15557
16234
  }
@@ -15591,6 +16268,7 @@ data:
15591
16268
  `;
15592
16269
  }
15593
16270
  controller.enqueue(encoder.encode(primingEvent));
16271
+ this._resumableStreams.add(streamId);
15594
16272
  }
15595
16273
  async handleGetRequest(req) {
15596
16274
  const acceptHeader = req.headers.get("accept");
@@ -15618,18 +16296,25 @@ data:
15618
16296
  }
15619
16297
  const encoder = new TextEncoder;
15620
16298
  let streamController;
16299
+ let keepAliveTimer = undefined;
15621
16300
  const readable = new ReadableStream({
15622
16301
  start: (controller) => {
15623
16302
  streamController = controller;
15624
16303
  },
15625
16304
  cancel: () => {
15626
- this._streamMapping.delete(this._standaloneSseStreamId);
16305
+ if (keepAliveTimer !== undefined) {
16306
+ clearInterval(keepAliveTimer);
16307
+ }
16308
+ if (this._streamMapping.get(this._standaloneSseStreamId)?.controller === streamController) {
16309
+ this._streamMapping.delete(this._standaloneSseStreamId);
16310
+ }
15627
16311
  }
15628
16312
  });
15629
16313
  const headers = {
15630
16314
  "Content-Type": "text/event-stream",
15631
16315
  "Cache-Control": "no-cache, no-transform",
15632
- Connection: "keep-alive"
16316
+ Connection: "keep-alive",
16317
+ "X-Accel-Buffering": "no"
15633
16318
  };
15634
16319
  if (this.sessionId !== undefined) {
15635
16320
  headers["mcp-session-id"] = this.sessionId;
@@ -15638,12 +16323,16 @@ data:
15638
16323
  controller: streamController,
15639
16324
  encoder,
15640
16325
  cleanup: () => {
16326
+ if (keepAliveTimer !== undefined) {
16327
+ clearInterval(keepAliveTimer);
16328
+ }
15641
16329
  this._streamMapping.delete(this._standaloneSseStreamId);
15642
16330
  try {
15643
16331
  streamController.close();
15644
16332
  } catch {}
15645
16333
  }
15646
16334
  });
16335
+ keepAliveTimer = this.startKeepAlive(streamController, encoder);
15647
16336
  return new Response(readable, { headers });
15648
16337
  }
15649
16338
  async replayEvents(lastEventId) {
@@ -15667,20 +16356,33 @@ data:
15667
16356
  const headers = {
15668
16357
  "Content-Type": "text/event-stream",
15669
16358
  "Cache-Control": "no-cache, no-transform",
15670
- Connection: "keep-alive"
16359
+ Connection: "keep-alive",
16360
+ "X-Accel-Buffering": "no"
15671
16361
  };
15672
16362
  if (this.sessionId !== undefined) {
15673
16363
  headers["mcp-session-id"] = this.sessionId;
15674
16364
  }
15675
16365
  const encoder = new TextEncoder;
15676
16366
  let streamController;
16367
+ let keepAliveTimer = undefined;
16368
+ let replayedStreamId = undefined;
16369
+ let cancelled = false;
15677
16370
  const readable = new ReadableStream({
15678
16371
  start: (controller) => {
15679
16372
  streamController = controller;
15680
16373
  },
15681
- cancel: () => {}
16374
+ cancel: () => {
16375
+ cancelled = true;
16376
+ if (keepAliveTimer !== undefined) {
16377
+ clearInterval(keepAliveTimer);
16378
+ }
16379
+ if (replayedStreamId !== undefined && this._streamMapping.get(replayedStreamId)?.controller === streamController) {
16380
+ this._streamMapping.delete(replayedStreamId);
16381
+ }
16382
+ }
15682
16383
  });
15683
- const replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {
16384
+ const replayedEventIds = new Set;
16385
+ replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, {
15684
16386
  send: async (eventId, message) => {
15685
16387
  const success = this.writeSSEEvent(streamController, encoder, message, eventId);
15686
16388
  if (!success) {
@@ -15688,19 +16390,34 @@ data:
15688
16390
  try {
15689
16391
  streamController.close();
15690
16392
  } catch {}
16393
+ } else {
16394
+ replayedEventIds.add(eventId);
15691
16395
  }
15692
16396
  }
15693
16397
  });
16398
+ if (this._closed || cancelled) {
16399
+ try {
16400
+ streamController.close();
16401
+ } catch {}
16402
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
16403
+ }
16404
+ this._streamMapping.get(replayedStreamId)?.cleanup();
15694
16405
  this._streamMapping.set(replayedStreamId, {
15695
16406
  controller: streamController,
15696
16407
  encoder,
16408
+ replayedEventIds,
15697
16409
  cleanup: () => {
16410
+ if (keepAliveTimer !== undefined) {
16411
+ clearInterval(keepAliveTimer);
16412
+ }
15698
16413
  this._streamMapping.delete(replayedStreamId);
15699
16414
  try {
15700
16415
  streamController.close();
15701
16416
  } catch {}
15702
16417
  }
15703
16418
  });
16419
+ this._resumableStreams.add(replayedStreamId);
16420
+ keepAliveTimer = this.startKeepAlive(streamController, encoder);
15704
16421
  return new Response(readable, { headers });
15705
16422
  } catch (error2) {
15706
16423
  this.onerror?.(error2);
@@ -15750,7 +16467,7 @@ data:
15750
16467
  return this.createJsonErrorResponse(406, -32000, "Not Acceptable: Client must accept both application/json and text/event-stream");
15751
16468
  }
15752
16469
  const ct = req.headers.get("content-type");
15753
- if (!ct || !ct.includes("application/json")) {
16470
+ if (!isJsonContentType(ct)) {
15754
16471
  this.onerror?.(new Error("Unsupported Media Type: Content-Type must be application/json"));
15755
16472
  return this.createJsonErrorResponse(415, -32000, "Unsupported Media Type: Content-Type must be application/json");
15756
16473
  }
@@ -15780,6 +16497,9 @@ data:
15780
16497
  this.onerror?.(new Error("Parse error: Invalid JSON-RPC message"));
15781
16498
  return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
15782
16499
  }
16500
+ if (this._closed) {
16501
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
16502
+ }
15783
16503
  const isInitializationRequest = messages.some(isInitializeRequest);
15784
16504
  if (isInitializationRequest) {
15785
16505
  if (this._initialized && this.sessionId !== undefined) {
@@ -15806,6 +16526,9 @@ data:
15806
16526
  return protocolError;
15807
16527
  }
15808
16528
  }
16529
+ if (this._closed) {
16530
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
16531
+ }
15809
16532
  const hasRequests = messages.some(isJSONRPCRequest);
15810
16533
  if (!hasRequests) {
15811
16534
  for (const message of messages) {
@@ -15836,18 +16559,25 @@ data:
15836
16559
  }
15837
16560
  const encoder = new TextEncoder;
15838
16561
  let streamController;
16562
+ let keepAliveTimer = undefined;
15839
16563
  const readable = new ReadableStream({
15840
16564
  start: (controller) => {
15841
16565
  streamController = controller;
15842
16566
  },
15843
16567
  cancel: () => {
15844
- this._streamMapping.delete(streamId);
16568
+ if (keepAliveTimer !== undefined) {
16569
+ clearInterval(keepAliveTimer);
16570
+ }
16571
+ if (this._streamMapping.get(streamId)?.controller === streamController) {
16572
+ this._streamMapping.delete(streamId);
16573
+ }
15845
16574
  }
15846
16575
  });
15847
16576
  const headers = {
15848
16577
  "Content-Type": "text/event-stream",
15849
- "Cache-Control": "no-cache",
15850
- Connection: "keep-alive"
16578
+ "Cache-Control": "no-cache, no-transform",
16579
+ Connection: "keep-alive",
16580
+ "X-Accel-Buffering": "no"
15851
16581
  };
15852
16582
  if (this.sessionId !== undefined) {
15853
16583
  headers["mcp-session-id"] = this.sessionId;
@@ -15858,6 +16588,9 @@ data:
15858
16588
  controller: streamController,
15859
16589
  encoder,
15860
16590
  cleanup: () => {
16591
+ if (keepAliveTimer !== undefined) {
16592
+ clearInterval(keepAliveTimer);
16593
+ }
15861
16594
  this._streamMapping.delete(streamId);
15862
16595
  try {
15863
16596
  streamController.close();
@@ -15867,19 +16600,33 @@ data:
15867
16600
  this._requestToStreamMapping.set(message.id, streamId);
15868
16601
  }
15869
16602
  }
15870
- await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
15871
- for (const message of messages) {
15872
- let closeSSEStream;
15873
- let closeStandaloneSSEStream;
15874
- if (isJSONRPCRequest(message) && this._eventStore && clientProtocolVersion >= "2025-11-25") {
15875
- closeSSEStream = () => {
15876
- this.closeSSEStream(message.id);
15877
- };
15878
- closeStandaloneSSEStream = () => {
15879
- this.closeStandaloneSSEStream();
15880
- };
16603
+ try {
16604
+ await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
16605
+ for (const message of messages) {
16606
+ let closeSSEStream;
16607
+ let closeStandaloneSSEStream;
16608
+ if (isJSONRPCRequest(message) && this._eventStore && clientProtocolVersion >= "2025-11-25") {
16609
+ closeSSEStream = () => {
16610
+ this.closeSSEStream(message.id);
16611
+ };
16612
+ closeStandaloneSSEStream = () => {
16613
+ this.closeStandaloneSSEStream();
16614
+ };
16615
+ }
16616
+ this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo, closeSSEStream, closeStandaloneSSEStream });
15881
16617
  }
15882
- this.onmessage?.(message, { authInfo: options?.authInfo, requestInfo, closeSSEStream, closeStandaloneSSEStream });
16618
+ } catch (error2) {
16619
+ this._streamMapping.get(streamId)?.cleanup();
16620
+ this._resumableStreams.delete(streamId);
16621
+ for (const message of messages) {
16622
+ if (isJSONRPCRequest(message)) {
16623
+ this._requestToStreamMapping.delete(message.id);
16624
+ }
16625
+ }
16626
+ throw error2;
16627
+ }
16628
+ if (this._streamMapping.get(streamId)?.controller === streamController) {
16629
+ keepAliveTimer = this.startKeepAlive(streamController, encoder);
15883
16630
  }
15884
16631
  return new Response(readable, { status: 200, headers });
15885
16632
  } catch (error2) {
@@ -15896,9 +16643,12 @@ data:
15896
16643
  if (protocolError) {
15897
16644
  return protocolError;
15898
16645
  }
15899
- await Promise.resolve(this._onsessionclosed?.(this.sessionId));
15900
- await this.close();
15901
- return new Response(null, { status: 200 });
16646
+ try {
16647
+ await Promise.resolve(this._onsessionclosed?.(this.sessionId));
16648
+ return new Response(null, { status: 200 });
16649
+ } finally {
16650
+ await this.close();
16651
+ }
15902
16652
  }
15903
16653
  validateSession(req) {
15904
16654
  if (this.sessionIdGenerator === undefined) {
@@ -15928,11 +16678,16 @@ data:
15928
16678
  return;
15929
16679
  }
15930
16680
  async close() {
16681
+ if (this._closed) {
16682
+ return;
16683
+ }
16684
+ this._closed = true;
15931
16685
  this._streamMapping.forEach(({ cleanup }) => {
15932
16686
  cleanup();
15933
16687
  });
15934
16688
  this._streamMapping.clear();
15935
16689
  this._requestResponseMap.clear();
16690
+ this._resumableStreams.clear();
15936
16691
  this.onclose?.();
15937
16692
  }
15938
16693
  closeSSEStream(requestId) {
@@ -15967,7 +16722,7 @@ data:
15967
16722
  if (standaloneSse === undefined) {
15968
16723
  return;
15969
16724
  }
15970
- if (standaloneSse.controller && standaloneSse.encoder) {
16725
+ if (standaloneSse.controller && standaloneSse.encoder && (eventId === undefined || !standaloneSse.replayedEventIds?.has(eventId))) {
15971
16726
  this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);
15972
16727
  }
15973
16728
  return;
@@ -15976,13 +16731,19 @@ data:
15976
16731
  if (!streamId) {
15977
16732
  throw new Error(`No connection established for request ID: ${String(requestId)}`);
15978
16733
  }
15979
- const stream = this._streamMapping.get(streamId);
15980
- if (!this._enableJsonResponse && stream?.controller && stream?.encoder) {
16734
+ let stream = this._streamMapping.get(streamId);
16735
+ if (!this._enableJsonResponse) {
15981
16736
  let eventId;
15982
16737
  if (this._eventStore) {
15983
16738
  eventId = await this._eventStore.storeEvent(streamId, message);
16739
+ stream = this._streamMapping.get(streamId);
16740
+ }
16741
+ if (stream?.controller && stream?.encoder && (eventId === undefined || !stream.replayedEventIds?.has(eventId))) {
16742
+ const written = this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
16743
+ if (written && eventId !== undefined) {
16744
+ this._resumableStreams.add(streamId);
16745
+ }
15984
16746
  }
15985
- this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
15986
16747
  }
15987
16748
  if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
15988
16749
  this._requestResponseMap.set(requestId, message);
@@ -15990,6 +16751,25 @@ data:
15990
16751
  const allResponsesReady = relatedIds.every((id) => this._requestResponseMap.has(id));
15991
16752
  if (allResponsesReady) {
15992
16753
  if (!stream) {
16754
+ if (this._closed) {
16755
+ for (const id of relatedIds) {
16756
+ this._requestResponseMap.delete(id);
16757
+ this._requestToStreamMapping.delete(id);
16758
+ }
16759
+ return;
16760
+ }
16761
+ if (!this._enableJsonResponse && this._eventStore && this._resumableStreams.has(streamId)) {
16762
+ for (const id of relatedIds) {
16763
+ this._requestResponseMap.delete(id);
16764
+ this._requestToStreamMapping.delete(id);
16765
+ }
16766
+ this._resumableStreams.delete(streamId);
16767
+ return;
16768
+ }
16769
+ for (const id of relatedIds) {
16770
+ this._requestResponseMap.delete(id);
16771
+ this._requestToStreamMapping.delete(id);
16772
+ }
15993
16773
  throw new Error(`No connection established for request ID: ${String(requestId)}`);
15994
16774
  }
15995
16775
  if (this._enableJsonResponse && stream.resolveJson) {
@@ -16012,6 +16792,7 @@ data:
16012
16792
  this._requestResponseMap.delete(id);
16013
16793
  this._requestToStreamMapping.delete(id);
16014
16794
  }
16795
+ this._resumableStreams.delete(streamId);
16015
16796
  }
16016
16797
  }
16017
16798
  }
@@ -16067,9 +16848,9 @@ function startMcpHttpServer(options) {
16067
16848
 
16068
16849
  // src/mcp/index.ts
16069
16850
  import { readFileSync as readFileSync5 } from "fs";
16070
- import { join as join5 } from "path";
16851
+ import { join as join7 } from "path";
16071
16852
 
16072
- // node_modules/zod/v3/external.js
16853
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
16073
16854
  var exports_external = {};
16074
16855
  __export(exports_external, {
16075
16856
  void: () => voidType,
@@ -16181,7 +16962,7 @@ __export(exports_external, {
16181
16962
  BRAND: () => BRAND
16182
16963
  });
16183
16964
 
16184
- // node_modules/zod/v3/helpers/util.js
16965
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js
16185
16966
  var util;
16186
16967
  (function(util2) {
16187
16968
  util2.assertEqual = (_) => {};
@@ -16312,7 +17093,7 @@ var getParsedType2 = (data) => {
16312
17093
  }
16313
17094
  };
16314
17095
 
16315
- // node_modules/zod/v3/ZodError.js
17096
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js
16316
17097
  var ZodIssueCode = util.arrayToEnum([
16317
17098
  "invalid_type",
16318
17099
  "invalid_literal",
@@ -16431,7 +17212,7 @@ ZodError2.create = (issues) => {
16431
17212
  return error2;
16432
17213
  };
16433
17214
 
16434
- // node_modules/zod/v3/locales/en.js
17215
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js
16435
17216
  var errorMap = (issue2, _ctx) => {
16436
17217
  let message;
16437
17218
  switch (issue2.code) {
@@ -16534,7 +17315,7 @@ var errorMap = (issue2, _ctx) => {
16534
17315
  };
16535
17316
  var en_default2 = errorMap;
16536
17317
 
16537
- // node_modules/zod/v3/errors.js
17318
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js
16538
17319
  var overrideErrorMap = en_default2;
16539
17320
  function setErrorMap(map) {
16540
17321
  overrideErrorMap = map;
@@ -16542,7 +17323,7 @@ function setErrorMap(map) {
16542
17323
  function getErrorMap() {
16543
17324
  return overrideErrorMap;
16544
17325
  }
16545
- // node_modules/zod/v3/helpers/parseUtil.js
17326
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
16546
17327
  var makeIssue = (params) => {
16547
17328
  const { data, path, errorMaps, issueData } = params;
16548
17329
  const fullPath = [...path, ...issueData.path || []];
@@ -16648,14 +17429,14 @@ var isAborted = (x) => x.status === "aborted";
16648
17429
  var isDirty = (x) => x.status === "dirty";
16649
17430
  var isValid = (x) => x.status === "valid";
16650
17431
  var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
16651
- // node_modules/zod/v3/helpers/errorUtil.js
17432
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
16652
17433
  var errorUtil;
16653
17434
  (function(errorUtil2) {
16654
17435
  errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
16655
17436
  errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
16656
17437
  })(errorUtil || (errorUtil = {}));
16657
17438
 
16658
- // node_modules/zod/v3/types.js
17439
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
16659
17440
  class ParseInputLazyPath {
16660
17441
  constructor(parent, value, path, key) {
16661
17442
  this._cachedPath = [];
@@ -20042,7 +20823,7 @@ var coerce = {
20042
20823
  date: (arg) => ZodDate.create({ ...arg, coerce: true })
20043
20824
  };
20044
20825
  var NEVER2 = INVALID;
20045
- // node_modules/zod/v4/mini/schemas.js
20826
+ // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/mini/schemas.js
20046
20827
  var ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => {
20047
20828
  if (!inst._zod)
20048
20829
  throw new Error("Uninitialized schema in ZodMiniType.");
@@ -20084,7 +20865,7 @@ function object2(shape, params) {
20084
20865
  };
20085
20866
  return new ZodMiniObject(def);
20086
20867
  }
20087
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
20868
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
20088
20869
  function isZ4Schema(s) {
20089
20870
  const schema = s;
20090
20871
  return !!schema._zod;
@@ -20168,17 +20949,34 @@ function normalizeObjectSchema(schema) {
20168
20949
  }
20169
20950
  return;
20170
20951
  }
20952
+ function getDotPath(path) {
20953
+ if (path.length === 0) {
20954
+ return "object root";
20955
+ }
20956
+ return path.reduce((acc, seg, index) => {
20957
+ if (index === 0) {
20958
+ return String(seg);
20959
+ }
20960
+ if (typeof seg === "number") {
20961
+ return `${acc}[${seg}]`;
20962
+ }
20963
+ return `${acc}.${seg}`;
20964
+ }, "");
20965
+ }
20171
20966
  function getParseErrorMessage(error2) {
20172
20967
  if (error2 && typeof error2 === "object") {
20968
+ if ("issues" in error2 && Array.isArray(error2.issues) && error2.issues.length > 0) {
20969
+ return error2.issues.map((i) => {
20970
+ if (!i.path?.length) {
20971
+ return i.message;
20972
+ }
20973
+ return `${i.message} at ${getDotPath(i.path)}`;
20974
+ }).join(`
20975
+ `);
20976
+ }
20173
20977
  if ("message" in error2 && typeof error2.message === "string") {
20174
20978
  return error2.message;
20175
20979
  }
20176
- if ("issues" in error2 && Array.isArray(error2.issues) && error2.issues.length > 0) {
20177
- const firstIssue = error2.issues[0];
20178
- if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) {
20179
- return String(firstIssue.message);
20180
- }
20181
- }
20182
20980
  try {
20183
20981
  return JSON.stringify(error2);
20184
20982
  } catch {
@@ -20228,12 +21026,12 @@ function getLiteralValue(schema) {
20228
21026
  return;
20229
21027
  }
20230
21028
 
20231
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
21029
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
20232
21030
  function isTerminal(status) {
20233
21031
  return status === "completed" || status === "failed" || status === "cancelled";
20234
21032
  }
20235
21033
 
20236
- // node_modules/zod-to-json-schema/dist/esm/Options.js
21034
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
20237
21035
  var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use");
20238
21036
  var defaultOptions = {
20239
21037
  name: undefined,
@@ -20266,7 +21064,7 @@ var getDefaultOptions = (options) => typeof options === "string" ? {
20266
21064
  ...defaultOptions,
20267
21065
  ...options
20268
21066
  };
20269
- // node_modules/zod-to-json-schema/dist/esm/Refs.js
21067
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Refs.js
20270
21068
  var getRefs = (options) => {
20271
21069
  const _options = getDefaultOptions(options);
20272
21070
  const currentPath = _options.name !== undefined ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath;
@@ -20285,7 +21083,7 @@ var getRefs = (options) => {
20285
21083
  ]))
20286
21084
  };
20287
21085
  };
20288
- // node_modules/zod-to-json-schema/dist/esm/errorMessages.js
21086
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/errorMessages.js
20289
21087
  function addErrorMessage(res, key, errorMessage, refs) {
20290
21088
  if (!refs?.errorMessages)
20291
21089
  return;
@@ -20300,7 +21098,7 @@ function setResponseValueAndErrors(res, key, value, errorMessage, refs) {
20300
21098
  res[key] = value;
20301
21099
  addErrorMessage(res, key, errorMessage, refs);
20302
21100
  }
20303
- // node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
21101
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js
20304
21102
  var getRelativePath = (pathA, pathB) => {
20305
21103
  let i = 0;
20306
21104
  for (;i < pathA.length && i < pathB.length; i++) {
@@ -20309,7 +21107,7 @@ var getRelativePath = (pathA, pathB) => {
20309
21107
  }
20310
21108
  return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
20311
21109
  };
20312
- // node_modules/zod-to-json-schema/dist/esm/parsers/any.js
21110
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/any.js
20313
21111
  function parseAnyDef(refs) {
20314
21112
  if (refs.target !== "openAi") {
20315
21113
  return {};
@@ -20325,7 +21123,7 @@ function parseAnyDef(refs) {
20325
21123
  };
20326
21124
  }
20327
21125
 
20328
- // node_modules/zod-to-json-schema/dist/esm/parsers/array.js
21126
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/array.js
20329
21127
  function parseArrayDef(def, refs) {
20330
21128
  const res = {
20331
21129
  type: "array"
@@ -20349,7 +21147,7 @@ function parseArrayDef(def, refs) {
20349
21147
  return res;
20350
21148
  }
20351
21149
 
20352
- // node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js
21150
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js
20353
21151
  function parseBigintDef(def, refs) {
20354
21152
  const res = {
20355
21153
  type: "integer",
@@ -20395,24 +21193,24 @@ function parseBigintDef(def, refs) {
20395
21193
  return res;
20396
21194
  }
20397
21195
 
20398
- // node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js
21196
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js
20399
21197
  function parseBooleanDef() {
20400
21198
  return {
20401
21199
  type: "boolean"
20402
21200
  };
20403
21201
  }
20404
21202
 
20405
- // node_modules/zod-to-json-schema/dist/esm/parsers/branded.js
21203
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js
20406
21204
  function parseBrandedDef(_def, refs) {
20407
21205
  return parseDef(_def.type._def, refs);
20408
21206
  }
20409
21207
 
20410
- // node_modules/zod-to-json-schema/dist/esm/parsers/catch.js
21208
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js
20411
21209
  var parseCatchDef = (def, refs) => {
20412
21210
  return parseDef(def.innerType._def, refs);
20413
21211
  };
20414
21212
 
20415
- // node_modules/zod-to-json-schema/dist/esm/parsers/date.js
21213
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/date.js
20416
21214
  function parseDateDef(def, refs, overrideDateStrategy) {
20417
21215
  const strategy = overrideDateStrategy ?? refs.dateStrategy;
20418
21216
  if (Array.isArray(strategy)) {
@@ -20457,7 +21255,7 @@ var integerDateParser = (def, refs) => {
20457
21255
  return res;
20458
21256
  };
20459
21257
 
20460
- // node_modules/zod-to-json-schema/dist/esm/parsers/default.js
21258
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/default.js
20461
21259
  function parseDefaultDef(_def, refs) {
20462
21260
  return {
20463
21261
  ...parseDef(_def.innerType._def, refs),
@@ -20465,12 +21263,12 @@ function parseDefaultDef(_def, refs) {
20465
21263
  };
20466
21264
  }
20467
21265
 
20468
- // node_modules/zod-to-json-schema/dist/esm/parsers/effects.js
21266
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js
20469
21267
  function parseEffectsDef(_def, refs) {
20470
21268
  return refs.effectStrategy === "input" ? parseDef(_def.schema._def, refs) : parseAnyDef(refs);
20471
21269
  }
20472
21270
 
20473
- // node_modules/zod-to-json-schema/dist/esm/parsers/enum.js
21271
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js
20474
21272
  function parseEnumDef(def) {
20475
21273
  return {
20476
21274
  type: "string",
@@ -20478,7 +21276,7 @@ function parseEnumDef(def) {
20478
21276
  };
20479
21277
  }
20480
21278
 
20481
- // node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js
21279
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js
20482
21280
  var isJsonSchema7AllOfType = (type) => {
20483
21281
  if ("type" in type && type.type === "string")
20484
21282
  return false;
@@ -20520,7 +21318,7 @@ function parseIntersectionDef(def, refs) {
20520
21318
  } : undefined;
20521
21319
  }
20522
21320
 
20523
- // node_modules/zod-to-json-schema/dist/esm/parsers/literal.js
21321
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js
20524
21322
  function parseLiteralDef(def, refs) {
20525
21323
  const parsedType2 = typeof def.value;
20526
21324
  if (parsedType2 !== "bigint" && parsedType2 !== "number" && parsedType2 !== "boolean" && parsedType2 !== "string") {
@@ -20540,7 +21338,7 @@ function parseLiteralDef(def, refs) {
20540
21338
  };
20541
21339
  }
20542
21340
 
20543
- // node_modules/zod-to-json-schema/dist/esm/parsers/string.js
21341
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
20544
21342
  var emojiRegex2 = undefined;
20545
21343
  var zodPatterns = {
20546
21344
  cuid: /^[cC][^\s-]{8,}$/,
@@ -20837,7 +21635,7 @@ function stringifyRegExpWithFlags(regex, refs) {
20837
21635
  return pattern;
20838
21636
  }
20839
21637
 
20840
- // node_modules/zod-to-json-schema/dist/esm/parsers/record.js
21638
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/record.js
20841
21639
  function parseRecordDef(def, refs) {
20842
21640
  if (refs.target === "openAi") {
20843
21641
  console.warn("Warning: OpenAI may not support records in schemas! Try an array of key-value pairs instead.");
@@ -20889,7 +21687,7 @@ function parseRecordDef(def, refs) {
20889
21687
  return schema;
20890
21688
  }
20891
21689
 
20892
- // node_modules/zod-to-json-schema/dist/esm/parsers/map.js
21690
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/map.js
20893
21691
  function parseMapDef(def, refs) {
20894
21692
  if (refs.mapStrategy === "record") {
20895
21693
  return parseRecordDef(def, refs);
@@ -20914,7 +21712,7 @@ function parseMapDef(def, refs) {
20914
21712
  };
20915
21713
  }
20916
21714
 
20917
- // node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js
21715
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js
20918
21716
  function parseNativeEnumDef(def) {
20919
21717
  const object3 = def.values;
20920
21718
  const actualKeys = Object.keys(def.values).filter((key) => {
@@ -20928,7 +21726,7 @@ function parseNativeEnumDef(def) {
20928
21726
  };
20929
21727
  }
20930
21728
 
20931
- // node_modules/zod-to-json-schema/dist/esm/parsers/never.js
21729
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/never.js
20932
21730
  function parseNeverDef(refs) {
20933
21731
  return refs.target === "openAi" ? undefined : {
20934
21732
  not: parseAnyDef({
@@ -20938,7 +21736,7 @@ function parseNeverDef(refs) {
20938
21736
  };
20939
21737
  }
20940
21738
 
20941
- // node_modules/zod-to-json-schema/dist/esm/parsers/null.js
21739
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/null.js
20942
21740
  function parseNullDef(refs) {
20943
21741
  return refs.target === "openApi3" ? {
20944
21742
  enum: ["null"],
@@ -20948,7 +21746,7 @@ function parseNullDef(refs) {
20948
21746
  };
20949
21747
  }
20950
21748
 
20951
- // node_modules/zod-to-json-schema/dist/esm/parsers/union.js
21749
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/union.js
20952
21750
  var primitiveMappings = {
20953
21751
  ZodString: "string",
20954
21752
  ZodNumber: "number",
@@ -21016,7 +21814,7 @@ var asAnyOf = (def, refs) => {
21016
21814
  return anyOf.length ? { anyOf } : undefined;
21017
21815
  };
21018
21816
 
21019
- // node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js
21817
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js
21020
21818
  function parseNullableDef(def, refs) {
21021
21819
  if (["ZodString", "ZodNumber", "ZodBigInt", "ZodBoolean", "ZodNull"].includes(def.innerType._def.typeName) && (!def.innerType._def.checks || !def.innerType._def.checks.length)) {
21022
21820
  if (refs.target === "openApi3") {
@@ -21048,7 +21846,7 @@ function parseNullableDef(def, refs) {
21048
21846
  return base && { anyOf: [base, { type: "null" }] };
21049
21847
  }
21050
21848
 
21051
- // node_modules/zod-to-json-schema/dist/esm/parsers/number.js
21849
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/number.js
21052
21850
  function parseNumberDef(def, refs) {
21053
21851
  const res = {
21054
21852
  type: "number"
@@ -21097,14 +21895,14 @@ function parseNumberDef(def, refs) {
21097
21895
  return res;
21098
21896
  }
21099
21897
 
21100
- // node_modules/zod-to-json-schema/dist/esm/parsers/object.js
21898
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/object.js
21101
21899
  function parseObjectDef(def, refs) {
21102
21900
  const forceOptionalIntoNullable = refs.target === "openAi";
21103
21901
  const result = {
21104
21902
  type: "object",
21105
21903
  properties: {}
21106
21904
  };
21107
- const required2 = [];
21905
+ const required3 = [];
21108
21906
  const shape = def.shape();
21109
21907
  for (const propName in shape) {
21110
21908
  let propDef = shape[propName];
@@ -21131,11 +21929,11 @@ function parseObjectDef(def, refs) {
21131
21929
  }
21132
21930
  result.properties[propName] = parsedDef;
21133
21931
  if (!propOptional) {
21134
- required2.push(propName);
21932
+ required3.push(propName);
21135
21933
  }
21136
21934
  }
21137
- if (required2.length) {
21138
- result.required = required2;
21935
+ if (required3.length) {
21936
+ result.required = required3;
21139
21937
  }
21140
21938
  const additionalProperties = decideAdditionalProperties(def, refs);
21141
21939
  if (additionalProperties !== undefined) {
@@ -21167,7 +21965,7 @@ function safeIsOptional(schema) {
21167
21965
  }
21168
21966
  }
21169
21967
 
21170
- // node_modules/zod-to-json-schema/dist/esm/parsers/optional.js
21968
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js
21171
21969
  var parseOptionalDef = (def, refs) => {
21172
21970
  if (refs.currentPath.toString() === refs.propertyPath?.toString()) {
21173
21971
  return parseDef(def.innerType._def, refs);
@@ -21186,7 +21984,7 @@ var parseOptionalDef = (def, refs) => {
21186
21984
  } : parseAnyDef(refs);
21187
21985
  };
21188
21986
 
21189
- // node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js
21987
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js
21190
21988
  var parsePipelineDef = (def, refs) => {
21191
21989
  if (refs.pipeStrategy === "input") {
21192
21990
  return parseDef(def.in._def, refs);
@@ -21206,12 +22004,12 @@ var parsePipelineDef = (def, refs) => {
21206
22004
  };
21207
22005
  };
21208
22006
 
21209
- // node_modules/zod-to-json-schema/dist/esm/parsers/promise.js
22007
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js
21210
22008
  function parsePromiseDef(def, refs) {
21211
22009
  return parseDef(def.type._def, refs);
21212
22010
  }
21213
22011
 
21214
- // node_modules/zod-to-json-schema/dist/esm/parsers/set.js
22012
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/set.js
21215
22013
  function parseSetDef(def, refs) {
21216
22014
  const items = parseDef(def.valueType._def, {
21217
22015
  ...refs,
@@ -21231,7 +22029,7 @@ function parseSetDef(def, refs) {
21231
22029
  return schema;
21232
22030
  }
21233
22031
 
21234
- // node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js
22032
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js
21235
22033
  function parseTupleDef(def, refs) {
21236
22034
  if (def.rest) {
21237
22035
  return {
@@ -21259,24 +22057,24 @@ function parseTupleDef(def, refs) {
21259
22057
  }
21260
22058
  }
21261
22059
 
21262
- // node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js
22060
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js
21263
22061
  function parseUndefinedDef(refs) {
21264
22062
  return {
21265
22063
  not: parseAnyDef(refs)
21266
22064
  };
21267
22065
  }
21268
22066
 
21269
- // node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js
22067
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js
21270
22068
  function parseUnknownDef(refs) {
21271
22069
  return parseAnyDef(refs);
21272
22070
  }
21273
22071
 
21274
- // node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js
22072
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js
21275
22073
  var parseReadonlyDef = (def, refs) => {
21276
22074
  return parseDef(def.innerType._def, refs);
21277
22075
  };
21278
22076
 
21279
- // node_modules/zod-to-json-schema/dist/esm/selectParser.js
22077
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/selectParser.js
21280
22078
  var selectParser = (def, typeName, refs) => {
21281
22079
  switch (typeName) {
21282
22080
  case ZodFirstPartyTypeKind.ZodString:
@@ -21354,7 +22152,7 @@ var selectParser = (def, typeName, refs) => {
21354
22152
  }
21355
22153
  };
21356
22154
 
21357
- // node_modules/zod-to-json-schema/dist/esm/parseDef.js
22155
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parseDef.js
21358
22156
  function parseDef(def, refs, forceResolution = false) {
21359
22157
  const seenItem = refs.seen.get(def);
21360
22158
  if (refs.override) {
@@ -21409,7 +22207,7 @@ var addMeta = (def, refs, jsonSchema) => {
21409
22207
  }
21410
22208
  return jsonSchema;
21411
22209
  };
21412
- // node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js
22210
+ // node_modules/.pnpm/zod-to-json-schema@3.25.2_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js
21413
22211
  var zodToJsonSchema = (schema, options) => {
21414
22212
  const refs = getRefs(options);
21415
22213
  let definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name2, schema2]) => ({
@@ -21469,7 +22267,7 @@ var zodToJsonSchema = (schema, options) => {
21469
22267
  }
21470
22268
  return combined;
21471
22269
  };
21472
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
22270
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
21473
22271
  function mapMiniTarget(t) {
21474
22272
  if (!t)
21475
22273
  return "draft-7";
@@ -21511,7 +22309,7 @@ function parseWithCompat(schema, data) {
21511
22309
  return result.data;
21512
22310
  }
21513
22311
 
21514
- // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
22312
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
21515
22313
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 60000;
21516
22314
 
21517
22315
  class Protocol {
@@ -21715,6 +22513,10 @@ class Protocol {
21715
22513
  this._progressHandlers.clear();
21716
22514
  this._taskProgressTokens.clear();
21717
22515
  this._pendingDebouncedNotifications.clear();
22516
+ for (const info of this._timeoutInfo.values()) {
22517
+ clearTimeout(info.timeoutId);
22518
+ }
22519
+ this._timeoutInfo.clear();
21718
22520
  for (const controller of this._requestHandlerAbortControllers.values()) {
21719
22521
  controller.abort();
21720
22522
  }
@@ -21845,7 +22647,9 @@ class Protocol {
21845
22647
  await capturedTransport?.send(errorResponse);
21846
22648
  }
21847
22649
  }).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => {
21848
- this._requestHandlerAbortControllers.delete(request.id);
22650
+ if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
22651
+ this._requestHandlerAbortControllers.delete(request.id);
22652
+ }
21849
22653
  });
21850
22654
  }
21851
22655
  _onprogress(notification) {
@@ -22346,7 +23150,7 @@ function mergeCapabilities(base, additional) {
22346
23150
  return result;
22347
23151
  }
22348
23152
 
22349
- // node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
23153
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
22350
23154
  var import_ajv = __toESM(require_ajv(), 1);
22351
23155
  var import_ajv_formats = __toESM(require_dist(), 1);
22352
23156
  function createDefaultAjvInstance() {
@@ -22386,7 +23190,7 @@ class AjvJsonSchemaValidator {
22386
23190
  }
22387
23191
  }
22388
23192
 
22389
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
23193
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
22390
23194
  class ExperimentalServerTasks {
22391
23195
  constructor(_server) {
22392
23196
  this._server = _server;
@@ -22464,7 +23268,7 @@ class ExperimentalServerTasks {
22464
23268
  }
22465
23269
  }
22466
23270
 
22467
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
23271
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
22468
23272
  function assertToolsCallTaskCapability(requests, method, entityName) {
22469
23273
  if (!requests) {
22470
23274
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -22499,7 +23303,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
22499
23303
  }
22500
23304
  }
22501
23305
 
22502
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
23306
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
22503
23307
  class Server extends Protocol {
22504
23308
  constructor(_serverInfo, options) {
22505
23309
  super(options);
@@ -22547,16 +23351,7 @@ class Server extends Protocol {
22547
23351
  if (!methodSchema) {
22548
23352
  throw new Error("Schema is missing a method literal");
22549
23353
  }
22550
- let methodValue;
22551
- if (isZ4Schema(methodSchema)) {
22552
- const v4Schema = methodSchema;
22553
- const v4Def = v4Schema._zod?.def;
22554
- methodValue = v4Def?.value ?? v4Schema.value;
22555
- } else {
22556
- const v3Schema = methodSchema;
22557
- const legacyDef = v3Schema._def;
22558
- methodValue = legacyDef?.value ?? v3Schema.value;
22559
- }
23354
+ const methodValue = getLiteralValue(methodSchema);
22560
23355
  if (typeof methodValue !== "string") {
22561
23356
  throw new Error("Schema method literal must be a string");
22562
23357
  }
@@ -22832,7 +23627,7 @@ class Server extends Protocol {
22832
23627
  }
22833
23628
  }
22834
23629
 
22835
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
23630
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
22836
23631
  var COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
22837
23632
  function isCompletable(schema) {
22838
23633
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -22846,7 +23641,7 @@ var McpZodTypeKind;
22846
23641
  McpZodTypeKind2["Completable"] = "McpCompletable";
22847
23642
  })(McpZodTypeKind || (McpZodTypeKind = {}));
22848
23643
 
22849
- // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
23644
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
22850
23645
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
22851
23646
  function validateToolName(name) {
22852
23647
  const warnings = [];
@@ -22904,7 +23699,7 @@ function validateAndWarnToolName(name) {
22904
23699
  return result.isValid;
22905
23700
  }
22906
23701
 
22907
- // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
23702
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
22908
23703
  class ExperimentalMcpServerTasks {
22909
23704
  constructor(_mcpServer) {
22910
23705
  this._mcpServer = _mcpServer;
@@ -22918,7 +23713,7 @@ class ExperimentalMcpServerTasks {
22918
23713
  return mcpServerInternal._createRegisteredTool(name, config2.title, config2.description, config2.inputSchema, config2.outputSchema, config2.annotations, execution, config2._meta, handler);
22919
23714
  }
22920
23715
  }
22921
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
23716
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
22922
23717
  class McpServer {
22923
23718
  constructor(serverInfo, options) {
22924
23719
  this._registeredResources = {};
@@ -23502,6 +24297,9 @@ class McpServer {
23502
24297
  annotations = rest.shift();
23503
24298
  }
23504
24299
  } else if (typeof firstArg === "object" && firstArg !== null) {
24300
+ if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
24301
+ throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
24302
+ }
23505
24303
  annotations = rest.shift();
23506
24304
  }
23507
24305
  }
@@ -23594,6 +24392,9 @@ function getZodSchemaObject(schema) {
23594
24392
  if (isZodRawShapeCompat(schema)) {
23595
24393
  return objectFromShape(schema);
23596
24394
  }
24395
+ if (!isZodSchemaInstance(schema)) {
24396
+ throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
24397
+ }
23597
24398
  return schema;
23598
24399
  }
23599
24400
  function promptArgumentsFromSchema(schema) {
@@ -23638,12 +24439,22 @@ var EMPTY_COMPLETION_RESULT = {
23638
24439
  }
23639
24440
  };
23640
24441
 
23641
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
24442
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23642
24443
  import process2 from "process";
23643
24444
 
23644
- // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
24445
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
24446
+ var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
24447
+
23645
24448
  class ReadBuffer {
24449
+ constructor(options) {
24450
+ this._maxBufferSize = options?.maxBufferSize ?? STDIO_DEFAULT_MAX_BUFFER_SIZE;
24451
+ }
23646
24452
  append(chunk) {
24453
+ const newSize = (this._buffer?.length ?? 0) + chunk.length;
24454
+ if (newSize > this._maxBufferSize) {
24455
+ this.clear();
24456
+ throw new Error(`ReadBuffer exceeded maximum size of ${this._maxBufferSize} bytes`);
24457
+ }
23647
24458
  this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
23648
24459
  }
23649
24460
  readMessage() {
@@ -23671,20 +24482,25 @@ function serializeMessage(message) {
23671
24482
  `;
23672
24483
  }
23673
24484
 
23674
- // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
24485
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.30.0_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23675
24486
  class StdioServerTransport {
23676
- constructor(_stdin = process2.stdin, _stdout = process2.stdout) {
24487
+ constructor(_stdin = process2.stdin, _stdout = process2.stdout, options) {
23677
24488
  this._stdin = _stdin;
23678
24489
  this._stdout = _stdout;
23679
- this._readBuffer = new ReadBuffer;
23680
24490
  this._started = false;
23681
24491
  this._ondata = (chunk) => {
23682
- this._readBuffer.append(chunk);
23683
- this.processReadBuffer();
24492
+ try {
24493
+ this._readBuffer.append(chunk);
24494
+ this.processReadBuffer();
24495
+ } catch (error2) {
24496
+ this.onerror?.(error2);
24497
+ this.close().catch(() => {});
24498
+ }
23684
24499
  };
23685
24500
  this._onerror = (error2) => {
23686
24501
  this.onerror?.(error2);
23687
24502
  };
24503
+ this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
23688
24504
  }
23689
24505
  async start() {
23690
24506
  if (this._started) {
@@ -23730,9 +24546,9 @@ class StdioServerTransport {
23730
24546
  }
23731
24547
 
23732
24548
  // src/lib/connector.ts
23733
- import { join as join4 } from "path";
24549
+ import { join as join5 } from "path";
23734
24550
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
23735
- import { homedir } from "os";
24551
+ import { homedir as homedir3 } from "os";
23736
24552
 
23737
24553
  class ConnectorNotInstalledError extends Error {
23738
24554
  connectorName;
@@ -23800,9 +24616,9 @@ async function runConnector(name, args, opts = {}) {
23800
24616
  }
23801
24617
  function getConnectorTokenPath(name, profile = "default") {
23802
24618
  const bases = [
23803
- join4(homedir(), ".connectors", `connect-${name}`, "profiles", profile, "tokens.json"),
23804
- join4(homedir(), ".connect", `connect-${name}`, "profiles", profile, "tokens.json"),
23805
- join4(homedir(), ".connect", `connect-${name}`, "tokens.json")
24619
+ join5(homedir3(), ".connectors", `connect-${name}`, "profiles", profile, "tokens.json"),
24620
+ join5(homedir3(), ".connect", `connect-${name}`, "profiles", profile, "tokens.json"),
24621
+ join5(homedir3(), ".connect", `connect-${name}`, "tokens.json")
23806
24622
  ];
23807
24623
  for (const p of bases) {
23808
24624
  if (existsSync5(p))
@@ -23894,6 +24710,7 @@ function getStorageStatus(db = getDatabase()) {
23894
24710
 
23895
24711
  // src/store/index.ts
23896
24712
  init_contacts();
24713
+ init_project_memberships2();
23897
24714
 
23898
24715
  // src/db/groups.ts
23899
24716
  init_database();
@@ -26496,6 +27313,15 @@ class LocalStore {
26496
27313
  async listContactIdsByProject(projectId) {
26497
27314
  return listContactIdsByProject(projectId, this.db);
26498
27315
  }
27316
+ async readContactProjectMembership(contactId, projectId) {
27317
+ return readContactProjectMembership(contactId, projectId, this.db);
27318
+ }
27319
+ async listContactProjectMemberships(projectId, maxItems) {
27320
+ return listContactProjectMemberships(projectId, maxItems, this.db);
27321
+ }
27322
+ async mutateContactProjectMembership(direction, input) {
27323
+ return mutateContactProjectMembership(direction, input, this.db);
27324
+ }
26499
27325
  async createCompany(input) {
26500
27326
  return createCompany(input, this.db);
26501
27327
  }
@@ -27118,20 +27944,33 @@ class ApiStore {
27118
27944
  async findContactByEmailAddress(address) {
27119
27945
  return this.getContactByEmail(address);
27120
27946
  }
27121
- async linkContactToProject() {
27122
- return unavailable("linkContactToProject");
27947
+ async linkContactToProject(contactId, projectId) {
27948
+ await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
27949
+ }
27950
+ async unlinkContactFromProject(contactId, projectId) {
27951
+ await this.del(`/contacts/${this.enc(contactId)}/projects/${this.enc(projectId)}`);
27123
27952
  }
27124
- async unlinkContactFromProject() {
27125
- return unavailable("unlinkContactFromProject");
27953
+ async getContactProjectIds(contactId) {
27954
+ return pick2(await this.g(`/contacts/${this.enc(contactId)}/projects`), "project_ids") ?? [];
27126
27955
  }
27127
- async getContactProjectIds() {
27128
- return unavailable("getContactProjectIds");
27956
+ async setContactProjects(contactId, projectIds) {
27957
+ await this.client.transport.put(`/contacts/${this.enc(contactId)}/projects`, { project_ids: projectIds });
27129
27958
  }
27130
- async setContactProjects() {
27131
- return unavailable("setContactProjects");
27959
+ async listContactIdsByProject(projectId) {
27960
+ return pick2(await this.g(`/projects/${this.enc(projectId)}/contacts`), "contact_ids") ?? [];
27132
27961
  }
27133
- async listContactIdsByProject() {
27134
- return unavailable("listContactIdsByProject");
27962
+ async readContactProjectMembership(contactId, projectId) {
27963
+ return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships/${this.enc(contactId)}`);
27964
+ }
27965
+ async listContactProjectMemberships(projectId, maxItems) {
27966
+ return this.client.transport.get(`/projects/${this.enc(projectId)}/contact-memberships`, { query: { max_items: maxItems } });
27967
+ }
27968
+ async mutateContactProjectMembership(direction, input) {
27969
+ return this.client.transport.post(`/projects/${this.enc(input.project_id)}/contact-memberships/${this.enc(input.contact_id)}/${direction}`, {
27970
+ operation_id: input.operation_id,
27971
+ step_id: input.step_id,
27972
+ expected_version: input.expected_version
27973
+ });
27135
27974
  }
27136
27975
  async createCompany(input) {
27137
27976
  const res = await this.client.create("companies", stripUndefined(input));
@@ -29017,6 +29856,7 @@ Only include fields that are actually visible in the document. Return valid JSON
29017
29856
  }
29018
29857
 
29019
29858
  // src/mcp/handlers/advanced.ts
29859
+ import { join as join6 } from "path";
29020
29860
  var json3 = (v) => ({
29021
29861
  content: [{ type: "text", text: JSON.stringify(v, null, 2) }]
29022
29862
  });
@@ -29024,8 +29864,8 @@ var _contactsAgents = new Map;
29024
29864
  var advancedHandlers = {
29025
29865
  get_field_history: async (a) => json3({ history: await getStore().getFieldHistory(a.contact_id, a.field_name) }),
29026
29866
  get_contact_at: async (a) => {
29027
- const snapshot = await getStore().getContactAt(a.contact_id, a.timestamp);
29028
- return json3({ contact_id: a.contact_id, timestamp: a.timestamp, snapshot });
29867
+ const snapshot2 = await getStore().getContactAt(a.contact_id, a.timestamp);
29868
+ return json3({ contact_id: a.contact_id, timestamp: a.timestamp, snapshot: snapshot2 });
29029
29869
  },
29030
29870
  get_job_history: async (a) => json3({ history: await getStore().getJobHistory(a.contact_id) }),
29031
29871
  add_job_entry: async (a) => json3(await getStore().addJobEntry(a.contact_id, {
@@ -29178,8 +30018,9 @@ var advancedHandlers = {
29178
30018
  const { contact_id, image, format } = a;
29179
30019
  await store.getContact(contact_id);
29180
30020
  const filename = await store.saveImage(contact_id, image, { format });
29181
- await store.updateContact(contact_id, { avatar_url: `~/.hasna/contacts/images/${filename}` });
29182
- return json3({ ok: true, contact_id, filename, avatar_url: `~/.hasna/contacts/images/${filename}` });
30021
+ const avatarUrl = join6(getImagesDir(), filename);
30022
+ await store.updateContact(contact_id, { avatar_url: avatarUrl });
30023
+ return json3({ ok: true, contact_id, filename, avatar_url: avatarUrl });
29183
30024
  },
29184
30025
  get_contact_photo: async (a) => {
29185
30026
  const { contact_id } = a;
@@ -29201,8 +30042,9 @@ var advancedHandlers = {
29201
30042
  const { company_id, image, format } = a;
29202
30043
  await store.getCompany(company_id);
29203
30044
  const filename = await store.saveImage(company_id, image, { format });
29204
- await store.updateCompany(company_id, { logo_url: `~/.hasna/contacts/images/${filename}` });
29205
- return json3({ ok: true, company_id, filename, logo_url: `~/.hasna/contacts/images/${filename}` });
30045
+ const logoUrl = join6(getImagesDir(), filename);
30046
+ await store.updateCompany(company_id, { logo_url: logoUrl });
30047
+ return json3({ ok: true, company_id, filename, logo_url: logoUrl });
29206
30048
  },
29207
30049
  get_company_logo: async (a) => {
29208
30050
  const { company_id } = a;
@@ -29501,11 +30343,11 @@ function jsonSchemaToZodType(schema) {
29501
30343
  }
29502
30344
  function jsonSchemaToZodObject(schema) {
29503
30345
  const objectSchema = schema && typeof schema === "object" ? schema : {};
29504
- const required2 = new Set(objectSchema.required ?? []);
30346
+ const required3 = new Set(objectSchema.required ?? []);
29505
30347
  const shape = {};
29506
30348
  for (const [key, value] of Object.entries(objectSchema.properties ?? {})) {
29507
30349
  const propertySchema = jsonSchemaToZodType(value);
29508
- shape[key] = required2.has(key) ? propertySchema : propertySchema.optional();
30350
+ shape[key] = required3.has(key) ? propertySchema : propertySchema.optional();
29509
30351
  }
29510
30352
  return describeSchema(exports_external.object(shape).passthrough(), objectSchema.description);
29511
30353
  }
@@ -30973,10 +31815,10 @@ var TOOL_DEFINITIONS = [
30973
31815
  { name: "get_deal_team", description: "Get the full buying committee for a deal with contact names and roles.", inputSchema: { type: "object", properties: { deal_id: { type: "string" } }, required: ["deal_id"] } },
30974
31816
  { name: "get_coverage_gaps", description: "Identify coverage gaps in a company account \u2014 missing economic buyer, technical evaluator, or org chart relationships.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
30975
31817
  { name: "get_recent_contact_events", description: "Polling fallback for change events \u2014 returns recent activity log entries, optionally filtered by event type or date.", inputSchema: { type: "object", properties: { since: { type: "string", description: "ISO 8601 datetime \u2014 only events after this date" }, event_types: { type: "array", items: { type: "string" } } } } },
30976
- { name: "set_contact_photo", description: "Set a contact's profile photo. Provide either a local file path or base64-encoded image data (with or without data URI prefix). Stores image in ~/.hasna/contacts/images/ and updates avatar_url. Supported formats: jpg, png, gif, webp, svg, avif.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, image: { type: "string", description: "File path (e.g. /tmp/photo.jpg) OR base64 data (e.g. data:image/png;base64,...) OR raw base64 string" }, format: { type: "string", description: "Image format hint when using raw base64 (jpg, png, webp). Not needed for file paths or data URIs." } }, required: ["contact_id", "image"] } },
31818
+ { name: "set_contact_photo", description: "Set a contact's profile photo. Provide either a local file path or base64-encoded image data (with or without data URI prefix). Stores image in the XDG data root images dir and updates avatar_url. Supported formats: jpg, png, gif, webp, svg, avif.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, image: { type: "string", description: "File path (e.g. /tmp/photo.jpg) OR base64 data (e.g. data:image/png;base64,...) OR raw base64 string" }, format: { type: "string", description: "Image format hint when using raw base64 (jpg, png, webp). Not needed for file paths or data URIs." } }, required: ["contact_id", "image"] } },
30977
31819
  { name: "get_contact_photo", description: "Get a contact's profile photo as base64 data URI. Returns null if no photo is set.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
30978
31820
  { name: "delete_contact_photo", description: "Remove a contact's profile photo.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
30979
- { name: "set_company_logo", description: "Set a company's logo image. Provide either a local file path or base64-encoded image data. Stores image in ~/.hasna/contacts/images/ and updates logo_url.", inputSchema: { type: "object", properties: { company_id: { type: "string" }, image: { type: "string", description: "File path or base64 data" }, format: { type: "string", description: "Image format hint for raw base64" } }, required: ["company_id", "image"] } },
31821
+ { name: "set_company_logo", description: "Set a company's logo image. Provide either a local file path or base64-encoded image data. Stores image in the XDG data root images dir and updates logo_url.", inputSchema: { type: "object", properties: { company_id: { type: "string" }, image: { type: "string", description: "File path or base64 data" }, format: { type: "string", description: "Image format hint for raw base64" } }, required: ["company_id", "image"] } },
30980
31822
  { name: "get_company_logo", description: "Get a company's logo as base64 data URI.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
30981
31823
  { name: "delete_company_logo", description: "Remove a company's logo image.", inputSchema: { type: "object", properties: { company_id: { type: "string" } }, required: ["company_id"] } },
30982
31824
  { name: "set_sensitivity", description: "Set a contact's sensitivity level (normal, confidential, restricted). Restricted contacts are hidden from list/search unless explicitly requested.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, sensitivity: { type: "string", enum: ["normal", "confidential", "restricted"] } }, required: ["contact_id", "sensitivity"] } },
@@ -30984,7 +31826,7 @@ var TOOL_DEFINITIONS = [
30984
31826
  { name: "vault_unlock", description: "Unlock the vault for this session with a passphrase.", inputSchema: { type: "object", properties: { passphrase: { type: "string" } }, required: ["passphrase"] } },
30985
31827
  { name: "vault_lock", description: "Lock the vault, clearing the encryption key from memory.", inputSchema: { type: "object", properties: {} } },
30986
31828
  { name: "vault_status", description: "Check vault initialization and lock status.", inputSchema: { type: "object", properties: {} } },
30987
- { name: "add_document", description: "Store a document for a contact (passport, tax_id, medical_record, etc.). Text values are encrypted; file attachments are stored plain so agents can read them. Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, doc_type: { type: "string", enum: [...DOCUMENT_TYPES] }, label: { type: "string" }, value: { type: "string", description: "Plaintext value (will be encrypted in DB)" }, file_path: { type: "string", description: "File to attach \u2014 stored PLAIN in ~/.hasna/contacts/documents/ for agent access" }, metadata: { type: "object" }, expires_at: { type: "string" } }, required: ["contact_id", "doc_type", "value"] } },
31829
+ { name: "add_document", description: "Store a document for a contact (passport, tax_id, medical_record, etc.). Text values are encrypted; file attachments are stored plain so agents can read them. Vault must be unlocked.", inputSchema: { type: "object", properties: { contact_id: { type: "string" }, doc_type: { type: "string", enum: [...DOCUMENT_TYPES] }, label: { type: "string" }, value: { type: "string", description: "Plaintext value (will be encrypted in DB)" }, file_path: { type: "string", description: "File to attach \u2014 stored PLAIN in the XDG data root documents dir for agent access" }, metadata: { type: "object" }, expires_at: { type: "string" } }, required: ["contact_id", "doc_type", "value"] } },
30988
31830
  { name: "list_documents", description: "List documents for a contact (metadata only \u2014 no decryption needed). Returns file_path for attachments so agents can read them directly.", inputSchema: { type: "object", properties: { contact_id: { type: "string" } }, required: ["contact_id"] } },
30989
31831
  { name: "get_document", description: "Get a document with decrypted value and file_path. Vault must be unlocked for the text value; file is always accessible.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
30990
31832
  { name: "get_document_file", description: "Get the plain file path for a document attachment. Agents can read this file directly \u2014 it is NOT encrypted. Returns null if no file attached.", inputSchema: { type: "object", properties: { document_id: { type: "string" } }, required: ["document_id"] } },
@@ -31094,7 +31936,7 @@ function registerContactsStorageTools(server) {
31094
31936
  // src/mcp/index.ts
31095
31937
  function getServerVersion() {
31096
31938
  try {
31097
- const packageJsonPath = join5(import.meta.dir, "..", "..", "package.json");
31939
+ const packageJsonPath = join7(import.meta.dir, "..", "..", "package.json");
31098
31940
  const pkg2 = JSON.parse(readFileSync5(packageJsonPath, "utf8"));
31099
31941
  return pkg2.version ?? "0.0.0";
31100
31942
  } catch {
@@ -31185,11 +32027,11 @@ function isLoopbackBindHost(hostname2) {
31185
32027
  const normalized = hostname2.trim().toLowerCase();
31186
32028
  return ["localhost", "127.0.0.1", "::1", "[::1]"].includes(normalized);
31187
32029
  }
31188
- function hasScope(principal, required2) {
31189
- const prefix = required2.split(":")[0];
31190
- return principal.scopes.has("*") || principal.scopes.has(required2) || principal.scopes.has(`${prefix}:*`);
32030
+ function hasScope(principal, required3) {
32031
+ const prefix = required3.split(":")[0];
32032
+ return principal.scopes.has("*") || principal.scopes.has(required3) || principal.scopes.has(`${prefix}:*`);
31191
32033
  }
31192
- function authenticateContactsRequest(req, required2, context = { allowUnauthenticatedLoopback: false }) {
32034
+ function authenticateContactsRequest(req, required3, context = { allowUnauthenticatedLoopback: false }) {
31193
32035
  const configured = parseTokenRecords();
31194
32036
  const token = bearerToken(req) ?? req.headers.get("x-contacts-token");
31195
32037
  if (token) {
@@ -31197,8 +32039,8 @@ function authenticateContactsRequest(req, required2, context = { allowUnauthenti
31197
32039
  if (!matched)
31198
32040
  return { ok: false, status: 401, message: "Invalid contacts token" };
31199
32041
  const principal = { id: "api-token", scopes: matched.scopes, localDevelopment: false };
31200
- if (!hasScope(principal, required2))
31201
- return { ok: false, status: 403, message: `Missing scope: ${required2}` };
32042
+ if (!hasScope(principal, required3))
32043
+ return { ok: false, status: 403, message: `Missing scope: ${required3}` };
31202
32044
  return { ok: true, principal };
31203
32045
  }
31204
32046
  if (configured.length === 0 && context.allowUnauthenticatedLoopback) {
@@ -31249,9 +32091,12 @@ function redactContactForExport(contact) {
31249
32091
  }
31250
32092
 
31251
32093
  // src/server/v1.ts
32094
+ init_project_memberships();
31252
32095
  init_cloud();
31253
32096
 
31254
32097
  // src/server/pg-store.ts
32098
+ init_project_memberships();
32099
+ import { createHash as createHash3 } from "crypto";
31255
32100
  function uuid3() {
31256
32101
  return crypto.randomUUID();
31257
32102
  }
@@ -31286,6 +32131,45 @@ function newUuid() {
31286
32131
  function nowIso() {
31287
32132
  return new Date().toISOString();
31288
32133
  }
32134
+ function contactProjectDigest(value) {
32135
+ return createHash3("sha256").update(value).digest("hex");
32136
+ }
32137
+ function contactProjectMembershipVersion(row) {
32138
+ return `cpmv_${contactProjectDigest(JSON.stringify([
32139
+ row.contact_id,
32140
+ row.project_id,
32141
+ Boolean(row.linked),
32142
+ Number(row.revision)
32143
+ ])).slice(0, 32)}`;
32144
+ }
32145
+ function contactProjectMembershipSnapshot(row) {
32146
+ return {
32147
+ contact_id: row.contact_id,
32148
+ project_id: row.project_id,
32149
+ linked: Boolean(row.linked),
32150
+ version: contactProjectMembershipVersion(row)
32151
+ };
32152
+ }
32153
+ function parseMembershipSnapshot(value) {
32154
+ if (typeof value === "string")
32155
+ return JSON.parse(value);
32156
+ return value;
32157
+ }
32158
+ function requiredMembershipValue(value, name) {
32159
+ const normalized = value.trim();
32160
+ if (!normalized)
32161
+ throw new Error(`${name} is required`);
32162
+ return normalized;
32163
+ }
32164
+ function normalizeMembershipMutationInput(input) {
32165
+ return {
32166
+ contact_id: requiredMembershipValue(input.contact_id, "contact_id"),
32167
+ project_id: requiredMembershipValue(input.project_id, "project_id"),
32168
+ operation_id: requiredMembershipValue(input.operation_id, "operation_id"),
32169
+ step_id: requiredMembershipValue(input.step_id, "step_id"),
32170
+ expected_version: requiredMembershipValue(input.expected_version, "expected_version")
32171
+ };
32172
+ }
31289
32173
  function parseJson(value) {
31290
32174
  if (!value)
31291
32175
  return {};
@@ -31353,6 +32237,29 @@ function mapTag(row) {
31353
32237
  created_at: iso(row.created_at)
31354
32238
  };
31355
32239
  }
32240
+ function mapEmail(row) {
32241
+ return {
32242
+ id: String(row["id"]),
32243
+ contact_id: row["contact_id"] ?? null,
32244
+ company_id: row["company_id"] ?? null,
32245
+ address: String(row["address"]),
32246
+ type: row["type"],
32247
+ is_primary: Boolean(row["is_primary"]),
32248
+ created_at: iso(row["created_at"])
32249
+ };
32250
+ }
32251
+ function mapPhone(row) {
32252
+ return {
32253
+ id: String(row["id"]),
32254
+ contact_id: row["contact_id"] ?? null,
32255
+ company_id: row["company_id"] ?? null,
32256
+ number: String(row["number"]),
32257
+ country_code: row["country_code"] ?? null,
32258
+ type: row["type"],
32259
+ is_primary: Boolean(row["is_primary"]),
32260
+ created_at: iso(row["created_at"])
32261
+ };
32262
+ }
31356
32263
 
31357
32264
  class ContactsPgStore {
31358
32265
  client;
@@ -31372,6 +32279,45 @@ class ContactsPgStore {
31372
32279
  tagsByContactId.get(row.contact_id)?.push(mapTag(row));
31373
32280
  return contacts.map((contact) => ({ ...contact, tags: tagsByContactId.get(contact.id) ?? [] }));
31374
32281
  }
32282
+ async attachContactMethods(contacts) {
32283
+ if (contacts.length === 0)
32284
+ return [];
32285
+ const ids = contacts.map((contact) => contact.id);
32286
+ const emailsByContactId = new Map(ids.map((id) => [id, []]));
32287
+ const phonesByContactId = new Map(ids.map((id) => [id, []]));
32288
+ const [emailRows, phoneRows] = await Promise.all([
32289
+ this.client.many(`SELECT * FROM emails WHERE contact_id = ANY($1::text[]) ORDER BY created_at ASC`, [ids]),
32290
+ this.client.many(`SELECT * FROM phones WHERE contact_id = ANY($1::text[]) ORDER BY created_at ASC`, [ids])
32291
+ ]);
32292
+ for (const row of emailRows)
32293
+ emailsByContactId.get(String(row["contact_id"]))?.push(mapEmail(row));
32294
+ for (const row of phoneRows)
32295
+ phonesByContactId.get(String(row["contact_id"]))?.push(mapPhone(row));
32296
+ return contacts.map((contact) => ({
32297
+ ...contact,
32298
+ emails: emailsByContactId.get(contact.id) ?? [],
32299
+ phones: phonesByContactId.get(contact.id) ?? []
32300
+ }));
32301
+ }
32302
+ async attachContactDetails(contacts) {
32303
+ return this.attachContactMethods(await this.attachTags(contacts));
32304
+ }
32305
+ async insertContactMethods(contactId, emails, phones) {
32306
+ for (const email2 of emails ?? []) {
32307
+ await this.client.query(`INSERT INTO emails (id, contact_id, company_id, address, type, is_primary)
32308
+ SELECT $1, $2, NULL, $3, $4, $5
32309
+ WHERE NOT EXISTS (
32310
+ SELECT 1 FROM emails WHERE contact_id = $2 AND LOWER(address) = LOWER($3)
32311
+ )`, [uuid3(), contactId, email2.address, email2.type ?? "work", email2.is_primary ?? false]);
32312
+ }
32313
+ for (const phone of phones ?? []) {
32314
+ await this.client.query(`INSERT INTO phones (id, contact_id, company_id, number, country_code, type, is_primary)
32315
+ SELECT $1, $2, NULL, $3, $4, $5, $6
32316
+ WHERE NOT EXISTS (
32317
+ SELECT 1 FROM phones WHERE contact_id = $2 AND number = $3
32318
+ )`, [uuid3(), contactId, phone.number, phone.country_code ?? null, phone.type ?? "mobile", phone.is_primary ?? false]);
32319
+ }
32320
+ }
31375
32321
  async listContacts(filter = {}) {
31376
32322
  const limit = Math.min(Math.max(filter.limit ?? 50, 1), 500);
31377
32323
  const offset = Math.max(filter.offset ?? 0, 0);
@@ -31397,13 +32343,13 @@ class ContactsPgStore {
31397
32343
  const countRow = await this.client.get(`SELECT COUNT(*)::text AS count FROM contacts ${whereSql}`, params);
31398
32344
  params.push(limit, offset);
31399
32345
  const rows = await this.client.many(`SELECT * FROM contacts ${whereSql} ORDER BY display_name ASC LIMIT $${params.length - 1} OFFSET $${params.length}`, params);
31400
- return { contacts: await this.attachTags(rows.map(mapContact)), count: Number(countRow?.count ?? rows.length) };
32346
+ return { contacts: await this.attachContactDetails(rows.map(mapContact)), count: Number(countRow?.count ?? rows.length) };
31401
32347
  }
31402
32348
  async getContact(id) {
31403
32349
  const row = await this.client.get(`SELECT * FROM contacts WHERE id = $1`, [id]);
31404
32350
  if (!row)
31405
32351
  return null;
31406
- return (await this.attachTags([mapContact(row)]))[0];
32352
+ return (await this.attachContactDetails([mapContact(row)]))[0];
31407
32353
  }
31408
32354
  async createContact(input) {
31409
32355
  const id = uuid3();
@@ -31439,7 +32385,8 @@ class ContactsPgStore {
31439
32385
  input.priority ?? 3,
31440
32386
  input.timezone ?? null
31441
32387
  ]);
31442
- return (await this.attachTags([mapContact(row)]))[0];
32388
+ await this.insertContactMethods(id, input.emails, input.phones);
32389
+ return (await this.attachContactDetails([mapContact(row)]))[0];
31443
32390
  }
31444
32391
  async updateContact(id, input) {
31445
32392
  const allowed = {};
@@ -31474,17 +32421,192 @@ class ContactsPgStore {
31474
32421
  allowed.custom_fields = JSON.stringify(input.custom_fields);
31475
32422
  }
31476
32423
  const keys = Object.keys(allowed);
31477
- if (keys.length === 0)
32424
+ const hasMethodAppends = Boolean(input.emails_add?.length || input.phones_add?.length);
32425
+ if (keys.length === 0 && !hasMethodAppends)
31478
32426
  return this.getContact(id);
31479
32427
  const sets = keys.map((k, i) => `${k} = $${i + 2}`);
31480
32428
  sets.push(`updated_at = NOW()`);
31481
32429
  const row = await this.client.get(`UPDATE contacts SET ${sets.join(", ")} WHERE id = $1 RETURNING *`, [id, ...keys.map((k) => allowed[k])]);
31482
- return row ? (await this.attachTags([mapContact(row)]))[0] : null;
32430
+ if (!row)
32431
+ return null;
32432
+ await this.insertContactMethods(id, input.emails_add, input.phones_add);
32433
+ return (await this.attachContactDetails([mapContact(row)]))[0];
31483
32434
  }
31484
32435
  async deleteContact(id) {
31485
32436
  const result = await this.client.query(`DELETE FROM contacts WHERE id = $1`, [id]);
31486
32437
  return result.rowCount > 0;
31487
32438
  }
32439
+ async linkContactToProject(contactId, projectId) {
32440
+ await this.client.transaction(async (client) => {
32441
+ await this.transitionContactProjectMembershipWithoutReceipt(client, contactId, projectId, true);
32442
+ });
32443
+ }
32444
+ async unlinkContactFromProject(contactId, projectId) {
32445
+ return this.client.transaction((client) => this.transitionContactProjectMembershipWithoutReceipt(client, contactId, projectId, false));
32446
+ }
32447
+ async getContactProjectIds(contactId) {
32448
+ const rows = await this.client.many(`SELECT project_id
32449
+ FROM contact_projects
32450
+ WHERE contact_id = $1
32451
+ ORDER BY project_id ASC`, [contactId]);
32452
+ return rows.map((row) => row.project_id);
32453
+ }
32454
+ async setContactProjects(contactId, projectIds) {
32455
+ const uniqueProjectIds = [...new Set(projectIds.map((projectId) => requiredMembershipValue(projectId, "project_id")))];
32456
+ await this.client.transaction(async (client) => {
32457
+ const current = await client.many(`SELECT project_id FROM contact_projects WHERE contact_id = $1`, [contactId]);
32458
+ const desired = new Set(uniqueProjectIds);
32459
+ const population = new Set([...current.map((row) => row.project_id), ...uniqueProjectIds]);
32460
+ for (const projectId of population) {
32461
+ await this.transitionContactProjectMembershipWithoutReceipt(client, contactId, projectId, desired.has(projectId));
32462
+ }
32463
+ });
32464
+ return uniqueProjectIds;
32465
+ }
32466
+ async listContactIdsByProject(projectId) {
32467
+ const rows = await this.client.many(`SELECT contact_id
32468
+ FROM contact_projects
32469
+ WHERE project_id = $1
32470
+ ORDER BY contact_id ASC`, [projectId]);
32471
+ return rows.map((row) => row.contact_id);
32472
+ }
32473
+ async contactProjectMembershipState(client, contactId, projectId, forUpdate = false) {
32474
+ let row = await client.get(`SELECT contact_id, project_id, linked, revision
32475
+ FROM contact_project_membership_states
32476
+ WHERE contact_id = $1 AND project_id = $2${forUpdate ? " FOR UPDATE" : ""}`, [contactId, projectId]);
32477
+ if (row)
32478
+ return row;
32479
+ const linked = Boolean(await client.get(`SELECT 1 AS present FROM contact_projects WHERE contact_id = $1 AND project_id = $2`, [contactId, projectId]));
32480
+ if (!forUpdate) {
32481
+ return { contact_id: contactId, project_id: projectId, linked, revision: 0 };
32482
+ }
32483
+ await client.execute(`INSERT INTO contact_project_membership_states
32484
+ (contact_id, project_id, linked, revision, updated_at)
32485
+ VALUES ($1, $2, $3, 0, NOW())
32486
+ ON CONFLICT (contact_id, project_id) DO NOTHING`, [contactId, projectId, linked]);
32487
+ row = await client.get(`SELECT contact_id, project_id, linked, revision
32488
+ FROM contact_project_membership_states
32489
+ WHERE contact_id = $1 AND project_id = $2
32490
+ FOR UPDATE`, [contactId, projectId]);
32491
+ if (!row)
32492
+ throw new Error("failed to initialize contact project membership state");
32493
+ return row;
32494
+ }
32495
+ async transitionContactProjectMembershipWithoutReceipt(client, contactId, projectId, linked) {
32496
+ const normalizedContactId = requiredMembershipValue(contactId, "contact_id");
32497
+ const normalizedProjectId = requiredMembershipValue(projectId, "project_id");
32498
+ const before = await this.contactProjectMembershipState(client, normalizedContactId, normalizedProjectId, true);
32499
+ const changed = Boolean(before.linked) !== linked;
32500
+ const revision = Number(before.revision) + (changed ? 1 : 0);
32501
+ await client.execute(`UPDATE contact_project_membership_states
32502
+ SET linked = $3, revision = $4, updated_at = NOW()
32503
+ WHERE contact_id = $1 AND project_id = $2`, [normalizedContactId, normalizedProjectId, linked, revision]);
32504
+ if (linked) {
32505
+ await client.execute(`INSERT INTO contact_projects (contact_id, project_id) VALUES ($1, $2)
32506
+ ON CONFLICT (contact_id, project_id) DO NOTHING`, [normalizedContactId, normalizedProjectId]);
32507
+ } else {
32508
+ await client.execute(`DELETE FROM contact_projects WHERE contact_id = $1 AND project_id = $2`, [normalizedContactId, normalizedProjectId]);
32509
+ }
32510
+ return changed;
32511
+ }
32512
+ async readContactProjectMembership(contactId, projectId) {
32513
+ return contactProjectMembershipSnapshot(await this.contactProjectMembershipState(this.client, contactId, projectId));
32514
+ }
32515
+ async listContactProjectMemberships(projectId, maxItems) {
32516
+ if (!Number.isInteger(maxItems) || maxItems < 1)
32517
+ throw new Error("max_items must be a positive integer");
32518
+ const rows = await this.client.many(`SELECT cp.contact_id, cp.project_id,
32519
+ COALESCE(state.linked, TRUE) AS linked,
32520
+ COALESCE(state.revision, 0) AS revision
32521
+ FROM contact_projects cp
32522
+ LEFT JOIN contact_project_membership_states state
32523
+ ON state.contact_id = cp.contact_id AND state.project_id = cp.project_id
32524
+ WHERE cp.project_id = $1
32525
+ ORDER BY cp.contact_id ASC
32526
+ LIMIT $2`, [projectId, maxItems + 1]);
32527
+ if (rows.length > maxItems) {
32528
+ throw new Error(`contact project membership collection exceeds max_items=${maxItems}`);
32529
+ }
32530
+ return {
32531
+ project_id: projectId,
32532
+ contact_ids: rows.map((row) => row.contact_id),
32533
+ complete: true,
32534
+ membership_revision: `cpml_${contactProjectDigest(JSON.stringify(rows.map(contactProjectMembershipSnapshot))).slice(0, 32)}`
32535
+ };
32536
+ }
32537
+ async mutateContactProjectMembership(direction, rawInput) {
32538
+ const input = normalizeMembershipMutationInput(rawInput);
32539
+ return this.client.transaction(async (client) => {
32540
+ await client.execute(`SELECT pg_advisory_xact_lock(hashtext($1), hashtext($2))`, [input.operation_id, input.step_id]);
32541
+ const receipt = await client.get(`SELECT direction, contact_id, project_id, operation_id, step_id, expected_version,
32542
+ before_json, after_json, receipt_id
32543
+ FROM contact_project_membership_receipts
32544
+ WHERE operation_id = $1 AND step_id = $2`, [input.operation_id, input.step_id]);
32545
+ if (receipt) {
32546
+ if (receipt.direction !== direction || receipt.contact_id !== input.contact_id || receipt.project_id !== input.project_id || receipt.expected_version !== input.expected_version) {
32547
+ throw new ContactProjectMembershipConflictError("operation_id/step_id already accepted for a different contact-project membership mutation");
32548
+ }
32549
+ return {
32550
+ outcome: "duplicate_of_accepted",
32551
+ operation_id: receipt.operation_id,
32552
+ step_id: receipt.step_id,
32553
+ before: parseMembershipSnapshot(receipt.before_json),
32554
+ after: parseMembershipSnapshot(receipt.after_json),
32555
+ receipt_id: receipt.receipt_id
32556
+ };
32557
+ }
32558
+ const contact = await client.get(`SELECT id FROM contacts WHERE id = $1`, [input.contact_id]);
32559
+ if (!contact)
32560
+ throw new Error(`contact not found: ${input.contact_id}`);
32561
+ const beforeRow = await this.contactProjectMembershipState(client, input.contact_id, input.project_id, true);
32562
+ const before = contactProjectMembershipSnapshot(beforeRow);
32563
+ if (before.version !== input.expected_version) {
32564
+ throw new ContactProjectMembershipConflictError(`contact project membership expected_version conflict: expected ${input.expected_version}, current ${before.version}`);
32565
+ }
32566
+ const desiredLinked = direction === "attach";
32567
+ const changed = before.linked !== desiredLinked;
32568
+ const revision = Number(beforeRow.revision) + (changed ? 1 : 0);
32569
+ const afterRow = { ...beforeRow, linked: desiredLinked, revision };
32570
+ await client.execute(`UPDATE contact_project_membership_states
32571
+ SET linked = $3, revision = $4, updated_at = NOW()
32572
+ WHERE contact_id = $1 AND project_id = $2`, [input.contact_id, input.project_id, desiredLinked, revision]);
32573
+ if (desiredLinked) {
32574
+ await client.execute(`INSERT INTO contact_projects (contact_id, project_id) VALUES ($1, $2)
32575
+ ON CONFLICT (contact_id, project_id) DO NOTHING`, [input.contact_id, input.project_id]);
32576
+ } else {
32577
+ await client.execute(`DELETE FROM contact_projects WHERE contact_id = $1 AND project_id = $2`, [input.contact_id, input.project_id]);
32578
+ }
32579
+ const after = contactProjectMembershipSnapshot(afterRow);
32580
+ const id = `cpmr_${contactProjectDigest(JSON.stringify([
32581
+ input.operation_id,
32582
+ input.step_id,
32583
+ input.contact_id,
32584
+ input.project_id
32585
+ ])).slice(0, 32)}`;
32586
+ await client.execute(`INSERT INTO contact_project_membership_receipts (
32587
+ receipt_id, direction, contact_id, project_id, operation_id, step_id,
32588
+ expected_version, before_json, after_json
32589
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9::jsonb)`, [
32590
+ id,
32591
+ direction,
32592
+ input.contact_id,
32593
+ input.project_id,
32594
+ input.operation_id,
32595
+ input.step_id,
32596
+ input.expected_version,
32597
+ JSON.stringify(before),
32598
+ JSON.stringify(after)
32599
+ ]);
32600
+ return {
32601
+ outcome: changed ? "accepted" : "duplicate_of_accepted",
32602
+ operation_id: input.operation_id,
32603
+ step_id: input.step_id,
32604
+ before,
32605
+ after,
32606
+ receipt_id: id
32607
+ };
32608
+ });
32609
+ }
31488
32610
  async listCompanies(filter = {}) {
31489
32611
  const limit = Math.min(Math.max(filter.limit ?? 50, 1), 500);
31490
32612
  const offset = Math.max(filter.offset ?? 0, 0);
@@ -32951,6 +34073,84 @@ function contactListFilterFromUrl(url) {
32951
34073
  ...url.searchParams.get("offset") ? { offset: Number(url.searchParams.get("offset")) } : {}
32952
34074
  };
32953
34075
  }
34076
+ async function handleContactProjectsRoute(req, method, segments, store) {
34077
+ const resource = segments[1];
34078
+ const id = segments[2];
34079
+ const sub = segments[3];
34080
+ if (resource === "contacts" && id && sub === "projects") {
34081
+ const contact = await store.getContact(id);
34082
+ if (!contact)
34083
+ return error2(404, "contact not found");
34084
+ const projectId = segments[4];
34085
+ if (projectId) {
34086
+ if (method === "PUT") {
34087
+ await store.linkContactToProject(id, projectId);
34088
+ return json5({ attached: true, contact_id: id, project_id: projectId });
34089
+ }
34090
+ if (method === "DELETE") {
34091
+ const removed = await store.unlinkContactFromProject(id, projectId);
34092
+ return json5({ removed, contact_id: id, project_id: projectId });
34093
+ }
34094
+ return error2(405, `method ${method} not allowed on /v1/contacts/:contact_id/projects/:project_id`);
34095
+ }
34096
+ if (method === "GET") {
34097
+ return json5({ contact_id: id, project_ids: await store.getContactProjectIds(id) });
34098
+ }
34099
+ if (method === "PUT") {
34100
+ const body = await readJson(req);
34101
+ if (!body || !Array.isArray(body.project_ids) || !body.project_ids.every((value) => typeof value === "string" && value.trim().length > 0)) {
34102
+ return error2(400, "project_ids must be an array of non-empty strings");
34103
+ }
34104
+ const projectIds = await store.setContactProjects(id, body.project_ids);
34105
+ return json5({ contact_id: id, project_ids: projectIds });
34106
+ }
34107
+ return error2(405, `method ${method} not allowed on /v1/contacts/:contact_id/projects`);
34108
+ }
34109
+ if (resource === "projects" && id && sub === "contacts") {
34110
+ if (method === "GET") {
34111
+ return json5({ project_id: id, contact_ids: await store.listContactIdsByProject(id) });
34112
+ }
34113
+ return error2(405, `method ${method} not allowed on /v1/projects/:project_id/contacts`);
34114
+ }
34115
+ if (resource === "projects" && id && sub === "contact-memberships") {
34116
+ const contactId = segments[4];
34117
+ const action = segments[5];
34118
+ if (!contactId) {
34119
+ if (method !== "GET") {
34120
+ return error2(405, `method ${method} not allowed on /v1/projects/:project_id/contact-memberships`);
34121
+ }
34122
+ const rawMaxItems = new URL(req.url).searchParams.get("max_items");
34123
+ const maxItems = rawMaxItems === null ? 1000 : Number(rawMaxItems);
34124
+ if (!Number.isInteger(maxItems) || maxItems < 1)
34125
+ return error2(400, "max_items must be a positive integer");
34126
+ return json5(await store.listContactProjectMemberships(id, maxItems));
34127
+ }
34128
+ if (!action) {
34129
+ if (method !== "GET") {
34130
+ return error2(405, `method ${method} not allowed on /v1/projects/:project_id/contact-memberships/:contact_id`);
34131
+ }
34132
+ const contact = await store.getContact(contactId);
34133
+ if (!contact)
34134
+ return error2(404, "contact not found");
34135
+ return json5(await store.readContactProjectMembership(contactId, id));
34136
+ }
34137
+ if ((action === "attach" || action === "detach") && method === "POST") {
34138
+ const body = await readJson(req);
34139
+ if (!body || typeof body.operation_id !== "string" || typeof body.step_id !== "string" || typeof body.expected_version !== "string" || body.operation_id.trim().length === 0 || body.step_id.trim().length === 0 || body.expected_version.trim().length === 0) {
34140
+ return error2(400, "operation_id, step_id, and expected_version are required non-empty strings");
34141
+ }
34142
+ return json5(await store.mutateContactProjectMembership(action, {
34143
+ contact_id: contactId,
34144
+ project_id: id,
34145
+ operation_id: body.operation_id,
34146
+ step_id: body.step_id,
34147
+ expected_version: body.expected_version
34148
+ }));
34149
+ }
34150
+ return error2(405, `method ${method} not allowed on contact-project membership route`);
34151
+ }
34152
+ return null;
34153
+ }
32954
34154
  async function handleV1Request(req, url) {
32955
34155
  const path = url.pathname;
32956
34156
  if (path !== "/v1" && !path.startsWith("/v1/"))
@@ -32985,6 +34185,9 @@ async function handleV1Request(req, url) {
32985
34185
  return v === null ? undefined : Number(v);
32986
34186
  };
32987
34187
  try {
34188
+ const projectLinks = await handleContactProjectsRoute(req, method, segments, store);
34189
+ if (projectLinks)
34190
+ return projectLinks;
32988
34191
  if (resource === "contacts" && id && sub) {
32989
34192
  if (sub === "tags") {
32990
34193
  const tagId = segments[4];
@@ -33686,6 +34889,8 @@ async function handleV1Request(req, url) {
33686
34889
  return error2(404, `unknown /v1 resource: ${resource ?? "(root)"}`);
33687
34890
  } catch (e) {
33688
34891
  const msg = e.message || "internal error";
34892
+ if (e instanceof ContactProjectMembershipConflictError)
34893
+ return error2(409, msg);
33689
34894
  if (/violates|constraint|invalid input|duplicate key/i.test(msg))
33690
34895
  return error2(400, msg);
33691
34896
  return error2(500, msg);
@@ -33694,7 +34899,7 @@ async function handleV1Request(req, url) {
33694
34899
 
33695
34900
  // src/lib/package-version.ts
33696
34901
  import { existsSync as existsSync8, readFileSync as readFileSync7 } from "fs";
33697
- import { dirname as dirname2, join as join6 } from "path";
34902
+ import { dirname as dirname2, join as join8 } from "path";
33698
34903
  import { fileURLToPath } from "url";
33699
34904
  var cached3 = null;
33700
34905
  function getPackageVersion() {
@@ -33703,7 +34908,7 @@ function getPackageVersion() {
33703
34908
  try {
33704
34909
  let dir = dirname2(fileURLToPath(import.meta.url));
33705
34910
  for (let i = 0;i < 8; i++) {
33706
- const pkgPath = join6(dir, "package.json");
34911
+ const pkgPath = join8(dir, "package.json");
33707
34912
  if (existsSync8(pkgPath)) {
33708
34913
  const pkg2 = JSON.parse(readFileSync7(pkgPath, "utf8"));
33709
34914
  if (pkg2.name === "@hasna/contacts" && pkg2.version) {
@@ -33859,6 +35064,58 @@ function buildV1OpenApiDocument(version2 = getPackageVersion()) {
33859
35064
  color: { type: "string" },
33860
35065
  description: { type: "string" }
33861
35066
  }
35067
+ },
35068
+ ProjectIdsInput: {
35069
+ type: "object",
35070
+ required: ["project_ids"],
35071
+ properties: {
35072
+ project_ids: {
35073
+ type: "array",
35074
+ items: { type: "string", minLength: 1 },
35075
+ uniqueItems: true
35076
+ }
35077
+ }
35078
+ },
35079
+ ContactProjectMembershipSnapshot: {
35080
+ type: "object",
35081
+ required: ["contact_id", "project_id", "linked", "version"],
35082
+ properties: {
35083
+ contact_id: { type: "string" },
35084
+ project_id: { type: "string" },
35085
+ linked: { type: "boolean" },
35086
+ version: { type: "string" }
35087
+ }
35088
+ },
35089
+ ContactProjectMembershipMutationInput: {
35090
+ type: "object",
35091
+ required: ["operation_id", "step_id", "expected_version"],
35092
+ properties: {
35093
+ operation_id: { type: "string", minLength: 1 },
35094
+ step_id: { type: "string", minLength: 1 },
35095
+ expected_version: { type: "string", minLength: 1 }
35096
+ }
35097
+ },
35098
+ ContactProjectMembershipMutationResult: {
35099
+ type: "object",
35100
+ required: ["outcome", "operation_id", "step_id", "before", "after", "receipt_id"],
35101
+ properties: {
35102
+ outcome: { type: "string", enum: ["accepted", "duplicate_of_accepted"] },
35103
+ operation_id: { type: "string" },
35104
+ step_id: { type: "string" },
35105
+ before: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" },
35106
+ after: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" },
35107
+ receipt_id: { type: "string" }
35108
+ }
35109
+ },
35110
+ ContactProjectMembershipListResult: {
35111
+ type: "object",
35112
+ required: ["project_id", "contact_ids", "complete", "membership_revision"],
35113
+ properties: {
35114
+ project_id: { type: "string" },
35115
+ contact_ids: { type: "array", items: { type: "string" } },
35116
+ complete: { type: "boolean", const: true },
35117
+ membership_revision: { type: "string" }
35118
+ }
33862
35119
  }
33863
35120
  }
33864
35121
  },
@@ -34059,6 +35316,167 @@ function buildV1OpenApiDocument(version2 = getPackageVersion()) {
34059
35316
  })
34060
35317
  }
34061
35318
  },
35319
+ "/v1/contacts/{contact_id}/projects": {
35320
+ get: {
35321
+ operationId: "getContactProjectIds",
35322
+ summary: "List project ids attached to a contact",
35323
+ parameters: [
35324
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } }
35325
+ ],
35326
+ responses: objResponse({
35327
+ contact_id: { type: "string" },
35328
+ project_ids: { type: "array", items: { type: "string" } }
35329
+ })
35330
+ },
35331
+ put: {
35332
+ operationId: "setContactProjects",
35333
+ summary: "Atomically replace a contact's project memberships",
35334
+ parameters: [
35335
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } }
35336
+ ],
35337
+ requestBody: {
35338
+ required: true,
35339
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ProjectIdsInput" } } }
35340
+ },
35341
+ responses: objResponse({
35342
+ contact_id: { type: "string" },
35343
+ project_ids: { type: "array", items: { type: "string" } }
35344
+ })
35345
+ }
35346
+ },
35347
+ "/v1/contacts/{contact_id}/projects/{project_id}": {
35348
+ put: {
35349
+ operationId: "linkContactToProject",
35350
+ summary: "Attach a contact to a project idempotently",
35351
+ parameters: [
35352
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } },
35353
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } }
35354
+ ],
35355
+ responses: objResponse({
35356
+ attached: { type: "boolean" },
35357
+ contact_id: { type: "string" },
35358
+ project_id: { type: "string" }
35359
+ })
35360
+ },
35361
+ delete: {
35362
+ operationId: "unlinkContactFromProject",
35363
+ summary: "Detach a contact from a project",
35364
+ parameters: [
35365
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } },
35366
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } }
35367
+ ],
35368
+ responses: objResponse({
35369
+ removed: { type: "boolean" },
35370
+ contact_id: { type: "string" },
35371
+ project_id: { type: "string" }
35372
+ })
35373
+ }
35374
+ },
35375
+ "/v1/projects/{project_id}/contacts": {
35376
+ get: {
35377
+ operationId: "listContactIdsByProject",
35378
+ summary: "List contact ids attached to a project",
35379
+ parameters: [
35380
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } }
35381
+ ],
35382
+ responses: objResponse({
35383
+ project_id: { type: "string" },
35384
+ contact_ids: { type: "array", items: { type: "string" } }
35385
+ })
35386
+ }
35387
+ },
35388
+ "/v1/projects/{project_id}/contact-memberships": {
35389
+ get: {
35390
+ operationId: "listContactProjectMemberships",
35391
+ summary: "List the complete authoritative contact membership collection for a project",
35392
+ parameters: [
35393
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } },
35394
+ { name: "max_items", in: "query", required: true, schema: { type: "integer", minimum: 1 } }
35395
+ ],
35396
+ responses: {
35397
+ "200": {
35398
+ content: {
35399
+ "application/json": {
35400
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipListResult" }
35401
+ }
35402
+ }
35403
+ }
35404
+ }
35405
+ }
35406
+ },
35407
+ "/v1/projects/{project_id}/contact-memberships/{contact_id}": {
35408
+ get: {
35409
+ operationId: "readContactProjectMembership",
35410
+ summary: "Read one authoritative contact-project membership snapshot",
35411
+ parameters: [
35412
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } },
35413
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } }
35414
+ ],
35415
+ responses: {
35416
+ "200": {
35417
+ content: {
35418
+ "application/json": {
35419
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipSnapshot" }
35420
+ }
35421
+ }
35422
+ }
35423
+ }
35424
+ }
35425
+ },
35426
+ "/v1/projects/{project_id}/contact-memberships/{contact_id}/attach": {
35427
+ post: {
35428
+ operationId: "attachContactProjectMembership",
35429
+ summary: "Attach a contact to a project under expected-version CAS with a replay-safe receipt",
35430
+ parameters: [
35431
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } },
35432
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } }
35433
+ ],
35434
+ requestBody: {
35435
+ required: true,
35436
+ content: {
35437
+ "application/json": {
35438
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationInput" }
35439
+ }
35440
+ }
35441
+ },
35442
+ responses: {
35443
+ "200": {
35444
+ content: {
35445
+ "application/json": {
35446
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationResult" }
35447
+ }
35448
+ }
35449
+ }
35450
+ }
35451
+ }
35452
+ },
35453
+ "/v1/projects/{project_id}/contact-memberships/{contact_id}/detach": {
35454
+ post: {
35455
+ operationId: "detachContactProjectMembership",
35456
+ summary: "Detach a contact from a project under expected-version CAS with a replay-safe receipt",
35457
+ parameters: [
35458
+ { name: "project_id", in: "path", required: true, schema: { type: "string" } },
35459
+ { name: "contact_id", in: "path", required: true, schema: { type: "string" } }
35460
+ ],
35461
+ requestBody: {
35462
+ required: true,
35463
+ content: {
35464
+ "application/json": {
35465
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationInput" }
35466
+ }
35467
+ }
35468
+ },
35469
+ responses: {
35470
+ "200": {
35471
+ content: {
35472
+ "application/json": {
35473
+ schema: { $ref: "#/components/schemas/ContactProjectMembershipMutationResult" }
35474
+ }
35475
+ }
35476
+ }
35477
+ }
35478
+ }
35479
+ },
34062
35480
  "/v1/stats": {
34063
35481
  get: {
34064
35482
  operationId: "getStats",
@@ -34076,7 +35494,7 @@ function buildV1OpenApiDocument(version2 = getPackageVersion()) {
34076
35494
 
34077
35495
  // src/server/serve.ts
34078
35496
  init_cloud();
34079
- var DASHBOARD_DIST = join7(import.meta.dir, "../../dashboard/dist");
35497
+ var DASHBOARD_DIST = join9(import.meta.dir, "../../dashboard/dist");
34080
35498
  var DEFAULT_REST_HOST = "127.0.0.1";
34081
35499
  function json6(data, status = 200) {
34082
35500
  return new Response(JSON.stringify(data), {
@@ -34109,9 +35527,9 @@ function requireScope(req, scope, options) {
34109
35527
  function isResponse(value) {
34110
35528
  return value instanceof Response;
34111
35529
  }
34112
- function privateFileHeaders(contentType) {
35530
+ function privateFileHeaders(contentType2) {
34113
35531
  return {
34114
- ...contentType ? { "Content-Type": contentType } : {},
35532
+ ...contentType2 ? { "Content-Type": contentType2 } : {},
34115
35533
  "Cache-Control": "private, no-store",
34116
35534
  "X-Content-Type-Options": "nosniff"
34117
35535
  };
@@ -34119,8 +35537,8 @@ function privateFileHeaders(contentType) {
34119
35537
  function isSafeEntityId(id) {
34120
35538
  return /^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/.test(id) && !id.includes("..") && !id.includes("/");
34121
35539
  }
34122
- function isPathInside(baseDir, filePath) {
34123
- const base = resolve2(baseDir);
35540
+ function isPathInside(baseDir2, filePath) {
35541
+ const base = resolve2(baseDir2);
34124
35542
  const file = resolve2(filePath);
34125
35543
  const rel = relative(base, file);
34126
35544
  return rel === "" || !!rel && !rel.startsWith("..") && !rel.startsWith("/") && !rel.includes("..\\");
@@ -34392,15 +35810,15 @@ async function handleImages(req, _url2, segments, options) {
34392
35810
  const principal = requireScope(req, "images:write", options);
34393
35811
  if (isResponse(principal))
34394
35812
  return principal;
34395
- const contentType = req.headers.get("content-type") || "";
34396
- if (contentType.includes("multipart/form-data")) {
35813
+ const contentType2 = req.headers.get("content-type") || "";
35814
+ if (contentType2.includes("multipart/form-data")) {
34397
35815
  const formData = await req.formData();
34398
35816
  const file = formData.get("image");
34399
35817
  if (!file)
34400
35818
  return apiError("No image file in form data");
34401
35819
  const ext = file.name?.split(".").pop() || "jpg";
34402
35820
  const buffer = Buffer.from(await file.arrayBuffer());
34403
- const tmpPath = join7(getImagesDir(), `_upload_${entityId}.${ext}`);
35821
+ const tmpPath = join9(getImagesDir(), `_upload_${entityId}.${ext}`);
34404
35822
  const { writeFileSync: wfs } = await import("fs");
34405
35823
  wfs(tmpPath, buffer);
34406
35824
  try {
@@ -34521,8 +35939,8 @@ function createContactsRequestHandler(options = {}) {
34521
35939
  if (isResponse(principal)) {
34522
35940
  response = principal;
34523
35941
  } else {
34524
- const filePath = join7(DASHBOARD_DIST, url.pathname === "/" ? "index.html" : url.pathname);
34525
- response = serveStaticFile(filePath) ?? serveStaticFile(join7(DASHBOARD_DIST, "index.html")) ?? new Response("Not Found", { status: 404 });
35942
+ const filePath = join9(DASHBOARD_DIST, url.pathname === "/" ? "index.html" : url.pathname);
35943
+ response = serveStaticFile(filePath) ?? serveStaticFile(join9(DASHBOARD_DIST, "index.html")) ?? new Response("Not Found", { status: 404 });
34526
35944
  }
34527
35945
  }
34528
35946
  } catch (err2) {