@nexusts/cli 0.9.7 → 0.9.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.ts +1 -0
- package/dist/core/naming.d.ts +20 -0
- package/dist/core/prompts.d.ts +14 -0
- package/dist/index.js +618 -631
- package/dist/index.js.map +32 -24
- package/dist/templates/auth/auth-instance.d.ts +14 -0
- package/dist/templates/auth/env-example.d.ts +9 -0
- package/dist/templates/index.d.ts +11 -0
- package/dist/templates/listener/listener.d.ts +9 -0
- package/dist/templates/model/drizzle-dialect.d.ts +12 -0
- package/dist/templates/queue/job.d.ts +9 -0
- package/dist/templates/queue/worker.d.ts +9 -0
- package/dist/templates/schedule/task.d.ts +9 -0
- package/dist/templates/session/session.d.ts +9 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45,6 +45,12 @@ function parseArgs(argv) {
|
|
|
45
45
|
if (longMatch) {
|
|
46
46
|
const [, name, inline] = longMatch;
|
|
47
47
|
const flagName = name;
|
|
48
|
+
if (flagName.startsWith("no-")) {
|
|
49
|
+
const key = flagName.slice(3);
|
|
50
|
+
setFlag(flags, key, false);
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
48
54
|
if (inline !== undefined) {
|
|
49
55
|
setFlag(flags, flagName, inline);
|
|
50
56
|
i++;
|
|
@@ -168,7 +174,8 @@ async function loadConfig(cwd = process.cwd()) {
|
|
|
168
174
|
const mod = await import(path);
|
|
169
175
|
config = mod.default ?? mod;
|
|
170
176
|
} catch (importErr) {
|
|
171
|
-
|
|
177
|
+
process.stderr.write(`[nx] Could not dynamically import ${candidate}: ${importErr instanceof Error ? importErr.message : String(importErr)}. Falling back to defaults.
|
|
178
|
+
`);
|
|
172
179
|
config = {};
|
|
173
180
|
}
|
|
174
181
|
}
|
|
@@ -247,6 +254,118 @@ import {
|
|
|
247
254
|
writeFileSync
|
|
248
255
|
} from "fs";
|
|
249
256
|
import { dirname, isAbsolute, relative, resolve as resolve2 } from "path";
|
|
257
|
+
|
|
258
|
+
// packages/cli/src/core/template.ts
|
|
259
|
+
var VAR_RE = /\{\{\s*([\w.]+)(?:\s*\|\s*(\w+))?\s*\}\}/;
|
|
260
|
+
var SECTION_RE = /\{\{\s*([#^])\s*([\w.]+)\s*\}\}([\s\S]*?)\{\{\s*\/\s*\2\s*\}\}/g;
|
|
261
|
+
function render(template, context) {
|
|
262
|
+
let out = template.replace(SECTION_RE, (_, kind, key, body) => {
|
|
263
|
+
const v = lookup(context, key);
|
|
264
|
+
const truthy = isTruthy(v);
|
|
265
|
+
if (kind === "#")
|
|
266
|
+
return truthy ? body : "";
|
|
267
|
+
return truthy ? "" : body;
|
|
268
|
+
});
|
|
269
|
+
let prev;
|
|
270
|
+
do {
|
|
271
|
+
prev = out;
|
|
272
|
+
out = out.replace(VAR_RE, (_, key, filter) => {
|
|
273
|
+
const v = lookup(context, key);
|
|
274
|
+
return applyFilter(v === undefined || v === null ? "" : String(v), filter);
|
|
275
|
+
});
|
|
276
|
+
} while (out !== prev);
|
|
277
|
+
return out;
|
|
278
|
+
}
|
|
279
|
+
function lookup(ctx, dotted) {
|
|
280
|
+
if (dotted in ctx)
|
|
281
|
+
return ctx[dotted];
|
|
282
|
+
const parts = dotted.split(".");
|
|
283
|
+
let cur = ctx;
|
|
284
|
+
for (const p of parts) {
|
|
285
|
+
if (cur == null || typeof cur !== "object")
|
|
286
|
+
return;
|
|
287
|
+
cur = cur[p];
|
|
288
|
+
}
|
|
289
|
+
return cur === undefined || cur === null ? undefined : cur;
|
|
290
|
+
}
|
|
291
|
+
function isTruthy(v) {
|
|
292
|
+
if (v === undefined || v === null)
|
|
293
|
+
return false;
|
|
294
|
+
if (typeof v === "string")
|
|
295
|
+
return v.length > 0 && v !== "false" && v !== "0";
|
|
296
|
+
if (typeof v === "number")
|
|
297
|
+
return v !== 0;
|
|
298
|
+
if (typeof v === "boolean")
|
|
299
|
+
return v;
|
|
300
|
+
if (Array.isArray(v))
|
|
301
|
+
return v.length > 0;
|
|
302
|
+
return Object.keys(v).length > 0;
|
|
303
|
+
}
|
|
304
|
+
function applyFilter(value, filter) {
|
|
305
|
+
switch (filter) {
|
|
306
|
+
case undefined:
|
|
307
|
+
case "raw":
|
|
308
|
+
return value;
|
|
309
|
+
case "upper":
|
|
310
|
+
return value.toUpperCase();
|
|
311
|
+
case "lower":
|
|
312
|
+
return value.toLowerCase();
|
|
313
|
+
case "pascal":
|
|
314
|
+
return toPascal(value);
|
|
315
|
+
case "camel":
|
|
316
|
+
return toCamel(value);
|
|
317
|
+
case "snake":
|
|
318
|
+
return toSnake(value);
|
|
319
|
+
case "kebab":
|
|
320
|
+
return toKebab(value);
|
|
321
|
+
case "plural":
|
|
322
|
+
return pluralize(value);
|
|
323
|
+
case "singular":
|
|
324
|
+
return singularize(value);
|
|
325
|
+
default:
|
|
326
|
+
throw new Error(`Unknown template filter: ${filter}`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function splitWords(s) {
|
|
330
|
+
return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean);
|
|
331
|
+
}
|
|
332
|
+
function toPascal(s) {
|
|
333
|
+
return splitWords(s).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
334
|
+
}
|
|
335
|
+
function toCamel(s) {
|
|
336
|
+
const p = toPascal(s);
|
|
337
|
+
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
338
|
+
}
|
|
339
|
+
function toSnake(s) {
|
|
340
|
+
return splitWords(s).map((w) => w.toLowerCase()).join("_");
|
|
341
|
+
}
|
|
342
|
+
function toKebab(s) {
|
|
343
|
+
return splitWords(s).map((w) => w.toLowerCase()).join("-");
|
|
344
|
+
}
|
|
345
|
+
function pluralize(s) {
|
|
346
|
+
if (!s)
|
|
347
|
+
return s;
|
|
348
|
+
if (/(s|x|z|ch|sh)$/i.test(s))
|
|
349
|
+
return `${s}es`;
|
|
350
|
+
if (/[^aeiou]y$/i.test(s))
|
|
351
|
+
return `${s.slice(0, -1)}ies`;
|
|
352
|
+
if (/y$/i.test(s))
|
|
353
|
+
return `${s}s`;
|
|
354
|
+
return `${s}s`;
|
|
355
|
+
}
|
|
356
|
+
function singularize(s) {
|
|
357
|
+
if (!s)
|
|
358
|
+
return s;
|
|
359
|
+
if (/(ses|xes|zes|ches|shes)$/i.test(s))
|
|
360
|
+
return s.slice(0, -2);
|
|
361
|
+
if (/ies$/i.test(s))
|
|
362
|
+
return `${s.slice(0, -3)}y`;
|
|
363
|
+
if (/s$/i.test(s) && s.length > 1)
|
|
364
|
+
return s.slice(0, -1);
|
|
365
|
+
return s;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// packages/cli/src/core/fs.ts
|
|
250
369
|
function writeFile(path, contents, opts = {}) {
|
|
251
370
|
const base = opts.base ?? process.cwd();
|
|
252
371
|
const target = isAbsolute(path) ? path : resolve2(base, path);
|
|
@@ -273,17 +392,6 @@ function nameVariants(input) {
|
|
|
273
392
|
pluralKebab: pluralize(kebab)
|
|
274
393
|
};
|
|
275
394
|
}
|
|
276
|
-
function pluralize(s) {
|
|
277
|
-
if (!s)
|
|
278
|
-
return s;
|
|
279
|
-
if (/(s|x|z|ch|sh)$/i.test(s))
|
|
280
|
-
return `${s}es`;
|
|
281
|
-
if (/[^aeiou]y$/i.test(s))
|
|
282
|
-
return `${s.slice(0, -1)}ies`;
|
|
283
|
-
if (/y$/i.test(s))
|
|
284
|
-
return `${s}s`;
|
|
285
|
-
return `${s}s`;
|
|
286
|
-
}
|
|
287
395
|
// packages/cli/src/core/logger.ts
|
|
288
396
|
var USE_COLOR = process.env.NO_COLOR === undefined && process.env.FORCE_COLOR !== "0" && process.stdout.isTTY === true;
|
|
289
397
|
var wrap = (open, close) => (s) => USE_COLOR ? `\x1B[${open}m${s}\x1B[${close}m` : s;
|
|
@@ -384,115 +492,32 @@ async function prompt(message, options = {}) {
|
|
|
384
492
|
async function select(message, choices, options = {}) {
|
|
385
493
|
return prompt(message, { ...options, choices });
|
|
386
494
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
});
|
|
405
|
-
} while (out !== prev);
|
|
406
|
-
return out;
|
|
407
|
-
}
|
|
408
|
-
function lookup(ctx, dotted) {
|
|
409
|
-
if (dotted in ctx)
|
|
410
|
-
return ctx[dotted];
|
|
411
|
-
const parts = dotted.split(".");
|
|
412
|
-
let cur = ctx;
|
|
413
|
-
for (const p of parts) {
|
|
414
|
-
if (cur == null || typeof cur !== "object")
|
|
415
|
-
return;
|
|
416
|
-
cur = cur[p];
|
|
495
|
+
var VALID_PROJECT_OPTIONS = {
|
|
496
|
+
style: ["nest", "adonis", "functional"],
|
|
497
|
+
view: ["rendu", "edge", "eta", "inertia", "none"],
|
|
498
|
+
orm: ["drizzle", "kysely", "none"],
|
|
499
|
+
db: ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"],
|
|
500
|
+
frontend: ["react", "vue", "svelte", "solid"]
|
|
501
|
+
};
|
|
502
|
+
async function resolveProjectOption(flags, key, valid, defaultVal, interactive) {
|
|
503
|
+
const flagVal = flags[key];
|
|
504
|
+
if (flagVal) {
|
|
505
|
+
if (valid.includes(flagVal))
|
|
506
|
+
return flagVal;
|
|
507
|
+
if (!interactive) {
|
|
508
|
+
logger.error(`Invalid --${key} "${flagVal}". Valid values: ${valid.join(", ")}`);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
logger.warn(`"${flagVal}" is not valid for --${key}. Please choose from the list.`);
|
|
417
512
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return v.length > 0 && v !== "false" && v !== "0";
|
|
425
|
-
if (typeof v === "number")
|
|
426
|
-
return v !== 0;
|
|
427
|
-
if (typeof v === "boolean")
|
|
428
|
-
return v;
|
|
429
|
-
if (Array.isArray(v))
|
|
430
|
-
return v.length > 0;
|
|
431
|
-
return Object.keys(v).length > 0;
|
|
432
|
-
}
|
|
433
|
-
function applyFilter(value, filter) {
|
|
434
|
-
switch (filter) {
|
|
435
|
-
case undefined:
|
|
436
|
-
case "raw":
|
|
437
|
-
return value;
|
|
438
|
-
case "upper":
|
|
439
|
-
return value.toUpperCase();
|
|
440
|
-
case "lower":
|
|
441
|
-
return value.toLowerCase();
|
|
442
|
-
case "pascal":
|
|
443
|
-
return toPascal(value);
|
|
444
|
-
case "camel":
|
|
445
|
-
return toCamel(value);
|
|
446
|
-
case "snake":
|
|
447
|
-
return toSnake(value);
|
|
448
|
-
case "kebab":
|
|
449
|
-
return toKebab(value);
|
|
450
|
-
case "plural":
|
|
451
|
-
return pluralize2(value);
|
|
452
|
-
case "singular":
|
|
453
|
-
return singularize(value);
|
|
454
|
-
default:
|
|
455
|
-
throw new Error(`Unknown template filter: ${filter}`);
|
|
513
|
+
const label = key === "style" ? "Routing style" : key === "view" ? "View engine" : key === "orm" ? "ORM driver" : key === "db" ? "Database driver" : "Inertia frontend";
|
|
514
|
+
for (;; ) {
|
|
515
|
+
const answer = await select(label, [...valid], { default: defaultVal });
|
|
516
|
+
if (valid.includes(answer))
|
|
517
|
+
return answer;
|
|
518
|
+
logger.warn(`"${answer}" is not valid. Please choose from: ${valid.join(", ")}`);
|
|
456
519
|
}
|
|
457
520
|
}
|
|
458
|
-
function splitWords(s) {
|
|
459
|
-
return s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_-]+/).filter(Boolean);
|
|
460
|
-
}
|
|
461
|
-
function toPascal(s) {
|
|
462
|
-
return splitWords(s).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
463
|
-
}
|
|
464
|
-
function toCamel(s) {
|
|
465
|
-
const p = toPascal(s);
|
|
466
|
-
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
467
|
-
}
|
|
468
|
-
function toSnake(s) {
|
|
469
|
-
return splitWords(s).map((w) => w.toLowerCase()).join("_");
|
|
470
|
-
}
|
|
471
|
-
function toKebab(s) {
|
|
472
|
-
return splitWords(s).map((w) => w.toLowerCase()).join("-");
|
|
473
|
-
}
|
|
474
|
-
function pluralize2(s) {
|
|
475
|
-
if (!s)
|
|
476
|
-
return s;
|
|
477
|
-
if (/(s|x|z|ch|sh)$/i.test(s))
|
|
478
|
-
return `${s}es`;
|
|
479
|
-
if (/[^aeiou]y$/i.test(s))
|
|
480
|
-
return `${s.slice(0, -1)}ies`;
|
|
481
|
-
if (/y$/i.test(s))
|
|
482
|
-
return `${s}s`;
|
|
483
|
-
return `${s}s`;
|
|
484
|
-
}
|
|
485
|
-
function singularize(s) {
|
|
486
|
-
if (!s)
|
|
487
|
-
return s;
|
|
488
|
-
if (/(ses|xes|zes|ches|shes)$/i.test(s))
|
|
489
|
-
return s.slice(0, -2);
|
|
490
|
-
if (/ies$/i.test(s))
|
|
491
|
-
return `${s.slice(0, -3)}y`;
|
|
492
|
-
if (/s$/i.test(s) && s.length > 1)
|
|
493
|
-
return s.slice(0, -1);
|
|
494
|
-
return s;
|
|
495
|
-
}
|
|
496
521
|
// packages/cli/src/core/version.ts
|
|
497
522
|
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
498
523
|
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
@@ -517,7 +542,9 @@ import { resolve as resolve4 } from "path";
|
|
|
517
542
|
|
|
518
543
|
// packages/cli/src/templates/controller/adonis.ts
|
|
519
544
|
var adonis_default = `
|
|
545
|
+
{{# hasService }}
|
|
520
546
|
import { {{ service }} } from '../services/{{ kebab }}.service.js';
|
|
547
|
+
{{/ hasService }}
|
|
521
548
|
|
|
522
549
|
export class {{ name }}Controller {
|
|
523
550
|
async index() {
|
|
@@ -580,7 +607,9 @@ export const {{ camel }}Routes = {
|
|
|
580
607
|
var nest_default = `
|
|
581
608
|
import { Controller, Delete, Get, Inject, Post, Put, inputValue } from '@nexusts/core';
|
|
582
609
|
import type { Context } from 'hono';
|
|
610
|
+
{{# hasService }}
|
|
583
611
|
import { {{ service }} } from '../services/{{ kebab }}.service.js';
|
|
612
|
+
{{/ hasService }}
|
|
584
613
|
|
|
585
614
|
@Controller('/{{ kebab }}s')
|
|
586
615
|
export class {{ name }}Controller {
|
|
@@ -993,11 +1022,202 @@ export class {{ repository }} extends KyselyRepository<any, '{{ tableName }}'> {
|
|
|
993
1022
|
}
|
|
994
1023
|
`.trimStart();
|
|
995
1024
|
|
|
996
|
-
// packages/cli/src/templates/
|
|
997
|
-
var
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1025
|
+
// packages/cli/src/templates/auth/auth-instance.ts
|
|
1026
|
+
var auth_instance_default = `/**
|
|
1027
|
+
* Better-auth instance \u2014 generated by \`nx make:auth\`.
|
|
1028
|
+
*
|
|
1029
|
+
* Edit \`nx.config.ts\` (\`auth\` section) instead of this file when possible.
|
|
1030
|
+
*/
|
|
1031
|
+
import { createAuth } from '@nexusts/auth';
|
|
1032
|
+
|
|
1033
|
+
export const auth = createAuth({
|
|
1034
|
+
{{^jwt}}
|
|
1035
|
+
jwt: { enabled: false },
|
|
1036
|
+
{{/jwt}}
|
|
1037
|
+
{{#jwt}}
|
|
1038
|
+
jwt: { enabled: true },
|
|
1039
|
+
{{/jwt}}
|
|
1040
|
+
{{#passkey}}
|
|
1041
|
+
passkey: {
|
|
1042
|
+
enabled: true,
|
|
1043
|
+
rpName: '{{ passkeyRpName }}',
|
|
1044
|
+
rpId: '{{ passkeyRpId }}',
|
|
1045
|
+
origin: '{{ passkeyOrigin }}',
|
|
1046
|
+
},
|
|
1047
|
+
{{/passkey}}
|
|
1048
|
+
{{#providers}}
|
|
1049
|
+
socialProviders: {
|
|
1050
|
+
{{#entries}}
|
|
1051
|
+
{{ name }}: {
|
|
1052
|
+
clientId: process.env.{{ envVar }}_CLIENT_ID!,
|
|
1053
|
+
clientSecret: process.env.{{ envVar }}_CLIENT_SECRET!,
|
|
1054
|
+
},
|
|
1055
|
+
{{/entries}}
|
|
1056
|
+
},
|
|
1057
|
+
{{/providers}}
|
|
1058
|
+
});
|
|
1059
|
+
`.trimStart();
|
|
1060
|
+
|
|
1061
|
+
// packages/cli/src/templates/auth/env-example.ts
|
|
1062
|
+
var env_example_default = `# Better Auth \u2014 generated by \`nx make:auth\`.
|
|
1063
|
+
# Generate a secret with: openssl rand -base64 32
|
|
1064
|
+
BETTER_AUTH_SECRET=
|
|
1065
|
+
BETTER_AUTH_URL=http://localhost:3000
|
|
1066
|
+
{{#providers}}
|
|
1067
|
+
{{#entries}}
|
|
1068
|
+
{{ envVar }}_CLIENT_ID=
|
|
1069
|
+
{{ envVar }}_CLIENT_SECRET=
|
|
1070
|
+
{{/entries}}
|
|
1071
|
+
{{/providers}}
|
|
1072
|
+
`.trimStart();
|
|
1073
|
+
|
|
1074
|
+
// packages/cli/src/templates/listener/listener.ts
|
|
1075
|
+
var listener_default = `
|
|
1076
|
+
import { Inject, Injectable } from '@nexusts/core';
|
|
1077
|
+
import { EventService, OnEvent } from '@nexusts/events';
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* {{ name }} listener \u2014 generated by \`nx make:listener {{ name }}\`.
|
|
1081
|
+
*
|
|
1082
|
+
* Register handlers below with \`@OnEvent(pattern)\`. Pair with
|
|
1083
|
+
* \`scanForListeners(this, eventService)\` at boot.
|
|
1084
|
+
*/
|
|
1085
|
+
@Injectable()
|
|
1086
|
+
export class {{ name }}Listener {
|
|
1087
|
+
@Inject(EventService.TOKEN) declare private readonly events: EventService;
|
|
1088
|
+
|
|
1089
|
+
// TODO: add @OnEvent('your.event') handlers below.
|
|
1090
|
+
|
|
1091
|
+
// @OnEvent('user.created')
|
|
1092
|
+
// async onUserCreated(payload: { userId: string; email: string }) {
|
|
1093
|
+
// this.events; // unused \u2014 remove if you don't need it
|
|
1094
|
+
// }
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
import { scanForListeners } from '@nexusts/events';
|
|
1098
|
+
|
|
1099
|
+
/**
|
|
1100
|
+
* Bootstrap helper \u2014 call this from your main.ts (or wherever you
|
|
1101
|
+
* wire up the application) to register every \`@OnEvent\` handler
|
|
1102
|
+
* on this listener class.
|
|
1103
|
+
*/
|
|
1104
|
+
export async function register{{ name }}(listener: {{ name }}Listener, events: EventService) {
|
|
1105
|
+
return scanForListeners(listener, events);
|
|
1106
|
+
}
|
|
1107
|
+
`.trimStart();
|
|
1108
|
+
|
|
1109
|
+
// packages/cli/src/templates/queue/worker.ts
|
|
1110
|
+
var worker_default = `
|
|
1111
|
+
import { Inject, Injectable } from '@nexusts/core';
|
|
1112
|
+
import { QueueService, OnQueueReady } from '@nexusts/queue';
|
|
1113
|
+
|
|
1114
|
+
/**
|
|
1115
|
+
* {{ name }} worker \u2014 generated by \`nx make:queue {{ name }}\`.
|
|
1116
|
+
*
|
|
1117
|
+
* Registers a handler for the \`{{ name }}\` job name on boot.
|
|
1118
|
+
*/
|
|
1119
|
+
@Injectable()
|
|
1120
|
+
export class {{ name }}Worker {
|
|
1121
|
+
@Inject(QueueService.TOKEN) declare private readonly queue: QueueService;
|
|
1122
|
+
|
|
1123
|
+
@OnQueueReady()
|
|
1124
|
+
async register(): Promise<void> {
|
|
1125
|
+
await this.queue.process('{{ name }}', async (data, ctx) => {
|
|
1126
|
+
ctx.prefix; // \u2192 "[queue:{{ name }}]"
|
|
1127
|
+
try {
|
|
1128
|
+
await this.handle(data as {{ name }}Data);
|
|
1129
|
+
return { status: 'completed' };
|
|
1130
|
+
} catch (err) {
|
|
1131
|
+
return {
|
|
1132
|
+
status: 'failed',
|
|
1133
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
1134
|
+
willRetry: ctx.attempts < 3,
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
/**
|
|
1141
|
+
* Replace this body with your actual worker logic.
|
|
1142
|
+
*/
|
|
1143
|
+
async handle(data: {{ name }}Data): Promise<void> {
|
|
1144
|
+
// TODO: implement
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
/**
|
|
1149
|
+
* Typed payload for the \`{{ name }}\` job.
|
|
1150
|
+
*/
|
|
1151
|
+
export interface {{ name }}Data {
|
|
1152
|
+
// TODO: define fields
|
|
1153
|
+
[key: string]: unknown;
|
|
1154
|
+
}
|
|
1155
|
+
`.trimStart();
|
|
1156
|
+
|
|
1157
|
+
// packages/cli/src/templates/queue/job.ts
|
|
1158
|
+
var job_default = `
|
|
1159
|
+
import { Inject, Injectable } from '@nexusts/core';
|
|
1160
|
+
import { QueueService } from '@nexusts/queue';
|
|
1161
|
+
import type { {{ name }}Data } from './{{ kebab }}.worker.js';
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Helper for enqueuing \`{{ name }}\` jobs from controllers / services.
|
|
1165
|
+
* Generated by \`nx make:queue {{ name }}\`.
|
|
1166
|
+
*/
|
|
1167
|
+
@Injectable()
|
|
1168
|
+
export class {{ name }}Job {
|
|
1169
|
+
@Inject(QueueService.TOKEN) declare private readonly queue: QueueService;
|
|
1170
|
+
|
|
1171
|
+
/** Enqueue a single {{ name }} job. */
|
|
1172
|
+
async enqueue(data: {{ name }}Data, options?: {
|
|
1173
|
+
delaySeconds?: number;
|
|
1174
|
+
attempts?: number;
|
|
1175
|
+
}) {
|
|
1176
|
+
return this.queue.add('{{ name }}', data, options);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/** Enqueue many at once. */
|
|
1180
|
+
async enqueueBatch(items: {{ name }}Data[]) {
|
|
1181
|
+
return this.queue.addBatch(
|
|
1182
|
+
items.map((data) => ({ name: '{{ name }}', data })),
|
|
1183
|
+
);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
`.trimStart();
|
|
1187
|
+
|
|
1188
|
+
// packages/cli/src/templates/schedule/task.ts
|
|
1189
|
+
var task_default = `
|
|
1190
|
+
import { Injectable } from '@nexusts/core';
|
|
1191
|
+
import { Cron, Interval, Timeout } from '@nexusts/schedule';
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* {{ name }} task \u2014 generated by \`nx make:schedule {{ name }}\`.
|
|
1195
|
+
*
|
|
1196
|
+
* Mark methods with \`@Cron\`, \`@Interval\`, or \`@Timeout\`.
|
|
1197
|
+
* Auto-detected at boot \u2014 no manual registration needed.
|
|
1198
|
+
*/
|
|
1199
|
+
@Injectable()
|
|
1200
|
+
export class {{ name }}Task {
|
|
1201
|
+
constructor() {}
|
|
1202
|
+
|
|
1203
|
+
// TODO: add @Cron, @Interval, or @Timeout handlers below.
|
|
1204
|
+
|
|
1205
|
+
// @Cron('0 * * * *') // every hour
|
|
1206
|
+
// async hourly() { /* ... */ }
|
|
1207
|
+
|
|
1208
|
+
// @Interval(60_000) // every minute
|
|
1209
|
+
// async tick() { /* ... */ }
|
|
1210
|
+
|
|
1211
|
+
// @Timeout(5_000) // 5s after boot
|
|
1212
|
+
// async startup() { /* ... */ }
|
|
1213
|
+
}
|
|
1214
|
+
`.trimStart();
|
|
1215
|
+
|
|
1216
|
+
// packages/cli/src/templates/service/service.ts
|
|
1217
|
+
var service_default = `
|
|
1218
|
+
import { Injectable, Inject } from '@nexusts/core';
|
|
1219
|
+
{{#hasRepo}}import { {{ repository }} } from '../repositories/{{ kebab }}.repository.js';{{/hasRepo}}
|
|
1220
|
+
|
|
1001
1221
|
@Injectable()
|
|
1002
1222
|
export class {{ name }}Service {
|
|
1003
1223
|
{{#hasRepo}}@Inject({{ repository }}) declare {{ repositoryCamel }}: {{ repository }};{{/hasRepo}}
|
|
@@ -1029,6 +1249,52 @@ export class {{ name }}Service {
|
|
|
1029
1249
|
}
|
|
1030
1250
|
`.trimStart();
|
|
1031
1251
|
|
|
1252
|
+
// packages/cli/src/templates/session/session.ts
|
|
1253
|
+
var session_default = `
|
|
1254
|
+
import { Inject, Injectable } from '@nexusts/core';
|
|
1255
|
+
import { SessionService } from '@nexusts/session';
|
|
1256
|
+
import type { SessionRecord } from '@nexusts/session';
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* {{ name }} session helper \u2014 generated by \`nx make:session {{ name }}\`.
|
|
1260
|
+
*
|
|
1261
|
+
* Wraps SessionService with typed accessors for the {{ name }} session's
|
|
1262
|
+
* data. Use from controllers / services.
|
|
1263
|
+
*/
|
|
1264
|
+
@Injectable()
|
|
1265
|
+
export class {{ name }}Session {
|
|
1266
|
+
@Inject(SessionService.TOKEN) declare private readonly sessions: SessionService;
|
|
1267
|
+
|
|
1268
|
+
/** Read the current session record (or null). */
|
|
1269
|
+
async getCurrent(sessionId: string | null | undefined) {
|
|
1270
|
+
if (!sessionId) return null;
|
|
1271
|
+
return this.sessions.read(sessionId);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/** Patch the {{ name }} session's data. */
|
|
1275
|
+
async update(sessionId: string, patch: {{ name }}DataPatch) {
|
|
1276
|
+
return this.sessions.update(sessionId, { dataPatch: patch });
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/** Destroy the session (logout-everywhere equivalent). */
|
|
1280
|
+
async destroy(sessionId: string) {
|
|
1281
|
+
return this.sessions.destroy(sessionId, 'logout');
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* Typed payload for the {{ name }} session.
|
|
1287
|
+
*
|
|
1288
|
+
* TODO: define the fields below to match your feature.
|
|
1289
|
+
*/
|
|
1290
|
+
export interface {{ name }}Data {
|
|
1291
|
+
// userId?: string;
|
|
1292
|
+
// createdAt?: string;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
export type {{ name }}DataPatch = Partial<{{ name }}Data>;
|
|
1296
|
+
`.trimStart();
|
|
1297
|
+
|
|
1032
1298
|
// packages/cli/src/templates/validator/validator.ts
|
|
1033
1299
|
var validator_default = `
|
|
1034
1300
|
import { z } from 'zod';
|
|
@@ -1053,7 +1319,18 @@ var templates = {
|
|
|
1053
1319
|
adonis: adonis_default,
|
|
1054
1320
|
functional: functional_default
|
|
1055
1321
|
},
|
|
1322
|
+
auth: {
|
|
1323
|
+
instance: auth_instance_default,
|
|
1324
|
+
"env.example": env_example_default
|
|
1325
|
+
},
|
|
1326
|
+
listener: listener_default,
|
|
1327
|
+
queue: {
|
|
1328
|
+
worker: worker_default,
|
|
1329
|
+
job: job_default
|
|
1330
|
+
},
|
|
1331
|
+
schedule: task_default,
|
|
1056
1332
|
service: service_default,
|
|
1333
|
+
session: session_default,
|
|
1057
1334
|
repository: {
|
|
1058
1335
|
drizzle: repository_default,
|
|
1059
1336
|
kysely: kysely_repository_default
|
|
@@ -1445,6 +1722,22 @@ bunx nx make:crud Post
|
|
|
1445
1722
|
`);
|
|
1446
1723
|
return created;
|
|
1447
1724
|
}
|
|
1725
|
+
// packages/cli/src/core/naming.ts
|
|
1726
|
+
function inferTableName(input) {
|
|
1727
|
+
if (!input)
|
|
1728
|
+
return "table";
|
|
1729
|
+
const m = /^create_(\w+)_table$/.exec(input);
|
|
1730
|
+
if (m)
|
|
1731
|
+
return m[1] ?? "table";
|
|
1732
|
+
const m2 = /^(?:add|remove|drop|alter)_(\w+)_to_(\w+)$/.exec(input);
|
|
1733
|
+
if (m2)
|
|
1734
|
+
return m2[2] ?? "table";
|
|
1735
|
+
return `${input.toLowerCase().replace(/s$/, "")}s`;
|
|
1736
|
+
}
|
|
1737
|
+
function formatTimestamp(d) {
|
|
1738
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1739
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}` + `_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
1740
|
+
}
|
|
1448
1741
|
// packages/cli/src/commands/info.ts
|
|
1449
1742
|
var infoCommand = {
|
|
1450
1743
|
name: "info",
|
|
@@ -1506,32 +1799,6 @@ var info_default = infoCommand;
|
|
|
1506
1799
|
// packages/cli/src/commands/init.ts
|
|
1507
1800
|
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1508
1801
|
import { resolve as resolve6 } from "path";
|
|
1509
|
-
var VALID_OPTIONS = {
|
|
1510
|
-
style: ["nest", "adonis", "functional"],
|
|
1511
|
-
view: ["rendu", "edge", "eta", "inertia", "none"],
|
|
1512
|
-
orm: ["drizzle", "kysely", "none"],
|
|
1513
|
-
db: ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"],
|
|
1514
|
-
frontend: ["react", "vue", "svelte", "solid"]
|
|
1515
|
-
};
|
|
1516
|
-
async function resolveOpt(flags, key, valid, defaultVal, interactive) {
|
|
1517
|
-
const flagVal = flags[key];
|
|
1518
|
-
if (flagVal) {
|
|
1519
|
-
if (valid.includes(flagVal))
|
|
1520
|
-
return flagVal;
|
|
1521
|
-
if (!interactive) {
|
|
1522
|
-
logger.error(`Invalid --${key} "${flagVal}". Valid values: ${valid.join(", ")}`);
|
|
1523
|
-
process.exit(1);
|
|
1524
|
-
}
|
|
1525
|
-
logger.warn(`"${flagVal}" is not valid for --${key}. Please choose from the list.`);
|
|
1526
|
-
}
|
|
1527
|
-
const label = key === "style" ? "Routing style" : key === "view" ? "View engine" : key === "orm" ? "ORM driver" : key === "db" ? "Database driver" : "Inertia frontend";
|
|
1528
|
-
for (;; ) {
|
|
1529
|
-
const answer = await select(label, [...valid], { default: defaultVal });
|
|
1530
|
-
if (valid.includes(answer))
|
|
1531
|
-
return answer;
|
|
1532
|
-
logger.warn(`"${answer}" is not valid. Please choose from: ${valid.join(", ")}`);
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
1802
|
var initCommand = {
|
|
1536
1803
|
name: "init",
|
|
1537
1804
|
aliases: ["i"],
|
|
@@ -1561,11 +1828,11 @@ var initCommand = {
|
|
|
1561
1828
|
const interactive = flagBool(ctx.flags, "interaction", true);
|
|
1562
1829
|
const force = flagBool(ctx.flags, "force", false);
|
|
1563
1830
|
const target = resolve6(ctx.cwd, ctx.flags.target ?? ".");
|
|
1564
|
-
const routing = await
|
|
1565
|
-
const view = await
|
|
1566
|
-
const orm = await
|
|
1567
|
-
const db = await
|
|
1568
|
-
const frontend = await
|
|
1831
|
+
const routing = await resolveProjectOption(ctx.flags, "style", VALID_PROJECT_OPTIONS.style, "nest", interactive);
|
|
1832
|
+
const view = await resolveProjectOption(ctx.flags, "view", VALID_PROJECT_OPTIONS.view, "rendu", interactive);
|
|
1833
|
+
const orm = await resolveProjectOption(ctx.flags, "orm", VALID_PROJECT_OPTIONS.orm, "drizzle", interactive);
|
|
1834
|
+
const db = await resolveProjectOption(ctx.flags, "db", VALID_PROJECT_OPTIONS.db, "bun-sqlite", interactive);
|
|
1835
|
+
const frontend = await resolveProjectOption(ctx.flags, "frontend", VALID_PROJECT_OPTIONS.frontend, "react", interactive);
|
|
1569
1836
|
const ssr = !flagBool(ctx.flags, "no-ssr", false);
|
|
1570
1837
|
const name = target.split("/").pop() ?? "nexus-app";
|
|
1571
1838
|
const dbUrl = db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "";
|
|
@@ -1751,51 +2018,6 @@ var init_default = initCommand;
|
|
|
1751
2018
|
|
|
1752
2019
|
// packages/cli/src/commands/make-auth.ts
|
|
1753
2020
|
import { resolve as resolve7 } from "path";
|
|
1754
|
-
var AUTH_INSTANCE_TEMPLATE = `/**
|
|
1755
|
-
* Better-auth instance \u2014 generated by \`nx make:auth\`.
|
|
1756
|
-
*
|
|
1757
|
-
* Edit \`nx.config.ts\` (\`auth\` section) instead of this file when possible.
|
|
1758
|
-
*/
|
|
1759
|
-
import { createAuth } from '@nexusts/auth';
|
|
1760
|
-
|
|
1761
|
-
export const auth = createAuth({
|
|
1762
|
-
{{^jwt}}
|
|
1763
|
-
jwt: { enabled: false },
|
|
1764
|
-
{{/jwt}}
|
|
1765
|
-
{{#jwt}}
|
|
1766
|
-
jwt: { enabled: true },
|
|
1767
|
-
{{/jwt}}
|
|
1768
|
-
{{#passkey}}
|
|
1769
|
-
passkey: {
|
|
1770
|
-
enabled: true,
|
|
1771
|
-
rpName: '{{ passkeyRpName }}',
|
|
1772
|
-
rpId: '{{ passkeyRpId }}',
|
|
1773
|
-
origin: '{{ passkeyOrigin }}',
|
|
1774
|
-
},
|
|
1775
|
-
{{/passkey}}
|
|
1776
|
-
{{#providers}}
|
|
1777
|
-
socialProviders: {
|
|
1778
|
-
{{#entries}}
|
|
1779
|
-
{{ name }}: {
|
|
1780
|
-
clientId: process.env.{{ envVar }}_CLIENT_ID!,
|
|
1781
|
-
clientSecret: process.env.{{ envVar }}_CLIENT_SECRET!,
|
|
1782
|
-
},
|
|
1783
|
-
{{/entries}}
|
|
1784
|
-
},
|
|
1785
|
-
{{/providers}}
|
|
1786
|
-
});
|
|
1787
|
-
`;
|
|
1788
|
-
var ENV_EXAMPLE_TEMPLATE = `# Better Auth \u2014 generated by \`nx make:auth\`.
|
|
1789
|
-
# Generate a secret with: openssl rand -base64 32
|
|
1790
|
-
BETTER_AUTH_SECRET=
|
|
1791
|
-
BETTER_AUTH_URL=http://localhost:3000
|
|
1792
|
-
{{#providers}}
|
|
1793
|
-
{{#entries}}
|
|
1794
|
-
{{ envVar }}_CLIENT_ID=
|
|
1795
|
-
{{ envVar }}_CLIENT_SECRET=
|
|
1796
|
-
{{/entries}}
|
|
1797
|
-
{{/providers}}
|
|
1798
|
-
`;
|
|
1799
2021
|
var MODULE_UPDATE_HINT = `import { AuthModule } from '@nexusts/auth';
|
|
1800
2022
|
// In your AppModule.imports:
|
|
1801
2023
|
imports: [AuthModule.forRoot({ /* ... */ })],
|
|
@@ -1855,7 +2077,7 @@ var makeAuthCommand = {
|
|
|
1855
2077
|
envVar: known?.env ?? p.toUpperCase()
|
|
1856
2078
|
};
|
|
1857
2079
|
});
|
|
1858
|
-
const authCode = render(
|
|
2080
|
+
const authCode = render(templates.auth.instance, {
|
|
1859
2081
|
jwt: jwtEnabled,
|
|
1860
2082
|
passkey: passkeyEnabled,
|
|
1861
2083
|
providers: providers.length > 0,
|
|
@@ -1870,7 +2092,7 @@ var makeAuthCommand = {
|
|
|
1870
2092
|
} else {
|
|
1871
2093
|
logger.warn(`skipped (exists): ${authOut}`);
|
|
1872
2094
|
}
|
|
1873
|
-
const envCode = render(
|
|
2095
|
+
const envCode = render(templates.auth["env.example"], {
|
|
1874
2096
|
providers: providers.length > 0,
|
|
1875
2097
|
entries
|
|
1876
2098
|
});
|
|
@@ -1944,8 +2166,9 @@ var makeControllerCommand = {
|
|
|
1944
2166
|
snake: variants.snake,
|
|
1945
2167
|
pascal: variants.pascal,
|
|
1946
2168
|
service: serviceName,
|
|
1947
|
-
serviceCamel
|
|
1948
|
-
|
|
2169
|
+
serviceCamel,
|
|
2170
|
+
hasService: !skipService
|
|
2171
|
+
});
|
|
1949
2172
|
const out = resolve8(ctx.cwd, ctx.config.paths.controllers, `${variants.kebab}.controller.ts`);
|
|
1950
2173
|
const ok = writeFile(out, code, { skipIfExists: false });
|
|
1951
2174
|
if (!ok) {
|
|
@@ -2078,6 +2301,96 @@ function mapDrizzleType(dialect, type) {
|
|
|
2078
2301
|
}
|
|
2079
2302
|
return "text";
|
|
2080
2303
|
}
|
|
2304
|
+
function mapSqlType(t, dialect) {
|
|
2305
|
+
const type = t.toLowerCase();
|
|
2306
|
+
switch (type) {
|
|
2307
|
+
case "text":
|
|
2308
|
+
case "string":
|
|
2309
|
+
case "varchar":
|
|
2310
|
+
return dialect === "mysql" ? "VARCHAR(255)" : "TEXT";
|
|
2311
|
+
case "int":
|
|
2312
|
+
case "integer":
|
|
2313
|
+
return dialect === "postgres" ? "INTEGER" : dialect === "mysql" ? "INT" : "INTEGER";
|
|
2314
|
+
case "bigint":
|
|
2315
|
+
case "bigintunsigned":
|
|
2316
|
+
return dialect === "postgres" ? "BIGINT" : "BIGINT UNSIGNED";
|
|
2317
|
+
case "serial":
|
|
2318
|
+
return dialect === "postgres" ? "SERIAL" : "INTEGER AUTO_INCREMENT";
|
|
2319
|
+
case "bool":
|
|
2320
|
+
case "boolean":
|
|
2321
|
+
return "BOOLEAN";
|
|
2322
|
+
case "float":
|
|
2323
|
+
case "number":
|
|
2324
|
+
case "real":
|
|
2325
|
+
case "double":
|
|
2326
|
+
return dialect === "mysql" ? "DOUBLE" : "REAL";
|
|
2327
|
+
case "datetime":
|
|
2328
|
+
case "timestamp":
|
|
2329
|
+
return dialect === "postgres" ? "TIMESTAMP" : dialect === "mysql" ? "DATETIME" : "INTEGER";
|
|
2330
|
+
case "date":
|
|
2331
|
+
return dialect === "mysql" ? "DATE" : "TEXT";
|
|
2332
|
+
case "json":
|
|
2333
|
+
return dialect === "postgres" ? "JSONB" : dialect === "mysql" ? "JSON" : "TEXT";
|
|
2334
|
+
case "jsonb":
|
|
2335
|
+
return dialect === "postgres" ? "JSONB" : "TEXT";
|
|
2336
|
+
default:
|
|
2337
|
+
return "TEXT";
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
function mapKyselyType(type) {
|
|
2341
|
+
switch (type.toLowerCase()) {
|
|
2342
|
+
case "text":
|
|
2343
|
+
case "string":
|
|
2344
|
+
case "varchar":
|
|
2345
|
+
return "text";
|
|
2346
|
+
case "int":
|
|
2347
|
+
case "integer":
|
|
2348
|
+
return "integer";
|
|
2349
|
+
case "bigint":
|
|
2350
|
+
return "bigint";
|
|
2351
|
+
case "bool":
|
|
2352
|
+
case "boolean":
|
|
2353
|
+
return "boolean";
|
|
2354
|
+
case "float":
|
|
2355
|
+
case "number":
|
|
2356
|
+
case "real":
|
|
2357
|
+
case "double":
|
|
2358
|
+
return "real";
|
|
2359
|
+
case "datetime":
|
|
2360
|
+
case "timestamp":
|
|
2361
|
+
case "date":
|
|
2362
|
+
case "json":
|
|
2363
|
+
case "jsonb":
|
|
2364
|
+
return "text";
|
|
2365
|
+
default:
|
|
2366
|
+
return "text";
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
function renderSqlColumns(cols, dialect) {
|
|
2370
|
+
return cols.map(([name, type]) => {
|
|
2371
|
+
const sqlType = mapSqlType(type, dialect);
|
|
2372
|
+
const notNull = /NOT NULL/i.test(sqlType) ? "" : " NOT NULL";
|
|
2373
|
+
return ` ${name} ${sqlType}${notNull},`;
|
|
2374
|
+
}).join(`
|
|
2375
|
+
`);
|
|
2376
|
+
}
|
|
2377
|
+
function renderKyselyColumns(cols) {
|
|
2378
|
+
return cols.map(([name, type]) => {
|
|
2379
|
+
const kyselyType = mapKyselyType(type);
|
|
2380
|
+
return ` .addColumn('${name}', '${kyselyType}', (col) => col.notNull())`;
|
|
2381
|
+
}).join(`
|
|
2382
|
+
`);
|
|
2383
|
+
}
|
|
2384
|
+
function renderDrizzleColumns(cols, dialect) {
|
|
2385
|
+
return cols.map(([name, type]) => {
|
|
2386
|
+
const helper = mapDrizzleType(dialect, type);
|
|
2387
|
+
return ` ${name}: ${helper}('${name}'),`;
|
|
2388
|
+
}).join(`
|
|
2389
|
+
`);
|
|
2390
|
+
}
|
|
2391
|
+
function isValidDialect(d) {
|
|
2392
|
+
return ["postgres", "mysql", "sqlite", "bun-sqlite", "d1"].includes(d);
|
|
2393
|
+
}
|
|
2081
2394
|
|
|
2082
2395
|
// packages/cli/src/commands/make-crud.ts
|
|
2083
2396
|
var makeCrudCommand = {
|
|
@@ -2173,7 +2486,7 @@ var makeCrudCommand = {
|
|
|
2173
2486
|
kebab: variants.kebab,
|
|
2174
2487
|
snake: variants.snake,
|
|
2175
2488
|
tableName,
|
|
2176
|
-
columns:
|
|
2489
|
+
columns: renderDrizzleColumns2(dialect)
|
|
2177
2490
|
});
|
|
2178
2491
|
} else {
|
|
2179
2492
|
const tpl = templates.model[orm];
|
|
@@ -2281,7 +2594,7 @@ function renderDefaultColumns(orm) {
|
|
|
2281
2594
|
}
|
|
2282
2595
|
return " title text,";
|
|
2283
2596
|
}
|
|
2284
|
-
function
|
|
2597
|
+
function renderDrizzleColumns2(dialect) {
|
|
2285
2598
|
const helper = mapDrizzleType(dialect, "text");
|
|
2286
2599
|
return ` title: ${helper}('title').notNull(),`;
|
|
2287
2600
|
}
|
|
@@ -2289,39 +2602,6 @@ var make_crud_default = makeCrudCommand;
|
|
|
2289
2602
|
|
|
2290
2603
|
// packages/cli/src/commands/make-listener.ts
|
|
2291
2604
|
import { resolve as resolve10 } from "path";
|
|
2292
|
-
var LISTENER_TEMPLATE = `
|
|
2293
|
-
import { Inject, Injectable } from '@nexusts/core';
|
|
2294
|
-
import { EventService, OnEvent } from '@nexusts/events';
|
|
2295
|
-
|
|
2296
|
-
/**
|
|
2297
|
-
* {{ name }} listener \u2014 generated by \`nx make:listener {{ name }}\`.
|
|
2298
|
-
*
|
|
2299
|
-
* Register handlers below with \`@OnEvent(pattern)\`. Pair with
|
|
2300
|
-
* \`scanForListeners(this, eventService)\` at boot.
|
|
2301
|
-
*/
|
|
2302
|
-
@Injectable()
|
|
2303
|
-
export class {{ name }}Listener {
|
|
2304
|
-
constructor(@Inject(EventService.TOKEN) private readonly events: EventService) {}
|
|
2305
|
-
|
|
2306
|
-
// TODO: add @OnEvent('your.event') handlers below.
|
|
2307
|
-
|
|
2308
|
-
// @OnEvent('user.created')
|
|
2309
|
-
// async onUserCreated(payload: { userId: string; email: string }) {
|
|
2310
|
-
// this.events; // unused \u2014 remove if you don't need it
|
|
2311
|
-
// }
|
|
2312
|
-
}
|
|
2313
|
-
|
|
2314
|
-
import { scanForListeners } from '@nexusts/events';
|
|
2315
|
-
|
|
2316
|
-
/**
|
|
2317
|
-
* Bootstrap helper \u2014 call this from your main.ts (or wherever you
|
|
2318
|
-
* wire up the application) to register every \`@OnEvent\` handler
|
|
2319
|
-
* on this listener class.
|
|
2320
|
-
*/
|
|
2321
|
-
export async function register{{ name }}(listener: {{ name }}Listener, events: EventService) {
|
|
2322
|
-
return scanForListeners(listener, events);
|
|
2323
|
-
}
|
|
2324
|
-
`.trimStart();
|
|
2325
2605
|
var makeListenerCommand = {
|
|
2326
2606
|
name: "make:listener",
|
|
2327
2607
|
aliases: ["ml", "make-listener"],
|
|
@@ -2335,7 +2615,7 @@ var makeListenerCommand = {
|
|
|
2335
2615
|
return 1;
|
|
2336
2616
|
}
|
|
2337
2617
|
const variants = nameVariants(name);
|
|
2338
|
-
const code = render(
|
|
2618
|
+
const code = render(templates.listener, {
|
|
2339
2619
|
name: variants.pascal,
|
|
2340
2620
|
kebab: variants.kebab
|
|
2341
2621
|
});
|
|
@@ -2405,192 +2685,87 @@ var makeMigrationCommand = {
|
|
|
2405
2685
|
{
|
|
2406
2686
|
name: "orm",
|
|
2407
2687
|
description: "Override ORM driver (drizzle|kysely|none)"
|
|
2408
|
-
},
|
|
2409
|
-
{
|
|
2410
|
-
name: "dialect",
|
|
2411
|
-
description: "Drizzle dialect (postgres|mysql|sqlite|bun-sqlite|d1). Default: bun-sqlite"
|
|
2412
|
-
}
|
|
2413
|
-
],
|
|
2414
|
-
async run(ctx) {
|
|
2415
|
-
const name = ctx.positional[0];
|
|
2416
|
-
if (!name) {
|
|
2417
|
-
logger.error("Usage: nx make:migration <Name> [--dialect ...]");
|
|
2418
|
-
return 1;
|
|
2419
|
-
}
|
|
2420
|
-
const orm = ctx.flags.orm ?? ctx.config.orm;
|
|
2421
|
-
const dialect = ctx.flags.dialect ?? ctx.config.dialect ?? "bun-sqlite";
|
|
2422
|
-
const isDrizzle = orm === "drizzle";
|
|
2423
|
-
const isKysely = orm === "kysely";
|
|
2424
|
-
const useGenericSql = orm === "none";
|
|
2425
|
-
const variants = nameVariants(name);
|
|
2426
|
-
const tableName = inferTableName(name);
|
|
2427
|
-
const colsFlag = ctx.flags.columns;
|
|
2428
|
-
const cols = parseColumns(colsFlag ?? "title:text");
|
|
2429
|
-
const drizzleColumns =
|
|
2430
|
-
const sqlColumns = renderSqlColumns(cols, dialect);
|
|
2431
|
-
let code;
|
|
2432
|
-
let extension;
|
|
2433
|
-
if (isKysely) {
|
|
2434
|
-
const tpl = templates.migration.kysely;
|
|
2435
|
-
code = render(tpl, {
|
|
2436
|
-
name: variants.pascal,
|
|
2437
|
-
snake: variants.snake,
|
|
2438
|
-
tableName,
|
|
2439
|
-
columns: renderKyselyColumns(cols),
|
|
2440
|
-
timestamp: formatTimestamp(new Date)
|
|
2441
|
-
});
|
|
2442
|
-
extension = "ts";
|
|
2443
|
-
} else if (isDrizzle) {
|
|
2444
|
-
if (!isValidDialect(dialect)) {
|
|
2445
|
-
logger.error(`Unsupported drizzle dialect: ${dialect}. Allowed: postgres, mysql, sqlite, bun-sqlite, d1.`);
|
|
2446
|
-
return 1;
|
|
2447
|
-
}
|
|
2448
|
-
code = renderDrizzleDialect(dialect);
|
|
2449
|
-
code = render(code, {
|
|
2450
|
-
name: variants.pascal,
|
|
2451
|
-
snake: variants.snake,
|
|
2452
|
-
tableName,
|
|
2453
|
-
columns: drizzleColumns,
|
|
2454
|
-
timestamp: formatTimestamp(new Date)
|
|
2455
|
-
});
|
|
2456
|
-
extension = "ts";
|
|
2457
|
-
} else if (useGenericSql) {
|
|
2458
|
-
const tpl = templates.migration.sql;
|
|
2459
|
-
code = render(tpl, {
|
|
2460
|
-
name: variants.pascal,
|
|
2461
|
-
snake: variants.snake,
|
|
2462
|
-
tableName,
|
|
2463
|
-
columns: sqlColumns,
|
|
2464
|
-
timestamp: formatTimestamp(new Date)
|
|
2465
|
-
});
|
|
2466
|
-
extension = "sql";
|
|
2467
|
-
} else {
|
|
2468
|
-
logger.error(`Unsupported ORM for migration: ${orm}. Allowed: drizzle, kysely, none.`);
|
|
2469
|
-
return 1;
|
|
2470
|
-
}
|
|
2471
|
-
const filename = `${formatTimestamp(new Date)}_${variants.snake}.${extension}`;
|
|
2472
|
-
const out = resolve12(ctx.cwd, ctx.config.paths.migrations, filename);
|
|
2473
|
-
writeFile(out, code);
|
|
2474
|
-
logger.success(`created ${out}`);
|
|
2475
|
-
if (isDrizzle || isKysely) {
|
|
2476
|
-
logger.finger(`run \`nx db:migrate\` to apply pending migrations.`);
|
|
2477
|
-
} else {
|
|
2478
|
-
logger.finger(`run \`bun nx db:migrate\` or your migration tool.`);
|
|
2479
|
-
}
|
|
2480
|
-
return 0;
|
|
2481
|
-
}
|
|
2482
|
-
};
|
|
2483
|
-
function isValidDialect(d) {
|
|
2484
|
-
return ["postgres", "mysql", "sqlite", "bun-sqlite", "d1"].includes(d);
|
|
2485
|
-
}
|
|
2486
|
-
function inferTableName(input) {
|
|
2487
|
-
const m = /^create_(\w+)_table$/.exec(input);
|
|
2488
|
-
if (m)
|
|
2489
|
-
return m[1] ?? "";
|
|
2490
|
-
const m2 = /^(?:add|remove|drop|alter)_(\w+)_to_(\w+)$/.exec(input);
|
|
2491
|
-
if (m2)
|
|
2492
|
-
return m2[2] ?? "";
|
|
2493
|
-
return `${input.toLowerCase().replace(/s$/, "")}s`;
|
|
2494
|
-
}
|
|
2495
|
-
function parseColumns(input) {
|
|
2496
|
-
const list = Array.isArray(input) ? input : input.split(",");
|
|
2497
|
-
return list.map((s) => s.trim()).filter(Boolean).map((c2) => {
|
|
2498
|
-
const [name, type = "text"] = c2.split(":");
|
|
2499
|
-
return [name, type];
|
|
2500
|
-
});
|
|
2501
|
-
}
|
|
2502
|
-
function renderSqlColumns(cols, dialect) {
|
|
2503
|
-
return cols.map(([name, type]) => {
|
|
2504
|
-
const sqlType = mapSqlType(type, dialect);
|
|
2505
|
-
const notNull = /NOT NULL/i.test(sqlType) ? "" : " NOT NULL";
|
|
2506
|
-
return ` ${name} ${sqlType}${notNull},`;
|
|
2507
|
-
}).join(`
|
|
2508
|
-
`);
|
|
2509
|
-
}
|
|
2510
|
-
function renderKyselyColumns(cols) {
|
|
2511
|
-
return cols.map(([name, type]) => {
|
|
2512
|
-
const kyselyType = mapKyselyType(type);
|
|
2513
|
-
return ` .addColumn('${name}', '${kyselyType}', (col) => col.notNull())`;
|
|
2514
|
-
}).join(`
|
|
2515
|
-
`);
|
|
2516
|
-
}
|
|
2517
|
-
function mapKyselyType(type) {
|
|
2518
|
-
switch (type.toLowerCase()) {
|
|
2519
|
-
case "text":
|
|
2520
|
-
case "string":
|
|
2521
|
-
case "varchar":
|
|
2522
|
-
return "text";
|
|
2523
|
-
case "int":
|
|
2524
|
-
case "integer":
|
|
2525
|
-
return "integer";
|
|
2526
|
-
case "bigint":
|
|
2527
|
-
return "bigint";
|
|
2528
|
-
case "bool":
|
|
2529
|
-
case "boolean":
|
|
2530
|
-
return "boolean";
|
|
2531
|
-
case "float":
|
|
2532
|
-
case "number":
|
|
2533
|
-
case "real":
|
|
2534
|
-
case "double":
|
|
2535
|
-
return "real";
|
|
2536
|
-
case "datetime":
|
|
2537
|
-
case "timestamp":
|
|
2538
|
-
return "text";
|
|
2539
|
-
case "date":
|
|
2540
|
-
return "text";
|
|
2541
|
-
case "json":
|
|
2542
|
-
case "jsonb":
|
|
2543
|
-
return "text";
|
|
2544
|
-
default:
|
|
2545
|
-
return "text";
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
function renderDrizzleColumns2(cols, dialect) {
|
|
2549
|
-
return cols.map(([name, type]) => {
|
|
2550
|
-
const helper = mapDrizzleType(dialect, type);
|
|
2551
|
-
return ` ${name}: ${helper}('${name}'),`;
|
|
2552
|
-
}).join(`
|
|
2553
|
-
`);
|
|
2554
|
-
}
|
|
2555
|
-
function mapSqlType(t, dialect) {
|
|
2556
|
-
const type = t.toLowerCase();
|
|
2557
|
-
switch (type) {
|
|
2558
|
-
case "text":
|
|
2559
|
-
case "string":
|
|
2560
|
-
case "varchar":
|
|
2561
|
-
return dialect === "mysql" ? "VARCHAR(255)" : "TEXT";
|
|
2562
|
-
case "int":
|
|
2563
|
-
case "integer":
|
|
2564
|
-
return dialect === "postgres" ? "INTEGER" : dialect === "mysql" ? "INT" : "INTEGER";
|
|
2565
|
-
case "bigint":
|
|
2566
|
-
case "bigintunsigned":
|
|
2567
|
-
return dialect === "postgres" ? "BIGINT" : "BIGINT UNSIGNED";
|
|
2568
|
-
case "serial":
|
|
2569
|
-
return dialect === "postgres" ? "SERIAL" : "INTEGER AUTO_INCREMENT";
|
|
2570
|
-
case "bool":
|
|
2571
|
-
case "boolean":
|
|
2572
|
-
return dialect === "mysql" ? "BOOLEAN" : "BOOLEAN";
|
|
2573
|
-
case "float":
|
|
2574
|
-
case "number":
|
|
2575
|
-
case "real":
|
|
2576
|
-
case "double":
|
|
2577
|
-
return dialect === "mysql" ? "DOUBLE" : "REAL";
|
|
2578
|
-
case "datetime":
|
|
2579
|
-
case "timestamp":
|
|
2580
|
-
return dialect === "postgres" ? "TIMESTAMP" : dialect === "mysql" ? "DATETIME" : "INTEGER";
|
|
2581
|
-
case "date":
|
|
2582
|
-
return dialect === "mysql" ? "DATE" : "TEXT";
|
|
2583
|
-
case "json":
|
|
2584
|
-
return dialect === "postgres" ? "JSONB" : dialect === "mysql" ? "JSON" : "TEXT";
|
|
2585
|
-
case "jsonb":
|
|
2586
|
-
return dialect === "postgres" ? "JSONB" : "TEXT";
|
|
2587
|
-
default:
|
|
2588
|
-
return "TEXT";
|
|
2688
|
+
},
|
|
2689
|
+
{
|
|
2690
|
+
name: "dialect",
|
|
2691
|
+
description: "Drizzle dialect (postgres|mysql|sqlite|bun-sqlite|d1). Default: bun-sqlite"
|
|
2692
|
+
}
|
|
2693
|
+
],
|
|
2694
|
+
async run(ctx) {
|
|
2695
|
+
const name = ctx.positional[0];
|
|
2696
|
+
if (!name) {
|
|
2697
|
+
logger.error("Usage: nx make:migration <Name> [--dialect ...]");
|
|
2698
|
+
return 1;
|
|
2699
|
+
}
|
|
2700
|
+
const orm = ctx.flags.orm ?? ctx.config.orm;
|
|
2701
|
+
const dialect = ctx.flags.dialect ?? ctx.config.dialect ?? "bun-sqlite";
|
|
2702
|
+
const isDrizzle = orm === "drizzle";
|
|
2703
|
+
const isKysely = orm === "kysely";
|
|
2704
|
+
const useGenericSql = orm === "none";
|
|
2705
|
+
const variants = nameVariants(name);
|
|
2706
|
+
const tableName = inferTableName(name);
|
|
2707
|
+
const colsFlag = ctx.flags.columns;
|
|
2708
|
+
const cols = parseColumns(colsFlag ?? "title:text");
|
|
2709
|
+
const drizzleColumns = renderDrizzleColumns(cols, dialect);
|
|
2710
|
+
const sqlColumns = renderSqlColumns(cols, dialect);
|
|
2711
|
+
let code;
|
|
2712
|
+
let extension;
|
|
2713
|
+
if (isKysely) {
|
|
2714
|
+
const tpl = templates.migration.kysely;
|
|
2715
|
+
code = render(tpl, {
|
|
2716
|
+
name: variants.pascal,
|
|
2717
|
+
snake: variants.snake,
|
|
2718
|
+
tableName,
|
|
2719
|
+
columns: renderKyselyColumns(cols),
|
|
2720
|
+
timestamp: formatTimestamp(new Date)
|
|
2721
|
+
});
|
|
2722
|
+
extension = "ts";
|
|
2723
|
+
} else if (isDrizzle) {
|
|
2724
|
+
if (!isValidDialect(dialect)) {
|
|
2725
|
+
logger.error(`Unsupported drizzle dialect: ${dialect}. Allowed: postgres, mysql, sqlite, bun-sqlite, d1.`);
|
|
2726
|
+
return 1;
|
|
2727
|
+
}
|
|
2728
|
+
code = renderDrizzleDialect(dialect);
|
|
2729
|
+
code = render(code, {
|
|
2730
|
+
name: variants.pascal,
|
|
2731
|
+
snake: variants.snake,
|
|
2732
|
+
tableName,
|
|
2733
|
+
columns: drizzleColumns,
|
|
2734
|
+
timestamp: formatTimestamp(new Date)
|
|
2735
|
+
});
|
|
2736
|
+
extension = "ts";
|
|
2737
|
+
} else if (useGenericSql) {
|
|
2738
|
+
const tpl = templates.migration.sql;
|
|
2739
|
+
code = render(tpl, {
|
|
2740
|
+
name: variants.pascal,
|
|
2741
|
+
snake: variants.snake,
|
|
2742
|
+
tableName,
|
|
2743
|
+
columns: sqlColumns,
|
|
2744
|
+
timestamp: formatTimestamp(new Date)
|
|
2745
|
+
});
|
|
2746
|
+
extension = "sql";
|
|
2747
|
+
} else {
|
|
2748
|
+
logger.error(`Unsupported ORM for migration: ${orm}. Allowed: drizzle, kysely, none.`);
|
|
2749
|
+
return 1;
|
|
2750
|
+
}
|
|
2751
|
+
const filename = `${formatTimestamp(new Date)}_${variants.snake}.${extension}`;
|
|
2752
|
+
const out = resolve12(ctx.cwd, ctx.config.paths.migrations, filename);
|
|
2753
|
+
writeFile(out, code);
|
|
2754
|
+
logger.success(`created ${out}`);
|
|
2755
|
+
if (isDrizzle || isKysely) {
|
|
2756
|
+
logger.finger(`run \`nx db:migrate\` to apply pending migrations.`);
|
|
2757
|
+
} else {
|
|
2758
|
+
logger.finger(`run \`bun nx db:migrate\` or your migration tool.`);
|
|
2759
|
+
}
|
|
2760
|
+
return 0;
|
|
2589
2761
|
}
|
|
2590
|
-
}
|
|
2591
|
-
function
|
|
2592
|
-
const
|
|
2593
|
-
return
|
|
2762
|
+
};
|
|
2763
|
+
function parseColumns(input) {
|
|
2764
|
+
const list = Array.isArray(input) ? input : input.split(",");
|
|
2765
|
+
return list.map((s) => s.trim()).filter(Boolean).map((c2) => {
|
|
2766
|
+
const [name, type = "text"] = c2.split(":");
|
|
2767
|
+
return [name, type];
|
|
2768
|
+
});
|
|
2594
2769
|
}
|
|
2595
2770
|
var make_migration_default = makeMigrationCommand;
|
|
2596
2771
|
|
|
@@ -2640,7 +2815,7 @@ var makeModelCommand = {
|
|
|
2640
2815
|
let code;
|
|
2641
2816
|
if (orm === "drizzle") {
|
|
2642
2817
|
const dialect = ctx.flags.dialect ?? ctx.config.dialect ?? "bun-sqlite";
|
|
2643
|
-
if (!
|
|
2818
|
+
if (!isValidDialect(dialect)) {
|
|
2644
2819
|
logger.error(`Unsupported drizzle dialect: ${dialect}. Allowed: postgres, mysql, sqlite, bun-sqlite, d1.`);
|
|
2645
2820
|
return 1;
|
|
2646
2821
|
}
|
|
@@ -2674,9 +2849,6 @@ var makeModelCommand = {
|
|
|
2674
2849
|
return 0;
|
|
2675
2850
|
}
|
|
2676
2851
|
};
|
|
2677
|
-
function isValidDialect2(d) {
|
|
2678
|
-
return ["postgres", "mysql", "sqlite", "bun-sqlite", "d1"].includes(d);
|
|
2679
|
-
}
|
|
2680
2852
|
function renderColumns(cols, orm, dialect) {
|
|
2681
2853
|
const flat = cols.flatMap((c2) => c2.split(",")).map((c2) => c2.trim()).filter(Boolean);
|
|
2682
2854
|
return flat.map((col) => {
|
|
@@ -2749,81 +2921,6 @@ var make_module_default = makeModuleCommand;
|
|
|
2749
2921
|
|
|
2750
2922
|
// packages/cli/src/commands/make-queue.ts
|
|
2751
2923
|
import { resolve as resolve15 } from "path";
|
|
2752
|
-
var WORKER_TEMPLATE = `
|
|
2753
|
-
import { Inject, Injectable } from '@nexusts/core';
|
|
2754
|
-
import { QueueService, OnQueueReady } from '@nexusts/queue';
|
|
2755
|
-
|
|
2756
|
-
/**
|
|
2757
|
-
* {{ name }} worker \u2014 generated by \`nx make:queue {{ name }}\`.
|
|
2758
|
-
*
|
|
2759
|
-
* Registers a handler for the \`{{ name }}\` job name on boot.
|
|
2760
|
-
*/
|
|
2761
|
-
@Injectable()
|
|
2762
|
-
export class {{ name }}Worker {
|
|
2763
|
-
constructor(@Inject(QueueService.TOKEN) private readonly queue: QueueService) {}
|
|
2764
|
-
|
|
2765
|
-
@OnQueueReady()
|
|
2766
|
-
async register(): Promise<void> {
|
|
2767
|
-
await this.queue.process('{{ name }}', async (data, ctx) => {
|
|
2768
|
-
ctx.prefix; // \u2192 "[queue:{{ name }}]"
|
|
2769
|
-
try {
|
|
2770
|
-
await this.handle(data as {{ name }}Data);
|
|
2771
|
-
return { status: 'completed' };
|
|
2772
|
-
} catch (err) {
|
|
2773
|
-
return {
|
|
2774
|
-
status: 'failed',
|
|
2775
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
2776
|
-
willRetry: ctx.attempts < 3,
|
|
2777
|
-
};
|
|
2778
|
-
}
|
|
2779
|
-
});
|
|
2780
|
-
}
|
|
2781
|
-
|
|
2782
|
-
/**
|
|
2783
|
-
* Replace this body with your actual worker logic.
|
|
2784
|
-
*/
|
|
2785
|
-
async handle(data: {{ name }}Data): Promise<void> {
|
|
2786
|
-
// TODO: implement
|
|
2787
|
-
}
|
|
2788
|
-
}
|
|
2789
|
-
|
|
2790
|
-
/**
|
|
2791
|
-
* Typed payload for the \`{{ name }}\` job.
|
|
2792
|
-
*/
|
|
2793
|
-
export interface {{ name }}Data {
|
|
2794
|
-
// TODO: define fields
|
|
2795
|
-
[key: string]: unknown;
|
|
2796
|
-
}
|
|
2797
|
-
`.trimStart();
|
|
2798
|
-
var JOB_HELPER_TEMPLATE = `
|
|
2799
|
-
import { Inject, Injectable } from '@nexusts/core';
|
|
2800
|
-
import { QueueService } from '@nexusts/queue';
|
|
2801
|
-
import type { {{ name }}Data } from './{{ kebab }}.worker.js';
|
|
2802
|
-
|
|
2803
|
-
/**
|
|
2804
|
-
* Helper for enqueuing \`{{ name }}\` jobs from controllers / services.
|
|
2805
|
-
* Generated by \`nx make:queue {{ name }}\`.
|
|
2806
|
-
*/
|
|
2807
|
-
@Injectable()
|
|
2808
|
-
export class {{ name }}Job {
|
|
2809
|
-
constructor(@Inject(QueueService.TOKEN) private readonly queue: QueueService) {}
|
|
2810
|
-
|
|
2811
|
-
/** Enqueue a single {{ name }} job. */
|
|
2812
|
-
async enqueue(data: {{ name }}Data, options?: {
|
|
2813
|
-
delaySeconds?: number;
|
|
2814
|
-
attempts?: number;
|
|
2815
|
-
}) {
|
|
2816
|
-
return this.queue.add('{{ name }}', data, options);
|
|
2817
|
-
}
|
|
2818
|
-
|
|
2819
|
-
/** Enqueue many at once. */
|
|
2820
|
-
async enqueueBatch(items: {{ name }}Data[]) {
|
|
2821
|
-
return this.queue.addBatch(
|
|
2822
|
-
items.map((data) => ({ name: '{{ name }}', data })),
|
|
2823
|
-
);
|
|
2824
|
-
}
|
|
2825
|
-
}
|
|
2826
|
-
`.trimStart();
|
|
2827
2924
|
var WIRE_HINT = `
|
|
2828
2925
|
import { QueueService } from '@nexusts/queue';
|
|
2829
2926
|
import { %%NAME%%Job } from './queue/jobs/%%KEBAB%%.job.js';
|
|
@@ -2872,11 +2969,11 @@ var makeQueueCommand = {
|
|
|
2872
2969
|
}
|
|
2873
2970
|
logger.heading(`Scaffolding ${variants.pascal} (backend=${backend})`);
|
|
2874
2971
|
if (!flagBool(ctx.flags, "no-worker", false)) {
|
|
2875
|
-
const code = render(
|
|
2972
|
+
const code = render(templates.queue.worker, {
|
|
2876
2973
|
name: variants.pascal,
|
|
2877
2974
|
kebab: variants.kebab
|
|
2878
2975
|
});
|
|
2879
|
-
const out = resolve15(ctx.cwd,
|
|
2976
|
+
const out = resolve15(ctx.cwd, `${ctx.config.paths.app}/queue/workers`, `${variants.kebab}.worker.ts`);
|
|
2880
2977
|
if (writeFile(out, code, { skipIfExists: true })) {
|
|
2881
2978
|
logger.success(`created ${out}`);
|
|
2882
2979
|
} else {
|
|
@@ -2884,11 +2981,11 @@ var makeQueueCommand = {
|
|
|
2884
2981
|
}
|
|
2885
2982
|
}
|
|
2886
2983
|
if (!flagBool(ctx.flags, "no-job", false)) {
|
|
2887
|
-
const code = render(
|
|
2984
|
+
const code = render(templates.queue.job, {
|
|
2888
2985
|
name: variants.pascal,
|
|
2889
2986
|
kebab: variants.kebab
|
|
2890
2987
|
});
|
|
2891
|
-
const out = resolve15(ctx.cwd,
|
|
2988
|
+
const out = resolve15(ctx.cwd, `${ctx.config.paths.app}/queue/jobs`, `${variants.kebab}.job.ts`);
|
|
2892
2989
|
if (writeFile(out, code, { skipIfExists: true })) {
|
|
2893
2990
|
logger.success(`created ${out}`);
|
|
2894
2991
|
} else {
|
|
@@ -2957,32 +3054,6 @@ var make_repository_default = makeRepositoryCommand;
|
|
|
2957
3054
|
|
|
2958
3055
|
// packages/cli/src/commands/make-schedule.ts
|
|
2959
3056
|
import { resolve as resolve17 } from "path";
|
|
2960
|
-
var TASK_TEMPLATE = `
|
|
2961
|
-
import { Injectable } from '@nexusts/core';
|
|
2962
|
-
import { Cron, Interval, Timeout } from '@nexusts/schedule';
|
|
2963
|
-
|
|
2964
|
-
/**
|
|
2965
|
-
* {{ name }} task \u2014 generated by \`nx make:schedule {{ name }}\`.
|
|
2966
|
-
*
|
|
2967
|
-
* Mark methods with \`@Cron\`, \`@Interval\`, or \`@Timeout\`.
|
|
2968
|
-
* Auto-detected at boot \u2014 no manual registration needed.
|
|
2969
|
-
*/
|
|
2970
|
-
@Injectable()
|
|
2971
|
-
export class {{ name }}Task {
|
|
2972
|
-
constructor() {}
|
|
2973
|
-
|
|
2974
|
-
// TODO: add @Cron, @Interval, or @Timeout handlers below.
|
|
2975
|
-
|
|
2976
|
-
// @Cron('0 * * * *') // every hour
|
|
2977
|
-
// async hourly() { /* ... */ }
|
|
2978
|
-
|
|
2979
|
-
// @Interval(60_000) // every minute
|
|
2980
|
-
// async tick() { /* ... */ }
|
|
2981
|
-
|
|
2982
|
-
// @Timeout(5_000) // 5s after boot
|
|
2983
|
-
// async startup() { /* ... */ }
|
|
2984
|
-
}
|
|
2985
|
-
`.trimStart();
|
|
2986
3057
|
var makeScheduleCommand = {
|
|
2987
3058
|
name: "make:schedule",
|
|
2988
3059
|
aliases: ["msk", "make-schedule"],
|
|
@@ -2996,11 +3067,11 @@ var makeScheduleCommand = {
|
|
|
2996
3067
|
return 1;
|
|
2997
3068
|
}
|
|
2998
3069
|
const variants = nameVariants(name);
|
|
2999
|
-
const code = render(
|
|
3070
|
+
const code = render(templates.schedule, {
|
|
3000
3071
|
name: variants.pascal,
|
|
3001
3072
|
kebab: variants.kebab
|
|
3002
3073
|
});
|
|
3003
|
-
const out = resolve17(ctx.cwd,
|
|
3074
|
+
const out = resolve17(ctx.cwd, `${ctx.config.paths.app}/schedule/tasks`, `${variants.kebab}.task.ts`);
|
|
3004
3075
|
if (writeFile(out, code, { skipIfExists: true })) {
|
|
3005
3076
|
logger.success(`created ${out}`);
|
|
3006
3077
|
} else {
|
|
@@ -3060,50 +3131,6 @@ var make_service_default = makeServiceCommand;
|
|
|
3060
3131
|
|
|
3061
3132
|
// packages/cli/src/commands/make-session.ts
|
|
3062
3133
|
import { resolve as resolve19 } from "path";
|
|
3063
|
-
var SESSION_TEMPLATE = `
|
|
3064
|
-
import { Inject, Injectable } from '@nexusts/core';
|
|
3065
|
-
import { SessionService } from '@nexusts/session';
|
|
3066
|
-
import type { SessionRecord } from '@nexusts/session';
|
|
3067
|
-
|
|
3068
|
-
/**
|
|
3069
|
-
* {{ name }} session helper \u2014 generated by \`nx make:session {{ name }}\`.
|
|
3070
|
-
*
|
|
3071
|
-
* Wraps SessionService with typed accessors for the {{ name }} session's
|
|
3072
|
-
* data. Use from controllers / services.
|
|
3073
|
-
*/
|
|
3074
|
-
@Injectable()
|
|
3075
|
-
export class {{ name }}Session {
|
|
3076
|
-
constructor(@Inject(SessionService.TOKEN) private readonly sessions: SessionService) {}
|
|
3077
|
-
|
|
3078
|
-
/** Read the current session record (or null). */
|
|
3079
|
-
async getCurrent(sessionId: string | null | undefined) {
|
|
3080
|
-
if (!sessionId) return null;
|
|
3081
|
-
return this.sessions.read(sessionId);
|
|
3082
|
-
}
|
|
3083
|
-
|
|
3084
|
-
/** Patch the {{ name }} session's data. */
|
|
3085
|
-
async update(sessionId: string, patch: {{ name }}DataPatch) {
|
|
3086
|
-
return this.sessions.update(sessionId, { dataPatch: patch });
|
|
3087
|
-
}
|
|
3088
|
-
|
|
3089
|
-
/** Destroy the session (logout-everywhere equivalent). */
|
|
3090
|
-
async destroy(sessionId: string) {
|
|
3091
|
-
return this.sessions.destroy(sessionId, 'logout');
|
|
3092
|
-
}
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
/**
|
|
3096
|
-
* Typed payload for the {{ name }} session.
|
|
3097
|
-
*
|
|
3098
|
-
* TODO: define the fields below to match your feature.
|
|
3099
|
-
*/
|
|
3100
|
-
export interface {{ name }}Data {
|
|
3101
|
-
// userId?: string;
|
|
3102
|
-
// createdAt?: string;
|
|
3103
|
-
}
|
|
3104
|
-
|
|
3105
|
-
export type {{ name }}DataPatch = Partial<{{ name }}Data>;
|
|
3106
|
-
`.trimStart();
|
|
3107
3134
|
var makeSessionCommand = {
|
|
3108
3135
|
name: "make:session",
|
|
3109
3136
|
aliases: ["msess", "make-session"],
|
|
@@ -3117,11 +3144,11 @@ var makeSessionCommand = {
|
|
|
3117
3144
|
return 1;
|
|
3118
3145
|
}
|
|
3119
3146
|
const variants = nameVariants(name);
|
|
3120
|
-
const code = render(
|
|
3147
|
+
const code = render(templates.session, {
|
|
3121
3148
|
name: variants.pascal,
|
|
3122
3149
|
kebab: variants.kebab
|
|
3123
3150
|
});
|
|
3124
|
-
const out = resolve19(ctx.cwd,
|
|
3151
|
+
const out = resolve19(ctx.cwd, `${ctx.config.paths.app}/session/services`, `${variants.kebab}.session.ts`);
|
|
3125
3152
|
if (writeFile(out, code, { skipIfExists: true })) {
|
|
3126
3153
|
logger.success(`created ${out}`);
|
|
3127
3154
|
} else {
|
|
@@ -3468,14 +3495,14 @@ async function runKyselyTemplate(cwd, name, _dialect) {
|
|
|
3468
3495
|
const migrationsDir = join(cwd, "app", "database", "migrations");
|
|
3469
3496
|
mkdirSync5(migrationsDir, { recursive: true });
|
|
3470
3497
|
const variants = nameVariants(name);
|
|
3471
|
-
const timestamp =
|
|
3498
|
+
const timestamp = formatTimestamp(new Date);
|
|
3472
3499
|
const filename = `${timestamp}_${variants.snake}.ts`;
|
|
3473
3500
|
const filepath = join(migrationsDir, filename);
|
|
3474
3501
|
const tpl = templates.migration.kysely;
|
|
3475
3502
|
const code = render(tpl, {
|
|
3476
3503
|
name: variants.pascal,
|
|
3477
3504
|
snake: variants.snake,
|
|
3478
|
-
tableName:
|
|
3505
|
+
tableName: inferTableName(name),
|
|
3479
3506
|
columns: "",
|
|
3480
3507
|
timestamp
|
|
3481
3508
|
});
|
|
@@ -3489,7 +3516,7 @@ async function runSqlTemplate(cwd, name, dialect) {
|
|
|
3489
3516
|
const { join } = await import("path");
|
|
3490
3517
|
const migrationsDir = join(cwd, "app", "database", "migrations");
|
|
3491
3518
|
mkdirSync5(migrationsDir, { recursive: true });
|
|
3492
|
-
const timestamp = Date
|
|
3519
|
+
const timestamp = formatTimestamp(new Date);
|
|
3493
3520
|
const filename = `${timestamp}_${name.replace(/[^a-z0-9_]+/g, "_")}.sql`;
|
|
3494
3521
|
const filepath = join(migrationsDir, filename);
|
|
3495
3522
|
const header = dialect === "postgres" || dialect === "mysql" ? `-- Migration: ${name}
|
|
@@ -3506,19 +3533,6 @@ async function runSqlTemplate(cwd, name, dialect) {
|
|
|
3506
3533
|
logger.info("Edit the SQL file, then run `nx db:migrate` to apply it.");
|
|
3507
3534
|
return 0;
|
|
3508
3535
|
}
|
|
3509
|
-
function inferTableName2(input) {
|
|
3510
|
-
const m = /^create_(\w+)_table$/.exec(input);
|
|
3511
|
-
if (m)
|
|
3512
|
-
return m[1] ?? "";
|
|
3513
|
-
const m2 = /^(?:add|remove|drop|alter)_(\w+)_to_(\w+)$/.exec(input);
|
|
3514
|
-
if (m2)
|
|
3515
|
-
return m2[2] ?? "";
|
|
3516
|
-
return `${input.toLowerCase().replace(/s$/, "")}s`;
|
|
3517
|
-
}
|
|
3518
|
-
function formatTimestamp2(d) {
|
|
3519
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
3520
|
-
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}_${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
|
|
3521
|
-
}
|
|
3522
3536
|
var db_generate_default = dbGenerateCommand;
|
|
3523
3537
|
|
|
3524
3538
|
// packages/cli/src/commands/db-seed.ts
|
|
@@ -3724,32 +3738,6 @@ var db_seed_default = dbSeedCommand;
|
|
|
3724
3738
|
// packages/cli/src/commands/new.ts
|
|
3725
3739
|
import { existsSync as existsSync7, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
3726
3740
|
import { resolve as resolve24 } from "path";
|
|
3727
|
-
var VALID_OPTIONS2 = {
|
|
3728
|
-
style: ["nest", "adonis", "functional"],
|
|
3729
|
-
view: ["rendu", "edge", "eta", "inertia", "none"],
|
|
3730
|
-
orm: ["drizzle", "kysely", "none"],
|
|
3731
|
-
db: ["bun-sqlite", "node-sqlite", "libsql", "postgres", "mysql", "none"],
|
|
3732
|
-
frontend: ["react", "vue", "svelte", "solid"]
|
|
3733
|
-
};
|
|
3734
|
-
async function resolveOpt2(flags, key, valid, defaultVal, interactive) {
|
|
3735
|
-
const flagVal = flags[key];
|
|
3736
|
-
if (flagVal) {
|
|
3737
|
-
if (valid.includes(flagVal))
|
|
3738
|
-
return flagVal;
|
|
3739
|
-
if (!interactive) {
|
|
3740
|
-
logger.error(`Invalid --${key} "${flagVal}". Valid values: ${valid.join(", ")}`);
|
|
3741
|
-
process.exit(1);
|
|
3742
|
-
}
|
|
3743
|
-
logger.warn(`"${flagVal}" is not valid for --${key}. Please choose from the list.`);
|
|
3744
|
-
}
|
|
3745
|
-
const label = key === "style" ? "Routing style" : key === "view" ? "View engine" : key === "orm" ? "ORM driver" : key === "db" ? "Database driver" : "Inertia frontend";
|
|
3746
|
-
for (;; ) {
|
|
3747
|
-
const answer = await select(label, [...valid], { default: defaultVal });
|
|
3748
|
-
if (valid.includes(answer))
|
|
3749
|
-
return answer;
|
|
3750
|
-
logger.warn(`"${answer}" is not valid. Please choose from: ${valid.join(", ")}`);
|
|
3751
|
-
}
|
|
3752
|
-
}
|
|
3753
3741
|
var newCommand = {
|
|
3754
3742
|
name: "new",
|
|
3755
3743
|
aliases: ["n"],
|
|
@@ -3779,11 +3767,11 @@ var newCommand = {
|
|
|
3779
3767
|
logger.error(`Directory "${name}" already exists.`);
|
|
3780
3768
|
return 1;
|
|
3781
3769
|
}
|
|
3782
|
-
const routing = await
|
|
3783
|
-
const view = await
|
|
3784
|
-
const orm = await
|
|
3785
|
-
const db = await
|
|
3786
|
-
const frontend = await
|
|
3770
|
+
const routing = await resolveProjectOption(ctx.flags, "style", VALID_PROJECT_OPTIONS.style, "nest", interactive);
|
|
3771
|
+
const view = await resolveProjectOption(ctx.flags, "view", VALID_PROJECT_OPTIONS.view, "rendu", interactive);
|
|
3772
|
+
const orm = await resolveProjectOption(ctx.flags, "orm", VALID_PROJECT_OPTIONS.orm, "drizzle", interactive);
|
|
3773
|
+
const db = await resolveProjectOption(ctx.flags, "db", VALID_PROJECT_OPTIONS.db, "bun-sqlite", interactive);
|
|
3774
|
+
const frontend = await resolveProjectOption(ctx.flags, "frontend", VALID_PROJECT_OPTIONS.frontend, "react", interactive);
|
|
3787
3775
|
const ssr = !flagBool(ctx.flags, "no-ssr", false);
|
|
3788
3776
|
mkdirSync5(target, { recursive: true });
|
|
3789
3777
|
const dbUrl = db === "bun-sqlite" || db === "node-sqlite" ? "app.db" : "";
|
|
@@ -4586,7 +4574,7 @@ async function main() {
|
|
|
4586
4574
|
const verbose = flagBool(parsed.flags, "verbose", false);
|
|
4587
4575
|
logger.setVerbose(verbose);
|
|
4588
4576
|
if (parsed.flags.version === true) {
|
|
4589
|
-
console.log(
|
|
4577
|
+
console.log(VERSION);
|
|
4590
4578
|
return 0;
|
|
4591
4579
|
}
|
|
4592
4580
|
if (parsed.flags.help === true || parsed.command === "help") {
|
|
@@ -4680,7 +4668,6 @@ Examples`));
|
|
|
4680
4668
|
}
|
|
4681
4669
|
console.log();
|
|
4682
4670
|
}
|
|
4683
|
-
var PKG_VERSION = "0.1.0";
|
|
4684
4671
|
main().then((code) => process.exit(code)).catch((err) => {
|
|
4685
4672
|
logger.error(err?.message ?? String(err));
|
|
4686
4673
|
if (process.env.NX_DEBUG === "1" && err?.stack) {
|
|
@@ -4689,5 +4676,5 @@ main().then((code) => process.exit(code)).catch((err) => {
|
|
|
4689
4676
|
process.exit(1);
|
|
4690
4677
|
});
|
|
4691
4678
|
|
|
4692
|
-
//# debugId=
|
|
4679
|
+
//# debugId=0BB397411F7B54EA64756E2164756E21
|
|
4693
4680
|
//# sourceMappingURL=index.js.map
|