@hasna/microservices 0.0.2 → 0.0.4

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 (89) hide show
  1. package/bin/index.js +70 -0
  2. package/bin/mcp.js +71 -1
  3. package/dist/index.js +70 -0
  4. package/microservices/microservice-ads/package.json +27 -0
  5. package/microservices/microservice-ads/src/cli/index.ts +407 -0
  6. package/microservices/microservice-ads/src/db/campaigns.ts +493 -0
  7. package/microservices/microservice-ads/src/db/database.ts +93 -0
  8. package/microservices/microservice-ads/src/db/migrations.ts +60 -0
  9. package/microservices/microservice-ads/src/index.ts +39 -0
  10. package/microservices/microservice-ads/src/mcp/index.ts +320 -0
  11. package/microservices/microservice-contracts/package.json +27 -0
  12. package/microservices/microservice-contracts/src/cli/index.ts +383 -0
  13. package/microservices/microservice-contracts/src/db/contracts.ts +496 -0
  14. package/microservices/microservice-contracts/src/db/database.ts +93 -0
  15. package/microservices/microservice-contracts/src/db/migrations.ts +58 -0
  16. package/microservices/microservice-contracts/src/index.ts +43 -0
  17. package/microservices/microservice-contracts/src/mcp/index.ts +308 -0
  18. package/microservices/microservice-domains/package.json +27 -0
  19. package/microservices/microservice-domains/src/cli/index.ts +438 -0
  20. package/microservices/microservice-domains/src/db/database.ts +93 -0
  21. package/microservices/microservice-domains/src/db/domains.ts +551 -0
  22. package/microservices/microservice-domains/src/db/migrations.ts +60 -0
  23. package/microservices/microservice-domains/src/index.ts +44 -0
  24. package/microservices/microservice-domains/src/mcp/index.ts +368 -0
  25. package/microservices/microservice-hiring/package.json +27 -0
  26. package/microservices/microservice-hiring/src/cli/index.ts +431 -0
  27. package/microservices/microservice-hiring/src/db/database.ts +93 -0
  28. package/microservices/microservice-hiring/src/db/hiring.ts +582 -0
  29. package/microservices/microservice-hiring/src/db/migrations.ts +68 -0
  30. package/microservices/microservice-hiring/src/index.ts +51 -0
  31. package/microservices/microservice-hiring/src/mcp/index.ts +464 -0
  32. package/microservices/microservice-payments/package.json +27 -0
  33. package/microservices/microservice-payments/src/cli/index.ts +357 -0
  34. package/microservices/microservice-payments/src/db/database.ts +93 -0
  35. package/microservices/microservice-payments/src/db/migrations.ts +63 -0
  36. package/microservices/microservice-payments/src/db/payments.ts +652 -0
  37. package/microservices/microservice-payments/src/index.ts +51 -0
  38. package/microservices/microservice-payments/src/mcp/index.ts +460 -0
  39. package/microservices/microservice-payroll/package.json +27 -0
  40. package/microservices/microservice-payroll/src/cli/index.ts +374 -0
  41. package/microservices/microservice-payroll/src/db/database.ts +93 -0
  42. package/microservices/microservice-payroll/src/db/migrations.ts +69 -0
  43. package/microservices/microservice-payroll/src/db/payroll.ts +741 -0
  44. package/microservices/microservice-payroll/src/index.ts +48 -0
  45. package/microservices/microservice-payroll/src/mcp/index.ts +420 -0
  46. package/microservices/microservice-shipping/package.json +27 -0
  47. package/microservices/microservice-shipping/src/cli/index.ts +398 -0
  48. package/microservices/microservice-shipping/src/db/database.ts +93 -0
  49. package/microservices/microservice-shipping/src/db/migrations.ts +61 -0
  50. package/microservices/microservice-shipping/src/db/shipping.ts +643 -0
  51. package/microservices/microservice-shipping/src/index.ts +53 -0
  52. package/microservices/microservice-shipping/src/mcp/index.ts +385 -0
  53. package/microservices/microservice-social/package.json +27 -0
  54. package/microservices/microservice-social/src/cli/index.ts +447 -0
  55. package/microservices/microservice-social/src/db/database.ts +93 -0
  56. package/microservices/microservice-social/src/db/migrations.ts +55 -0
  57. package/microservices/microservice-social/src/db/social.ts +672 -0
  58. package/microservices/microservice-social/src/index.ts +46 -0
  59. package/microservices/microservice-social/src/mcp/index.ts +435 -0
  60. package/microservices/microservice-subscriptions/package.json +27 -0
  61. package/microservices/microservice-subscriptions/src/cli/index.ts +400 -0
  62. package/microservices/microservice-subscriptions/src/db/database.ts +93 -0
  63. package/microservices/microservice-subscriptions/src/db/migrations.ts +57 -0
  64. package/microservices/microservice-subscriptions/src/db/subscriptions.ts +692 -0
  65. package/microservices/microservice-subscriptions/src/index.ts +41 -0
  66. package/microservices/microservice-subscriptions/src/mcp/index.ts +365 -0
  67. package/microservices/microservice-transcriber/package.json +28 -0
  68. package/microservices/microservice-transcriber/src/cli/index.ts +1347 -0
  69. package/microservices/microservice-transcriber/src/db/annotations.ts +37 -0
  70. package/microservices/microservice-transcriber/src/db/database.ts +82 -0
  71. package/microservices/microservice-transcriber/src/db/migrations.ts +72 -0
  72. package/microservices/microservice-transcriber/src/db/transcripts.ts +395 -0
  73. package/microservices/microservice-transcriber/src/index.ts +43 -0
  74. package/microservices/microservice-transcriber/src/lib/config.ts +77 -0
  75. package/microservices/microservice-transcriber/src/lib/diff.ts +91 -0
  76. package/microservices/microservice-transcriber/src/lib/downloader.ts +570 -0
  77. package/microservices/microservice-transcriber/src/lib/feeds.ts +62 -0
  78. package/microservices/microservice-transcriber/src/lib/live.ts +94 -0
  79. package/microservices/microservice-transcriber/src/lib/notion.ts +129 -0
  80. package/microservices/microservice-transcriber/src/lib/providers.ts +713 -0
  81. package/microservices/microservice-transcriber/src/lib/summarizer.ts +147 -0
  82. package/microservices/microservice-transcriber/src/lib/translator.ts +75 -0
  83. package/microservices/microservice-transcriber/src/lib/webhook.ts +37 -0
  84. package/microservices/microservice-transcriber/src/mcp/index.ts +1070 -0
  85. package/microservices/microservice-transcriber/src/server/index.ts +199 -0
  86. package/package.json +1 -1
  87. package/microservices/microservice-invoices/dashboard/dist/assets/index-Bngq7FNM.css +0 -1
  88. package/microservices/microservice-invoices/dashboard/dist/assets/index-aHW4ARZR.js +0 -124
  89. package/microservices/microservice-invoices/dashboard/dist/index.html +0 -13
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from "commander";
4
+ import {
5
+ createOrder,
6
+ getOrder,
7
+ listOrders,
8
+ updateOrder,
9
+ deleteOrder,
10
+ searchOrders,
11
+ listByStatus,
12
+ } from "../db/shipping.js";
13
+ import {
14
+ createShipment,
15
+ getShipment,
16
+ getShipmentByTracking,
17
+ listShipments,
18
+ updateShipment,
19
+ } from "../db/shipping.js";
20
+ import {
21
+ createReturn,
22
+ listReturns,
23
+ updateReturn,
24
+ } from "../db/shipping.js";
25
+ import {
26
+ getShippingStats,
27
+ getCostsByCarrier,
28
+ } from "../db/shipping.js";
29
+
30
+ const program = new Command();
31
+
32
+ program
33
+ .name("microservice-shipping")
34
+ .description("Shipping and order management microservice")
35
+ .version("0.0.1");
36
+
37
+ // --- Orders ---
38
+
39
+ const orderCmd = program.command("order").description("Order management");
40
+
41
+ orderCmd
42
+ .command("create")
43
+ .description("Create a new order")
44
+ .requiredOption("--customer-name <name>", "Customer name")
45
+ .option("--customer-email <email>", "Customer email")
46
+ .option("--address <json>", "Address as JSON {street,city,state,zip,country}")
47
+ .option("--items <json>", "Items as JSON array [{name,qty,weight,value}]")
48
+ .option("--currency <code>", "Currency code", "USD")
49
+ .option("--json", "Output as JSON", false)
50
+ .action((opts) => {
51
+ const address = opts.address ? JSON.parse(opts.address) : { street: "", city: "", state: "", zip: "", country: "" };
52
+ const items = opts.items ? JSON.parse(opts.items) : [];
53
+
54
+ const order = createOrder({
55
+ customer_name: opts.customerName,
56
+ customer_email: opts.customerEmail,
57
+ address,
58
+ items,
59
+ currency: opts.currency,
60
+ });
61
+
62
+ if (opts.json) {
63
+ console.log(JSON.stringify(order, null, 2));
64
+ } else {
65
+ console.log(`Created order: ${order.id} for ${order.customer_name} ($${order.total_value} ${order.currency})`);
66
+ }
67
+ });
68
+
69
+ orderCmd
70
+ .command("list")
71
+ .description("List orders")
72
+ .option("--status <status>", "Filter by status")
73
+ .option("--search <query>", "Search by customer name or email")
74
+ .option("--limit <n>", "Limit results")
75
+ .option("--json", "Output as JSON", false)
76
+ .action((opts) => {
77
+ const orders = listOrders({
78
+ status: opts.status,
79
+ search: opts.search,
80
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
81
+ });
82
+
83
+ if (opts.json) {
84
+ console.log(JSON.stringify(orders, null, 2));
85
+ } else {
86
+ if (orders.length === 0) {
87
+ console.log("No orders found.");
88
+ return;
89
+ }
90
+ for (const o of orders) {
91
+ console.log(` [${o.status}] ${o.id.slice(0, 8)}... ${o.customer_name} — $${o.total_value} ${o.currency}`);
92
+ }
93
+ console.log(`\n${orders.length} order(s)`);
94
+ }
95
+ });
96
+
97
+ orderCmd
98
+ .command("get")
99
+ .description("Get an order by ID")
100
+ .argument("<id>", "Order ID")
101
+ .option("--json", "Output as JSON", false)
102
+ .action((id, opts) => {
103
+ const order = getOrder(id);
104
+ if (!order) {
105
+ console.error(`Order '${id}' not found.`);
106
+ process.exit(1);
107
+ }
108
+
109
+ if (opts.json) {
110
+ console.log(JSON.stringify(order, null, 2));
111
+ } else {
112
+ console.log(`Order: ${order.id}`);
113
+ console.log(` Customer: ${order.customer_name}`);
114
+ if (order.customer_email) console.log(` Email: ${order.customer_email}`);
115
+ console.log(` Status: ${order.status}`);
116
+ console.log(` Total: $${order.total_value} ${order.currency}`);
117
+ console.log(` Items: ${order.items.length}`);
118
+ console.log(` Address: ${order.address.street}, ${order.address.city}, ${order.address.state} ${order.address.zip}`);
119
+ }
120
+ });
121
+
122
+ orderCmd
123
+ .command("update")
124
+ .description("Update an order")
125
+ .argument("<id>", "Order ID")
126
+ .option("--customer-name <name>", "Customer name")
127
+ .option("--customer-email <email>", "Customer email")
128
+ .option("--status <status>", "Order status")
129
+ .option("--address <json>", "Address as JSON")
130
+ .option("--items <json>", "Items as JSON")
131
+ .option("--currency <code>", "Currency code")
132
+ .option("--json", "Output as JSON", false)
133
+ .action((id, opts) => {
134
+ const input: Record<string, unknown> = {};
135
+ if (opts.customerName !== undefined) input.customer_name = opts.customerName;
136
+ if (opts.customerEmail !== undefined) input.customer_email = opts.customerEmail;
137
+ if (opts.status !== undefined) input.status = opts.status;
138
+ if (opts.address !== undefined) input.address = JSON.parse(opts.address);
139
+ if (opts.items !== undefined) input.items = JSON.parse(opts.items);
140
+ if (opts.currency !== undefined) input.currency = opts.currency;
141
+
142
+ const order = updateOrder(id, input);
143
+ if (!order) {
144
+ console.error(`Order '${id}' not found.`);
145
+ process.exit(1);
146
+ }
147
+
148
+ if (opts.json) {
149
+ console.log(JSON.stringify(order, null, 2));
150
+ } else {
151
+ console.log(`Updated order: ${order.id} [${order.status}]`);
152
+ }
153
+ });
154
+
155
+ // --- Ship (create shipment) ---
156
+
157
+ program
158
+ .command("ship")
159
+ .description("Create a shipment for an order")
160
+ .requiredOption("--order <id>", "Order ID")
161
+ .requiredOption("--carrier <carrier>", "Carrier (ups/fedex/usps/dhl)")
162
+ .option("--tracking <number>", "Tracking number")
163
+ .option("--service <service>", "Service level (ground/express/overnight)", "ground")
164
+ .option("--cost <amount>", "Shipping cost")
165
+ .option("--weight <kg>", "Package weight")
166
+ .option("--estimated-delivery <date>", "Estimated delivery date")
167
+ .option("--json", "Output as JSON", false)
168
+ .action((opts) => {
169
+ const shipment = createShipment({
170
+ order_id: opts.order,
171
+ carrier: opts.carrier,
172
+ tracking_number: opts.tracking,
173
+ service: opts.service,
174
+ cost: opts.cost ? parseFloat(opts.cost) : undefined,
175
+ weight: opts.weight ? parseFloat(opts.weight) : undefined,
176
+ estimated_delivery: opts.estimatedDelivery,
177
+ });
178
+
179
+ if (opts.json) {
180
+ console.log(JSON.stringify(shipment, null, 2));
181
+ } else {
182
+ console.log(`Created shipment: ${shipment.id} via ${shipment.carrier} (${shipment.service})`);
183
+ if (shipment.tracking_number) console.log(` Tracking: ${shipment.tracking_number}`);
184
+ }
185
+ });
186
+
187
+ // --- Track ---
188
+
189
+ program
190
+ .command("track")
191
+ .description("Track a shipment by tracking number or shipment ID")
192
+ .argument("<identifier>", "Tracking number or shipment ID")
193
+ .option("--json", "Output as JSON", false)
194
+ .action((identifier, opts) => {
195
+ let shipment = getShipmentByTracking(identifier);
196
+ if (!shipment) {
197
+ shipment = getShipment(identifier);
198
+ }
199
+ if (!shipment) {
200
+ console.error(`Shipment '${identifier}' not found.`);
201
+ process.exit(1);
202
+ }
203
+
204
+ if (opts.json) {
205
+ console.log(JSON.stringify(shipment, null, 2));
206
+ } else {
207
+ console.log(`Shipment: ${shipment.id}`);
208
+ console.log(` Carrier: ${shipment.carrier}`);
209
+ console.log(` Service: ${shipment.service}`);
210
+ console.log(` Status: ${shipment.status}`);
211
+ if (shipment.tracking_number) console.log(` Tracking: ${shipment.tracking_number}`);
212
+ if (shipment.shipped_at) console.log(` Shipped: ${shipment.shipped_at}`);
213
+ if (shipment.estimated_delivery) console.log(` ETA: ${shipment.estimated_delivery}`);
214
+ if (shipment.delivered_at) console.log(` Delivered: ${shipment.delivered_at}`);
215
+ }
216
+ });
217
+
218
+ // --- Shipments ---
219
+
220
+ const shipmentsCmd = program.command("shipments").description("Shipment management");
221
+
222
+ shipmentsCmd
223
+ .command("list")
224
+ .description("List shipments")
225
+ .option("--order <id>", "Filter by order ID")
226
+ .option("--carrier <carrier>", "Filter by carrier")
227
+ .option("--status <status>", "Filter by status")
228
+ .option("--limit <n>", "Limit results")
229
+ .option("--json", "Output as JSON", false)
230
+ .action((opts) => {
231
+ const shipments = listShipments({
232
+ order_id: opts.order,
233
+ carrier: opts.carrier,
234
+ status: opts.status,
235
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
236
+ });
237
+
238
+ if (opts.json) {
239
+ console.log(JSON.stringify(shipments, null, 2));
240
+ } else {
241
+ if (shipments.length === 0) {
242
+ console.log("No shipments found.");
243
+ return;
244
+ }
245
+ for (const s of shipments) {
246
+ const tracking = s.tracking_number ? ` (${s.tracking_number})` : "";
247
+ console.log(` [${s.status}] ${s.id.slice(0, 8)}... ${s.carrier}/${s.service}${tracking}`);
248
+ }
249
+ console.log(`\n${shipments.length} shipment(s)`);
250
+ }
251
+ });
252
+
253
+ // --- Returns ---
254
+
255
+ const returnCmd = program.command("return").description("Return management");
256
+
257
+ returnCmd
258
+ .command("create")
259
+ .description("Create a return request")
260
+ .requiredOption("--order <id>", "Order ID")
261
+ .option("--reason <reason>", "Return reason")
262
+ .option("--json", "Output as JSON", false)
263
+ .action((opts) => {
264
+ const ret = createReturn({
265
+ order_id: opts.order,
266
+ reason: opts.reason,
267
+ });
268
+
269
+ if (opts.json) {
270
+ console.log(JSON.stringify(ret, null, 2));
271
+ } else {
272
+ console.log(`Created return: ${ret.id} for order ${ret.order_id} [${ret.status}]`);
273
+ }
274
+ });
275
+
276
+ returnCmd
277
+ .command("list")
278
+ .description("List returns")
279
+ .option("--order <id>", "Filter by order ID")
280
+ .option("--status <status>", "Filter by status")
281
+ .option("--limit <n>", "Limit results")
282
+ .option("--json", "Output as JSON", false)
283
+ .action((opts) => {
284
+ const returns = listReturns({
285
+ order_id: opts.order,
286
+ status: opts.status,
287
+ limit: opts.limit ? parseInt(opts.limit) : undefined,
288
+ });
289
+
290
+ if (opts.json) {
291
+ console.log(JSON.stringify(returns, null, 2));
292
+ } else {
293
+ if (returns.length === 0) {
294
+ console.log("No returns found.");
295
+ return;
296
+ }
297
+ for (const r of returns) {
298
+ const reason = r.reason ? ` — ${r.reason}` : "";
299
+ console.log(` [${r.status}] ${r.id.slice(0, 8)}... order ${r.order_id.slice(0, 8)}...${reason}`);
300
+ }
301
+ console.log(`\n${returns.length} return(s)`);
302
+ }
303
+ });
304
+
305
+ returnCmd
306
+ .command("process")
307
+ .description("Process a return (update status)")
308
+ .argument("<id>", "Return ID")
309
+ .requiredOption("--status <status>", "New status (approved/received/refunded)")
310
+ .option("--json", "Output as JSON", false)
311
+ .action((id, opts) => {
312
+ const ret = updateReturn(id, { status: opts.status });
313
+ if (!ret) {
314
+ console.error(`Return '${id}' not found.`);
315
+ process.exit(1);
316
+ }
317
+
318
+ if (opts.json) {
319
+ console.log(JSON.stringify(ret, null, 2));
320
+ } else {
321
+ console.log(`Updated return: ${ret.id} [${ret.status}]`);
322
+ }
323
+ });
324
+
325
+ // --- Carriers ---
326
+
327
+ program
328
+ .command("carriers")
329
+ .description("List supported carriers")
330
+ .option("--json", "Output as JSON", false)
331
+ .action((opts) => {
332
+ const carriers = [
333
+ { code: "ups", name: "UPS", services: ["ground", "express", "overnight"] },
334
+ { code: "fedex", name: "FedEx", services: ["ground", "express", "overnight"] },
335
+ { code: "usps", name: "USPS", services: ["ground", "express"] },
336
+ { code: "dhl", name: "DHL", services: ["ground", "express", "overnight"] },
337
+ ];
338
+
339
+ if (opts.json) {
340
+ console.log(JSON.stringify(carriers, null, 2));
341
+ } else {
342
+ for (const c of carriers) {
343
+ console.log(` ${c.code.toUpperCase()} (${c.name}) — ${c.services.join(", ")}`);
344
+ }
345
+ }
346
+ });
347
+
348
+ // --- Stats ---
349
+
350
+ program
351
+ .command("stats")
352
+ .description("Show shipping statistics")
353
+ .option("--json", "Output as JSON", false)
354
+ .action((opts) => {
355
+ const stats = getShippingStats();
356
+
357
+ if (opts.json) {
358
+ console.log(JSON.stringify(stats, null, 2));
359
+ } else {
360
+ console.log("Shipping Statistics:");
361
+ console.log(` Orders: ${stats.total_orders}`);
362
+ for (const [status, count] of Object.entries(stats.orders_by_status)) {
363
+ console.log(` ${status}: ${count}`);
364
+ }
365
+ console.log(` Shipments: ${stats.total_shipments}`);
366
+ for (const [status, count] of Object.entries(stats.shipments_by_status)) {
367
+ console.log(` ${status}: ${count}`);
368
+ }
369
+ console.log(` Returns: ${stats.total_returns}`);
370
+ console.log(` Revenue: $${stats.total_revenue.toFixed(2)}`);
371
+ console.log(` Shipping Cost: $${stats.total_shipping_cost.toFixed(2)}`);
372
+ }
373
+ });
374
+
375
+ // --- Costs ---
376
+
377
+ program
378
+ .command("costs")
379
+ .description("Show shipping costs by carrier")
380
+ .option("--json", "Output as JSON", false)
381
+ .action((opts) => {
382
+ const costs = getCostsByCarrier();
383
+
384
+ if (opts.json) {
385
+ console.log(JSON.stringify(costs, null, 2));
386
+ } else {
387
+ if (costs.length === 0) {
388
+ console.log("No shipping cost data.");
389
+ return;
390
+ }
391
+ console.log("Costs by Carrier:");
392
+ for (const c of costs) {
393
+ console.log(` ${c.carrier.toUpperCase()}: $${c.total_cost.toFixed(2)} total, ${c.shipment_count} shipments, $${c.avg_cost.toFixed(2)} avg`);
394
+ }
395
+ }
396
+ });
397
+
398
+ program.parse(process.argv);
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Database connection for microservice-shipping
3
+ */
4
+
5
+ import { Database } from "bun:sqlite";
6
+ import { existsSync, mkdirSync } from "node:fs";
7
+ import { dirname, join, resolve } from "node:path";
8
+ import { MIGRATIONS } from "./migrations.js";
9
+
10
+ let _db: Database | null = null;
11
+
12
+ function getDbPath(): string {
13
+ // Environment variable override
14
+ if (process.env["MICROSERVICES_DIR"]) {
15
+ return join(process.env["MICROSERVICES_DIR"], "microservice-shipping", "data.db");
16
+ }
17
+
18
+ // Check for .microservices in current or parent directories
19
+ let dir = resolve(process.cwd());
20
+ while (true) {
21
+ const candidate = join(dir, ".microservices", "microservice-shipping", "data.db");
22
+ const msDir = join(dir, ".microservices");
23
+ if (existsSync(msDir)) return candidate;
24
+ const parent = dirname(dir);
25
+ if (parent === dir) break;
26
+ dir = parent;
27
+ }
28
+
29
+ // Global fallback
30
+ const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
31
+ return join(home, ".microservices", "microservice-shipping", "data.db");
32
+ }
33
+
34
+ function ensureDir(filePath: string): void {
35
+ const dir = dirname(resolve(filePath));
36
+ if (!existsSync(dir)) {
37
+ mkdirSync(dir, { recursive: true });
38
+ }
39
+ }
40
+
41
+ export function getDatabase(): Database {
42
+ if (_db) return _db;
43
+
44
+ const dbPath = getDbPath();
45
+ ensureDir(dbPath);
46
+
47
+ _db = new Database(dbPath);
48
+ _db.exec("PRAGMA journal_mode = WAL");
49
+ _db.exec("PRAGMA foreign_keys = ON");
50
+
51
+ // Create migrations table
52
+ _db.exec(`
53
+ CREATE TABLE IF NOT EXISTS _migrations (
54
+ id INTEGER PRIMARY KEY,
55
+ name TEXT NOT NULL,
56
+ applied_at TEXT NOT NULL DEFAULT (datetime('now'))
57
+ )
58
+ `);
59
+
60
+ // Apply pending migrations
61
+ const applied = _db
62
+ .query("SELECT id FROM _migrations ORDER BY id")
63
+ .all() as { id: number }[];
64
+ const appliedIds = new Set(applied.map((r) => r.id));
65
+
66
+ for (const migration of MIGRATIONS) {
67
+ if (appliedIds.has(migration.id)) continue;
68
+
69
+ _db.exec("BEGIN");
70
+ try {
71
+ _db.exec(migration.sql);
72
+ _db.prepare("INSERT INTO _migrations (id, name) VALUES (?, ?)").run(
73
+ migration.id,
74
+ migration.name
75
+ );
76
+ _db.exec("COMMIT");
77
+ } catch (error) {
78
+ _db.exec("ROLLBACK");
79
+ throw new Error(
80
+ `Migration ${migration.id} (${migration.name}) failed: ${error instanceof Error ? error.message : String(error)}`
81
+ );
82
+ }
83
+ }
84
+
85
+ return _db;
86
+ }
87
+
88
+ export function closeDatabase(): void {
89
+ if (_db) {
90
+ _db.close();
91
+ _db = null;
92
+ }
93
+ }
@@ -0,0 +1,61 @@
1
+ export interface MigrationEntry {
2
+ id: number;
3
+ name: string;
4
+ sql: string;
5
+ }
6
+
7
+ export const MIGRATIONS: MigrationEntry[] = [
8
+ {
9
+ id: 1,
10
+ name: "initial_schema",
11
+ sql: `
12
+ CREATE TABLE IF NOT EXISTS orders (
13
+ id TEXT PRIMARY KEY,
14
+ customer_name TEXT NOT NULL,
15
+ customer_email TEXT,
16
+ address TEXT NOT NULL DEFAULT '{}',
17
+ items TEXT NOT NULL DEFAULT '[]',
18
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','processing','shipped','delivered','returned')),
19
+ total_value REAL NOT NULL DEFAULT 0,
20
+ currency TEXT NOT NULL DEFAULT 'USD',
21
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
22
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
23
+ );
24
+
25
+ CREATE TABLE IF NOT EXISTS shipments (
26
+ id TEXT PRIMARY KEY,
27
+ order_id TEXT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
28
+ carrier TEXT NOT NULL CHECK(carrier IN ('ups','fedex','usps','dhl')),
29
+ tracking_number TEXT,
30
+ service TEXT NOT NULL DEFAULT 'ground' CHECK(service IN ('ground','express','overnight')),
31
+ status TEXT NOT NULL DEFAULT 'label_created' CHECK(status IN ('label_created','in_transit','out_for_delivery','delivered','exception')),
32
+ shipped_at TEXT,
33
+ estimated_delivery TEXT,
34
+ delivered_at TEXT,
35
+ cost REAL,
36
+ weight REAL,
37
+ dimensions TEXT,
38
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
39
+ );
40
+
41
+ CREATE TABLE IF NOT EXISTS returns (
42
+ id TEXT PRIMARY KEY,
43
+ order_id TEXT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
44
+ reason TEXT,
45
+ status TEXT NOT NULL DEFAULT 'requested' CHECK(status IN ('requested','approved','received','refunded')),
46
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
47
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
48
+ );
49
+
50
+ CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status);
51
+ CREATE INDEX IF NOT EXISTS idx_orders_customer_email ON orders(customer_email);
52
+ CREATE INDEX IF NOT EXISTS idx_orders_customer_name ON orders(customer_name);
53
+ CREATE INDEX IF NOT EXISTS idx_shipments_order ON shipments(order_id);
54
+ CREATE INDEX IF NOT EXISTS idx_shipments_carrier ON shipments(carrier);
55
+ CREATE INDEX IF NOT EXISTS idx_shipments_tracking ON shipments(tracking_number);
56
+ CREATE INDEX IF NOT EXISTS idx_shipments_status ON shipments(status);
57
+ CREATE INDEX IF NOT EXISTS idx_returns_order ON returns(order_id);
58
+ CREATE INDEX IF NOT EXISTS idx_returns_status ON returns(status);
59
+ `,
60
+ },
61
+ ];