atomic-queues 2.0.2 → 2.1.0
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 +134 -14
- 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 +7 -3
- 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 +6 -1
- 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
|
@@ -159,12 +159,20 @@ The library auto-discovers `@CommandHandler` and `@QueryHandler` classes at boot
|
|
|
159
159
|
// Fire-and-forget
|
|
160
160
|
await queueBus.enqueue(new WithdrawCommand(accountId, 100));
|
|
161
161
|
|
|
162
|
-
// Enqueue and block until result
|
|
162
|
+
// Enqueue and block until result — return type inferred from Reply<T> brand
|
|
163
163
|
const balance = await queueBus.enqueueAndWait(new GetBalanceQuery(accountId));
|
|
164
164
|
|
|
165
165
|
// Scoped to an entity type
|
|
166
166
|
await queueBus.forEntity('account').enqueueBulk([charge1, charge2, charge3]);
|
|
167
167
|
|
|
168
|
+
// Cross-service: string-based API — no class import needed
|
|
169
|
+
await queueBus.enqueue('warehouse', 'ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
170
|
+
const stock = await queueBus.enqueueAndWait('warehouse', 'GetStockQuery', 'SKU-001', { sku: 'SKU-001' });
|
|
171
|
+
|
|
172
|
+
// Scoped cross-service
|
|
173
|
+
const warehouse = queueBus.forEntity('warehouse');
|
|
174
|
+
await warehouse.enqueue('ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
175
|
+
|
|
168
176
|
// Actor-style direct send
|
|
169
177
|
await actorSystem.send('account', accountId, new DepositCommand(100));
|
|
170
178
|
const balance = await actorSystem.sendAndWait('account', accountId, new GetBalanceQuery());
|
|
@@ -228,17 +236,23 @@ atomic-queues replaces all of that with Redis.
|
|
|
228
236
|
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
237
|
|
|
230
238
|
```typescript
|
|
231
|
-
//
|
|
239
|
+
// warehouse-service: defines and handles the entity
|
|
232
240
|
AtomicQueuesModule.forRoot({
|
|
233
241
|
redis: { url: process.env.REDIS_URL },
|
|
234
|
-
registry: { enabled: true, serviceName: '
|
|
242
|
+
registry: { enabled: true, serviceName: 'warehouse-service' },
|
|
235
243
|
})
|
|
236
244
|
|
|
237
|
-
//
|
|
238
|
-
|
|
245
|
+
// order-service: generate classes from the live registry, then use them like local CQRS
|
|
246
|
+
import { ReserveStockCommand, GetStockQuery } from './generated';
|
|
247
|
+
|
|
248
|
+
await queueBus.enqueue(new ReserveStockCommand({ sku: 'SKU-001', quantity: 50 }));
|
|
249
|
+
const stock = await queueBus.enqueueAndWait(new GetStockQuery({ sku: 'SKU-001' }));
|
|
250
|
+
stock.available; // fully typed — no string API, no explicit timeout, no code dependency on warehouse-service
|
|
239
251
|
```
|
|
240
252
|
|
|
241
|
-
When `
|
|
253
|
+
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.
|
|
254
|
+
|
|
255
|
+
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
256
|
|
|
243
257
|
### What this replaces
|
|
244
258
|
|
|
@@ -252,7 +266,7 @@ Think about what you no longer need:
|
|
|
252
266
|
|
|
253
267
|
**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
268
|
|
|
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
|
|
269
|
+
**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
270
|
|
|
257
271
|
**No separate dead-letter infrastructure.** Failed messages are dead-lettered per entity type in Redis, queryable via the same connection.
|
|
258
272
|
|
|
@@ -281,15 +295,118 @@ The Zod schema serializes to JSON Schema and stores in the registry. Every servi
|
|
|
281
295
|
|
|
282
296
|
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
297
|
|
|
284
|
-
###
|
|
298
|
+
### Runtime introspection
|
|
299
|
+
|
|
300
|
+
Any service can discover what the cluster offers at runtime — no config files, no shared code:
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
const contracts = await queueBus.introspect();
|
|
304
|
+
|
|
305
|
+
contracts.entityTypes(); // ['account', 'warehouse', ...]
|
|
306
|
+
contracts.hasEntity('warehouse'); // true
|
|
307
|
+
contracts.messagesFor('warehouse'); // ['ReserveStockCommand', 'GetStockQuery']
|
|
308
|
+
contracts.accepts('warehouse', 'ReserveStockCommand'); // true
|
|
309
|
+
contracts.schemaFor('warehouse', 'ReserveStockCommand'); // { properties: { sku: ..., quantity: ... } }
|
|
310
|
+
contracts.replySchemaFor('warehouse', 'GetStockQuery'); // { properties: { sku: ..., available: ... } }
|
|
311
|
+
|
|
312
|
+
// Human-readable summary for logging/debugging
|
|
313
|
+
console.log(contracts.toString());
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Raw cross-service API
|
|
317
|
+
|
|
318
|
+
For quick prototyping or dynamic dispatch, you can also use the string-based API — no classes, no codegen, no imports:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
// Fire-and-forget
|
|
322
|
+
await queueBus.enqueue('warehouse', 'ReserveStockCommand', 'SKU-001', {
|
|
323
|
+
sku: 'SKU-001',
|
|
324
|
+
quantity: 50,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// Request-reply
|
|
328
|
+
const stock = await queueBus.enqueueAndWait('warehouse', 'GetStockQuery', 'SKU-001', {
|
|
329
|
+
sku: 'SKU-001',
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Scoped to an entity type
|
|
333
|
+
const warehouse = queueBus.forEntity('warehouse');
|
|
334
|
+
await warehouse.enqueue('ReserveStockCommand', 'SKU-001', { sku: 'SKU-001', quantity: 50 });
|
|
335
|
+
const stock = await warehouse.enqueueAndWait('GetStockQuery', 'SKU-001', { sku: 'SKU-001' });
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
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
339
|
|
|
286
|
-
|
|
340
|
+
### Class codegen (recommended)
|
|
341
|
+
|
|
342
|
+
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
343
|
|
|
288
344
|
```bash
|
|
289
|
-
|
|
345
|
+
npx atomic-queues generate --classes -o src/generated
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
This produces one file per entity type plus a barrel `index.ts`:
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
src/generated/
|
|
352
|
+
warehouse.ts # ReserveStockCommand, GetStockQuery, data interfaces, reply interfaces
|
|
353
|
+
billing.ts # ChargeCommand, GetInvoiceQuery, ...
|
|
354
|
+
index.ts # export * from './warehouse'; export * from './billing';
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Then use them exactly like local command/query classes:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { ReserveStockCommand, GetStockQuery } from './generated';
|
|
361
|
+
|
|
362
|
+
// Fire-and-forget — full autocomplete on constructor fields
|
|
363
|
+
await queueBus.enqueue(new ReserveStockCommand({ sku: 'SKU-001', quantity: 50 }));
|
|
364
|
+
|
|
365
|
+
// Request-reply — return type inferred from Reply<T> brand, no explicit timeout
|
|
366
|
+
const stock = await queueBus.enqueueAndWait(new GetStockQuery({ sku: 'SKU-001' }));
|
|
367
|
+
stock.available; // typed as number — full IDE support
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
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.
|
|
371
|
+
|
|
372
|
+
You can also filter to specific entity types:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
npx atomic-queues generate --classes -o src/generated --entities warehouse,billing
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Other codegen formats
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# TypeScript interfaces + DispatchMap (for typed string-based API)
|
|
382
|
+
npx atomic-queues generate --ts --output ./generated/contracts.ts
|
|
383
|
+
|
|
384
|
+
# JSON Schema (language-agnostic)
|
|
385
|
+
npx atomic-queues generate --json-schema --output ./generated/schema.json
|
|
386
|
+
|
|
387
|
+
# Full registry snapshot
|
|
388
|
+
npx atomic-queues generate --snapshot --output ./generated/snapshot.json
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Config-driven timeouts
|
|
392
|
+
|
|
393
|
+
`enqueueAndWait` resolves timeouts automatically — you never need to pass one explicitly:
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
AtomicQueuesModule.forRoot({
|
|
397
|
+
executor: {
|
|
398
|
+
gateTTL: 30,
|
|
399
|
+
defaultReplyTimeout: 15000, // global fallback: 15s
|
|
400
|
+
},
|
|
401
|
+
entities: {
|
|
402
|
+
warehouse: {
|
|
403
|
+
replyTimeout: 5000, // warehouse-specific: 5s
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
})
|
|
290
407
|
```
|
|
291
408
|
|
|
292
|
-
|
|
409
|
+
Resolution chain: explicit arg → per-entity `replyTimeout` → global `defaultReplyTimeout` → `gateTTL * 2 * 1000`. If nothing is configured, defaults to 60s.
|
|
293
410
|
|
|
294
411
|
---
|
|
295
412
|
|
|
@@ -368,8 +485,9 @@ AtomicQueuesModule.forRoot({
|
|
|
368
485
|
redis: { host: 'localhost', port: 6379 },
|
|
369
486
|
|
|
370
487
|
executor: {
|
|
371
|
-
poolSize: 1,
|
|
372
|
-
gateTTL: 30,
|
|
488
|
+
poolSize: 1, // concurrent executors per node
|
|
489
|
+
gateTTL: 30, // seconds before gate expires (safety net)
|
|
490
|
+
defaultReplyTimeout: 15000, // global default for enqueueAndWait (ms)
|
|
373
491
|
},
|
|
374
492
|
|
|
375
493
|
entities: {
|
|
@@ -379,6 +497,7 @@ AtomicQueuesModule.forRoot({
|
|
|
379
497
|
retry: { maxAttempts: 5, backoff: 'exponential', backoffDelay: 2000 },
|
|
380
498
|
actorIdleTimeout: 120000,
|
|
381
499
|
statePersistence: true,
|
|
500
|
+
replyTimeout: 5000, // per-entity enqueueAndWait timeout (ms)
|
|
382
501
|
},
|
|
383
502
|
},
|
|
384
503
|
|
|
@@ -446,6 +565,7 @@ npm install zod zod-to-json-schema # for schema validation in the registry
|
|
|
446
565
|
| `@Actor('type')` | Declare a virtual actor class |
|
|
447
566
|
| `@On(MessageClass)` | Handle a message type on an actor |
|
|
448
567
|
| `@Schema(zodSchema)` | Attach a Zod schema for registry validation |
|
|
568
|
+
| `@ReplySchema(zodSchema)` | Attach a reply schema for query codegen |
|
|
449
569
|
|
|
450
570
|
---
|
|
451
571
|
|
|
@@ -457,7 +577,7 @@ V2 is a full rewrite of the internals. BullMQ is removed. Workers are removed. T
|
|
|
457
577
|
|
|
458
578
|
**What's removed**: `@WorkerProcessor`, `@JobHandler`, `@EntityScaler`, `@OnSpawnWorker`, `@OnTerminateWorker`, `@GetActiveEntities`, `@GetDesiredWorkerCount`, `.forProcessor()`. All worker and scaling concepts are gone.
|
|
459
579
|
|
|
460
|
-
**What's new**: `@Actor`, `@On`, `@Schema`, `ActorSystem`, `RegistryService`, distributed registry, codegen CLI.
|
|
580
|
+
**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
581
|
|
|
462
582
|
**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
583
|
|
|
@@ -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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"}
|