@veloxts/cli 0.6.31 → 0.6.52
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 +252 -0
- package/dist/cli.js +2 -0
- package/dist/commands/schedule.d.ts +14 -0
- package/dist/commands/schedule.js +324 -0
- package/dist/generators/generators/event.d.ts +35 -0
- package/dist/generators/generators/event.js +99 -0
- package/dist/generators/generators/index.d.ts +5 -0
- package/dist/generators/generators/index.js +15 -0
- package/dist/generators/generators/job.d.ts +36 -0
- package/dist/generators/generators/job.js +98 -0
- package/dist/generators/generators/mail.d.ts +36 -0
- package/dist/generators/generators/mail.js +90 -0
- package/dist/generators/generators/storage.d.ts +35 -0
- package/dist/generators/generators/storage.js +104 -0
- package/dist/generators/generators/task.d.ts +36 -0
- package/dist/generators/generators/task.js +99 -0
- package/dist/generators/templates/event.d.ts +21 -0
- package/dist/generators/templates/event.js +410 -0
- package/dist/generators/templates/job.d.ts +23 -0
- package/dist/generators/templates/job.js +352 -0
- package/dist/generators/templates/mail.d.ts +21 -0
- package/dist/generators/templates/mail.js +411 -0
- package/dist/generators/templates/storage.d.ts +23 -0
- package/dist/generators/templates/storage.js +556 -0
- package/dist/generators/templates/task.d.ts +33 -0
- package/dist/generators/templates/task.js +189 -0
- package/package.json +8 -6
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task Generator
|
|
3
|
+
*
|
|
4
|
+
* Scaffolds scheduled task files for VeloxTS applications.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* velox make task <name> [options]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* velox make task cleanup-tokens # Simple daily task
|
|
11
|
+
* velox make task send-digest --callbacks # Task with success/failure callbacks
|
|
12
|
+
* velox make task backup-db --no-overlap # Task that prevents overlapping runs
|
|
13
|
+
* velox make task report --constraints # Task with time/day constraints
|
|
14
|
+
*/
|
|
15
|
+
import { BaseGenerator } from '../base.js';
|
|
16
|
+
import { getTaskInstructions, getTaskPath, taskTemplate, } from '../templates/task.js';
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Generator Implementation
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Task generator - creates scheduled task files
|
|
22
|
+
*/
|
|
23
|
+
export class TaskGenerator extends BaseGenerator {
|
|
24
|
+
metadata = {
|
|
25
|
+
name: 'task',
|
|
26
|
+
description: 'Generate scheduled task definitions',
|
|
27
|
+
longDescription: `
|
|
28
|
+
Scaffold scheduled task definitions for VeloxTS applications.
|
|
29
|
+
|
|
30
|
+
Tasks are background operations that run on a cron-like schedule using the
|
|
31
|
+
@veloxts/scheduler package. They support fluent scheduling, constraints,
|
|
32
|
+
overlap prevention, and success/failure callbacks.
|
|
33
|
+
|
|
34
|
+
Examples:
|
|
35
|
+
velox make task cleanup-tokens # Simple daily task
|
|
36
|
+
velox make task send-digest --callbacks # Task with success/failure callbacks
|
|
37
|
+
velox make task backup-db --no-overlap # Task that prevents overlapping runs
|
|
38
|
+
velox make task report --constraints # Task with time/day constraints
|
|
39
|
+
`,
|
|
40
|
+
aliases: ['scheduled-task', 'cron'],
|
|
41
|
+
category: 'infrastructure',
|
|
42
|
+
};
|
|
43
|
+
options = [
|
|
44
|
+
{
|
|
45
|
+
name: 'callbacks',
|
|
46
|
+
short: 'c',
|
|
47
|
+
description: 'Generate task with onSuccess/onFailure callbacks',
|
|
48
|
+
type: 'boolean',
|
|
49
|
+
default: false,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'constraints',
|
|
53
|
+
short: 't',
|
|
54
|
+
description: 'Generate task with time/day constraints',
|
|
55
|
+
type: 'boolean',
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'no-overlap',
|
|
60
|
+
short: 'n',
|
|
61
|
+
description: 'Generate task with overlap prevention',
|
|
62
|
+
type: 'boolean',
|
|
63
|
+
default: false,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* Validate and transform raw options
|
|
68
|
+
*/
|
|
69
|
+
validateOptions(raw) {
|
|
70
|
+
return {
|
|
71
|
+
callbacks: Boolean(raw.callbacks ?? false),
|
|
72
|
+
constraints: Boolean(raw.constraints ?? false),
|
|
73
|
+
noOverlap: Boolean(raw['no-overlap'] ?? raw.noOverlap ?? false),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate task files
|
|
78
|
+
*/
|
|
79
|
+
async generate(config) {
|
|
80
|
+
const context = this.createContext(config);
|
|
81
|
+
const files = [];
|
|
82
|
+
// Generate task file
|
|
83
|
+
const taskContent = taskTemplate(context);
|
|
84
|
+
files.push({
|
|
85
|
+
path: getTaskPath(config.entityName, config.project),
|
|
86
|
+
content: taskContent,
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
files,
|
|
90
|
+
postInstructions: getTaskInstructions(config.entityName, config.options),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Factory function for creating a TaskGenerator instance
|
|
96
|
+
*/
|
|
97
|
+
export function createTaskGenerator() {
|
|
98
|
+
return new TaskGenerator();
|
|
99
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Template
|
|
3
|
+
*
|
|
4
|
+
* Generates event-related files for VeloxTS applications.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProjectContext, TemplateFunction } from '../types.js';
|
|
7
|
+
export interface EventOptions {
|
|
8
|
+
/** Generate event listener/handler */
|
|
9
|
+
listener: boolean;
|
|
10
|
+
/** Generate channel configuration with authorization */
|
|
11
|
+
channel: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get the path for an event file based on variant
|
|
15
|
+
*/
|
|
16
|
+
export declare function getEventPath(entityName: string, _project: ProjectContext, options: EventOptions): string;
|
|
17
|
+
/**
|
|
18
|
+
* Event template function
|
|
19
|
+
*/
|
|
20
|
+
export declare const eventTemplate: TemplateFunction<EventOptions>;
|
|
21
|
+
export declare function getEventInstructions(entityName: string, options: EventOptions): string;
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Template
|
|
3
|
+
*
|
|
4
|
+
* Generates event-related files for VeloxTS applications.
|
|
5
|
+
*/
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Path Helpers
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/**
|
|
10
|
+
* Get the path for an event file based on variant
|
|
11
|
+
*/
|
|
12
|
+
export function getEventPath(entityName, _project, options) {
|
|
13
|
+
if (options.listener) {
|
|
14
|
+
return `src/events/listeners/${entityName.toLowerCase()}.ts`;
|
|
15
|
+
}
|
|
16
|
+
if (options.channel) {
|
|
17
|
+
return `src/events/channels/${entityName.toLowerCase()}.ts`;
|
|
18
|
+
}
|
|
19
|
+
// Default: broadcastable event
|
|
20
|
+
return `src/events/${entityName.toLowerCase()}.ts`;
|
|
21
|
+
}
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Templates
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Generate broadcastable event definition
|
|
27
|
+
*/
|
|
28
|
+
function generateBroadcastEvent(ctx) {
|
|
29
|
+
const { entity } = ctx;
|
|
30
|
+
return `/**
|
|
31
|
+
* ${entity.pascal} Event
|
|
32
|
+
*
|
|
33
|
+
* Broadcastable event for ${entity.humanReadable} notifications.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { defineEvent } from '@veloxts/events';
|
|
37
|
+
import { z } from 'zod';
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Schema
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
const ${entity.pascal}EventSchema = z.object({
|
|
44
|
+
id: z.string().uuid(),
|
|
45
|
+
timestamp: z.number(),
|
|
46
|
+
// TODO: Add your event payload fields
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export type ${entity.pascal}EventData = z.infer<typeof ${entity.pascal}EventSchema>;
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Event Definition
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* ${entity.pascal} event
|
|
57
|
+
*
|
|
58
|
+
* Fired when ${entity.humanReadable} occurs. Can be broadcast to connected clients
|
|
59
|
+
* via WebSocket channels.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* \`\`\`typescript
|
|
63
|
+
* import { ${entity.camel}Event } from '@/events/${entity.kebab}';
|
|
64
|
+
* import { events } from '@/events';
|
|
65
|
+
*
|
|
66
|
+
* // Dispatch event locally
|
|
67
|
+
* await events.dispatch(${entity.camel}Event, {
|
|
68
|
+
* id: '...',
|
|
69
|
+
* timestamp: Date.now(),
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* // Broadcast to specific channel
|
|
73
|
+
* await events.broadcast('user.123', ${entity.camel}Event, {
|
|
74
|
+
* id: '...',
|
|
75
|
+
* timestamp: Date.now(),
|
|
76
|
+
* });
|
|
77
|
+
* \`\`\`
|
|
78
|
+
*/
|
|
79
|
+
export const ${entity.camel}Event = defineEvent({
|
|
80
|
+
name: '${entity.kebab}',
|
|
81
|
+
schema: ${entity.pascal}EventSchema,
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Determine which channels should receive this event broadcast.
|
|
85
|
+
* Return an array of channel names to broadcast to.
|
|
86
|
+
*/
|
|
87
|
+
broadcastOn: ({ id }) => [
|
|
88
|
+
\`${entity.kebab}.\${id}\`, // Specific ${entity.humanReadable}
|
|
89
|
+
'${entity.kebab}', // All ${entity.plural}
|
|
90
|
+
],
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Optional: Transform data before broadcasting to clients.
|
|
94
|
+
* Useful for removing sensitive fields or adding computed properties.
|
|
95
|
+
*/
|
|
96
|
+
// broadcastWith: (data) => ({
|
|
97
|
+
// id: data.id,
|
|
98
|
+
// timestamp: data.timestamp,
|
|
99
|
+
// }),
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Optional: Determine if event should be broadcast.
|
|
103
|
+
* Return false to prevent broadcasting (local event only).
|
|
104
|
+
*/
|
|
105
|
+
// shouldBroadcast: ({ id }) => {
|
|
106
|
+
// // Only broadcast for specific conditions
|
|
107
|
+
// return true;
|
|
108
|
+
// },
|
|
109
|
+
});
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Generate event listener/handler
|
|
114
|
+
*/
|
|
115
|
+
function generateEventListener(ctx) {
|
|
116
|
+
const { entity } = ctx;
|
|
117
|
+
return `/**
|
|
118
|
+
* ${entity.pascal} Listener
|
|
119
|
+
*
|
|
120
|
+
* Event listener for handling ${entity.humanReadable} events.
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
import { defineListener } from '@veloxts/events';
|
|
124
|
+
import { z } from 'zod';
|
|
125
|
+
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Event Schema
|
|
128
|
+
// ============================================================================
|
|
129
|
+
|
|
130
|
+
const ${entity.pascal}EventSchema = z.object({
|
|
131
|
+
id: z.string().uuid(),
|
|
132
|
+
// TODO: Add event payload fields matching your event definition
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export type ${entity.pascal}EventData = z.infer<typeof ${entity.pascal}EventSchema>;
|
|
136
|
+
|
|
137
|
+
// ============================================================================
|
|
138
|
+
// Listener Definition
|
|
139
|
+
// ============================================================================
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* ${entity.pascal} event listener
|
|
143
|
+
*
|
|
144
|
+
* Handles ${entity.humanReadable} events when they are dispatched.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* \`\`\`typescript
|
|
148
|
+
* import { ${entity.camel}Listener } from '@/events/listeners/${entity.kebab}';
|
|
149
|
+
* import { events } from '@/events';
|
|
150
|
+
*
|
|
151
|
+
* // Register listener
|
|
152
|
+
* events.listen('${entity.kebab}', ${entity.camel}Listener);
|
|
153
|
+
*
|
|
154
|
+
* // Or in your event service configuration:
|
|
155
|
+
* const eventService = createEventService({
|
|
156
|
+
* listeners: {
|
|
157
|
+
* '${entity.kebab}': [${entity.camel}Listener],
|
|
158
|
+
* },
|
|
159
|
+
* });
|
|
160
|
+
* \`\`\`
|
|
161
|
+
*/
|
|
162
|
+
export const ${entity.camel}Listener = defineListener({
|
|
163
|
+
name: '${entity.kebab}-handler',
|
|
164
|
+
schema: ${entity.pascal}EventSchema,
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Handle the event
|
|
168
|
+
*/
|
|
169
|
+
handler: async ({ data, ctx }) => {
|
|
170
|
+
// TODO: Implement your event handling logic
|
|
171
|
+
// Access database via ctx.db (if ORM plugin registered)
|
|
172
|
+
// Access user context via ctx.user (if auth plugin registered)
|
|
173
|
+
|
|
174
|
+
console.log(\`Handling ${entity.humanReadable} event:\`, data.id);
|
|
175
|
+
|
|
176
|
+
// Example: Update database
|
|
177
|
+
// await ctx.db.${entity.camel}.update({
|
|
178
|
+
// where: { id: data.id },
|
|
179
|
+
// data: { processedAt: new Date() },
|
|
180
|
+
// });
|
|
181
|
+
|
|
182
|
+
// Example: Send notification
|
|
183
|
+
// await sendNotification(data.id);
|
|
184
|
+
|
|
185
|
+
// Example: Dispatch another event
|
|
186
|
+
// await ctx.events.dispatch(anotherEvent, { ... });
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Optional: Determine if this listener should handle the event.
|
|
191
|
+
* Return false to skip handling.
|
|
192
|
+
*/
|
|
193
|
+
// shouldHandle: ({ data }) => {
|
|
194
|
+
// // Only handle specific conditions
|
|
195
|
+
// return true;
|
|
196
|
+
// },
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Optional: Queue configuration for async processing.
|
|
200
|
+
* If provided, listener will be queued instead of executed immediately.
|
|
201
|
+
*/
|
|
202
|
+
// queue: {
|
|
203
|
+
// name: '${entity.kebab}-events',
|
|
204
|
+
// attempts: 3,
|
|
205
|
+
// backoff: {
|
|
206
|
+
// type: 'exponential',
|
|
207
|
+
// delay: 1000,
|
|
208
|
+
// },
|
|
209
|
+
// },
|
|
210
|
+
});
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Generate channel configuration with authorization
|
|
215
|
+
*/
|
|
216
|
+
function generateChannel(ctx) {
|
|
217
|
+
const { entity } = ctx;
|
|
218
|
+
return `/**
|
|
219
|
+
* ${entity.pascal} Channel
|
|
220
|
+
*
|
|
221
|
+
* Channel configuration and authorization for ${entity.humanReadable} events.
|
|
222
|
+
*/
|
|
223
|
+
|
|
224
|
+
import { defineChannel } from '@veloxts/events';
|
|
225
|
+
import { z } from 'zod';
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Channel Parameters Schema
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
const ${entity.pascal}ChannelParamsSchema = z.object({
|
|
232
|
+
${entity.camel}Id: z.string().uuid(),
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
export type ${entity.pascal}ChannelParams = z.infer<typeof ${entity.pascal}ChannelParamsSchema>;
|
|
236
|
+
|
|
237
|
+
// ============================================================================
|
|
238
|
+
// Channel Definition
|
|
239
|
+
// ============================================================================
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* ${entity.pascal} channel configuration
|
|
243
|
+
*
|
|
244
|
+
* Defines authorization rules and configuration for subscribing to
|
|
245
|
+
* ${entity.humanReadable} events via WebSocket.
|
|
246
|
+
*
|
|
247
|
+
* Channel name pattern: \`${entity.kebab}.{${entity.camel}Id}\`
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* \`\`\`typescript
|
|
251
|
+
* // Frontend client subscribing to channel
|
|
252
|
+
* import { socket } from '@/lib/socket';
|
|
253
|
+
*
|
|
254
|
+
* socket.subscribe('${entity.kebab}.123', (event) => {
|
|
255
|
+
* console.log('Received event:', event);
|
|
256
|
+
* });
|
|
257
|
+
* \`\`\`
|
|
258
|
+
*/
|
|
259
|
+
export const ${entity.camel}Channel = defineChannel({
|
|
260
|
+
/**
|
|
261
|
+
* Channel name pattern (use {param} for dynamic segments)
|
|
262
|
+
*/
|
|
263
|
+
pattern: '${entity.kebab}.{${entity.camel}Id}',
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Channel parameters schema for validation
|
|
267
|
+
*/
|
|
268
|
+
schema: ${entity.pascal}ChannelParamsSchema,
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Authorization logic - determine if user can subscribe to this channel
|
|
272
|
+
*
|
|
273
|
+
* @returns true to allow, false to deny, or throw error for specific messages
|
|
274
|
+
*/
|
|
275
|
+
authorize: async ({ params, ctx }) => {
|
|
276
|
+
// TODO: Implement authorization logic
|
|
277
|
+
|
|
278
|
+
// Example: Public channel (anyone can subscribe)
|
|
279
|
+
// return true;
|
|
280
|
+
|
|
281
|
+
// Example: Authenticated users only
|
|
282
|
+
if (!ctx.user) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Example: Check ownership or permissions
|
|
287
|
+
const ${entity.camel} = await ctx.db.${entity.camel}.findUnique({
|
|
288
|
+
where: { id: params.${entity.camel}Id },
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!${entity.camel}) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Check if user owns the ${entity.humanReadable} or has permission
|
|
296
|
+
return ${entity.camel}.userId === ctx.user.id;
|
|
297
|
+
|
|
298
|
+
// Example: Role-based access
|
|
299
|
+
// return ctx.user.role === 'admin' || ${entity.camel}.userId === ctx.user.id;
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Optional: Transform channel params before matching
|
|
304
|
+
* Useful for normalizing IDs or adding computed values
|
|
305
|
+
*/
|
|
306
|
+
// transform: (params) => ({
|
|
307
|
+
// ...params,
|
|
308
|
+
// ${entity.camel}Id: params.${entity.camel}Id.toLowerCase(),
|
|
309
|
+
// }),
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Optional: Rate limiting configuration
|
|
313
|
+
* Prevents abuse by limiting subscription attempts
|
|
314
|
+
*/
|
|
315
|
+
// rateLimit: {
|
|
316
|
+
// maxAttempts: 10,
|
|
317
|
+
// windowMs: 60000, // 1 minute
|
|
318
|
+
// },
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// Public Channel Variant
|
|
323
|
+
// ============================================================================
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Public ${entity.pascal} channel (no authentication required)
|
|
327
|
+
*
|
|
328
|
+
* Use this for public ${entity.humanReadable} updates that don't require authorization.
|
|
329
|
+
*
|
|
330
|
+
* Channel name: \`public.${entity.kebab}\`
|
|
331
|
+
*/
|
|
332
|
+
export const public${entity.pascal}Channel = defineChannel({
|
|
333
|
+
pattern: 'public.${entity.kebab}',
|
|
334
|
+
schema: z.object({}),
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Public channel - anyone can subscribe
|
|
338
|
+
*/
|
|
339
|
+
authorize: async () => true,
|
|
340
|
+
});
|
|
341
|
+
`;
|
|
342
|
+
}
|
|
343
|
+
// ============================================================================
|
|
344
|
+
// Main Template
|
|
345
|
+
// ============================================================================
|
|
346
|
+
/**
|
|
347
|
+
* Event template function
|
|
348
|
+
*/
|
|
349
|
+
export const eventTemplate = (ctx) => {
|
|
350
|
+
if (ctx.options.listener) {
|
|
351
|
+
return generateEventListener(ctx);
|
|
352
|
+
}
|
|
353
|
+
if (ctx.options.channel) {
|
|
354
|
+
return generateChannel(ctx);
|
|
355
|
+
}
|
|
356
|
+
// Default: broadcastable event
|
|
357
|
+
return generateBroadcastEvent(ctx);
|
|
358
|
+
};
|
|
359
|
+
// ============================================================================
|
|
360
|
+
// Post-generation Instructions
|
|
361
|
+
// ============================================================================
|
|
362
|
+
export function getEventInstructions(entityName, options) {
|
|
363
|
+
const lines = [];
|
|
364
|
+
if (options.listener) {
|
|
365
|
+
lines.push(`Your ${entityName} listener has been created.`, '', 'Next steps:');
|
|
366
|
+
lines.push(' 1. Update the Zod schema to match your event payload');
|
|
367
|
+
lines.push(' 2. Implement the handler logic');
|
|
368
|
+
lines.push(' 3. Register the listener in your event service:');
|
|
369
|
+
lines.push('');
|
|
370
|
+
lines.push(" import { events } from '@/events';");
|
|
371
|
+
lines.push(` import { ${entityName}Listener } from '@/events/listeners/${entityName.toLowerCase()}';`);
|
|
372
|
+
lines.push('');
|
|
373
|
+
lines.push(` events.listen('${entityName.toLowerCase()}', ${entityName}Listener);`);
|
|
374
|
+
lines.push('');
|
|
375
|
+
lines.push(' 4. Optional: Configure queue settings for async processing');
|
|
376
|
+
}
|
|
377
|
+
else if (options.channel) {
|
|
378
|
+
lines.push(`Your ${entityName} channel has been created.`, '', 'Next steps:');
|
|
379
|
+
lines.push(' 1. Update the channel pattern and schema for your use case');
|
|
380
|
+
lines.push(' 2. Implement authorization logic');
|
|
381
|
+
lines.push(' 3. Register the channel in your WebSocket server:');
|
|
382
|
+
lines.push('');
|
|
383
|
+
lines.push(" import { channels } from '@/events';");
|
|
384
|
+
lines.push(` import { ${entityName}Channel } from '@/events/channels/${entityName.toLowerCase()}';`);
|
|
385
|
+
lines.push('');
|
|
386
|
+
lines.push(` channels.register(${entityName}Channel);`);
|
|
387
|
+
lines.push('');
|
|
388
|
+
lines.push(' 4. Subscribe from your frontend:');
|
|
389
|
+
lines.push('');
|
|
390
|
+
lines.push(" socket.subscribe('pattern', (event) => { ... });");
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
lines.push(`Your ${entityName} event has been created.`, '', 'Next steps:');
|
|
394
|
+
lines.push(' 1. Update the Zod schema with your event payload fields');
|
|
395
|
+
lines.push(' 2. Customize the broadcast channels in broadcastOn()');
|
|
396
|
+
lines.push(' 3. Dispatch the event from your procedures:');
|
|
397
|
+
lines.push('');
|
|
398
|
+
lines.push(" import { events } from '@/events';");
|
|
399
|
+
lines.push(` import { ${entityName}Event } from '@/events/${entityName.toLowerCase()}';`);
|
|
400
|
+
lines.push('');
|
|
401
|
+
lines.push(` await events.dispatch(${entityName}Event, {`);
|
|
402
|
+
lines.push(" id: '...',");
|
|
403
|
+
lines.push(' timestamp: Date.now(),');
|
|
404
|
+
lines.push(' });');
|
|
405
|
+
lines.push('');
|
|
406
|
+
lines.push(' 4. Optional: Add event listeners to handle the event');
|
|
407
|
+
lines.push(` velox make event ${entityName} --listener`);
|
|
408
|
+
}
|
|
409
|
+
return lines.join('\n');
|
|
410
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job Template
|
|
3
|
+
*
|
|
4
|
+
* Generates background job files for VeloxTS applications.
|
|
5
|
+
*/
|
|
6
|
+
import type { ProjectContext, TemplateFunction } from '../types.js';
|
|
7
|
+
export interface JobOptions {
|
|
8
|
+
/** Custom queue assignment */
|
|
9
|
+
queue: boolean;
|
|
10
|
+
/** Retry and backoff configuration */
|
|
11
|
+
retry: boolean;
|
|
12
|
+
/** Progress tracking */
|
|
13
|
+
progress: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get the path for a job file
|
|
17
|
+
*/
|
|
18
|
+
export declare function getJobPath(entityName: string, _project: ProjectContext): string;
|
|
19
|
+
/**
|
|
20
|
+
* Job template function
|
|
21
|
+
*/
|
|
22
|
+
export declare const jobTemplate: TemplateFunction<JobOptions>;
|
|
23
|
+
export declare function getJobInstructions(entityName: string, options: JobOptions): string;
|