@vennyx/solicrm-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,4140 @@
1
+ import { z } from "zod";
2
+ import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ //#region packages/contracts/src/fields.ts
6
+ /**
7
+ * Alan kataloğu — `field_key` → kolon/tip/yetenek eşlemesi.
8
+ *
9
+ * NEDEN VAR: kaydedilmiş görünümler, filtreler, sıralama, CSV içe/dışa aktarma
10
+ * ve MCP/SDK tip üretimi HEPSİ bu katalogdan okur. Hiçbir yerde kullanıcıdan
11
+ * gelen bir değer doğrudan kolon adına dönüşmez — `resolveField()` bilinmeyen
12
+ * anahtar için `undefined` döner ve çağıran 400 verir. Bu, SQL enjeksiyonu ve
13
+ * bilgi sızdırmanın sınırıdır.
14
+ *
15
+ * İLERİYE DÖNÜK: özel alanlar (custom fields) eklendiğinde katalog runtime'da
16
+ * tenant'ın tanımlarıyla genişletilir; tüketici kod değişmez (bkz. spec §6.7).
17
+ */
18
+ const CRM_RESOURCES = [
19
+ "contacts",
20
+ "companies",
21
+ "deals",
22
+ "tasks"
23
+ ];
24
+ const FIELD_CATALOG = {
25
+ contacts: {
26
+ first_name: {
27
+ column: "first_name",
28
+ type: "text",
29
+ filterable: true,
30
+ sortable: true
31
+ },
32
+ last_name: {
33
+ column: "last_name",
34
+ type: "text",
35
+ filterable: true,
36
+ sortable: true
37
+ },
38
+ email: {
39
+ column: "email",
40
+ type: "text",
41
+ filterable: true,
42
+ sortable: true
43
+ },
44
+ phone: {
45
+ column: "phone",
46
+ type: "text",
47
+ filterable: true,
48
+ sortable: false
49
+ },
50
+ job_title: {
51
+ column: "job_title",
52
+ type: "text",
53
+ filterable: true,
54
+ sortable: true
55
+ },
56
+ city: {
57
+ column: "city",
58
+ type: "text",
59
+ filterable: true,
60
+ sortable: true
61
+ },
62
+ country: {
63
+ column: "country",
64
+ type: "text",
65
+ filterable: true,
66
+ sortable: true
67
+ },
68
+ status: {
69
+ column: "status",
70
+ type: "enum",
71
+ filterable: true,
72
+ sortable: true,
73
+ values: [
74
+ "lead",
75
+ "active",
76
+ "inactive"
77
+ ]
78
+ },
79
+ company_id: {
80
+ column: "company_id",
81
+ type: "uuid",
82
+ filterable: true,
83
+ sortable: false
84
+ },
85
+ owner_user_id: {
86
+ column: "owner_user_id",
87
+ type: "uuid",
88
+ filterable: true,
89
+ sortable: false
90
+ },
91
+ created_at: {
92
+ column: "created_at",
93
+ type: "date",
94
+ filterable: true,
95
+ sortable: true
96
+ }
97
+ },
98
+ companies: {
99
+ name: {
100
+ column: "name",
101
+ type: "text",
102
+ filterable: true,
103
+ sortable: true
104
+ },
105
+ domain: {
106
+ column: "domain",
107
+ type: "text",
108
+ filterable: true,
109
+ sortable: true
110
+ },
111
+ industry: {
112
+ column: "industry",
113
+ type: "text",
114
+ filterable: true,
115
+ sortable: true
116
+ },
117
+ city: {
118
+ column: "city",
119
+ type: "text",
120
+ filterable: true,
121
+ sortable: true
122
+ },
123
+ country: {
124
+ column: "country",
125
+ type: "text",
126
+ filterable: true,
127
+ sortable: true
128
+ },
129
+ annual_revenue: {
130
+ column: "annual_revenue",
131
+ type: "money",
132
+ filterable: true,
133
+ sortable: true
134
+ },
135
+ owner_user_id: {
136
+ column: "owner_user_id",
137
+ type: "uuid",
138
+ filterable: true,
139
+ sortable: false
140
+ },
141
+ created_at: {
142
+ column: "created_at",
143
+ type: "date",
144
+ filterable: true,
145
+ sortable: true
146
+ }
147
+ },
148
+ deals: {
149
+ title: {
150
+ column: "title",
151
+ type: "text",
152
+ filterable: true,
153
+ sortable: true
154
+ },
155
+ amount: {
156
+ column: "amount",
157
+ type: "money",
158
+ filterable: true,
159
+ sortable: true
160
+ },
161
+ currency_code: {
162
+ column: "currency_code",
163
+ type: "text",
164
+ filterable: true,
165
+ sortable: false
166
+ },
167
+ stage_id: {
168
+ column: "stage_id",
169
+ type: "uuid",
170
+ filterable: true,
171
+ sortable: false
172
+ },
173
+ pipeline_id: {
174
+ column: "pipeline_id",
175
+ type: "uuid",
176
+ filterable: true,
177
+ sortable: false
178
+ },
179
+ company_id: {
180
+ column: "company_id",
181
+ type: "uuid",
182
+ filterable: true,
183
+ sortable: false
184
+ },
185
+ outcome: {
186
+ column: "outcome",
187
+ type: "enum",
188
+ filterable: true,
189
+ sortable: true,
190
+ values: [
191
+ "open",
192
+ "won",
193
+ "lost"
194
+ ]
195
+ },
196
+ expected_close_date: {
197
+ column: "expected_close_date",
198
+ type: "date",
199
+ filterable: true,
200
+ sortable: true
201
+ },
202
+ owner_user_id: {
203
+ column: "owner_user_id",
204
+ type: "uuid",
205
+ filterable: true,
206
+ sortable: false
207
+ },
208
+ created_at: {
209
+ column: "created_at",
210
+ type: "date",
211
+ filterable: true,
212
+ sortable: true
213
+ }
214
+ },
215
+ tasks: {
216
+ title: {
217
+ column: "title",
218
+ type: "text",
219
+ filterable: true,
220
+ sortable: true
221
+ },
222
+ due_at: {
223
+ column: "due_at",
224
+ type: "date",
225
+ filterable: true,
226
+ sortable: true
227
+ },
228
+ completed_at: {
229
+ column: "completed_at",
230
+ type: "date",
231
+ filterable: true,
232
+ sortable: true
233
+ },
234
+ priority: {
235
+ column: "priority",
236
+ type: "enum",
237
+ filterable: true,
238
+ sortable: true,
239
+ values: [
240
+ "low",
241
+ "normal",
242
+ "high",
243
+ "urgent"
244
+ ]
245
+ },
246
+ assignee_user_id: {
247
+ column: "assignee_user_id",
248
+ type: "uuid",
249
+ filterable: true,
250
+ sortable: false
251
+ },
252
+ created_at: {
253
+ column: "created_at",
254
+ type: "date",
255
+ filterable: true,
256
+ sortable: true
257
+ }
258
+ }
259
+ };
260
+ /**
261
+ * Bir `field_key`'i katalogdan çözer. Bilinmeyen anahtar → `undefined`
262
+ * (çağıran 400 döner). ASLA anahtarı kolon adı olarak varsaymaz.
263
+ */
264
+ function resolveField(resource, key) {
265
+ const catalog = FIELD_CATALOG[resource];
266
+ return Object.hasOwn(catalog, key) ? catalog[key] : void 0;
267
+ }
268
+ //#endregion
269
+ //#region packages/contracts/src/crm.ts
270
+ /**
271
+ * CRM domain genelinde paylaşılan zod katalogları (spec §6.2/§6.3).
272
+ *
273
+ * NOT — Task 3 (deals) genişletmesi: `targetTypeSchema` artık `'deal'`
274
+ * içeriyor (tek satırlık, geriye uyumlu genişleme; hiçbir tüketici tipi
275
+ * DARALTILMADI, yalnız genişledi — bkz. `apps/api/src/lib/polymorphic-target.ts`,
276
+ * `packages/db/drizzle/0028_deals_guard.sql`'daki `CREATE OR REPLACE FUNCTION`).
277
+ *
278
+ * NOT — spec'in tam liste vermediği katalog değerleri (bu dosyanın kararı):
279
+ * `contactSourceSchema` ve `companySizeSchema` spec §6.2'de yalnız "text+katalog"
280
+ * / kendine özgü alan olarak anılıyor, değer kümesi verilmiyor — makul bir
281
+ * başlangıç kümesi seçildi (bkz. plan "Yeni ortak altyapı kararları / D").
282
+ */
283
+ const targetTypeSchema = z.enum([
284
+ "contact",
285
+ "company",
286
+ "deal"
287
+ ], "geçerli bir hedef türü olmalı (contact, company, deal)");
288
+ /** Fırsat sonucu (spec §6.2) — DB'de `deals_outcome_check` CHECK kısıtıyla eşleşir */
289
+ const dealOutcomeSchema = z.enum([
290
+ "open",
291
+ "won",
292
+ "lost"
293
+ ], "geçerli bir sonuç olmalı (open, won, lost)");
294
+ const contactStatusSchema = z.enum([
295
+ "lead",
296
+ "active",
297
+ "inactive"
298
+ ], "geçerli bir durum olmalı (lead, active, inactive)");
299
+ /** Spec'te tam liste verilmemiş — makul bir başlangıç kümesi (bkz. dosya başı NOT) */
300
+ const contactSourceSchema = z.enum([
301
+ "website",
302
+ "referral",
303
+ "cold_outreach",
304
+ "event",
305
+ "social_media",
306
+ "other"
307
+ ], "geçerli bir kaynak olmalı");
308
+ /** Spec'te tam liste verilmemiş — makul bir başlangıç kümesi (bkz. dosya başı NOT) */
309
+ const companySizeSchema = z.enum([
310
+ "1-10",
311
+ "11-50",
312
+ "51-200",
313
+ "201-500",
314
+ "500+"
315
+ ], "geçerli bir şirket büyüklüğü olmalı");
316
+ const sortDirectionSchema = z.enum(["asc", "desc"], "yön asc veya desc olmalı");
317
+ const filterOperandSchema = z.enum([
318
+ "eq",
319
+ "neq",
320
+ "contains",
321
+ "starts_with",
322
+ "gt",
323
+ "gte",
324
+ "lt",
325
+ "lte",
326
+ "in",
327
+ "is_null",
328
+ "is_not_null"
329
+ ], "geçerli bir karşılaştırma operatörü olmalı");
330
+ /**
331
+ * Aktivite türü (spec §6.2) — `stage_change` Faz 5 Task 3'ün `/move` ucunun
332
+ * ürettiği otomatik aktiviteyle eşleşir (bkz. `apps/api/src/routes/deals.ts`).
333
+ */
334
+ const activityKindSchema = z.enum([
335
+ "call",
336
+ "meeting",
337
+ "email",
338
+ "note",
339
+ "task",
340
+ "stage_change"
341
+ ], "geçerli bir aktivite türü olmalı (call, meeting, email, note, task, stage_change)");
342
+ /** Spec'te tam liste verilmemiş — makul bir başlangıç kümesi (bkz. plan "Yeni ortak altyapı kararları / D") */
343
+ const taskStatusSchema = z.enum([
344
+ "todo",
345
+ "in_progress",
346
+ "done",
347
+ "cancelled"
348
+ ], "geçerli bir durum olmalı (todo, in_progress, done, cancelled)");
349
+ /** Spec'in verdiği tam liste (§6.2) */
350
+ const taskPrioritySchema = z.enum([
351
+ "low",
352
+ "normal",
353
+ "high",
354
+ "urgent"
355
+ ], "geçerli bir öncelik olmalı (low, normal, high, urgent)");
356
+ z.object({
357
+ targetType: targetTypeSchema,
358
+ targetId: z.uuid("geçerli bir hedef kimliği olmalı")
359
+ });
360
+ /**
361
+ * Kaydedilmiş görünüm katalogları (spec §6.5, Faz 5 Task 5).
362
+ * `savedViewResourceSchema` = `CRM_RESOURCES`'ın zod karşılığı — `fields.ts`
363
+ * DEĞİŞTİRİLMEDEN (`CRM_RESOURCES` sabiti oradan İÇE AKTARILIR, yeniden
364
+ * TANIMLANMAZ, tek gerçek kaynak korunur).
365
+ */
366
+ const savedViewResourceSchema = z.enum(CRM_RESOURCES, "geçerli bir CRM kaynağı olmalı (contacts, companies, deals, tasks)");
367
+ const savedViewLayoutSchema = z.enum(["table", "kanban"], "geçerli bir düzen olmalı (table, kanban)");
368
+ const savedViewVisibilitySchema = z.enum(["private", "tenant"], "geçerli bir görünürlük olmalı (private, tenant)");
369
+ const filterLogicalOperatorSchema = z.enum([
370
+ "and",
371
+ "or",
372
+ "not"
373
+ ], "geçerli bir mantıksal operatör olmalı (and, or, not)");
374
+ /**
375
+ * `numeric(18,2)` DB kolonuna eşlenen PARA alanları için ORTAK biçim
376
+ * doğrulaması (spec §6.1 — para asla native `number`/aritmetikle hesaplanmaz,
377
+ * yalnız string olarak taşınır, `bignumber.js` dışında ASLA işlenmez).
378
+ *
379
+ * [Fix round 2 — doğruluk reviewer'ının bulduğu, Task 3'ün `deals.amount`
380
+ * fix'iyle BİREBİR aynı sınıf hata]: `companies.annual_revenue` DB'de `text`
381
+ * kolondu ama `FIELD_CATALOG.companies.annual_revenue` `type:'money'`,
382
+ * `sortable:true`, `filterable:true` ilan ediyordu — `text` kolonda `ORDER
383
+ * BY`/`>`/`<` LEXICOGRAPHIC karşılaştırma yapar (`"100" < "20" < "9"`), bu
384
+ * SESSİZ bir doğruluk hatasıdır. Bu şema, Task 3'ün önceden yalnız
385
+ * `deals.ts` içinde TANIMLADIĞI `dealAmountSchema`'nın (aynı kısıtlar: en
386
+ * fazla 2 ondalık basamak, en fazla 18 anlamlı basamak — `numeric(18,2)`
387
+ * sınırı) buraya TAŞINMIŞ/PAYLAŞILAN hâlidir — İKİ ayrı DB kolonu (deals.amount,
388
+ * companies.annual_revenue) TEK doğrulama mantığından geçer (drift riski
389
+ * olmasın diye, DRY).
390
+ */
391
+ const numericMoneySchema = z.string().trim().max(40, "en fazla 40 karakter olabilir").regex(/^-?\d+(\.\d{1,2})?$/, "geçerli bir ondalık tutar olmalı, en fazla 2 ondalık basamak (örn. 1234.56)").refine((v) => v.replace(/[-.]/g, "").length <= 18, "en fazla 18 anlamlı basamak olabilir (numeric(18,2) sınırı)");
392
+ const pageQuerySchema = z.object({
393
+ cursor: z.string().max(2e3, "geçersiz sayfalama imleci").optional(),
394
+ limit: z.coerce.number("sayı olmalı").int("tam sayı olmalı").min(1, "en az 1 olmalı").max(100, `en fazla 100 olabilir`).optional()
395
+ });
396
+ function pagedResponseSchema(itemSchema) {
397
+ return z.object({
398
+ items: z.array(itemSchema),
399
+ nextCursor: z.string().nullable(),
400
+ hasMore: z.boolean()
401
+ });
402
+ }
403
+ //#endregion
404
+ //#region packages/contracts/src/notes.ts
405
+ /**
406
+ * Not (note) sözleşmeleri (spec §6.2/§6.3) — `tasks.ts`'in BİREBİR aynı kalıbı,
407
+ * `assigneeUserId` yerine `authorUserId`, `status`/`priority` YOK.
408
+ */
409
+ const noteBodySchema = z.record(z.string(), z.unknown());
410
+ const createNoteRequestSchema = z.object({
411
+ title: z.string().trim().max(200, "en fazla 200 karakter olabilir").optional(),
412
+ body: noteBodySchema.optional(),
413
+ bodyFormat: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
414
+ authorUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional()
415
+ });
416
+ z.object({
417
+ title: z.string().trim().max(200, "en fazla 200 karakter olabilir").nullable().optional(),
418
+ body: noteBodySchema.optional(),
419
+ bodyFormat: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
420
+ authorUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").nullable().optional()
421
+ });
422
+ const noteLinkResponseSchema = z.object({
423
+ id: z.uuid(),
424
+ targetType: targetTypeSchema,
425
+ targetId: z.uuid(),
426
+ targetCachedName: z.string(),
427
+ createdAt: z.iso.datetime()
428
+ });
429
+ const noteResponseSchema = z.object({
430
+ id: z.uuid(),
431
+ title: z.string().nullable(),
432
+ body: noteBodySchema.nullable(),
433
+ bodyFormat: z.string().nullable(),
434
+ authorUserId: z.uuid().nullable(),
435
+ createdAt: z.iso.datetime(),
436
+ updatedAt: z.iso.datetime()
437
+ });
438
+ noteResponseSchema.extend({ links: z.array(noteLinkResponseSchema) });
439
+ pagedResponseSchema(noteResponseSchema);
440
+ //#endregion
441
+ //#region packages/contracts/src/tasks.ts
442
+ /**
443
+ * Görev (task) sözleşmeleri (spec §6.2/§6.3).
444
+ *
445
+ * KRİTİK KURAL: `dueAt`/`completedAt` kullanıcının Date/Time Handling
446
+ * Guideline'ına uygun olarak `apps/api/src/routes/tasks.ts`'te luxon
447
+ * (`DateTime`) ile dönüştürülür — burada yalnız ISO 8601 biçim doğrulaması
448
+ * yapılır (zod), gerçek dönüşüm route katmanının işidir.
449
+ *
450
+ * `assigneeUserId` PATCH'te `null` gönderilerek atamanın KALDIRILMASI
451
+ * desteklenir (`nullable().optional()`) — `undefined` "değiştirme", `null`
452
+ * "kaldır" anlamına gelir (companies/contacts/deals'ın PARTIAL şemalarından
453
+ * FARKLI bir kalıp, çünkü onlarda "atamayı kaldır" ihtiyacı yoktu).
454
+ */
455
+ const taskBodySchema = z.record(z.string(), z.unknown());
456
+ const createTaskRequestSchema = z.object({
457
+ title: z.string().trim().min(1, "başlık gerekli").max(200, "en fazla 200 karakter olabilir"),
458
+ body: taskBodySchema.optional(),
459
+ bodyFormat: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
460
+ dueAt: z.iso.datetime("geçerli bir tarih olmalı").optional(),
461
+ status: taskStatusSchema.default("todo"),
462
+ priority: taskPrioritySchema.default("normal"),
463
+ assigneeUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional()
464
+ });
465
+ z.object({
466
+ title: z.string().trim().min(1, "başlık gerekli").max(200, "en fazla 200 karakter olabilir").optional(),
467
+ body: taskBodySchema.optional(),
468
+ bodyFormat: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
469
+ dueAt: z.iso.datetime("geçerli bir tarih olmalı").nullable().optional(),
470
+ status: taskStatusSchema.optional(),
471
+ priority: taskPrioritySchema.optional(),
472
+ assigneeUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").nullable().optional()
473
+ });
474
+ const taskLinkResponseSchema = z.object({
475
+ id: z.uuid(),
476
+ targetType: targetTypeSchema,
477
+ targetId: z.uuid(),
478
+ targetCachedName: z.string(),
479
+ createdAt: z.iso.datetime()
480
+ });
481
+ const taskResponseSchema = z.object({
482
+ id: z.uuid(),
483
+ title: z.string(),
484
+ body: taskBodySchema.nullable(),
485
+ bodyFormat: z.string().nullable(),
486
+ dueAt: z.iso.datetime().nullable(),
487
+ completedAt: z.iso.datetime().nullable(),
488
+ status: taskStatusSchema,
489
+ priority: taskPrioritySchema,
490
+ assigneeUserId: z.uuid().nullable(),
491
+ createdAt: z.iso.datetime(),
492
+ updatedAt: z.iso.datetime()
493
+ });
494
+ taskResponseSchema.extend({ links: z.array(taskLinkResponseSchema) });
495
+ pagedResponseSchema(taskResponseSchema);
496
+ //#endregion
497
+ //#region packages/contracts/src/activities.ts
498
+ /**
499
+ * Aktivite (activity) sözleşmeleri (spec §6.2/§6.3).
500
+ *
501
+ * KRİTİK KURAL: `updateActivityRequestSchema` `.strict()`'tir ve `kind`/
502
+ * `targetType`/`targetId`'yi hiç TANIMLAMAZ — bir aktivitenin hedefi/türü
503
+ * OLUŞTURULDUKTAN SONRA değiştirilemez (bkz. `packages/db/src/schema/activities.ts`
504
+ * dosya başı yorumu, `apps/api/src/routes/activities.ts`). `deals.ts`'teki
505
+ * `updateDealRequestSchema`'nın AYNI kalıbı (bkz. o dosyanın dosya başı yorumu).
506
+ *
507
+ * TEKNİK BORÇ KAPANIŞI (Faz 6 Task 7): `GET /v1/tenants/:id/targets/
508
+ * :targetType/:targetId/timeline` ucu Faz 5 Task 4'ün plan dışı eklediği bir
509
+ * uçtu ve YANIT ZARFI için bir sözleşme şeması export ETMEMİŞTİ — SPA
510
+ * (`apps/dashboard/src/lib/timeline-one-shot.ts`) bu yüzden kendi elle yazılmış
511
+ * bir tipe (`.array()` bileşimi) doğrulama yapıyordu. `targetTimelineResponseSchema`
512
+ * artık TEK gerçek kaynak; iki taraf da (`apps/api/src/routes/activities.ts`
513
+ * VE SPA) buradan okur. `TIMELINE_LIMIT_PER_RESOURCE` de AYNI nedenle burada:
514
+ * backend'in `activities.ts`'teki elle kopyalanmış `= 50` sabiti ile SPA'nın
515
+ * `timeline-panel.tsx`'teki `TIMELINE_ONE_SHOT_CAP = 50` sabiti birbirinden
516
+ * BAĞIMSIZ, elle senkron tutuluyordu (drift riski, bkz. Faz 6 Task 6 raporu
517
+ * "Endişeler").
518
+ */
519
+ const activityPropertiesSchema = z.record(z.string(), z.unknown());
520
+ const createActivityRequestSchema = z.object({
521
+ kind: activityKindSchema,
522
+ subject: z.string().trim().max(200, "en fazla 200 karakter olabilir").optional(),
523
+ body: z.string().trim().max(1e4, "en fazla 10000 karakter olabilir").optional(),
524
+ happensAt: z.iso.datetime("geçerli bir tarih olmalı"),
525
+ durationMinutes: z.number("sayı olmalı").int("tam sayı olmalı").min(0, "negatif olamaz").max(43200, "en fazla 43200 dakika (30 gün) olabilir").optional(),
526
+ targetType: targetTypeSchema,
527
+ targetId: z.uuid("geçerli bir hedef kimliği olmalı"),
528
+ actorUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional(),
529
+ properties: activityPropertiesSchema.optional()
530
+ });
531
+ z.object({
532
+ subject: z.string().trim().max(200, "en fazla 200 karakter olabilir").optional(),
533
+ body: z.string().trim().max(1e4, "en fazla 10000 karakter olabilir").optional(),
534
+ happensAt: z.iso.datetime("geçerli bir tarih olmalı").optional(),
535
+ durationMinutes: z.number("sayı olmalı").int("tam sayı olmalı").min(0, "negatif olamaz").max(43200, "en fazla 43200 dakika (30 gün) olabilir").optional(),
536
+ actorUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional(),
537
+ properties: activityPropertiesSchema.optional()
538
+ }).strict();
539
+ const activityResponseSchema = z.object({
540
+ id: z.uuid(),
541
+ kind: activityKindSchema,
542
+ subject: z.string().nullable(),
543
+ body: z.string().nullable(),
544
+ happensAt: z.iso.datetime(),
545
+ durationMinutes: z.number().nullable(),
546
+ targetType: targetTypeSchema,
547
+ targetId: z.uuid(),
548
+ targetCachedName: z.string(),
549
+ properties: activityPropertiesSchema,
550
+ actorUserId: z.uuid().nullable(),
551
+ createdAt: z.iso.datetime(),
552
+ updatedAt: z.iso.datetime()
553
+ });
554
+ pagedResponseSchema(activityResponseSchema);
555
+ z.object({
556
+ activities: activityResponseSchema.array(),
557
+ tasks: taskResponseSchema.array(),
558
+ notes: noteResponseSchema.array()
559
+ });
560
+ clone();
561
+ var isNumeric = /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
562
+ var mathceil = Math.ceil;
563
+ var mathfloor = Math.floor;
564
+ var bignumberError = "[BigNumber Error] ";
565
+ var BASE = 0x5af3107a4000;
566
+ var LOG_BASE = 14;
567
+ var MAX_SAFE_INTEGER = 9007199254740991;
568
+ var POWS_TEN = [
569
+ 1,
570
+ 10,
571
+ 100,
572
+ 1e3,
573
+ 1e4,
574
+ 1e5,
575
+ 1e6,
576
+ 1e7,
577
+ 1e8,
578
+ 1e9,
579
+ 1e10,
580
+ 1e11,
581
+ 0xe8d4a51000,
582
+ 0x9184e72a000
583
+ ];
584
+ var SQRT_BASE = 1e7;
585
+ var MAX = 1e9;
586
+ function clone(configObject) {
587
+ var div, convertBase, basePrefix = /^(-?)0([xbo])(?=[^.])/i, isInfinityOrNaN = /^-?(Infinity|NaN)$/, whitespaceOrPlus = /^\s*\+(?!-)|^\s+|\s+$/g, P = BigNumber.prototype = {
588
+ constructor: BigNumber,
589
+ toString: null,
590
+ valueOf: null
591
+ }, ONE = new BigNumber(1), DECIMAL_PLACES = 20, ROUNDING_MODE = 4, TO_EXP_NEG = -7, TO_EXP_POS = 21, MIN_EXP = -1e7, MAX_EXP = 1e7, CRYPTO = false, STRICT = true, MODULO_MODE = 1, POW_PRECISION = 0, FORMAT = {
592
+ prefix: "",
593
+ negativeSign: "-",
594
+ positiveSign: "",
595
+ groupSeparator: ",",
596
+ groupSize: 3,
597
+ secondaryGroupSize: 0,
598
+ decimalSeparator: ".",
599
+ fractionGroupSeparator: "",
600
+ fractionGroupSize: 0,
601
+ suffix: ""
602
+ }, ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
603
+ function BigNumber(v, b) {
604
+ var e, i, str, t, x = this;
605
+ if (!(x instanceof BigNumber)) return new BigNumber(v, b);
606
+ t = typeof v;
607
+ if (b == null) {
608
+ if (isBigNumber(v)) {
609
+ x.s = v.s;
610
+ if (!v.c || v.e > MAX_EXP) x.c = x.e = null;
611
+ else if (v.e < MIN_EXP) x.c = [x.e = 0];
612
+ else {
613
+ x.e = v.e;
614
+ x.c = v.c.slice();
615
+ }
616
+ return;
617
+ }
618
+ if (t == "number") {
619
+ if (v * 0 != 0) {
620
+ x.s = isNaN(v) ? null : v < 0 ? -1 : 1;
621
+ x.c = x.e = null;
622
+ return;
623
+ }
624
+ x.s = 1 / v < 0 ? (v = -v, -1) : 1;
625
+ if (v === ~~v) {
626
+ for (e = 0, i = v; i >= 10; i /= 10, e++);
627
+ if (e > MAX_EXP) x.c = x.e = null;
628
+ else {
629
+ x.e = e;
630
+ x.c = [v];
631
+ }
632
+ return;
633
+ }
634
+ return parseValidString(x, String(v));
635
+ }
636
+ if (t == "bigint") {
637
+ x.s = v < 0 ? (v = -v, -1) : 1;
638
+ return parseValidString(x, String(v));
639
+ }
640
+ if (t == "string") str = v;
641
+ else {
642
+ if (STRICT) throw Error(bignumberError + "BigNumber, string, number, or BigInt expected: " + v);
643
+ str = String(v);
644
+ }
645
+ if (isNumeric.test(str)) {
646
+ x.s = str.charCodeAt(0) == 45 ? (str = str.slice(1), -1) : 1;
647
+ return parseValidString(x, str);
648
+ }
649
+ str = str.replace(whitespaceOrPlus, "");
650
+ if (isInfinityOrNaN.test(str)) {
651
+ x.s = isNaN(str) ? null : str < 0 ? -1 : 1;
652
+ x.c = x.e = null;
653
+ return;
654
+ }
655
+ str = str.replace(basePrefix, function(m, p1, p2) {
656
+ b = (p2 = p2.toLowerCase()) == "x" ? 16 : p2 == "b" ? 2 : 8;
657
+ return p1;
658
+ });
659
+ if (b) return parseBaseString(x, str, b, v);
660
+ str = str.replace(/(\d)_(?=\d)/g, "$1");
661
+ if (isNumeric.test(str)) {
662
+ x.s = str.charCodeAt(0) == 45 ? (str = str.slice(1), -1) : 1;
663
+ return parseValidString(x, str);
664
+ }
665
+ if (STRICT) throw Error(bignumberError + "Not a number: " + v);
666
+ x.s = x.c = x.e = null;
667
+ } else {
668
+ if (t != "string") {
669
+ if (STRICT) throw Error(bignumberError + "String expected: " + v);
670
+ v = String(v);
671
+ }
672
+ intCheck(b, 2, ALPHABET.length, "Base");
673
+ parseBaseString(x, v.replace(whitespaceOrPlus, ""), b, v);
674
+ }
675
+ }
676
+ BigNumber.clone = clone;
677
+ BigNumber.ROUND_UP = 0;
678
+ BigNumber.ROUND_DOWN = 1;
679
+ BigNumber.ROUND_CEIL = 2;
680
+ BigNumber.ROUND_FLOOR = 3;
681
+ BigNumber.ROUND_HALF_UP = 4;
682
+ BigNumber.ROUND_HALF_DOWN = 5;
683
+ BigNumber.ROUND_HALF_EVEN = 6;
684
+ BigNumber.ROUND_HALF_CEIL = 7;
685
+ BigNumber.ROUND_HALF_FLOOR = 8;
686
+ BigNumber.EUCLID = 9;
687
+ BigNumber.config = BigNumber.set = function(obj) {
688
+ var p, v;
689
+ if (obj != null) if (typeof obj == "object") {
690
+ if (obj.hasOwnProperty(p = "DECIMAL_PLACES")) DECIMAL_PLACES = intCheck(obj[p], 0, MAX, p);
691
+ if (obj.hasOwnProperty(p = "ROUNDING_MODE")) ROUNDING_MODE = intCheck(obj[p], 0, 8, p);
692
+ if (obj.hasOwnProperty(p = "EXPONENTIAL_AT")) {
693
+ v = obj[p];
694
+ if (isArray(v)) {
695
+ intCheck(v[0], -MAX, 0, p);
696
+ intCheck(v[1], 0, MAX, p);
697
+ TO_EXP_NEG = v[0];
698
+ TO_EXP_POS = v[1];
699
+ } else TO_EXP_NEG = -(TO_EXP_POS = intCheck(v, -MAX, MAX, p) < 0 ? -v : v);
700
+ }
701
+ if (obj.hasOwnProperty(p = "RANGE")) {
702
+ v = obj[p];
703
+ if (v) if (isArray(v)) {
704
+ intCheck(v[0], -MAX, -1, p);
705
+ intCheck(v[1], 1, MAX, p);
706
+ MIN_EXP = v[0];
707
+ MAX_EXP = v[1];
708
+ } else MIN_EXP = -(MAX_EXP = intCheck(v, -MAX, MAX, p) < 0 ? -v : v);
709
+ else throw Error(bignumberError + p + " cannot be zero: " + v);
710
+ }
711
+ if (obj.hasOwnProperty(p = "CRYPTO")) {
712
+ v = obj[p];
713
+ if (v === !!v) if (v) if (typeof crypto != "undefined" && crypto && (crypto.getRandomValues || crypto.randomBytes)) CRYPTO = v;
714
+ else {
715
+ CRYPTO = !v;
716
+ throw Error(bignumberError + "crypto unavailable");
717
+ }
718
+ else CRYPTO = v;
719
+ else throw Error(bignumberError + p + " not true or false: " + v);
720
+ }
721
+ if (obj.hasOwnProperty(p = "STRICT")) {
722
+ v = obj[p];
723
+ if (v === !!v) STRICT = v;
724
+ else throw Error(bignumberError + p + " not true or false: " + v);
725
+ }
726
+ if (obj.hasOwnProperty(p = "MODULO_MODE")) MODULO_MODE = intCheck(obj[p], 0, 9, p);
727
+ if (obj.hasOwnProperty(p = "POW_PRECISION")) POW_PRECISION = intCheck(obj[p], 0, MAX, p);
728
+ if (obj.hasOwnProperty(p = "FORMAT")) {
729
+ v = obj[p];
730
+ if (typeof v == "object") {
731
+ for (p in v) if (v.hasOwnProperty(p) && FORMAT.hasOwnProperty(p)) FORMAT[p] = v[p];
732
+ } else throw Error(bignumberError + p + " not an object: " + v);
733
+ }
734
+ if (obj.hasOwnProperty(p = "ALPHABET")) {
735
+ v = obj[p];
736
+ if (typeof v == "string" && !/^.?$|[+\-.\s]|(.).*\1/.test(v)) ALPHABET = v;
737
+ else throw Error(bignumberError + p + " invalid: " + v);
738
+ }
739
+ } else throw Error(bignumberError + "Object expected: " + obj);
740
+ return {
741
+ DECIMAL_PLACES,
742
+ ROUNDING_MODE,
743
+ EXPONENTIAL_AT: [TO_EXP_NEG, TO_EXP_POS],
744
+ RANGE: [MIN_EXP, MAX_EXP],
745
+ CRYPTO,
746
+ STRICT,
747
+ MODULO_MODE,
748
+ POW_PRECISION,
749
+ FORMAT,
750
+ ALPHABET
751
+ };
752
+ };
753
+ BigNumber.fromFormat = function(str, options) {
754
+ if (typeof str !== "string") throw Error(bignumberError + "Not a string: " + str);
755
+ if (options == null) options = FORMAT;
756
+ else if (typeof options != "object") throw Error(bignumberError + "Argument not an object: " + options);
757
+ else options = resolveFormatOptions(options);
758
+ var i, isNeg, integerPart, fractionPart, negativeSign = options.negativeSign || "-", positiveSign = options.positiveSign || "", prefix = options.prefix || "", suffix = options.suffix || "", groupSeparator = options.groupSeparator || "", decimalSeparator = options.decimalSeparator || ".", fractionGroupSeparator = options.fractionGroupSeparator || "";
759
+ if (prefix && str.indexOf(prefix) === 0) str = str.slice(prefix.length);
760
+ if (suffix && str.lastIndexOf(suffix) === str.length - suffix.length) str = str.slice(0, -suffix.length);
761
+ if (negativeSign && str.indexOf(negativeSign) === 0) {
762
+ str = str.slice(negativeSign.length);
763
+ isNeg = true;
764
+ } else if (positiveSign && str.indexOf(positiveSign) === 0) str = str.slice(positiveSign.length);
765
+ i = str.indexOf(decimalSeparator);
766
+ if (i < 0) {
767
+ if (groupSeparator) while (str.indexOf(groupSeparator) > -1) str = str.replace(groupSeparator, "");
768
+ } else {
769
+ integerPart = str.slice(0, i);
770
+ fractionPart = str.slice(i + decimalSeparator.length);
771
+ if (groupSeparator) while (integerPart.indexOf(groupSeparator) > -1) integerPart = integerPart.replace(groupSeparator, "");
772
+ if (fractionGroupSeparator) while (fractionPart.indexOf(fractionGroupSeparator) > -1) fractionPart = fractionPart.replace(fractionGroupSeparator, "");
773
+ str = integerPart + "." + fractionPart;
774
+ }
775
+ return new BigNumber(isNeg ? "-" + str : str);
776
+ };
777
+ BigNumber.isBigNumber = function(v) {
778
+ if (!isBigNumber(v)) return false;
779
+ var i, n, c = v.c, e = v.e, s = v.s;
780
+ if (!isArray(c)) return c === null && e === null && (s === null || s === 1 || s === -1);
781
+ if (s !== 1 && s !== -1 || e < -MAX || e > MAX || e !== mathfloor(e)) return false;
782
+ if (c[0] === 0) return e === 0 && c.length === 1;
783
+ i = (e + 1) % LOG_BASE;
784
+ if (i < 1) i += LOG_BASE;
785
+ if (String(c[0]).length !== i) return false;
786
+ for (i = 0; i < c.length; i++) {
787
+ n = c[i];
788
+ if (n < 0 || n >= BASE || n !== mathfloor(n)) return false;
789
+ }
790
+ return n !== 0;
791
+ };
792
+ BigNumber.maximum = BigNumber.max = function() {
793
+ return maxOrMin(arguments, -1);
794
+ };
795
+ BigNumber.minimum = BigNumber.min = function() {
796
+ return maxOrMin(arguments, 1);
797
+ };
798
+ BigNumber.random = (function() {
799
+ var pow2_53 = 9007199254740992;
800
+ var random53bitInt = Math.random() * pow2_53 & 2097151 ? function() {
801
+ return mathfloor(Math.random() * pow2_53);
802
+ } : function() {
803
+ return (Math.random() * 1073741824 | 0) * 8388608 + (Math.random() * 8388608 | 0);
804
+ };
805
+ return function(dp) {
806
+ var a, b, e, k, v, i = 0, c = [], rand = new BigNumber(ONE);
807
+ dp = dp == null ? DECIMAL_PLACES : intCheck(dp, 0, MAX);
808
+ k = mathceil(dp / LOG_BASE);
809
+ if (CRYPTO) if (crypto.getRandomValues) {
810
+ a = crypto.getRandomValues(new Uint32Array(k *= 2));
811
+ for (; i < k;) {
812
+ v = a[i] * 131072 + (a[i + 1] >>> 11);
813
+ if (v >= 9e15) {
814
+ b = crypto.getRandomValues(/* @__PURE__ */ new Uint32Array(2));
815
+ a[i] = b[0];
816
+ a[i + 1] = b[1];
817
+ } else {
818
+ c.push(v % 0x5af3107a4000);
819
+ i += 2;
820
+ }
821
+ }
822
+ i = k / 2;
823
+ } else if (crypto.randomBytes) {
824
+ a = crypto.randomBytes(k *= 7);
825
+ for (; i < k;) {
826
+ v = (a[i] & 31) * 281474976710656 + a[i + 1] * 1099511627776 + a[i + 2] * 4294967296 + a[i + 3] * 16777216 + (a[i + 4] << 16) + (a[i + 5] << 8) + a[i + 6];
827
+ if (v >= 9e15) crypto.randomBytes(7).copy(a, i);
828
+ else {
829
+ c.push(v % 0x5af3107a4000);
830
+ i += 7;
831
+ }
832
+ }
833
+ i = k / 7;
834
+ } else {
835
+ CRYPTO = false;
836
+ throw Error(bignumberError + "crypto unavailable");
837
+ }
838
+ if (!CRYPTO) for (; i < k;) {
839
+ v = random53bitInt();
840
+ if (v < 9e15) c[i++] = v % 0x5af3107a4000;
841
+ }
842
+ k = c[--i];
843
+ dp %= LOG_BASE;
844
+ if (k && dp) {
845
+ v = POWS_TEN[LOG_BASE - dp];
846
+ c[i] = mathfloor(k / v) * v;
847
+ }
848
+ for (; c[i] === 0; c.pop(), i--);
849
+ if (i < 0) c = [e = 0];
850
+ else {
851
+ for (e = -1; c[0] === 0; c.splice(0, 1), e -= LOG_BASE);
852
+ for (i = 1, v = c[0]; v >= 10; v /= 10, i++);
853
+ if (i < LOG_BASE) e -= LOG_BASE - i;
854
+ }
855
+ rand.e = e;
856
+ rand.c = c;
857
+ return rand;
858
+ };
859
+ })();
860
+ BigNumber.sum = function() {
861
+ var i = 0, sum = new BigNumber(0);
862
+ for (; i < arguments.length;) sum = sum.plus(arguments[i++]);
863
+ return sum;
864
+ };
865
+ function parseValidString(x, str) {
866
+ var e, i, len;
867
+ if ((e = str.indexOf(".")) > -1) str = str.replace(".", "");
868
+ if ((i = str.search(/e/i)) > 0) {
869
+ if (e < 0) e = i;
870
+ e += +str.slice(i + 1);
871
+ str = str.substring(0, i);
872
+ } else if (e < 0) e = str.length;
873
+ for (i = 0; str.charCodeAt(i) === 48; i++);
874
+ for (len = str.length; str.charCodeAt(--len) === 48;);
875
+ if (str = str.slice(i, ++len)) {
876
+ len -= i;
877
+ e = e - i - 1;
878
+ if (e > MAX_EXP) x.c = x.e = null;
879
+ else if (e < MIN_EXP) x.c = [x.e = 0];
880
+ else {
881
+ x.e = e;
882
+ x.c = [];
883
+ i = (e + 1) % LOG_BASE;
884
+ if (e < 0) i += LOG_BASE;
885
+ if (i < len) {
886
+ if (i) x.c.push(+str.slice(0, i));
887
+ for (len -= LOG_BASE; i < len;) x.c.push(+str.slice(i, i += LOG_BASE));
888
+ i = LOG_BASE - (str = str.slice(i)).length;
889
+ } else i -= len;
890
+ for (; i--; str += "0");
891
+ x.c.push(+str);
892
+ }
893
+ } else x.c = [x.e = 0];
894
+ }
895
+ function parseBaseString(x, str, b, v) {
896
+ var c, len, alphabet = ALPHABET.slice(0, b), i = 0, clean = "", hasDot = false, prevIsNumeral = false, caseChanged = false;
897
+ x.s = str.charCodeAt(0) === 45 ? (str = str.slice(1), -1) : 1;
898
+ for (len = str.length; i < len; i++) {
899
+ c = str.charAt(i);
900
+ if (alphabet.indexOf(c) >= 0) {
901
+ clean += c;
902
+ prevIsNumeral = true;
903
+ continue;
904
+ }
905
+ if (c == "_") {
906
+ if (prevIsNumeral && i + 1 < len) {
907
+ prevIsNumeral = false;
908
+ continue;
909
+ }
910
+ } else if (c == ".") {
911
+ if (i == 0 || !hasDot && prevIsNumeral) {
912
+ if (i + 1 == len) break;
913
+ if (i == 0) clean = "0";
914
+ clean += c;
915
+ hasDot = true;
916
+ prevIsNumeral = false;
917
+ continue;
918
+ }
919
+ } else if (!caseChanged) {
920
+ if (str == str.toUpperCase() && alphabet == alphabet.toLowerCase() && (str = str.toLowerCase()) || str == str.toLowerCase() && alphabet == alphabet.toUpperCase() && (str = str.toUpperCase())) {
921
+ i = -1;
922
+ clean = "";
923
+ caseChanged = true;
924
+ hasDot = prevIsNumeral = false;
925
+ continue;
926
+ }
927
+ }
928
+ if (STRICT) throw Error(bignumberError + "Not a base " + b + " number: " + v);
929
+ x.s = x.c = x.e = null;
930
+ return;
931
+ }
932
+ parseValidString(x, convertBase(clean, b, 10, x.s));
933
+ }
934
+ convertBase = (function() {
935
+ var decimal = "0123456789";
936
+ function toBaseOut(str, baseIn, baseOut, alphabet) {
937
+ var j, arr = [0], arrL, i = 0, len = str.length;
938
+ for (; i < len;) {
939
+ for (arrL = arr.length; arrL--; arr[arrL] *= baseIn);
940
+ arr[0] += alphabet.indexOf(str.charAt(i++));
941
+ for (j = 0; j < arr.length; j++) if (arr[j] > baseOut - 1) {
942
+ if (arr[j + 1] == null) arr[j + 1] = 0;
943
+ arr[j + 1] += arr[j] / baseOut | 0;
944
+ arr[j] %= baseOut;
945
+ }
946
+ }
947
+ return arr.reverse();
948
+ }
949
+ return function(str, baseIn, baseOut, sign, callerIsToString) {
950
+ var alphabet, d, e, k, r, x, xc, y, i = str.indexOf("."), dp = DECIMAL_PLACES, rm = ROUNDING_MODE;
951
+ if (i >= 0) {
952
+ k = POW_PRECISION;
953
+ POW_PRECISION = 0;
954
+ str = str.replace(".", "");
955
+ y = new BigNumber(baseIn);
956
+ x = y.pow(str.length - i);
957
+ POW_PRECISION = k;
958
+ y.c = toBaseOut(toFixedPoint(coeffToString(x.c), x.e, "0"), 10, baseOut, decimal);
959
+ y.e = y.c.length;
960
+ }
961
+ xc = toBaseOut(str, baseIn, baseOut, callerIsToString ? (alphabet = ALPHABET, decimal) : (alphabet = decimal, ALPHABET));
962
+ e = k = xc.length;
963
+ for (; xc[--k] == 0; xc.pop());
964
+ if (!xc[0]) return alphabet.charAt(0);
965
+ if (i < 0) --e;
966
+ else {
967
+ x.c = xc;
968
+ x.e = e;
969
+ x.s = sign;
970
+ x = div(x, y, dp, rm, baseOut);
971
+ xc = x.c;
972
+ r = x.r;
973
+ e = x.e;
974
+ }
975
+ d = e + dp + 1;
976
+ i = xc[d];
977
+ k = baseOut / 2;
978
+ r = r || d < 0 || xc[d + 1] != null;
979
+ r = rm < 4 ? (i != null || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) : i > k || i == k && (rm == 4 || r || rm == 6 && xc[d - 1] & 1 || rm == (x.s < 0 ? 8 : 7));
980
+ if (d < 1 || !xc[0]) str = r ? toFixedPoint(alphabet.charAt(1), -dp, alphabet.charAt(0)) : alphabet.charAt(0);
981
+ else {
982
+ if (d < xc.length) xc.length = d;
983
+ if (r) for (--baseOut; ++xc[--d] > baseOut;) {
984
+ xc[d] = 0;
985
+ if (!d) {
986
+ ++e;
987
+ xc = [1].concat(xc);
988
+ }
989
+ }
990
+ for (k = xc.length; !xc[--k];);
991
+ for (i = 0, str = ""; i <= k; str += alphabet.charAt(xc[i++]));
992
+ str = toFixedPoint(str, e, alphabet.charAt(0));
993
+ }
994
+ return str;
995
+ };
996
+ })();
997
+ div = (function() {
998
+ function multiply(x, k, base) {
999
+ var m, temp, xlo, xhi, carry = 0, i = x.length, klo = k % SQRT_BASE, khi = k / SQRT_BASE | 0;
1000
+ for (x = x.slice(); i--;) {
1001
+ xlo = x[i] % SQRT_BASE;
1002
+ xhi = x[i] / SQRT_BASE | 0;
1003
+ m = khi * xlo + xhi * klo;
1004
+ temp = klo * xlo + m % SQRT_BASE * SQRT_BASE + carry;
1005
+ carry = (temp / base | 0) + (m / SQRT_BASE | 0) + khi * xhi;
1006
+ x[i] = temp % base;
1007
+ }
1008
+ if (carry) x = [carry].concat(x);
1009
+ return x;
1010
+ }
1011
+ function compare(a, b, aL, bL) {
1012
+ var i, cmp;
1013
+ if (aL != bL) cmp = aL > bL ? 1 : -1;
1014
+ else for (i = cmp = 0; i < aL; i++) if (a[i] != b[i]) {
1015
+ cmp = a[i] > b[i] ? 1 : -1;
1016
+ break;
1017
+ }
1018
+ return cmp;
1019
+ }
1020
+ function subtract(a, b, aL, base) {
1021
+ var i = 0;
1022
+ for (; aL--;) {
1023
+ a[aL] -= i;
1024
+ i = a[aL] < b[aL] ? 1 : 0;
1025
+ a[aL] = i * base + a[aL] - b[aL];
1026
+ }
1027
+ for (; !a[0] && a.length > 1; a.splice(0, 1));
1028
+ }
1029
+ return function(x, y, dp, rm, base) {
1030
+ var cmp, e, i, more, n, prod, prodL, q, qc, rem, remL, rem0, xi, xL, yc0, yL, yz, s = x.s == y.s ? 1 : -1, xc = x.c, yc = y.c;
1031
+ if (!xc || !xc[0] || !yc || !yc[0]) return new BigNumber(!x.s || !y.s || (xc ? yc && xc[0] == yc[0] : !yc) ? NaN : xc && xc[0] == 0 || !yc ? s * 0 : s / 0);
1032
+ q = new BigNumber(s);
1033
+ qc = q.c = [];
1034
+ e = x.e - y.e;
1035
+ s = dp + e + 1;
1036
+ if (!base) {
1037
+ base = BASE;
1038
+ e = bitFloor(x.e / LOG_BASE) - bitFloor(y.e / LOG_BASE);
1039
+ s = s / LOG_BASE | 0;
1040
+ }
1041
+ for (i = 0; yc[i] == (xc[i] || 0); i++);
1042
+ if (yc[i] > (xc[i] || 0)) e--;
1043
+ if (s < 0) {
1044
+ qc.push(1);
1045
+ more = true;
1046
+ } else {
1047
+ xL = xc.length;
1048
+ yL = yc.length;
1049
+ i = 0;
1050
+ s += 2;
1051
+ n = mathfloor(base / (yc[0] + 1));
1052
+ if (n > 1) {
1053
+ yc = multiply(yc, n, base);
1054
+ xc = multiply(xc, n, base);
1055
+ yL = yc.length;
1056
+ xL = xc.length;
1057
+ }
1058
+ xi = yL;
1059
+ rem = xc.slice(0, yL);
1060
+ remL = rem.length;
1061
+ for (; remL < yL; rem[remL++] = 0);
1062
+ yz = yc.slice();
1063
+ yz = [0].concat(yz);
1064
+ yc0 = yc[0];
1065
+ if (yc[1] >= base / 2) yc0++;
1066
+ do {
1067
+ n = 0;
1068
+ cmp = compare(yc, rem, yL, remL);
1069
+ if (cmp < 0) {
1070
+ rem0 = rem[0];
1071
+ if (yL != remL) rem0 = rem0 * base + (rem[1] || 0);
1072
+ n = mathfloor(rem0 / yc0);
1073
+ if (n > 1) {
1074
+ if (n >= base) n = base - 1;
1075
+ prod = multiply(yc, n, base);
1076
+ prodL = prod.length;
1077
+ remL = rem.length;
1078
+ while (compare(prod, rem, prodL, remL) == 1) {
1079
+ n--;
1080
+ subtract(prod, yL < prodL ? yz : yc, prodL, base);
1081
+ prodL = prod.length;
1082
+ cmp = 1;
1083
+ }
1084
+ } else {
1085
+ if (n == 0) cmp = n = 1;
1086
+ prod = yc.slice();
1087
+ prodL = prod.length;
1088
+ }
1089
+ if (prodL < remL) prod = [0].concat(prod);
1090
+ subtract(rem, prod, remL, base);
1091
+ remL = rem.length;
1092
+ if (cmp == -1) while (compare(yc, rem, yL, remL) < 1) {
1093
+ n++;
1094
+ subtract(rem, yL < remL ? yz : yc, remL, base);
1095
+ remL = rem.length;
1096
+ }
1097
+ } else if (cmp === 0) {
1098
+ n++;
1099
+ rem = [0];
1100
+ }
1101
+ qc[i++] = n;
1102
+ if (rem[0]) rem[remL++] = xc[xi] || 0;
1103
+ else {
1104
+ rem = [xc[xi]];
1105
+ remL = 1;
1106
+ }
1107
+ } while ((xi++ < xL || rem[0] != null) && s--);
1108
+ more = rem[0] != null;
1109
+ if (!qc[0]) qc.splice(0, 1);
1110
+ }
1111
+ if (base == BASE) {
1112
+ for (i = 1, s = qc[0]; s >= 10; s /= 10, i++);
1113
+ round(q, dp + (q.e = i + e * LOG_BASE - 1) + 1, rm, more);
1114
+ } else {
1115
+ q.e = e;
1116
+ q.r = +more;
1117
+ }
1118
+ return q;
1119
+ };
1120
+ })();
1121
+ function format(n, i, rm, id) {
1122
+ var c0, e, ne, len, str;
1123
+ rm = rm == null ? ROUNDING_MODE : intCheck(rm, 0, 8);
1124
+ if (!n.c) return n.toString();
1125
+ c0 = n.c[0];
1126
+ ne = n.e;
1127
+ if (i == null) {
1128
+ str = coeffToString(n.c);
1129
+ str = id == 1 || id == 2 && (ne <= TO_EXP_NEG || ne >= TO_EXP_POS) ? toExponential(str, ne) : toFixedPoint(str, ne, "0");
1130
+ } else {
1131
+ n = round(new BigNumber(n), i, rm);
1132
+ e = n.e;
1133
+ str = coeffToString(n.c);
1134
+ len = str.length;
1135
+ if (id == 1 || id == 2 && (i <= e || e <= TO_EXP_NEG)) {
1136
+ for (; len < i; str += "0", len++);
1137
+ str = toExponential(str, e);
1138
+ } else {
1139
+ i -= ne + (id === 2 && e > ne);
1140
+ str = toFixedPoint(str, e, "0");
1141
+ if (e + 1 > len) {
1142
+ if (--i > 0) for (str += "."; i--; str += "0");
1143
+ } else {
1144
+ i += e - len;
1145
+ if (i > 0) {
1146
+ if (e + 1 == len) str += ".";
1147
+ for (; i--; str += "0");
1148
+ }
1149
+ }
1150
+ }
1151
+ }
1152
+ return n.s < 0 && c0 ? "-" + str : str;
1153
+ }
1154
+ function isBigNumber(v) {
1155
+ return v instanceof BigNumber || !!v && v._isBigNumber === true;
1156
+ }
1157
+ function maxOrMin(args, n) {
1158
+ var k, y, i = 1, x = new BigNumber(args[0]);
1159
+ for (; i < args.length; i++) {
1160
+ y = new BigNumber(args[i]);
1161
+ if (!y.s || (k = compare(x, y)) === n || k === 0 && x.s === n) x = y;
1162
+ }
1163
+ return x;
1164
+ }
1165
+ function normalise(n, c, e) {
1166
+ var i = 1, j = c.length;
1167
+ for (; !c[--j]; c.pop());
1168
+ for (j = c[0]; j >= 10; j /= 10, i++);
1169
+ if ((e = i + e * LOG_BASE - 1) > MAX_EXP) n.c = n.e = null;
1170
+ else if (e < MIN_EXP) n.c = [n.e = 0];
1171
+ else {
1172
+ n.e = e;
1173
+ n.c = c;
1174
+ }
1175
+ return n;
1176
+ }
1177
+ function resolveFormatOptions(options) {
1178
+ var key, resolved = {};
1179
+ for (key in FORMAT) if (FORMAT.hasOwnProperty(key)) resolved[key] = options.hasOwnProperty(key) ? options[key] : FORMAT[key];
1180
+ return resolved;
1181
+ }
1182
+ function round(x, sd, rm, r) {
1183
+ var d, i, j, k, n, ni, rd, xc = x.c, pows10 = POWS_TEN;
1184
+ if (xc) {
1185
+ out: {
1186
+ for (d = 1, k = xc[0]; k >= 10; k /= 10, d++);
1187
+ i = sd - d;
1188
+ if (i < 0) {
1189
+ i += LOG_BASE;
1190
+ j = sd;
1191
+ n = xc[ni = 0];
1192
+ rd = mathfloor(n / pows10[d - j - 1] % 10);
1193
+ } else {
1194
+ ni = mathceil((i + 1) / LOG_BASE);
1195
+ if (ni >= xc.length) if (r) {
1196
+ for (; xc.length <= ni; xc.push(0));
1197
+ n = rd = 0;
1198
+ d = 1;
1199
+ i %= LOG_BASE;
1200
+ j = i - LOG_BASE + 1;
1201
+ } else break out;
1202
+ else {
1203
+ n = k = xc[ni];
1204
+ for (d = 1; k >= 10; k /= 10, d++);
1205
+ i %= LOG_BASE;
1206
+ j = i - LOG_BASE + d;
1207
+ rd = j < 0 ? 0 : mathfloor(n / pows10[d - j - 1] % 10);
1208
+ }
1209
+ }
1210
+ r = r || sd < 0 || xc[ni + 1] != null || (j < 0 ? n : n % pows10[d - j - 1]);
1211
+ r = rm < 4 ? (rd || r) && (rm == 0 || rm == (x.s < 0 ? 3 : 2)) : rd > 5 || rd == 5 && (rm == 4 || r || rm == 6 && (i > 0 ? j > 0 ? n / pows10[d - j] : 0 : xc[ni - 1]) % 10 & 1 || rm == (x.s < 0 ? 8 : 7));
1212
+ if (sd < 1 || !xc[0]) {
1213
+ xc.length = 0;
1214
+ if (r) {
1215
+ sd -= x.e + 1;
1216
+ xc[0] = pows10[(LOG_BASE - sd % LOG_BASE) % LOG_BASE];
1217
+ x.e = -sd || 0;
1218
+ } else xc[0] = x.e = 0;
1219
+ return x;
1220
+ }
1221
+ if (i == 0) {
1222
+ xc.length = ni;
1223
+ k = 1;
1224
+ ni--;
1225
+ } else {
1226
+ xc.length = ni + 1;
1227
+ k = pows10[LOG_BASE - i];
1228
+ xc[ni] = j > 0 ? mathfloor(n / pows10[d - j] % pows10[j]) * k : 0;
1229
+ }
1230
+ if (r) for (;;) if (ni == 0) {
1231
+ for (i = 1, j = xc[0]; j >= 10; j /= 10, i++);
1232
+ j = xc[0] += k;
1233
+ for (k = 1; j >= 10; j /= 10, k++);
1234
+ if (i != k) {
1235
+ x.e++;
1236
+ if (xc[0] == BASE) xc[0] = 1;
1237
+ }
1238
+ break;
1239
+ } else {
1240
+ xc[ni] += k;
1241
+ if (xc[ni] != BASE) break;
1242
+ xc[ni--] = 0;
1243
+ k = 1;
1244
+ }
1245
+ for (i = xc.length; xc[--i] === 0; xc.pop());
1246
+ }
1247
+ if (x.e > MAX_EXP) x.c = x.e = null;
1248
+ else if (x.e < MIN_EXP) x.c = [x.e = 0];
1249
+ }
1250
+ return x;
1251
+ }
1252
+ function valueOf(n) {
1253
+ var str, e = n.e;
1254
+ if (e === null) return n.toString();
1255
+ str = coeffToString(n.c);
1256
+ str = e <= TO_EXP_NEG || e >= TO_EXP_POS ? toExponential(str, e) : toFixedPoint(str, e, "0");
1257
+ return n.s < 0 ? "-" + str : str;
1258
+ }
1259
+ P.absoluteValue = P.abs = function() {
1260
+ var x = new BigNumber(this);
1261
+ if (x.s < 0) x.s = 1;
1262
+ return x;
1263
+ };
1264
+ P.comparedTo = function(y, b) {
1265
+ return compare(this, new BigNumber(y, b));
1266
+ };
1267
+ P.decimalPlaces = P.dp = function(dp, rm) {
1268
+ var c, n, v, x = this;
1269
+ if (dp != null) return round(new BigNumber(x), intCheck(dp, -MAX, MAX) + x.e + 1, rm == null ? ROUNDING_MODE : intCheck(rm, 0, 8));
1270
+ if (!(c = x.c)) return null;
1271
+ n = ((v = c.length - 1) - bitFloor(this.e / LOG_BASE)) * LOG_BASE;
1272
+ if (v = c[v]) for (; v % 10 == 0; v /= 10, n--);
1273
+ if (n < 0) n = 0;
1274
+ return n;
1275
+ };
1276
+ P.dividedBy = P.div = function(y, b) {
1277
+ return div(this, new BigNumber(y, b), DECIMAL_PLACES, ROUNDING_MODE);
1278
+ };
1279
+ P.dividedToIntegerBy = P.idiv = function(y, b) {
1280
+ return div(this, new BigNumber(y, b), 0, 1);
1281
+ };
1282
+ P.exponentiatedBy = P.pow = function(n, m) {
1283
+ var half, isModExp, i, k, more, nIsBig, nIsNeg, nIsOdd, y, x = this;
1284
+ n = new BigNumber(n);
1285
+ if (n.c && !n.isInteger()) throw Error(bignumberError + "Exponent not an integer: " + valueOf(n));
1286
+ if (m != null) m = new BigNumber(m);
1287
+ nIsBig = n.e > 14;
1288
+ if (!x.c || !x.c[0] || x.c[0] == 1 && !x.e && x.c.length == 1 || !n.c || !n.c[0]) {
1289
+ y = new BigNumber(Math.pow(+valueOf(x), nIsBig ? n.s * (2 - isOdd(n)) : +valueOf(n)));
1290
+ return m ? y.mod(m) : y;
1291
+ }
1292
+ nIsNeg = n.s < 0;
1293
+ if (m) {
1294
+ if (m.c ? !m.c[0] : !m.s) return new BigNumber(NaN);
1295
+ isModExp = !nIsNeg && x.isInteger() && m.isInteger();
1296
+ if (isModExp) x = x.mod(m);
1297
+ } else if (n.e > 9 && (x.e > 0 || x.e < -1 || (x.e == 0 ? x.c[0] > 1 || nIsBig && x.c[1] >= 24e7 : x.c[0] < 8e13 || nIsBig && x.c[0] <= 9999975e7))) {
1298
+ k = x.s < 0 && isOdd(n) ? -0 : 0;
1299
+ if (x.e > -1) k = 1 / k;
1300
+ return new BigNumber(nIsNeg ? 1 / k : k);
1301
+ } else if (POW_PRECISION) k = mathceil(POW_PRECISION / LOG_BASE + 2);
1302
+ if (nIsBig) {
1303
+ half = new BigNumber(.5);
1304
+ if (nIsNeg) n.s = 1;
1305
+ nIsOdd = isOdd(n);
1306
+ } else {
1307
+ i = Math.abs(+valueOf(n));
1308
+ nIsOdd = i % 2;
1309
+ }
1310
+ y = new BigNumber(ONE);
1311
+ for (;;) {
1312
+ if (nIsOdd) {
1313
+ y = y.times(x);
1314
+ if (!y.c) break;
1315
+ if (k) {
1316
+ if (y.c.length > k) y.c.length = k;
1317
+ } else if (isModExp) y = y.mod(m);
1318
+ }
1319
+ if (i) {
1320
+ i = mathfloor(i / 2);
1321
+ if (i === 0) break;
1322
+ nIsOdd = i % 2;
1323
+ } else {
1324
+ n = n.times(half);
1325
+ round(n, n.e + 1, 1);
1326
+ if (n.e > 14) nIsOdd = isOdd(n);
1327
+ else {
1328
+ i = +valueOf(n);
1329
+ if (i === 0) break;
1330
+ nIsOdd = i % 2;
1331
+ }
1332
+ }
1333
+ x = x.times(x);
1334
+ if (k) {
1335
+ if (x.c && x.c.length > k) x.c.length = k;
1336
+ } else if (isModExp) x = x.mod(m);
1337
+ }
1338
+ if (isModExp) return y;
1339
+ if (nIsNeg) y = ONE.div(y);
1340
+ return m ? y.mod(m) : k ? round(y, POW_PRECISION, ROUNDING_MODE, more) : y;
1341
+ };
1342
+ P.integerValue = function(rm) {
1343
+ var n = new BigNumber(this);
1344
+ return round(n, n.e + 1, rm == null ? ROUNDING_MODE : intCheck(rm, 0, 8));
1345
+ };
1346
+ P.isEqualTo = P.eq = function(y, b) {
1347
+ return compare(this, new BigNumber(y, b)) === 0;
1348
+ };
1349
+ P.isFinite = function() {
1350
+ return !!this.c;
1351
+ };
1352
+ P.isGreaterThan = P.gt = function(y, b) {
1353
+ return compare(this, new BigNumber(y, b)) > 0;
1354
+ };
1355
+ P.isGreaterThanOrEqualTo = P.gte = function(y, b) {
1356
+ return (b = compare(this, new BigNumber(y, b))) === 1 || b === 0;
1357
+ };
1358
+ P.isInteger = function() {
1359
+ return !!this.c && bitFloor(this.e / LOG_BASE) > this.c.length - 2;
1360
+ };
1361
+ P.isLessThan = P.lt = function(y, b) {
1362
+ return compare(this, new BigNumber(y, b)) < 0;
1363
+ };
1364
+ P.isLessThanOrEqualTo = P.lte = function(y, b) {
1365
+ return (b = compare(this, new BigNumber(y, b))) === -1 || b === 0;
1366
+ };
1367
+ P.isNaN = function() {
1368
+ return !this.s;
1369
+ };
1370
+ P.isNegative = function() {
1371
+ return this.s < 0;
1372
+ };
1373
+ P.isPositive = function() {
1374
+ return this.s > 0;
1375
+ };
1376
+ P.isZero = function() {
1377
+ return !!this.c && this.c[0] == 0;
1378
+ };
1379
+ P.minus = function(y, b) {
1380
+ var i, j, t, xLTy, x = this, a = x.s;
1381
+ y = new BigNumber(y, b);
1382
+ b = y.s;
1383
+ if (!a || !b) return new BigNumber(NaN);
1384
+ if (a != b) {
1385
+ y.s = -b;
1386
+ return x.plus(y);
1387
+ }
1388
+ var xe = x.e / LOG_BASE, ye = y.e / LOG_BASE, xc = x.c, yc = y.c;
1389
+ if (!xe || !ye) {
1390
+ if (!xc || !yc) return xc ? (y.s = -b, y) : new BigNumber(yc ? x : NaN);
1391
+ if (!xc[0] || !yc[0]) return yc[0] ? (y.s = -b, y) : new BigNumber(xc[0] ? x : ROUNDING_MODE == 3 ? -0 : 0);
1392
+ }
1393
+ xe = bitFloor(xe);
1394
+ ye = bitFloor(ye);
1395
+ xc = xc.slice();
1396
+ if (a = xe - ye) {
1397
+ if (xLTy = a < 0) {
1398
+ a = -a;
1399
+ t = xc;
1400
+ } else {
1401
+ ye = xe;
1402
+ t = yc;
1403
+ }
1404
+ t.reverse();
1405
+ for (b = a; b--; t.push(0));
1406
+ t.reverse();
1407
+ } else {
1408
+ j = (xLTy = (a = xc.length) < (b = yc.length)) ? a : b;
1409
+ for (a = b = 0; b < j; b++) if (xc[b] != yc[b]) {
1410
+ xLTy = xc[b] < yc[b];
1411
+ break;
1412
+ }
1413
+ }
1414
+ if (xLTy) {
1415
+ t = xc;
1416
+ xc = yc;
1417
+ yc = t;
1418
+ y.s = -y.s;
1419
+ }
1420
+ b = (j = yc.length) - (i = xc.length);
1421
+ if (b > 0) for (; b--; xc[i++] = 0);
1422
+ b = BASE - 1;
1423
+ for (; j > a;) {
1424
+ if (xc[--j] < yc[j]) {
1425
+ for (i = j; i && !xc[--i]; xc[i] = b);
1426
+ --xc[i];
1427
+ xc[j] += BASE;
1428
+ }
1429
+ xc[j] -= yc[j];
1430
+ }
1431
+ for (; xc[0] == 0; xc.splice(0, 1), --ye);
1432
+ if (!xc[0]) {
1433
+ y.s = ROUNDING_MODE == 3 ? -1 : 1;
1434
+ y.c = [y.e = 0];
1435
+ return y;
1436
+ }
1437
+ return normalise(y, xc, ye);
1438
+ };
1439
+ P.modulo = P.mod = function(y, b) {
1440
+ var q, s, x = this;
1441
+ y = new BigNumber(y, b);
1442
+ if (!x.c || !y.s || y.c && !y.c[0]) return new BigNumber(NaN);
1443
+ else if (!y.c || x.c && !x.c[0]) return new BigNumber(x);
1444
+ if (MODULO_MODE == 9) {
1445
+ s = y.s;
1446
+ y.s = 1;
1447
+ q = div(x, y, 0, 3);
1448
+ y.s = s;
1449
+ q.s *= s;
1450
+ } else q = div(x, y, 0, MODULO_MODE);
1451
+ y = x.minus(q.times(y));
1452
+ if (!y.c[0] && MODULO_MODE == 1) y.s = x.s;
1453
+ return y;
1454
+ };
1455
+ P.multipliedBy = P.times = function(y, b) {
1456
+ var c, e, i, j, k, m, xcL, xlo, xhi, ycL, ylo, yhi, zc, base, sqrtBase, x = this, xc = x.c, yc = (y = new BigNumber(y, b)).c;
1457
+ if (!xc || !yc || !xc[0] || !yc[0]) {
1458
+ if (!x.s || !y.s || xc && !xc[0] && !yc || yc && !yc[0] && !xc) y.c = y.e = y.s = null;
1459
+ else {
1460
+ y.s *= x.s;
1461
+ if (!xc || !yc) y.c = y.e = null;
1462
+ else {
1463
+ y.c = [0];
1464
+ y.e = 0;
1465
+ }
1466
+ }
1467
+ return y;
1468
+ }
1469
+ e = bitFloor(x.e / LOG_BASE) + bitFloor(y.e / LOG_BASE);
1470
+ y.s *= x.s;
1471
+ xcL = xc.length;
1472
+ ycL = yc.length;
1473
+ if (xcL < ycL) {
1474
+ zc = xc;
1475
+ xc = yc;
1476
+ yc = zc;
1477
+ i = xcL;
1478
+ xcL = ycL;
1479
+ ycL = i;
1480
+ }
1481
+ for (i = xcL + ycL, zc = []; i--; zc.push(0));
1482
+ base = BASE;
1483
+ sqrtBase = SQRT_BASE;
1484
+ for (i = ycL; --i >= 0;) {
1485
+ c = 0;
1486
+ ylo = yc[i] % sqrtBase;
1487
+ yhi = yc[i] / sqrtBase | 0;
1488
+ for (k = xcL, j = i + k; j > i;) {
1489
+ xlo = xc[--k] % sqrtBase;
1490
+ xhi = xc[k] / sqrtBase | 0;
1491
+ m = yhi * xlo + xhi * ylo;
1492
+ xlo = ylo * xlo + m % sqrtBase * sqrtBase + zc[j] + c;
1493
+ c = (xlo / base | 0) + (m / sqrtBase | 0) + yhi * xhi;
1494
+ zc[j--] = xlo % base;
1495
+ }
1496
+ zc[j] = c;
1497
+ }
1498
+ if (c) ++e;
1499
+ else zc.splice(0, 1);
1500
+ return normalise(y, zc, e);
1501
+ };
1502
+ P.negated = function() {
1503
+ var x = new BigNumber(this);
1504
+ x.s = -x.s || null;
1505
+ return x;
1506
+ };
1507
+ P.plus = function(y, b) {
1508
+ var t, x = this, a = x.s;
1509
+ y = new BigNumber(y, b);
1510
+ b = y.s;
1511
+ if (!a || !b) return new BigNumber(NaN);
1512
+ if (a != b) {
1513
+ y.s = -b;
1514
+ return x.minus(y);
1515
+ }
1516
+ var xe = x.e / LOG_BASE, ye = y.e / LOG_BASE, xc = x.c, yc = y.c;
1517
+ if (!xe || !ye) {
1518
+ if (!xc || !yc) return new BigNumber(a / 0);
1519
+ if (!xc[0] || !yc[0]) return yc[0] ? y : new BigNumber(xc[0] ? x : a * 0);
1520
+ }
1521
+ xe = bitFloor(xe);
1522
+ ye = bitFloor(ye);
1523
+ xc = xc.slice();
1524
+ if (a = xe - ye) {
1525
+ if (a > 0) {
1526
+ ye = xe;
1527
+ t = yc;
1528
+ } else {
1529
+ a = -a;
1530
+ t = xc;
1531
+ }
1532
+ t.reverse();
1533
+ for (; a--; t.push(0));
1534
+ t.reverse();
1535
+ }
1536
+ a = xc.length;
1537
+ b = yc.length;
1538
+ if (a - b < 0) {
1539
+ t = yc;
1540
+ yc = xc;
1541
+ xc = t;
1542
+ b = a;
1543
+ }
1544
+ for (a = 0; b;) {
1545
+ a = (xc[--b] = xc[b] + yc[b] + a) / BASE | 0;
1546
+ xc[b] = BASE === xc[b] ? 0 : xc[b] % BASE;
1547
+ }
1548
+ if (a) {
1549
+ xc = [a].concat(xc);
1550
+ ++ye;
1551
+ }
1552
+ return normalise(y, xc, ye);
1553
+ };
1554
+ P.precision = P.sd = function(sd, rm) {
1555
+ var c, n, v, x = this;
1556
+ if (sd != null && sd !== !!sd) return round(new BigNumber(x), intCheck(sd, 1, MAX), rm == null ? ROUNDING_MODE : intCheck(rm, 0, 8));
1557
+ if (!(c = x.c)) return null;
1558
+ v = c.length - 1;
1559
+ n = v * LOG_BASE + 1;
1560
+ if (v = c[v]) {
1561
+ for (; v % 10 == 0; v /= 10, n--);
1562
+ for (v = c[0]; v >= 10; v /= 10, n++);
1563
+ }
1564
+ if (sd && x.e + 1 > n) n = x.e + 1;
1565
+ return n;
1566
+ };
1567
+ P.shiftedBy = function(k) {
1568
+ return this.times("1e" + intCheck(k, -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER));
1569
+ };
1570
+ P.squareRoot = P.sqrt = function() {
1571
+ var m, n, r, rep, t, x = this, c = x.c, s = x.s, e = x.e, dp = DECIMAL_PLACES + 4, half = new BigNumber("0.5");
1572
+ if (s !== 1 || !c || !c[0]) return new BigNumber(!s || s < 0 && (!c || c[0]) ? NaN : c ? x : Infinity);
1573
+ s = Math.sqrt(+valueOf(x));
1574
+ if (s == 0 || s == Infinity) {
1575
+ n = coeffToString(c);
1576
+ if ((n.length + e) % 2 == 0) n += "0";
1577
+ s = Math.sqrt(+n);
1578
+ e = bitFloor((e + 1) / 2) - (e < 0 || e % 2);
1579
+ if (s == Infinity) n = "5e" + e;
1580
+ else {
1581
+ n = s.toExponential();
1582
+ n = n.slice(0, n.indexOf("e") + 1) + e;
1583
+ }
1584
+ r = new BigNumber(n);
1585
+ } else r = new BigNumber(s + "");
1586
+ if (r.c[0]) {
1587
+ e = r.e;
1588
+ s = e + dp;
1589
+ if (s < 3) s = 0;
1590
+ for (;;) {
1591
+ t = r;
1592
+ r = half.times(t.plus(div(x, t, dp, 1)));
1593
+ if (coeffToString(t.c).slice(0, s) === (n = coeffToString(r.c)).slice(0, s)) {
1594
+ if (r.e < e) --s;
1595
+ n = n.slice(s - 3, s + 1);
1596
+ if (n == "9999" || !rep && n == "4999") {
1597
+ if (!rep) {
1598
+ round(t, t.e + DECIMAL_PLACES + 2, 0);
1599
+ if (t.times(t).eq(x)) {
1600
+ r = t;
1601
+ break;
1602
+ }
1603
+ }
1604
+ dp += 4;
1605
+ s += 4;
1606
+ rep = 1;
1607
+ } else {
1608
+ if (!+n || !+n.slice(1) && n.charAt(0) == "5") {
1609
+ round(r, r.e + DECIMAL_PLACES + 2, 1);
1610
+ m = !r.times(r).eq(x);
1611
+ }
1612
+ break;
1613
+ }
1614
+ }
1615
+ }
1616
+ }
1617
+ return round(r, r.e + DECIMAL_PLACES + 1, ROUNDING_MODE, m);
1618
+ };
1619
+ if (typeof BigInt == "function") P.toBigInt = function(rm) {
1620
+ var x = this;
1621
+ if (!x.c) return null;
1622
+ return BigInt(format(x, x.e + 1, rm));
1623
+ };
1624
+ P.toExponential = function(dp, rm) {
1625
+ return format(this, dp == null ? dp : intCheck(dp, 0, MAX) + 1, rm, 1);
1626
+ };
1627
+ P.toFixed = function(dp, rm) {
1628
+ return format(this, dp == null ? dp : intCheck(dp, -MAX, MAX) + this.e + 1, rm);
1629
+ };
1630
+ P.toFormat = function(dp, rm, options) {
1631
+ var isNeg, min, max, str, x = this;
1632
+ if (options == null) {
1633
+ options = FORMAT;
1634
+ if (dp != null) {
1635
+ if (rm != null) {
1636
+ if (typeof rm == "object") {
1637
+ options = resolveFormatOptions(rm);
1638
+ rm = null;
1639
+ }
1640
+ } else if (typeof dp == "object" && !isArray(dp)) {
1641
+ options = resolveFormatOptions(dp);
1642
+ dp = rm = null;
1643
+ }
1644
+ }
1645
+ } else if (typeof options != "object") throw Error(bignumberError + "Argument not an object: " + options);
1646
+ else options = resolveFormatOptions(options);
1647
+ if (dp != null) if (isArray(dp) && dp.length <= 2) {
1648
+ min = dp[0];
1649
+ max = dp[1];
1650
+ dp = x.dp();
1651
+ if (max != null && dp > intCheck(max, 0, MAX)) dp = max;
1652
+ if (min != null && intCheck(min, 0, MAX) !== 0) {
1653
+ if (max != null && min > max) throw Error(bignumberError + "Minimum must not exceed maximum");
1654
+ if (dp < min) dp = min;
1655
+ }
1656
+ } else intCheck(dp, -MAX, MAX);
1657
+ str = x.toFixed(dp, rm);
1658
+ isNeg = str.charCodeAt(0) === 45;
1659
+ if (isNeg) str = str.slice(1);
1660
+ if (x.c) {
1661
+ var i, arr = str.split("."), g1 = +options.groupSize, g2 = +options.secondaryGroupSize, groupSeparator = options.groupSeparator || "", intPart = arr[0], fractionPart = arr[1], len = intPart.length;
1662
+ if (g2) {
1663
+ i = g1;
1664
+ g1 = g2;
1665
+ g2 = i;
1666
+ len -= i;
1667
+ }
1668
+ if (g1 > 0 && len > 0) {
1669
+ i = len % g1 || g1;
1670
+ str = intPart.substr(0, i);
1671
+ for (; i < len; i += g1) str += groupSeparator + intPart.substr(i, g1);
1672
+ if (g2 > 0) str += groupSeparator + intPart.slice(i);
1673
+ } else str = intPart;
1674
+ if (fractionPart) {
1675
+ i = +options.fractionGroupSize;
1676
+ if (i) fractionPart = fractionPart.replace(new RegExp("\\d{" + i + "}\\B", "g"), "$&" + (options.fractionGroupSeparator || ""));
1677
+ str += (options.decimalSeparator || "") + fractionPart;
1678
+ }
1679
+ }
1680
+ return (options.prefix || "") + (isNeg ? options.negativeSign || "" : x.s > 0 ? options.positiveSign || "" : "") + str + (options.suffix || "");
1681
+ };
1682
+ P.toFraction = function(md) {
1683
+ var d, d0, d1, d2, e, exp, n, n0, n1, q, r, s, xd, xn, x = this, xc = x.c;
1684
+ if (md != null) {
1685
+ n = new BigNumber(md);
1686
+ if (!n.isInteger() && (n.c || n.s !== 1) || n.lt(ONE)) throw Error(bignumberError + "Argument " + (n.isInteger() ? "out of range: " : "not an integer: ") + valueOf(n));
1687
+ }
1688
+ if (!xc) return [new BigNumber(x.s || 0), new BigNumber(0)];
1689
+ d = new BigNumber(ONE);
1690
+ n1 = d0 = new BigNumber(ONE);
1691
+ d1 = n0 = new BigNumber(ONE);
1692
+ s = coeffToString(xc);
1693
+ e = d.e = s.length - x.e - 1;
1694
+ d.c[0] = POWS_TEN[(exp = e % LOG_BASE) < 0 ? LOG_BASE + exp : exp];
1695
+ md = !md || n.comparedTo(d) > 0 ? e > 0 ? d : n1 : n;
1696
+ exp = MAX_EXP;
1697
+ MAX_EXP = Infinity;
1698
+ n = new BigNumber(s);
1699
+ xn = n;
1700
+ xd = d;
1701
+ n0.c[0] = 0;
1702
+ for (;;) {
1703
+ q = div(n, d, 0, 1);
1704
+ d2 = d0.plus(q.times(d1));
1705
+ if (d2.comparedTo(md) == 1) break;
1706
+ d0 = d1;
1707
+ d1 = d2;
1708
+ n1 = n0.plus(q.times(d2 = n1));
1709
+ n0 = d2;
1710
+ d = n.minus(q.times(d2 = d));
1711
+ n = d2;
1712
+ }
1713
+ d2 = div(md.minus(d0), d1, 0, 1);
1714
+ n0 = n0.plus(d2.times(n1));
1715
+ d0 = d0.plus(d2.times(d1));
1716
+ r = n1.times(xd).minus(xn.times(d1)).abs().times(d0).comparedTo(n0.times(xd).minus(xn.times(d0)).abs().times(d1)) < 1 ? [n1, d1] : [n0, d0];
1717
+ r[0].s = x.s;
1718
+ MAX_EXP = exp;
1719
+ return r;
1720
+ };
1721
+ P.toNumber = function() {
1722
+ return +valueOf(this);
1723
+ };
1724
+ P.toObject = function() {
1725
+ var x = this;
1726
+ return {
1727
+ c: x.c ? x.c.slice() : null,
1728
+ e: x.e,
1729
+ s: x.s
1730
+ };
1731
+ };
1732
+ P.toPrecision = function(sd, rm) {
1733
+ return format(this, sd == null ? sd : intCheck(sd, 1, MAX), rm, 2);
1734
+ };
1735
+ P.toString = function(b) {
1736
+ var str, n = this, s = n.s, e = n.e;
1737
+ if (e === null) if (s) {
1738
+ str = "Infinity";
1739
+ if (s < 0) str = "-" + str;
1740
+ } else str = "NaN";
1741
+ else {
1742
+ if (b == null) str = e <= TO_EXP_NEG || e >= TO_EXP_POS ? toExponential(coeffToString(n.c), e) : toFixedPoint(coeffToString(n.c), e, "0");
1743
+ else {
1744
+ intCheck(b, 2, ALPHABET.length, "Base");
1745
+ str = convertBase(toFixedPoint(coeffToString(n.c), e, "0"), 10, b, s, true);
1746
+ }
1747
+ if (s < 0 && n.c[0]) str = "-" + str;
1748
+ }
1749
+ return str;
1750
+ };
1751
+ P.valueOf = P.toJSON = function() {
1752
+ return valueOf(this);
1753
+ };
1754
+ P._isBigNumber = true;
1755
+ if (configObject != null) BigNumber.set(configObject);
1756
+ return BigNumber;
1757
+ }
1758
+ function bitFloor(n) {
1759
+ var i = n | 0;
1760
+ return n > 0 || n === i ? i : i - 1;
1761
+ }
1762
+ function coeffToString(a) {
1763
+ var s, z, i = 1, j = a.length, r = a[0] + "";
1764
+ for (; i < j;) {
1765
+ s = a[i++] + "";
1766
+ z = LOG_BASE - s.length;
1767
+ for (; z--; s = "0" + s);
1768
+ r += s;
1769
+ }
1770
+ for (j = r.length; r.charCodeAt(--j) === 48;);
1771
+ return r.slice(0, j + 1 || 1);
1772
+ }
1773
+ function compare(x, y) {
1774
+ var a, b, xc = x.c, yc = y.c, i = x.s, j = y.s, k = x.e, l = y.e;
1775
+ if (!i || !j) return null;
1776
+ a = xc && !xc[0];
1777
+ b = yc && !yc[0];
1778
+ if (a || b) return a ? b ? 0 : -j : i;
1779
+ if (i != j) return i;
1780
+ a = i < 0;
1781
+ b = k == l;
1782
+ if (!xc || !yc) return b ? 0 : !xc ^ a ? 1 : -1;
1783
+ if (!b) return k > l ^ a ? 1 : -1;
1784
+ j = (k = xc.length) < (l = yc.length) ? k : l;
1785
+ for (i = 0; i < j; i++) if (xc[i] != yc[i]) return xc[i] > yc[i] ^ a ? 1 : -1;
1786
+ return k == l ? 0 : k > l ^ a ? 1 : -1;
1787
+ }
1788
+ function intCheck(n, min, max, name) {
1789
+ if (n < min || n > max || n !== mathfloor(n)) throw Error(bignumberError + (name || "Argument") + (typeof n == "number" ? n < min || n > max ? " out of range: " : " not an integer: " : " not a primitive number: ") + String(n));
1790
+ return n;
1791
+ }
1792
+ function isArray(obj) {
1793
+ return {}.toString.call(obj) == "[object Array]";
1794
+ }
1795
+ function isOdd(n) {
1796
+ var k = n.c.length - 1;
1797
+ return bitFloor(n.e / LOG_BASE) == k && n.c[k] % 2 != 0;
1798
+ }
1799
+ function toExponential(str, e) {
1800
+ return (str.length > 1 ? str.charAt(0) + "." + str.slice(1) : str) + (e < 0 ? "e" : "e+") + e;
1801
+ }
1802
+ function toFixedPoint(str, e, z) {
1803
+ var len, zs;
1804
+ if (e < 0) {
1805
+ for (zs = z + "."; ++e; zs += z);
1806
+ str = zs + str;
1807
+ } else {
1808
+ len = str.length;
1809
+ if (++e > len) {
1810
+ for (zs = z, e -= len; --e; zs += z);
1811
+ str += zs;
1812
+ } else if (e < len) str = str.slice(0, e) + "." + str.slice(e);
1813
+ }
1814
+ return str;
1815
+ }
1816
+ //#endregion
1817
+ //#region packages/contracts/src/api-keys.ts
1818
+ /**
1819
+ * API anahtarı sözleşmeleri — permission-mask modeli (spec §5.5).
1820
+ *
1821
+ * KRİTİK KURAL: `scope`, oluşturma anındaki tavandır — gerçek etkili kapsam
1822
+ * istek anında sahibinin GÜNCEL tenant rolüyle kesişiminden hesaplanır (bkz.
1823
+ * apps/api/src/lib/api-key-auth.ts). Bu şemalar yalnız gövde şeklini tanımlar,
1824
+ * yetki hesabını YAPMAZ.
1825
+ */
1826
+ const apiKeyScopeSchema = z.enum([
1827
+ "read",
1828
+ "write",
1829
+ "admin"
1830
+ ], "geçerli bir kapsam olmalı (read, write, admin)");
1831
+ z.object({
1832
+ name: z.string().trim().min(2, "ad en az 2 karakter olmalı").max(80, "ad en fazla 80 karakter olabilir"),
1833
+ scope: apiKeyScopeSchema
1834
+ });
1835
+ z.object({
1836
+ id: z.uuid(),
1837
+ name: z.string(),
1838
+ scope: apiKeyScopeSchema,
1839
+ prefix: z.string(),
1840
+ token: z.string()
1841
+ });
1842
+ const apiKeyResponseSchema = z.object({
1843
+ id: z.uuid(),
1844
+ name: z.string(),
1845
+ scope: apiKeyScopeSchema,
1846
+ prefix: z.string(),
1847
+ lastUsedAt: z.iso.datetime().nullable(),
1848
+ revokedAt: z.iso.datetime().nullable(),
1849
+ createdAt: z.iso.datetime()
1850
+ });
1851
+ z.object({ keys: z.array(apiKeyResponseSchema) });
1852
+ //#endregion
1853
+ //#region packages/contracts/src/common.ts
1854
+ /**
1855
+ * Ortak zod şemaları ve doğrulayıcılar — birden fazla domain tarafından
1856
+ * paylaşılan küçük yapı taşları. Domain'e özgü şemalar kendi dosyalarında
1857
+ * durur (ör. plans.ts, fields.ts).
1858
+ *
1859
+ * Hata mesajları Türkçe: bu şemalar doğrudan API yanıtına dönüşüyor.
1860
+ */
1861
+ /** Tenant üyeliği rolü — sıralı: viewer < member < admin < owner */
1862
+ const memberRoleSchema = z.enum([
1863
+ "owner",
1864
+ "admin",
1865
+ "member",
1866
+ "viewer"
1867
+ ], "geçerli bir rol olmalı (owner, admin, member, viewer)");
1868
+ /**
1869
+ * Tenant slug'ı. NOT: "ayrılmış adlar" (ör. `admin`, `api`, `www`) şu anda
1870
+ * route katmanında FİLTRELENMİYOR — slug bir subdomain değil path-prefix
1871
+ * olarak kullanıldığından (bkz. spec) bu bir güvenlik açığı oluşturmuyor;
1872
+ * yalnız kullanıcı deneyimi/marka çakışması riski Faz 4A kapsamı dışında
1873
+ * bırakıldı. İleride ayrılmış ad listesi eklenirse burası genişletilir.
1874
+ */
1875
+ const tenantSlugSchema = z.string().min(2, "en az 2 karakter olmalı").max(64, "en fazla 64 karakter olabilir").regex(/^[a-z][a-z0-9-]*$/, "küçük harf, rakam ve tire kullanılabilir");
1876
+ /**
1877
+ * Katı hex renk — tenant branding'inde doğrudan CSS'e yazıldığı için
1878
+ * serbest string ASLA kabul edilmez (CSS enjeksiyonu sınırı).
1879
+ */
1880
+ const hexColorSchema = z.string().regex(/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/, "geçerli bir hex renk olmalı (örn. #14b8a6)");
1881
+ z.string().regex(/^\+[1-9]\d{7,14}$/, "uluslararası biçimde olmalı (örn. +905321234567)");
1882
+ /** Muhasebe/bildirim e-postası */
1883
+ const emailSchema = z.email("geçerli bir e-posta adresi olmalı").max(160, "en fazla 160 karakter olabilir");
1884
+ /**
1885
+ * TC Kimlik Numarası checksum doğrulaması (11 hane, sıfırla başlamaz).
1886
+ * 10. hane: (tek konumların toplamı×7 - çift konumların toplamı) mod 10
1887
+ * 11. hane: ilk 10 hanenin toplamı mod 10
1888
+ */
1889
+ function validateTckn(value) {
1890
+ if (!/^[1-9]\d{10}$/.test(value)) return false;
1891
+ const d = [...value].map(Number);
1892
+ let oddSum = 0;
1893
+ let evenSum = 0;
1894
+ for (let i = 0; i < 9; i += 1) {
1895
+ const digit = d[i];
1896
+ if (digit === void 0) return false;
1897
+ if (i % 2 === 0) oddSum += digit;
1898
+ else evenSum += digit;
1899
+ }
1900
+ const tenth = d[9];
1901
+ const eleventh = d[10];
1902
+ if (tenth === void 0 || eleventh === void 0) return false;
1903
+ if (((oddSum * 7 - evenSum) % 10 + 10) % 10 !== tenth) return false;
1904
+ return d.slice(0, 10).reduce((a, b) => a + b, 0) % 10 === eleventh;
1905
+ }
1906
+ /**
1907
+ * Vergi Kimlik Numarası checksum doğrulaması (10 hane).
1908
+ * Maliye Bakanlığı algoritması.
1909
+ */
1910
+ function validateVkn(value) {
1911
+ if (!/^\d{10}$/.test(value)) return false;
1912
+ const d = [...value].map(Number);
1913
+ let sum = 0;
1914
+ for (let i = 0; i < 9; i += 1) {
1915
+ const digit = d[i];
1916
+ if (digit === void 0) return false;
1917
+ const tmp = (digit + 10 - (i + 1)) % 10;
1918
+ if (tmp === 9) sum += tmp;
1919
+ else sum += tmp * 2 ** (10 - (i + 1)) % 9;
1920
+ }
1921
+ const last = d[9];
1922
+ if (last === void 0) return false;
1923
+ return (10 - sum % 10) % 10 === last;
1924
+ }
1925
+ z.object({
1926
+ /** Zitadel'den PKCE akışıyla alınan id_token */
1927
+ idToken: z.string().min(1, "idToken gerekli").max(8192, "idToken çok uzun"),
1928
+ /** id_token profil claim'i taşımıyorsa userinfo tamamlaması için (opsiyonel) */
1929
+ accessToken: z.string().min(1, "accessToken boş olamaz").max(8192, "accessToken çok uzun").optional()
1930
+ });
1931
+ z.object({
1932
+ token: z.string(),
1933
+ user: z.object({
1934
+ id: z.uuid(),
1935
+ email: z.string(),
1936
+ name: z.string()
1937
+ })
1938
+ });
1939
+ z.object({
1940
+ user: z.object({
1941
+ id: z.uuid(),
1942
+ email: z.string(),
1943
+ name: z.string()
1944
+ }),
1945
+ tenants: z.array(z.object({
1946
+ id: z.uuid(),
1947
+ name: z.string(),
1948
+ slug: z.string(),
1949
+ role: memberRoleSchema
1950
+ }))
1951
+ });
1952
+ z.object({
1953
+ name: z.string().trim().min(2, "ad en az 2 karakter olmalı").max(120, "ad en fazla 120 karakter olabilir"),
1954
+ slug: tenantSlugSchema
1955
+ });
1956
+ z.object({
1957
+ id: z.uuid(),
1958
+ name: z.string(),
1959
+ slug: z.string(),
1960
+ role: memberRoleSchema
1961
+ });
1962
+ /** `GET /v1/tenants/:id/members` yanıtındaki tek üye satırı */
1963
+ const tenantMemberSchema = z.object({
1964
+ userId: z.uuid(),
1965
+ email: z.string(),
1966
+ name: z.string(),
1967
+ role: memberRoleSchema
1968
+ });
1969
+ z.object({ members: z.array(tenantMemberSchema) });
1970
+ z.object({ role: memberRoleSchema });
1971
+ z.object({
1972
+ userId: z.uuid(),
1973
+ role: memberRoleSchema
1974
+ });
1975
+ const COUNTRY_CODE_SET = /* @__PURE__ */ new Set([
1976
+ "AD",
1977
+ "AE",
1978
+ "AF",
1979
+ "AG",
1980
+ "AI",
1981
+ "AL",
1982
+ "AM",
1983
+ "AO",
1984
+ "AQ",
1985
+ "AR",
1986
+ "AS",
1987
+ "AT",
1988
+ "AU",
1989
+ "AW",
1990
+ "AX",
1991
+ "AZ",
1992
+ "BA",
1993
+ "BB",
1994
+ "BD",
1995
+ "BE",
1996
+ "BF",
1997
+ "BG",
1998
+ "BH",
1999
+ "BI",
2000
+ "BJ",
2001
+ "BL",
2002
+ "BM",
2003
+ "BN",
2004
+ "BO",
2005
+ "BQ",
2006
+ "BR",
2007
+ "BS",
2008
+ "BT",
2009
+ "BV",
2010
+ "BW",
2011
+ "BY",
2012
+ "BZ",
2013
+ "CA",
2014
+ "CC",
2015
+ "CD",
2016
+ "CF",
2017
+ "CG",
2018
+ "CH",
2019
+ "CI",
2020
+ "CK",
2021
+ "CL",
2022
+ "CM",
2023
+ "CN",
2024
+ "CO",
2025
+ "CR",
2026
+ "CU",
2027
+ "CV",
2028
+ "CW",
2029
+ "CX",
2030
+ "CY",
2031
+ "CZ",
2032
+ "DE",
2033
+ "DJ",
2034
+ "DK",
2035
+ "DM",
2036
+ "DO",
2037
+ "DZ",
2038
+ "EC",
2039
+ "EE",
2040
+ "EG",
2041
+ "EH",
2042
+ "ER",
2043
+ "ES",
2044
+ "ET",
2045
+ "FI",
2046
+ "FJ",
2047
+ "FK",
2048
+ "FM",
2049
+ "FO",
2050
+ "FR",
2051
+ "GA",
2052
+ "GB",
2053
+ "GD",
2054
+ "GE",
2055
+ "GF",
2056
+ "GG",
2057
+ "GH",
2058
+ "GI",
2059
+ "GL",
2060
+ "GM",
2061
+ "GN",
2062
+ "GP",
2063
+ "GQ",
2064
+ "GR",
2065
+ "GS",
2066
+ "GT",
2067
+ "GU",
2068
+ "GW",
2069
+ "GY",
2070
+ "HK",
2071
+ "HM",
2072
+ "HN",
2073
+ "HR",
2074
+ "HT",
2075
+ "HU",
2076
+ "ID",
2077
+ "IE",
2078
+ "IL",
2079
+ "IM",
2080
+ "IN",
2081
+ "IO",
2082
+ "IQ",
2083
+ "IR",
2084
+ "IS",
2085
+ "IT",
2086
+ "JE",
2087
+ "JM",
2088
+ "JO",
2089
+ "JP",
2090
+ "KE",
2091
+ "KG",
2092
+ "KH",
2093
+ "KI",
2094
+ "KM",
2095
+ "KN",
2096
+ "KP",
2097
+ "KR",
2098
+ "KW",
2099
+ "KY",
2100
+ "KZ",
2101
+ "LA",
2102
+ "LB",
2103
+ "LC",
2104
+ "LI",
2105
+ "LK",
2106
+ "LR",
2107
+ "LS",
2108
+ "LT",
2109
+ "LU",
2110
+ "LV",
2111
+ "LY",
2112
+ "MA",
2113
+ "MC",
2114
+ "MD",
2115
+ "ME",
2116
+ "MF",
2117
+ "MG",
2118
+ "MH",
2119
+ "MK",
2120
+ "ML",
2121
+ "MM",
2122
+ "MN",
2123
+ "MO",
2124
+ "MP",
2125
+ "MQ",
2126
+ "MR",
2127
+ "MS",
2128
+ "MT",
2129
+ "MU",
2130
+ "MV",
2131
+ "MW",
2132
+ "MX",
2133
+ "MY",
2134
+ "MZ",
2135
+ "NA",
2136
+ "NC",
2137
+ "NE",
2138
+ "NF",
2139
+ "NG",
2140
+ "NI",
2141
+ "NL",
2142
+ "NO",
2143
+ "NP",
2144
+ "NR",
2145
+ "NU",
2146
+ "NZ",
2147
+ "OM",
2148
+ "PA",
2149
+ "PE",
2150
+ "PF",
2151
+ "PG",
2152
+ "PH",
2153
+ "PK",
2154
+ "PL",
2155
+ "PM",
2156
+ "PN",
2157
+ "PR",
2158
+ "PS",
2159
+ "PT",
2160
+ "PW",
2161
+ "PY",
2162
+ "QA",
2163
+ "RE",
2164
+ "RO",
2165
+ "RS",
2166
+ "RU",
2167
+ "RW",
2168
+ "SA",
2169
+ "SB",
2170
+ "SC",
2171
+ "SD",
2172
+ "SE",
2173
+ "SG",
2174
+ "SH",
2175
+ "SI",
2176
+ "SJ",
2177
+ "SK",
2178
+ "SL",
2179
+ "SM",
2180
+ "SN",
2181
+ "SO",
2182
+ "SR",
2183
+ "SS",
2184
+ "ST",
2185
+ "SV",
2186
+ "SX",
2187
+ "SY",
2188
+ "SZ",
2189
+ "TC",
2190
+ "TD",
2191
+ "TF",
2192
+ "TG",
2193
+ "TH",
2194
+ "TJ",
2195
+ "TK",
2196
+ "TL",
2197
+ "TM",
2198
+ "TN",
2199
+ "TO",
2200
+ "TR",
2201
+ "TT",
2202
+ "TV",
2203
+ "TW",
2204
+ "TZ",
2205
+ "UA",
2206
+ "UG",
2207
+ "UM",
2208
+ "US",
2209
+ "UY",
2210
+ "UZ",
2211
+ "VA",
2212
+ "VC",
2213
+ "VE",
2214
+ "VG",
2215
+ "VI",
2216
+ "VN",
2217
+ "VU",
2218
+ "WF",
2219
+ "WS",
2220
+ "YE",
2221
+ "YT",
2222
+ "ZA",
2223
+ "ZM",
2224
+ "ZW"
2225
+ ]);
2226
+ //#endregion
2227
+ //#region packages/contracts/src/plans.ts
2228
+ const PLAN_CODES = [
2229
+ "free",
2230
+ "pro",
2231
+ "team",
2232
+ "enterprise"
2233
+ ];
2234
+ /** Platform admin yanıtlarında (bkz. contracts/platform.ts) `tenants.planCode`'un doğrulaması */
2235
+ const planCodeSchema = z.enum(PLAN_CODES, "geçerli bir plan olmalı");
2236
+ const BILLING_PERIODS = ["monthly", "yearly"];
2237
+ //#endregion
2238
+ //#region packages/contracts/src/billing.ts
2239
+ /**
2240
+ * Faturalama sözleşmeleri — fatura profili (ülke/bireysel/kurumsal/yurtdışı),
2241
+ * checkout isteği, abonelik/fatura yanıt şekilleri (spec §8.5–§8.8).
2242
+ *
2243
+ * KRİTİK KURAL: `accountingEmail` HER ÜÇ varyantta da ZORUNLU ve ülkeden
2244
+ * BAĞIMSIZDIR (brief madde 8) — tüm muhasebe e-postaları BURAYA gider.
2245
+ */
2246
+ const countryCodeSchema = z.string().length(2, "ISO 3166-1 iki harfli ülke kodu olmalı").refine((value) => COUNTRY_CODE_SET.has(value), "geçerli bir ülke kodu olmalı");
2247
+ z.enum([
2248
+ "tr_individual",
2249
+ "tr_corporate",
2250
+ "international"
2251
+ ], "geçerli bir fatura profili türü olmalı");
2252
+ const trIndividualInputSchema = z.object({
2253
+ kind: z.literal("tr_individual"),
2254
+ country: z.literal("TR"),
2255
+ fullName: z.string().trim().min(1, "ad soyad gerekli").max(200, "en fazla 200 karakter olabilir"),
2256
+ tckn: z.string().refine(validateTckn, "geçerli bir TC Kimlik Numarası olmalı"),
2257
+ address: z.string().trim().min(1, "adres gerekli").max(500, "en fazla 500 karakter olabilir"),
2258
+ city: z.string().trim().min(1, "şehir gerekli").max(120, "en fazla 120 karakter olabilir"),
2259
+ district: z.string().trim().min(1, "ilçe gerekli").max(120, "en fazla 120 karakter olabilir"),
2260
+ accountingEmail: emailSchema
2261
+ });
2262
+ const trCorporateInputSchema = z.object({
2263
+ kind: z.literal("tr_corporate"),
2264
+ country: z.literal("TR"),
2265
+ legalName: z.string().trim().min(1, "unvan gerekli").max(250, "en fazla 250 karakter olabilir"),
2266
+ vkn: z.string().refine(validateVkn, "geçerli bir Vergi Kimlik Numarası olmalı"),
2267
+ taxOffice: z.string().trim().min(1, "vergi dairesi gerekli").max(120, "en fazla 120 karakter olabilir"),
2268
+ address: z.string().trim().min(1, "adres gerekli").max(500, "en fazla 500 karakter olabilir"),
2269
+ city: z.string().trim().min(1, "şehir gerekli").max(120, "en fazla 120 karakter olabilir"),
2270
+ district: z.string().trim().min(1, "ilçe gerekli").max(120, "en fazla 120 karakter olabilir"),
2271
+ accountingEmail: emailSchema
2272
+ });
2273
+ const internationalInputSchema = z.object({
2274
+ kind: z.literal("international"),
2275
+ country: countryCodeSchema.refine((value) => value !== "TR", "yurt dışı profilde ülke TR olamaz"),
2276
+ fullName: z.string().trim().min(1, "ad/unvan gerekli").max(250, "en fazla 250 karakter olabilir"),
2277
+ address: z.string().trim().min(1, "adres gerekli").max(500, "en fazla 500 karakter olabilir"),
2278
+ taxId: z.string().trim().max(60, "en fazla 60 karakter olabilir").optional(),
2279
+ accountingEmail: emailSchema
2280
+ });
2281
+ const billingProfileInputSchema = z.discriminatedUnion("kind", [
2282
+ trIndividualInputSchema,
2283
+ trCorporateInputSchema,
2284
+ internationalInputSchema
2285
+ ]);
2286
+ z.object({
2287
+ data: billingProfileInputSchema,
2288
+ maskedTaxId: z.string().nullable(),
2289
+ createdAt: z.iso.datetime(),
2290
+ updatedAt: z.iso.datetime()
2291
+ });
2292
+ const purchasablePlanCodeSchema = z.enum(PLAN_CODES, "geçerli bir plan olmalı").refine((value) => value === "pro" || value === "team", "yalnız pro veya team paketleri için satın alma başlatılabilir");
2293
+ z.object({
2294
+ planCode: purchasablePlanCodeSchema,
2295
+ billingPeriod: z.enum(BILLING_PERIODS, "geçerli bir dönem olmalı")
2296
+ });
2297
+ z.object({ purchaseUrl: z.url() });
2298
+ z.object({ code: z.literal("billing_profile_required") });
2299
+ const subscriptionStatusValueSchema = z.enum([
2300
+ "trialing",
2301
+ "active",
2302
+ "past_due",
2303
+ "cancelled",
2304
+ "refunded"
2305
+ ]);
2306
+ z.object({
2307
+ planCode: z.enum(PLAN_CODES),
2308
+ billingPeriod: z.enum(BILLING_PERIODS).nullable(),
2309
+ status: subscriptionStatusValueSchema.nullable(),
2310
+ currentPeriodEnd: z.iso.datetime().nullable(),
2311
+ cancelAtPeriodEnd: z.boolean()
2312
+ });
2313
+ const invoiceResponseSchema = z.object({
2314
+ id: z.uuid(),
2315
+ invoiceNumber: z.string(),
2316
+ kind: z.enum(["invoice", "refund"]),
2317
+ status: z.enum([
2318
+ "draft",
2319
+ "formalized",
2320
+ "cancelled"
2321
+ ]),
2322
+ amountGross: z.string(),
2323
+ amountNet: z.string(),
2324
+ amountVat: z.string(),
2325
+ currencyCode: z.string(),
2326
+ vatRate: z.string(),
2327
+ vatExemptionCode: z.string().nullable(),
2328
+ hasPdf: z.boolean(),
2329
+ issuedAt: z.iso.datetime()
2330
+ });
2331
+ z.object({
2332
+ items: z.array(invoiceResponseSchema),
2333
+ nextCursor: z.string().nullable(),
2334
+ hasMore: z.boolean()
2335
+ });
2336
+ z.enum(["invoice", "refund"]);
2337
+ //#endregion
2338
+ //#region packages/contracts/src/companies.ts
2339
+ /**
2340
+ * Şirket (company) sözleşmeleri (spec §6.2).
2341
+ *
2342
+ * KRİTİK KURAL: `annualRevenue` PARA alanıdır — string olarak taşınır, hiçbir
2343
+ * yerde native `number`'a çevrilip aritmetik yapılmaz (bkz. spec §6.1,
2344
+ * `bignumber.js` dışında ASLA hesaplanmaz).
2345
+ *
2346
+ * [Fix round 2 — doğruluk reviewer'ının bulduğu, Task 3'ün `deals.amount`
2347
+ * fix'iyle BİREBİR aynı sınıf hata]: DB kolonu `text`TEN `numeric(18,2)`'ye
2348
+ * taşındı (`FIELD_CATALOG.companies.annual_revenue` `sortable`/`filterable`
2349
+ * ilan ediyordu ama `text`te `ORDER BY`/`>`/`<` LEXICOGRAPHIC karşılaştırma
2350
+ * yapıyordu — bkz. `packages/db/src/schema/companies.ts` dosya başı yorumu,
2351
+ * `packages/db/drizzle/0035_companies_annual_revenue_numeric.sql`). Biçim
2352
+ * kısıtı `deals.amount` ile AYNI (`./crm.ts` `numericMoneySchema`, DRY).
2353
+ */
2354
+ const createCompanyRequestSchema = z.object({
2355
+ name: z.string().trim().min(1, "ad gerekli").max(200, "en fazla 200 karakter olabilir"),
2356
+ domain: z.string().trim().max(255, "en fazla 255 karakter olabilir").optional(),
2357
+ industry: z.string().trim().max(120, "en fazla 120 karakter olabilir").optional(),
2358
+ size: companySizeSchema.optional(),
2359
+ phone: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
2360
+ website: z.url("geçerli bir URL olmalı").max(500, "en fazla 500 karakter olabilir").optional(),
2361
+ address: z.string().trim().max(500, "en fazla 500 karakter olabilir").optional(),
2362
+ city: z.string().trim().max(120, "en fazla 120 karakter olabilir").optional(),
2363
+ country: z.string().trim().max(120, "en fazla 120 karakter olabilir").optional(),
2364
+ /** PARA — bkz. dosya başı KRİTİK KURAL, `numeric(18,2)` ile eşleşen biçim kısıtı */
2365
+ annualRevenue: numericMoneySchema.optional(),
2366
+ linkedinUrl: z.url("geçerli bir URL olmalı").max(500, "en fazla 500 karakter olabilir").optional(),
2367
+ ownerUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional()
2368
+ });
2369
+ const updateCompanyRequestSchema = createCompanyRequestSchema.partial();
2370
+ pagedResponseSchema(z.object({
2371
+ id: z.uuid(),
2372
+ name: z.string(),
2373
+ domain: z.string().nullable(),
2374
+ industry: z.string().nullable(),
2375
+ size: companySizeSchema.nullable(),
2376
+ phone: z.string().nullable(),
2377
+ website: z.string().nullable(),
2378
+ address: z.string().nullable(),
2379
+ city: z.string().nullable(),
2380
+ country: z.string().nullable(),
2381
+ annualRevenue: z.string().nullable(),
2382
+ linkedinUrl: z.string().nullable(),
2383
+ ownerUserId: z.uuid().nullable(),
2384
+ createdAt: z.iso.datetime(),
2385
+ updatedAt: z.iso.datetime()
2386
+ }));
2387
+ //#endregion
2388
+ //#region packages/contracts/src/contacts.ts
2389
+ /**
2390
+ * Kişi (contact) sözleşmeleri (spec §6.2). `companyId` nullable — bir kişi
2391
+ * şirketsiz de var olabilir (bireysel müşteri).
2392
+ */
2393
+ const createContactRequestSchema = z.object({
2394
+ firstName: z.string().trim().min(1, "ad gerekli").max(120, "en fazla 120 karakter olabilir"),
2395
+ lastName: z.string().trim().min(1, "soyad gerekli").max(120, "en fazla 120 karakter olabilir"),
2396
+ email: z.email("geçerli bir e-posta adresi olmalı").max(160).optional(),
2397
+ phone: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
2398
+ mobile: z.string().trim().max(40, "en fazla 40 karakter olabilir").optional(),
2399
+ jobTitle: z.string().trim().max(160, "en fazla 160 karakter olabilir").optional(),
2400
+ linkedinUrl: z.url("geçerli bir URL olmalı").max(500, "en fazla 500 karakter olabilir").optional(),
2401
+ avatarUrl: z.url("geçerli bir URL olmalı").max(500, "en fazla 500 karakter olabilir").optional(),
2402
+ city: z.string().trim().max(120, "en fazla 120 karakter olabilir").optional(),
2403
+ country: z.string().trim().max(120, "en fazla 120 karakter olabilir").optional(),
2404
+ ownerUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional(),
2405
+ source: contactSourceSchema.optional(),
2406
+ status: contactStatusSchema.default("lead"),
2407
+ companyId: z.uuid("geçerli bir şirket kimliği olmalı").optional()
2408
+ });
2409
+ /**
2410
+ * [Fix round 1 — Faz 10 Task 3] `status` AÇIKÇA yeniden tanımlanır; düz
2411
+ * `.partial()` YETMEZ.
2412
+ *
2413
+ * ÖLÇÜLEN KUSUR: zod 4'te `.partial()` bir alanı `.optional()` yapar ama
2414
+ * `.default()`'unu KALDIRMAZ ⇒ `createContactRequestSchema.partial()`'in
2415
+ * `status`'ü hâlâ `.default('lead')` taşıyordu ve
2416
+ * `updateContactRequestSchema.parse({ city: 'x' })` → `{ city:'x',
2417
+ * status:'lead' }` üretiyordu (canlı ölçüldü). `PATCH /v1/tenants/:id/
2418
+ * contacts/:contactId` gövdeyi `validateJson(updateContactRequestSchema)` ile
2419
+ * doğrulayıp `.set({ ...body, updatedById })` yazdığı için
2420
+ * (`apps/api/src/routes/contacts.ts:314`) `status` GÖNDERMEYEN her kısmi
2421
+ * güncelleme, `active`/`inactive` bir kişiyi SESSİZCE `lead`'e geri
2422
+ * düşürüyordu — dashboard'ın düzenleme formu `status`'ü her zaman gönderdiği
2423
+ * için belirti orada görünmüyordu; kusuru MCP'nin `update_contact` aracı
2424
+ * (yalnız verilen alanları gönderir) ortaya çıkardı.
2425
+ *
2426
+ * KARDEŞ ŞEMALARDA AYNI KUSUR YOK ama gerekçesi TESADÜF: `companies`/
2427
+ * `pipelines`'ın create şemalarında `.default()`'lu alan HİÇ YOK,
2428
+ * `updateDealRequestSchema` ise `.partial()` DEĞİL elle yazılmış bir şemadır.
2429
+ * ⇒ `.default()`'lu bir alan içeren bir create şemasından `.partial()` ile
2430
+ * update şeması TÜRETİRKEN o alanı DAİMA açıkça `.optional()` olarak ez.
2431
+ */
2432
+ const updateContactRequestSchema = createContactRequestSchema.partial().extend({ status: contactStatusSchema.optional() });
2433
+ pagedResponseSchema(z.object({
2434
+ id: z.uuid(),
2435
+ firstName: z.string(),
2436
+ lastName: z.string(),
2437
+ email: z.string().nullable(),
2438
+ phone: z.string().nullable(),
2439
+ mobile: z.string().nullable(),
2440
+ jobTitle: z.string().nullable(),
2441
+ linkedinUrl: z.string().nullable(),
2442
+ avatarUrl: z.string().nullable(),
2443
+ city: z.string().nullable(),
2444
+ country: z.string().nullable(),
2445
+ ownerUserId: z.uuid().nullable(),
2446
+ source: contactSourceSchema.nullable(),
2447
+ status: contactStatusSchema,
2448
+ companyId: z.uuid().nullable(),
2449
+ createdAt: z.iso.datetime(),
2450
+ updatedAt: z.iso.datetime()
2451
+ }));
2452
+ z.object({
2453
+ code: z.literal("contact_limit_reached"),
2454
+ limit: z.number(),
2455
+ planCode: planCodeSchema
2456
+ });
2457
+ z.string().regex(/^[A-Z]{3}$/, "geçerli bir 3 harfli para birimi kodu olmalı (örn. TRY)");
2458
+ //#endregion
2459
+ //#region packages/contracts/src/deals.ts
2460
+ /**
2461
+ * Fırsat (deal) sözleşmeleri (spec §6.2/§6.4).
2462
+ *
2463
+ * KRİTİK KURAL: `updateDealRequestSchema` `.strict()`'tir ve `stageId`/
2464
+ * `pipelineId`/`position`/`outcome`/`closedAt` alanlarını hiç TANIMLAMAZ —
2465
+ * bu alanlar yalnız `moveDealRequestSchema` (`/move` ucu) üzerinden değişir
2466
+ * (bkz. `packages/db/src/schema/deals.ts` dosya başı KRİTİK KURAL 2,
2467
+ * `apps/api/src/routes/deals.ts`). `.strict()` altında bilinmeyen bir anahtar
2468
+ * zod'un `unrecognized_keys` hatasını üretir; bu, `apps/api/src/lib/validate.ts`
2469
+ * içindeki `ZOD_DEFAULT_MESSAGE` deseniyle ZATEN eşleşiyor (`Unrecognized key`
2470
+ * ile başlar) — bu yüzden ayrı bir Türkçe mesaj GEREKMEZ, jenerik "Geçersiz
2471
+ * istek gövdesi" mesajına düşer (bu, Global Constraints #3'ün belgelediği
2472
+ * bilinçli istisna).
2473
+ *
2474
+ * FIX ROUND 1 (bkz. task-3-report.md): `amount` DB'de `numeric(18,2)`'ye
2475
+ * taşındı (önceden `text` idi — `sortable`/`filterable` ilan edilmiş bir
2476
+ * alanın metin karşılaştırmasıyla sıralanması sessiz bir doğruluk hatasıydı).
2477
+ * Biçim kısıtı (yalnız `-?\d+(\.\d{1,2})?`, en fazla 18 anlamlı basamak) ARTIK
2478
+ * `./crm.ts`'teki `numericMoneySchema`'da PAYLAŞILIYOR (Fix round 2 —
2479
+ * `companies.annual_revenue` AYNI sınıf hatayı taşıdığı için oraya taşındı,
2480
+ * bkz. o dosyanın JSDoc'u) — bilimsel gösterim (`1e10`), `NaN`/`Infinity`,
2481
+ * çift işaret (`--5`), virgüllü ondalık (`1,5`) ve 2'den fazla ondalık basamak
2482
+ * hep REDDEDİLİR. PARA YİNE string olarak taşınır, `bignumber.js` dışında
2483
+ * hesaplanmaz.
2484
+ */
2485
+ /** ISO 4217 kodu — 3 büyük harf (DB `char(3)` kolonuyla eşleşir) */
2486
+ const currencyCodeSchema = z.string().trim().regex(/^[A-Z]{3}$/, "ISO 4217 biçiminde 3 büyük harf olmalı (örn. TRY)");
2487
+ /** Fırsat tutarı — DB `numeric(18,2)` ile BİREBİR eşleşen ortak biçim kısıtı (bkz. `crm.ts` `numericMoneySchema`) */
2488
+ const dealAmountSchema = numericMoneySchema;
2489
+ const createDealRequestSchema = z.object({
2490
+ title: z.string().trim().min(1, "başlık gerekli").max(200, "en fazla 200 karakter olabilir"),
2491
+ pipelineId: z.uuid("geçerli bir pipeline kimliği olmalı"),
2492
+ stageId: z.uuid("geçerli bir aşama kimliği olmalı"),
2493
+ amount: dealAmountSchema.optional(),
2494
+ currencyCode: currencyCodeSchema.default("TRY"),
2495
+ companyId: z.uuid("geçerli bir şirket kimliği olmalı").optional(),
2496
+ primaryContactId: z.uuid("geçerli bir kişi kimliği olmalı").optional(),
2497
+ expectedCloseDate: z.iso.datetime("geçerli bir tarih olmalı").optional(),
2498
+ ownerUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional()
2499
+ });
2500
+ /**
2501
+ * Genel güncelleme — `stageId`/`pipelineId`/`position`/`outcome`/`closedAt`
2502
+ * BİLEREK YOK (bkz. dosya başı KRİTİK KURAL). `.strict()`: bu alanlardan biri
2503
+ * gönderilirse 400 (bkz. `apps/api/test/deals.test.ts`).
2504
+ */
2505
+ const updateDealRequestSchema = z.object({
2506
+ title: z.string().trim().min(1, "başlık gerekli").max(200, "en fazla 200 karakter olabilir").optional(),
2507
+ amount: dealAmountSchema.optional(),
2508
+ currencyCode: currencyCodeSchema.optional(),
2509
+ companyId: z.uuid("geçerli bir şirket kimliği olmalı").optional(),
2510
+ primaryContactId: z.uuid("geçerli bir kişi kimliği olmalı").optional(),
2511
+ expectedCloseDate: z.iso.datetime("geçerli bir tarih olmalı").optional(),
2512
+ ownerUserId: z.uuid("geçerli bir kullanıcı kimliği olmalı").optional(),
2513
+ lostReason: z.string().trim().max(500, "en fazla 500 karakter olabilir").optional()
2514
+ }).strict();
2515
+ /**
2516
+ * Kanban taşıma kontratı (spec §6.4) — pipeline'ın `reorderRequestSchema`'sıyla
2517
+ * AYNI komşu şekli (`beforeId`/`afterId`), yalnız `stageId` hedefi eklenir.
2518
+ * `pipelineId` YOK: hedef aşamanın pipeline'ı, fırsatın yeni pipeline'ı olarak
2519
+ * KABUL EDİLİR (bkz. routes/deals.ts) — istemcinin ayrıca tutarlı bir
2520
+ * `pipelineId` göndermesini gerektirmeden çapraz-pipeline taşımaya izin verir.
2521
+ */
2522
+ const moveDealRequestSchema = z.object({
2523
+ stageId: z.uuid("geçerli bir aşama kimliği olmalı"),
2524
+ beforeId: z.uuid("geçerli bir kimlik olmalı").optional(),
2525
+ afterId: z.uuid("geçerli bir kimlik olmalı").optional()
2526
+ });
2527
+ z.object({
2528
+ contactId: z.uuid("geçerli bir kişi kimliği olmalı"),
2529
+ role: z.string().trim().max(80, "en fazla 80 karakter olabilir").optional()
2530
+ });
2531
+ const dealContactResponseSchema = z.object({
2532
+ contactId: z.uuid(),
2533
+ role: z.string().nullable(),
2534
+ createdAt: z.iso.datetime()
2535
+ });
2536
+ const dealResponseSchema = z.object({
2537
+ id: z.uuid(),
2538
+ pipelineId: z.uuid(),
2539
+ stageId: z.uuid(),
2540
+ companyId: z.uuid().nullable(),
2541
+ primaryContactId: z.uuid().nullable(),
2542
+ title: z.string(),
2543
+ amount: z.string().nullable(),
2544
+ currencyCode: z.string(),
2545
+ position: z.number(),
2546
+ expectedCloseDate: z.iso.datetime().nullable(),
2547
+ closedAt: z.iso.datetime().nullable(),
2548
+ outcome: dealOutcomeSchema,
2549
+ lostReason: z.string().nullable(),
2550
+ ownerUserId: z.uuid().nullable(),
2551
+ createdAt: z.iso.datetime(),
2552
+ updatedAt: z.iso.datetime()
2553
+ });
2554
+ dealResponseSchema.extend({ contacts: z.array(dealContactResponseSchema) });
2555
+ pagedResponseSchema(dealResponseSchema);
2556
+ z.object({
2557
+ email: emailSchema,
2558
+ role: memberRoleSchema
2559
+ });
2560
+ const inviteStatusSchema = z.enum([
2561
+ "pending",
2562
+ "accepted",
2563
+ "revoked"
2564
+ ]);
2565
+ /** Tenant içi davet listesi satırı — token/hash ASLA yer almaz */
2566
+ const inviteResponseSchema = z.object({
2567
+ id: z.uuid(),
2568
+ email: z.string(),
2569
+ role: memberRoleSchema,
2570
+ status: inviteStatusSchema,
2571
+ expiresAt: z.iso.datetime(),
2572
+ createdAt: z.iso.datetime()
2573
+ });
2574
+ z.object({ invites: z.array(inviteResponseSchema) });
2575
+ z.object({
2576
+ tenantName: z.string(),
2577
+ email: z.string(),
2578
+ role: memberRoleSchema,
2579
+ status: inviteStatusSchema,
2580
+ expiresAt: z.iso.datetime()
2581
+ });
2582
+ z.object({
2583
+ tenantId: z.uuid(),
2584
+ tenantName: z.string(),
2585
+ role: memberRoleSchema
2586
+ });
2587
+ //#endregion
2588
+ //#region packages/contracts/src/legal/company.ts
2589
+ const VENNYX_COMPANY = {
2590
+ legalName: "Vennyx Yazılım Danışmanlık A.Ş.",
2591
+ taxOffice: "Uluçınar Vergi Dairesi",
2592
+ taxNumber: "9241072735",
2593
+ tradeRegistryNo: "40690",
2594
+ tradeRegistryCity: "Gebze",
2595
+ mersisNo: "0924107273500001",
2596
+ address: "Muallimköy Mah. Deniz Cad. No: 143/8 - 1.1.C1 Blok - Zemin Kat - Kapı No: Z01 - 41400 Gebze/Kocaeli",
2597
+ kepAddress: "vennyxyazilim@hs01.kep.tr",
2598
+ phone: "+90 (850) 242 8992",
2599
+ website: "https://solicrm.com",
2600
+ legalEmail: "legal@solicrm.com",
2601
+ privacyEmail: "privacy@solicrm.com"
2602
+ };
2603
+ /** Hukuki belgelerin "Veri Sorumlusu/Sağlayıcı Kimliği" bölümünde tekrar eden paragraf */
2604
+ function companyIdentityParagraph(locale) {
2605
+ const c = VENNYX_COMPANY;
2606
+ if (locale === "tr") return `${c.legalName} · ${c.taxOffice} · VN ${c.taxNumber} · ${c.tradeRegistryCity} Ticaret Sicil No ${c.tradeRegistryNo} · MERSİS ${c.mersisNo} · ${c.address}`;
2607
+ return `${c.legalName} · Tax office: ${c.taxOffice} · Tax No: ${c.taxNumber} · Trade Registry No (${c.tradeRegistryCity}): ${c.tradeRegistryNo} · MERSIS: ${c.mersisNo} · ${c.address}, Türkiye`;
2608
+ }
2609
+ /** İletişim/başvuru bölümünde tekrar eden paragraf */
2610
+ function contactParagraph(locale) {
2611
+ const c = VENNYX_COMPANY;
2612
+ if (locale === "tr") return `KEP: ${c.kepAddress} · Tel: ${c.phone} · Hukuki başvurular: ${c.legalEmail} · Gizlilik başvuruları: ${c.privacyEmail}`;
2613
+ return `Registered e-mail (KEP): ${c.kepAddress} · Phone: ${c.phone} · Legal inquiries: ${c.legalEmail} · Privacy inquiries: ${c.privacyEmail}`;
2614
+ }
2615
+ contactParagraph("tr"), contactParagraph("en");
2616
+ companyIdentityParagraph("tr"), contactParagraph("tr"), companyIdentityParagraph("en"), contactParagraph("en");
2617
+ companyIdentityParagraph("tr"), contactParagraph("tr"), companyIdentityParagraph("en"), contactParagraph("en");
2618
+ companyIdentityParagraph("tr"), contactParagraph("tr"), companyIdentityParagraph("en"), contactParagraph("en");
2619
+ companyIdentityParagraph("tr"), contactParagraph("tr"), companyIdentityParagraph("en"), contactParagraph("en");
2620
+ contactParagraph("tr"), contactParagraph("en");
2621
+ contactParagraph("tr"), contactParagraph("en");
2622
+ contactParagraph("tr"), contactParagraph("en");
2623
+ companyIdentityParagraph("tr"), contactParagraph("tr"), companyIdentityParagraph("en"), contactParagraph("en");
2624
+ //#endregion
2625
+ //#region packages/contracts/src/legal/slugs.ts
2626
+ /** Spec §10'un tam listesi — 9 belge, sıra `/legal` hub'ının kart sırasıdır */
2627
+ const LEGAL_SLUGS = [
2628
+ "terms",
2629
+ "privacy",
2630
+ "cookies",
2631
+ "kvkk",
2632
+ "distance-sales",
2633
+ "refund-policy",
2634
+ "dpa",
2635
+ "subprocessors",
2636
+ "sla"
2637
+ ];
2638
+ //#endregion
2639
+ //#region packages/contracts/src/legal/types.ts
2640
+ /**
2641
+ * `@solicrm/i18n`'in `SupportedLanguage`'ı İLE AYNI kümedir ama BAĞIMSIZ
2642
+ * tanımlanır (contracts, i18n'e bağımlı OLMAMALI — katman ihlali). Senkron
2643
+ * kalması `test/legal-locale-parity.test.ts` ile mekanik olarak zorlanır.
2644
+ */
2645
+ const LEGAL_LOCALES = ["tr", "en"];
2646
+ z.object({ documentSlug: z.enum(LEGAL_SLUGS, "geçerli bir belge olmalı") });
2647
+ const legalAcceptanceResponseSchema = z.object({
2648
+ id: z.uuid(),
2649
+ documentSlug: z.enum(LEGAL_SLUGS),
2650
+ locale: z.enum(LEGAL_LOCALES),
2651
+ effectiveDate: z.string(),
2652
+ acceptedAt: z.iso.datetime(),
2653
+ isCurrentVersion: z.boolean()
2654
+ });
2655
+ z.object({ acceptances: z.array(legalAcceptanceResponseSchema) });
2656
+ //#endregion
2657
+ //#region packages/contracts/src/pipelines.ts
2658
+ /**
2659
+ * Pipeline + aşama (stage) sözleşmeleri (spec §6.2/§6.4).
2660
+ *
2661
+ * KRİTİK KURAL: `probability` (yüzde 0-100) PARA DEĞİLDİR — native `number`
2662
+ * kabul edilir (bkz. spec §6.1/plan Global Constraints #19, DB kolonu
2663
+ * `numeric(5,2)`). `pipelineStageColorSchema`, `common.ts`'teki `hexColorSchema`'yı
2664
+ * YENİDEN KULLANIR — spec renk formatı vermiyor, yeni bir renk kataloğu
2665
+ * İCAT EDİLMEDİ.
2666
+ */
2667
+ const pipelineStageColorSchema = hexColorSchema;
2668
+ z.object({
2669
+ name: z.string().trim().min(1, "ad gerekli").max(120, "en fazla 120 karakter olabilir"),
2670
+ isDefault: z.boolean().optional()
2671
+ }).partial();
2672
+ const pipelineResponseSchema = z.object({
2673
+ id: z.uuid(),
2674
+ name: z.string(),
2675
+ isDefault: z.boolean(),
2676
+ position: z.number(),
2677
+ createdAt: z.iso.datetime(),
2678
+ updatedAt: z.iso.datetime()
2679
+ });
2680
+ z.object({ pipelines: z.array(pipelineResponseSchema) });
2681
+ /**
2682
+ * `key`, programatik eşleştirme için kullanılır (ör. `is_won`/`is_lost`
2683
+ * kanban kolonunu bulmak) — bu yüzden slug benzeri kısıtlı bir biçim taşır
2684
+ * (küçük harf, rakam, alt çizgi).
2685
+ */
2686
+ const stageKeySchema = z.string().trim().min(1, "anahtar gerekli").max(60, "en fazla 60 karakter olabilir").regex(/^[a-z][a-z0-9_]*$/, "küçük harf, rakam ve alt çizgi kullanılabilir");
2687
+ z.object({
2688
+ key: stageKeySchema,
2689
+ label: z.string().trim().min(1, "etiket gerekli").max(120, "en fazla 120 karakter olabilir"),
2690
+ color: pipelineStageColorSchema.optional(),
2691
+ probability: z.number().min(0, "en az 0 olmalı").max(100, "en fazla 100 olmalı").optional(),
2692
+ isWon: z.boolean().optional(),
2693
+ isLost: z.boolean().optional()
2694
+ }).partial();
2695
+ const stageResponseSchema = z.object({
2696
+ id: z.uuid(),
2697
+ pipelineId: z.uuid(),
2698
+ key: z.string(),
2699
+ label: z.string(),
2700
+ color: z.string().nullable(),
2701
+ position: z.number(),
2702
+ probability: z.string().nullable(),
2703
+ isWon: z.boolean(),
2704
+ isLost: z.boolean(),
2705
+ createdAt: z.iso.datetime(),
2706
+ updatedAt: z.iso.datetime()
2707
+ });
2708
+ z.object({ stages: z.array(stageResponseSchema) });
2709
+ z.object({
2710
+ beforeId: z.uuid("geçerli bir kimlik olmalı").optional(),
2711
+ afterId: z.uuid("geçerli bir kimlik olmalı").optional()
2712
+ });
2713
+ //#endregion
2714
+ //#region packages/contracts/src/platform.ts
2715
+ /**
2716
+ * Platform yönetimi sözleşmeleri (spec §5.8) — yalnız Vennyx personeli
2717
+ * (`SOLICRM_ADMIN_EMAILS` allowlist'i, bkz. `apps/api/src/lib/platform-admin.ts`)
2718
+ * tarafından tüketilir.
2719
+ */
2720
+ const platformTenantSummarySchema = z.object({
2721
+ id: z.uuid(),
2722
+ name: z.string(),
2723
+ slug: z.string(),
2724
+ planCode: planCodeSchema,
2725
+ suspendedAt: z.iso.datetime().nullable(),
2726
+ createdAt: z.iso.datetime(),
2727
+ memberCount: z.number().int().nonnegative()
2728
+ });
2729
+ z.object({ tenants: z.array(platformTenantSummarySchema) });
2730
+ z.object({
2731
+ memberCount: z.number().int().nonnegative(),
2732
+ activeApiKeyCount: z.number().int().nonnegative(),
2733
+ pendingInviteCount: z.number().int().nonnegative(),
2734
+ ssoEnabled: z.boolean()
2735
+ });
2736
+ z.object({ email: z.string() });
2737
+ /**
2738
+ * `GET /v1/admin/tenants/:id/audit-logs` satırı (Faz 9 Task 2, plan kararı "E").
2739
+ *
2740
+ * `data` NEDEN `z.unknown()`: `audit_logs.data` şemasız bir jsonb kolonudur —
2741
+ * her `writeAudit()` çağrısı kendi eylemine özgü bir şekil yazar (18 dosyada 55
2742
+ * çağrı), ortak bir yapı YOKTUR. Burada bir şekil DAYATMAK, bugün geçerli bir
2743
+ * denetim satırını yarın şema hatasıyla GÖRÜNMEZ kılardı — denetim kaydı
2744
+ * append-only olduğu için o satır bir daha DÜZELTİLEMEZ. Zod v4'te `z.unknown()`
2745
+ * anahtarın VARLIĞINI yine zorunlu kılar (ölçüldü: `{}` girdisi `success:false`),
2746
+ * yani "alan hiç gelmedi" ile "alan null" ayrımı KORUNUR.
2747
+ *
2748
+ * KRİTİK — `data` NORMALİZE EDİLMİŞ şekli tanımlar: ham `tx.execute()` yolu bu
2749
+ * kolonu jsonb çift-encode kusuru nedeniyle STRING olarak döndürebilir; route
2750
+ * katmanı tek bir `JSON.parse` ile bunu geri alır (bkz.
2751
+ * `apps/api/src/routes/platform-audit.ts`).
2752
+ */
2753
+ const adminAuditLogEntrySchema = z.object({
2754
+ id: z.uuid(),
2755
+ actorEmail: z.string().nullable(),
2756
+ action: z.string(),
2757
+ target: z.string().nullable(),
2758
+ data: z.unknown().nullable(),
2759
+ createdAt: z.iso.datetime()
2760
+ });
2761
+ z.object({
2762
+ items: z.array(adminAuditLogEntrySchema),
2763
+ nextCursor: z.string().nullable(),
2764
+ hasMore: z.boolean()
2765
+ });
2766
+ /**
2767
+ * Zehirli/çözülmemiş webhook olayı satırı — `GET /v1/admin/webhook-events`
2768
+ * (Faz 7 Task 6'nın ürettiği uç, Faz 9 Task 3 tüketicisi).
2769
+ *
2770
+ * KRİTİK — `payload` BİLİNÇLİ OLARAK YOK: `webhook_events.payload` Whop'un ham
2771
+ * gövdesidir (müşteri/ödeme bilgisi taşır, `safeJsonb` kolonu). Bu sözleşme onu
2772
+ * TAŞIMAZ ve route katmanı kolonu SELECT bile ETMEZ — operatör ekranı olayı
2773
+ * TANIMLAMAYA yeten alanlarla sınırlıdır (kaynak/tür/zaman/hata). Ham gövdeye
2774
+ * ihtiyaç duyulursa doğru yer DB erişimidir, bir HTTP yanıtı değil.
2775
+ *
2776
+ * `error` SERBEST METİNDİR ve route katmanında hassas sayı dizileri (VKN/TCKN/
2777
+ * kart benzeri) MASKELENMİŞ olarak gelir — bkz. `apps/api/src/lib/mask-sensitive.ts`.
2778
+ *
2779
+ * İKİ "çözülmemiş" sınıfı bu üç alandan TÜRETİLİR (bkz. `lib/webhook-health.ts`):
2780
+ * `error !== null` ⇒ iş kuralı hatası; `error === null && processedAt === null`
2781
+ * ⇒ hiç işlenemedi/sıkıştı.
2782
+ */
2783
+ const adminWebhookEventSchema = z.object({
2784
+ id: z.uuid(),
2785
+ source: z.string(),
2786
+ eventType: z.string(),
2787
+ receivedAt: z.iso.datetime(),
2788
+ processedAt: z.iso.datetime().nullable(),
2789
+ error: z.string().nullable()
2790
+ });
2791
+ z.object({ items: z.array(adminWebhookEventSchema) });
2792
+ z.object({ reason: z.string().trim().min(1, "kapatma gerekçesi gerekli").max(1e3, "en fazla 1000 karakter olabilir") });
2793
+ z.object({
2794
+ id: z.uuid(),
2795
+ closedAt: z.iso.datetime(),
2796
+ closedReason: z.string(),
2797
+ closedBy: z.string()
2798
+ });
2799
+ z.object({
2800
+ effective: z.boolean(),
2801
+ source: z.enum(["platform-setting", "env-fallback"]),
2802
+ envFallback: z.boolean(),
2803
+ updatedAt: z.iso.datetime().nullable(),
2804
+ updatedByEmail: z.string().nullable()
2805
+ });
2806
+ z.object({ enabled: z.boolean("etkin değeri true veya false olmalı") });
2807
+ z.object({
2808
+ paymentId: z.uuid(),
2809
+ tenantId: z.uuid(),
2810
+ tenantName: z.string(),
2811
+ whopPaymentId: z.string(),
2812
+ paymentStatus: z.string(),
2813
+ /** `numeric(18,2)` — API'de DAİMA dizge (kesinlik korunur, bkz. CLAUDE.md) */
2814
+ amountGross: z.string(),
2815
+ refundedAmount: z.string(),
2816
+ currencyCode: z.string(),
2817
+ /** `'passed' | 'out_of_band' | 'not_verifiable'` — bant dışı bir ödemeyi iade etmek ÖZEL dikkat ister */
2818
+ amountCheckStatus: z.string(),
2819
+ paidAt: z.iso.datetime().nullable(),
2820
+ invoice: z.object({
2821
+ id: z.uuid(),
2822
+ invoiceNumber: z.string(),
2823
+ status: z.string(),
2824
+ amountGross: z.string(),
2825
+ currencyCode: z.string(),
2826
+ formalizedAt: z.iso.datetime().nullable()
2827
+ }).nullable(),
2828
+ billing: z.object({
2829
+ kind: z.string(),
2830
+ country: z.string(),
2831
+ maskedTaxId: z.string().nullable()
2832
+ }).nullable(),
2833
+ openRefund: z.object({
2834
+ id: z.uuid(),
2835
+ status: z.string(),
2836
+ amountGross: z.string(),
2837
+ attemptCount: z.number().int().nonnegative(),
2838
+ lastAttemptAt: z.iso.datetime().nullable()
2839
+ }).nullable()
2840
+ });
2841
+ z.object({
2842
+ refundId: z.uuid(),
2843
+ status: z.enum([
2844
+ "pending",
2845
+ "completed",
2846
+ "awaiting_balance",
2847
+ "failed"
2848
+ ]),
2849
+ message: z.string()
2850
+ });
2851
+ const filterLeafSchema = z.object({
2852
+ fieldKey: z.string().trim().min(1, "alan adı gerekli").max(80, "en fazla 80 karakter olabilir"),
2853
+ operand: filterOperandSchema,
2854
+ value: z.unknown()
2855
+ });
2856
+ const filterGroupSchema = z.lazy(() => z.object({
2857
+ logicalOperator: filterLogicalOperatorSchema,
2858
+ filters: z.array(filterLeafSchema).max(20, "bir grupta en fazla 20 filtre olabilir"),
2859
+ groups: z.array(filterGroupSchema).max(5, "bir grupta en fazla 5 alt grup olabilir")
2860
+ }));
2861
+ /** Bir filtre grubu ağacının GERÇEK azami derinliğini ölçer (bkz. dosya başı KRİTİK KURAL) */
2862
+ function filterGroupDepth(group) {
2863
+ if (group.groups.length === 0) return 1;
2864
+ return 1 + Math.max(...group.groups.map(filterGroupDepth));
2865
+ }
2866
+ const fieldSelectionSchema = z.object({
2867
+ fieldKey: z.string().trim().min(1, "alan adı gerekli").max(80, "en fazla 80 karakter olabilir"),
2868
+ isVisible: z.boolean().default(true)
2869
+ });
2870
+ const sortSelectionSchema = z.object({
2871
+ fieldKey: z.string().trim().min(1, "alan adı gerekli").max(80, "en fazla 80 karakter olabilir"),
2872
+ direction: sortDirectionSchema
2873
+ });
2874
+ /** Derinlik aşımını yakalayan paylaşılan `superRefine` — create/update ŞEMASI ikisi de kullanır */
2875
+ function checkFilterGroupDepth(rootFilterGroup, ctx) {
2876
+ if (rootFilterGroup === void 0) return;
2877
+ if (filterGroupDepth(rootFilterGroup) > 5) ctx.addIssue({
2878
+ code: "custom",
2879
+ path: ["rootFilterGroup"],
2880
+ message: `filtre grubu en fazla 5 seviye iç içe olabilir`
2881
+ });
2882
+ }
2883
+ z.object({
2884
+ name: z.string().trim().min(1, "ad gerekli").max(120, "en fazla 120 karakter olabilir"),
2885
+ icon: z.string().trim().max(60, "en fazla 60 karakter olabilir").optional(),
2886
+ resource: savedViewResourceSchema,
2887
+ layout: savedViewLayoutSchema.default("table"),
2888
+ visibility: savedViewVisibilitySchema.default("private"),
2889
+ groupByFieldKey: z.string().trim().min(1, "alan adı gerekli").max(80, "en fazla 80 karakter olabilir").optional(),
2890
+ isDefault: z.boolean().optional(),
2891
+ fields: z.array(fieldSelectionSchema).max(60, "en fazla 60 kolon olabilir").default([]),
2892
+ sorts: z.array(sortSelectionSchema).max(5, "en fazla 5 sıralama alanı olabilir").default([]),
2893
+ rootFilterGroup: filterGroupSchema.optional()
2894
+ }).superRefine((data, ctx) => checkFilterGroupDepth(data.rootFilterGroup, ctx));
2895
+ z.object({
2896
+ name: z.string().trim().min(1, "ad gerekli").max(120, "en fazla 120 karakter olabilir").optional(),
2897
+ icon: z.string().trim().max(60, "en fazla 60 karakter olabilir").nullable().optional(),
2898
+ layout: savedViewLayoutSchema.optional(),
2899
+ visibility: savedViewVisibilitySchema.optional(),
2900
+ groupByFieldKey: z.string().trim().min(1, "alan adı gerekli").max(80, "en fazla 80 karakter olabilir").nullable().optional(),
2901
+ isDefault: z.boolean().optional(),
2902
+ fields: z.array(fieldSelectionSchema).max(60, "en fazla 60 kolon olabilir").optional(),
2903
+ sorts: z.array(sortSelectionSchema).max(5, "en fazla 5 sıralama alanı olabilir").optional(),
2904
+ rootFilterGroup: filterGroupSchema.nullable().optional()
2905
+ }).strict().superRefine((data, ctx) => {
2906
+ if (data.rootFilterGroup !== null) checkFilterGroupDepth(data.rootFilterGroup, ctx);
2907
+ });
2908
+ const savedViewFieldResponseSchema = z.object({
2909
+ fieldKey: z.string(),
2910
+ isVisible: z.boolean()
2911
+ });
2912
+ const savedViewSortResponseSchema = z.object({
2913
+ fieldKey: z.string(),
2914
+ direction: sortDirectionSchema
2915
+ });
2916
+ z.object({
2917
+ id: z.uuid(),
2918
+ name: z.string(),
2919
+ icon: z.string().nullable(),
2920
+ resource: savedViewResourceSchema,
2921
+ layout: savedViewLayoutSchema,
2922
+ /** Sidebar sırası — `pipelineResponseSchema`/`stageResponseSchema` ile TUTARLILIK için döndürülür (bu fazda bir /reorder ucu YOK, yalnız append-only, bkz. routes/saved-views.ts `nextViewPosition`) */
2923
+ position: z.number(),
2924
+ visibility: savedViewVisibilitySchema,
2925
+ createdById: z.uuid(),
2926
+ groupByFieldKey: z.string().nullable(),
2927
+ isDefault: z.boolean(),
2928
+ fields: z.array(savedViewFieldResponseSchema),
2929
+ sorts: z.array(savedViewSortResponseSchema),
2930
+ rootFilterGroup: filterGroupSchema.nullable(),
2931
+ createdAt: z.iso.datetime(),
2932
+ updatedAt: z.iso.datetime()
2933
+ });
2934
+ /**
2935
+ * Liste ucu (`GET /saved-views`) YANITI — özet şekil, `fields`/`sorts`/
2936
+ * `rootFilterGroup` İÇERMEZ (bkz. `apps/api/src/routes/saved-views.ts`
2937
+ * KRİTİK KURAL — N+1 önleme: bir tenant'ın TÜM görünümlerini listelerken
2938
+ * her biri için 4 ek sorgu (fields/sorts/groups/filters) atmak N+1 olurdu;
2939
+ * tam detay yalnız TEKİL görünüm uçlarında (`GET .../saved-views/:viewId`,
2940
+ * `POST .../apply`) döner — o uçlarda "N" zaten 1'dir).
2941
+ */
2942
+ const savedViewSummaryResponseSchema = z.object({
2943
+ id: z.uuid(),
2944
+ name: z.string(),
2945
+ icon: z.string().nullable(),
2946
+ resource: savedViewResourceSchema,
2947
+ layout: savedViewLayoutSchema,
2948
+ position: z.number(),
2949
+ visibility: savedViewVisibilitySchema,
2950
+ createdById: z.uuid(),
2951
+ isDefault: z.boolean(),
2952
+ createdAt: z.iso.datetime(),
2953
+ updatedAt: z.iso.datetime()
2954
+ });
2955
+ z.object({ views: z.array(savedViewSummaryResponseSchema) });
2956
+ const searchableResourceSchema = z.enum([
2957
+ "contacts",
2958
+ "companies",
2959
+ "deals"
2960
+ ]);
2961
+ const searchResultItemSchema = z.object({
2962
+ id: z.uuid("geçersiz kimlik"),
2963
+ resource: searchableResourceSchema,
2964
+ /** Kaynağın görünen adı — contacts: ad soyad, companies: unvan, deals: başlık */
2965
+ title: z.string(),
2966
+ /** İkincil satır (e-posta / alan adı / tutar) — kolon boşsa `null` */
2967
+ snippet: z.string().nullable()
2968
+ });
2969
+ z.object({ items: z.array(searchResultItemSchema) });
2970
+ const searchQuerySchema = z.object({
2971
+ q: z.string().trim().min(1, "arama metni boş olamaz").max(200, `en fazla 200 karakter olabilir`),
2972
+ limit: z.coerce.number("sayı olmalı").int("tam sayı olmalı").min(1, "en az 1 olmalı").max(50, `en fazla 50 olabilir`).optional(),
2973
+ /**
2974
+ * Aranacak kaynakları daraltır (virgülle ayrılmış). Verilmezse ÜÇÜ de
2975
+ * aranır. Bilinmeyen bir kaynak adı 400 üretir — sessizce yok SAYILMAZ
2976
+ * (fail-closed: "tanımadığım filtreyi görmezden gel" bir fail-open kalıbıdır).
2977
+ */
2978
+ resources: z.string().transform((raw) => raw.split(",").map((part) => part.trim())).pipe(z.array(searchableResourceSchema).min(1, "en az bir kaynak belirtilmeli")).optional()
2979
+ });
2980
+ //#endregion
2981
+ //#region packages/contracts/src/sso.ts
2982
+ /**
2983
+ * BYO-SSO tenant bağlantısı sözleşmeleri (spec §5.7).
2984
+ *
2985
+ * KRİTİK KURAL: `ssoConnectionResponseSchema` `clientSecret`/`clientSecretEncrypted`
2986
+ * ALANI TAŞIMAZ — sır listeleme/okuma yanıtlarında ASLA dönmez.
2987
+ */
2988
+ const DOMAIN_RE = /^(?!-)[a-z0-9-]{1,63}(?<!-)(\.[a-z0-9-]{1,63})+$/;
2989
+ /**
2990
+ * Genel (kişisel) e-posta sağlayıcı alan adları — bunlar SSO `emailDomain`
2991
+ * olarak KABUL EDİLMEZ (spec'in "gmail.com gibi genel bir alan adını
2992
+ * doğrulamaya çalışırsa?" tehdidi — bir tenant bu alan adını doğrulayabilseydi
2993
+ * TÜM Gmail kullanıcılarını kendi tenant'ına çekebilirdi). TAM/kapsayıcı bir
2994
+ * liste DEĞİLDİR — ileride genişletilebilir bir başlangıç kümesidir (bkz.
2995
+ * plan "Belirsiz bırakılan noktalar" #4).
2996
+ *
2997
+ * Plandaki temel liste (gmail/outlook/yahoo/icloud ailesi ve büyük bölgesel
2998
+ * sağlayıcılar) OLDUĞU GİBİ alındı. Task brifinginin "eksik görürsen genişlet"
2999
+ * talimatıyla implementasyon anında GENUINE olarak eklenenler (regex her biri
3000
+ * AYRI bir alan adı olduğu için kök alan adı listede olsa da eşleşmez):
3001
+ * Microsoft'un bölgesel tüketici alan adları (`outlook.co.uk`, `hotmail.co.uk`,
3002
+ * `live.co.uk`, `hotmail.fr`, `live.fr`, `hotmail.de`, `live.de` — `outlook.com`/
3003
+ * `hotmail.com`/`live.com` zaten listede ama bunlar ayrı TLD'ler) ve büyük ISP
3004
+ * webmail'leri (kurumsal alan adı OLMASI beklenmeyen, geniş bireysel kullanıcı
3005
+ * tabanına sahip): `comcast.net`/`att.net`/`verizon.net`/`sbcglobal.net` (ABD),
3006
+ * `btinternet.com` (İngiltere), `free.fr` (Fransa).
3007
+ */
3008
+ const PUBLIC_EMAIL_DOMAINS = /* @__PURE__ */ new Set([
3009
+ "gmail.com",
3010
+ "googlemail.com",
3011
+ "outlook.com",
3012
+ "hotmail.com",
3013
+ "live.com",
3014
+ "msn.com",
3015
+ "yahoo.com",
3016
+ "yahoo.co.uk",
3017
+ "yahoo.fr",
3018
+ "ymail.com",
3019
+ "icloud.com",
3020
+ "me.com",
3021
+ "mac.com",
3022
+ "aol.com",
3023
+ "protonmail.com",
3024
+ "proton.me",
3025
+ "pm.me",
3026
+ "gmx.com",
3027
+ "gmx.de",
3028
+ "gmx.net",
3029
+ "mail.com",
3030
+ "yandex.com",
3031
+ "yandex.ru",
3032
+ "zoho.com",
3033
+ "fastmail.com",
3034
+ "hey.com",
3035
+ "hushmail.com",
3036
+ "tutanota.com",
3037
+ "tuta.io",
3038
+ "inbox.com",
3039
+ "mail.ru",
3040
+ "qq.com",
3041
+ "163.com",
3042
+ "126.com",
3043
+ "naver.com",
3044
+ "daum.net",
3045
+ "web.de",
3046
+ "t-online.de",
3047
+ "orange.fr",
3048
+ "laposte.net",
3049
+ "libero.it",
3050
+ "virgilio.it",
3051
+ "seznam.cz",
3052
+ "wp.pl",
3053
+ "onet.pl",
3054
+ "rocketmail.com",
3055
+ "outlook.co.uk",
3056
+ "hotmail.co.uk",
3057
+ "live.co.uk",
3058
+ "hotmail.fr",
3059
+ "live.fr",
3060
+ "hotmail.de",
3061
+ "live.de",
3062
+ "comcast.net",
3063
+ "att.net",
3064
+ "verizon.net",
3065
+ "sbcglobal.net",
3066
+ "btinternet.com",
3067
+ "free.fr"
3068
+ ]);
3069
+ /**
3070
+ * [Fix round 1 — Minor 1, code review bulgusu]: `PUBLIC_EMAIL_DOMAINS.has(value)`
3071
+ * yalnız TAM eşleşme kontrol ediyordu — `mail.gmail.com` gibi bir ALT alan adı
3072
+ * engeli atlıyordu (canlı doğrulandı: `ACCEPTED` dönüyordu). Pratikte risk
3073
+ * düşük (saldırgan gerçek sağlayıcının alt alan DNS'ini kontrol edemez, TXT
3074
+ * sahiplik kanıtı hâlâ gerekir) ama kafa karıştırıcı/gereksiz bir açık —
3075
+ * engellenen bir kök alan adının HER alt alan adı da (`x.gmail.com`,
3076
+ * `a.b.gmail.com` vb.) reddedilir. `value` zaten `.toLowerCase()`'dan SONRA
3077
+ * buraya gelir (zincirleme sırası), bu yüzden büyük/küçük harf normalize
3078
+ * edilmiş durumda.
3079
+ */
3080
+ function isPublicEmailProviderDomain(domain) {
3081
+ if (PUBLIC_EMAIL_DOMAINS.has(domain)) return true;
3082
+ for (const blocked of PUBLIC_EMAIL_DOMAINS) if (domain.endsWith(`.${blocked}`)) return true;
3083
+ return false;
3084
+ }
3085
+ const ssoEmailDomainSchema = z.string().trim().toLowerCase().max(255, "en fazla 255 karakter olabilir").regex(DOMAIN_RE, "geçerli bir alan adı olmalı (örn. ornek.com)").refine((value) => !isPublicEmailProviderDomain(value), "genel bir e-posta sağlayıcı alan adı SSO için kullanılamaz");
3086
+ /**
3087
+ * Format kontrolü — prod'da yalnız https zorunluluğu ROUTE katmanında ayrıca
3088
+ * uygulanır (bkz. sso-admin.ts, `NODE_ENV==='production'` kontrolü).
3089
+ *
3090
+ * [Fix round 1 — Minor 2, code review bulgusu]: bu şema ÖNCEDEN
3091
+ * `http://127.0.0.1:PORT`'u da (localhost ile AYNI) dev istisnasına
3092
+ * sokuyordu, ama `lib/ssrf-guard.ts`'in `assertPublicHttpsUrl`'i yalnız
3093
+ * `localhost` HOST ADINI dev-bypass eder — çıplak `127.0.0.1` IP'sini HER
3094
+ * ZAMAN reddeder (bkz. o dosyanın KRİTİK KURAL 1 yorumu: kritik güvenlik
3095
+ * kuralı "özel IP aralıklarına (…, 127/8, …) gitmemeli" der, istisna YALNIZ
3096
+ * `localhost` için tanımlıdır). Sonuç: `http://127.0.0.1:9000` issuer'ıyla
3097
+ * bağlantı OLUŞTURULABİLİYORDU (bu şema kabul ediyordu) ama `/test` ucu HER
3098
+ * ZAMAN başarısız oluyordu (guard reddediyordu) — güvenlik açığı değil ama
3099
+ * kafa karıştırıcı bir tutarsızlık.
3100
+ *
3101
+ * ÇÖZÜM YÖNÜ — şema DARALTILDI (guard GENİŞLETİLMEDİ): guard'ın davranışı
3102
+ * kritik güvenlik kuralının kelimesi kelimesine yazdığı, implementasyon
3103
+ * anında canlı test edilip reviewer tarafından bağımsız doğrulanmış bir
3104
+ * karardı (bkz. Task 1 raporu) — bunu GEVŞETMEK yerine şemayı guard'a
3105
+ * UYDURMAK, sistemin her yerinde AYNI (daha sıkı) kuralı korur. Artık yalnız
3106
+ * `http://localhost` (port'lu/portsuz) dev istisnası kabul edilir; çıplak
3107
+ * `127.0.0.1` (ve `::1`) issuer olarak asla kabul edilmez — bu ikisi zaten
3108
+ * `ssrf-guard.ts`'te de reddediliyordu.
3109
+ */
3110
+ const ssoIssuerSchema = z.url("geçerli bir URL olmalı").max(500, "en fazla 500 karakter olabilir").refine((value) => value.startsWith("https://") || /^http:\/\/localhost(:\d+)?(\/|$)/.test(value), "issuer https olmalı (yalnız geliştirmede localhost istisnası var)");
3111
+ z.object({
3112
+ issuer: ssoIssuerSchema,
3113
+ clientId: z.string().trim().min(1, "clientId gerekli").max(255, "en fazla 255 karakter olabilir"),
3114
+ clientSecret: z.string().trim().min(1, "clientSecret gerekli").max(2e3, "en fazla 2000 karakter olabilir"),
3115
+ emailDomain: ssoEmailDomainSchema
3116
+ });
3117
+ z.object({
3118
+ id: z.uuid(),
3119
+ issuer: z.string(),
3120
+ clientId: z.string(),
3121
+ emailDomain: z.string(),
3122
+ domainVerificationToken: z.string(),
3123
+ domainVerified: z.boolean(),
3124
+ enabled: z.boolean(),
3125
+ createdAt: z.iso.datetime(),
3126
+ updatedAt: z.iso.datetime()
3127
+ });
3128
+ z.object({
3129
+ ok: z.boolean(),
3130
+ reason: z.string().optional()
3131
+ });
3132
+ z.object({
3133
+ code: z.literal("integration_requires_plan"),
3134
+ requiredPlans: z.array(z.enum(["team", "enterprise"]))
3135
+ });
3136
+ z.object({
3137
+ ssoAvailable: z.boolean(),
3138
+ tenantId: z.uuid().optional(),
3139
+ tenantName: z.string().optional()
3140
+ });
3141
+ z.object({ code: z.string().min(1, "code gerekli").max(200, "en fazla 200 karakter olabilir") });
3142
+ //#endregion
3143
+ //#region packages/mcp/src/config.ts
3144
+ /**
3145
+ * `npx @vennyx/solicrm-mcp`'nin ortam değişkeninden yapılandırma okuması.
3146
+ *
3147
+ * KRİTİK KURAL 1 — ZORUNLU DEĞER YOKSA AÇIK HATA, VARSAYILANA DÜŞÜLMEZ.
3148
+ * `SOLICRM_API_KEY` ve `SOLICRM_TENANT_ID` ikisi de ZORUNLUDUR ve eksikse
3149
+ * süreç net bir Türkçe mesajla ölür. Sessiz bir varsayılan (`''`, `'default'`)
3150
+ * ilk araç çağrısında anlaşılmaz bir 401/404'e dönüşürdü — bu projenin beş
3151
+ * ayrı yerden çıkmış kusur sınıfı (bkz. kök `CLAUDE.md` "Sessiz kusurlar").
3152
+ *
3153
+ * KRİTİK KURAL 2 — `SOLICRM_TENANT_ID` NEDEN GEREKLİ. SoliCRM'in URL şeması
3154
+ * `/v1/tenants/:tenantId/...`'dir (bkz. `@vennyx/solicrm`'in
3155
+ * `TenantScopedResource`) ve API anahtarının hangi tenant'a bağlı olduğunu
3156
+ * SÖYLEYEN bir uç YOK — anahtarı çözen tek yer sunucunun kendi
3157
+ * `app_resolve_api_key`'idir ve o HTTP'ye açık değildir. Yani istemci URL'i
3158
+ * kuramaz. İleride "anahtarımın tenant'ı ne" ucu eklenirse bu env
3159
+ * GEREKSİZLEŞİR (barındırılan `POST /mcp` ucu zaten tenant'ı anahtardan
3160
+ * çözdüğü için ondan hiçbir env istemez).
3161
+ *
3162
+ * KRİTİK KURAL 3 — `SOLICRM_SCOPE` BİR GÜVENLİK KAPISI DEĞİLDİR, UX
3163
+ * FİLTRESİDİR. Bu değer `tools/list`'in hangi araçları göstereceğini belirler
3164
+ * (bkz. `scope.ts` KRİTİK KURAL 3a: "LLM göremediği aracı denemez"). GERÇEK
3165
+ * yetki sınırı sunucudadır ve HER istekte yeniden hesaplanır
3166
+ * (`ROLE_SCOPE_CEILING[güncel rol] ∩ anahtar kapsamı`,
3167
+ * `apps/api/src/lib/crm-access.ts`). Bu yüzden VERİLMEZSE `admin`'e düşer:
3168
+ * tüm araçlar kaydedilir, yetkisiz olanı sunucu 403'le reddeder. `read`'e
3169
+ * düşmek daha "güvenli" GÖRÜNÜR ama değildir — hiçbir şeyi kapatmaz, yalnız
3170
+ * yazma yetkisi OLAN bir anahtarın yazma araçlarını görünmez yapıp
3171
+ * kullanıcının teşhis edemeyeceği bir arıza üretir. VERİLİRSE ve geçersizse
3172
+ * (`Admin`, `full`, ...) SESSİZCE varsayılana düşülMEZ, açık hata verilir.
3173
+ */
3174
+ /** Bkz. KRİTİK KURAL 3 — kapsam verilmediğinde sunucuyu yetkili taraf sayarız */
3175
+ const DEFAULT_MCP_SCOPE = "admin";
3176
+ function readTrimmed(env, name) {
3177
+ const raw = env[name];
3178
+ if (raw === void 0) return void 0;
3179
+ const trimmed = raw.trim();
3180
+ return trimmed.length === 0 ? void 0 : trimmed;
3181
+ }
3182
+ function loadConfigFromEnv(env) {
3183
+ const apiKey = readTrimmed(env, "SOLICRM_API_KEY");
3184
+ if (apiKey === void 0) throw new Error("SOLICRM_API_KEY ortam değişkeni zorunludur (dashboard → API anahtarları bölümünden oluşturun).");
3185
+ const tenantId = readTrimmed(env, "SOLICRM_TENANT_ID");
3186
+ if (tenantId === void 0) throw new Error("SOLICRM_TENANT_ID ortam değişkeni zorunludur (dashboard → kiracı ayarlarındaki kiracı kimliği).");
3187
+ const rawScope = readTrimmed(env, "SOLICRM_SCOPE");
3188
+ let scope = DEFAULT_MCP_SCOPE;
3189
+ if (rawScope !== void 0) {
3190
+ const parsed = apiKeyScopeSchema.safeParse(rawScope);
3191
+ if (!parsed.success) throw new Error(`SOLICRM_SCOPE geçersiz: "${rawScope}". Geçerli değerler: read, write, admin (boş bırakılırsa ${DEFAULT_MCP_SCOPE}).`);
3192
+ scope = parsed.data;
3193
+ }
3194
+ return {
3195
+ apiKey,
3196
+ tenantId,
3197
+ baseUrl: readTrimmed(env, "SOLICRM_BASE_URL"),
3198
+ scope
3199
+ };
3200
+ }
3201
+ //#endregion
3202
+ //#region packages/mcp/src/prompts.ts
3203
+ const MCP_PROMPT_NAMES = ["summarize_pipeline"];
3204
+ function registerSummarizePipelinePrompt(server) {
3205
+ return server.registerPrompt("summarize_pipeline", {
3206
+ title: "Pipeline özeti",
3207
+ description: "Guides an agent through summarising the health of one sales pipeline using the SoliCRM tools.",
3208
+ argsSchema: { pipelineName: z.string().describe("Name of the pipeline to summarise, as shown by list_pipelines") }
3209
+ }, ({ pipelineName }) => ({ messages: [{
3210
+ role: "user",
3211
+ content: {
3212
+ type: "text",
3213
+ text: [
3214
+ `Summarise the health of the SoliCRM pipeline named "${pipelineName}".`,
3215
+ "1. Call list_pipelines and find the pipeline whose name matches; note its id and the ordered stages.",
3216
+ "2. Call list_deals with filters { pipeline_id: \"<that id>\" } and page through with the returned nextCursor until hasMore is false.",
3217
+ "3. Group the deals by stage. For each stage report the count, the summed amount and the oldest deal.",
3218
+ "4. Amounts are decimal STRINGS (numeric(18,2)) — add them with exact decimal arithmetic, never with floating-point math, and keep the currency code with every total.",
3219
+ "5. Close with the two or three stages that look most at risk (largest value sitting in the earliest stages, or deals with the oldest expectedCloseDate)."
3220
+ ].join("\n")
3221
+ }
3222
+ }] }));
3223
+ }
3224
+ //#endregion
3225
+ //#region packages/mcp/src/errors.ts
3226
+ /**
3227
+ * MCP katmanının KENDİ hata sınıfları — sunucudan GELMEYEN, bu paketin
3228
+ * ürettiği başarısızlıklar.
3229
+ *
3230
+ * DİL İSTİSNASI (Global Constraints #8): buradaki mesajlar İngilizce'dir.
3231
+ * "Kullanıcıya dönen mesajlar Türkçe" kuralı `apps/api`'nin `jsonError()`
3232
+ * zarfı içindir; bu paket global npm tüketicilerine (Claude Code/Desktop) gider
3233
+ * ve mesajları bir LLM okur. Sunucudan gelen TÜRKÇE metin ise ÇEVRİLMEZ,
3234
+ * `toMcpToolError` onu AYNEN taşır (bkz. `result.ts`).
3235
+ */
3236
+ /** Bu paketin ürettiği her hatanın taban sınıfı — tüketici tek `catch` ile kapsar. */
3237
+ var SolicrmMcpError = class extends Error {
3238
+ constructor(message) {
3239
+ super(message);
3240
+ this.name = "SolicrmMcpError";
3241
+ Object.setPrototypeOf(this, new.target.prototype);
3242
+ }
3243
+ };
3244
+ /**
3245
+ * Çağıranın etkin kapsamı aracın gerektirdiği minimumun ALTINDA.
3246
+ *
3247
+ * KRİTİK KURAL — bu hata TEK yetki sınırı DEĞİLDİR, İKİNCİ katmandır. Gerçek
3248
+ * yetki kontrolü sunucu tarafındadır: her araç çağrısı SDK üzerinden
3249
+ * `/v1/tenants/:id/...` uçlarına gider ve orada
3250
+ * `checkApiKeyTenantAndScope` (`apps/api/src/lib/crm-access.ts`) çalışır — o
3251
+ * kontrol `effectiveScope`'u (= `ROLE_SCOPE_CEILING[güncel rol] ∩ anahtar
3252
+ * kapsamı`) HER istekte DB'den YENİDEN hesaplar. Bu paketteki `scope` ise
3253
+ * sunucu kurulurken bir kez alınmış bir ANLIK GÖRÜNTÜDÜR (bkz. `scope.ts`).
3254
+ */
3255
+ var McpScopeError = class extends SolicrmMcpError {
3256
+ toolName;
3257
+ requiredScope;
3258
+ actualScope;
3259
+ constructor(params) {
3260
+ super(`Tool "${params.toolName}" requires the "${params.requiredScope}" API key scope, but this key's effective scope is "${params.actualScope}".`);
3261
+ this.name = "McpScopeError";
3262
+ this.toolName = params.toolName;
3263
+ this.requiredScope = params.requiredScope;
3264
+ this.actualScope = params.actualScope;
3265
+ }
3266
+ };
3267
+ /**
3268
+ * Araç girdisi zod şemasını GEÇTİ ama anlamsal olarak tutarsız (ör. sıralama
3269
+ * yönü verilmiş, sıralanacak alan verilmemiş).
3270
+ *
3271
+ * NEDEN AYRI BİR HATA: bu projenin "fail-open savunma savunma değildir"
3272
+ * dersinin doğrudan uygulaması — tutarsız girdiyi SESSİZCE yok saymak
3273
+ * (`sortDirection`'ı görmezden gelip sırasız sonuç döndürmek) çağırana
3274
+ * "istediğin sıralamayı yaptım" yalanını söylerdi.
3275
+ */
3276
+ var McpInputError = class extends SolicrmMcpError {
3277
+ constructor(message) {
3278
+ super(message);
3279
+ this.name = "McpInputError";
3280
+ }
3281
+ };
3282
+ //#endregion
3283
+ //#region packages/mcp/src/scope.ts
3284
+ /**
3285
+ * Araç adı → gereken MİNİMUM coarse kapsam kataloğu, ve iki kapsam katmanının
3286
+ * paylaştığı karşılaştırma.
3287
+ *
3288
+ * KRİTİK KURAL 1 — KATALOG TOTAL, `Record<string, ApiKeyScope>` DEĞİL.
3289
+ * `TOOL_MIN_SCOPE`'un anahtarı `McpToolName` literal union'ıdır, bu yüzden
3290
+ * `TOOL_MIN_SCOPE[name]` HİÇBİR ZAMAN `undefined` olamaz (`noUncheckedIndexedAccess`
3291
+ * altında bile). Plan taslağı `Record<string, ApiKeyScope>` + `if (need !== undefined)`
3292
+ * yazıyordu; o biçimde katalogda UNUTULAN bir araç adı `undefined` verir ve kapı
3293
+ * SESSİZCE HİÇ UYGULANMAZ — projenin "fail-open savunma savunma değildir"
3294
+ * dersinin ta kendisi. Bu biçimde ise yeni bir araç adı eklerken kapsamını
3295
+ * yazmamak DERLEME HATASIDIR.
3296
+ *
3297
+ * KRİTİK KURAL 2 — BU PAKETTEKİ `scope` BİR ANLIK GÖRÜNTÜDÜR, YETKİ SINIRI
3298
+ * DEĞİLDİR. `createSolicrmMcpServer(client, scope)` çağrıldığı anda geçerli olan
3299
+ * etkin kapsam kapanışta (closure) taşınır; süreç yaşarken YENİDEN
3300
+ * HESAPLANMAZ. Gerçek yetki sınırı sunucudadır: her araç çağrısı SDK üzerinden
3301
+ * API'ye gider ve `checkApiKeyTenantAndScope` (`apps/api/src/lib/crm-access.ts`)
3302
+ * `effectiveScope`'u (= `ROLE_SCOPE_CEILING[güncel rol] ∩ anahtar kapsamı`) HER
3303
+ * istekte DB'den yeniden hesaplar — bu yüzden anahtarın sahibi demote edilince
3304
+ * ayrı bir iptal job'ı gerekmiyor. Plan Task 3 Step 4'ün "çağrı-anı kontrolü
3305
+ * gerekli ÇÜNKÜ stdio süreci uzun ömürlü ve sahibin rolü düşürülebilir"
3306
+ * gerekçesi YANLIŞTIR: çağrı-anı kontrolü de AYNI dondurulmuş `scope`'u okur,
3307
+ * demote'u göremez. Çağrı-anı kontrolünün GERÇEK değeri aşağıdaki KRİTİK
3308
+ * KURAL 3'tür.
3309
+ *
3310
+ * KRİTİK KURAL 3 — İKİ KATMAN, İKİ AYRI KUSURA KARŞI. (a) Kayıt-anı
3311
+ * `.disable()` `tools/list`'i filtreler: LLM göremediği bir aracı denemez
3312
+ * (bağlam kirliliği + gereksiz 403 turu yok). (b) Çağrı-anı `assertScope`
3313
+ * BİZİM KENDİ REGRESYONUMUZA karşıdır: `server.ts`'in disable döngüsü
3314
+ * kaldırılır/yanlış sıraya konursa ya da bir araç kayıt listesine eklenip
3315
+ * döngüye girmezse, handler yine reddeder. İkisi bağımsız testlerle sabitlenir.
3316
+ */
3317
+ /**
3318
+ * Kayıtlı TÜM araçların kanonik ad listesi (spec §12.1'in 22 aracı — `search`
3319
+ * dâhil; plan metninin "21 araç" sayımı hatalıdır, `search` ZATEN §12.1
3320
+ * listesindedir, ona EK değildir).
3321
+ *
3322
+ * Bu dizi TEK gerçek kaynaktır: `TOOL_MIN_SCOPE` ondan tiplenir ve
3323
+ * `test/server.test.ts` `tools/list` çıktısının bununla BİREBİR eşleştiğini
3324
+ * doğrular.
3325
+ */
3326
+ const MCP_TOOL_NAMES = [
3327
+ "list_contacts",
3328
+ "get_contact",
3329
+ "create_contact",
3330
+ "update_contact",
3331
+ "list_companies",
3332
+ "get_company",
3333
+ "create_company",
3334
+ "update_company",
3335
+ "list_deals",
3336
+ "get_deal",
3337
+ "create_deal",
3338
+ "update_deal",
3339
+ "move_deal_stage",
3340
+ "list_pipelines",
3341
+ "list_activities",
3342
+ "create_activity",
3343
+ "list_tasks",
3344
+ "create_task",
3345
+ "complete_task",
3346
+ "create_note",
3347
+ "search",
3348
+ "list_saved_views"
3349
+ ];
3350
+ /**
3351
+ * Kapsam rütbeleri — `apps/api/src/lib/api-key-auth.ts`'in `SCOPE_RANK`'ıyla
3352
+ * BİREBİR aynı. İçe aktarılMAZ çünkü bu paket npm'e yayınlanır ve `apps/api`'ye
3353
+ * (Hono, Drizzle, Postgres) bağımlı OLAMAZ. Drift riski TİP SEVİYESİNDE
3354
+ * kapatılmıştır: `Record<ApiKeyScope, number>` `@solicrm/contracts`'ın
3355
+ * `apiKeyScopeSchema` enum'una bağlıdır ⇒ contracts'a dördüncü bir kapsam
3356
+ * eklenirse bu satır DERLENMEZ.
3357
+ */
3358
+ const SCOPE_RANK = {
3359
+ read: 0,
3360
+ write: 1,
3361
+ admin: 2
3362
+ };
3363
+ /** `SCOPE_RANK`'ın anahtar kümesinin contracts'la aynı olduğunu ÇALIŞMA ZAMANINDA da kanıtlanabilir kılar. */
3364
+ const API_KEY_SCOPES = apiKeyScopeSchema.options;
3365
+ const TOOL_MIN_SCOPE = {
3366
+ list_contacts: "read",
3367
+ get_contact: "read",
3368
+ create_contact: "write",
3369
+ update_contact: "write",
3370
+ list_companies: "read",
3371
+ get_company: "read",
3372
+ create_company: "write",
3373
+ update_company: "write",
3374
+ list_deals: "read",
3375
+ get_deal: "read",
3376
+ create_deal: "write",
3377
+ update_deal: "write",
3378
+ move_deal_stage: "write",
3379
+ list_pipelines: "read",
3380
+ list_activities: "read",
3381
+ create_activity: "write",
3382
+ list_tasks: "read",
3383
+ create_task: "write",
3384
+ complete_task: "write",
3385
+ create_note: "write",
3386
+ search: "read",
3387
+ list_saved_views: "read"
3388
+ };
3389
+ /** `actual`, `required` kapsamını karşılıyor mu (read < write < admin) */
3390
+ function scopeSatisfies(actual, required) {
3391
+ return SCOPE_RANK[actual] >= SCOPE_RANK[required];
3392
+ }
3393
+ /**
3394
+ * Çağrı-anı kapsam kapısı — HER araç handler'ının İLK satırı (bkz. KRİTİK
3395
+ * KURAL 3/b). Yetersizse fırlatır; `toMcpToolError` bunu `isError: true`
3396
+ * sonucuna çevirir.
3397
+ */
3398
+ function assertScope(scope, toolName) {
3399
+ const required = TOOL_MIN_SCOPE[toolName];
3400
+ if (!scopeSatisfies(scope, required)) throw new McpScopeError({
3401
+ toolName,
3402
+ requiredScope: required,
3403
+ actualScope: scope
3404
+ });
3405
+ }
3406
+ //#endregion
3407
+ //#region packages/mcp/src/resources.ts
3408
+ const MCP_RESOURCE_NAMES = ["contact", "deal"];
3409
+ /** Kaynak → gereken minimum kapsam. `TOOL_MIN_SCOPE` gibi TOTAL (fail-open yok). */
3410
+ const RESOURCE_MIN_SCOPE = {
3411
+ contact: "read",
3412
+ deal: "read"
3413
+ };
3414
+ /** URI şablonu değişkeni tek değer de dizi de olabilir — ilkini alır, yoksa hata. */
3415
+ function firstVariable(value, name) {
3416
+ const first = Array.isArray(value) ? value[0] : value;
3417
+ if (first === void 0 || first.length === 0) throw new McpInputError(`Resource URI is missing the "${name}" segment.`);
3418
+ return first;
3419
+ }
3420
+ function assertResourceScope(scope, name) {
3421
+ const required = RESOURCE_MIN_SCOPE[name];
3422
+ if (!scopeSatisfies(scope, required)) throw new McpInputError(`Resource "${name}" requires the "${required}" API key scope, but this key's effective scope is "${scope}".`);
3423
+ }
3424
+ function registerCrmResources(server, client, scope) {
3425
+ return [["contact", server.registerResource("contact", new ResourceTemplate("solicrm://contacts/{id}", { list: void 0 }), {
3426
+ title: "SoliCRM kişisi",
3427
+ mimeType: "application/json"
3428
+ }, async (uri, variables) => {
3429
+ assertResourceScope(scope, "contact");
3430
+ const contact = await client.contacts.get(firstVariable(variables.id, "id"));
3431
+ return { contents: [{
3432
+ uri: uri.href,
3433
+ mimeType: "application/json",
3434
+ text: JSON.stringify(contact, null, 2)
3435
+ }] };
3436
+ })], ["deal", server.registerResource("deal", new ResourceTemplate("solicrm://deals/{id}", { list: void 0 }), {
3437
+ title: "SoliCRM fırsatı",
3438
+ mimeType: "application/json"
3439
+ }, async (uri, variables) => {
3440
+ assertResourceScope(scope, "deal");
3441
+ const deal = await client.deals.get(firstVariable(variables.id, "id"));
3442
+ return { contents: [{
3443
+ uri: uri.href,
3444
+ mimeType: "application/json",
3445
+ text: JSON.stringify(deal, null, 2)
3446
+ }] };
3447
+ })]];
3448
+ }
3449
+ //#endregion
3450
+ //#region packages/mcp/src/result.ts
3451
+ /** Beklenmeyen (bizim kusurumuz olan) hatalar için TEK jenerik metin. */
3452
+ const UNEXPECTED_ERROR_MESSAGE = "An unexpected error occurred while calling the SoliCRM API.";
3453
+ /**
3454
+ * SDK'nın hata sınıflarının `this.name` değerlerinin KAPALI listesi. Listede
3455
+ * OLMAYAN her şey jenerik metne düşer (fail-closed).
3456
+ */
3457
+ const SDK_ERROR_NAMES = [
3458
+ "SolicrmError",
3459
+ "SolicrmApiError",
3460
+ "SolicrmResponseError"
3461
+ ];
3462
+ /**
3463
+ * TİP DÜZEYİ KANIT (a): gerçek `SolicrmApiError` yukarıdaki yapısal sözleşmeyi
3464
+ * KARŞILIYOR. Hiç çağrılmaz; tek işi bir sapmada `tsc -b`'yi kırmaktır.
3465
+ */
3466
+ function assertApiErrorShape(err) {
3467
+ return err;
3468
+ }
3469
+ /**
3470
+ * TİP DÜZEYİ KANIT (a-2): `SDK_ERROR_NAMES`'in karşılık geldiği ÜÇ sınıf
3471
+ * gerçekten var ve hepsi `Error` alt sınıfı. Biri kaldırılır/yeniden
3472
+ * adlandırılırsa import satırı ve bu imza DERLENMEZ.
3473
+ */
3474
+ function assertSdkErrorNames(errors) {
3475
+ return errors;
3476
+ }
3477
+ /** Her aracın ORTAK başarı biçimi — veriyi okunabilir (girintili) JSON metne çevirir. */
3478
+ function jsonResult(data) {
3479
+ return { content: [{
3480
+ type: "text",
3481
+ text: JSON.stringify(data, null, 2)
3482
+ }] };
3483
+ }
3484
+ /** Bir hatayı MCP'nin `isError: true` sonucuna çevirir (JSON-RPC hatası DEĞİL). */
3485
+ function toMcpToolError(err) {
3486
+ return {
3487
+ isError: true,
3488
+ content: [{
3489
+ type: "text",
3490
+ text: describeError(err)
3491
+ }]
3492
+ };
3493
+ }
3494
+ /** Ham gövdeyi + status'ü koruyan API hatası okuması; hata bu şekilde değilse `undefined`. */
3495
+ function readApiError(err) {
3496
+ if (!(err instanceof Error) || err.name !== "SolicrmApiError") return void 0;
3497
+ if (!("status" in err) || typeof err.status !== "number") return void 0;
3498
+ if (!("body" in err) || !("serverMessage" in err)) return void 0;
3499
+ const serverMessage = err.serverMessage;
3500
+ return {
3501
+ status: err.status,
3502
+ body: err.body,
3503
+ serverMessage: typeof serverMessage === "string" ? serverMessage : void 0,
3504
+ message: err.message
3505
+ };
3506
+ }
3507
+ function describeError(err) {
3508
+ if (err instanceof SolicrmMcpError) return err.message;
3509
+ const apiError = readApiError(err);
3510
+ if (apiError !== void 0) return JSON.stringify({
3511
+ status: apiError.status,
3512
+ message: apiError.serverMessage ?? apiError.message,
3513
+ body: apiError.body
3514
+ }, null, 2);
3515
+ if (err instanceof Error && SDK_ERROR_NAMES.includes(err.name)) return err.message;
3516
+ return UNEXPECTED_ERROR_MESSAGE;
3517
+ }
3518
+ //#endregion
3519
+ //#region packages/mcp/src/tools/define.ts
3520
+ /**
3521
+ * Bir araç handler'ını MCP callback'ine çevirir: kapsam kapısı → handler →
3522
+ * `jsonResult`; her fırlatma `isError: true` sonucuna dönüşür (JSON-RPC
3523
+ * hatasına DEĞİL — LLM hatayı okuyup düzeltebilsin).
3524
+ */
3525
+ function guarded(scope, name, handler) {
3526
+ return async (args) => {
3527
+ try {
3528
+ assertScope(scope, name);
3529
+ return jsonResult(await handler(args));
3530
+ } catch (err) {
3531
+ return toMcpToolError(err);
3532
+ }
3533
+ };
3534
+ }
3535
+ /** Girdi almayan araçlar için (`list_pipelines`) — aynı üç garanti, argümansız. */
3536
+ function guardedNoArgs(scope, name, handler) {
3537
+ return async () => {
3538
+ try {
3539
+ assertScope(scope, name);
3540
+ return jsonResult(await handler());
3541
+ } catch (err) {
3542
+ return toMcpToolError(err);
3543
+ }
3544
+ };
3545
+ }
3546
+ //#endregion
3547
+ //#region packages/mcp/src/tools/ids.ts
3548
+ /**
3549
+ * Araç girdilerindeki kimlik (id) alanlarının TEK tanımı. `@solicrm/contracts`
3550
+ * kaynak gövdelerinde `z.uuid('geçerli bir <şey> kimliği olmalı')` kalıbını
3551
+ * kullanıyor; yol parametreleri için bir sözleşme şeması YOK (uçlar onları yol
3552
+ * segmentinden okur), bu yüzden aynı kalıp burada tek yerde kurulur — 8 araç
3553
+ * dosyasında tekrarlanmaz.
3554
+ */
3555
+ /** `label` Türkçe okunur bir isimdir ("kişi", "şirket", "fırsat"...) */
3556
+ function idField(label) {
3557
+ return z.uuid(`geçerli bir ${label} kimliği olmalı`);
3558
+ }
3559
+ //#endregion
3560
+ //#region packages/mcp/src/tools/activities.ts
3561
+ const listActivitiesInputSchema = pageQuerySchema.extend({
3562
+ targetType: targetTypeSchema.optional(),
3563
+ targetId: idField("hedef").optional()
3564
+ });
3565
+ function toActivityListQuery(args) {
3566
+ if (args.targetType !== void 0 !== (args.targetId !== void 0)) throw new McpInputError("targetType and targetId must be provided together. Send both to filter by a target, or neither to list every activity.");
3567
+ const query = {
3568
+ cursor: args.cursor,
3569
+ limit: args.limit
3570
+ };
3571
+ if (args.targetType !== void 0 && args.targetId !== void 0) query.target = {
3572
+ type: args.targetType,
3573
+ id: args.targetId
3574
+ };
3575
+ return query;
3576
+ }
3577
+ function registerActivityTools(server, client, scope) {
3578
+ return [["list_activities", server.registerTool("list_activities", {
3579
+ title: "Aktiviteleri listele",
3580
+ description: "Lists activities, cursor-paginated. Pass targetType AND targetId together to see one record’s timeline.",
3581
+ inputSchema: listActivitiesInputSchema
3582
+ }, guarded(scope, "list_activities", (args) => client.activities.list(toActivityListQuery(args))))], ["create_activity", server.registerTool("create_activity", {
3583
+ title: "Aktivite oluştur",
3584
+ description: "Logs an activity (call, meeting, email, note...) against a contact, company or deal. `happensAt` is an ISO 8601 UTC timestamp.",
3585
+ inputSchema: createActivityRequestSchema
3586
+ }, guarded(scope, "create_activity", (args) => client.activities.create(args)))]];
3587
+ }
3588
+ //#endregion
3589
+ //#region packages/mcp/src/list-input.ts
3590
+ /**
3591
+ * CRM liste araçlarının (`list_contacts`/`list_companies`/`list_deals`/
3592
+ * `list_tasks`) PAYLAŞTIĞI girdi şeması ve SDK sorgusuna çevirisi.
3593
+ *
3594
+ * KRİTİK KURAL 1 — ALAN KATALOĞU KOPYALANMAZ. Geçerli `sortField`/`filters`
3595
+ * anahtarları `@solicrm/contracts`'ın `FIELD_CATALOG`'undan ÇALIŞMA ZAMANINDA
3596
+ * okunur (araç açıklaması bile oradan üretilir, bkz. `describeCrmListFields`);
3597
+ * bu dosyada tek bir alan adı literali YOKTUR. Doğrulamanın kendisi de burada
3598
+ * TEKRARLANMAZ — SDK'nın `buildCrmListQuery`'si `resolveField` + `sortable`/
3599
+ * `filterable` bayrak kontrolünü zaten yapıyor ve bilinmeyen/uygun olmayan
3600
+ * alanda `SolicrmError` fırlatıyor (fail-closed).
3601
+ *
3602
+ * KRİTİK KURAL 2 — SAYFA SINIRLARI DA KOPYALANMAZ. `cursor`/`limit`
3603
+ * `pageQuerySchema`'dan `.extend()` ile MİRAS ALINIR ⇒ `PAGE_MAX_LIMIT`
3604
+ * değişirse araç şeması da değişir. `z.coerce.number()`'ın JSON Schema
3605
+ * karşılığının `{"type":"integer","minimum":1,"maximum":100}` olduğu canlı
3606
+ * ölçüldü — MCP tarafında sorun ÜRETMİYOR.
3607
+ *
3608
+ * KRİTİK KURAL 3 — `sortDirection` VERİLİP `sortField` VERİLMEZSE SESSİZCE
3609
+ * YOK SAYILMAZ, HATA VERİLİR (`McpInputError`). "Tanımadığım/eksik girdiyi
3610
+ * görmezden gel" bir fail-open kalıbıdır: çağıran sıralama istediğini sanır,
3611
+ * sırasız sonuç alır.
3612
+ *
3613
+ * KRİTİK KURAL 4 — ALAN ADLARI KANONİK KATALOĞA KARŞI DOĞRULANIR, DESENE
3614
+ * KARŞI DEĞİL. İlk uygulama `propertyNames` deseni (`^[a-z][a-z0-9_]*$`)
3615
+ * kullanıyor ve JSDoc'u "`__proto__`/`constructor` şema seviyesinde
3616
+ * reddedilir" diye İDDİA ediyordu. **Ölçüm bu iddiayı ÇÜRÜTTÜ:** `constructor`
3617
+ * deseni SAĞLIYOR (tamamı küçük harf) ⇒ belgelenen koruma YOKTU. Bu, bu
3618
+ * deponun "belgelenen koruma hiç çağrılmamış olabilir" kusur sınıfının ta
3619
+ * kendisi. Düzeltme: anahtarlar `resolveField(resource, key)` ile doğrulanır;
3620
+ * o fonksiyon `Object.hasOwn` allowlist'i olduğu için prototip zincirinden
3621
+ * gelen hiçbir ad kolon adına dönüşemez. Bu yüzden şema artık bir FABRİKADIR
3622
+ * — kaynağı bilmeden doğrulama yapılamaz.
3623
+ *
3624
+ * `__proto__` İSTİSNASI, DÜRÜST HÂLİ (ölçüldü): `zod@4.4.3`'ün `z.record`'u
3625
+ * `JSON.parse` ile gelen own `__proto__` anahtarını **doğrulamadan SESSİZCE
3626
+ * ATAR** — `safeParse` `success: true` döner ama çıktı `{}`'dir, yani anahtar
3627
+ * `refine`'a HİÇ ULAŞMAZ. Sonuç güvenlik açısından güvenlidir (anahtar
3628
+ * `buildCrmListQuery`'ye ve dolayısıyla SQL'e hiç geçmez) ama bir REDDETME
3629
+ * DEĞİL, bir DÜŞÜRMEDİR; `test/list-input.test.ts` bu ayrımı olduğu gibi
3630
+ * sabitler. Diğer prototip adları (`constructor`, `hasOwnProperty`,
3631
+ * `toString`, `valueOf`, `prototype`) GERÇEKTEN reddedilir.
3632
+ *
3633
+ * BAYRAK KONTROLÜ BURADA TEKRARLANMAZ: `sortable`/`filterable` denetimi
3634
+ * SDK'nın `buildCrmListQuery`'sinde yaşar (TEK sahip). Buradaki kontrol yalnız
3635
+ * "bu kaynakta böyle bir alan VAR MI" sorusunu yanıtlar.
3636
+ */
3637
+ /**
3638
+ * Bir CRM kaynağı için liste aracı girdi şeması. Alan adları o kaynağın
3639
+ * `FIELD_CATALOG` girdisine karşı doğrulanır (bkz. KRİTİK KURAL 4).
3640
+ */
3641
+ function buildCrmListInputSchema(resource) {
3642
+ const knownField = (key) => resolveField(resource, key) !== void 0;
3643
+ const unknownFieldMessage = `${resource} kaynağında böyle bir alan yok`;
3644
+ return pageQuerySchema.extend({
3645
+ sortField: z.string().trim().refine(knownField, unknownFieldMessage).optional(),
3646
+ sortDirection: sortDirectionSchema.optional(),
3647
+ filters: z.record(z.string().trim().refine(knownField, unknownFieldMessage), z.string().max(500, "en fazla 500 karakter olabilir")).optional()
3648
+ });
3649
+ }
3650
+ /**
3651
+ * Araç girdisini SDK'nın `CrmListQuery`'sine çevirir. `tenantId` DİYE BİR ALAN
3652
+ * YOKTUR ve olamaz — tenant her zaman `SolicrmClient`'ın kurulduğu andaki
3653
+ * değerdir (bkz. `server.ts` dosya başı).
3654
+ */
3655
+ function toCrmListQuery(args) {
3656
+ if (args.sortField === void 0 && args.sortDirection !== void 0) throw new McpInputError("sortDirection was given without sortField. Provide the field to sort by, or omit both.");
3657
+ const query = {
3658
+ cursor: args.cursor,
3659
+ limit: args.limit,
3660
+ filters: args.filters
3661
+ };
3662
+ if (args.sortField !== void 0) query.sort = {
3663
+ field: args.sortField,
3664
+ direction: args.sortDirection ?? "asc"
3665
+ };
3666
+ return query;
3667
+ }
3668
+ /**
3669
+ * Bir kaynağın sıralanabilir/filtrelenebilir alanlarını araç açıklamasına
3670
+ * yazılacak metne çevirir — LLM'in geçersiz alan adı denemesini azaltır.
3671
+ * Liste `FIELD_CATALOG`'dan ÜRETİLİR, elle yazılmaz.
3672
+ */
3673
+ function describeCrmListFields(resource) {
3674
+ const catalog = FIELD_CATALOG[resource];
3675
+ const sortable = [];
3676
+ const filterable = [];
3677
+ for (const [key, def] of Object.entries(catalog)) {
3678
+ if (def.sortable) sortable.push(key);
3679
+ if (def.filterable) filterable.push(key);
3680
+ }
3681
+ return `Sortable fields: ${sortable.join(", ")}. Filterable fields: ${filterable.join(", ")}.`;
3682
+ }
3683
+ //#endregion
3684
+ //#region packages/mcp/src/tools/companies.ts
3685
+ const listCompaniesInputSchema = buildCrmListInputSchema("companies");
3686
+ const getCompanyInputSchema = z.object({ companyId: idField("şirket") });
3687
+ const updateCompanyInputSchema = updateCompanyRequestSchema.extend({ companyId: idField("şirket") });
3688
+ function registerCompanyTools(server, client, scope) {
3689
+ return [
3690
+ ["list_companies", server.registerTool("list_companies", {
3691
+ title: "Şirketleri listele",
3692
+ description: `Lists companies in the tenant, cursor-paginated. ${describeCrmListFields("companies")}`,
3693
+ inputSchema: listCompaniesInputSchema
3694
+ }, guarded(scope, "list_companies", (args) => client.companies.list(toCrmListQuery(args))))],
3695
+ ["get_company", server.registerTool("get_company", {
3696
+ title: "Şirket getir",
3697
+ description: "Fetches a single company by id.",
3698
+ inputSchema: getCompanyInputSchema
3699
+ }, guarded(scope, "get_company", (args) => client.companies.get(args.companyId)))],
3700
+ ["create_company", server.registerTool("create_company", {
3701
+ title: "Şirket oluştur",
3702
+ description: "Creates a company. `annualRevenue` is a decimal STRING (e.g. \"1250000.00\"), never a JSON number — the API stores it as numeric(18,2).",
3703
+ inputSchema: createCompanyRequestSchema
3704
+ }, guarded(scope, "create_company", (args) => client.companies.create(args)))],
3705
+ ["update_company", server.registerTool("update_company", {
3706
+ title: "Şirketi güncelle",
3707
+ description: "Updates a company. Only the fields you send are changed.",
3708
+ inputSchema: updateCompanyInputSchema
3709
+ }, guarded(scope, "update_company", ({ companyId, ...body }) => client.companies.update(companyId, body)))]
3710
+ ];
3711
+ }
3712
+ //#endregion
3713
+ //#region packages/mcp/src/tools/contacts.ts
3714
+ const listContactsInputSchema = buildCrmListInputSchema("contacts");
3715
+ const getContactInputSchema = z.object({ contactId: idField("kişi") });
3716
+ const updateContactInputSchema = updateContactRequestSchema.extend({ contactId: idField("kişi") });
3717
+ function registerContactTools(server, client, scope) {
3718
+ return [
3719
+ ["list_contacts", server.registerTool("list_contacts", {
3720
+ title: "Kişileri listele",
3721
+ description: `Lists contacts in the tenant, cursor-paginated. ${describeCrmListFields("contacts")}`,
3722
+ inputSchema: listContactsInputSchema
3723
+ }, guarded(scope, "list_contacts", (args) => client.contacts.list(toCrmListQuery(args))))],
3724
+ ["get_contact", server.registerTool("get_contact", {
3725
+ title: "Kişi getir",
3726
+ description: "Fetches a single contact by id.",
3727
+ inputSchema: getContactInputSchema
3728
+ }, guarded(scope, "get_contact", (args) => client.contacts.get(args.contactId)))],
3729
+ ["create_contact", server.registerTool("create_contact", {
3730
+ title: "Kişi oluştur",
3731
+ description: "Creates a contact. Counts against the tenant plan limit — a 402 result carries the limit and plan code in its body.",
3732
+ inputSchema: createContactRequestSchema
3733
+ }, guarded(scope, "create_contact", (args) => client.contacts.create(args)))],
3734
+ ["update_contact", server.registerTool("update_contact", {
3735
+ title: "Kişiyi güncelle",
3736
+ description: "Updates a contact. Only the fields you send are changed.",
3737
+ inputSchema: updateContactInputSchema
3738
+ }, guarded(scope, "update_contact", ({ contactId, ...body }) => client.contacts.update(contactId, body)))]
3739
+ ];
3740
+ }
3741
+ //#endregion
3742
+ //#region packages/mcp/src/tools/deals.ts
3743
+ const listDealsInputSchema = buildCrmListInputSchema("deals");
3744
+ const getDealInputSchema = z.object({ dealId: idField("fırsat") });
3745
+ const updateDealInputSchema = updateDealRequestSchema.extend({ dealId: idField("fırsat") });
3746
+ const moveDealInputSchema = moveDealRequestSchema.extend({ dealId: idField("fırsat") });
3747
+ function registerDealTools(server, client, scope) {
3748
+ return [
3749
+ ["list_deals", server.registerTool("list_deals", {
3750
+ title: "Fırsatları listele",
3751
+ description: `Lists deals in the tenant, cursor-paginated. ${describeCrmListFields("deals")}`,
3752
+ inputSchema: listDealsInputSchema
3753
+ }, guarded(scope, "list_deals", (args) => client.deals.list(toCrmListQuery(args))))],
3754
+ ["get_deal", server.registerTool("get_deal", {
3755
+ title: "Fırsat getir",
3756
+ description: "Fetches a single deal by id, including its stage and linked contacts.",
3757
+ inputSchema: getDealInputSchema
3758
+ }, guarded(scope, "get_deal", (args) => client.deals.get(args.dealId)))],
3759
+ ["create_deal", server.registerTool("create_deal", {
3760
+ title: "Fırsat oluştur",
3761
+ description: "Creates a deal in a pipeline stage. `amount` is a decimal STRING (e.g. \"45000.00\"), never a JSON number. Call list_pipelines first to get valid pipelineId/stageId values.",
3762
+ inputSchema: createDealRequestSchema
3763
+ }, guarded(scope, "create_deal", (args) => client.deals.create(args)))],
3764
+ ["update_deal", server.registerTool("update_deal", {
3765
+ title: "Fırsatı güncelle",
3766
+ description: "Updates a deal. Stage/pipeline changes are rejected here by design — use move_deal_stage instead.",
3767
+ inputSchema: updateDealInputSchema
3768
+ }, guarded(scope, "update_deal", ({ dealId, ...body }) => client.deals.update(dealId, body)))],
3769
+ ["move_deal_stage", server.registerTool("move_deal_stage", {
3770
+ title: "Fırsatı aşamaya taşı",
3771
+ description: "Moves a deal to another stage (kanban). Optional beforeId/afterId place it between two neighbouring deals; omit both to append.",
3772
+ inputSchema: moveDealInputSchema
3773
+ }, guarded(scope, "move_deal_stage", ({ dealId, ...body }) => client.deals.move(dealId, body)))]
3774
+ ];
3775
+ }
3776
+ //#endregion
3777
+ //#region packages/mcp/src/tools/notes.ts
3778
+ function registerNoteTools(server, client, scope) {
3779
+ return [["create_note", server.registerTool("create_note", {
3780
+ title: "Not oluştur",
3781
+ description: "Creates a note. `body` is a free-form JSON object (rich text document). NOTE: the note is created unlinked — it will not appear on any contact/company/deal timeline until it is attached, which this MCP server does not expose.",
3782
+ inputSchema: createNoteRequestSchema
3783
+ }, guarded(scope, "create_note", (args) => client.notes.create(args)))]];
3784
+ }
3785
+ //#endregion
3786
+ //#region packages/mcp/src/tools/pipelines.ts
3787
+ function registerPipelineTools(server, client, scope) {
3788
+ return [["list_pipelines", server.registerTool("list_pipelines", {
3789
+ title: "Pipeline’ları listele",
3790
+ description: "Lists every pipeline with its ordered stages. Use this to resolve pipelineId/stageId before creating or moving a deal."
3791
+ }, guardedNoArgs(scope, "list_pipelines", () => client.pipelines.list()))]];
3792
+ }
3793
+ //#endregion
3794
+ //#region packages/mcp/src/tools/search.ts
3795
+ const searchInputSchema = z.object({
3796
+ q: searchQuerySchema.shape.q,
3797
+ limit: searchQuerySchema.shape.limit,
3798
+ resources: z.array(searchableResourceSchema).min(1, "en az bir kaynak belirtilmeli").optional()
3799
+ });
3800
+ function toSearchParams(args) {
3801
+ return {
3802
+ q: args.q,
3803
+ limit: args.limit,
3804
+ resources: args.resources
3805
+ };
3806
+ }
3807
+ function registerSearchTools(server, client, scope) {
3808
+ return [["search", server.registerTool("search", {
3809
+ title: "Çapraz arama",
3810
+ description: "Full-text search across contacts, companies and deals in one call. Returns the best matches only (single page, no cursor). Omit `resources` to search all three.",
3811
+ inputSchema: searchInputSchema
3812
+ }, guarded(scope, "search", (args) => client.search.query(toSearchParams(args))))]];
3813
+ }
3814
+ //#endregion
3815
+ //#region packages/mcp/src/tools/tasks.ts
3816
+ const listTasksInputSchema = buildCrmListInputSchema("tasks");
3817
+ const completeTaskInputSchema = z.object({ taskId: idField("görev") });
3818
+ function registerTaskTools(server, client, scope) {
3819
+ return [
3820
+ ["list_tasks", server.registerTool("list_tasks", {
3821
+ title: "Görevleri listele",
3822
+ description: `Lists tasks in the tenant, cursor-paginated. ${describeCrmListFields("tasks")}`,
3823
+ inputSchema: listTasksInputSchema
3824
+ }, guarded(scope, "list_tasks", (args) => client.tasks.list(toCrmListQuery(args))))],
3825
+ ["create_task", server.registerTool("create_task", {
3826
+ title: "Görev oluştur",
3827
+ description: "Creates a task. NOTE: the task is created unlinked — it will not appear on any contact/company/deal timeline until it is attached, which this MCP server does not expose. `dueAt` is an ISO 8601 UTC timestamp.",
3828
+ inputSchema: createTaskRequestSchema
3829
+ }, guarded(scope, "create_task", (args) => client.tasks.create(args)))],
3830
+ ["complete_task", server.registerTool("complete_task", {
3831
+ title: "Görevi tamamla",
3832
+ description: "Marks a task as completed and returns the updated task.",
3833
+ inputSchema: completeTaskInputSchema
3834
+ }, guarded(scope, "complete_task", (args) => client.tasks.complete(args.taskId)))]
3835
+ ];
3836
+ }
3837
+ //#endregion
3838
+ //#region packages/mcp/src/tools/views.ts
3839
+ const listSavedViewsInputSchema = z.object({ resource: savedViewResourceSchema.optional() });
3840
+ function registerSavedViewTools(server, client, scope) {
3841
+ return [["list_saved_views", server.registerTool("list_saved_views", {
3842
+ title: "Kaydedilmiş görünümleri listele",
3843
+ description: "Lists saved views (the tenant’s stored filter/sort presets) with their fields, sorts and filters. Optionally narrow to one CRM resource.",
3844
+ inputSchema: listSavedViewsInputSchema
3845
+ }, guarded(scope, "list_saved_views", (args) => client.views.list(args)))]];
3846
+ }
3847
+ //#endregion
3848
+ //#region packages/mcp/src/tools/index.ts
3849
+ function registerAllTools(server, client, scope) {
3850
+ return [
3851
+ ...registerContactTools(server, client, scope),
3852
+ ...registerCompanyTools(server, client, scope),
3853
+ ...registerDealTools(server, client, scope),
3854
+ ...registerPipelineTools(server, client, scope),
3855
+ ...registerActivityTools(server, client, scope),
3856
+ ...registerTaskTools(server, client, scope),
3857
+ ...registerNoteTools(server, client, scope),
3858
+ ...registerSearchTools(server, client, scope),
3859
+ ...registerSavedViewTools(server, client, scope)
3860
+ ];
3861
+ }
3862
+ //#endregion
3863
+ //#region packages/mcp/src/server.ts
3864
+ /**
3865
+ * SoliCRM ürün MCP sunucusu — 22 araç, 2 kaynak, 1 prompt. TRANSPORT-AGNOSTİK:
3866
+ * bu dosya ne HTTP ne stdio bilir; çağıran (Task 4) `server.connect(transport)`
3867
+ * ile bir taşımaya bağlar.
3868
+ *
3869
+ * KRİTİK KURAL 1 — TENANT HER ZAMAN `SolicrmClient`'IN KURULDUĞU ANDAKİ
3870
+ * DEĞERDİR, HİÇBİR ARAÇ GİRDİSİNDEN GELMEZ. Bu iddia üç bağımsız mekanizmayla
3871
+ * ayakta duruyor ve üçü de ölçülmüş durumda:
3872
+ * (a) Hiçbir araç girdi şemasında `tenantId` alanı TANIMLI DEĞİL, ve
3873
+ * `test/server.test.ts` `tools/list`'in ÜRETTİĞİ JSON Schema'ları
3874
+ * tarayarak bunu her araç için doğruluyor (gelecekte eklenen bir araç da
3875
+ * yakalanır).
3876
+ * (b) MCP SDK girdiyi zod ile ayrıştırır ve zod obje şemaları BİLİNMEYEN
3877
+ * ANAHTARLARI ATAR (canlı ölçüldü: `{firstName, lastName, tenantId:
3878
+ * 'ATTACKER-TENANT'}` → handler `{firstName, lastName, status:'lead'}`
3879
+ * görüyor) ⇒ bir LLM `tenantId` uydursa bile handler'a hiç ULAŞMAZ.
3880
+ * (c) Araç handler'ları argümanları SDK'nın `CallOptions` (`{ tenantId }`)
3881
+ * parametresine GEÇİRMEZ — `guarded()`'ın imzası öyle bir kanal
3882
+ * sunmuyor (bkz. `tools/define.ts` KRİTİK KURAL 1).
3883
+ * `[H4]` kanıtı `test/tenant-isolation.test.ts`'te: şemaya GEÇİCİ bir
3884
+ * `tenantId` alanı eklenip (yani (b) devre dışı bırakılıp) isteğin YİNE
3885
+ * client'ın tenant'ına gittiği ölçülüyor.
3886
+ *
3887
+ * KRİTİK KURAL 2 — STATELESS: her `createSolicrmMcpServer` çağrısı KENDİ
3888
+ * kapanışını kurar. Paylaşılan mutable state, modül düzeyi cache veya
3889
+ * `reset*()` gerektiren bir singleton YOKTUR ⇒ eşzamanlı isteklerde yarış
3890
+ * yapısal olarak imkânsız. Bu pakete cache EKLENMEZ; gerekirse önce
3891
+ * "ilk dolum yarışı + hata sonrası zehirlenme" sorularının ikisi de
3892
+ * yanıtlanır (bkz. kök `CLAUDE.md` "Yeni cache = iki zorunlu soru").
3893
+ *
3894
+ * KRİTİK KURAL 3 — KAPSAM İKİ KATMANLIDIR, ama İKİSİ DE SUNUCUNUN YERİNE
3895
+ * GEÇMEZ. Ayrıntı ve dürüst sınırlar için bkz. `scope.ts` KRİTİK KURAL 2/3.
3896
+ */
3897
+ /** `dist/cli.js`'nin (Task 4) `package.json` okumadan taşıdığı sürüm sabiti. */
3898
+ const SOLICRM_MCP_VERSION = "0.1.0";
3899
+ const SOLICRM_MCP_SERVER_NAME = "solicrm";
3900
+ /**
3901
+ * Verilen istemci ve etkin kapsam için TAZE bir `McpServer` kurar.
3902
+ *
3903
+ * @param client Kurulmuş `SolicrmClient` — `tenantId`'si (ve API anahtarı)
3904
+ * çağıranın kimliğinden gelir; bu fonksiyon onu SORGULAMAZ ve DEĞİŞTİRMEZ.
3905
+ * @param scope Çağıranın ŞU ANDAKİ etkin kapsamı (`ROLE_SCOPE_CEILING[rol] ∩
3906
+ * anahtar kapsamı`). Anlık görüntüdür — bkz. `scope.ts` KRİTİK KURAL 2.
3907
+ */
3908
+ function createSolicrmMcpServer(client, scope) {
3909
+ const server = new McpServer({
3910
+ name: SOLICRM_MCP_SERVER_NAME,
3911
+ version: SOLICRM_MCP_VERSION
3912
+ });
3913
+ const toolHandles = registerAllTools(server, client, scope);
3914
+ const resourceHandles = registerCrmResources(server, client, scope);
3915
+ registerSummarizePipelinePrompt(server);
3916
+ for (const [name, handle] of toolHandles) if (!scopeSatisfies(scope, TOOL_MIN_SCOPE[name])) handle.disable();
3917
+ for (const [name, handle] of resourceHandles) if (!scopeSatisfies(scope, RESOURCE_MIN_SCOPE[name])) handle.disable();
3918
+ return server;
3919
+ }
3920
+ //#endregion
3921
+ //#region packages/mcp/src/transports/http.ts
3922
+ /** Bkz. KRİTİK KURAL 1(a) */
3923
+ const DEFAULT_HTTP_HOST = "127.0.0.1";
3924
+ const DEFAULT_HTTP_PORT = 3737;
3925
+ /** `--http` sunucusunun MCP yolu */
3926
+ const MCP_HTTP_PATH = "/mcp";
3927
+ const BROWSER_ORIGIN_REJECTED_MESSAGE = "Tarayıcıdan gelen istekler reddedilir (Origin başlığı taşıyor).";
3928
+ const METHOD_NOT_ALLOWED_MESSAGE = "Bu uçta yalnız POST desteklenir (stateless MCP).";
3929
+ const NOT_FOUND_MESSAGE = `Yalnız POST ${MCP_HTTP_PATH} desteklenir.`;
3930
+ function jsonResponse(status, body, headers = {}) {
3931
+ return new Response(JSON.stringify(body), {
3932
+ status,
3933
+ headers: {
3934
+ "content-type": "application/json",
3935
+ ...headers
3936
+ }
3937
+ });
3938
+ }
3939
+ /** Bkz. KRİTİK KURAL 1(b) — saf, tek satırlık, ayrı test edilebilir karar */
3940
+ function hasBrowserOrigin(request) {
3941
+ const origin = request.headers.get("origin");
3942
+ return origin !== null && origin.trim().length > 0;
3943
+ }
3944
+ /**
3945
+ * `Request → Response`. Port açmaz, soket kurmaz ⇒ testte doğrudan çağrılır.
3946
+ * Protokol hataları JSON-RPC zarfıyla transport'tan gelir; BU katmanın kendi
3947
+ * redleri (405/404/403) `{error}` zarfını kullanır — hosted uçtaki AYNI ayrım
3948
+ * (bkz. `apps/api/src/routes/mcp.ts` KRİTİK KURAL 5).
3949
+ */
3950
+ function createMcpHttpHandler(deps) {
3951
+ return async (request) => {
3952
+ if (hasBrowserOrigin(request)) return jsonResponse(403, { error: BROWSER_ORIGIN_REJECTED_MESSAGE });
3953
+ const { pathname } = new URL(request.url);
3954
+ if (pathname !== "/mcp") return jsonResponse(404, { error: NOT_FOUND_MESSAGE });
3955
+ if (request.method !== "POST") return jsonResponse(405, { error: METHOD_NOT_ALLOWED_MESSAGE }, { Allow: "POST" });
3956
+ const server = deps.createServer();
3957
+ const transport = new WebStandardStreamableHTTPServerTransport({ enableJsonResponse: true });
3958
+ await server.connect(transport);
3959
+ return await transport.handleRequest(request);
3960
+ };
3961
+ }
3962
+ /**
3963
+ * `node:http` yapıştırıcısı — Node ve Bun'da AYNI şekilde çalışır (`Bun.serve`
3964
+ * kullanılmadı: paket npm'e yayınlanıyor ve `npx` altında Node ile koşuyor).
3965
+ */
3966
+ async function startMcpHttpServer(options) {
3967
+ const { createServer } = await import("node:http");
3968
+ const host = options.host ?? "127.0.0.1";
3969
+ const port = options.port ?? 3737;
3970
+ const server = createServer((req, res) => {
3971
+ (async () => {
3972
+ try {
3973
+ const chunks = [];
3974
+ for await (const chunk of req) chunks.push(typeof chunk === "string" ? new TextEncoder().encode(chunk) : chunk);
3975
+ const method = req.method ?? "GET";
3976
+ const headers = new Headers();
3977
+ for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string") headers.set(key, value);
3978
+ else if (Array.isArray(value)) for (const item of value) headers.append(key, item);
3979
+ const hasBody = method !== "GET" && method !== "HEAD" && chunks.length > 0;
3980
+ const request = new Request(`http://${host}:${port}${req.url ?? "/"}`, {
3981
+ method,
3982
+ headers,
3983
+ ...hasBody ? { body: Buffer.concat(chunks) } : {}
3984
+ });
3985
+ for (const [key, value] of headers) request.headers.set(key, value);
3986
+ const response = await options.handler(request);
3987
+ const outHeaders = {};
3988
+ response.headers.forEach((value, key) => {
3989
+ outHeaders[key] = value;
3990
+ });
3991
+ res.writeHead(response.status, outHeaders);
3992
+ res.end(response.body === null ? void 0 : Buffer.from(await response.arrayBuffer()));
3993
+ } catch (error) {
3994
+ console.error("solicrm-mcp http isteği başarısız:", error);
3995
+ if (!res.headersSent) res.writeHead(500, { "content-type": "application/json" });
3996
+ res.end(JSON.stringify({ error: "Sunucu hatası" }));
3997
+ }
3998
+ })();
3999
+ });
4000
+ await new Promise((resolve, reject) => {
4001
+ server.once("error", reject);
4002
+ server.listen(port, host, () => {
4003
+ server.removeListener("error", reject);
4004
+ resolve();
4005
+ });
4006
+ });
4007
+ const address = server.address();
4008
+ return {
4009
+ host,
4010
+ port: typeof address === "object" && address !== null ? address.port : port,
4011
+ close: () => new Promise((resolve, reject) => {
4012
+ server.close((err) => err === void 0 || err === null ? resolve() : reject(err));
4013
+ })
4014
+ };
4015
+ }
4016
+ //#endregion
4017
+ //#region packages/mcp/src/transports/stdio.ts
4018
+ async function connectStdioTransport(server) {
4019
+ const transport = new StdioServerTransport();
4020
+ await server.connect(transport);
4021
+ return transport;
4022
+ }
4023
+ //#endregion
4024
+ //#region packages/mcp/src/cli.ts
4025
+ const PORT_PATTERN = /^[0-9]+$/;
4026
+ const USAGE = `solicrm-mcp — SoliCRM MCP sunucusu (v${SOLICRM_MCP_VERSION})
4027
+
4028
+ Kullanım:
4029
+ solicrm-mcp stdio taşımasıyla başlar (varsayılan)
4030
+ solicrm-mcp --http [port] bağımsız Streamable HTTP sunucusu (varsayılan port ${DEFAULT_HTTP_PORT})
4031
+ solicrm-mcp --help bu metni yazar
4032
+ solicrm-mcp --version sürümü yazar
4033
+
4034
+ Ortam değişkenleri:
4035
+ SOLICRM_API_KEY (zorunlu) scrm_ ile başlayan tenant API anahtarı
4036
+ SOLICRM_TENANT_ID (zorunlu) kiracı kimliği (UUID)
4037
+ SOLICRM_BASE_URL (opsiyonel) API tabanı; verilmezse https://api.solicrm.com
4038
+ SOLICRM_SCOPE (opsiyonel) read|write|admin — yalnız tools/list filtresi,
4039
+ gerçek yetki sunucuda her istekte yeniden hesaplanır`;
4040
+ /** Bkz. KRİTİK KURAL 3 */
4041
+ function parsePort(raw) {
4042
+ if (!PORT_PATTERN.test(raw)) throw new Error(`--http için geçersiz port: "${raw}". 1–65535 arası bir tam sayı bekleniyor.`);
4043
+ const port = Number.parseInt(raw, 10);
4044
+ if (port < 1 || port > 65535) throw new Error(`--http için geçersiz port: "${raw}". 1–65535 arası bir tam sayı bekleniyor.`);
4045
+ return port;
4046
+ }
4047
+ function parseCliArgs(argv) {
4048
+ let transport = "stdio";
4049
+ let port;
4050
+ let help = false;
4051
+ let version = false;
4052
+ for (let i = 0; i < argv.length; i += 1) {
4053
+ const arg = argv[i];
4054
+ if (arg === void 0) continue;
4055
+ if (arg === "--help" || arg === "-h") {
4056
+ help = true;
4057
+ continue;
4058
+ }
4059
+ if (arg === "--version" || arg === "-v") {
4060
+ version = true;
4061
+ continue;
4062
+ }
4063
+ if (arg === "--http") {
4064
+ transport = "http";
4065
+ const next = argv[i + 1];
4066
+ if (next !== void 0 && !next.startsWith("-")) {
4067
+ port = parsePort(next);
4068
+ i += 1;
4069
+ }
4070
+ continue;
4071
+ }
4072
+ if (arg.startsWith("--http=")) {
4073
+ transport = "http";
4074
+ port = parsePort(arg.slice(7));
4075
+ continue;
4076
+ }
4077
+ throw new Error(`Bilinmeyen argüman: "${arg}". Kullanım için --help.`);
4078
+ }
4079
+ return {
4080
+ transport,
4081
+ port,
4082
+ help,
4083
+ version
4084
+ };
4085
+ }
4086
+ /**
4087
+ * `@vennyx/solicrm`'i ÇALIŞMA ZAMANINDA import eder. `await import(...)`
4088
+ * BİLİNÇLİ: modül düzeyinde statik bir import, `--help`/`--version` gibi
4089
+ * SDK'ya hiç ihtiyaç duymayan yolları da SDK'nın yüklenmesine bağlardı.
4090
+ */
4091
+ async function createRealClient(config) {
4092
+ const { SolicrmClient } = await import("@vennyx/solicrm");
4093
+ return new SolicrmClient({
4094
+ apiKey: config.apiKey,
4095
+ tenantId: config.tenantId,
4096
+ baseUrl: config.baseUrl
4097
+ });
4098
+ }
4099
+ async function runCli(argv, deps = {}) {
4100
+ const log = deps.log ?? ((message) => console.error(message));
4101
+ const args = parseCliArgs(argv);
4102
+ if (args.help) {
4103
+ log(USAGE);
4104
+ return;
4105
+ }
4106
+ if (args.version) {
4107
+ log(SOLICRM_MCP_VERSION);
4108
+ return;
4109
+ }
4110
+ const config = (deps.loadConfig ?? loadConfigFromEnv)(deps.env ?? process.env);
4111
+ const createClient = deps.createClient;
4112
+ const client = createClient === void 0 ? await createRealClient(config) : createClient(config);
4113
+ const createServer = deps.createServer ?? createSolicrmMcpServer;
4114
+ if (args.transport === "http") {
4115
+ const running = await (deps.startHttp ?? (async ({ handler, port }) => await startMcpHttpServer({
4116
+ handler,
4117
+ port
4118
+ })))({
4119
+ handler: createMcpHttpHandler({ createServer: () => createServer(client, config.scope) }),
4120
+ port: args.port
4121
+ });
4122
+ log(`solicrm-mcp HTTP dinliyor: http://${running.host}:${running.port}${MCP_HTTP_PATH}`);
4123
+ return;
4124
+ }
4125
+ await (deps.connectStdio ?? connectStdioTransport)(createServer(client, config.scope));
4126
+ log("solicrm-mcp stdio taşımasında hazır.");
4127
+ }
4128
+ //#endregion
4129
+ //#region packages/mcp/src/client.ts
4130
+ /**
4131
+ * TİP DÜZEYİ KANIT — gerçek `SolicrmClient` bu dar yüzeyi KARŞILIYOR.
4132
+ * Fonksiyon hiç ÇAĞRILMAZ; tek işi `tsc -b`'yi bir sapmada kırmaktır (bir
4133
+ * metot adı değişir/kaldırılırsa derleme burada patlar). `as` cast'i YOK,
4134
+ * çalışma zamanı maliyeti YOK.
4135
+ */
4136
+ function assertSolicrmClientSatisfiesMcpSurface(client) {
4137
+ return client;
4138
+ }
4139
+ //#endregion
4140
+ export { API_KEY_SCOPES, BROWSER_ORIGIN_REJECTED_MESSAGE, DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT, DEFAULT_MCP_SCOPE, MCP_HTTP_PATH, MCP_PROMPT_NAMES, MCP_RESOURCE_NAMES, MCP_TOOL_NAMES, McpInputError, McpScopeError, RESOURCE_MIN_SCOPE, SCOPE_RANK, SDK_ERROR_NAMES, SOLICRM_MCP_SERVER_NAME, SOLICRM_MCP_VERSION, SolicrmMcpError, TOOL_MIN_SCOPE, UNEXPECTED_ERROR_MESSAGE, USAGE, apiKeyScopeSchema, assertApiErrorShape, assertScope, assertSdkErrorNames, assertSolicrmClientSatisfiesMcpSurface, buildCrmListInputSchema, connectStdioTransport, createMcpHttpHandler, createSolicrmMcpServer, describeCrmListFields, firstVariable, guarded, guardedNoArgs, hasBrowserOrigin, idField, jsonResult, loadConfigFromEnv, parseCliArgs, parsePort, registerActivityTools, registerAllTools, registerCompanyTools, registerContactTools, registerCrmResources, registerDealTools, registerNoteTools, registerPipelineTools, registerSavedViewTools, registerSearchTools, registerSummarizePipelinePrompt, registerTaskTools, runCli, scopeSatisfies, startMcpHttpServer, toActivityListQuery, toCrmListQuery, toMcpToolError, toSearchParams };