@pattern-stack/codegen 0.2.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/CHANGELOG.md +67 -0
- package/README.md +214 -0
- package/dist/runtime/analytics/index.d.ts +6 -0
- package/dist/runtime/analytics/index.js +49 -0
- package/dist/runtime/analytics/index.js.map +1 -0
- package/dist/runtime/analytics/metrics.d.ts +75 -0
- package/dist/runtime/analytics/metrics.js +1 -0
- package/dist/runtime/analytics/metrics.js.map +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js +1 -0
- package/dist/runtime/analytics/packs/crm-entity-measures.js.map +1 -0
- package/dist/runtime/analytics/packs/index.d.ts +3 -0
- package/dist/runtime/analytics/packs/index.js +1 -0
- package/dist/runtime/analytics/packs/index.js.map +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.d.ts +21 -0
- package/dist/runtime/analytics/packs/monetary-measures.js +1 -0
- package/dist/runtime/analytics/packs/monetary-measures.js.map +1 -0
- package/dist/runtime/analytics/specs.d.ts +49 -0
- package/dist/runtime/analytics/specs.js +1 -0
- package/dist/runtime/analytics/specs.js.map +1 -0
- package/dist/runtime/analytics/types.d.ts +85 -0
- package/dist/runtime/analytics/types.js +49 -0
- package/dist/runtime/analytics/types.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-repository.d.ts +26 -0
- package/dist/runtime/base-classes/activity-entity-repository.js +195 -0
- package/dist/runtime/base-classes/activity-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/activity-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/activity-entity-service.js +214 -0
- package/dist/runtime/base-classes/activity-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/base-read-use-cases.d.ts +68 -0
- package/dist/runtime/base-classes/base-read-use-cases.js +32 -0
- package/dist/runtime/base-classes/base-read-use-cases.js.map +1 -0
- package/dist/runtime/base-classes/base-repository.d.ts +99 -0
- package/dist/runtime/base-classes/base-repository.js +160 -0
- package/dist/runtime/base-classes/base-repository.js.map +1 -0
- package/dist/runtime/base-classes/base-service.d.ts +98 -0
- package/dist/runtime/base-classes/base-service.js +186 -0
- package/dist/runtime/base-classes/base-service.js.map +1 -0
- package/dist/runtime/base-classes/index.d.ts +18 -0
- package/dist/runtime/base-classes/index.js +617 -0
- package/dist/runtime/base-classes/index.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.d.ts +17 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js +166 -0
- package/dist/runtime/base-classes/knowledge-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/knowledge-entity-service.d.ts +15 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js +192 -0
- package/dist/runtime/base-classes/knowledge-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/lifecycle-events.d.ts +49 -0
- package/dist/runtime/base-classes/lifecycle-events.js +76 -0
- package/dist/runtime/base-classes/lifecycle-events.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-repository.d.ts +27 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js +212 -0
- package/dist/runtime/base-classes/metadata-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/metadata-entity-service.d.ts +39 -0
- package/dist/runtime/base-classes/metadata-entity-service.js +214 -0
- package/dist/runtime/base-classes/metadata-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-repository.d.ts +32 -0
- package/dist/runtime/base-classes/synced-entity-repository.js +203 -0
- package/dist/runtime/base-classes/synced-entity-repository.js.map +1 -0
- package/dist/runtime/base-classes/synced-entity-service.d.ts +41 -0
- package/dist/runtime/base-classes/synced-entity-service.js +215 -0
- package/dist/runtime/base-classes/synced-entity-service.js.map +1 -0
- package/dist/runtime/base-classes/with-analytics.d.ts +18 -0
- package/dist/runtime/base-classes/with-analytics.js +11 -0
- package/dist/runtime/base-classes/with-analytics.js.map +1 -0
- package/dist/runtime/constants/tokens.d.ts +29 -0
- package/dist/runtime/constants/tokens.js +8 -0
- package/dist/runtime/constants/tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.d.ts +30 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js +1 -0
- package/dist/runtime/subsystems/analytics/analytics-query.protocol.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.module.d.ts +34 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js +117 -0
- package/dist/runtime/subsystems/analytics/analytics.module.js.map +1 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.d.ts +24 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js +10 -0
- package/dist/runtime/subsystems/analytics/analytics.tokens.js.map +1 -0
- package/dist/runtime/subsystems/analytics/cube-backend.d.ts +28 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js +71 -0
- package/dist/runtime/subsystems/analytics/cube-backend.js.map +1 -0
- package/dist/runtime/subsystems/analytics/index.d.ts +6 -0
- package/dist/runtime/subsystems/analytics/index.js +122 -0
- package/dist/runtime/subsystems/analytics/index.js.map +1 -0
- package/dist/runtime/subsystems/analytics/noop-backend.d.ts +7 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js +25 -0
- package/dist/runtime/subsystems/analytics/noop-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.d.ts +43 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js +133 -0
- package/dist/runtime/subsystems/cache/cache.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.d.ts +21 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js +100 -0
- package/dist/runtime/subsystems/cache/cache.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.module.d.ts +37 -0
- package/dist/runtime/subsystems/cache/cache.module.js +272 -0
- package/dist/runtime/subsystems/cache/cache.module.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.d.ts +42 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js +1 -0
- package/dist/runtime/subsystems/cache/cache.protocol.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.schema.d.ts +64 -0
- package/dist/runtime/subsystems/cache/cache.schema.js +18 -0
- package/dist/runtime/subsystems/cache/cache.schema.js.map +1 -0
- package/dist/runtime/subsystems/cache/cache.tokens.d.ts +18 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js +8 -0
- package/dist/runtime/subsystems/cache/cache.tokens.js.map +1 -0
- package/dist/runtime/subsystems/cache/index.d.ts +11 -0
- package/dist/runtime/subsystems/cache/index.js +277 -0
- package/dist/runtime/subsystems/cache/index.js.map +1 -0
- package/dist/runtime/subsystems/events/domain-events.schema.d.ts +187 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js +32 -0
- package/dist/runtime/subsystems/events/domain-events.schema.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js +199 -0
- package/dist/runtime/subsystems/events/event-bus.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.d.ts +18 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js +71 -0
- package/dist/runtime/subsystems/events/event-bus.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.d.ts +52 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js +1 -0
- package/dist/runtime/subsystems/events/event-bus.protocol.js.map +1 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.d.ts +95 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js +229 -0
- package/dist/runtime/subsystems/events/event-bus.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/events/events.module.d.ts +46 -0
- package/dist/runtime/subsystems/events/events.module.js +531 -0
- package/dist/runtime/subsystems/events/events.module.js.map +1 -0
- package/dist/runtime/subsystems/events/events.tokens.d.ts +19 -0
- package/dist/runtime/subsystems/events/events.tokens.js +8 -0
- package/dist/runtime/subsystems/events/events.tokens.js.map +1 -0
- package/dist/runtime/subsystems/events/index.d.ts +12 -0
- package/dist/runtime/subsystems/events/index.js +536 -0
- package/dist/runtime/subsystems/events/index.js.map +1 -0
- package/dist/runtime/subsystems/index.d.ts +24 -0
- package/dist/runtime/subsystems/index.js +1643 -0
- package/dist/runtime/subsystems/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/index.d.ts +14 -0
- package/dist/runtime/subsystems/jobs/index.js +680 -0
- package/dist/runtime/subsystems/jobs/index.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.d.ts +54 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js +186 -0
- package/dist/runtime/subsystems/jobs/job-queue.bullmq-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.d.ts +38 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js +228 -0
- package/dist/runtime/subsystems/jobs/job-queue.drizzle-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.d.ts +12 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.d.ts +48 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.protocol.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.d.ts +46 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js +187 -0
- package/dist/runtime/subsystems/jobs/job-queue.redis-backend.js.map +1 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.d.ts +237 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js +44 -0
- package/dist/runtime/subsystems/jobs/job-queue.schema.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.module.d.ts +18 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js +676 -0
- package/dist/runtime/subsystems/jobs/jobs.module.js.map +1 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.d.ts +13 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js +8 -0
- package/dist/runtime/subsystems/jobs/jobs.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/index.d.ts +6 -0
- package/dist/runtime/subsystems/storage/index.js +204 -0
- package/dist/runtime/subsystems/storage/index.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.d.ts +18 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js +108 -0
- package/dist/runtime/subsystems/storage/storage.local-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.d.ts +28 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js +72 -0
- package/dist/runtime/subsystems/storage/storage.memory-backend.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.module.d.ts +40 -0
- package/dist/runtime/subsystems/storage/storage.module.js +201 -0
- package/dist/runtime/subsystems/storage/storage.module.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.d.ts +69 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js +1 -0
- package/dist/runtime/subsystems/storage/storage.protocol.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.tokens.d.ts +11 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js +6 -0
- package/dist/runtime/subsystems/storage/storage.tokens.js.map +1 -0
- package/dist/runtime/subsystems/storage/storage.utils.d.ts +9 -0
- package/dist/runtime/subsystems/storage/storage.utils.js +18 -0
- package/dist/runtime/subsystems/storage/storage.utils.js.map +1 -0
- package/dist/runtime/types/drizzle.d.ts +17 -0
- package/dist/runtime/types/drizzle.js +1 -0
- package/dist/runtime/types/drizzle.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +7365 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/index.d.ts +2384 -0
- package/dist/src/index.js +2198 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +114 -0
- package/templates/broadcast/new/backend-interface.ejs.t +47 -0
- package/templates/broadcast/new/bridge-listener.ejs.t +67 -0
- package/templates/broadcast/new/channel.ejs.t +77 -0
- package/templates/broadcast/new/index.ejs.t +21 -0
- package/templates/broadcast/new/memory-backend.ejs.t +87 -0
- package/templates/broadcast/new/module.ejs.t +57 -0
- package/templates/broadcast/new/prompt.js +268 -0
- package/templates/broadcast/new/websocket-backend.ejs.t +259 -0
- package/templates/entity/new/backend/application/commands/create.ejs.t +55 -0
- package/templates/entity/new/backend/application/commands/delete.ejs.t +45 -0
- package/templates/entity/new/backend/application/commands/grouped-index.ejs.t +149 -0
- package/templates/entity/new/backend/application/commands/index.ejs.t +15 -0
- package/templates/entity/new/backend/application/commands/update.ejs.t +58 -0
- package/templates/entity/new/backend/application/queries/declarative-queries.ejs.t +36 -0
- package/templates/entity/new/backend/application/queries/get-by-id.ejs.t +42 -0
- package/templates/entity/new/backend/application/queries/grouped-index.ejs.t +81 -0
- package/templates/entity/new/backend/application/queries/index.ejs.t +14 -0
- package/templates/entity/new/backend/application/queries/list.ejs.t +36 -0
- package/templates/entity/new/backend/application/schemas/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/application/schemas/dto.ejs.t +45 -0
- package/templates/entity/new/backend/database/_inject-index.ejs.t +7 -0
- package/templates/entity/new/backend/database/electric-migration.ejs.t +21 -0
- package/templates/entity/new/backend/database/repository.ejs.t +450 -0
- package/templates/entity/new/backend/database/schema.ejs.t +248 -0
- package/templates/entity/new/backend/domain/_inject-index.ejs.t +12 -0
- package/templates/entity/new/backend/domain/entity.ejs.t +108 -0
- package/templates/entity/new/backend/domain/grouped-index.ejs.t +163 -0
- package/templates/entity/new/backend/domain/index.ejs.t +15 -0
- package/templates/entity/new/backend/domain/repository-interface.ejs.t +71 -0
- package/templates/entity/new/backend/modules/core/_ensure-anchor-tokens.ejs.t +10 -0
- package/templates/entity/new/backend/modules/core/_inject-token.ejs.t +7 -0
- package/templates/entity/new/backend/modules/core/module.ejs.t +67 -0
- package/templates/entity/new/backend/modules/trpc/module.ejs.t +67 -0
- package/templates/entity/new/backend/presentation/controller.ejs.t +201 -0
- package/templates/entity/new/clean-lite-ps/controller.ejs.t +37 -0
- package/templates/entity/new/clean-lite-ps/dto/create.ejs.t +17 -0
- package/templates/entity/new/clean-lite-ps/dto/output.ejs.t +25 -0
- package/templates/entity/new/clean-lite-ps/dto/update.ejs.t +11 -0
- package/templates/entity/new/clean-lite-ps/entity.ejs.t +52 -0
- package/templates/entity/new/clean-lite-ps/index.ejs.t +20 -0
- package/templates/entity/new/clean-lite-ps/module.ejs.t +43 -0
- package/templates/entity/new/clean-lite-ps/prompt-extension.js +617 -0
- package/templates/entity/new/clean-lite-ps/repository.ejs.t +62 -0
- package/templates/entity/new/clean-lite-ps/service.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/entity/new/clean-lite-ps/use-cases/find-by-id.ejs.t +16 -0
- package/templates/entity/new/clean-lite-ps/use-cases/list.ejs.t +16 -0
- package/templates/entity/new/frontend/_inject-entities-entry.ejs.t +7 -0
- package/templates/entity/new/frontend/_inject-entities-import.ejs.t +7 -0
- package/templates/entity/new/frontend/collections/_ensure-anchor-collections.ejs.t +10 -0
- package/templates/entity/new/frontend/collections/_inject-index.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/_inject-schema-import.ejs.t +9 -0
- package/templates/entity/new/frontend/collections/collection.ejs.t +61 -0
- package/templates/entity/new/frontend/collections/collections-base.ejs.t +24 -0
- package/templates/entity/new/frontend/entity/collection.ejs.t +172 -0
- package/templates/entity/new/frontend/entity/combined.ejs.t +474 -0
- package/templates/entity/new/frontend/entity/fields.ejs.t +104 -0
- package/templates/entity/new/frontend/entity/hooks.ejs.t +73 -0
- package/templates/entity/new/frontend/entity/index.ejs.t +21 -0
- package/templates/entity/new/frontend/entity/mutation-hooks.ejs.t +84 -0
- package/templates/entity/new/frontend/entity/mutations.ejs.t +38 -0
- package/templates/entity/new/frontend/entity/types.ejs.t +59 -0
- package/templates/entity/new/frontend/generated/_inject-index-export.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-import.ejs.t +7 -0
- package/templates/entity/new/frontend/generated/_inject-index-registry.ejs.t +7 -0
- package/templates/entity/new/frontend/store/_inject-collection-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-collections.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-entity.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-import.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-lookups.ejs.t +9 -0
- package/templates/entity/new/frontend/store/_inject-resolve.ejs.t +10 -0
- package/templates/entity/new/frontend/store/hooks.ejs.t +72 -0
- package/templates/entity/new/frontend/unified-entity.ejs.t +28 -0
- package/templates/entity/new/prompt.js +1421 -0
- package/templates/relationship/new/controller.ejs.t +36 -0
- package/templates/relationship/new/dto/create.ejs.t +41 -0
- package/templates/relationship/new/dto/output.ejs.t +44 -0
- package/templates/relationship/new/dto/update.ejs.t +10 -0
- package/templates/relationship/new/entity.ejs.t +98 -0
- package/templates/relationship/new/index.ejs.t +19 -0
- package/templates/relationship/new/module.ejs.t +35 -0
- package/templates/relationship/new/prompt.js +682 -0
- package/templates/relationship/new/repository.ejs.t +54 -0
- package/templates/relationship/new/service.ejs.t +31 -0
- package/templates/relationship/new/use-cases/declarative-queries.ejs.t +34 -0
- package/templates/relationship/new/use-cases/find-by-id.ejs.t +16 -0
- package/templates/relationship/new/use-cases/list.ejs.t +16 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: src/infrastructure/broadcast/websocket-broadcast.backend.ts
|
|
3
|
+
force: true
|
|
4
|
+
---
|
|
5
|
+
import { Injectable, Logger } from '@nestjs/common'
|
|
6
|
+
import {
|
|
7
|
+
WebSocketGateway,
|
|
8
|
+
WebSocketServer,
|
|
9
|
+
SubscribeMessage,
|
|
10
|
+
OnGatewayConnection,
|
|
11
|
+
OnGatewayDisconnect,
|
|
12
|
+
ConnectedSocket,
|
|
13
|
+
MessageBody,
|
|
14
|
+
} from '@nestjs/websockets'
|
|
15
|
+
import type { Server, Socket } from 'socket.io'
|
|
16
|
+
import type {
|
|
17
|
+
BroadcastBackend,
|
|
18
|
+
BroadcastHandler,
|
|
19
|
+
} from './broadcast-backend.interface'
|
|
20
|
+
|
|
21
|
+
interface SubscribePayload {
|
|
22
|
+
channels: string[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface UnsubscribePayload {
|
|
26
|
+
channels: string[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface BroadcastMessage {
|
|
30
|
+
channel: string
|
|
31
|
+
event: string
|
|
32
|
+
payload: Record<string, unknown>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* WebSocket-based broadcast backend using Socket.IO.
|
|
37
|
+
* Clients connect and subscribe to channels to receive real-time domain events.
|
|
38
|
+
*
|
|
39
|
+
* Protocol:
|
|
40
|
+
* Client → Server: { "subscribe": ["session", "agent"] }
|
|
41
|
+
* Client → Server: { "unsubscribe": ["session"] }
|
|
42
|
+
* Server → Client: { "channel": "session", "event": "session.running", "payload": {...} }
|
|
43
|
+
*/
|
|
44
|
+
@Injectable()
|
|
45
|
+
@WebSocketGateway({
|
|
46
|
+
path: '<%= websocketPath %>',
|
|
47
|
+
cors: {
|
|
48
|
+
origin: '<%= corsOrigin %>',
|
|
49
|
+
credentials: <%= corsCredentials %>,
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
export class WebSocketBroadcastBackend
|
|
53
|
+
implements BroadcastBackend, OnGatewayConnection, OnGatewayDisconnect
|
|
54
|
+
{
|
|
55
|
+
private readonly logger = new Logger(WebSocketBroadcastBackend.name)
|
|
56
|
+
|
|
57
|
+
@WebSocketServer()
|
|
58
|
+
private server!: Server
|
|
59
|
+
|
|
60
|
+
/** channel -> set of subscribed sockets */
|
|
61
|
+
private channelSubscribers = new Map<string, Set<Socket>>()
|
|
62
|
+
|
|
63
|
+
/** socket -> set of channels subscribed to */
|
|
64
|
+
private socketChannels = new Map<Socket, Set<string>>()
|
|
65
|
+
|
|
66
|
+
/** server-side handlers for channels */
|
|
67
|
+
private handlers = new Map<string, Set<BroadcastHandler>>()
|
|
68
|
+
|
|
69
|
+
private closed = false
|
|
70
|
+
|
|
71
|
+
readonly supportsPush = true
|
|
72
|
+
|
|
73
|
+
handleConnection(client: Socket): void {
|
|
74
|
+
this.logger.log(`Client connected: ${client.id}`)
|
|
75
|
+
this.socketChannels.set(client, new Set())
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
handleDisconnect(client: Socket): void {
|
|
79
|
+
this.logger.log(`Client disconnected: ${client.id}`)
|
|
80
|
+
|
|
81
|
+
// Remove client from all channel subscriptions
|
|
82
|
+
const channels = this.socketChannels.get(client)
|
|
83
|
+
if (channels) {
|
|
84
|
+
for (const channel of channels) {
|
|
85
|
+
const subscribers = this.channelSubscribers.get(channel)
|
|
86
|
+
if (subscribers) {
|
|
87
|
+
subscribers.delete(client)
|
|
88
|
+
if (subscribers.size === 0) {
|
|
89
|
+
this.channelSubscribers.delete(channel)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
this.socketChannels.delete(client)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@SubscribeMessage('subscribe')
|
|
98
|
+
handleSubscribe(
|
|
99
|
+
@ConnectedSocket() client: Socket,
|
|
100
|
+
@MessageBody() data: SubscribePayload,
|
|
101
|
+
): { subscribed: string[] } {
|
|
102
|
+
const channels = data?.channels ?? []
|
|
103
|
+
const subscribedChannels: string[] = []
|
|
104
|
+
|
|
105
|
+
for (const channel of channels) {
|
|
106
|
+
if (typeof channel !== 'string' || channel.length === 0) {
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add to channel subscribers
|
|
111
|
+
let subscribers = this.channelSubscribers.get(channel)
|
|
112
|
+
if (!subscribers) {
|
|
113
|
+
subscribers = new Set()
|
|
114
|
+
this.channelSubscribers.set(channel, subscribers)
|
|
115
|
+
}
|
|
116
|
+
subscribers.add(client)
|
|
117
|
+
|
|
118
|
+
// Track which channels this socket is subscribed to
|
|
119
|
+
const socketChans = this.socketChannels.get(client)
|
|
120
|
+
if (socketChans) {
|
|
121
|
+
socketChans.add(channel)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
subscribedChannels.push(channel)
|
|
125
|
+
this.logger.debug(`Client ${client.id} subscribed to channel: ${channel}`)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { subscribed: subscribedChannels }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@SubscribeMessage('unsubscribe')
|
|
132
|
+
handleUnsubscribe(
|
|
133
|
+
@ConnectedSocket() client: Socket,
|
|
134
|
+
@MessageBody() data: UnsubscribePayload,
|
|
135
|
+
): { unsubscribed: string[] } {
|
|
136
|
+
const channels = data?.channels ?? []
|
|
137
|
+
const unsubscribedChannels: string[] = []
|
|
138
|
+
|
|
139
|
+
for (const channel of channels) {
|
|
140
|
+
if (typeof channel !== 'string' || channel.length === 0) {
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Remove from channel subscribers
|
|
145
|
+
const subscribers = this.channelSubscribers.get(channel)
|
|
146
|
+
if (subscribers) {
|
|
147
|
+
subscribers.delete(client)
|
|
148
|
+
if (subscribers.size === 0) {
|
|
149
|
+
this.channelSubscribers.delete(channel)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Remove from socket's channel list
|
|
154
|
+
const socketChans = this.socketChannels.get(client)
|
|
155
|
+
if (socketChans) {
|
|
156
|
+
socketChans.delete(channel)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
unsubscribedChannels.push(channel)
|
|
160
|
+
this.logger.debug(
|
|
161
|
+
`Client ${client.id} unsubscribed from channel: ${channel}`,
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { unsubscribed: unsubscribedChannels }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async broadcast(
|
|
169
|
+
channel: string,
|
|
170
|
+
eventType: string,
|
|
171
|
+
payload: Record<string, unknown>,
|
|
172
|
+
): Promise<void> {
|
|
173
|
+
if (this.closed) {
|
|
174
|
+
throw new Error('Broadcast backend is closed')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const message: BroadcastMessage = {
|
|
178
|
+
channel,
|
|
179
|
+
event: eventType,
|
|
180
|
+
payload,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Send to WebSocket clients
|
|
184
|
+
const subscribers = this.channelSubscribers.get(channel)
|
|
185
|
+
if (subscribers && subscribers.size > 0) {
|
|
186
|
+
for (const socket of subscribers) {
|
|
187
|
+
socket.emit('broadcast', message)
|
|
188
|
+
}
|
|
189
|
+
this.logger.debug(
|
|
190
|
+
`Broadcast to ${subscribers.size} clients on channel "${channel}": ${eventType}`,
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Call server-side handlers
|
|
195
|
+
const channelHandlers = this.handlers.get(channel)
|
|
196
|
+
if (channelHandlers && channelHandlers.size > 0) {
|
|
197
|
+
const promises = Array.from(channelHandlers).map((handler) =>
|
|
198
|
+
handler(eventType, payload).catch((error) => {
|
|
199
|
+
this.logger.error(
|
|
200
|
+
`Handler error on channel "${channel}": ${error.message}`,
|
|
201
|
+
)
|
|
202
|
+
}),
|
|
203
|
+
)
|
|
204
|
+
await Promise.all(promises)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async subscribe(channel: string, handler: BroadcastHandler): Promise<void> {
|
|
209
|
+
if (this.closed) {
|
|
210
|
+
throw new Error('Broadcast backend is closed')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let channelHandlers = this.handlers.get(channel)
|
|
214
|
+
if (!channelHandlers) {
|
|
215
|
+
channelHandlers = new Set()
|
|
216
|
+
this.handlers.set(channel, channelHandlers)
|
|
217
|
+
}
|
|
218
|
+
channelHandlers.add(handler)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async unsubscribe(channel: string): Promise<void> {
|
|
222
|
+
this.handlers.delete(channel)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async healthCheck(): Promise<boolean> {
|
|
226
|
+
return !this.closed && this.server !== undefined
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async close(): Promise<void> {
|
|
230
|
+
this.closed = true
|
|
231
|
+
|
|
232
|
+
// Disconnect all clients
|
|
233
|
+
for (const socket of this.socketChannels.keys()) {
|
|
234
|
+
socket.disconnect(true)
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
this.channelSubscribers.clear()
|
|
238
|
+
this.socketChannels.clear()
|
|
239
|
+
this.handlers.clear()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Get statistics about current connections (for monitoring)
|
|
244
|
+
*/
|
|
245
|
+
getStats(): {
|
|
246
|
+
totalConnections: number
|
|
247
|
+
channelCounts: Record<string, number>
|
|
248
|
+
} {
|
|
249
|
+
const channelCounts: Record<string, number> = {}
|
|
250
|
+
for (const [channel, subscribers] of this.channelSubscribers) {
|
|
251
|
+
channelCounts[channel] = subscribers.size
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
totalConnections: this.socketChannels.size,
|
|
256
|
+
channelCounts,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.commands ? outputPaths.createCommand : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.createCommand) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* Create <%= className %> Command
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*
|
|
11
|
+
* EXTENSION POINTS (add business logic here):
|
|
12
|
+
* - Validation: Add domain validation rules before creating
|
|
13
|
+
* - Defaults: Compute default values not handled by DTO
|
|
14
|
+
* - Side effects: Emit events, send notifications, create related entities
|
|
15
|
+
* - Authorization: Check user permissions beyond route-level auth
|
|
16
|
+
* - Audit: Log creation for compliance/debugging
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
20
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
21
|
+
import type { Create<%= className %>Input, I<%= className %>Repository } from '<%= imports.domain %>';
|
|
22
|
+
import { <%= className %> } from '<%= imports.domain %>';
|
|
23
|
+
import type { Create<%= className %>Dto } from '<%= imports.schemas %>';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class <%= createCommandClass %> {
|
|
27
|
+
constructor(
|
|
28
|
+
@Inject(<%= repositoryToken %>)
|
|
29
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
async execute(dto: Create<%= className %>Dto): Promise<<%= className %>> {
|
|
33
|
+
// TODO: Add pre-create validation and business rules here
|
|
34
|
+
|
|
35
|
+
// Map DTO to domain input
|
|
36
|
+
const input: Create<%= className %>Input = {
|
|
37
|
+
<% fields.forEach((field) => { -%>
|
|
38
|
+
<% if (field.required) { -%>
|
|
39
|
+
<%= field.camelName %>: dto.<%= field.camelName %>,
|
|
40
|
+
<% } else if (field.nullable) { -%>
|
|
41
|
+
<%= field.camelName %>: dto.<%= field.camelName %> ?? null,
|
|
42
|
+
<% } else { -%>
|
|
43
|
+
<%= field.camelName %>: dto.<%= field.camelName %>,
|
|
44
|
+
<% } -%>
|
|
45
|
+
<% }) -%>
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const created = await this.<%= camelName %>Repository.create(input);
|
|
49
|
+
|
|
50
|
+
// TODO: Add post-create side effects here (events, notifications, etc.)
|
|
51
|
+
|
|
52
|
+
return created;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
<% } -%>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.commands ? outputPaths.deleteCommand : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.deleteCommand) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* Delete <%= className %> Command
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*
|
|
11
|
+
* EXTENSION POINTS (add business logic here):
|
|
12
|
+
* - Authorization: Check if user can delete this specific record
|
|
13
|
+
* - Referential integrity: Check for dependent records, cascade or block
|
|
14
|
+
* - Soft delete: Mark as deleted instead of hard delete
|
|
15
|
+
* - Side effects: Emit events, clean up related data, notify subscribers
|
|
16
|
+
* - Audit: Log deletion for compliance/debugging
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
|
20
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
21
|
+
import type { I<%= className %>Repository } from '<%= imports.domain %>';
|
|
22
|
+
import { <%= className %> } from '<%= imports.domain %>';
|
|
23
|
+
|
|
24
|
+
@Injectable()
|
|
25
|
+
export class <%= deleteCommandClass %> {
|
|
26
|
+
constructor(
|
|
27
|
+
@Inject(<%= repositoryToken %>)
|
|
28
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
29
|
+
) {}
|
|
30
|
+
|
|
31
|
+
async execute(id: string): Promise<<%= className %>> {
|
|
32
|
+
// TODO: Add pre-delete validation here
|
|
33
|
+
// e.g., check for dependent records, verify user permissions
|
|
34
|
+
|
|
35
|
+
const deleted = await this.<%= camelName %>Repository.delete(id);
|
|
36
|
+
if (!deleted) {
|
|
37
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// TODO: Add post-delete side effects here (events, cleanup, etc.)
|
|
41
|
+
|
|
42
|
+
return deleted;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
<% } -%>
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.commands ? outputPaths.commandsGroupedIndex : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.commandsGroupedIndex) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* <%= className %> Commands Module
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*
|
|
11
|
+
* This file contains all command classes for <%= className %>
|
|
12
|
+
* using grouped file layout (file_grouping: "grouped")
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
|
16
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
17
|
+
import type { Create<%= className %>Input, I<%= className %>Repository, Update<%= className %>Input } from '<%= imports.domain %>';
|
|
18
|
+
import { <%= className %> } from '<%= imports.domain %>';
|
|
19
|
+
import type { Create<%= className %>Dto, Update<%= className %>Dto } from '<%= imports.schemas %>';
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Create Command
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create <%= className %> Command
|
|
27
|
+
*
|
|
28
|
+
* EXTENSION POINTS (add business logic here):
|
|
29
|
+
* - Validation: Add domain validation rules before creating
|
|
30
|
+
* - Defaults: Compute default values not handled by DTO
|
|
31
|
+
* - Side effects: Emit events, send notifications, create related entities
|
|
32
|
+
* - Authorization: Check user permissions beyond route-level auth
|
|
33
|
+
* - Audit: Log creation for compliance/debugging
|
|
34
|
+
*/
|
|
35
|
+
@Injectable()
|
|
36
|
+
export class <%= createCommandClass %> {
|
|
37
|
+
constructor(
|
|
38
|
+
@Inject(<%= repositoryToken %>)
|
|
39
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
40
|
+
) {}
|
|
41
|
+
|
|
42
|
+
async execute(dto: Create<%= className %>Dto): Promise<<%= className %>> {
|
|
43
|
+
// TODO: Add pre-create validation and business rules here
|
|
44
|
+
|
|
45
|
+
// Map DTO to domain input
|
|
46
|
+
const input: Create<%= className %>Input = {
|
|
47
|
+
<% fields.forEach((field) => { -%>
|
|
48
|
+
<% if (field.required) { -%>
|
|
49
|
+
<%= field.camelName %>: dto.<%= field.camelName %>,
|
|
50
|
+
<% } else if (field.nullable) { -%>
|
|
51
|
+
<%= field.camelName %>: dto.<%= field.camelName %> ?? null,
|
|
52
|
+
<% } else { -%>
|
|
53
|
+
<%= field.camelName %>: dto.<%= field.camelName %>,
|
|
54
|
+
<% } -%>
|
|
55
|
+
<% }) -%>
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const created = await this.<%= camelName %>Repository.create(input);
|
|
59
|
+
|
|
60
|
+
// TODO: Add post-create side effects here (events, notifications, etc.)
|
|
61
|
+
|
|
62
|
+
return created;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ============================================================================
|
|
67
|
+
// Update Command
|
|
68
|
+
// ============================================================================
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Update <%= className %> Command
|
|
72
|
+
*
|
|
73
|
+
* EXTENSION POINTS (add business logic here):
|
|
74
|
+
* - Validation: Add domain validation rules (e.g., state transitions)
|
|
75
|
+
* - Authorization: Check if user can modify this specific record
|
|
76
|
+
* - Conflict detection: Optimistic locking, version checks
|
|
77
|
+
* - Side effects: Emit events, invalidate caches, notify subscribers
|
|
78
|
+
* - Audit: Log changes for compliance/debugging
|
|
79
|
+
*/
|
|
80
|
+
@Injectable()
|
|
81
|
+
export class <%= updateCommandClass %> {
|
|
82
|
+
constructor(
|
|
83
|
+
@Inject(<%= repositoryToken %>)
|
|
84
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
85
|
+
) {}
|
|
86
|
+
|
|
87
|
+
async execute(id: string, dto: Update<%= className %>Dto): Promise<<%= className %>> {
|
|
88
|
+
const existing = await this.<%= camelName %>Repository.findById(id);
|
|
89
|
+
if (!existing) {
|
|
90
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// TODO: Add pre-update validation and business rules here
|
|
94
|
+
// e.g., check state transitions, verify user permissions on this record
|
|
95
|
+
|
|
96
|
+
// Map DTO to domain input (only include defined fields)
|
|
97
|
+
const input: Update<%= className %>Input = {
|
|
98
|
+
<% fields.forEach((field) => { -%>
|
|
99
|
+
...(dto.<%= field.camelName %> !== undefined && { <%= field.camelName %>: dto.<%= field.camelName %> }),
|
|
100
|
+
<% }) -%>
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const updated = await this.<%= camelName %>Repository.update(id, input);
|
|
104
|
+
if (!updated) {
|
|
105
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// TODO: Add post-update side effects here (events, cache invalidation, etc.)
|
|
109
|
+
|
|
110
|
+
return updated;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// Delete Command
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Delete <%= className %> Command
|
|
120
|
+
*
|
|
121
|
+
* EXTENSION POINTS (add business logic here):
|
|
122
|
+
* - Authorization: Check if user can delete this specific record
|
|
123
|
+
* - Referential integrity: Check for dependent records, cascade or block
|
|
124
|
+
* - Soft delete: Mark as deleted instead of hard delete
|
|
125
|
+
* - Side effects: Emit events, clean up related data, notify subscribers
|
|
126
|
+
* - Audit: Log deletion for compliance/debugging
|
|
127
|
+
*/
|
|
128
|
+
@Injectable()
|
|
129
|
+
export class <%= deleteCommandClass %> {
|
|
130
|
+
constructor(
|
|
131
|
+
@Inject(<%= repositoryToken %>)
|
|
132
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
133
|
+
) {}
|
|
134
|
+
|
|
135
|
+
async execute(id: string): Promise<<%= className %>> {
|
|
136
|
+
// TODO: Add pre-delete validation here
|
|
137
|
+
// e.g., check for dependent records, verify user permissions
|
|
138
|
+
|
|
139
|
+
const deleted = await this.<%= camelName %>Repository.delete(id);
|
|
140
|
+
if (!deleted) {
|
|
141
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// TODO: Add post-delete side effects here (events, cleanup, etc.)
|
|
145
|
+
|
|
146
|
+
return deleted;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
<% } -%>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.commands ? outputPaths.commandsIndex : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.commandsIndex) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* <%= className %> Commands Barrel Export
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export * from './<%= fileNames.createCommand.replace('.ts', '') %>';
|
|
13
|
+
export * from './<%= fileNames.updateCommand.replace('.ts', '') %>';
|
|
14
|
+
export * from './<%= fileNames.deleteCommand.replace('.ts', '') %>';
|
|
15
|
+
<% } -%>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.commands ? outputPaths.updateCommand : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.updateCommand) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* Update <%= className %> Command
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*
|
|
11
|
+
* EXTENSION POINTS (add business logic here):
|
|
12
|
+
* - Validation: Add domain validation rules (e.g., state transitions)
|
|
13
|
+
* - Authorization: Check if user can modify this specific record
|
|
14
|
+
* - Conflict detection: Optimistic locking, version checks
|
|
15
|
+
* - Side effects: Emit events, invalidate caches, notify subscribers
|
|
16
|
+
* - Audit: Log changes for compliance/debugging
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
|
20
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
21
|
+
import type { I<%= className %>Repository, Update<%= className %>Input } from '<%= imports.domain %>';
|
|
22
|
+
import { <%= className %> } from '<%= imports.domain %>';
|
|
23
|
+
import type { Update<%= className %>Dto } from '<%= imports.schemas %>';
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class <%= updateCommandClass %> {
|
|
27
|
+
constructor(
|
|
28
|
+
@Inject(<%= repositoryToken %>)
|
|
29
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
30
|
+
) {}
|
|
31
|
+
|
|
32
|
+
async execute(id: string, dto: Update<%= className %>Dto): Promise<<%= className %>> {
|
|
33
|
+
const existing = await this.<%= camelName %>Repository.findById(id);
|
|
34
|
+
if (!existing) {
|
|
35
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// TODO: Add pre-update validation and business rules here
|
|
39
|
+
// e.g., check state transitions, verify user permissions on this record
|
|
40
|
+
|
|
41
|
+
// Map DTO to domain input (only include defined fields)
|
|
42
|
+
const input: Update<%= className %>Input = {
|
|
43
|
+
<% fields.forEach((field) => { -%>
|
|
44
|
+
...(dto.<%= field.camelName %> !== undefined && { <%= field.camelName %>: dto.<%= field.camelName %> }),
|
|
45
|
+
<% }) -%>
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const updated = await this.<%= camelName %>Repository.update(id, input);
|
|
49
|
+
if (!updated) {
|
|
50
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// TODO: Add post-update side effects here (events, cache invalidation, etc.)
|
|
54
|
+
|
|
55
|
+
return updated;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
<% } -%>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= hasDeclarativeQueries ? `${basePaths.backendSrc}/${paths.queries}/declarative-queries.ts` : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (hasDeclarativeQueries) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* Declarative Query Classes for <%= className %>
|
|
9
|
+
* Generated from queries: block in entity YAML - do not edit directly
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
13
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
14
|
+
import type { I<%= className %>Repository } from '<%= imports.domain %>';
|
|
15
|
+
import type { <%= className %> } from '<%= imports.domain %>';
|
|
16
|
+
|
|
17
|
+
<% processedQueries.forEach((q) => { -%>
|
|
18
|
+
@Injectable()
|
|
19
|
+
export class <%= q.useCaseClassName %> {
|
|
20
|
+
constructor(
|
|
21
|
+
@Inject(<%= repositoryToken %>)
|
|
22
|
+
private readonly repository: I<%= className %>Repository,
|
|
23
|
+
) {}
|
|
24
|
+
|
|
25
|
+
async execute(<%- q.params.map(p => `${p.camelName}: ${p.tsType}`).join(', ') %>): Promise<<%- q.returnType %>> {
|
|
26
|
+
return this.repository.<%= q.methodName %>(<%= q.params.map(p => p.camelName).join(', ') %>);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
<% }) -%>
|
|
31
|
+
export const declarativeQueryClasses = [
|
|
32
|
+
<% processedQueries.forEach((q) => { -%>
|
|
33
|
+
<%= q.useCaseClassName %>,
|
|
34
|
+
<% }) -%>
|
|
35
|
+
];
|
|
36
|
+
<% } -%>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
to: "<%= generate.queries ? outputPaths.getByIdQuery : '' %>"
|
|
3
|
+
skip_if: <%= !isCleanArchitecture %>
|
|
4
|
+
force: true
|
|
5
|
+
---
|
|
6
|
+
<% if (outputPaths.getByIdQuery) { -%>
|
|
7
|
+
/**
|
|
8
|
+
* Get <%= className %> By ID Query
|
|
9
|
+
* Generated by entity codegen - do not edit directly
|
|
10
|
+
*
|
|
11
|
+
* EXTENSION POINTS (add query logic here):
|
|
12
|
+
* - Authorization: Check if user can access this specific record
|
|
13
|
+
* - Enrichment: Add computed fields or related data
|
|
14
|
+
* - Caching: Add cache layer for frequently accessed records
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { Inject, Injectable, NotFoundException } from '@nestjs/common';
|
|
18
|
+
import { <%= repositoryToken %> } from '<%= imports.constants %>';
|
|
19
|
+
import type { I<%= className %>Repository<%= hasRelationships ? `, ${className}With` : '' %> } from '<%= imports.domain %>';
|
|
20
|
+
import { <%= className %> } from '<%= imports.domain %>';
|
|
21
|
+
|
|
22
|
+
@Injectable()
|
|
23
|
+
export class <%= getByIdQueryClass %> {
|
|
24
|
+
constructor(
|
|
25
|
+
@Inject(<%= repositoryToken %>)
|
|
26
|
+
private readonly <%= camelName %>Repository: I<%= className %>Repository,
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
async execute(id: string<%= hasRelationships ? `, include?: ${className}With` : '' %>): Promise<<%= className %>> {
|
|
30
|
+
// TODO: Add authorization check if needed (row-level security)
|
|
31
|
+
|
|
32
|
+
const entity = await this.<%= camelName %>Repository.findById(id<%= hasRelationships ? ', include' : '' %>);
|
|
33
|
+
if (!entity) {
|
|
34
|
+
throw new NotFoundException(`<%= className %> with id ${id} not found`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// TODO: Add enrichment or computed fields if needed
|
|
38
|
+
|
|
39
|
+
return entity;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
<% } -%>
|