@ductape/mcp 0.1.23 → 0.1.25
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 +178 -2
- package/package.json +1 -1
- package/src/index.ts +178 -2
package/dist/index.js
CHANGED
|
@@ -54,6 +54,67 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
54
54
|
ductape_cli("db migrate rollback") # roll back last migration
|
|
55
55
|
ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
|
|
56
56
|
|
|
57
|
+
DATABASE SCHEMA FILE — ductape/database/schema.json
|
|
58
|
+
─────────────────────────────────────────────────────────────────────────
|
|
59
|
+
IMPORTANT: "db schema generate" READS schema.json and emits migration files.
|
|
60
|
+
It does NOT create schema.json from scratch. You must write this file first.
|
|
61
|
+
|
|
62
|
+
If schema.json does not exist, "db schema generate" now auto-creates it and
|
|
63
|
+
attempts to hydrate it from the cloud (pass --db <tag> to enable cloud hydration):
|
|
64
|
+
ductape_cli("db schema generate --db statecraft-core")
|
|
65
|
+
If the cloud database is empty, schema.json will be created as [] and you must
|
|
66
|
+
populate it before running the command again.
|
|
67
|
+
|
|
68
|
+
FILE FORMAT — ductape/database/schema.json is a JSON array:
|
|
69
|
+
[
|
|
70
|
+
{
|
|
71
|
+
"db": "<database-component-tag>",
|
|
72
|
+
"tables": {
|
|
73
|
+
"<collection_name>": {
|
|
74
|
+
"<field_name>": {
|
|
75
|
+
"type": "string|number|boolean|date|datetime|timestamp|uuid|object|array|json|text|decimal|bigint|smallint|double|binary|blob|time",
|
|
76
|
+
"required": true, // false by default (nullable)
|
|
77
|
+
"unique": true, // unique constraint on this field
|
|
78
|
+
"index": true, // create an index on this field (use with unique for unique index)
|
|
79
|
+
"default": "value", // default value ("now"/"NOW" → CURRENT_TIMESTAMP)
|
|
80
|
+
"maxlength": 255, // max length for string fields
|
|
81
|
+
"enum": ["a", "b", "c"], // restrict to enum values (sets type to string automatically)
|
|
82
|
+
"primaryKey": true, // mark as primary key
|
|
83
|
+
"autoGenerate": true, // auto-generate value on insert
|
|
84
|
+
"float": true // treat number as float (use with type: "number")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
SINGLE-FIELD INDEX: set index: true (+ unique: true for unique constraint)
|
|
92
|
+
"matchId": { "type": "string", "required": true, "unique": true, "index": true }
|
|
93
|
+
|
|
94
|
+
COMPOSITE INDEX: schema.json does not support composite indexes.
|
|
95
|
+
After running db schema generate, add a manual migration file in
|
|
96
|
+
ductape/migrations/<db-tag>/ with type "createIndex" and a fields array:
|
|
97
|
+
{
|
|
98
|
+
"tag": "create_map_events_match_seq_idx",
|
|
99
|
+
"name": "Composite unique index on matchId+sequence",
|
|
100
|
+
"up": [{
|
|
101
|
+
"type": "createIndex",
|
|
102
|
+
"collection": "map_events",
|
|
103
|
+
"name": "map_events_matchId_sequence_idx",
|
|
104
|
+
"fields": [{ "name": "matchId" }, { "name": "sequence" }],
|
|
105
|
+
"unique": true,
|
|
106
|
+
"ifNotExists": true
|
|
107
|
+
}],
|
|
108
|
+
"down": [{ "type": "dropIndex", "collection": "map_events", "name": "map_events_matchId_sequence_idx", "ifExists": true }],
|
|
109
|
+
"createdAt": "<ISO timestamp>"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
WORKFLOW:
|
|
113
|
+
1. Create/edit ductape/database/schema.json with your collection definitions
|
|
114
|
+
2. ductape_cli("db schema generate --db <tag>") → writes migration files
|
|
115
|
+
3. ductape_cli("db migrate --db <tag>") → applies pending migrations
|
|
116
|
+
4. Add manual migration files for composite/partial indexes not expressible in schema.json
|
|
117
|
+
|
|
57
118
|
1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
|
|
58
119
|
ALL creation and update operations require an access key and CANNOT go through ductape_execute.
|
|
59
120
|
→ Use ductape_cli for every create/update operation. Examples:
|
|
@@ -111,6 +172,107 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
111
172
|
|
|
112
173
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
113
174
|
|
|
175
|
+
━━━ NESTJS INTEGRATION — use @ductape/nestjs ━━━
|
|
176
|
+
|
|
177
|
+
When the target application is a NestJS service or controller, always use @ductape/nestjs
|
|
178
|
+
instead of instantiating @ductape/sdk directly. It provides NestJS DI integration,
|
|
179
|
+
global interceptors, decorators, and type-safe resource handles.
|
|
180
|
+
|
|
181
|
+
SETUP — register once in AppModule:
|
|
182
|
+
|
|
183
|
+
import { DuctapeModule } from '@ductape/nestjs';
|
|
184
|
+
|
|
185
|
+
@Module({
|
|
186
|
+
imports: [
|
|
187
|
+
DuctapeModule.forIntegration({
|
|
188
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
189
|
+
product: 'my-product', // optional default — overridable per controller
|
|
190
|
+
env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
|
|
191
|
+
}),
|
|
192
|
+
],
|
|
193
|
+
})
|
|
194
|
+
export class AppModule {}
|
|
195
|
+
|
|
196
|
+
// Async (e.g. pulling key from ConfigService):
|
|
197
|
+
DuctapeModule.forRootAsync({
|
|
198
|
+
imports: [ConfigModule],
|
|
199
|
+
inject: [ConfigService],
|
|
200
|
+
useFactory: (cfg: ConfigService) => ({
|
|
201
|
+
accessKey: cfg.get('DUCTAPE_ACCESS_KEY'),
|
|
202
|
+
product: cfg.get('DUCTAPE_PRODUCT'),
|
|
203
|
+
env: cfg.get('DUCTAPE_ENV'),
|
|
204
|
+
}),
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
INJECTING IN SERVICES AND CONTROLLERS — use @InjectContext():
|
|
208
|
+
|
|
209
|
+
import { InjectContext, DuctapeContext } from '@ductape/nestjs';
|
|
210
|
+
|
|
211
|
+
@Injectable()
|
|
212
|
+
export class MatchService {
|
|
213
|
+
constructor(@InjectContext() private readonly ductape: DuctapeContext) {}
|
|
214
|
+
|
|
215
|
+
async publishMatchState(matchId: string, state: object) {
|
|
216
|
+
await this.ductape.sdk.events.produce({
|
|
217
|
+
product: 'my-product',
|
|
218
|
+
env: 'prd',
|
|
219
|
+
event: 'statecraft-events:match-state',
|
|
220
|
+
message: { matchId, ...state },
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async queryDatabase(env: string) {
|
|
225
|
+
const db = await this.ductape.database('core-db', { env });
|
|
226
|
+
return db.query({ table: 'matches', where: { active: true } });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
AVAILABLE ON DuctapeContext:
|
|
231
|
+
|
|
232
|
+
ctx.sdk — full @ductape/sdk instance (events, sessions, etc.)
|
|
233
|
+
ctx.database(tag, overrides?) — connected DatabaseHandle (query, insert, update, delete)
|
|
234
|
+
ctx.storage(tag, overrides?) — StorageHandle (upload, download, remove)
|
|
235
|
+
ctx.cache(tag, overrides?) — CacheHandle (get, set, delete)
|
|
236
|
+
ctx.graph(tag, overrides?) — GraphHandle
|
|
237
|
+
ctx.vector(tag, overrides?) — VectorHandle
|
|
238
|
+
ctx.agent(tag, overrides?) — AgentHandle
|
|
239
|
+
ctx.warehouse(overrides?) — WarehouseHandle
|
|
240
|
+
ctx.cloudTiers(query?) — query available cloud resource tiers and pricing
|
|
241
|
+
ctx.runJob({ product, env, event, input? }) — trigger a product job
|
|
242
|
+
|
|
243
|
+
CONTROLLER DECORATORS:
|
|
244
|
+
|
|
245
|
+
@Product('my-product') — override default product at class or method level
|
|
246
|
+
@Env('prd') — override default env
|
|
247
|
+
@ApiRun({ app, action }) — bind a controller method to an app action (interceptor runs it automatically)
|
|
248
|
+
@ApiConfig({ app, ... }) — shared app credentials at class level
|
|
249
|
+
@Webhook.Register(...) — register a webhook consumer URL
|
|
250
|
+
@Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
|
|
251
|
+
|
|
252
|
+
FOR MESSAGING (events.produce / events.consume):
|
|
253
|
+
|
|
254
|
+
Access via ctx.sdk.events — no dedicated NestJS handle, the SDK instance is sufficient:
|
|
255
|
+
|
|
256
|
+
await this.ductape.sdk.events.produce({
|
|
257
|
+
product: 'my-product', env: 'prd',
|
|
258
|
+
event: 'broker-tag:topic-tag',
|
|
259
|
+
message: { ... },
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await this.ductape.sdk.events.consume({
|
|
263
|
+
product: 'my-product', env: 'prd',
|
|
264
|
+
event: 'broker-tag:topic-tag',
|
|
265
|
+
callback: async (message) => { /* handle */ },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
|
|
269
|
+
|
|
270
|
+
DuctapeDatabaseModule.forTags(['core-db']) → inject with @Database('core-db')
|
|
271
|
+
DuctapeStorageModule.forTags(['assets']) → inject with @Storage('assets')
|
|
272
|
+
DuctapeCacheModule, DuctapeGraphModule, DuctapeVectorModule — same pattern
|
|
273
|
+
|
|
274
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
275
|
+
|
|
114
276
|
ALL params are passed as a JSON array in positional order matching the SDK signature.
|
|
115
277
|
|
|
116
278
|
━━━ MODULE: product ━━━
|
|
@@ -340,11 +502,23 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
340
502
|
storage.dispatch [{ product, env, storage, operation, input, retries?, session?, cache?, schedule? }] ← CALL ductape_generate_payload FIRST (operation_family="storage", method="dispatch", targets={storage})
|
|
341
503
|
|
|
342
504
|
━━━ MODULE: databases ━━━
|
|
343
|
-
databases.create [{ product, tag, name, description?, type: "mongodb"|"postgresql"|"mysql"|"sqlite",
|
|
505
|
+
databases.create [{ product, tag, name, description?, type: "mongodb"|"postgresql"|"mysql"|"sqlite",
|
|
506
|
+
envs: [{ slug, connection_url?,
|
|
507
|
+
cloud?, ← workspace cloud-connection tag (omit connection_url when using this)
|
|
508
|
+
authMode?, ← "cloud_connection" when using cloud field
|
|
509
|
+
dbName?, ← REQUIRED for MongoDB cloud connections — the database name inside the cluster (e.g. "myapp_snd")
|
|
510
|
+
instance?, ← RDS/Cloud SQL instance id (provision flow only)
|
|
511
|
+
region? }] }]
|
|
512
|
+
MONGODB CLOUD CONNECTION RULE: when type="mongodb" and cloud is set, dbName is MANDATORY.
|
|
513
|
+
Without dbName the Atlas API returns a URL with no database name and the adapter will throw
|
|
514
|
+
at migrate/connect time. Provide one dbName per env — they typically differ per environment:
|
|
515
|
+
e.g. { slug:"snd", cloud:"atlas-conn", authMode:"cloud_connection", dbName:"myapp_snd" }
|
|
516
|
+
{ slug:"prd", cloud:"atlas-conn", authMode:"cloud_connection", dbName:"myapp_prd" }
|
|
344
517
|
databases.register [product_tag, data: IProductDatabase]
|
|
345
518
|
databases.list [product_tag]
|
|
346
519
|
databases.fetch [product_tag, database_tag]
|
|
347
|
-
databases.updateDatabase [product_tag, database_tag, data: { name
|
|
520
|
+
databases.updateDatabase [product_tag, database_tag, data: { name?, description?, type?,
|
|
521
|
+
envs?: [{ slug, connection_url?, cloud?, authMode?, dbName?, instance?, region? }] }]
|
|
348
522
|
databases.connect [{ product, env, database }]
|
|
349
523
|
databases.testConnection [{ product, env, database }]
|
|
350
524
|
databases.disconnect []
|
|
@@ -2490,6 +2664,8 @@ async function main() {
|
|
|
2490
2664
|
' azure-blob→storage, cloud-sql→database, neptune→graph, opensearch→vector. Attempting an unsupported pair\n' +
|
|
2491
2665
|
' will return an error; do not retry with a different tier — report the limitation to the user.\n' +
|
|
2492
2666
|
' - Atlas (MongoDB Atlas) import flow — service identifier is "atlas-cluster" (required, not optional):\n' +
|
|
2667
|
+
' IMPORTANT: dbName is mandatory for every MongoDB env — see "MONGODB CLOUD CONNECTION RULE" in the\n' +
|
|
2668
|
+
' databases module above. The SDK enforces this at create time; omitting it throws a validation error.\n' +
|
|
2493
2669
|
' Step 1 — discover the cluster name:\n' +
|
|
2494
2670
|
' ductape_cli("cloud resources list -f /tmp/atlas-list.json --json")\n' +
|
|
2495
2671
|
' File: {"cloud": "<atlas-connection-tag>", "service": "atlas-cluster"}\n' +
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -65,6 +65,67 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
65
65
|
ductape_cli("db migrate rollback") # roll back last migration
|
|
66
66
|
ductape_cli("db migrate rollback -n 3") # roll back last 3 migrations
|
|
67
67
|
|
|
68
|
+
DATABASE SCHEMA FILE — ductape/database/schema.json
|
|
69
|
+
─────────────────────────────────────────────────────────────────────────
|
|
70
|
+
IMPORTANT: "db schema generate" READS schema.json and emits migration files.
|
|
71
|
+
It does NOT create schema.json from scratch. You must write this file first.
|
|
72
|
+
|
|
73
|
+
If schema.json does not exist, "db schema generate" now auto-creates it and
|
|
74
|
+
attempts to hydrate it from the cloud (pass --db <tag> to enable cloud hydration):
|
|
75
|
+
ductape_cli("db schema generate --db statecraft-core")
|
|
76
|
+
If the cloud database is empty, schema.json will be created as [] and you must
|
|
77
|
+
populate it before running the command again.
|
|
78
|
+
|
|
79
|
+
FILE FORMAT — ductape/database/schema.json is a JSON array:
|
|
80
|
+
[
|
|
81
|
+
{
|
|
82
|
+
"db": "<database-component-tag>",
|
|
83
|
+
"tables": {
|
|
84
|
+
"<collection_name>": {
|
|
85
|
+
"<field_name>": {
|
|
86
|
+
"type": "string|number|boolean|date|datetime|timestamp|uuid|object|array|json|text|decimal|bigint|smallint|double|binary|blob|time",
|
|
87
|
+
"required": true, // false by default (nullable)
|
|
88
|
+
"unique": true, // unique constraint on this field
|
|
89
|
+
"index": true, // create an index on this field (use with unique for unique index)
|
|
90
|
+
"default": "value", // default value ("now"/"NOW" → CURRENT_TIMESTAMP)
|
|
91
|
+
"maxlength": 255, // max length for string fields
|
|
92
|
+
"enum": ["a", "b", "c"], // restrict to enum values (sets type to string automatically)
|
|
93
|
+
"primaryKey": true, // mark as primary key
|
|
94
|
+
"autoGenerate": true, // auto-generate value on insert
|
|
95
|
+
"float": true // treat number as float (use with type: "number")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
SINGLE-FIELD INDEX: set index: true (+ unique: true for unique constraint)
|
|
103
|
+
"matchId": { "type": "string", "required": true, "unique": true, "index": true }
|
|
104
|
+
|
|
105
|
+
COMPOSITE INDEX: schema.json does not support composite indexes.
|
|
106
|
+
After running db schema generate, add a manual migration file in
|
|
107
|
+
ductape/migrations/<db-tag>/ with type "createIndex" and a fields array:
|
|
108
|
+
{
|
|
109
|
+
"tag": "create_map_events_match_seq_idx",
|
|
110
|
+
"name": "Composite unique index on matchId+sequence",
|
|
111
|
+
"up": [{
|
|
112
|
+
"type": "createIndex",
|
|
113
|
+
"collection": "map_events",
|
|
114
|
+
"name": "map_events_matchId_sequence_idx",
|
|
115
|
+
"fields": [{ "name": "matchId" }, { "name": "sequence" }],
|
|
116
|
+
"unique": true,
|
|
117
|
+
"ifNotExists": true
|
|
118
|
+
}],
|
|
119
|
+
"down": [{ "type": "dropIndex", "collection": "map_events", "name": "map_events_matchId_sequence_idx", "ifExists": true }],
|
|
120
|
+
"createdAt": "<ISO timestamp>"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
WORKFLOW:
|
|
124
|
+
1. Create/edit ductape/database/schema.json with your collection definitions
|
|
125
|
+
2. ductape_cli("db schema generate --db <tag>") → writes migration files
|
|
126
|
+
3. ductape_cli("db migrate --db <tag>") → applies pending migrations
|
|
127
|
+
4. Add manual migration files for composite/partial indexes not expressible in schema.json
|
|
128
|
+
|
|
68
129
|
1. ASSET CREATION / UPDATE (create, update, add, register… for ANY asset type)
|
|
69
130
|
ALL creation and update operations require an access key and CANNOT go through ductape_execute.
|
|
70
131
|
→ Use ductape_cli for every create/update operation. Examples:
|
|
@@ -122,6 +183,107 @@ There are THREE categories of operations. Use the right tool for each:
|
|
|
122
183
|
|
|
123
184
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
124
185
|
|
|
186
|
+
━━━ NESTJS INTEGRATION — use @ductape/nestjs ━━━
|
|
187
|
+
|
|
188
|
+
When the target application is a NestJS service or controller, always use @ductape/nestjs
|
|
189
|
+
instead of instantiating @ductape/sdk directly. It provides NestJS DI integration,
|
|
190
|
+
global interceptors, decorators, and type-safe resource handles.
|
|
191
|
+
|
|
192
|
+
SETUP — register once in AppModule:
|
|
193
|
+
|
|
194
|
+
import { DuctapeModule } from '@ductape/nestjs';
|
|
195
|
+
|
|
196
|
+
@Module({
|
|
197
|
+
imports: [
|
|
198
|
+
DuctapeModule.forIntegration({
|
|
199
|
+
accessKey: process.env.DUCTAPE_ACCESS_KEY,
|
|
200
|
+
product: 'my-product', // optional default — overridable per controller
|
|
201
|
+
env: process.env.NODE_ENV === 'production' ? 'prd' : 'snd',
|
|
202
|
+
}),
|
|
203
|
+
],
|
|
204
|
+
})
|
|
205
|
+
export class AppModule {}
|
|
206
|
+
|
|
207
|
+
// Async (e.g. pulling key from ConfigService):
|
|
208
|
+
DuctapeModule.forRootAsync({
|
|
209
|
+
imports: [ConfigModule],
|
|
210
|
+
inject: [ConfigService],
|
|
211
|
+
useFactory: (cfg: ConfigService) => ({
|
|
212
|
+
accessKey: cfg.get('DUCTAPE_ACCESS_KEY'),
|
|
213
|
+
product: cfg.get('DUCTAPE_PRODUCT'),
|
|
214
|
+
env: cfg.get('DUCTAPE_ENV'),
|
|
215
|
+
}),
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
INJECTING IN SERVICES AND CONTROLLERS — use @InjectContext():
|
|
219
|
+
|
|
220
|
+
import { InjectContext, DuctapeContext } from '@ductape/nestjs';
|
|
221
|
+
|
|
222
|
+
@Injectable()
|
|
223
|
+
export class MatchService {
|
|
224
|
+
constructor(@InjectContext() private readonly ductape: DuctapeContext) {}
|
|
225
|
+
|
|
226
|
+
async publishMatchState(matchId: string, state: object) {
|
|
227
|
+
await this.ductape.sdk.events.produce({
|
|
228
|
+
product: 'my-product',
|
|
229
|
+
env: 'prd',
|
|
230
|
+
event: 'statecraft-events:match-state',
|
|
231
|
+
message: { matchId, ...state },
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async queryDatabase(env: string) {
|
|
236
|
+
const db = await this.ductape.database('core-db', { env });
|
|
237
|
+
return db.query({ table: 'matches', where: { active: true } });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
AVAILABLE ON DuctapeContext:
|
|
242
|
+
|
|
243
|
+
ctx.sdk — full @ductape/sdk instance (events, sessions, etc.)
|
|
244
|
+
ctx.database(tag, overrides?) — connected DatabaseHandle (query, insert, update, delete)
|
|
245
|
+
ctx.storage(tag, overrides?) — StorageHandle (upload, download, remove)
|
|
246
|
+
ctx.cache(tag, overrides?) — CacheHandle (get, set, delete)
|
|
247
|
+
ctx.graph(tag, overrides?) — GraphHandle
|
|
248
|
+
ctx.vector(tag, overrides?) — VectorHandle
|
|
249
|
+
ctx.agent(tag, overrides?) — AgentHandle
|
|
250
|
+
ctx.warehouse(overrides?) — WarehouseHandle
|
|
251
|
+
ctx.cloudTiers(query?) — query available cloud resource tiers and pricing
|
|
252
|
+
ctx.runJob({ product, env, event, input? }) — trigger a product job
|
|
253
|
+
|
|
254
|
+
CONTROLLER DECORATORS:
|
|
255
|
+
|
|
256
|
+
@Product('my-product') — override default product at class or method level
|
|
257
|
+
@Env('prd') — override default env
|
|
258
|
+
@ApiRun({ app, action }) — bind a controller method to an app action (interceptor runs it automatically)
|
|
259
|
+
@ApiConfig({ app, ... }) — shared app credentials at class level
|
|
260
|
+
@Webhook.Register(...) — register a webhook consumer URL
|
|
261
|
+
@Webhook.Consumer(...) — mark inbound handler for forwarded webhook payloads
|
|
262
|
+
|
|
263
|
+
FOR MESSAGING (events.produce / events.consume):
|
|
264
|
+
|
|
265
|
+
Access via ctx.sdk.events — no dedicated NestJS handle, the SDK instance is sufficient:
|
|
266
|
+
|
|
267
|
+
await this.ductape.sdk.events.produce({
|
|
268
|
+
product: 'my-product', env: 'prd',
|
|
269
|
+
event: 'broker-tag:topic-tag',
|
|
270
|
+
message: { ... },
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
await this.ductape.sdk.events.consume({
|
|
274
|
+
product: 'my-product', env: 'prd',
|
|
275
|
+
event: 'broker-tag:topic-tag',
|
|
276
|
+
callback: async (message) => { /* handle */ },
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
SPECIALIZED MODULES (for injecting handles directly without @InjectContext):
|
|
280
|
+
|
|
281
|
+
DuctapeDatabaseModule.forTags(['core-db']) → inject with @Database('core-db')
|
|
282
|
+
DuctapeStorageModule.forTags(['assets']) → inject with @Storage('assets')
|
|
283
|
+
DuctapeCacheModule, DuctapeGraphModule, DuctapeVectorModule — same pattern
|
|
284
|
+
|
|
285
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
286
|
+
|
|
125
287
|
ALL params are passed as a JSON array in positional order matching the SDK signature.
|
|
126
288
|
|
|
127
289
|
━━━ MODULE: product ━━━
|
|
@@ -351,11 +513,23 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
|
|
|
351
513
|
storage.dispatch [{ product, env, storage, operation, input, retries?, session?, cache?, schedule? }] ← CALL ductape_generate_payload FIRST (operation_family="storage", method="dispatch", targets={storage})
|
|
352
514
|
|
|
353
515
|
━━━ MODULE: databases ━━━
|
|
354
|
-
databases.create [{ product, tag, name, description?, type: "mongodb"|"postgresql"|"mysql"|"sqlite",
|
|
516
|
+
databases.create [{ product, tag, name, description?, type: "mongodb"|"postgresql"|"mysql"|"sqlite",
|
|
517
|
+
envs: [{ slug, connection_url?,
|
|
518
|
+
cloud?, ← workspace cloud-connection tag (omit connection_url when using this)
|
|
519
|
+
authMode?, ← "cloud_connection" when using cloud field
|
|
520
|
+
dbName?, ← REQUIRED for MongoDB cloud connections — the database name inside the cluster (e.g. "myapp_snd")
|
|
521
|
+
instance?, ← RDS/Cloud SQL instance id (provision flow only)
|
|
522
|
+
region? }] }]
|
|
523
|
+
MONGODB CLOUD CONNECTION RULE: when type="mongodb" and cloud is set, dbName is MANDATORY.
|
|
524
|
+
Without dbName the Atlas API returns a URL with no database name and the adapter will throw
|
|
525
|
+
at migrate/connect time. Provide one dbName per env — they typically differ per environment:
|
|
526
|
+
e.g. { slug:"snd", cloud:"atlas-conn", authMode:"cloud_connection", dbName:"myapp_snd" }
|
|
527
|
+
{ slug:"prd", cloud:"atlas-conn", authMode:"cloud_connection", dbName:"myapp_prd" }
|
|
355
528
|
databases.register [product_tag, data: IProductDatabase]
|
|
356
529
|
databases.list [product_tag]
|
|
357
530
|
databases.fetch [product_tag, database_tag]
|
|
358
|
-
databases.updateDatabase [product_tag, database_tag, data: { name
|
|
531
|
+
databases.updateDatabase [product_tag, database_tag, data: { name?, description?, type?,
|
|
532
|
+
envs?: [{ slug, connection_url?, cloud?, authMode?, dbName?, instance?, region? }] }]
|
|
359
533
|
databases.connect [{ product, env, database }]
|
|
360
534
|
databases.testConnection [{ product, env, database }]
|
|
361
535
|
databases.disconnect []
|
|
@@ -2616,6 +2790,8 @@ async function main() {
|
|
|
2616
2790
|
' azure-blob→storage, cloud-sql→database, neptune→graph, opensearch→vector. Attempting an unsupported pair\n' +
|
|
2617
2791
|
' will return an error; do not retry with a different tier — report the limitation to the user.\n' +
|
|
2618
2792
|
' - Atlas (MongoDB Atlas) import flow — service identifier is "atlas-cluster" (required, not optional):\n' +
|
|
2793
|
+
' IMPORTANT: dbName is mandatory for every MongoDB env — see "MONGODB CLOUD CONNECTION RULE" in the\n' +
|
|
2794
|
+
' databases module above. The SDK enforces this at create time; omitting it throws a validation error.\n' +
|
|
2619
2795
|
' Step 1 — discover the cluster name:\n' +
|
|
2620
2796
|
' ductape_cli("cloud resources list -f /tmp/atlas-list.json --json")\n' +
|
|
2621
2797
|
' File: {"cloud": "<atlas-connection-tag>", "service": "atlas-cluster"}\n' +
|