@ductape/mcp 0.1.40 → 0.1.41
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 +74 -42
- package/package.json +1 -1
- package/src/index.ts +71 -39
package/dist/index.js
CHANGED
|
@@ -249,21 +249,46 @@ CONTROLLER DECORATORS:
|
|
|
249
249
|
@Webhook.Register(...) — register a webhook consumer URL
|
|
250
250
|
@Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
|
|
251
251
|
|
|
252
|
-
FOR MESSAGING (events.produce / events.consume):
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
event: 'broker-tag:topic-tag'
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
252
|
+
FOR MESSAGING (events.produce / events.consume / events.dispatch):
|
|
253
|
+
|
|
254
|
+
In @ductape/nestjs, inject DuctapeContextService (not raw Ductape):
|
|
255
|
+
constructor(private readonly ductape: DuctapeContextService) {}
|
|
256
|
+
|
|
257
|
+
Produce immediately (method decorator — returns payload as message):
|
|
258
|
+
@Events.Produce({ event: 'broker-tag:topic-tag' })
|
|
259
|
+
emitOrderCreated(payload: { orderId: string }) { return payload; }
|
|
260
|
+
|
|
261
|
+
Dispatch (scheduled or immediate) — static schedule in decorator:
|
|
262
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:order-created', schedule: { every: 60000 } })
|
|
263
|
+
dispatchHeartbeat(payload: { message: { ping: boolean } }) { return payload; }
|
|
264
|
+
|
|
265
|
+
Dispatch with dynamic schedule — method returns { message, schedule?, retries? }:
|
|
266
|
+
@Events.Dispatch({ broker: 'statecraft-events', event: 'statecraft-events:boundary-due' })
|
|
267
|
+
scheduleBoundary(match: MatchLifecycle) {
|
|
268
|
+
return {
|
|
269
|
+
message: buildBoundaryCommand(match),
|
|
270
|
+
schedule: { start_at: match.nextBoundaryAt },
|
|
271
|
+
retries: 5,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// method return takes precedence over decorator schedule; use sdk.events.dispatch() directly
|
|
275
|
+
// when even the broker/event must vary at call time.
|
|
276
|
+
|
|
277
|
+
Consume (method decorator — method is called for each incoming message):
|
|
278
|
+
@Events.Consumer({ event: 'order-events:order-created' })
|
|
279
|
+
async onOrderCreated(message: { orderId: string; total: number }) {
|
|
280
|
+
await this.processOrder(message);
|
|
281
|
+
// return to ack; throw to nack
|
|
282
|
+
}
|
|
283
|
+
// DuctapeEventsConsumerService wires this up automatically at module init.
|
|
284
|
+
// No manual onModuleInit needed when using the decorator.
|
|
285
|
+
|
|
286
|
+
Low-level SDK access (for produce only — not needed for consume with the decorator):
|
|
287
|
+
await this.ductape.sdk.events.produce({
|
|
288
|
+
product: 'my-product', env: 'prd',
|
|
289
|
+
event: 'broker-tag:topic-tag',
|
|
290
|
+
message: { ... },
|
|
291
|
+
});
|
|
267
292
|
|
|
268
293
|
SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
|
|
269
294
|
|
|
@@ -2476,16 +2501,30 @@ Import (register an EXISTING cloud resource):
|
|
|
2476
2501
|
idempotencyTtl?: 86400, // seconds; default 86400 (24h)
|
|
2477
2502
|
});
|
|
2478
2503
|
|
|
2479
|
-
NESTJS — method
|
|
2504
|
+
NESTJS — method decorators:
|
|
2480
2505
|
import { Events } from '@ductape/nestjs';
|
|
2481
2506
|
@Injectable() export class OrdersService {
|
|
2507
|
+
// Immediate produce — method returns the message payload:
|
|
2482
2508
|
@Events.Produce({ event: 'order-events:order-created' })
|
|
2483
2509
|
emitOrderCreated(payload: { orderId: string; total: number }) { return payload; }
|
|
2484
2510
|
|
|
2485
|
-
//
|
|
2486
|
-
@Events.Dispatch({ broker: 'order-events', event: 'order-events:
|
|
2487
|
-
|
|
2488
|
-
|
|
2511
|
+
// Dispatch with static schedule (known at deploy time):
|
|
2512
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:reminder-due',
|
|
2513
|
+
schedule: { every: 86400000 } })
|
|
2514
|
+
scheduleReminder(payload: { message: { orderId: string } }) { return payload; }
|
|
2515
|
+
|
|
2516
|
+
// Dispatch with dynamic schedule (known at call time) — method returns { message, schedule?, retries? }:
|
|
2517
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:fulfillment-due' })
|
|
2518
|
+
scheduleFulfillment(order: Order) {
|
|
2519
|
+
return {
|
|
2520
|
+
message: { orderId: order.id, items: order.items },
|
|
2521
|
+
schedule: { start_at: order.expectedAt },
|
|
2522
|
+
retries: 3,
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
// Called as: await this.ordersService.scheduleFulfillment(order);
|
|
2526
|
+
// When method return has a 'message' key, schedule/retries from return take precedence over decorator config.
|
|
2527
|
+
// For even more control (dynamic broker/event), use sdk.events.dispatch() directly.
|
|
2489
2528
|
}
|
|
2490
2529
|
|
|
2491
2530
|
CLIENT-SIDE (browser — publishable key):
|
|
@@ -2522,11 +2561,6 @@ Import (register an EXISTING cloud resource):
|
|
|
2522
2561
|
|
|
2523
2562
|
━━━ STEP 4: CONSUME — WRITTEN IN APPLICATION CODE ━━━
|
|
2524
2563
|
|
|
2525
|
-
Consumers are auto-registered by the SDK on first consume call.
|
|
2526
|
-
Consumer registration options (all optional — used for tracking in Workbench):
|
|
2527
|
-
consumer?: { tag?: string, name?: string, description?: string }
|
|
2528
|
-
If tag is omitted, Ductape generates one: "consumer-<brokerTag>-<topicTag>".
|
|
2529
|
-
|
|
2530
2564
|
ACK BEHAVIOR (automatic):
|
|
2531
2565
|
- Callback returns successfully → message is acknowledged (ack)
|
|
2532
2566
|
- Callback throws → message is tracked as failed; broker nacks/retries per provider behavior
|
|
@@ -2545,34 +2579,32 @@ Import (register an EXISTING cloud resource):
|
|
|
2545
2579
|
Run multiple instances of your service to scale consumption.
|
|
2546
2580
|
|
|
2547
2581
|
GENERAL BACKEND (TypeScript/Node.js — not NestJS):
|
|
2548
|
-
Start consuming in your module init or service startup:
|
|
2549
2582
|
await ductape.events.consume({
|
|
2550
2583
|
product: "my-product",
|
|
2551
2584
|
env: "prd",
|
|
2552
|
-
event: "order-events:order-created",
|
|
2585
|
+
event: "order-events:order-created", // ALWAYS "broker-tag:topic-tag"
|
|
2553
2586
|
callback: async (message) => {
|
|
2554
|
-
// All real processing logic goes here.
|
|
2555
2587
|
// Throw to nack. Return to ack.
|
|
2556
2588
|
await processOrder(message as { orderId: string; total: number });
|
|
2557
2589
|
},
|
|
2558
|
-
consumer?: { tag: "order-processor", name: "Order Processor" },
|
|
2559
2590
|
});
|
|
2560
2591
|
|
|
2561
|
-
NESTJS — use
|
|
2592
|
+
NESTJS — use @Events.Consumer decorator (preferred):
|
|
2593
|
+
import { Events } from '@ductape/nestjs';
|
|
2562
2594
|
@Injectable()
|
|
2563
|
-
export class OrderConsumerService
|
|
2564
|
-
|
|
2565
|
-
async
|
|
2566
|
-
await this.
|
|
2567
|
-
|
|
2568
|
-
env: process.env.DUCTAPE_ENV || 'prd',
|
|
2569
|
-
event: "order-events:order-created",
|
|
2570
|
-
callback: async (message) => { await this.handle(message); },
|
|
2571
|
-
consumer: { tag: "order-consumer", name: "Order Consumer" },
|
|
2572
|
-
});
|
|
2595
|
+
export class OrderConsumerService {
|
|
2596
|
+
@Events.Consumer({ event: 'order-events:order-created' })
|
|
2597
|
+
async onOrderCreated(message: { orderId: string; total: number }) {
|
|
2598
|
+
await this.processOrder(message);
|
|
2599
|
+
// return to ack; throw to nack
|
|
2573
2600
|
}
|
|
2574
|
-
private async
|
|
2601
|
+
private async processOrder(msg: { orderId: string; total: number }) { /* ... */ }
|
|
2575
2602
|
}
|
|
2603
|
+
// DuctapeEventsConsumerService (auto-registered by DuctapeModule) wires this up at startup.
|
|
2604
|
+
// No manual onModuleInit needed.
|
|
2605
|
+
|
|
2606
|
+
// If you need to override product/env for a specific consumer:
|
|
2607
|
+
@Events.Consumer({ event: 'order-events:order-created', product: 'my-product', env: 'prd' })
|
|
2576
2608
|
|
|
2577
2609
|
CLIENT-SIDE: Clients CANNOT consume. Event consumption is always server-side only.
|
|
2578
2610
|
This is the key distinction between server topics (produce + consume) and client-observable
|
|
@@ -3531,7 +3563,7 @@ async function main() {
|
|
|
3531
3563
|
' "product":"my-product","component":"core-db","env":"prd","resource":"Cluster0","dbName":"myapp_prd"}]\n' +
|
|
3532
3564
|
' - Message broker / event broker import:\n' +
|
|
3533
3565
|
' CLI accepts these aliases for the messageBrokers module: events, event, broker, brokers, message-brokers.\n' +
|
|
3534
|
-
' List existing brokers: ductape_cli("resources events list
|
|
3566
|
+
' List existing brokers: ductape_cli("resources events list --json")\n' +
|
|
3535
3567
|
' GCP Pub/Sub service identifier is "pubsub". AWS SQS is "sqs". Azure Service Bus is "servicebus".\n' +
|
|
3536
3568
|
' Message brokers are import-only (no provision-persist). Import flow is the same as storage.\n' +
|
|
3537
3569
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -260,21 +260,46 @@ CONTROLLER DECORATORS:
|
|
|
260
260
|
@Webhook.Register(...) — register a webhook consumer URL
|
|
261
261
|
@Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
|
|
262
262
|
|
|
263
|
-
FOR MESSAGING (events.produce / events.consume):
|
|
263
|
+
FOR MESSAGING (events.produce / events.consume / events.dispatch):
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
In @ductape/nestjs, inject DuctapeContextService (not raw Ductape):
|
|
266
|
+
constructor(private readonly ductape: DuctapeContextService) {}
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
message: { ... },
|
|
271
|
-
});
|
|
268
|
+
Produce immediately (method decorator — returns payload as message):
|
|
269
|
+
@Events.Produce({ event: 'broker-tag:topic-tag' })
|
|
270
|
+
emitOrderCreated(payload: { orderId: string }) { return payload; }
|
|
272
271
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
272
|
+
Dispatch (scheduled or immediate) — static schedule in decorator:
|
|
273
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:order-created', schedule: { every: 60000 } })
|
|
274
|
+
dispatchHeartbeat(payload: { message: { ping: boolean } }) { return payload; }
|
|
275
|
+
|
|
276
|
+
Dispatch with dynamic schedule — method returns { message, schedule?, retries? }:
|
|
277
|
+
@Events.Dispatch({ broker: 'statecraft-events', event: 'statecraft-events:boundary-due' })
|
|
278
|
+
scheduleBoundary(match: MatchLifecycle) {
|
|
279
|
+
return {
|
|
280
|
+
message: buildBoundaryCommand(match),
|
|
281
|
+
schedule: { start_at: match.nextBoundaryAt },
|
|
282
|
+
retries: 5,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
// method return takes precedence over decorator schedule; use sdk.events.dispatch() directly
|
|
286
|
+
// when even the broker/event must vary at call time.
|
|
287
|
+
|
|
288
|
+
Consume (method decorator — method is called for each incoming message):
|
|
289
|
+
@Events.Consumer({ event: 'order-events:order-created' })
|
|
290
|
+
async onOrderCreated(message: { orderId: string; total: number }) {
|
|
291
|
+
await this.processOrder(message);
|
|
292
|
+
// return to ack; throw to nack
|
|
293
|
+
}
|
|
294
|
+
// DuctapeEventsConsumerService wires this up automatically at module init.
|
|
295
|
+
// No manual onModuleInit needed when using the decorator.
|
|
296
|
+
|
|
297
|
+
Low-level SDK access (for produce only — not needed for consume with the decorator):
|
|
298
|
+
await this.ductape.sdk.events.produce({
|
|
299
|
+
product: 'my-product', env: 'prd',
|
|
300
|
+
event: 'broker-tag:topic-tag',
|
|
301
|
+
message: { ... },
|
|
302
|
+
});
|
|
278
303
|
|
|
279
304
|
SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
|
|
280
305
|
|
|
@@ -2549,16 +2574,30 @@ Import (register an EXISTING cloud resource):
|
|
|
2549
2574
|
idempotencyTtl?: 86400, // seconds; default 86400 (24h)
|
|
2550
2575
|
});
|
|
2551
2576
|
|
|
2552
|
-
NESTJS — method
|
|
2577
|
+
NESTJS — method decorators:
|
|
2553
2578
|
import { Events } from '@ductape/nestjs';
|
|
2554
2579
|
@Injectable() export class OrdersService {
|
|
2580
|
+
// Immediate produce — method returns the message payload:
|
|
2555
2581
|
@Events.Produce({ event: 'order-events:order-created' })
|
|
2556
2582
|
emitOrderCreated(payload: { orderId: string; total: number }) { return payload; }
|
|
2557
2583
|
|
|
2558
|
-
//
|
|
2559
|
-
@Events.Dispatch({ broker: 'order-events', event: 'order-events:
|
|
2560
|
-
|
|
2561
|
-
|
|
2584
|
+
// Dispatch with static schedule (known at deploy time):
|
|
2585
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:reminder-due',
|
|
2586
|
+
schedule: { every: 86400000 } })
|
|
2587
|
+
scheduleReminder(payload: { message: { orderId: string } }) { return payload; }
|
|
2588
|
+
|
|
2589
|
+
// Dispatch with dynamic schedule (known at call time) — method returns { message, schedule?, retries? }:
|
|
2590
|
+
@Events.Dispatch({ broker: 'order-events', event: 'order-events:fulfillment-due' })
|
|
2591
|
+
scheduleFulfillment(order: Order) {
|
|
2592
|
+
return {
|
|
2593
|
+
message: { orderId: order.id, items: order.items },
|
|
2594
|
+
schedule: { start_at: order.expectedAt },
|
|
2595
|
+
retries: 3,
|
|
2596
|
+
};
|
|
2597
|
+
}
|
|
2598
|
+
// Called as: await this.ordersService.scheduleFulfillment(order);
|
|
2599
|
+
// When method return has a 'message' key, schedule/retries from return take precedence over decorator config.
|
|
2600
|
+
// For even more control (dynamic broker/event), use sdk.events.dispatch() directly.
|
|
2562
2601
|
}
|
|
2563
2602
|
|
|
2564
2603
|
CLIENT-SIDE (browser — publishable key):
|
|
@@ -2595,11 +2634,6 @@ Import (register an EXISTING cloud resource):
|
|
|
2595
2634
|
|
|
2596
2635
|
━━━ STEP 4: CONSUME — WRITTEN IN APPLICATION CODE ━━━
|
|
2597
2636
|
|
|
2598
|
-
Consumers are auto-registered by the SDK on first consume call.
|
|
2599
|
-
Consumer registration options (all optional — used for tracking in Workbench):
|
|
2600
|
-
consumer?: { tag?: string, name?: string, description?: string }
|
|
2601
|
-
If tag is omitted, Ductape generates one: "consumer-<brokerTag>-<topicTag>".
|
|
2602
|
-
|
|
2603
2637
|
ACK BEHAVIOR (automatic):
|
|
2604
2638
|
- Callback returns successfully → message is acknowledged (ack)
|
|
2605
2639
|
- Callback throws → message is tracked as failed; broker nacks/retries per provider behavior
|
|
@@ -2618,34 +2652,32 @@ Import (register an EXISTING cloud resource):
|
|
|
2618
2652
|
Run multiple instances of your service to scale consumption.
|
|
2619
2653
|
|
|
2620
2654
|
GENERAL BACKEND (TypeScript/Node.js — not NestJS):
|
|
2621
|
-
Start consuming in your module init or service startup:
|
|
2622
2655
|
await ductape.events.consume({
|
|
2623
2656
|
product: "my-product",
|
|
2624
2657
|
env: "prd",
|
|
2625
|
-
event: "order-events:order-created",
|
|
2658
|
+
event: "order-events:order-created", // ALWAYS "broker-tag:topic-tag"
|
|
2626
2659
|
callback: async (message) => {
|
|
2627
|
-
// All real processing logic goes here.
|
|
2628
2660
|
// Throw to nack. Return to ack.
|
|
2629
2661
|
await processOrder(message as { orderId: string; total: number });
|
|
2630
2662
|
},
|
|
2631
|
-
consumer?: { tag: "order-processor", name: "Order Processor" },
|
|
2632
2663
|
});
|
|
2633
2664
|
|
|
2634
|
-
NESTJS — use
|
|
2665
|
+
NESTJS — use @Events.Consumer decorator (preferred):
|
|
2666
|
+
import { Events } from '@ductape/nestjs';
|
|
2635
2667
|
@Injectable()
|
|
2636
|
-
export class OrderConsumerService
|
|
2637
|
-
|
|
2638
|
-
async
|
|
2639
|
-
await this.
|
|
2640
|
-
|
|
2641
|
-
env: process.env.DUCTAPE_ENV || 'prd',
|
|
2642
|
-
event: "order-events:order-created",
|
|
2643
|
-
callback: async (message) => { await this.handle(message); },
|
|
2644
|
-
consumer: { tag: "order-consumer", name: "Order Consumer" },
|
|
2645
|
-
});
|
|
2668
|
+
export class OrderConsumerService {
|
|
2669
|
+
@Events.Consumer({ event: 'order-events:order-created' })
|
|
2670
|
+
async onOrderCreated(message: { orderId: string; total: number }) {
|
|
2671
|
+
await this.processOrder(message);
|
|
2672
|
+
// return to ack; throw to nack
|
|
2646
2673
|
}
|
|
2647
|
-
private async
|
|
2674
|
+
private async processOrder(msg: { orderId: string; total: number }) { /* ... */ }
|
|
2648
2675
|
}
|
|
2676
|
+
// DuctapeEventsConsumerService (auto-registered by DuctapeModule) wires this up at startup.
|
|
2677
|
+
// No manual onModuleInit needed.
|
|
2678
|
+
|
|
2679
|
+
// If you need to override product/env for a specific consumer:
|
|
2680
|
+
@Events.Consumer({ event: 'order-events:order-created', product: 'my-product', env: 'prd' })
|
|
2649
2681
|
|
|
2650
2682
|
CLIENT-SIDE: Clients CANNOT consume. Event consumption is always server-side only.
|
|
2651
2683
|
This is the key distinction between server topics (produce + consume) and client-observable
|
|
@@ -3667,7 +3699,7 @@ async function main() {
|
|
|
3667
3699
|
' "product":"my-product","component":"core-db","env":"prd","resource":"Cluster0","dbName":"myapp_prd"}]\n' +
|
|
3668
3700
|
' - Message broker / event broker import:\n' +
|
|
3669
3701
|
' CLI accepts these aliases for the messageBrokers module: events, event, broker, brokers, message-brokers.\n' +
|
|
3670
|
-
' List existing brokers: ductape_cli("resources events list
|
|
3702
|
+
' List existing brokers: ductape_cli("resources events list --json")\n' +
|
|
3671
3703
|
' GCP Pub/Sub service identifier is "pubsub". AWS SQS is "sqs". Azure Service Bus is "servicebus".\n' +
|
|
3672
3704
|
' Message brokers are import-only (no provision-persist). Import flow is the same as storage.\n' +
|
|
3673
3705
|
' type field = "messageBrokers" (not "messagebrokers" or "events").\n' +
|