@ooneex/cli 1.16.0 → 1.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1602 -1277
- package/dist/index.js.map +16 -10
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -4878,11 +4878,197 @@ var require_pluralize = __commonJS((exports, module) => {
|
|
|
4878
4878
|
// src/index.ts
|
|
4879
4879
|
import { run } from "@ooneex/command";
|
|
4880
4880
|
|
|
4881
|
-
// src/commands/
|
|
4882
|
-
import { homedir } from "os";
|
|
4881
|
+
// src/commands/AppBuildCommand.ts
|
|
4883
4882
|
import { join } from "path";
|
|
4884
4883
|
import { decorator } from "@ooneex/command";
|
|
4885
4884
|
import { TerminalLogger } from "@ooneex/logger";
|
|
4885
|
+
class AppBuildCommand {
|
|
4886
|
+
getName() {
|
|
4887
|
+
return "app:build";
|
|
4888
|
+
}
|
|
4889
|
+
getDescription() {
|
|
4890
|
+
return "Build the application";
|
|
4891
|
+
}
|
|
4892
|
+
async run() {
|
|
4893
|
+
const logger = new TerminalLogger;
|
|
4894
|
+
const appDir = join(process.cwd(), "modules", "app");
|
|
4895
|
+
const packageJsonFile = Bun.file(join(appDir, "package.json"));
|
|
4896
|
+
if (!await packageJsonFile.exists()) {
|
|
4897
|
+
logger.error("Module app not found", undefined, {
|
|
4898
|
+
showTimestamp: false,
|
|
4899
|
+
showArrow: false,
|
|
4900
|
+
useSymbol: true
|
|
4901
|
+
});
|
|
4902
|
+
return;
|
|
4903
|
+
}
|
|
4904
|
+
const packageJson = await packageJsonFile.json();
|
|
4905
|
+
const name = packageJson.name ?? "app";
|
|
4906
|
+
logger.info(`Building ${name}...`, undefined, {
|
|
4907
|
+
showTimestamp: false,
|
|
4908
|
+
showArrow: false,
|
|
4909
|
+
useSymbol: true
|
|
4910
|
+
});
|
|
4911
|
+
const proc = Bun.spawn(["bun", "build", "./src/index.ts", "--outdir", "./dist", "--target", "bun"], {
|
|
4912
|
+
cwd: appDir,
|
|
4913
|
+
stdout: "inherit",
|
|
4914
|
+
stderr: "inherit"
|
|
4915
|
+
});
|
|
4916
|
+
const exitCode = await proc.exited;
|
|
4917
|
+
if (exitCode === 0) {
|
|
4918
|
+
logger.success(`Build completed for ${name}`, undefined, {
|
|
4919
|
+
showTimestamp: false,
|
|
4920
|
+
showArrow: false,
|
|
4921
|
+
useSymbol: true
|
|
4922
|
+
});
|
|
4923
|
+
} else {
|
|
4924
|
+
logger.error(`Build failed for ${name} (exit code: ${exitCode})`, undefined, {
|
|
4925
|
+
showTimestamp: false,
|
|
4926
|
+
showArrow: false,
|
|
4927
|
+
useSymbol: true
|
|
4928
|
+
});
|
|
4929
|
+
}
|
|
4930
|
+
}
|
|
4931
|
+
}
|
|
4932
|
+
AppBuildCommand = __legacyDecorateClassTS([
|
|
4933
|
+
decorator.command()
|
|
4934
|
+
], AppBuildCommand);
|
|
4935
|
+
// src/commands/AppStartCommand.ts
|
|
4936
|
+
import { join as join2 } from "path";
|
|
4937
|
+
import { decorator as decorator2 } from "@ooneex/command";
|
|
4938
|
+
import { TerminalLogger as TerminalLogger2 } from "@ooneex/logger";
|
|
4939
|
+
class AppStartCommand {
|
|
4940
|
+
getName() {
|
|
4941
|
+
return "app:start";
|
|
4942
|
+
}
|
|
4943
|
+
getDescription() {
|
|
4944
|
+
return "Start the application";
|
|
4945
|
+
}
|
|
4946
|
+
async run() {
|
|
4947
|
+
const logger = new TerminalLogger2;
|
|
4948
|
+
const appDir = join2(process.cwd(), "modules", "app");
|
|
4949
|
+
const packageJsonFile = Bun.file(join2(appDir, "package.json"));
|
|
4950
|
+
if (!await packageJsonFile.exists()) {
|
|
4951
|
+
logger.error("Module app not found", undefined, {
|
|
4952
|
+
showTimestamp: false,
|
|
4953
|
+
showArrow: false,
|
|
4954
|
+
useSymbol: true
|
|
4955
|
+
});
|
|
4956
|
+
return;
|
|
4957
|
+
}
|
|
4958
|
+
const packageJson = await packageJsonFile.json();
|
|
4959
|
+
const name = packageJson.name ?? "app";
|
|
4960
|
+
const composeFile = join2(appDir, "docker-compose.yml");
|
|
4961
|
+
const composeExists = await Bun.file(composeFile).exists();
|
|
4962
|
+
if (composeExists) {
|
|
4963
|
+
logger.info(`Starting Docker services for ${name}...`, undefined, {
|
|
4964
|
+
showTimestamp: false,
|
|
4965
|
+
showArrow: false,
|
|
4966
|
+
useSymbol: true
|
|
4967
|
+
});
|
|
4968
|
+
const docker = Bun.spawn(["docker", "compose", "up", "-d"], {
|
|
4969
|
+
cwd: appDir,
|
|
4970
|
+
stdout: "inherit",
|
|
4971
|
+
stderr: "inherit"
|
|
4972
|
+
});
|
|
4973
|
+
const dockerExitCode = await docker.exited;
|
|
4974
|
+
if (dockerExitCode === 0) {
|
|
4975
|
+
logger.success(`Docker services started for ${name}`, undefined, {
|
|
4976
|
+
showTimestamp: false,
|
|
4977
|
+
showArrow: false,
|
|
4978
|
+
useSymbol: true
|
|
4979
|
+
});
|
|
4980
|
+
} else {
|
|
4981
|
+
logger.error(`Docker services failed for ${name} (exit code: ${dockerExitCode})`, undefined, {
|
|
4982
|
+
showTimestamp: false,
|
|
4983
|
+
showArrow: false,
|
|
4984
|
+
useSymbol: true
|
|
4985
|
+
});
|
|
4986
|
+
return;
|
|
4987
|
+
}
|
|
4988
|
+
}
|
|
4989
|
+
logger.info(`Starting ${name}...`, undefined, {
|
|
4990
|
+
showTimestamp: false,
|
|
4991
|
+
showArrow: false,
|
|
4992
|
+
useSymbol: true
|
|
4993
|
+
});
|
|
4994
|
+
const entryPoint = join2(appDir, "src", "index.ts");
|
|
4995
|
+
const app = Bun.spawn(["bun", "--hot", "run", entryPoint], {
|
|
4996
|
+
cwd: process.cwd(),
|
|
4997
|
+
stdout: "inherit",
|
|
4998
|
+
stderr: "inherit"
|
|
4999
|
+
});
|
|
5000
|
+
const appExitCode = await app.exited;
|
|
5001
|
+
if (appExitCode !== 0) {
|
|
5002
|
+
logger.error(`Application exited with code ${appExitCode}`, undefined, {
|
|
5003
|
+
showTimestamp: false,
|
|
5004
|
+
showArrow: false,
|
|
5005
|
+
useSymbol: true
|
|
5006
|
+
});
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
AppStartCommand = __legacyDecorateClassTS([
|
|
5011
|
+
decorator2.command()
|
|
5012
|
+
], AppStartCommand);
|
|
5013
|
+
// src/commands/AppStopCommand.ts
|
|
5014
|
+
import { join as join3 } from "path";
|
|
5015
|
+
import { decorator as decorator3 } from "@ooneex/command";
|
|
5016
|
+
import { TerminalLogger as TerminalLogger3 } from "@ooneex/logger";
|
|
5017
|
+
class AppStopCommand {
|
|
5018
|
+
getName() {
|
|
5019
|
+
return "app:stop";
|
|
5020
|
+
}
|
|
5021
|
+
getDescription() {
|
|
5022
|
+
return "Stop the application";
|
|
5023
|
+
}
|
|
5024
|
+
async run() {
|
|
5025
|
+
const logger = new TerminalLogger3;
|
|
5026
|
+
const appDir = join3(process.cwd(), "modules", "app");
|
|
5027
|
+
const packageJsonFile = Bun.file(join3(appDir, "package.json"));
|
|
5028
|
+
if (!await packageJsonFile.exists()) {
|
|
5029
|
+
logger.error("Module app not found", undefined, {
|
|
5030
|
+
showTimestamp: false,
|
|
5031
|
+
showArrow: false,
|
|
5032
|
+
useSymbol: true
|
|
5033
|
+
});
|
|
5034
|
+
return;
|
|
5035
|
+
}
|
|
5036
|
+
const packageJson = await packageJsonFile.json();
|
|
5037
|
+
const name = packageJson.name ?? "app";
|
|
5038
|
+
logger.info(`Stopping Docker services for ${name}...`, undefined, {
|
|
5039
|
+
showTimestamp: false,
|
|
5040
|
+
showArrow: false,
|
|
5041
|
+
useSymbol: true
|
|
5042
|
+
});
|
|
5043
|
+
const proc = Bun.spawn(["docker", "compose", "down"], {
|
|
5044
|
+
cwd: appDir,
|
|
5045
|
+
stdout: "inherit",
|
|
5046
|
+
stderr: "inherit"
|
|
5047
|
+
});
|
|
5048
|
+
const exitCode = await proc.exited;
|
|
5049
|
+
if (exitCode === 0) {
|
|
5050
|
+
logger.success(`Docker services stopped for ${name}`, undefined, {
|
|
5051
|
+
showTimestamp: false,
|
|
5052
|
+
showArrow: false,
|
|
5053
|
+
useSymbol: true
|
|
5054
|
+
});
|
|
5055
|
+
} else {
|
|
5056
|
+
logger.error(`Failed to stop Docker services for ${name} (exit code: ${exitCode})`, undefined, {
|
|
5057
|
+
showTimestamp: false,
|
|
5058
|
+
showArrow: false,
|
|
5059
|
+
useSymbol: true
|
|
5060
|
+
});
|
|
5061
|
+
}
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
5064
|
+
AppStopCommand = __legacyDecorateClassTS([
|
|
5065
|
+
decorator3.command()
|
|
5066
|
+
], AppStopCommand);
|
|
5067
|
+
// src/commands/CompletionZshCommand.ts
|
|
5068
|
+
import { homedir } from "os";
|
|
5069
|
+
import { join as join4 } from "path";
|
|
5070
|
+
import { decorator as decorator4 } from "@ooneex/command";
|
|
5071
|
+
import { TerminalLogger as TerminalLogger4 } from "@ooneex/logger";
|
|
4886
5072
|
|
|
4887
5073
|
// src/templates/completions/_oo.txt
|
|
4888
5074
|
var _oo_default = `#compdef oo ooneex
|
|
@@ -4898,7 +5084,11 @@ _oo_modules() {
|
|
|
4898
5084
|
_oo() {
|
|
4899
5085
|
local -a commands
|
|
4900
5086
|
commands=(
|
|
5087
|
+
'app\\:build:Build the application'
|
|
5088
|
+
'app\\:start:Start the application'
|
|
5089
|
+
'app\\:stop:Stop the application'
|
|
4901
5090
|
'completion\\:zsh:Install Zsh completion for oo command'
|
|
5091
|
+
'help:Show available commands'
|
|
4902
5092
|
'make\\:ai:Generate a new AI class'
|
|
4903
5093
|
'make\\:analytics:Generate a new analytics class'
|
|
4904
5094
|
'make\\:app:Generate a new application'
|
|
@@ -4914,6 +5104,7 @@ _oo() {
|
|
|
4914
5104
|
'make\\:mailer:Generate a new mailer class'
|
|
4915
5105
|
'make\\:middleware:Generate a new middleware class'
|
|
4916
5106
|
'make\\:migration:Generate a new migration file'
|
|
5107
|
+
'migration\\:up:Run migrations for all modules'
|
|
4917
5108
|
'make\\:module:Generate a new module'
|
|
4918
5109
|
'make\\:permission:Generate a new permission class'
|
|
4919
5110
|
'make\\:pubsub:Generate a new PubSub event class'
|
|
@@ -4934,6 +5125,7 @@ _oo() {
|
|
|
4934
5125
|
'make\\:resource\\:user:Generate user resource (entity, migration, repository)'
|
|
4935
5126
|
'make\\:resource\\:video:Generate video resource (entity, migration, repository)'
|
|
4936
5127
|
'make\\:seed:Generate a new seed file'
|
|
5128
|
+
'seed\\:run:Run seeds for all modules'
|
|
4937
5129
|
'make\\:service:Generate a new service class'
|
|
4938
5130
|
'make\\:storage:Generate a new storage class'
|
|
4939
5131
|
'make\\:vector-database:Generate a new vector database class'
|
|
@@ -5005,7 +5197,7 @@ _oo() {
|
|
|
5005
5197
|
'--name=[Name of the resource]:name' \\
|
|
5006
5198
|
'--module=[Module name]:module:_oo_modules'
|
|
5007
5199
|
;;
|
|
5008
|
-
make:release|make:resource:book|make:resource:calendar-event|make:resource:category|make:resource:color|make:resource:discount|make:resource:folder|make:resource:image|make:resource:note|make:resource:status|make:resource:tag|make:resource:task|make:resource:topic|make:resource:user|make:resource:video|make:claude:skill|completion:zsh)
|
|
5200
|
+
app:build|app:start|app:stop|help|make:release|make:resource:book|make:resource:calendar-event|make:resource:category|make:resource:color|make:resource:discount|make:resource:folder|make:resource:image|make:resource:note|make:resource:status|make:resource:tag|make:resource:task|make:resource:topic|make:resource:user|make:resource:video|make:claude:skill|migration:up|seed:run|completion:zsh)
|
|
5009
5201
|
;;
|
|
5010
5202
|
esac
|
|
5011
5203
|
;;
|
|
@@ -5029,7 +5221,11 @@ _ooneex_modules() {
|
|
|
5029
5221
|
_ooneex() {
|
|
5030
5222
|
local -a commands
|
|
5031
5223
|
commands=(
|
|
5224
|
+
'app\\:build:Build the application'
|
|
5225
|
+
'app\\:start:Start the application'
|
|
5226
|
+
'app\\:stop:Stop the application'
|
|
5032
5227
|
'completion\\:zsh:Install Zsh completion for oo command'
|
|
5228
|
+
'help:Show available commands'
|
|
5033
5229
|
'make\\:ai:Generate a new AI class'
|
|
5034
5230
|
'make\\:analytics:Generate a new analytics class'
|
|
5035
5231
|
'make\\:app:Generate a new application'
|
|
@@ -5045,6 +5241,7 @@ _ooneex() {
|
|
|
5045
5241
|
'make\\:mailer:Generate a new mailer class'
|
|
5046
5242
|
'make\\:middleware:Generate a new middleware class'
|
|
5047
5243
|
'make\\:migration:Generate a new migration file'
|
|
5244
|
+
'migration\\:up:Run migrations for all modules'
|
|
5048
5245
|
'make\\:module:Generate a new module'
|
|
5049
5246
|
'make\\:permission:Generate a new permission class'
|
|
5050
5247
|
'make\\:pubsub:Generate a new PubSub event class'
|
|
@@ -5065,6 +5262,7 @@ _ooneex() {
|
|
|
5065
5262
|
'make\\:resource\\:user:Generate user resource (entity, migration, repository)'
|
|
5066
5263
|
'make\\:resource\\:video:Generate video resource (entity, migration, repository)'
|
|
5067
5264
|
'make\\:seed:Generate a new seed file'
|
|
5265
|
+
'seed\\:run:Run seeds for all modules'
|
|
5068
5266
|
'make\\:service:Generate a new service class'
|
|
5069
5267
|
'make\\:storage:Generate a new storage class'
|
|
5070
5268
|
'make\\:vector-database:Generate a new vector database class'
|
|
@@ -5136,7 +5334,7 @@ _ooneex() {
|
|
|
5136
5334
|
'--name=[Name of the resource]:name' \\
|
|
5137
5335
|
'--module=[Module name]:module:_ooneex_modules'
|
|
5138
5336
|
;;
|
|
5139
|
-
make:release|make:resource:book|make:resource:calendar-event|make:resource:category|make:resource:color|make:resource:discount|make:resource:folder|make:resource:image|make:resource:note|make:resource:status|make:resource:tag|make:resource:task|make:resource:topic|make:resource:user|make:resource:video|make:claude:skill|completion:zsh)
|
|
5337
|
+
app:build|app:start|app:stop|help|make:release|make:resource:book|make:resource:calendar-event|make:resource:category|make:resource:color|make:resource:discount|make:resource:folder|make:resource:image|make:resource:note|make:resource:status|make:resource:tag|make:resource:task|make:resource:topic|make:resource:user|make:resource:video|make:claude:skill|migration:up|seed:run|completion:zsh)
|
|
5140
5338
|
;;
|
|
5141
5339
|
esac
|
|
5142
5340
|
;;
|
|
@@ -5155,12 +5353,12 @@ class CompletionZshCommand {
|
|
|
5155
5353
|
return "Install Zsh completion for oo command";
|
|
5156
5354
|
}
|
|
5157
5355
|
async run() {
|
|
5158
|
-
const completionDir =
|
|
5159
|
-
const ooFilePath =
|
|
5356
|
+
const completionDir = join4(homedir(), ".zsh");
|
|
5357
|
+
const ooFilePath = join4(completionDir, "_oo");
|
|
5160
5358
|
await Bun.write(ooFilePath, _oo_default);
|
|
5161
|
-
const ooneexFilePath =
|
|
5359
|
+
const ooneexFilePath = join4(completionDir, "_ooneex");
|
|
5162
5360
|
await Bun.write(ooneexFilePath, _ooneex_default);
|
|
5163
|
-
const logger = new
|
|
5361
|
+
const logger = new TerminalLogger4;
|
|
5164
5362
|
logger.success(`${ooFilePath} created successfully`, undefined, {
|
|
5165
5363
|
showTimestamp: false,
|
|
5166
5364
|
showArrow: false,
|
|
@@ -5181,12 +5379,42 @@ class CompletionZshCommand {
|
|
|
5181
5379
|
}
|
|
5182
5380
|
}
|
|
5183
5381
|
CompletionZshCommand = __legacyDecorateClassTS([
|
|
5184
|
-
|
|
5382
|
+
decorator4.command()
|
|
5185
5383
|
], CompletionZshCommand);
|
|
5384
|
+
// src/commands/HelpCommand.ts
|
|
5385
|
+
import { COMMANDS_CONTAINER, decorator as decorator5 } from "@ooneex/command";
|
|
5386
|
+
class HelpCommand {
|
|
5387
|
+
getName() {
|
|
5388
|
+
return "help";
|
|
5389
|
+
}
|
|
5390
|
+
getDescription() {
|
|
5391
|
+
return "Show available commands";
|
|
5392
|
+
}
|
|
5393
|
+
run() {
|
|
5394
|
+
const commands = [];
|
|
5395
|
+
for (const CommandClass of COMMANDS_CONTAINER) {
|
|
5396
|
+
const command = new CommandClass;
|
|
5397
|
+
commands.push({ name: command.getName(), description: command.getDescription() });
|
|
5398
|
+
}
|
|
5399
|
+
commands.sort((a, b) => a.name.localeCompare(b.name));
|
|
5400
|
+
const maxNameLength = Math.max(...commands.map((c) => c.name.length));
|
|
5401
|
+
const lines = ["", "Available commands:", ""];
|
|
5402
|
+
for (const { name, description } of commands) {
|
|
5403
|
+
lines.push(` ${name.padEnd(maxNameLength + 2)}${description}`);
|
|
5404
|
+
}
|
|
5405
|
+
lines.push("");
|
|
5406
|
+
process.stdout.write(`${lines.join(`
|
|
5407
|
+
`)}
|
|
5408
|
+
`);
|
|
5409
|
+
}
|
|
5410
|
+
}
|
|
5411
|
+
HelpCommand = __legacyDecorateClassTS([
|
|
5412
|
+
decorator5.command()
|
|
5413
|
+
], HelpCommand);
|
|
5186
5414
|
// src/commands/MakeAiCommand.ts
|
|
5187
|
-
import { join as
|
|
5188
|
-
import { decorator as
|
|
5189
|
-
import { TerminalLogger as
|
|
5415
|
+
import { join as join7 } from "path";
|
|
5416
|
+
import { decorator as decorator7 } from "@ooneex/command";
|
|
5417
|
+
import { TerminalLogger as TerminalLogger6 } from "@ooneex/logger";
|
|
5190
5418
|
import { toPascalCase as toPascalCase2 } from "@ooneex/utils";
|
|
5191
5419
|
|
|
5192
5420
|
// src/prompts/askName.ts
|
|
@@ -5283,12 +5511,12 @@ export class {{NAME}}Ai implements IAiChat<OpenAiConfigType> {
|
|
|
5283
5511
|
`;
|
|
5284
5512
|
|
|
5285
5513
|
// src/utils.ts
|
|
5286
|
-
import { join as
|
|
5514
|
+
import { join as join6 } from "path";
|
|
5287
5515
|
|
|
5288
5516
|
// src/commands/MakeModuleCommand.ts
|
|
5289
|
-
import { join as
|
|
5290
|
-
import { decorator as
|
|
5291
|
-
import { TerminalLogger as
|
|
5517
|
+
import { join as join5 } from "path";
|
|
5518
|
+
import { decorator as decorator6 } from "@ooneex/command";
|
|
5519
|
+
import { TerminalLogger as TerminalLogger5 } from "@ooneex/logger";
|
|
5292
5520
|
import { toKebabCase, toPascalCase } from "@ooneex/utils";
|
|
5293
5521
|
|
|
5294
5522
|
// src/templates/module/command.run.txt
|
|
@@ -5436,53 +5664,53 @@ class MakeModuleCommand {
|
|
|
5436
5664
|
}
|
|
5437
5665
|
const pascalName = toPascalCase(name).replace(/Module$/, "");
|
|
5438
5666
|
const kebabName = toKebabCase(pascalName);
|
|
5439
|
-
const moduleDir =
|
|
5440
|
-
const srcDir =
|
|
5441
|
-
const testsDir =
|
|
5667
|
+
const moduleDir = join5(cwd, "modules", kebabName);
|
|
5668
|
+
const srcDir = join5(moduleDir, "src");
|
|
5669
|
+
const testsDir = join5(moduleDir, "tests");
|
|
5442
5670
|
const moduleContent = module_default.replace(/{{NAME}}/g, pascalName);
|
|
5443
5671
|
const packageContent = package_default.replace(/{{NAME}}/g, kebabName);
|
|
5444
5672
|
const testContent = test_default.replace(/{{NAME}}/g, pascalName);
|
|
5445
|
-
await Bun.write(
|
|
5673
|
+
await Bun.write(join5(srcDir, `${pascalName}Module.ts`), moduleContent);
|
|
5446
5674
|
if (!skipMigrations) {
|
|
5447
|
-
await Bun.write(
|
|
5675
|
+
await Bun.write(join5(srcDir, "migrations", "migrations.ts"), "");
|
|
5448
5676
|
}
|
|
5449
5677
|
if (!skipSeeds) {
|
|
5450
|
-
await Bun.write(
|
|
5678
|
+
await Bun.write(join5(srcDir, "seeds", "seeds.ts"), "");
|
|
5451
5679
|
}
|
|
5452
5680
|
if (!skipCommands) {
|
|
5453
|
-
await Bun.write(
|
|
5454
|
-
const binCommandRunPath =
|
|
5681
|
+
await Bun.write(join5(srcDir, "commands", "commands.ts"), "");
|
|
5682
|
+
const binCommandRunPath = join5(moduleDir, "bin", "command", "run.ts");
|
|
5455
5683
|
const binCommandRunFile = Bun.file(binCommandRunPath);
|
|
5456
5684
|
if (!await binCommandRunFile.exists()) {
|
|
5457
5685
|
await Bun.write(binCommandRunPath, command_run_default);
|
|
5458
5686
|
}
|
|
5459
5687
|
}
|
|
5460
|
-
await Bun.write(
|
|
5461
|
-
await Bun.write(
|
|
5462
|
-
await Bun.write(
|
|
5688
|
+
await Bun.write(join5(moduleDir, "package.json"), packageContent);
|
|
5689
|
+
await Bun.write(join5(moduleDir, "tsconfig.json"), tsconfig_default);
|
|
5690
|
+
await Bun.write(join5(testsDir, `${pascalName}Module.spec.ts`), testContent);
|
|
5463
5691
|
if (kebabName !== "app") {
|
|
5464
|
-
const appModulePath =
|
|
5692
|
+
const appModulePath = join5(cwd, "modules", "app", "src", "AppModule.ts");
|
|
5465
5693
|
if (await Bun.file(appModulePath).exists()) {
|
|
5466
5694
|
await this.addToAppModule(appModulePath, pascalName, kebabName);
|
|
5467
5695
|
}
|
|
5468
|
-
const appTsconfigPath =
|
|
5696
|
+
const appTsconfigPath = join5(cwd, "modules", "app", "tsconfig.json");
|
|
5469
5697
|
if (await Bun.file(appTsconfigPath).exists()) {
|
|
5470
5698
|
await this.addPathAlias(appTsconfigPath, kebabName);
|
|
5471
5699
|
}
|
|
5472
5700
|
}
|
|
5473
|
-
const commitlintPath =
|
|
5701
|
+
const commitlintPath = join5(cwd, ".commitlintrc.ts");
|
|
5474
5702
|
if (await Bun.file(commitlintPath).exists()) {
|
|
5475
5703
|
await this.addModuleScope(commitlintPath, kebabName);
|
|
5476
5704
|
}
|
|
5477
5705
|
if (!silent) {
|
|
5478
|
-
const logger = new
|
|
5706
|
+
const logger = new TerminalLogger5;
|
|
5479
5707
|
logger.success(`modules/${kebabName} created successfully`, undefined, {
|
|
5480
5708
|
showTimestamp: false,
|
|
5481
5709
|
showArrow: false,
|
|
5482
5710
|
useSymbol: true
|
|
5483
5711
|
});
|
|
5484
5712
|
}
|
|
5485
|
-
const packageJsonPath =
|
|
5713
|
+
const packageJsonPath = join5(process.cwd(), "package.json");
|
|
5486
5714
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
5487
5715
|
const deps = packageJson.dependencies ?? {};
|
|
5488
5716
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -5497,13 +5725,13 @@ class MakeModuleCommand {
|
|
|
5497
5725
|
}
|
|
5498
5726
|
}
|
|
5499
5727
|
MakeModuleCommand = __legacyDecorateClassTS([
|
|
5500
|
-
|
|
5728
|
+
decorator6.command()
|
|
5501
5729
|
], MakeModuleCommand);
|
|
5502
5730
|
|
|
5503
5731
|
// src/utils.ts
|
|
5504
5732
|
var ensureModule = async (module) => {
|
|
5505
|
-
const moduleDir =
|
|
5506
|
-
const moduleDirExists = await Bun.file(
|
|
5733
|
+
const moduleDir = join6(process.cwd(), "modules", module);
|
|
5734
|
+
const moduleDirExists = await Bun.file(join6(moduleDir, "package.json")).exists();
|
|
5507
5735
|
if (!moduleDirExists) {
|
|
5508
5736
|
const makeModule = new MakeModuleCommand;
|
|
5509
5737
|
await makeModule.run({ name: module, cwd: process.cwd(), silent: true });
|
|
@@ -5528,28 +5756,28 @@ class MakeAiCommand {
|
|
|
5528
5756
|
await ensureModule(module);
|
|
5529
5757
|
}
|
|
5530
5758
|
const content = ai_default.replace(/{{NAME}}/g, name);
|
|
5531
|
-
const base = module ?
|
|
5532
|
-
const aiLocalDir =
|
|
5533
|
-
const aiDir =
|
|
5534
|
-
const filePath =
|
|
5759
|
+
const base = module ? join7("modules", module) : ".";
|
|
5760
|
+
const aiLocalDir = join7(base, "src", "ai");
|
|
5761
|
+
const aiDir = join7(process.cwd(), aiLocalDir);
|
|
5762
|
+
const filePath = join7(aiDir, `${name}Ai.ts`);
|
|
5535
5763
|
await Bun.write(filePath, content);
|
|
5536
5764
|
const testContent = ai_test_default.replace(/{{NAME}}/g, name);
|
|
5537
|
-
const testsLocalDir =
|
|
5538
|
-
const testsDir =
|
|
5539
|
-
const testFilePath =
|
|
5765
|
+
const testsLocalDir = join7(base, "tests", "ai");
|
|
5766
|
+
const testsDir = join7(process.cwd(), testsLocalDir);
|
|
5767
|
+
const testFilePath = join7(testsDir, `${name}Ai.spec.ts`);
|
|
5540
5768
|
await Bun.write(testFilePath, testContent);
|
|
5541
|
-
const logger = new
|
|
5542
|
-
logger.success(`${
|
|
5769
|
+
const logger = new TerminalLogger6;
|
|
5770
|
+
logger.success(`${join7(aiLocalDir, name)}Ai.ts created successfully`, undefined, {
|
|
5543
5771
|
showTimestamp: false,
|
|
5544
5772
|
showArrow: false,
|
|
5545
5773
|
useSymbol: true
|
|
5546
5774
|
});
|
|
5547
|
-
logger.success(`${
|
|
5775
|
+
logger.success(`${join7(testsLocalDir, name)}Ai.spec.ts created successfully`, undefined, {
|
|
5548
5776
|
showTimestamp: false,
|
|
5549
5777
|
showArrow: false,
|
|
5550
5778
|
useSymbol: true
|
|
5551
5779
|
});
|
|
5552
|
-
const packageJsonPath =
|
|
5780
|
+
const packageJsonPath = join7(process.cwd(), "package.json");
|
|
5553
5781
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
5554
5782
|
const deps = packageJson.dependencies ?? {};
|
|
5555
5783
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -5564,12 +5792,12 @@ class MakeAiCommand {
|
|
|
5564
5792
|
}
|
|
5565
5793
|
}
|
|
5566
5794
|
MakeAiCommand = __legacyDecorateClassTS([
|
|
5567
|
-
|
|
5795
|
+
decorator7.command()
|
|
5568
5796
|
], MakeAiCommand);
|
|
5569
5797
|
// src/commands/MakeAnalyticsCommand.ts
|
|
5570
|
-
import { join as
|
|
5571
|
-
import { decorator as
|
|
5572
|
-
import { TerminalLogger as
|
|
5798
|
+
import { join as join8 } from "path";
|
|
5799
|
+
import { decorator as decorator8 } from "@ooneex/command";
|
|
5800
|
+
import { TerminalLogger as TerminalLogger7 } from "@ooneex/logger";
|
|
5573
5801
|
import { toPascalCase as toPascalCase3 } from "@ooneex/utils";
|
|
5574
5802
|
|
|
5575
5803
|
// src/templates/analytics.test.txt
|
|
@@ -5620,28 +5848,28 @@ class MakeAnalyticsCommand {
|
|
|
5620
5848
|
if (module) {
|
|
5621
5849
|
await ensureModule(module);
|
|
5622
5850
|
}
|
|
5623
|
-
const base = module ?
|
|
5624
|
-
const analyticsLocalDir =
|
|
5625
|
-
const analyticsDir =
|
|
5626
|
-
const filePath =
|
|
5851
|
+
const base = module ? join8("modules", module) : ".";
|
|
5852
|
+
const analyticsLocalDir = join8(base, "src", "analytics");
|
|
5853
|
+
const analyticsDir = join8(process.cwd(), analyticsLocalDir);
|
|
5854
|
+
const filePath = join8(analyticsDir, `${name}Analytics.ts`);
|
|
5627
5855
|
await Bun.write(filePath, content);
|
|
5628
5856
|
const testContent = analytics_test_default.replace(/{{NAME}}/g, name);
|
|
5629
|
-
const testsLocalDir =
|
|
5630
|
-
const testsDir =
|
|
5631
|
-
const testFilePath =
|
|
5857
|
+
const testsLocalDir = join8(base, "tests", "analytics");
|
|
5858
|
+
const testsDir = join8(process.cwd(), testsLocalDir);
|
|
5859
|
+
const testFilePath = join8(testsDir, `${name}Analytics.spec.ts`);
|
|
5632
5860
|
await Bun.write(testFilePath, testContent);
|
|
5633
|
-
const logger = new
|
|
5634
|
-
logger.success(`${
|
|
5861
|
+
const logger = new TerminalLogger7;
|
|
5862
|
+
logger.success(`${join8(analyticsLocalDir, name)}Analytics.ts created successfully`, undefined, {
|
|
5635
5863
|
showTimestamp: false,
|
|
5636
5864
|
showArrow: false,
|
|
5637
5865
|
useSymbol: true
|
|
5638
5866
|
});
|
|
5639
|
-
logger.success(`${
|
|
5867
|
+
logger.success(`${join8(testsLocalDir, name)}Analytics.spec.ts created successfully`, undefined, {
|
|
5640
5868
|
showTimestamp: false,
|
|
5641
5869
|
showArrow: false,
|
|
5642
5870
|
useSymbol: true
|
|
5643
5871
|
});
|
|
5644
|
-
const packageJsonPath =
|
|
5872
|
+
const packageJsonPath = join8(process.cwd(), "package.json");
|
|
5645
5873
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
5646
5874
|
const deps = packageJson.dependencies ?? {};
|
|
5647
5875
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -5656,13 +5884,13 @@ class MakeAnalyticsCommand {
|
|
|
5656
5884
|
}
|
|
5657
5885
|
}
|
|
5658
5886
|
MakeAnalyticsCommand = __legacyDecorateClassTS([
|
|
5659
|
-
|
|
5887
|
+
decorator8.command()
|
|
5660
5888
|
], MakeAnalyticsCommand);
|
|
5661
5889
|
// src/commands/MakeAppCommand.ts
|
|
5662
|
-
import { join as
|
|
5663
|
-
import { decorator as
|
|
5664
|
-
import { TerminalLogger as
|
|
5665
|
-
import { toKebabCase as
|
|
5890
|
+
import { join as join9 } from "path";
|
|
5891
|
+
import { decorator as decorator9 } from "@ooneex/command";
|
|
5892
|
+
import { TerminalLogger as TerminalLogger8 } from "@ooneex/logger";
|
|
5893
|
+
import { toKebabCase as toKebabCase2, toSnakeCase } from "@ooneex/utils";
|
|
5666
5894
|
|
|
5667
5895
|
// src/prompts/askDestination.ts
|
|
5668
5896
|
var import_enquirer2 = __toESM(require_enquirer(), 1);
|
|
@@ -6134,10 +6362,6 @@ FROM base AS prerelease
|
|
|
6134
6362
|
COPY --from=build /temp/dev/node_modules node_modules
|
|
6135
6363
|
COPY . .
|
|
6136
6364
|
|
|
6137
|
-
# run migrations and seed
|
|
6138
|
-
RUN bun run migration:up
|
|
6139
|
-
RUN bun run seed:run
|
|
6140
|
-
|
|
6141
6365
|
# build
|
|
6142
6366
|
ENV NODE_ENV=production
|
|
6143
6367
|
RUN bun run build
|
|
@@ -6479,9 +6703,7 @@ var package_json_default = `{
|
|
|
6479
6703
|
"fmt": "bunx biome check --write",
|
|
6480
6704
|
"lint": "bunx nx run-many -t lint --output-style=stream --verbose",
|
|
6481
6705
|
"test": "bunx nx run-many -t test --output-style=stream --verbose",
|
|
6482
|
-
"
|
|
6483
|
-
"migration:up": "bunx nx run-many -t migration:up --output-style=stream --verbose",
|
|
6484
|
-
"seed:run": "bunx nx run-many -t seed:run --output-style=stream --verbose",
|
|
6706
|
+
"stop": "bunx nx run-many -t stop --output-style=stream --verbose",
|
|
6485
6707
|
"commit": "bunx commit"
|
|
6486
6708
|
},
|
|
6487
6709
|
"workspaces": [
|
|
@@ -6521,14 +6743,7 @@ cp modules/app/.env.example modules/app/.env
|
|
|
6521
6743
|
|
|
6522
6744
|
Edit \`modules/app/.env\` with your configuration values.
|
|
6523
6745
|
|
|
6524
|
-
### 3.
|
|
6525
|
-
|
|
6526
|
-
\`\`\`bash
|
|
6527
|
-
bun run migration:up
|
|
6528
|
-
bun run seed:run
|
|
6529
|
-
\`\`\`
|
|
6530
|
-
|
|
6531
|
-
### 4. Start the development server
|
|
6746
|
+
### 3. Start the development server
|
|
6532
6747
|
|
|
6533
6748
|
\`\`\`bash
|
|
6534
6749
|
bun run dev
|
|
@@ -6545,9 +6760,7 @@ The server starts on \`http://localhost:3000\` by default.
|
|
|
6545
6760
|
| \`bun run test\` | Run all tests |
|
|
6546
6761
|
| \`bun run lint\` | Run TypeScript check + Biome linting |
|
|
6547
6762
|
| \`bun run fmt\` | Format code with Biome |
|
|
6548
|
-
| \`bun run
|
|
6549
|
-
| \`bun run seed:run\` | Populate database with seed data |
|
|
6550
|
-
| \`bun run docker:stop\` | Stop all Docker containers |
|
|
6763
|
+
| \`bun run stop\` | Stop all Docker containers |
|
|
6551
6764
|
| \`bun run commit\` | Interactive conventional commit |
|
|
6552
6765
|
|
|
6553
6766
|
## Module Architecture
|
|
@@ -6791,500 +7004,33 @@ var zed_settings_json_default = `{
|
|
|
6791
7004
|
}
|
|
6792
7005
|
`;
|
|
6793
7006
|
|
|
6794
|
-
// src/commands/
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
import { toKebabCase as toKebabCase2, toPascalCase as toPascalCase4, trim } from "@ooneex/utils";
|
|
6799
|
-
|
|
6800
|
-
// src/prompts/askConfirm.ts
|
|
6801
|
-
var import_enquirer3 = __toESM(require_enquirer(), 1);
|
|
6802
|
-
var askConfirm = async (config) => {
|
|
6803
|
-
const response = await import_enquirer3.prompt({
|
|
6804
|
-
type: "confirm",
|
|
6805
|
-
name: "confirm",
|
|
6806
|
-
message: config.message,
|
|
6807
|
-
initial: config.initial
|
|
6808
|
-
});
|
|
6809
|
-
return response.confirm;
|
|
6810
|
-
};
|
|
6811
|
-
|
|
6812
|
-
// ../types/dist/index.js
|
|
6813
|
-
var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
6814
|
-
|
|
6815
|
-
// src/prompts/askRouteMethod.ts
|
|
6816
|
-
var import_enquirer4 = __toESM(require_enquirer(), 1);
|
|
6817
|
-
|
|
6818
|
-
// src/constraints/AssertRouteMethod.ts
|
|
6819
|
-
import { Assert as Assert2, Validation as Validation2 } from "@ooneex/validation";
|
|
6820
|
-
|
|
6821
|
-
class AssertRouteMethod extends Validation2 {
|
|
6822
|
-
getConstraint() {
|
|
6823
|
-
return Assert2("string >= 3");
|
|
7007
|
+
// src/commands/MakeAppCommand.ts
|
|
7008
|
+
class MakeAppCommand {
|
|
7009
|
+
getName() {
|
|
7010
|
+
return "make:app";
|
|
6824
7011
|
}
|
|
6825
|
-
|
|
6826
|
-
return
|
|
7012
|
+
getDescription() {
|
|
7013
|
+
return "Generate a new application";
|
|
6827
7014
|
}
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
if (!
|
|
6831
|
-
|
|
6832
|
-
}
|
|
6833
|
-
const method = data;
|
|
6834
|
-
if (method.trim() !== method) {
|
|
6835
|
-
return {
|
|
6836
|
-
isValid: false,
|
|
6837
|
-
message: this.getErrorMessage() || "Invalid route method format"
|
|
6838
|
-
};
|
|
6839
|
-
}
|
|
6840
|
-
const upperMethod = method.toUpperCase();
|
|
6841
|
-
if (!HTTP_METHODS.includes(upperMethod)) {
|
|
6842
|
-
return {
|
|
6843
|
-
isValid: false,
|
|
6844
|
-
message: this.getErrorMessage() || "Invalid route method"
|
|
6845
|
-
};
|
|
7015
|
+
async run(options) {
|
|
7016
|
+
let { name, destination } = options;
|
|
7017
|
+
if (!name) {
|
|
7018
|
+
name = await askName({ message: "Enter application name" });
|
|
6846
7019
|
}
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
}
|
|
6851
|
-
}
|
|
6852
|
-
|
|
6853
|
-
// src/prompts/askRouteMethod.ts
|
|
6854
|
-
var askRouteMethod = async (config) => {
|
|
6855
|
-
const response = await import_enquirer4.prompt({
|
|
6856
|
-
type: "select",
|
|
6857
|
-
name: "method",
|
|
6858
|
-
message: config.message,
|
|
6859
|
-
initial: config.initial ?? 0,
|
|
6860
|
-
choices: HTTP_METHODS.map((method) => method),
|
|
6861
|
-
validate: (value) => {
|
|
6862
|
-
const constraint = new AssertRouteMethod;
|
|
6863
|
-
const result = constraint.validate(value);
|
|
6864
|
-
if (!result.isValid) {
|
|
6865
|
-
return result.message || "Route method is invalid";
|
|
6866
|
-
}
|
|
6867
|
-
return true;
|
|
6868
|
-
}
|
|
6869
|
-
});
|
|
6870
|
-
return response.method;
|
|
6871
|
-
};
|
|
6872
|
-
|
|
6873
|
-
// src/prompts/askRouteName.ts
|
|
6874
|
-
var import_enquirer5 = __toESM(require_enquirer(), 1);
|
|
6875
|
-
|
|
6876
|
-
// src/constraints/AssertRouteName.ts
|
|
6877
|
-
import { Assert as Assert3, Validation as Validation3 } from "@ooneex/validation";
|
|
6878
|
-
var ROUTE_NAME_REGEX = /^[a-zA-Z0-9]+\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$/;
|
|
6879
|
-
|
|
6880
|
-
class AssertRouteName extends Validation3 {
|
|
6881
|
-
getConstraint() {
|
|
6882
|
-
return Assert3("string >= 7");
|
|
6883
|
-
}
|
|
6884
|
-
getErrorMessage() {
|
|
6885
|
-
return "Route name must follow format: namespace.resource.action (e.g., 'api.users.list')";
|
|
6886
|
-
}
|
|
6887
|
-
validate(data, constraint) {
|
|
6888
|
-
const basicValidation = super.validate(data, constraint);
|
|
6889
|
-
if (!basicValidation.isValid) {
|
|
6890
|
-
return basicValidation;
|
|
6891
|
-
}
|
|
6892
|
-
const routeName = data;
|
|
6893
|
-
if (routeName.trim() !== routeName) {
|
|
6894
|
-
return {
|
|
6895
|
-
isValid: false,
|
|
6896
|
-
message: this.getErrorMessage() || "Invalid route name format"
|
|
6897
|
-
};
|
|
6898
|
-
}
|
|
6899
|
-
if (!ROUTE_NAME_REGEX.test(routeName)) {
|
|
6900
|
-
return {
|
|
6901
|
-
isValid: false,
|
|
6902
|
-
message: this.getErrorMessage() || "Invalid route name format"
|
|
6903
|
-
};
|
|
6904
|
-
}
|
|
6905
|
-
const segments = routeName.split(".");
|
|
6906
|
-
if (segments.length !== 3) {
|
|
6907
|
-
return {
|
|
6908
|
-
isValid: false,
|
|
6909
|
-
message: this.getErrorMessage() || "Invalid route name format"
|
|
6910
|
-
};
|
|
6911
|
-
}
|
|
6912
|
-
const [namespace, resource, action] = segments;
|
|
6913
|
-
if (!namespace || !resource || !action) {
|
|
6914
|
-
return {
|
|
6915
|
-
isValid: false,
|
|
6916
|
-
message: this.getErrorMessage() || "Invalid route name format"
|
|
6917
|
-
};
|
|
6918
|
-
}
|
|
6919
|
-
return {
|
|
6920
|
-
isValid: true
|
|
6921
|
-
};
|
|
6922
|
-
}
|
|
6923
|
-
}
|
|
6924
|
-
|
|
6925
|
-
// src/prompts/askRouteName.ts
|
|
6926
|
-
var askRouteName = async (config) => {
|
|
6927
|
-
const response = await import_enquirer5.prompt({
|
|
6928
|
-
type: "input",
|
|
6929
|
-
name: "routeName",
|
|
6930
|
-
message: config.message,
|
|
6931
|
-
validate: (value) => {
|
|
6932
|
-
const constraint = new AssertRouteName;
|
|
6933
|
-
const result = constraint.validate(value);
|
|
6934
|
-
if (!result.isValid) {
|
|
6935
|
-
return result.message || "Route name is invalid";
|
|
6936
|
-
}
|
|
6937
|
-
return true;
|
|
6938
|
-
}
|
|
6939
|
-
});
|
|
6940
|
-
return response.routeName;
|
|
6941
|
-
};
|
|
6942
|
-
|
|
6943
|
-
// src/prompts/askRoutePath.ts
|
|
6944
|
-
var import_enquirer6 = __toESM(require_enquirer(), 1);
|
|
6945
|
-
|
|
6946
|
-
// src/constraints/AssertRoutePath.ts
|
|
6947
|
-
import { Assert as Assert4, Validation as Validation4 } from "@ooneex/validation";
|
|
6948
|
-
var ROUTE_PATH_MIN_LENGTH = 1;
|
|
6949
|
-
var ROUTE_PATH_REGEX = /^\/[\w\-/:]*$/;
|
|
6950
|
-
var VALID_SEGMENT_REGEX = /^[a-zA-Z0-9\-_]+$/;
|
|
6951
|
-
var PARAM_SEGMENT_REGEX = /^:[a-zA-Z][a-zA-Z0-9]*$/;
|
|
6952
|
-
|
|
6953
|
-
class AssertRoutePath extends Validation4 {
|
|
6954
|
-
getConstraint() {
|
|
6955
|
-
return Assert4(`string >= ${ROUTE_PATH_MIN_LENGTH}`);
|
|
6956
|
-
}
|
|
6957
|
-
getErrorMessage() {
|
|
6958
|
-
return "Route path must start with '/' and contain only valid segments (e.g., '/users', '/api/users/:id')";
|
|
6959
|
-
}
|
|
6960
|
-
validate(data, constraint) {
|
|
6961
|
-
const basicValidation = super.validate(data, constraint);
|
|
6962
|
-
if (!basicValidation.isValid) {
|
|
6963
|
-
return basicValidation;
|
|
6964
|
-
}
|
|
6965
|
-
const path = data;
|
|
6966
|
-
if (path.trim() !== path) {
|
|
6967
|
-
return {
|
|
6968
|
-
isValid: false,
|
|
6969
|
-
message: this.getErrorMessage() || "Invalid route path format"
|
|
6970
|
-
};
|
|
6971
|
-
}
|
|
6972
|
-
if (!path.startsWith("/")) {
|
|
6973
|
-
return {
|
|
6974
|
-
isValid: false,
|
|
6975
|
-
message: "Route path must start with '/'"
|
|
6976
|
-
};
|
|
6977
|
-
}
|
|
6978
|
-
if (path.length > 1 && path.endsWith("/")) {
|
|
6979
|
-
return {
|
|
6980
|
-
isValid: false,
|
|
6981
|
-
message: "Route path cannot end with '/' (except for root path)"
|
|
6982
|
-
};
|
|
6983
|
-
}
|
|
6984
|
-
if (!ROUTE_PATH_REGEX.test(path)) {
|
|
6985
|
-
return {
|
|
6986
|
-
isValid: false,
|
|
6987
|
-
message: this.getErrorMessage() || "Invalid route path format"
|
|
6988
|
-
};
|
|
6989
|
-
}
|
|
6990
|
-
if (path === "/") {
|
|
6991
|
-
return {
|
|
6992
|
-
isValid: true
|
|
6993
|
-
};
|
|
6994
|
-
}
|
|
6995
|
-
const segments = path.slice(1).split("/");
|
|
6996
|
-
for (const segment of segments) {
|
|
6997
|
-
if (!segment) {
|
|
6998
|
-
return {
|
|
6999
|
-
isValid: false,
|
|
7000
|
-
message: "Route path cannot contain empty segments (double slashes)"
|
|
7001
|
-
};
|
|
7002
|
-
}
|
|
7003
|
-
if (segment.startsWith(":")) {
|
|
7004
|
-
if (!PARAM_SEGMENT_REGEX.test(segment)) {
|
|
7005
|
-
return {
|
|
7006
|
-
isValid: false,
|
|
7007
|
-
message: `Invalid parameter segment '${segment}'. Parameters must follow format ':paramName' with alphanumeric characters only`
|
|
7008
|
-
};
|
|
7009
|
-
}
|
|
7010
|
-
} else {
|
|
7011
|
-
if (!VALID_SEGMENT_REGEX.test(segment)) {
|
|
7012
|
-
return {
|
|
7013
|
-
isValid: false,
|
|
7014
|
-
message: `Invalid path segment '${segment}'. Segments must contain only letters, numbers, hyphens, and underscores`
|
|
7015
|
-
};
|
|
7016
|
-
}
|
|
7017
|
-
}
|
|
7018
|
-
}
|
|
7019
|
-
return {
|
|
7020
|
-
isValid: true
|
|
7021
|
-
};
|
|
7022
|
-
}
|
|
7023
|
-
}
|
|
7024
|
-
|
|
7025
|
-
// src/prompts/askRoutePath.ts
|
|
7026
|
-
var askRoutePath = async (config) => {
|
|
7027
|
-
const response = await import_enquirer6.prompt({
|
|
7028
|
-
type: "input",
|
|
7029
|
-
name: "path",
|
|
7030
|
-
message: config.message,
|
|
7031
|
-
initial: config.initial ?? "/",
|
|
7032
|
-
validate: (value) => {
|
|
7033
|
-
const constraint = new AssertRoutePath;
|
|
7034
|
-
const result = constraint.validate(value);
|
|
7035
|
-
if (!result.isValid) {
|
|
7036
|
-
return result.message || "Route path is invalid";
|
|
7037
|
-
}
|
|
7038
|
-
return true;
|
|
7039
|
-
}
|
|
7040
|
-
});
|
|
7041
|
-
return response.path;
|
|
7042
|
-
};
|
|
7043
|
-
|
|
7044
|
-
// src/templates/controller.socket.txt
|
|
7045
|
-
var controller_socket_default = `import type { ContextType } from "@ooneex/socket";
|
|
7046
|
-
import { ERole } from "@ooneex/role";
|
|
7047
|
-
import { Route } from "@ooneex/routing";
|
|
7048
|
-
import { Assert } from "@ooneex/validation";
|
|
7049
|
-
|
|
7050
|
-
export type {{TYPE_NAME}}RouteType = {
|
|
7051
|
-
params: {
|
|
7052
|
-
|
|
7053
|
-
},
|
|
7054
|
-
payload: {
|
|
7055
|
-
|
|
7056
|
-
},
|
|
7057
|
-
queries: {
|
|
7058
|
-
|
|
7059
|
-
},
|
|
7060
|
-
response: {
|
|
7061
|
-
|
|
7062
|
-
},
|
|
7063
|
-
};
|
|
7064
|
-
|
|
7065
|
-
@Route.socket("{{ROUTE_PATH}}", {
|
|
7066
|
-
name: "{{ROUTE_NAME}}",
|
|
7067
|
-
version: 1,
|
|
7068
|
-
description: "",
|
|
7069
|
-
params: {
|
|
7070
|
-
// id: Assert("string"),
|
|
7071
|
-
},
|
|
7072
|
-
payload: Assert({
|
|
7073
|
-
|
|
7074
|
-
}),
|
|
7075
|
-
queries: Assert({
|
|
7076
|
-
|
|
7077
|
-
}),
|
|
7078
|
-
response: Assert({
|
|
7079
|
-
|
|
7080
|
-
}),
|
|
7081
|
-
roles: [ERole.USER],
|
|
7082
|
-
})
|
|
7083
|
-
export class {{NAME}}Controller {
|
|
7084
|
-
public async index(context: ContextType<{{TYPE_NAME}}RouteType>) {
|
|
7085
|
-
// const { id } = context.params;
|
|
7086
|
-
|
|
7087
|
-
return context.response.json({
|
|
7088
|
-
|
|
7089
|
-
});
|
|
7090
|
-
}
|
|
7091
|
-
}
|
|
7092
|
-
`;
|
|
7093
|
-
|
|
7094
|
-
// src/templates/controller.test.txt
|
|
7095
|
-
var controller_test_default = `import { describe, expect, test } from "bun:test";
|
|
7096
|
-
import { {{NAME}}Controller } from "@/controllers/{{NAME}}Controller";
|
|
7097
|
-
|
|
7098
|
-
describe("{{NAME}}Controller", () => {
|
|
7099
|
-
test("should have class name ending with 'Controller'", () => {
|
|
7100
|
-
expect({{NAME}}Controller.name.endsWith("Controller")).toBe(true);
|
|
7101
|
-
});
|
|
7102
|
-
|
|
7103
|
-
test("should have 'index' method", () => {
|
|
7104
|
-
expect({{NAME}}Controller.prototype.index).toBeDefined();
|
|
7105
|
-
expect(typeof {{NAME}}Controller.prototype.index).toBe("function");
|
|
7106
|
-
});
|
|
7107
|
-
});
|
|
7108
|
-
`;
|
|
7109
|
-
|
|
7110
|
-
// src/templates/controller.txt
|
|
7111
|
-
var controller_default = `import type { ContextType } from "@ooneex/controller";
|
|
7112
|
-
import { ERole } from "@ooneex/role";
|
|
7113
|
-
import { Route } from "@ooneex/routing";
|
|
7114
|
-
import { Assert } from "@ooneex/validation";
|
|
7115
|
-
|
|
7116
|
-
export type {{TYPE_NAME}}RouteType = {
|
|
7117
|
-
params: {
|
|
7118
|
-
|
|
7119
|
-
},
|
|
7120
|
-
payload: {
|
|
7121
|
-
|
|
7122
|
-
},
|
|
7123
|
-
queries: {
|
|
7124
|
-
|
|
7125
|
-
},
|
|
7126
|
-
response: {
|
|
7127
|
-
|
|
7128
|
-
},
|
|
7129
|
-
};
|
|
7130
|
-
|
|
7131
|
-
@Route.{{ROUTE_METHOD}}("{{ROUTE_PATH}}", {
|
|
7132
|
-
name: "{{ROUTE_NAME}}",
|
|
7133
|
-
version: 1,
|
|
7134
|
-
description: "",
|
|
7135
|
-
params: {
|
|
7136
|
-
// id: Assert("string"),
|
|
7137
|
-
},
|
|
7138
|
-
payload: Assert({
|
|
7139
|
-
|
|
7140
|
-
}),
|
|
7141
|
-
queries: Assert({
|
|
7142
|
-
|
|
7143
|
-
}),
|
|
7144
|
-
response: Assert({
|
|
7145
|
-
|
|
7146
|
-
}),
|
|
7147
|
-
roles: [ERole.USER],
|
|
7148
|
-
})
|
|
7149
|
-
export class {{NAME}}Controller {
|
|
7150
|
-
public async index(context: ContextType<{{TYPE_NAME}}RouteType>) {
|
|
7151
|
-
// const { id } = context.params;
|
|
7152
|
-
|
|
7153
|
-
return context.response.json({
|
|
7154
|
-
|
|
7155
|
-
});
|
|
7156
|
-
}
|
|
7157
|
-
}
|
|
7158
|
-
`;
|
|
7159
|
-
|
|
7160
|
-
// src/commands/MakeControllerCommand.ts
|
|
7161
|
-
class MakeControllerCommand {
|
|
7162
|
-
getName() {
|
|
7163
|
-
return "make:controller";
|
|
7164
|
-
}
|
|
7165
|
-
getDescription() {
|
|
7166
|
-
return "Generate a new controller class";
|
|
7167
|
-
}
|
|
7168
|
-
async addToModule(modulePath, controllerName) {
|
|
7169
|
-
let content = await Bun.file(modulePath).text();
|
|
7170
|
-
const className = `${controllerName}Controller`;
|
|
7171
|
-
const importLine = `import { ${className} } from "./controllers/${className}";
|
|
7172
|
-
`;
|
|
7173
|
-
const lastImportIndex = content.lastIndexOf("import ");
|
|
7174
|
-
const lineEnd = content.indexOf(`
|
|
7175
|
-
`, lastImportIndex);
|
|
7176
|
-
content = `${content.slice(0, lineEnd + 1)}${importLine}${content.slice(lineEnd + 1)}`;
|
|
7177
|
-
const regex = /(controllers:\s*\[)([^\]]*)/s;
|
|
7178
|
-
const match = content.match(regex);
|
|
7179
|
-
if (match) {
|
|
7180
|
-
const existing = match[2]?.trim();
|
|
7181
|
-
const newValue = existing ? `${existing}, ${className}` : className;
|
|
7182
|
-
content = content.replace(regex, `$1${newValue}`);
|
|
7183
|
-
}
|
|
7184
|
-
await Bun.write(modulePath, content);
|
|
7185
|
-
}
|
|
7186
|
-
async run(options) {
|
|
7187
|
-
let { name, module, isSocket } = options;
|
|
7188
|
-
if (!name) {
|
|
7189
|
-
name = await askName({ message: "Enter controller name" });
|
|
7190
|
-
}
|
|
7191
|
-
if (isSocket === undefined) {
|
|
7192
|
-
isSocket = await askConfirm({ message: "Is this a socket controller?" });
|
|
7193
|
-
}
|
|
7194
|
-
name = toPascalCase4(name).replace(/Controller$/, "");
|
|
7195
|
-
const { route = {} } = options;
|
|
7196
|
-
const selectedTemplate = isSocket ? controller_socket_default : controller_default;
|
|
7197
|
-
let content = selectedTemplate.replaceAll("{{NAME}}", name);
|
|
7198
|
-
if (!route.name) {
|
|
7199
|
-
route.name = await askRouteName({ message: "Enter route name (e.g., api.user.create)" });
|
|
7200
|
-
}
|
|
7201
|
-
const routeTypeName = toPascalCase4(route.name);
|
|
7202
|
-
content = content.replaceAll("{{ROUTE_NAME}}", route.name).replaceAll("{{TYPE_NAME}}", routeTypeName);
|
|
7203
|
-
if (!route.path) {
|
|
7204
|
-
route.path = await askRoutePath({ message: "Enter route path", initial: "/" });
|
|
7205
|
-
}
|
|
7206
|
-
const routePath = `/${toKebabCase2(trim(route.path, "/"))}`;
|
|
7207
|
-
content = content.replaceAll("{{ROUTE_PATH}}", routePath);
|
|
7208
|
-
if (!isSocket && !route.method) {
|
|
7209
|
-
route.method = await askRouteMethod({ message: "Enter route method" });
|
|
7210
|
-
}
|
|
7211
|
-
if (!isSocket && route.method) {
|
|
7212
|
-
content = content.replaceAll("{{ROUTE_METHOD}}", route.method.toLowerCase());
|
|
7213
|
-
}
|
|
7214
|
-
if (module) {
|
|
7215
|
-
await ensureModule(module);
|
|
7216
|
-
}
|
|
7217
|
-
const base = module ? join6("modules", module) : ".";
|
|
7218
|
-
const controllersLocalDir = join6(base, "src", "controllers");
|
|
7219
|
-
const controllersDir = join6(process.cwd(), controllersLocalDir);
|
|
7220
|
-
const filePath = join6(controllersDir, `${name}Controller.ts`);
|
|
7221
|
-
await Bun.write(filePath, content);
|
|
7222
|
-
const testContent = controller_test_default.replace(/{{NAME}}/g, name);
|
|
7223
|
-
const testsLocalDir = join6(base, "tests", "controllers");
|
|
7224
|
-
const testsDir = join6(process.cwd(), testsLocalDir);
|
|
7225
|
-
const testFilePath = join6(testsDir, `${name}Controller.spec.ts`);
|
|
7226
|
-
await Bun.write(testFilePath, testContent);
|
|
7227
|
-
const modulePascalName = module ? toPascalCase4(module) : toPascalCase4(basename(process.cwd()));
|
|
7228
|
-
const modulePath = join6(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
7229
|
-
if (await Bun.file(modulePath).exists()) {
|
|
7230
|
-
await this.addToModule(modulePath, name);
|
|
7231
|
-
}
|
|
7232
|
-
const logger = new TerminalLogger5;
|
|
7233
|
-
logger.success(`${join6(controllersLocalDir, name)}Controller.ts created successfully`, undefined, {
|
|
7234
|
-
showTimestamp: false,
|
|
7235
|
-
showArrow: false,
|
|
7236
|
-
useSymbol: true
|
|
7237
|
-
});
|
|
7238
|
-
logger.success(`${join6(testsLocalDir, name)}Controller.spec.ts created successfully`, undefined, {
|
|
7239
|
-
showTimestamp: false,
|
|
7240
|
-
showArrow: false,
|
|
7241
|
-
useSymbol: true
|
|
7242
|
-
});
|
|
7243
|
-
const packageJsonPath = join6(process.cwd(), "package.json");
|
|
7244
|
-
const packageJson = await Bun.file(packageJsonPath).json();
|
|
7245
|
-
const deps = packageJson.dependencies ?? {};
|
|
7246
|
-
const devDeps = packageJson.devDependencies ?? {};
|
|
7247
|
-
if (!deps["@ooneex/controller"] && !devDeps["@ooneex/controller"]) {
|
|
7248
|
-
const install = Bun.spawn(["bun", "add", "@ooneex/controller"], {
|
|
7249
|
-
cwd: process.cwd(),
|
|
7250
|
-
stdout: "ignore",
|
|
7251
|
-
stderr: "inherit"
|
|
7252
|
-
});
|
|
7253
|
-
await install.exited;
|
|
7254
|
-
}
|
|
7255
|
-
}
|
|
7256
|
-
}
|
|
7257
|
-
MakeControllerCommand = __legacyDecorateClassTS([
|
|
7258
|
-
decorator5.command()
|
|
7259
|
-
], MakeControllerCommand);
|
|
7260
|
-
|
|
7261
|
-
// src/commands/MakeAppCommand.ts
|
|
7262
|
-
class MakeAppCommand {
|
|
7263
|
-
getName() {
|
|
7264
|
-
return "make:app";
|
|
7265
|
-
}
|
|
7266
|
-
getDescription() {
|
|
7267
|
-
return "Generate a new application";
|
|
7268
|
-
}
|
|
7269
|
-
async run(options) {
|
|
7270
|
-
let { name, destination } = options;
|
|
7271
|
-
if (!name) {
|
|
7272
|
-
name = await askName({ message: "Enter application name" });
|
|
7273
|
-
}
|
|
7274
|
-
const kebabName = toKebabCase3(name);
|
|
7275
|
-
if (!destination) {
|
|
7276
|
-
destination = await askDestination({ message: "Enter destination path", initial: kebabName });
|
|
7020
|
+
const kebabName = toKebabCase2(name);
|
|
7021
|
+
if (!destination) {
|
|
7022
|
+
destination = await askDestination({ message: "Enter destination path", initial: kebabName });
|
|
7277
7023
|
}
|
|
7278
7024
|
const packageContent = package_json_default.replace(/{{NAME}}/g, kebabName);
|
|
7279
|
-
await Bun.write(
|
|
7280
|
-
await Bun.write(
|
|
7281
|
-
await Bun.write(
|
|
7282
|
-
await Bun.write(
|
|
7283
|
-
await Bun.write(
|
|
7284
|
-
await Bun.write(
|
|
7285
|
-
await Bun.write(
|
|
7286
|
-
await Bun.write(
|
|
7287
|
-
await Bun.write(
|
|
7025
|
+
await Bun.write(join9(destination, ".commitlintrc.ts"), _commitlintrc_ts_default);
|
|
7026
|
+
await Bun.write(join9(destination, ".gitignore"), _gitignore_default);
|
|
7027
|
+
await Bun.write(join9(destination, "biome.jsonc"), biome_jsonc_default);
|
|
7028
|
+
await Bun.write(join9(destination, "bunfig.toml"), bunfig_toml_default);
|
|
7029
|
+
await Bun.write(join9(destination, "nx.json"), nx_json_default);
|
|
7030
|
+
await Bun.write(join9(destination, "package.json"), packageContent);
|
|
7031
|
+
await Bun.write(join9(destination, "README.md"), README_md_default.replace(/{{NAME}}/g, kebabName));
|
|
7032
|
+
await Bun.write(join9(destination, "tsconfig.json"), tsconfig_json_default);
|
|
7033
|
+
await Bun.write(join9(destination, ".zed", "settings.json"), zed_settings_json_default);
|
|
7288
7034
|
const makeModuleCommand = new MakeModuleCommand;
|
|
7289
7035
|
await makeModuleCommand.run({
|
|
7290
7036
|
name: "app",
|
|
@@ -7293,34 +7039,23 @@ class MakeAppCommand {
|
|
|
7293
7039
|
skipMigrations: true,
|
|
7294
7040
|
skipSeeds: true
|
|
7295
7041
|
});
|
|
7296
|
-
const appModulePackagePath =
|
|
7042
|
+
const appModulePackagePath = join9(destination, "modules", "app", "package.json");
|
|
7297
7043
|
const appModulePackageJson = await Bun.file(appModulePackagePath).json();
|
|
7298
7044
|
appModulePackageJson.scripts.dev = "docker compose up -d && bun --hot run ./src/index.ts";
|
|
7299
|
-
appModulePackageJson.scripts
|
|
7045
|
+
appModulePackageJson.scripts.stop = "docker compose down";
|
|
7300
7046
|
appModulePackageJson.scripts.build = "bun build ./src/index.ts --outdir ./dist --target bun";
|
|
7301
7047
|
await Bun.write(appModulePackagePath, JSON.stringify(appModulePackageJson, null, 2));
|
|
7302
7048
|
const envContent = env_default.replace(/^DATABASE_URL=/m, 'DATABASE_URL="postgresql://ooneex:ooneex@localhost:5432/ooneex"').replace(/^CACHE_REDIS_URL=/m, 'CACHE_REDIS_URL="redis://localhost:6379"').replace(/^PUBSUB_REDIS_URL=/m, 'PUBSUB_REDIS_URL="redis://localhost:6379"').replace(/^RATE_LIMIT_REDIS_URL=/m, 'RATE_LIMIT_REDIS_URL="redis://localhost:6379"').replace(/^DATABASE_REDIS_URL=/m, 'DATABASE_REDIS_URL="redis://localhost:6379"');
|
|
7303
|
-
await Bun.write(
|
|
7304
|
-
await Bun.write(
|
|
7305
|
-
await Bun.write(
|
|
7306
|
-
await Bun.write(
|
|
7049
|
+
await Bun.write(join9(destination, "modules", "app", ".env"), envContent);
|
|
7050
|
+
await Bun.write(join9(destination, "modules", "app", ".env.example"), env_default);
|
|
7051
|
+
await Bun.write(join9(destination, "modules", "app", "src", "databases", "AppDatabase.ts"), app_database_default);
|
|
7052
|
+
await Bun.write(join9(destination, "modules", "app", "src", "index.ts"), index_ts_default);
|
|
7307
7053
|
const snakeName = toSnakeCase(name);
|
|
7308
7054
|
const dockerComposeContent = docker_compose_yml_default.replace(/{{NAME}}/g, snakeName);
|
|
7309
|
-
await Bun.write(
|
|
7055
|
+
await Bun.write(join9(destination, "modules", "app", "docker-compose.yml"), dockerComposeContent);
|
|
7310
7056
|
const dockerfileContent = Dockerfile_default.replace(/{{NAME}}/g, snakeName);
|
|
7311
|
-
await Bun.write(
|
|
7312
|
-
await Bun.write(
|
|
7313
|
-
const makeControllerCommand = new MakeControllerCommand;
|
|
7314
|
-
await makeControllerCommand.run({
|
|
7315
|
-
name: "HealthCheck",
|
|
7316
|
-
isSocket: false,
|
|
7317
|
-
module: "app",
|
|
7318
|
-
route: {
|
|
7319
|
-
name: "api.health.check",
|
|
7320
|
-
path: "/healthcheck",
|
|
7321
|
-
method: "GET"
|
|
7322
|
-
}
|
|
7323
|
-
});
|
|
7057
|
+
await Bun.write(join9(destination, "modules", "app", "Dockerfile"), dockerfileContent);
|
|
7058
|
+
await Bun.write(join9(destination, "modules", "app", "var", ".gitkeep"), "");
|
|
7324
7059
|
const gitInit = Bun.spawn(["git", "init"], { cwd: destination, stdout: "ignore", stderr: "inherit" });
|
|
7325
7060
|
await gitInit.exited;
|
|
7326
7061
|
const addDeps = Bun.spawn([
|
|
@@ -7379,9 +7114,9 @@ class MakeAppCommand {
|
|
|
7379
7114
|
await addDevDeps.exited;
|
|
7380
7115
|
const huskyInit = Bun.spawn(["bunx", "husky", "init"], { cwd: destination, stdout: "ignore", stderr: "inherit" });
|
|
7381
7116
|
await huskyInit.exited;
|
|
7382
|
-
await Bun.write(
|
|
7383
|
-
await Bun.write(
|
|
7384
|
-
const logger = new
|
|
7117
|
+
await Bun.write(join9(destination, ".husky", "pre-commit"), "lint-staged");
|
|
7118
|
+
await Bun.write(join9(destination, ".husky", "commit-msg"), `bunx commitlint --edit "$1"`);
|
|
7119
|
+
const logger = new TerminalLogger8;
|
|
7385
7120
|
logger.success(`${kebabName} created successfully at ${destination}`, undefined, {
|
|
7386
7121
|
showTimestamp: false,
|
|
7387
7122
|
showArrow: false,
|
|
@@ -7390,512 +7125,978 @@ class MakeAppCommand {
|
|
|
7390
7125
|
}
|
|
7391
7126
|
}
|
|
7392
7127
|
MakeAppCommand = __legacyDecorateClassTS([
|
|
7393
|
-
|
|
7128
|
+
decorator9.command()
|
|
7394
7129
|
], MakeAppCommand);
|
|
7395
7130
|
// src/commands/MakeCacheCommand.ts
|
|
7396
|
-
import { join as
|
|
7397
|
-
import { decorator as
|
|
7398
|
-
import { TerminalLogger as
|
|
7399
|
-
import { toPascalCase as
|
|
7131
|
+
import { join as join10 } from "path";
|
|
7132
|
+
import { decorator as decorator10 } from "@ooneex/command";
|
|
7133
|
+
import { TerminalLogger as TerminalLogger9 } from "@ooneex/logger";
|
|
7134
|
+
import { toPascalCase as toPascalCase4 } from "@ooneex/utils";
|
|
7135
|
+
|
|
7136
|
+
// src/templates/cache.test.txt
|
|
7137
|
+
var cache_test_default = `import { describe, expect, test } from "bun:test";
|
|
7138
|
+
import { {{NAME}}Cache } from "@/cache/{{NAME}}Cache";
|
|
7139
|
+
|
|
7140
|
+
describe("{{NAME}}Cache", () => {
|
|
7141
|
+
test("should have class name ending with 'Cache'", () => {
|
|
7142
|
+
expect({{NAME}}Cache.name.endsWith("Cache")).toBe(true);
|
|
7143
|
+
});
|
|
7144
|
+
|
|
7145
|
+
test("should have 'get' method", () => {
|
|
7146
|
+
expect({{NAME}}Cache.prototype.get).toBeDefined();
|
|
7147
|
+
expect(typeof {{NAME}}Cache.prototype.get).toBe("function");
|
|
7148
|
+
});
|
|
7149
|
+
|
|
7150
|
+
test("should have 'set' method", () => {
|
|
7151
|
+
expect({{NAME}}Cache.prototype.set).toBeDefined();
|
|
7152
|
+
expect(typeof {{NAME}}Cache.prototype.set).toBe("function");
|
|
7153
|
+
});
|
|
7154
|
+
|
|
7155
|
+
test("should have 'delete' method", () => {
|
|
7156
|
+
expect({{NAME}}Cache.prototype.delete).toBeDefined();
|
|
7157
|
+
expect(typeof {{NAME}}Cache.prototype.delete).toBe("function");
|
|
7158
|
+
});
|
|
7159
|
+
|
|
7160
|
+
test("should have 'has' method", () => {
|
|
7161
|
+
expect({{NAME}}Cache.prototype.has).toBeDefined();
|
|
7162
|
+
expect(typeof {{NAME}}Cache.prototype.has).toBe("function");
|
|
7163
|
+
});
|
|
7164
|
+
});
|
|
7165
|
+
`;
|
|
7166
|
+
|
|
7167
|
+
// src/templates/cache.txt
|
|
7168
|
+
var cache_default = `import { CacheException, decorator } from "@ooneex/cache";
|
|
7169
|
+
import type { ICache } from "@ooneex/cache";
|
|
7170
|
+
|
|
7171
|
+
@decorator.cache()
|
|
7172
|
+
export class {{NAME}}Cache implements ICache {
|
|
7173
|
+
public async get<T = unknown>(key: string): Promise<T | undefined> {
|
|
7174
|
+
throw new CacheException(\`Failed to get key "\${key}": Not implemented\`);
|
|
7175
|
+
}
|
|
7176
|
+
|
|
7177
|
+
public async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void> {
|
|
7178
|
+
throw new CacheException(\`Failed to set key "\${key}": Not implemented\`);
|
|
7179
|
+
}
|
|
7180
|
+
|
|
7181
|
+
public async delete(key: string): Promise<boolean> {
|
|
7182
|
+
throw new CacheException(\`Failed to delete key "\${key}": Not implemented\`);
|
|
7183
|
+
}
|
|
7184
|
+
|
|
7185
|
+
public async has(key: string): Promise<boolean> {
|
|
7186
|
+
throw new CacheException(\`Failed to check if key "\${key}" exists: Not implemented\`);
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
`;
|
|
7190
|
+
|
|
7191
|
+
// src/commands/MakeCacheCommand.ts
|
|
7192
|
+
class MakeCacheCommand {
|
|
7193
|
+
getName() {
|
|
7194
|
+
return "make:cache";
|
|
7195
|
+
}
|
|
7196
|
+
getDescription() {
|
|
7197
|
+
return "Generate a new cache class";
|
|
7198
|
+
}
|
|
7199
|
+
async run(options) {
|
|
7200
|
+
let { name, module } = options;
|
|
7201
|
+
if (!name) {
|
|
7202
|
+
name = await askName({ message: "Enter cache name" });
|
|
7203
|
+
}
|
|
7204
|
+
name = toPascalCase4(name).replace(/Cache$/, "");
|
|
7205
|
+
const content = cache_default.replace(/{{NAME}}/g, name);
|
|
7206
|
+
if (module) {
|
|
7207
|
+
await ensureModule(module);
|
|
7208
|
+
}
|
|
7209
|
+
const base = module ? join10("modules", module) : ".";
|
|
7210
|
+
const cacheLocalDir = join10(base, "src", "cache");
|
|
7211
|
+
const cacheDir = join10(process.cwd(), cacheLocalDir);
|
|
7212
|
+
const filePath = join10(cacheDir, `${name}Cache.ts`);
|
|
7213
|
+
await Bun.write(filePath, content);
|
|
7214
|
+
const testContent = cache_test_default.replace(/{{NAME}}/g, name);
|
|
7215
|
+
const testsLocalDir = join10(base, "tests", "cache");
|
|
7216
|
+
const testsDir = join10(process.cwd(), testsLocalDir);
|
|
7217
|
+
const testFilePath = join10(testsDir, `${name}Cache.spec.ts`);
|
|
7218
|
+
await Bun.write(testFilePath, testContent);
|
|
7219
|
+
const logger = new TerminalLogger9;
|
|
7220
|
+
logger.success(`${join10(cacheLocalDir, name)}Cache.ts created successfully`, undefined, {
|
|
7221
|
+
showTimestamp: false,
|
|
7222
|
+
showArrow: false,
|
|
7223
|
+
useSymbol: true
|
|
7224
|
+
});
|
|
7225
|
+
logger.success(`${join10(testsLocalDir, name)}Cache.spec.ts created successfully`, undefined, {
|
|
7226
|
+
showTimestamp: false,
|
|
7227
|
+
showArrow: false,
|
|
7228
|
+
useSymbol: true
|
|
7229
|
+
});
|
|
7230
|
+
const packageJsonPath = join10(process.cwd(), "package.json");
|
|
7231
|
+
const packageJson = await Bun.file(packageJsonPath).json();
|
|
7232
|
+
const deps = packageJson.dependencies ?? {};
|
|
7233
|
+
const devDeps = packageJson.devDependencies ?? {};
|
|
7234
|
+
if (!deps["@ooneex/cache"] && !devDeps["@ooneex/cache"]) {
|
|
7235
|
+
const install = Bun.spawn(["bun", "add", "@ooneex/cache"], {
|
|
7236
|
+
cwd: process.cwd(),
|
|
7237
|
+
stdout: "ignore",
|
|
7238
|
+
stderr: "inherit"
|
|
7239
|
+
});
|
|
7240
|
+
await install.exited;
|
|
7241
|
+
}
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
MakeCacheCommand = __legacyDecorateClassTS([
|
|
7245
|
+
decorator10.command()
|
|
7246
|
+
], MakeCacheCommand);
|
|
7247
|
+
// src/commands/MakeClaudeSkillCommand.ts
|
|
7248
|
+
import { join as join11 } from "path";
|
|
7249
|
+
import { decorator as decorator11 } from "@ooneex/command";
|
|
7250
|
+
import { TerminalLogger as TerminalLogger10 } from "@ooneex/logger";
|
|
7251
|
+
|
|
7252
|
+
// src/templates/claude/skills/commit.md.txt
|
|
7253
|
+
var commit_md_default = `---
|
|
7254
|
+
name: commit
|
|
7255
|
+
description: Create commit messages grouped by module. Analyzes git changes, groups files under modules/ by module name, and creates separate commits following commitlint conventions. Uses common scope for non-module changes.
|
|
7256
|
+
---
|
|
7257
|
+
|
|
7258
|
+
# Commit by Module
|
|
7259
|
+
|
|
7260
|
+
Create separate commits for each modified module, following the project's commitlint conventions.
|
|
7261
|
+
|
|
7262
|
+
## Workflow
|
|
7263
|
+
|
|
7264
|
+
1. **Analyze staged/unstaged changes**
|
|
7265
|
+
- Run \`git status --porcelain\` to get all modified files
|
|
7266
|
+
- Group changes by module (files under \`modules/<module-name>/\`)
|
|
7267
|
+
- Identify non-module changes (files not in \`modules/\`) as scope \`common\`
|
|
7268
|
+
|
|
7269
|
+
2. **For each module with changes**
|
|
7270
|
+
- Stage only that module's files: \`git add modules/<module-name>/\`
|
|
7271
|
+
- Determine the appropriate commit type based on changes
|
|
7272
|
+
- Create a commit with proper format: \`type(scope): Subject\`
|
|
7273
|
+
- The scope is the module name in lower-case
|
|
7274
|
+
- Repeat for next module
|
|
7275
|
+
|
|
7276
|
+
3. **Handle non-module changes**
|
|
7277
|
+
- Files outside \`modules/\` (e.g., \`bun.lock\`, \`package.json\`, config files, \`packages/\` changes) use scope \`common\`
|
|
7278
|
+
- Stage and commit separately from module changes
|
|
7279
|
+
|
|
7280
|
+
## Commit Message Format
|
|
7281
|
+
|
|
7282
|
+
\`\`\`
|
|
7283
|
+
type(scope): Subject line
|
|
7284
|
+
\`\`\`
|
|
7285
|
+
|
|
7286
|
+
### Valid Types
|
|
7287
|
+
- \`feat\`: New feature
|
|
7288
|
+
- \`fix\`: Bug fix
|
|
7289
|
+
- \`refactor\`: Code refactoring (no new feature, no bug fix)
|
|
7290
|
+
- \`test\`: Adding/updating tests
|
|
7291
|
+
- \`chore\`: Maintenance tasks (dependencies, configs)
|
|
7292
|
+
- \`docs\`: Documentation changes
|
|
7293
|
+
- \`style\`: Code style changes (formatting, whitespace)
|
|
7294
|
+
- \`perf\`: Performance improvements
|
|
7295
|
+
- \`build\`: Build system changes
|
|
7296
|
+
- \`ci\`: CI configuration changes
|
|
7297
|
+
- \`revert\`: Revert previous commit
|
|
7298
|
+
|
|
7299
|
+
### Scope Rules
|
|
7300
|
+
- For files under \`modules/<name>/\`: use the module name as scope (e.g., \`module\` scope matches the commitlint config)
|
|
7301
|
+
- For all other files: use \`common\`
|
|
7302
|
+
- Scope must be lower-case
|
|
7303
|
+
- Scope must never be empty
|
|
7304
|
+
- If the module name matches a valid commitlint scope, use it directly
|
|
7305
|
+
- If it does not match, use \`module\` as the scope
|
|
7306
|
+
|
|
7307
|
+
### Subject Rules
|
|
7308
|
+
- Use sentence-case, start-case, pascal-case, or upper-case
|
|
7309
|
+
- No period at the end
|
|
7310
|
+
- Maximum 100 characters for entire header
|
|
7311
|
+
- Use imperative mood ("Add feature" not "Added feature")
|
|
7312
|
+
|
|
7313
|
+
## Determining Commit Type
|
|
7314
|
+
|
|
7315
|
+
Analyze the changes to determine the appropriate type:
|
|
7316
|
+
|
|
7317
|
+
| Change Pattern | Type |
|
|
7318
|
+
|---------------|------|
|
|
7319
|
+
| New files with functionality | \`feat\` |
|
|
7320
|
+
| Bug fixes, error corrections | \`fix\` |
|
|
7321
|
+
| Code restructuring, renaming | \`refactor\` |
|
|
7322
|
+
| New/updated test files (\`*.spec.ts\`) | \`test\` |
|
|
7323
|
+
| Documentation files (\`*.md\`) | \`docs\` |
|
|
7324
|
+
| Dependency updates, lock files | \`chore\` |
|
|
7325
|
+
| Build configs, scripts | \`build\` |
|
|
7326
|
+
| CI/CD files | \`ci\` |
|
|
7327
|
+
| Formatting only | \`style\` |
|
|
7328
|
+
| Performance optimizations | \`perf\` |
|
|
7400
7329
|
|
|
7401
|
-
|
|
7402
|
-
var cache_test_default = `import { describe, expect, test } from "bun:test";
|
|
7403
|
-
import { {{NAME}}Cache } from "@/cache/{{NAME}}Cache";
|
|
7330
|
+
## Examples
|
|
7404
7331
|
|
|
7405
|
-
|
|
7406
|
-
test("should have class name ending with 'Cache'", () => {
|
|
7407
|
-
expect({{NAME}}Cache.name.endsWith("Cache")).toBe(true);
|
|
7408
|
-
});
|
|
7332
|
+
### Example 1: Multiple Module Changes
|
|
7409
7333
|
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7334
|
+
Git status shows:
|
|
7335
|
+
\`\`\`
|
|
7336
|
+
M modules/user/src/services/UserService.ts
|
|
7337
|
+
A modules/user/src/services/AuthService.ts
|
|
7338
|
+
M modules/user/tests/UserService.spec.ts
|
|
7339
|
+
M modules/product/src/entities/Product.ts
|
|
7340
|
+
M modules/product/src/repositories/ProductRepository.ts
|
|
7341
|
+
M bun.lock
|
|
7342
|
+
M packages/cache/src/index.ts
|
|
7343
|
+
\`\`\`
|
|
7414
7344
|
|
|
7415
|
-
|
|
7416
|
-
|
|
7417
|
-
|
|
7418
|
-
|
|
7345
|
+
Commands to execute:
|
|
7346
|
+
\`\`\`bash
|
|
7347
|
+
# Commit user module
|
|
7348
|
+
git add modules/user/
|
|
7349
|
+
git commit -m "feat(user): Add AuthService and update UserService"
|
|
7419
7350
|
|
|
7420
|
-
|
|
7421
|
-
|
|
7422
|
-
|
|
7423
|
-
});
|
|
7351
|
+
# Commit product module
|
|
7352
|
+
git add modules/product/
|
|
7353
|
+
git commit -m "refactor(product): Update Product entity and repository"
|
|
7424
7354
|
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7355
|
+
# Commit non-module changes
|
|
7356
|
+
git add bun.lock packages/cache/
|
|
7357
|
+
git commit -m "chore(common): Update dependencies and cache package"
|
|
7358
|
+
\`\`\`
|
|
7359
|
+
|
|
7360
|
+
### Example 2: Single Module with Tests
|
|
7361
|
+
|
|
7362
|
+
Git status shows:
|
|
7363
|
+
\`\`\`
|
|
7364
|
+
A modules/payment/src/services/StripeService.ts
|
|
7365
|
+
A modules/payment/tests/StripeService.spec.ts
|
|
7366
|
+
\`\`\`
|
|
7367
|
+
|
|
7368
|
+
Command:
|
|
7369
|
+
\`\`\`bash
|
|
7370
|
+
git add modules/payment/
|
|
7371
|
+
git commit -m "feat(payment): Add StripeService with tests"
|
|
7372
|
+
\`\`\`
|
|
7373
|
+
|
|
7374
|
+
### Example 3: Only Non-module Changes
|
|
7375
|
+
|
|
7376
|
+
Git status shows:
|
|
7377
|
+
\`\`\`
|
|
7378
|
+
M package.json
|
|
7379
|
+
M bun.lock
|
|
7380
|
+
M packages/logger/src/Logger.ts
|
|
7381
|
+
\`\`\`
|
|
7382
|
+
|
|
7383
|
+
Command:
|
|
7384
|
+
\`\`\`bash
|
|
7385
|
+
git add package.json bun.lock packages/logger/
|
|
7386
|
+
git commit -m "chore(common): Update dependencies and logger package"
|
|
7387
|
+
\`\`\`
|
|
7388
|
+
|
|
7389
|
+
### Example 4: Module Refactoring with Deletions
|
|
7390
|
+
|
|
7391
|
+
Git status shows:
|
|
7392
|
+
\`\`\`
|
|
7393
|
+
D modules/order/src/services/OldOrderService.ts
|
|
7394
|
+
A modules/order/src/services/OrderService.ts
|
|
7395
|
+
M modules/order/src/index.ts
|
|
7396
|
+
\`\`\`
|
|
7397
|
+
|
|
7398
|
+
Command:
|
|
7399
|
+
\`\`\`bash
|
|
7400
|
+
git add modules/order/
|
|
7401
|
+
git commit -m "refactor(order): Replace OldOrderService with OrderService"
|
|
7402
|
+
\`\`\`
|
|
7403
|
+
|
|
7404
|
+
## Subject Line Guidelines
|
|
7405
|
+
|
|
7406
|
+
Write clear, descriptive subjects:
|
|
7407
|
+
|
|
7408
|
+
| Good | Bad |
|
|
7409
|
+
|------|-----|
|
|
7410
|
+
| \`Add Stripe payment integration\` | \`stripe\` |
|
|
7411
|
+
| \`Fix null check in user validation\` | \`fix bug\` |
|
|
7412
|
+
| \`Update order processing flow\` | \`changes\` |
|
|
7413
|
+
| \`Remove deprecated auth methods\` | \`cleanup\` |
|
|
7414
|
+
| \`Rename UserAdapter to UserService\` | \`rename\` |
|
|
7415
|
+
|
|
7416
|
+
## Pre-commit Checklist
|
|
7417
|
+
|
|
7418
|
+
Before committing, verify:
|
|
7419
|
+
1. Changes are logically grouped by module
|
|
7420
|
+
2. Tests pass for modified modules
|
|
7421
|
+
3. No debug code or console.logs left in
|
|
7422
|
+
4. Commit message follows the format exactly
|
|
7423
|
+
5. For Entity class, if property is optional, add \`null\` to its type.
|
|
7424
|
+
|
|
7425
|
+
## Handling Special Cases
|
|
7426
|
+
|
|
7427
|
+
### Mixed Changes in One Module
|
|
7428
|
+
If a module has both new features and bug fixes, prioritize:
|
|
7429
|
+
1. \`feat\` if primary change is new functionality
|
|
7430
|
+
2. \`fix\` if primary change is a bug fix
|
|
7431
|
+
3. Split into multiple commits if changes are truly independent
|
|
7432
|
+
|
|
7433
|
+
### Deleted Files Only
|
|
7434
|
+
Use \`refactor\` for removing deprecated code:
|
|
7435
|
+
\`\`\`bash
|
|
7436
|
+
git add modules/user/
|
|
7437
|
+
git commit -m "refactor(user): Remove deprecated UserAdapter"
|
|
7438
|
+
\`\`\`
|
|
7439
|
+
|
|
7440
|
+
### Renamed/Moved Files
|
|
7441
|
+
Use \`refactor\` for file reorganization:
|
|
7442
|
+
\`\`\`bash
|
|
7443
|
+
git add modules/product/
|
|
7444
|
+
git commit -m "refactor(product): Reorganize service file structure"
|
|
7445
|
+
\`\`\`
|
|
7430
7446
|
`;
|
|
7431
7447
|
|
|
7432
|
-
// src/templates/
|
|
7433
|
-
var
|
|
7434
|
-
import type { ICache } from "@ooneex/cache";
|
|
7448
|
+
// src/templates/claude/skills/make.ai.md.txt
|
|
7449
|
+
var make_ai_md_default = '---\nname: make:ai\ndescription: Generate a new AI class with its test file, then complete the generated code. Use when creating a new AI chat class that uses OpenAI via the @ooneex/ai package.\n---\n\n# Make AI Class\n\nGenerate a new AI class and its test file using the `make:ai` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the AI class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:ai --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/ai/<Name>Ai.ts` - The AI class file (or `modules/<module>/src/ai/<Name>Ai.ts` with `--module`)\n- `tests/ai/<Name>Ai.spec.ts` - The test file (or `modules/<module>/tests/ai/<Name>Ai.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the AI class\n\nEdit `src/ai/<Name>Ai.ts` to complete the implementation:\n\n- Update the prompt in the `run` method from `"My prompt"` to a meaningful prompt based on the class purpose\n- Update the prompt in the `runStream` method from `"My prompt"` to a meaningful prompt based on the class purpose\n- Add any additional configuration or methods relevant to the AI class purpose\n- Ensure proper typing for the `run<T>()` generic return type\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { decorator, type IAiChat, OpenAi, type OpenAiConfigType } from "@ooneex/ai";\nimport { inject } from "@ooneex/container";\n\n@decorator.ai()\nexport class <Name>Ai implements IAiChat<OpenAiConfigType> {\n constructor(@inject(OpenAi) private readonly ai: OpenAi) {}\n\n public async run<T>(prompt?: string, config?: Omit<OpenAiConfigType, "prompt">): Promise<T> {\n return this.ai.run<T>(prompt || "My prompt", config);\n }\n\n public async *runStream(\n prompt?: string,\n config?: Omit<OpenAiConfigType, "prompt" | "output">,\n ): AsyncGenerator<string, void, unknown> {\n yield* this.ai.runStream(prompt || "My prompt", config);\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/ai/<Name>Ai.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, run method, runStream method)\n- Add tests relevant to the specific AI class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Ai } from "@/ai/<Name>Ai";\n\ndescribe("<Name>Ai", () => {\n test("should have class name ending with \'Ai\'", () => {\n expect(<Name>Ai.name.endsWith("Ai")).toBe(true);\n });\n\n test("should have \'run\' method", () => {\n expect(<Name>Ai.prototype.run).toBeDefined();\n expect(typeof <Name>Ai.prototype.run).toBe("function");\n });\n\n test("should have \'runStream\' method", () => {\n expect(<Name>Ai.prototype.runStream).toBeDefined();\n expect(typeof <Name>Ai.prototype.runStream).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/ai/<Name>Ai.ts tests/ai/<Name>Ai.spec.ts\n```\n';
|
|
7435
7450
|
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
public async get<T = unknown>(key: string): Promise<T | undefined> {
|
|
7439
|
-
throw new CacheException(\`Failed to get key "\${key}": Not implemented\`);
|
|
7440
|
-
}
|
|
7451
|
+
// src/templates/claude/skills/make.analytics.md.txt
|
|
7452
|
+
var make_analytics_md_default = '---\nname: make:analytics\ndescription: Generate a new analytics class with its test file, then complete the generated code. Use when creating a new analytics tracking class that uses the @ooneex/analytics package.\n---\n\n# Make Analytics Class\n\nGenerate a new analytics class and its test file using the `make:analytics` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the analytics class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:analytics --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/analytics/<Name>Analytics.ts` - The analytics class file (or `modules/<module>/src/analytics/<Name>Analytics.ts` with `--module`)\n- `tests/analytics/<Name>Analytics.spec.ts` - The test file (or `modules/<module>/tests/analytics/<Name>Analytics.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the analytics class\n\nEdit `src/analytics/<Name>Analytics.ts` to complete the implementation:\n\n- Implement the `capture` method with actual analytics tracking logic\n- Define a proper type for `CaptureOptionsType` instead of `Record<string, unknown>` based on the analytics purpose\n- Add any additional methods or properties relevant to the analytics class purpose\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { type IAnalytics, decorator } from "@ooneex/analytics";\n\ntype CaptureOptionsType = Record<string, unknown>;\n\n@decorator.analytics()\nexport class <Name>Analytics<T extends CaptureOptionsType = CaptureOptionsType> implements IAnalytics<T> {\n public capture(options: T): void {\n // console.log("Analytics captured:", options);\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/analytics/<Name>Analytics.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, capture method)\n- Add tests relevant to the specific analytics class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Analytics } from "@/analytics/<Name>Analytics";\n\ndescribe("<Name>Analytics", () => {\n test("should have class name ending with \'Analytics\'", () => {\n expect(<Name>Analytics.name.endsWith("Analytics")).toBe(true);\n });\n\n test("should have \'capture\' method", () => {\n expect(<Name>Analytics.prototype.capture).toBeDefined();\n expect(typeof <Name>Analytics.prototype.capture).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/analytics/<Name>Analytics.ts tests/analytics/<Name>Analytics.spec.ts\n```\n';
|
|
7441
7453
|
|
|
7442
|
-
|
|
7443
|
-
throw new CacheException(
|
|
7444
|
-
}
|
|
7454
|
+
// src/templates/claude/skills/make.cache.md.txt
|
|
7455
|
+
var make_cache_md_default = '---\nname: make:cache\ndescription: Generate a new cache adapter class with its test file, then complete the generated code. Use when creating a new cache adapter that implements the ICache interface from @ooneex/cache.\n---\n\n# Make Cache Class\n\nGenerate a new cache class and its test file using the `make:cache` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the cache class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:cache --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/cache/<Name>Cache.ts` - The cache class file (or `modules/<module>/src/cache/<Name>Cache.ts` with `--module`)\n- `tests/cache/<Name>Cache.spec.ts` - The test file (or `modules/<module>/tests/cache/<Name>Cache.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the cache class\n\nEdit `src/cache/<Name>Cache.ts` to complete the implementation:\n\n- Implement the `get` method to retrieve cached values by key\n- Implement the `set` method to store values with optional TTL\n- Implement the `delete` method to remove cached entries\n- Implement the `has` method to check key existence\n- Replace the `CacheException` throws with actual cache logic\n- Inject any required dependencies (e.g., Redis client) via the constructor\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { CacheException, type ICache, decorator } from "@ooneex/cache";\n\n@decorator.cache()\nexport class <Name>Cache implements ICache {\n public async get<T = unknown>(key: string): Promise<T | undefined> {\n throw new CacheException(`Failed to get key "${key}": Not implemented`);\n }\n\n public async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void> {\n throw new CacheException(`Failed to set key "${key}": Not implemented`);\n }\n\n public async delete(key: string): Promise<boolean> {\n throw new CacheException(`Failed to delete key "${key}": Not implemented`);\n }\n\n public async has(key: string): Promise<boolean> {\n throw new CacheException(`Failed to check if key "${key}" exists: Not implemented`);\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/cache/<Name>Cache.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, get, set, delete, has methods)\n- Add tests relevant to the specific cache class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Cache } from "@/cache/<Name>Cache";\n\ndescribe("<Name>Cache", () => {\n test("should have class name ending with \'Cache\'", () => {\n expect(<Name>Cache.name.endsWith("Cache")).toBe(true);\n });\n\n test("should have \'get\' method", () => {\n expect(<Name>Cache.prototype.get).toBeDefined();\n expect(typeof <Name>Cache.prototype.get).toBe("function");\n });\n\n test("should have \'set\' method", () => {\n expect(<Name>Cache.prototype.set).toBeDefined();\n expect(typeof <Name>Cache.prototype.set).toBe("function");\n });\n\n test("should have \'delete\' method", () => {\n expect(<Name>Cache.prototype.delete).toBeDefined();\n expect(typeof <Name>Cache.prototype.delete).toBe("function");\n });\n\n test("should have \'has\' method", () => {\n expect(<Name>Cache.prototype.has).toBeDefined();\n expect(typeof <Name>Cache.prototype.has).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/cache/<Name>Cache.ts tests/cache/<Name>Cache.spec.ts\n```\n';
|
|
7445
7456
|
|
|
7446
|
-
|
|
7447
|
-
throw new CacheException(\`Failed to delete key "\${key}": Not implemented\`);
|
|
7448
|
-
}
|
|
7457
|
+
// src/templates/claude/skills/make.controller.md.txt
|
|
7458
|
+
var make_controller_md_default = "---\nname: make:controller\ndescription: Generate a new controller class with route type and test file, then complete the generated code. Use when creating a new HTTP or WebSocket controller with routing, validation, and role-based access.\n---\n\n# Make Controller Class\n\nGenerate a new controller class, its route type, and test file using the `make:controller` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from \"@ooneex/container\";\nimport { decorator, type ISeed } from \"@ooneex/seeds\";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the controller class and related files:\n\n```bash\nbunx @ooneex/cli@latest make:controller --name=<name> --module=<module> --is-socket=<true|false> --route-name=<route.name> --route-path=<route.path> --route-method=<route.method>\n```\n\nWhere:\n- `<name>` is the controller name provided by the user\n- `--module` is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root\n- `--is-socket` determines HTTP vs WebSocket controller (defaults to `false`)\n- `--route-name` is the route name using dot notation: `<resource>.<action>` (e.g., `user.create`, `book.list`, `flashcard.delete`)\n- `--route-path` is the route path (e.g., `/api/users`)\n- `--route-method` is the HTTP method (e.g., `get`, `post`, `put`, `patch`, `delete`) \u2014 only for HTTP controllers\n\nThe command will generate (paths prefixed with `modules/<module>/` when `--module` is provided):\n- `src/controllers/<Name>Controller.ts` - The controller class file\n- `src/types/routes/<route.name>.ts` - The route type file (will be moved into the controller file \u2014 see step 3)\n- `tests/controllers/<Name>Controller.spec.ts` - The test file\n\n### 2. Read the generated files\n\nRead all three generated files to understand the scaffolded code.\n\n### 3. Complete the route type\n\n**IMPORTANT: Keep the route type inside the controller file**, not in a separate type file. Define the type directly in `src/controllers/<Name>Controller.ts` above the class definition. Delete the generated `src/types/routes/<route.name>.ts` file if it was created.\n\n**Remove unnecessary `params`, `payload`, and `queries` \u2014 only include what the route actually needs:**\n\n- **`params`** \u2014 Include only when the route path contains dynamic segments (e.g., `/api/users/:id`). Remove entirely for routes with no URL parameters (e.g., `/api/users`).\n- **`payload`** \u2014 Include only for methods that accept a request body (`post`, `put`, `patch`). Remove entirely for `get` and `delete` routes.\n- **`queries`** \u2014 Include only when the route supports query string filtering, pagination, or sorting (e.g., list/search endpoints). Remove entirely when not needed.\n- **`response`** \u2014 Always include.\n\nThe route type structure follows this pattern (remove unused sections):\n\n```typescript\n// Example: GET /api/users (list) \u2014 no params, no payload, has queries\ntype <TypeName>RouteType = {\n queries: {\n\n },\n response: {\n\n },\n};\n\n// Example: POST /api/users (create) \u2014 no params, has payload, no queries\ntype <TypeName>RouteType = {\n payload: {\n\n },\n response: {\n\n },\n};\n\n// Example: GET /api/users/:id (detail) \u2014 has params, no payload, no queries\ntype <TypeName>RouteType = {\n params: {\n\n },\n response: {\n\n },\n};\n\n// Example: PUT /api/users/:id (update) \u2014 has params, has payload, no queries\ntype <TypeName>RouteType = {\n params: {\n\n },\n payload: {\n\n },\n response: {\n\n },\n};\n```\n\n### 4. Complete the controller class\n\nEdit `src/controllers/<Name>Controller.ts` to complete the implementation:\n\n- Set appropriate `roles` for access control\n- Add a meaningful `description` for the route that explains what the endpoint does (e.g., `\"Create a new user account\"`, `\"List all books with pagination\"`)\n- **Keep the controller thin** \u2014 do NOT put business logic in the controller. Put all logic in the corresponding service and inject the service into the controller via the constructor. The controller's `index` method should only delegate to the service.\n- **Remove unnecessary `params`, `payload`, and `queries`** from both the route type and the `@Route` decorator \u2014 only include what the route actually needs (see step 3 rules).\n\n**Add or remove `params`, `payload`, and `queries` in the `@Route` decorator to match the route type (step 3):**\n\n- **`params`** \u2014 Include with `Assert()` validators only when the route has URL parameters. Remove the `params` key entirely otherwise.\n- **`payload`** \u2014 Include with `Assert({...})` only for `post`, `put`, `patch` methods. Remove the `payload` key entirely for `get` and `delete`.\n- **`queries`** \u2014 Include with `Assert({...})` only when query parameters are expected. Remove the `queries` key entirely otherwise.\n- **`response`** \u2014 Always include with `Assert({...})`.\n\n**HTTP controller** generated structure (remove `params`, `payload`, `queries` as needed \u2014 see rules above):\n\n```typescript\nimport type { ContextType } from \"@ooneex/controller\";\nimport { ERole } from \"@ooneex/role\";\nimport { Route } from \"@ooneex/routing\";\nimport { Assert } from \"@ooneex/validation\";\n\ntype <TypeName>RouteType = {\n // Only include params, payload, queries as needed (see step 3)\n response: {\n\n },\n};\n\n@Route.<method>(\"<route.path>\", {\n name: \"<route.name>\",\n version: 1,\n description: \"\",\n // Only include params, payload, queries as needed (see step 3)\n response: Assert({\n\n }),\n roles: [ERole.USER],\n})\nexport class <Name>Controller {\n public async index(context: ContextType<<TypeName>RouteType>) {\n return context.response.json({\n\n });\n }\n}\n```\n\n**Socket controller** generated structure (remove `params`, `payload`, `queries` as needed \u2014 see rules above):\n\n```typescript\nimport type { ContextType } from \"@ooneex/socket\";\nimport { ERole } from \"@ooneex/role\";\nimport { Route } from \"@ooneex/routing\";\nimport { Assert } from \"@ooneex/validation\";\n\ntype <TypeName>RouteType = {\n // Only include params, payload, queries as needed (see step 3)\n response: {\n\n },\n};\n\n@Route.socket(\"<route.path>\", {\n name: \"<route.name>\",\n version: 1,\n description: \"\",\n // Only include params, payload, queries as needed (see step 3)\n response: Assert({\n\n }),\n roles: [ERole.USER],\n})\nexport class <Name>Controller {\n public async index(context: ContextType<<TypeName>RouteType>) {\n return context.response.json({\n\n });\n }\n}\n```\n\n### 5. Complete the test file\n\nEdit `tests/controllers/<Name>Controller.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, index method)\n- Add tests relevant to the specific controller behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from \"bun:test\";\nimport { <Name>Controller } from \"@/controllers/<Name>Controller\";\n\ndescribe(\"<Name>Controller\", () => {\n test(\"should have class name ending with 'Controller'\", () => {\n expect(<Name>Controller.name.endsWith(\"Controller\")).toBe(true);\n });\n\n test(\"should have 'index' method\", () => {\n expect(<Name>Controller.prototype.index).toBeDefined();\n expect(typeof <Name>Controller.prototype.index).toBe(\"function\");\n });\n});\n```\n\n### 6. Register the controller in the module\n\nAdd the new controller to the module's `controllers` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from \"@ooneex/module\";\nimport { <Name>Controller } from \"./controllers/<Name>Controller\";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [<Name>Controller],\n entities: [],\n middlewares: [],\n cronJobs: [],\n events: [],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other controllers registered, append the new controller to the existing `controllers` array and add the import alongside existing imports.\n\n### 7. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/controllers/<Name>Controller.ts tests/controllers/<Name>Controller.spec.ts\n```\n\n### 8. Create the service\n\nAfter the controller is created, generate a service class for the controller's business logic using the `make:service` skill:\n\n```\n/make:service --name=<Name>\n```\n\nWhere `<Name>` matches the controller name (e.g., if the controller is `CreateUserController`, the service is `CreateUserService`).\n\n### 9. Create the pubsub event\n\nAfter the service is created, generate a pubsub event class for the controller's domain events using the `make:pubsub` skill:\n\n```\n/make:pubsub --name=<Name> --channel=<resource>.<action>\n```\n\nWhere:\n- `<Name>` matches the controller name (e.g., if the controller is `CreateUserController`, the event is `CreateUserEvent`)\n- `<resource>.<action>` follows the same dot notation as the route name (e.g., `user.create`, `book.delete`)\n\nOnce the event is created:\n- Inject the **service** into the **event** via the constructor, and call the service's `execute` method from the event's `handler` method.\n- Inject the **event** into the **controller** via the constructor, and publish the event from the controller's `index` method.\n";
|
|
7449
7459
|
|
|
7450
|
-
|
|
7451
|
-
|
|
7460
|
+
// src/templates/claude/skills/make.cron.md.txt
|
|
7461
|
+
var make_cron_md_default = '---\nname: make:cron\ndescription: Generate a new cron job class with its test file, then complete the generated code. Use when creating a new scheduled task that extends the Cron base class from @ooneex/cron.\n---\n\n# Make Cron Class\n\nGenerate a new cron class and its test file using the `make:cron` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the cron class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:cron --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/cron/<Name>Cron.ts` - The cron class file (or `modules/<module>/src/cron/<Name>Cron.ts` with `--module`)\n- `tests/cron/<Name>Cron.spec.ts` - The test file (or `modules/<module>/tests/cron/<Name>Cron.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the cron class\n\nEdit `src/cron/<Name>Cron.ts` to complete the implementation:\n\n- Set the appropriate cron schedule in `getTime()` (e.g., `"every 5 minutes"`, `"every 1 hours"`, `"every 30 seconds"`)\n- Set the timezone in `getTimeZone()` if needed, or keep `null` for server timezone\n- Implement the `handler()` method with the actual cron job logic\n- Inject any required dependencies via the constructor\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport type { TimeZoneType } from "@ooneex/country";\nimport { Cron, type CronTimeType, decorator } from "@ooneex/cron";\n\n@decorator.cron()\nexport class <Name>Cron extends Cron {\n public getTime(): CronTimeType {\n // Examples: "every 5 minutes", "every 1 hours", "every 30 seconds"\n return "every 1 hours";\n }\n\n public getTimeZone(): TimeZoneType | null {\n // Return null to use server timezone, or specify a timezone like "Europe/Paris"\n return null;\n }\n\n public async handler(): Promise<void> {\n // Implement your cron handler logic here\n // console.log("<Name>Cron handler executed");\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/cron/<Name>Cron.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, getTime, getTimeZone, handler methods)\n- Add tests relevant to the specific cron class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Cron } from "@/cron/<Name>Cron";\n\ndescribe("<Name>Cron", () => {\n test("should have class name ending with \'Cron\'", () => {\n expect(<Name>Cron.name.endsWith("Cron")).toBe(true);\n });\n\n test("should have \'getTime\' method", () => {\n expect(<Name>Cron.prototype.getTime).toBeDefined();\n expect(typeof <Name>Cron.prototype.getTime).toBe("function");\n });\n\n test("should have \'getTimeZone\' method", () => {\n expect(<Name>Cron.prototype.getTimeZone).toBeDefined();\n expect(typeof <Name>Cron.prototype.getTimeZone).toBe("function");\n });\n\n test("should have \'handler\' method", () => {\n expect(<Name>Cron.prototype.handler).toBeDefined();\n expect(typeof <Name>Cron.prototype.handler).toBe("function");\n });\n});\n```\n\n### 5. Register the cron job in the module\n\nAdd the new cron job to the module\'s `cronJobs` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from "@ooneex/module";\nimport { <Name>Cron } from "./cron/<Name>Cron";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [],\n entities: [],\n middlewares: [],\n cronJobs: [<Name>Cron],\n events: [],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other cron jobs registered, append the new cron job to the existing `cronJobs` array and add the import alongside existing imports.\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/cron/<Name>Cron.ts tests/cron/<Name>Cron.spec.ts\n```\n';
|
|
7462
|
+
|
|
7463
|
+
// src/templates/claude/skills/make.database.md.txt
|
|
7464
|
+
var make_database_md_default = '---\nname: make:database\ndescription: Generate a new database class with its test file, then complete the generated code. Use when creating a new database adapter that extends TypeormDatabase from @ooneex/database.\n---\n\n# Make Database Class\n\nGenerate a new database class and its test file using the `make:database` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the database class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:database --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/databases/<Name>Database.ts` - The database class file (or `modules/<module>/src/databases/<Name>Database.ts` with `--module`)\n- `tests/databases/<Name>Database.spec.ts` - The test file (or `modules/<module>/tests/databases/<Name>Database.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the database class\n\nEdit `src/databases/<Name>Database.ts` to complete the implementation:\n\n- Add entity imports and register them in the `entities` array\n- Adjust the database path if needed (default is `"var/db"`)\n- Configure DataSource options as appropriate (type, synchronize, etc.)\n- Inject any required dependencies via the constructor\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { DataSource } from "typeorm";\nimport { TypeormDatabase, DatabaseException, decorator } from "@ooneex/database";\n\n@decorator.database()\nexport class <Name>Database extends TypeormDatabase {\n public getSource(database?: string): DataSource {\n database = database || "var/db";\n\n this.source = new DataSource({\n synchronize: false,\n entities: [\n // TODO: Load your entities here\n ],\n enableWAL: true,\n busyErrorRetry: 2000,\n busyTimeout: 30_000,\n database,\n type: "sqlite",\n });\n\n return this.source;\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/databases/<Name>Database.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, getSource method)\n- Add tests relevant to the specific database class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Database } from "@/databases/<Name>Database";\n\ndescribe("<Name>Database", () => {\n test("should have class name ending with \'Database\'", () => {\n expect(<Name>Database.name.endsWith("Database")).toBe(true);\n });\n\n test("should have \'getSource\' method", () => {\n expect(<Name>Database.prototype.getSource).toBeDefined();\n expect(typeof <Name>Database.prototype.getSource).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/databases/<Name>Database.ts tests/databases/<Name>Database.spec.ts\n```\n';
|
|
7465
|
+
|
|
7466
|
+
// src/templates/claude/skills/make.entity.md.txt
|
|
7467
|
+
var make_entity_md_default = "---\nname: make:entity\ndescription: Generate a new TypeORM entity class with its test file, then complete the generated code. Use when creating a new database entity with columns, relations, and table mapping.\n---\n\n# Make Entity Class\n\nGenerate a new entity class and its test file using the `make:entity` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- Avoid non-null assertions (`!`). For example, `public name!: string;` should be `public name: string;`\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from \"@ooneex/container\";\nimport { decorator, type ISeed } from \"@ooneex/seeds\";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the entity class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:entity --name=<name> --module=<module> --table-name=<table_name>\n```\n\nWhere `<name>` is the name provided by the user. The `--table-name` option is optional \u2014 if omitted, it defaults to the snake_case pluralized form of the name (e.g., `UserProfile` becomes `user_profiles`). The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/entities/<Name>Entity.ts` - The entity class file (or `modules/<module>/src/entities/<Name>Entity.ts` with `--module`)\n- `tests/entities/<Name>Entity.spec.ts` - The test file (or `modules/<module>/tests/entities/<Name>Entity.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the entity class\n\nEdit `src/entities/<Name>Entity.ts` to complete the implementation:\n\n- Add entity-specific columns with appropriate TypeORM decorators (`@Column`)\n- Add relations if needed (`@ManyToOne`, `@OneToMany`, `@ManyToMany`, etc.)\n- Remove any scaffolded columns that are not relevant to the entity\n- Adjust column types, lengths, and constraints as needed\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport type { LocaleType } from \"@ooneex/translation\";\nimport { random } from \"@ooneex/utils\";\nimport { Column, CreateDateColumn, DeleteDateColumn, PrimaryColumn, UpdateDateColumn } from \"typeorm\";\n\n@Entity({\n name: \"<table_name>\",\n})\nexport class <Name>Entity extends BaseEntity {\n @PrimaryColumn({ name: \"id\", type: \"varchar\", length: 25 })\n id: string = random.nanoid(25);\n\n @Column({\n name: \"is_locked\",\n type: \"boolean\",\n default: false,\n nullable: true,\n })\n isLocked?: boolean;\n\n @Column({ name: \"locked_at\", type: \"timestamptz\", nullable: true })\n lockedAt?: Date;\n\n @Column({\n name: \"is_blocked\",\n type: \"boolean\",\n default: false,\n nullable: true,\n })\n isBlocked?: boolean;\n\n @Column({ name: \"blocked_at\", type: \"timestamptz\", nullable: true })\n blockedAt?: Date;\n\n @Column({ name: \"block_reason\", type: \"text\", nullable: true })\n blockReason?: string;\n\n @Column({ name: \"is_public\", type: \"boolean\", default: true, nullable: true })\n isPublic?: boolean;\n\n @Column({ name: \"lang\", type: \"varchar\", length: 10, nullable: true })\n lang?: LocaleType;\n\n @CreateDateColumn({ name: \"created_at\" })\n createdAt?: Date;\n\n @UpdateDateColumn({ name: \"updated_at\" })\n updatedAt?: Date;\n\n @DeleteDateColumn({ name: \"deleted_at\" })\n deletedAt?: Date;\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/entities/<Name>Entity.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, id, default columns)\n- Add tests for any new entity-specific columns and relations\n- Update tests if scaffolded columns were removed\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from \"bun:test\";\nimport { <Name>Entity } from \"@/entities/<Name>Entity\";\n\ndescribe(\"<Name>Entity\", () => {\n test(\"should have class name ending with 'Entity'\", () => {\n expect(<Name>Entity.name.endsWith(\"Entity\")).toBe(true);\n });\n\n test(\"should have 'id' property with default nanoid\", () => {\n const entity = new <Name>Entity();\n expect(entity.id).toBeDefined();\n expect(typeof entity.id).toBe(\"string\");\n expect(entity.id.length).toBe(25);\n });\n\n test(\"should have 'isLocked' property\", () => {\n const entity = new <Name>Entity();\n expect(\"isLocked\" in entity).toBe(true);\n });\n\n // ... additional property tests\n});\n```\n\n### 5. Register the entity in the module\n\nAdd the new entity to the module's `entities` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from \"@ooneex/module\";\nimport { <Name>Entity } from \"./entities/<Name>Entity\";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [],\n entities: [<Name>Entity],\n middlewares: [],\n cronJobs: [],\n events: [],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other entities registered, append the new entity to the existing `entities` array and add the import alongside existing imports.\n\n### 6. Create a migration for the entity\n\nAfter creating or updating an entity, generate a migration to apply the corresponding schema changes to the database.\n\nRun the generator:\n\n```bash\nbunx @ooneex/cli@latest make:migration\n```\n\nThen read the generated migration file in `src/migrations/` and complete it:\n\n- In the `up` method, write the SQL to create the table (or alter it if updating an existing entity). Include all columns, types, constraints, defaults, and indexes matching the entity definition.\n- In the `down` method, write the reverse SQL to undo the changes (e.g., `DROP TABLE` or `ALTER TABLE DROP COLUMN`).\n- If the migration depends on another migration (e.g., a foreign key referencing another table), add that migration class to the `getDependencies()` return array.\n\nExample `up` method for a new entity:\n\n```typescript\npublic async up(tx: TransactionSQL): Promise<void> {\n await tx`\n CREATE TABLE IF NOT EXISTS <table_name> (\n id VARCHAR(25) PRIMARY KEY,\n is_locked BOOLEAN DEFAULT false,\n locked_at TIMESTAMPTZ,\n is_blocked BOOLEAN DEFAULT false,\n blocked_at TIMESTAMPTZ,\n block_reason TEXT,\n is_public BOOLEAN DEFAULT true,\n lang VARCHAR(10),\n created_at TIMESTAMPTZ DEFAULT NOW(),\n updated_at TIMESTAMPTZ DEFAULT NOW(),\n deleted_at TIMESTAMPTZ\n )\n `;\n}\n```\n\n### 7. Create a repository for the entity\n\nAfter creating the entity, generate a repository to handle database operations for it.\n\nRun the generator:\n\n```bash\nbunx @ooneex/cli@latest make:repository --name=<name>\n```\n\nWhere `<name>` is the same name used for the entity. The command will generate:\n- `src/repositories/<Name>Repository.ts` - The repository class\n- `tests/repositories/<Name>Repository.spec.ts` - The test file\n\nThen read the generated files and complete the repository implementation:\n\n- Adjust search fields in the `find()` method to match the entity's searchable columns\n- Customize relations loading in `findOne`/`findOneBy` if the entity has relations\n- Add any domain-specific methods relevant to the entity\n- Remove methods that don't apply to the entity\n- Update tests to match the final repository methods\n\n### 8. Lint and format\n\nRun linting and formatting on all generated files:\n\n```bash\nbunx biome check --fix src/entities/<Name>Entity.ts tests/entities/<Name>Entity.spec.ts src/repositories/<Name>Repository.ts tests/repositories/<Name>Repository.spec.ts src/migrations/\n```\n";
|
|
7468
|
+
|
|
7469
|
+
// src/templates/claude/skills/make.logger.md.txt
|
|
7470
|
+
var make_logger_md_default = '---\nname: make:logger\ndescription: Generate a new logger class with its test file, then complete the generated code. Use when creating a new logger that implements the ILogger interface from @ooneex/logger.\n---\n\n# Make Logger Class\n\nGenerate a new logger class and its test file using the `make:logger` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the logger class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:logger --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/loggers/<Name>Logger.ts` - The logger class file (or `modules/<module>/src/loggers/<Name>Logger.ts` with `--module`)\n- `tests/loggers/<Name>Logger.spec.ts` - The test file (or `modules/<module>/tests/loggers/<Name>Logger.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the logger class\n\nEdit `src/loggers/<Name>Logger.ts` to complete the implementation:\n\n- Implement the `init()` method to set up the logger (e.g., open file handles, configure transports)\n- Implement `log`, `debug`, `info`, `success`, `warn`, and `error` methods with actual logging logic\n- Inject any required dependencies via the constructor\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport type { IException } from "@ooneex/exception";\nimport { type ILogger, decorator } from "@ooneex/logger";\nimport type { ScalarType } from "@ooneex/types";\n\n@decorator.logger()\nexport class <Name>Logger implements ILogger {\n public async init(): Promise<void> {\n // Initialize your logger here\n }\n\n public log(message: string, data?: Record<string, ScalarType>): void {\n // Handle general logging\n }\n\n public debug(message: string, data?: Record<string, ScalarType>): void {\n // Handle debug logging\n }\n\n public info(message: string, data?: Record<string, ScalarType>): void {\n // Handle info logging\n }\n\n public success(message: string, data?: Record<string, ScalarType>): void {\n // Handle success logging\n }\n\n public warn(message: string, data?: Record<string, ScalarType>): void {\n // Handle warning logging\n }\n\n public error(message: string | IException, data?: Record<string, ScalarType>): void {\n // Handle error logging\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/loggers/<Name>Logger.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, init, log, debug, info, success, warn, error methods)\n- Add tests relevant to the specific logger class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Logger } from "@/loggers/<Name>Logger";\n\ndescribe("<Name>Logger", () => {\n test("should have class name ending with \'Logger\'", () => {\n expect(<Name>Logger.name.endsWith("Logger")).toBe(true);\n });\n\n test("should have \'init\' method", () => {\n expect(<Name>Logger.prototype.init).toBeDefined();\n expect(typeof <Name>Logger.prototype.init).toBe("function");\n });\n\n test("should have \'log\' method", () => {\n expect(<Name>Logger.prototype.log).toBeDefined();\n expect(typeof <Name>Logger.prototype.log).toBe("function");\n });\n\n test("should have \'debug\' method", () => {\n expect(<Name>Logger.prototype.debug).toBeDefined();\n expect(typeof <Name>Logger.prototype.debug).toBe("function");\n });\n\n test("should have \'info\' method", () => {\n expect(<Name>Logger.prototype.info).toBeDefined();\n expect(typeof <Name>Logger.prototype.info).toBe("function");\n });\n\n test("should have \'success\' method", () => {\n expect(<Name>Logger.prototype.success).toBeDefined();\n expect(typeof <Name>Logger.prototype.success).toBe("function");\n });\n\n test("should have \'warn\' method", () => {\n expect(<Name>Logger.prototype.warn).toBeDefined();\n expect(typeof <Name>Logger.prototype.warn).toBe("function");\n });\n\n test("should have \'error\' method", () => {\n expect(<Name>Logger.prototype.error).toBeDefined();\n expect(typeof <Name>Logger.prototype.error).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/loggers/<Name>Logger.ts tests/loggers/<Name>Logger.spec.ts\n```\n';
|
|
7471
|
+
|
|
7472
|
+
// src/templates/claude/skills/make.mailer.md.txt
|
|
7473
|
+
var make_mailer_md_default = '---\nname: make:mailer\ndescription: Generate a new mailer class with its template and test files, then complete the generated code. Use when creating a new email sender with JSX template using @ooneex/mailer.\n---\n\n# Make Mailer Class\n\nGenerate a new mailer class, its JSX template, and test files using the `make:mailer` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the mailer class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:mailer --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate (paths prefixed with `modules/<module>/` when `--module` is provided):\n- `src/mailers/<Name>Mailer.ts` - The mailer class file\n- `src/mailers/<Name>MailerTemplate.tsx` - The JSX email template\n- `tests/mailers/<Name>Mailer.spec.ts` - The mailer test file\n- `tests/mailers/<Name>MailerTemplate.spec.ts` - The template test file\n\n### 2. Read the generated files\n\nRead all four generated files to understand the scaffolded code.\n\n### 3. Complete the mailer class\n\nEdit `src/mailers/<Name>Mailer.ts` to complete the implementation:\n\n- Adjust the `send` method config type if additional parameters are needed\n- Add any pre-send logic (validation, data transformation, etc.)\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport type { IMailer } from "@ooneex/mailer";\nimport { type <Name>MailerPropsType, <Name>MailerTemplate } from "./<Name>MailerTemplate";\n\nexport class <Name>Mailer implements IMailer {\n constructor(\n @inject("mailer")\n private readonly mailer: IMailer,\n ) {}\n\n public send = async (config: {\n to: string[];\n subject: string;\n from?: { name: string; address: string };\n data?: <Name>MailerPropsType;\n }): Promise<void> => {\n const { data, ...rest } = config;\n\n await this.mailer.send({\n ...rest,\n content: <Name>MailerTemplate(data),\n });\n };\n}\n```\n\n### 4. Complete the mailer template\n\nEdit `src/mailers/<Name>MailerTemplate.tsx` to complete the implementation:\n\n- Update `<Name>MailerPropsType` with the actual props needed for the email\n- Build the email body using `MailerLayout` components (Header, Body, Footer)\n- Add email content, styling, and dynamic data rendering\n\nThe generated template structure follows this pattern:\n\n```tsx\nimport { MailerLayout } from "@ooneex/mailer";\n\nexport type <Name>MailerPropsType = {\n link: string;\n};\n\nexport const <Name>MailerTemplate = (props?: <Name>MailerPropsType) => (\n <MailerLayout>\n <MailerLayout.Header />\n <MailerLayout.Body>\n <a href={props?.link}>Login</a>\n </MailerLayout.Body>\n <MailerLayout.Footer />\n </MailerLayout>\n);\n```\n\n### 5. Complete the test files\n\nEdit `tests/mailers/<Name>Mailer.spec.ts` and `tests/mailers/<Name>MailerTemplate.spec.ts` to add meaningful tests beyond the scaffolded ones.\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/mailers/<Name>Mailer.ts src/mailers/<Name>MailerTemplate.tsx tests/mailers/<Name>Mailer.spec.ts tests/mailers/<Name>MailerTemplate.spec.ts\n```\n';
|
|
7474
|
+
|
|
7475
|
+
// src/templates/claude/skills/make.middleware.md.txt
|
|
7476
|
+
var make_middleware_md_default = '---\nname: make:middleware\ndescription: Generate a new middleware class with its test file, then complete the generated code. Use when creating a new HTTP or WebSocket middleware that implements IMiddleware from @ooneex/middleware.\n---\n\n# Make Middleware Class\n\nGenerate a new middleware class and its test file using the `make:middleware` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the middleware class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:middleware --name=<name> --module=<module> --is-socket=<true|false>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The `--is-socket` option determines whether to generate an HTTP middleware or a WebSocket middleware (defaults to `false` if omitted). The command will generate:\n- `src/middlewares/<Name>Middleware.ts` - The middleware class file (or `modules/<module>/src/middlewares/<Name>Middleware.ts` with `--module`)\n- `tests/middlewares/<Name>Middleware.spec.ts` - The test file (or `modules/<module>/tests/middlewares/<Name>Middleware.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the middleware class\n\nEdit `src/middlewares/<Name>Middleware.ts` to complete the implementation:\n\n- Implement the `handler` method with actual middleware logic\n- Add request/response transformations, authentication checks, logging, etc.\n- Inject any required dependencies via the constructor\n\n**HTTP middleware** generated structure:\n\n```typescript\nimport type { ContextType } from "@ooneex/controller";\nimport { decorator, type IMiddleware } from "@ooneex/middleware";\n\n@decorator.middleware()\nexport class <Name>Middleware implements IMiddleware {\n public async handler(context: ContextType): Promise<ContextType> {\n // Example: Add custom header\n // context.response.header("X-Custom-Header", "value");\n\n return context\n }\n}\n```\n\n**Socket middleware** generated structure:\n\n```typescript\nimport type { ContextType } from "@ooneex/socket";\nimport { decorator, type IMiddleware } from "@ooneex/middleware";\n\n@decorator.middleware()\nexport class <Name>Middleware implements IMiddleware {\n public async handler(context: ContextType): Promise<ContextType> {\n // Example: Add custom header\n // context.response.header("X-Custom-Header", "value");\n\n return context\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/middlewares/<Name>Middleware.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, handler method)\n- Add tests relevant to the specific middleware behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Middleware } from "@/middlewares/<Name>Middleware";\n\ndescribe("<Name>Middleware", () => {\n test("should have class name ending with \'Middleware\'", () => {\n expect(<Name>Middleware.name.endsWith("Middleware")).toBe(true);\n });\n\n test("should have \'handler\' method", () => {\n expect(<Name>Middleware.prototype.handler).toBeDefined();\n expect(typeof <Name>Middleware.prototype.handler).toBe("function");\n });\n});\n```\n\n### 5. Register the middleware in the module\n\nAdd the new middleware to the module\'s `middlewares` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from "@ooneex/module";\nimport { <Name>Middleware } from "./middlewares/<Name>Middleware";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [],\n entities: [],\n middlewares: [<Name>Middleware],\n cronJobs: [],\n events: [],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other middlewares registered, append the new middleware to the existing `middlewares` array and add the import alongside existing imports.\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/middlewares/<Name>Middleware.ts tests/middlewares/<Name>Middleware.spec.ts\n```\n';
|
|
7477
|
+
|
|
7478
|
+
// src/templates/claude/skills/make.migration.md.txt
|
|
7479
|
+
var make_migration_md_default = '---\nname: make:migration\ndescription: Generate a new database migration file with its test file, then complete the generated code. Use when creating a new database migration for schema changes using @ooneex/migrations.\n---\n\n# Make Migration\n\nGenerate a new migration file and its test file using the `make:migration` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the migration file and test files:\n\n```bash\nbunx @ooneex/cli@latest make:migration --module=<module>\n```\n\nThe `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/migrations/Migration<version>.ts` - The migration class file (or `modules/<module>/src/migrations/Migration<version>.ts` with `--module`)\n- `tests/migrations/Migration<version>.spec.ts` - The test file (or `modules/<module>/tests/migrations/Migration<version>.spec.ts` with `--module`)\n\nIt will also:\n- Generate a `migrations.ts` root export file in the migrations directory\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the migration\n\nEdit `src/migrations/Migration<version>.ts` to implement:\n\n- The `up` method with the schema changes (create tables, add columns, create indexes, etc.)\n- The `down` method with the reverse operations to undo the migration\n\n### 4. Complete the test file\n\nEdit `tests/migrations/Migration<version>.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, up, down, getVersion, getDependencies methods)\n- Add tests relevant to the specific migration behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { Migration<version> } from "@/migrations/Migration<version>";\n\ndescribe("Migration<version>", () => {\n test("should have class name starting with \'Migration\'", () => {\n expect(Migration<version>.name.startsWith("Migration")).toBe(true);\n });\n\n test("should have \'up\' method", () => {\n expect(Migration<version>.prototype.up).toBeDefined();\n expect(typeof Migration<version>.prototype.up).toBe("function");\n });\n\n test("should have \'down\' method", () => {\n expect(Migration<version>.prototype.down).toBeDefined();\n expect(typeof Migration<version>.prototype.down).toBe("function");\n });\n\n test("should have \'getVersion\' method", () => {\n expect(Migration<version>.prototype.getVersion).toBeDefined();\n expect(typeof Migration<version>.prototype.getVersion).toBe("function");\n });\n\n test("should have \'getDependencies\' method", () => {\n expect(Migration<version>.prototype.getDependencies).toBeDefined();\n expect(typeof Migration<version>.prototype.getDependencies).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/migrations/Migration<version>.ts tests/migrations/Migration<version>.spec.ts\n```\n';
|
|
7480
|
+
|
|
7481
|
+
// src/templates/claude/skills/make.permission.md.txt
|
|
7482
|
+
var make_permission_md_default = '---\nname: make:permission\ndescription: Generate a new permission class with its test file, then complete the generated code. Use when creating a new permission that extends Permission from @ooneex/permission.\n---\n\n# Make Permission Class\n\nGenerate a new permission class and its test file using the `make:permission` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the permission class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:permission --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/permissions/<Name>Permission.ts` - The permission class file (or `modules/<module>/src/permissions/<Name>Permission.ts` with `--module`)\n- `tests/permissions/<Name>Permission.spec.ts` - The test file (or `modules/<module>/tests/permissions/<Name>Permission.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the permission class\n\nEdit `src/permissions/<Name>Permission.ts` to complete the implementation:\n\n- Implement the `allow()` method with permission rules using `this.ability.can()`\n- Implement the `setUserPermissions()` method with role-based permission logic\n- Define which actions (read, create, update, delete, manage) are allowed on which entities\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { decorator, Permission } from "@ooneex/permission";\nimport type { IUser } from "@ooneex/user";\n\n@decorator.permission()\nexport class <Name>Permission extends Permission {\n public allow(): this {\n // Example: Add permissions using this.ability.can()\n // this.ability.can("read", "YourEntity");\n // this.ability.can(["read", "update"], "YourEntity", { userId: user.id });\n\n return this;\n }\n\n public setUserPermissions(user: IUser | null): this {\n if (!user) {\n return this;\n }\n\n // Example: Grant full access to admins\n // const { roles } = user;\n // if (roles.includes(ERole.ADMIN)) {\n // this.ability.can("manage", "all");\n // return this;\n // }\n\n // Example: Grant specific permissions based on roles\n // for (const role of roles) {\n // if (role === ERole.USER) {\n // this.ability.can(["read", "update"], "YourEntity", { userId: user.id });\n // }\n //\n // if (role === ERole.GUEST) {\n // this.ability.can("read", "YourEntity", { public: true });\n // }\n // }\n\n return this;\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/permissions/<Name>Permission.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, extends Permission, allow, setUserPermissions methods)\n- Add tests relevant to the specific permission rules\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { Permission } from "@ooneex/permission";\nimport { <Name>Permission } from "@/permissions/<Name>Permission";\n\ndescribe("<Name>Permission", () => {\n test("should have class name ending with \'Permission\'", () => {\n expect(<Name>Permission.name.endsWith("Permission")).toBe(true);\n });\n\n test("should extend Permission", () => {\n const permission = new <Name>Permission();\n expect(permission).toBeInstanceOf(Permission);\n });\n\n test("should have \'allow\' method", () => {\n expect(<Name>Permission.prototype.allow).toBeDefined();\n expect(typeof <Name>Permission.prototype.allow).toBe("function");\n });\n\n test("should have \'setUserPermissions\' method", () => {\n expect(<Name>Permission.prototype.setUserPermissions).toBeDefined();\n expect(typeof <Name>Permission.prototype.setUserPermissions).toBe("function");\n });\n});\n```\n\n### 5. Register the permission in the module\n\nThe permission class is standalone and does not need to be registered in a module.\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/permissions/<Name>Permission.ts tests/permissions/<Name>Permission.spec.ts\n```\n';
|
|
7483
|
+
|
|
7484
|
+
// src/templates/claude/skills/make.pubsub.md.txt
|
|
7485
|
+
var make_pubsub_md_default = '---\nname: make:pubsub\ndescription: Generate a new PubSub event class with its test file, then complete the generated code. Use when creating a new publish/subscribe event that extends PubSub from @ooneex/pub-sub.\n---\n\n# Make PubSub Event Class\n\nGenerate a new PubSub event class and its test file using the `make:pubsub` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the PubSub event class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:pubsub --name=<name> --module=<module> --channel=<channel>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The `--channel` option is optional \u2014 if omitted, it defaults to the kebab-case form of the name (e.g., `UserCreated` becomes `user-created`). The command will generate:\n- `src/events/<Name>Event.ts` - The event class file (or `modules/<module>/src/events/<Name>Event.ts` with `--module`)\n- `tests/events/<Name>Event.spec.ts` - The test file (or `modules/<module>/tests/events/<Name>Event.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the PubSub event class\n\nEdit `src/events/<Name>Event.ts` to complete the implementation:\n\n- Define a proper data type instead of `Record<string, ScalarType>` for the event payload\n- Implement the `handler()` method with actual event handling logic\n- Set the appropriate channel name in `getChannel()`\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport type { ScalarType } from "@ooneex/types";\nimport { decorator, PubSub, RedisPubSub } from "@ooneex/pub-sub";\n\n@decorator.pubSub()\nexport class <Name>Event<Data extends Record<string, ScalarType> = Record<string, ScalarType>> extends PubSub<Data> {\n constructor(\n @inject(RedisPubSub)\n client: RedisPubSub<Data>,\n ) {\n super(client);\n }\n\n public getChannel(): string {\n return "<channel>";\n }\n\n public async handler(context: { data: Data; channel: string }): Promise<void> {\n console.log(context);\n // TODO: Implement handler logic here\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/events/<Name>Event.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, getChannel, handler, publish, subscribe, unsubscribe, unsubscribeAll methods)\n- Add tests relevant to the specific event behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>PubSub } from "@/pubsub/<Name>PubSub";\n\ndescribe("<Name>PubSub", () => {\n test("should have class name ending with \'PubSub\'", () => {\n expect(<Name>PubSub.name.endsWith("PubSub")).toBe(true);\n });\n\n test("should have \'getChannel\' method", () => {\n expect(<Name>PubSub.prototype.getChannel).toBeDefined();\n expect(typeof <Name>PubSub.prototype.getChannel).toBe("function");\n });\n\n test("should have \'handler\' method", () => {\n expect(<Name>PubSub.prototype.handler).toBeDefined();\n expect(typeof <Name>PubSub.prototype.handler).toBe("function");\n });\n\n test("should have \'publish\' method", () => {\n expect(<Name>PubSub.prototype.publish).toBeDefined();\n expect(typeof <Name>PubSub.prototype.publish).toBe("function");\n });\n\n test("should have \'subscribe\' method", () => {\n expect(<Name>PubSub.prototype.subscribe).toBeDefined();\n expect(typeof <Name>PubSub.prototype.subscribe).toBe("function");\n });\n\n test("should have \'unsubscribe\' method", () => {\n expect(<Name>PubSub.prototype.unsubscribe).toBeDefined();\n expect(typeof <Name>PubSub.prototype.unsubscribe).toBe("function");\n });\n\n test("should have \'unsubscribeAll\' method", () => {\n expect(<Name>PubSub.prototype.unsubscribeAll).toBeDefined();\n expect(typeof <Name>PubSub.prototype.unsubscribeAll).toBe("function");\n });\n});\n```\n\n### 5. Register the event in the module\n\nAdd the new event to the module\'s `events` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from "@ooneex/module";\nimport { <Name>Event } from "./events/<Name>Event";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [],\n entities: [],\n middlewares: [],\n cronJobs: [],\n events: [<Name>Event],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other events registered, append the new event to the existing `events` array and add the import alongside existing imports.\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/events/<Name>Event.ts tests/events/<Name>Event.spec.ts\n```\n';
|
|
7486
|
+
|
|
7487
|
+
// src/templates/claude/skills/make.repository.md.txt
|
|
7488
|
+
var make_repository_md_default = '---\nname: make:repository\ndescription: Generate a new repository class with its test file, then complete the generated code. Use when creating a new TypeORM repository for database operations on an entity.\n---\n\n# Make Repository Class\n\nGenerate a new repository class and its test file using the `make:repository` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the repository class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:repository --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/repositories/<Name>Repository.ts` - The repository class file (or `modules/<module>/src/repositories/<Name>Repository.ts` with `--module`)\n- `tests/repositories/<Name>Repository.spec.ts` - The test file (or `modules/<module>/tests/repositories/<Name>Repository.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the repository class\n\nEdit `src/repositories/<Name>Repository.ts` to complete the implementation:\n\n- Verify the entity import path matches the actual entity location\n- Adjust the `find` method\'s search fields (default searches `name` with `ILike`)\n- Customize relations loading in `findOne`/`findOneBy` if needed\n\n#### Adding methods\n\nLook at the entity\'s fields, relations, and business context to determine if custom domain-specific methods are needed. For example:\n- A `SessionRepository` might need `revokeSession(sessionId: string)` and `revokeAllUserSessions(userId: string)`\n- A `NotificationRepository` might need `markAsRead(id: string)` and `markAllAsRead(userId: string)`\n- Entities with status fields may need methods like `archive(id: string)` or `activate(id: string)`\n\nRead related entities, services, or actions in the module to understand what operations the repository should support, then add the appropriate methods.\n\n#### Removing methods\n\nRemove scaffolded methods that don\'t make sense for the entity\'s context:\n- Remove `createMany`/`updateMany` if the entity is always managed individually (e.g., user profiles, settings)\n- Remove `delete` if the entity uses soft deletes only (use a custom `softDelete` or `archive` method instead)\n- Remove `find` if the entity is only ever accessed by ID or specific criteria (e.g., singleton config entities)\n- Remove `count` if there\'s no use case for counting records of this entity\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport type { ITypeormDatabase } from "@ooneex/database";\nimport { decorator } from "@ooneex/repository";\nimport type { FilterResultType } from "@ooneex/types";\nimport type { FindManyOptions, FindOptionsWhere, Repository, SaveOptions, UpdateResult } from "typeorm";\nimport { ILike } from "typeorm";\nimport { <Name>Entity } from "../entities/<Name>Entity";\n\n@decorator.repository()\nexport class <Name>Repository {\n constructor(\n @inject("database")\n private readonly database: ITypeormDatabase,\n ) {}\n\n public async open(): Promise<Repository<<Name>Entity>> {\n return await this.database.open(<Name>Entity);\n }\n\n public async close(): Promise<void> {\n await this.database.close();\n }\n\n public async find(\n criteria: FindManyOptions<<Name>Entity> & { page?: number; limit?: number; q?: string },\n ): Promise<FilterResultType<<Name>Entity>> {\n // ... pagination and search logic\n }\n\n public async findOne(id: string): Promise<<Name>Entity | null> { ... }\n public async findOneBy(criteria: FindOptionsWhere<<Name>Entity>): Promise<<Name>Entity | null> { ... }\n public async create(entity: <Name>Entity, options?: SaveOptions): Promise<<Name>Entity> { ... }\n public async createMany(entities: <Name>Entity[], options?: SaveOptions): Promise<<Name>Entity[]> { ... }\n public async update(entity: <Name>Entity, options?: SaveOptions): Promise<<Name>Entity> { ... }\n public async updateMany(entities: <Name>Entity[], options?: SaveOptions): Promise<<Name>Entity[]> { ... }\n public async delete(criteria: FindOptionsWhere<<Name>Entity> | FindOptionsWhere<<Name>Entity>[]): Promise<UpdateResult> { ... }\n public async count(criteria?: FindOptionsWhere<<Name>Entity> | FindOptionsWhere<<Name>Entity>[]): Promise<number> { ... }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/repositories/<Name>Repository.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep scaffolded tests for methods that remain in the repository (remove tests for methods that were removed)\n- Add tests for any custom domain-specific methods added to the repository\n- Add tests relevant to the specific repository behavior\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/repositories/<Name>Repository.ts tests/repositories/<Name>Repository.spec.ts\n```\n';
|
|
7489
|
+
|
|
7490
|
+
// src/templates/claude/skills/make.seed.md.txt
|
|
7491
|
+
var make_seed_md_default = '---\nname: make:seed\ndescription: Generate a new database seed file with its test file, then complete the generated code. Use when creating seed data for populating the database using @ooneex/seeds.\n---\n\n# Make Seed\n\nGenerate a new seed file and its test file using the `make:seed` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the seed file and test files:\n\n```bash\nbunx @ooneex/cli@latest make:seed --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/seeds/<Name>Seed.ts` - The seed class file (or `modules/<module>/src/seeds/<Name>Seed.ts` with `--module`)\n- `src/seeds/<name-seed>.yml` - The seed data file in kebab-case (or `modules/<module>/src/seeds/<name-seed>.yml` with `--module`)\n- `tests/seeds/<Name>Seed.spec.ts` - The test file (or `modules/<module>/tests/seeds/<Name>Seed.spec.ts` with `--module`)\n\nIt will also:\n- Generate a `seeds.ts` root export file in the seeds directory\n\n### 2. Read the generated files\n\nRead all generated files to understand the scaffolded code.\n\n### 3. Complete the seed data\n\nEdit `src/seeds/<name-seed>.yml` to add the seed data entries. Each entry should have hardcoded nanoid values for `id` fields (generate via `bun -e "import { random } from \'@ooneex/utils\'; console.log(random.nanoid())"`)\n\n- Do NOT use sequential IDs like `"item-1"`, `"item-2"`\n- Ensure the same entity uses the same ID everywhere it appears\n\n### 4. Complete the seed class\n\nEdit `src/seeds/<Name>Seed.ts` to implement:\n\n- Import the relevant entity classes and repository\n- Use `resolve()` from `@ooneex/container` to get the repository instance\n- Map the imported YAML data to entity instances\n- Use the repository to persist the entities\n\n### 5. Complete the test file\n\nEdit `tests/seeds/<Name>Seed.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, run, isActive, getDependencies, data yml file)\n- Add tests relevant to the specific seed behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { existsSync } from "node:fs";\nimport { join } from "node:path";\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Seed } from "@/seeds/<Name>Seed";\n\ndescribe("<Name>Seed", () => {\n test("should have class name ending with \'Seed\'", () => {\n expect(<Name>Seed.name.endsWith("Seed")).toBe(true);\n });\n\n test("should have \'run\' method", () => {\n expect(<Name>Seed.prototype.run).toBeDefined();\n expect(typeof <Name>Seed.prototype.run).toBe("function");\n });\n\n test("should have \'isActive\' method", () => {\n expect(<Name>Seed.prototype.isActive).toBeDefined();\n expect(typeof <Name>Seed.prototype.isActive).toBe("function");\n });\n\n test("should have \'getDependencies\' method", () => {\n expect(<Name>Seed.prototype.getDependencies).toBeDefined();\n expect(typeof <Name>Seed.prototype.getDependencies).toBe("function");\n });\n\n test("should have a data yml file", () => {\n const dataFile = join(__dirname, "..", "src", "seeds", "<name-seed>.yml");\n expect(existsSync(dataFile)).toBe(true);\n });\n});\n```\n\n### 6. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/seeds/<Name>Seed.ts src/seeds/<name-seed>.yml tests/seeds/<Name>Seed.spec.ts\n```\n';
|
|
7492
|
+
|
|
7493
|
+
// src/templates/claude/skills/make.service.md.txt
|
|
7494
|
+
var make_service_md_default = '---\nname: make:service\ndescription: Generate a new service class with its test file, then complete the generated code. Use when creating a new business logic service that implements IService from @ooneex/service.\n---\n\n# Make Service Class\n\nGenerate a new service class and its test file using the `make:service` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the service class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:service --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/services/<Name>Service.ts` - The service class file (or `modules/<module>/src/services/<Name>Service.ts` with `--module`)\n- `tests/services/<Name>Service.spec.ts` - The test file (or `modules/<module>/tests/services/<Name>Service.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the service class\n\nEdit `src/services/<Name>Service.ts` to complete the implementation:\n\n- Define a proper type for `ServiceDataType` instead of `Record<string, unknown>`\n- Implement the `execute()` method with actual business logic\n- Inject any required dependencies (repositories, other services, etc.) via the constructor\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { type IService, decorator } from "@ooneex/service";\n\ntype ServiceDataType = Record<string, unknown>;\n\n@decorator.service()\nexport class <Name>Service implements IService {\n public async execute(data?: ServiceDataType): Promise<void> {\n // TODO: Implement service logic\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/services/<Name>Service.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, execute method)\n- Add tests relevant to the specific service behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>Service } from "@/services/<Name>Service";\n\ndescribe("<Name>Service", () => {\n test("should have class name ending with \'Service\'", () => {\n expect(<Name>Service.name.endsWith("Service")).toBe(true);\n });\n\n test("should have \'execute\' method", () => {\n expect(<Name>Service.prototype.execute).toBeDefined();\n expect(typeof <Name>Service.prototype.execute).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/services/<Name>Service.ts tests/services/<Name>Service.spec.ts\n```\n';
|
|
7495
|
+
|
|
7496
|
+
// src/templates/claude/skills/make.storage.md.txt
|
|
7497
|
+
var make_storage_md_default = '---\nname: make:storage\ndescription: Generate a new storage class with its test file, then complete the generated code. Use when creating a new S3-compatible storage adapter that extends Storage from @ooneex/storage.\n---\n\n# Make Storage Class\n\nGenerate a new storage class and its test file using the `make:storage` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the storage class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:storage --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/storage/<Name>Storage.ts` - The storage class file (or `modules/<module>/src/storage/<Name>Storage.ts` with `--module`)\n- `tests/storage/<Name>Storage.spec.ts` - The test file (or `modules/<module>/tests/storage/<Name>Storage.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the storage class\n\nEdit `src/storage/<Name>Storage.ts` to complete the implementation:\n\n- Set the `bucket` property to the appropriate bucket name\n- Verify the environment variable names match the project configuration (`STORAGE_<NAME_UPPER>_ACCESS_KEY`, `STORAGE_<NAME_UPPER>_SECRET_KEY`, `STORAGE_<NAME_UPPER>_ENDPOINT`, `STORAGE_<NAME_UPPER>_REGION`)\n- Add any additional storage-specific methods if needed\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { Storage, decorator, StorageException } from "@ooneex/storage";\nimport type { S3Options } from "bun";\n\n@decorator.storage()\nexport class <Name>Storage extends Storage {\n protected bucket: string;\n private readonly accessKey: string;\n private readonly secretKey: string;\n private readonly endpoint: string;\n private readonly region: string;\n\n constructor(options?: {\n accessKey?: string;\n secretKey?: string;\n endpoint?: string;\n region?: string;\n }) {\n super();\n\n const accessKey = options?.accessKey || Bun.env.STORAGE_<NAME_UPPER>_ACCESS_KEY;\n const secretKey = options?.secretKey || Bun.env.STORAGE_<NAME_UPPER>_SECRET_KEY;\n const endpoint = options?.endpoint || Bun.env.STORAGE_<NAME_UPPER>_ENDPOINT;\n\n // ... validation throws StorageException if missing ...\n\n this.accessKey = accessKey;\n this.secretKey = secretKey;\n this.endpoint = endpoint;\n this.region = options?.region || Bun.env.STORAGE_<NAME_UPPER>_REGION || "auto";\n }\n\n public getOptions(): S3Options {\n return {\n accessKeyId: this.accessKey,\n secretAccessKey: this.secretKey,\n endpoint: this.endpoint,\n bucket: this.bucket,\n region: this.region,\n };\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/storage/<Name>Storage.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, bucket field, getOptions method)\n- Add tests relevant to the specific storage class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>StorageAdapter } from "@/storage/<Name>StorageAdapter";\n\ndescribe("<Name>StorageAdapter", () => {\n test("should have class name ending with \'StorageAdapter\'", () => {\n expect(<Name>StorageAdapter.name.endsWith("StorageAdapter")).toBe(true);\n });\n\n test("should have \'bucket\' field", () => {\n expect("bucket" in <Name>StorageAdapter.prototype || "bucket" in Object.getOwnPropertyNames(<Name>StorageAdapter.prototype)).toBe(true);\n });\n\n test("should have \'getOptions\' method", () => {\n expect(<Name>StorageAdapter.prototype.getOptions).toBeDefined();\n expect(typeof <Name>StorageAdapter.prototype.getOptions).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/storage/<Name>Storage.ts tests/storage/<Name>Storage.spec.ts\n```\n';
|
|
7498
|
+
|
|
7499
|
+
// src/templates/claude/skills/make.vector-database.md.txt
|
|
7500
|
+
var make_vector_database_md_default = '---\nname: make:vector-database\ndescription: Generate a new vector database class with its test file, then complete the generated code. Use when creating a new vector database that extends VectorDatabase from @ooneex/rag.\n---\n\n# Make Vector Database Class\n\nGenerate a new vector database class and its test file using the `make:vector-database` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from "@ooneex/container";\nimport { decorator, type ISeed } from "@ooneex/seeds";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the vector database class and test files:\n\n```bash\nbunx @ooneex/cli@latest make:vector-database --name=<name> --module=<module>\n```\n\nWhere `<name>` is the name provided by the user. The `--module` option is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root. The command will generate:\n- `src/databases/<Name>VectorDatabase.ts` - The vector database class file (or `modules/<module>/src/databases/<Name>VectorDatabase.ts` with `--module`)\n- `tests/databases/<Name>VectorDatabase.spec.ts` - The test file (or `modules/<module>/tests/databases/<Name>VectorDatabase.spec.ts` with `--module`)\n\n### 2. Read the generated files\n\nRead both generated files to understand the scaffolded code.\n\n### 3. Complete the vector database class\n\nEdit `src/databases/<Name>VectorDatabase.ts` to complete the implementation:\n\n- Set the `getDatabaseUri()` return value to the actual LanceDB database path\n- Configure the embedding provider and model in `getEmbeddingModel()`\n- Define the custom data fields in `DataType` and map them in `getSchema()`\n- Import the appropriate Apache Arrow types for your schema fields\n\nThe generated class structure follows this pattern:\n\n```typescript\nimport { type EmbeddingModelType, type EmbeddingProviderType, type FieldValueType, VectorDatabase, decorator } from "@ooneex/rag";\nimport { Utf8 } from "apache-arrow";\n\ntype DataType = {\n name: string;\n};\n\n@decorator.vectorDatabase()\nexport class <Name>VectorDatabase extends VectorDatabase<DataType> {\n public getDatabaseUri(): string {\n return "";\n }\n\n public getEmbeddingModel(): { provider: EmbeddingProviderType; model: EmbeddingModelType["model"] } {\n return { provider: "openai", model: "text-embedding-ada-002" };\n }\n\n public getSchema(): { [K in keyof DataType]: FieldValueType } {\n return {\n name: new Utf8(),\n };\n }\n}\n```\n\n### 4. Complete the test file\n\nEdit `tests/databases/<Name>VectorDatabase.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, getDatabaseUri, getEmbeddingModel, getSchema methods)\n- Add tests relevant to the specific vector database class behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from "bun:test";\nimport { <Name>VectorDatabase } from "@/databases/<Name>VectorDatabase";\n\ndescribe("<Name>VectorDatabase", () => {\n test("should have class name ending with \'VectorDatabase\'", () => {\n expect(<Name>VectorDatabase.name.endsWith("VectorDatabase")).toBe(true);\n });\n\n test("should have \'getDatabaseUri\' method", () => {\n expect(<Name>VectorDatabase.prototype.getDatabaseUri).toBeDefined();\n expect(typeof <Name>VectorDatabase.prototype.getDatabaseUri).toBe("function");\n });\n\n test("should have \'getEmbeddingModel\' method", () => {\n expect(<Name>VectorDatabase.prototype.getEmbeddingModel).toBeDefined();\n expect(typeof <Name>VectorDatabase.prototype.getEmbeddingModel).toBe("function");\n });\n\n test("should have \'getSchema\' method", () => {\n expect(<Name>VectorDatabase.prototype.getSchema).toBeDefined();\n expect(typeof <Name>VectorDatabase.prototype.getSchema).toBe("function");\n });\n});\n```\n\n### 5. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/databases/<Name>VectorDatabase.ts tests/databases/<Name>VectorDatabase.spec.ts\n```\n';
|
|
7501
|
+
|
|
7502
|
+
// src/templates/claude/skills/optimize.md.txt
|
|
7503
|
+
var optimize_md_default = "---\nname: optimize\ndescription: Optimize a module's codebase for quality, performance, and clean conventions. Enforces arrow functions (except class methods), type/interface naming, removes duplication, and ensures only important tests remain.\n---\n\n# Optimize Codebase\n\nOptimize a module's codebase for quality, performance, and clean conventions.\n\n## Coding Conventions\n\nApply these conventions across all files in the target module:\n\n- **Arrow functions everywhere** \u2014 use arrow functions for all function expressions, callbacks, standalone functions, and variable declarations. The ONLY exception is class methods, which must use regular method syntax.\n- **Type naming** \u2014 all type aliases must end with the `Type` suffix (e.g., `UserDataType`, `ConfigOptionsType`). Rename any that don't comply.\n- **Interface naming** \u2014 all interface names must start with the `I` prefix (e.g., `IUser`, `IService`, `IRepository`). Rename any that don't comply.\n- **No definite assignment assertions** \u2014 never use `!` on class properties (e.g., `email!: string`). Use a default value or make the property optional (`?`) instead.\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`).\n- For Entity class, if property is optional, add `null` to its type.\n- For dependencies, use `inject` from `@ooneex/container`. For example:\n\n```typescript\nimport { inject } from \"@ooneex/container\";\nimport { decorator, type ISeed } from \"@ooneex/seeds\";\n\n@decorator.seed()\nexport class BookSeed implements ISeed {\n constructor(\n @inject(BookRepository)\n private readonly repository: BookRepository,\n ) {}\n}\n```\n\n## Steps\n\n### 1. Identify the target\n\nDetermine the module to optimize. If the user specifies a module name, work in `modules/<module>/`. If no module is specified, ask the user which module to optimize.\n\n### 2. Read and analyze the module\n\nRead all source files (`src/**/*.ts`) and test files (`tests/**/*.ts`) in the target module. Build a full picture of:\n\n- All types, interfaces, classes, and functions\n- Dependencies between files\n- Existing test coverage\n\n### 3. Enforce naming conventions\n\nScan every file and fix:\n\n- **Types not ending with `Type`** \u2014 rename the type and update all references across the module\n- **Interfaces not starting with `I`** \u2014 rename the interface and update all references across the module\n- **Non-arrow functions** \u2014 convert all function expressions, callbacks, and standalone functions to arrow functions. Do NOT convert class methods.\n- **Missing visibility modifiers** \u2014 add explicit `public`, `private`, or `protected` to all class methods and properties\n\n### 4. Remove code duplication\n\nIdentify duplicated or near-duplicated code within the module:\n\n- Extract shared logic into well-named helper arrow functions or base classes\n- Consolidate repeated type definitions\n- Merge similar utility functions\n- Remove dead code (unused imports, unreachable branches, unused variables)\n\n### 5. Optimize for performance\n\nReview and improve:\n\n- Replace inefficient loops or repeated iterations with single-pass approaches where possible\n- Use `Map`/`Set` instead of arrays for lookups when appropriate\n- Avoid unnecessary object spreads or deep clones\n- Prefer early returns to reduce nesting\n- Remove unnecessary `async`/`await` where a direct return suffices\n- Eliminate redundant null/undefined checks when the type system already guarantees the value\n\n### 6. Optimize tests\n\nReview all test files and restructure:\n\n- **Remove trivial tests** \u2014 delete tests that only check obvious things (e.g., \"class name ends with X\", \"method exists\") unless they serve as smoke tests for generated code\n- **Keep and improve important tests** \u2014 focus on tests that verify actual business logic, edge cases, error handling, and integration behavior\n- **Write fewer but more meaningful tests** \u2014 each test should validate a real scenario or invariant, not just existence checks\n- **Consolidate redundant test cases** \u2014 merge tests that cover the same code path with slightly different inputs into parameterized patterns\n- **Ensure critical paths are covered** \u2014 every public method with logic should have at least one test covering its happy path and one covering its error/edge case\n\n### 7. Final cleanup\n\n- Remove all unused imports\n- Remove empty files or files with no exports\n- Ensure consistent formatting\n\n### 8. Lint and format\n\nRun linting and formatting on all modified files:\n\n```bash\nbunx biome check --fix <list of modified files>\n```\n\n### 9. Run tests\n\nRun the module's tests to verify nothing is broken:\n\n```bash\nbun test <module test directory>\n```\n\nIf any test fails, fix the issue and re-run until all tests pass.\n";
|
|
7504
|
+
|
|
7505
|
+
// src/commands/MakeClaudeSkillCommand.ts
|
|
7506
|
+
var skills = {
|
|
7507
|
+
"make.ai": make_ai_md_default,
|
|
7508
|
+
"make.analytics": make_analytics_md_default,
|
|
7509
|
+
"make.cache": make_cache_md_default,
|
|
7510
|
+
"make.controller": make_controller_md_default,
|
|
7511
|
+
"make.cron": make_cron_md_default,
|
|
7512
|
+
"make.database": make_database_md_default,
|
|
7513
|
+
"make.entity": make_entity_md_default,
|
|
7514
|
+
"make.logger": make_logger_md_default,
|
|
7515
|
+
"make.mailer": make_mailer_md_default,
|
|
7516
|
+
"make.middleware": make_middleware_md_default,
|
|
7517
|
+
"make.migration": make_migration_md_default,
|
|
7518
|
+
"make.permission": make_permission_md_default,
|
|
7519
|
+
"make.pubsub": make_pubsub_md_default,
|
|
7520
|
+
"make.repository": make_repository_md_default,
|
|
7521
|
+
"make.seed": make_seed_md_default,
|
|
7522
|
+
"make.service": make_service_md_default,
|
|
7523
|
+
"make.storage": make_storage_md_default,
|
|
7524
|
+
"make.vector-database": make_vector_database_md_default,
|
|
7525
|
+
commit: commit_md_default,
|
|
7526
|
+
optimize: optimize_md_default
|
|
7527
|
+
};
|
|
7528
|
+
|
|
7529
|
+
class MakeClaudeSkillCommand {
|
|
7530
|
+
getName() {
|
|
7531
|
+
return "make:claude:skill";
|
|
7532
|
+
}
|
|
7533
|
+
getDescription() {
|
|
7534
|
+
return "Generate Claude skills from templates";
|
|
7535
|
+
}
|
|
7536
|
+
async run() {
|
|
7537
|
+
const skillsLocalDir = join11(".claude", "skills");
|
|
7538
|
+
const skillsDir = join11(process.cwd(), skillsLocalDir);
|
|
7539
|
+
const logger = new TerminalLogger10;
|
|
7540
|
+
for (const [skillName, content] of Object.entries(skills)) {
|
|
7541
|
+
const dirName = skillName.replace(/\./g, "-");
|
|
7542
|
+
const filePath = join11(skillsDir, dirName, "SKILL.md");
|
|
7543
|
+
await Bun.write(filePath, content);
|
|
7544
|
+
logger.success(`${join11(skillsLocalDir, dirName, "SKILL.md")} created successfully`, undefined, {
|
|
7545
|
+
showTimestamp: false,
|
|
7546
|
+
showArrow: false,
|
|
7547
|
+
useSymbol: true
|
|
7548
|
+
});
|
|
7549
|
+
}
|
|
7452
7550
|
}
|
|
7453
7551
|
}
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7552
|
+
MakeClaudeSkillCommand = __legacyDecorateClassTS([
|
|
7553
|
+
decorator11.command()
|
|
7554
|
+
], MakeClaudeSkillCommand);
|
|
7555
|
+
// src/commands/MakeCommandCommand.ts
|
|
7556
|
+
import { join as join12 } from "path";
|
|
7557
|
+
import { commandCreate, decorator as decorator12 } from "@ooneex/command";
|
|
7558
|
+
import { TerminalLogger as TerminalLogger11 } from "@ooneex/logger";
|
|
7559
|
+
class MakeCommandCommand {
|
|
7458
7560
|
getName() {
|
|
7459
|
-
return "make:
|
|
7561
|
+
return "make:command";
|
|
7460
7562
|
}
|
|
7461
7563
|
getDescription() {
|
|
7462
|
-
return "Generate a new
|
|
7564
|
+
return "Generate a new command class";
|
|
7463
7565
|
}
|
|
7464
7566
|
async run(options) {
|
|
7465
7567
|
let { name, module } = options;
|
|
7466
7568
|
if (!name) {
|
|
7467
|
-
name = await askName({ message: "Enter
|
|
7569
|
+
name = await askName({ message: "Enter name" });
|
|
7570
|
+
}
|
|
7571
|
+
if (module) {
|
|
7572
|
+
await ensureModule(module);
|
|
7573
|
+
}
|
|
7574
|
+
const base = module ? join12("modules", module) : ".";
|
|
7575
|
+
const { commandPath, testPath } = await commandCreate({
|
|
7576
|
+
name,
|
|
7577
|
+
commandDir: join12(base, "src", "commands"),
|
|
7578
|
+
testsDir: join12(base, "tests", "commands")
|
|
7579
|
+
});
|
|
7580
|
+
if (module && module !== "app") {
|
|
7581
|
+
const appCommandsRootPath = join12(process.cwd(), "modules", "app", "src", "commands", "commands.ts");
|
|
7582
|
+
const appCommandsRootFile = Bun.file(appCommandsRootPath);
|
|
7583
|
+
const importLine = `import "@${module}/commands/commands";`;
|
|
7584
|
+
if (await appCommandsRootFile.exists()) {
|
|
7585
|
+
const appCommandsContent = await appCommandsRootFile.text();
|
|
7586
|
+
if (!appCommandsContent.includes(importLine)) {
|
|
7587
|
+
await Bun.write(appCommandsRootPath, `${appCommandsContent.trimEnd()}
|
|
7588
|
+
${importLine}
|
|
7589
|
+
`);
|
|
7590
|
+
}
|
|
7591
|
+
} else {
|
|
7592
|
+
await Bun.write(appCommandsRootPath, `${importLine}
|
|
7593
|
+
`);
|
|
7594
|
+
}
|
|
7468
7595
|
}
|
|
7469
|
-
|
|
7470
|
-
const
|
|
7471
|
-
if (
|
|
7472
|
-
await
|
|
7596
|
+
const binCommandRunPath = join12(process.cwd(), "modules", "app", "bin", "command", "run.ts");
|
|
7597
|
+
const binCommandRunFile = Bun.file(binCommandRunPath);
|
|
7598
|
+
if (!await binCommandRunFile.exists()) {
|
|
7599
|
+
await Bun.write(binCommandRunPath, command_run_default);
|
|
7473
7600
|
}
|
|
7474
|
-
const
|
|
7475
|
-
const
|
|
7476
|
-
|
|
7477
|
-
|
|
7478
|
-
|
|
7479
|
-
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
const
|
|
7483
|
-
|
|
7484
|
-
const logger = new TerminalLogger7;
|
|
7485
|
-
logger.success(`${join8(cacheLocalDir, name)}Cache.ts created successfully`, undefined, {
|
|
7601
|
+
const packageJsonPath = join12(process.cwd(), "modules", "app", "package.json");
|
|
7602
|
+
const packageJsonFile = Bun.file(packageJsonPath);
|
|
7603
|
+
if (await packageJsonFile.exists()) {
|
|
7604
|
+
const packageJson = await packageJsonFile.json();
|
|
7605
|
+
packageJson.scripts = packageJson.scripts || {};
|
|
7606
|
+
packageJson.scripts.command = "bun ./bin/command/run.ts";
|
|
7607
|
+
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
7608
|
+
}
|
|
7609
|
+
const logger = new TerminalLogger11;
|
|
7610
|
+
logger.success(`${commandPath} created successfully`, undefined, {
|
|
7486
7611
|
showTimestamp: false,
|
|
7487
7612
|
showArrow: false,
|
|
7488
7613
|
useSymbol: true
|
|
7489
7614
|
});
|
|
7490
|
-
logger.success(`${
|
|
7615
|
+
logger.success(`${testPath} created successfully`, undefined, {
|
|
7491
7616
|
showTimestamp: false,
|
|
7492
7617
|
showArrow: false,
|
|
7493
7618
|
useSymbol: true
|
|
7494
7619
|
});
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
const install = Bun.spawn(["bun", "add", "@ooneex/cache"], {
|
|
7501
|
-
cwd: process.cwd(),
|
|
7502
|
-
stdout: "ignore",
|
|
7503
|
-
stderr: "inherit"
|
|
7504
|
-
});
|
|
7505
|
-
await install.exited;
|
|
7506
|
-
}
|
|
7620
|
+
logger.info("Run 'bun run command' to execute commands", undefined, {
|
|
7621
|
+
showTimestamp: false,
|
|
7622
|
+
showArrow: true,
|
|
7623
|
+
showLevel: false
|
|
7624
|
+
});
|
|
7507
7625
|
}
|
|
7508
7626
|
}
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
],
|
|
7512
|
-
// src/commands/
|
|
7513
|
-
import { join as
|
|
7514
|
-
import { decorator as
|
|
7515
|
-
import { TerminalLogger as
|
|
7516
|
-
|
|
7517
|
-
// src/templates/claude/skills/commit.md.txt
|
|
7518
|
-
var commit_md_default = `---
|
|
7519
|
-
name: commit
|
|
7520
|
-
description: Create commit messages grouped by module. Analyzes git changes, groups files under modules/ by module name, and creates separate commits following commitlint conventions. Uses common scope for non-module changes.
|
|
7521
|
-
---
|
|
7522
|
-
|
|
7523
|
-
# Commit by Module
|
|
7524
|
-
|
|
7525
|
-
Create separate commits for each modified module, following the project's commitlint conventions.
|
|
7526
|
-
|
|
7527
|
-
## Workflow
|
|
7528
|
-
|
|
7529
|
-
1. **Analyze staged/unstaged changes**
|
|
7530
|
-
- Run \`git status --porcelain\` to get all modified files
|
|
7531
|
-
- Group changes by module (files under \`modules/<module-name>/\`)
|
|
7532
|
-
- Identify non-module changes (files not in \`modules/\`) as scope \`common\`
|
|
7533
|
-
|
|
7534
|
-
2. **For each module with changes**
|
|
7535
|
-
- Stage only that module's files: \`git add modules/<module-name>/\`
|
|
7536
|
-
- Determine the appropriate commit type based on changes
|
|
7537
|
-
- Create a commit with proper format: \`type(scope): Subject\`
|
|
7538
|
-
- The scope is the module name in lower-case
|
|
7539
|
-
- Repeat for next module
|
|
7540
|
-
|
|
7541
|
-
3. **Handle non-module changes**
|
|
7542
|
-
- Files outside \`modules/\` (e.g., \`bun.lock\`, \`package.json\`, config files, \`packages/\` changes) use scope \`common\`
|
|
7543
|
-
- Stage and commit separately from module changes
|
|
7544
|
-
|
|
7545
|
-
## Commit Message Format
|
|
7546
|
-
|
|
7547
|
-
\`\`\`
|
|
7548
|
-
type(scope): Subject line
|
|
7549
|
-
\`\`\`
|
|
7550
|
-
|
|
7551
|
-
### Valid Types
|
|
7552
|
-
- \`feat\`: New feature
|
|
7553
|
-
- \`fix\`: Bug fix
|
|
7554
|
-
- \`refactor\`: Code refactoring (no new feature, no bug fix)
|
|
7555
|
-
- \`test\`: Adding/updating tests
|
|
7556
|
-
- \`chore\`: Maintenance tasks (dependencies, configs)
|
|
7557
|
-
- \`docs\`: Documentation changes
|
|
7558
|
-
- \`style\`: Code style changes (formatting, whitespace)
|
|
7559
|
-
- \`perf\`: Performance improvements
|
|
7560
|
-
- \`build\`: Build system changes
|
|
7561
|
-
- \`ci\`: CI configuration changes
|
|
7562
|
-
- \`revert\`: Revert previous commit
|
|
7563
|
-
|
|
7564
|
-
### Scope Rules
|
|
7565
|
-
- For files under \`modules/<name>/\`: use the module name as scope (e.g., \`module\` scope matches the commitlint config)
|
|
7566
|
-
- For all other files: use \`common\`
|
|
7567
|
-
- Scope must be lower-case
|
|
7568
|
-
- Scope must never be empty
|
|
7569
|
-
- If the module name matches a valid commitlint scope, use it directly
|
|
7570
|
-
- If it does not match, use \`module\` as the scope
|
|
7571
|
-
|
|
7572
|
-
### Subject Rules
|
|
7573
|
-
- Use sentence-case, start-case, pascal-case, or upper-case
|
|
7574
|
-
- No period at the end
|
|
7575
|
-
- Maximum 100 characters for entire header
|
|
7576
|
-
- Use imperative mood ("Add feature" not "Added feature")
|
|
7577
|
-
|
|
7578
|
-
## Determining Commit Type
|
|
7579
|
-
|
|
7580
|
-
Analyze the changes to determine the appropriate type:
|
|
7581
|
-
|
|
7582
|
-
| Change Pattern | Type |
|
|
7583
|
-
|---------------|------|
|
|
7584
|
-
| New files with functionality | \`feat\` |
|
|
7585
|
-
| Bug fixes, error corrections | \`fix\` |
|
|
7586
|
-
| Code restructuring, renaming | \`refactor\` |
|
|
7587
|
-
| New/updated test files (\`*.spec.ts\`) | \`test\` |
|
|
7588
|
-
| Documentation files (\`*.md\`) | \`docs\` |
|
|
7589
|
-
| Dependency updates, lock files | \`chore\` |
|
|
7590
|
-
| Build configs, scripts | \`build\` |
|
|
7591
|
-
| CI/CD files | \`ci\` |
|
|
7592
|
-
| Formatting only | \`style\` |
|
|
7593
|
-
| Performance optimizations | \`perf\` |
|
|
7594
|
-
|
|
7595
|
-
## Examples
|
|
7596
|
-
|
|
7597
|
-
### Example 1: Multiple Module Changes
|
|
7598
|
-
|
|
7599
|
-
Git status shows:
|
|
7600
|
-
\`\`\`
|
|
7601
|
-
M modules/user/src/services/UserService.ts
|
|
7602
|
-
A modules/user/src/services/AuthService.ts
|
|
7603
|
-
M modules/user/tests/UserService.spec.ts
|
|
7604
|
-
M modules/product/src/entities/Product.ts
|
|
7605
|
-
M modules/product/src/repositories/ProductRepository.ts
|
|
7606
|
-
M bun.lock
|
|
7607
|
-
M packages/cache/src/index.ts
|
|
7608
|
-
\`\`\`
|
|
7609
|
-
|
|
7610
|
-
Commands to execute:
|
|
7611
|
-
\`\`\`bash
|
|
7612
|
-
# Commit user module
|
|
7613
|
-
git add modules/user/
|
|
7614
|
-
git commit -m "feat(user): Add AuthService and update UserService"
|
|
7627
|
+
MakeCommandCommand = __legacyDecorateClassTS([
|
|
7628
|
+
decorator12.command()
|
|
7629
|
+
], MakeCommandCommand);
|
|
7630
|
+
// src/commands/MakeControllerCommand.ts
|
|
7631
|
+
import { basename, join as join13 } from "path";
|
|
7632
|
+
import { decorator as decorator13 } from "@ooneex/command";
|
|
7633
|
+
import { TerminalLogger as TerminalLogger12 } from "@ooneex/logger";
|
|
7634
|
+
import { toKebabCase as toKebabCase3, toPascalCase as toPascalCase5, trim } from "@ooneex/utils";
|
|
7615
7635
|
|
|
7616
|
-
|
|
7617
|
-
|
|
7618
|
-
|
|
7636
|
+
// src/prompts/askConfirm.ts
|
|
7637
|
+
var import_enquirer3 = __toESM(require_enquirer(), 1);
|
|
7638
|
+
var askConfirm = async (config) => {
|
|
7639
|
+
const response = await import_enquirer3.prompt({
|
|
7640
|
+
type: "confirm",
|
|
7641
|
+
name: "confirm",
|
|
7642
|
+
message: config.message,
|
|
7643
|
+
initial: config.initial
|
|
7644
|
+
});
|
|
7645
|
+
return response.confirm;
|
|
7646
|
+
};
|
|
7619
7647
|
|
|
7620
|
-
|
|
7621
|
-
|
|
7622
|
-
git commit -m "chore(common): Update dependencies and cache package"
|
|
7623
|
-
\`\`\`
|
|
7648
|
+
// ../types/dist/index.js
|
|
7649
|
+
var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
|
|
7624
7650
|
|
|
7625
|
-
|
|
7651
|
+
// src/prompts/askRouteMethod.ts
|
|
7652
|
+
var import_enquirer4 = __toESM(require_enquirer(), 1);
|
|
7626
7653
|
|
|
7627
|
-
|
|
7628
|
-
|
|
7629
|
-
A modules/payment/src/services/StripeService.ts
|
|
7630
|
-
A modules/payment/tests/StripeService.spec.ts
|
|
7631
|
-
\`\`\`
|
|
7654
|
+
// src/constraints/AssertRouteMethod.ts
|
|
7655
|
+
import { Assert as Assert2, Validation as Validation2 } from "@ooneex/validation";
|
|
7632
7656
|
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7657
|
+
class AssertRouteMethod extends Validation2 {
|
|
7658
|
+
getConstraint() {
|
|
7659
|
+
return Assert2("string >= 3");
|
|
7660
|
+
}
|
|
7661
|
+
getErrorMessage() {
|
|
7662
|
+
return `Route method must be one of: ${HTTP_METHODS.join(", ")}`;
|
|
7663
|
+
}
|
|
7664
|
+
validate(data, constraint) {
|
|
7665
|
+
const basicValidation = super.validate(data, constraint);
|
|
7666
|
+
if (!basicValidation.isValid) {
|
|
7667
|
+
return basicValidation;
|
|
7668
|
+
}
|
|
7669
|
+
const method = data;
|
|
7670
|
+
if (method.trim() !== method) {
|
|
7671
|
+
return {
|
|
7672
|
+
isValid: false,
|
|
7673
|
+
message: this.getErrorMessage() || "Invalid route method format"
|
|
7674
|
+
};
|
|
7675
|
+
}
|
|
7676
|
+
const upperMethod = method.toUpperCase();
|
|
7677
|
+
if (!HTTP_METHODS.includes(upperMethod)) {
|
|
7678
|
+
return {
|
|
7679
|
+
isValid: false,
|
|
7680
|
+
message: this.getErrorMessage() || "Invalid route method"
|
|
7681
|
+
};
|
|
7682
|
+
}
|
|
7683
|
+
return {
|
|
7684
|
+
isValid: true
|
|
7685
|
+
};
|
|
7686
|
+
}
|
|
7687
|
+
}
|
|
7638
7688
|
|
|
7639
|
-
|
|
7689
|
+
// src/prompts/askRouteMethod.ts
|
|
7690
|
+
var askRouteMethod = async (config) => {
|
|
7691
|
+
const response = await import_enquirer4.prompt({
|
|
7692
|
+
type: "select",
|
|
7693
|
+
name: "method",
|
|
7694
|
+
message: config.message,
|
|
7695
|
+
initial: config.initial ?? 0,
|
|
7696
|
+
choices: HTTP_METHODS.map((method) => method),
|
|
7697
|
+
validate: (value) => {
|
|
7698
|
+
const constraint = new AssertRouteMethod;
|
|
7699
|
+
const result = constraint.validate(value);
|
|
7700
|
+
if (!result.isValid) {
|
|
7701
|
+
return result.message || "Route method is invalid";
|
|
7702
|
+
}
|
|
7703
|
+
return true;
|
|
7704
|
+
}
|
|
7705
|
+
});
|
|
7706
|
+
return response.method;
|
|
7707
|
+
};
|
|
7640
7708
|
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
M package.json
|
|
7644
|
-
M bun.lock
|
|
7645
|
-
M packages/logger/src/Logger.ts
|
|
7646
|
-
\`\`\`
|
|
7709
|
+
// src/prompts/askRouteName.ts
|
|
7710
|
+
var import_enquirer5 = __toESM(require_enquirer(), 1);
|
|
7647
7711
|
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
git commit -m "chore(common): Update dependencies and logger package"
|
|
7652
|
-
\`\`\`
|
|
7712
|
+
// src/constraints/AssertRouteName.ts
|
|
7713
|
+
import { Assert as Assert3, Validation as Validation3 } from "@ooneex/validation";
|
|
7714
|
+
var ROUTE_NAME_REGEX = /^[a-zA-Z0-9]+\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$/;
|
|
7653
7715
|
|
|
7654
|
-
|
|
7716
|
+
class AssertRouteName extends Validation3 {
|
|
7717
|
+
getConstraint() {
|
|
7718
|
+
return Assert3("string >= 7");
|
|
7719
|
+
}
|
|
7720
|
+
getErrorMessage() {
|
|
7721
|
+
return "Route name must follow format: namespace.resource.action (e.g., 'api.users.list')";
|
|
7722
|
+
}
|
|
7723
|
+
validate(data, constraint) {
|
|
7724
|
+
const basicValidation = super.validate(data, constraint);
|
|
7725
|
+
if (!basicValidation.isValid) {
|
|
7726
|
+
return basicValidation;
|
|
7727
|
+
}
|
|
7728
|
+
const routeName = data;
|
|
7729
|
+
if (routeName.trim() !== routeName) {
|
|
7730
|
+
return {
|
|
7731
|
+
isValid: false,
|
|
7732
|
+
message: this.getErrorMessage() || "Invalid route name format"
|
|
7733
|
+
};
|
|
7734
|
+
}
|
|
7735
|
+
if (!ROUTE_NAME_REGEX.test(routeName)) {
|
|
7736
|
+
return {
|
|
7737
|
+
isValid: false,
|
|
7738
|
+
message: this.getErrorMessage() || "Invalid route name format"
|
|
7739
|
+
};
|
|
7740
|
+
}
|
|
7741
|
+
const segments = routeName.split(".");
|
|
7742
|
+
if (segments.length !== 3) {
|
|
7743
|
+
return {
|
|
7744
|
+
isValid: false,
|
|
7745
|
+
message: this.getErrorMessage() || "Invalid route name format"
|
|
7746
|
+
};
|
|
7747
|
+
}
|
|
7748
|
+
const [namespace, resource, action] = segments;
|
|
7749
|
+
if (!namespace || !resource || !action) {
|
|
7750
|
+
return {
|
|
7751
|
+
isValid: false,
|
|
7752
|
+
message: this.getErrorMessage() || "Invalid route name format"
|
|
7753
|
+
};
|
|
7754
|
+
}
|
|
7755
|
+
return {
|
|
7756
|
+
isValid: true
|
|
7757
|
+
};
|
|
7758
|
+
}
|
|
7759
|
+
}
|
|
7655
7760
|
|
|
7656
|
-
|
|
7657
|
-
|
|
7658
|
-
|
|
7659
|
-
|
|
7660
|
-
|
|
7661
|
-
|
|
7761
|
+
// src/prompts/askRouteName.ts
|
|
7762
|
+
var askRouteName = async (config) => {
|
|
7763
|
+
const response = await import_enquirer5.prompt({
|
|
7764
|
+
type: "input",
|
|
7765
|
+
name: "routeName",
|
|
7766
|
+
message: config.message,
|
|
7767
|
+
validate: (value) => {
|
|
7768
|
+
const constraint = new AssertRouteName;
|
|
7769
|
+
const result = constraint.validate(value);
|
|
7770
|
+
if (!result.isValid) {
|
|
7771
|
+
return result.message || "Route name is invalid";
|
|
7772
|
+
}
|
|
7773
|
+
return true;
|
|
7774
|
+
}
|
|
7775
|
+
});
|
|
7776
|
+
return response.routeName;
|
|
7777
|
+
};
|
|
7662
7778
|
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
git add modules/order/
|
|
7666
|
-
git commit -m "refactor(order): Replace OldOrderService with OrderService"
|
|
7667
|
-
\`\`\`
|
|
7779
|
+
// src/prompts/askRoutePath.ts
|
|
7780
|
+
var import_enquirer6 = __toESM(require_enquirer(), 1);
|
|
7668
7781
|
|
|
7669
|
-
|
|
7782
|
+
// src/constraints/AssertRoutePath.ts
|
|
7783
|
+
import { Assert as Assert4, Validation as Validation4 } from "@ooneex/validation";
|
|
7784
|
+
var ROUTE_PATH_MIN_LENGTH = 1;
|
|
7785
|
+
var ROUTE_PATH_REGEX = /^\/[\w\-/:]*$/;
|
|
7786
|
+
var VALID_SEGMENT_REGEX = /^[a-zA-Z0-9\-_]+$/;
|
|
7787
|
+
var PARAM_SEGMENT_REGEX = /^:[a-zA-Z][a-zA-Z0-9]*$/;
|
|
7670
7788
|
|
|
7671
|
-
|
|
7789
|
+
class AssertRoutePath extends Validation4 {
|
|
7790
|
+
getConstraint() {
|
|
7791
|
+
return Assert4(`string >= ${ROUTE_PATH_MIN_LENGTH}`);
|
|
7792
|
+
}
|
|
7793
|
+
getErrorMessage() {
|
|
7794
|
+
return "Route path must start with '/' and contain only valid segments (e.g., '/users', '/api/users/:id')";
|
|
7795
|
+
}
|
|
7796
|
+
validate(data, constraint) {
|
|
7797
|
+
const basicValidation = super.validate(data, constraint);
|
|
7798
|
+
if (!basicValidation.isValid) {
|
|
7799
|
+
return basicValidation;
|
|
7800
|
+
}
|
|
7801
|
+
const path = data;
|
|
7802
|
+
if (path.trim() !== path) {
|
|
7803
|
+
return {
|
|
7804
|
+
isValid: false,
|
|
7805
|
+
message: this.getErrorMessage() || "Invalid route path format"
|
|
7806
|
+
};
|
|
7807
|
+
}
|
|
7808
|
+
if (!path.startsWith("/")) {
|
|
7809
|
+
return {
|
|
7810
|
+
isValid: false,
|
|
7811
|
+
message: "Route path must start with '/'"
|
|
7812
|
+
};
|
|
7813
|
+
}
|
|
7814
|
+
if (path.length > 1 && path.endsWith("/")) {
|
|
7815
|
+
return {
|
|
7816
|
+
isValid: false,
|
|
7817
|
+
message: "Route path cannot end with '/' (except for root path)"
|
|
7818
|
+
};
|
|
7819
|
+
}
|
|
7820
|
+
if (!ROUTE_PATH_REGEX.test(path)) {
|
|
7821
|
+
return {
|
|
7822
|
+
isValid: false,
|
|
7823
|
+
message: this.getErrorMessage() || "Invalid route path format"
|
|
7824
|
+
};
|
|
7825
|
+
}
|
|
7826
|
+
if (path === "/") {
|
|
7827
|
+
return {
|
|
7828
|
+
isValid: true
|
|
7829
|
+
};
|
|
7830
|
+
}
|
|
7831
|
+
const segments = path.slice(1).split("/");
|
|
7832
|
+
for (const segment of segments) {
|
|
7833
|
+
if (!segment) {
|
|
7834
|
+
return {
|
|
7835
|
+
isValid: false,
|
|
7836
|
+
message: "Route path cannot contain empty segments (double slashes)"
|
|
7837
|
+
};
|
|
7838
|
+
}
|
|
7839
|
+
if (segment.startsWith(":")) {
|
|
7840
|
+
if (!PARAM_SEGMENT_REGEX.test(segment)) {
|
|
7841
|
+
return {
|
|
7842
|
+
isValid: false,
|
|
7843
|
+
message: `Invalid parameter segment '${segment}'. Parameters must follow format ':paramName' with alphanumeric characters only`
|
|
7844
|
+
};
|
|
7845
|
+
}
|
|
7846
|
+
} else {
|
|
7847
|
+
if (!VALID_SEGMENT_REGEX.test(segment)) {
|
|
7848
|
+
return {
|
|
7849
|
+
isValid: false,
|
|
7850
|
+
message: `Invalid path segment '${segment}'. Segments must contain only letters, numbers, hyphens, and underscores`
|
|
7851
|
+
};
|
|
7852
|
+
}
|
|
7853
|
+
}
|
|
7854
|
+
}
|
|
7855
|
+
return {
|
|
7856
|
+
isValid: true
|
|
7857
|
+
};
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7672
7860
|
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
|
|
7679
|
-
|
|
7861
|
+
// src/prompts/askRoutePath.ts
|
|
7862
|
+
var askRoutePath = async (config) => {
|
|
7863
|
+
const response = await import_enquirer6.prompt({
|
|
7864
|
+
type: "input",
|
|
7865
|
+
name: "path",
|
|
7866
|
+
message: config.message,
|
|
7867
|
+
initial: config.initial ?? "/",
|
|
7868
|
+
validate: (value) => {
|
|
7869
|
+
const constraint = new AssertRoutePath;
|
|
7870
|
+
const result = constraint.validate(value);
|
|
7871
|
+
if (!result.isValid) {
|
|
7872
|
+
return result.message || "Route path is invalid";
|
|
7873
|
+
}
|
|
7874
|
+
return true;
|
|
7875
|
+
}
|
|
7876
|
+
});
|
|
7877
|
+
return response.path;
|
|
7878
|
+
};
|
|
7680
7879
|
|
|
7681
|
-
|
|
7880
|
+
// src/templates/controller.socket.txt
|
|
7881
|
+
var controller_socket_default = `import type { ContextType } from "@ooneex/socket";
|
|
7882
|
+
import { ERole } from "@ooneex/role";
|
|
7883
|
+
import { Route } from "@ooneex/routing";
|
|
7884
|
+
import { Assert } from "@ooneex/validation";
|
|
7682
7885
|
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
2. Tests pass for modified modules
|
|
7686
|
-
3. No debug code or console.logs left in
|
|
7687
|
-
4. Commit message follows the format exactly
|
|
7688
|
-
5. For Entity class, if property is optional, add \`null\` to its type.
|
|
7886
|
+
export type {{TYPE_NAME}}RouteType = {
|
|
7887
|
+
params: {
|
|
7689
7888
|
|
|
7690
|
-
|
|
7889
|
+
},
|
|
7890
|
+
payload: {
|
|
7691
7891
|
|
|
7692
|
-
|
|
7693
|
-
|
|
7694
|
-
1. \`feat\` if primary change is new functionality
|
|
7695
|
-
2. \`fix\` if primary change is a bug fix
|
|
7696
|
-
3. Split into multiple commits if changes are truly independent
|
|
7892
|
+
},
|
|
7893
|
+
queries: {
|
|
7697
7894
|
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
\`\`\`bash
|
|
7701
|
-
git add modules/user/
|
|
7702
|
-
git commit -m "refactor(user): Remove deprecated UserAdapter"
|
|
7703
|
-
\`\`\`
|
|
7895
|
+
},
|
|
7896
|
+
response: {
|
|
7704
7897
|
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
\`\`\`bash
|
|
7708
|
-
git add modules/product/
|
|
7709
|
-
git commit -m "refactor(product): Reorganize service file structure"
|
|
7710
|
-
\`\`\`
|
|
7711
|
-
`;
|
|
7898
|
+
},
|
|
7899
|
+
};
|
|
7712
7900
|
|
|
7713
|
-
|
|
7714
|
-
|
|
7901
|
+
@Route.socket("{{ROUTE_PATH}}", {
|
|
7902
|
+
name: "{{ROUTE_NAME}}",
|
|
7903
|
+
version: 1,
|
|
7904
|
+
description: "",
|
|
7905
|
+
params: {
|
|
7906
|
+
// id: Assert("string"),
|
|
7907
|
+
},
|
|
7908
|
+
payload: Assert({
|
|
7715
7909
|
|
|
7716
|
-
|
|
7717
|
-
|
|
7910
|
+
}),
|
|
7911
|
+
queries: Assert({
|
|
7718
7912
|
|
|
7719
|
-
|
|
7720
|
-
|
|
7913
|
+
}),
|
|
7914
|
+
response: Assert({
|
|
7721
7915
|
|
|
7722
|
-
|
|
7723
|
-
var make_controller_md_default = "---\nname: make:controller\ndescription: Generate a new controller class with route type and test file, then complete the generated code. Use when creating a new HTTP or WebSocket controller with routing, validation, and role-based access.\n---\n\n# Make Controller Class\n\nGenerate a new controller class, its route type, and test file using the `make:controller` CLI command, then complete the generated code with proper implementation.\n\n## Coding Conventions\n\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`)\n- Always use arrow functions when possible, but NOT for class methods (class methods use regular method syntax)\n- End type names with the `Type` suffix (e.g., `ExceptionStackFrameType`, `ServiceDataType`)\n- Start interface names with the `I` prefix (e.g., `IException`, `IService`, `IAiChat`)\n- For Entity class, if property is optional, add `null` to its type.\n\n## Steps\n\n### 1. Run the generator\n\nRun the following command to scaffold the controller class and related files:\n\n```bash\nbunx @ooneex/cli@latest make:controller --name=<name> --module=<module> --is-socket=<true|false> --route-name=<route.name> --route-path=<route.path> --route-method=<route.method>\n```\n\nWhere:\n- `<name>` is the controller name provided by the user\n- `--module` is optional \u2014 if provided, files are generated under `modules/<module>/` instead of the project root\n- `--is-socket` determines HTTP vs WebSocket controller (defaults to `false`)\n- `--route-name` is the route name using dot notation: `<resource>.<action>` (e.g., `user.create`, `book.list`, `flashcard.delete`)\n- `--route-path` is the route path (e.g., `/api/users`)\n- `--route-method` is the HTTP method (e.g., `get`, `post`, `put`, `patch`, `delete`) \u2014 only for HTTP controllers\n\nThe command will generate (paths prefixed with `modules/<module>/` when `--module` is provided):\n- `src/controllers/<Name>Controller.ts` - The controller class file\n- `src/types/routes/<route.name>.ts` - The route type file (will be moved into the controller file \u2014 see step 3)\n- `tests/controllers/<Name>Controller.spec.ts` - The test file\n\n### 2. Read the generated files\n\nRead all three generated files to understand the scaffolded code.\n\n### 3. Complete the route type\n\n**IMPORTANT: Keep the route type inside the controller file**, not in a separate type file. Define the type directly in `src/controllers/<Name>Controller.ts` above the class definition. Delete the generated `src/types/routes/<route.name>.ts` file if it was created.\n\n**Remove unnecessary `params`, `payload`, and `queries` \u2014 only include what the route actually needs:**\n\n- **`params`** \u2014 Include only when the route path contains dynamic segments (e.g., `/api/users/:id`). Remove entirely for routes with no URL parameters (e.g., `/api/users`).\n- **`payload`** \u2014 Include only for methods that accept a request body (`post`, `put`, `patch`). Remove entirely for `get` and `delete` routes.\n- **`queries`** \u2014 Include only when the route supports query string filtering, pagination, or sorting (e.g., list/search endpoints). Remove entirely when not needed.\n- **`response`** \u2014 Always include.\n\nThe route type structure follows this pattern (remove unused sections):\n\n```typescript\n// Example: GET /api/users (list) \u2014 no params, no payload, has queries\ntype <TypeName>RouteType = {\n queries: {\n\n },\n response: {\n\n },\n};\n\n// Example: POST /api/users (create) \u2014 no params, has payload, no queries\ntype <TypeName>RouteType = {\n payload: {\n\n },\n response: {\n\n },\n};\n\n// Example: GET /api/users/:id (detail) \u2014 has params, no payload, no queries\ntype <TypeName>RouteType = {\n params: {\n\n },\n response: {\n\n },\n};\n\n// Example: PUT /api/users/:id (update) \u2014 has params, has payload, no queries\ntype <TypeName>RouteType = {\n params: {\n\n },\n payload: {\n\n },\n response: {\n\n },\n};\n```\n\n### 4. Complete the controller class\n\nEdit `src/controllers/<Name>Controller.ts` to complete the implementation:\n\n- Set appropriate `roles` for access control\n- Add a meaningful `description` for the route that explains what the endpoint does (e.g., `\"Create a new user account\"`, `\"List all books with pagination\"`)\n- **Keep the controller thin** \u2014 do NOT put business logic in the controller. Put all logic in the corresponding service and inject the service into the controller via the constructor. The controller's `index` method should only delegate to the service.\n- **Remove unnecessary `params`, `payload`, and `queries`** from both the route type and the `@Route` decorator \u2014 only include what the route actually needs (see step 3 rules).\n\n**Add or remove `params`, `payload`, and `queries` in the `@Route` decorator to match the route type (step 3):**\n\n- **`params`** \u2014 Include with `Assert()` validators only when the route has URL parameters. Remove the `params` key entirely otherwise.\n- **`payload`** \u2014 Include with `Assert({...})` only for `post`, `put`, `patch` methods. Remove the `payload` key entirely for `get` and `delete`.\n- **`queries`** \u2014 Include with `Assert({...})` only when query parameters are expected. Remove the `queries` key entirely otherwise.\n- **`response`** \u2014 Always include with `Assert({...})`.\n\n**HTTP controller** generated structure (remove `params`, `payload`, `queries` as needed \u2014 see rules above):\n\n```typescript\nimport type { ContextType } from \"@ooneex/controller\";\nimport { ERole } from \"@ooneex/role\";\nimport { Route } from \"@ooneex/routing\";\nimport { Assert } from \"@ooneex/validation\";\n\ntype <TypeName>RouteType = {\n // Only include params, payload, queries as needed (see step 3)\n response: {\n\n },\n};\n\n@Route.<method>(\"<route.path>\", {\n name: \"<route.name>\",\n version: 1,\n description: \"\",\n // Only include params, payload, queries as needed (see step 3)\n response: Assert({\n\n }),\n roles: [ERole.USER],\n})\nexport class <Name>Controller {\n public async index(context: ContextType<<TypeName>RouteType>) {\n return context.response.json({\n\n });\n }\n}\n```\n\n**Socket controller** generated structure (remove `params`, `payload`, `queries` as needed \u2014 see rules above):\n\n```typescript\nimport type { ContextType } from \"@ooneex/socket\";\nimport { ERole } from \"@ooneex/role\";\nimport { Route } from \"@ooneex/routing\";\nimport { Assert } from \"@ooneex/validation\";\n\ntype <TypeName>RouteType = {\n // Only include params, payload, queries as needed (see step 3)\n response: {\n\n },\n};\n\n@Route.socket(\"<route.path>\", {\n name: \"<route.name>\",\n version: 1,\n description: \"\",\n // Only include params, payload, queries as needed (see step 3)\n response: Assert({\n\n }),\n roles: [ERole.USER],\n})\nexport class <Name>Controller {\n public async index(context: ContextType<<TypeName>RouteType>) {\n return context.response.json({\n\n });\n }\n}\n```\n\n### 5. Complete the test file\n\nEdit `tests/controllers/<Name>Controller.spec.ts` to add meaningful tests beyond the scaffolded ones:\n\n- Keep the existing scaffolded tests (class name, index method)\n- Add tests relevant to the specific controller behavior\n\nThe generated test structure follows this pattern:\n\n```typescript\nimport { describe, expect, test } from \"bun:test\";\nimport { <Name>Controller } from \"@/controllers/<Name>Controller\";\n\ndescribe(\"<Name>Controller\", () => {\n test(\"should have class name ending with 'Controller'\", () => {\n expect(<Name>Controller.name.endsWith(\"Controller\")).toBe(true);\n });\n\n test(\"should have 'index' method\", () => {\n expect(<Name>Controller.prototype.index).toBeDefined();\n expect(typeof <Name>Controller.prototype.index).toBe(\"function\");\n });\n});\n```\n\n### 6. Register the controller in the module\n\nAdd the new controller to the module's `controllers` array in `src/<PascalModuleName>Module.ts` (e.g., `src/BookModule.ts` for the `book` module):\n\n```typescript\nimport type { ModuleType } from \"@ooneex/module\";\nimport { <Name>Controller } from \"./controllers/<Name>Controller\";\n\nexport const <PascalModuleName>Module: ModuleType = {\n controllers: [<Name>Controller],\n entities: [],\n middlewares: [],\n cronJobs: [],\n events: [],\n};\n```\n\nThe module file uses PascalCase naming: `<PascalModuleName>Module.ts` with export `<PascalModuleName>Module`.\n\nIf the module already has other controllers registered, append the new controller to the existing `controllers` array and add the import alongside existing imports.\n\n### 7. Lint and format\n\nRun linting and formatting on the generated files:\n\n```bash\nbunx biome check --fix src/controllers/<Name>Controller.ts tests/controllers/<Name>Controller.spec.ts\n```\n\n### 8. Create the service\n\nAfter the controller is created, generate a service class for the controller's business logic using the `make:service` skill:\n\n```\n/make:service --name=<Name>\n```\n\nWhere `<Name>` matches the controller name (e.g., if the controller is `CreateUserController`, the service is `CreateUserService`).\n\n### 9. Create the pubsub event\n\nAfter the service is created, generate a pubsub event class for the controller's domain events using the `make:pubsub` skill:\n\n```\n/make:pubsub --name=<Name> --channel=<resource>.<action>\n```\n\nWhere:\n- `<Name>` matches the controller name (e.g., if the controller is `CreateUserController`, the event is `CreateUserEvent`)\n- `<resource>.<action>` follows the same dot notation as the route name (e.g., `user.create`, `book.delete`)\n\nOnce the event is created:\n- Inject the **service** into the **event** via the constructor, and call the service's `execute` method from the event's `handler` method.\n- Inject the **event** into the **controller** via the constructor, and publish the event from the controller's `index` method.\n";
|
|
7916
|
+
}),
|
|
7917
|
+
roles: [ERole.USER],
|
|
7918
|
+
})
|
|
7919
|
+
export class {{NAME}}Controller {
|
|
7920
|
+
public async index(context: ContextType<{{TYPE_NAME}}RouteType>) {
|
|
7921
|
+
// const { id } = context.params;
|
|
7724
7922
|
|
|
7725
|
-
|
|
7726
|
-
|
|
7923
|
+
return context.response.json({
|
|
7924
|
+
|
|
7925
|
+
});
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
`;
|
|
7727
7929
|
|
|
7728
|
-
// src/templates/
|
|
7729
|
-
var
|
|
7930
|
+
// src/templates/controller.test.txt
|
|
7931
|
+
var controller_test_default = `import { describe, expect, test } from "bun:test";
|
|
7932
|
+
import { {{NAME}}Controller } from "@/controllers/{{NAME}}Controller";
|
|
7730
7933
|
|
|
7731
|
-
|
|
7732
|
-
|
|
7934
|
+
describe("{{NAME}}Controller", () => {
|
|
7935
|
+
test("should have class name ending with 'Controller'", () => {
|
|
7936
|
+
expect({{NAME}}Controller.name.endsWith("Controller")).toBe(true);
|
|
7937
|
+
});
|
|
7733
7938
|
|
|
7734
|
-
|
|
7735
|
-
|
|
7939
|
+
test("should have 'index' method", () => {
|
|
7940
|
+
expect({{NAME}}Controller.prototype.index).toBeDefined();
|
|
7941
|
+
expect(typeof {{NAME}}Controller.prototype.index).toBe("function");
|
|
7942
|
+
});
|
|
7943
|
+
});
|
|
7944
|
+
`;
|
|
7736
7945
|
|
|
7737
|
-
// src/templates/
|
|
7738
|
-
var
|
|
7946
|
+
// src/templates/controller.txt
|
|
7947
|
+
var controller_default = `import type { ContextType } from "@ooneex/controller";
|
|
7948
|
+
import { ERole } from "@ooneex/role";
|
|
7949
|
+
import { Route } from "@ooneex/routing";
|
|
7950
|
+
import { Assert } from "@ooneex/validation";
|
|
7739
7951
|
|
|
7740
|
-
|
|
7741
|
-
|
|
7952
|
+
export type {{TYPE_NAME}}RouteType = {
|
|
7953
|
+
params: {
|
|
7742
7954
|
|
|
7743
|
-
|
|
7744
|
-
|
|
7955
|
+
},
|
|
7956
|
+
payload: {
|
|
7745
7957
|
|
|
7746
|
-
|
|
7747
|
-
|
|
7958
|
+
},
|
|
7959
|
+
queries: {
|
|
7748
7960
|
|
|
7749
|
-
|
|
7750
|
-
|
|
7961
|
+
},
|
|
7962
|
+
response: {
|
|
7751
7963
|
|
|
7752
|
-
|
|
7753
|
-
|
|
7964
|
+
},
|
|
7965
|
+
};
|
|
7754
7966
|
|
|
7755
|
-
|
|
7756
|
-
|
|
7967
|
+
@Route.{{ROUTE_METHOD}}("{{ROUTE_PATH}}", {
|
|
7968
|
+
name: "{{ROUTE_NAME}}",
|
|
7969
|
+
version: 1,
|
|
7970
|
+
description: "",
|
|
7971
|
+
params: {
|
|
7972
|
+
// id: Assert("string"),
|
|
7973
|
+
},
|
|
7974
|
+
payload: Assert({
|
|
7757
7975
|
|
|
7758
|
-
|
|
7759
|
-
|
|
7976
|
+
}),
|
|
7977
|
+
queries: Assert({
|
|
7760
7978
|
|
|
7761
|
-
|
|
7762
|
-
|
|
7979
|
+
}),
|
|
7980
|
+
response: Assert({
|
|
7763
7981
|
|
|
7764
|
-
|
|
7765
|
-
|
|
7982
|
+
}),
|
|
7983
|
+
roles: [ERole.USER],
|
|
7984
|
+
})
|
|
7985
|
+
export class {{NAME}}Controller {
|
|
7986
|
+
public async index(context: ContextType<{{TYPE_NAME}}RouteType>) {
|
|
7987
|
+
// const { id } = context.params;
|
|
7766
7988
|
|
|
7767
|
-
|
|
7768
|
-
var optimize_md_default = "---\nname: optimize\ndescription: Optimize a module's codebase for quality, performance, and clean conventions. Enforces arrow functions (except class methods), type/interface naming, removes duplication, and ensures only important tests remain.\n---\n\n# Optimize Codebase\n\nOptimize a module's codebase for quality, performance, and clean conventions.\n\n## Coding Conventions\n\nApply these conventions across all files in the target module:\n\n- **Arrow functions everywhere** \u2014 use arrow functions for all function expressions, callbacks, standalone functions, and variable declarations. The ONLY exception is class methods, which must use regular method syntax.\n- **Type naming** \u2014 all type aliases must end with the `Type` suffix (e.g., `UserDataType`, `ConfigOptionsType`). Rename any that don't comply.\n- **Interface naming** \u2014 all interface names must start with the `I` prefix (e.g., `IUser`, `IService`, `IRepository`). Rename any that don't comply.\n- **No definite assignment assertions** \u2014 never use `!` on class properties (e.g., `email!: string`). Use a default value or make the property optional (`?`) instead.\n- Always explicitly show visibility on class methods and properties (`private`, `public`, `protected`).\n- For Entity class, if property is optional, add `null` to its type.\n\n## Steps\n\n### 1. Identify the target\n\nDetermine the module to optimize. If the user specifies a module name, work in `modules/<module>/`. If no module is specified, ask the user which module to optimize.\n\n### 2. Read and analyze the module\n\nRead all source files (`src/**/*.ts`) and test files (`tests/**/*.ts`) in the target module. Build a full picture of:\n\n- All types, interfaces, classes, and functions\n- Dependencies between files\n- Existing test coverage\n\n### 3. Enforce naming conventions\n\nScan every file and fix:\n\n- **Types not ending with `Type`** \u2014 rename the type and update all references across the module\n- **Interfaces not starting with `I`** \u2014 rename the interface and update all references across the module\n- **Non-arrow functions** \u2014 convert all function expressions, callbacks, and standalone functions to arrow functions. Do NOT convert class methods.\n- **Missing visibility modifiers** \u2014 add explicit `public`, `private`, or `protected` to all class methods and properties\n\n### 4. Remove code duplication\n\nIdentify duplicated or near-duplicated code within the module:\n\n- Extract shared logic into well-named helper arrow functions or base classes\n- Consolidate repeated type definitions\n- Merge similar utility functions\n- Remove dead code (unused imports, unreachable branches, unused variables)\n\n### 5. Optimize for performance\n\nReview and improve:\n\n- Replace inefficient loops or repeated iterations with single-pass approaches where possible\n- Use `Map`/`Set` instead of arrays for lookups when appropriate\n- Avoid unnecessary object spreads or deep clones\n- Prefer early returns to reduce nesting\n- Remove unnecessary `async`/`await` where a direct return suffices\n- Eliminate redundant null/undefined checks when the type system already guarantees the value\n\n### 6. Optimize tests\n\nReview all test files and restructure:\n\n- **Remove trivial tests** \u2014 delete tests that only check obvious things (e.g., \"class name ends with X\", \"method exists\") unless they serve as smoke tests for generated code\n- **Keep and improve important tests** \u2014 focus on tests that verify actual business logic, edge cases, error handling, and integration behavior\n- **Write fewer but more meaningful tests** \u2014 each test should validate a real scenario or invariant, not just existence checks\n- **Consolidate redundant test cases** \u2014 merge tests that cover the same code path with slightly different inputs into parameterized patterns\n- **Ensure critical paths are covered** \u2014 every public method with logic should have at least one test covering its happy path and one covering its error/edge case\n\n### 7. Final cleanup\n\n- Remove all unused imports\n- Remove empty files or files with no exports\n- Ensure consistent formatting\n\n### 8. Lint and format\n\nRun linting and formatting on all modified files:\n\n```bash\nbunx biome check --fix <list of modified files>\n```\n\n### 9. Run tests\n\nRun the module's tests to verify nothing is broken:\n\n```bash\nbun test <module test directory>\n```\n\nIf any test fails, fix the issue and re-run until all tests pass.\n";
|
|
7989
|
+
return context.response.json({
|
|
7769
7990
|
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
"make.cache": make_cache_md_default,
|
|
7775
|
-
"make.controller": make_controller_md_default,
|
|
7776
|
-
"make.cron": make_cron_md_default,
|
|
7777
|
-
"make.database": make_database_md_default,
|
|
7778
|
-
"make.entity": make_entity_md_default,
|
|
7779
|
-
"make.logger": make_logger_md_default,
|
|
7780
|
-
"make.mailer": make_mailer_md_default,
|
|
7781
|
-
"make.middleware": make_middleware_md_default,
|
|
7782
|
-
"make.migration": make_migration_md_default,
|
|
7783
|
-
"make.permission": make_permission_md_default,
|
|
7784
|
-
"make.pubsub": make_pubsub_md_default,
|
|
7785
|
-
"make.repository": make_repository_md_default,
|
|
7786
|
-
"make.seed": make_seed_md_default,
|
|
7787
|
-
"make.service": make_service_md_default,
|
|
7788
|
-
"make.storage": make_storage_md_default,
|
|
7789
|
-
"make.vector-database": make_vector_database_md_default,
|
|
7790
|
-
commit: commit_md_default,
|
|
7791
|
-
optimize: optimize_md_default
|
|
7792
|
-
};
|
|
7991
|
+
});
|
|
7992
|
+
}
|
|
7993
|
+
}
|
|
7994
|
+
`;
|
|
7793
7995
|
|
|
7794
|
-
|
|
7996
|
+
// src/commands/MakeControllerCommand.ts
|
|
7997
|
+
class MakeControllerCommand {
|
|
7795
7998
|
getName() {
|
|
7796
|
-
return "make:
|
|
7999
|
+
return "make:controller";
|
|
7797
8000
|
}
|
|
7798
8001
|
getDescription() {
|
|
7799
|
-
return "Generate
|
|
8002
|
+
return "Generate a new controller class";
|
|
7800
8003
|
}
|
|
7801
|
-
async
|
|
7802
|
-
|
|
7803
|
-
const
|
|
7804
|
-
const
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
8004
|
+
async addToModule(modulePath, controllerName) {
|
|
8005
|
+
let content = await Bun.file(modulePath).text();
|
|
8006
|
+
const className = `${controllerName}Controller`;
|
|
8007
|
+
const importLine = `import { ${className} } from "./controllers/${className}";
|
|
8008
|
+
`;
|
|
8009
|
+
const lastImportIndex = content.lastIndexOf("import ");
|
|
8010
|
+
const lineEnd = content.indexOf(`
|
|
8011
|
+
`, lastImportIndex);
|
|
8012
|
+
content = `${content.slice(0, lineEnd + 1)}${importLine}${content.slice(lineEnd + 1)}`;
|
|
8013
|
+
const regex = /(controllers:\s*\[)([^\]]*)/s;
|
|
8014
|
+
const match = content.match(regex);
|
|
8015
|
+
if (match) {
|
|
8016
|
+
const existing = match[2]?.trim();
|
|
8017
|
+
const newValue = existing ? `${existing}, ${className}` : className;
|
|
8018
|
+
content = content.replace(regex, `$1${newValue}`);
|
|
7814
8019
|
}
|
|
7815
|
-
|
|
7816
|
-
}
|
|
7817
|
-
MakeClaudeSkillCommand = __legacyDecorateClassTS([
|
|
7818
|
-
decorator8.command()
|
|
7819
|
-
], MakeClaudeSkillCommand);
|
|
7820
|
-
// src/commands/MakeCommandCommand.ts
|
|
7821
|
-
import { join as join10 } from "path";
|
|
7822
|
-
import { commandCreate, decorator as decorator9 } from "@ooneex/command";
|
|
7823
|
-
import { TerminalLogger as TerminalLogger9 } from "@ooneex/logger";
|
|
7824
|
-
class MakeCommandCommand {
|
|
7825
|
-
getName() {
|
|
7826
|
-
return "make:command";
|
|
7827
|
-
}
|
|
7828
|
-
getDescription() {
|
|
7829
|
-
return "Generate a new command class";
|
|
8020
|
+
await Bun.write(modulePath, content);
|
|
7830
8021
|
}
|
|
7831
8022
|
async run(options) {
|
|
7832
|
-
let { name, module } = options;
|
|
8023
|
+
let { name, module, isSocket } = options;
|
|
7833
8024
|
if (!name) {
|
|
7834
|
-
name = await askName({ message: "Enter name" });
|
|
8025
|
+
name = await askName({ message: "Enter controller name" });
|
|
7835
8026
|
}
|
|
7836
|
-
if (
|
|
7837
|
-
await
|
|
8027
|
+
if (isSocket === undefined) {
|
|
8028
|
+
isSocket = await askConfirm({ message: "Is this a socket controller?" });
|
|
7838
8029
|
}
|
|
7839
|
-
|
|
7840
|
-
const {
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
if (module && module !== "app") {
|
|
7846
|
-
const appCommandsRootPath = join10(process.cwd(), "modules", "app", "src", "commands", "commands.ts");
|
|
7847
|
-
const appCommandsRootFile = Bun.file(appCommandsRootPath);
|
|
7848
|
-
const importLine = `import "@${module}/commands/commands";`;
|
|
7849
|
-
if (await appCommandsRootFile.exists()) {
|
|
7850
|
-
const appCommandsContent = await appCommandsRootFile.text();
|
|
7851
|
-
if (!appCommandsContent.includes(importLine)) {
|
|
7852
|
-
await Bun.write(appCommandsRootPath, `${appCommandsContent.trimEnd()}
|
|
7853
|
-
${importLine}
|
|
7854
|
-
`);
|
|
7855
|
-
}
|
|
7856
|
-
} else {
|
|
7857
|
-
await Bun.write(appCommandsRootPath, `${importLine}
|
|
7858
|
-
`);
|
|
7859
|
-
}
|
|
8030
|
+
name = toPascalCase5(name).replace(/Controller$/, "");
|
|
8031
|
+
const { route = {} } = options;
|
|
8032
|
+
const selectedTemplate = isSocket ? controller_socket_default : controller_default;
|
|
8033
|
+
let content = selectedTemplate.replaceAll("{{NAME}}", name);
|
|
8034
|
+
if (!route.name) {
|
|
8035
|
+
route.name = await askRouteName({ message: "Enter route name (e.g., api.user.create)" });
|
|
7860
8036
|
}
|
|
7861
|
-
const
|
|
7862
|
-
|
|
7863
|
-
if (!
|
|
7864
|
-
await
|
|
8037
|
+
const routeTypeName = toPascalCase5(route.name);
|
|
8038
|
+
content = content.replaceAll("{{ROUTE_NAME}}", route.name).replaceAll("{{TYPE_NAME}}", routeTypeName);
|
|
8039
|
+
if (!route.path) {
|
|
8040
|
+
route.path = await askRoutePath({ message: "Enter route path", initial: "/" });
|
|
7865
8041
|
}
|
|
7866
|
-
const
|
|
7867
|
-
|
|
7868
|
-
if (
|
|
7869
|
-
|
|
7870
|
-
packageJson.scripts = packageJson.scripts || {};
|
|
7871
|
-
packageJson.scripts.command = "bun ./bin/command/run.ts";
|
|
7872
|
-
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
8042
|
+
const routePath = `/${toKebabCase3(trim(route.path, "/"))}`;
|
|
8043
|
+
content = content.replaceAll("{{ROUTE_PATH}}", routePath);
|
|
8044
|
+
if (!isSocket && !route.method) {
|
|
8045
|
+
route.method = await askRouteMethod({ message: "Enter route method" });
|
|
7873
8046
|
}
|
|
7874
|
-
|
|
7875
|
-
|
|
8047
|
+
if (!isSocket && route.method) {
|
|
8048
|
+
content = content.replaceAll("{{ROUTE_METHOD}}", route.method.toLowerCase());
|
|
8049
|
+
}
|
|
8050
|
+
if (module) {
|
|
8051
|
+
await ensureModule(module);
|
|
8052
|
+
}
|
|
8053
|
+
const base = module ? join13("modules", module) : ".";
|
|
8054
|
+
const controllersLocalDir = join13(base, "src", "controllers");
|
|
8055
|
+
const controllersDir = join13(process.cwd(), controllersLocalDir);
|
|
8056
|
+
const filePath = join13(controllersDir, `${name}Controller.ts`);
|
|
8057
|
+
await Bun.write(filePath, content);
|
|
8058
|
+
const testContent = controller_test_default.replace(/{{NAME}}/g, name);
|
|
8059
|
+
const testsLocalDir = join13(base, "tests", "controllers");
|
|
8060
|
+
const testsDir = join13(process.cwd(), testsLocalDir);
|
|
8061
|
+
const testFilePath = join13(testsDir, `${name}Controller.spec.ts`);
|
|
8062
|
+
await Bun.write(testFilePath, testContent);
|
|
8063
|
+
const modulePascalName = module ? toPascalCase5(module) : toPascalCase5(basename(process.cwd()));
|
|
8064
|
+
const modulePath = join13(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
8065
|
+
if (await Bun.file(modulePath).exists()) {
|
|
8066
|
+
await this.addToModule(modulePath, name);
|
|
8067
|
+
}
|
|
8068
|
+
const logger = new TerminalLogger12;
|
|
8069
|
+
logger.success(`${join13(controllersLocalDir, name)}Controller.ts created successfully`, undefined, {
|
|
7876
8070
|
showTimestamp: false,
|
|
7877
8071
|
showArrow: false,
|
|
7878
8072
|
useSymbol: true
|
|
7879
8073
|
});
|
|
7880
|
-
logger.success(`${
|
|
8074
|
+
logger.success(`${join13(testsLocalDir, name)}Controller.spec.ts created successfully`, undefined, {
|
|
7881
8075
|
showTimestamp: false,
|
|
7882
8076
|
showArrow: false,
|
|
7883
8077
|
useSymbol: true
|
|
7884
8078
|
});
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
|
|
8079
|
+
const packageJsonPath = join13(process.cwd(), "package.json");
|
|
8080
|
+
const packageJson = await Bun.file(packageJsonPath).json();
|
|
8081
|
+
const deps = packageJson.dependencies ?? {};
|
|
8082
|
+
const devDeps = packageJson.devDependencies ?? {};
|
|
8083
|
+
if (!deps["@ooneex/controller"] && !devDeps["@ooneex/controller"]) {
|
|
8084
|
+
const install = Bun.spawn(["bun", "add", "@ooneex/controller"], {
|
|
8085
|
+
cwd: process.cwd(),
|
|
8086
|
+
stdout: "ignore",
|
|
8087
|
+
stderr: "inherit"
|
|
8088
|
+
});
|
|
8089
|
+
await install.exited;
|
|
8090
|
+
}
|
|
7890
8091
|
}
|
|
7891
8092
|
}
|
|
7892
|
-
|
|
7893
|
-
|
|
7894
|
-
],
|
|
8093
|
+
MakeControllerCommand = __legacyDecorateClassTS([
|
|
8094
|
+
decorator13.command()
|
|
8095
|
+
], MakeControllerCommand);
|
|
7895
8096
|
// src/commands/MakeCronCommand.ts
|
|
7896
|
-
import { basename as basename2, join as
|
|
7897
|
-
import { decorator as
|
|
7898
|
-
import { TerminalLogger as
|
|
8097
|
+
import { basename as basename2, join as join14 } from "path";
|
|
8098
|
+
import { decorator as decorator14 } from "@ooneex/command";
|
|
8099
|
+
import { TerminalLogger as TerminalLogger13 } from "@ooneex/logger";
|
|
7899
8100
|
import { toPascalCase as toPascalCase6 } from "@ooneex/utils";
|
|
7900
8101
|
|
|
7901
8102
|
// src/templates/cron.test.txt
|
|
@@ -7984,33 +8185,33 @@ class MakeCronCommand {
|
|
|
7984
8185
|
if (module) {
|
|
7985
8186
|
await ensureModule(module);
|
|
7986
8187
|
}
|
|
7987
|
-
const base = module ?
|
|
7988
|
-
const cronLocalDir =
|
|
7989
|
-
const cronDir =
|
|
7990
|
-
const filePath =
|
|
8188
|
+
const base = module ? join14("modules", module) : ".";
|
|
8189
|
+
const cronLocalDir = join14(base, "src", "crons");
|
|
8190
|
+
const cronDir = join14(process.cwd(), cronLocalDir);
|
|
8191
|
+
const filePath = join14(cronDir, `${name}Cron.ts`);
|
|
7991
8192
|
await Bun.write(filePath, content);
|
|
7992
8193
|
const testContent = cron_test_default.replace(/{{NAME}}/g, name);
|
|
7993
|
-
const testsLocalDir =
|
|
7994
|
-
const testsDir =
|
|
7995
|
-
const testFilePath =
|
|
8194
|
+
const testsLocalDir = join14(base, "tests", "crons");
|
|
8195
|
+
const testsDir = join14(process.cwd(), testsLocalDir);
|
|
8196
|
+
const testFilePath = join14(testsDir, `${name}Cron.spec.ts`);
|
|
7996
8197
|
await Bun.write(testFilePath, testContent);
|
|
7997
8198
|
const modulePascalName = module ? toPascalCase6(module) : toPascalCase6(basename2(process.cwd()));
|
|
7998
|
-
const modulePath =
|
|
8199
|
+
const modulePath = join14(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
7999
8200
|
if (await Bun.file(modulePath).exists()) {
|
|
8000
8201
|
await this.addToModule(modulePath, name);
|
|
8001
8202
|
}
|
|
8002
|
-
const logger = new
|
|
8003
|
-
logger.success(`${
|
|
8203
|
+
const logger = new TerminalLogger13;
|
|
8204
|
+
logger.success(`${join14(cronLocalDir, name)}Cron.ts created successfully`, undefined, {
|
|
8004
8205
|
showTimestamp: false,
|
|
8005
8206
|
showArrow: false,
|
|
8006
8207
|
useSymbol: true
|
|
8007
8208
|
});
|
|
8008
|
-
logger.success(`${
|
|
8209
|
+
logger.success(`${join14(testsLocalDir, name)}Cron.spec.ts created successfully`, undefined, {
|
|
8009
8210
|
showTimestamp: false,
|
|
8010
8211
|
showArrow: false,
|
|
8011
8212
|
useSymbol: true
|
|
8012
8213
|
});
|
|
8013
|
-
const packageJsonPath =
|
|
8214
|
+
const packageJsonPath = join14(process.cwd(), "package.json");
|
|
8014
8215
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
8015
8216
|
const deps = packageJson.dependencies ?? {};
|
|
8016
8217
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -8025,12 +8226,12 @@ class MakeCronCommand {
|
|
|
8025
8226
|
}
|
|
8026
8227
|
}
|
|
8027
8228
|
MakeCronCommand = __legacyDecorateClassTS([
|
|
8028
|
-
|
|
8229
|
+
decorator14.command()
|
|
8029
8230
|
], MakeCronCommand);
|
|
8030
8231
|
// src/commands/MakeDatabaseCommand.ts
|
|
8031
|
-
import { join as
|
|
8032
|
-
import { decorator as
|
|
8033
|
-
import { TerminalLogger as
|
|
8232
|
+
import { join as join15 } from "path";
|
|
8233
|
+
import { decorator as decorator15 } from "@ooneex/command";
|
|
8234
|
+
import { TerminalLogger as TerminalLogger14 } from "@ooneex/logger";
|
|
8034
8235
|
import { toPascalCase as toPascalCase7 } from "@ooneex/utils";
|
|
8035
8236
|
|
|
8036
8237
|
// src/templates/database.test.txt
|
|
@@ -8093,28 +8294,28 @@ class MakeDatabaseCommand {
|
|
|
8093
8294
|
if (module) {
|
|
8094
8295
|
await ensureModule(module);
|
|
8095
8296
|
}
|
|
8096
|
-
const base = module ?
|
|
8097
|
-
const databaseLocalDir =
|
|
8098
|
-
const databaseDir =
|
|
8099
|
-
const filePath =
|
|
8297
|
+
const base = module ? join15("modules", module) : ".";
|
|
8298
|
+
const databaseLocalDir = join15(base, "src", "databases");
|
|
8299
|
+
const databaseDir = join15(process.cwd(), databaseLocalDir);
|
|
8300
|
+
const filePath = join15(databaseDir, `${name}Database.ts`);
|
|
8100
8301
|
await Bun.write(filePath, content);
|
|
8101
8302
|
const testContent = database_test_default.replace(/{{NAME}}/g, name);
|
|
8102
|
-
const testsLocalDir =
|
|
8103
|
-
const testsDir =
|
|
8104
|
-
const testFilePath =
|
|
8303
|
+
const testsLocalDir = join15(base, "tests", "databases");
|
|
8304
|
+
const testsDir = join15(process.cwd(), testsLocalDir);
|
|
8305
|
+
const testFilePath = join15(testsDir, `${name}Database.spec.ts`);
|
|
8105
8306
|
await Bun.write(testFilePath, testContent);
|
|
8106
|
-
const logger = new
|
|
8107
|
-
logger.success(`${
|
|
8307
|
+
const logger = new TerminalLogger14;
|
|
8308
|
+
logger.success(`${join15(databaseLocalDir, name)}Database.ts created successfully`, undefined, {
|
|
8108
8309
|
showTimestamp: false,
|
|
8109
8310
|
showArrow: false,
|
|
8110
8311
|
useSymbol: true
|
|
8111
8312
|
});
|
|
8112
|
-
logger.success(`${
|
|
8313
|
+
logger.success(`${join15(testsLocalDir, name)}Database.spec.ts created successfully`, undefined, {
|
|
8113
8314
|
showTimestamp: false,
|
|
8114
8315
|
showArrow: false,
|
|
8115
8316
|
useSymbol: true
|
|
8116
8317
|
});
|
|
8117
|
-
const packageJsonPath =
|
|
8318
|
+
const packageJsonPath = join15(process.cwd(), "package.json");
|
|
8118
8319
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
8119
8320
|
const deps = packageJson.dependencies ?? {};
|
|
8120
8321
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -8129,12 +8330,12 @@ class MakeDatabaseCommand {
|
|
|
8129
8330
|
}
|
|
8130
8331
|
}
|
|
8131
8332
|
MakeDatabaseCommand = __legacyDecorateClassTS([
|
|
8132
|
-
|
|
8333
|
+
decorator15.command()
|
|
8133
8334
|
], MakeDatabaseCommand);
|
|
8134
8335
|
// src/commands/MakeDockerCommand.ts
|
|
8135
|
-
import { join as
|
|
8136
|
-
import { decorator as
|
|
8137
|
-
import { TerminalLogger as
|
|
8336
|
+
import { join as join16 } from "path";
|
|
8337
|
+
import { decorator as decorator16 } from "@ooneex/command";
|
|
8338
|
+
import { TerminalLogger as TerminalLogger15 } from "@ooneex/logger";
|
|
8138
8339
|
var {YAML } = globalThis.Bun;
|
|
8139
8340
|
|
|
8140
8341
|
// src/prompts/askDockerService.ts
|
|
@@ -8627,9 +8828,9 @@ class MakeDockerCommand {
|
|
|
8627
8828
|
name = await askDockerService({ message: "Select docker service" });
|
|
8628
8829
|
}
|
|
8629
8830
|
const templateContent = templates[name];
|
|
8630
|
-
const base =
|
|
8631
|
-
const composePath =
|
|
8632
|
-
const logger = new
|
|
8831
|
+
const base = join16("modules", "app");
|
|
8832
|
+
const composePath = join16(process.cwd(), base, "docker-compose.yml");
|
|
8833
|
+
const logger = new TerminalLogger15;
|
|
8633
8834
|
const composeFile = Bun.file(composePath);
|
|
8634
8835
|
if (await composeFile.exists()) {
|
|
8635
8836
|
const existingContent = await composeFile.text();
|
|
@@ -8688,7 +8889,7 @@ volumes:
|
|
|
8688
8889
|
} else {
|
|
8689
8890
|
await Bun.write(composePath, templateContent);
|
|
8690
8891
|
}
|
|
8691
|
-
const packageJsonPath =
|
|
8892
|
+
const packageJsonPath = join16(process.cwd(), base, "package.json");
|
|
8692
8893
|
const packageJsonFile = Bun.file(packageJsonPath);
|
|
8693
8894
|
if (await packageJsonFile.exists()) {
|
|
8694
8895
|
const packageJson = await packageJsonFile.json();
|
|
@@ -8709,13 +8910,13 @@ volumes:
|
|
|
8709
8910
|
}
|
|
8710
8911
|
}
|
|
8711
8912
|
MakeDockerCommand = __legacyDecorateClassTS([
|
|
8712
|
-
|
|
8913
|
+
decorator16.command()
|
|
8713
8914
|
], MakeDockerCommand);
|
|
8714
8915
|
// src/commands/MakeEntityCommand.ts
|
|
8715
|
-
var import_pluralize = __toESM(require_pluralize(), 1);
|
|
8716
|
-
import { basename as basename3, join as
|
|
8717
|
-
import { decorator as
|
|
8718
|
-
import { TerminalLogger as
|
|
8916
|
+
var import_pluralize = __toESM(require_pluralize(), 1);
|
|
8917
|
+
import { basename as basename3, join as join17 } from "path";
|
|
8918
|
+
import { decorator as decorator17 } from "@ooneex/command";
|
|
8919
|
+
import { TerminalLogger as TerminalLogger16 } from "@ooneex/logger";
|
|
8719
8920
|
import { toPascalCase as toPascalCase8, toSnakeCase as toSnakeCase2 } from "@ooneex/utils";
|
|
8720
8921
|
|
|
8721
8922
|
// src/templates/entity.test.txt
|
|
@@ -8879,28 +9080,28 @@ class MakeEntityCommand {
|
|
|
8879
9080
|
if (module) {
|
|
8880
9081
|
await ensureModule(module);
|
|
8881
9082
|
}
|
|
8882
|
-
const base = module ?
|
|
8883
|
-
const entitiesLocalDir =
|
|
8884
|
-
const entitiesDir =
|
|
8885
|
-
const filePath =
|
|
9083
|
+
const base = module ? join17("modules", module) : ".";
|
|
9084
|
+
const entitiesLocalDir = join17(base, "src", "entities");
|
|
9085
|
+
const entitiesDir = join17(process.cwd(), entitiesLocalDir);
|
|
9086
|
+
const filePath = join17(entitiesDir, `${name}Entity.ts`);
|
|
8886
9087
|
await Bun.write(filePath, content);
|
|
8887
9088
|
const testContent = entity_test_default.replace(/{{NAME}}/g, name);
|
|
8888
|
-
const testsLocalDir =
|
|
8889
|
-
const testsDir =
|
|
8890
|
-
const testFilePath =
|
|
9089
|
+
const testsLocalDir = join17(base, "tests", "entities");
|
|
9090
|
+
const testsDir = join17(process.cwd(), testsLocalDir);
|
|
9091
|
+
const testFilePath = join17(testsDir, `${name}Entity.spec.ts`);
|
|
8891
9092
|
await Bun.write(testFilePath, testContent);
|
|
8892
9093
|
const modulePascalName = module ? toPascalCase8(module) : toPascalCase8(basename3(process.cwd()));
|
|
8893
|
-
const modulePath =
|
|
9094
|
+
const modulePath = join17(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
8894
9095
|
if (await Bun.file(modulePath).exists()) {
|
|
8895
9096
|
await this.addToModule(modulePath, name);
|
|
8896
9097
|
}
|
|
8897
|
-
const logger = new
|
|
8898
|
-
logger.success(`${
|
|
9098
|
+
const logger = new TerminalLogger16;
|
|
9099
|
+
logger.success(`${join17(entitiesLocalDir, name)}Entity.ts created successfully`, undefined, {
|
|
8899
9100
|
showTimestamp: false,
|
|
8900
9101
|
showArrow: false,
|
|
8901
9102
|
useSymbol: true
|
|
8902
9103
|
});
|
|
8903
|
-
logger.success(`${
|
|
9104
|
+
logger.success(`${join17(testsLocalDir, name)}Entity.spec.ts created successfully`, undefined, {
|
|
8904
9105
|
showTimestamp: false,
|
|
8905
9106
|
showArrow: false,
|
|
8906
9107
|
useSymbol: true
|
|
@@ -8908,12 +9109,12 @@ class MakeEntityCommand {
|
|
|
8908
9109
|
}
|
|
8909
9110
|
}
|
|
8910
9111
|
MakeEntityCommand = __legacyDecorateClassTS([
|
|
8911
|
-
|
|
9112
|
+
decorator17.command()
|
|
8912
9113
|
], MakeEntityCommand);
|
|
8913
9114
|
// src/commands/MakeLoggerCommand.ts
|
|
8914
|
-
import { join as
|
|
8915
|
-
import { decorator as
|
|
8916
|
-
import { TerminalLogger as
|
|
9115
|
+
import { join as join18 } from "path";
|
|
9116
|
+
import { decorator as decorator18 } from "@ooneex/command";
|
|
9117
|
+
import { TerminalLogger as TerminalLogger17 } from "@ooneex/logger";
|
|
8917
9118
|
import { toPascalCase as toPascalCase9 } from "@ooneex/utils";
|
|
8918
9119
|
|
|
8919
9120
|
// src/templates/logger.test.txt
|
|
@@ -9018,28 +9219,28 @@ class MakeLoggerCommand {
|
|
|
9018
9219
|
if (module) {
|
|
9019
9220
|
await ensureModule(module);
|
|
9020
9221
|
}
|
|
9021
|
-
const base = module ?
|
|
9022
|
-
const loggerLocalDir =
|
|
9023
|
-
const loggerDir =
|
|
9024
|
-
const filePath =
|
|
9222
|
+
const base = module ? join18("modules", module) : ".";
|
|
9223
|
+
const loggerLocalDir = join18(base, "src", "loggers");
|
|
9224
|
+
const loggerDir = join18(process.cwd(), loggerLocalDir);
|
|
9225
|
+
const filePath = join18(loggerDir, `${name}Logger.ts`);
|
|
9025
9226
|
await Bun.write(filePath, content);
|
|
9026
9227
|
const testContent = logger_test_default.replace(/{{NAME}}/g, name);
|
|
9027
|
-
const testsLocalDir =
|
|
9028
|
-
const testsDir =
|
|
9029
|
-
const testFilePath =
|
|
9228
|
+
const testsLocalDir = join18(base, "tests", "loggers");
|
|
9229
|
+
const testsDir = join18(process.cwd(), testsLocalDir);
|
|
9230
|
+
const testFilePath = join18(testsDir, `${name}Logger.spec.ts`);
|
|
9030
9231
|
await Bun.write(testFilePath, testContent);
|
|
9031
|
-
const logger = new
|
|
9032
|
-
logger.success(`${
|
|
9232
|
+
const logger = new TerminalLogger17;
|
|
9233
|
+
logger.success(`${join18(loggerLocalDir, name)}Logger.ts created successfully`, undefined, {
|
|
9033
9234
|
showTimestamp: false,
|
|
9034
9235
|
showArrow: false,
|
|
9035
9236
|
useSymbol: true
|
|
9036
9237
|
});
|
|
9037
|
-
logger.success(`${
|
|
9238
|
+
logger.success(`${join18(testsLocalDir, name)}Logger.spec.ts created successfully`, undefined, {
|
|
9038
9239
|
showTimestamp: false,
|
|
9039
9240
|
showArrow: false,
|
|
9040
9241
|
useSymbol: true
|
|
9041
9242
|
});
|
|
9042
|
-
const packageJsonPath =
|
|
9243
|
+
const packageJsonPath = join18(process.cwd(), "package.json");
|
|
9043
9244
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
9044
9245
|
const deps = packageJson.dependencies ?? {};
|
|
9045
9246
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -9054,12 +9255,12 @@ class MakeLoggerCommand {
|
|
|
9054
9255
|
}
|
|
9055
9256
|
}
|
|
9056
9257
|
MakeLoggerCommand = __legacyDecorateClassTS([
|
|
9057
|
-
|
|
9258
|
+
decorator18.command()
|
|
9058
9259
|
], MakeLoggerCommand);
|
|
9059
9260
|
// src/commands/MakeMailerCommand.ts
|
|
9060
|
-
import { join as
|
|
9061
|
-
import { decorator as
|
|
9062
|
-
import { TerminalLogger as
|
|
9261
|
+
import { join as join19 } from "path";
|
|
9262
|
+
import { decorator as decorator19 } from "@ooneex/command";
|
|
9263
|
+
import { TerminalLogger as TerminalLogger18 } from "@ooneex/logger";
|
|
9063
9264
|
import { toPascalCase as toPascalCase10 } from "@ooneex/utils";
|
|
9064
9265
|
|
|
9065
9266
|
// src/templates/mailer/mailer.test.txt
|
|
@@ -9157,43 +9358,43 @@ class MakeMailerCommand {
|
|
|
9157
9358
|
if (module) {
|
|
9158
9359
|
await ensureModule(module);
|
|
9159
9360
|
}
|
|
9160
|
-
const base = module ?
|
|
9161
|
-
const mailerLocalDir =
|
|
9162
|
-
const mailerDir =
|
|
9163
|
-
const mailerFilePath =
|
|
9164
|
-
const templateFilePath =
|
|
9361
|
+
const base = module ? join19("modules", module) : ".";
|
|
9362
|
+
const mailerLocalDir = join19(base, "src", "mailers");
|
|
9363
|
+
const mailerDir = join19(process.cwd(), mailerLocalDir);
|
|
9364
|
+
const mailerFilePath = join19(mailerDir, `${name}Mailer.ts`);
|
|
9365
|
+
const templateFilePath = join19(mailerDir, `${name}MailerTemplate.tsx`);
|
|
9165
9366
|
await Bun.write(mailerFilePath, mailerContent);
|
|
9166
9367
|
await Bun.write(templateFilePath, templateContent);
|
|
9167
9368
|
const mailerTestContent = mailer_test_default.replace(/{{NAME}}/g, name);
|
|
9168
9369
|
const templateTestContent = mailer_template_test_default.replace(/{{NAME}}/g, name);
|
|
9169
|
-
const testsLocalDir =
|
|
9170
|
-
const testsDir =
|
|
9171
|
-
const mailerTestFilePath =
|
|
9172
|
-
const templateTestFilePath =
|
|
9370
|
+
const testsLocalDir = join19(base, "tests", "mailers");
|
|
9371
|
+
const testsDir = join19(process.cwd(), testsLocalDir);
|
|
9372
|
+
const mailerTestFilePath = join19(testsDir, `${name}Mailer.spec.ts`);
|
|
9373
|
+
const templateTestFilePath = join19(testsDir, `${name}MailerTemplate.spec.ts`);
|
|
9173
9374
|
await Bun.write(mailerTestFilePath, mailerTestContent);
|
|
9174
9375
|
await Bun.write(templateTestFilePath, templateTestContent);
|
|
9175
|
-
const logger = new
|
|
9176
|
-
logger.success(`${
|
|
9376
|
+
const logger = new TerminalLogger18;
|
|
9377
|
+
logger.success(`${join19(mailerLocalDir, name)}Mailer.ts created successfully`, undefined, {
|
|
9177
9378
|
showTimestamp: false,
|
|
9178
9379
|
showArrow: false,
|
|
9179
9380
|
useSymbol: true
|
|
9180
9381
|
});
|
|
9181
|
-
logger.success(`${
|
|
9382
|
+
logger.success(`${join19(mailerLocalDir, name)}MailerTemplate.tsx created successfully`, undefined, {
|
|
9182
9383
|
showTimestamp: false,
|
|
9183
9384
|
showArrow: false,
|
|
9184
9385
|
useSymbol: true
|
|
9185
9386
|
});
|
|
9186
|
-
logger.success(`${
|
|
9387
|
+
logger.success(`${join19(testsLocalDir, name)}Mailer.spec.ts created successfully`, undefined, {
|
|
9187
9388
|
showTimestamp: false,
|
|
9188
9389
|
showArrow: false,
|
|
9189
9390
|
useSymbol: true
|
|
9190
9391
|
});
|
|
9191
|
-
logger.success(`${
|
|
9392
|
+
logger.success(`${join19(testsLocalDir, name)}MailerTemplate.spec.ts created successfully`, undefined, {
|
|
9192
9393
|
showTimestamp: false,
|
|
9193
9394
|
showArrow: false,
|
|
9194
9395
|
useSymbol: true
|
|
9195
9396
|
});
|
|
9196
|
-
const packageJsonPath =
|
|
9397
|
+
const packageJsonPath = join19(process.cwd(), "package.json");
|
|
9197
9398
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
9198
9399
|
const deps = packageJson.dependencies ?? {};
|
|
9199
9400
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -9208,12 +9409,12 @@ class MakeMailerCommand {
|
|
|
9208
9409
|
}
|
|
9209
9410
|
}
|
|
9210
9411
|
MakeMailerCommand = __legacyDecorateClassTS([
|
|
9211
|
-
|
|
9412
|
+
decorator19.command()
|
|
9212
9413
|
], MakeMailerCommand);
|
|
9213
9414
|
// src/commands/MakeMiddlewareCommand.ts
|
|
9214
|
-
import { basename as basename4, join as
|
|
9215
|
-
import { decorator as
|
|
9216
|
-
import { TerminalLogger as
|
|
9415
|
+
import { basename as basename4, join as join20 } from "path";
|
|
9416
|
+
import { decorator as decorator20 } from "@ooneex/command";
|
|
9417
|
+
import { TerminalLogger as TerminalLogger19 } from "@ooneex/logger";
|
|
9217
9418
|
import { toPascalCase as toPascalCase11 } from "@ooneex/utils";
|
|
9218
9419
|
|
|
9219
9420
|
// src/templates/middleware.socket.txt
|
|
@@ -9302,33 +9503,33 @@ class MakeMiddlewareCommand {
|
|
|
9302
9503
|
if (module) {
|
|
9303
9504
|
await ensureModule(module);
|
|
9304
9505
|
}
|
|
9305
|
-
const base = module ?
|
|
9306
|
-
const middlewareLocalDir =
|
|
9307
|
-
const middlewareDir =
|
|
9308
|
-
const filePath =
|
|
9506
|
+
const base = module ? join20("modules", module) : ".";
|
|
9507
|
+
const middlewareLocalDir = join20(base, "src", "middlewares");
|
|
9508
|
+
const middlewareDir = join20(process.cwd(), middlewareLocalDir);
|
|
9509
|
+
const filePath = join20(middlewareDir, `${name}Middleware.ts`);
|
|
9309
9510
|
await Bun.write(filePath, content);
|
|
9310
9511
|
const testContent = middleware_test_default.replace(/{{NAME}}/g, name);
|
|
9311
|
-
const testsLocalDir =
|
|
9312
|
-
const testsDir =
|
|
9313
|
-
const testFilePath =
|
|
9512
|
+
const testsLocalDir = join20(base, "tests", "middlewares");
|
|
9513
|
+
const testsDir = join20(process.cwd(), testsLocalDir);
|
|
9514
|
+
const testFilePath = join20(testsDir, `${name}Middleware.spec.ts`);
|
|
9314
9515
|
await Bun.write(testFilePath, testContent);
|
|
9315
9516
|
const modulePascalName = module ? toPascalCase11(module) : toPascalCase11(basename4(process.cwd()));
|
|
9316
|
-
const modulePath =
|
|
9517
|
+
const modulePath = join20(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
9317
9518
|
if (await Bun.file(modulePath).exists()) {
|
|
9318
9519
|
await this.addToModule(modulePath, name);
|
|
9319
9520
|
}
|
|
9320
|
-
const logger = new
|
|
9321
|
-
logger.success(`${
|
|
9521
|
+
const logger = new TerminalLogger19;
|
|
9522
|
+
logger.success(`${join20(middlewareLocalDir, name)}Middleware.ts created successfully`, undefined, {
|
|
9322
9523
|
showTimestamp: false,
|
|
9323
9524
|
showArrow: false,
|
|
9324
9525
|
useSymbol: true
|
|
9325
9526
|
});
|
|
9326
|
-
logger.success(`${
|
|
9527
|
+
logger.success(`${join20(testsLocalDir, name)}Middleware.spec.ts created successfully`, undefined, {
|
|
9327
9528
|
showTimestamp: false,
|
|
9328
9529
|
showArrow: false,
|
|
9329
9530
|
useSymbol: true
|
|
9330
9531
|
});
|
|
9331
|
-
const packageJsonPath =
|
|
9532
|
+
const packageJsonPath = join20(process.cwd(), "package.json");
|
|
9332
9533
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
9333
9534
|
const deps = packageJson.dependencies ?? {};
|
|
9334
9535
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -9343,21 +9544,21 @@ class MakeMiddlewareCommand {
|
|
|
9343
9544
|
}
|
|
9344
9545
|
}
|
|
9345
9546
|
MakeMiddlewareCommand = __legacyDecorateClassTS([
|
|
9346
|
-
|
|
9547
|
+
decorator20.command()
|
|
9347
9548
|
], MakeMiddlewareCommand);
|
|
9348
9549
|
// src/commands/MakeMigrationCommand.ts
|
|
9349
|
-
import { join as
|
|
9350
|
-
import { decorator as
|
|
9351
|
-
import { TerminalLogger as
|
|
9550
|
+
import { join as join21 } from "path";
|
|
9551
|
+
import { decorator as decorator21 } from "@ooneex/command";
|
|
9552
|
+
import { TerminalLogger as TerminalLogger20 } from "@ooneex/logger";
|
|
9352
9553
|
import { migrationCreate } from "@ooneex/migrations";
|
|
9353
9554
|
|
|
9354
9555
|
// src/templates/module/migration.up.txt
|
|
9355
9556
|
var migration_up_default = `#!/usr/bin/env bun
|
|
9356
9557
|
|
|
9357
|
-
import {
|
|
9558
|
+
import { up } from "@ooneex/migrations";
|
|
9358
9559
|
import "@/migrations/migrations";
|
|
9359
9560
|
|
|
9360
|
-
await
|
|
9561
|
+
await up({
|
|
9361
9562
|
databaseUrl: Bun.env.DATABASE_URL,
|
|
9362
9563
|
tableName: "migrations",
|
|
9363
9564
|
});
|
|
@@ -9376,41 +9577,29 @@ class MakeMigrationCommand {
|
|
|
9376
9577
|
if (module) {
|
|
9377
9578
|
await ensureModule(module);
|
|
9378
9579
|
}
|
|
9379
|
-
const base = module ?
|
|
9580
|
+
const base = module ? join21("modules", module) : ".";
|
|
9380
9581
|
const { migrationPath: filePath } = await migrationCreate({
|
|
9381
|
-
migrationsDir:
|
|
9382
|
-
testsDir:
|
|
9582
|
+
migrationsDir: join21(base, "src", "migrations"),
|
|
9583
|
+
testsDir: join21(base, "tests", "migrations")
|
|
9383
9584
|
});
|
|
9384
|
-
const binMigrationUpPath =
|
|
9585
|
+
const binMigrationUpPath = join21(process.cwd(), base, "bin", "migration", "up.ts");
|
|
9385
9586
|
const binMigrationUpFile = Bun.file(binMigrationUpPath);
|
|
9386
9587
|
if (!await binMigrationUpFile.exists()) {
|
|
9387
9588
|
await Bun.write(binMigrationUpPath, migration_up_default);
|
|
9388
9589
|
}
|
|
9389
|
-
const packageJsonPath =
|
|
9390
|
-
const
|
|
9391
|
-
if (await packageJsonFile.exists()) {
|
|
9392
|
-
const packageJson = await packageJsonFile.json();
|
|
9393
|
-
packageJson.scripts = packageJson.scripts || {};
|
|
9394
|
-
packageJson.scripts["migration:up"] = "bun ./bin/migration/up.ts";
|
|
9395
|
-
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
9396
|
-
}
|
|
9397
|
-
const logger = new TerminalLogger17;
|
|
9590
|
+
const packageJsonPath = join21(process.cwd(), base, "package.json");
|
|
9591
|
+
const logger = new TerminalLogger20;
|
|
9398
9592
|
logger.success(`${filePath} created successfully`, undefined, {
|
|
9399
9593
|
showTimestamp: false,
|
|
9400
9594
|
showArrow: false,
|
|
9401
9595
|
useSymbol: true
|
|
9402
9596
|
});
|
|
9403
|
-
logger.info("Run 'bun run migration:up' to execute migrations", undefined, {
|
|
9404
|
-
showTimestamp: false,
|
|
9405
|
-
showArrow: true,
|
|
9406
|
-
showLevel: false
|
|
9407
|
-
});
|
|
9408
9597
|
const pkgJson = await Bun.file(packageJsonPath).json();
|
|
9409
9598
|
const deps = pkgJson.dependencies ?? {};
|
|
9410
9599
|
const devDeps = pkgJson.devDependencies ?? {};
|
|
9411
9600
|
if (!deps["@ooneex/migrations"] && !devDeps["@ooneex/migrations"]) {
|
|
9412
9601
|
const install = Bun.spawn(["bun", "add", "--dev", "@ooneex/migrations"], {
|
|
9413
|
-
cwd:
|
|
9602
|
+
cwd: join21(process.cwd(), base),
|
|
9414
9603
|
stdout: "ignore",
|
|
9415
9604
|
stderr: "inherit"
|
|
9416
9605
|
});
|
|
@@ -9419,12 +9608,12 @@ class MakeMigrationCommand {
|
|
|
9419
9608
|
}
|
|
9420
9609
|
}
|
|
9421
9610
|
MakeMigrationCommand = __legacyDecorateClassTS([
|
|
9422
|
-
|
|
9611
|
+
decorator21.command()
|
|
9423
9612
|
], MakeMigrationCommand);
|
|
9424
9613
|
// src/commands/MakePermissionCommand.ts
|
|
9425
|
-
import { join as
|
|
9426
|
-
import { decorator as
|
|
9427
|
-
import { TerminalLogger as
|
|
9614
|
+
import { join as join22 } from "path";
|
|
9615
|
+
import { decorator as decorator22 } from "@ooneex/command";
|
|
9616
|
+
import { TerminalLogger as TerminalLogger21 } from "@ooneex/logger";
|
|
9428
9617
|
import { toPascalCase as toPascalCase12 } from "@ooneex/utils";
|
|
9429
9618
|
|
|
9430
9619
|
// src/templates/permission.test.txt
|
|
@@ -9515,28 +9704,28 @@ class MakePermissionCommand {
|
|
|
9515
9704
|
if (module) {
|
|
9516
9705
|
await ensureModule(module);
|
|
9517
9706
|
}
|
|
9518
|
-
const base = module ?
|
|
9519
|
-
const permissionLocalDir =
|
|
9520
|
-
const permissionDir =
|
|
9521
|
-
const filePath =
|
|
9707
|
+
const base = module ? join22("modules", module) : ".";
|
|
9708
|
+
const permissionLocalDir = join22(base, "src", "permissions");
|
|
9709
|
+
const permissionDir = join22(process.cwd(), permissionLocalDir);
|
|
9710
|
+
const filePath = join22(permissionDir, `${name}Permission.ts`);
|
|
9522
9711
|
await Bun.write(filePath, content);
|
|
9523
9712
|
const testContent = permission_test_default.replace(/{{NAME}}/g, name);
|
|
9524
|
-
const testsLocalDir =
|
|
9525
|
-
const testsDir =
|
|
9526
|
-
const testFilePath =
|
|
9713
|
+
const testsLocalDir = join22(base, "tests", "permissions");
|
|
9714
|
+
const testsDir = join22(process.cwd(), testsLocalDir);
|
|
9715
|
+
const testFilePath = join22(testsDir, `${name}Permission.spec.ts`);
|
|
9527
9716
|
await Bun.write(testFilePath, testContent);
|
|
9528
|
-
const logger = new
|
|
9529
|
-
logger.success(`${
|
|
9717
|
+
const logger = new TerminalLogger21;
|
|
9718
|
+
logger.success(`${join22(permissionLocalDir, name)}Permission.ts created successfully`, undefined, {
|
|
9530
9719
|
showTimestamp: false,
|
|
9531
9720
|
showArrow: false,
|
|
9532
9721
|
useSymbol: true
|
|
9533
9722
|
});
|
|
9534
|
-
logger.success(`${
|
|
9723
|
+
logger.success(`${join22(testsLocalDir, name)}Permission.spec.ts created successfully`, undefined, {
|
|
9535
9724
|
showTimestamp: false,
|
|
9536
9725
|
showArrow: false,
|
|
9537
9726
|
useSymbol: true
|
|
9538
9727
|
});
|
|
9539
|
-
const packageJsonPath =
|
|
9728
|
+
const packageJsonPath = join22(process.cwd(), "package.json");
|
|
9540
9729
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
9541
9730
|
const deps = packageJson.dependencies ?? {};
|
|
9542
9731
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -9551,12 +9740,12 @@ class MakePermissionCommand {
|
|
|
9551
9740
|
}
|
|
9552
9741
|
}
|
|
9553
9742
|
MakePermissionCommand = __legacyDecorateClassTS([
|
|
9554
|
-
|
|
9743
|
+
decorator22.command()
|
|
9555
9744
|
], MakePermissionCommand);
|
|
9556
9745
|
// src/commands/MakePubSubCommand.ts
|
|
9557
|
-
import { basename as basename5, join as
|
|
9558
|
-
import { decorator as
|
|
9559
|
-
import { TerminalLogger as
|
|
9746
|
+
import { basename as basename5, join as join23 } from "path";
|
|
9747
|
+
import { decorator as decorator23 } from "@ooneex/command";
|
|
9748
|
+
import { TerminalLogger as TerminalLogger22 } from "@ooneex/logger";
|
|
9560
9749
|
import { toKebabCase as toKebabCase4, toPascalCase as toPascalCase13 } from "@ooneex/utils";
|
|
9561
9750
|
|
|
9562
9751
|
// src/templates/pubsub.test.txt
|
|
@@ -9664,33 +9853,33 @@ class MakePubSubCommand {
|
|
|
9664
9853
|
if (module) {
|
|
9665
9854
|
await ensureModule(module);
|
|
9666
9855
|
}
|
|
9667
|
-
const base = module ?
|
|
9668
|
-
const pubSubLocalDir =
|
|
9669
|
-
const pubSubDir =
|
|
9670
|
-
const filePath =
|
|
9856
|
+
const base = module ? join23("modules", module) : ".";
|
|
9857
|
+
const pubSubLocalDir = join23(base, "src", "events");
|
|
9858
|
+
const pubSubDir = join23(process.cwd(), pubSubLocalDir);
|
|
9859
|
+
const filePath = join23(pubSubDir, `${name}Event.ts`);
|
|
9671
9860
|
await Bun.write(filePath, content);
|
|
9672
9861
|
const testContent = pubsub_test_default.replace(/{{NAME}}/g, name);
|
|
9673
|
-
const testsLocalDir =
|
|
9674
|
-
const testsDir =
|
|
9675
|
-
const testFilePath =
|
|
9862
|
+
const testsLocalDir = join23(base, "tests", "events");
|
|
9863
|
+
const testsDir = join23(process.cwd(), testsLocalDir);
|
|
9864
|
+
const testFilePath = join23(testsDir, `${name}Event.spec.ts`);
|
|
9676
9865
|
await Bun.write(testFilePath, testContent);
|
|
9677
9866
|
const modulePascalName = module ? toPascalCase13(module) : toPascalCase13(basename5(process.cwd()));
|
|
9678
|
-
const modulePath =
|
|
9867
|
+
const modulePath = join23(process.cwd(), base, "src", `${modulePascalName}Module.ts`);
|
|
9679
9868
|
if (await Bun.file(modulePath).exists()) {
|
|
9680
9869
|
await this.addToModule(modulePath, name);
|
|
9681
9870
|
}
|
|
9682
|
-
const logger = new
|
|
9683
|
-
logger.success(`${
|
|
9871
|
+
const logger = new TerminalLogger22;
|
|
9872
|
+
logger.success(`${join23(pubSubLocalDir, name)}Event.ts created successfully`, undefined, {
|
|
9684
9873
|
showTimestamp: false,
|
|
9685
9874
|
showArrow: false,
|
|
9686
9875
|
useSymbol: true
|
|
9687
9876
|
});
|
|
9688
|
-
logger.success(`${
|
|
9877
|
+
logger.success(`${join23(testsLocalDir, name)}Event.spec.ts created successfully`, undefined, {
|
|
9689
9878
|
showTimestamp: false,
|
|
9690
9879
|
showArrow: false,
|
|
9691
9880
|
useSymbol: true
|
|
9692
9881
|
});
|
|
9693
|
-
const packageJsonPath =
|
|
9882
|
+
const packageJsonPath = join23(process.cwd(), "package.json");
|
|
9694
9883
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
9695
9884
|
const deps = packageJson.dependencies ?? {};
|
|
9696
9885
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -9705,13 +9894,13 @@ class MakePubSubCommand {
|
|
|
9705
9894
|
}
|
|
9706
9895
|
}
|
|
9707
9896
|
MakePubSubCommand = __legacyDecorateClassTS([
|
|
9708
|
-
|
|
9897
|
+
decorator23.command()
|
|
9709
9898
|
], MakePubSubCommand);
|
|
9710
9899
|
// src/commands/MakeReleaseCommand.ts
|
|
9711
9900
|
import { readdir } from "fs/promises";
|
|
9712
|
-
import { join as
|
|
9713
|
-
import { decorator as
|
|
9714
|
-
import { TerminalLogger as
|
|
9901
|
+
import { join as join24 } from "path";
|
|
9902
|
+
import { decorator as decorator24 } from "@ooneex/command";
|
|
9903
|
+
import { TerminalLogger as TerminalLogger23 } from "@ooneex/logger";
|
|
9715
9904
|
var {$ } = globalThis.Bun;
|
|
9716
9905
|
var COMMIT_TYPE_TO_CATEGORY = {
|
|
9717
9906
|
feat: "Added",
|
|
@@ -9734,7 +9923,7 @@ class MakeReleaseCommand {
|
|
|
9734
9923
|
return "Release packages with version bump, changelog, and git tag";
|
|
9735
9924
|
}
|
|
9736
9925
|
async run() {
|
|
9737
|
-
const logger = new
|
|
9926
|
+
const logger = new TerminalLogger23;
|
|
9738
9927
|
const cwd = process.cwd();
|
|
9739
9928
|
const dirs = [];
|
|
9740
9929
|
for (const { name, type } of [
|
|
@@ -9742,8 +9931,8 @@ class MakeReleaseCommand {
|
|
|
9742
9931
|
{ name: "modules", type: "module" }
|
|
9743
9932
|
]) {
|
|
9744
9933
|
try {
|
|
9745
|
-
const entries = await readdir(
|
|
9746
|
-
dirs.push(...entries.filter((d) => d.isDirectory()).map((d) => ({ base:
|
|
9934
|
+
const entries = await readdir(join24(cwd, name), { withFileTypes: true });
|
|
9935
|
+
dirs.push(...entries.filter((d) => d.isDirectory()).map((d) => ({ base: join24(name, d.name), type })));
|
|
9747
9936
|
} catch {}
|
|
9748
9937
|
}
|
|
9749
9938
|
const logOptions = { showTimestamp: false, showArrow: false, useSymbol: true };
|
|
@@ -9753,8 +9942,8 @@ class MakeReleaseCommand {
|
|
|
9753
9942
|
}
|
|
9754
9943
|
let releasedCount = 0;
|
|
9755
9944
|
for (const dir of dirs) {
|
|
9756
|
-
const fullDir =
|
|
9757
|
-
const pkgJsonPath =
|
|
9945
|
+
const fullDir = join24(cwd, dir.base);
|
|
9946
|
+
const pkgJsonPath = join24(fullDir, "package.json");
|
|
9758
9947
|
const pkgJsonFile = Bun.file(pkgJsonPath);
|
|
9759
9948
|
if (!await pkgJsonFile.exists()) {
|
|
9760
9949
|
continue;
|
|
@@ -9772,7 +9961,7 @@ class MakeReleaseCommand {
|
|
|
9772
9961
|
await Bun.write(pkgJsonPath, `${JSON.stringify(pkgJson, null, 2)}
|
|
9773
9962
|
`);
|
|
9774
9963
|
await this.updateChangelog(fullDir, newVersion, tag, commits);
|
|
9775
|
-
await this.gitAdd(
|
|
9964
|
+
await this.gitAdd(join24(dir.base, "package.json"), join24(dir.base, "CHANGELOG.md"));
|
|
9776
9965
|
await this.gitCommit(`chore(release): ${pkgJson.name}@${newVersion}`);
|
|
9777
9966
|
await this.gitTag(tag, `chore(release): ${pkgJson.name}@${newVersion}`);
|
|
9778
9967
|
logger.success(`${pkgJson.name}@${newVersion} released (${bumpType} bump, ${commits.length} commit(s))`, undefined, logOptions);
|
|
@@ -9869,7 +10058,7 @@ class MakeReleaseCommand {
|
|
|
9869
10058
|
}
|
|
9870
10059
|
}
|
|
9871
10060
|
async updateChangelog(dir, version, tag, commits) {
|
|
9872
|
-
const changelogPath =
|
|
10061
|
+
const changelogPath = join24(dir, "CHANGELOG.md");
|
|
9873
10062
|
const today = new Date().toISOString().split("T")[0];
|
|
9874
10063
|
const repoUrl = await this.getRepoUrl();
|
|
9875
10064
|
const grouped = new Map;
|
|
@@ -9940,12 +10129,12 @@ ${section}
|
|
|
9940
10129
|
}
|
|
9941
10130
|
}
|
|
9942
10131
|
MakeReleaseCommand = __legacyDecorateClassTS([
|
|
9943
|
-
|
|
10132
|
+
decorator24.command()
|
|
9944
10133
|
], MakeReleaseCommand);
|
|
9945
10134
|
// src/commands/MakeRepositoryCommand.ts
|
|
9946
|
-
import { join as
|
|
9947
|
-
import { decorator as
|
|
9948
|
-
import { TerminalLogger as
|
|
10135
|
+
import { join as join25 } from "path";
|
|
10136
|
+
import { decorator as decorator25 } from "@ooneex/command";
|
|
10137
|
+
import { TerminalLogger as TerminalLogger24 } from "@ooneex/logger";
|
|
9949
10138
|
import { toPascalCase as toPascalCase14 } from "@ooneex/utils";
|
|
9950
10139
|
|
|
9951
10140
|
// src/templates/repository.test.txt
|
|
@@ -10159,28 +10348,28 @@ class MakeRepositoryCommand {
|
|
|
10159
10348
|
if (module) {
|
|
10160
10349
|
await ensureModule(module);
|
|
10161
10350
|
}
|
|
10162
|
-
const base = module ?
|
|
10163
|
-
const repositoriesLocalDir =
|
|
10164
|
-
const repositoriesDir =
|
|
10165
|
-
const filePath =
|
|
10351
|
+
const base = module ? join25("modules", module) : ".";
|
|
10352
|
+
const repositoriesLocalDir = join25(base, "src", "repositories");
|
|
10353
|
+
const repositoriesDir = join25(process.cwd(), repositoriesLocalDir);
|
|
10354
|
+
const filePath = join25(repositoriesDir, `${name}Repository.ts`);
|
|
10166
10355
|
await Bun.write(filePath, content);
|
|
10167
10356
|
const testContent = repository_test_default.replace(/{{NAME}}/g, name);
|
|
10168
|
-
const testsLocalDir =
|
|
10169
|
-
const testsDir =
|
|
10170
|
-
const testFilePath =
|
|
10357
|
+
const testsLocalDir = join25(base, "tests", "repositories");
|
|
10358
|
+
const testsDir = join25(process.cwd(), testsLocalDir);
|
|
10359
|
+
const testFilePath = join25(testsDir, `${name}Repository.spec.ts`);
|
|
10171
10360
|
await Bun.write(testFilePath, testContent);
|
|
10172
|
-
const logger = new
|
|
10173
|
-
logger.success(`${
|
|
10361
|
+
const logger = new TerminalLogger24;
|
|
10362
|
+
logger.success(`${join25(repositoriesLocalDir, name)}Repository.ts created successfully`, undefined, {
|
|
10174
10363
|
showTimestamp: false,
|
|
10175
10364
|
showArrow: false,
|
|
10176
10365
|
useSymbol: true
|
|
10177
10366
|
});
|
|
10178
|
-
logger.success(`${
|
|
10367
|
+
logger.success(`${join25(testsLocalDir, name)}Repository.spec.ts created successfully`, undefined, {
|
|
10179
10368
|
showTimestamp: false,
|
|
10180
10369
|
showArrow: false,
|
|
10181
10370
|
useSymbol: true
|
|
10182
10371
|
});
|
|
10183
|
-
const packageJsonPath =
|
|
10372
|
+
const packageJsonPath = join25(process.cwd(), "package.json");
|
|
10184
10373
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
10185
10374
|
const deps = packageJson.dependencies ?? {};
|
|
10186
10375
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -10195,11 +10384,11 @@ class MakeRepositoryCommand {
|
|
|
10195
10384
|
}
|
|
10196
10385
|
}
|
|
10197
10386
|
MakeRepositoryCommand = __legacyDecorateClassTS([
|
|
10198
|
-
|
|
10387
|
+
decorator25.command()
|
|
10199
10388
|
], MakeRepositoryCommand);
|
|
10200
10389
|
// src/commands/MakeResourceBookCommand.ts
|
|
10201
|
-
import { join as
|
|
10202
|
-
import { decorator as
|
|
10390
|
+
import { join as join27 } from "path";
|
|
10391
|
+
import { decorator as decorator27 } from "@ooneex/command";
|
|
10203
10392
|
var {Glob } = globalThis.Bun;
|
|
10204
10393
|
|
|
10205
10394
|
// src/templates/resources/book/BookEntity.txt
|
|
@@ -10895,9 +11084,9 @@ export class UpdateBookService implements IService {
|
|
|
10895
11084
|
`;
|
|
10896
11085
|
|
|
10897
11086
|
// src/commands/MakeServiceCommand.ts
|
|
10898
|
-
import { join as
|
|
10899
|
-
import { decorator as
|
|
10900
|
-
import { TerminalLogger as
|
|
11087
|
+
import { join as join26 } from "path";
|
|
11088
|
+
import { decorator as decorator26 } from "@ooneex/command";
|
|
11089
|
+
import { TerminalLogger as TerminalLogger25 } from "@ooneex/logger";
|
|
10901
11090
|
import { toPascalCase as toPascalCase15 } from "@ooneex/utils";
|
|
10902
11091
|
|
|
10903
11092
|
// src/templates/service.test.txt
|
|
@@ -10949,28 +11138,28 @@ class MakeServiceCommand {
|
|
|
10949
11138
|
if (module) {
|
|
10950
11139
|
await ensureModule(module);
|
|
10951
11140
|
}
|
|
10952
|
-
const base = module ?
|
|
10953
|
-
const serviceLocalDir =
|
|
10954
|
-
const serviceDir =
|
|
10955
|
-
const filePath =
|
|
11141
|
+
const base = module ? join26("modules", module) : ".";
|
|
11142
|
+
const serviceLocalDir = join26(base, "src", "services");
|
|
11143
|
+
const serviceDir = join26(process.cwd(), serviceLocalDir);
|
|
11144
|
+
const filePath = join26(serviceDir, `${name}Service.ts`);
|
|
10956
11145
|
await Bun.write(filePath, content);
|
|
10957
11146
|
const testContent = service_test_default.replace(/{{NAME}}/g, name);
|
|
10958
|
-
const testsLocalDir =
|
|
10959
|
-
const testsDir =
|
|
10960
|
-
const testFilePath =
|
|
11147
|
+
const testsLocalDir = join26(base, "tests", "services");
|
|
11148
|
+
const testsDir = join26(process.cwd(), testsLocalDir);
|
|
11149
|
+
const testFilePath = join26(testsDir, `${name}Service.spec.ts`);
|
|
10961
11150
|
await Bun.write(testFilePath, testContent);
|
|
10962
|
-
const logger = new
|
|
10963
|
-
logger.success(`${
|
|
11151
|
+
const logger = new TerminalLogger25;
|
|
11152
|
+
logger.success(`${join26(serviceLocalDir, name)}Service.ts created successfully`, undefined, {
|
|
10964
11153
|
showTimestamp: false,
|
|
10965
11154
|
showArrow: false,
|
|
10966
11155
|
useSymbol: true
|
|
10967
11156
|
});
|
|
10968
|
-
logger.success(`${
|
|
11157
|
+
logger.success(`${join26(testsLocalDir, name)}Service.spec.ts created successfully`, undefined, {
|
|
10969
11158
|
showTimestamp: false,
|
|
10970
11159
|
showArrow: false,
|
|
10971
11160
|
useSymbol: true
|
|
10972
11161
|
});
|
|
10973
|
-
const packageJsonPath =
|
|
11162
|
+
const packageJsonPath = join26(process.cwd(), "package.json");
|
|
10974
11163
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
10975
11164
|
const deps = packageJson.dependencies ?? {};
|
|
10976
11165
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -10985,7 +11174,7 @@ class MakeServiceCommand {
|
|
|
10985
11174
|
}
|
|
10986
11175
|
}
|
|
10987
11176
|
MakeServiceCommand = __legacyDecorateClassTS([
|
|
10988
|
-
|
|
11177
|
+
decorator26.command()
|
|
10989
11178
|
], MakeServiceCommand);
|
|
10990
11179
|
|
|
10991
11180
|
// src/commands/MakeResourceBookCommand.ts
|
|
@@ -10998,7 +11187,7 @@ class MakeResourceBookCommand {
|
|
|
10998
11187
|
}
|
|
10999
11188
|
async run() {
|
|
11000
11189
|
const module = "book";
|
|
11001
|
-
const base =
|
|
11190
|
+
const base = join27("modules", module);
|
|
11002
11191
|
const makeModuleCommand = new MakeModuleCommand;
|
|
11003
11192
|
await makeModuleCommand.run({ name: module, silent: true, skipMigrations: false, skipSeeds: true });
|
|
11004
11193
|
const makeEntityCommand = new MakeEntityCommand;
|
|
@@ -11018,26 +11207,26 @@ class MakeResourceBookCommand {
|
|
|
11018
11207
|
for (const controller of controllers) {
|
|
11019
11208
|
await makeControllerCommand.run({ ...controller, module, isSocket: false });
|
|
11020
11209
|
}
|
|
11021
|
-
const controllersDir =
|
|
11022
|
-
await Bun.write(
|
|
11023
|
-
await Bun.write(
|
|
11024
|
-
await Bun.write(
|
|
11025
|
-
await Bun.write(
|
|
11026
|
-
await Bun.write(
|
|
11210
|
+
const controllersDir = join27(process.cwd(), base, "src", "controllers");
|
|
11211
|
+
await Bun.write(join27(controllersDir, "CreateBookController.ts"), CreateBookController_default);
|
|
11212
|
+
await Bun.write(join27(controllersDir, "GetBookController.ts"), GetBookController_default);
|
|
11213
|
+
await Bun.write(join27(controllersDir, "ListBooksController.ts"), ListBooksController_default);
|
|
11214
|
+
await Bun.write(join27(controllersDir, "UpdateBookController.ts"), UpdateBookController_default);
|
|
11215
|
+
await Bun.write(join27(controllersDir, "DeleteBookController.ts"), DeleteBookController_default);
|
|
11027
11216
|
const makeServiceCommand = new MakeServiceCommand;
|
|
11028
11217
|
const services = ["CreateBook", "GetBook", "ListBooks", "UpdateBook", "DeleteBook"];
|
|
11029
11218
|
for (const name of services) {
|
|
11030
11219
|
await makeServiceCommand.run({ name, module });
|
|
11031
11220
|
}
|
|
11032
|
-
const servicesDir =
|
|
11033
|
-
await Bun.write(
|
|
11034
|
-
await Bun.write(
|
|
11035
|
-
await Bun.write(
|
|
11036
|
-
await Bun.write(
|
|
11037
|
-
await Bun.write(
|
|
11038
|
-
const entityPath =
|
|
11221
|
+
const servicesDir = join27(process.cwd(), base, "src", "services");
|
|
11222
|
+
await Bun.write(join27(servicesDir, "CreateBookService.ts"), CreateBookService_default);
|
|
11223
|
+
await Bun.write(join27(servicesDir, "GetBookService.ts"), GetBookService_default);
|
|
11224
|
+
await Bun.write(join27(servicesDir, "ListBooksService.ts"), ListBooksService_default);
|
|
11225
|
+
await Bun.write(join27(servicesDir, "UpdateBookService.ts"), UpdateBookService_default);
|
|
11226
|
+
await Bun.write(join27(servicesDir, "DeleteBookService.ts"), DeleteBookService_default);
|
|
11227
|
+
const entityPath = join27(process.cwd(), base, "src", "entities", "BookEntity.ts");
|
|
11039
11228
|
await Bun.write(entityPath, BookEntity_default);
|
|
11040
|
-
const migrationsDir =
|
|
11229
|
+
const migrationsDir = join27(process.cwd(), base, "src", "migrations");
|
|
11041
11230
|
const glob = new Glob("Migration*.ts");
|
|
11042
11231
|
for await (const file of glob.scan(migrationsDir)) {
|
|
11043
11232
|
if (file === "migrations.ts")
|
|
@@ -11045,28 +11234,28 @@ class MakeResourceBookCommand {
|
|
|
11045
11234
|
const name = file.replace(/\.ts$/, "");
|
|
11046
11235
|
const version = name.replace("Migration", "");
|
|
11047
11236
|
const content = BookMigration_default.replaceAll("{{ name }}", name).replaceAll("{{ version }}", version);
|
|
11048
|
-
await Bun.write(
|
|
11237
|
+
await Bun.write(join27(migrationsDir, file), content);
|
|
11049
11238
|
}
|
|
11050
|
-
const repositoryPath =
|
|
11239
|
+
const repositoryPath = join27(process.cwd(), base, "src", "repositories", "BookRepository.ts");
|
|
11051
11240
|
await Bun.write(repositoryPath, BookRepository_default);
|
|
11052
11241
|
}
|
|
11053
11242
|
}
|
|
11054
11243
|
MakeResourceBookCommand = __legacyDecorateClassTS([
|
|
11055
|
-
|
|
11244
|
+
decorator27.command()
|
|
11056
11245
|
], MakeResourceBookCommand);
|
|
11057
11246
|
// src/commands/MakeSeedCommand.ts
|
|
11058
|
-
import { join as
|
|
11059
|
-
import { decorator as
|
|
11060
|
-
import { TerminalLogger as
|
|
11247
|
+
import { join as join28 } from "path";
|
|
11248
|
+
import { decorator as decorator28 } from "@ooneex/command";
|
|
11249
|
+
import { TerminalLogger as TerminalLogger26 } from "@ooneex/logger";
|
|
11061
11250
|
import { seedCreate } from "@ooneex/seeds";
|
|
11062
11251
|
|
|
11063
11252
|
// src/templates/module/seed.run.txt
|
|
11064
11253
|
var seed_run_default = `#!/usr/bin/env bun
|
|
11065
11254
|
|
|
11066
|
-
import {
|
|
11255
|
+
import { run } from "@ooneex/seeds";
|
|
11067
11256
|
import "@/seeds/seeds";
|
|
11068
11257
|
|
|
11069
|
-
await
|
|
11258
|
+
await run();
|
|
11070
11259
|
`;
|
|
11071
11260
|
|
|
11072
11261
|
// src/commands/MakeSeedCommand.ts
|
|
@@ -11085,26 +11274,19 @@ class MakeSeedCommand {
|
|
|
11085
11274
|
if (module) {
|
|
11086
11275
|
await ensureModule(module);
|
|
11087
11276
|
}
|
|
11088
|
-
const base = module ?
|
|
11277
|
+
const base = module ? join28("modules", module) : ".";
|
|
11089
11278
|
const { seedPath: filePath, dataPath } = await seedCreate({
|
|
11090
11279
|
name,
|
|
11091
|
-
seedsDir:
|
|
11092
|
-
testsDir:
|
|
11280
|
+
seedsDir: join28(base, "src", "seeds"),
|
|
11281
|
+
testsDir: join28(base, "tests", "seeds")
|
|
11093
11282
|
});
|
|
11094
|
-
const binSeedRunPath =
|
|
11283
|
+
const binSeedRunPath = join28(process.cwd(), base, "bin", "seed", "run.ts");
|
|
11095
11284
|
const binSeedRunFile = Bun.file(binSeedRunPath);
|
|
11096
11285
|
if (!await binSeedRunFile.exists()) {
|
|
11097
11286
|
await Bun.write(binSeedRunPath, seed_run_default);
|
|
11098
11287
|
}
|
|
11099
|
-
const packageJsonPath =
|
|
11100
|
-
const
|
|
11101
|
-
if (await packageJsonFile.exists()) {
|
|
11102
|
-
const packageJson = await packageJsonFile.json();
|
|
11103
|
-
packageJson.scripts = packageJson.scripts || {};
|
|
11104
|
-
packageJson.scripts["seed:run"] = "bun ./bin/seed/run.ts";
|
|
11105
|
-
await Bun.write(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
11106
|
-
}
|
|
11107
|
-
const logger = new TerminalLogger23;
|
|
11288
|
+
const packageJsonPath = join28(process.cwd(), base, "package.json");
|
|
11289
|
+
const logger = new TerminalLogger26;
|
|
11108
11290
|
logger.success(`${filePath} created successfully`, undefined, {
|
|
11109
11291
|
showTimestamp: false,
|
|
11110
11292
|
showArrow: false,
|
|
@@ -11115,17 +11297,12 @@ class MakeSeedCommand {
|
|
|
11115
11297
|
showArrow: false,
|
|
11116
11298
|
useSymbol: true
|
|
11117
11299
|
});
|
|
11118
|
-
logger.info("Run 'bun run seed:run' to execute seeds", undefined, {
|
|
11119
|
-
showTimestamp: false,
|
|
11120
|
-
showArrow: true,
|
|
11121
|
-
showLevel: false
|
|
11122
|
-
});
|
|
11123
11300
|
const pkgJson = await Bun.file(packageJsonPath).json();
|
|
11124
11301
|
const deps = pkgJson.dependencies ?? {};
|
|
11125
11302
|
const devDeps = pkgJson.devDependencies ?? {};
|
|
11126
11303
|
if (!deps["@ooneex/seeds"] && !devDeps["@ooneex/seeds"]) {
|
|
11127
11304
|
const install = Bun.spawn(["bun", "add", "--dev", "@ooneex/seeds"], {
|
|
11128
|
-
cwd:
|
|
11305
|
+
cwd: join28(process.cwd(), base),
|
|
11129
11306
|
stdout: "ignore",
|
|
11130
11307
|
stderr: "inherit"
|
|
11131
11308
|
});
|
|
@@ -11134,12 +11311,12 @@ class MakeSeedCommand {
|
|
|
11134
11311
|
}
|
|
11135
11312
|
}
|
|
11136
11313
|
MakeSeedCommand = __legacyDecorateClassTS([
|
|
11137
|
-
|
|
11314
|
+
decorator28.command()
|
|
11138
11315
|
], MakeSeedCommand);
|
|
11139
11316
|
// src/commands/MakeStorageCommand.ts
|
|
11140
|
-
import { join as
|
|
11141
|
-
import { decorator as
|
|
11142
|
-
import { TerminalLogger as
|
|
11317
|
+
import { join as join29 } from "path";
|
|
11318
|
+
import { decorator as decorator29 } from "@ooneex/command";
|
|
11319
|
+
import { TerminalLogger as TerminalLogger27 } from "@ooneex/logger";
|
|
11143
11320
|
import { toPascalCase as toPascalCase16, toSnakeCase as toSnakeCase3 } from "@ooneex/utils";
|
|
11144
11321
|
|
|
11145
11322
|
// src/templates/storage.test.txt
|
|
@@ -11239,28 +11416,28 @@ class MakeStorageCommand {
|
|
|
11239
11416
|
if (module) {
|
|
11240
11417
|
await ensureModule(module);
|
|
11241
11418
|
}
|
|
11242
|
-
const base = module ?
|
|
11243
|
-
const storageLocalDir =
|
|
11244
|
-
const storageDir =
|
|
11245
|
-
const filePath =
|
|
11419
|
+
const base = module ? join29("modules", module) : ".";
|
|
11420
|
+
const storageLocalDir = join29(base, "src", "storage");
|
|
11421
|
+
const storageDir = join29(process.cwd(), storageLocalDir);
|
|
11422
|
+
const filePath = join29(storageDir, `${name}Storage.ts`);
|
|
11246
11423
|
await Bun.write(filePath, content);
|
|
11247
11424
|
const testContent = storage_test_default.replace(/{{NAME}}/g, name);
|
|
11248
|
-
const testsLocalDir =
|
|
11249
|
-
const testsDir =
|
|
11250
|
-
const testFilePath =
|
|
11425
|
+
const testsLocalDir = join29(base, "tests", "storage");
|
|
11426
|
+
const testsDir = join29(process.cwd(), testsLocalDir);
|
|
11427
|
+
const testFilePath = join29(testsDir, `${name}Storage.spec.ts`);
|
|
11251
11428
|
await Bun.write(testFilePath, testContent);
|
|
11252
|
-
const logger = new
|
|
11253
|
-
logger.success(`${
|
|
11429
|
+
const logger = new TerminalLogger27;
|
|
11430
|
+
logger.success(`${join29(storageLocalDir, name)}Storage.ts created successfully`, undefined, {
|
|
11254
11431
|
showTimestamp: false,
|
|
11255
11432
|
showArrow: false,
|
|
11256
11433
|
useSymbol: true
|
|
11257
11434
|
});
|
|
11258
|
-
logger.success(`${
|
|
11435
|
+
logger.success(`${join29(testsLocalDir, name)}Storage.spec.ts created successfully`, undefined, {
|
|
11259
11436
|
showTimestamp: false,
|
|
11260
11437
|
showArrow: false,
|
|
11261
11438
|
useSymbol: true
|
|
11262
11439
|
});
|
|
11263
|
-
const packageJsonPath =
|
|
11440
|
+
const packageJsonPath = join29(process.cwd(), "package.json");
|
|
11264
11441
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
11265
11442
|
const deps = packageJson.dependencies ?? {};
|
|
11266
11443
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -11275,12 +11452,12 @@ class MakeStorageCommand {
|
|
|
11275
11452
|
}
|
|
11276
11453
|
}
|
|
11277
11454
|
MakeStorageCommand = __legacyDecorateClassTS([
|
|
11278
|
-
|
|
11455
|
+
decorator29.command()
|
|
11279
11456
|
], MakeStorageCommand);
|
|
11280
11457
|
// src/commands/MakeVectorDatabaseCommand.ts
|
|
11281
|
-
import { join as
|
|
11282
|
-
import { decorator as
|
|
11283
|
-
import { TerminalLogger as
|
|
11458
|
+
import { join as join30 } from "path";
|
|
11459
|
+
import { decorator as decorator30 } from "@ooneex/command";
|
|
11460
|
+
import { TerminalLogger as TerminalLogger28 } from "@ooneex/logger";
|
|
11284
11461
|
import { toPascalCase as toPascalCase17 } from "@ooneex/utils";
|
|
11285
11462
|
|
|
11286
11463
|
// src/templates/vector-database.test.txt
|
|
@@ -11354,28 +11531,28 @@ class MakeVectorDatabaseCommand {
|
|
|
11354
11531
|
if (module) {
|
|
11355
11532
|
await ensureModule(module);
|
|
11356
11533
|
}
|
|
11357
|
-
const base = module ?
|
|
11358
|
-
const vectorDatabaseLocalDir =
|
|
11359
|
-
const vectorDatabaseDir =
|
|
11360
|
-
const filePath =
|
|
11534
|
+
const base = module ? join30("modules", module) : ".";
|
|
11535
|
+
const vectorDatabaseLocalDir = join30(base, "src", "databases");
|
|
11536
|
+
const vectorDatabaseDir = join30(process.cwd(), vectorDatabaseLocalDir);
|
|
11537
|
+
const filePath = join30(vectorDatabaseDir, `${name}VectorDatabase.ts`);
|
|
11361
11538
|
await Bun.write(filePath, content);
|
|
11362
11539
|
const testContent = vector_database_test_default.replace(/{{NAME}}/g, name);
|
|
11363
|
-
const testsLocalDir =
|
|
11364
|
-
const testsDir =
|
|
11365
|
-
const testFilePath =
|
|
11540
|
+
const testsLocalDir = join30(base, "tests", "databases");
|
|
11541
|
+
const testsDir = join30(process.cwd(), testsLocalDir);
|
|
11542
|
+
const testFilePath = join30(testsDir, `${name}VectorDatabase.spec.ts`);
|
|
11366
11543
|
await Bun.write(testFilePath, testContent);
|
|
11367
|
-
const logger = new
|
|
11368
|
-
logger.success(`${
|
|
11544
|
+
const logger = new TerminalLogger28;
|
|
11545
|
+
logger.success(`${join30(vectorDatabaseLocalDir, name)}VectorDatabase.ts created successfully`, undefined, {
|
|
11369
11546
|
showTimestamp: false,
|
|
11370
11547
|
showArrow: false,
|
|
11371
11548
|
useSymbol: true
|
|
11372
11549
|
});
|
|
11373
|
-
logger.success(`${
|
|
11550
|
+
logger.success(`${join30(testsLocalDir, name)}VectorDatabase.spec.ts created successfully`, undefined, {
|
|
11374
11551
|
showTimestamp: false,
|
|
11375
11552
|
showArrow: false,
|
|
11376
11553
|
useSymbol: true
|
|
11377
11554
|
});
|
|
11378
|
-
const packageJsonPath =
|
|
11555
|
+
const packageJsonPath = join30(process.cwd(), "package.json");
|
|
11379
11556
|
const packageJson = await Bun.file(packageJsonPath).json();
|
|
11380
11557
|
const deps = packageJson.dependencies ?? {};
|
|
11381
11558
|
const devDeps = packageJson.devDependencies ?? {};
|
|
@@ -11390,9 +11567,157 @@ class MakeVectorDatabaseCommand {
|
|
|
11390
11567
|
}
|
|
11391
11568
|
}
|
|
11392
11569
|
MakeVectorDatabaseCommand = __legacyDecorateClassTS([
|
|
11393
|
-
|
|
11570
|
+
decorator30.command()
|
|
11394
11571
|
], MakeVectorDatabaseCommand);
|
|
11572
|
+
// src/commands/MigrationUpCommand.ts
|
|
11573
|
+
import { existsSync } from "fs";
|
|
11574
|
+
import { join as join31 } from "path";
|
|
11575
|
+
import { decorator as decorator31 } from "@ooneex/command";
|
|
11576
|
+
import { TerminalLogger as TerminalLogger29 } from "@ooneex/logger";
|
|
11577
|
+
class MigrationUpCommand {
|
|
11578
|
+
getName() {
|
|
11579
|
+
return "migration:up";
|
|
11580
|
+
}
|
|
11581
|
+
getDescription() {
|
|
11582
|
+
return "Run migrations for all modules";
|
|
11583
|
+
}
|
|
11584
|
+
async run() {
|
|
11585
|
+
const logger = new TerminalLogger29;
|
|
11586
|
+
const modulesDir = join31(process.cwd(), "modules");
|
|
11587
|
+
if (!existsSync(modulesDir)) {
|
|
11588
|
+
logger.warn("No modules with migrations found", undefined, {
|
|
11589
|
+
showTimestamp: false,
|
|
11590
|
+
showArrow: false,
|
|
11591
|
+
useSymbol: true
|
|
11592
|
+
});
|
|
11593
|
+
return;
|
|
11594
|
+
}
|
|
11595
|
+
const glob = new Bun.Glob("*/package.json");
|
|
11596
|
+
const modules = [];
|
|
11597
|
+
for await (const match of glob.scan({ cwd: modulesDir, onlyFiles: true })) {
|
|
11598
|
+
const entry = match.replace("/package.json", "");
|
|
11599
|
+
const moduleDir = join31(modulesDir, entry);
|
|
11600
|
+
const migrationUpFile = Bun.file(join31(moduleDir, "bin", "migration", "up.ts"));
|
|
11601
|
+
if (await migrationUpFile.exists()) {
|
|
11602
|
+
const packageJson = await Bun.file(join31(modulesDir, match)).json();
|
|
11603
|
+
modules.push({ name: packageJson.name ?? entry, dir: moduleDir });
|
|
11604
|
+
}
|
|
11605
|
+
}
|
|
11606
|
+
if (modules.length === 0) {
|
|
11607
|
+
logger.warn("No modules with migrations found", undefined, {
|
|
11608
|
+
showTimestamp: false,
|
|
11609
|
+
showArrow: false,
|
|
11610
|
+
useSymbol: true
|
|
11611
|
+
});
|
|
11612
|
+
return;
|
|
11613
|
+
}
|
|
11614
|
+
for (const { name, dir } of modules) {
|
|
11615
|
+
const migrationUpPath = join31(dir, "bin", "migration", "up.ts");
|
|
11616
|
+
logger.info(`Running migrations for ${name}...`, undefined, {
|
|
11617
|
+
showTimestamp: false,
|
|
11618
|
+
showArrow: false,
|
|
11619
|
+
useSymbol: true
|
|
11620
|
+
});
|
|
11621
|
+
const proc = Bun.spawn(["bun", "run", migrationUpPath], {
|
|
11622
|
+
cwd: dir,
|
|
11623
|
+
stdout: "inherit",
|
|
11624
|
+
stderr: "inherit"
|
|
11625
|
+
});
|
|
11626
|
+
const exitCode = await proc.exited;
|
|
11627
|
+
if (exitCode === 0) {
|
|
11628
|
+
logger.success(`Migrations completed for ${name}`, undefined, {
|
|
11629
|
+
showTimestamp: false,
|
|
11630
|
+
showArrow: false,
|
|
11631
|
+
useSymbol: true
|
|
11632
|
+
});
|
|
11633
|
+
} else {
|
|
11634
|
+
logger.error(`Migrations failed for ${name} (exit code: ${exitCode})`, undefined, {
|
|
11635
|
+
showTimestamp: false,
|
|
11636
|
+
showArrow: false,
|
|
11637
|
+
useSymbol: true
|
|
11638
|
+
});
|
|
11639
|
+
}
|
|
11640
|
+
}
|
|
11641
|
+
}
|
|
11642
|
+
}
|
|
11643
|
+
MigrationUpCommand = __legacyDecorateClassTS([
|
|
11644
|
+
decorator31.command()
|
|
11645
|
+
], MigrationUpCommand);
|
|
11646
|
+
// src/commands/SeedRunCommand.ts
|
|
11647
|
+
import { existsSync as existsSync2 } from "fs";
|
|
11648
|
+
import { join as join32 } from "path";
|
|
11649
|
+
import { decorator as decorator32 } from "@ooneex/command";
|
|
11650
|
+
import { TerminalLogger as TerminalLogger30 } from "@ooneex/logger";
|
|
11651
|
+
class SeedRunCommand {
|
|
11652
|
+
getName() {
|
|
11653
|
+
return "seed:run";
|
|
11654
|
+
}
|
|
11655
|
+
getDescription() {
|
|
11656
|
+
return "Run seeds for all modules";
|
|
11657
|
+
}
|
|
11658
|
+
async run() {
|
|
11659
|
+
const logger = new TerminalLogger30;
|
|
11660
|
+
const modulesDir = join32(process.cwd(), "modules");
|
|
11661
|
+
if (!existsSync2(modulesDir)) {
|
|
11662
|
+
logger.warn("No modules with seeds found", undefined, {
|
|
11663
|
+
showTimestamp: false,
|
|
11664
|
+
showArrow: false,
|
|
11665
|
+
useSymbol: true
|
|
11666
|
+
});
|
|
11667
|
+
return;
|
|
11668
|
+
}
|
|
11669
|
+
const glob = new Bun.Glob("*/package.json");
|
|
11670
|
+
const modules = [];
|
|
11671
|
+
for await (const match of glob.scan({ cwd: modulesDir, onlyFiles: true })) {
|
|
11672
|
+
const entry = match.replace("/package.json", "");
|
|
11673
|
+
const moduleDir = join32(modulesDir, entry);
|
|
11674
|
+
const seedRunFile = Bun.file(join32(moduleDir, "bin", "seed", "run.ts"));
|
|
11675
|
+
if (await seedRunFile.exists()) {
|
|
11676
|
+
const packageJson = await Bun.file(join32(modulesDir, match)).json();
|
|
11677
|
+
modules.push({ name: packageJson.name ?? entry, dir: moduleDir });
|
|
11678
|
+
}
|
|
11679
|
+
}
|
|
11680
|
+
if (modules.length === 0) {
|
|
11681
|
+
logger.warn("No modules with seeds found", undefined, {
|
|
11682
|
+
showTimestamp: false,
|
|
11683
|
+
showArrow: false,
|
|
11684
|
+
useSymbol: true
|
|
11685
|
+
});
|
|
11686
|
+
return;
|
|
11687
|
+
}
|
|
11688
|
+
for (const { name, dir } of modules) {
|
|
11689
|
+
const seedRunPath = join32(dir, "bin", "seed", "run.ts");
|
|
11690
|
+
logger.info(`Running seeds for ${name}...`, undefined, {
|
|
11691
|
+
showTimestamp: false,
|
|
11692
|
+
showArrow: false,
|
|
11693
|
+
useSymbol: true
|
|
11694
|
+
});
|
|
11695
|
+
const proc = Bun.spawn(["bun", "run", seedRunPath], {
|
|
11696
|
+
cwd: dir,
|
|
11697
|
+
stdout: "inherit",
|
|
11698
|
+
stderr: "inherit"
|
|
11699
|
+
});
|
|
11700
|
+
const exitCode = await proc.exited;
|
|
11701
|
+
if (exitCode === 0) {
|
|
11702
|
+
logger.success(`Seeds completed for ${name}`, undefined, {
|
|
11703
|
+
showTimestamp: false,
|
|
11704
|
+
showArrow: false,
|
|
11705
|
+
useSymbol: true
|
|
11706
|
+
});
|
|
11707
|
+
} else {
|
|
11708
|
+
logger.error(`Seeds failed for ${name} (exit code: ${exitCode})`, undefined, {
|
|
11709
|
+
showTimestamp: false,
|
|
11710
|
+
showArrow: false,
|
|
11711
|
+
useSymbol: true
|
|
11712
|
+
});
|
|
11713
|
+
}
|
|
11714
|
+
}
|
|
11715
|
+
}
|
|
11716
|
+
}
|
|
11717
|
+
SeedRunCommand = __legacyDecorateClassTS([
|
|
11718
|
+
decorator32.command()
|
|
11719
|
+
], SeedRunCommand);
|
|
11395
11720
|
// src/index.ts
|
|
11396
11721
|
await run();
|
|
11397
11722
|
|
|
11398
|
-
//# debugId=
|
|
11723
|
+
//# debugId=1317A3FF7CEC8E3164756E2164756E21
|