@hasna/microservices 0.0.10 → 0.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/bin/index.js +86 -1
  2. package/bin/mcp.js +86 -1
  3. package/dist/index.js +86 -1
  4. package/microservices/microservice-analytics/package.json +27 -0
  5. package/microservices/microservice-analytics/src/cli/index.ts +373 -0
  6. package/microservices/microservice-analytics/src/db/analytics.ts +564 -0
  7. package/microservices/microservice-analytics/src/db/database.ts +93 -0
  8. package/microservices/microservice-analytics/src/db/migrations.ts +50 -0
  9. package/microservices/microservice-analytics/src/index.ts +37 -0
  10. package/microservices/microservice-analytics/src/mcp/index.ts +334 -0
  11. package/microservices/microservice-assets/package.json +27 -0
  12. package/microservices/microservice-assets/src/cli/index.ts +375 -0
  13. package/microservices/microservice-assets/src/db/assets.ts +370 -0
  14. package/microservices/microservice-assets/src/db/database.ts +93 -0
  15. package/microservices/microservice-assets/src/db/migrations.ts +51 -0
  16. package/microservices/microservice-assets/src/index.ts +32 -0
  17. package/microservices/microservice-assets/src/mcp/index.ts +346 -0
  18. package/microservices/microservice-compliance/package.json +27 -0
  19. package/microservices/microservice-compliance/src/cli/index.ts +467 -0
  20. package/microservices/microservice-compliance/src/db/compliance.ts +633 -0
  21. package/microservices/microservice-compliance/src/db/database.ts +93 -0
  22. package/microservices/microservice-compliance/src/db/migrations.ts +63 -0
  23. package/microservices/microservice-compliance/src/index.ts +46 -0
  24. package/microservices/microservice-compliance/src/mcp/index.ts +438 -0
  25. package/microservices/microservice-habits/package.json +27 -0
  26. package/microservices/microservice-habits/src/cli/index.ts +315 -0
  27. package/microservices/microservice-habits/src/db/database.ts +93 -0
  28. package/microservices/microservice-habits/src/db/habits.ts +451 -0
  29. package/microservices/microservice-habits/src/db/migrations.ts +46 -0
  30. package/microservices/microservice-habits/src/index.ts +31 -0
  31. package/microservices/microservice-habits/src/mcp/index.ts +313 -0
  32. package/microservices/microservice-health/package.json +27 -0
  33. package/microservices/microservice-health/src/cli/index.ts +484 -0
  34. package/microservices/microservice-health/src/db/database.ts +93 -0
  35. package/microservices/microservice-health/src/db/health.ts +708 -0
  36. package/microservices/microservice-health/src/db/migrations.ts +70 -0
  37. package/microservices/microservice-health/src/index.ts +63 -0
  38. package/microservices/microservice-health/src/mcp/index.ts +437 -0
  39. package/microservices/microservice-notifications/package.json +27 -0
  40. package/microservices/microservice-notifications/src/cli/index.ts +349 -0
  41. package/microservices/microservice-notifications/src/db/database.ts +93 -0
  42. package/microservices/microservice-notifications/src/db/migrations.ts +62 -0
  43. package/microservices/microservice-notifications/src/db/notifications.ts +509 -0
  44. package/microservices/microservice-notifications/src/index.ts +41 -0
  45. package/microservices/microservice-notifications/src/mcp/index.ts +422 -0
  46. package/microservices/microservice-products/package.json +27 -0
  47. package/microservices/microservice-products/src/cli/index.ts +416 -0
  48. package/microservices/microservice-products/src/db/categories.ts +154 -0
  49. package/microservices/microservice-products/src/db/database.ts +93 -0
  50. package/microservices/microservice-products/src/db/migrations.ts +58 -0
  51. package/microservices/microservice-products/src/db/pricing-tiers.ts +66 -0
  52. package/microservices/microservice-products/src/db/products.ts +452 -0
  53. package/microservices/microservice-products/src/index.ts +53 -0
  54. package/microservices/microservice-products/src/mcp/index.ts +453 -0
  55. package/microservices/microservice-projects/package.json +27 -0
  56. package/microservices/microservice-projects/src/cli/index.ts +480 -0
  57. package/microservices/microservice-projects/src/db/database.ts +93 -0
  58. package/microservices/microservice-projects/src/db/migrations.ts +65 -0
  59. package/microservices/microservice-projects/src/db/projects.ts +715 -0
  60. package/microservices/microservice-projects/src/index.ts +57 -0
  61. package/microservices/microservice-projects/src/mcp/index.ts +501 -0
  62. package/microservices/microservice-proposals/package.json +27 -0
  63. package/microservices/microservice-proposals/src/cli/index.ts +400 -0
  64. package/microservices/microservice-proposals/src/db/database.ts +93 -0
  65. package/microservices/microservice-proposals/src/db/migrations.ts +52 -0
  66. package/microservices/microservice-proposals/src/db/proposals.ts +532 -0
  67. package/microservices/microservice-proposals/src/index.ts +37 -0
  68. package/microservices/microservice-proposals/src/mcp/index.ts +375 -0
  69. package/microservices/microservice-reading/package.json +27 -0
  70. package/microservices/microservice-reading/src/cli/index.ts +464 -0
  71. package/microservices/microservice-reading/src/db/database.ts +93 -0
  72. package/microservices/microservice-reading/src/db/migrations.ts +59 -0
  73. package/microservices/microservice-reading/src/db/reading.ts +524 -0
  74. package/microservices/microservice-reading/src/index.ts +51 -0
  75. package/microservices/microservice-reading/src/mcp/index.ts +368 -0
  76. package/microservices/microservice-travel/package.json +27 -0
  77. package/microservices/microservice-travel/src/cli/index.ts +505 -0
  78. package/microservices/microservice-travel/src/db/database.ts +93 -0
  79. package/microservices/microservice-travel/src/db/migrations.ts +77 -0
  80. package/microservices/microservice-travel/src/db/travel.ts +802 -0
  81. package/microservices/microservice-travel/src/index.ts +60 -0
  82. package/microservices/microservice-travel/src/mcp/index.ts +495 -0
  83. package/microservices/microservice-wiki/package.json +27 -0
  84. package/microservices/microservice-wiki/src/cli/index.ts +345 -0
  85. package/microservices/microservice-wiki/src/db/database.ts +93 -0
  86. package/microservices/microservice-wiki/src/db/migrations.ts +55 -0
  87. package/microservices/microservice-wiki/src/db/wiki.ts +395 -0
  88. package/microservices/microservice-wiki/src/index.ts +32 -0
  89. package/microservices/microservice-wiki/src/mcp/index.ts +344 -0
  90. package/package.json +1 -1
