atomic-queues 2.0.2 → 2.1.1
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/README.md +138 -19
- package/dist/cli/generators/classes.d.ts +12 -0
- package/dist/cli/generators/classes.d.ts.map +1 -0
- package/dist/cli/generators/classes.js +141 -0
- package/dist/cli/generators/classes.js.map +1 -0
- package/dist/cli/generators/typescript.d.ts.map +1 -1
- package/dist/cli/generators/typescript.js +35 -0
- package/dist/cli/generators/typescript.js.map +1 -1
- package/dist/cli/index.js +170 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/domain/interfaces/config.interfaces.d.ts +4 -0
- package/dist/domain/interfaces/config.interfaces.d.ts.map +1 -1
- package/dist/domain/interfaces/index.d.ts +1 -0
- package/dist/domain/interfaces/index.d.ts.map +1 -1
- package/dist/domain/interfaces/index.js +1 -0
- package/dist/domain/interfaces/index.js.map +1 -1
- package/dist/domain/interfaces/reply.types.d.ts +27 -0
- package/dist/domain/interfaces/reply.types.d.ts.map +1 -0
- package/dist/domain/interfaces/reply.types.js +19 -0
- package/dist/domain/interfaces/reply.types.js.map +1 -0
- package/dist/services/actor-registry/actor-registry.service.d.ts.map +1 -1
- package/dist/services/actor-registry/actor-registry.service.js +29 -8
- package/dist/services/actor-registry/actor-registry.service.js.map +1 -1
- package/dist/services/executor-pool/executor-pool.service.d.ts +7 -0
- package/dist/services/executor-pool/executor-pool.service.d.ts.map +1 -1
- package/dist/services/executor-pool/executor-pool.service.js +34 -1
- package/dist/services/executor-pool/executor-pool.service.js.map +1 -1
- package/dist/services/handler-executor/handler-executor.service.d.ts +1 -0
- package/dist/services/handler-executor/handler-executor.service.d.ts.map +1 -1
- package/dist/services/handler-executor/handler-executor.service.js +16 -0
- package/dist/services/handler-executor/handler-executor.service.js.map +1 -1
- package/dist/services/queue-bus/cluster-contracts.d.ts +77 -0
- package/dist/services/queue-bus/cluster-contracts.d.ts.map +1 -0
- package/dist/services/queue-bus/cluster-contracts.js +118 -0
- package/dist/services/queue-bus/cluster-contracts.js.map +1 -0
- package/dist/services/queue-bus/index.d.ts +1 -0
- package/dist/services/queue-bus/index.d.ts.map +1 -1
- package/dist/services/queue-bus/index.js +1 -0
- package/dist/services/queue-bus/index.js.map +1 -1
- package/dist/services/queue-bus/queue-bus.service.d.ts +42 -13
- package/dist/services/queue-bus/queue-bus.service.d.ts.map +1 -1
- package/dist/services/queue-bus/queue-bus.service.js +77 -21
- package/dist/services/queue-bus/queue-bus.service.js.map +1 -1
- package/dist/services/registry/registry.service.d.ts.map +1 -1
- package/dist/services/registry/registry.service.js +31 -10
- package/dist/services/registry/registry.service.js.map +1 -1
- package/dist/services/registry/registry.types.d.ts +1 -0
- package/dist/services/registry/registry.types.d.ts.map +1 -1
- package/dist/services/scheduler/scheduler.service.d.ts +1 -1
- package/dist/services/scheduler/scheduler.service.d.ts.map +1 -1
- package/dist/services/scheduler/scheduler.service.js +36 -14
- package/dist/services/scheduler/scheduler.service.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -108,21 +108,20 @@ That's the entire contract. `@EntityType` says "this message targets the `accoun
|
|
|
108
108
|
|
|
109
109
|
atomic-queues gives you two ways to handle messages, and they're not different systems — they're two levels of abstraction over the same dispatch engine.
|
|
110
110
|
|
|
111
|
-
**Actors** are the foundational primitive. An actor class *is* an entity — its fields are the state, its methods are message handlers. The runtime manages its lifecycle: activate on first message, evict from memory on idle, persist state to Redis automatically, restore on reactivation.
|
|
111
|
+
**Actors** are the foundational primitive. An actor class *is* an entity — its fields are the state, its methods are message handlers. The runtime manages its lifecycle: activate on first message, evict from memory on idle, persist state to Redis automatically, restore on reactivation. The entity type is inferred from the message classes' `@EntityType` decorator — no `@Actor` decorator required.
|
|
112
112
|
|
|
113
113
|
```typescript
|
|
114
|
-
@Actor('account')
|
|
115
114
|
@Injectable()
|
|
116
115
|
export class AccountActor {
|
|
117
116
|
private balance = 0;
|
|
118
117
|
|
|
119
|
-
@On(DepositCommand)
|
|
118
|
+
@On(DepositCommand) // DepositCommand has @EntityType('account')
|
|
120
119
|
async deposit(msg: DepositCommand) {
|
|
121
120
|
this.balance += msg.amount;
|
|
122
121
|
return this.balance;
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
@On(WithdrawCommand)
|
|
124
|
+
@On(WithdrawCommand) // WithdrawCommand has @EntityType('account')
|
|
126
125
|
async withdraw(msg: WithdrawCommand) {
|
|
127
126
|
if (this.balance < msg.amount) throw new InsufficientFunds();
|
|
128
127
|
this.balance -= msg.amount;
|
|
@@ -159,12 +158,20 @@ The library auto-discovers `@CommandHandler` and `@QueryHandler` classes at boot
|
|
|
159
158
|
// Fire-and-forget
|
|
160
159
|
await queueBus.enqueue(new WithdrawCommand(accountId, 100));
|
|
161
160
|
|
|
162
|
-
// Enqueue and block until result
|
|
161
|
+
// Enqueue and block until result — return type inferred from Reply<T> brand
|
|
163
162
|
const balance = await queueBus.enqueueAndWait(new GetBalanceQuery(accountId));
|
|
164
163
|
|
|
165
164
|
// Scoped to an entity type
|
|
166
165
|
await queueBus.forEntity('account').enqueueBulk([charge1, charge2, charge3]);
|
|
167
166
|
|
|
167
|
+
// Cross-service: string-based API — no class import needed
|
|
168
|
+
await queueBus.enqueue('warehouse', 'ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
169
|
+
const stock = await queueBus.enqueueAndWait('warehouse', 'GetStockQuery', 'SKU-001', { sku: 'SKU-001' });
|
|
170
|
+
|
|
171
|
+
// Scoped cross-service
|
|
172
|
+
const warehouse = queueBus.forEntity('warehouse');
|
|
173
|
+
await warehouse.enqueue('ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
174
|
+
|
|
168
175
|
// Actor-style direct send
|
|
169
176
|
await actorSystem.send('account', accountId, new DepositCommand(100));
|
|
170
177
|
const balance = await actorSystem.sendAndWait('account', accountId, new GetBalanceQuery());
|
|
@@ -228,17 +235,23 @@ atomic-queues replaces all of that with Redis.
|
|
|
228
235
|
Enable the distributed registry and any service connected to the same Redis instance can send typed messages to any entity — regardless of which service owns the handler.
|
|
229
236
|
|
|
230
237
|
```typescript
|
|
231
|
-
//
|
|
238
|
+
// warehouse-service: defines and handles the entity
|
|
232
239
|
AtomicQueuesModule.forRoot({
|
|
233
240
|
redis: { url: process.env.REDIS_URL },
|
|
234
|
-
registry: { enabled: true, serviceName: '
|
|
241
|
+
registry: { enabled: true, serviceName: 'warehouse-service' },
|
|
235
242
|
})
|
|
236
243
|
|
|
237
|
-
//
|
|
238
|
-
|
|
244
|
+
// order-service: generate classes from the live registry, then use them like local CQRS
|
|
245
|
+
import { ReserveStockCommand, GetStockQuery } from './generated';
|
|
246
|
+
|
|
247
|
+
await queueBus.enqueue(new ReserveStockCommand({ sku: 'SKU-001', quantity: 50 }));
|
|
248
|
+
const stock = await queueBus.enqueueAndWait(new GetStockQuery({ sku: 'SKU-001' }));
|
|
249
|
+
stock.available; // fully typed — no string API, no explicit timeout, no code dependency on warehouse-service
|
|
239
250
|
```
|
|
240
251
|
|
|
241
|
-
When `
|
|
252
|
+
When `warehouse-service` starts, it scans its own `@Actor`, `@CommandHandler`, and `@QueryHandler` classes and publishes **entity contracts** to Redis — a JSON document listing the entity type, accepted messages, optional JSON schemas, and reply schemas, refreshed via heartbeat TTL. When `order-service` enqueues a message, the registry validates it at the call site *before* it enters the log: entity type exists, message name is accepted, payload matches schema. Errors are immediate and descriptive — not silent dead letters discovered hours later in a DLQ dashboard.
|
|
253
|
+
|
|
254
|
+
The Lua scheduler ensures each node only dispatches messages for entity types it owns handlers for. Services that don't own any handlers (API gateways, pure producers) participate in the registry without stealing messages from handler-owning nodes.
|
|
242
255
|
|
|
243
256
|
### What this replaces
|
|
244
257
|
|
|
@@ -252,7 +265,7 @@ Think about what you no longer need:
|
|
|
252
265
|
|
|
253
266
|
**No service discovery.** The registry *is* service discovery. When a service starts, it publishes what it handles. When a service stops, its registrations TTL out. Other services discover capabilities by reading the registry.
|
|
254
267
|
|
|
255
|
-
**No serialization framework.** Messages are JSON. The wire protocol is three Redis commands. No Protobuf compilation step, no `.proto` files, no code generation from IDL. (Though atomic-queues does offer codegen from the live registry — it generates TypeScript
|
|
268
|
+
**No serialization framework.** Messages are JSON. The wire protocol is three Redis commands. No Protobuf compilation step, no `.proto` files, no code generation from IDL. (Though atomic-queues does offer codegen from the live registry — it generates decorated TypeScript classes so Service A gets compile-time type safety for messages destined to Service B, without importing Service B's code.)
|
|
256
269
|
|
|
257
270
|
**No separate dead-letter infrastructure.** Failed messages are dead-lettered per entity type in Redis, queryable via the same connection.
|
|
258
271
|
|
|
@@ -281,15 +294,118 @@ The Zod schema serializes to JSON Schema and stores in the registry. Every servi
|
|
|
281
294
|
|
|
282
295
|
Multiple services can handle different message types on the same entity. Service A handles `DepositCommand` and `WithdrawCommand` on the `account` entity type. Service B handles `FreezeAccountCommand` on the same entity type. The registry merges their contracts automatically. The dispatch gate still ensures single-writer semantics per entity instance, regardless of which service's executor picks up the message.
|
|
283
296
|
|
|
284
|
-
###
|
|
297
|
+
### Runtime introspection
|
|
298
|
+
|
|
299
|
+
Any service can discover what the cluster offers at runtime — no config files, no shared code:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const contracts = await queueBus.introspect();
|
|
303
|
+
|
|
304
|
+
contracts.entityTypes(); // ['account', 'warehouse', ...]
|
|
305
|
+
contracts.hasEntity('warehouse'); // true
|
|
306
|
+
contracts.messagesFor('warehouse'); // ['ReserveStockCommand', 'GetStockQuery']
|
|
307
|
+
contracts.accepts('warehouse', 'ReserveStockCommand'); // true
|
|
308
|
+
contracts.schemaFor('warehouse', 'ReserveStockCommand'); // { properties: { sku: ..., quantity: ... } }
|
|
309
|
+
contracts.replySchemaFor('warehouse', 'GetStockQuery'); // { properties: { sku: ..., available: ... } }
|
|
310
|
+
|
|
311
|
+
// Human-readable summary for logging/debugging
|
|
312
|
+
console.log(contracts.toString());
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Raw cross-service API
|
|
316
|
+
|
|
317
|
+
For quick prototyping or dynamic dispatch, you can also use the string-based API — no classes, no codegen, no imports:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// Fire-and-forget
|
|
321
|
+
await queueBus.enqueue('warehouse', 'ReserveStockCommand', 'SKU-001', {
|
|
322
|
+
sku: 'SKU-001',
|
|
323
|
+
quantity: 50,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// Request-reply
|
|
327
|
+
const stock = await queueBus.enqueueAndWait('warehouse', 'GetStockQuery', 'SKU-001', {
|
|
328
|
+
sku: 'SKU-001',
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Scoped to an entity type
|
|
332
|
+
const warehouse = queueBus.forEntity('warehouse');
|
|
333
|
+
await warehouse.enqueue('ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
334
|
+
const stock = await warehouse.enqueueAndWait('GetStockQuery', 'SKU-001', { sku: 'SKU-001' });
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
This works out of the box — the registry validates entity type and message name at the call site. For production services, class codegen gives you full type safety.
|
|
285
338
|
|
|
286
|
-
|
|
339
|
+
### Class codegen (recommended)
|
|
340
|
+
|
|
341
|
+
Generate fully decorated TypeScript classes from the live registry — import them and use them like local CQRS classes with full autocomplete, type safety, and zero string APIs:
|
|
287
342
|
|
|
288
343
|
```bash
|
|
289
|
-
|
|
344
|
+
npx atomic-queues generate --classes -o src/generated
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
This produces one file per entity type plus a barrel `index.ts`:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
src/generated/
|
|
351
|
+
warehouse.ts # ReserveStockCommand, GetStockQuery, data interfaces, reply interfaces
|
|
352
|
+
billing.ts # ChargeCommand, GetInvoiceQuery, ...
|
|
353
|
+
index.ts # export * from './warehouse'; export * from './billing';
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Then use them exactly like local command/query classes:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { ReserveStockCommand, GetStockQuery } from './generated';
|
|
360
|
+
|
|
361
|
+
// Fire-and-forget — full autocomplete on constructor fields
|
|
362
|
+
await queueBus.enqueue(new ReserveStockCommand({ sku: 'SKU-001', quantity: 50 }));
|
|
363
|
+
|
|
364
|
+
// Request-reply — return type inferred from Reply<T> brand, no explicit timeout
|
|
365
|
+
const stock = await queueBus.enqueueAndWait(new GetStockQuery({ sku: 'SKU-001' }));
|
|
366
|
+
stock.available; // typed as number — full IDE support
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
Generated query classes implement `Reply<T>` via a phantom type brand, so `enqueueAndWait` infers the return type at compile time with zero runtime cost. No explicit generics, no timeout parameter — timeouts are resolved from config.
|
|
370
|
+
|
|
371
|
+
You can also filter to specific entity types:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
npx atomic-queues generate --classes -o src/generated --entities warehouse,billing
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Other codegen formats
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
# TypeScript interfaces + DispatchMap (for typed string-based API)
|
|
381
|
+
npx atomic-queues generate --ts --output ./generated/contracts.ts
|
|
382
|
+
|
|
383
|
+
# JSON Schema (language-agnostic)
|
|
384
|
+
npx atomic-queues generate --json-schema --output ./generated/schema.json
|
|
385
|
+
|
|
386
|
+
# Full registry snapshot
|
|
387
|
+
npx atomic-queues generate --snapshot --output ./generated/snapshot.json
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Config-driven timeouts
|
|
391
|
+
|
|
392
|
+
`enqueueAndWait` resolves timeouts automatically — you never need to pass one explicitly:
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
AtomicQueuesModule.forRoot({
|
|
396
|
+
executor: {
|
|
397
|
+
gateTTL: 30,
|
|
398
|
+
defaultReplyTimeout: 15000, // global fallback: 15s
|
|
399
|
+
},
|
|
400
|
+
entities: {
|
|
401
|
+
warehouse: {
|
|
402
|
+
replyTimeout: 5000, // warehouse-specific: 5s
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
})
|
|
290
406
|
```
|
|
291
407
|
|
|
292
|
-
|
|
408
|
+
Resolution chain: explicit arg → per-entity `replyTimeout` → global `defaultReplyTimeout` → `gateTTL * 2 * 1000`. If nothing is configured, defaults to 60s.
|
|
293
409
|
|
|
294
410
|
---
|
|
295
411
|
|
|
@@ -368,8 +484,9 @@ AtomicQueuesModule.forRoot({
|
|
|
368
484
|
redis: { host: 'localhost', port: 6379 },
|
|
369
485
|
|
|
370
486
|
executor: {
|
|
371
|
-
poolSize: 1,
|
|
372
|
-
gateTTL: 30,
|
|
487
|
+
poolSize: 1, // concurrent executors per node
|
|
488
|
+
gateTTL: 30, // seconds before gate expires (safety net)
|
|
489
|
+
defaultReplyTimeout: 15000, // global default for enqueueAndWait (ms)
|
|
373
490
|
},
|
|
374
491
|
|
|
375
492
|
entities: {
|
|
@@ -379,6 +496,7 @@ AtomicQueuesModule.forRoot({
|
|
|
379
496
|
retry: { maxAttempts: 5, backoff: 'exponential', backoffDelay: 2000 },
|
|
380
497
|
actorIdleTimeout: 120000,
|
|
381
498
|
statePersistence: true,
|
|
499
|
+
replyTimeout: 5000, // per-entity enqueueAndWait timeout (ms)
|
|
382
500
|
},
|
|
383
501
|
},
|
|
384
502
|
|
|
@@ -443,9 +561,10 @@ npm install zod zod-to-json-schema # for schema validation in the registry
|
|
|
443
561
|
| `@EntityType('type')` | Route a message to an entity type |
|
|
444
562
|
| `@QueueEntityId()` | Mark the property holding the entity ID |
|
|
445
563
|
| `@QueueEntity('type', 'prop')` | Combined entity type + ID |
|
|
446
|
-
| `@Actor('type')` |
|
|
564
|
+
| `@Actor('type')` | Explicitly declare an actor (optional — entity type is inferred from `@On` handlers) |
|
|
447
565
|
| `@On(MessageClass)` | Handle a message type on an actor |
|
|
448
566
|
| `@Schema(zodSchema)` | Attach a Zod schema for registry validation |
|
|
567
|
+
| `@ReplySchema(zodSchema)` | Attach a reply schema for query codegen |
|
|
449
568
|
|
|
450
569
|
---
|
|
451
570
|
|
|
@@ -457,7 +576,7 @@ V2 is a full rewrite of the internals. BullMQ is removed. Workers are removed. T
|
|
|
457
576
|
|
|
458
577
|
**What's removed**: `@WorkerProcessor`, `@JobHandler`, `@EntityScaler`, `@OnSpawnWorker`, `@OnTerminateWorker`, `@GetActiveEntities`, `@GetDesiredWorkerCount`, `.forProcessor()`. All worker and scaling concepts are gone.
|
|
459
578
|
|
|
460
|
-
**What's new**: `@Actor`, `@On`, `@Schema`, `ActorSystem`, `RegistryService`, distributed registry, codegen CLI.
|
|
579
|
+
**What's new**: `@Actor`, `@On`, `@Schema`, `@ReplySchema`, `ActorSystem`, `RegistryService`, distributed registry, runtime introspection (`queueBus.introspect()`), cross-service string-based API, `Reply<T>` phantom type, class codegen CLI (`--classes`), config-driven timeouts.
|
|
461
580
|
|
|
462
581
|
**Migration steps**: (1) remove all `@WorkerProcessor` classes — replace with `@Actor` or configure entity defaults in module config; (2) remove all scaling decorators; (3) run the data migration script to drain in-flight BullMQ jobs to the new log format; (4) remove `bullmq` and `@nestjs/bullmq` from your dependencies.
|
|
463
582
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RegistrySnapshot } from '../../services/registry/registry.types';
|
|
2
|
+
export interface GeneratedFile {
|
|
3
|
+
filename: string;
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Generates instantiable TypeScript classes from the registry snapshot.
|
|
8
|
+
* Each entity type gets its own file with decorated command/query classes
|
|
9
|
+
* that work directly with `queueBus.enqueue()` and `queueBus.enqueueAndWait()`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateClasses(snapshot: RegistrySnapshot): GeneratedFile[];
|
|
12
|
+
//# sourceMappingURL=classes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.d.ts","sourceRoot":"","sources":["../../../src/cli/generators/classes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA+B,MAAM,wCAAwC,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,gBAAgB,GAAG,aAAa,EAAE,CAqB3E"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateClasses = generateClasses;
|
|
4
|
+
/**
|
|
5
|
+
* Generates instantiable TypeScript classes from the registry snapshot.
|
|
6
|
+
* Each entity type gets its own file with decorated command/query classes
|
|
7
|
+
* that work directly with `queueBus.enqueue()` and `queueBus.enqueueAndWait()`.
|
|
8
|
+
*/
|
|
9
|
+
function generateClasses(snapshot) {
|
|
10
|
+
const files = [];
|
|
11
|
+
for (const entity of snapshot.entities) {
|
|
12
|
+
const filename = `${kebabCase(entity.entityType)}.ts`;
|
|
13
|
+
const content = generateEntityFile(entity, snapshot);
|
|
14
|
+
files.push({ filename, content });
|
|
15
|
+
}
|
|
16
|
+
// Barrel index
|
|
17
|
+
const barrelLines = [
|
|
18
|
+
header(snapshot),
|
|
19
|
+
'',
|
|
20
|
+
];
|
|
21
|
+
for (const entity of snapshot.entities) {
|
|
22
|
+
barrelLines.push(`export * from './${kebabCase(entity.entityType)}';`);
|
|
23
|
+
}
|
|
24
|
+
barrelLines.push('');
|
|
25
|
+
files.push({ filename: 'index.ts', content: barrelLines.join('\n') });
|
|
26
|
+
return files;
|
|
27
|
+
}
|
|
28
|
+
function generateEntityFile(entity, snapshot) {
|
|
29
|
+
const lines = [];
|
|
30
|
+
const messages = Object.entries(entity.messages);
|
|
31
|
+
const hasReply = messages.some(([, spec]) => spec.kind === 'query' && spec.replySchema?.properties);
|
|
32
|
+
lines.push(header(snapshot));
|
|
33
|
+
lines.push(`// Entity: ${entity.entityType} (service: ${entity.serviceName})`);
|
|
34
|
+
lines.push('');
|
|
35
|
+
// Imports
|
|
36
|
+
const imports = ['EntityType'];
|
|
37
|
+
const hasEntityId = messages.some(([, spec]) => spec.entityIdField);
|
|
38
|
+
if (hasEntityId)
|
|
39
|
+
imports.push('QueueEntityId');
|
|
40
|
+
lines.push(`import { ${imports.join(', ')} } from 'atomic-queues';`);
|
|
41
|
+
if (hasReply) {
|
|
42
|
+
lines.push(`import type { Reply } from 'atomic-queues';`);
|
|
43
|
+
}
|
|
44
|
+
lines.push('');
|
|
45
|
+
for (const [msgName, spec] of messages) {
|
|
46
|
+
lines.push(generateMessage(entity.entityType, msgName, spec));
|
|
47
|
+
lines.push('');
|
|
48
|
+
}
|
|
49
|
+
return lines.join('\n');
|
|
50
|
+
}
|
|
51
|
+
function generateMessage(entityType, msgName, spec) {
|
|
52
|
+
const lines = [];
|
|
53
|
+
const fields = extractFields(spec);
|
|
54
|
+
const hasReplySchema = !!spec.replySchema?.properties;
|
|
55
|
+
// Data interface
|
|
56
|
+
const dataInterfaceName = `${msgName}Data`;
|
|
57
|
+
lines.push(`export interface ${dataInterfaceName} {`);
|
|
58
|
+
for (const field of fields) {
|
|
59
|
+
lines.push(` ${field.name}${field.required ? '' : '?'}: ${field.tsType};`);
|
|
60
|
+
}
|
|
61
|
+
lines.push('}');
|
|
62
|
+
lines.push('');
|
|
63
|
+
// Reply interface (for queries with reply schemas)
|
|
64
|
+
if (hasReplySchema) {
|
|
65
|
+
const replyFields = extractFieldsFromSchema(spec.replySchema);
|
|
66
|
+
const replyName = `${msgName}Reply`;
|
|
67
|
+
lines.push(`export interface ${replyName} {`);
|
|
68
|
+
for (const field of replyFields) {
|
|
69
|
+
lines.push(` ${field.name}${field.required ? '' : '?'}: ${field.tsType};`);
|
|
70
|
+
}
|
|
71
|
+
lines.push('}');
|
|
72
|
+
lines.push('');
|
|
73
|
+
}
|
|
74
|
+
// Class
|
|
75
|
+
const replyImpl = hasReplySchema ? ` implements Reply<${msgName}Reply>` : '';
|
|
76
|
+
lines.push(`@EntityType('${entityType}')`);
|
|
77
|
+
lines.push(`export class ${msgName}${replyImpl} {`);
|
|
78
|
+
// Phantom reply brand (type-only)
|
|
79
|
+
if (hasReplySchema) {
|
|
80
|
+
lines.push(` declare readonly __reply: ${msgName}Reply;`);
|
|
81
|
+
lines.push('');
|
|
82
|
+
}
|
|
83
|
+
// Fields with @QueueEntityId on the entity ID field
|
|
84
|
+
for (const field of fields) {
|
|
85
|
+
if (field.name === spec.entityIdField) {
|
|
86
|
+
lines.push(` @QueueEntityId() readonly ${field.name}!: ${field.tsType};`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
lines.push(` readonly ${field.name}${field.required ? '!' : '?'}: ${field.tsType};`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Constructor
|
|
93
|
+
lines.push('');
|
|
94
|
+
lines.push(` constructor(data: ${dataInterfaceName}) {`);
|
|
95
|
+
lines.push(' Object.assign(this, data);');
|
|
96
|
+
lines.push(' }');
|
|
97
|
+
lines.push('}');
|
|
98
|
+
return lines.join('\n');
|
|
99
|
+
}
|
|
100
|
+
function extractFields(spec) {
|
|
101
|
+
return extractFieldsFromSchema(spec.schema);
|
|
102
|
+
}
|
|
103
|
+
function extractFieldsFromSchema(schema) {
|
|
104
|
+
if (!schema?.properties)
|
|
105
|
+
return [];
|
|
106
|
+
const required = new Set(schema.required ?? []);
|
|
107
|
+
return Object.entries(schema.properties).map(([name, propSchema]) => ({
|
|
108
|
+
name,
|
|
109
|
+
tsType: jsonSchemaTypeToTS(propSchema),
|
|
110
|
+
required: required.has(name),
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
function jsonSchemaTypeToTS(schema) {
|
|
114
|
+
if (!schema.type)
|
|
115
|
+
return 'any';
|
|
116
|
+
switch (schema.type) {
|
|
117
|
+
case 'string': return schema.enum ? schema.enum.map((v) => `'${v}'`).join(' | ') : 'string';
|
|
118
|
+
case 'number':
|
|
119
|
+
case 'integer': return 'number';
|
|
120
|
+
case 'boolean': return 'boolean';
|
|
121
|
+
case 'array': return schema.items ? `${jsonSchemaTypeToTS(schema.items)}[]` : 'any[]';
|
|
122
|
+
case 'object': return 'Record<string, any>';
|
|
123
|
+
case 'null': return 'null';
|
|
124
|
+
default: return 'any';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function header(snapshot) {
|
|
128
|
+
return [
|
|
129
|
+
'// Auto-generated by atomic-queues — do not edit',
|
|
130
|
+
`// Source: Redis registry (prefix: '${snapshot.keyPrefix}')`,
|
|
131
|
+
`// Generated: ${new Date(snapshot.generatedAt).toISOString()}`,
|
|
132
|
+
'// Regenerate: npx atomic-queues generate --classes',
|
|
133
|
+
].join('\n');
|
|
134
|
+
}
|
|
135
|
+
function kebabCase(str) {
|
|
136
|
+
return str
|
|
137
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
138
|
+
.replace(/[\s_]+/g, '-')
|
|
139
|
+
.toLowerCase();
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=classes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classes.js","sourceRoot":"","sources":["../../../src/cli/generators/classes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/cli/generators/typescript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE1E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"typescript.d.ts","sourceRoot":"","sources":["../../../src/cli/generators/typescript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AAE1E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAiGrE"}
|
|
@@ -8,6 +8,7 @@ function generateTypeScript(snapshot) {
|
|
|
8
8
|
lines.push(`// Generated: ${new Date(snapshot.generatedAt).toISOString()}`);
|
|
9
9
|
lines.push('// DO NOT EDIT — regenerate with: npx atomic-queues generate --ts');
|
|
10
10
|
lines.push('');
|
|
11
|
+
// ── Per-entity namespaces with message interfaces ──────────────────────
|
|
11
12
|
for (const entity of snapshot.entities) {
|
|
12
13
|
const namespace = pascalCase(entity.entityType) + 'Entity';
|
|
13
14
|
lines.push(`export namespace ${namespace} {`);
|
|
@@ -37,6 +38,40 @@ function generateTypeScript(snapshot) {
|
|
|
37
38
|
lines.push('}');
|
|
38
39
|
lines.push('');
|
|
39
40
|
}
|
|
41
|
+
// ── DispatchMap — maps entity type → message name → payload type ───────
|
|
42
|
+
lines.push('// ── Dispatch type maps (for use with TypedDispatch / TypedDispatchAndWait) ──');
|
|
43
|
+
lines.push('');
|
|
44
|
+
lines.push('export interface DispatchMap {');
|
|
45
|
+
for (const entity of snapshot.entities) {
|
|
46
|
+
const namespace = pascalCase(entity.entityType) + 'Entity';
|
|
47
|
+
lines.push(` '${entity.entityType}': {`);
|
|
48
|
+
for (const [msgName, spec] of Object.entries(entity.messages)) {
|
|
49
|
+
const hasSchema = spec.schema?.properties && Object.keys(spec.schema.properties).length > 0;
|
|
50
|
+
const type = hasSchema ? `${namespace}.${msgName}` : 'Record<string, any>';
|
|
51
|
+
lines.push(` '${msgName}': ${type};`);
|
|
52
|
+
}
|
|
53
|
+
lines.push(' };');
|
|
54
|
+
}
|
|
55
|
+
lines.push('}');
|
|
56
|
+
lines.push('');
|
|
57
|
+
// ── ReplyMap — maps entity type → query name → reply type ─────────────
|
|
58
|
+
const hasAnyReply = snapshot.entities.some(e => Object.values(e.messages).some(m => m.kind === 'query' && m.replySchema?.properties));
|
|
59
|
+
if (hasAnyReply) {
|
|
60
|
+
lines.push('export interface ReplyMap {');
|
|
61
|
+
for (const entity of snapshot.entities) {
|
|
62
|
+
const namespace = pascalCase(entity.entityType) + 'Entity';
|
|
63
|
+
const queries = Object.entries(entity.messages).filter(([, spec]) => spec.kind === 'query' && spec.replySchema?.properties);
|
|
64
|
+
if (queries.length > 0) {
|
|
65
|
+
lines.push(` '${entity.entityType}': {`);
|
|
66
|
+
for (const [msgName] of queries) {
|
|
67
|
+
lines.push(` '${msgName}': ${namespace}.${msgName}Reply;`);
|
|
68
|
+
}
|
|
69
|
+
lines.push(' };');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
lines.push('}');
|
|
73
|
+
lines.push('');
|
|
74
|
+
}
|
|
40
75
|
return lines.join('\n');
|
|
41
76
|
}
|
|
42
77
|
function jsonSchemaTypeToTS(schema) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../../src/cli/generators/typescript.ts"],"names":[],"mappings":";;AAEA,
|
|
1
|
+
{"version":3,"file":"typescript.js","sourceRoot":"","sources":["../../../src/cli/generators/typescript.ts"],"names":[],"mappings":"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"}
|