@@ -0,0 +1,532 @@
1
+ /**
2
+ * Proposal CRUD operations and business logic
3
+ */
4
+
5
+ import { getDatabase } from "./database.js";
6
+
7
+ // --- Types ---
8
+
9
+ export type ProposalStatus = "draft" | "sent" | "viewed" | "accepted" | "declined" | "expired";
10
+
11
+ export interface ProposalItem {
12
+ description: string;
13
+ quantity: number;
14
+ unit_price: number;
15
+ amount: number;
16
+ }
17
+
18
+ export interface Proposal {
19
+ id: string;
20
+ title: string;
21
+ client_name: string;
22
+ client_email: string | null;
23
+ status: ProposalStatus;
24
+ items: ProposalItem[];
25
+ subtotal: number;
26
+ tax_rate: number;
27
+ tax_amount: number;
28
+ discount: number;
29
+ total: number;
30
+ currency: string;
31
+ valid_until: string | null;
32
+ notes: string | null;
33
+ terms: string | null;
34
+ sent_at: string | null;
35
+ viewed_at: string | null;
36
+ responded_at: string | null;
37
+ metadata: Record<string, unknown>;
38
+ created_at: string;
39
+ updated_at: string;
40
+ }
41
+
42
+ interface ProposalRow {
43
+ id: string;
44
+ title: string;
45
+ client_name: string;
46
+ client_email: string | null;
47
+ status: ProposalStatus;
48
+ items: string;
49
+ subtotal: number;
50
+ tax_rate: number;
51
+ tax_amount: number;
52
+ discount: number;
53
+ total: number;
54
+ currency: string;
55
+ valid_until: string | null;
56
+ notes: string | null;
57
+ terms: string | null;
58
+ sent_at: string | null;
59
+ viewed_at: string | null;
60
+ responded_at: string | null;
61
+ metadata: string;
62
+ created_at: string;
63
+ updated_at: string;
64
+ }
65
+
66
+ function rowToProposal(row: ProposalRow): Proposal {
67
+ return {
68
+ ...row,
69
+ items: JSON.parse(row.items || "[]"),
70
+ metadata: JSON.parse(row.metadata || "{}"),
71
+ };
72
+ }
73
+
74
+ function calculateTotals(items: ProposalItem[], taxRate: number, discount: number) {
75
+ const subtotal = items.reduce((sum, item) => sum + item.amount, 0);
76
+ const taxAmount = (subtotal - discount) * (taxRate / 100);
77
+ const total = subtotal - discount + taxAmount;
78
+ return { subtotal, tax_amount: taxAmount, total };
79
+ }
80
+
81
+ // --- Proposal Templates ---
82
+
83
+ export interface ProposalTemplate {
84
+ id: string;
85
+ name: string;
86
+ items: ProposalItem[];
87
+ terms: string | null;
88
+ notes: string | null;
89
+ created_at: string;
90
+ }
91
+
92
+ interface ProposalTemplateRow {
93
+ id: string;
94
+ name: string;
95
+ items: string;
96
+ terms: string | null;
97
+ notes: string | null;
98
+ created_at: string;
99
+ }
100
+
101
+ function rowToTemplate(row: ProposalTemplateRow): ProposalTemplate {
102
+ return {
103
+ ...row,
104
+ items: JSON.parse(row.items || "[]"),
105
+ };
106
+ }
107
+
108
+ export interface CreateTemplateInput {
109
+ name: string;
110
+ items?: ProposalItem[];
111
+ terms?: string;
112
+ notes?: string;
113
+ }
114
+
115
+ export function createTemplate(input: CreateTemplateInput): ProposalTemplate {
116
+ const db = getDatabase();
117
+ const id = crypto.randomUUID();
118
+
119
+ db.prepare(
120
+ `INSERT INTO proposal_templates (id, name, items, terms, notes)
121
+ VALUES (?, ?, ?, ?, ?)`
122
+ ).run(
123
+ id,
124
+ input.name,
125
+ JSON.stringify(input.items || []),
126
+ input.terms || null,
127
+ input.notes || null
128
+ );
129
+
130
+ return getTemplate(id)!;
131
+ }
132
+
133
+ export function getTemplate(id: string): ProposalTemplate | null {
134
+ const db = getDatabase();
135
+ const row = db.prepare("SELECT * FROM proposal_templates WHERE id = ?").get(id) as ProposalTemplateRow | null;
136
+ return row ? rowToTemplate(row) : null;
137
+ }
138
+
139
+ export function listTemplates(): ProposalTemplate[] {
140
+ const db = getDatabase();
141
+ const rows = db.prepare("SELECT * FROM proposal_templates ORDER BY name").all() as ProposalTemplateRow[];
142
+ return rows.map(rowToTemplate);
143
+ }
144
+
145
+ export function deleteTemplate(id: string): boolean {
146
+ const db = getDatabase();
147
+ const result = db.prepare("DELETE FROM proposal_templates WHERE id = ?").run(id);
148
+ return result.changes > 0;
149
+ }
150
+
151
+ // --- Proposal CRUD ---
152
+
153
+ export interface CreateProposalInput {
154
+ title: string;
155
+ client_name: string;
156
+ client_email?: string;
157
+ items?: ProposalItem[];
158
+ tax_rate?: number;
159
+ discount?: number;
160
+ currency?: string;
161
+ valid_until?: string;
162
+ notes?: string;
163
+ terms?: string;
164
+ metadata?: Record<string, unknown>;
165
+ }
166
+
167
+ export function createProposal(input: CreateProposalInput): Proposal {
168
+ const db = getDatabase();
169
+ const id = crypto.randomUUID();
170
+ const items = input.items || [];
171
+ const taxRate = input.tax_rate || 0;
172
+ const discount = input.discount || 0;
173
+ const { subtotal, tax_amount, total } = calculateTotals(items, taxRate, discount);
174
+ const metadata = JSON.stringify(input.metadata || {});
175
+
176
+ db.prepare(
177
+ `INSERT INTO proposals (id, title, client_name, client_email, items, subtotal, tax_rate, tax_amount, discount, total, currency, valid_until, notes, terms, metadata)
178
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
179
+ ).run(
180
+ id,
181
+ input.title,
182
+ input.client_name,
183
+ input.client_email || null,
184
+ JSON.stringify(items),
185
+ subtotal,
186
+ taxRate,
187
+ tax_amount,
188
+ discount,
189
+ total,
190
+ input.currency || "USD",
191
+ input.valid_until || null,
192
+ input.notes || null,
193
+ input.terms || null,
194
+ metadata
195
+ );
196
+
197
+ return getProposal(id)!;
198
+ }
199
+
200
+ export function getProposal(id: string): Proposal | null {
201
+ const db = getDatabase();
202
+ const row = db.prepare("SELECT * FROM proposals WHERE id = ?").get(id) as ProposalRow | null;
203
+ return row ? rowToProposal(row) : null;
204
+ }
205
+
206
+ export interface ListProposalsOptions {
207
+ status?: ProposalStatus;
208
+ client_name?: string;
209
+ search?: string;
210
+ limit?: number;
211
+ offset?: number;
212
+ }
213
+
214
+ export function listProposals(options: ListProposalsOptions = {}): Proposal[] {
215
+ const db = getDatabase();
216
+ const conditions: string[] = [];
217
+ const params: unknown[] = [];
218
+
219
+ if (options.status) {
220
+ conditions.push("status = ?");
221
+ params.push(options.status);
222
+ }
223
+
224
+ if (options.client_name) {
225
+ conditions.push("client_name = ?");
226
+ params.push(options.client_name);
227
+ }
228
+
229
+ if (options.search) {
230
+ conditions.push(
231
+ "(title LIKE ? OR client_name LIKE ? OR client_email LIKE ? OR notes LIKE ?)"
232
+ );
233
+ const q = `%${options.search}%`;
234
+ params.push(q, q, q, q);
235
+ }
236
+
237
+ let sql = "SELECT * FROM proposals";
238
+ if (conditions.length > 0) {
239
+ sql += " WHERE " + conditions.join(" AND ");
240
+ }
241
+ sql += " ORDER BY created_at DESC";
242
+
243
+ if (options.limit) {
244
+ sql += " LIMIT ?";
245
+ params.push(options.limit);
246
+ }
247
+ if (options.offset) {
248
+ sql += " OFFSET ?";
249
+ params.push(options.offset);
250
+ }
251
+
252
+ const rows = db.prepare(sql).all(...params) as ProposalRow[];
253
+ return rows.map(rowToProposal);
254
+ }
255
+
256
+ export interface UpdateProposalInput {
257
+ title?: string;
258
+ client_name?: string;
259
+ client_email?: string;
260
+ items?: ProposalItem[];
261
+ tax_rate?: number;
262
+ discount?: number;
263
+ currency?: string;
264
+ valid_until?: string;
265
+ notes?: string;
266
+ terms?: string;
267
+ metadata?: Record<string, unknown>;
268
+ }
269
+
270
+ export function updateProposal(
271
+ id: string,
272
+ input: UpdateProposalInput
273
+ ): Proposal | null {
274
+ const db = getDatabase();
275
+ const existing = getProposal(id);
276
+ if (!existing) return null;
277
+
278
+ const sets: string[] = [];
279
+ const params: unknown[] = [];
280
+
281
+ if (input.title !== undefined) {
282
+ sets.push("title = ?");
283
+ params.push(input.title);
284
+ }
285
+ if (input.client_name !== undefined) {
286
+ sets.push("client_name = ?");
287
+ params.push(input.client_name);
288
+ }
289
+ if (input.client_email !== undefined) {
290
+ sets.push("client_email = ?");
291
+ params.push(input.client_email);
292
+ }
293
+ if (input.currency !== undefined) {
294
+ sets.push("currency = ?");
295
+ params.push(input.currency);
296
+ }
297
+ if (input.valid_until !== undefined) {
298
+ sets.push("valid_until = ?");
299
+ params.push(input.valid_until);
300
+ }
301
+ if (input.notes !== undefined) {
302
+ sets.push("notes = ?");
303
+ params.push(input.notes);
304
+ }
305
+ if (input.terms !== undefined) {
306
+ sets.push("terms = ?");
307
+ params.push(input.terms);
308
+ }
309
+ if (input.metadata !== undefined) {
310
+ sets.push("metadata = ?");
311
+ params.push(JSON.stringify(input.metadata));
312
+ }
313
+
314
+ // Recalculate totals if items, tax_rate, or discount changed
315
+ const items = input.items !== undefined ? input.items : existing.items;
316
+ const taxRate = input.tax_rate !== undefined ? input.tax_rate : existing.tax_rate;
317
+ const discount = input.discount !== undefined ? input.discount : existing.discount;
318
+
319
+ if (input.items !== undefined || input.tax_rate !== undefined || input.discount !== undefined) {
320
+ const { subtotal, tax_amount, total } = calculateTotals(items, taxRate, discount);
321
+ sets.push("items = ?");
322
+ params.push(JSON.stringify(items));
323
+ sets.push("subtotal = ?");
324
+ params.push(subtotal);
325
+ sets.push("tax_rate = ?");
326
+ params.push(taxRate);
327
+ sets.push("tax_amount = ?");
328
+ params.push(tax_amount);
329
+ sets.push("discount = ?");
330
+ params.push(discount);
331
+ sets.push("total = ?");
332
+ params.push(total);
333
+ }
334
+
335
+ if (sets.length === 0) return existing;
336
+
337
+ sets.push("updated_at = datetime('now')");
338
+ params.push(id);
339
+
340
+ db.prepare(
341
+ `UPDATE proposals SET ${sets.join(", ")} WHERE id = ?`
342
+ ).run(...params);
343
+
344
+ return getProposal(id);
345
+ }
346
+
347
+ export function deleteProposal(id: string): boolean {
348
+ const db = getDatabase();
349
+ const result = db.prepare("DELETE FROM proposals WHERE id = ?").run(id);
350
+ return result.changes > 0;
351
+ }
352
+
353
+ // --- Business Logic ---
354
+
355
+ export function sendProposal(id: string): Proposal | null {
356
+ const db = getDatabase();
357
+ const existing = getProposal(id);
358
+ if (!existing) return null;
359
+
360
+ db.prepare(
361
+ `UPDATE proposals SET status = 'sent', sent_at = datetime('now'), updated_at = datetime('now') WHERE id = ?`
362
+ ).run(id);
363
+
364
+ return getProposal(id);
365
+ }
366
+
367
+ export function markViewed(id: string): Proposal | null {
368
+ const db = getDatabase();
369
+ const existing = getProposal(id);
370
+ if (!existing) return null;
371
+
372
+ db.prepare(
373
+ `UPDATE proposals SET status = 'viewed', viewed_at = datetime('now'), updated_at = datetime('now') WHERE id = ?`
374
+ ).run(id);
375
+
376
+ return getProposal(id);
377
+ }
378
+
379
+ export function acceptProposal(id: string): Proposal | null {
380
+ const db = getDatabase();
381
+ const existing = getProposal(id);
382
+ if (!existing) return null;
383
+
384
+ db.prepare(
385
+ `UPDATE proposals SET status = 'accepted', responded_at = datetime('now'), updated_at = datetime('now') WHERE id = ?`
386
+ ).run(id);
387
+
388
+ return getProposal(id);
389
+ }
390
+
391
+ export function declineProposal(id: string, reason?: string): Proposal | null {
392
+ const db = getDatabase();
393
+ const existing = getProposal(id);
394
+ if (!existing) return null;
395
+
396
+ const metadata = { ...existing.metadata, decline_reason: reason || null };
397
+
398
+ db.prepare(
399
+ `UPDATE proposals SET status = 'declined', responded_at = datetime('now'), metadata = ?, updated_at = datetime('now') WHERE id = ?`
400
+ ).run(JSON.stringify(metadata), id);
401
+
402
+ return getProposal(id);
403
+ }
404
+
405
+ export interface InvoiceData {
406
+ client_name: string;
407
+ client_email: string | null;
408
+ items: ProposalItem[];
409
+ subtotal: number;
410
+ tax_rate: number;
411
+ tax_amount: number;
412
+ discount: number;
413
+ total: number;
414
+ currency: string;
415
+ notes: string | null;
416
+ terms: string | null;
417
+ proposal_id: string;
418
+ }
419
+
420
+ export function convertToInvoice(id: string): InvoiceData | null {
421
+ const proposal = getProposal(id);
422
+ if (!proposal) return null;
423
+
424
+ return {
425
+ client_name: proposal.client_name,
426
+ client_email: proposal.client_email,
427
+ items: proposal.items,
428
+ subtotal: proposal.subtotal,
429
+ tax_rate: proposal.tax_rate,
430
+ tax_amount: proposal.tax_amount,
431
+ discount: proposal.discount,
432
+ total: proposal.total,
433
+ currency: proposal.currency,
434
+ notes: proposal.notes,
435
+ terms: proposal.terms,
436
+ proposal_id: proposal.id,
437
+ };
438
+ }
439
+
440
+ export function listExpiring(days: number): Proposal[] {
441
+ const db = getDatabase();
442
+ const rows = db.prepare(
443
+ `SELECT * FROM proposals
444
+ WHERE status IN ('draft', 'sent', 'viewed')
445
+ AND valid_until IS NOT NULL
446
+ AND valid_until <= datetime('now', '+' || ? || ' days')
447
+ AND valid_until >= datetime('now')
448
+ ORDER BY valid_until ASC`
449
+ ).all(days) as ProposalRow[];
450
+ return rows.map(rowToProposal);
451
+ }
452
+
453
+ export interface ProposalStats {
454
+ total: number;
455
+ by_status: Record<ProposalStatus, number>;
456
+ total_value: number;
457
+ average_value: number;
458
+ conversion_rate: number;
459
+ accepted_value: number;
460
+ }
461
+
462
+ export function getProposalStats(): ProposalStats {
463
+ const db = getDatabase();
464
+
465
+ const totalRow = db.prepare("SELECT COUNT(*) as count FROM proposals").get() as { count: number };
466
+ const total = totalRow.count;
467
+
468
+ const statusRows = db.prepare(
469
+ "SELECT status, COUNT(*) as count FROM proposals GROUP BY status"
470
+ ).all() as { status: ProposalStatus; count: number }[];
471
+
472
+ const byStatus: Record<ProposalStatus, number> = {
473
+ draft: 0,
474
+ sent: 0,
475
+ viewed: 0,
476
+ accepted: 0,
477
+ declined: 0,
478
+ expired: 0,
479
+ };
480
+ for (const row of statusRows) {
481
+ byStatus[row.status] = row.count;
482
+ }
483
+
484
+ const valueRow = db.prepare(
485
+ "SELECT COALESCE(SUM(total), 0) as total_value, COALESCE(AVG(total), 0) as avg_value FROM proposals"
486
+ ).get() as { total_value: number; avg_value: number };
487
+
488
+ const acceptedValueRow = db.prepare(
489
+ "SELECT COALESCE(SUM(total), 0) as accepted_value FROM proposals WHERE status = 'accepted'"
490
+ ).get() as { accepted_value: number };
491
+
492
+ // Conversion rate: accepted / (accepted + declined)
493
+ const decided = byStatus.accepted + byStatus.declined;
494
+ const conversionRate = decided > 0 ? (byStatus.accepted / decided) * 100 : 0;
495
+
496
+ return {
497
+ total,
498
+ by_status: byStatus,
499
+ total_value: valueRow.total_value,
500
+ average_value: valueRow.avg_value,
501
+ conversion_rate: conversionRate,
502
+ accepted_value: acceptedValueRow.accepted_value,
503
+ };
504
+ }
505
+
506
+ export function searchProposals(query: string): Proposal[] {
507
+ return listProposals({ search: query });
508
+ }
509
+
510
+ export function useTemplate(
511
+ templateId: string,
512
+ overrides: { title: string; client_name: string; client_email?: string; valid_until?: string }
513
+ ): Proposal | null {
514
+ const template = getTemplate(templateId);
515
+ if (!template) return null;
516
+
517
+ return createProposal({
518
+ title: overrides.title,
519
+ client_name: overrides.client_name,
520
+ client_email: overrides.client_email,
521
+ items: template.items,
522
+ notes: template.notes || undefined,
523
+ terms: template.terms || undefined,
524
+ valid_until: overrides.valid_until,
525
+ });
526
+ }
527
+
528
+ export function countProposals(): number {
529
+ const db = getDatabase();
530
+ const row = db.prepare("SELECT COUNT(*) as count FROM proposals").get() as { count: number };
531
+ return row.count;
532
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * microservice-proposals — Proposal management microservice
3
+ */
4
+
5
+ export {
6
+ createProposal,
7
+ getProposal,
8
+ listProposals,
9
+ updateProposal,
10
+ deleteProposal,
11
+ sendProposal,
12
+ markViewed,
13
+ acceptProposal,
14
+ declineProposal,
15
+ convertToInvoice,
16
+ listExpiring,
17
+ getProposalStats,
18
+ searchProposals,
19
+ countProposals,
20
+ createTemplate,
21
+ getTemplate,
22
+ listTemplates,
23
+ deleteTemplate,
24
+ useTemplate,
25
+ type Proposal,
26
+ type ProposalItem,
27
+ type ProposalStatus,
28
+ type ProposalStats,
29
+ type InvoiceData,
30
+ type ProposalTemplate,
31
+ type CreateProposalInput,
32
+ type UpdateProposalInput,
33
+ type ListProposalsOptions,
34
+ type CreateTemplateInput,
35
+ } from "./db/proposals.js";
36
+
37
+ export { getDatabase, closeDatabase } from "./db/database.js";